@agimon-ai/doompi-core 0.0.1-alpha.79 → 0.0.1-alpha.80

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 (45) hide show
  1. package/dist/mcp-facet.cjs +1 -0
  2. package/dist/mcp-facet.d.cts +2 -2
  3. package/dist/mcp-facet.d.mts +2 -2
  4. package/dist/mcp-facet.mjs +2 -2
  5. package/dist/schemas/headless.cjs.map +1 -1
  6. package/dist/schemas/headless.d.cts +17 -2
  7. package/dist/schemas/headless.d.cts.map +1 -1
  8. package/dist/schemas/headless.d.mts +17 -2
  9. package/dist/schemas/headless.d.mts.map +1 -1
  10. package/dist/schemas/headless.mjs.map +1 -1
  11. package/dist/schemas/mcpFacet.cjs +5 -0
  12. package/dist/schemas/mcpFacet.cjs.map +1 -1
  13. package/dist/schemas/mcpFacet.d.cts +24 -0
  14. package/dist/schemas/mcpFacet.d.cts.map +1 -1
  15. package/dist/schemas/mcpFacet.d.mts +24 -0
  16. package/dist/schemas/mcpFacet.d.mts.map +1 -1
  17. package/dist/schemas/mcpFacet.mjs +5 -1
  18. package/dist/schemas/mcpFacet.mjs.map +1 -1
  19. package/dist/server/sessionMcpHandler.cjs +33 -9
  20. package/dist/server/sessionMcpHandler.cjs.map +1 -1
  21. package/dist/server/sessionMcpHandler.d.cts.map +1 -1
  22. package/dist/server/sessionMcpHandler.d.mts.map +1 -1
  23. package/dist/server/sessionMcpHandler.mjs +33 -9
  24. package/dist/server/sessionMcpHandler.mjs.map +1 -1
  25. package/dist/session-mcp.d.cts +2 -2
  26. package/dist/session-mcp.d.mts +2 -2
  27. package/dist/systems/main/adapters/headlessHost.cjs +6 -3
  28. package/dist/systems/main/adapters/headlessHost.cjs.map +1 -1
  29. package/dist/systems/main/adapters/headlessHost.d.cts +2 -0
  30. package/dist/systems/main/adapters/headlessHost.d.cts.map +1 -1
  31. package/dist/systems/main/adapters/headlessHost.d.mts +2 -0
  32. package/dist/systems/main/adapters/headlessHost.d.mts.map +1 -1
  33. package/dist/systems/main/adapters/headlessHost.mjs +6 -3
  34. package/dist/systems/main/adapters/headlessHost.mjs.map +1 -1
  35. package/dist/systems/main/adapters/headlessSessionHost.cjs +49 -4
  36. package/dist/systems/main/adapters/headlessSessionHost.cjs.map +1 -1
  37. package/dist/systems/main/adapters/headlessSessionHost.d.cts.map +1 -1
  38. package/dist/systems/main/adapters/headlessSessionHost.d.mts.map +1 -1
  39. package/dist/systems/main/adapters/headlessSessionHost.mjs +49 -4
  40. package/dist/systems/main/adapters/headlessSessionHost.mjs.map +1 -1
  41. package/dist/types/server/sessionToolSurface.d.cts +5 -0
  42. package/dist/types/server/sessionToolSurface.d.cts.map +1 -1
  43. package/dist/types/server/sessionToolSurface.d.mts +5 -0
  44. package/dist/types/server/sessionToolSurface.d.mts.map +1 -1
  45. package/package.json +11 -11
@@ -7,5 +7,6 @@ exports.DOOM_MCP_BUNDLE_VERSION = require_mcpBundle.DOOM_MCP_BUNDLE_VERSION;
7
7
  exports.defineMcpPlugin = require_mcpFacet.defineMcpPlugin;
8
8
  exports.defineMcpSkill = require_mcpFacet.defineMcpSkill;
9
9
  exports.defineMcpTool = require_mcpFacet.defineMcpTool;
10
+ exports.defineMcpUiResource = require_mcpFacet.defineMcpUiResource;
10
11
  exports.loadMcpBundle = require_mcpBundleLoader.loadMcpBundle;
11
12
  exports.parseDoomMcpBundle = require_mcpBundle.parseDoomMcpBundle;
@@ -1,4 +1,4 @@
1
- import { DoomMcpContextSnapshot, DoomMcpPluginContext, DoomMcpPluginDefinition, DoomMcpServiceScope, DoomMcpSessionPlugin, DoomMcpSkill, defineMcpPlugin, defineMcpSkill, defineMcpTool } from "./schemas/mcpFacet.cjs";
1
+ import { DoomMcpContextSnapshot, DoomMcpPluginContext, DoomMcpPluginDefinition, DoomMcpServiceScope, DoomMcpSessionPlugin, DoomMcpSkill, DoomMcpUiResource, defineMcpPlugin, defineMcpSkill, defineMcpTool, defineMcpUiResource } from "./schemas/mcpFacet.cjs";
2
2
  import { DOOM_MCP_BUNDLE_FILE, DOOM_MCP_BUNDLE_VERSION, DoomMcpBundle, DoomMcpBundleEntry, parseDoomMcpBundle } from "./schemas/mcpBundle.cjs";
3
3
  import { LoadMcpBundleOptions, LoadedMcpBundle, LoadedMcpPlugin, loadMcpBundle } from "./server/mcpBundleLoader.cjs";
4
- export { DOOM_MCP_BUNDLE_FILE, DOOM_MCP_BUNDLE_VERSION, type DoomMcpBundle, type DoomMcpBundleEntry, type DoomMcpContextSnapshot, type DoomMcpPluginContext, type DoomMcpPluginDefinition, type DoomMcpServiceScope, type DoomMcpSessionPlugin, type DoomMcpSkill, type LoadMcpBundleOptions, type LoadedMcpBundle, type LoadedMcpPlugin, defineMcpPlugin, defineMcpSkill, defineMcpTool, loadMcpBundle, parseDoomMcpBundle };
4
+ export { DOOM_MCP_BUNDLE_FILE, DOOM_MCP_BUNDLE_VERSION, type DoomMcpBundle, type DoomMcpBundleEntry, type DoomMcpContextSnapshot, type DoomMcpPluginContext, type DoomMcpPluginDefinition, type DoomMcpServiceScope, type DoomMcpSessionPlugin, type DoomMcpSkill, type DoomMcpUiResource, type LoadMcpBundleOptions, type LoadedMcpBundle, type LoadedMcpPlugin, defineMcpPlugin, defineMcpSkill, defineMcpTool, defineMcpUiResource, loadMcpBundle, parseDoomMcpBundle };
@@ -1,4 +1,4 @@
1
- import { DoomMcpContextSnapshot, DoomMcpPluginContext, DoomMcpPluginDefinition, DoomMcpServiceScope, DoomMcpSessionPlugin, DoomMcpSkill, defineMcpPlugin, defineMcpSkill, defineMcpTool } from "./schemas/mcpFacet.mjs";
1
+ import { DoomMcpContextSnapshot, DoomMcpPluginContext, DoomMcpPluginDefinition, DoomMcpServiceScope, DoomMcpSessionPlugin, DoomMcpSkill, DoomMcpUiResource, defineMcpPlugin, defineMcpSkill, defineMcpTool, defineMcpUiResource } from "./schemas/mcpFacet.mjs";
2
2
  import { DOOM_MCP_BUNDLE_FILE, DOOM_MCP_BUNDLE_VERSION, DoomMcpBundle, DoomMcpBundleEntry, parseDoomMcpBundle } from "./schemas/mcpBundle.mjs";
3
3
  import { LoadMcpBundleOptions, LoadedMcpBundle, LoadedMcpPlugin, loadMcpBundle } from "./server/mcpBundleLoader.mjs";
4
- export { DOOM_MCP_BUNDLE_FILE, DOOM_MCP_BUNDLE_VERSION, type DoomMcpBundle, type DoomMcpBundleEntry, type DoomMcpContextSnapshot, type DoomMcpPluginContext, type DoomMcpPluginDefinition, type DoomMcpServiceScope, type DoomMcpSessionPlugin, type DoomMcpSkill, type LoadMcpBundleOptions, type LoadedMcpBundle, type LoadedMcpPlugin, defineMcpPlugin, defineMcpSkill, defineMcpTool, loadMcpBundle, parseDoomMcpBundle };
4
+ export { DOOM_MCP_BUNDLE_FILE, DOOM_MCP_BUNDLE_VERSION, type DoomMcpBundle, type DoomMcpBundleEntry, type DoomMcpContextSnapshot, type DoomMcpPluginContext, type DoomMcpPluginDefinition, type DoomMcpServiceScope, type DoomMcpSessionPlugin, type DoomMcpSkill, type DoomMcpUiResource, type LoadMcpBundleOptions, type LoadedMcpBundle, type LoadedMcpPlugin, defineMcpPlugin, defineMcpSkill, defineMcpTool, defineMcpUiResource, loadMcpBundle, parseDoomMcpBundle };
@@ -1,4 +1,4 @@
1
- import { defineMcpPlugin, defineMcpSkill, defineMcpTool } from "./schemas/mcpFacet.mjs";
1
+ import { defineMcpPlugin, defineMcpSkill, defineMcpTool, defineMcpUiResource } from "./schemas/mcpFacet.mjs";
2
2
  import { DOOM_MCP_BUNDLE_FILE, DOOM_MCP_BUNDLE_VERSION, parseDoomMcpBundle } from "./schemas/mcpBundle.mjs";
3
3
  import { loadMcpBundle } from "./server/mcpBundleLoader.mjs";
4
- export { DOOM_MCP_BUNDLE_FILE, DOOM_MCP_BUNDLE_VERSION, defineMcpPlugin, defineMcpSkill, defineMcpTool, loadMcpBundle, parseDoomMcpBundle };
4
+ export { DOOM_MCP_BUNDLE_FILE, DOOM_MCP_BUNDLE_VERSION, defineMcpPlugin, defineMcpSkill, defineMcpTool, defineMcpUiResource, loadMcpBundle, parseDoomMcpBundle };
@@ -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 { Tool } from '@modelcontextprotocol/sdk/types.js';\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 /** Public MCP result data. Internal renderer details are not a remote result contract. */\n structuredContent?: Record<string, unknown>;\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 annotations?: Tool['annotations'];\n outputSchema?: Tool['outputSchema'];\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":";;AASA,MAAa,6BAA6B;AAC1C,MAAa,sBAAsB,OAAO,IAAI,qBAAqB;AAkBnE,SAAgB,sBAAsB,SAAqD;CACzF,OAAQ,QAAwE;AAClF;AA0QA,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 { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js';\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 /** Public MCP result data. Internal renderer details are not a remote result contract. */\n structuredContent?: Record<string, unknown>;\n /** Component-only MCP metadata, never inferred from internal renderer details. */\n _meta?: CallToolResult['_meta'];\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 /** Optional capability ceiling. An empty list disables every tool. */\n allowedTools?: readonly string[];\n /**\n * Narrow exclusions without disabling unrelated tools. Exclude a tool your package owns, or a\n * specific foreign tool the mode exists to withdraw. Never enumerate an allowlist here.\n */\n excludedTools?: 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 annotations?: Tool['annotations'];\n outputSchema?: Tool['outputSchema'];\n /** Remote presentation only. Component tool access must be explicitly opted into. */\n _meta?: Tool['_meta'] & {\n ui?: {\n resourceUri?: string;\n visibility?: ('model' | 'app')[];\n };\n };\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":";;AASA,MAAa,6BAA6B;AAC1C,MAAa,sBAAsB,OAAO,IAAI,qBAAqB;AAkBnE,SAAgB,sBAAsB,SAAqD;CACzF,OAAQ,QAAwE;AAClF;AAyRA,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"}
@@ -3,7 +3,7 @@ import { DoomNotificationRequest } from "./notification.cjs";
3
3
  import { Static, TSchema } from "typebox";
4
4
  import { Context } from "@deepseek-ai/cordis";
5
5
  import { ThinkingLevel } from "@earendil-works/pi-agent-core";
6
- import { Tool } from "@modelcontextprotocol/sdk/types.js";
6
+ import { CallToolResult, Tool } from "@modelcontextprotocol/sdk/types.js";
7
7
  //#region src/schemas/headless.d.ts
8
8
  /** Session-only contributions. This is not a Pi ExtensionAPI or a TUI adapter. */
9
9
  export declare const DOOM_HEADLESS_HOST_SERVICE = "doom/headless-host";
@@ -47,6 +47,8 @@ export interface DoomHeadlessToolResult {
47
47
  content: DoomHeadlessContent[];
48
48
  /** Public MCP result data. Internal renderer details are not a remote result contract. */
49
49
  structuredContent?: Record<string, unknown>;
50
+ /** Component-only MCP metadata, never inferred from internal renderer details. */
51
+ _meta?: CallToolResult['_meta'];
50
52
  details?: unknown;
51
53
  isError?: boolean;
52
54
  }
@@ -163,7 +165,13 @@ export interface DoomHeadlessCondition {
163
165
  }
164
166
  export interface DoomHeadlessToolRestriction {
165
167
  when?: DoomHeadlessCondition;
166
- allowedTools: readonly string[];
168
+ /** Optional capability ceiling. An empty list disables every tool. */
169
+ allowedTools?: readonly string[];
170
+ /**
171
+ * Narrow exclusions without disabling unrelated tools. Exclude a tool your package owns, or a
172
+ * specific foreign tool the mode exists to withdraw. Never enumerate an allowlist here.
173
+ */
174
+ excludedTools?: readonly string[];
167
175
  }
168
176
  export interface DoomHeadlessTool<TParameters extends TSchema = TSchema> {
169
177
  when?: DoomHeadlessCondition;
@@ -173,6 +181,13 @@ export interface DoomHeadlessTool<TParameters extends TSchema = TSchema> {
173
181
  parameters: TParameters;
174
182
  annotations?: Tool['annotations'];
175
183
  outputSchema?: Tool['outputSchema'];
184
+ /** Remote presentation only. Component tool access must be explicitly opted into. */
185
+ _meta?: Tool['_meta'] & {
186
+ ui?: {
187
+ resourceUri?: string;
188
+ visibility?: ('model' | 'app')[];
189
+ };
190
+ };
176
191
  promptSnippet?: string;
177
192
  promptGuidelines?: readonly string[];
178
193
  executionMode?: 'parallel' | 'serial';
@@ -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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"}
@@ -2,7 +2,7 @@ import { DoomServerBundleEntry } from "./serverBundle.mjs";
2
2
  import { DoomNotificationRequest } from "./notification.mjs";
3
3
  import { Static, TSchema } from "typebox";
4
4
  import { Context } from "@deepseek-ai/cordis";
5
- import { Tool } from "@modelcontextprotocol/sdk/types.js";
5
+ import { CallToolResult, Tool } from "@modelcontextprotocol/sdk/types.js";
6
6
  import { ThinkingLevel } from "@earendil-works/pi-agent-core";
7
7
  //#region src/schemas/headless.d.ts
8
8
  /** Session-only contributions. This is not a Pi ExtensionAPI or a TUI adapter. */
@@ -47,6 +47,8 @@ export interface DoomHeadlessToolResult {
47
47
  content: DoomHeadlessContent[];
48
48
  /** Public MCP result data. Internal renderer details are not a remote result contract. */
49
49
  structuredContent?: Record<string, unknown>;
50
+ /** Component-only MCP metadata, never inferred from internal renderer details. */
51
+ _meta?: CallToolResult['_meta'];
50
52
  details?: unknown;
51
53
  isError?: boolean;
52
54
  }
@@ -163,7 +165,13 @@ export interface DoomHeadlessCondition {
163
165
  }
164
166
  export interface DoomHeadlessToolRestriction {
165
167
  when?: DoomHeadlessCondition;
166
- allowedTools: readonly string[];
168
+ /** Optional capability ceiling. An empty list disables every tool. */
169
+ allowedTools?: readonly string[];
170
+ /**
171
+ * Narrow exclusions without disabling unrelated tools. Exclude a tool your package owns, or a
172
+ * specific foreign tool the mode exists to withdraw. Never enumerate an allowlist here.
173
+ */
174
+ excludedTools?: readonly string[];
167
175
  }
168
176
  export interface DoomHeadlessTool<TParameters extends TSchema = TSchema> {
169
177
  when?: DoomHeadlessCondition;
@@ -173,6 +181,13 @@ export interface DoomHeadlessTool<TParameters extends TSchema = TSchema> {
173
181
  parameters: TParameters;
174
182
  annotations?: Tool['annotations'];
175
183
  outputSchema?: Tool['outputSchema'];
184
+ /** Remote presentation only. Component tool access must be explicitly opted into. */
185
+ _meta?: Tool['_meta'] & {
186
+ ui?: {
187
+ resourceUri?: string;
188
+ visibility?: ('model' | 'app')[];
189
+ };
190
+ };
176
191
  promptSnippet?: string;
177
192
  promptGuidelines?: readonly string[];
178
193
  executionMode?: 'parallel' | 'serial';
@@ -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 { Tool } from '@modelcontextprotocol/sdk/types.js';\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 /** Public MCP result data. Internal renderer details are not a remote result contract. */\n structuredContent?: Record<string, unknown>;\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 annotations?: Tool['annotations'];\n outputSchema?: Tool['outputSchema'];\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":";;AASA,MAAa,6BAA6B;AAC1C,MAAa,sBAAsB,OAAO,IAAI,qBAAqB;AAkBnE,SAAgB,sBAAsB,SAAqD;CACzF,OAAQ,QAAwE;AAClF;AA0QA,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 { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js';\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 /** Public MCP result data. Internal renderer details are not a remote result contract. */\n structuredContent?: Record<string, unknown>;\n /** Component-only MCP metadata, never inferred from internal renderer details. */\n _meta?: CallToolResult['_meta'];\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 /** Optional capability ceiling. An empty list disables every tool. */\n allowedTools?: readonly string[];\n /**\n * Narrow exclusions without disabling unrelated tools. Exclude a tool your package owns, or a\n * specific foreign tool the mode exists to withdraw. Never enumerate an allowlist here.\n */\n excludedTools?: 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 annotations?: Tool['annotations'];\n outputSchema?: Tool['outputSchema'];\n /** Remote presentation only. Component tool access must be explicitly opted into. */\n _meta?: Tool['_meta'] & {\n ui?: {\n resourceUri?: string;\n visibility?: ('model' | 'app')[];\n };\n };\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":";;AASA,MAAa,6BAA6B;AAC1C,MAAa,sBAAsB,OAAO,IAAI,qBAAqB;AAkBnE,SAAgB,sBAAsB,SAAqD;CACzF,OAAQ,QAAwE;AAClF;AAyRA,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"}
@@ -11,9 +11,14 @@ function defineMcpTool(tool) {
11
11
  function defineMcpSkill(skill) {
12
12
  return skill;
13
13
  }
14
+ /** `resource/[name].mcp.ts`. Static UI resources stay separate from session guidance. */
15
+ function defineMcpUiResource(resource) {
16
+ return resource;
17
+ }
14
18
  //#endregion
15
19
  exports.defineMcpPlugin = defineMcpPlugin;
16
20
  exports.defineMcpSkill = defineMcpSkill;
17
21
  exports.defineMcpTool = defineMcpTool;
22
+ exports.defineMcpUiResource = defineMcpUiResource;
18
23
 
19
24
  //# sourceMappingURL=mcpFacet.cjs.map
@@ -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/** 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 readonly minorModes?: 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":";;AAiEA,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 readonly minorModes?: 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 package-owned, immutable UI template. Session data belongs in tool results, never in this HTML. */\nexport interface DoomMcpUiResource {\n readonly uri: string;\n readonly name: string;\n readonly description?: string;\n readonly mimeType: 'text/html;profile=mcp-app';\n readonly _meta?: Record<string, unknown> & {\n ui?: {\n csp?: {\n connectDomains?: string[];\n resourceDomains?: string[];\n frameDomains?: string[];\n baseUriDomains?: string[];\n };\n domain?: string;\n prefersBorder?: boolean;\n };\n };\n /** Loads static packaged HTML without an execution context. */\n read(): 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 readonly uiResources?: readonly DoomMcpUiResource[];\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\n/** `resource/[name].mcp.ts`. Static UI resources stay separate from session guidance. */\nexport function defineMcpUiResource(resource: DoomMcpUiResource): DoomMcpUiResource {\n return resource;\n}\n"],"mappings":";;AAwFA,SAAgB,gBAAgB,YAA8D;CAC5F,OAAO;AACT;;AAGA,SAAgB,cACd,MACwF;CACxF,OAAO;AACT;;AAGA,SAAgB,eACd,OACsD;CACtD,OAAO;AACT;;AAGA,SAAgB,oBAAoB,UAAgD;CAClF,OAAO;AACT"}
@@ -30,10 +30,32 @@ export interface DoomMcpSkill {
30
30
  readonly description: string;
31
31
  read(context: DoomHeadlessExecutionContext): string | Promise<string>;
32
32
  }
33
+ /** A package-owned, immutable UI template. Session data belongs in tool results, never in this HTML. */
34
+ export interface DoomMcpUiResource {
35
+ readonly uri: string;
36
+ readonly name: string;
37
+ readonly description?: string;
38
+ readonly mimeType: 'text/html;profile=mcp-app';
39
+ readonly _meta?: Record<string, unknown> & {
40
+ ui?: {
41
+ csp?: {
42
+ connectDomains?: string[];
43
+ resourceDomains?: string[];
44
+ frameDomains?: string[];
45
+ baseUriDomains?: string[];
46
+ };
47
+ domain?: string;
48
+ prefersBorder?: boolean;
49
+ };
50
+ };
51
+ /** Loads static packaged HTML without an execution context. */
52
+ read(): string | Promise<string>;
53
+ }
33
54
  /** A dedicated remote surface, deliberately separate from local agent registrations. */
34
55
  export interface DoomMcpSessionPlugin {
35
56
  readonly tools?: readonly DoomHeadlessTool<TSchema>[];
36
57
  readonly skills?: readonly DoomMcpSkill[];
58
+ readonly uiResources?: readonly DoomMcpUiResource[];
37
59
  }
38
60
  export interface DoomMcpServiceScope {
39
61
  /** Reads a service already mounted in this session without exposing host registration APIs. */
@@ -63,5 +85,7 @@ export declare function defineMcpPlugin(definition: DoomMcpPluginDefinition): Do
63
85
  export declare function defineMcpTool<TParameters extends TSchema = TSchema, TContext = unknown>(tool: DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>)): DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>);
64
86
  /** `skill/[name].mcp.ts`. Keeps remote guidance out of the local prompt surface. */
65
87
  export declare function defineMcpSkill<TContext = unknown>(skill: DoomMcpSkill | ((context: TContext) => DoomMcpSkill)): DoomMcpSkill | ((context: TContext) => DoomMcpSkill);
88
+ /** `resource/[name].mcp.ts`. Static UI resources stay separate from session guidance. */
89
+ export declare function defineMcpUiResource(resource: DoomMcpUiResource): DoomMcpUiResource;
66
90
  //#endregion
67
91
  //# sourceMappingURL=mcpFacet.d.cts.map
@@ -1 +1 @@
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;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
+ {"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;aACA;;WAEF;aAAkC;aAAuB;;WACzD;;;iBAIM;WACN;WACA;EACT,KAAK,SAAS,wCAAwC;;;iBAIvC;WACN;WACA;WACA;WACA;WACA,QAAQ;IACf;MACE;QACE;QACA;QACA;QACA;;MAEF;MACA;;;;EAIJ,iBAAiB;;;iBAIF;WACN,iBAAiB,iBAAiB;WAClC,kBAAkB;WAClB,uBAAuB;;iBAGjB;;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;;wBAK1B,oBAAoB,UAAU,oBAAoB"}
@@ -30,10 +30,32 @@ export interface DoomMcpSkill {
30
30
  readonly description: string;
31
31
  read(context: DoomHeadlessExecutionContext): string | Promise<string>;
32
32
  }
33
+ /** A package-owned, immutable UI template. Session data belongs in tool results, never in this HTML. */
34
+ export interface DoomMcpUiResource {
35
+ readonly uri: string;
36
+ readonly name: string;
37
+ readonly description?: string;
38
+ readonly mimeType: 'text/html;profile=mcp-app';
39
+ readonly _meta?: Record<string, unknown> & {
40
+ ui?: {
41
+ csp?: {
42
+ connectDomains?: string[];
43
+ resourceDomains?: string[];
44
+ frameDomains?: string[];
45
+ baseUriDomains?: string[];
46
+ };
47
+ domain?: string;
48
+ prefersBorder?: boolean;
49
+ };
50
+ };
51
+ /** Loads static packaged HTML without an execution context. */
52
+ read(): string | Promise<string>;
53
+ }
33
54
  /** A dedicated remote surface, deliberately separate from local agent registrations. */
34
55
  export interface DoomMcpSessionPlugin {
35
56
  readonly tools?: readonly DoomHeadlessTool<TSchema>[];
36
57
  readonly skills?: readonly DoomMcpSkill[];
58
+ readonly uiResources?: readonly DoomMcpUiResource[];
37
59
  }
38
60
  export interface DoomMcpServiceScope {
39
61
  /** Reads a service already mounted in this session without exposing host registration APIs. */
@@ -63,5 +85,7 @@ export declare function defineMcpPlugin(definition: DoomMcpPluginDefinition): Do
63
85
  export declare function defineMcpTool<TParameters extends TSchema = TSchema, TContext = unknown>(tool: DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>)): DoomHeadlessTool<TParameters> | ((context: TContext) => DoomHeadlessTool<TParameters>);
64
86
  /** `skill/[name].mcp.ts`. Keeps remote guidance out of the local prompt surface. */
65
87
  export declare function defineMcpSkill<TContext = unknown>(skill: DoomMcpSkill | ((context: TContext) => DoomMcpSkill)): DoomMcpSkill | ((context: TContext) => DoomMcpSkill);
88
+ /** `resource/[name].mcp.ts`. Static UI resources stay separate from session guidance. */
89
+ export declare function defineMcpUiResource(resource: DoomMcpUiResource): DoomMcpUiResource;
66
90
  //#endregion
67
91
  //# sourceMappingURL=mcpFacet.d.mts.map
@@ -1 +1 @@
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;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
+ {"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;aACA;;WAEF;aAAkC;aAAuB;;WACzD;;;iBAIM;WACN;WACA;EACT,KAAK,SAAS,wCAAwC;;;iBAIvC;WACN;WACA;WACA;WACA;WACA,QAAQ;IACf;MACE;QACE;QACA;QACA;QACA;;MAEF;MACA;;;;EAIJ,iBAAiB;;;iBAIF;WACN,iBAAiB,iBAAiB;WAClC,kBAAkB;WAClB,uBAAuB;;iBAGjB;;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;;wBAK1B,oBAAoB,UAAU,oBAAoB"}
@@ -11,7 +11,11 @@ function defineMcpTool(tool) {
11
11
  function defineMcpSkill(skill) {
12
12
  return skill;
13
13
  }
14
+ /** `resource/[name].mcp.ts`. Static UI resources stay separate from session guidance. */
15
+ function defineMcpUiResource(resource) {
16
+ return resource;
17
+ }
14
18
  //#endregion
15
- export { defineMcpPlugin, defineMcpSkill, defineMcpTool };
19
+ export { defineMcpPlugin, defineMcpSkill, defineMcpTool, defineMcpUiResource };
16
20
 
17
21
  //# sourceMappingURL=mcpFacet.mjs.map
@@ -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/** 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 readonly minorModes?: 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":";;AAiEA,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 readonly minorModes?: 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 package-owned, immutable UI template. Session data belongs in tool results, never in this HTML. */\nexport interface DoomMcpUiResource {\n readonly uri: string;\n readonly name: string;\n readonly description?: string;\n readonly mimeType: 'text/html;profile=mcp-app';\n readonly _meta?: Record<string, unknown> & {\n ui?: {\n csp?: {\n connectDomains?: string[];\n resourceDomains?: string[];\n frameDomains?: string[];\n baseUriDomains?: string[];\n };\n domain?: string;\n prefersBorder?: boolean;\n };\n };\n /** Loads static packaged HTML without an execution context. */\n read(): 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 readonly uiResources?: readonly DoomMcpUiResource[];\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\n/** `resource/[name].mcp.ts`. Static UI resources stay separate from session guidance. */\nexport function defineMcpUiResource(resource: DoomMcpUiResource): DoomMcpUiResource {\n return resource;\n}\n"],"mappings":";;AAwFA,SAAgB,gBAAgB,YAA8D;CAC5F,OAAO;AACT;;AAGA,SAAgB,cACd,MACwF;CACxF,OAAO;AACT;;AAGA,SAAgB,eACd,OACsD;CACtD,OAAO;AACT;;AAGA,SAAgB,oBAAoB,UAAgD;CAClF,OAAO;AACT"}
@@ -37,10 +37,13 @@ function grantedSurface(grant, surface) {
37
37
  const snapshot = surface.readSurface();
38
38
  const toolNames = new Set(grant.tools);
39
39
  const skillNames = new Set(grant.skills);
40
+ const tools = grant.scope === "session" ? snapshot.tools : snapshot.tools.filter((tool) => toolNames.has(tool.name));
41
+ const resourceUris = new Set(tools.map((tool) => tool._meta?.ui?.resourceUri));
40
42
  return {
41
43
  snapshot,
42
- tools: grant.scope === "session" ? snapshot.tools : snapshot.tools.filter((tool) => toolNames.has(tool.name)),
43
- skills: grant.scope === "session" ? snapshot.skills : snapshot.skills.filter((skill) => skillNames.has(skill.name))
44
+ tools,
45
+ skills: grant.scope === "session" ? snapshot.skills : snapshot.skills.filter((skill) => skillNames.has(skill.name)),
46
+ uiResources: (snapshot.uiResources ?? []).filter((resource) => resourceUris.has(resource.uri))
44
47
  };
45
48
  }
46
49
  /** Creates a Bearer-only, stateless Streamable HTTP MCP request handler for session capabilities. */
@@ -108,7 +111,14 @@ function createSessionMcpHttpHandler(options) {
108
111
  description: tool.description,
109
112
  inputSchema: tool.parameters,
110
113
  ...tool.annotations === void 0 ? {} : { annotations: tool.annotations },
111
- ...tool.outputSchema === void 0 ? {} : { outputSchema: tool.outputSchema }
114
+ ...tool.outputSchema === void 0 ? {} : { outputSchema: tool.outputSchema },
115
+ _meta: {
116
+ ...tool._meta,
117
+ ui: {
118
+ ...tool._meta?.ui,
119
+ visibility: tool._meta?.ui?.visibility ?? ["model"]
120
+ }
121
+ }
112
122
  })) };
113
123
  });
114
124
  server.setRequestHandler(_modelcontextprotocol_sdk_types_js.CallToolRequestSchema, async (message, extra) => {
@@ -152,7 +162,7 @@ function createSessionMcpHttpHandler(options) {
152
162
  const active = await resolveTarget(true);
153
163
  const { snapshot, tools } = grantedSurface(active.grant, active.target.toolSurface);
154
164
  const selected = tools.find((tool) => tool.name === message.params.name);
155
- if (!selected || !(0, node_util.isDeepStrictEqual)(selected.parameters, advertised.parameters) || !(0, node_util.isDeepStrictEqual)(selected.outputSchema, advertised.outputSchema) || !(0, node_util.isDeepStrictEqual)(selected.annotations, advertised.annotations)) throw new require_index.SessionMcpConversationError("SESSION_TOOL_SURFACE_CHANGED", "The target session does not expose the approved tool contract. Refresh the connection or restore a compatible session selection.");
165
+ if (!selected || !(0, node_util.isDeepStrictEqual)(selected.parameters, advertised.parameters) || !(0, node_util.isDeepStrictEqual)(selected.outputSchema, advertised.outputSchema) || !(0, node_util.isDeepStrictEqual)(selected.annotations, advertised.annotations) || !(0, node_util.isDeepStrictEqual)(selected._meta, advertised._meta)) throw new require_index.SessionMcpConversationError("SESSION_TOOL_SURFACE_CHANGED", "The target session does not expose the approved tool contract. Refresh the connection or restore a compatible session selection.");
156
166
  const recheck = async () => {
157
167
  const current = await resolveTarget(false);
158
168
  if (current.target.toolSurface !== active.target.toolSurface || current.target.generation !== active.target.generation || current.target.sessionId !== active.target.sessionId) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidRequest, "The session tool surface has changed.");
@@ -198,6 +208,7 @@ function createSessionMcpHttpHandler(options) {
198
208
  return {
199
209
  content: result.content,
200
210
  ...result.structuredContent === void 0 ? {} : { structuredContent: result.structuredContent },
211
+ ...result._meta === void 0 ? {} : { _meta: result._meta },
201
212
  isError: result.isError ?? false
202
213
  };
203
214
  } catch (error) {
@@ -221,19 +232,32 @@ function createSessionMcpHttpHandler(options) {
221
232
  });
222
233
  server.setRequestHandler(_modelcontextprotocol_sdk_types_js.ListResourcesRequestSchema, async () => {
223
234
  const active = await authorizeOperation();
224
- if (active.grant.routing === "conversation") return { resources: [] };
225
- const { skills } = grantedSurface(active.grant, active.target.toolSurface);
226
- return { resources: skills.map((skill) => ({
235
+ const { skills, uiResources } = grantedSurface(active.grant, active.target.toolSurface);
236
+ return { resources: [...uiResources, ...active.grant.routing === "conversation" ? [] : skills.map((skill) => ({
227
237
  uri: skill.uri,
228
238
  name: skill.name,
229
239
  description: skill.description,
230
240
  mimeType: "text/markdown"
231
- })) };
241
+ }))] };
232
242
  });
233
243
  server.setRequestHandler(_modelcontextprotocol_sdk_types_js.ReadResourceRequestSchema, async (message) => {
234
244
  const active = await authorizeOperation();
245
+ const { snapshot, skills, uiResources } = grantedSurface(active.grant, active.target.toolSurface);
246
+ const resource = uiResources.find((candidate) => candidate.uri === message.params.uri);
247
+ if (resource !== void 0 && active.target.toolSurface.readUiResource !== void 0) {
248
+ request.signal.throwIfAborted();
249
+ const text = await active.target.toolSurface.readUiResource(snapshot.revision, resource.uri);
250
+ request.signal.throwIfAborted();
251
+ const current = await authorizeOperation();
252
+ const after = grantedSurface(current.grant, current.target.toolSurface);
253
+ if (current.target.toolSurface !== active.target.toolSurface || after.snapshot.revision !== snapshot.revision || !(0, node_util.isDeepStrictEqual)(after.uiResources.find((candidate) => candidate.uri === resource.uri), resource)) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidRequest, "The session UI resource surface has changed.");
254
+ return { contents: [{
255
+ ...resource,
256
+ text
257
+ }] };
258
+ }
259
+ if (message.params.uri.startsWith("ui:")) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidParams, "UI resource is not granted or active.");
235
260
  if (active.grant.routing === "conversation") throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidParams, "Use load_skill with conversation metadata to read target-session guidance.");
236
- const { snapshot, skills } = grantedSurface(active.grant, active.target.toolSurface);
237
261
  const skill = skills.find((candidate) => candidate.uri === message.params.uri);
238
262
  if (skill === void 0) throw new _modelcontextprotocol_sdk_types_js.McpError(_modelcontextprotocol_sdk_types_js.ErrorCode.InvalidParams, "Skill resource is not granted or active.");
239
263
  const text = await active.target.toolSurface.readSkill(snapshot.revision, skill.uri);