{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/canvas/sdk-shim/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Child-side `@github/copilot-sdk/extension` implementation.\n *\n * Design: `docs/canvas-extensions-design.md` §4, §4.1. A canvas extension from\n * `github/awesome-copilot` is a directory of plain `.mjs` files whose only\n * external import is `@github/copilot-sdk/extension` — GitHub's own\n * `docs/extensions.md` states the import \"is resolved automatically — you don't\n * install it\", and none of the 23 catalog extensions ships `node_modules`. This\n * module is what hoocode resolves that specifier to inside the forked child, so a\n * catalog canvas runs byte-identical to upstream with nothing written into its\n * directory.\n *\n * What this is not: the SDK's `joinSession` resolves to a full `CopilotSession`\n * (messaging, events, RPC passthrough, factories). This shim implements the\n * canvas surface and `log` only. `tools`, `hooks`, and `factories` are accepted\n * and reported as unsupported so an extension that uses them fails visibly rather\n * than half-working (design doc §6.2). The canvas types themselves are held\n * structurally identical to the SDK's by\n * `test/canvas-protocol-conformance.test.ts`.\n */\n\nimport {\n\tCANVAS_ENVELOPE_VERSION,\n\tCANVAS_ERROR_CODE_INTERNAL,\n\tCANVAS_ERROR_CODE_UNKNOWN_TARGET,\n\tCANVAS_METHOD_CLOSE,\n\tCANVAS_METHOD_INVOKE_ACTION,\n\tCANVAS_METHOD_OPEN,\n\tCANVAS_RESERVED_ACTION_PREFIX,\n\tCANVAS_SDK_PROTOCOL_VERSION,\n\ttype CanvasActionDeclaration,\n\ttype CanvasActionInvokeResult,\n\ttype CanvasChildToHostMessage,\n\ttype CanvasDeclaration,\n\ttype CanvasJsonSchema,\n\ttype CanvasLogLevel,\n\tCanvasMessageDecoder,\n\ttype CanvasProviderCloseRequest,\n\ttype CanvasProviderInvokeActionRequest,\n\ttype CanvasProviderOpenRequest,\n\ttype CanvasProviderOpenResult,\n\tencodeCanvasMessage,\n\tisCanvasHostToChildMessage,\n\ttype JsonValue,\n} from \"../protocol.js\";\n\n/**\n * A single agent-callable action contributed by a canvas. Mirrors the SDK's\n * `CanvasAction`: the `handler` closure is stripped before the declaration is\n * sent and dispatched in-process here.\n */\nexport interface CanvasAction {\n\t/** Action identifier, unique within the canvas. */\n\tname: string;\n\t/** Description shown to the model when picking an action. */\n\tdescription?: string;\n\t/** Optional JSON Schema for the action's `input` payload. */\n\tinputSchema?: CanvasJsonSchema;\n\t/** Required per-action dispatch handler. */\n\thandler: (ctx: CanvasProviderInvokeActionRequest) => Promise<unknown> | unknown;\n}\n\n/** Options accepted by {@link createCanvas}. Mirrors the SDK's `CanvasOptions`. */\nexport interface CanvasOptions {\n\t/** Canvas id, unique within the declaring connection. */\n\tid: string;\n\t/** Human-readable label shown in discovery and host UI chrome. */\n\tdisplayName: string;\n\t/** Short, single-sentence description shown to the agent in canvas catalogs. */\n\tdescription: string;\n\t/** Optional JSON Schema for the `input` payload accepted by `canvas.open`. */\n\tinputSchema?: CanvasJsonSchema;\n\t/** Agent-invocable actions, each carrying its own required handler. */\n\tactions?: CanvasAction[];\n\t/** Required. Open a new canvas instance. */\n\topen: (ctx: CanvasProviderOpenRequest) => Promise<CanvasProviderOpenResult> | CanvasProviderOpenResult;\n\t/**\n\t * Optional. Notified when a canvas instance is closed. Fire-and-forget: the\n\t * return value is ignored and errors are logged but not surfaced.\n\t */\n\tonClose?: (ctx: CanvasProviderCloseRequest) => Promise<void> | void;\n}\n\n/** Structured error returned from canvas handlers. Mirrors the SDK's `CanvasError`. */\nexport class CanvasError extends Error {\n\treadonly code: string;\n\n\tconstructor(code: string, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CanvasError\";\n\t\tthis.code = code;\n\t}\n\n\t/** Default error when an action is declared but no `handler` is wired. */\n\tstatic noHandler(): CanvasError {\n\t\treturn new CanvasError(\"no_handler\", \"Canvas action has no handler.\");\n\t}\n}\n\n/** A registered canvas: declarative metadata plus in-process handler closures. */\nexport class Canvas {\n\treadonly declaration: CanvasDeclaration;\n\treadonly open: NonNullable<CanvasOptions[\"open\"]>;\n\treadonly onClose?: CanvasOptions[\"onClose\"];\n\t/** Handlers by action name, kept out of the declaration that crosses the wire. */\n\treadonly handlers: ReadonlyMap<string, CanvasAction[\"handler\"]>;\n\n\tconstructor(options: CanvasOptions) {\n\t\tconst actions: CanvasActionDeclaration[] = [];\n\t\tconst handlers = new Map<string, CanvasAction[\"handler\"]>();\n\t\tfor (const action of options.actions ?? []) {\n\t\t\tif (action.name.startsWith(CANVAS_RESERVED_ACTION_PREFIX)) {\n\t\t\t\tthrow new CanvasError(\n\t\t\t\t\t\"invalid_action_name\",\n\t\t\t\t\t`Action \"${action.name}\" must not start with \"${CANVAS_RESERVED_ACTION_PREFIX}\" — that prefix is reserved for lifecycle verbs.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (handlers.has(action.name)) {\n\t\t\t\tthrow new CanvasError(\"duplicate_action\", `Action \"${action.name}\" is declared more than once.`);\n\t\t\t}\n\t\t\tactions.push({ name: action.name, description: action.description, inputSchema: action.inputSchema });\n\t\t\thandlers.set(action.name, action.handler);\n\t\t}\n\t\tthis.declaration = {\n\t\t\tid: options.id,\n\t\t\tdisplayName: options.displayName,\n\t\t\tdescription: options.description,\n\t\t\tinputSchema: options.inputSchema,\n\t\t\tactions: actions.length > 0 ? actions : undefined,\n\t\t};\n\t\tthis.open = options.open;\n\t\tthis.onClose = options.onClose;\n\t\tthis.handlers = handlers;\n\t}\n}\n\n/** Create a canvas declaration with bound in-process handlers. */\nexport function createCanvas(options: CanvasOptions): Canvas {\n\treturn new Canvas(options);\n}\n\n/** Surfaces a Copilot extension may declare that this shim does not implement. */\nconst UNSUPPORTED_KEYS = [\"tools\", \"hooks\", \"factories\", \"onPermissionRequest\"] as const;\n\n/** Configuration accepted by {@link joinSession}. */\nexport interface JoinSessionConfig {\n\tcanvases?: Canvas[];\n\t/** Accepted and reported as unsupported (design doc §6.2). */\n\ttools?: unknown;\n\t/** Accepted and reported as unsupported (design doc §6.2). */\n\thooks?: unknown;\n\t/** Accepted and reported as unsupported (design doc §6.2). */\n\tfactories?: unknown;\n\t/** Accepted and reported as unsupported (design doc §6.2). */\n\tonPermissionRequest?: unknown;\n}\n\n/**\n * The subset of the SDK's `CopilotSession` this shim provides. Deliberately small:\n * see the module header.\n */\nexport interface CanvasShimSession {\n\t/** Log a message to the session timeline. */\n\tlog(message: string, options?: { level?: CanvasLogLevel; ephemeral?: boolean }): Promise<void>;\n}\n\n/** Streams the shim reads from and writes to. Injectable so tests need no child process. */\nexport interface CanvasShimStreams {\n\tinput: { setEncoding(encoding: \"utf8\"): unknown; on(event: \"data\", listener: (chunk: string) => void): unknown };\n\twrite(chunk: string): unknown;\n\t/** Provider identifier; the runner passes it in the environment. */\n\textensionId: string;\n}\n\nfunction defaultStreams(): CanvasShimStreams {\n\treturn {\n\t\tinput: process.stdin,\n\t\twrite: (chunk: string) => process.stdout.write(chunk),\n\t\textensionId: process.env.HOOCODE_CANVAS_EXTENSION_ID ?? \"unknown-extension\",\n\t};\n}\n\nfunction toJsonValue(value: unknown): JsonValue {\n\t// Handler return values are author-supplied. Round-tripping through JSON is what\n\t// the wire does anyway, so do it here where a cycle or a BigInt fails loudly with\n\t// the action's name attached rather than mid-write.\n\treturn value === undefined ? null : (JSON.parse(JSON.stringify(value)) as JsonValue);\n}\n\nfunction errorOf(cause: unknown): { code: string; message: string } {\n\tif (cause instanceof CanvasError) return { code: cause.code, message: cause.message };\n\treturn {\n\t\tcode: CANVAS_ERROR_CODE_INTERNAL,\n\t\tmessage: cause instanceof Error ? cause.message : String(cause),\n\t};\n}\n\n/**\n * Join the host session: announce the declared canvases, then serve provider\n * callbacks until the input stream ends.\n */\nexport async function joinSession(\n\tconfig: JoinSessionConfig = {},\n\tstreams: CanvasShimStreams = defaultStreams(),\n): Promise<CanvasShimSession> {\n\tconst canvases = new Map<string, Canvas>();\n\tfor (const canvas of config.canvases ?? []) {\n\t\tif (canvases.has(canvas.declaration.id)) {\n\t\t\tthrow new CanvasError(\"duplicate_canvas\", `Canvas \"${canvas.declaration.id}\" is declared more than once.`);\n\t\t}\n\t\tcanvases.set(canvas.declaration.id, canvas);\n\t}\n\n\tconst send = (message: CanvasChildToHostMessage): void => {\n\t\tstreams.write(encodeCanvasMessage(message));\n\t};\n\n\tsend({\n\t\tenvelope: CANVAS_ENVELOPE_VERSION,\n\t\ttype: \"ready\",\n\t\tprotocolVersion: CANVAS_SDK_PROTOCOL_VERSION,\n\t\textensionId: streams.extensionId,\n\t\tcanvases: [...canvases.values()].map((canvas) => canvas.declaration),\n\t\tunsupported: UNSUPPORTED_KEYS.filter((key) => config[key] !== undefined),\n\t});\n\n\tconst decoder = new CanvasMessageDecoder();\n\tstreams.input.setEncoding(\"utf8\");\n\tstreams.input.on(\"data\", (chunk: string) => {\n\t\tfor (const value of decoder.push(chunk).values) {\n\t\t\tif (!isCanvasHostToChildMessage(value)) continue;\n\t\t\tvoid dispatch(canvases, value.id, value.method, value.params, send);\n\t\t}\n\t});\n\n\treturn {\n\t\tlog: async (message, options) => {\n\t\t\tsend({\n\t\t\t\tenvelope: CANVAS_ENVELOPE_VERSION,\n\t\t\t\ttype: \"log\",\n\t\t\t\tmessage,\n\t\t\t\tlevel: options?.level,\n\t\t\t\tephemeral: options?.ephemeral,\n\t\t\t});\n\t\t},\n\t};\n}\n\nasync function dispatch(\n\tcanvases: ReadonlyMap<string, Canvas>,\n\tid: number,\n\tmethod: string,\n\tparams: JsonValue,\n\tsend: (message: CanvasChildToHostMessage) => void,\n): Promise<void> {\n\ttry {\n\t\tsend({\n\t\t\tenvelope: CANVAS_ENVELOPE_VERSION,\n\t\t\ttype: \"response\",\n\t\t\tid,\n\t\t\tresult: await invoke(canvases, method, params),\n\t\t});\n\t} catch (cause) {\n\t\tconst { code, message } = errorOf(cause);\n\t\tsend({ envelope: CANVAS_ENVELOPE_VERSION, type: \"error\", id, code, message });\n\t}\n}\n\nasync function invoke(canvases: ReadonlyMap<string, Canvas>, method: string, params: JsonValue): Promise<JsonValue> {\n\tif (method === CANVAS_METHOD_OPEN) {\n\t\tconst ctx = params as unknown as CanvasProviderOpenRequest;\n\t\treturn toJsonValue(await resolve(canvases, ctx.canvasId).open(ctx));\n\t}\n\tif (method === CANVAS_METHOD_CLOSE) {\n\t\tconst ctx = params as unknown as CanvasProviderCloseRequest;\n\t\t// Fire-and-forget per the SDK contract: a failing onClose must not fail the close.\n\t\tawait resolve(canvases, ctx.canvasId).onClose?.(ctx);\n\t\treturn null;\n\t}\n\tif (method === CANVAS_METHOD_INVOKE_ACTION) {\n\t\tconst ctx = params as unknown as CanvasProviderInvokeActionRequest;\n\t\tconst canvas = resolve(canvases, ctx.canvasId);\n\t\tconst handler = canvas.handlers.get(ctx.actionName);\n\t\tif (!handler) {\n\t\t\tthrow new CanvasError(\n\t\t\t\tCANVAS_ERROR_CODE_UNKNOWN_TARGET,\n\t\t\t\t`Canvas \"${ctx.canvasId}\" does not declare an action named \"${ctx.actionName}\".`,\n\t\t\t);\n\t\t}\n\t\tconst result: CanvasActionInvokeResult = toJsonValue(await handler(ctx));\n\t\treturn result;\n\t}\n\t// Unreachable while the host only sends methods `isCanvasHostToChildMessage`\n\t// accepts, but naming it keeps this function total if the protocol grows.\n\tthrow new CanvasError(CANVAS_ERROR_CODE_UNKNOWN_TARGET, `Unsupported provider method \"${method}\".`);\n}\n\nfunction resolve(canvases: ReadonlyMap<string, Canvas>, canvasId: string): Canvas {\n\tconst canvas = canvases.get(canvasId);\n\tif (!canvas) {\n\t\tthrow new CanvasError(CANVAS_ERROR_CODE_UNKNOWN_TARGET, `No canvas is registered with id \"${canvasId}\".`);\n\t}\n\treturn canvas;\n}\n"]}