55 * so a partially-denied block is trimmed to the operations this viewer may configure.
66 */
77
8+ import { omit } from '@sim/utils/object'
9+ import { type V2BlockDetail , v2BlockDetailSchema } from '@/lib/api/contracts/v2/catalog'
810import { agentCliFail } from '@/lib/mothership/agent-cli/types'
911import type { AgentCliRawResult } from '@/lib/mothership/generated/agent-cli'
1012import { resolveDeniedBlockOperations } from '@/lib/mothership/integration-tool-projection'
@@ -16,22 +18,13 @@ export interface CurationViewer {
1618 userId : string
1719}
1820
19- interface BlockDetailShape {
20- type : string
21- operations ?: Record < string , unknown >
22- tools ?: Array < { id ?: unknown } >
23- }
24-
25- function parseBlockDetail ( stdout : string ) : BlockDetailShape | null {
26- let parsed : unknown
21+ function parseBlockDetail ( stdout : string ) : V2BlockDetail | null {
2722 try {
28- parsed = JSON . parse ( stdout )
23+ const parsed = v2BlockDetailSchema . safeParse ( JSON . parse ( stdout ) )
24+ return parsed . success ? parsed . data : null
2925 } catch {
3026 return null
3127 }
32- if ( ! parsed || typeof parsed !== 'object' || Array . isArray ( parsed ) ) return null
33- const candidate = parsed as { type ?: unknown }
34- return typeof candidate . type === 'string' ? ( parsed as BlockDetailShape ) : null
3528}
3629
3730export async function curateBlockDetail (
@@ -45,16 +38,28 @@ export async function curateBlockDetail(
4538 if ( ! deniedTools ?. length ) return result
4639 const isToolAllowed = createToolAccessGate ( deniedTools )
4740 const denied = resolveDeniedBlockOperations ( deniedTools , isToolAllowed )
48- if ( denied . fullyDenied . has ( detail . type ) ) {
49- return agentCliFail ( `Block "${ detail . type } " is not available to you in this workspace.` )
41+ if ( denied . fullyDenied . has ( detail . id ) ) {
42+ return agentCliFail ( `Block "${ detail . id } " is not available to you in this workspace.` )
5043 }
51- const deniedOperations = denied . needsProjection . get ( detail . type )
44+ const deniedOperations = denied . needsProjection . get ( detail . id )
5245 if ( ! deniedOperations ) return result
53- const operations = Object . fromEntries (
54- Object . entries ( detail . operations ?? { } ) . filter ( ( [ id ] ) => ! deniedOperations . has ( id ) )
46+ const operations = omit ( detail . operations , [ ...deniedOperations ] )
47+ const tools = detail . tools . filter ( ( tool ) => isToolAllowed ( tool . id ) )
48+ const inputSchema = detail . inputSchema . map ( ( field ) =>
49+ field . id === 'operation'
50+ ? { ...field , options : field . options ?. filter ( ( option ) => ! deniedOperations . has ( option . id ) ) }
51+ : field
5552 )
56- const tools = ( detail . tools ?? [ ] ) . filter (
57- ( tool ) => typeof tool . id !== 'string' || isToolAllowed ( tool . id )
58- )
59- return { ...result , stdout : JSON . stringify ( { ...detail , operations, tools } , null , 2 ) }
53+ return {
54+ ...result ,
55+ stdout : JSON . stringify ( {
56+ ...detail ,
57+ operations,
58+ operationIds : detail . operationIds . filter ( ( id ) => ! deniedOperations . has ( id ) ) ,
59+ operationInputSchema : omit ( detail . operationInputSchema , [ ...deniedOperations ] ) ,
60+ inputSchema,
61+ tools,
62+ toolIds : detail . toolIds . filter ( isToolAllowed ) ,
63+ } ) ,
64+ }
6065}
0 commit comments