@agimon-ai/doompi-core 0.0.1-alpha.77 → 0.0.1-alpha.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/headless.d.cts +2 -2
  2. package/dist/headless.d.mts +2 -2
  3. package/dist/mcp-facet.d.cts +2 -2
  4. package/dist/mcp-facet.d.mts +2 -2
  5. package/dist/runtime-cordis-host.cjs.map +1 -1
  6. package/dist/runtime-cordis-host.d.cts +1 -1
  7. package/dist/runtime-cordis-host.d.cts.map +1 -1
  8. package/dist/runtime-cordis-host.d.mts +1 -1
  9. package/dist/runtime-cordis-host.d.mts.map +1 -1
  10. package/dist/runtime-cordis-host.mjs.map +1 -1
  11. package/dist/runtime-pi-extension.cjs +6 -2
  12. package/dist/runtime-pi-extension.cjs.map +1 -1
  13. package/dist/runtime-pi-extension.d.cts.map +1 -1
  14. package/dist/runtime-pi-extension.d.mts.map +1 -1
  15. package/dist/runtime-pi-extension.mjs +6 -2
  16. package/dist/runtime-pi-extension.mjs.map +1 -1
  17. package/dist/schemas/builtinApiContracts.cjs +1 -1
  18. package/dist/schemas/builtinApiContracts.cjs.map +1 -1
  19. package/dist/schemas/builtinApiContracts.mjs +1 -1
  20. package/dist/schemas/builtinApiContracts.mjs.map +1 -1
  21. package/dist/schemas/headless.cjs.map +1 -1
  22. package/dist/schemas/headless.d.cts +10 -0
  23. package/dist/schemas/headless.d.cts.map +1 -1
  24. package/dist/schemas/headless.d.mts +10 -0
  25. package/dist/schemas/headless.d.mts.map +1 -1
  26. package/dist/schemas/headless.mjs.map +1 -1
  27. package/dist/schemas/mcpFacet.cjs.map +1 -1
  28. package/dist/schemas/mcpFacet.d.cts +26 -0
  29. package/dist/schemas/mcpFacet.d.cts.map +1 -1
  30. package/dist/schemas/mcpFacet.d.mts +26 -0
  31. package/dist/schemas/mcpFacet.d.mts.map +1 -1
  32. package/dist/schemas/mcpFacet.mjs.map +1 -1
  33. package/dist/server/sessionMcpHandler.cjs +34 -5
  34. package/dist/server/sessionMcpHandler.cjs.map +1 -1
  35. package/dist/server/sessionMcpHandler.mjs +34 -5
  36. package/dist/server/sessionMcpHandler.mjs.map +1 -1
  37. package/dist/services/historyCreationFileSystem/index.cjs +3 -2
  38. package/dist/services/historyCreationFileSystem/index.cjs.map +1 -1
  39. package/dist/services/historyCreationFileSystem/index.mjs +3 -2
  40. package/dist/services/historyCreationFileSystem/index.mjs.map +1 -1
  41. package/dist/services/piExtensionHost/index.cjs +19 -7
  42. package/dist/services/piExtensionHost/index.cjs.map +1 -1
  43. package/dist/services/piExtensionHost/index.mjs +19 -7
  44. package/dist/services/piExtensionHost/index.mjs.map +1 -1
  45. package/dist/services/pluginLifecycle/index.cjs +1 -1
  46. package/dist/services/pluginLifecycle/index.cjs.map +1 -1
  47. package/dist/services/pluginLifecycle/index.mjs +1 -1
  48. package/dist/services/pluginLifecycle/index.mjs.map +1 -1
  49. package/dist/systems/main/adapters/headlessSessionHost.cjs +77 -11
  50. package/dist/systems/main/adapters/headlessSessionHost.cjs.map +1 -1
  51. package/dist/systems/main/adapters/headlessSessionHost.d.cts.map +1 -1
  52. package/dist/systems/main/adapters/headlessSessionHost.d.mts.map +1 -1
  53. package/dist/systems/main/adapters/headlessSessionHost.mjs +76 -11
  54. package/dist/systems/main/adapters/headlessSessionHost.mjs.map +1 -1
  55. package/dist/testing/pi/testHost.cjs +6 -0
  56. package/dist/testing/pi/testHost.cjs.map +1 -1
  57. package/dist/testing/pi/testHost.mjs +6 -0
  58. package/dist/testing/pi/testHost.mjs.map +1 -1
  59. package/dist/types/server/sessionToolSurface.d.cts +3 -2
  60. package/dist/types/server/sessionToolSurface.d.cts.map +1 -1
  61. package/dist/types/server/sessionToolSurface.d.mts +3 -2
  62. package/dist/types/server/sessionToolSurface.d.mts.map +1 -1
  63. package/package.json +14 -14
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-pi-extension.mjs","names":[],"sources":["../src/extensions/piExtension.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ToolDefinition } from '@earendil-works/pi-coding-agent';\nimport type { TSchema } from 'typebox';\n\nimport { connectDoomCordisHost, type DoomCordisRuntimeService } from '../pi/cordisHost';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService, type DoomHelpService } from '../schemas/help';\nimport type { PiToolDeclaration } from '../schemas/piTool';\nimport type { DoomPluginTool, DoomPluginCommand } from '../schemas/pluginContributions';\nimport {\n DOOM_TOOL_OVERRIDES_SERVICE,\n requireDoomToolOverrides,\n type DoomToolOverrideClaim,\n} from '../schemas/toolOverrides';\nimport type { DoomToolRestrictionDefinition } from '../schemas/toolSurface';\nimport { DOOM_TOOL_SURFACE_SERVICE, requireDoomToolSurface } from '../schemas/toolSurface';\nimport { createPluginLifecycle, type PluginLifecycleHooks } from '../services/pluginLifecycle';\n\n/** Extract native overloads without copying Pi event payload or result contracts. */\ntype NativeEventOverloads = ExtensionAPI['on'] extends {\n (event: infer E1 extends string, handler: infer H1): void;\n (event: infer E2 extends string, handler: infer H2): void;\n (event: infer E3 extends string, handler: infer H3): void;\n (event: infer E4 extends string, handler: infer H4): void;\n (event: infer E5 extends string, handler: infer H5): void;\n (event: infer E6 extends string, handler: infer H6): void;\n (event: infer E7 extends string, handler: infer H7): void;\n (event: infer E8 extends string, handler: infer H8): void;\n (event: infer E9 extends string, handler: infer H9): void;\n (event: infer E10 extends string, handler: infer H10): void;\n (event: infer E11 extends string, handler: infer H11): void;\n (event: infer E12 extends string, handler: infer H12): void;\n (event: infer E13 extends string, handler: infer H13): void;\n (event: infer E14 extends string, handler: infer H14): void;\n (event: infer E15 extends string, handler: infer H15): void;\n (event: infer E16 extends string, handler: infer H16): void;\n (event: infer E17 extends string, handler: infer H17): void;\n (event: infer E18 extends string, handler: infer H18): void;\n (event: infer E19 extends string, handler: infer H19): void;\n (event: infer E20 extends string, handler: infer H20): void;\n (event: infer E21 extends string, handler: infer H21): void;\n (event: infer E22 extends string, handler: infer H22): void;\n (event: infer E23 extends string, handler: infer H23): void;\n (event: infer E24 extends string, handler: infer H24): void;\n (event: infer E25 extends string, handler: infer H25): void;\n (event: infer E26 extends string, handler: infer H26): void;\n (event: infer E27 extends string, handler: infer H27): void;\n (event: infer E28 extends string, handler: infer H28): void;\n (event: infer E29 extends string, handler: infer H29): void;\n (event: infer E30 extends string, handler: infer H30): void;\n (event: infer E31 extends string, handler: infer H31): void;\n (event: infer E32 extends string, handler: infer H32): void;\n (event: infer E33 extends string, handler: infer H33): void;\n (event: infer E34 extends string, handler: infer H34): void;\n (event: infer E35 extends string, handler: infer H35): void;\n (event: infer E36 extends string, handler: infer H36): void;\n (event: infer E37 extends string, handler: infer H37): void;\n (event: infer E38 extends string, handler: infer H38): void;\n (event: infer E39 extends string, handler: infer H39): void;\n (event: infer E40 extends string, handler: infer H40): void;\n}\n ?\n | [E1, H1]\n | [E2, H2]\n | [E3, H3]\n | [E4, H4]\n | [E5, H5]\n | [E6, H6]\n | [E7, H7]\n | [E8, H8]\n | [E9, H9]\n | [E10, H10]\n | [E11, H11]\n | [E12, H12]\n | [E13, H13]\n | [E14, H14]\n | [E15, H15]\n | [E16, H16]\n | [E17, H17]\n | [E18, H18]\n | [E19, H19]\n | [E20, H20]\n | [E21, H21]\n | [E22, H22]\n | [E23, H23]\n | [E24, H24]\n | [E25, H25]\n | [E26, H26]\n | [E27, H27]\n | [E28, H28]\n | [E29, H29]\n | [E30, H30]\n | [E31, H31]\n | [E32, H32]\n | [E33, H33]\n | [E34, H34]\n | [E35, H35]\n | [E36, H36]\n | [E37, H37]\n | [E38, H38]\n | [E39, H39]\n | [E40, H40]\n : never;\n\nexport type PiEventHandlers = {\n readonly [Pair in NativeEventOverloads as Pair[0]]?: Pair[1];\n};\n\nexport interface PiPluginContext<TOptions = undefined> {\n readonly context: Context;\n readonly pi: ExtensionAPI;\n readonly options: TOptions | undefined;\n readonly runtime: DoomCordisRuntimeService | undefined;\n readonly signal: AbortSignal;\n}\n\nexport type PiToolRestriction = DoomToolRestrictionDefinition;\n\nexport interface PiPluginTool extends DoomPluginTool {\n readonly pi?: Pick<Parameters<ExtensionAPI['registerTool']>[0], 'renderShell' | 'renderCall' | 'renderResult'>;\n}\n\nexport interface PiToolOverride extends DoomToolOverrideClaim {\n /** Contributions registered only while this provider owns the override. */\n readonly tools: readonly string[];\n readonly replacements: readonly (PiToolDeclaration | Parameters<ExtensionAPI['registerTool']>[0])[];\n}\n\nexport type PiToolContribution = PiPluginTool | PiToolDeclaration | Parameters<ExtensionAPI['registerTool']>[0];\n\n/** Snapshots reuse immutable declarations. A registered name cannot adopt a different declaration until remount. */\nexport interface PiToolCollection {\n snapshot(): readonly PiToolContribution[];\n subscribe(listener: () => void): () => void;\n}\n\nexport interface PiPluginContributions<TOptions = undefined> extends PluginLifecycleHooks<PiPluginContext<TOptions>> {\n readonly services?: readonly Parameters<Context['plugin']>[0][];\n readonly tools?: readonly PiToolContribution[] | PiToolCollection;\n readonly commands?: readonly (DoomPluginCommand | readonly [...Parameters<ExtensionAPI['registerCommand']>])[];\n readonly events?: PiEventHandlers;\n readonly resources?: readonly Parameters<DoomHelpService['register']>[0][];\n readonly toolRestrictions?: readonly PiToolRestriction[];\n readonly toolOverrides?: readonly PiToolOverride[];\n readonly providers?: readonly (readonly [...Parameters<ExtensionAPI['registerProvider']>])[];\n readonly messageRenderers?: readonly (readonly [...Parameters<ExtensionAPI['registerMessageRenderer']>])[];\n readonly entryRenderers?: readonly (readonly [...Parameters<ExtensionAPI['registerEntryRenderer']>])[];\n readonly markdownTransformers?: readonly Parameters<ExtensionAPI['registerMarkdownTransformer']>[0][];\n readonly shortcuts?: readonly (readonly [...Parameters<ExtensionAPI['registerShortcut']>])[];\n readonly flags?: readonly (readonly [...Parameters<ExtensionAPI['registerFlag']>])[];\n}\n\nexport interface PiExtensionDefinition<TOptions = undefined> extends PiPluginContributions<TOptions> {\n readonly name: string;\n}\n\nexport interface DefinedPiExtension<TOptions = undefined> {\n (pi: ExtensionAPI, options?: TOptions): Promise<void>;\n install(\n this: void,\n context: Context,\n pi: ExtensionAPI,\n options?: TOptions,\n runtime?: DoomCordisRuntimeService,\n ): Promise<void>;\n}\n\nexport function definePiExtension<TOptions = undefined>(\n definition: PiExtensionDefinition<TOptions>,\n): DefinedPiExtension<TOptions>;\nexport function definePiExtension<TOptions = undefined>(\n name: string,\n factory: (\n context: PiPluginContext<TOptions>,\n ) => PiPluginContributions<TOptions> | Promise<PiPluginContributions<TOptions>>,\n): DefinedPiExtension<TOptions>;\nexport function definePiExtension<TOptions = undefined>(\n definition: string | PiExtensionDefinition<TOptions>,\n factory?: (\n context: PiPluginContext<TOptions>,\n ) => PiPluginContributions<TOptions> | Promise<PiPluginContributions<TOptions>>,\n): DefinedPiExtension<TOptions> {\n const name = typeof definition === 'string' ? definition : definition.name;\n const install = async (\n context: Context,\n pi: ExtensionAPI,\n options?: TOptions,\n runtime?: DoomCordisRuntimeService,\n ): Promise<void> => {\n const lifecycle = createPluginLifecycle((signal): PiPluginContext<TOptions> => ({\n context,\n pi,\n options,\n runtime,\n signal,\n }));\n context.effect(() => () => lifecycle.dispose(), `${name}/lifecycle`);\n const tool = (item: PiToolContribution, executionSignal?: () => AbortSignal): void => {\n if ('kind' in item && item.kind === 'pi-tool') {\n item.register(pi, executionSignal);\n return;\n }\n if (!('kind' in item)) {\n if (!executionSignal) {\n pi.registerTool(item);\n return;\n }\n pi.registerTool({\n ...item,\n async execute(toolCallId, input, signal, update, context) {\n const current = executionSignal();\n current.throwIfAborted();\n return item.execute(\n toolCallId,\n input,\n signal ? AbortSignal.any([signal, current]) : current,\n update,\n context,\n );\n },\n });\n return;\n }\n pi.registerTool({\n ...item.pi,\n name: item.name,\n label: item.label ?? item.name,\n description: item.description,\n parameters: item.parameters,\n promptSnippet: item.promptSnippet,\n promptGuidelines: item.promptGuidelines ? [...item.promptGuidelines] : undefined,\n executionMode: item.executionMode === 'serial' ? 'sequential' : item.executionMode,\n async execute(toolCallId, input, signal, update, execution) {\n const current = executionSignal?.() ?? lifecycle.context.signal;\n current.throwIfAborted();\n const result = await item.execute(input, {\n toolCallId,\n cwd: execution.cwd,\n signal: signal ? AbortSignal.any([signal, current]) : current,\n update: (result) => update?.({ ...result, details: result.details }),\n notify: (request) => {\n if (execution.hasUI) execution.ui.notify(request.body, request.level ?? 'info');\n },\n });\n return { ...result, details: result.details };\n },\n });\n };\n await lifecycle.mount(\n () => (typeof definition === 'string' ? factory!(lifecycle.context) : definition),\n async (contributions) => {\n for (const service of contributions.services ?? []) {\n const fiber = context.plugin(service);\n lifecycle.own(() => fiber.dispose());\n await fiber;\n }\n const tools = contributions.tools;\n if (tools && 'snapshot' in tools) {\n const registered = new Map<string, { readonly declaration: PiToolContribution; active?: AbortController }>();\n let disposed = false;\n let subscribing = true;\n let reconciling = false;\n let pending = false;\n const deactivate = (): void => {\n for (const entry of registered.values()) {\n entry.active?.abort();\n entry.active = undefined;\n }\n };\n lifecycle.own(() => {\n disposed = true;\n deactivate();\n });\n const reconcile = (): void => {\n pending = true;\n if (disposed || lifecycle.context.signal.aborted) {\n deactivate();\n return;\n }\n if (reconciling || subscribing) return;\n reconciling = true;\n try {\n do {\n pending = false;\n const snapshot = tools.snapshot();\n const names = new Map<string, PiToolContribution>();\n for (const declaration of snapshot) {\n if (names.has(declaration.name)) throw new Error(`Duplicate live tool name: ${declaration.name}`);\n names.set(declaration.name, declaration);\n }\n for (const [name, entry] of registered) {\n if (names.get(name) !== entry.declaration) {\n entry.active?.abort();\n entry.active = undefined;\n }\n }\n for (const declaration of snapshot) {\n let entry = registered.get(declaration.name);\n if (entry && entry.declaration !== declaration) entry = undefined;\n if (entry) {\n entry.active ??= new AbortController();\n continue;\n }\n entry = { declaration, active: new AbortController() };\n const current = entry;\n registered.set(declaration.name, entry);\n try {\n tool(declaration, () => {\n lifecycle.context.signal.throwIfAborted();\n if (!current.active || disposed)\n throw new Error(`Tool ${declaration.name} is unavailable in this plugin generation.`);\n return AbortSignal.any([lifecycle.context.signal, current.active.signal]);\n });\n } catch (error) {\n registered.delete(declaration.name);\n throw error;\n }\n }\n } while (pending && !disposed);\n } catch (error) {\n deactivate();\n throw error;\n } finally {\n reconciling = false;\n }\n };\n const unsubscribe = tools.subscribe(() => {\n try {\n reconcile();\n } catch {\n /* An invalid update leaves registered tools unavailable. */\n }\n });\n lifecycle.own(() => {\n disposed = true;\n deactivate();\n unsubscribe();\n });\n subscribing = false;\n reconcile();\n } else {\n for (const item of tools ?? []) tool(item);\n }\n for (const item of contributions.commands ?? []) {\n if (!('kind' in item)) {\n pi.registerCommand(...item);\n continue;\n }\n pi.registerCommand(item.name, {\n description: item.description,\n async handler(args, execution) {\n lifecycle.context.signal.throwIfAborted();\n await item.execute(args, {\n cwd: execution.cwd,\n signal: lifecycle.context.signal,\n notify: (request) => {\n if (execution.hasUI) execution.ui.notify(request.body, request.level ?? 'info');\n },\n });\n },\n });\n }\n // The public mapped handlers retain each native event's input and return type.\n const on = pi.on.bind(pi) as (event: string, handler: unknown) => void;\n for (const [event, handler] of Object.entries(contributions.events ?? {})) on(event, handler);\n for (const item of contributions.providers ?? []) pi.registerProvider(...item);\n for (const item of contributions.messageRenderers ?? []) pi.registerMessageRenderer(...item);\n for (const item of contributions.entryRenderers ?? []) pi.registerEntryRenderer(...item);\n for (const item of contributions.markdownTransformers ?? []) pi.registerMarkdownTransformer(item);\n for (const item of contributions.shortcuts ?? []) pi.registerShortcut(...item);\n for (const item of contributions.flags ?? []) pi.registerFlag(...item);\n const bind = (service: string, register: (child: Context) => void): void => {\n const fiber = context.inject([service], register);\n lifecycle.own(() => fiber.dispose());\n };\n if (contributions.resources?.length)\n bind(DOOM_HELP_SERVICE, (child) => {\n for (const resource of contributions.resources!) {\n const handle = requireDoomHelpService(child).register(resource);\n child.effect(() => () => handle.dispose());\n }\n });\n if (contributions.toolRestrictions?.length)\n bind(DOOM_TOOL_SURFACE_SERVICE, (child) => {\n for (const restriction of contributions.toolRestrictions!) {\n const handle = requireDoomToolSurface(child).register(restriction);\n child.effect(() => () => handle.dispose());\n const unsubscribe = restriction.subscribe?.(() => handle.update(restriction.restrict));\n if (unsubscribe) child.effect(() => unsubscribe);\n }\n });\n if (contributions.toolOverrides?.length)\n bind(DOOM_TOOL_OVERRIDES_SERVICE, (child) => {\n for (const override of contributions.toolOverrides!) {\n const handle = requireDoomToolOverrides(child).claim(override);\n child.effect(() => () => handle.dispose());\n if (handle.granted) for (const replacement of override.replacements) tool(replacement);\n }\n });\n },\n );\n };\n const activate = async (pi: ExtensionAPI, options?: TOptions): Promise<void> => {\n const connection = await connectDoomCordisHost(pi, name);\n const fiber = connection.root.plugin(async (context) => {\n await install(context, pi, options, connection.runtime);\n });\n let completion: Promise<void> | undefined;\n const dispose = (): Promise<void> =>\n (completion ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })());\n try {\n await fiber;\n pi.on('session_shutdown', dispose);\n } catch (error) {\n try {\n await dispose();\n } catch (cleanupError) {\n throw new AggregateError([error, cleanupError], `${name} startup and cleanup failed.`);\n }\n throw error;\n }\n };\n return Object.assign(activate, { install });\n}\n\n/**\n * What a CLI tool's presentation file supplies.\n *\n * The terminal is a frontend. `renderCall` and `renderResult` draw a tool call\n * into the TUI exactly as a cockpit tool renderer draws it into the browser,\n * so they are authored on the frontend side under a `.cli` platform suffix and\n * never alongside the tool's `execute`.\n */\nexport type PiToolRenderers<TParameters extends TSchema = TSchema, TDetails = unknown, TState = unknown> = Pick<\n ToolDefinition<TParameters, TDetails, TState>,\n 'renderShell' | 'renderCall' | 'renderResult'\n>;\n\n/** A terminal renderer declaration or a factory resolved from its session mount. */\nexport type PiToolRendererFile<\n TContext = unknown,\n TParameters extends TSchema = TSchema,\n TDetails = unknown,\n TState = unknown,\n> =\n | PiToolRenderers<TParameters, TDetails, TState>\n | ((context: TContext) => PiToolRenderers<TParameters, TDetails, TState>);\n/**\n * `(frontend)/tool/<name>.cli.tsx`. The filename names the tool these draw.\n *\n * Generic over the tool's schema, because a renderer reads the call it is\n * drawing: state the parameter type and `renderResult` sees the real arguments\n * instead of `unknown`, which is what the single-file version got for free.\n */\nexport function definePiToolRenderer<TParameters extends TSchema = TSchema, TDetails = unknown, TState = unknown>(\n file: PiToolRenderers<TParameters, TDetails, TState>,\n): PiToolRenderers<TParameters, TDetails, TState> {\n return file;\n}\n\n/** `(frontend)/tool/<name>.cli.tsx` when the renderer needs mount context. */\nexport function defineCliToolRenderer<\n TContext,\n TParameters extends TSchema = TSchema,\n TDetails = unknown,\n TState = unknown,\n>(\n file: PiToolRendererFile<TContext, TParameters, TDetails, TState>,\n): PiToolRendererFile<TContext, TParameters, TDetails, TState> {\n return file;\n}\n\n/**\n * `(frontend)/message/<name>.cli.tsx`. A renderer for one custom message type.\n *\n * The type is stated rather than derived from the filename, because it is a\n * protocol constant shared with whatever emits the message. A path-derived\n * name would silently stop matching the moment either side was renamed.\n */\nexport function defineMessageRenderer(\n messageType: Parameters<ExtensionAPI['registerMessageRenderer']>[0],\n render: Parameters<ExtensionAPI['registerMessageRenderer']>[1],\n): readonly [...Parameters<ExtensionAPI['registerMessageRenderer']>] {\n return [messageType, render];\n}\n\n/**\n * Attaches a presentation file's renderers to the tool its filename names.\n *\n * Three tool shapes reach a contribution array and each carries renderers in a\n * different place, so a generated entry cannot spell this merge itself. A\n * native Pi definition takes them at the top level, the portable shape nests\n * them under `pi`, and `definePiTool` owns its own registration, so that one\n * is reached by narrowing the host it registers against. `register` asks for\n * nothing but `registerTool`, which is what makes the last case a two-line\n * shim rather than a proxy over the whole extension API.\n */\nexport function withPiRenderers(tool: PiToolContribution, renderers: PiToolRenderers): PiToolContribution {\n if (!('kind' in tool)) return { ...tool, ...renderers };\n if (tool.kind !== 'pi-tool') return { ...tool, pi: { ...tool.pi, ...renderers } };\n return {\n ...tool,\n register(pi, executionSignal) {\n tool.register(\n { registerTool: (definition) => pi.registerTool({ ...definition, ...renderers }) },\n executionSignal,\n );\n },\n };\n}\n\n/**\n * Splits routed tool files into the two arrays the Pi host keeps apart.\n *\n * A tool that replaces a name Pi already ships is not a second registration,\n * it is a claim the override service arbitrates, and only the winner\n * registers. The folder convention keeps both in `tool/`, because from the\n * author's side both are \"this package provides grep\"; which one it is lives\n * in the value, through `definePiTool(definition, { overrides: true })`.\n *\n * A generated entry therefore cannot decide this from the path, and calls\n * this instead. Every static claim from one package is one atomic set, which\n * is what the override service expects. A live collection stays intact so its\n * subscription continues to drive registration changes.\n */\nexport function piToolContributions(\n source: string,\n items: readonly PiToolContribution[] | PiToolCollection,\n): Pick<PiPluginContributions, 'tools' | 'toolOverrides'> {\n if (!Array.isArray(items)) return { tools: items as PiToolCollection };\n const added: PiToolContribution[] = [];\n const claimed: PiToolDeclaration[] = [];\n for (const item of items) {\n // Only a native declaration may claim a name: a replacement is handed to\n // Pi's registry directly, which the portable shape is not.\n if ('kind' in item && item.kind === 'pi-tool' && item.overrides === true) claimed.push(item);\n else added.push(item);\n }\n if (claimed.length === 0) return { tools: added };\n return {\n tools: added,\n toolOverrides: [{ source, tools: claimed.map((item) => item.name), replacements: claimed }],\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runtime-pi-extension.mjs","names":[],"sources":["../src/extensions/piExtension.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ToolDefinition } from '@earendil-works/pi-coding-agent';\nimport type { TSchema } from 'typebox';\n\nimport { connectDoomCordisHost, type DoomCordisRuntimeService } from '../pi/cordisHost';\nimport { DOOM_HELP_SERVICE, requireDoomHelpService, type DoomHelpService } from '../schemas/help';\nimport type { PiToolDeclaration } from '../schemas/piTool';\nimport type { DoomPluginTool, DoomPluginCommand } from '../schemas/pluginContributions';\nimport {\n DOOM_TOOL_OVERRIDES_SERVICE,\n requireDoomToolOverrides,\n type DoomToolOverrideClaim,\n} from '../schemas/toolOverrides';\nimport type { DoomToolRestrictionDefinition } from '../schemas/toolSurface';\nimport { DOOM_TOOL_SURFACE_SERVICE, requireDoomToolSurface } from '../schemas/toolSurface';\nimport { createPluginLifecycle, type PluginLifecycleHooks } from '../services/pluginLifecycle';\n\n/** Extract native overloads without copying Pi event payload or result contracts. */\ntype NativeEventOverloads = ExtensionAPI['on'] extends {\n (event: infer E1 extends string, handler: infer H1): void;\n (event: infer E2 extends string, handler: infer H2): void;\n (event: infer E3 extends string, handler: infer H3): void;\n (event: infer E4 extends string, handler: infer H4): void;\n (event: infer E5 extends string, handler: infer H5): void;\n (event: infer E6 extends string, handler: infer H6): void;\n (event: infer E7 extends string, handler: infer H7): void;\n (event: infer E8 extends string, handler: infer H8): void;\n (event: infer E9 extends string, handler: infer H9): void;\n (event: infer E10 extends string, handler: infer H10): void;\n (event: infer E11 extends string, handler: infer H11): void;\n (event: infer E12 extends string, handler: infer H12): void;\n (event: infer E13 extends string, handler: infer H13): void;\n (event: infer E14 extends string, handler: infer H14): void;\n (event: infer E15 extends string, handler: infer H15): void;\n (event: infer E16 extends string, handler: infer H16): void;\n (event: infer E17 extends string, handler: infer H17): void;\n (event: infer E18 extends string, handler: infer H18): void;\n (event: infer E19 extends string, handler: infer H19): void;\n (event: infer E20 extends string, handler: infer H20): void;\n (event: infer E21 extends string, handler: infer H21): void;\n (event: infer E22 extends string, handler: infer H22): void;\n (event: infer E23 extends string, handler: infer H23): void;\n (event: infer E24 extends string, handler: infer H24): void;\n (event: infer E25 extends string, handler: infer H25): void;\n (event: infer E26 extends string, handler: infer H26): void;\n (event: infer E27 extends string, handler: infer H27): void;\n (event: infer E28 extends string, handler: infer H28): void;\n (event: infer E29 extends string, handler: infer H29): void;\n (event: infer E30 extends string, handler: infer H30): void;\n (event: infer E31 extends string, handler: infer H31): void;\n (event: infer E32 extends string, handler: infer H32): void;\n (event: infer E33 extends string, handler: infer H33): void;\n (event: infer E34 extends string, handler: infer H34): void;\n (event: infer E35 extends string, handler: infer H35): void;\n (event: infer E36 extends string, handler: infer H36): void;\n (event: infer E37 extends string, handler: infer H37): void;\n (event: infer E38 extends string, handler: infer H38): void;\n (event: infer E39 extends string, handler: infer H39): void;\n (event: infer E40 extends string, handler: infer H40): void;\n}\n ?\n | [E1, H1]\n | [E2, H2]\n | [E3, H3]\n | [E4, H4]\n | [E5, H5]\n | [E6, H6]\n | [E7, H7]\n | [E8, H8]\n | [E9, H9]\n | [E10, H10]\n | [E11, H11]\n | [E12, H12]\n | [E13, H13]\n | [E14, H14]\n | [E15, H15]\n | [E16, H16]\n | [E17, H17]\n | [E18, H18]\n | [E19, H19]\n | [E20, H20]\n | [E21, H21]\n | [E22, H22]\n | [E23, H23]\n | [E24, H24]\n | [E25, H25]\n | [E26, H26]\n | [E27, H27]\n | [E28, H28]\n | [E29, H29]\n | [E30, H30]\n | [E31, H31]\n | [E32, H32]\n | [E33, H33]\n | [E34, H34]\n | [E35, H35]\n | [E36, H36]\n | [E37, H37]\n | [E38, H38]\n | [E39, H39]\n | [E40, H40]\n : never;\n\nexport type PiEventHandlers = {\n readonly [Pair in NativeEventOverloads as Pair[0]]?: Pair[1];\n};\n\nexport interface PiPluginContext<TOptions = undefined> {\n readonly context: Context;\n readonly pi: ExtensionAPI;\n readonly options: TOptions | undefined;\n readonly runtime: DoomCordisRuntimeService | undefined;\n readonly signal: AbortSignal;\n}\n\nexport type PiToolRestriction = DoomToolRestrictionDefinition;\n\nexport interface PiPluginTool extends DoomPluginTool {\n readonly pi?: Pick<Parameters<ExtensionAPI['registerTool']>[0], 'renderShell' | 'renderCall' | 'renderResult'>;\n}\n\nexport interface PiToolOverride extends DoomToolOverrideClaim {\n /** Contributions registered only while this provider owns the override. */\n readonly tools: readonly string[];\n readonly replacements: readonly (PiToolDeclaration | Parameters<ExtensionAPI['registerTool']>[0])[];\n}\n\nexport type PiToolContribution = PiPluginTool | PiToolDeclaration | Parameters<ExtensionAPI['registerTool']>[0];\n\n/** Snapshots reuse immutable declarations. A registered name cannot adopt a different declaration until remount. */\nexport interface PiToolCollection {\n snapshot(): readonly PiToolContribution[];\n subscribe(listener: () => void): () => void;\n}\n\nexport interface PiPluginContributions<TOptions = undefined> extends PluginLifecycleHooks<PiPluginContext<TOptions>> {\n readonly services?: readonly Parameters<Context['plugin']>[0][];\n readonly tools?: readonly PiToolContribution[] | PiToolCollection;\n readonly commands?: readonly (DoomPluginCommand | readonly [...Parameters<ExtensionAPI['registerCommand']>])[];\n readonly events?: PiEventHandlers;\n readonly resources?: readonly Parameters<DoomHelpService['register']>[0][];\n readonly toolRestrictions?: readonly PiToolRestriction[];\n readonly toolOverrides?: readonly PiToolOverride[];\n readonly providers?: readonly (readonly [...Parameters<ExtensionAPI['registerProvider']>])[];\n readonly messageRenderers?: readonly (readonly [...Parameters<ExtensionAPI['registerMessageRenderer']>])[];\n readonly entryRenderers?: readonly (readonly [...Parameters<ExtensionAPI['registerEntryRenderer']>])[];\n readonly markdownTransformers?: readonly Parameters<ExtensionAPI['registerMarkdownTransformer']>[0][];\n readonly shortcuts?: readonly (readonly [...Parameters<ExtensionAPI['registerShortcut']>])[];\n readonly flags?: readonly (readonly [...Parameters<ExtensionAPI['registerFlag']>])[];\n}\n\nexport interface PiExtensionDefinition<TOptions = undefined> extends PiPluginContributions<TOptions> {\n readonly name: string;\n}\n\nexport interface DefinedPiExtension<TOptions = undefined> {\n (pi: ExtensionAPI, options?: TOptions): Promise<void>;\n install(\n this: void,\n context: Context,\n pi: ExtensionAPI,\n options?: TOptions,\n runtime?: DoomCordisRuntimeService,\n ): Promise<void>;\n}\n\nexport function definePiExtension<TOptions = undefined>(\n definition: PiExtensionDefinition<TOptions>,\n): DefinedPiExtension<TOptions>;\nexport function definePiExtension<TOptions = undefined>(\n name: string,\n factory: (\n context: PiPluginContext<TOptions>,\n ) => PiPluginContributions<TOptions> | Promise<PiPluginContributions<TOptions>>,\n): DefinedPiExtension<TOptions>;\nexport function definePiExtension<TOptions = undefined>(\n definition: string | PiExtensionDefinition<TOptions>,\n factory?: (\n context: PiPluginContext<TOptions>,\n ) => PiPluginContributions<TOptions> | Promise<PiPluginContributions<TOptions>>,\n): DefinedPiExtension<TOptions> {\n const name = typeof definition === 'string' ? definition : definition.name;\n const install = async (\n context: Context,\n pi: ExtensionAPI,\n options?: TOptions,\n runtime?: DoomCordisRuntimeService,\n ): Promise<void> => {\n const lifecycle = createPluginLifecycle((signal): PiPluginContext<TOptions> => ({\n context,\n pi,\n options,\n runtime,\n signal,\n }));\n context.effect(() => () => lifecycle.dispose(), `${name}/lifecycle`);\n const tool = (item: PiToolContribution, executionSignal?: () => AbortSignal): void => {\n if ('kind' in item && item.kind === 'pi-tool') {\n item.register(pi, executionSignal);\n return;\n }\n if (!('kind' in item)) {\n if (!executionSignal) {\n pi.registerTool(item);\n return;\n }\n pi.registerTool({\n ...item,\n async execute(toolCallId, input, signal, update, context) {\n const current = executionSignal();\n current.throwIfAborted();\n return item.execute(\n toolCallId,\n input,\n signal ? AbortSignal.any([signal, current]) : current,\n update,\n context,\n );\n },\n });\n return;\n }\n pi.registerTool({\n ...item.pi,\n name: item.name,\n label: item.label ?? item.name,\n description: item.description,\n parameters: item.parameters,\n promptSnippet: item.promptSnippet,\n promptGuidelines: item.promptGuidelines ? [...item.promptGuidelines] : undefined,\n executionMode: item.executionMode === 'serial' ? 'sequential' : item.executionMode,\n async execute(toolCallId, input, signal, update, execution) {\n const current = executionSignal?.() ?? lifecycle.context.signal;\n current.throwIfAborted();\n const result = await item.execute(input, {\n toolCallId,\n cwd: execution.cwd,\n signal: signal ? AbortSignal.any([signal, current]) : current,\n update: (result) => update?.({ ...result, details: result.details }),\n notify: (request) => {\n if (execution.hasUI) execution.ui.notify(request.body, request.level ?? 'info');\n },\n });\n return { ...result, details: result.details };\n },\n });\n };\n await lifecycle.mount(\n () => (typeof definition === 'string' ? factory!(lifecycle.context) : definition),\n async (contributions) => {\n for (const service of contributions.services ?? []) {\n const fiber = context.plugin(service);\n lifecycle.own(() => fiber.dispose());\n await fiber;\n }\n const tools = contributions.tools;\n if (tools && 'snapshot' in tools) {\n const registered = new Map<string, { readonly declaration: PiToolContribution; active?: AbortController }>();\n let disposed = false;\n let subscribing = true;\n let reconciling = false;\n let pending = false;\n const deactivate = (): void => {\n for (const entry of registered.values()) {\n entry.active?.abort();\n entry.active = undefined;\n }\n };\n lifecycle.own(() => {\n disposed = true;\n deactivate();\n });\n const reconcile = (): void => {\n pending = true;\n if (disposed || lifecycle.context.signal.aborted) {\n deactivate();\n return;\n }\n if (reconciling || subscribing) return;\n reconciling = true;\n try {\n do {\n pending = false;\n const snapshot = tools.snapshot();\n const names = new Map<string, PiToolContribution>();\n for (const declaration of snapshot) {\n if (names.has(declaration.name)) throw new Error(`Duplicate live tool name: ${declaration.name}`);\n names.set(declaration.name, declaration);\n }\n for (const [name, entry] of registered) {\n if (names.get(name) !== entry.declaration) {\n entry.active?.abort();\n entry.active = undefined;\n }\n }\n for (const declaration of snapshot) {\n let entry = registered.get(declaration.name);\n if (entry && entry.declaration !== declaration) entry = undefined;\n if (entry) {\n entry.active ??= new AbortController();\n continue;\n }\n entry = { declaration, active: new AbortController() };\n const current = entry;\n registered.set(declaration.name, entry);\n try {\n tool(declaration, () => {\n lifecycle.context.signal.throwIfAborted();\n if (!current.active || disposed)\n throw new Error(`Tool ${declaration.name} is unavailable in this plugin generation.`);\n return AbortSignal.any([lifecycle.context.signal, current.active.signal]);\n });\n } catch (error) {\n registered.delete(declaration.name);\n throw error;\n }\n }\n } while (pending && !disposed);\n } catch (error) {\n deactivate();\n throw error;\n } finally {\n reconciling = false;\n }\n };\n const unsubscribe = tools.subscribe(() => {\n try {\n reconcile();\n } catch {\n /* An invalid update leaves registered tools unavailable. */\n }\n });\n lifecycle.own(() => {\n disposed = true;\n deactivate();\n unsubscribe();\n });\n subscribing = false;\n reconcile();\n } else {\n for (const item of tools ?? []) tool(item);\n }\n for (const item of contributions.commands ?? []) {\n if (!('kind' in item)) {\n pi.registerCommand(...item);\n continue;\n }\n pi.registerCommand(item.name, {\n description: item.description,\n async handler(args, execution) {\n lifecycle.context.signal.throwIfAborted();\n await item.execute(args, {\n cwd: execution.cwd,\n signal: lifecycle.context.signal,\n notify: (request) => {\n if (execution.hasUI) execution.ui.notify(request.body, request.level ?? 'info');\n },\n });\n },\n });\n }\n // The public mapped handlers retain each native event's input and return type.\n const on = pi.on.bind(pi) as (event: string, handler: unknown) => ReturnType<ExtensionAPI['on']>;\n for (const [event, handler] of Object.entries(contributions.events ?? {})) lifecycle.own(on(event, handler));\n for (const item of contributions.providers ?? []) pi.registerProvider(...item);\n for (const item of contributions.messageRenderers ?? []) pi.registerMessageRenderer(...item);\n for (const item of contributions.entryRenderers ?? []) pi.registerEntryRenderer(...item);\n for (const item of contributions.markdownTransformers ?? []) pi.registerMarkdownTransformer(item);\n for (const item of contributions.shortcuts ?? []) pi.registerShortcut(...item);\n for (const item of contributions.flags ?? []) pi.registerFlag(...item);\n const bind = (service: string, register: (child: Context) => void): void => {\n const fiber = context.inject([service], register);\n lifecycle.own(() => fiber.dispose());\n };\n if (contributions.resources?.length)\n bind(DOOM_HELP_SERVICE, (child) => {\n for (const resource of contributions.resources!) {\n const handle = requireDoomHelpService(child).register(resource);\n child.effect(() => () => handle.dispose());\n }\n });\n if (contributions.toolRestrictions?.length)\n bind(DOOM_TOOL_SURFACE_SERVICE, (child) => {\n for (const restriction of contributions.toolRestrictions!) {\n const handle = requireDoomToolSurface(child).register(restriction);\n child.effect(() => () => handle.dispose());\n const unsubscribe = restriction.subscribe?.(() => handle.update(restriction.restrict));\n if (unsubscribe) child.effect(() => unsubscribe);\n }\n });\n if (contributions.toolOverrides?.length)\n bind(DOOM_TOOL_OVERRIDES_SERVICE, (child) => {\n for (const override of contributions.toolOverrides!) {\n const handle = requireDoomToolOverrides(child).claim(override);\n child.effect(() => () => handle.dispose());\n if (handle.granted) for (const replacement of override.replacements) tool(replacement);\n }\n });\n },\n );\n };\n const activate = async (pi: ExtensionAPI, options?: TOptions): Promise<void> => {\n const connection = await connectDoomCordisHost(pi, name);\n const fiber = connection.root.plugin(async (context) => {\n await install(context, pi, options, connection.runtime);\n });\n let completion: Promise<void> | undefined;\n let unsubscribeShutdown: (() => void) | undefined;\n const dispose = (): Promise<void> =>\n (completion ??= (async () => {\n try {\n unsubscribeShutdown?.();\n unsubscribeShutdown = undefined;\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })());\n try {\n await fiber;\n const shutdownRegistration = pi.on('session_shutdown', dispose) as unknown;\n if (typeof shutdownRegistration === 'function') unsubscribeShutdown = shutdownRegistration as () => void;\n } catch (error) {\n try {\n await dispose();\n } catch (cleanupError) {\n throw new AggregateError([error, cleanupError], `${name} startup and cleanup failed.`);\n }\n throw error;\n }\n };\n return Object.assign(activate, { install });\n}\n\n/**\n * What a CLI tool's presentation file supplies.\n *\n * The terminal is a frontend. `renderCall` and `renderResult` draw a tool call\n * into the TUI exactly as a cockpit tool renderer draws it into the browser,\n * so they are authored on the frontend side under a `.cli` platform suffix and\n * never alongside the tool's `execute`.\n */\nexport type PiToolRenderers<TParameters extends TSchema = TSchema, TDetails = unknown, TState = unknown> = Pick<\n ToolDefinition<TParameters, TDetails, TState>,\n 'renderShell' | 'renderCall' | 'renderResult'\n>;\n\n/** A terminal renderer declaration or a factory resolved from its session mount. */\nexport type PiToolRendererFile<\n TContext = unknown,\n TParameters extends TSchema = TSchema,\n TDetails = unknown,\n TState = unknown,\n> =\n | PiToolRenderers<TParameters, TDetails, TState>\n | ((context: TContext) => PiToolRenderers<TParameters, TDetails, TState>);\n/**\n * `(frontend)/tool/<name>.cli.tsx`. The filename names the tool these draw.\n *\n * Generic over the tool's schema, because a renderer reads the call it is\n * drawing: state the parameter type and `renderResult` sees the real arguments\n * instead of `unknown`, which is what the single-file version got for free.\n */\nexport function definePiToolRenderer<TParameters extends TSchema = TSchema, TDetails = unknown, TState = unknown>(\n file: PiToolRenderers<TParameters, TDetails, TState>,\n): PiToolRenderers<TParameters, TDetails, TState> {\n return file;\n}\n\n/** `(frontend)/tool/<name>.cli.tsx` when the renderer needs mount context. */\nexport function defineCliToolRenderer<\n TContext,\n TParameters extends TSchema = TSchema,\n TDetails = unknown,\n TState = unknown,\n>(\n file: PiToolRendererFile<TContext, TParameters, TDetails, TState>,\n): PiToolRendererFile<TContext, TParameters, TDetails, TState> {\n return file;\n}\n\n/**\n * `(frontend)/message/<name>.cli.tsx`. A renderer for one custom message type.\n *\n * The type is stated rather than derived from the filename, because it is a\n * protocol constant shared with whatever emits the message. A path-derived\n * name would silently stop matching the moment either side was renamed.\n */\nexport function defineMessageRenderer(\n messageType: Parameters<ExtensionAPI['registerMessageRenderer']>[0],\n render: Parameters<ExtensionAPI['registerMessageRenderer']>[1],\n): readonly [...Parameters<ExtensionAPI['registerMessageRenderer']>] {\n return [messageType, render];\n}\n\n/**\n * Attaches a presentation file's renderers to the tool its filename names.\n *\n * Three tool shapes reach a contribution array and each carries renderers in a\n * different place, so a generated entry cannot spell this merge itself. A\n * native Pi definition takes them at the top level, the portable shape nests\n * them under `pi`, and `definePiTool` owns its own registration, so that one\n * is reached by narrowing the host it registers against. `register` asks for\n * nothing but `registerTool`, which is what makes the last case a two-line\n * shim rather than a proxy over the whole extension API.\n */\nexport function withPiRenderers(tool: PiToolContribution, renderers: PiToolRenderers): PiToolContribution {\n if (!('kind' in tool)) return { ...tool, ...renderers };\n if (tool.kind !== 'pi-tool') return { ...tool, pi: { ...tool.pi, ...renderers } };\n return {\n ...tool,\n register(pi, executionSignal) {\n tool.register(\n { registerTool: (definition) => pi.registerTool({ ...definition, ...renderers }) },\n executionSignal,\n );\n },\n };\n}\n\n/**\n * Splits routed tool files into the two arrays the Pi host keeps apart.\n *\n * A tool that replaces a name Pi already ships is not a second registration,\n * it is a claim the override service arbitrates, and only the winner\n * registers. The folder convention keeps both in `tool/`, because from the\n * author's side both are \"this package provides grep\"; which one it is lives\n * in the value, through `definePiTool(definition, { overrides: true })`.\n *\n * A generated entry therefore cannot decide this from the path, and calls\n * this instead. Every static claim from one package is one atomic set, which\n * is what the override service expects. A live collection stays intact so its\n * subscription continues to drive registration changes.\n */\nexport function piToolContributions(\n source: string,\n items: readonly PiToolContribution[] | PiToolCollection,\n): Pick<PiPluginContributions, 'tools' | 'toolOverrides'> {\n if (!Array.isArray(items)) return { tools: items as PiToolCollection };\n const added: PiToolContribution[] = [];\n const claimed: PiToolDeclaration[] = [];\n for (const item of items) {\n // Only a native declaration may claim a name: a replacement is handed to\n // Pi's registry directly, which the portable shape is not.\n if ('kind' in item && item.kind === 'pi-tool' && item.overrides === true) claimed.push(item);\n else added.push(item);\n }\n if (claimed.length === 0) return { tools: added };\n return {\n tools: added,\n toolOverrides: [{ source, tools: claimed.map((item) => item.name), replacements: claimed }],\n };\n}\n"],"mappings":"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"}
@@ -14,7 +14,7 @@ const builtinApiContract = require_apiContracts.defineApiContract({
14
14
  },
15
15
  protocols: {
16
16
  pi: String(_earendil_works_pi_protocol.PROTOCOL_VERSION),
17
- chord: "0.85.1",
17
+ chord: "0.86.0",
18
18
  sealed: "1"
19
19
  },
20
20
  http: [
@@ -1 +1 @@
1
- {"version":3,"file":"builtinApiContracts.cjs","names":["defineApiContract","SealedEnvelopeSchema","SealedHttpRequestSchema","SealedHttpResponseSchema","PROTOCOL_VERSION","headlessHttpContracts","machineHttpContracts","sessionHttpContracts","remoteHttpContracts","bundleHttpContracts","sessionSocketContracts","transportSocketContracts"],"sources":["../../src/schemas/builtinApiContracts.ts"],"sourcesContent":["import { PROTOCOL_VERSION } from '@earendil-works/pi-protocol';\n\nimport { defineApiContract } from './apiContracts';\nimport { headlessHttpContracts, machineHttpContracts, sessionHttpContracts } from './httpApiContracts';\nimport {\n SealedEnvelopeSchema,\n SealedHttpRequestSchema,\n SealedHttpResponseSchema,\n remoteHttpContracts,\n bundleHttpContracts,\n} from './remoteApiContracts';\nimport { sessionSocketContracts } from './sessionApiContracts';\nimport { transportSocketContracts } from './transportApiContracts';\n\nexport const builtinApiContract = defineApiContract({\n version: 1,\n schemas: {\n SealedEnvelope: SealedEnvelopeSchema,\n SealedHttpRequest: SealedHttpRequestSchema,\n SealedHttpResponse: SealedHttpResponseSchema,\n },\n protocols: { pi: String(PROTOCOL_VERSION), chord: '0.85.1', sealed: '1' },\n http: [\n ...headlessHttpContracts,\n ...machineHttpContracts,\n ...sessionHttpContracts,\n ...remoteHttpContracts,\n ...bundleHttpContracts,\n ],\n sockets: [...sessionSocketContracts, ...transportSocketContracts],\n dynamic: [\n 'WebAuthn options and credential responses follow the installed WebAuthn implementation.',\n 'Tool inputs/results and journal entries contain runtime-defined JSON.',\n 'Selected channel and presentation payload schemas are contributed by their owning package.',\n ],\n});\n"],"mappings":";;;;;;;AAcA,MAAa,qBAAqBA,qBAAAA,kBAAkB;CAClD,SAAS;CACT,SAAS;EACP,gBAAgBC,2BAAAA;EAChB,mBAAmBC,2BAAAA;EACnB,oBAAoBC,2BAAAA;CACtB;CACA,WAAW;EAAE,IAAI,OAAOC,4BAAAA,gBAAgB;EAAG,OAAO;EAAU,QAAQ;CAAI;CACxE,MAAM;EACJ,GAAGC,yBAAAA;EACH,GAAGC,yBAAAA;EACH,GAAGC,yBAAAA;EACH,GAAGC,2BAAAA;EACH,GAAGC,2BAAAA;CACL;CACA,SAAS,CAAC,GAAGC,4BAAAA,wBAAwB,GAAGC,8BAAAA,wBAAwB;CAChE,SAAS;EACP;EACA;EACA;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"builtinApiContracts.cjs","names":["defineApiContract","SealedEnvelopeSchema","SealedHttpRequestSchema","SealedHttpResponseSchema","PROTOCOL_VERSION","headlessHttpContracts","machineHttpContracts","sessionHttpContracts","remoteHttpContracts","bundleHttpContracts","sessionSocketContracts","transportSocketContracts"],"sources":["../../src/schemas/builtinApiContracts.ts"],"sourcesContent":["import { PROTOCOL_VERSION } from '@earendil-works/pi-protocol';\n\nimport { defineApiContract } from './apiContracts';\nimport { headlessHttpContracts, machineHttpContracts, sessionHttpContracts } from './httpApiContracts';\nimport {\n SealedEnvelopeSchema,\n SealedHttpRequestSchema,\n SealedHttpResponseSchema,\n remoteHttpContracts,\n bundleHttpContracts,\n} from './remoteApiContracts';\nimport { sessionSocketContracts } from './sessionApiContracts';\nimport { transportSocketContracts } from './transportApiContracts';\n\nexport const builtinApiContract = defineApiContract({\n version: 1,\n schemas: {\n SealedEnvelope: SealedEnvelopeSchema,\n SealedHttpRequest: SealedHttpRequestSchema,\n SealedHttpResponse: SealedHttpResponseSchema,\n },\n protocols: { pi: String(PROTOCOL_VERSION), chord: '0.86.0', sealed: '1' },\n http: [\n ...headlessHttpContracts,\n ...machineHttpContracts,\n ...sessionHttpContracts,\n ...remoteHttpContracts,\n ...bundleHttpContracts,\n ],\n sockets: [...sessionSocketContracts, ...transportSocketContracts],\n dynamic: [\n 'WebAuthn options and credential responses follow the installed WebAuthn implementation.',\n 'Tool inputs/results and journal entries contain runtime-defined JSON.',\n 'Selected channel and presentation payload schemas are contributed by their owning package.',\n ],\n});\n"],"mappings":";;;;;;;AAcA,MAAa,qBAAqBA,qBAAAA,kBAAkB;CAClD,SAAS;CACT,SAAS;EACP,gBAAgBC,2BAAAA;EAChB,mBAAmBC,2BAAAA;EACnB,oBAAoBC,2BAAAA;CACtB;CACA,WAAW;EAAE,IAAI,OAAOC,4BAAAA,gBAAgB;EAAG,OAAO;EAAU,QAAQ;CAAI;CACxE,MAAM;EACJ,GAAGC,yBAAAA;EACH,GAAGC,yBAAAA;EACH,GAAGC,yBAAAA;EACH,GAAGC,2BAAAA;EACH,GAAGC,2BAAAA;CACL;CACA,SAAS,CAAC,GAAGC,4BAAAA,wBAAwB,GAAGC,8BAAAA,wBAAwB;CAChE,SAAS;EACP;EACA;EACA;CACF;AACF,CAAC"}
@@ -14,7 +14,7 @@ const builtinApiContract = defineApiContract({
14
14
  },
15
15
  protocols: {
16
16
  pi: String(PROTOCOL_VERSION),
17
- chord: "0.85.1",
17
+ chord: "0.86.0",
18
18
  sealed: "1"
19
19
  },
20
20
  http: [
@@ -1 +1 @@
1
- {"version":3,"file":"builtinApiContracts.mjs","names":[],"sources":["../../src/schemas/builtinApiContracts.ts"],"sourcesContent":["import { PROTOCOL_VERSION } from '@earendil-works/pi-protocol';\n\nimport { defineApiContract } from './apiContracts';\nimport { headlessHttpContracts, machineHttpContracts, sessionHttpContracts } from './httpApiContracts';\nimport {\n SealedEnvelopeSchema,\n SealedHttpRequestSchema,\n SealedHttpResponseSchema,\n remoteHttpContracts,\n bundleHttpContracts,\n} from './remoteApiContracts';\nimport { sessionSocketContracts } from './sessionApiContracts';\nimport { transportSocketContracts } from './transportApiContracts';\n\nexport const builtinApiContract = defineApiContract({\n version: 1,\n schemas: {\n SealedEnvelope: SealedEnvelopeSchema,\n SealedHttpRequest: SealedHttpRequestSchema,\n SealedHttpResponse: SealedHttpResponseSchema,\n },\n protocols: { pi: String(PROTOCOL_VERSION), chord: '0.85.1', sealed: '1' },\n http: [\n ...headlessHttpContracts,\n ...machineHttpContracts,\n ...sessionHttpContracts,\n ...remoteHttpContracts,\n ...bundleHttpContracts,\n ],\n sockets: [...sessionSocketContracts, ...transportSocketContracts],\n dynamic: [\n 'WebAuthn options and credential responses follow the installed WebAuthn implementation.',\n 'Tool inputs/results and journal entries contain runtime-defined JSON.',\n 'Selected channel and presentation payload schemas are contributed by their owning package.',\n ],\n});\n"],"mappings":";;;;;;;AAcA,MAAa,qBAAqB,kBAAkB;CAClD,SAAS;CACT,SAAS;EACP,gBAAgB;EAChB,mBAAmB;EACnB,oBAAoB;CACtB;CACA,WAAW;EAAE,IAAI,OAAO,gBAAgB;EAAG,OAAO;EAAU,QAAQ;CAAI;CACxE,MAAM;EACJ,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;CACL;CACA,SAAS,CAAC,GAAG,wBAAwB,GAAG,wBAAwB;CAChE,SAAS;EACP;EACA;EACA;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"builtinApiContracts.mjs","names":[],"sources":["../../src/schemas/builtinApiContracts.ts"],"sourcesContent":["import { PROTOCOL_VERSION } from '@earendil-works/pi-protocol';\n\nimport { defineApiContract } from './apiContracts';\nimport { headlessHttpContracts, machineHttpContracts, sessionHttpContracts } from './httpApiContracts';\nimport {\n SealedEnvelopeSchema,\n SealedHttpRequestSchema,\n SealedHttpResponseSchema,\n remoteHttpContracts,\n bundleHttpContracts,\n} from './remoteApiContracts';\nimport { sessionSocketContracts } from './sessionApiContracts';\nimport { transportSocketContracts } from './transportApiContracts';\n\nexport const builtinApiContract = defineApiContract({\n version: 1,\n schemas: {\n SealedEnvelope: SealedEnvelopeSchema,\n SealedHttpRequest: SealedHttpRequestSchema,\n SealedHttpResponse: SealedHttpResponseSchema,\n },\n protocols: { pi: String(PROTOCOL_VERSION), chord: '0.86.0', sealed: '1' },\n http: [\n ...headlessHttpContracts,\n ...machineHttpContracts,\n ...sessionHttpContracts,\n ...remoteHttpContracts,\n ...bundleHttpContracts,\n ],\n sockets: [...sessionSocketContracts, ...transportSocketContracts],\n dynamic: [\n 'WebAuthn options and credential responses follow the installed WebAuthn implementation.',\n 'Tool inputs/results and journal entries contain runtime-defined JSON.',\n 'Selected channel and presentation payload schemas are contributed by their owning package.',\n ],\n});\n"],"mappings":";;;;;;;AAcA,MAAa,qBAAqB,kBAAkB;CAClD,SAAS;CACT,SAAS;EACP,gBAAgB;EAChB,mBAAmB;EACnB,oBAAoB;CACtB;CACA,WAAW;EAAE,IAAI,OAAO,gBAAgB;EAAG,OAAO;EAAU,QAAQ;CAAI;CACxE,MAAM;EACJ,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;CACL;CACA,SAAS,CAAC,GAAG,wBAAwB,GAAG,wBAAwB;CAChE,SAAS;EACP;EACA;EACA;CACF;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"headless.cjs","names":[],"sources":["../../src/schemas/headless.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { Static, TSchema } from 'typebox';\n\nimport type { DoomNotificationRequest } from './notification';\nimport type { DoomServerBundleEntry } from './serverBundle';\n\n/** Session-only contributions. This is not a Pi ExtensionAPI or a TUI adapter. */\nexport const DOOM_HEADLESS_HOST_SERVICE = 'doom/headless-host';\nexport const DOOM_HEADLESS_OWNER = Symbol.for('doom/headless-owner');\n\nexport interface DoomHeadlessSelection {\n readonly majorMode: string;\n readonly activeLayers: readonly string[];\n readonly domains: readonly string[];\n readonly profile?: string;\n /** Feature-owned activation sets, keyed by the contributing capability. */\n readonly state?: Readonly<Record<string, readonly string[]>>;\n}\n\n/** One package-owned selection transition. Server facets change exactly one axis per operation. */\nexport type DoomHeadlessSelectionChange =\n | { readonly axis: 'majorMode'; readonly majorMode: string }\n | { readonly axis: 'domains'; readonly domains: readonly string[] }\n | { readonly axis: 'profile'; readonly profile?: string }\n | { readonly axis: 'state'; readonly key: string; readonly values: readonly string[] };\n\nexport function readDoomHeadlessOwner(context: Context): DoomServerBundleEntry | undefined {\n return (context as Context & { [DOOM_HEADLESS_OWNER]?: DoomServerBundleEntry })[DOOM_HEADLESS_OWNER];\n}\n\nexport interface DoomHeadlessRegistration {\n dispose(): void;\n}\n\nexport type DoomHeadlessContent = { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string };\n\nexport interface DoomHeadlessToolResult {\n content: DoomHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n}\n\nexport interface DoomHeadlessClientRequest {\n kind: 'confirm' | 'select' | 'input';\n title: string;\n message?: string;\n options?: readonly { label: string; description?: string; value: string }[];\n initialValue?: string;\n multiline?: boolean;\n}\n\nexport interface DoomHeadlessClient {\n /**\n * Operator-facing surface only: the body is flattened to one line and capped at 4096 characters\n * by schemas/notification.ts:73-83, and the model never sees it. Use session.admitPrompt to\n * reach the model.\n */\n notify(request: DoomNotificationRequest): void | Promise<void>;\n request(request: DoomHeadlessClientRequest, signal?: AbortSignal): Promise<unknown>;\n setStatus(source: string, text: string | undefined): void;\n appendComposerText?(text: string): void;\n}\n\nexport interface DoomHeadlessModelSettings {\n model?: { provider: string; id: string };\n thinkingLevel: ThinkingLevel;\n}\n\nexport interface DoomHeadlessSession {\n readModelSettings?(): Promise<DoomHeadlessModelSettings>;\n setModelSettings?(settings: Partial<DoomHeadlessModelSettings>): Promise<void>;\n /** Query durable history on demand; hosts do not retain the entire transcript. */\n entries(query?: {\n type?: 'custom' | 'message';\n customType?: string;\n limit?: number;\n }): readonly Record<string, unknown>[] | Promise<readonly Record<string, unknown>[]>;\n appendCustomEntry(type: string, data: unknown): Promise<void>;\n /**\n * Delivers and awaits the turn. 'steer' and 'followUp' only enqueue into a running turn; they do\n * NOT wake an idle agent.\n */\n prompt(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n /**\n * Resolves when accepted by the session, before the agent turn settles. Here 'steer' describes\n * how to deliver IF a turn is already running; an idle agent is woken either way, matching Pi's\n * ExtensionAPI.sendMessage({ triggerTurn: true, deliverAs: 'steer' }).\n */\n admitPrompt?(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n abort(): Promise<void>;\n compact(instructions?: string): Promise<void>;\n activity(): Promise<{ hasPendingMessages: boolean; isIdle: boolean }>;\n /** Capture the current persisted branch for an in-process child fork. */\n forkSource?(): Promise<{ kind: 'v4-fork'; sessionFile: string; branch: string; entryId?: string }>;\n}\n\nexport interface DoomHeadlessExecutionContext {\n readonly cwd: string;\n /** Admitted configuration root, distinct from the tool execution directory. */\n readonly repoRoot: string;\n readonly sessionId: string;\n /** Immutable session configuration. Facets must not read or mutate process.env. */\n readonly environment: Readonly<Record<string, string | undefined>>;\n readonly client: DoomHeadlessClient;\n readonly session: DoomHeadlessSession;\n readonly model?: { provider: string; id: string };\n readonly textCompletion?: {\n available(reference: string): boolean;\n complete(\n reference: string,\n request: {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n },\n ): Promise<string>;\n };\n readonly selection: DoomHeadlessSelection;\n shutdown(): void;\n}\n\n/** Conditions narrow an eligible owner's contributions; they can never restore a disabled owner. */\nexport interface DoomHeadlessCondition {\n state?: Readonly<Record<string, string>>;\n attribution?: { kind: 'minor' | 'domain'; mode: string; label?: string };\n domain?: string;\n}\n\nexport interface DoomHeadlessToolRestriction {\n when?: DoomHeadlessCondition;\n allowedTools: readonly string[];\n}\n\nexport interface DoomHeadlessTool<TParameters extends TSchema = TSchema> {\n when?: DoomHeadlessCondition;\n name: string;\n label?: string;\n description: string;\n parameters: TParameters;\n promptSnippet?: string;\n promptGuidelines?: readonly string[];\n executionMode?: 'parallel' | 'serial';\n execute(\n toolCallId: string,\n parameters: Static<TParameters>,\n signal: AbortSignal | undefined,\n onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n context: DoomHeadlessExecutionContext,\n ): Promise<DoomHeadlessToolResult>;\n}\n\n/**\n * One package contribution to the session's resource surface.\n *\n * `kind` decides what the model is billed for, and the difference is large:\n * - `context` is EAGER. `read()` runs on every prompt build and the text is pasted\n * into the system prompt verbatim. Reserve it for small live session state that\n * exists nowhere on disk. Never use it for a file the package ships: a README\n * registered this way costs its full length on every single turn.\n * - `skill` is LAZY. Only `name`, `description` and `path` reach the prompt; the\n * agent reads the file itself when the description matches the task.\n * - `prompt` is inert until the user invokes it by name.\n *\n * The rule in one line: if it is a file the package ships, it is a `skill`.\n */\nexport interface DoomHeadlessResource {\n when?: DoomHeadlessCondition;\n name: string;\n /** Model-visible selection signal. A `skill` without one tells the agent nothing. */\n description?: string;\n /** Absolute path of the file `read()` returns, so an advertised skill is one the agent can open. */\n path?: string;\n kind: 'prompt' | 'skill' | 'context';\n read(context: DoomHeadlessExecutionContext): string | Promise<string>;\n}\n\nexport interface DoomHeadlessCommand {\n when?: DoomHeadlessCondition;\n name: string;\n description: string;\n execute(args: string, context: DoomHeadlessExecutionContext): void | Promise<void>;\n}\n\nexport type DoomHeadlessEventName =\n | 'session_start'\n | 'session_shutdown'\n | 'session_tree'\n | 'before_agent_start'\n | 'agent_start'\n | 'agent_settled'\n | 'turn_start'\n | 'turn_end'\n | 'context'\n | 'before_provider_request'\n | 'message_start'\n | 'message_update'\n | 'message_end'\n | 'tool_call'\n | 'tool_execution_start'\n | 'tool_execution_update'\n | 'tool_execution_end'\n | 'tool_result'\n | 'model_select'\n | 'session_before_compact'\n | 'session_compact';\n\nexport type DoomHeadlessHookEvent<E extends DoomHeadlessEventName> = E extends 'before_provider_request'\n ? { lane: string; runId: string; model: { provider: string; id: string }; payload: unknown }\n : E extends 'session_before_compact'\n ? { reason: string; preparation: unknown; instructions?: string; customInstructions?: string }\n : E extends 'model_select'\n ? { model: { provider: string; id: string } }\n : Readonly<Record<string, unknown>>;\n\nexport interface DoomHeadlessCompactionResult {\n summary: string;\n tokensBefore: number;\n retainedTail: Record<string, unknown>[];\n usage?: Record<string, unknown>;\n details?: unknown;\n}\n\nexport type DoomHeadlessHookResult<E extends DoomHeadlessEventName> = E extends 'before_provider_request'\n ? { payload: unknown } | void\n : E extends 'session_before_compact'\n ? { cancel?: boolean; decline?: boolean; compaction?: DoomHeadlessCompactionResult } | void\n : E extends 'context'\n ? { messages?: unknown[]; systemPrompt?: string } | void\n : E extends 'before_agent_start'\n ? { systemPrompt?: string } | void\n : E extends 'tool_call'\n ? { args?: Record<string, unknown>; block?: { reason: string; terminate?: boolean } } | void\n : E extends 'tool_result'\n ? {\n content?: DoomHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n usage?: Record<string, unknown>;\n terminate?: boolean;\n } | void\n : unknown;\n\nexport type DoomHeadlessHook<E extends DoomHeadlessEventName = DoomHeadlessEventName> = E extends DoomHeadlessEventName\n ? {\n when?: DoomHeadlessCondition;\n event: E;\n handle(\n event: Readonly<DoomHeadlessHookEvent<E>>,\n context: DoomHeadlessExecutionContext,\n ): DoomHeadlessHookResult<E> | Promise<DoomHeadlessHookResult<E>>;\n }\n : never;\n\n/** A retained facet can register activity without starting disabled watchers or services. */\nexport interface DoomHeadlessActivity {\n when?: DoomHeadlessCondition;\n name: string;\n start(context: DoomHeadlessExecutionContext): (() => void | Promise<void>) | Promise<() => void | Promise<void>>;\n}\n\n/** Registrations inherit package ownership from the caller's Cordis facet. */\nexport interface DoomHeadlessHostService {\n readonly context: DoomHeadlessExecutionContext;\n changeSelection(change: DoomHeadlessSelectionChange): Promise<void>;\n assertActive(source?: string): void;\n subscribeSelection(listener: (selection: DoomHeadlessSelection) => void | Promise<void>): () => void;\n registerToolRestriction(restriction: DoomHeadlessToolRestriction): DoomHeadlessRegistration;\n registerTool<TParameters extends TSchema>(tool: DoomHeadlessTool<TParameters>): DoomHeadlessRegistration;\n registerResource(resource: DoomHeadlessResource): DoomHeadlessRegistration;\n registerCommand(command: DoomHeadlessCommand): DoomHeadlessRegistration;\n registerHook(hook: DoomHeadlessHook): DoomHeadlessRegistration;\n registerActivity(activity: DoomHeadlessActivity): DoomHeadlessRegistration;\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'doom/headless-host': DoomHeadlessHostService;\n }\n}\n\nexport function readDoomHeadlessHost(context: Context): DoomHeadlessHostService | undefined {\n return context.get(DOOM_HEADLESS_HOST_SERVICE) as DoomHeadlessHostService | undefined;\n}\n\nexport function requireDoomHeadlessHost(context: Context): DoomHeadlessHostService {\n const host = readDoomHeadlessHost(context);\n if (!host) throw new Error('This contribution requires the Doom headless session host.');\n return host;\n}\n"],"mappings":";;AAQA,MAAa,6BAA6B;AAC1C,MAAa,sBAAsB,OAAO,IAAI,qBAAqB;AAkBnE,SAAgB,sBAAsB,SAAqD;CACzF,OAAQ,QAAwE;AAClF;AA8PA,SAAgB,qBAAqB,SAAuD;CAC1F,OAAO,QAAQ,IAAI,0BAA0B;AAC/C;AAEA,SAAgB,wBAAwB,SAA2C;CACjF,MAAM,OAAO,qBAAqB,OAAO;CACzC,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,4DAA4D;CACvF,OAAO;AACT"}
1
+ {"version":3,"file":"headless.cjs","names":[],"sources":["../../src/schemas/headless.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { Static, TSchema } from 'typebox';\n\nimport type { DoomNotificationRequest } from './notification';\nimport type { DoomServerBundleEntry } from './serverBundle';\n\n/** Session-only contributions. This is not a Pi ExtensionAPI or a TUI adapter. */\nexport const DOOM_HEADLESS_HOST_SERVICE = 'doom/headless-host';\nexport const DOOM_HEADLESS_OWNER = Symbol.for('doom/headless-owner');\n\nexport interface DoomHeadlessSelection {\n readonly majorMode: string;\n readonly activeLayers: readonly string[];\n readonly domains: readonly string[];\n readonly profile?: string;\n /** Feature-owned activation sets, keyed by the contributing capability. */\n readonly state?: Readonly<Record<string, readonly string[]>>;\n}\n\n/** One package-owned selection transition. Server facets change exactly one axis per operation. */\nexport type DoomHeadlessSelectionChange =\n | { readonly axis: 'majorMode'; readonly majorMode: string }\n | { readonly axis: 'domains'; readonly domains: readonly string[] }\n | { readonly axis: 'profile'; readonly profile?: string }\n | { readonly axis: 'state'; readonly key: string; readonly values: readonly string[] };\n\nexport function readDoomHeadlessOwner(context: Context): DoomServerBundleEntry | undefined {\n return (context as Context & { [DOOM_HEADLESS_OWNER]?: DoomServerBundleEntry })[DOOM_HEADLESS_OWNER];\n}\n\nexport interface DoomHeadlessRegistration {\n dispose(): void;\n}\n\nexport type DoomHeadlessContent = { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string };\n\nexport interface DoomHeadlessToolResult {\n content: DoomHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n}\n\nexport interface DoomHeadlessClientRequest {\n kind: 'confirm' | 'select' | 'input';\n title: string;\n message?: string;\n options?: readonly { label: string; description?: string; value: string }[];\n initialValue?: string;\n multiline?: boolean;\n}\n\nexport interface DoomHeadlessClient {\n /**\n * Operator-facing surface only: the body is flattened to one line and capped at 4096 characters\n * by schemas/notification.ts:73-83, and the model never sees it. Use session.admitPrompt to\n * reach the model.\n */\n notify(request: DoomNotificationRequest): void | Promise<void>;\n request(request: DoomHeadlessClientRequest, signal?: AbortSignal): Promise<unknown>;\n setStatus(source: string, text: string | undefined): void;\n appendComposerText?(text: string): void;\n}\n\nexport interface DoomHeadlessModelSettings {\n model?: { provider: string; id: string };\n thinkingLevel: ThinkingLevel;\n}\n\nexport interface DoomHeadlessSession {\n readModelSettings?(): Promise<DoomHeadlessModelSettings>;\n setModelSettings?(settings: Partial<DoomHeadlessModelSettings>): Promise<void>;\n /** Query durable history on demand; hosts do not retain the entire transcript. */\n entries(query?: {\n type?: 'custom' | 'message';\n customType?: string;\n limit?: number;\n }): readonly Record<string, unknown>[] | Promise<readonly Record<string, unknown>[]>;\n appendCustomEntry(type: string, data: unknown): Promise<void>;\n /**\n * Delivers and awaits the turn. 'steer' and 'followUp' only enqueue into a running turn; they do\n * NOT wake an idle agent.\n */\n prompt(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n /**\n * Resolves when accepted by the session, before the agent turn settles. Here 'steer' describes\n * how to deliver IF a turn is already running; an idle agent is woken either way, matching Pi's\n * ExtensionAPI.sendMessage({ triggerTurn: true, deliverAs: 'steer' }).\n */\n admitPrompt?(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n abort(): Promise<void>;\n compact(instructions?: string): Promise<void>;\n activity(): Promise<{ hasPendingMessages: boolean; isIdle: boolean }>;\n /** Capture the current persisted branch for an in-process child fork. */\n forkSource?(): Promise<{ kind: 'v4-fork'; sessionFile: string; branch: string; entryId?: string }>;\n}\n\n/** A grant-filtered skill view available only during one remote MCP tool invocation. */\nexport interface DoomMcpSkillAccess {\n list(): Promise<readonly { name: string; description: string }[]>;\n read(name: string): Promise<string>;\n}\n\nexport interface DoomHeadlessExecutionContext {\n readonly cwd: string;\n /** Admitted configuration root, distinct from the tool execution directory. */\n readonly repoRoot: string;\n readonly sessionId: string;\n /** Immutable session configuration. Facets must not read or mutate process.env. */\n readonly environment: Readonly<Record<string, string | undefined>>;\n readonly client: DoomHeadlessClient;\n readonly session: DoomHeadlessSession;\n readonly model?: { provider: string; id: string };\n readonly textCompletion?: {\n available(reference: string): boolean;\n complete(\n reference: string,\n request: {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n },\n ): Promise<string>;\n };\n readonly selection: DoomHeadlessSelection;\n /** Present only while an authenticated remote MCP tool is running. */\n readonly mcpSkills?: DoomMcpSkillAccess;\n shutdown(): void;\n}\n\n/** Conditions narrow an eligible owner's contributions; they can never restore a disabled owner. */\nexport interface DoomHeadlessCondition {\n state?: Readonly<Record<string, string>>;\n attribution?: { kind: 'minor' | 'domain'; mode: string; label?: string };\n domain?: string;\n}\n\nexport interface DoomHeadlessToolRestriction {\n when?: DoomHeadlessCondition;\n allowedTools: readonly string[];\n}\n\nexport interface DoomHeadlessTool<TParameters extends TSchema = TSchema> {\n when?: DoomHeadlessCondition;\n name: string;\n label?: string;\n description: string;\n parameters: TParameters;\n promptSnippet?: string;\n promptGuidelines?: readonly string[];\n executionMode?: 'parallel' | 'serial';\n execute(\n toolCallId: string,\n parameters: Static<TParameters>,\n signal: AbortSignal | undefined,\n onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n context: DoomHeadlessExecutionContext,\n ): Promise<DoomHeadlessToolResult>;\n}\n\n/**\n * One package contribution to the session's resource surface.\n *\n * `kind` decides what the model is billed for, and the difference is large:\n * - `context` is EAGER. `read()` runs on every prompt build and the text is pasted\n * into the system prompt verbatim. Reserve it for small live session state that\n * exists nowhere on disk. Never use it for a file the package ships: a README\n * registered this way costs its full length on every single turn.\n * - `skill` is LAZY. Only `name`, `description` and `path` reach the prompt; the\n * agent reads the file itself when the description matches the task.\n * - `prompt` is inert until the user invokes it by name.\n *\n * The rule in one line: if it is a file the package ships, it is a `skill`.\n */\nexport interface DoomHeadlessResource {\n when?: DoomHeadlessCondition;\n name: string;\n /** Model-visible selection signal. A `skill` without one tells the agent nothing. */\n description?: string;\n /** Absolute path of the file `read()` returns, so an advertised skill is one the agent can open. */\n path?: string;\n kind: 'prompt' | 'skill' | 'context';\n read(context: DoomHeadlessExecutionContext): string | Promise<string>;\n}\n\nexport interface DoomHeadlessCommand {\n when?: DoomHeadlessCondition;\n name: string;\n description: string;\n execute(args: string, context: DoomHeadlessExecutionContext): void | Promise<void>;\n}\n\nexport type DoomHeadlessEventName =\n | 'session_start'\n | 'session_shutdown'\n | 'session_tree'\n | 'before_agent_start'\n | 'agent_start'\n | 'agent_settled'\n | 'turn_start'\n | 'turn_end'\n | 'context'\n | 'before_provider_request'\n | 'message_start'\n | 'message_update'\n | 'message_end'\n | 'tool_call'\n | 'tool_execution_start'\n | 'tool_execution_update'\n | 'tool_execution_end'\n | 'tool_result'\n | 'model_select'\n | 'session_before_compact'\n | 'session_compact';\n\nexport type DoomHeadlessHookEvent<E extends DoomHeadlessEventName> = E extends 'before_provider_request'\n ? { lane: string; runId: string; model: { provider: string; id: string }; payload: unknown }\n : E extends 'session_before_compact'\n ? { reason: string; preparation: unknown; instructions?: string; customInstructions?: string }\n : E extends 'model_select'\n ? { model: { provider: string; id: string } }\n : Readonly<Record<string, unknown>>;\n\nexport interface DoomHeadlessCompactionResult {\n summary: string;\n tokensBefore: number;\n retainedTail: Record<string, unknown>[];\n usage?: Record<string, unknown>;\n details?: unknown;\n}\n\nexport type DoomHeadlessHookResult<E extends DoomHeadlessEventName> = E extends 'before_provider_request'\n ? { payload: unknown } | void\n : E extends 'session_before_compact'\n ? { cancel?: boolean; decline?: boolean; compaction?: DoomHeadlessCompactionResult } | void\n : E extends 'context'\n ? { messages?: unknown[]; systemPrompt?: string } | void\n : E extends 'before_agent_start'\n ? { systemPrompt?: string } | void\n : E extends 'tool_call'\n ? { args?: Record<string, unknown>; block?: { reason: string; terminate?: boolean } } | void\n : E extends 'tool_result'\n ? {\n content?: DoomHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n usage?: Record<string, unknown>;\n terminate?: boolean;\n } | void\n : unknown;\n\nexport type DoomHeadlessHook<E extends DoomHeadlessEventName = DoomHeadlessEventName> = E extends DoomHeadlessEventName\n ? {\n when?: DoomHeadlessCondition;\n event: E;\n handle(\n event: Readonly<DoomHeadlessHookEvent<E>>,\n context: DoomHeadlessExecutionContext,\n ): DoomHeadlessHookResult<E> | Promise<DoomHeadlessHookResult<E>>;\n }\n : never;\n\n/** A retained facet can register activity without starting disabled watchers or services. */\nexport interface DoomHeadlessActivity {\n when?: DoomHeadlessCondition;\n name: string;\n start(context: DoomHeadlessExecutionContext): (() => void | Promise<void>) | Promise<() => void | Promise<void>>;\n}\n\n/** Registrations inherit package ownership from the caller's Cordis facet. */\nexport interface DoomHeadlessHostService {\n readonly context: DoomHeadlessExecutionContext;\n changeSelection(change: DoomHeadlessSelectionChange): Promise<void>;\n assertActive(source?: string): void;\n subscribeSelection(listener: (selection: DoomHeadlessSelection) => void | Promise<void>): () => void;\n registerToolRestriction(restriction: DoomHeadlessToolRestriction): DoomHeadlessRegistration;\n registerTool<TParameters extends TSchema>(tool: DoomHeadlessTool<TParameters>): DoomHeadlessRegistration;\n registerResource(resource: DoomHeadlessResource): DoomHeadlessRegistration;\n registerCommand(command: DoomHeadlessCommand): DoomHeadlessRegistration;\n registerHook(hook: DoomHeadlessHook): DoomHeadlessRegistration;\n registerActivity(activity: DoomHeadlessActivity): DoomHeadlessRegistration;\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'doom/headless-host': DoomHeadlessHostService;\n }\n}\n\nexport function readDoomHeadlessHost(context: Context): DoomHeadlessHostService | undefined {\n return context.get(DOOM_HEADLESS_HOST_SERVICE) as DoomHeadlessHostService | undefined;\n}\n\nexport function requireDoomHeadlessHost(context: Context): DoomHeadlessHostService {\n const host = readDoomHeadlessHost(context);\n if (!host) throw new Error('This contribution requires the Doom headless session host.');\n return host;\n}\n"],"mappings":";;AAQA,MAAa,6BAA6B;AAC1C,MAAa,sBAAsB,OAAO,IAAI,qBAAqB;AAkBnE,SAAgB,sBAAsB,SAAqD;CACzF,OAAQ,QAAwE;AAClF;AAsQA,SAAgB,qBAAqB,SAAuD;CAC1F,OAAO,QAAQ,IAAI,0BAA0B;AAC/C;AAEA,SAAgB,wBAAwB,SAA2C;CACjF,MAAM,OAAO,qBAAqB,OAAO;CACzC,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,4DAA4D;CACvF,OAAO;AACT"}
@@ -112,6 +112,14 @@ export interface DoomHeadlessSession {
112
112
  entryId?: string;
113
113
  }>;
114
114
  }
115
+ /** A grant-filtered skill view available only during one remote MCP tool invocation. */
116
+ export interface DoomMcpSkillAccess {
117
+ list(): Promise<readonly {
118
+ name: string;
119
+ description: string;
120
+ }[]>;
121
+ read(name: string): Promise<string>;
122
+ }
115
123
  export interface DoomHeadlessExecutionContext {
116
124
  readonly cwd: string;
117
125
  /** Admitted configuration root, distinct from the tool execution directory. */
@@ -136,6 +144,8 @@ export interface DoomHeadlessExecutionContext {
136
144
  }): Promise<string>;
137
145
  };
138
146
  readonly selection: DoomHeadlessSelection;
147
+ /** Present only while an authenticated remote MCP tool is running. */
148
+ readonly mcpSkills?: DoomMcpSkillAccess;
139
149
  shutdown(): void;
140
150
  }
141
151
  /** Conditions narrow an eligible owner's contributions; they can never restore a disabled owner. */
@@ -1 +1 @@
1
- {"version":3,"file":"headless.d.cts","names":[],"sources":["../../src/schemas/headless.ts"],"mappings":"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"}
1
+ {"version":3,"file":"headless.d.cts","names":[],"sources":["../../src/schemas/headless.ts"],"mappings":"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"}
@@ -112,6 +112,14 @@ export interface DoomHeadlessSession {
112
112
  entryId?: string;
113
113
  }>;
114
114
  }
115
+ /** A grant-filtered skill view available only during one remote MCP tool invocation. */
116
+ export interface DoomMcpSkillAccess {
117
+ list(): Promise<readonly {
118
+ name: string;
119
+ description: string;
120
+ }[]>;
121
+ read(name: string): Promise<string>;
122
+ }
115
123
  export interface DoomHeadlessExecutionContext {
116
124
  readonly cwd: string;
117
125
  /** Admitted configuration root, distinct from the tool execution directory. */
@@ -136,6 +144,8 @@ export interface DoomHeadlessExecutionContext {
136
144
  }): Promise<string>;
137
145
  };
138
146
  readonly selection: DoomHeadlessSelection;
147
+ /** Present only while an authenticated remote MCP tool is running. */
148
+ readonly mcpSkills?: DoomMcpSkillAccess;
139
149
  shutdown(): void;
140
150
  }
141
151
  /** Conditions narrow an eligible owner's contributions; they can never restore a disabled owner. */
@@ -1 +1 @@
1
- {"version":3,"file":"headless.d.mts","names":[],"sources":["../../src/schemas/headless.ts"],"mappings":"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"}
1
+ {"version":3,"file":"headless.d.mts","names":[],"sources":["../../src/schemas/headless.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"headless.mjs","names":[],"sources":["../../src/schemas/headless.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { Static, TSchema } from 'typebox';\n\nimport type { DoomNotificationRequest } from './notification';\nimport type { DoomServerBundleEntry } from './serverBundle';\n\n/** Session-only contributions. This is not a Pi ExtensionAPI or a TUI adapter. */\nexport const DOOM_HEADLESS_HOST_SERVICE = 'doom/headless-host';\nexport const DOOM_HEADLESS_OWNER = Symbol.for('doom/headless-owner');\n\nexport interface DoomHeadlessSelection {\n readonly majorMode: string;\n readonly activeLayers: readonly string[];\n readonly domains: readonly string[];\n readonly profile?: string;\n /** Feature-owned activation sets, keyed by the contributing capability. */\n readonly state?: Readonly<Record<string, readonly string[]>>;\n}\n\n/** One package-owned selection transition. Server facets change exactly one axis per operation. */\nexport type DoomHeadlessSelectionChange =\n | { readonly axis: 'majorMode'; readonly majorMode: string }\n | { readonly axis: 'domains'; readonly domains: readonly string[] }\n | { readonly axis: 'profile'; readonly profile?: string }\n | { readonly axis: 'state'; readonly key: string; readonly values: readonly string[] };\n\nexport function readDoomHeadlessOwner(context: Context): DoomServerBundleEntry | undefined {\n return (context as Context & { [DOOM_HEADLESS_OWNER]?: DoomServerBundleEntry })[DOOM_HEADLESS_OWNER];\n}\n\nexport interface DoomHeadlessRegistration {\n dispose(): void;\n}\n\nexport type DoomHeadlessContent = { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string };\n\nexport interface DoomHeadlessToolResult {\n content: DoomHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n}\n\nexport interface DoomHeadlessClientRequest {\n kind: 'confirm' | 'select' | 'input';\n title: string;\n message?: string;\n options?: readonly { label: string; description?: string; value: string }[];\n initialValue?: string;\n multiline?: boolean;\n}\n\nexport interface DoomHeadlessClient {\n /**\n * Operator-facing surface only: the body is flattened to one line and capped at 4096 characters\n * by schemas/notification.ts:73-83, and the model never sees it. Use session.admitPrompt to\n * reach the model.\n */\n notify(request: DoomNotificationRequest): void | Promise<void>;\n request(request: DoomHeadlessClientRequest, signal?: AbortSignal): Promise<unknown>;\n setStatus(source: string, text: string | undefined): void;\n appendComposerText?(text: string): void;\n}\n\nexport interface DoomHeadlessModelSettings {\n model?: { provider: string; id: string };\n thinkingLevel: ThinkingLevel;\n}\n\nexport interface DoomHeadlessSession {\n readModelSettings?(): Promise<DoomHeadlessModelSettings>;\n setModelSettings?(settings: Partial<DoomHeadlessModelSettings>): Promise<void>;\n /** Query durable history on demand; hosts do not retain the entire transcript. */\n entries(query?: {\n type?: 'custom' | 'message';\n customType?: string;\n limit?: number;\n }): readonly Record<string, unknown>[] | Promise<readonly Record<string, unknown>[]>;\n appendCustomEntry(type: string, data: unknown): Promise<void>;\n /**\n * Delivers and awaits the turn. 'steer' and 'followUp' only enqueue into a running turn; they do\n * NOT wake an idle agent.\n */\n prompt(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n /**\n * Resolves when accepted by the session, before the agent turn settles. Here 'steer' describes\n * how to deliver IF a turn is already running; an idle agent is woken either way, matching Pi's\n * ExtensionAPI.sendMessage({ triggerTurn: true, deliverAs: 'steer' }).\n */\n admitPrompt?(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n abort(): Promise<void>;\n compact(instructions?: string): Promise<void>;\n activity(): Promise<{ hasPendingMessages: boolean; isIdle: boolean }>;\n /** Capture the current persisted branch for an in-process child fork. */\n forkSource?(): Promise<{ kind: 'v4-fork'; sessionFile: string; branch: string; entryId?: string }>;\n}\n\nexport interface DoomHeadlessExecutionContext {\n readonly cwd: string;\n /** Admitted configuration root, distinct from the tool execution directory. */\n readonly repoRoot: string;\n readonly sessionId: string;\n /** Immutable session configuration. Facets must not read or mutate process.env. */\n readonly environment: Readonly<Record<string, string | undefined>>;\n readonly client: DoomHeadlessClient;\n readonly session: DoomHeadlessSession;\n readonly model?: { provider: string; id: string };\n readonly textCompletion?: {\n available(reference: string): boolean;\n complete(\n reference: string,\n request: {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n },\n ): Promise<string>;\n };\n readonly selection: DoomHeadlessSelection;\n shutdown(): void;\n}\n\n/** Conditions narrow an eligible owner's contributions; they can never restore a disabled owner. */\nexport interface DoomHeadlessCondition {\n state?: Readonly<Record<string, string>>;\n attribution?: { kind: 'minor' | 'domain'; mode: string; label?: string };\n domain?: string;\n}\n\nexport interface DoomHeadlessToolRestriction {\n when?: DoomHeadlessCondition;\n allowedTools: readonly string[];\n}\n\nexport interface DoomHeadlessTool<TParameters extends TSchema = TSchema> {\n when?: DoomHeadlessCondition;\n name: string;\n label?: string;\n description: string;\n parameters: TParameters;\n promptSnippet?: string;\n promptGuidelines?: readonly string[];\n executionMode?: 'parallel' | 'serial';\n execute(\n toolCallId: string,\n parameters: Static<TParameters>,\n signal: AbortSignal | undefined,\n onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n context: DoomHeadlessExecutionContext,\n ): Promise<DoomHeadlessToolResult>;\n}\n\n/**\n * One package contribution to the session's resource surface.\n *\n * `kind` decides what the model is billed for, and the difference is large:\n * - `context` is EAGER. `read()` runs on every prompt build and the text is pasted\n * into the system prompt verbatim. Reserve it for small live session state that\n * exists nowhere on disk. Never use it for a file the package ships: a README\n * registered this way costs its full length on every single turn.\n * - `skill` is LAZY. Only `name`, `description` and `path` reach the prompt; the\n * agent reads the file itself when the description matches the task.\n * - `prompt` is inert until the user invokes it by name.\n *\n * The rule in one line: if it is a file the package ships, it is a `skill`.\n */\nexport interface DoomHeadlessResource {\n when?: DoomHeadlessCondition;\n name: string;\n /** Model-visible selection signal. A `skill` without one tells the agent nothing. */\n description?: string;\n /** Absolute path of the file `read()` returns, so an advertised skill is one the agent can open. */\n path?: string;\n kind: 'prompt' | 'skill' | 'context';\n read(context: DoomHeadlessExecutionContext): string | Promise<string>;\n}\n\nexport interface DoomHeadlessCommand {\n when?: DoomHeadlessCondition;\n name: string;\n description: string;\n execute(args: string, context: DoomHeadlessExecutionContext): void | Promise<void>;\n}\n\nexport type DoomHeadlessEventName =\n | 'session_start'\n | 'session_shutdown'\n | 'session_tree'\n | 'before_agent_start'\n | 'agent_start'\n | 'agent_settled'\n | 'turn_start'\n | 'turn_end'\n | 'context'\n | 'before_provider_request'\n | 'message_start'\n | 'message_update'\n | 'message_end'\n | 'tool_call'\n | 'tool_execution_start'\n | 'tool_execution_update'\n | 'tool_execution_end'\n | 'tool_result'\n | 'model_select'\n | 'session_before_compact'\n | 'session_compact';\n\nexport type DoomHeadlessHookEvent<E extends DoomHeadlessEventName> = E extends 'before_provider_request'\n ? { lane: string; runId: string; model: { provider: string; id: string }; payload: unknown }\n : E extends 'session_before_compact'\n ? { reason: string; preparation: unknown; instructions?: string; customInstructions?: string }\n : E extends 'model_select'\n ? { model: { provider: string; id: string } }\n : Readonly<Record<string, unknown>>;\n\nexport interface DoomHeadlessCompactionResult {\n summary: string;\n tokensBefore: number;\n retainedTail: Record<string, unknown>[];\n usage?: Record<string, unknown>;\n details?: unknown;\n}\n\nexport type DoomHeadlessHookResult<E extends DoomHeadlessEventName> = E extends 'before_provider_request'\n ? { payload: unknown } | void\n : E extends 'session_before_compact'\n ? { cancel?: boolean; decline?: boolean; compaction?: DoomHeadlessCompactionResult } | void\n : E extends 'context'\n ? { messages?: unknown[]; systemPrompt?: string } | void\n : E extends 'before_agent_start'\n ? { systemPrompt?: string } | void\n : E extends 'tool_call'\n ? { args?: Record<string, unknown>; block?: { reason: string; terminate?: boolean } } | void\n : E extends 'tool_result'\n ? {\n content?: DoomHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n usage?: Record<string, unknown>;\n terminate?: boolean;\n } | void\n : unknown;\n\nexport type DoomHeadlessHook<E extends DoomHeadlessEventName = DoomHeadlessEventName> = E extends DoomHeadlessEventName\n ? {\n when?: DoomHeadlessCondition;\n event: E;\n handle(\n event: Readonly<DoomHeadlessHookEvent<E>>,\n context: DoomHeadlessExecutionContext,\n ): DoomHeadlessHookResult<E> | Promise<DoomHeadlessHookResult<E>>;\n }\n : never;\n\n/** A retained facet can register activity without starting disabled watchers or services. */\nexport interface DoomHeadlessActivity {\n when?: DoomHeadlessCondition;\n name: string;\n start(context: DoomHeadlessExecutionContext): (() => void | Promise<void>) | Promise<() => void | Promise<void>>;\n}\n\n/** Registrations inherit package ownership from the caller's Cordis facet. */\nexport interface DoomHeadlessHostService {\n readonly context: DoomHeadlessExecutionContext;\n changeSelection(change: DoomHeadlessSelectionChange): Promise<void>;\n assertActive(source?: string): void;\n subscribeSelection(listener: (selection: DoomHeadlessSelection) => void | Promise<void>): () => void;\n registerToolRestriction(restriction: DoomHeadlessToolRestriction): DoomHeadlessRegistration;\n registerTool<TParameters extends TSchema>(tool: DoomHeadlessTool<TParameters>): DoomHeadlessRegistration;\n registerResource(resource: DoomHeadlessResource): DoomHeadlessRegistration;\n registerCommand(command: DoomHeadlessCommand): DoomHeadlessRegistration;\n registerHook(hook: DoomHeadlessHook): DoomHeadlessRegistration;\n registerActivity(activity: DoomHeadlessActivity): DoomHeadlessRegistration;\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'doom/headless-host': DoomHeadlessHostService;\n }\n}\n\nexport function readDoomHeadlessHost(context: Context): DoomHeadlessHostService | undefined {\n return context.get(DOOM_HEADLESS_HOST_SERVICE) as DoomHeadlessHostService | undefined;\n}\n\nexport function requireDoomHeadlessHost(context: Context): DoomHeadlessHostService {\n const host = readDoomHeadlessHost(context);\n if (!host) throw new Error('This contribution requires the Doom headless session host.');\n return host;\n}\n"],"mappings":";;AAQA,MAAa,6BAA6B;AAC1C,MAAa,sBAAsB,OAAO,IAAI,qBAAqB;AAkBnE,SAAgB,sBAAsB,SAAqD;CACzF,OAAQ,QAAwE;AAClF;AA8PA,SAAgB,qBAAqB,SAAuD;CAC1F,OAAO,QAAQ,IAAI,0BAA0B;AAC/C;AAEA,SAAgB,wBAAwB,SAA2C;CACjF,MAAM,OAAO,qBAAqB,OAAO;CACzC,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,4DAA4D;CACvF,OAAO;AACT"}
1
+ {"version":3,"file":"headless.mjs","names":[],"sources":["../../src/schemas/headless.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { Static, TSchema } from 'typebox';\n\nimport type { DoomNotificationRequest } from './notification';\nimport type { DoomServerBundleEntry } from './serverBundle';\n\n/** Session-only contributions. This is not a Pi ExtensionAPI or a TUI adapter. */\nexport const DOOM_HEADLESS_HOST_SERVICE = 'doom/headless-host';\nexport const DOOM_HEADLESS_OWNER = Symbol.for('doom/headless-owner');\n\nexport interface DoomHeadlessSelection {\n readonly majorMode: string;\n readonly activeLayers: readonly string[];\n readonly domains: readonly string[];\n readonly profile?: string;\n /** Feature-owned activation sets, keyed by the contributing capability. */\n readonly state?: Readonly<Record<string, readonly string[]>>;\n}\n\n/** One package-owned selection transition. Server facets change exactly one axis per operation. */\nexport type DoomHeadlessSelectionChange =\n | { readonly axis: 'majorMode'; readonly majorMode: string }\n | { readonly axis: 'domains'; readonly domains: readonly string[] }\n | { readonly axis: 'profile'; readonly profile?: string }\n | { readonly axis: 'state'; readonly key: string; readonly values: readonly string[] };\n\nexport function readDoomHeadlessOwner(context: Context): DoomServerBundleEntry | undefined {\n return (context as Context & { [DOOM_HEADLESS_OWNER]?: DoomServerBundleEntry })[DOOM_HEADLESS_OWNER];\n}\n\nexport interface DoomHeadlessRegistration {\n dispose(): void;\n}\n\nexport type DoomHeadlessContent = { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string };\n\nexport interface DoomHeadlessToolResult {\n content: DoomHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n}\n\nexport interface DoomHeadlessClientRequest {\n kind: 'confirm' | 'select' | 'input';\n title: string;\n message?: string;\n options?: readonly { label: string; description?: string; value: string }[];\n initialValue?: string;\n multiline?: boolean;\n}\n\nexport interface DoomHeadlessClient {\n /**\n * Operator-facing surface only: the body is flattened to one line and capped at 4096 characters\n * by schemas/notification.ts:73-83, and the model never sees it. Use session.admitPrompt to\n * reach the model.\n */\n notify(request: DoomNotificationRequest): void | Promise<void>;\n request(request: DoomHeadlessClientRequest, signal?: AbortSignal): Promise<unknown>;\n setStatus(source: string, text: string | undefined): void;\n appendComposerText?(text: string): void;\n}\n\nexport interface DoomHeadlessModelSettings {\n model?: { provider: string; id: string };\n thinkingLevel: ThinkingLevel;\n}\n\nexport interface DoomHeadlessSession {\n readModelSettings?(): Promise<DoomHeadlessModelSettings>;\n setModelSettings?(settings: Partial<DoomHeadlessModelSettings>): Promise<void>;\n /** Query durable history on demand; hosts do not retain the entire transcript. */\n entries(query?: {\n type?: 'custom' | 'message';\n customType?: string;\n limit?: number;\n }): readonly Record<string, unknown>[] | Promise<readonly Record<string, unknown>[]>;\n appendCustomEntry(type: string, data: unknown): Promise<void>;\n /**\n * Delivers and awaits the turn. 'steer' and 'followUp' only enqueue into a running turn; they do\n * NOT wake an idle agent.\n */\n prompt(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n /**\n * Resolves when accepted by the session, before the agent turn settles. Here 'steer' describes\n * how to deliver IF a turn is already running; an idle agent is woken either way, matching Pi's\n * ExtensionAPI.sendMessage({ triggerTurn: true, deliverAs: 'steer' }).\n */\n admitPrompt?(text: string, delivery?: 'prompt' | 'steer' | 'followUp'): Promise<void>;\n abort(): Promise<void>;\n compact(instructions?: string): Promise<void>;\n activity(): Promise<{ hasPendingMessages: boolean; isIdle: boolean }>;\n /** Capture the current persisted branch for an in-process child fork. */\n forkSource?(): Promise<{ kind: 'v4-fork'; sessionFile: string; branch: string; entryId?: string }>;\n}\n\n/** A grant-filtered skill view available only during one remote MCP tool invocation. */\nexport interface DoomMcpSkillAccess {\n list(): Promise<readonly { name: string; description: string }[]>;\n read(name: string): Promise<string>;\n}\n\nexport interface DoomHeadlessExecutionContext {\n readonly cwd: string;\n /** Admitted configuration root, distinct from the tool execution directory. */\n readonly repoRoot: string;\n readonly sessionId: string;\n /** Immutable session configuration. Facets must not read or mutate process.env. */\n readonly environment: Readonly<Record<string, string | undefined>>;\n readonly client: DoomHeadlessClient;\n readonly session: DoomHeadlessSession;\n readonly model?: { provider: string; id: string };\n readonly textCompletion?: {\n available(reference: string): boolean;\n complete(\n reference: string,\n request: {\n systemPrompt: string;\n input: string;\n maxTokens: number;\n cacheRetention?: 'none' | 'short' | 'long';\n signal?: AbortSignal;\n },\n ): Promise<string>;\n };\n readonly selection: DoomHeadlessSelection;\n /** Present only while an authenticated remote MCP tool is running. */\n readonly mcpSkills?: DoomMcpSkillAccess;\n shutdown(): void;\n}\n\n/** Conditions narrow an eligible owner's contributions; they can never restore a disabled owner. */\nexport interface DoomHeadlessCondition {\n state?: Readonly<Record<string, string>>;\n attribution?: { kind: 'minor' | 'domain'; mode: string; label?: string };\n domain?: string;\n}\n\nexport interface DoomHeadlessToolRestriction {\n when?: DoomHeadlessCondition;\n allowedTools: readonly string[];\n}\n\nexport interface DoomHeadlessTool<TParameters extends TSchema = TSchema> {\n when?: DoomHeadlessCondition;\n name: string;\n label?: string;\n description: string;\n parameters: TParameters;\n promptSnippet?: string;\n promptGuidelines?: readonly string[];\n executionMode?: 'parallel' | 'serial';\n execute(\n toolCallId: string,\n parameters: Static<TParameters>,\n signal: AbortSignal | undefined,\n onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n context: DoomHeadlessExecutionContext,\n ): Promise<DoomHeadlessToolResult>;\n}\n\n/**\n * One package contribution to the session's resource surface.\n *\n * `kind` decides what the model is billed for, and the difference is large:\n * - `context` is EAGER. `read()` runs on every prompt build and the text is pasted\n * into the system prompt verbatim. Reserve it for small live session state that\n * exists nowhere on disk. Never use it for a file the package ships: a README\n * registered this way costs its full length on every single turn.\n * - `skill` is LAZY. Only `name`, `description` and `path` reach the prompt; the\n * agent reads the file itself when the description matches the task.\n * - `prompt` is inert until the user invokes it by name.\n *\n * The rule in one line: if it is a file the package ships, it is a `skill`.\n */\nexport interface DoomHeadlessResource {\n when?: DoomHeadlessCondition;\n name: string;\n /** Model-visible selection signal. A `skill` without one tells the agent nothing. */\n description?: string;\n /** Absolute path of the file `read()` returns, so an advertised skill is one the agent can open. */\n path?: string;\n kind: 'prompt' | 'skill' | 'context';\n read(context: DoomHeadlessExecutionContext): string | Promise<string>;\n}\n\nexport interface DoomHeadlessCommand {\n when?: DoomHeadlessCondition;\n name: string;\n description: string;\n execute(args: string, context: DoomHeadlessExecutionContext): void | Promise<void>;\n}\n\nexport type DoomHeadlessEventName =\n | 'session_start'\n | 'session_shutdown'\n | 'session_tree'\n | 'before_agent_start'\n | 'agent_start'\n | 'agent_settled'\n | 'turn_start'\n | 'turn_end'\n | 'context'\n | 'before_provider_request'\n | 'message_start'\n | 'message_update'\n | 'message_end'\n | 'tool_call'\n | 'tool_execution_start'\n | 'tool_execution_update'\n | 'tool_execution_end'\n | 'tool_result'\n | 'model_select'\n | 'session_before_compact'\n | 'session_compact';\n\nexport type DoomHeadlessHookEvent<E extends DoomHeadlessEventName> = E extends 'before_provider_request'\n ? { lane: string; runId: string; model: { provider: string; id: string }; payload: unknown }\n : E extends 'session_before_compact'\n ? { reason: string; preparation: unknown; instructions?: string; customInstructions?: string }\n : E extends 'model_select'\n ? { model: { provider: string; id: string } }\n : Readonly<Record<string, unknown>>;\n\nexport interface DoomHeadlessCompactionResult {\n summary: string;\n tokensBefore: number;\n retainedTail: Record<string, unknown>[];\n usage?: Record<string, unknown>;\n details?: unknown;\n}\n\nexport type DoomHeadlessHookResult<E extends DoomHeadlessEventName> = E extends 'before_provider_request'\n ? { payload: unknown } | void\n : E extends 'session_before_compact'\n ? { cancel?: boolean; decline?: boolean; compaction?: DoomHeadlessCompactionResult } | void\n : E extends 'context'\n ? { messages?: unknown[]; systemPrompt?: string } | void\n : E extends 'before_agent_start'\n ? { systemPrompt?: string } | void\n : E extends 'tool_call'\n ? { args?: Record<string, unknown>; block?: { reason: string; terminate?: boolean } } | void\n : E extends 'tool_result'\n ? {\n content?: DoomHeadlessContent[];\n details?: unknown;\n isError?: boolean;\n usage?: Record<string, unknown>;\n terminate?: boolean;\n } | void\n : unknown;\n\nexport type DoomHeadlessHook<E extends DoomHeadlessEventName = DoomHeadlessEventName> = E extends DoomHeadlessEventName\n ? {\n when?: DoomHeadlessCondition;\n event: E;\n handle(\n event: Readonly<DoomHeadlessHookEvent<E>>,\n context: DoomHeadlessExecutionContext,\n ): DoomHeadlessHookResult<E> | Promise<DoomHeadlessHookResult<E>>;\n }\n : never;\n\n/** A retained facet can register activity without starting disabled watchers or services. */\nexport interface DoomHeadlessActivity {\n when?: DoomHeadlessCondition;\n name: string;\n start(context: DoomHeadlessExecutionContext): (() => void | Promise<void>) | Promise<() => void | Promise<void>>;\n}\n\n/** Registrations inherit package ownership from the caller's Cordis facet. */\nexport interface DoomHeadlessHostService {\n readonly context: DoomHeadlessExecutionContext;\n changeSelection(change: DoomHeadlessSelectionChange): Promise<void>;\n assertActive(source?: string): void;\n subscribeSelection(listener: (selection: DoomHeadlessSelection) => void | Promise<void>): () => void;\n registerToolRestriction(restriction: DoomHeadlessToolRestriction): DoomHeadlessRegistration;\n registerTool<TParameters extends TSchema>(tool: DoomHeadlessTool<TParameters>): DoomHeadlessRegistration;\n registerResource(resource: DoomHeadlessResource): DoomHeadlessRegistration;\n registerCommand(command: DoomHeadlessCommand): DoomHeadlessRegistration;\n registerHook(hook: DoomHeadlessHook): DoomHeadlessRegistration;\n registerActivity(activity: DoomHeadlessActivity): DoomHeadlessRegistration;\n}\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'doom/headless-host': DoomHeadlessHostService;\n }\n}\n\nexport function readDoomHeadlessHost(context: Context): DoomHeadlessHostService | undefined {\n return context.get(DOOM_HEADLESS_HOST_SERVICE) as DoomHeadlessHostService | undefined;\n}\n\nexport function requireDoomHeadlessHost(context: Context): DoomHeadlessHostService {\n const host = readDoomHeadlessHost(context);\n if (!host) throw new Error('This contribution requires the Doom headless session host.');\n return host;\n}\n"],"mappings":";;AAQA,MAAa,6BAA6B;AAC1C,MAAa,sBAAsB,OAAO,IAAI,qBAAqB;AAkBnE,SAAgB,sBAAsB,SAAqD;CACzF,OAAQ,QAAwE;AAClF;AAsQA,SAAgB,qBAAqB,SAAuD;CAC1F,OAAO,QAAQ,IAAI,0BAA0B;AAC/C;AAEA,SAAgB,wBAAwB,SAA2C;CACjF,MAAM,OAAO,qBAAqB,OAAO;CACzC,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,4DAA4D;CACvF,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"mcpFacet.cjs","names":[],"sources":["../../src/schemas/mcpFacet.ts"],"sourcesContent":["import type { TSchema } from 'typebox';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessSelection,\n DoomHeadlessSelectionChange,\n DoomHeadlessTool,\n} from './headless';\n\n/** A lazy Markdown resource available only to remote MCP clients. */\nexport interface DoomMcpSkill {\n readonly name: string;\n readonly description: string;\n read(context: DoomHeadlessExecutionContext): string | Promise<string>;\n}\n\n/** A dedicated remote surface, deliberately separate from local agent registrations. */\nexport interface DoomMcpSessionPlugin {\n readonly tools?: readonly DoomHeadlessTool<TSchema>[];\n readonly skills?: readonly DoomMcpSkill[];\n}\n\nexport interface DoomMcpServiceScope {\n /** Reads a service already mounted in this session without exposing host registration APIs. */\n get<T>(name: string): T | undefined;\n}\n\nexport interface DoomMcpPluginContext {\n readonly execution: DoomHeadlessExecutionContext;\n readonly services: DoomMcpServiceScope;\n readonly selection: {\n read(): DoomHeadlessSelection;\n change(change: DoomHeadlessSelectionChange): Promise<void>;\n };\n /** Aborted when this plugin surface is replaced or the session closes. */\n readonly signal: AbortSignal;\n}\n\nexport interface DoomMcpPluginDefinition {\n readonly name: string;\n readonly session:\n | DoomMcpSessionPlugin\n | ((context: DoomMcpPluginContext) => DoomMcpSessionPlugin | Promise<DoomMcpSessionPlugin>);\n}\n\n/** Makes an MCP declaration's contract explicit without registering it locally. */\nexport function defineMcpPlugin(definition: DoomMcpPluginDefinition): DoomMcpPluginDefinition {\n return definition;\n}\n\n/** `tool/[name].mcp.ts`. Keeps the declaration independently typed. */\nexport function defineMcpTool<TParameters extends TSchema = TSchema, TContext = unknown>(\n tool: DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>),\n): DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>) {\n return tool;\n}\n\n/** `skill/[name].mcp.ts`. Keeps remote guidance out of the local prompt surface. */\nexport function defineMcpSkill<TContext = unknown>(\n skill: DoomMcpSkill | ((context: TContext) => DoomMcpSkill),\n): DoomMcpSkill | ((context: TContext) => DoomMcpSkill) {\n return skill;\n}\n"],"mappings":";;AA8CA,SAAgB,gBAAgB,YAA8D;CAC5F,OAAO;AACT;;AAGA,SAAgB,cACd,MACwF;CACxF,OAAO;AACT;;AAGA,SAAgB,eACd,OACsD;CACtD,OAAO;AACT"}
1
+ {"version":3,"file":"mcpFacet.cjs","names":[],"sources":["../../src/schemas/mcpFacet.ts"],"sourcesContent":["import type { TSchema } from 'typebox';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessSelection,\n DoomHeadlessSelectionChange,\n DoomHeadlessTool,\n} from './headless';\n\n/** Repository-scoped instructions and applied selection safe to disclose to a remote MCP client. */\nexport interface DoomMcpContextSnapshot {\n readonly session: { readonly id: string; readonly revision: number };\n readonly repository: { readonly root: string; readonly cwd: string };\n readonly selection: {\n readonly profile: string | null;\n readonly domains: readonly string[];\n readonly majorMode: string;\n readonly activeLayers: readonly string[];\n };\n readonly instructions: readonly { readonly path: string; readonly content: string }[];\n readonly persona: string | null;\n}\n\n/** A lazy Markdown resource available only to remote MCP clients. */\nexport interface DoomMcpSkill {\n readonly name: string;\n readonly description: string;\n read(context: DoomHeadlessExecutionContext): string | Promise<string>;\n}\n\n/** A dedicated remote surface, deliberately separate from local agent registrations. */\nexport interface DoomMcpSessionPlugin {\n readonly tools?: readonly DoomHeadlessTool<TSchema>[];\n readonly skills?: readonly DoomMcpSkill[];\n}\n\nexport interface DoomMcpServiceScope {\n /** Reads a service already mounted in this session without exposing host registration APIs. */\n get<T>(name: string): T | undefined;\n}\n\nexport interface DoomMcpPluginContext {\n readonly execution: DoomHeadlessExecutionContext;\n readonly services: DoomMcpServiceScope;\n readonly selection: {\n read(): DoomHeadlessSelection;\n change(change: DoomHeadlessSelectionChange): Promise<void>;\n };\n /** Reads the allowlisted repository context from the current applied session selection. */\n loadContext(): DoomMcpContextSnapshot;\n /** Aborted when this plugin surface is replaced or the session closes. */\n readonly signal: AbortSignal;\n /** Rebuilds the remote surface after a live MCP catalog changes. */\n refresh(): void;\n}\n\nexport interface DoomMcpPluginDefinition {\n readonly name: string;\n readonly session:\n | DoomMcpSessionPlugin\n | ((context: DoomMcpPluginContext) => DoomMcpSessionPlugin | Promise<DoomMcpSessionPlugin>);\n}\n\n/** Makes an MCP declaration's contract explicit without registering it locally. */\nexport function defineMcpPlugin(definition: DoomMcpPluginDefinition): DoomMcpPluginDefinition {\n return definition;\n}\n\n/** `tool/[name].mcp.ts`. Keeps the declaration independently typed. */\nexport function defineMcpTool<TParameters extends TSchema = TSchema, TContext = unknown>(\n tool: DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>),\n): DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>) {\n return tool;\n}\n\n/** `skill/[name].mcp.ts`. Keeps remote guidance out of the local prompt surface. */\nexport function defineMcpSkill<TContext = unknown>(\n skill: DoomMcpSkill | ((context: TContext) => DoomMcpSkill),\n): DoomMcpSkill | ((context: TContext) => DoomMcpSkill) {\n return skill;\n}\n"],"mappings":";;AAgEA,SAAgB,gBAAgB,YAA8D;CAC5F,OAAO;AACT;;AAGA,SAAgB,cACd,MACwF;CACxF,OAAO;AACT;;AAGA,SAAgB,eACd,OACsD;CACtD,OAAO;AACT"}
@@ -1,6 +1,28 @@
1
1
  import { DoomHeadlessExecutionContext, DoomHeadlessSelection, DoomHeadlessSelectionChange, DoomHeadlessTool } from "./headless.cjs";
2
2
  import { TSchema } from "typebox";
3
3
  //#region src/schemas/mcpFacet.d.ts
4
+ /** Repository-scoped instructions and applied selection safe to disclose to a remote MCP client. */
5
+ export interface DoomMcpContextSnapshot {
6
+ readonly session: {
7
+ readonly id: string;
8
+ readonly revision: number;
9
+ };
10
+ readonly repository: {
11
+ readonly root: string;
12
+ readonly cwd: string;
13
+ };
14
+ readonly selection: {
15
+ readonly profile: string | null;
16
+ readonly domains: readonly string[];
17
+ readonly majorMode: string;
18
+ readonly activeLayers: readonly string[];
19
+ };
20
+ readonly instructions: readonly {
21
+ readonly path: string;
22
+ readonly content: string;
23
+ }[];
24
+ readonly persona: string | null;
25
+ }
4
26
  /** A lazy Markdown resource available only to remote MCP clients. */
5
27
  export interface DoomMcpSkill {
6
28
  readonly name: string;
@@ -23,8 +45,12 @@ export interface DoomMcpPluginContext {
23
45
  read(): DoomHeadlessSelection;
24
46
  change(change: DoomHeadlessSelectionChange): Promise<void>;
25
47
  };
48
+ /** Reads the allowlisted repository context from the current applied session selection. */
49
+ loadContext(): DoomMcpContextSnapshot;
26
50
  /** Aborted when this plugin surface is replaced or the session closes. */
27
51
  readonly signal: AbortSignal;
52
+ /** Rebuilds the remote surface after a live MCP catalog changes. */
53
+ refresh(): void;
28
54
  }
29
55
  export interface DoomMcpPluginDefinition {
30
56
  readonly name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"mcpFacet.d.cts","names":[],"sources":["../../src/schemas/mcpFacet.ts"],"mappings":";;;;iBAUiB;WACN;WACA;EACT,KAAK,SAAS,wCAAwC;;;iBAIvC;WACN,iBAAiB,iBAAiB;WAClC,kBAAkB;;iBAGZ;;EAEf,IAAI,GAAG,eAAe;;iBAGP;WACN,WAAW;WACX,UAAU;WACV;IACP,QAAQ;IACR,OAAO,QAAQ,8BAA8B;;;WAGtC,QAAQ;;iBAGF;WACN;WACA,SACL,yBACE,SAAS,yBAAyB,uBAAuB,QAAQ;;;wBAIzD,gBAAgB,YAAY,0BAA0B;;wBAKtD,cAAc,oBAAoB,UAAU,SAAS,oBACnE,MAAM,iBAAiB,iBAAiB,SAAS,aAAa,iBAAiB,gBAC9E,iBAAiB,iBAAiB,SAAS,aAAa,iBAAiB;;wBAK5D,eAAe,oBAC7B,OAAO,iBAAiB,SAAS,aAAa,gBAC7C,iBAAiB,SAAS,aAAa"}
1
+ {"version":3,"file":"mcpFacet.d.cts","names":[],"sources":["../../src/schemas/mcpFacet.ts"],"mappings":";;;;iBAUiB;WACN;aAAoB;aAAqB;;WACzC;aAAuB;aAAuB;;WAC9C;aACE;aACA;aACA;aACA;;WAEF;aAAkC;aAAuB;;WACzD;;;iBAIM;WACN;WACA;EACT,KAAK,SAAS,wCAAwC;;;iBAIvC;WACN,iBAAiB,iBAAiB;WAClC,kBAAkB;;iBAGZ;;EAEf,IAAI,GAAG,eAAe;;iBAGP;WACN,WAAW;WACX,UAAU;WACV;IACP,QAAQ;IACR,OAAO,QAAQ,8BAA8B;;;EAG/C,eAAe;;WAEN,QAAQ;;EAEjB;;iBAGe;WACN;WACA,SACL,yBACE,SAAS,yBAAyB,uBAAuB,QAAQ;;;wBAIzD,gBAAgB,YAAY,0BAA0B;;wBAKtD,cAAc,oBAAoB,UAAU,SAAS,oBACnE,MAAM,iBAAiB,iBAAiB,SAAS,aAAa,iBAAiB,gBAC9E,iBAAiB,iBAAiB,SAAS,aAAa,iBAAiB;;wBAK5D,eAAe,oBAC7B,OAAO,iBAAiB,SAAS,aAAa,gBAC7C,iBAAiB,SAAS,aAAa"}
@@ -1,6 +1,28 @@
1
1
  import { DoomHeadlessExecutionContext, DoomHeadlessSelection, DoomHeadlessSelectionChange, DoomHeadlessTool } from "./headless.mjs";
2
2
  import { TSchema } from "typebox";
3
3
  //#region src/schemas/mcpFacet.d.ts
4
+ /** Repository-scoped instructions and applied selection safe to disclose to a remote MCP client. */
5
+ export interface DoomMcpContextSnapshot {
6
+ readonly session: {
7
+ readonly id: string;
8
+ readonly revision: number;
9
+ };
10
+ readonly repository: {
11
+ readonly root: string;
12
+ readonly cwd: string;
13
+ };
14
+ readonly selection: {
15
+ readonly profile: string | null;
16
+ readonly domains: readonly string[];
17
+ readonly majorMode: string;
18
+ readonly activeLayers: readonly string[];
19
+ };
20
+ readonly instructions: readonly {
21
+ readonly path: string;
22
+ readonly content: string;
23
+ }[];
24
+ readonly persona: string | null;
25
+ }
4
26
  /** A lazy Markdown resource available only to remote MCP clients. */
5
27
  export interface DoomMcpSkill {
6
28
  readonly name: string;
@@ -23,8 +45,12 @@ export interface DoomMcpPluginContext {
23
45
  read(): DoomHeadlessSelection;
24
46
  change(change: DoomHeadlessSelectionChange): Promise<void>;
25
47
  };
48
+ /** Reads the allowlisted repository context from the current applied session selection. */
49
+ loadContext(): DoomMcpContextSnapshot;
26
50
  /** Aborted when this plugin surface is replaced or the session closes. */
27
51
  readonly signal: AbortSignal;
52
+ /** Rebuilds the remote surface after a live MCP catalog changes. */
53
+ refresh(): void;
28
54
  }
29
55
  export interface DoomMcpPluginDefinition {
30
56
  readonly name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"mcpFacet.d.mts","names":[],"sources":["../../src/schemas/mcpFacet.ts"],"mappings":";;;;iBAUiB;WACN;WACA;EACT,KAAK,SAAS,wCAAwC;;;iBAIvC;WACN,iBAAiB,iBAAiB;WAClC,kBAAkB;;iBAGZ;;EAEf,IAAI,GAAG,eAAe;;iBAGP;WACN,WAAW;WACX,UAAU;WACV;IACP,QAAQ;IACR,OAAO,QAAQ,8BAA8B;;;WAGtC,QAAQ;;iBAGF;WACN;WACA,SACL,yBACE,SAAS,yBAAyB,uBAAuB,QAAQ;;;wBAIzD,gBAAgB,YAAY,0BAA0B;;wBAKtD,cAAc,oBAAoB,UAAU,SAAS,oBACnE,MAAM,iBAAiB,iBAAiB,SAAS,aAAa,iBAAiB,gBAC9E,iBAAiB,iBAAiB,SAAS,aAAa,iBAAiB;;wBAK5D,eAAe,oBAC7B,OAAO,iBAAiB,SAAS,aAAa,gBAC7C,iBAAiB,SAAS,aAAa"}
1
+ {"version":3,"file":"mcpFacet.d.mts","names":[],"sources":["../../src/schemas/mcpFacet.ts"],"mappings":";;;;iBAUiB;WACN;aAAoB;aAAqB;;WACzC;aAAuB;aAAuB;;WAC9C;aACE;aACA;aACA;aACA;;WAEF;aAAkC;aAAuB;;WACzD;;;iBAIM;WACN;WACA;EACT,KAAK,SAAS,wCAAwC;;;iBAIvC;WACN,iBAAiB,iBAAiB;WAClC,kBAAkB;;iBAGZ;;EAEf,IAAI,GAAG,eAAe;;iBAGP;WACN,WAAW;WACX,UAAU;WACV;IACP,QAAQ;IACR,OAAO,QAAQ,8BAA8B;;;EAG/C,eAAe;;WAEN,QAAQ;;EAEjB;;iBAGe;WACN;WACA,SACL,yBACE,SAAS,yBAAyB,uBAAuB,QAAQ;;;wBAIzD,gBAAgB,YAAY,0BAA0B;;wBAKtD,cAAc,oBAAoB,UAAU,SAAS,oBACnE,MAAM,iBAAiB,iBAAiB,SAAS,aAAa,iBAAiB,gBAC9E,iBAAiB,iBAAiB,SAAS,aAAa,iBAAiB;;wBAK5D,eAAe,oBAC7B,OAAO,iBAAiB,SAAS,aAAa,gBAC7C,iBAAiB,SAAS,aAAa"}
@@ -1 +1 @@
1
- {"version":3,"file":"mcpFacet.mjs","names":[],"sources":["../../src/schemas/mcpFacet.ts"],"sourcesContent":["import type { TSchema } from 'typebox';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessSelection,\n DoomHeadlessSelectionChange,\n DoomHeadlessTool,\n} from './headless';\n\n/** A lazy Markdown resource available only to remote MCP clients. */\nexport interface DoomMcpSkill {\n readonly name: string;\n readonly description: string;\n read(context: DoomHeadlessExecutionContext): string | Promise<string>;\n}\n\n/** A dedicated remote surface, deliberately separate from local agent registrations. */\nexport interface DoomMcpSessionPlugin {\n readonly tools?: readonly DoomHeadlessTool<TSchema>[];\n readonly skills?: readonly DoomMcpSkill[];\n}\n\nexport interface DoomMcpServiceScope {\n /** Reads a service already mounted in this session without exposing host registration APIs. */\n get<T>(name: string): T | undefined;\n}\n\nexport interface DoomMcpPluginContext {\n readonly execution: DoomHeadlessExecutionContext;\n readonly services: DoomMcpServiceScope;\n readonly selection: {\n read(): DoomHeadlessSelection;\n change(change: DoomHeadlessSelectionChange): Promise<void>;\n };\n /** Aborted when this plugin surface is replaced or the session closes. */\n readonly signal: AbortSignal;\n}\n\nexport interface DoomMcpPluginDefinition {\n readonly name: string;\n readonly session:\n | DoomMcpSessionPlugin\n | ((context: DoomMcpPluginContext) => DoomMcpSessionPlugin | Promise<DoomMcpSessionPlugin>);\n}\n\n/** Makes an MCP declaration's contract explicit without registering it locally. */\nexport function defineMcpPlugin(definition: DoomMcpPluginDefinition): DoomMcpPluginDefinition {\n return definition;\n}\n\n/** `tool/[name].mcp.ts`. Keeps the declaration independently typed. */\nexport function defineMcpTool<TParameters extends TSchema = TSchema, TContext = unknown>(\n tool: DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>),\n): DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>) {\n return tool;\n}\n\n/** `skill/[name].mcp.ts`. Keeps remote guidance out of the local prompt surface. */\nexport function defineMcpSkill<TContext = unknown>(\n skill: DoomMcpSkill | ((context: TContext) => DoomMcpSkill),\n): DoomMcpSkill | ((context: TContext) => DoomMcpSkill) {\n return skill;\n}\n"],"mappings":";;AA8CA,SAAgB,gBAAgB,YAA8D;CAC5F,OAAO;AACT;;AAGA,SAAgB,cACd,MACwF;CACxF,OAAO;AACT;;AAGA,SAAgB,eACd,OACsD;CACtD,OAAO;AACT"}
1
+ {"version":3,"file":"mcpFacet.mjs","names":[],"sources":["../../src/schemas/mcpFacet.ts"],"sourcesContent":["import type { TSchema } from 'typebox';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessSelection,\n DoomHeadlessSelectionChange,\n DoomHeadlessTool,\n} from './headless';\n\n/** Repository-scoped instructions and applied selection safe to disclose to a remote MCP client. */\nexport interface DoomMcpContextSnapshot {\n readonly session: { readonly id: string; readonly revision: number };\n readonly repository: { readonly root: string; readonly cwd: string };\n readonly selection: {\n readonly profile: string | null;\n readonly domains: readonly string[];\n readonly majorMode: string;\n readonly activeLayers: readonly string[];\n };\n readonly instructions: readonly { readonly path: string; readonly content: string }[];\n readonly persona: string | null;\n}\n\n/** A lazy Markdown resource available only to remote MCP clients. */\nexport interface DoomMcpSkill {\n readonly name: string;\n readonly description: string;\n read(context: DoomHeadlessExecutionContext): string | Promise<string>;\n}\n\n/** A dedicated remote surface, deliberately separate from local agent registrations. */\nexport interface DoomMcpSessionPlugin {\n readonly tools?: readonly DoomHeadlessTool<TSchema>[];\n readonly skills?: readonly DoomMcpSkill[];\n}\n\nexport interface DoomMcpServiceScope {\n /** Reads a service already mounted in this session without exposing host registration APIs. */\n get<T>(name: string): T | undefined;\n}\n\nexport interface DoomMcpPluginContext {\n readonly execution: DoomHeadlessExecutionContext;\n readonly services: DoomMcpServiceScope;\n readonly selection: {\n read(): DoomHeadlessSelection;\n change(change: DoomHeadlessSelectionChange): Promise<void>;\n };\n /** Reads the allowlisted repository context from the current applied session selection. */\n loadContext(): DoomMcpContextSnapshot;\n /** Aborted when this plugin surface is replaced or the session closes. */\n readonly signal: AbortSignal;\n /** Rebuilds the remote surface after a live MCP catalog changes. */\n refresh(): void;\n}\n\nexport interface DoomMcpPluginDefinition {\n readonly name: string;\n readonly session:\n | DoomMcpSessionPlugin\n | ((context: DoomMcpPluginContext) => DoomMcpSessionPlugin | Promise<DoomMcpSessionPlugin>);\n}\n\n/** Makes an MCP declaration's contract explicit without registering it locally. */\nexport function defineMcpPlugin(definition: DoomMcpPluginDefinition): DoomMcpPluginDefinition {\n return definition;\n}\n\n/** `tool/[name].mcp.ts`. Keeps the declaration independently typed. */\nexport function defineMcpTool<TParameters extends TSchema = TSchema, TContext = unknown>(\n tool: DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>),\n): DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>) {\n return tool;\n}\n\n/** `skill/[name].mcp.ts`. Keeps remote guidance out of the local prompt surface. */\nexport function defineMcpSkill<TContext = unknown>(\n skill: DoomMcpSkill | ((context: TContext) => DoomMcpSkill),\n): DoomMcpSkill | ((context: TContext) => DoomMcpSkill) {\n return skill;\n}\n"],"mappings":";;AAgEA,SAAgB,gBAAgB,YAA8D;CAC5F,OAAO;AACT;;AAGA,SAAgB,cACd,MACwF;CACxF,OAAO;AACT;;AAGA,SAAgB,eACd,OACsD;CACtD,OAAO;AACT"}
@@ -100,20 +100,48 @@ function createSessionMcpHttpHandler(options) {
100
100
  if (operations.has(key)) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidRequest, "A tool call with this request identity is already active.");
101
101
  const controller = new AbortController();
102
102
  operations.set(key, controller);
103
+ const signal = AbortSignal.any([
104
+ request.signal,
105
+ extra.signal,
106
+ controller.signal
107
+ ]);
108
+ let skillAccessOpen = true;
103
109
  try {
104
110
  const active = await authorizeOperation();
105
111
  if (active.grant.scope === "restricted" && !active.grant.tools.includes(message.params.name)) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidParams, `Tool '${message.params.name}' is not granted.`);
106
112
  const { snapshot, tools } = grantedSurface(active.grant, active.target.toolSurface);
107
113
  if (!tools.some((tool) => tool.name === message.params.name)) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidParams, `Tool '${message.params.name}' is not active.`);
114
+ const authorizedSkills = async () => {
115
+ if (!skillAccessOpen) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidRequest, "Remote skill access is no longer active.");
116
+ signal.throwIfAborted();
117
+ const current = await authorizeOperation();
118
+ if (current.target.toolSurface !== active.target.toolSurface) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidRequest, "The session tool surface has changed.");
119
+ signal.throwIfAborted();
120
+ return grantedSurface(current.grant, current.target.toolSurface);
121
+ };
108
122
  const result = await active.target.toolSurface.invokeTool({
109
123
  revision: snapshot.revision,
110
124
  name: message.params.name,
111
125
  arguments: message.params.arguments ?? {},
112
- signal: AbortSignal.any([
113
- request.signal,
114
- extra.signal,
115
- controller.signal
116
- ]),
126
+ signal,
127
+ mcpSkills: {
128
+ async list() {
129
+ const { skills } = await authorizedSkills();
130
+ return skills.map(({ name, description }) => ({
131
+ name,
132
+ description
133
+ }));
134
+ },
135
+ async read(name) {
136
+ const current = await authorizedSkills();
137
+ const skill = current.skills.find((candidate) => candidate.name === name);
138
+ if (skill === void 0) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidParams, "Skill is not granted or active.");
139
+ const text = await active.target.toolSurface.readSkill(current.snapshot.revision, skill.uri);
140
+ signal.throwIfAborted();
141
+ if ((await authorizedSkills()).snapshot.revision !== current.snapshot.revision) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidRequest, "The session skill surface has changed.");
142
+ return text;
143
+ }
144
+ },
117
145
  authorize: async () => {
118
146
  const current = await authorizeOperation();
119
147
  if (current.target.toolSurface !== active.target.toolSurface) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidRequest, "The session tool surface has changed.");
@@ -126,6 +154,7 @@ function createSessionMcpHttpHandler(options) {
126
154
  isError: result.isError ?? false
127
155
  };
128
156
  } finally {
157
+ skillAccessOpen = false;
129
158
  operations.delete(key);
130
159
  }
131
160
  });