@asm-agent/coding-agent 0.8.8 → 0.9.1

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 (182) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +2 -7
  3. package/dist/asm-agent-runtime/src/__pycache__/asm_cm_driver.cpython-313.pyc +0 -0
  4. package/dist/asm-agent-runtime/src/__pycache__/phase76_address_head.cpython-313.pyc +0 -0
  5. package/dist/asm-agent-runtime/src/rlm/__init__.py +1 -1
  6. package/dist/asm-agent-runtime/src/rlm/__pycache__/__init__.cpython-312.pyc +0 -0
  7. package/dist/asm-agent-runtime/src/rlm/__pycache__/__init__.cpython-313.pyc +0 -0
  8. package/dist/asm-agent-runtime/src/rlm/__pycache__/bash.cpython-312.pyc +0 -0
  9. package/dist/asm-agent-runtime/src/rlm/__pycache__/bash.cpython-313.pyc +0 -0
  10. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp.cpython-312.pyc +0 -0
  11. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp_base.cpython-312.pyc +0 -0
  12. package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-312.pyc +0 -0
  13. package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-313.pyc +0 -0
  14. package/dist/asm-agent-runtime/src/rlm/__pycache__/skill.cpython-313.pyc +0 -0
  15. package/dist/asm-agent-runtime/src/rlm/bash.py +94 -31
  16. package/dist/asm-agent-runtime/src/rlm/repl.md +3 -3
  17. package/dist/asm-agent-runtime/src/rlm/repl.py +46 -45
  18. package/dist/asm-agent-runtime/test/__pycache__/test_bash.cpython-313.pyc +0 -0
  19. package/dist/asm-agent-runtime/test/__pycache__/test_repl.cpython-313.pyc +0 -0
  20. package/dist/asm-agent-runtime/test/test_bash.py +74 -19
  21. package/dist/asm-agent-runtime/test/test_repl.py +67 -3
  22. package/dist/asm-agent-runtime/uv.lock +446 -155
  23. package/dist/bundle/{anthropic-OFECORE7.js → anthropic-2VRH36LG.js} +2 -2
  24. package/dist/bundle/{azure-openai-responses-AOVTLR6N.js → azure-openai-responses-COYWHJPT.js} +2 -2
  25. package/dist/bundle/{bundled-modules-JQCJDEIY.js → bundled-modules-YKMFOFDW.js} +3 -3
  26. package/dist/bundle/{chunk-JC2TT4MZ.js → chunk-JBR32RCG.js} +4943 -1917
  27. package/dist/bundle/{chunk-M67RGWDQ.js → chunk-US4QGESM.js} +43 -44
  28. package/dist/bundle/{chunk-A65OM3TZ.js → chunk-V3BASDCN.js} +1 -1
  29. package/dist/bundle/{chunk-7TB7CGPV.js → chunk-YL37EJB6.js} +978 -1821
  30. package/dist/bundle/{cli-main-FNHDQGQD.js → cli-main-D7CFAHKG.js} +3 -3
  31. package/dist/bundle/cli.js +1 -1
  32. package/dist/bundle/{google-RVJL6RIQ.js → google-CSTXROSV.js} +1 -1
  33. package/dist/bundle/{google-vertex-EZTRRRH6.js → google-vertex-MEAR43KB.js} +1 -1
  34. package/dist/bundle/{main-IYXIIAJN.js → main-NKNJEUDP.js} +3 -3
  35. package/dist/bundle/{mistral-FSGKSMA7.js → mistral-VAF7JADB.js} +1 -1
  36. package/dist/bundle/{openai-codex-responses-VM5Y564D.js → openai-codex-responses-RWZ6P4I3.js} +2 -2
  37. package/dist/bundle/{openai-completions-X46FOQDN.js → openai-completions-CFB3BOTU.js} +3 -3
  38. package/dist/bundle/{openai-responses-SOQNEPBE.js → openai-responses-4OPGGCBI.js} +2 -2
  39. package/dist/config.d.ts +0 -1
  40. package/dist/config.d.ts.map +1 -1
  41. package/dist/config.js +0 -3
  42. package/dist/config.js.map +1 -1
  43. package/dist/core/agent-session-config.d.ts +1 -2
  44. package/dist/core/agent-session-config.d.ts.map +1 -1
  45. package/dist/core/agent-session-config.js +0 -3
  46. package/dist/core/agent-session-config.js.map +1 -1
  47. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  48. package/dist/core/agent-session-runtime.js +0 -8
  49. package/dist/core/agent-session-runtime.js.map +1 -1
  50. package/dist/core/agent-session-services.d.ts +0 -2
  51. package/dist/core/agent-session-services.d.ts.map +1 -1
  52. package/dist/core/agent-session-services.js +0 -22
  53. package/dist/core/agent-session-services.js.map +1 -1
  54. package/dist/core/agent-session.d.ts +8 -1
  55. package/dist/core/agent-session.d.ts.map +1 -1
  56. package/dist/core/agent-session.js +100 -22
  57. package/dist/core/agent-session.js.map +1 -1
  58. package/dist/core/kernel/bootstrap.d.ts.map +1 -1
  59. package/dist/core/kernel/bootstrap.js +1 -1
  60. package/dist/core/kernel/bootstrap.js.map +1 -1
  61. package/dist/core/kernel/repl-manager.d.ts +32 -8
  62. package/dist/core/kernel/repl-manager.d.ts.map +1 -1
  63. package/dist/core/kernel/repl-manager.js +401 -102
  64. package/dist/core/kernel/repl-manager.js.map +1 -1
  65. package/dist/core/kernel/shared.d.ts +11 -7
  66. package/dist/core/kernel/shared.d.ts.map +1 -1
  67. package/dist/core/kernel/shared.js +2 -5
  68. package/dist/core/kernel/shared.js.map +1 -1
  69. package/dist/core/mcp/mcp-manager.d.ts +4 -2
  70. package/dist/core/mcp/mcp-manager.d.ts.map +1 -1
  71. package/dist/core/mcp/mcp-manager.js +9 -7
  72. package/dist/core/mcp/mcp-manager.js.map +1 -1
  73. package/dist/core/model-registry.d.ts.map +1 -1
  74. package/dist/core/model-registry.js +1 -1
  75. package/dist/core/model-registry.js.map +1 -1
  76. package/dist/core/prime-inference-auth.d.ts +0 -9
  77. package/dist/core/prime-inference-auth.d.ts.map +1 -1
  78. package/dist/core/prime-inference-auth.js +0 -50
  79. package/dist/core/prime-inference-auth.js.map +1 -1
  80. package/dist/core/provider-display-names.d.ts.map +1 -1
  81. package/dist/core/provider-display-names.js +0 -1
  82. package/dist/core/provider-display-names.js.map +1 -1
  83. package/dist/core/session-manager.d.ts +0 -4
  84. package/dist/core/session-manager.d.ts.map +1 -1
  85. package/dist/core/session-manager.js +0 -22
  86. package/dist/core/session-manager.js.map +1 -1
  87. package/dist/core/settings-manager.d.ts +4 -16
  88. package/dist/core/settings-manager.d.ts.map +1 -1
  89. package/dist/core/settings-manager.js +13 -43
  90. package/dist/core/settings-manager.js.map +1 -1
  91. package/dist/core/slash-commands.d.ts.map +1 -1
  92. package/dist/core/slash-commands.js +0 -5
  93. package/dist/core/slash-commands.js.map +1 -1
  94. package/dist/core/tools/acp-mcp.d.ts +6 -0
  95. package/dist/core/tools/acp-mcp.d.ts.map +1 -0
  96. package/dist/core/tools/acp-mcp.js +86 -0
  97. package/dist/core/tools/acp-mcp.js.map +1 -0
  98. package/dist/core/tools/index.d.ts +1 -0
  99. package/dist/core/tools/index.d.ts.map +1 -1
  100. package/dist/core/tools/index.js +1 -0
  101. package/dist/core/tools/index.js.map +1 -1
  102. package/dist/core/tools/ipython.d.ts.map +1 -1
  103. package/dist/core/tools/ipython.js +9 -4
  104. package/dist/core/tools/ipython.js.map +1 -1
  105. package/dist/main.d.ts.map +1 -1
  106. package/dist/main.js +2 -11
  107. package/dist/main.js.map +1 -1
  108. package/dist/modes/acp/acp-mode.d.ts.map +1 -1
  109. package/dist/modes/acp/acp-mode.js +1 -1
  110. package/dist/modes/acp/acp-mode.js.map +1 -1
  111. package/dist/modes/agent-connection/daemon-agent-connection.d.ts +0 -2
  112. package/dist/modes/agent-connection/daemon-agent-connection.d.ts.map +1 -1
  113. package/dist/modes/agent-connection/daemon-agent-connection.js +0 -2
  114. package/dist/modes/agent-connection/daemon-agent-connection.js.map +1 -1
  115. package/dist/modes/agents-view/agents-view-mode.d.ts.map +1 -1
  116. package/dist/modes/agents-view/agents-view-mode.js +0 -19
  117. package/dist/modes/agents-view/agents-view-mode.js.map +1 -1
  118. package/dist/modes/daemon/daemon-mode.d.ts +1 -10
  119. package/dist/modes/daemon/daemon-mode.d.ts.map +1 -1
  120. package/dist/modes/daemon/daemon-mode.js +134 -274
  121. package/dist/modes/daemon/daemon-mode.js.map +1 -1
  122. package/dist/modes/daemon/daemon-protocol.d.ts +11 -5
  123. package/dist/modes/daemon/daemon-protocol.d.ts.map +1 -1
  124. package/dist/modes/daemon/daemon-protocol.js +6 -8
  125. package/dist/modes/daemon/daemon-protocol.js.map +1 -1
  126. package/dist/modes/daemon/daemon-supervisor.d.ts +0 -3
  127. package/dist/modes/daemon/daemon-supervisor.d.ts.map +1 -1
  128. package/dist/modes/daemon/daemon-supervisor.js +18 -52
  129. package/dist/modes/daemon/daemon-supervisor.js.map +1 -1
  130. package/dist/modes/daemon/daemon-worker-protocol.d.ts +1 -6
  131. package/dist/modes/daemon/daemon-worker-protocol.d.ts.map +1 -1
  132. package/dist/modes/daemon/daemon-worker-protocol.js +0 -2
  133. package/dist/modes/daemon/daemon-worker-protocol.js.map +1 -1
  134. package/dist/modes/interactive/auth-flows.d.ts +0 -2
  135. package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
  136. package/dist/modes/interactive/auth-flows.js +1 -101
  137. package/dist/modes/interactive/auth-flows.js.map +1 -1
  138. package/dist/modes/interactive/components/assistant-message.d.ts +6 -1
  139. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  140. package/dist/modes/interactive/components/assistant-message.js +13 -3
  141. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  142. package/dist/modes/interactive/components/footer.d.ts +1 -1
  143. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  144. package/dist/modes/interactive/components/footer.js +2 -2
  145. package/dist/modes/interactive/components/footer.js.map +1 -1
  146. package/dist/modes/interactive/components/mermaid.d.ts +12 -0
  147. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
  148. package/dist/modes/interactive/components/mermaid.js +63 -0
  149. package/dist/modes/interactive/components/mermaid.js.map +1 -0
  150. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  151. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  152. package/dist/modes/interactive/components/settings-selector.js +10 -0
  153. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  154. package/dist/modes/interactive/feature-hints.d.ts.map +1 -1
  155. package/dist/modes/interactive/feature-hints.js +0 -4
  156. package/dist/modes/interactive/feature-hints.js.map +1 -1
  157. package/dist/modes/interactive/interactive-mode.d.ts +1 -7
  158. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  159. package/dist/modes/interactive/interactive-mode.js +23 -279
  160. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  161. package/dist/utils/version-check.d.ts.map +1 -1
  162. package/dist/utils/version-check.js +1 -1
  163. package/dist/utils/version-check.js.map +1 -1
  164. package/docs/settings.md +1 -31
  165. package/docs/usage.md +1 -4
  166. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  167. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  168. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  169. package/examples/extensions/sandbox/package-lock.json +2 -2
  170. package/examples/extensions/sandbox/package.json +1 -1
  171. package/examples/extensions/with-deps/package-lock.json +2 -2
  172. package/examples/extensions/with-deps/package.json +1 -1
  173. package/examples/gpt-6-astra.models.json +24 -0
  174. package/package.json +6 -5
  175. package/dist/core/agent-traces.d.ts +0 -130
  176. package/dist/core/agent-traces.d.ts.map +0 -1
  177. package/dist/core/agent-traces.js +0 -746
  178. package/dist/core/agent-traces.js.map +0 -1
  179. package/dist/core/telemetry.d.ts +0 -92
  180. package/dist/core/telemetry.d.ts.map +0 -1
  181. package/dist/core/telemetry.js +0 -633
  182. package/dist/core/telemetry.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/core/kernel/shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerSessionResourceCleanup } from \"@asm-agent/ai\";\nimport type { KernelBootstrapProgressHandler, KernelPythonSkill } from \"./bootstrap.js\";\nimport type { RestoreResult, SnapshotResult } from \"./state-snapshot.js\";\n\nexport const DEFAULT_MAX_OUTPUT_CHARS = 65536;\nexport const HOST_REQUEST_DISPOSE_TIMEOUT_MS = 5000;\nexport const KERNEL_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const DEFAULT_SNAPSHOT_DEBOUNCE_MS = 1500;\n// Cap how long a graceful dispose waits on the final snapshot; the debounced\n// on-disk copy is the fallback if this is exceeded.\nexport const SNAPSHOT_DISPOSE_TIMEOUT_MS = 5000;\nexport const SNAPSHOT_EXECUTION_TIMEOUT_MS = 5000;\nexport const KERNEL_ABORT_GRACE_MS = 1000;\nexport const KERNEL_BUSY_REUSE_WAIT_MS = 5000;\nexport const KERNEL_BUSY_INTERRUPT_INTERVAL_MS = 500;\nexport const MAX_LATE_SENT_AGENT_MESSAGE_HANDLERS = 256;\nconst KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE =\n\t\"The Python kernel is still running the previously interrupted cell. Wait and try again, or kill the kernel to start fresh.\";\n\nexport class KernelBusyAfterInterruptError extends Error {\n\tconstructor() {\n\t\tsuper(KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE);\n\t\tthis.name = \"KernelBusyAfterInterruptError\";\n\t}\n}\n\n/**\n * Handles one typed request from Python code running in the kernel.\n * The returned record is sent back verbatim as the reply payload.\n *\n * The unary form is the dispatcher and registration contract; context-aware\n * handlers are declared separately below.\n */\nexport type HostRequestHandler = (payload: Record<string, unknown>) => Promise<Record<string, unknown>>;\n\n/**\n * Per-call authority supplied by the host-request dispatcher.\n * `requestId` is an opaque host-minted correlation token and `isCurrent()`\n * lets an implementation reject work after its authority is revoked.\n */\nexport interface HostRequestContext {\n\treadonly requestId: string;\n\treadonly generation: number;\n\treadonly signal: AbortSignal;\n\tisCurrent(): boolean;\n}\n\nconst hostRequestHandlerBrand = Symbol(\"hostRequestHandler\");\n\n/** A context-aware implementation that must receive dispatcher authority. */\nexport type HostRequestHandlerImplementation = (\n\tpayload: Record<string, unknown>,\n\tcontext: HostRequestContext,\n) => Promise<Record<string, unknown>>;\n\n/** A factory-minted, context-aware host-request handler capability. */\ntype HostRequestHandlerCapability = HostRequestHandlerImplementation & { readonly [hostRequestHandlerBrand]: true };\n\n/** Runtime provenance cannot be recreated by copying the nominal symbol property. */\nconst factoryCreatedHostRequestHandlers = new WeakSet<object>();\n\nfunction assertGenuineHostRequestContext(context: unknown): asserts context is HostRequestContext {\n\tif (\n\t\ttypeof context !== \"object\" ||\n\t\tcontext === null ||\n\t\ttypeof (context as HostRequestContext).requestId !== \"string\" ||\n\t\t!(context as HostRequestContext).requestId ||\n\t\t!Number.isSafeInteger((context as HostRequestContext).generation) ||\n\t\ttypeof (context as HostRequestContext).isCurrent !== \"function\" ||\n\t\ttypeof (context as HostRequestContext).signal !== \"object\" ||\n\t\t(context as HostRequestContext).signal === null ||\n\t\ttypeof (context as HostRequestContext).signal.aborted !== \"boolean\" ||\n\t\ttypeof (context as HostRequestContext).signal.addEventListener !== \"function\"\n\t) {\n\t\tthrow new Error(\"host request context is invalid\");\n\t}\n}\n\n/**\n * Creates a branded wrapper rather than mutating its implementation. Both its\n * generic shape and runtime arity reject unary callbacks before they can run.\n */\nexport function createHostRequestHandler<T extends HostRequestHandlerImplementation>(\n\timplementation: T,\n\t..._unaryRejection: Parameters<T> extends [unknown, unknown, ...unknown[]]\n\t\t? []\n\t\t: [\"host request handlers must accept payload and context\"]\n): HostRequestHandlerCapability {\n\tif (implementation.length < 2) throw new Error(\"host request handlers must accept payload and context\");\n\tconst handler = async (payload: Record<string, unknown>, context: HostRequestContext) => {\n\t\tassertGenuineHostRequestContext(context);\n\t\treturn implementation(payload, context);\n\t};\n\tfactoryCreatedHostRequestHandlers.add(handler);\n\treturn Object.defineProperty(handler, hostRequestHandlerBrand, { value: true }) as HostRequestHandlerCapability;\n}\n\n/** Reject copied-symbol and raw-function forgeries before they observe authenticated payloads. */\nexport function assertHostRequestHandler(value: unknown): asserts value is HostRequestHandlerCapability {\n\tif (\n\t\ttypeof value !== \"function\" ||\n\t\t(value as Partial<HostRequestHandlerCapability>)[hostRequestHandlerBrand] !== true ||\n\t\t!factoryCreatedHostRequestHandlers.has(value)\n\t) {\n\t\tthrow new Error(\"host request handler is not a dispatcher-created capability\");\n\t}\n}\n\n/** Host request handlers keyed by request type (e.g. \"rlm.run\", \"goal.complete\"). */\nexport type HostRequestHandlers = Record<string, HostRequestHandler>;\n\n/** Where and how to persist the kernel's user namespace so it survives resume. */\nexport interface KernelSnapshotConfig {\n\t/** Absolute path for the dill payload. */\n\tpath: string;\n\t/** Absolute path for the JSON manifest written alongside the payload. */\n\tmanifestPath: string;\n\t/** Maximum aggregate snapshot size. Default 256 MiB. */\n\tmaxBytes?: number;\n\t/** Maximum serialized size of one variable. Default 16 MiB. */\n\tmaxVariableBytes?: number;\n\t/** Debounce window for the auto-snapshot after a successful execution. Default 1500 ms. */\n\tdebounceMs?: number;\n}\n\nexport interface KernelManagerOptions {\n\t/** Python interpreter with the kernel runtime available. Defaults to the auto-bootstrapped kernel. */\n\tpython?: string;\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tsessionId?: string;\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly KernelPythonSkill[];\n\t/** Persist/revive the user namespace across kernel restarts and session resume. */\n\tsnapshot?: KernelSnapshotConfig;\n}\n\nexport interface KernelStartOptions {\n\tonBootstrapProgress?: KernelBootstrapProgressHandler;\n\tsignal?: AbortSignal;\n}\n\nexport interface ExecuteOptions {\n\t/** Aborting interrupts the kernel out-of-band. */\n\tsignal?: AbortSignal;\n\tonStream?: (chunk: string, name: \"stdout\" | \"stderr\") => void;\n\tonLateSentAgentMessage?: (message: KernelSentAgentMessage) => void;\n\t/** Cap stdout / stderr / result at this many characters. Default 65536. */\n\tmaxOutputChars?: number;\n\t/** Synthetic host cell (snapshot/restore/list); excluded from lastCellCode attribution. */\n\tinternal?: boolean;\n}\n\n/** MIME tag the `edit` skill emits diff payloads under. */\nexport const DIFF_DISPLAY_MIME = \"application/vnd.asm-agent.diff+json\";\n\n/** MIME tag the `attach-image` skill emits media payloads under. */\nexport const ATTACHMENT_DISPLAY_MIME = \"application/vnd.asm-agent.attachment+json\";\n\n/** MIME tag the `agent-message` skill emits after sending a message. */\nexport const AGENT_MESSAGE_DISPLAY_MIME = \"application/vnd.asm-agent.agent-message+json\";\n\n/**\n * Hard ceiling on a single attachment's base64 payload, a defensive guard\n * against a runaway direct display emit. The `attach-image` skill caps\n * its own images well under this (see `_MAX_IMAGE_BYTES`), so a skill-produced\n * attachment is never dropped here — only a non-skill emit can hit this.\n */\nexport const MAX_ATTACHMENT_DATA_CHARS = 10_000_000;\n\n/** One file edit, captured from a {@link DIFF_DISPLAY_MIME} display payload. */\nexport interface KernelDiffDisplay {\n\tpath: string;\n\toldStr: string;\n\tnewStr: string;\n\t/** 1-based line where `oldStr` begins in the file, for absolute line numbers. */\n\tstartLine?: number;\n}\n\n/** One media attachment, captured from an {@link ATTACHMENT_DISPLAY_MIME} display payload. */\nexport interface KernelAttachment {\n\tmimeType: string;\n\t/** base64-encoded bytes. */\n\tdata: string;\n\t/** Source path, surfaced to the TUI renderer. */\n\tpath?: string;\n}\n\nexport interface KernelSentAgentMessage {\n\tid: string;\n\tmessage: string;\n\tdeliveryStatus: \"delivered\" | \"queued\";\n\treceiverRole?: \"parent\" | \"sibling\" | \"child\";\n\ttarget: {\n\t\tactiveSessionId: string;\n\t\tsessionId: string;\n\t\tsessionName?: string;\n\t};\n}\n\nexport interface ExecuteResult {\n\tstdout: string;\n\tstderr: string;\n\t/** Text of the cell's trailing expression value, if the cell produced one. */\n\tresult?: string;\n\t/** Diffs emitted via display events, in order. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments emitted via display events, in order. */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell, in order. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** Output that arrived without this cell's id (user threads, other cells' leftovers, raw fd writes). */\n\tbackgroundOutput?: string;\n\tstatus: \"ok\" | \"error\" | \"aborted\";\n\terror?: { ename: string; evalue: string; traceback: string[] };\n\tdurationMs: number;\n}\n\n/** Parse a {@link DIFF_DISPLAY_MIME} payload, tolerating malformed input. */\nexport function parseDiffDisplay(payload: unknown): KernelDiffDisplay | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { path, old_str: oldStr, new_str: newStr, start_line: startLine } = payload;\n\tif (typeof path !== \"string\" || typeof oldStr !== \"string\" || typeof newStr !== \"string\") {\n\t\treturn undefined;\n\t}\n\treturn { path, oldStr, newStr, startLine: typeof startLine === \"number\" ? startLine : undefined };\n}\n\n/**\n * Parse an {@link ATTACHMENT_DISPLAY_MIME} payload. Malformed payloads are\n * tolerantly ignored (`undefined`); a well-formed payload exceeding\n * {@link MAX_ATTACHMENT_DATA_CHARS} is reported as `\"oversized\"` so the caller\n * can fail the cell loudly rather than silently dropping the image.\n */\nexport function parseAttachmentDisplay(payload: unknown): KernelAttachment | \"oversized\" | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { mime_type: mimeType, data, path } = payload;\n\tif (typeof mimeType !== \"string\" || typeof data !== \"string\") {\n\t\treturn undefined;\n\t}\n\tif (data.length > MAX_ATTACHMENT_DATA_CHARS) {\n\t\treturn \"oversized\";\n\t}\n\treturn { mimeType, data, path: typeof path === \"string\" ? path : undefined };\n}\n\nexport function parseSentAgentMessage(payload: unknown): KernelSentAgentMessage | undefined {\n\tif (!isRecord(payload) || !isRecord(payload.target)) {\n\t\treturn undefined;\n\t}\n\tconst { id, message, deliveryStatus, receiverRole, target } = payload;\n\tconst { activeSessionId, sessionId, sessionName } = target;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof message !== \"string\" ||\n\t\t(deliveryStatus !== \"delivered\" && deliveryStatus !== \"queued\") ||\n\t\ttypeof activeSessionId !== \"string\" ||\n\t\ttypeof sessionId !== \"string\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tid,\n\t\tmessage,\n\t\tdeliveryStatus,\n\t\t...(receiverRole === \"parent\" || receiverRole === \"sibling\" || receiverRole === \"child\" ? { receiverRole } : {}),\n\t\ttarget: {\n\t\t\tactiveSessionId,\n\t\t\tsessionId,\n\t\t\t...(typeof sessionName === \"string\" ? { sessionName } : {}),\n\t\t},\n\t};\n}\n\nexport function createKernelStartupAbortError(): Error {\n\treturn new Error(\"Kernel startup aborted\");\n}\n\nexport function raceStartupWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\treturn Promise.reject(createKernelStartupAbortError());\n\t}\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst cleanup = () => signal.removeEventListener(\"abort\", abort);\n\t\tconst abort = () => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(createKernelStartupAbortError());\n\t\t};\n\t\tsignal.addEventListener(\"abort\", abort, { once: true });\n\t\tpromise.then(\n\t\t\t(value) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nexport interface Deferred<T> {\n\tpromise: Promise<T>;\n\tresolve: (value: T) => void;\n\treject: (error: Error) => void;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nexport function createDeferred<T>(): Deferred<T> {\n\tlet resolve!: (value: T) => void;\n\tlet reject!: (error: Error) => void;\n\tconst promise = new Promise<T>((promiseResolve, promiseReject) => {\n\t\tresolve = promiseResolve;\n\t\treject = promiseReject;\n\t});\n\treturn { promise, resolve, reject };\n}\n\n/** Public surface every kernel client exposes to the provisioner and session layer. */\nexport interface KernelClient {\n\treadonly ownerSessionId: string | undefined;\n\treadonly isRunning: boolean;\n\tstart(options?: KernelStartOptions): Promise<void>;\n\texecute(code: string, opts?: ExecuteOptions): Promise<ExecuteResult>;\n\tshutdown(opts?: { snapshot?: boolean }): Promise<boolean>;\n\trestart(): Promise<void>;\n\tkill(): Promise<void>;\n\tdispose(): Promise<void>;\n\tdisposeSync(): void;\n\tsnapshotState(): Promise<SnapshotResult | null>;\n\tpruneOversizedVariables(): Promise<SnapshotResult | null>;\n\trestoreState(): Promise<RestoreResult | null>;\n\tlistNamespaceNames(signal?: AbortSignal): Promise<string[] | null>;\n}\n\n// One registry serves every client kind; two parallel registries would\n// double-install process signal handlers.\nexport const liveKernels = new Set<KernelClient>();\nlet signalHandlersInstalled = false;\n\nregisterSessionResourceCleanup((sessionId) => {\n\tfor (const k of liveKernels) {\n\t\tif (!sessionId || k.ownerSessionId === sessionId) {\n\t\t\tvoid k.dispose();\n\t\t}\n\t}\n});\n\nexport function installSignalHandlersOnce(): void {\n\tif (signalHandlersInstalled) return;\n\tsignalHandlersInstalled = true;\n\n\tconst asyncShutdown = async (): Promise<void> => {\n\t\t// These paths can await, so flush the namespace snapshot before tearing down.\n\t\tawait Promise.allSettled([...liveKernels].map((k) => k.shutdown({ snapshot: true })));\n\t};\n\n\t// `beforeExit` and signal handlers can await async cleanup. `exit`\n\t// can only do sync work (Node won't run pending microtasks past it),\n\t// so it falls back to `disposeSync()` which kills the child synchronously.\n\tprocess.on(\"beforeExit\", () => {\n\t\tvoid asyncShutdown();\n\t});\n\tprocess.on(\"SIGINT\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(130));\n\t});\n\tprocess.on(\"SIGTERM\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(143));\n\t});\n\tprocess.on(\"exit\", () => {\n\t\tfor (const k of liveKernels) k.disposeSync();\n\t});\n}\n"]}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/core/kernel/shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerSessionResourceCleanup } from \"@asm-agent/ai\";\nimport type { KernelBootstrapProgressHandler, KernelPythonSkill } from \"./bootstrap.js\";\nimport type { RestoreResult, SnapshotResult } from \"./state-snapshot.js\";\n\nexport const DEFAULT_MAX_OUTPUT_CHARS = 65536;\nexport const HOST_REQUEST_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const KERNEL_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const DEFAULT_SNAPSHOT_DEBOUNCE_MS = 1500;\nexport const SNAPSHOT_EXECUTION_TIMEOUT_MS = 5000;\nexport const KERNEL_ABORT_GRACE_MS = 1000;\nexport const KERNEL_BUSY_REUSE_WAIT_MS = 5000;\nexport const KERNEL_BUSY_INTERRUPT_INTERVAL_MS = 500;\nexport const MAX_LATE_SENT_AGENT_MESSAGE_HANDLERS = 256;\nconst KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE =\n\t\"The Python kernel is still running the previously interrupted cell. Wait and try again, or kill the kernel to start fresh.\";\n\nexport class KernelBusyAfterInterruptError extends Error {\n\tconstructor() {\n\t\tsuper(KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE);\n\t\tthis.name = \"KernelBusyAfterInterruptError\";\n\t}\n}\n\n/**\n * Handles one typed request from Python code running in the kernel.\n * The returned record is delivered verbatim to the Python caller.\n *\n * The unary form is the dispatcher and registration contract; context-aware\n * handlers are declared separately below.\n */\nexport type HostRequestHandler = (payload: Record<string, unknown>) => Promise<Record<string, unknown>>;\n\n/**\n * Per-call authority supplied by the host-request dispatcher.\n * `requestId` is an opaque host-minted correlation token and `isCurrent()`\n * lets an implementation reject work after its authority is revoked.\n */\nexport interface HostRequestContext {\n\treadonly requestId: string;\n\treadonly generation: number;\n\treadonly signal: AbortSignal;\n\tisCurrent(): boolean;\n}\n\nconst hostRequestHandlerBrand = Symbol(\"hostRequestHandler\");\n\n/** A context-aware implementation that must receive dispatcher authority. */\nexport type HostRequestHandlerImplementation = (\n\tpayload: Record<string, unknown>,\n\tcontext: HostRequestContext,\n) => Promise<Record<string, unknown>>;\n\n/** A factory-minted, context-aware host-request handler capability. */\ntype HostRequestHandlerCapability = HostRequestHandlerImplementation & { readonly [hostRequestHandlerBrand]: true };\n\n/** Runtime provenance cannot be recreated by copying the nominal symbol property. */\nconst factoryCreatedHostRequestHandlers = new WeakSet<object>();\n\nfunction assertGenuineHostRequestContext(context: unknown): asserts context is HostRequestContext {\n\tif (\n\t\ttypeof context !== \"object\" ||\n\t\tcontext === null ||\n\t\ttypeof (context as HostRequestContext).requestId !== \"string\" ||\n\t\t!(context as HostRequestContext).requestId ||\n\t\t!Number.isSafeInteger((context as HostRequestContext).generation) ||\n\t\ttypeof (context as HostRequestContext).isCurrent !== \"function\" ||\n\t\ttypeof (context as HostRequestContext).signal !== \"object\" ||\n\t\t(context as HostRequestContext).signal === null ||\n\t\ttypeof (context as HostRequestContext).signal.aborted !== \"boolean\" ||\n\t\ttypeof (context as HostRequestContext).signal.addEventListener !== \"function\"\n\t) {\n\t\tthrow new Error(\"host request context is invalid\");\n\t}\n}\n\n/**\n * Creates a branded wrapper rather than mutating its implementation. Both its\n * generic shape and runtime arity reject unary callbacks before they can run.\n */\nexport function createHostRequestHandler<T extends HostRequestHandlerImplementation>(\n\timplementation: T,\n\t..._unaryRejection: Parameters<T> extends [unknown, unknown, ...unknown[]]\n\t\t? []\n\t\t: [\"host request handlers must accept payload and context\"]\n): HostRequestHandlerCapability {\n\tif (implementation.length < 2) throw new Error(\"host request handlers must accept payload and context\");\n\tconst handler = async (payload: Record<string, unknown>, context: HostRequestContext) => {\n\t\tassertGenuineHostRequestContext(context);\n\t\treturn implementation(payload, context);\n\t};\n\tfactoryCreatedHostRequestHandlers.add(handler);\n\treturn Object.defineProperty(handler, hostRequestHandlerBrand, { value: true }) as HostRequestHandlerCapability;\n}\n\n/** Reject copied-symbol and raw-function forgeries before they observe authenticated payloads. */\nexport function assertHostRequestHandler(value: unknown): asserts value is HostRequestHandlerCapability {\n\tif (\n\t\ttypeof value !== \"function\" ||\n\t\t(value as Partial<HostRequestHandlerCapability>)[hostRequestHandlerBrand] !== true ||\n\t\t!factoryCreatedHostRequestHandlers.has(value)\n\t) {\n\t\tthrow new Error(\"host request handler is not a dispatcher-created capability\");\n\t}\n}\n\n/** Host request handlers keyed by request type (e.g. \"rlm.run\", \"goal.complete\"). */\nexport type HostRequestHandlers = Record<string, HostRequestHandler>;\n\n/** Where and how to persist the kernel's user namespace so it survives resume. */\nexport interface KernelSnapshotConfig {\n\t/** Absolute path for the dill payload. */\n\tpath: string;\n\t/** Absolute path for the JSON manifest written alongside the payload. */\n\tmanifestPath: string;\n\t/** Maximum aggregate snapshot size. Default 256 MiB. */\n\tmaxBytes?: number;\n\t/** Maximum serialized size of one variable. Default 16 MiB. */\n\tmaxVariableBytes?: number;\n\t/** Debounce window for the auto-snapshot after a successful execution. Default 1500 ms. */\n\tdebounceMs?: number;\n}\n\nexport interface KernelManagerOptions {\n\t/** Python interpreter with the kernel runtime available. Defaults to the auto-bootstrapped kernel. */\n\tpython?: string;\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tsessionId?: string;\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly KernelPythonSkill[];\n\t/** Persist/revive the user namespace across kernel restarts and session resume. */\n\tsnapshot?: KernelSnapshotConfig;\n\t/** Runtime bootstrap re-run on a protocol-repaired kernel so live handles (rlm, bash, skills) exist again. */\n\tbootstrapCode?: string;\n}\n\nexport interface KernelStartOptions {\n\tonBootstrapProgress?: KernelBootstrapProgressHandler;\n\tsignal?: AbortSignal;\n}\n\nexport interface ExecuteOptions {\n\t/** Aborting interrupts the kernel out-of-band. */\n\tsignal?: AbortSignal;\n\tonStream?: (chunk: string, name: \"stdout\" | \"stderr\") => void;\n\tonLateSentAgentMessage?: (message: KernelSentAgentMessage) => void;\n\t/** Cap stdout / stderr / result at this many characters. Default 65536. */\n\tmaxOutputChars?: number;\n\t/** Synthetic host cell (snapshot/restore/list); excluded from lastCellCode attribution. */\n\tinternal?: boolean;\n\t/** The protocol repair's own restore; exempt from waiting on the repair it belongs to. */\n\tprotocolRepair?: boolean;\n}\n\n/** MIME tag the `edit` skill emits diff payloads under. */\nexport const DIFF_DISPLAY_MIME = \"application/vnd.asm-agent.diff+json\";\n\n/** MIME tag the `attach-image` skill emits media payloads under. */\nexport const ATTACHMENT_DISPLAY_MIME = \"application/vnd.asm-agent.attachment+json\";\n\n/** MIME tag the `agent-message` skill emits after sending a message. */\nexport const AGENT_MESSAGE_DISPLAY_MIME = \"application/vnd.asm-agent.agent-message+json\";\n\n/**\n * Hard ceiling on a single attachment's base64 payload, a defensive guard\n * against a runaway direct display emit. The `attach-image` skill caps\n * its own images well under this (see `_MAX_IMAGE_BYTES`), so a skill-produced\n * attachment is never dropped here — only a non-skill emit can hit this.\n */\nexport const MAX_ATTACHMENT_DATA_CHARS = 10_000_000;\n\n/** One file edit, captured from a {@link DIFF_DISPLAY_MIME} display payload. */\nexport interface KernelDiffDisplay {\n\tpath: string;\n\toldStr: string;\n\tnewStr: string;\n\t/** 1-based line where `oldStr` begins in the file, for absolute line numbers. */\n\tstartLine?: number;\n}\n\n/** One media attachment, captured from an {@link ATTACHMENT_DISPLAY_MIME} display payload. */\nexport interface KernelAttachment {\n\tmimeType: string;\n\t/** base64-encoded bytes. */\n\tdata: string;\n\t/** Source path, surfaced to the TUI renderer. */\n\tpath?: string;\n}\n\nexport interface KernelSentAgentMessage {\n\tid: string;\n\tmessage: string;\n\tdeliveryStatus: \"delivered\" | \"queued\";\n\treceiverRole?: \"parent\" | \"sibling\" | \"child\";\n\ttarget: {\n\t\tactiveSessionId: string;\n\t\tsessionId: string;\n\t\tsessionName?: string;\n\t};\n}\n\nexport interface ExecuteResult {\n\tstdout: string;\n\tstderr: string;\n\t/** Text of the cell's trailing expression value, if the cell produced one. */\n\tresult?: string;\n\t/** Diffs emitted via display events, in order. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments emitted via display events, in order. */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell, in order. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** Output that arrived without this cell's id (user threads, other cells' leftovers, raw fd writes). */\n\tbackgroundOutput?: string;\n\tstatus: \"ok\" | \"error\" | \"aborted\";\n\terror?: { ename: string; evalue: string; traceback: string[] };\n\tdurationMs: number;\n}\n\n/** Parse a {@link DIFF_DISPLAY_MIME} payload, tolerating malformed input. */\nexport function parseDiffDisplay(payload: unknown): KernelDiffDisplay | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { path, old_str: oldStr, new_str: newStr, start_line: startLine } = payload;\n\tif (typeof path !== \"string\" || typeof oldStr !== \"string\" || typeof newStr !== \"string\") {\n\t\treturn undefined;\n\t}\n\treturn { path, oldStr, newStr, startLine: typeof startLine === \"number\" ? startLine : undefined };\n}\n\n/**\n * Parse an {@link ATTACHMENT_DISPLAY_MIME} payload. Malformed payloads are\n * tolerantly ignored (`undefined`); a well-formed payload exceeding\n * {@link MAX_ATTACHMENT_DATA_CHARS} is reported as `\"oversized\"` so the caller\n * can fail the cell loudly rather than silently dropping the image.\n */\nexport function parseAttachmentDisplay(payload: unknown): KernelAttachment | \"oversized\" | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { mime_type: mimeType, data, path } = payload;\n\tif (typeof mimeType !== \"string\" || typeof data !== \"string\") {\n\t\treturn undefined;\n\t}\n\tif (data.length > MAX_ATTACHMENT_DATA_CHARS) {\n\t\treturn \"oversized\";\n\t}\n\treturn { mimeType, data, path: typeof path === \"string\" ? path : undefined };\n}\n\nexport function parseSentAgentMessage(payload: unknown): KernelSentAgentMessage | undefined {\n\tif (!isRecord(payload) || !isRecord(payload.target)) {\n\t\treturn undefined;\n\t}\n\tconst { id, message, deliveryStatus, receiverRole, target } = payload;\n\tconst { activeSessionId, sessionId, sessionName } = target;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof message !== \"string\" ||\n\t\t(deliveryStatus !== \"delivered\" && deliveryStatus !== \"queued\") ||\n\t\ttypeof activeSessionId !== \"string\" ||\n\t\ttypeof sessionId !== \"string\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tid,\n\t\tmessage,\n\t\tdeliveryStatus,\n\t\t...(receiverRole === \"parent\" || receiverRole === \"sibling\" || receiverRole === \"child\" ? { receiverRole } : {}),\n\t\ttarget: {\n\t\t\tactiveSessionId,\n\t\t\tsessionId,\n\t\t\t...(typeof sessionName === \"string\" ? { sessionName } : {}),\n\t\t},\n\t};\n}\n\nexport function createKernelStartupAbortError(): Error {\n\treturn new Error(\"Kernel startup aborted\");\n}\n\nexport function raceStartupWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\treturn Promise.reject(createKernelStartupAbortError());\n\t}\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst cleanup = () => signal.removeEventListener(\"abort\", abort);\n\t\tconst abort = () => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(createKernelStartupAbortError());\n\t\t};\n\t\tsignal.addEventListener(\"abort\", abort, { once: true });\n\t\tpromise.then(\n\t\t\t(value) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nexport interface Deferred<T> {\n\tpromise: Promise<T>;\n\tresolve: (value: T) => void;\n\treject: (error: Error) => void;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nexport function createDeferred<T>(): Deferred<T> {\n\tlet resolve!: (value: T) => void;\n\tlet reject!: (error: Error) => void;\n\tconst promise = new Promise<T>((promiseResolve, promiseReject) => {\n\t\tresolve = promiseResolve;\n\t\treject = promiseReject;\n\t});\n\treturn { promise, resolve, reject };\n}\n\nexport interface KernelShutdownOptions {\n\tsnapshot?: boolean;\n\tdrainHostRequests?: boolean;\n}\n\n/** Public surface every kernel client exposes to the provisioner and session layer. */\nexport interface KernelClient {\n\treadonly ownerSessionId: string | undefined;\n\treadonly isRunning: boolean;\n\tstart(options?: KernelStartOptions): Promise<void>;\n\texecute(code: string, opts?: ExecuteOptions): Promise<ExecuteResult>;\n\tshutdown(opts?: KernelShutdownOptions): Promise<boolean>;\n\trestart(): Promise<void>;\n\tkill(): Promise<void>;\n\tdisposeSync(): void;\n\tsnapshotState(): Promise<SnapshotResult | null>;\n\tpruneOversizedVariables(): Promise<SnapshotResult | null>;\n\trestoreState(): Promise<RestoreResult | null>;\n\tlistNamespaceNames(signal?: AbortSignal): Promise<string[] | null>;\n}\n\n// One registry serves every client kind; two parallel registries would\n// double-install process signal handlers.\nexport const liveKernels = new Set<KernelClient>();\nlet signalHandlersInstalled = false;\n\nregisterSessionResourceCleanup((sessionId) => {\n\tfor (const k of liveKernels) {\n\t\tif (!sessionId || k.ownerSessionId === sessionId) {\n\t\t\tvoid k.shutdown({ snapshot: true, drainHostRequests: true });\n\t\t}\n\t}\n});\n\nexport function installSignalHandlersOnce(): void {\n\tif (signalHandlersInstalled) return;\n\tsignalHandlersInstalled = true;\n\n\tconst asyncShutdown = async (): Promise<void> => {\n\t\t// These paths can await, so flush the namespace snapshot before tearing down.\n\t\tawait Promise.allSettled([...liveKernels].map((k) => k.shutdown({ snapshot: true })));\n\t};\n\n\t// `beforeExit` and signal handlers can await async cleanup. `exit`\n\t// can only do sync work (Node won't run pending microtasks past it),\n\t// so it falls back to `disposeSync()` which kills the child synchronously.\n\tprocess.on(\"beforeExit\", () => {\n\t\tvoid asyncShutdown();\n\t});\n\tprocess.on(\"SIGINT\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(130));\n\t});\n\tprocess.on(\"SIGTERM\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(143));\n\t});\n\tprocess.on(\"exit\", () => {\n\t\tfor (const k of liveKernels) k.disposeSync();\n\t});\n}\n"]}
@@ -1,11 +1,8 @@
1
1
  import { registerSessionResourceCleanup } from "@asm-agent/ai";
2
2
  export const DEFAULT_MAX_OUTPUT_CHARS = 65536;
3
- export const HOST_REQUEST_DISPOSE_TIMEOUT_MS = 5000;
3
+ export const HOST_REQUEST_SHUTDOWN_TIMEOUT_MS = 5000;
4
4
  export const KERNEL_SHUTDOWN_TIMEOUT_MS = 5000;
5
5
  export const DEFAULT_SNAPSHOT_DEBOUNCE_MS = 1500;
6
- // Cap how long a graceful dispose waits on the final snapshot; the debounced
7
- // on-disk copy is the fallback if this is exceeded.
8
- export const SNAPSHOT_DISPOSE_TIMEOUT_MS = 5000;
9
6
  export const SNAPSHOT_EXECUTION_TIMEOUT_MS = 5000;
10
7
  export const KERNEL_ABORT_GRACE_MS = 1000;
11
8
  export const KERNEL_BUSY_REUSE_WAIT_MS = 5000;
@@ -186,7 +183,7 @@ let signalHandlersInstalled = false;
186
183
  registerSessionResourceCleanup((sessionId) => {
187
184
  for (const k of liveKernels) {
188
185
  if (!sessionId || k.ownerSessionId === sessionId) {
189
- void k.dispose();
186
+ void k.shutdown({ snapshot: true, drainHostRequests: true });
190
187
  }
191
188
  }
192
189
  });
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/core/kernel/shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerSessionResourceCleanup } from \"@asm-agent/ai\";\nimport type { KernelBootstrapProgressHandler, KernelPythonSkill } from \"./bootstrap.js\";\nimport type { RestoreResult, SnapshotResult } from \"./state-snapshot.js\";\n\nexport const DEFAULT_MAX_OUTPUT_CHARS = 65536;\nexport const HOST_REQUEST_DISPOSE_TIMEOUT_MS = 5000;\nexport const KERNEL_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const DEFAULT_SNAPSHOT_DEBOUNCE_MS = 1500;\n// Cap how long a graceful dispose waits on the final snapshot; the debounced\n// on-disk copy is the fallback if this is exceeded.\nexport const SNAPSHOT_DISPOSE_TIMEOUT_MS = 5000;\nexport const SNAPSHOT_EXECUTION_TIMEOUT_MS = 5000;\nexport const KERNEL_ABORT_GRACE_MS = 1000;\nexport const KERNEL_BUSY_REUSE_WAIT_MS = 5000;\nexport const KERNEL_BUSY_INTERRUPT_INTERVAL_MS = 500;\nexport const MAX_LATE_SENT_AGENT_MESSAGE_HANDLERS = 256;\nconst KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE =\n\t\"The Python kernel is still running the previously interrupted cell. Wait and try again, or kill the kernel to start fresh.\";\n\nexport class KernelBusyAfterInterruptError extends Error {\n\tconstructor() {\n\t\tsuper(KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE);\n\t\tthis.name = \"KernelBusyAfterInterruptError\";\n\t}\n}\n\n/**\n * Handles one typed request from Python code running in the kernel.\n * The returned record is sent back verbatim as the reply payload.\n *\n * The unary form is the dispatcher and registration contract; context-aware\n * handlers are declared separately below.\n */\nexport type HostRequestHandler = (payload: Record<string, unknown>) => Promise<Record<string, unknown>>;\n\n/**\n * Per-call authority supplied by the host-request dispatcher.\n * `requestId` is an opaque host-minted correlation token and `isCurrent()`\n * lets an implementation reject work after its authority is revoked.\n */\nexport interface HostRequestContext {\n\treadonly requestId: string;\n\treadonly generation: number;\n\treadonly signal: AbortSignal;\n\tisCurrent(): boolean;\n}\n\nconst hostRequestHandlerBrand = Symbol(\"hostRequestHandler\");\n\n/** A context-aware implementation that must receive dispatcher authority. */\nexport type HostRequestHandlerImplementation = (\n\tpayload: Record<string, unknown>,\n\tcontext: HostRequestContext,\n) => Promise<Record<string, unknown>>;\n\n/** A factory-minted, context-aware host-request handler capability. */\ntype HostRequestHandlerCapability = HostRequestHandlerImplementation & { readonly [hostRequestHandlerBrand]: true };\n\n/** Runtime provenance cannot be recreated by copying the nominal symbol property. */\nconst factoryCreatedHostRequestHandlers = new WeakSet<object>();\n\nfunction assertGenuineHostRequestContext(context: unknown): asserts context is HostRequestContext {\n\tif (\n\t\ttypeof context !== \"object\" ||\n\t\tcontext === null ||\n\t\ttypeof (context as HostRequestContext).requestId !== \"string\" ||\n\t\t!(context as HostRequestContext).requestId ||\n\t\t!Number.isSafeInteger((context as HostRequestContext).generation) ||\n\t\ttypeof (context as HostRequestContext).isCurrent !== \"function\" ||\n\t\ttypeof (context as HostRequestContext).signal !== \"object\" ||\n\t\t(context as HostRequestContext).signal === null ||\n\t\ttypeof (context as HostRequestContext).signal.aborted !== \"boolean\" ||\n\t\ttypeof (context as HostRequestContext).signal.addEventListener !== \"function\"\n\t) {\n\t\tthrow new Error(\"host request context is invalid\");\n\t}\n}\n\n/**\n * Creates a branded wrapper rather than mutating its implementation. Both its\n * generic shape and runtime arity reject unary callbacks before they can run.\n */\nexport function createHostRequestHandler<T extends HostRequestHandlerImplementation>(\n\timplementation: T,\n\t..._unaryRejection: Parameters<T> extends [unknown, unknown, ...unknown[]]\n\t\t? []\n\t\t: [\"host request handlers must accept payload and context\"]\n): HostRequestHandlerCapability {\n\tif (implementation.length < 2) throw new Error(\"host request handlers must accept payload and context\");\n\tconst handler = async (payload: Record<string, unknown>, context: HostRequestContext) => {\n\t\tassertGenuineHostRequestContext(context);\n\t\treturn implementation(payload, context);\n\t};\n\tfactoryCreatedHostRequestHandlers.add(handler);\n\treturn Object.defineProperty(handler, hostRequestHandlerBrand, { value: true }) as HostRequestHandlerCapability;\n}\n\n/** Reject copied-symbol and raw-function forgeries before they observe authenticated payloads. */\nexport function assertHostRequestHandler(value: unknown): asserts value is HostRequestHandlerCapability {\n\tif (\n\t\ttypeof value !== \"function\" ||\n\t\t(value as Partial<HostRequestHandlerCapability>)[hostRequestHandlerBrand] !== true ||\n\t\t!factoryCreatedHostRequestHandlers.has(value)\n\t) {\n\t\tthrow new Error(\"host request handler is not a dispatcher-created capability\");\n\t}\n}\n\n/** Host request handlers keyed by request type (e.g. \"rlm.run\", \"goal.complete\"). */\nexport type HostRequestHandlers = Record<string, HostRequestHandler>;\n\n/** Where and how to persist the kernel's user namespace so it survives resume. */\nexport interface KernelSnapshotConfig {\n\t/** Absolute path for the dill payload. */\n\tpath: string;\n\t/** Absolute path for the JSON manifest written alongside the payload. */\n\tmanifestPath: string;\n\t/** Maximum aggregate snapshot size. Default 256 MiB. */\n\tmaxBytes?: number;\n\t/** Maximum serialized size of one variable. Default 16 MiB. */\n\tmaxVariableBytes?: number;\n\t/** Debounce window for the auto-snapshot after a successful execution. Default 1500 ms. */\n\tdebounceMs?: number;\n}\n\nexport interface KernelManagerOptions {\n\t/** Python interpreter with the kernel runtime available. Defaults to the auto-bootstrapped kernel. */\n\tpython?: string;\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tsessionId?: string;\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly KernelPythonSkill[];\n\t/** Persist/revive the user namespace across kernel restarts and session resume. */\n\tsnapshot?: KernelSnapshotConfig;\n}\n\nexport interface KernelStartOptions {\n\tonBootstrapProgress?: KernelBootstrapProgressHandler;\n\tsignal?: AbortSignal;\n}\n\nexport interface ExecuteOptions {\n\t/** Aborting interrupts the kernel out-of-band. */\n\tsignal?: AbortSignal;\n\tonStream?: (chunk: string, name: \"stdout\" | \"stderr\") => void;\n\tonLateSentAgentMessage?: (message: KernelSentAgentMessage) => void;\n\t/** Cap stdout / stderr / result at this many characters. Default 65536. */\n\tmaxOutputChars?: number;\n\t/** Synthetic host cell (snapshot/restore/list); excluded from lastCellCode attribution. */\n\tinternal?: boolean;\n}\n\n/** MIME tag the `edit` skill emits diff payloads under. */\nexport const DIFF_DISPLAY_MIME = \"application/vnd.asm-agent.diff+json\";\n\n/** MIME tag the `attach-image` skill emits media payloads under. */\nexport const ATTACHMENT_DISPLAY_MIME = \"application/vnd.asm-agent.attachment+json\";\n\n/** MIME tag the `agent-message` skill emits after sending a message. */\nexport const AGENT_MESSAGE_DISPLAY_MIME = \"application/vnd.asm-agent.agent-message+json\";\n\n/**\n * Hard ceiling on a single attachment's base64 payload, a defensive guard\n * against a runaway direct display emit. The `attach-image` skill caps\n * its own images well under this (see `_MAX_IMAGE_BYTES`), so a skill-produced\n * attachment is never dropped here — only a non-skill emit can hit this.\n */\nexport const MAX_ATTACHMENT_DATA_CHARS = 10_000_000;\n\n/** One file edit, captured from a {@link DIFF_DISPLAY_MIME} display payload. */\nexport interface KernelDiffDisplay {\n\tpath: string;\n\toldStr: string;\n\tnewStr: string;\n\t/** 1-based line where `oldStr` begins in the file, for absolute line numbers. */\n\tstartLine?: number;\n}\n\n/** One media attachment, captured from an {@link ATTACHMENT_DISPLAY_MIME} display payload. */\nexport interface KernelAttachment {\n\tmimeType: string;\n\t/** base64-encoded bytes. */\n\tdata: string;\n\t/** Source path, surfaced to the TUI renderer. */\n\tpath?: string;\n}\n\nexport interface KernelSentAgentMessage {\n\tid: string;\n\tmessage: string;\n\tdeliveryStatus: \"delivered\" | \"queued\";\n\treceiverRole?: \"parent\" | \"sibling\" | \"child\";\n\ttarget: {\n\t\tactiveSessionId: string;\n\t\tsessionId: string;\n\t\tsessionName?: string;\n\t};\n}\n\nexport interface ExecuteResult {\n\tstdout: string;\n\tstderr: string;\n\t/** Text of the cell's trailing expression value, if the cell produced one. */\n\tresult?: string;\n\t/** Diffs emitted via display events, in order. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments emitted via display events, in order. */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell, in order. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** Output that arrived without this cell's id (user threads, other cells' leftovers, raw fd writes). */\n\tbackgroundOutput?: string;\n\tstatus: \"ok\" | \"error\" | \"aborted\";\n\terror?: { ename: string; evalue: string; traceback: string[] };\n\tdurationMs: number;\n}\n\n/** Parse a {@link DIFF_DISPLAY_MIME} payload, tolerating malformed input. */\nexport function parseDiffDisplay(payload: unknown): KernelDiffDisplay | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { path, old_str: oldStr, new_str: newStr, start_line: startLine } = payload;\n\tif (typeof path !== \"string\" || typeof oldStr !== \"string\" || typeof newStr !== \"string\") {\n\t\treturn undefined;\n\t}\n\treturn { path, oldStr, newStr, startLine: typeof startLine === \"number\" ? startLine : undefined };\n}\n\n/**\n * Parse an {@link ATTACHMENT_DISPLAY_MIME} payload. Malformed payloads are\n * tolerantly ignored (`undefined`); a well-formed payload exceeding\n * {@link MAX_ATTACHMENT_DATA_CHARS} is reported as `\"oversized\"` so the caller\n * can fail the cell loudly rather than silently dropping the image.\n */\nexport function parseAttachmentDisplay(payload: unknown): KernelAttachment | \"oversized\" | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { mime_type: mimeType, data, path } = payload;\n\tif (typeof mimeType !== \"string\" || typeof data !== \"string\") {\n\t\treturn undefined;\n\t}\n\tif (data.length > MAX_ATTACHMENT_DATA_CHARS) {\n\t\treturn \"oversized\";\n\t}\n\treturn { mimeType, data, path: typeof path === \"string\" ? path : undefined };\n}\n\nexport function parseSentAgentMessage(payload: unknown): KernelSentAgentMessage | undefined {\n\tif (!isRecord(payload) || !isRecord(payload.target)) {\n\t\treturn undefined;\n\t}\n\tconst { id, message, deliveryStatus, receiverRole, target } = payload;\n\tconst { activeSessionId, sessionId, sessionName } = target;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof message !== \"string\" ||\n\t\t(deliveryStatus !== \"delivered\" && deliveryStatus !== \"queued\") ||\n\t\ttypeof activeSessionId !== \"string\" ||\n\t\ttypeof sessionId !== \"string\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tid,\n\t\tmessage,\n\t\tdeliveryStatus,\n\t\t...(receiverRole === \"parent\" || receiverRole === \"sibling\" || receiverRole === \"child\" ? { receiverRole } : {}),\n\t\ttarget: {\n\t\t\tactiveSessionId,\n\t\t\tsessionId,\n\t\t\t...(typeof sessionName === \"string\" ? { sessionName } : {}),\n\t\t},\n\t};\n}\n\nexport function createKernelStartupAbortError(): Error {\n\treturn new Error(\"Kernel startup aborted\");\n}\n\nexport function raceStartupWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\treturn Promise.reject(createKernelStartupAbortError());\n\t}\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst cleanup = () => signal.removeEventListener(\"abort\", abort);\n\t\tconst abort = () => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(createKernelStartupAbortError());\n\t\t};\n\t\tsignal.addEventListener(\"abort\", abort, { once: true });\n\t\tpromise.then(\n\t\t\t(value) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nexport interface Deferred<T> {\n\tpromise: Promise<T>;\n\tresolve: (value: T) => void;\n\treject: (error: Error) => void;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nexport function createDeferred<T>(): Deferred<T> {\n\tlet resolve!: (value: T) => void;\n\tlet reject!: (error: Error) => void;\n\tconst promise = new Promise<T>((promiseResolve, promiseReject) => {\n\t\tresolve = promiseResolve;\n\t\treject = promiseReject;\n\t});\n\treturn { promise, resolve, reject };\n}\n\n/** Public surface every kernel client exposes to the provisioner and session layer. */\nexport interface KernelClient {\n\treadonly ownerSessionId: string | undefined;\n\treadonly isRunning: boolean;\n\tstart(options?: KernelStartOptions): Promise<void>;\n\texecute(code: string, opts?: ExecuteOptions): Promise<ExecuteResult>;\n\tshutdown(opts?: { snapshot?: boolean }): Promise<boolean>;\n\trestart(): Promise<void>;\n\tkill(): Promise<void>;\n\tdispose(): Promise<void>;\n\tdisposeSync(): void;\n\tsnapshotState(): Promise<SnapshotResult | null>;\n\tpruneOversizedVariables(): Promise<SnapshotResult | null>;\n\trestoreState(): Promise<RestoreResult | null>;\n\tlistNamespaceNames(signal?: AbortSignal): Promise<string[] | null>;\n}\n\n// One registry serves every client kind; two parallel registries would\n// double-install process signal handlers.\nexport const liveKernels = new Set<KernelClient>();\nlet signalHandlersInstalled = false;\n\nregisterSessionResourceCleanup((sessionId) => {\n\tfor (const k of liveKernels) {\n\t\tif (!sessionId || k.ownerSessionId === sessionId) {\n\t\t\tvoid k.dispose();\n\t\t}\n\t}\n});\n\nexport function installSignalHandlersOnce(): void {\n\tif (signalHandlersInstalled) return;\n\tsignalHandlersInstalled = true;\n\n\tconst asyncShutdown = async (): Promise<void> => {\n\t\t// These paths can await, so flush the namespace snapshot before tearing down.\n\t\tawait Promise.allSettled([...liveKernels].map((k) => k.shutdown({ snapshot: true })));\n\t};\n\n\t// `beforeExit` and signal handlers can await async cleanup. `exit`\n\t// can only do sync work (Node won't run pending microtasks past it),\n\t// so it falls back to `disposeSync()` which kills the child synchronously.\n\tprocess.on(\"beforeExit\", () => {\n\t\tvoid asyncShutdown();\n\t});\n\tprocess.on(\"SIGINT\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(130));\n\t});\n\tprocess.on(\"SIGTERM\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(143));\n\t});\n\tprocess.on(\"exit\", () => {\n\t\tfor (const k of liveKernels) k.disposeSync();\n\t});\n}\n"]}
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/core/kernel/shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerSessionResourceCleanup } from \"@asm-agent/ai\";\nimport type { KernelBootstrapProgressHandler, KernelPythonSkill } from \"./bootstrap.js\";\nimport type { RestoreResult, SnapshotResult } from \"./state-snapshot.js\";\n\nexport const DEFAULT_MAX_OUTPUT_CHARS = 65536;\nexport const HOST_REQUEST_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const KERNEL_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const DEFAULT_SNAPSHOT_DEBOUNCE_MS = 1500;\nexport const SNAPSHOT_EXECUTION_TIMEOUT_MS = 5000;\nexport const KERNEL_ABORT_GRACE_MS = 1000;\nexport const KERNEL_BUSY_REUSE_WAIT_MS = 5000;\nexport const KERNEL_BUSY_INTERRUPT_INTERVAL_MS = 500;\nexport const MAX_LATE_SENT_AGENT_MESSAGE_HANDLERS = 256;\nconst KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE =\n\t\"The Python kernel is still running the previously interrupted cell. Wait and try again, or kill the kernel to start fresh.\";\n\nexport class KernelBusyAfterInterruptError extends Error {\n\tconstructor() {\n\t\tsuper(KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE);\n\t\tthis.name = \"KernelBusyAfterInterruptError\";\n\t}\n}\n\n/**\n * Handles one typed request from Python code running in the kernel.\n * The returned record is delivered verbatim to the Python caller.\n *\n * The unary form is the dispatcher and registration contract; context-aware\n * handlers are declared separately below.\n */\nexport type HostRequestHandler = (payload: Record<string, unknown>) => Promise<Record<string, unknown>>;\n\n/**\n * Per-call authority supplied by the host-request dispatcher.\n * `requestId` is an opaque host-minted correlation token and `isCurrent()`\n * lets an implementation reject work after its authority is revoked.\n */\nexport interface HostRequestContext {\n\treadonly requestId: string;\n\treadonly generation: number;\n\treadonly signal: AbortSignal;\n\tisCurrent(): boolean;\n}\n\nconst hostRequestHandlerBrand = Symbol(\"hostRequestHandler\");\n\n/** A context-aware implementation that must receive dispatcher authority. */\nexport type HostRequestHandlerImplementation = (\n\tpayload: Record<string, unknown>,\n\tcontext: HostRequestContext,\n) => Promise<Record<string, unknown>>;\n\n/** A factory-minted, context-aware host-request handler capability. */\ntype HostRequestHandlerCapability = HostRequestHandlerImplementation & { readonly [hostRequestHandlerBrand]: true };\n\n/** Runtime provenance cannot be recreated by copying the nominal symbol property. */\nconst factoryCreatedHostRequestHandlers = new WeakSet<object>();\n\nfunction assertGenuineHostRequestContext(context: unknown): asserts context is HostRequestContext {\n\tif (\n\t\ttypeof context !== \"object\" ||\n\t\tcontext === null ||\n\t\ttypeof (context as HostRequestContext).requestId !== \"string\" ||\n\t\t!(context as HostRequestContext).requestId ||\n\t\t!Number.isSafeInteger((context as HostRequestContext).generation) ||\n\t\ttypeof (context as HostRequestContext).isCurrent !== \"function\" ||\n\t\ttypeof (context as HostRequestContext).signal !== \"object\" ||\n\t\t(context as HostRequestContext).signal === null ||\n\t\ttypeof (context as HostRequestContext).signal.aborted !== \"boolean\" ||\n\t\ttypeof (context as HostRequestContext).signal.addEventListener !== \"function\"\n\t) {\n\t\tthrow new Error(\"host request context is invalid\");\n\t}\n}\n\n/**\n * Creates a branded wrapper rather than mutating its implementation. Both its\n * generic shape and runtime arity reject unary callbacks before they can run.\n */\nexport function createHostRequestHandler<T extends HostRequestHandlerImplementation>(\n\timplementation: T,\n\t..._unaryRejection: Parameters<T> extends [unknown, unknown, ...unknown[]]\n\t\t? []\n\t\t: [\"host request handlers must accept payload and context\"]\n): HostRequestHandlerCapability {\n\tif (implementation.length < 2) throw new Error(\"host request handlers must accept payload and context\");\n\tconst handler = async (payload: Record<string, unknown>, context: HostRequestContext) => {\n\t\tassertGenuineHostRequestContext(context);\n\t\treturn implementation(payload, context);\n\t};\n\tfactoryCreatedHostRequestHandlers.add(handler);\n\treturn Object.defineProperty(handler, hostRequestHandlerBrand, { value: true }) as HostRequestHandlerCapability;\n}\n\n/** Reject copied-symbol and raw-function forgeries before they observe authenticated payloads. */\nexport function assertHostRequestHandler(value: unknown): asserts value is HostRequestHandlerCapability {\n\tif (\n\t\ttypeof value !== \"function\" ||\n\t\t(value as Partial<HostRequestHandlerCapability>)[hostRequestHandlerBrand] !== true ||\n\t\t!factoryCreatedHostRequestHandlers.has(value)\n\t) {\n\t\tthrow new Error(\"host request handler is not a dispatcher-created capability\");\n\t}\n}\n\n/** Host request handlers keyed by request type (e.g. \"rlm.run\", \"goal.complete\"). */\nexport type HostRequestHandlers = Record<string, HostRequestHandler>;\n\n/** Where and how to persist the kernel's user namespace so it survives resume. */\nexport interface KernelSnapshotConfig {\n\t/** Absolute path for the dill payload. */\n\tpath: string;\n\t/** Absolute path for the JSON manifest written alongside the payload. */\n\tmanifestPath: string;\n\t/** Maximum aggregate snapshot size. Default 256 MiB. */\n\tmaxBytes?: number;\n\t/** Maximum serialized size of one variable. Default 16 MiB. */\n\tmaxVariableBytes?: number;\n\t/** Debounce window for the auto-snapshot after a successful execution. Default 1500 ms. */\n\tdebounceMs?: number;\n}\n\nexport interface KernelManagerOptions {\n\t/** Python interpreter with the kernel runtime available. Defaults to the auto-bootstrapped kernel. */\n\tpython?: string;\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tsessionId?: string;\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly KernelPythonSkill[];\n\t/** Persist/revive the user namespace across kernel restarts and session resume. */\n\tsnapshot?: KernelSnapshotConfig;\n\t/** Runtime bootstrap re-run on a protocol-repaired kernel so live handles (rlm, bash, skills) exist again. */\n\tbootstrapCode?: string;\n}\n\nexport interface KernelStartOptions {\n\tonBootstrapProgress?: KernelBootstrapProgressHandler;\n\tsignal?: AbortSignal;\n}\n\nexport interface ExecuteOptions {\n\t/** Aborting interrupts the kernel out-of-band. */\n\tsignal?: AbortSignal;\n\tonStream?: (chunk: string, name: \"stdout\" | \"stderr\") => void;\n\tonLateSentAgentMessage?: (message: KernelSentAgentMessage) => void;\n\t/** Cap stdout / stderr / result at this many characters. Default 65536. */\n\tmaxOutputChars?: number;\n\t/** Synthetic host cell (snapshot/restore/list); excluded from lastCellCode attribution. */\n\tinternal?: boolean;\n\t/** The protocol repair's own restore; exempt from waiting on the repair it belongs to. */\n\tprotocolRepair?: boolean;\n}\n\n/** MIME tag the `edit` skill emits diff payloads under. */\nexport const DIFF_DISPLAY_MIME = \"application/vnd.asm-agent.diff+json\";\n\n/** MIME tag the `attach-image` skill emits media payloads under. */\nexport const ATTACHMENT_DISPLAY_MIME = \"application/vnd.asm-agent.attachment+json\";\n\n/** MIME tag the `agent-message` skill emits after sending a message. */\nexport const AGENT_MESSAGE_DISPLAY_MIME = \"application/vnd.asm-agent.agent-message+json\";\n\n/**\n * Hard ceiling on a single attachment's base64 payload, a defensive guard\n * against a runaway direct display emit. The `attach-image` skill caps\n * its own images well under this (see `_MAX_IMAGE_BYTES`), so a skill-produced\n * attachment is never dropped here — only a non-skill emit can hit this.\n */\nexport const MAX_ATTACHMENT_DATA_CHARS = 10_000_000;\n\n/** One file edit, captured from a {@link DIFF_DISPLAY_MIME} display payload. */\nexport interface KernelDiffDisplay {\n\tpath: string;\n\toldStr: string;\n\tnewStr: string;\n\t/** 1-based line where `oldStr` begins in the file, for absolute line numbers. */\n\tstartLine?: number;\n}\n\n/** One media attachment, captured from an {@link ATTACHMENT_DISPLAY_MIME} display payload. */\nexport interface KernelAttachment {\n\tmimeType: string;\n\t/** base64-encoded bytes. */\n\tdata: string;\n\t/** Source path, surfaced to the TUI renderer. */\n\tpath?: string;\n}\n\nexport interface KernelSentAgentMessage {\n\tid: string;\n\tmessage: string;\n\tdeliveryStatus: \"delivered\" | \"queued\";\n\treceiverRole?: \"parent\" | \"sibling\" | \"child\";\n\ttarget: {\n\t\tactiveSessionId: string;\n\t\tsessionId: string;\n\t\tsessionName?: string;\n\t};\n}\n\nexport interface ExecuteResult {\n\tstdout: string;\n\tstderr: string;\n\t/** Text of the cell's trailing expression value, if the cell produced one. */\n\tresult?: string;\n\t/** Diffs emitted via display events, in order. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments emitted via display events, in order. */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell, in order. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** Output that arrived without this cell's id (user threads, other cells' leftovers, raw fd writes). */\n\tbackgroundOutput?: string;\n\tstatus: \"ok\" | \"error\" | \"aborted\";\n\terror?: { ename: string; evalue: string; traceback: string[] };\n\tdurationMs: number;\n}\n\n/** Parse a {@link DIFF_DISPLAY_MIME} payload, tolerating malformed input. */\nexport function parseDiffDisplay(payload: unknown): KernelDiffDisplay | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { path, old_str: oldStr, new_str: newStr, start_line: startLine } = payload;\n\tif (typeof path !== \"string\" || typeof oldStr !== \"string\" || typeof newStr !== \"string\") {\n\t\treturn undefined;\n\t}\n\treturn { path, oldStr, newStr, startLine: typeof startLine === \"number\" ? startLine : undefined };\n}\n\n/**\n * Parse an {@link ATTACHMENT_DISPLAY_MIME} payload. Malformed payloads are\n * tolerantly ignored (`undefined`); a well-formed payload exceeding\n * {@link MAX_ATTACHMENT_DATA_CHARS} is reported as `\"oversized\"` so the caller\n * can fail the cell loudly rather than silently dropping the image.\n */\nexport function parseAttachmentDisplay(payload: unknown): KernelAttachment | \"oversized\" | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { mime_type: mimeType, data, path } = payload;\n\tif (typeof mimeType !== \"string\" || typeof data !== \"string\") {\n\t\treturn undefined;\n\t}\n\tif (data.length > MAX_ATTACHMENT_DATA_CHARS) {\n\t\treturn \"oversized\";\n\t}\n\treturn { mimeType, data, path: typeof path === \"string\" ? path : undefined };\n}\n\nexport function parseSentAgentMessage(payload: unknown): KernelSentAgentMessage | undefined {\n\tif (!isRecord(payload) || !isRecord(payload.target)) {\n\t\treturn undefined;\n\t}\n\tconst { id, message, deliveryStatus, receiverRole, target } = payload;\n\tconst { activeSessionId, sessionId, sessionName } = target;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof message !== \"string\" ||\n\t\t(deliveryStatus !== \"delivered\" && deliveryStatus !== \"queued\") ||\n\t\ttypeof activeSessionId !== \"string\" ||\n\t\ttypeof sessionId !== \"string\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tid,\n\t\tmessage,\n\t\tdeliveryStatus,\n\t\t...(receiverRole === \"parent\" || receiverRole === \"sibling\" || receiverRole === \"child\" ? { receiverRole } : {}),\n\t\ttarget: {\n\t\t\tactiveSessionId,\n\t\t\tsessionId,\n\t\t\t...(typeof sessionName === \"string\" ? { sessionName } : {}),\n\t\t},\n\t};\n}\n\nexport function createKernelStartupAbortError(): Error {\n\treturn new Error(\"Kernel startup aborted\");\n}\n\nexport function raceStartupWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\treturn Promise.reject(createKernelStartupAbortError());\n\t}\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst cleanup = () => signal.removeEventListener(\"abort\", abort);\n\t\tconst abort = () => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(createKernelStartupAbortError());\n\t\t};\n\t\tsignal.addEventListener(\"abort\", abort, { once: true });\n\t\tpromise.then(\n\t\t\t(value) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nexport interface Deferred<T> {\n\tpromise: Promise<T>;\n\tresolve: (value: T) => void;\n\treject: (error: Error) => void;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nexport function createDeferred<T>(): Deferred<T> {\n\tlet resolve!: (value: T) => void;\n\tlet reject!: (error: Error) => void;\n\tconst promise = new Promise<T>((promiseResolve, promiseReject) => {\n\t\tresolve = promiseResolve;\n\t\treject = promiseReject;\n\t});\n\treturn { promise, resolve, reject };\n}\n\nexport interface KernelShutdownOptions {\n\tsnapshot?: boolean;\n\tdrainHostRequests?: boolean;\n}\n\n/** Public surface every kernel client exposes to the provisioner and session layer. */\nexport interface KernelClient {\n\treadonly ownerSessionId: string | undefined;\n\treadonly isRunning: boolean;\n\tstart(options?: KernelStartOptions): Promise<void>;\n\texecute(code: string, opts?: ExecuteOptions): Promise<ExecuteResult>;\n\tshutdown(opts?: KernelShutdownOptions): Promise<boolean>;\n\trestart(): Promise<void>;\n\tkill(): Promise<void>;\n\tdisposeSync(): void;\n\tsnapshotState(): Promise<SnapshotResult | null>;\n\tpruneOversizedVariables(): Promise<SnapshotResult | null>;\n\trestoreState(): Promise<RestoreResult | null>;\n\tlistNamespaceNames(signal?: AbortSignal): Promise<string[] | null>;\n}\n\n// One registry serves every client kind; two parallel registries would\n// double-install process signal handlers.\nexport const liveKernels = new Set<KernelClient>();\nlet signalHandlersInstalled = false;\n\nregisterSessionResourceCleanup((sessionId) => {\n\tfor (const k of liveKernels) {\n\t\tif (!sessionId || k.ownerSessionId === sessionId) {\n\t\t\tvoid k.shutdown({ snapshot: true, drainHostRequests: true });\n\t\t}\n\t}\n});\n\nexport function installSignalHandlersOnce(): void {\n\tif (signalHandlersInstalled) return;\n\tsignalHandlersInstalled = true;\n\n\tconst asyncShutdown = async (): Promise<void> => {\n\t\t// These paths can await, so flush the namespace snapshot before tearing down.\n\t\tawait Promise.allSettled([...liveKernels].map((k) => k.shutdown({ snapshot: true })));\n\t};\n\n\t// `beforeExit` and signal handlers can await async cleanup. `exit`\n\t// can only do sync work (Node won't run pending microtasks past it),\n\t// so it falls back to `disposeSync()` which kills the child synchronously.\n\tprocess.on(\"beforeExit\", () => {\n\t\tvoid asyncShutdown();\n\t});\n\tprocess.on(\"SIGINT\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(130));\n\t});\n\tprocess.on(\"SIGTERM\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(143));\n\t});\n\tprocess.on(\"exit\", () => {\n\t\tfor (const k of liveKernels) k.disposeSync();\n\t});\n}\n"]}
@@ -37,8 +37,10 @@ export declare class McpManager {
37
37
  getDisabledBuiltinSkillOverrides(): string[];
38
38
  /** Host-request handlers exposed to the kernel. */
39
39
  hostHandlers(): Record<string, (payload: Record<string, unknown>) => Promise<Record<string, unknown>>>;
40
- /** Enabled persistent and session-scoped servers available through the generic kernel API. */
41
- getEnabledGenericServers(): string[];
40
+ /** Session-scoped servers supplied by the active ACP client. */
41
+ getAcpServers(): AcpMcpServerConfig[];
42
+ /** Enabled user-declared servers available through the generic kernel API. */
43
+ getEnabledPersistentGenericServers(): string[];
42
44
  /** Status for the /mcp list command. */
43
45
  listStatus(): Array<{
44
46
  server: string;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-manager.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/mcp-manager.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,qFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,SAAS,CAAC;IACnE,kFAAkF;IAClF,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAcD,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoD;IACnF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAoC;IAChE,OAAO,CAAC,YAAY,CAA0C;IAC9D,OAAO,CAAC,UAAU,CAAyC;IAC3D,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,qFAAqF;IACrF,OAAO,CAAC,yBAAyB,CAAqB;IAEtD,YAAY,OAAO,EAAE,iBAAiB,EAMrC;IAED,+EAA+E;IAC/E,OAAO,IAAI,IAAI,CAGd;IAED,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE7C;IAED,iBAAiB,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAqBlF;IAED,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,iBAAiB;IAKzB;;;;OAIG;IACH,qBAAqB,IAAI,IAAI,CAuB5B;IAED,iFAAiF;IACjF,OAAO,CAAC,QAAQ;IAkBhB,gGAAgG;IAChG,gCAAgC,IAAI,MAAM,EAAE,CAS3C;IAED,mDAAmD;IACnD,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAwCrG;IAED,8FAA8F;IAC9F,wBAAwB,IAAI,MAAM,EAAE,CAYnC;IAED,wCAAwC;IACxC,UAAU,IAAI,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAO3F;CACD","sourcesContent":["// Host side of MCP integrations. The protocol itself runs Python-side in the kernel; the host\n// only registers OAuth providers, gates integration skills by auth, and serves mcp.* host-requests.\n\nimport {\n\tBUILTIN_MCP_CATALOG,\n\tcreateMcpOAuthProvider,\n\tgetCatalogEntry,\n\tregisterBuiltinMcpOAuthProviders,\n} from \"@asm-agent/ai/mcp\";\nimport { registerOAuthProvider, unregisterOAuthProvider } from \"@asm-agent/ai/oauth\";\nimport type { AuthStorage } from \"../auth-storage.js\";\nimport type { McpServerConfig } from \"../settings-manager.js\";\nimport type { AcpMcpServerConfig } from \"./acp-mcp-types.js\";\n\nexport interface McpManagerOptions {\n\tauthStorage: AuthStorage;\n\t/** Reads the current Settings.mcpServers (name → config). Re-read on refresh(). */\n\tgetUserServers?: () => Record<string, McpServerConfig> | undefined;\n\t/** Start an interactive host-side login for a server. Provided by the UI mode. */\n\tbeginLogin?: (server: string) => Promise<void>;\n}\n\n/** A resolved integration: a catalog/user entry plus its provider id. */\nconst GENERIC_SERVER_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;\n\ninterface ResolvedIntegration {\n\tserver: string;\n\tlabel: string;\n\tconfig: McpServerConfig;\n\tusesOAuth: boolean;\n\t/** True when this came from Settings.mcpServers (may override a catalog name). */\n\tuserDeclared?: boolean;\n}\n\nexport class McpManager {\n\tprivate readonly authStorage: AuthStorage;\n\tprivate readonly getUserServers: () => Record<string, McpServerConfig> | undefined;\n\tprivate readonly beginLogin?: (server: string) => Promise<void>;\n\tprivate integrations = new Map<string, ResolvedIntegration>();\n\tprivate acpServers = new Map<string, AcpMcpServerConfig>();\n\tprivate acpOwnerId?: string;\n\t/** Provider ids we registered for user servers, so refresh can drop removed ones. */\n\tprivate registeredUserProviderIds = new Set<string>();\n\n\tconstructor(options: McpManagerOptions) {\n\t\tthis.authStorage = options.authStorage;\n\t\tthis.getUserServers = options.getUserServers ?? (() => undefined);\n\t\tthis.beginLogin = options.beginLogin;\n\t\tthis.resolveIntegrations();\n\t\tthis.registerProviders();\n\t}\n\n\t/** Re-read settings and re-register providers; call after a session reload. */\n\trefresh(): void {\n\t\tthis.resolveIntegrations();\n\t\tthis.registerProviders();\n\t}\n\n\tcanReleaseAcpServers(ownerId: string): boolean {\n\t\treturn this.acpOwnerId === undefined || this.acpOwnerId === ownerId;\n\t}\n\n\treplaceAcpServers(servers: readonly AcpMcpServerConfig[], ownerId: string): boolean {\n\t\tif (!ownerId) throw new Error(\"ACP MCP owner id is required\");\n\t\tif (servers.length === 0 && this.acpOwnerId !== ownerId) return false;\n\t\tif (servers.length > 0 && this.acpOwnerId && this.acpOwnerId !== ownerId) {\n\t\t\tthrow new Error(\"ACP MCP configuration is owned by another client\");\n\t\t}\n\n\t\tconst next = new Map<string, AcpMcpServerConfig>();\n\t\tfor (const server of servers) {\n\t\t\tif (next.has(server.name)) throw new Error(`Duplicate ACP MCP server: ${server.name}`);\n\t\t\tnext.set(server.name, server);\n\t\t}\n\t\tconst unchanged =\n\t\t\tnext.size === this.acpServers.size &&\n\t\t\tArray.from(next).every(\n\t\t\t\t([name, config]) => JSON.stringify(this.acpServers.get(name)) === JSON.stringify(config),\n\t\t\t);\n\t\tif (unchanged) return false;\n\t\tthis.acpServers = next;\n\t\tthis.acpOwnerId = next.size > 0 ? ownerId : undefined;\n\t\treturn true;\n\t}\n\n\tprivate providerId(server: string): string {\n\t\treturn `mcp:${server}`;\n\t}\n\n\tprivate resolveIntegrations(): void {\n\t\tconst integrations = new Map<string, ResolvedIntegration>();\n\t\tfor (const entry of BUILTIN_MCP_CATALOG) {\n\t\t\tintegrations.set(entry.server, {\n\t\t\t\tserver: entry.server,\n\t\t\t\tlabel: entry.label,\n\t\t\t\tconfig: { type: \"http\", url: entry.url, oauth: true },\n\t\t\t\tusesOAuth: entry.oauth?.kind === \"oauth\",\n\t\t\t});\n\t\t}\n\t\tfor (const [server, config] of Object.entries(this.getUserServers() ?? {})) {\n\t\t\tintegrations.set(server, {\n\t\t\t\tserver,\n\t\t\t\tlabel: server,\n\t\t\t\tconfig,\n\t\t\t\tusesOAuth: config.type === \"http\" && config.oauth === true,\n\t\t\t\tuserDeclared: true,\n\t\t\t});\n\t\t}\n\t\tthis.integrations = integrations;\n\t}\n\n\tprivate registerProviders(): void {\n\t\tregisterBuiltinMcpOAuthProviders();\n\t\tthis.registerUserProviders();\n\t}\n\n\t/**\n\t * Register OAuth providers for user-declared (non-catalog) servers. Public so it\n\t * can run after ModelRegistry.refresh() resets the registry — otherwise custom\n\t * `mcp:<server>` providers vanish on every refresh (e.g. post-login).\n\t */\n\tregisterUserProviders(): void {\n\t\tconst current = new Set<string>();\n\t\tfor (const integration of this.integrations.values()) {\n\t\t\tif (!integration.userDeclared || integration.config.type !== \"http\" || getCatalogEntry(integration.server)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst id = this.providerId(integration.server);\n\t\t\tif (integration.usesOAuth) {\n\t\t\t\tcurrent.add(id);\n\t\t\t\tregisterOAuthProvider(\n\t\t\t\t\tcreateMcpOAuthProvider({\n\t\t\t\t\t\tserver: integration.server,\n\t\t\t\t\t\tlabel: integration.label,\n\t\t\t\t\t\turl: integration.config.url,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// Drop providers for user servers removed since the last registration.\n\t\tfor (const id of this.registeredUserProviderIds) {\n\t\t\tif (!current.has(id)) unregisterOAuthProvider(id);\n\t\t}\n\t\tthis.registeredUserProviderIds = current;\n\t}\n\n\t/** True when valid credentials exist for the integration (drives enablement). */\n\tprivate isAuthed(integration: ResolvedIntegration): boolean {\n\t\tif (integration.config.enabled === false) return false;\n\t\tif (integration.userDeclared && getCatalogEntry(integration.server)) return false;\n\t\tif (integration.config.type === \"stdio\") return true;\n\t\tconst { bearerTokenEnvVar } = integration.config;\n\t\tif (!integration.usesOAuth && !bearerTokenEnvVar) return true;\n\t\tif (bearerTokenEnvVar && process.env[bearerTokenEnvVar]?.trim()) {\n\t\t\treturn true;\n\t\t}\n\t\tconst cred = this.authStorage.get(this.providerId(integration.server));\n\t\tif (cred === undefined) return false;\n\t\t// Builtin URLs are code-constant; only user-declared endpoints can be retargeted, so only their\n\t\t// tokens must prove where they belong. Mismatched or unbound tokens require re-login.\n\t\tif (!integration.userDeclared) return true;\n\t\tconst endpoint = (cred as { endpoint?: string }).endpoint;\n\t\treturn typeof endpoint === \"string\" && endpoint === integration.config.url;\n\t}\n\n\t/** `-<server>/SKILL.md` overrides for every built-in integration the user isn't logged into. */\n\tgetDisabledBuiltinSkillOverrides(): string[] {\n\t\tconst overrides: string[] = [];\n\t\tfor (const entry of BUILTIN_MCP_CATALOG) {\n\t\t\tconst integration = this.integrations.get(entry.server);\n\t\t\tif (integration && !this.isAuthed(integration)) {\n\t\t\t\toverrides.push(`-${entry.server}/SKILL.md`);\n\t\t\t}\n\t\t}\n\t\treturn overrides;\n\t}\n\n\t/** Host-request handlers exposed to the kernel. */\n\thostHandlers(): Record<string, (payload: Record<string, unknown>) => Promise<Record<string, unknown>>> {\n\t\tconst handlers: Record<string, (payload: Record<string, unknown>) => Promise<Record<string, unknown>>> = {\n\t\t\t\"mcp.refresh\": async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.refresh requires a server\");\n\t\t\t\tif (this.acpServers.has(server)) throw new Error(`ACP MCP server ${server} does not use host OAuth`);\n\t\t\t\t// getApiKey refreshes + rewrites auth.json under lock; Python re-reads.\n\t\t\t\t// Surface failure (throw) instead of a false success so the kernel can\n\t\t\t\t// report a refresh error rather than a misleading \"not enabled\".\n\t\t\t\tconst key = await this.authStorage.getApiKey(this.providerId(server));\n\t\t\t\tif (!key) throw new Error(`Could not refresh credentials for ${server}`);\n\t\t\t\treturn {};\n\t\t\t},\n\t\t\t// Resolved config so the kernel skill connects to the same URL the host\n\t\t\t// registered/authenticated (honors a user's mcpServers `url` override).\n\t\t\t\"mcp.config\": async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.config requires a server\");\n\t\t\t\tconst acpServer = this.acpServers.get(server);\n\t\t\t\tif (acpServer) {\n\t\t\t\t\tconst { name: _name, ...config } = acpServer;\n\t\t\t\t\treturn { ...config, credentialSource: \"acp\" };\n\t\t\t\t}\n\t\t\t\tconst integration = this.integrations.get(server);\n\t\t\t\tif (!integration?.userDeclared || getCatalogEntry(server)) return {};\n\t\t\t\treturn { ...integration.config };\n\t\t\t},\n\t\t};\n\t\t// Only expose begin_login when an interactive login is actually wired, so the\n\t\t// kernel doesn't get a handler whose only behavior is to throw.\n\t\tconst beginLogin = this.beginLogin;\n\t\tif (beginLogin) {\n\t\t\thandlers[\"mcp.begin_login\"] = async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.begin_login requires a server\");\n\t\t\t\tawait beginLogin(server);\n\t\t\t\treturn {};\n\t\t\t};\n\t\t}\n\t\treturn handlers;\n\t}\n\n\t/** Enabled persistent and session-scoped servers available through the generic kernel API. */\n\tgetEnabledGenericServers(): string[] {\n\t\tconst servers = Array.from(this.integrations.values())\n\t\t\t.filter(\n\t\t\t\t(integration) =>\n\t\t\t\t\tintegration.userDeclared &&\n\t\t\t\t\tGENERIC_SERVER_NAME_PATTERN.test(integration.server) &&\n\t\t\t\t\t!getCatalogEntry(integration.server) &&\n\t\t\t\t\tthis.isAuthed(integration),\n\t\t\t)\n\t\t\t.map((integration) => integration.server);\n\t\tfor (const server of this.acpServers.keys()) servers.push(server);\n\t\treturn [...new Set(servers)].sort((left, right) => left.localeCompare(right));\n\t}\n\n\t/** Status for the /mcp list command. */\n\tlistStatus(): Array<{ server: string; label: string; enabled: boolean; usesOAuth: boolean }> {\n\t\treturn Array.from(this.integrations.values()).map((integration) => ({\n\t\t\tserver: integration.server,\n\t\t\tlabel: integration.label,\n\t\t\tenabled: this.isAuthed(integration),\n\t\t\tusesOAuth: integration.usesOAuth,\n\t\t}));\n\t}\n}\n"]}
1
+ {"version":3,"file":"mcp-manager.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/mcp-manager.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,qFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,SAAS,CAAC;IACnE,kFAAkF;IAClF,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAcD,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoD;IACnF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAoC;IAChE,OAAO,CAAC,YAAY,CAA0C;IAC9D,OAAO,CAAC,UAAU,CAAyC;IAC3D,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,qFAAqF;IACrF,OAAO,CAAC,yBAAyB,CAAqB;IAEtD,YAAY,OAAO,EAAE,iBAAiB,EAMrC;IAED,+EAA+E;IAC/E,OAAO,IAAI,IAAI,CAGd;IAED,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE7C;IAED,iBAAiB,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAqBlF;IAED,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,iBAAiB;IAKzB;;;;OAIG;IACH,qBAAqB,IAAI,IAAI,CAuB5B;IAED,iFAAiF;IACjF,OAAO,CAAC,QAAQ;IAkBhB,gGAAgG;IAChG,gCAAgC,IAAI,MAAM,EAAE,CAS3C;IAED,mDAAmD;IACnD,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAwCrG;IAED,gEAAgE;IAChE,aAAa,IAAI,kBAAkB,EAAE,CAEpC;IAED,8EAA8E;IAC9E,kCAAkC,IAAI,MAAM,EAAE,CAW7C;IAED,wCAAwC;IACxC,UAAU,IAAI,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAO3F;CACD","sourcesContent":["// Host side of MCP integrations. The protocol itself runs Python-side in the kernel; the host\n// only registers OAuth providers, gates integration skills by auth, and serves mcp.* host-requests.\n\nimport {\n\tBUILTIN_MCP_CATALOG,\n\tcreateMcpOAuthProvider,\n\tgetCatalogEntry,\n\tregisterBuiltinMcpOAuthProviders,\n} from \"@asm-agent/ai/mcp\";\nimport { registerOAuthProvider, unregisterOAuthProvider } from \"@asm-agent/ai/oauth\";\nimport type { AuthStorage } from \"../auth-storage.js\";\nimport type { McpServerConfig } from \"../settings-manager.js\";\nimport type { AcpMcpServerConfig } from \"./acp-mcp-types.js\";\n\nexport interface McpManagerOptions {\n\tauthStorage: AuthStorage;\n\t/** Reads the current Settings.mcpServers (name → config). Re-read on refresh(). */\n\tgetUserServers?: () => Record<string, McpServerConfig> | undefined;\n\t/** Start an interactive host-side login for a server. Provided by the UI mode. */\n\tbeginLogin?: (server: string) => Promise<void>;\n}\n\n/** A resolved integration: a catalog/user entry plus its provider id. */\nconst GENERIC_SERVER_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;\n\ninterface ResolvedIntegration {\n\tserver: string;\n\tlabel: string;\n\tconfig: McpServerConfig;\n\tusesOAuth: boolean;\n\t/** True when this came from Settings.mcpServers (may override a catalog name). */\n\tuserDeclared?: boolean;\n}\n\nexport class McpManager {\n\tprivate readonly authStorage: AuthStorage;\n\tprivate readonly getUserServers: () => Record<string, McpServerConfig> | undefined;\n\tprivate readonly beginLogin?: (server: string) => Promise<void>;\n\tprivate integrations = new Map<string, ResolvedIntegration>();\n\tprivate acpServers = new Map<string, AcpMcpServerConfig>();\n\tprivate acpOwnerId?: string;\n\t/** Provider ids we registered for user servers, so refresh can drop removed ones. */\n\tprivate registeredUserProviderIds = new Set<string>();\n\n\tconstructor(options: McpManagerOptions) {\n\t\tthis.authStorage = options.authStorage;\n\t\tthis.getUserServers = options.getUserServers ?? (() => undefined);\n\t\tthis.beginLogin = options.beginLogin;\n\t\tthis.resolveIntegrations();\n\t\tthis.registerProviders();\n\t}\n\n\t/** Re-read settings and re-register providers; call after a session reload. */\n\trefresh(): void {\n\t\tthis.resolveIntegrations();\n\t\tthis.registerProviders();\n\t}\n\n\tcanReleaseAcpServers(ownerId: string): boolean {\n\t\treturn this.acpOwnerId === undefined || this.acpOwnerId === ownerId;\n\t}\n\n\treplaceAcpServers(servers: readonly AcpMcpServerConfig[], ownerId: string): boolean {\n\t\tif (!ownerId) throw new Error(\"ACP MCP owner id is required\");\n\t\tif (servers.length === 0 && this.acpOwnerId !== ownerId) return false;\n\t\tif (servers.length > 0 && this.acpOwnerId && this.acpOwnerId !== ownerId) {\n\t\t\tthrow new Error(\"ACP MCP configuration is owned by another client\");\n\t\t}\n\n\t\tconst next = new Map<string, AcpMcpServerConfig>();\n\t\tfor (const server of servers) {\n\t\t\tif (next.has(server.name)) throw new Error(`Duplicate ACP MCP server: ${server.name}`);\n\t\t\tnext.set(server.name, server);\n\t\t}\n\t\tconst unchanged =\n\t\t\tnext.size === this.acpServers.size &&\n\t\t\tArray.from(next).every(\n\t\t\t\t([name, config]) => JSON.stringify(this.acpServers.get(name)) === JSON.stringify(config),\n\t\t\t);\n\t\tif (unchanged) return false;\n\t\tthis.acpServers = next;\n\t\tthis.acpOwnerId = next.size > 0 ? ownerId : undefined;\n\t\treturn true;\n\t}\n\n\tprivate providerId(server: string): string {\n\t\treturn `mcp:${server}`;\n\t}\n\n\tprivate resolveIntegrations(): void {\n\t\tconst integrations = new Map<string, ResolvedIntegration>();\n\t\tfor (const entry of BUILTIN_MCP_CATALOG) {\n\t\t\tintegrations.set(entry.server, {\n\t\t\t\tserver: entry.server,\n\t\t\t\tlabel: entry.label,\n\t\t\t\tconfig: { type: \"http\", url: entry.url, oauth: true },\n\t\t\t\tusesOAuth: entry.oauth?.kind === \"oauth\",\n\t\t\t});\n\t\t}\n\t\tfor (const [server, config] of Object.entries(this.getUserServers() ?? {})) {\n\t\t\tintegrations.set(server, {\n\t\t\t\tserver,\n\t\t\t\tlabel: server,\n\t\t\t\tconfig,\n\t\t\t\tusesOAuth: config.type === \"http\" && config.oauth === true,\n\t\t\t\tuserDeclared: true,\n\t\t\t});\n\t\t}\n\t\tthis.integrations = integrations;\n\t}\n\n\tprivate registerProviders(): void {\n\t\tregisterBuiltinMcpOAuthProviders();\n\t\tthis.registerUserProviders();\n\t}\n\n\t/**\n\t * Register OAuth providers for user-declared (non-catalog) servers. Public so it\n\t * can run after ModelRegistry.refresh() resets the registry — otherwise custom\n\t * `mcp:<server>` providers vanish on every refresh (e.g. post-login).\n\t */\n\tregisterUserProviders(): void {\n\t\tconst current = new Set<string>();\n\t\tfor (const integration of this.integrations.values()) {\n\t\t\tif (!integration.userDeclared || integration.config.type !== \"http\" || getCatalogEntry(integration.server)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst id = this.providerId(integration.server);\n\t\t\tif (integration.usesOAuth) {\n\t\t\t\tcurrent.add(id);\n\t\t\t\tregisterOAuthProvider(\n\t\t\t\t\tcreateMcpOAuthProvider({\n\t\t\t\t\t\tserver: integration.server,\n\t\t\t\t\t\tlabel: integration.label,\n\t\t\t\t\t\turl: integration.config.url,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// Drop providers for user servers removed since the last registration.\n\t\tfor (const id of this.registeredUserProviderIds) {\n\t\t\tif (!current.has(id)) unregisterOAuthProvider(id);\n\t\t}\n\t\tthis.registeredUserProviderIds = current;\n\t}\n\n\t/** True when valid credentials exist for the integration (drives enablement). */\n\tprivate isAuthed(integration: ResolvedIntegration): boolean {\n\t\tif (integration.config.enabled === false) return false;\n\t\tif (integration.userDeclared && getCatalogEntry(integration.server)) return false;\n\t\tif (integration.config.type === \"stdio\") return true;\n\t\tconst { bearerTokenEnvVar } = integration.config;\n\t\tif (!integration.usesOAuth && !bearerTokenEnvVar) return true;\n\t\tif (bearerTokenEnvVar && process.env[bearerTokenEnvVar]?.trim()) {\n\t\t\treturn true;\n\t\t}\n\t\tconst cred = this.authStorage.get(this.providerId(integration.server));\n\t\tif (cred === undefined) return false;\n\t\t// Builtin URLs are code-constant; only user-declared endpoints can be retargeted, so only their\n\t\t// tokens must prove where they belong. Mismatched or unbound tokens require re-login.\n\t\tif (!integration.userDeclared) return true;\n\t\tconst endpoint = (cred as { endpoint?: string }).endpoint;\n\t\treturn typeof endpoint === \"string\" && endpoint === integration.config.url;\n\t}\n\n\t/** `-<server>/SKILL.md` overrides for every built-in integration the user isn't logged into. */\n\tgetDisabledBuiltinSkillOverrides(): string[] {\n\t\tconst overrides: string[] = [];\n\t\tfor (const entry of BUILTIN_MCP_CATALOG) {\n\t\t\tconst integration = this.integrations.get(entry.server);\n\t\t\tif (integration && !this.isAuthed(integration)) {\n\t\t\t\toverrides.push(`-${entry.server}/SKILL.md`);\n\t\t\t}\n\t\t}\n\t\treturn overrides;\n\t}\n\n\t/** Host-request handlers exposed to the kernel. */\n\thostHandlers(): Record<string, (payload: Record<string, unknown>) => Promise<Record<string, unknown>>> {\n\t\tconst handlers: Record<string, (payload: Record<string, unknown>) => Promise<Record<string, unknown>>> = {\n\t\t\t\"mcp.refresh\": async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.refresh requires a server\");\n\t\t\t\tif (this.acpServers.has(server)) throw new Error(`ACP MCP server ${server} does not use host OAuth`);\n\t\t\t\t// getApiKey refreshes + rewrites auth.json under lock; Python re-reads.\n\t\t\t\t// Surface failure (throw) instead of a false success so the kernel can\n\t\t\t\t// report a refresh error rather than a misleading \"not enabled\".\n\t\t\t\tconst key = await this.authStorage.getApiKey(this.providerId(server));\n\t\t\t\tif (!key) throw new Error(`Could not refresh credentials for ${server}`);\n\t\t\t\treturn {};\n\t\t\t},\n\t\t\t// Resolved config so the kernel skill connects to the same URL the host\n\t\t\t// registered/authenticated (honors a user's mcpServers `url` override).\n\t\t\t\"mcp.config\": async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.config requires a server\");\n\t\t\t\tconst acpServer = this.acpServers.get(server);\n\t\t\t\tif (acpServer) {\n\t\t\t\t\tconst { name: _name, ...config } = acpServer;\n\t\t\t\t\treturn { ...config, credentialSource: \"acp\" };\n\t\t\t\t}\n\t\t\t\tconst integration = this.integrations.get(server);\n\t\t\t\tif (!integration?.userDeclared || getCatalogEntry(server)) return {};\n\t\t\t\treturn { ...integration.config };\n\t\t\t},\n\t\t};\n\t\t// Only expose begin_login when an interactive login is actually wired, so the\n\t\t// kernel doesn't get a handler whose only behavior is to throw.\n\t\tconst beginLogin = this.beginLogin;\n\t\tif (beginLogin) {\n\t\t\thandlers[\"mcp.begin_login\"] = async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.begin_login requires a server\");\n\t\t\t\tawait beginLogin(server);\n\t\t\t\treturn {};\n\t\t\t};\n\t\t}\n\t\treturn handlers;\n\t}\n\n\t/** Session-scoped servers supplied by the active ACP client. */\n\tgetAcpServers(): AcpMcpServerConfig[] {\n\t\treturn [...this.acpServers.values()];\n\t}\n\n\t/** Enabled user-declared servers available through the generic kernel API. */\n\tgetEnabledPersistentGenericServers(): string[] {\n\t\treturn Array.from(this.integrations.values())\n\t\t\t.filter(\n\t\t\t\t(integration) =>\n\t\t\t\t\tintegration.userDeclared &&\n\t\t\t\t\tGENERIC_SERVER_NAME_PATTERN.test(integration.server) &&\n\t\t\t\t\t!getCatalogEntry(integration.server) &&\n\t\t\t\t\tthis.isAuthed(integration),\n\t\t\t)\n\t\t\t.map((integration) => integration.server)\n\t\t\t.sort((left, right) => left.localeCompare(right));\n\t}\n\n\t/** Status for the /mcp list command. */\n\tlistStatus(): Array<{ server: string; label: string; enabled: boolean; usesOAuth: boolean }> {\n\t\treturn Array.from(this.integrations.values()).map((integration) => ({\n\t\t\tserver: integration.server,\n\t\t\tlabel: integration.label,\n\t\t\tenabled: this.isAuthed(integration),\n\t\t\tusesOAuth: integration.usesOAuth,\n\t\t}));\n\t}\n}\n"]}
@@ -189,17 +189,19 @@ export class McpManager {
189
189
  }
190
190
  return handlers;
191
191
  }
192
- /** Enabled persistent and session-scoped servers available through the generic kernel API. */
193
- getEnabledGenericServers() {
194
- const servers = Array.from(this.integrations.values())
192
+ /** Session-scoped servers supplied by the active ACP client. */
193
+ getAcpServers() {
194
+ return [...this.acpServers.values()];
195
+ }
196
+ /** Enabled user-declared servers available through the generic kernel API. */
197
+ getEnabledPersistentGenericServers() {
198
+ return Array.from(this.integrations.values())
195
199
  .filter((integration) => integration.userDeclared &&
196
200
  GENERIC_SERVER_NAME_PATTERN.test(integration.server) &&
197
201
  !getCatalogEntry(integration.server) &&
198
202
  this.isAuthed(integration))
199
- .map((integration) => integration.server);
200
- for (const server of this.acpServers.keys())
201
- servers.push(server);
202
- return [...new Set(servers)].sort((left, right) => left.localeCompare(right));
203
+ .map((integration) => integration.server)
204
+ .sort((left, right) => left.localeCompare(right));
203
205
  }
204
206
  /** Status for the /mcp list command. */
205
207
  listStatus() {
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-manager.js","sourceRoot":"","sources":["../../../src/core/mcp/mcp-manager.ts"],"names":[],"mappings":"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","sourcesContent":["// Host side of MCP integrations. The protocol itself runs Python-side in the kernel; the host\n// only registers OAuth providers, gates integration skills by auth, and serves mcp.* host-requests.\n\nimport {\n\tBUILTIN_MCP_CATALOG,\n\tcreateMcpOAuthProvider,\n\tgetCatalogEntry,\n\tregisterBuiltinMcpOAuthProviders,\n} from \"@asm-agent/ai/mcp\";\nimport { registerOAuthProvider, unregisterOAuthProvider } from \"@asm-agent/ai/oauth\";\nimport type { AuthStorage } from \"../auth-storage.js\";\nimport type { McpServerConfig } from \"../settings-manager.js\";\nimport type { AcpMcpServerConfig } from \"./acp-mcp-types.js\";\n\nexport interface McpManagerOptions {\n\tauthStorage: AuthStorage;\n\t/** Reads the current Settings.mcpServers (name → config). Re-read on refresh(). */\n\tgetUserServers?: () => Record<string, McpServerConfig> | undefined;\n\t/** Start an interactive host-side login for a server. Provided by the UI mode. */\n\tbeginLogin?: (server: string) => Promise<void>;\n}\n\n/** A resolved integration: a catalog/user entry plus its provider id. */\nconst GENERIC_SERVER_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;\n\ninterface ResolvedIntegration {\n\tserver: string;\n\tlabel: string;\n\tconfig: McpServerConfig;\n\tusesOAuth: boolean;\n\t/** True when this came from Settings.mcpServers (may override a catalog name). */\n\tuserDeclared?: boolean;\n}\n\nexport class McpManager {\n\tprivate readonly authStorage: AuthStorage;\n\tprivate readonly getUserServers: () => Record<string, McpServerConfig> | undefined;\n\tprivate readonly beginLogin?: (server: string) => Promise<void>;\n\tprivate integrations = new Map<string, ResolvedIntegration>();\n\tprivate acpServers = new Map<string, AcpMcpServerConfig>();\n\tprivate acpOwnerId?: string;\n\t/** Provider ids we registered for user servers, so refresh can drop removed ones. */\n\tprivate registeredUserProviderIds = new Set<string>();\n\n\tconstructor(options: McpManagerOptions) {\n\t\tthis.authStorage = options.authStorage;\n\t\tthis.getUserServers = options.getUserServers ?? (() => undefined);\n\t\tthis.beginLogin = options.beginLogin;\n\t\tthis.resolveIntegrations();\n\t\tthis.registerProviders();\n\t}\n\n\t/** Re-read settings and re-register providers; call after a session reload. */\n\trefresh(): void {\n\t\tthis.resolveIntegrations();\n\t\tthis.registerProviders();\n\t}\n\n\tcanReleaseAcpServers(ownerId: string): boolean {\n\t\treturn this.acpOwnerId === undefined || this.acpOwnerId === ownerId;\n\t}\n\n\treplaceAcpServers(servers: readonly AcpMcpServerConfig[], ownerId: string): boolean {\n\t\tif (!ownerId) throw new Error(\"ACP MCP owner id is required\");\n\t\tif (servers.length === 0 && this.acpOwnerId !== ownerId) return false;\n\t\tif (servers.length > 0 && this.acpOwnerId && this.acpOwnerId !== ownerId) {\n\t\t\tthrow new Error(\"ACP MCP configuration is owned by another client\");\n\t\t}\n\n\t\tconst next = new Map<string, AcpMcpServerConfig>();\n\t\tfor (const server of servers) {\n\t\t\tif (next.has(server.name)) throw new Error(`Duplicate ACP MCP server: ${server.name}`);\n\t\t\tnext.set(server.name, server);\n\t\t}\n\t\tconst unchanged =\n\t\t\tnext.size === this.acpServers.size &&\n\t\t\tArray.from(next).every(\n\t\t\t\t([name, config]) => JSON.stringify(this.acpServers.get(name)) === JSON.stringify(config),\n\t\t\t);\n\t\tif (unchanged) return false;\n\t\tthis.acpServers = next;\n\t\tthis.acpOwnerId = next.size > 0 ? ownerId : undefined;\n\t\treturn true;\n\t}\n\n\tprivate providerId(server: string): string {\n\t\treturn `mcp:${server}`;\n\t}\n\n\tprivate resolveIntegrations(): void {\n\t\tconst integrations = new Map<string, ResolvedIntegration>();\n\t\tfor (const entry of BUILTIN_MCP_CATALOG) {\n\t\t\tintegrations.set(entry.server, {\n\t\t\t\tserver: entry.server,\n\t\t\t\tlabel: entry.label,\n\t\t\t\tconfig: { type: \"http\", url: entry.url, oauth: true },\n\t\t\t\tusesOAuth: entry.oauth?.kind === \"oauth\",\n\t\t\t});\n\t\t}\n\t\tfor (const [server, config] of Object.entries(this.getUserServers() ?? {})) {\n\t\t\tintegrations.set(server, {\n\t\t\t\tserver,\n\t\t\t\tlabel: server,\n\t\t\t\tconfig,\n\t\t\t\tusesOAuth: config.type === \"http\" && config.oauth === true,\n\t\t\t\tuserDeclared: true,\n\t\t\t});\n\t\t}\n\t\tthis.integrations = integrations;\n\t}\n\n\tprivate registerProviders(): void {\n\t\tregisterBuiltinMcpOAuthProviders();\n\t\tthis.registerUserProviders();\n\t}\n\n\t/**\n\t * Register OAuth providers for user-declared (non-catalog) servers. Public so it\n\t * can run after ModelRegistry.refresh() resets the registry — otherwise custom\n\t * `mcp:<server>` providers vanish on every refresh (e.g. post-login).\n\t */\n\tregisterUserProviders(): void {\n\t\tconst current = new Set<string>();\n\t\tfor (const integration of this.integrations.values()) {\n\t\t\tif (!integration.userDeclared || integration.config.type !== \"http\" || getCatalogEntry(integration.server)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst id = this.providerId(integration.server);\n\t\t\tif (integration.usesOAuth) {\n\t\t\t\tcurrent.add(id);\n\t\t\t\tregisterOAuthProvider(\n\t\t\t\t\tcreateMcpOAuthProvider({\n\t\t\t\t\t\tserver: integration.server,\n\t\t\t\t\t\tlabel: integration.label,\n\t\t\t\t\t\turl: integration.config.url,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// Drop providers for user servers removed since the last registration.\n\t\tfor (const id of this.registeredUserProviderIds) {\n\t\t\tif (!current.has(id)) unregisterOAuthProvider(id);\n\t\t}\n\t\tthis.registeredUserProviderIds = current;\n\t}\n\n\t/** True when valid credentials exist for the integration (drives enablement). */\n\tprivate isAuthed(integration: ResolvedIntegration): boolean {\n\t\tif (integration.config.enabled === false) return false;\n\t\tif (integration.userDeclared && getCatalogEntry(integration.server)) return false;\n\t\tif (integration.config.type === \"stdio\") return true;\n\t\tconst { bearerTokenEnvVar } = integration.config;\n\t\tif (!integration.usesOAuth && !bearerTokenEnvVar) return true;\n\t\tif (bearerTokenEnvVar && process.env[bearerTokenEnvVar]?.trim()) {\n\t\t\treturn true;\n\t\t}\n\t\tconst cred = this.authStorage.get(this.providerId(integration.server));\n\t\tif (cred === undefined) return false;\n\t\t// Builtin URLs are code-constant; only user-declared endpoints can be retargeted, so only their\n\t\t// tokens must prove where they belong. Mismatched or unbound tokens require re-login.\n\t\tif (!integration.userDeclared) return true;\n\t\tconst endpoint = (cred as { endpoint?: string }).endpoint;\n\t\treturn typeof endpoint === \"string\" && endpoint === integration.config.url;\n\t}\n\n\t/** `-<server>/SKILL.md` overrides for every built-in integration the user isn't logged into. */\n\tgetDisabledBuiltinSkillOverrides(): string[] {\n\t\tconst overrides: string[] = [];\n\t\tfor (const entry of BUILTIN_MCP_CATALOG) {\n\t\t\tconst integration = this.integrations.get(entry.server);\n\t\t\tif (integration && !this.isAuthed(integration)) {\n\t\t\t\toverrides.push(`-${entry.server}/SKILL.md`);\n\t\t\t}\n\t\t}\n\t\treturn overrides;\n\t}\n\n\t/** Host-request handlers exposed to the kernel. */\n\thostHandlers(): Record<string, (payload: Record<string, unknown>) => Promise<Record<string, unknown>>> {\n\t\tconst handlers: Record<string, (payload: Record<string, unknown>) => Promise<Record<string, unknown>>> = {\n\t\t\t\"mcp.refresh\": async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.refresh requires a server\");\n\t\t\t\tif (this.acpServers.has(server)) throw new Error(`ACP MCP server ${server} does not use host OAuth`);\n\t\t\t\t// getApiKey refreshes + rewrites auth.json under lock; Python re-reads.\n\t\t\t\t// Surface failure (throw) instead of a false success so the kernel can\n\t\t\t\t// report a refresh error rather than a misleading \"not enabled\".\n\t\t\t\tconst key = await this.authStorage.getApiKey(this.providerId(server));\n\t\t\t\tif (!key) throw new Error(`Could not refresh credentials for ${server}`);\n\t\t\t\treturn {};\n\t\t\t},\n\t\t\t// Resolved config so the kernel skill connects to the same URL the host\n\t\t\t// registered/authenticated (honors a user's mcpServers `url` override).\n\t\t\t\"mcp.config\": async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.config requires a server\");\n\t\t\t\tconst acpServer = this.acpServers.get(server);\n\t\t\t\tif (acpServer) {\n\t\t\t\t\tconst { name: _name, ...config } = acpServer;\n\t\t\t\t\treturn { ...config, credentialSource: \"acp\" };\n\t\t\t\t}\n\t\t\t\tconst integration = this.integrations.get(server);\n\t\t\t\tif (!integration?.userDeclared || getCatalogEntry(server)) return {};\n\t\t\t\treturn { ...integration.config };\n\t\t\t},\n\t\t};\n\t\t// Only expose begin_login when an interactive login is actually wired, so the\n\t\t// kernel doesn't get a handler whose only behavior is to throw.\n\t\tconst beginLogin = this.beginLogin;\n\t\tif (beginLogin) {\n\t\t\thandlers[\"mcp.begin_login\"] = async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.begin_login requires a server\");\n\t\t\t\tawait beginLogin(server);\n\t\t\t\treturn {};\n\t\t\t};\n\t\t}\n\t\treturn handlers;\n\t}\n\n\t/** Enabled persistent and session-scoped servers available through the generic kernel API. */\n\tgetEnabledGenericServers(): string[] {\n\t\tconst servers = Array.from(this.integrations.values())\n\t\t\t.filter(\n\t\t\t\t(integration) =>\n\t\t\t\t\tintegration.userDeclared &&\n\t\t\t\t\tGENERIC_SERVER_NAME_PATTERN.test(integration.server) &&\n\t\t\t\t\t!getCatalogEntry(integration.server) &&\n\t\t\t\t\tthis.isAuthed(integration),\n\t\t\t)\n\t\t\t.map((integration) => integration.server);\n\t\tfor (const server of this.acpServers.keys()) servers.push(server);\n\t\treturn [...new Set(servers)].sort((left, right) => left.localeCompare(right));\n\t}\n\n\t/** Status for the /mcp list command. */\n\tlistStatus(): Array<{ server: string; label: string; enabled: boolean; usesOAuth: boolean }> {\n\t\treturn Array.from(this.integrations.values()).map((integration) => ({\n\t\t\tserver: integration.server,\n\t\t\tlabel: integration.label,\n\t\t\tenabled: this.isAuthed(integration),\n\t\t\tusesOAuth: integration.usesOAuth,\n\t\t}));\n\t}\n}\n"]}
1
+ {"version":3,"file":"mcp-manager.js","sourceRoot":"","sources":["../../../src/core/mcp/mcp-manager.ts"],"names":[],"mappings":"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","sourcesContent":["// Host side of MCP integrations. The protocol itself runs Python-side in the kernel; the host\n// only registers OAuth providers, gates integration skills by auth, and serves mcp.* host-requests.\n\nimport {\n\tBUILTIN_MCP_CATALOG,\n\tcreateMcpOAuthProvider,\n\tgetCatalogEntry,\n\tregisterBuiltinMcpOAuthProviders,\n} from \"@asm-agent/ai/mcp\";\nimport { registerOAuthProvider, unregisterOAuthProvider } from \"@asm-agent/ai/oauth\";\nimport type { AuthStorage } from \"../auth-storage.js\";\nimport type { McpServerConfig } from \"../settings-manager.js\";\nimport type { AcpMcpServerConfig } from \"./acp-mcp-types.js\";\n\nexport interface McpManagerOptions {\n\tauthStorage: AuthStorage;\n\t/** Reads the current Settings.mcpServers (name → config). Re-read on refresh(). */\n\tgetUserServers?: () => Record<string, McpServerConfig> | undefined;\n\t/** Start an interactive host-side login for a server. Provided by the UI mode. */\n\tbeginLogin?: (server: string) => Promise<void>;\n}\n\n/** A resolved integration: a catalog/user entry plus its provider id. */\nconst GENERIC_SERVER_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;\n\ninterface ResolvedIntegration {\n\tserver: string;\n\tlabel: string;\n\tconfig: McpServerConfig;\n\tusesOAuth: boolean;\n\t/** True when this came from Settings.mcpServers (may override a catalog name). */\n\tuserDeclared?: boolean;\n}\n\nexport class McpManager {\n\tprivate readonly authStorage: AuthStorage;\n\tprivate readonly getUserServers: () => Record<string, McpServerConfig> | undefined;\n\tprivate readonly beginLogin?: (server: string) => Promise<void>;\n\tprivate integrations = new Map<string, ResolvedIntegration>();\n\tprivate acpServers = new Map<string, AcpMcpServerConfig>();\n\tprivate acpOwnerId?: string;\n\t/** Provider ids we registered for user servers, so refresh can drop removed ones. */\n\tprivate registeredUserProviderIds = new Set<string>();\n\n\tconstructor(options: McpManagerOptions) {\n\t\tthis.authStorage = options.authStorage;\n\t\tthis.getUserServers = options.getUserServers ?? (() => undefined);\n\t\tthis.beginLogin = options.beginLogin;\n\t\tthis.resolveIntegrations();\n\t\tthis.registerProviders();\n\t}\n\n\t/** Re-read settings and re-register providers; call after a session reload. */\n\trefresh(): void {\n\t\tthis.resolveIntegrations();\n\t\tthis.registerProviders();\n\t}\n\n\tcanReleaseAcpServers(ownerId: string): boolean {\n\t\treturn this.acpOwnerId === undefined || this.acpOwnerId === ownerId;\n\t}\n\n\treplaceAcpServers(servers: readonly AcpMcpServerConfig[], ownerId: string): boolean {\n\t\tif (!ownerId) throw new Error(\"ACP MCP owner id is required\");\n\t\tif (servers.length === 0 && this.acpOwnerId !== ownerId) return false;\n\t\tif (servers.length > 0 && this.acpOwnerId && this.acpOwnerId !== ownerId) {\n\t\t\tthrow new Error(\"ACP MCP configuration is owned by another client\");\n\t\t}\n\n\t\tconst next = new Map<string, AcpMcpServerConfig>();\n\t\tfor (const server of servers) {\n\t\t\tif (next.has(server.name)) throw new Error(`Duplicate ACP MCP server: ${server.name}`);\n\t\t\tnext.set(server.name, server);\n\t\t}\n\t\tconst unchanged =\n\t\t\tnext.size === this.acpServers.size &&\n\t\t\tArray.from(next).every(\n\t\t\t\t([name, config]) => JSON.stringify(this.acpServers.get(name)) === JSON.stringify(config),\n\t\t\t);\n\t\tif (unchanged) return false;\n\t\tthis.acpServers = next;\n\t\tthis.acpOwnerId = next.size > 0 ? ownerId : undefined;\n\t\treturn true;\n\t}\n\n\tprivate providerId(server: string): string {\n\t\treturn `mcp:${server}`;\n\t}\n\n\tprivate resolveIntegrations(): void {\n\t\tconst integrations = new Map<string, ResolvedIntegration>();\n\t\tfor (const entry of BUILTIN_MCP_CATALOG) {\n\t\t\tintegrations.set(entry.server, {\n\t\t\t\tserver: entry.server,\n\t\t\t\tlabel: entry.label,\n\t\t\t\tconfig: { type: \"http\", url: entry.url, oauth: true },\n\t\t\t\tusesOAuth: entry.oauth?.kind === \"oauth\",\n\t\t\t});\n\t\t}\n\t\tfor (const [server, config] of Object.entries(this.getUserServers() ?? {})) {\n\t\t\tintegrations.set(server, {\n\t\t\t\tserver,\n\t\t\t\tlabel: server,\n\t\t\t\tconfig,\n\t\t\t\tusesOAuth: config.type === \"http\" && config.oauth === true,\n\t\t\t\tuserDeclared: true,\n\t\t\t});\n\t\t}\n\t\tthis.integrations = integrations;\n\t}\n\n\tprivate registerProviders(): void {\n\t\tregisterBuiltinMcpOAuthProviders();\n\t\tthis.registerUserProviders();\n\t}\n\n\t/**\n\t * Register OAuth providers for user-declared (non-catalog) servers. Public so it\n\t * can run after ModelRegistry.refresh() resets the registry — otherwise custom\n\t * `mcp:<server>` providers vanish on every refresh (e.g. post-login).\n\t */\n\tregisterUserProviders(): void {\n\t\tconst current = new Set<string>();\n\t\tfor (const integration of this.integrations.values()) {\n\t\t\tif (!integration.userDeclared || integration.config.type !== \"http\" || getCatalogEntry(integration.server)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst id = this.providerId(integration.server);\n\t\t\tif (integration.usesOAuth) {\n\t\t\t\tcurrent.add(id);\n\t\t\t\tregisterOAuthProvider(\n\t\t\t\t\tcreateMcpOAuthProvider({\n\t\t\t\t\t\tserver: integration.server,\n\t\t\t\t\t\tlabel: integration.label,\n\t\t\t\t\t\turl: integration.config.url,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// Drop providers for user servers removed since the last registration.\n\t\tfor (const id of this.registeredUserProviderIds) {\n\t\t\tif (!current.has(id)) unregisterOAuthProvider(id);\n\t\t}\n\t\tthis.registeredUserProviderIds = current;\n\t}\n\n\t/** True when valid credentials exist for the integration (drives enablement). */\n\tprivate isAuthed(integration: ResolvedIntegration): boolean {\n\t\tif (integration.config.enabled === false) return false;\n\t\tif (integration.userDeclared && getCatalogEntry(integration.server)) return false;\n\t\tif (integration.config.type === \"stdio\") return true;\n\t\tconst { bearerTokenEnvVar } = integration.config;\n\t\tif (!integration.usesOAuth && !bearerTokenEnvVar) return true;\n\t\tif (bearerTokenEnvVar && process.env[bearerTokenEnvVar]?.trim()) {\n\t\t\treturn true;\n\t\t}\n\t\tconst cred = this.authStorage.get(this.providerId(integration.server));\n\t\tif (cred === undefined) return false;\n\t\t// Builtin URLs are code-constant; only user-declared endpoints can be retargeted, so only their\n\t\t// tokens must prove where they belong. Mismatched or unbound tokens require re-login.\n\t\tif (!integration.userDeclared) return true;\n\t\tconst endpoint = (cred as { endpoint?: string }).endpoint;\n\t\treturn typeof endpoint === \"string\" && endpoint === integration.config.url;\n\t}\n\n\t/** `-<server>/SKILL.md` overrides for every built-in integration the user isn't logged into. */\n\tgetDisabledBuiltinSkillOverrides(): string[] {\n\t\tconst overrides: string[] = [];\n\t\tfor (const entry of BUILTIN_MCP_CATALOG) {\n\t\t\tconst integration = this.integrations.get(entry.server);\n\t\t\tif (integration && !this.isAuthed(integration)) {\n\t\t\t\toverrides.push(`-${entry.server}/SKILL.md`);\n\t\t\t}\n\t\t}\n\t\treturn overrides;\n\t}\n\n\t/** Host-request handlers exposed to the kernel. */\n\thostHandlers(): Record<string, (payload: Record<string, unknown>) => Promise<Record<string, unknown>>> {\n\t\tconst handlers: Record<string, (payload: Record<string, unknown>) => Promise<Record<string, unknown>>> = {\n\t\t\t\"mcp.refresh\": async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.refresh requires a server\");\n\t\t\t\tif (this.acpServers.has(server)) throw new Error(`ACP MCP server ${server} does not use host OAuth`);\n\t\t\t\t// getApiKey refreshes + rewrites auth.json under lock; Python re-reads.\n\t\t\t\t// Surface failure (throw) instead of a false success so the kernel can\n\t\t\t\t// report a refresh error rather than a misleading \"not enabled\".\n\t\t\t\tconst key = await this.authStorage.getApiKey(this.providerId(server));\n\t\t\t\tif (!key) throw new Error(`Could not refresh credentials for ${server}`);\n\t\t\t\treturn {};\n\t\t\t},\n\t\t\t// Resolved config so the kernel skill connects to the same URL the host\n\t\t\t// registered/authenticated (honors a user's mcpServers `url` override).\n\t\t\t\"mcp.config\": async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.config requires a server\");\n\t\t\t\tconst acpServer = this.acpServers.get(server);\n\t\t\t\tif (acpServer) {\n\t\t\t\t\tconst { name: _name, ...config } = acpServer;\n\t\t\t\t\treturn { ...config, credentialSource: \"acp\" };\n\t\t\t\t}\n\t\t\t\tconst integration = this.integrations.get(server);\n\t\t\t\tif (!integration?.userDeclared || getCatalogEntry(server)) return {};\n\t\t\t\treturn { ...integration.config };\n\t\t\t},\n\t\t};\n\t\t// Only expose begin_login when an interactive login is actually wired, so the\n\t\t// kernel doesn't get a handler whose only behavior is to throw.\n\t\tconst beginLogin = this.beginLogin;\n\t\tif (beginLogin) {\n\t\t\thandlers[\"mcp.begin_login\"] = async (payload) => {\n\t\t\t\tconst server = String(payload.server ?? \"\");\n\t\t\t\tif (!server) throw new Error(\"mcp.begin_login requires a server\");\n\t\t\t\tawait beginLogin(server);\n\t\t\t\treturn {};\n\t\t\t};\n\t\t}\n\t\treturn handlers;\n\t}\n\n\t/** Session-scoped servers supplied by the active ACP client. */\n\tgetAcpServers(): AcpMcpServerConfig[] {\n\t\treturn [...this.acpServers.values()];\n\t}\n\n\t/** Enabled user-declared servers available through the generic kernel API. */\n\tgetEnabledPersistentGenericServers(): string[] {\n\t\treturn Array.from(this.integrations.values())\n\t\t\t.filter(\n\t\t\t\t(integration) =>\n\t\t\t\t\tintegration.userDeclared &&\n\t\t\t\t\tGENERIC_SERVER_NAME_PATTERN.test(integration.server) &&\n\t\t\t\t\t!getCatalogEntry(integration.server) &&\n\t\t\t\t\tthis.isAuthed(integration),\n\t\t\t)\n\t\t\t.map((integration) => integration.server)\n\t\t\t.sort((left, right) => left.localeCompare(right));\n\t}\n\n\t/** Status for the /mcp list command. */\n\tlistStatus(): Array<{ server: string; label: string; enabled: boolean; usesOAuth: boolean }> {\n\t\treturn Array.from(this.integrations.values()).map((integration) => ({\n\t\t\tserver: integration.server,\n\t\t\tlabel: integration.label,\n\t\t\tenabled: this.isAuthed(integration),\n\t\t\tusesOAuth: integration.usesOAuth,\n\t\t}));\n\t}\n}\n"]}