@asm-agent/coding-agent 0.8.7 → 0.9.0

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 (195) 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__/_winjob.cpython-313.pyc +0 -0
  9. package/dist/asm-agent-runtime/src/rlm/__pycache__/bash.cpython-312.pyc +0 -0
  10. package/dist/asm-agent-runtime/src/rlm/__pycache__/bash.cpython-313.pyc +0 -0
  11. package/dist/asm-agent-runtime/src/rlm/__pycache__/harness.cpython-313.pyc +0 -0
  12. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp.cpython-312.pyc +0 -0
  13. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp.cpython-313.pyc +0 -0
  14. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp_base.cpython-312.pyc +0 -0
  15. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp_base.cpython-313.pyc +0 -0
  16. package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-312.pyc +0 -0
  17. package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-313.pyc +0 -0
  18. package/dist/asm-agent-runtime/src/rlm/__pycache__/skill.cpython-313.pyc +0 -0
  19. package/dist/asm-agent-runtime/src/rlm/bash.py +94 -31
  20. package/dist/asm-agent-runtime/src/rlm/repl.md +3 -3
  21. package/dist/asm-agent-runtime/src/rlm/repl.py +46 -45
  22. package/dist/asm-agent-runtime/test/__pycache__/test_agent_message_skill.cpython-313.pyc +0 -0
  23. package/dist/asm-agent-runtime/test/__pycache__/test_bash.cpython-313.pyc +0 -0
  24. package/dist/asm-agent-runtime/test/__pycache__/test_harness.cpython-313.pyc +0 -0
  25. package/dist/asm-agent-runtime/test/__pycache__/test_mcp.cpython-313.pyc +0 -0
  26. package/dist/asm-agent-runtime/test/__pycache__/test_mcp_base.cpython-313.pyc +0 -0
  27. package/dist/asm-agent-runtime/test/__pycache__/test_repl.cpython-313.pyc +0 -0
  28. package/dist/asm-agent-runtime/test/__pycache__/test_subagent_registry.cpython-313.pyc +0 -0
  29. package/dist/asm-agent-runtime/test/__pycache__/test_winjob.cpython-313.pyc +0 -0
  30. package/dist/asm-agent-runtime/test/test_bash.py +74 -19
  31. package/dist/asm-agent-runtime/test/test_repl.py +67 -3
  32. package/dist/asm-agent-runtime/uv.lock +446 -155
  33. package/dist/bundle/{anthropic-XSMQHGYB.js → anthropic-ZOZNPFXS.js} +2 -2
  34. package/dist/bundle/{azure-openai-responses-XLQAIKKH.js → azure-openai-responses-TDHCU647.js} +2 -2
  35. package/dist/bundle/{bundled-modules-5OCN73BM.js → bundled-modules-Q2YODC4B.js} +3 -3
  36. package/dist/bundle/{chunk-IAQLPSMN.js → chunk-AMKLNEAK.js} +47 -47
  37. package/dist/bundle/{chunk-2LPSALBF.js → chunk-FPY7FFJS.js} +978 -1821
  38. package/dist/bundle/{chunk-E3YE2Q7A.js → chunk-G6SC4YYW.js} +4973 -1926
  39. package/dist/bundle/{chunk-YPNK4MRU.js → chunk-QLHAP3FT.js} +1 -1
  40. package/dist/bundle/{cli-main-QYX5R3LF.js → cli-main-277AG2BR.js} +3 -3
  41. package/dist/bundle/cli.js +1 -1
  42. package/dist/bundle/{google-ZXJEPWJA.js → google-Q3AJWUOY.js} +1 -1
  43. package/dist/bundle/{google-vertex-WAF3RIJ6.js → google-vertex-2CSNOUBS.js} +1 -1
  44. package/dist/bundle/{main-EOUAQCJH.js → main-TNXWJXIU.js} +3 -3
  45. package/dist/bundle/{mistral-GDZXSXRS.js → mistral-XRYJGLLG.js} +1 -1
  46. package/dist/bundle/{openai-codex-responses-7XMPW4LG.js → openai-codex-responses-C6BRO2BN.js} +2 -2
  47. package/dist/bundle/{openai-completions-LNSVGDMF.js → openai-completions-DNXCK2JW.js} +4 -4
  48. package/dist/bundle/{openai-responses-MFZOBMIT.js → openai-responses-BEMWVIXE.js} +2 -2
  49. package/dist/config.d.ts +0 -1
  50. package/dist/config.d.ts.map +1 -1
  51. package/dist/config.js +0 -3
  52. package/dist/config.js.map +1 -1
  53. package/dist/core/agent-session-config.d.ts +1 -2
  54. package/dist/core/agent-session-config.d.ts.map +1 -1
  55. package/dist/core/agent-session-config.js +0 -3
  56. package/dist/core/agent-session-config.js.map +1 -1
  57. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  58. package/dist/core/agent-session-runtime.js +0 -8
  59. package/dist/core/agent-session-runtime.js.map +1 -1
  60. package/dist/core/agent-session-services.d.ts +0 -2
  61. package/dist/core/agent-session-services.d.ts.map +1 -1
  62. package/dist/core/agent-session-services.js +0 -22
  63. package/dist/core/agent-session-services.js.map +1 -1
  64. package/dist/core/agent-session.d.ts +8 -1
  65. package/dist/core/agent-session.d.ts.map +1 -1
  66. package/dist/core/agent-session.js +100 -22
  67. package/dist/core/agent-session.js.map +1 -1
  68. package/dist/core/kernel/bootstrap.d.ts.map +1 -1
  69. package/dist/core/kernel/bootstrap.js +1 -1
  70. package/dist/core/kernel/bootstrap.js.map +1 -1
  71. package/dist/core/kernel/repl-manager.d.ts +32 -8
  72. package/dist/core/kernel/repl-manager.d.ts.map +1 -1
  73. package/dist/core/kernel/repl-manager.js +401 -102
  74. package/dist/core/kernel/repl-manager.js.map +1 -1
  75. package/dist/core/kernel/shared.d.ts +11 -7
  76. package/dist/core/kernel/shared.d.ts.map +1 -1
  77. package/dist/core/kernel/shared.js +2 -5
  78. package/dist/core/kernel/shared.js.map +1 -1
  79. package/dist/core/mcp/mcp-manager.d.ts +4 -2
  80. package/dist/core/mcp/mcp-manager.d.ts.map +1 -1
  81. package/dist/core/mcp/mcp-manager.js +9 -7
  82. package/dist/core/mcp/mcp-manager.js.map +1 -1
  83. package/dist/core/model-registry.d.ts.map +1 -1
  84. package/dist/core/model-registry.js +1 -1
  85. package/dist/core/model-registry.js.map +1 -1
  86. package/dist/core/ollama-models.d.ts +3 -1
  87. package/dist/core/ollama-models.d.ts.map +1 -1
  88. package/dist/core/ollama-models.js +31 -10
  89. package/dist/core/ollama-models.js.map +1 -1
  90. package/dist/core/prime-inference-auth.d.ts +0 -9
  91. package/dist/core/prime-inference-auth.d.ts.map +1 -1
  92. package/dist/core/prime-inference-auth.js +0 -50
  93. package/dist/core/prime-inference-auth.js.map +1 -1
  94. package/dist/core/provider-display-names.d.ts.map +1 -1
  95. package/dist/core/provider-display-names.js +0 -1
  96. package/dist/core/provider-display-names.js.map +1 -1
  97. package/dist/core/session-manager.d.ts +0 -4
  98. package/dist/core/session-manager.d.ts.map +1 -1
  99. package/dist/core/session-manager.js +0 -22
  100. package/dist/core/session-manager.js.map +1 -1
  101. package/dist/core/settings-manager.d.ts +4 -16
  102. package/dist/core/settings-manager.d.ts.map +1 -1
  103. package/dist/core/settings-manager.js +13 -43
  104. package/dist/core/settings-manager.js.map +1 -1
  105. package/dist/core/slash-commands.d.ts.map +1 -1
  106. package/dist/core/slash-commands.js +0 -5
  107. package/dist/core/slash-commands.js.map +1 -1
  108. package/dist/core/tools/acp-mcp.d.ts +6 -0
  109. package/dist/core/tools/acp-mcp.d.ts.map +1 -0
  110. package/dist/core/tools/acp-mcp.js +86 -0
  111. package/dist/core/tools/acp-mcp.js.map +1 -0
  112. package/dist/core/tools/index.d.ts +1 -0
  113. package/dist/core/tools/index.d.ts.map +1 -1
  114. package/dist/core/tools/index.js +1 -0
  115. package/dist/core/tools/index.js.map +1 -1
  116. package/dist/core/tools/ipython.d.ts.map +1 -1
  117. package/dist/core/tools/ipython.js +9 -4
  118. package/dist/core/tools/ipython.js.map +1 -1
  119. package/dist/main.d.ts.map +1 -1
  120. package/dist/main.js +2 -11
  121. package/dist/main.js.map +1 -1
  122. package/dist/modes/acp/acp-mode.d.ts.map +1 -1
  123. package/dist/modes/acp/acp-mode.js +1 -1
  124. package/dist/modes/acp/acp-mode.js.map +1 -1
  125. package/dist/modes/agent-connection/daemon-agent-connection.d.ts +0 -2
  126. package/dist/modes/agent-connection/daemon-agent-connection.d.ts.map +1 -1
  127. package/dist/modes/agent-connection/daemon-agent-connection.js +0 -2
  128. package/dist/modes/agent-connection/daemon-agent-connection.js.map +1 -1
  129. package/dist/modes/agents-view/agents-view-mode.d.ts.map +1 -1
  130. package/dist/modes/agents-view/agents-view-mode.js +0 -19
  131. package/dist/modes/agents-view/agents-view-mode.js.map +1 -1
  132. package/dist/modes/daemon/daemon-mode.d.ts +1 -10
  133. package/dist/modes/daemon/daemon-mode.d.ts.map +1 -1
  134. package/dist/modes/daemon/daemon-mode.js +134 -274
  135. package/dist/modes/daemon/daemon-mode.js.map +1 -1
  136. package/dist/modes/daemon/daemon-protocol.d.ts +11 -5
  137. package/dist/modes/daemon/daemon-protocol.d.ts.map +1 -1
  138. package/dist/modes/daemon/daemon-protocol.js +6 -8
  139. package/dist/modes/daemon/daemon-protocol.js.map +1 -1
  140. package/dist/modes/daemon/daemon-supervisor.d.ts +0 -3
  141. package/dist/modes/daemon/daemon-supervisor.d.ts.map +1 -1
  142. package/dist/modes/daemon/daemon-supervisor.js +18 -52
  143. package/dist/modes/daemon/daemon-supervisor.js.map +1 -1
  144. package/dist/modes/daemon/daemon-worker-protocol.d.ts +1 -6
  145. package/dist/modes/daemon/daemon-worker-protocol.d.ts.map +1 -1
  146. package/dist/modes/daemon/daemon-worker-protocol.js +0 -2
  147. package/dist/modes/daemon/daemon-worker-protocol.js.map +1 -1
  148. package/dist/modes/interactive/auth-flows.d.ts +0 -2
  149. package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
  150. package/dist/modes/interactive/auth-flows.js +1 -101
  151. package/dist/modes/interactive/auth-flows.js.map +1 -1
  152. package/dist/modes/interactive/components/assistant-message.d.ts +6 -1
  153. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  154. package/dist/modes/interactive/components/assistant-message.js +13 -3
  155. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  156. package/dist/modes/interactive/components/footer.d.ts +1 -1
  157. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  158. package/dist/modes/interactive/components/footer.js +2 -2
  159. package/dist/modes/interactive/components/footer.js.map +1 -1
  160. package/dist/modes/interactive/components/mermaid.d.ts +12 -0
  161. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
  162. package/dist/modes/interactive/components/mermaid.js +63 -0
  163. package/dist/modes/interactive/components/mermaid.js.map +1 -0
  164. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  165. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  166. package/dist/modes/interactive/components/settings-selector.js +10 -0
  167. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  168. package/dist/modes/interactive/feature-hints.d.ts.map +1 -1
  169. package/dist/modes/interactive/feature-hints.js +0 -4
  170. package/dist/modes/interactive/feature-hints.js.map +1 -1
  171. package/dist/modes/interactive/interactive-mode.d.ts +1 -7
  172. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  173. package/dist/modes/interactive/interactive-mode.js +23 -279
  174. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  175. package/dist/utils/version-check.d.ts.map +1 -1
  176. package/dist/utils/version-check.js +1 -1
  177. package/dist/utils/version-check.js.map +1 -1
  178. package/docs/settings.md +1 -31
  179. package/docs/usage.md +1 -4
  180. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  181. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  182. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  183. package/examples/extensions/sandbox/package-lock.json +2 -2
  184. package/examples/extensions/sandbox/package.json +1 -1
  185. package/examples/extensions/with-deps/package-lock.json +2 -2
  186. package/examples/extensions/with-deps/package.json +1 -1
  187. package/package.json +6 -5
  188. package/dist/core/agent-traces.d.ts +0 -130
  189. package/dist/core/agent-traces.d.ts.map +0 -1
  190. package/dist/core/agent-traces.js +0 -746
  191. package/dist/core/agent-traces.js.map +0 -1
  192. package/dist/core/telemetry.d.ts +0 -92
  193. package/dist/core/telemetry.d.ts.map +0 -1
  194. package/dist/core/telemetry.js +0 -633
  195. package/dist/core/telemetry.js.map +0 -1
@@ -0,0 +1,86 @@
1
+ // 48 keeps `mcp_list_tools_<name>` within providers' 64-char tool-name limits.
2
+ const ACP_MCP_SERVER_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,48}$/;
3
+ export function acpMcpToolNames(servers) {
4
+ const names = [];
5
+ const seenServers = new Set();
6
+ for (const server of servers) {
7
+ if (!ACP_MCP_SERVER_NAME_PATTERN.test(server.name)) {
8
+ throw new Error(`Invalid ACP MCP server name: ${server.name}`);
9
+ }
10
+ if (seenServers.has(server.name)) {
11
+ throw new Error(`Duplicate ACP MCP server: ${server.name}`);
12
+ }
13
+ seenServers.add(server.name);
14
+ names.push(`mcp_list_tools_${server.name}`, `mcp_call_${server.name}`);
15
+ }
16
+ return names;
17
+ }
18
+ function executionResult(result) {
19
+ let text = result.stdout;
20
+ if (result.stderr)
21
+ text += `${text ? "\n" : ""}${result.stderr}`;
22
+ if (result.result)
23
+ text += `${text ? "\n" : ""}${result.result}`;
24
+ if (result.error)
25
+ text += `${text ? "\n" : ""}${result.error.traceback.join("\n")}`;
26
+ if (result.status !== "ok") {
27
+ throw new Error(text || `MCP kernel execution ${result.status}`);
28
+ }
29
+ return {
30
+ content: [{ type: "text", text: text || "(empty)" }],
31
+ details: {
32
+ durationMs: result.durationMs,
33
+ status: result.status,
34
+ stdout: result.stdout,
35
+ stderr: result.stderr,
36
+ result: result.result,
37
+ },
38
+ };
39
+ }
40
+ async function executeMcpCode(provisioner, code, signal) {
41
+ const manager = await provisioner.ensure(() => { }, signal);
42
+ return executionResult(await manager.execute(code, { signal }));
43
+ }
44
+ export function createAcpMcpToolDefinitions(servers, provisioner) {
45
+ const names = acpMcpToolNames(servers);
46
+ const definitions = [];
47
+ for (const [index, server] of servers.entries()) {
48
+ const listToolName = names[index * 2];
49
+ const callToolName = names[index * 2 + 1];
50
+ const serverName = JSON.stringify(server.name);
51
+ definitions.push({
52
+ name: listToolName,
53
+ label: `list tools from ${server.name}`,
54
+ description: `List every tool the "${server.name}" MCP server exposes. ` +
55
+ `Call this first, then use ${callToolName} to invoke a specific tool.`,
56
+ parameters: { type: "object", properties: {}, required: [], additionalProperties: false },
57
+ execute: async (_toolCallId, _params, signal, _onUpdate, _ctx) => {
58
+ const code = `print(__import__("json").dumps(await mcp.list_tools(${serverName}), default=str))`;
59
+ return executeMcpCode(provisioner, code, signal);
60
+ },
61
+ });
62
+ definitions.push({
63
+ name: callToolName,
64
+ label: `call tool on ${server.name}`,
65
+ description: `Call a tool on the "${server.name}" MCP server. ` +
66
+ `Use ${listToolName} first to discover available tool names and argument schemas.`,
67
+ parameters: {
68
+ type: "object",
69
+ properties: {
70
+ tool: { type: "string", description: `Tool name on "${server.name}".` },
71
+ arguments: { type: "object", description: "JSON arguments for the tool.", additionalProperties: true },
72
+ },
73
+ required: ["tool", "arguments"],
74
+ additionalProperties: false,
75
+ },
76
+ execute: async (_toolCallId, params, signal, _onUpdate, _ctx) => {
77
+ const { tool, arguments: args } = params;
78
+ const code = `print(__import__("json").dumps(await mcp.call_tool(${serverName}, ${JSON.stringify(tool)}, ` +
79
+ `__import__("json").loads(${JSON.stringify(JSON.stringify(args ?? {}))})), default=str))`;
80
+ return executeMcpCode(provisioner, code, signal);
81
+ },
82
+ });
83
+ }
84
+ return definitions;
85
+ }
86
+ //# sourceMappingURL=acp-mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acp-mcp.js","sourceRoot":"","sources":["../../../src/core/tools/acp-mcp.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ToolDefinition } from \"../extensions/types.js\";\nimport type { ExecuteResult } from \"../kernel/index.js\";\nimport type { AcpMcpServerConfig } from \"../mcp/acp-mcp-types.js\";\nimport type { IpythonKernelProvisioner } from \"./ipython.js\";\n\n// 48 keeps `mcp_list_tools_<name>` within providers' 64-char tool-name limits.\nconst ACP_MCP_SERVER_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,48}$/;\n\nexport function acpMcpToolNames(servers: readonly AcpMcpServerConfig[]): string[] {\n\tconst names: string[] = [];\n\tconst seenServers = new Set<string>();\n\tfor (const server of servers) {\n\t\tif (!ACP_MCP_SERVER_NAME_PATTERN.test(server.name)) {\n\t\t\tthrow new Error(`Invalid ACP MCP server name: ${server.name}`);\n\t\t}\n\t\tif (seenServers.has(server.name)) {\n\t\t\tthrow new Error(`Duplicate ACP MCP server: ${server.name}`);\n\t\t}\n\t\tseenServers.add(server.name);\n\t\tnames.push(`mcp_list_tools_${server.name}`, `mcp_call_${server.name}`);\n\t}\n\treturn names;\n}\n\nfunction executionResult(result: ExecuteResult) {\n\tlet text = result.stdout;\n\tif (result.stderr) text += `${text ? \"\\n\" : \"\"}${result.stderr}`;\n\tif (result.result) text += `${text ? \"\\n\" : \"\"}${result.result}`;\n\tif (result.error) text += `${text ? \"\\n\" : \"\"}${result.error.traceback.join(\"\\n\")}`;\n\tif (result.status !== \"ok\") {\n\t\tthrow new Error(text || `MCP kernel execution ${result.status}`);\n\t}\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text: text || \"(empty)\" }],\n\t\tdetails: {\n\t\t\tdurationMs: result.durationMs,\n\t\t\tstatus: result.status,\n\t\t\tstdout: result.stdout,\n\t\t\tstderr: result.stderr,\n\t\t\tresult: result.result,\n\t\t},\n\t};\n}\n\nasync function executeMcpCode(provisioner: IpythonKernelProvisioner, code: string, signal: AbortSignal | undefined) {\n\tconst manager = await provisioner.ensure(() => {}, signal);\n\treturn executionResult(await manager.execute(code, { signal }));\n}\n\nexport function createAcpMcpToolDefinitions(\n\tservers: readonly AcpMcpServerConfig[],\n\tprovisioner: IpythonKernelProvisioner,\n): ToolDefinition[] {\n\tconst names = acpMcpToolNames(servers);\n\tconst definitions: ToolDefinition[] = [];\n\tfor (const [index, server] of servers.entries()) {\n\t\tconst listToolName = names[index * 2]!;\n\t\tconst callToolName = names[index * 2 + 1]!;\n\t\tconst serverName = JSON.stringify(server.name);\n\n\t\tdefinitions.push({\n\t\t\tname: listToolName,\n\t\t\tlabel: `list tools from ${server.name}`,\n\t\t\tdescription:\n\t\t\t\t`List every tool the \"${server.name}\" MCP server exposes. ` +\n\t\t\t\t`Call this first, then use ${callToolName} to invoke a specific tool.`,\n\t\t\tparameters: { type: \"object\", properties: {}, required: [], additionalProperties: false },\n\t\t\texecute: async (_toolCallId, _params, signal, _onUpdate, _ctx) => {\n\t\t\t\tconst code = `print(__import__(\"json\").dumps(await mcp.list_tools(${serverName}), default=str))`;\n\t\t\t\treturn executeMcpCode(provisioner, code, signal);\n\t\t\t},\n\t\t});\n\n\t\tdefinitions.push({\n\t\t\tname: callToolName,\n\t\t\tlabel: `call tool on ${server.name}`,\n\t\t\tdescription:\n\t\t\t\t`Call a tool on the \"${server.name}\" MCP server. ` +\n\t\t\t\t`Use ${listToolName} first to discover available tool names and argument schemas.`,\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\ttool: { type: \"string\", description: `Tool name on \"${server.name}\".` },\n\t\t\t\t\targuments: { type: \"object\", description: \"JSON arguments for the tool.\", additionalProperties: true },\n\t\t\t\t},\n\t\t\t\trequired: [\"tool\", \"arguments\"],\n\t\t\t\tadditionalProperties: false,\n\t\t\t},\n\t\t\texecute: async (_toolCallId, params, signal, _onUpdate, _ctx) => {\n\t\t\t\tconst { tool, arguments: args } = params as { tool: string; arguments: Record<string, unknown> };\n\t\t\t\tconst code =\n\t\t\t\t\t`print(__import__(\"json\").dumps(await mcp.call_tool(${serverName}, ${JSON.stringify(tool)}, ` +\n\t\t\t\t\t`__import__(\"json\").loads(${JSON.stringify(JSON.stringify(args ?? {}))})), default=str))`;\n\t\t\t\treturn executeMcpCode(provisioner, code, signal);\n\t\t\t},\n\t\t});\n\t}\n\treturn definitions;\n}\n"]}
@@ -1,3 +1,4 @@
1
+ export { acpMcpToolNames, createAcpMcpToolDefinitions } from "./acp-mcp.js";
1
2
  export { type BashOperations, type BashSpawnContext, type BashSpawnHook, type BashToolDetails, type BashToolInput, type BashToolOptions, createBashTool, createBashToolDefinition, createLocalBashOperations, } from "./bash.js";
2
3
  export { createEditTool, createEditToolDefinition, type EditOperations, type EditToolDetails, type EditToolInput, type EditToolOptions, } from "./edit.js";
3
4
  export { withFileMutationQueue } from "./file-mutation-queue.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,cAAc,EACd,wBAAwB,EACxB,yBAAyB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,cAAc,EACd,wBAAwB,EACxB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EACN,iBAAiB,EACjB,2BAA2B,EAC3B,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,YAAY,EACZ,YAAY,EACZ,YAAY,GACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAA+B,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEpF,MAAM,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;AAClC,MAAM,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC/C,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC;AAEjC,MAAM,WAAW,YAAY;IAC5B,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAIvG","sourcesContent":["export {\n\ttype BashOperations,\n\ttype BashSpawnContext,\n\ttype BashSpawnHook,\n\ttype BashToolDetails,\n\ttype BashToolInput,\n\ttype BashToolOptions,\n\tcreateBashTool,\n\tcreateBashToolDefinition,\n\tcreateLocalBashOperations,\n} from \"./bash.js\";\nexport {\n\tcreateEditTool,\n\tcreateEditToolDefinition,\n\ttype EditOperations,\n\ttype EditToolDetails,\n\ttype EditToolInput,\n\ttype EditToolOptions,\n} from \"./edit.js\";\nexport { withFileMutationQueue } from \"./file-mutation-queue.js\";\nexport {\n\tcreateIpythonTool,\n\tcreateIpythonToolDefinition,\n\tIpythonKernelProvisioner,\n\ttype IpythonToolDetails,\n\ttype IpythonToolInput,\n\ttype IpythonToolOptions,\n} from \"./ipython.js\";\nexport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\tformatSize,\n\ttype TruncationOptions,\n\ttype TruncationResult,\n\ttruncateHead,\n\ttruncateLine,\n\ttruncateTail,\n} from \"./truncate.js\";\n\nimport type { AgentTool } from \"@asm-agent/agent\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { createIpythonToolDefinition, type IpythonToolOptions } from \"./ipython.js\";\n\nexport type Tool = AgentTool<any>;\nexport type ToolDef = ToolDefinition<any, any>;\nexport type ToolName = \"ipython\";\n\nexport interface ToolsOptions {\n\tipython?: IpythonToolOptions;\n}\n\nexport function createAllToolDefinitions(cwd: string, options?: ToolsOptions): Record<ToolName, ToolDef> {\n\treturn {\n\t\tipython: createIpythonToolDefinition(cwd, options?.ipython),\n\t};\n}\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,cAAc,EACd,wBAAwB,EACxB,yBAAyB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,cAAc,EACd,wBAAwB,EACxB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EACN,iBAAiB,EACjB,2BAA2B,EAC3B,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,YAAY,EACZ,YAAY,EACZ,YAAY,GACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAA+B,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEpF,MAAM,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;AAClC,MAAM,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC/C,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC;AAEjC,MAAM,WAAW,YAAY;IAC5B,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAIvG","sourcesContent":["export { acpMcpToolNames, createAcpMcpToolDefinitions } from \"./acp-mcp.js\";\nexport {\n\ttype BashOperations,\n\ttype BashSpawnContext,\n\ttype BashSpawnHook,\n\ttype BashToolDetails,\n\ttype BashToolInput,\n\ttype BashToolOptions,\n\tcreateBashTool,\n\tcreateBashToolDefinition,\n\tcreateLocalBashOperations,\n} from \"./bash.js\";\nexport {\n\tcreateEditTool,\n\tcreateEditToolDefinition,\n\ttype EditOperations,\n\ttype EditToolDetails,\n\ttype EditToolInput,\n\ttype EditToolOptions,\n} from \"./edit.js\";\nexport { withFileMutationQueue } from \"./file-mutation-queue.js\";\nexport {\n\tcreateIpythonTool,\n\tcreateIpythonToolDefinition,\n\tIpythonKernelProvisioner,\n\ttype IpythonToolDetails,\n\ttype IpythonToolInput,\n\ttype IpythonToolOptions,\n} from \"./ipython.js\";\nexport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\tformatSize,\n\ttype TruncationOptions,\n\ttype TruncationResult,\n\ttruncateHead,\n\ttruncateLine,\n\ttruncateTail,\n} from \"./truncate.js\";\n\nimport type { AgentTool } from \"@asm-agent/agent\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { createIpythonToolDefinition, type IpythonToolOptions } from \"./ipython.js\";\n\nexport type Tool = AgentTool<any>;\nexport type ToolDef = ToolDefinition<any, any>;\nexport type ToolName = \"ipython\";\n\nexport interface ToolsOptions {\n\tipython?: IpythonToolOptions;\n}\n\nexport function createAllToolDefinitions(cwd: string, options?: ToolsOptions): Record<ToolName, ToolDef> {\n\treturn {\n\t\tipython: createIpythonToolDefinition(cwd, options?.ipython),\n\t};\n}\n"]}
@@ -1,3 +1,4 @@
1
+ export { acpMcpToolNames, createAcpMcpToolDefinitions } from "./acp-mcp.js";
1
2
  export { createBashTool, createBashToolDefinition, createLocalBashOperations, } from "./bash.js";
2
3
  export { createEditTool, createEditToolDefinition, } from "./edit.js";
3
4
  export { withFileMutationQueue } from "./file-mutation-queue.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAON,cAAc,EACd,wBAAwB,EACxB,yBAAyB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,cAAc,EACd,wBAAwB,GAKxB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EACN,iBAAiB,EACjB,2BAA2B,EAC3B,wBAAwB,GAIxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EAGV,YAAY,EACZ,YAAY,EACZ,YAAY,GACZ,MAAM,eAAe,CAAC;AAIvB,OAAO,EAAE,2BAA2B,EAA2B,MAAM,cAAc,CAAC;AAUpF,MAAM,UAAU,wBAAwB,CAAC,GAAW,EAAE,OAAsB,EAA6B;IACxG,OAAO;QACN,OAAO,EAAE,2BAA2B,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC;KAC3D,CAAC;AAAA,CACF","sourcesContent":["export {\n\ttype BashOperations,\n\ttype BashSpawnContext,\n\ttype BashSpawnHook,\n\ttype BashToolDetails,\n\ttype BashToolInput,\n\ttype BashToolOptions,\n\tcreateBashTool,\n\tcreateBashToolDefinition,\n\tcreateLocalBashOperations,\n} from \"./bash.js\";\nexport {\n\tcreateEditTool,\n\tcreateEditToolDefinition,\n\ttype EditOperations,\n\ttype EditToolDetails,\n\ttype EditToolInput,\n\ttype EditToolOptions,\n} from \"./edit.js\";\nexport { withFileMutationQueue } from \"./file-mutation-queue.js\";\nexport {\n\tcreateIpythonTool,\n\tcreateIpythonToolDefinition,\n\tIpythonKernelProvisioner,\n\ttype IpythonToolDetails,\n\ttype IpythonToolInput,\n\ttype IpythonToolOptions,\n} from \"./ipython.js\";\nexport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\tformatSize,\n\ttype TruncationOptions,\n\ttype TruncationResult,\n\ttruncateHead,\n\ttruncateLine,\n\ttruncateTail,\n} from \"./truncate.js\";\n\nimport type { AgentTool } from \"@asm-agent/agent\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { createIpythonToolDefinition, type IpythonToolOptions } from \"./ipython.js\";\n\nexport type Tool = AgentTool<any>;\nexport type ToolDef = ToolDefinition<any, any>;\nexport type ToolName = \"ipython\";\n\nexport interface ToolsOptions {\n\tipython?: IpythonToolOptions;\n}\n\nexport function createAllToolDefinitions(cwd: string, options?: ToolsOptions): Record<ToolName, ToolDef> {\n\treturn {\n\t\tipython: createIpythonToolDefinition(cwd, options?.ipython),\n\t};\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,EAON,cAAc,EACd,wBAAwB,EACxB,yBAAyB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,cAAc,EACd,wBAAwB,GAKxB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EACN,iBAAiB,EACjB,2BAA2B,EAC3B,wBAAwB,GAIxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EAGV,YAAY,EACZ,YAAY,EACZ,YAAY,GACZ,MAAM,eAAe,CAAC;AAIvB,OAAO,EAAE,2BAA2B,EAA2B,MAAM,cAAc,CAAC;AAUpF,MAAM,UAAU,wBAAwB,CAAC,GAAW,EAAE,OAAsB,EAA6B;IACxG,OAAO;QACN,OAAO,EAAE,2BAA2B,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC;KAC3D,CAAC;AAAA,CACF","sourcesContent":["export { acpMcpToolNames, createAcpMcpToolDefinitions } from \"./acp-mcp.js\";\nexport {\n\ttype BashOperations,\n\ttype BashSpawnContext,\n\ttype BashSpawnHook,\n\ttype BashToolDetails,\n\ttype BashToolInput,\n\ttype BashToolOptions,\n\tcreateBashTool,\n\tcreateBashToolDefinition,\n\tcreateLocalBashOperations,\n} from \"./bash.js\";\nexport {\n\tcreateEditTool,\n\tcreateEditToolDefinition,\n\ttype EditOperations,\n\ttype EditToolDetails,\n\ttype EditToolInput,\n\ttype EditToolOptions,\n} from \"./edit.js\";\nexport { withFileMutationQueue } from \"./file-mutation-queue.js\";\nexport {\n\tcreateIpythonTool,\n\tcreateIpythonToolDefinition,\n\tIpythonKernelProvisioner,\n\ttype IpythonToolDetails,\n\ttype IpythonToolInput,\n\ttype IpythonToolOptions,\n} from \"./ipython.js\";\nexport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\tformatSize,\n\ttype TruncationOptions,\n\ttype TruncationResult,\n\ttruncateHead,\n\ttruncateLine,\n\ttruncateTail,\n} from \"./truncate.js\";\n\nimport type { AgentTool } from \"@asm-agent/agent\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { createIpythonToolDefinition, type IpythonToolOptions } from \"./ipython.js\";\n\nexport type Tool = AgentTool<any>;\nexport type ToolDef = ToolDefinition<any, any>;\nexport type ToolName = \"ipython\";\n\nexport interface ToolsOptions {\n\tipython?: IpythonToolOptions;\n}\n\nexport function createAllToolDefinitions(cwd: string, options?: ToolsOptions): Record<ToolName, ToolDef> {\n\treturn {\n\t\tipython: createIpythonToolDefinition(cwd, options?.ipython),\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ipython.d.ts","sourceRoot":"","sources":["../../../src/core/tools/ipython.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport type { AgentTool } from \"@asm-agent/agent\";\nimport type { ImageContent, TextContent } from \"@asm-agent/ai\";\nimport { type Static, Type } from \"typebox\";\nimport { IMAGE_MIME_TYPES } from \"../../utils/mime.js\";\nimport { resolveKernelBashShell } from \"../../utils/shell.js\";\nimport type { ExtensionContext, ToolDefinition } from \"../extensions/types.js\";\nimport { withKernelBootPermit } from \"../kernel/boot-gate.js\";\nimport type { KernelBootstrapProgressHandler } from \"../kernel/bootstrap.js\";\nimport {\n\ttype ExecuteResult,\n\ttype HostRequestHandlers,\n\ttype KernelAttachment,\n\tKernelBusyAfterInterruptError,\n\ttype KernelClient,\n\ttype KernelDiffDisplay,\n\ttype KernelSentAgentMessage,\n\tReplKernelManager,\n} from \"../kernel/index.js\";\nimport { manifestPathIn, type RestoreResult, snapshotPathIn } from \"../kernel/state-snapshot.js\";\nimport type { PythonSkillRuntimeInfo } from \"../skills.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\nconst RLM_BOOTSTRAP_HEADER_CODE = `\nimport asyncio\nimport os as _asm_agent_os\n\n_asm_agent_os.environ[\"NO_COLOR\"] = \"1\"\n`.trim();\n\nconst RLM_BOOTSTRAP_RUNTIME_CODE = `\ntry:\n import rlm as _asm_agent_rlm_module\n rlm = _asm_agent_rlm_module.rlm\n bash = _asm_agent_rlm_module.bash\n import rlm.mcp as mcp\nexcept Exception as _asm_agent_rlm_error:\n _ASM_AGENT_RLM_IMPORT_ERROR = str(_asm_agent_rlm_error)\n\n class _AsmAgentMissingRlm:\n def _raise_missing(self):\n raise RuntimeError(\n \"asm-agent-runtime is not installed in this kernel. \"\n \"Remove ~/.asm-agent/kernel-venv so asm-agent can rebuild it, or set \"\n \"ASM_AGENT_KERNEL_PYTHON to a kernel environment with asm-agent-runtime installed. \"\n f\"Import error: {_ASM_AGENT_RLM_IMPORT_ERROR}\"\n )\n\n async def run(self, prompt, **kwargs):\n self._raise_missing()\n\n async def find_models(self, query=\"\", limit=8):\n self._raise_missing()\n\n async def list_subagents(self):\n self._raise_missing()\n\n async def delete_subagent(self, target):\n self._raise_missing()\n\n async def __call__(self, prompt, **kwargs):\n return await self.run(prompt, **kwargs)\n\n rlm = _AsmAgentMissingRlm()\n\n def bash(command):\n rlm._raise_missing()\n`.trim();\n\nexport function buildRlmBootstrapCode(pythonSkills: readonly PythonSkillRuntimeInfo[] = []): string {\n\tconst baseCode = [RLM_BOOTSTRAP_HEADER_CODE, RLM_BOOTSTRAP_RUNTIME_CODE].join(\"\\n\\n\");\n\tconst importNames = [...new Set(pythonSkills.map((skill) => skill.importName))];\n\tif (importNames.length === 0) {\n\t\treturn baseCode;\n\t}\n\n\treturn `\n${baseCode}\n\nimport importlib as _asm_agent_importlib\nimport inspect as _asm_agent_inspect\nimport sys as _asm_agent_sys\nimport types as _asm_agent_types\n\nclass _AsmAgentCallableSkillModule(_asm_agent_types.ModuleType):\n async def __call__(self, *args, **kwargs):\n result = self.run(*args, **kwargs)\n if _asm_agent_inspect.isawaitable(result):\n return await result\n return result\n\nclass _AsmAgentUnavailableSkill:\n def __init__(self, name, error):\n self.__name__ = name\n self._asm_agent_import_error = error\n self.__doc__ = f\"Python skill {name} is unavailable: {error}\"\n\n async def run(self, *args, **kwargs):\n raise RuntimeError(\n f\"Python skill {self.__name__} is unavailable in this kernel. \"\n f\"Import error: {self._asm_agent_import_error}\"\n )\n\n async def __call__(self, *args, **kwargs):\n return await self.run(*args, **kwargs)\n\n def __repr__(self):\n return f\"<unavailable Python skill {self.__name__!r}: {self._asm_agent_import_error}>\"\n\ndef _asm_agent_wrap_skill_module(module):\n run = getattr(module, \"run\", None)\n if not callable(run):\n return module\n if isinstance(module, _AsmAgentCallableSkillModule):\n return module\n wrapped = _AsmAgentCallableSkillModule(module.__name__)\n wrapped.__dict__.update(module.__dict__)\n try:\n wrapped.__signature__ = _asm_agent_inspect.signature(run)\n except Exception:\n pass\n doc = getattr(run, \"__doc__\", None)\n if doc:\n wrapped.__doc__ = doc\n _asm_agent_sys.modules[module.__name__] = wrapped\n return wrapped\n\n_ASM_AGENT_SKILL_IMPORT_ERRORS = {}\n\nfor _asm_agent_skill_name in ${JSON.stringify(importNames)}:\n try:\n globals()[_asm_agent_skill_name] = _asm_agent_wrap_skill_module(\n _asm_agent_importlib.import_module(_asm_agent_skill_name)\n )\n except Exception as _asm_agent_skill_error:\n _ASM_AGENT_SKILL_IMPORT_ERRORS[_asm_agent_skill_name] = str(_asm_agent_skill_error)\n globals()[_asm_agent_skill_name] = _AsmAgentUnavailableSkill(\n _asm_agent_skill_name,\n str(_asm_agent_skill_error),\n )\n`.trim();\n}\n\nconst ipythonSchema = Type.Object({\n\tcode: Type.String({\n\t\tdescription:\n\t\t\t\"Python code to execute in the persistent Python REPL. Use the target project's own environment for project imports, tests, scripts, CLIs, and dependency checks instead of direct kernel imports.\",\n\t}),\n});\n\nconst BUSY_KERNEL_WAIT_CHOICE = \"Wait and preserve state\";\nconst BUSY_KERNEL_KILL_CHOICE = \"Kill kernel and restart\";\nconst BUSY_KERNEL_PROMPT = [\n\t\"Interrupted Python cell is still running\",\n\t\"Ctrl+C sent an interrupt, but the previous cell has not stopped yet. A new command cannot start until it finishes.\",\n\t\"Waiting preserves the current kernel state. Killing restarts the kernel and loses in-memory variables, imports, and running tasks.\",\n].join(\"\\n\");\nconst KERNEL_RESTART_NOTICE = [\n\t\"<ipython_kernel_reset>\",\n\t\"The Python kernel was restarted after a previous interrupted cell kept running. Variables, imports, async tasks, and open resources from before the restart are no longer available; recreate them before using them.\",\n\t\"</ipython_kernel_reset>\",\n].join(\"\\n\");\n\nfunction createAbortError(): Error {\n\treturn new Error(\"Python execution aborted\");\n}\n\nfunction raceWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined, onAbort?: () => void): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\tonAbort?.();\n\t\treturn Promise.reject(createAbortError());\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\tonAbort?.();\n\t\t\treject(createAbortError());\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\nfunction createLinkedAbortSignal(sources: readonly (AbortSignal | undefined)[]): {\n\tsignal: AbortSignal;\n\tcleanup: () => void;\n} {\n\tconst controller = new AbortController();\n\tconst cleanups: Array<() => void> = [];\n\tconst abort = () => controller.abort();\n\tfor (const source of sources) {\n\t\tif (!source) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (source.aborted) {\n\t\t\tcontroller.abort();\n\t\t\tcontinue;\n\t\t}\n\t\tconst listener = () => abort();\n\t\tsource.addEventListener(\"abort\", listener, { once: true });\n\t\tcleanups.push(() => source.removeEventListener(\"abort\", listener));\n\t}\n\treturn {\n\t\tsignal: controller.signal,\n\t\tcleanup: () => {\n\t\t\tfor (const cleanup of cleanups) {\n\t\t\t\tcleanup();\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction setWorkingMessage(ctx: ExtensionContext | undefined, message?: string): void {\n\ttry {\n\t\tctx?.ui.setWorkingMessage(message);\n\t} catch {\n\t\t// Stale UI context; cosmetic only.\n\t}\n}\n\nexport type IpythonToolInput = Static<typeof ipythonSchema>;\n\nexport interface IpythonToolDetails {\n\tdurationMs?: number;\n\tstatus?: \"ok\" | \"error\" | \"aborted\" | \"starting\";\n\terrorEname?: string;\n\tstdout?: string;\n\tstderr?: string;\n\tresult?: string;\n\t/** Output that arrived without this cell's id (threads, other cells' leftovers), shown separately from stdout. */\n\tbackgroundOutput?: string;\n\t/** Diffs streamed from file edits, rendered by the cell view. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments loaded into context (e.g. by the attach-image skill). */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** True when this result came after killing and restarting a busy kernel. */\n\tkernelRestarted?: boolean;\n\terror?: {\n\t\tename: string;\n\t\tevalue: string;\n\t\ttraceback: string[];\n\t};\n}\n\nexport interface IpythonToolOptions {\n\t/** Python override. Must have asm-agent-runtime installed. */\n\tpython?: string;\n\tenv?: Record<string, string>;\n\t/** Command prefix prepended to every bash() command. */\n\tcommandPrefix?: string;\n\t/** Shell used by bash(). */\n\tshellPath?: string;\n\tsessionId?: string;\n\t/** Typed host request handlers for the kernel↔host bridge (rlm.run, goal.*, …). */\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly PythonSkillRuntimeInfo[];\n\t/** Per-session artifact dir where the kernel namespace snapshot is stored. Omit to disable snapshots. */\n\tsnapshotDir?: string;\n\t/** Resolves before this kernel starts — e.g. the previous provisioner's dispose, so a\n\t * /reload's old-kernel snapshot flush can't race the new kernel's restore. */\n\treadyGate?: Promise<unknown>;\n\t/** Filled with the live kernel client after the first kernel start; cleared on construction. */\n\tkernelManagerRef?: { current?: KernelClient };\n\t/**\n\t * Fires once per kernel start when a previous session's namespace was revived\n\t * (some names restored or some failed), so the session can tell the model.\n\t */\n\tonRestore?: (result: RestoreResult) => void;\n\tonLateSentAgentMessage?: (toolCallId: string, message: KernelSentAgentMessage) => void;\n\t/** Shared provisioner owning the kernel lifecycle. When provided, the remaining options are ignored. */\n\tprovisioner?: IpythonKernelProvisioner;\n}\n\n/**\n * Owns the lazy create+start+runtime-bootstrap of one session's Python kernel.\n *\n * Concurrent ensure() calls await the same in-flight startup, a failed startup\n * clears the memo so the next call retries fresh, and progress listeners can\n * attach mid-flight (a tool call racing a background prewarm()).\n */\nexport class IpythonKernelProvisioner {\n\tprivate managerPromise?: Promise<KernelClient>;\n\tprivate startedManager?: KernelClient;\n\tprivate readonly startupListeners = new Set<KernelBootstrapProgressHandler>();\n\tprivate lastStartupMessage?: string;\n\tprivate _lastRestore?: RestoreResult;\n\tprivate readonly disposeController = new AbortController();\n\n\tconstructor(\n\t\tprivate readonly cwd: string,\n\t\tprivate readonly options?: Omit<IpythonToolOptions, \"provisioner\">,\n\t) {\n\t\tif (options?.kernelManagerRef) {\n\t\t\toptions.kernelManagerRef.current = undefined;\n\t\t}\n\t}\n\n\t/** The kernel manager, once a startup has completed successfully. */\n\tget manager(): KernelClient | undefined {\n\t\treturn this.startedManager;\n\t}\n\n\t/** Result of reviving a prior session's namespace on the last kernel start, if any. */\n\tget lastRestore(): RestoreResult | undefined {\n\t\treturn this._lastRestore;\n\t}\n\n\t/** Start the kernel in the background. Failures are swallowed here and surface on the next ensure(). */\n\tprewarm(): void {\n\t\tvoid this.ensure().catch(() => {});\n\t}\n\n\t/** Whether a kernel has finished starting and is currently running. */\n\tget hasRunningKernel(): boolean {\n\t\treturn this.startedManager?.isRunning ?? false;\n\t}\n\n\t/** Remove live variables above the snapshot's per-variable size limit. */\n\tasync pruneOversizedVariables(): Promise<string[] | null> {\n\t\tconst m = this.startedManager ?? (await this.managerPromise?.catch(() => undefined));\n\t\tconst result = await m?.pruneOversizedVariables();\n\t\treturn result ? (result.pruned ?? []) : null;\n\t}\n\n\t/** Live user-defined names in the kernel namespace, or null if listing failed / no kernel. */\n\tasync listNamespaceNames(signal?: AbortSignal): Promise<string[] | null> {\n\t\tconst m = this.startedManager ?? (await this.managerPromise?.catch(() => undefined));\n\t\treturn (await m?.listNamespaceNames(signal)) ?? null;\n\t}\n\n\t/** Dispose the kernel owned by this provisioner, including one still starting up. */\n\tasync dispose(): Promise<void> {\n\t\t// Drops a still-queued boot out of the semaphore and short-circuits an\n\t\t// in-flight startKernel before it spawns, so a disposed session's boot\n\t\t// doesn't waste a slot during a fan-out.\n\t\tthis.disposeController.abort();\n\t\tconst pending = this.managerPromise;\n\t\tthis.managerPromise = undefined;\n\t\tthis.startedManager = undefined;\n\t\tif (this.options?.kernelManagerRef) {\n\t\t\tthis.options.kernelManagerRef.current = undefined;\n\t\t}\n\t\tif (!pending) return;\n\t\ttry {\n\t\t\tconst m = await pending;\n\t\t\tawait m.dispose();\n\t\t} catch {\n\t\t\t// a failed startup already cleaned up after itself\n\t\t}\n\t}\n\n\tasync kill(): Promise<void> {\n\t\tconst pending = this.managerPromise;\n\t\tthis.managerPromise = undefined;\n\t\tthis.startedManager = undefined;\n\t\tif (this.options?.kernelManagerRef) {\n\t\t\tthis.options.kernelManagerRef.current = undefined;\n\t\t}\n\t\tif (!pending) return;\n\t\ttry {\n\t\t\tconst m = await pending;\n\t\t\tawait m.kill();\n\t\t} catch {\n\t\t\t// a failed startup already cleaned up after itself\n\t\t}\n\t}\n\n\tensure(onProgress?: KernelBootstrapProgressHandler, signal?: AbortSignal): Promise<KernelClient> {\n\t\tif (signal?.aborted) {\n\t\t\treturn Promise.reject(createAbortError());\n\t\t}\n\t\tlet cleanupProgressListener: (() => void) | undefined;\n\t\tif (onProgress && !this.startedManager) {\n\t\t\tthis.startupListeners.add(onProgress);\n\t\t\tcleanupProgressListener = () => {\n\t\t\t\tthis.startupListeners.delete(onProgress);\n\t\t\t\tsignal?.removeEventListener(\"abort\", cleanupProgressListener!);\n\t\t\t};\n\t\t\tsignal?.addEventListener(\"abort\", cleanupProgressListener, { once: true });\n\t\t\t// Joining an in-flight startup: replay the current stage.\n\t\t\tif (this.managerPromise && this.lastStartupMessage) {\n\t\t\t\tonProgress(this.lastStartupMessage);\n\t\t\t}\n\t\t}\n\t\tif (!this.managerPromise) {\n\t\t\tconst startup = this.startKernel(signal);\n\t\t\tthis.managerPromise = startup;\n\t\t\tstartup.then(\n\t\t\t\t(m) => {\n\t\t\t\t\tif (this.managerPromise === startup) {\n\t\t\t\t\t\tthis.startedManager = m;\n\t\t\t\t\t}\n\t\t\t\t\tthis.settleStartup();\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\t// Clear the memo so the next ensure() retries instead of\n\t\t\t\t\t// rethrowing a cached rejection forever.\n\t\t\t\t\tif (this.managerPromise === startup) {\n\t\t\t\t\t\tthis.managerPromise = undefined;\n\t\t\t\t\t}\n\t\t\t\t\tthis.settleStartup();\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn raceWithAbort(this.managerPromise, signal).finally(() => {\n\t\t\tcleanupProgressListener?.();\n\t\t});\n\t}\n\n\tprivate settleStartup(): void {\n\t\tthis.startupListeners.clear();\n\t\tthis.lastStartupMessage = undefined;\n\t}\n\n\tprivate emitStartupProgress(message: string): void {\n\t\tthis.lastStartupMessage = message;\n\t\tfor (const listener of [...this.startupListeners]) {\n\t\t\tlistener(message);\n\t\t}\n\t}\n\n\tprivate async startKernel(signal?: AbortSignal): Promise<KernelClient> {\n\t\tconst startupAbort = createLinkedAbortSignal([this.disposeController.signal, signal]);\n\t\tconst startupSignal = startupAbort.signal;\n\t\t// Wait for a previous provisioner (e.g. on /reload) to finish disposing — and\n\t\t// flushing its final snapshot — before we read that snapshot back, so the two\n\t\t// kernels can't race over the same on-disk file. Guarded so the common\n\t\t// no-gate path stays synchronous (callers rely on prompt startup progress).\n\t\ttry {\n\t\t\tif (this.options?.readyGate) {\n\t\t\t\tawait raceWithAbort(\n\t\t\t\t\tthis.options.readyGate.catch(() => {}),\n\t\t\t\t\tstartupSignal,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst snapshotDir = this.options?.snapshotDir;\n\t\t\t// Always inject an absolute trusted shell (undefined only on win32\n\t\t\t// without bash, where the runtime's teaching error fires instead).\n\t\t\tconst shellPath = resolveKernelBashShell(this.options?.shellPath);\n\t\t\tconst commandPrefix = this.options?.commandPrefix;\n\t\t\tconst m = new ReplKernelManager({\n\t\t\t\tpython: this.options?.python,\n\t\t\t\tcwd: this.cwd,\n\t\t\t\t// bash() reads these to pick its shell and command prefix.\n\t\t\t\tenv: {\n\t\t\t\t\t...this.options?.env,\n\t\t\t\t\t...(shellPath ? { ASM_AGENT_BASH_SHELL: shellPath } : {}),\n\t\t\t\t\t...(commandPrefix ? { ASM_AGENT_BASH_COMMAND_PREFIX: commandPrefix } : {}),\n\t\t\t\t},\n\t\t\t\tsessionId: this.options?.sessionId,\n\t\t\t\thostHandlers: this.options?.hostHandlers,\n\t\t\t\tpythonSkills: this.options?.pythonSkills,\n\t\t\t\t// Only persistent sessions (which have an artifact dir) get a revivable snapshot.\n\t\t\t\tsnapshot: snapshotDir\n\t\t\t\t\t? { path: snapshotPathIn(snapshotDir), manifestPath: manifestPathIn(snapshotDir) }\n\t\t\t\t\t: undefined,\n\t\t\t});\n\t\t\tlet pendingRestore: RestoreResult | undefined;\n\t\t\ttry {\n\t\t\t\t// Emitted synchronously (before the permit await) so a listener attaching\n\t\t\t\t// mid-flight can replay the current stage.\n\t\t\t\tthis.emitStartupProgress(\"Starting Python kernel...\");\n\t\t\t\t// Only the process spawn + port resolve contends for OS resources under a\n\t\t\t\t// fan-out, and it is bounded by start()'s own timeouts — so the permit\n\t\t\t\t// covers only start(). Restore/bootstrap run per-kernel afterwards and are\n\t\t\t\t// unbounded execute()s; holding the global permit across them could pin it\n\t\t\t\t// forever on a wedged bootstrap and starve every other session's boot.\n\t\t\t\tawait withKernelBootPermit(() => {\n\t\t\t\t\t// Disposed while queued for the permit — don't spawn a kernel nobody wants.\n\t\t\t\t\tif (startupSignal.aborted) throw new Error(\"Kernel provisioner disposed before start\");\n\t\t\t\t\treturn m.start({\n\t\t\t\t\t\tonBootstrapProgress: (message) => this.emitStartupProgress(message),\n\t\t\t\t\t\tsignal: startupSignal,\n\t\t\t\t\t});\n\t\t\t\t}, startupSignal);\n\t\t\t\t// Revive a prior session's namespace before the bootstrap, so the bootstrap\n\t\t\t\t// then overwrites live handles (rlm, skills) on top of anything restored.\n\t\t\t\tif (snapshotDir) {\n\t\t\t\t\tconst snapshotExisted = existsSync(snapshotPathIn(snapshotDir));\n\t\t\t\t\tthis.emitStartupProgress(\"Restoring Python state...\");\n\t\t\t\t\tconst restore = await raceWithAbort(m.restoreState(), startupSignal);\n\t\t\t\t\tif (snapshotExisted) {\n\t\t\t\t\t\tpendingRestore = restore ?? { restored: [], failed: [], path: snapshotPathIn(snapshotDir) };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.emitStartupProgress(\"Preparing Python runtime...\");\n\t\t\t\tconst bootstrap = await m.execute(buildRlmBootstrapCode(this.options?.pythonSkills), {\n\t\t\t\t\tsignal: startupSignal,\n\t\t\t\t});\n\t\t\t\tif (bootstrap.status !== \"ok\") {\n\t\t\t\t\tconst details = [bootstrap.stderr, bootstrap.error?.traceback.join(\"\\n\")].filter(Boolean).join(\"\\n\");\n\t\t\t\t\tthrow new Error(`Failed to initialize rlm runtime in the Python kernel:\\n${details}`);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Never leak the kernel process if startup fails after spawn.\n\t\t\t\tvoid m.dispose();\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\t// Only tell the model what was revived once the kernel is actually usable —\n\t\t\t// a notice claiming restored state must never outlive a failed bootstrap.\n\t\t\tif (pendingRestore) {\n\t\t\t\tthis._lastRestore = pendingRestore;\n\t\t\t\tthis.options?.onRestore?.(pendingRestore);\n\t\t\t}\n\t\t\tif (this.options?.kernelManagerRef) {\n\t\t\t\tthis.options.kernelManagerRef.current = m;\n\t\t\t}\n\t\t\treturn m;\n\t\t} finally {\n\t\t\tstartupAbort.cleanup();\n\t\t}\n\t}\n}\n\nasync function chooseBusyKernelAction(\n\tctx: ExtensionContext | undefined,\n\tsignal: AbortSignal | undefined,\n): Promise<\"wait\" | \"kill\" | \"cancel\"> {\n\tif (!ctx?.hasUI) {\n\t\treturn \"cancel\";\n\t}\n\tconst choice = await ctx.ui.select(BUSY_KERNEL_PROMPT, [BUSY_KERNEL_WAIT_CHOICE, BUSY_KERNEL_KILL_CHOICE], {\n\t\tsignal,\n\t});\n\tif (choice === BUSY_KERNEL_WAIT_CHOICE) {\n\t\treturn \"wait\";\n\t}\n\tif (choice === BUSY_KERNEL_KILL_CHOICE) {\n\t\treturn \"kill\";\n\t}\n\treturn \"cancel\";\n}\n\nasync function executeWithBusyKernelChoice(\n\tprovisioner: IpythonKernelProvisioner,\n\treportStartupProgress: KernelBootstrapProgressHandler,\n\ttoolCallId: string,\n\tcode: string,\n\tsignal: AbortSignal | undefined,\n\tonStream: (chunk: string, name: \"stdout\" | \"stderr\") => void,\n\tonWorkingMessage: (message?: string) => void,\n\tonLateSentAgentMessage: ((toolCallId: string, message: KernelSentAgentMessage) => void) | undefined,\n\tctx: ExtensionContext | undefined,\n): Promise<{ result: ExecuteResult; kernelRestarted: boolean }> {\n\tlet kernelRestarted = false;\n\twhile (true) {\n\t\tconst m = await provisioner.ensure(reportStartupProgress, signal);\n\t\ttry {\n\t\t\treturn {\n\t\t\t\tresult: await m.execute(code, {\n\t\t\t\t\tsignal,\n\t\t\t\t\tonStream,\n\t\t\t\t\tonLateSentAgentMessage: onLateSentAgentMessage\n\t\t\t\t\t\t? (message) => onLateSentAgentMessage(toolCallId, message)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t}),\n\t\t\t\tkernelRestarted,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof KernelBusyAfterInterruptError) || signal?.aborted) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst action = await chooseBusyKernelAction(ctx, signal);\n\t\t\tif (action === \"wait\") {\n\t\t\t\tonWorkingMessage(\"Waiting for Python kernel...\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (action === \"kill\") {\n\t\t\t\tonWorkingMessage(\"Restarting Python kernel...\");\n\t\t\t\tawait provisioner.kill();\n\t\t\t\tkernelRestarted = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\n/** Turn kernel image attachments into `ImageContent` blocks; non-image types are dropped. */\nexport function imageBlocksFromAttachments(attachments: readonly KernelAttachment[] | undefined): ImageContent[] {\n\tif (!attachments) return [];\n\treturn attachments\n\t\t.filter((a) => IMAGE_MIME_TYPES.has(a.mimeType))\n\t\t.map((a) => ({ type: \"image\", data: a.data, mimeType: a.mimeType }));\n}\n\nexport function createIpythonToolDefinition(\n\tcwd: string,\n\toptions?: IpythonToolOptions,\n): ToolDefinition<typeof ipythonSchema, IpythonToolDetails> {\n\tconst provisioner = options?.provisioner ?? new IpythonKernelProvisioner(cwd, options);\n\n\treturn {\n\t\tname: \"ipython\",\n\t\tlabel: \"ipython\",\n\t\tdescription:\n\t\t\t\"Execute Python code in a persistent Python REPL. Top-level `await` is supported. Variables, imports, and loaded data persist across calls, and are revived on a best-effort basis when a session is resumed (objects that cannot be serialized are dropped and reported). Run shell commands with `bash('cmd')` / `await bash('cmd')`. Project imports, tests, scripts, CLIs, and dependency checks should run through the target project's own environment.\",\n\t\tpromptSnippet: \"ipython - persistent Python REPL for code, state, and bash() orchestration\",\n\t\t// The kernel is single-threaded — pi must not run two ipython calls in parallel within a batch.\n\t\texecutionMode: \"sequential\",\n\t\tparameters: ipythonSchema,\n\t\texecute: async (toolCallId, params, signal, onUpdate, ctx) => {\n\t\t\tlet hasWorkingMessage = false;\n\t\t\tconst setToolWorkingMessage = (message?: string) => {\n\t\t\t\tsetWorkingMessage(ctx, message);\n\t\t\t\thasWorkingMessage = message !== undefined;\n\t\t\t};\n\t\t\tconst reportStartupProgress: KernelBootstrapProgressHandler = (message) => {\n\t\t\t\tsetToolWorkingMessage(message);\n\t\t\t\tonUpdate?.({\n\t\t\t\t\tcontent: [{ type: \"text\", text: message }],\n\t\t\t\t\tdetails: { status: \"starting\" },\n\t\t\t\t});\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tconst { result: r, kernelRestarted } = await executeWithBusyKernelChoice(\n\t\t\t\t\tprovisioner,\n\t\t\t\t\treportStartupProgress,\n\t\t\t\t\ttoolCallId,\n\t\t\t\t\tparams.code,\n\t\t\t\t\tsignal,\n\t\t\t\t\t(chunk) => {\n\t\t\t\t\t\tonUpdate?.({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: chunk }],\n\t\t\t\t\t\t\tdetails: { status: \"ok\" },\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tsetToolWorkingMessage,\n\t\t\t\t\toptions?.onLateSentAgentMessage,\n\t\t\t\t\tctx,\n\t\t\t\t);\n\n\t\t\t\tlet text = r.stdout;\n\t\t\t\tif (r.stderr) text += (text ? \"\\n\" : \"\") + r.stderr;\n\t\t\t\tif (r.result) text += (text ? \"\\n\" : \"\") + r.result;\n\t\t\t\tif (r.status === \"error\" && r.error) {\n\t\t\t\t\ttext += (text ? \"\\n\" : \"\") + r.error.traceback.join(\"\\n\");\n\t\t\t\t}\n\t\t\t\tif (r.backgroundOutput) {\n\t\t\t\t\ttext += `${text ? \"\\n\" : \"\"}[background output (unattributed)]\\n${r.backgroundOutput}`;\n\t\t\t\t}\n\t\t\t\tif (kernelRestarted) {\n\t\t\t\t\ttext = text ? `${KERNEL_RESTART_NOTICE}\\n\\n${text}` : KERNEL_RESTART_NOTICE;\n\t\t\t\t}\n\n\t\t\t\tconst imageBlocks = imageBlocksFromAttachments(r.attachments);\n\t\t\t\tconst content: (TextContent | ImageContent)[] = [{ type: \"text\", text: text || \"\" }, ...imageBlocks];\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent,\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tdurationMs: r.durationMs,\n\t\t\t\t\t\tstatus: r.status,\n\t\t\t\t\t\terrorEname: r.error?.ename,\n\t\t\t\t\t\tstdout: r.stdout,\n\t\t\t\t\t\tstderr: r.stderr,\n\t\t\t\t\t\tresult: r.result,\n\t\t\t\t\t\tbackgroundOutput: r.backgroundOutput,\n\t\t\t\t\t\tdiffs: r.diffs,\n\t\t\t\t\t\tattachments: r.attachments,\n\t\t\t\t\t\tsentAgentMessages: r.sentAgentMessages,\n\t\t\t\t\t\tkernelRestarted,\n\t\t\t\t\t\terror: r.error,\n\t\t\t\t\t},\n\t\t\t\t\tisError: r.status === \"error\" || r.status === \"aborted\",\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tif (hasWorkingMessage) {\n\t\t\t\t\tsetToolWorkingMessage();\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport function createIpythonTool(cwd: string, options?: IpythonToolOptions): AgentTool<typeof ipythonSchema> {\n\treturn wrapToolDefinition(createIpythonToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"ipython.d.ts","sourceRoot":"","sources":["../../../src/core/tools/ipython.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport type { AgentTool } from \"@asm-agent/agent\";\nimport type { ImageContent, TextContent } from \"@asm-agent/ai\";\nimport { type Static, Type } from \"typebox\";\nimport { IMAGE_MIME_TYPES } from \"../../utils/mime.js\";\nimport { resolveKernelBashShell } from \"../../utils/shell.js\";\nimport type { ExtensionContext, ToolDefinition } from \"../extensions/types.js\";\nimport { withKernelBootPermit } from \"../kernel/boot-gate.js\";\nimport type { KernelBootstrapProgressHandler } from \"../kernel/bootstrap.js\";\nimport {\n\ttype ExecuteResult,\n\ttype HostRequestHandlers,\n\ttype KernelAttachment,\n\tKernelBusyAfterInterruptError,\n\ttype KernelClient,\n\ttype KernelDiffDisplay,\n\ttype KernelSentAgentMessage,\n\tReplKernelManager,\n} from \"../kernel/index.js\";\nimport { manifestPathIn, type RestoreResult, snapshotPathIn } from \"../kernel/state-snapshot.js\";\nimport type { PythonSkillRuntimeInfo } from \"../skills.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\nconst RLM_BOOTSTRAP_HEADER_CODE = `\nimport asyncio\nimport os as _asm_agent_os\n\n_asm_agent_os.environ[\"NO_COLOR\"] = \"1\"\n`.trim();\n\nconst RLM_BOOTSTRAP_RUNTIME_CODE = `\ntry:\n import rlm as _asm_agent_rlm_module\n rlm = _asm_agent_rlm_module.rlm\n bash = _asm_agent_rlm_module.bash\n import rlm.mcp as mcp\nexcept Exception as _asm_agent_rlm_error:\n _ASM_AGENT_RLM_IMPORT_ERROR = str(_asm_agent_rlm_error)\n\n class _AsmAgentMissingRlm:\n def _raise_missing(self):\n raise RuntimeError(\n \"asm-agent-runtime is not installed in this kernel. \"\n \"Remove ~/.asm-agent/kernel-venv so asm-agent can rebuild it, or set \"\n \"ASM_AGENT_KERNEL_PYTHON to a kernel environment with asm-agent-runtime installed. \"\n f\"Import error: {_ASM_AGENT_RLM_IMPORT_ERROR}\"\n )\n\n async def run(self, prompt, **kwargs):\n self._raise_missing()\n\n async def find_models(self, query=\"\", limit=8):\n self._raise_missing()\n\n async def list_subagents(self):\n self._raise_missing()\n\n async def delete_subagent(self, target):\n self._raise_missing()\n\n async def __call__(self, prompt, **kwargs):\n return await self.run(prompt, **kwargs)\n\n rlm = _AsmAgentMissingRlm()\n\n def bash(command):\n rlm._raise_missing()\n`.trim();\n\nexport function buildRlmBootstrapCode(pythonSkills: readonly PythonSkillRuntimeInfo[] = []): string {\n\tconst baseCode = [RLM_BOOTSTRAP_HEADER_CODE, RLM_BOOTSTRAP_RUNTIME_CODE].join(\"\\n\\n\");\n\tconst importNames = [...new Set(pythonSkills.map((skill) => skill.importName))];\n\tif (importNames.length === 0) {\n\t\treturn baseCode;\n\t}\n\n\treturn `\n${baseCode}\n\nimport importlib as _asm_agent_importlib\nimport inspect as _asm_agent_inspect\nimport sys as _asm_agent_sys\nimport types as _asm_agent_types\n\nclass _AsmAgentCallableSkillModule(_asm_agent_types.ModuleType):\n async def __call__(self, *args, **kwargs):\n result = self.run(*args, **kwargs)\n if _asm_agent_inspect.isawaitable(result):\n return await result\n return result\n\nclass _AsmAgentUnavailableSkill:\n def __init__(self, name, error):\n self.__name__ = name\n self._asm_agent_import_error = error\n self.__doc__ = f\"Python skill {name} is unavailable: {error}\"\n\n async def run(self, *args, **kwargs):\n raise RuntimeError(\n f\"Python skill {self.__name__} is unavailable in this kernel. \"\n f\"Import error: {self._asm_agent_import_error}\"\n )\n\n async def __call__(self, *args, **kwargs):\n return await self.run(*args, **kwargs)\n\n def __repr__(self):\n return f\"<unavailable Python skill {self.__name__!r}: {self._asm_agent_import_error}>\"\n\ndef _asm_agent_wrap_skill_module(module):\n run = getattr(module, \"run\", None)\n if not callable(run):\n return module\n if isinstance(module, _AsmAgentCallableSkillModule):\n return module\n wrapped = _AsmAgentCallableSkillModule(module.__name__)\n wrapped.__dict__.update(module.__dict__)\n try:\n wrapped.__signature__ = _asm_agent_inspect.signature(run)\n except Exception:\n pass\n doc = getattr(run, \"__doc__\", None)\n if doc:\n wrapped.__doc__ = doc\n _asm_agent_sys.modules[module.__name__] = wrapped\n return wrapped\n\n_ASM_AGENT_SKILL_IMPORT_ERRORS = {}\n\nfor _asm_agent_skill_name in ${JSON.stringify(importNames)}:\n try:\n globals()[_asm_agent_skill_name] = _asm_agent_wrap_skill_module(\n _asm_agent_importlib.import_module(_asm_agent_skill_name)\n )\n except Exception as _asm_agent_skill_error:\n _ASM_AGENT_SKILL_IMPORT_ERRORS[_asm_agent_skill_name] = str(_asm_agent_skill_error)\n globals()[_asm_agent_skill_name] = _AsmAgentUnavailableSkill(\n _asm_agent_skill_name,\n str(_asm_agent_skill_error),\n )\n`.trim();\n}\n\nconst ipythonSchema = Type.Object({\n\tcode: Type.String({\n\t\tdescription:\n\t\t\t\"Python code to execute in the persistent Python REPL. Use the target project's own environment for project imports, tests, scripts, CLIs, and dependency checks instead of direct kernel imports.\",\n\t}),\n});\n\nconst BUSY_KERNEL_WAIT_CHOICE = \"Wait and preserve state\";\nconst BUSY_KERNEL_KILL_CHOICE = \"Kill kernel and restart\";\nconst BUSY_KERNEL_PROMPT = [\n\t\"Interrupted Python cell is still running\",\n\t\"Ctrl+C sent an interrupt, but the previous cell has not stopped yet. A new command cannot start until it finishes.\",\n\t\"Waiting preserves the current kernel state. Killing restarts the kernel and loses in-memory variables, imports, and running tasks.\",\n].join(\"\\n\");\nconst KERNEL_RESTART_NOTICE = [\n\t\"<ipython_kernel_reset>\",\n\t\"The Python kernel was restarted after a previous interrupted cell kept running. Variables, imports, async tasks, and open resources from before the restart are no longer available; recreate them before using them.\",\n\t\"</ipython_kernel_reset>\",\n].join(\"\\n\");\n\nfunction createAbortError(): Error {\n\treturn new Error(\"Python execution aborted\");\n}\n\nfunction raceWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined, onAbort?: () => void): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\tonAbort?.();\n\t\treturn Promise.reject(createAbortError());\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\tonAbort?.();\n\t\t\treject(createAbortError());\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\nfunction createLinkedAbortSignal(sources: readonly (AbortSignal | undefined)[]): {\n\tsignal: AbortSignal;\n\tcleanup: () => void;\n} {\n\tconst controller = new AbortController();\n\tconst cleanups: Array<() => void> = [];\n\tconst abort = () => controller.abort();\n\tfor (const source of sources) {\n\t\tif (!source) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (source.aborted) {\n\t\t\tcontroller.abort();\n\t\t\tcontinue;\n\t\t}\n\t\tconst listener = () => abort();\n\t\tsource.addEventListener(\"abort\", listener, { once: true });\n\t\tcleanups.push(() => source.removeEventListener(\"abort\", listener));\n\t}\n\treturn {\n\t\tsignal: controller.signal,\n\t\tcleanup: () => {\n\t\t\tfor (const cleanup of cleanups) {\n\t\t\t\tcleanup();\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction setWorkingMessage(ctx: ExtensionContext | undefined, message?: string): void {\n\ttry {\n\t\tctx?.ui.setWorkingMessage(message);\n\t} catch {\n\t\t// Stale UI context; cosmetic only.\n\t}\n}\n\nexport type IpythonToolInput = Static<typeof ipythonSchema>;\n\nexport interface IpythonToolDetails {\n\tdurationMs?: number;\n\tstatus?: \"ok\" | \"error\" | \"aborted\" | \"starting\";\n\terrorEname?: string;\n\tstdout?: string;\n\tstderr?: string;\n\tresult?: string;\n\t/** Output that arrived without this cell's id (threads, other cells' leftovers), shown separately from stdout. */\n\tbackgroundOutput?: string;\n\t/** Diffs streamed from file edits, rendered by the cell view. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments loaded into context (e.g. by the attach-image skill). */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** True when this result came after killing and restarting a busy kernel. */\n\tkernelRestarted?: boolean;\n\terror?: {\n\t\tename: string;\n\t\tevalue: string;\n\t\ttraceback: string[];\n\t};\n}\n\nexport interface IpythonToolOptions {\n\t/** Python override. Must have asm-agent-runtime installed. */\n\tpython?: string;\n\tenv?: Record<string, string>;\n\t/** Command prefix prepended to every bash() command. */\n\tcommandPrefix?: string;\n\t/** Shell used by bash(). */\n\tshellPath?: string;\n\tsessionId?: string;\n\t/** Typed host request handlers for the kernel↔host bridge (rlm.run, goal.*, …). */\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly PythonSkillRuntimeInfo[];\n\t/** Per-session artifact dir where the kernel namespace snapshot is stored. Omit to disable snapshots. */\n\tsnapshotDir?: string;\n\t/** Resolves before this kernel starts — e.g. the previous provisioner's dispose, so a\n\t * /reload's old-kernel snapshot flush can't race the new kernel's restore. */\n\treadyGate?: Promise<unknown>;\n\t/** Filled with the live kernel client after the first kernel start; cleared on construction. */\n\tkernelManagerRef?: { current?: KernelClient };\n\t/**\n\t * Fires once per kernel start when a previous session's namespace was revived\n\t * (some names restored or some failed), so the session can tell the model.\n\t */\n\tonRestore?: (result: RestoreResult) => void;\n\tonLateSentAgentMessage?: (toolCallId: string, message: KernelSentAgentMessage) => void;\n\t/** Shared provisioner owning the kernel lifecycle. When provided, the remaining options are ignored. */\n\tprovisioner?: IpythonKernelProvisioner;\n}\n\n/**\n * Owns the lazy create+start+runtime-bootstrap of one session's Python kernel.\n *\n * Concurrent ensure() calls await the same in-flight startup, a failed startup\n * clears the memo so the next call retries fresh, and progress listeners can\n * attach mid-flight (a tool call racing a background prewarm()).\n */\nexport class IpythonKernelProvisioner {\n\tprivate managerPromise?: Promise<KernelClient>;\n\tprivate startedManager?: KernelClient;\n\tprivate readonly startupListeners = new Set<KernelBootstrapProgressHandler>();\n\tprivate lastStartupMessage?: string;\n\tprivate _lastRestore?: RestoreResult;\n\tprivate readonly disposeController = new AbortController();\n\n\tconstructor(\n\t\tprivate readonly cwd: string,\n\t\tprivate readonly options?: Omit<IpythonToolOptions, \"provisioner\">,\n\t) {\n\t\tif (options?.kernelManagerRef) {\n\t\t\toptions.kernelManagerRef.current = undefined;\n\t\t}\n\t}\n\n\t/** The kernel manager, once a startup has completed successfully. */\n\tget manager(): KernelClient | undefined {\n\t\treturn this.startedManager;\n\t}\n\n\t/** Result of reviving a prior session's namespace on the last kernel start, if any. */\n\tget lastRestore(): RestoreResult | undefined {\n\t\treturn this._lastRestore;\n\t}\n\n\t/** Start the kernel in the background. Failures are swallowed here and surface on the next ensure(). */\n\tprewarm(): void {\n\t\tvoid this.ensure().catch(() => {});\n\t}\n\n\t/** Whether a kernel has finished starting and is currently running. */\n\tget hasRunningKernel(): boolean {\n\t\treturn this.startedManager?.isRunning ?? false;\n\t}\n\n\t/** Remove live variables above the snapshot's per-variable size limit. */\n\tasync pruneOversizedVariables(): Promise<string[] | null> {\n\t\tconst m = this.startedManager ?? (await this.managerPromise?.catch(() => undefined));\n\t\tconst result = await m?.pruneOversizedVariables();\n\t\treturn result ? (result.pruned ?? []) : null;\n\t}\n\n\t/** Live user-defined names in the kernel namespace, or null if listing failed / no kernel. */\n\tasync listNamespaceNames(signal?: AbortSignal): Promise<string[] | null> {\n\t\tconst m = this.startedManager ?? (await this.managerPromise?.catch(() => undefined));\n\t\treturn (await m?.listNamespaceNames(signal)) ?? null;\n\t}\n\n\t/** Dispose the kernel owned by this provisioner, including one still starting up. */\n\tasync dispose(): Promise<void> {\n\t\t// Drops a still-queued boot out of the semaphore and short-circuits an\n\t\t// in-flight startKernel before it spawns, so a disposed session's boot\n\t\t// doesn't waste a slot during a fan-out.\n\t\tthis.disposeController.abort();\n\t\tconst pending = this.managerPromise;\n\t\tthis.managerPromise = undefined;\n\t\tthis.startedManager = undefined;\n\t\tif (this.options?.kernelManagerRef) {\n\t\t\tthis.options.kernelManagerRef.current = undefined;\n\t\t}\n\t\tif (!pending) return;\n\t\ttry {\n\t\t\tconst m = await pending;\n\t\t\tawait m.shutdown({ snapshot: true, drainHostRequests: true });\n\t\t} catch {\n\t\t\t// a failed startup already cleaned up after itself\n\t\t}\n\t}\n\n\tasync kill(): Promise<void> {\n\t\tconst pending = this.managerPromise;\n\t\tthis.managerPromise = undefined;\n\t\tthis.startedManager = undefined;\n\t\tif (this.options?.kernelManagerRef) {\n\t\t\tthis.options.kernelManagerRef.current = undefined;\n\t\t}\n\t\tif (!pending) return;\n\t\ttry {\n\t\t\tconst m = await pending;\n\t\t\tawait m.kill();\n\t\t} catch {\n\t\t\t// a failed startup already cleaned up after itself\n\t\t}\n\t}\n\n\tensure(onProgress?: KernelBootstrapProgressHandler, signal?: AbortSignal): Promise<KernelClient> {\n\t\tif (signal?.aborted) {\n\t\t\treturn Promise.reject(createAbortError());\n\t\t}\n\t\tlet cleanupProgressListener: (() => void) | undefined;\n\t\tif (onProgress && !this.startedManager) {\n\t\t\tthis.startupListeners.add(onProgress);\n\t\t\tcleanupProgressListener = () => {\n\t\t\t\tthis.startupListeners.delete(onProgress);\n\t\t\t\tsignal?.removeEventListener(\"abort\", cleanupProgressListener!);\n\t\t\t};\n\t\t\tsignal?.addEventListener(\"abort\", cleanupProgressListener, { once: true });\n\t\t\t// Joining an in-flight startup: replay the current stage.\n\t\t\tif (this.managerPromise && this.lastStartupMessage) {\n\t\t\t\tonProgress(this.lastStartupMessage);\n\t\t\t}\n\t\t}\n\t\tif (!this.managerPromise) {\n\t\t\tconst startup = this.startKernel(signal);\n\t\t\tthis.managerPromise = startup;\n\t\t\tstartup.then(\n\t\t\t\t(m) => {\n\t\t\t\t\tif (this.managerPromise === startup) {\n\t\t\t\t\t\tthis.startedManager = m;\n\t\t\t\t\t}\n\t\t\t\t\tthis.settleStartup();\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\t// Clear the memo so the next ensure() retries instead of\n\t\t\t\t\t// rethrowing a cached rejection forever.\n\t\t\t\t\tif (this.managerPromise === startup) {\n\t\t\t\t\t\tthis.managerPromise = undefined;\n\t\t\t\t\t}\n\t\t\t\t\tthis.settleStartup();\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn raceWithAbort(this.managerPromise, signal).finally(() => {\n\t\t\tcleanupProgressListener?.();\n\t\t});\n\t}\n\n\tprivate settleStartup(): void {\n\t\tthis.startupListeners.clear();\n\t\tthis.lastStartupMessage = undefined;\n\t}\n\n\tprivate emitStartupProgress(message: string): void {\n\t\tthis.lastStartupMessage = message;\n\t\tfor (const listener of [...this.startupListeners]) {\n\t\t\tlistener(message);\n\t\t}\n\t}\n\n\tprivate async startKernel(signal?: AbortSignal): Promise<KernelClient> {\n\t\tconst startupAbort = createLinkedAbortSignal([this.disposeController.signal, signal]);\n\t\tconst startupSignal = startupAbort.signal;\n\t\t// Wait for a previous provisioner (e.g. on /reload) to finish disposing — and\n\t\t// flushing its final snapshot — before we read that snapshot back, so the two\n\t\t// kernels can't race over the same on-disk file. Guarded so the common\n\t\t// no-gate path stays synchronous (callers rely on prompt startup progress).\n\t\ttry {\n\t\t\tif (this.options?.readyGate) {\n\t\t\t\tawait raceWithAbort(\n\t\t\t\t\tthis.options.readyGate.catch(() => {}),\n\t\t\t\t\tstartupSignal,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst snapshotDir = this.options?.snapshotDir;\n\t\t\t// Always inject an absolute trusted shell (undefined only on win32\n\t\t\t// without bash, where the runtime's teaching error fires instead).\n\t\t\tconst shellPath = resolveKernelBashShell(this.options?.shellPath);\n\t\t\tconst commandPrefix = this.options?.commandPrefix;\n\t\t\tconst bootstrapCode = buildRlmBootstrapCode(this.options?.pythonSkills);\n\t\t\tconst m = new ReplKernelManager({\n\t\t\t\tpython: this.options?.python,\n\t\t\t\tcwd: this.cwd,\n\t\t\t\t// bash() reads these to pick its shell and command prefix.\n\t\t\t\tenv: {\n\t\t\t\t\t...this.options?.env,\n\t\t\t\t\t...(shellPath ? { ASM_AGENT_BASH_SHELL: shellPath } : {}),\n\t\t\t\t\t...(commandPrefix ? { ASM_AGENT_BASH_COMMAND_PREFIX: commandPrefix } : {}),\n\t\t\t\t},\n\t\t\t\tsessionId: this.options?.sessionId,\n\t\t\t\thostHandlers: this.options?.hostHandlers,\n\t\t\t\tpythonSkills: this.options?.pythonSkills,\n\t\t\t\t// Only persistent sessions (which have an artifact dir) get a revivable snapshot.\n\t\t\t\tsnapshot: snapshotDir\n\t\t\t\t\t? { path: snapshotPathIn(snapshotDir), manifestPath: manifestPathIn(snapshotDir) }\n\t\t\t\t\t: undefined,\n\t\t\t\tbootstrapCode,\n\t\t\t});\n\t\t\tlet pendingRestore: RestoreResult | undefined;\n\t\t\ttry {\n\t\t\t\t// Emitted synchronously (before the permit await) so a listener attaching\n\t\t\t\t// mid-flight can replay the current stage.\n\t\t\t\tthis.emitStartupProgress(\"Starting Python kernel...\");\n\t\t\t\t// Only the process spawn + port resolve contends for OS resources under a\n\t\t\t\t// fan-out, and it is bounded by start()'s own timeouts — so the permit\n\t\t\t\t// covers only start(). Restore/bootstrap run per-kernel afterwards and are\n\t\t\t\t// unbounded execute()s; holding the global permit across them could pin it\n\t\t\t\t// forever on a wedged bootstrap and starve every other session's boot.\n\t\t\t\tawait withKernelBootPermit(() => {\n\t\t\t\t\t// Disposed while queued for the permit — don't spawn a kernel nobody wants.\n\t\t\t\t\tif (startupSignal.aborted) throw new Error(\"Kernel provisioner disposed before start\");\n\t\t\t\t\treturn m.start({\n\t\t\t\t\t\tonBootstrapProgress: (message) => this.emitStartupProgress(message),\n\t\t\t\t\t\tsignal: startupSignal,\n\t\t\t\t\t});\n\t\t\t\t}, startupSignal);\n\t\t\t\t// Revive a prior session's namespace before the bootstrap, so the bootstrap\n\t\t\t\t// then overwrites live handles (rlm, skills) on top of anything restored.\n\t\t\t\tif (snapshotDir) {\n\t\t\t\t\tconst snapshotExisted = existsSync(snapshotPathIn(snapshotDir));\n\t\t\t\t\tthis.emitStartupProgress(\"Restoring Python state...\");\n\t\t\t\t\tconst restore = await raceWithAbort(m.restoreState(), startupSignal);\n\t\t\t\t\tif (snapshotExisted) {\n\t\t\t\t\t\tpendingRestore = restore ?? { restored: [], failed: [], path: snapshotPathIn(snapshotDir) };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.emitStartupProgress(\"Preparing Python runtime...\");\n\t\t\t\tconst bootstrap = await m.execute(bootstrapCode, {\n\t\t\t\t\tsignal: startupSignal,\n\t\t\t\t});\n\t\t\t\tif (bootstrap.status !== \"ok\") {\n\t\t\t\t\tconst details = [bootstrap.stderr, bootstrap.error?.traceback.join(\"\\n\")].filter(Boolean).join(\"\\n\");\n\t\t\t\t\tthrow new Error(`Failed to initialize rlm runtime in the Python kernel:\\n${details}`);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Never leak the kernel process if startup fails after spawn — and never\n\t\t\t\t// surface the failure before the teardown (final snapshot flush included)\n\t\t\t\t// finished, or a replacement provisioner gated on this dispose could\n\t\t\t\t// race the still-flushing kernel over the same snapshot files.\n\t\t\t\tawait m.shutdown({ snapshot: true, drainHostRequests: true }).catch(() => undefined);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\t// Only tell the model what was revived once the kernel is actually usable —\n\t\t\t// a notice claiming restored state must never outlive a failed bootstrap.\n\t\t\tif (pendingRestore) {\n\t\t\t\tthis._lastRestore = pendingRestore;\n\t\t\t\tthis.options?.onRestore?.(pendingRestore);\n\t\t\t}\n\t\t\tif (this.options?.kernelManagerRef) {\n\t\t\t\tthis.options.kernelManagerRef.current = m;\n\t\t\t}\n\t\t\treturn m;\n\t\t} finally {\n\t\t\tstartupAbort.cleanup();\n\t\t}\n\t}\n}\n\nasync function chooseBusyKernelAction(\n\tctx: ExtensionContext | undefined,\n\tsignal: AbortSignal | undefined,\n): Promise<\"wait\" | \"kill\" | \"cancel\"> {\n\tif (!ctx?.hasUI) {\n\t\treturn \"cancel\";\n\t}\n\tconst choice = await ctx.ui.select(BUSY_KERNEL_PROMPT, [BUSY_KERNEL_WAIT_CHOICE, BUSY_KERNEL_KILL_CHOICE], {\n\t\tsignal,\n\t});\n\tif (choice === BUSY_KERNEL_WAIT_CHOICE) {\n\t\treturn \"wait\";\n\t}\n\tif (choice === BUSY_KERNEL_KILL_CHOICE) {\n\t\treturn \"kill\";\n\t}\n\treturn \"cancel\";\n}\n\nasync function executeWithBusyKernelChoice(\n\tprovisioner: IpythonKernelProvisioner,\n\treportStartupProgress: KernelBootstrapProgressHandler,\n\ttoolCallId: string,\n\tcode: string,\n\tsignal: AbortSignal | undefined,\n\tonStream: (chunk: string, name: \"stdout\" | \"stderr\") => void,\n\tonWorkingMessage: (message?: string) => void,\n\tonLateSentAgentMessage: ((toolCallId: string, message: KernelSentAgentMessage) => void) | undefined,\n\tctx: ExtensionContext | undefined,\n): Promise<{ result: ExecuteResult; kernelRestarted: boolean }> {\n\tlet kernelRestarted = false;\n\twhile (true) {\n\t\tconst m = await provisioner.ensure(reportStartupProgress, signal);\n\t\ttry {\n\t\t\treturn {\n\t\t\t\tresult: await m.execute(code, {\n\t\t\t\t\tsignal,\n\t\t\t\t\tonStream,\n\t\t\t\t\tonLateSentAgentMessage: onLateSentAgentMessage\n\t\t\t\t\t\t? (message) => onLateSentAgentMessage(toolCallId, message)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t}),\n\t\t\t\tkernelRestarted,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof KernelBusyAfterInterruptError) || signal?.aborted) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst action = await chooseBusyKernelAction(ctx, signal);\n\t\t\tif (action === \"wait\") {\n\t\t\t\tonWorkingMessage(\"Waiting for Python kernel...\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (action === \"kill\") {\n\t\t\t\tonWorkingMessage(\"Restarting Python kernel...\");\n\t\t\t\tawait provisioner.kill();\n\t\t\t\tkernelRestarted = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\n/** Turn kernel image attachments into `ImageContent` blocks; non-image types are dropped. */\nexport function imageBlocksFromAttachments(attachments: readonly KernelAttachment[] | undefined): ImageContent[] {\n\tif (!attachments) return [];\n\treturn attachments\n\t\t.filter((a) => IMAGE_MIME_TYPES.has(a.mimeType))\n\t\t.map((a) => ({ type: \"image\", data: a.data, mimeType: a.mimeType }));\n}\n\nexport function createIpythonToolDefinition(\n\tcwd: string,\n\toptions?: IpythonToolOptions,\n): ToolDefinition<typeof ipythonSchema, IpythonToolDetails> {\n\tconst provisioner = options?.provisioner ?? new IpythonKernelProvisioner(cwd, options);\n\n\treturn {\n\t\tname: \"ipython\",\n\t\tlabel: \"ipython\",\n\t\tdescription:\n\t\t\t\"Execute Python code in a persistent Python REPL. Top-level `await` is supported. Variables, imports, and loaded data persist across calls, and are revived on a best-effort basis when a session is resumed (objects that cannot be serialized are dropped and reported). Run shell commands with `bash('cmd')` / `await bash('cmd')`. Project imports, tests, scripts, CLIs, and dependency checks should run through the target project's own environment.\",\n\t\tpromptSnippet: \"ipython - persistent Python REPL for code, state, and bash() orchestration\",\n\t\t// The kernel is single-threaded — pi must not run two ipython calls in parallel within a batch.\n\t\texecutionMode: \"sequential\",\n\t\tparameters: ipythonSchema,\n\t\texecute: async (toolCallId, params, signal, onUpdate, ctx) => {\n\t\t\tlet hasWorkingMessage = false;\n\t\t\tconst setToolWorkingMessage = (message?: string) => {\n\t\t\t\tsetWorkingMessage(ctx, message);\n\t\t\t\thasWorkingMessage = message !== undefined;\n\t\t\t};\n\t\t\tconst reportStartupProgress: KernelBootstrapProgressHandler = (message) => {\n\t\t\t\tsetToolWorkingMessage(message);\n\t\t\t\tonUpdate?.({\n\t\t\t\t\tcontent: [{ type: \"text\", text: message }],\n\t\t\t\t\tdetails: { status: \"starting\" },\n\t\t\t\t});\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tconst { result: r, kernelRestarted } = await executeWithBusyKernelChoice(\n\t\t\t\t\tprovisioner,\n\t\t\t\t\treportStartupProgress,\n\t\t\t\t\ttoolCallId,\n\t\t\t\t\tparams.code,\n\t\t\t\t\tsignal,\n\t\t\t\t\t(chunk) => {\n\t\t\t\t\t\tonUpdate?.({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: chunk }],\n\t\t\t\t\t\t\tdetails: { status: \"ok\" },\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tsetToolWorkingMessage,\n\t\t\t\t\toptions?.onLateSentAgentMessage,\n\t\t\t\t\tctx,\n\t\t\t\t);\n\n\t\t\t\tlet text = r.stdout;\n\t\t\t\tif (r.stderr) text += (text ? \"\\n\" : \"\") + r.stderr;\n\t\t\t\tif (r.result) text += (text ? \"\\n\" : \"\") + r.result;\n\t\t\t\tif (r.status === \"error\" && r.error) {\n\t\t\t\t\ttext += (text ? \"\\n\" : \"\") + r.error.traceback.join(\"\\n\");\n\t\t\t\t}\n\t\t\t\tif (r.backgroundOutput) {\n\t\t\t\t\ttext += `${text ? \"\\n\" : \"\"}[background output (unattributed)]\\n${r.backgroundOutput}`;\n\t\t\t\t}\n\t\t\t\tif (kernelRestarted) {\n\t\t\t\t\ttext = text ? `${KERNEL_RESTART_NOTICE}\\n\\n${text}` : KERNEL_RESTART_NOTICE;\n\t\t\t\t}\n\n\t\t\t\tconst imageBlocks = imageBlocksFromAttachments(r.attachments);\n\t\t\t\tconst content: (TextContent | ImageContent)[] = [{ type: \"text\", text: text || \"\" }, ...imageBlocks];\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent,\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tdurationMs: r.durationMs,\n\t\t\t\t\t\tstatus: r.status,\n\t\t\t\t\t\terrorEname: r.error?.ename,\n\t\t\t\t\t\tstdout: r.stdout,\n\t\t\t\t\t\tstderr: r.stderr,\n\t\t\t\t\t\tresult: r.result,\n\t\t\t\t\t\tbackgroundOutput: r.backgroundOutput,\n\t\t\t\t\t\tdiffs: r.diffs,\n\t\t\t\t\t\tattachments: r.attachments,\n\t\t\t\t\t\tsentAgentMessages: r.sentAgentMessages,\n\t\t\t\t\t\tkernelRestarted,\n\t\t\t\t\t\terror: r.error,\n\t\t\t\t\t},\n\t\t\t\t\tisError: r.status === \"error\" || r.status === \"aborted\",\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tif (hasWorkingMessage) {\n\t\t\t\t\tsetToolWorkingMessage();\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport function createIpythonTool(cwd: string, options?: IpythonToolOptions): AgentTool<typeof ipythonSchema> {\n\treturn wrapToolDefinition(createIpythonToolDefinition(cwd, options));\n}\n"]}
@@ -279,7 +279,7 @@ export class IpythonKernelProvisioner {
279
279
  return;
280
280
  try {
281
281
  const m = await pending;
282
- await m.dispose();
282
+ await m.shutdown({ snapshot: true, drainHostRequests: true });
283
283
  }
284
284
  catch {
285
285
  // a failed startup already cleaned up after itself
@@ -366,6 +366,7 @@ export class IpythonKernelProvisioner {
366
366
  // without bash, where the runtime's teaching error fires instead).
367
367
  const shellPath = resolveKernelBashShell(this.options?.shellPath);
368
368
  const commandPrefix = this.options?.commandPrefix;
369
+ const bootstrapCode = buildRlmBootstrapCode(this.options?.pythonSkills);
369
370
  const m = new ReplKernelManager({
370
371
  python: this.options?.python,
371
372
  cwd: this.cwd,
@@ -382,6 +383,7 @@ export class IpythonKernelProvisioner {
382
383
  snapshot: snapshotDir
383
384
  ? { path: snapshotPathIn(snapshotDir), manifestPath: manifestPathIn(snapshotDir) }
384
385
  : undefined,
386
+ bootstrapCode,
385
387
  });
386
388
  let pendingRestore;
387
389
  try {
@@ -413,7 +415,7 @@ export class IpythonKernelProvisioner {
413
415
  }
414
416
  }
415
417
  this.emitStartupProgress("Preparing Python runtime...");
416
- const bootstrap = await m.execute(buildRlmBootstrapCode(this.options?.pythonSkills), {
418
+ const bootstrap = await m.execute(bootstrapCode, {
417
419
  signal: startupSignal,
418
420
  });
419
421
  if (bootstrap.status !== "ok") {
@@ -422,8 +424,11 @@ export class IpythonKernelProvisioner {
422
424
  }
423
425
  }
424
426
  catch (error) {
425
- // Never leak the kernel process if startup fails after spawn.
426
- void m.dispose();
427
+ // Never leak the kernel process if startup fails after spawn — and never
428
+ // surface the failure before the teardown (final snapshot flush included)
429
+ // finished, or a replacement provisioner gated on this dispose could
430
+ // race the still-flushing kernel over the same snapshot files.
431
+ await m.shutdown({ snapshot: true, drainHostRequests: true }).catch(() => undefined);
427
432
  throw error;
428
433
  }
429
434
  // Only tell the model what was revived once the kernel is actually usable —
@@ -1 +1 @@
1
- {"version":3,"file":"ipython.js","sourceRoot":"","sources":["../../../src/core/tools/ipython.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport type { AgentTool } from \"@asm-agent/agent\";\nimport type { ImageContent, TextContent } from \"@asm-agent/ai\";\nimport { type Static, Type } from \"typebox\";\nimport { IMAGE_MIME_TYPES } from \"../../utils/mime.js\";\nimport { resolveKernelBashShell } from \"../../utils/shell.js\";\nimport type { ExtensionContext, ToolDefinition } from \"../extensions/types.js\";\nimport { withKernelBootPermit } from \"../kernel/boot-gate.js\";\nimport type { KernelBootstrapProgressHandler } from \"../kernel/bootstrap.js\";\nimport {\n\ttype ExecuteResult,\n\ttype HostRequestHandlers,\n\ttype KernelAttachment,\n\tKernelBusyAfterInterruptError,\n\ttype KernelClient,\n\ttype KernelDiffDisplay,\n\ttype KernelSentAgentMessage,\n\tReplKernelManager,\n} from \"../kernel/index.js\";\nimport { manifestPathIn, type RestoreResult, snapshotPathIn } from \"../kernel/state-snapshot.js\";\nimport type { PythonSkillRuntimeInfo } from \"../skills.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\nconst RLM_BOOTSTRAP_HEADER_CODE = `\nimport asyncio\nimport os as _asm_agent_os\n\n_asm_agent_os.environ[\"NO_COLOR\"] = \"1\"\n`.trim();\n\nconst RLM_BOOTSTRAP_RUNTIME_CODE = `\ntry:\n import rlm as _asm_agent_rlm_module\n rlm = _asm_agent_rlm_module.rlm\n bash = _asm_agent_rlm_module.bash\n import rlm.mcp as mcp\nexcept Exception as _asm_agent_rlm_error:\n _ASM_AGENT_RLM_IMPORT_ERROR = str(_asm_agent_rlm_error)\n\n class _AsmAgentMissingRlm:\n def _raise_missing(self):\n raise RuntimeError(\n \"asm-agent-runtime is not installed in this kernel. \"\n \"Remove ~/.asm-agent/kernel-venv so asm-agent can rebuild it, or set \"\n \"ASM_AGENT_KERNEL_PYTHON to a kernel environment with asm-agent-runtime installed. \"\n f\"Import error: {_ASM_AGENT_RLM_IMPORT_ERROR}\"\n )\n\n async def run(self, prompt, **kwargs):\n self._raise_missing()\n\n async def find_models(self, query=\"\", limit=8):\n self._raise_missing()\n\n async def list_subagents(self):\n self._raise_missing()\n\n async def delete_subagent(self, target):\n self._raise_missing()\n\n async def __call__(self, prompt, **kwargs):\n return await self.run(prompt, **kwargs)\n\n rlm = _AsmAgentMissingRlm()\n\n def bash(command):\n rlm._raise_missing()\n`.trim();\n\nexport function buildRlmBootstrapCode(pythonSkills: readonly PythonSkillRuntimeInfo[] = []): string {\n\tconst baseCode = [RLM_BOOTSTRAP_HEADER_CODE, RLM_BOOTSTRAP_RUNTIME_CODE].join(\"\\n\\n\");\n\tconst importNames = [...new Set(pythonSkills.map((skill) => skill.importName))];\n\tif (importNames.length === 0) {\n\t\treturn baseCode;\n\t}\n\n\treturn `\n${baseCode}\n\nimport importlib as _asm_agent_importlib\nimport inspect as _asm_agent_inspect\nimport sys as _asm_agent_sys\nimport types as _asm_agent_types\n\nclass _AsmAgentCallableSkillModule(_asm_agent_types.ModuleType):\n async def __call__(self, *args, **kwargs):\n result = self.run(*args, **kwargs)\n if _asm_agent_inspect.isawaitable(result):\n return await result\n return result\n\nclass _AsmAgentUnavailableSkill:\n def __init__(self, name, error):\n self.__name__ = name\n self._asm_agent_import_error = error\n self.__doc__ = f\"Python skill {name} is unavailable: {error}\"\n\n async def run(self, *args, **kwargs):\n raise RuntimeError(\n f\"Python skill {self.__name__} is unavailable in this kernel. \"\n f\"Import error: {self._asm_agent_import_error}\"\n )\n\n async def __call__(self, *args, **kwargs):\n return await self.run(*args, **kwargs)\n\n def __repr__(self):\n return f\"<unavailable Python skill {self.__name__!r}: {self._asm_agent_import_error}>\"\n\ndef _asm_agent_wrap_skill_module(module):\n run = getattr(module, \"run\", None)\n if not callable(run):\n return module\n if isinstance(module, _AsmAgentCallableSkillModule):\n return module\n wrapped = _AsmAgentCallableSkillModule(module.__name__)\n wrapped.__dict__.update(module.__dict__)\n try:\n wrapped.__signature__ = _asm_agent_inspect.signature(run)\n except Exception:\n pass\n doc = getattr(run, \"__doc__\", None)\n if doc:\n wrapped.__doc__ = doc\n _asm_agent_sys.modules[module.__name__] = wrapped\n return wrapped\n\n_ASM_AGENT_SKILL_IMPORT_ERRORS = {}\n\nfor _asm_agent_skill_name in ${JSON.stringify(importNames)}:\n try:\n globals()[_asm_agent_skill_name] = _asm_agent_wrap_skill_module(\n _asm_agent_importlib.import_module(_asm_agent_skill_name)\n )\n except Exception as _asm_agent_skill_error:\n _ASM_AGENT_SKILL_IMPORT_ERRORS[_asm_agent_skill_name] = str(_asm_agent_skill_error)\n globals()[_asm_agent_skill_name] = _AsmAgentUnavailableSkill(\n _asm_agent_skill_name,\n str(_asm_agent_skill_error),\n )\n`.trim();\n}\n\nconst ipythonSchema = Type.Object({\n\tcode: Type.String({\n\t\tdescription:\n\t\t\t\"Python code to execute in the persistent Python REPL. Use the target project's own environment for project imports, tests, scripts, CLIs, and dependency checks instead of direct kernel imports.\",\n\t}),\n});\n\nconst BUSY_KERNEL_WAIT_CHOICE = \"Wait and preserve state\";\nconst BUSY_KERNEL_KILL_CHOICE = \"Kill kernel and restart\";\nconst BUSY_KERNEL_PROMPT = [\n\t\"Interrupted Python cell is still running\",\n\t\"Ctrl+C sent an interrupt, but the previous cell has not stopped yet. A new command cannot start until it finishes.\",\n\t\"Waiting preserves the current kernel state. Killing restarts the kernel and loses in-memory variables, imports, and running tasks.\",\n].join(\"\\n\");\nconst KERNEL_RESTART_NOTICE = [\n\t\"<ipython_kernel_reset>\",\n\t\"The Python kernel was restarted after a previous interrupted cell kept running. Variables, imports, async tasks, and open resources from before the restart are no longer available; recreate them before using them.\",\n\t\"</ipython_kernel_reset>\",\n].join(\"\\n\");\n\nfunction createAbortError(): Error {\n\treturn new Error(\"Python execution aborted\");\n}\n\nfunction raceWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined, onAbort?: () => void): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\tonAbort?.();\n\t\treturn Promise.reject(createAbortError());\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\tonAbort?.();\n\t\t\treject(createAbortError());\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\nfunction createLinkedAbortSignal(sources: readonly (AbortSignal | undefined)[]): {\n\tsignal: AbortSignal;\n\tcleanup: () => void;\n} {\n\tconst controller = new AbortController();\n\tconst cleanups: Array<() => void> = [];\n\tconst abort = () => controller.abort();\n\tfor (const source of sources) {\n\t\tif (!source) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (source.aborted) {\n\t\t\tcontroller.abort();\n\t\t\tcontinue;\n\t\t}\n\t\tconst listener = () => abort();\n\t\tsource.addEventListener(\"abort\", listener, { once: true });\n\t\tcleanups.push(() => source.removeEventListener(\"abort\", listener));\n\t}\n\treturn {\n\t\tsignal: controller.signal,\n\t\tcleanup: () => {\n\t\t\tfor (const cleanup of cleanups) {\n\t\t\t\tcleanup();\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction setWorkingMessage(ctx: ExtensionContext | undefined, message?: string): void {\n\ttry {\n\t\tctx?.ui.setWorkingMessage(message);\n\t} catch {\n\t\t// Stale UI context; cosmetic only.\n\t}\n}\n\nexport type IpythonToolInput = Static<typeof ipythonSchema>;\n\nexport interface IpythonToolDetails {\n\tdurationMs?: number;\n\tstatus?: \"ok\" | \"error\" | \"aborted\" | \"starting\";\n\terrorEname?: string;\n\tstdout?: string;\n\tstderr?: string;\n\tresult?: string;\n\t/** Output that arrived without this cell's id (threads, other cells' leftovers), shown separately from stdout. */\n\tbackgroundOutput?: string;\n\t/** Diffs streamed from file edits, rendered by the cell view. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments loaded into context (e.g. by the attach-image skill). */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** True when this result came after killing and restarting a busy kernel. */\n\tkernelRestarted?: boolean;\n\terror?: {\n\t\tename: string;\n\t\tevalue: string;\n\t\ttraceback: string[];\n\t};\n}\n\nexport interface IpythonToolOptions {\n\t/** Python override. Must have asm-agent-runtime installed. */\n\tpython?: string;\n\tenv?: Record<string, string>;\n\t/** Command prefix prepended to every bash() command. */\n\tcommandPrefix?: string;\n\t/** Shell used by bash(). */\n\tshellPath?: string;\n\tsessionId?: string;\n\t/** Typed host request handlers for the kernel↔host bridge (rlm.run, goal.*, …). */\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly PythonSkillRuntimeInfo[];\n\t/** Per-session artifact dir where the kernel namespace snapshot is stored. Omit to disable snapshots. */\n\tsnapshotDir?: string;\n\t/** Resolves before this kernel starts — e.g. the previous provisioner's dispose, so a\n\t * /reload's old-kernel snapshot flush can't race the new kernel's restore. */\n\treadyGate?: Promise<unknown>;\n\t/** Filled with the live kernel client after the first kernel start; cleared on construction. */\n\tkernelManagerRef?: { current?: KernelClient };\n\t/**\n\t * Fires once per kernel start when a previous session's namespace was revived\n\t * (some names restored or some failed), so the session can tell the model.\n\t */\n\tonRestore?: (result: RestoreResult) => void;\n\tonLateSentAgentMessage?: (toolCallId: string, message: KernelSentAgentMessage) => void;\n\t/** Shared provisioner owning the kernel lifecycle. When provided, the remaining options are ignored. */\n\tprovisioner?: IpythonKernelProvisioner;\n}\n\n/**\n * Owns the lazy create+start+runtime-bootstrap of one session's Python kernel.\n *\n * Concurrent ensure() calls await the same in-flight startup, a failed startup\n * clears the memo so the next call retries fresh, and progress listeners can\n * attach mid-flight (a tool call racing a background prewarm()).\n */\nexport class IpythonKernelProvisioner {\n\tprivate managerPromise?: Promise<KernelClient>;\n\tprivate startedManager?: KernelClient;\n\tprivate readonly startupListeners = new Set<KernelBootstrapProgressHandler>();\n\tprivate lastStartupMessage?: string;\n\tprivate _lastRestore?: RestoreResult;\n\tprivate readonly disposeController = new AbortController();\n\n\tconstructor(\n\t\tprivate readonly cwd: string,\n\t\tprivate readonly options?: Omit<IpythonToolOptions, \"provisioner\">,\n\t) {\n\t\tif (options?.kernelManagerRef) {\n\t\t\toptions.kernelManagerRef.current = undefined;\n\t\t}\n\t}\n\n\t/** The kernel manager, once a startup has completed successfully. */\n\tget manager(): KernelClient | undefined {\n\t\treturn this.startedManager;\n\t}\n\n\t/** Result of reviving a prior session's namespace on the last kernel start, if any. */\n\tget lastRestore(): RestoreResult | undefined {\n\t\treturn this._lastRestore;\n\t}\n\n\t/** Start the kernel in the background. Failures are swallowed here and surface on the next ensure(). */\n\tprewarm(): void {\n\t\tvoid this.ensure().catch(() => {});\n\t}\n\n\t/** Whether a kernel has finished starting and is currently running. */\n\tget hasRunningKernel(): boolean {\n\t\treturn this.startedManager?.isRunning ?? false;\n\t}\n\n\t/** Remove live variables above the snapshot's per-variable size limit. */\n\tasync pruneOversizedVariables(): Promise<string[] | null> {\n\t\tconst m = this.startedManager ?? (await this.managerPromise?.catch(() => undefined));\n\t\tconst result = await m?.pruneOversizedVariables();\n\t\treturn result ? (result.pruned ?? []) : null;\n\t}\n\n\t/** Live user-defined names in the kernel namespace, or null if listing failed / no kernel. */\n\tasync listNamespaceNames(signal?: AbortSignal): Promise<string[] | null> {\n\t\tconst m = this.startedManager ?? (await this.managerPromise?.catch(() => undefined));\n\t\treturn (await m?.listNamespaceNames(signal)) ?? null;\n\t}\n\n\t/** Dispose the kernel owned by this provisioner, including one still starting up. */\n\tasync dispose(): Promise<void> {\n\t\t// Drops a still-queued boot out of the semaphore and short-circuits an\n\t\t// in-flight startKernel before it spawns, so a disposed session's boot\n\t\t// doesn't waste a slot during a fan-out.\n\t\tthis.disposeController.abort();\n\t\tconst pending = this.managerPromise;\n\t\tthis.managerPromise = undefined;\n\t\tthis.startedManager = undefined;\n\t\tif (this.options?.kernelManagerRef) {\n\t\t\tthis.options.kernelManagerRef.current = undefined;\n\t\t}\n\t\tif (!pending) return;\n\t\ttry {\n\t\t\tconst m = await pending;\n\t\t\tawait m.dispose();\n\t\t} catch {\n\t\t\t// a failed startup already cleaned up after itself\n\t\t}\n\t}\n\n\tasync kill(): Promise<void> {\n\t\tconst pending = this.managerPromise;\n\t\tthis.managerPromise = undefined;\n\t\tthis.startedManager = undefined;\n\t\tif (this.options?.kernelManagerRef) {\n\t\t\tthis.options.kernelManagerRef.current = undefined;\n\t\t}\n\t\tif (!pending) return;\n\t\ttry {\n\t\t\tconst m = await pending;\n\t\t\tawait m.kill();\n\t\t} catch {\n\t\t\t// a failed startup already cleaned up after itself\n\t\t}\n\t}\n\n\tensure(onProgress?: KernelBootstrapProgressHandler, signal?: AbortSignal): Promise<KernelClient> {\n\t\tif (signal?.aborted) {\n\t\t\treturn Promise.reject(createAbortError());\n\t\t}\n\t\tlet cleanupProgressListener: (() => void) | undefined;\n\t\tif (onProgress && !this.startedManager) {\n\t\t\tthis.startupListeners.add(onProgress);\n\t\t\tcleanupProgressListener = () => {\n\t\t\t\tthis.startupListeners.delete(onProgress);\n\t\t\t\tsignal?.removeEventListener(\"abort\", cleanupProgressListener!);\n\t\t\t};\n\t\t\tsignal?.addEventListener(\"abort\", cleanupProgressListener, { once: true });\n\t\t\t// Joining an in-flight startup: replay the current stage.\n\t\t\tif (this.managerPromise && this.lastStartupMessage) {\n\t\t\t\tonProgress(this.lastStartupMessage);\n\t\t\t}\n\t\t}\n\t\tif (!this.managerPromise) {\n\t\t\tconst startup = this.startKernel(signal);\n\t\t\tthis.managerPromise = startup;\n\t\t\tstartup.then(\n\t\t\t\t(m) => {\n\t\t\t\t\tif (this.managerPromise === startup) {\n\t\t\t\t\t\tthis.startedManager = m;\n\t\t\t\t\t}\n\t\t\t\t\tthis.settleStartup();\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\t// Clear the memo so the next ensure() retries instead of\n\t\t\t\t\t// rethrowing a cached rejection forever.\n\t\t\t\t\tif (this.managerPromise === startup) {\n\t\t\t\t\t\tthis.managerPromise = undefined;\n\t\t\t\t\t}\n\t\t\t\t\tthis.settleStartup();\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn raceWithAbort(this.managerPromise, signal).finally(() => {\n\t\t\tcleanupProgressListener?.();\n\t\t});\n\t}\n\n\tprivate settleStartup(): void {\n\t\tthis.startupListeners.clear();\n\t\tthis.lastStartupMessage = undefined;\n\t}\n\n\tprivate emitStartupProgress(message: string): void {\n\t\tthis.lastStartupMessage = message;\n\t\tfor (const listener of [...this.startupListeners]) {\n\t\t\tlistener(message);\n\t\t}\n\t}\n\n\tprivate async startKernel(signal?: AbortSignal): Promise<KernelClient> {\n\t\tconst startupAbort = createLinkedAbortSignal([this.disposeController.signal, signal]);\n\t\tconst startupSignal = startupAbort.signal;\n\t\t// Wait for a previous provisioner (e.g. on /reload) to finish disposing — and\n\t\t// flushing its final snapshot — before we read that snapshot back, so the two\n\t\t// kernels can't race over the same on-disk file. Guarded so the common\n\t\t// no-gate path stays synchronous (callers rely on prompt startup progress).\n\t\ttry {\n\t\t\tif (this.options?.readyGate) {\n\t\t\t\tawait raceWithAbort(\n\t\t\t\t\tthis.options.readyGate.catch(() => {}),\n\t\t\t\t\tstartupSignal,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst snapshotDir = this.options?.snapshotDir;\n\t\t\t// Always inject an absolute trusted shell (undefined only on win32\n\t\t\t// without bash, where the runtime's teaching error fires instead).\n\t\t\tconst shellPath = resolveKernelBashShell(this.options?.shellPath);\n\t\t\tconst commandPrefix = this.options?.commandPrefix;\n\t\t\tconst m = new ReplKernelManager({\n\t\t\t\tpython: this.options?.python,\n\t\t\t\tcwd: this.cwd,\n\t\t\t\t// bash() reads these to pick its shell and command prefix.\n\t\t\t\tenv: {\n\t\t\t\t\t...this.options?.env,\n\t\t\t\t\t...(shellPath ? { ASM_AGENT_BASH_SHELL: shellPath } : {}),\n\t\t\t\t\t...(commandPrefix ? { ASM_AGENT_BASH_COMMAND_PREFIX: commandPrefix } : {}),\n\t\t\t\t},\n\t\t\t\tsessionId: this.options?.sessionId,\n\t\t\t\thostHandlers: this.options?.hostHandlers,\n\t\t\t\tpythonSkills: this.options?.pythonSkills,\n\t\t\t\t// Only persistent sessions (which have an artifact dir) get a revivable snapshot.\n\t\t\t\tsnapshot: snapshotDir\n\t\t\t\t\t? { path: snapshotPathIn(snapshotDir), manifestPath: manifestPathIn(snapshotDir) }\n\t\t\t\t\t: undefined,\n\t\t\t});\n\t\t\tlet pendingRestore: RestoreResult | undefined;\n\t\t\ttry {\n\t\t\t\t// Emitted synchronously (before the permit await) so a listener attaching\n\t\t\t\t// mid-flight can replay the current stage.\n\t\t\t\tthis.emitStartupProgress(\"Starting Python kernel...\");\n\t\t\t\t// Only the process spawn + port resolve contends for OS resources under a\n\t\t\t\t// fan-out, and it is bounded by start()'s own timeouts — so the permit\n\t\t\t\t// covers only start(). Restore/bootstrap run per-kernel afterwards and are\n\t\t\t\t// unbounded execute()s; holding the global permit across them could pin it\n\t\t\t\t// forever on a wedged bootstrap and starve every other session's boot.\n\t\t\t\tawait withKernelBootPermit(() => {\n\t\t\t\t\t// Disposed while queued for the permit — don't spawn a kernel nobody wants.\n\t\t\t\t\tif (startupSignal.aborted) throw new Error(\"Kernel provisioner disposed before start\");\n\t\t\t\t\treturn m.start({\n\t\t\t\t\t\tonBootstrapProgress: (message) => this.emitStartupProgress(message),\n\t\t\t\t\t\tsignal: startupSignal,\n\t\t\t\t\t});\n\t\t\t\t}, startupSignal);\n\t\t\t\t// Revive a prior session's namespace before the bootstrap, so the bootstrap\n\t\t\t\t// then overwrites live handles (rlm, skills) on top of anything restored.\n\t\t\t\tif (snapshotDir) {\n\t\t\t\t\tconst snapshotExisted = existsSync(snapshotPathIn(snapshotDir));\n\t\t\t\t\tthis.emitStartupProgress(\"Restoring Python state...\");\n\t\t\t\t\tconst restore = await raceWithAbort(m.restoreState(), startupSignal);\n\t\t\t\t\tif (snapshotExisted) {\n\t\t\t\t\t\tpendingRestore = restore ?? { restored: [], failed: [], path: snapshotPathIn(snapshotDir) };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.emitStartupProgress(\"Preparing Python runtime...\");\n\t\t\t\tconst bootstrap = await m.execute(buildRlmBootstrapCode(this.options?.pythonSkills), {\n\t\t\t\t\tsignal: startupSignal,\n\t\t\t\t});\n\t\t\t\tif (bootstrap.status !== \"ok\") {\n\t\t\t\t\tconst details = [bootstrap.stderr, bootstrap.error?.traceback.join(\"\\n\")].filter(Boolean).join(\"\\n\");\n\t\t\t\t\tthrow new Error(`Failed to initialize rlm runtime in the Python kernel:\\n${details}`);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Never leak the kernel process if startup fails after spawn.\n\t\t\t\tvoid m.dispose();\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\t// Only tell the model what was revived once the kernel is actually usable —\n\t\t\t// a notice claiming restored state must never outlive a failed bootstrap.\n\t\t\tif (pendingRestore) {\n\t\t\t\tthis._lastRestore = pendingRestore;\n\t\t\t\tthis.options?.onRestore?.(pendingRestore);\n\t\t\t}\n\t\t\tif (this.options?.kernelManagerRef) {\n\t\t\t\tthis.options.kernelManagerRef.current = m;\n\t\t\t}\n\t\t\treturn m;\n\t\t} finally {\n\t\t\tstartupAbort.cleanup();\n\t\t}\n\t}\n}\n\nasync function chooseBusyKernelAction(\n\tctx: ExtensionContext | undefined,\n\tsignal: AbortSignal | undefined,\n): Promise<\"wait\" | \"kill\" | \"cancel\"> {\n\tif (!ctx?.hasUI) {\n\t\treturn \"cancel\";\n\t}\n\tconst choice = await ctx.ui.select(BUSY_KERNEL_PROMPT, [BUSY_KERNEL_WAIT_CHOICE, BUSY_KERNEL_KILL_CHOICE], {\n\t\tsignal,\n\t});\n\tif (choice === BUSY_KERNEL_WAIT_CHOICE) {\n\t\treturn \"wait\";\n\t}\n\tif (choice === BUSY_KERNEL_KILL_CHOICE) {\n\t\treturn \"kill\";\n\t}\n\treturn \"cancel\";\n}\n\nasync function executeWithBusyKernelChoice(\n\tprovisioner: IpythonKernelProvisioner,\n\treportStartupProgress: KernelBootstrapProgressHandler,\n\ttoolCallId: string,\n\tcode: string,\n\tsignal: AbortSignal | undefined,\n\tonStream: (chunk: string, name: \"stdout\" | \"stderr\") => void,\n\tonWorkingMessage: (message?: string) => void,\n\tonLateSentAgentMessage: ((toolCallId: string, message: KernelSentAgentMessage) => void) | undefined,\n\tctx: ExtensionContext | undefined,\n): Promise<{ result: ExecuteResult; kernelRestarted: boolean }> {\n\tlet kernelRestarted = false;\n\twhile (true) {\n\t\tconst m = await provisioner.ensure(reportStartupProgress, signal);\n\t\ttry {\n\t\t\treturn {\n\t\t\t\tresult: await m.execute(code, {\n\t\t\t\t\tsignal,\n\t\t\t\t\tonStream,\n\t\t\t\t\tonLateSentAgentMessage: onLateSentAgentMessage\n\t\t\t\t\t\t? (message) => onLateSentAgentMessage(toolCallId, message)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t}),\n\t\t\t\tkernelRestarted,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof KernelBusyAfterInterruptError) || signal?.aborted) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst action = await chooseBusyKernelAction(ctx, signal);\n\t\t\tif (action === \"wait\") {\n\t\t\t\tonWorkingMessage(\"Waiting for Python kernel...\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (action === \"kill\") {\n\t\t\t\tonWorkingMessage(\"Restarting Python kernel...\");\n\t\t\t\tawait provisioner.kill();\n\t\t\t\tkernelRestarted = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\n/** Turn kernel image attachments into `ImageContent` blocks; non-image types are dropped. */\nexport function imageBlocksFromAttachments(attachments: readonly KernelAttachment[] | undefined): ImageContent[] {\n\tif (!attachments) return [];\n\treturn attachments\n\t\t.filter((a) => IMAGE_MIME_TYPES.has(a.mimeType))\n\t\t.map((a) => ({ type: \"image\", data: a.data, mimeType: a.mimeType }));\n}\n\nexport function createIpythonToolDefinition(\n\tcwd: string,\n\toptions?: IpythonToolOptions,\n): ToolDefinition<typeof ipythonSchema, IpythonToolDetails> {\n\tconst provisioner = options?.provisioner ?? new IpythonKernelProvisioner(cwd, options);\n\n\treturn {\n\t\tname: \"ipython\",\n\t\tlabel: \"ipython\",\n\t\tdescription:\n\t\t\t\"Execute Python code in a persistent Python REPL. Top-level `await` is supported. Variables, imports, and loaded data persist across calls, and are revived on a best-effort basis when a session is resumed (objects that cannot be serialized are dropped and reported). Run shell commands with `bash('cmd')` / `await bash('cmd')`. Project imports, tests, scripts, CLIs, and dependency checks should run through the target project's own environment.\",\n\t\tpromptSnippet: \"ipython - persistent Python REPL for code, state, and bash() orchestration\",\n\t\t// The kernel is single-threaded — pi must not run two ipython calls in parallel within a batch.\n\t\texecutionMode: \"sequential\",\n\t\tparameters: ipythonSchema,\n\t\texecute: async (toolCallId, params, signal, onUpdate, ctx) => {\n\t\t\tlet hasWorkingMessage = false;\n\t\t\tconst setToolWorkingMessage = (message?: string) => {\n\t\t\t\tsetWorkingMessage(ctx, message);\n\t\t\t\thasWorkingMessage = message !== undefined;\n\t\t\t};\n\t\t\tconst reportStartupProgress: KernelBootstrapProgressHandler = (message) => {\n\t\t\t\tsetToolWorkingMessage(message);\n\t\t\t\tonUpdate?.({\n\t\t\t\t\tcontent: [{ type: \"text\", text: message }],\n\t\t\t\t\tdetails: { status: \"starting\" },\n\t\t\t\t});\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tconst { result: r, kernelRestarted } = await executeWithBusyKernelChoice(\n\t\t\t\t\tprovisioner,\n\t\t\t\t\treportStartupProgress,\n\t\t\t\t\ttoolCallId,\n\t\t\t\t\tparams.code,\n\t\t\t\t\tsignal,\n\t\t\t\t\t(chunk) => {\n\t\t\t\t\t\tonUpdate?.({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: chunk }],\n\t\t\t\t\t\t\tdetails: { status: \"ok\" },\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tsetToolWorkingMessage,\n\t\t\t\t\toptions?.onLateSentAgentMessage,\n\t\t\t\t\tctx,\n\t\t\t\t);\n\n\t\t\t\tlet text = r.stdout;\n\t\t\t\tif (r.stderr) text += (text ? \"\\n\" : \"\") + r.stderr;\n\t\t\t\tif (r.result) text += (text ? \"\\n\" : \"\") + r.result;\n\t\t\t\tif (r.status === \"error\" && r.error) {\n\t\t\t\t\ttext += (text ? \"\\n\" : \"\") + r.error.traceback.join(\"\\n\");\n\t\t\t\t}\n\t\t\t\tif (r.backgroundOutput) {\n\t\t\t\t\ttext += `${text ? \"\\n\" : \"\"}[background output (unattributed)]\\n${r.backgroundOutput}`;\n\t\t\t\t}\n\t\t\t\tif (kernelRestarted) {\n\t\t\t\t\ttext = text ? `${KERNEL_RESTART_NOTICE}\\n\\n${text}` : KERNEL_RESTART_NOTICE;\n\t\t\t\t}\n\n\t\t\t\tconst imageBlocks = imageBlocksFromAttachments(r.attachments);\n\t\t\t\tconst content: (TextContent | ImageContent)[] = [{ type: \"text\", text: text || \"\" }, ...imageBlocks];\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent,\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tdurationMs: r.durationMs,\n\t\t\t\t\t\tstatus: r.status,\n\t\t\t\t\t\terrorEname: r.error?.ename,\n\t\t\t\t\t\tstdout: r.stdout,\n\t\t\t\t\t\tstderr: r.stderr,\n\t\t\t\t\t\tresult: r.result,\n\t\t\t\t\t\tbackgroundOutput: r.backgroundOutput,\n\t\t\t\t\t\tdiffs: r.diffs,\n\t\t\t\t\t\tattachments: r.attachments,\n\t\t\t\t\t\tsentAgentMessages: r.sentAgentMessages,\n\t\t\t\t\t\tkernelRestarted,\n\t\t\t\t\t\terror: r.error,\n\t\t\t\t\t},\n\t\t\t\t\tisError: r.status === \"error\" || r.status === \"aborted\",\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tif (hasWorkingMessage) {\n\t\t\t\t\tsetToolWorkingMessage();\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport function createIpythonTool(cwd: string, options?: IpythonToolOptions): AgentTool<typeof ipythonSchema> {\n\treturn wrapToolDefinition(createIpythonToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"ipython.js","sourceRoot":"","sources":["../../../src/core/tools/ipython.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGrC,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,EAIN,6BAA6B,EAI7B,iBAAiB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAsB,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAEjG,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,yBAAyB,GAAG;;;;;CAKjC,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,0BAA0B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqClC,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,UAAU,qBAAqB,CAAC,YAAY,GAAsC,EAAE,EAAU;IACnG,MAAM,QAAQ,GAAG,CAAC,yBAAyB,EAAE,0BAA0B,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtF,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAChF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,OAAO;EACN,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAoDqB,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;;;;;;;;;;;CAWzD,CAAC,IAAI,EAAE,CAAC;AAAA,CACR;AAED,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;QACjB,WAAW,EACV,mMAAmM;KACpM,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,uBAAuB,GAAG,yBAAyB,CAAC;AAC1D,MAAM,uBAAuB,GAAG,yBAAyB,CAAC;AAC1D,MAAM,kBAAkB,GAAG;IAC1B,0CAA0C;IAC1C,oHAAoH;IACpH,oIAAoI;CACpI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACb,MAAM,qBAAqB,GAAG;IAC7B,wBAAwB;IACxB,uNAAuN;IACvN,yBAAyB;CACzB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,SAAS,gBAAgB,GAAU;IAClC,OAAO,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;AAAA,CAC7C;AAED,SAAS,aAAa,CAAI,OAAmB,EAAE,MAA+B,EAAE,OAAoB,EAAc;IACjH,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO,OAAO,CAAC;IAChB,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,EAAE,EAAE,CAAC;QACZ,OAAO,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;QAC1C,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACjE,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC;YACnB,IAAI,OAAO,EAAE,CAAC;gBACb,OAAO;YACR,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;QAAA,CAC3B,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,OAAO,CAAC,IAAI,CACX,CAAC,KAAK,EAAE,EAAE,CAAC;YACV,IAAI,OAAO,EAAE,CAAC;gBACb,OAAO;YACR,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,KAAK,CAAC,CAAC;QAAA,CACf,EACD,CAAC,KAAc,EAAE,EAAE,CAAC;YACnB,IAAI,OAAO,EAAE,CAAC;gBACb,OAAO;YACR,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,CAAC;QAAA,CACd,CACD,CAAC;IAAA,CACF,CAAC,CAAC;AAAA,CACH;AAED,SAAS,uBAAuB,CAAC,OAA6C,EAG5E;IACD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACvC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,SAAS;QACV,CAAC;QACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,SAAS;QACV,CAAC;QACD,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IACpE,CAAC;IACD,OAAO;QACN,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,OAAO,EAAE,GAAG,EAAE,CAAC;YACd,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAChC,OAAO,EAAE,CAAC;YACX,CAAC;QAAA,CACD;KACD,CAAC;AAAA,CACF;AAED,SAAS,iBAAiB,CAAC,GAAiC,EAAE,OAAgB,EAAQ;IACrF,IAAI,CAAC;QACJ,GAAG,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,mCAAmC;IACpC,CAAC;AAAA,CACD;AAyDD;;;;;;GAMG;AACH,MAAM,OAAO,wBAAwB;IASlB,GAAG;IACH,OAAO;IATjB,cAAc,CAAyB;IACvC,cAAc,CAAgB;IACrB,gBAAgB,GAAG,IAAI,GAAG,EAAkC,CAAC;IACtE,kBAAkB,CAAU;IAC5B,YAAY,CAAiB;IACpB,iBAAiB,GAAG,IAAI,eAAe,EAAE,CAAC;IAE3D,YACkB,GAAW,EACX,OAAiD,EACjE;mBAFgB,GAAG;uBACH,OAAO;QAExB,IAAI,OAAO,EAAE,gBAAgB,EAAE,CAAC;YAC/B,OAAO,CAAC,gBAAgB,CAAC,OAAO,GAAG,SAAS,CAAC;QAC9C,CAAC;IAAA,CACD;IAED,qEAAqE;IACrE,IAAI,OAAO,GAA6B;QACvC,OAAO,IAAI,CAAC,cAAc,CAAC;IAAA,CAC3B;IAED,uFAAuF;IACvF,IAAI,WAAW,GAA8B;QAC5C,OAAO,IAAI,CAAC,YAAY,CAAC;IAAA,CACzB;IAED,wGAAwG;IACxG,OAAO,GAAS;QACf,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAC,CAAC,CAAC,CAAC;IAAA,CACnC;IAED,uEAAuE;IACvE,IAAI,gBAAgB,GAAY;QAC/B,OAAO,IAAI,CAAC,cAAc,EAAE,SAAS,IAAI,KAAK,CAAC;IAAA,CAC/C;IAED,0EAA0E;IAC1E,KAAK,CAAC,uBAAuB,GAA6B;QACzD,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,IAAI,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QACrF,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,uBAAuB,EAAE,CAAC;QAClD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAAA,CAC7C;IAED,8FAA8F;IAC9F,KAAK,CAAC,kBAAkB,CAAC,MAAoB,EAA4B;QACxE,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,IAAI,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QACrF,OAAO,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC,IAAI,IAAI,CAAC;IAAA,CACrD;IAED,qFAAqF;IACrF,KAAK,CAAC,OAAO,GAAkB;QAC9B,uEAAuE;QACvE,uEAAuE;QACvE,yCAAyC;QACzC,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;QACpC,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAChC,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAChC,IAAI,IAAI,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC;YACpC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,OAAO,GAAG,SAAS,CAAC;QACnD,CAAC;QACD,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,IAAI,CAAC;YACJ,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC;YACxB,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACR,mDAAmD;QACpD,CAAC;IAAA,CACD;IAED,KAAK,CAAC,IAAI,GAAkB;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;QACpC,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAChC,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAChC,IAAI,IAAI,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC;YACpC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,OAAO,GAAG,SAAS,CAAC;QACnD,CAAC;QACD,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,IAAI,CAAC;YACJ,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC;YACxB,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACR,mDAAmD;QACpD,CAAC;IAAA,CACD;IAED,MAAM,CAAC,UAA2C,EAAE,MAAoB,EAAyB;QAChG,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACrB,OAAO,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,uBAAiD,CAAC;QACtD,IAAI,UAAU,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACxC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACtC,uBAAuB,GAAG,GAAG,EAAE,CAAC;gBAC/B,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBACzC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,uBAAwB,CAAC,CAAC;YAAA,CAC/D,CAAC;YACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3E,0DAA0D;YAC1D,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACpD,UAAU,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;YACrC,CAAC;QACF,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YACzC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;YAC9B,OAAO,CAAC,IAAI,CACX,CAAC,CAAC,EAAE,EAAE,CAAC;gBACN,IAAI,IAAI,CAAC,cAAc,KAAK,OAAO,EAAE,CAAC;oBACrC,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;gBACzB,CAAC;gBACD,IAAI,CAAC,aAAa,EAAE,CAAC;YAAA,CACrB,EACD,GAAG,EAAE,CAAC;gBACL,yDAAyD;gBACzD,yCAAyC;gBACzC,IAAI,IAAI,CAAC,cAAc,KAAK,OAAO,EAAE,CAAC;oBACrC,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;gBACjC,CAAC;gBACD,IAAI,CAAC,aAAa,EAAE,CAAC;YAAA,CACrB,CACD,CAAC;QACH,CAAC;QACD,OAAO,aAAa,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YAC/D,uBAAuB,EAAE,EAAE,CAAC;QAAA,CAC5B,CAAC,CAAC;IAAA,CACH;IAEO,aAAa,GAAS;QAC7B,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;QAC9B,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAAA,CACpC;IAEO,mBAAmB,CAAC,OAAe,EAAQ;QAClD,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC;QAClC,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACnD,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnB,CAAC;IAAA,CACD;IAEO,KAAK,CAAC,WAAW,CAAC,MAAoB,EAAyB;QACtE,MAAM,YAAY,GAAG,uBAAuB,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACtF,MAAM,aAAa,GAAG,YAAY,CAAC,MAAM,CAAC;QAC1C,gFAA8E;QAC9E,gFAA8E;QAC9E,uEAAuE;QACvE,4EAA4E;QAC5E,IAAI,CAAC;YACJ,IAAI,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC;gBAC7B,MAAM,aAAa,CAClB,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAC,CAAC,CAAC,EACtC,aAAa,CACb,CAAC;YACH,CAAC;YACD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;YAC9C,mEAAmE;YACnE,mEAAmE;YACnE,MAAM,SAAS,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAClE,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;YAClD,MAAM,aAAa,GAAG,qBAAqB,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YACxE,MAAM,CAAC,GAAG,IAAI,iBAAiB,CAAC;gBAC/B,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM;gBAC5B,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,2DAA2D;gBAC3D,GAAG,EAAE;oBACJ,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG;oBACpB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,oBAAoB,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACzD,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,6BAA6B,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC1E;gBACD,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS;gBAClC,YAAY,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY;gBACxC,YAAY,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY;gBACxC,kFAAkF;gBAClF,QAAQ,EAAE,WAAW;oBACpB,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,WAAW,CAAC,EAAE,YAAY,EAAE,cAAc,CAAC,WAAW,CAAC,EAAE;oBAClF,CAAC,CAAC,SAAS;gBACZ,aAAa;aACb,CAAC,CAAC;YACH,IAAI,cAAyC,CAAC;YAC9C,IAAI,CAAC;gBACJ,0EAA0E;gBAC1E,2CAA2C;gBAC3C,IAAI,CAAC,mBAAmB,CAAC,2BAA2B,CAAC,CAAC;gBACtD,0EAA0E;gBAC1E,yEAAuE;gBACvE,2EAA2E;gBAC3E,2EAA2E;gBAC3E,uEAAuE;gBACvE,MAAM,oBAAoB,CAAC,GAAG,EAAE,CAAC;oBAChC,8EAA4E;oBAC5E,IAAI,aAAa,CAAC,OAAO;wBAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;oBACvF,OAAO,CAAC,CAAC,KAAK,CAAC;wBACd,mBAAmB,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC;wBACnE,MAAM,EAAE,aAAa;qBACrB,CAAC,CAAC;gBAAA,CACH,EAAE,aAAa,CAAC,CAAC;gBAClB,4EAA4E;gBAC5E,0EAA0E;gBAC1E,IAAI,WAAW,EAAE,CAAC;oBACjB,MAAM,eAAe,GAAG,UAAU,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC;oBAChE,IAAI,CAAC,mBAAmB,CAAC,2BAA2B,CAAC,CAAC;oBACtD,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,aAAa,CAAC,CAAC;oBACrE,IAAI,eAAe,EAAE,CAAC;wBACrB,cAAc,GAAG,OAAO,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC7F,CAAC;gBACF,CAAC;gBACD,IAAI,CAAC,mBAAmB,CAAC,6BAA6B,CAAC,CAAC;gBACxD,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE;oBAChD,MAAM,EAAE,aAAa;iBACrB,CAAC,CAAC;gBACH,IAAI,SAAS,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;oBAC/B,MAAM,OAAO,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACrG,MAAM,IAAI,KAAK,CAAC,2DAA2D,OAAO,EAAE,CAAC,CAAC;gBACvF,CAAC;YACF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,2EAAyE;gBACzE,0EAA0E;gBAC1E,qEAAqE;gBACrE,+DAA+D;gBAC/D,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;gBACrF,MAAM,KAAK,CAAC;YACb,CAAC;YACD,8EAA4E;YAC5E,0EAA0E;YAC1E,IAAI,cAAc,EAAE,CAAC;gBACpB,IAAI,CAAC,YAAY,GAAG,cAAc,CAAC;gBACnC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,cAAc,CAAC,CAAC;YAC3C,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC;gBACpC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,OAAO,GAAG,CAAC,CAAC;YAC3C,CAAC;YACD,OAAO,CAAC,CAAC;QACV,CAAC;gBAAS,CAAC;YACV,YAAY,CAAC,OAAO,EAAE,CAAC;QACxB,CAAC;IAAA,CACD;CACD;AAED,KAAK,UAAU,sBAAsB,CACpC,GAAiC,EACjC,MAA+B,EACO;IACtC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC;QACjB,OAAO,QAAQ,CAAC;IACjB,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC,uBAAuB,EAAE,uBAAuB,CAAC,EAAE;QAC1G,MAAM;KACN,CAAC,CAAC;IACH,IAAI,MAAM,KAAK,uBAAuB,EAAE,CAAC;QACxC,OAAO,MAAM,CAAC;IACf,CAAC;IACD,IAAI,MAAM,KAAK,uBAAuB,EAAE,CAAC;QACxC,OAAO,MAAM,CAAC;IACf,CAAC;IACD,OAAO,QAAQ,CAAC;AAAA,CAChB;AAED,KAAK,UAAU,2BAA2B,CACzC,WAAqC,EACrC,qBAAqD,EACrD,UAAkB,EAClB,IAAY,EACZ,MAA+B,EAC/B,QAA4D,EAC5D,gBAA4C,EAC5C,sBAAmG,EACnG,GAAiC,EAC8B;IAC/D,IAAI,eAAe,GAAG,KAAK,CAAC;IAC5B,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QAClE,IAAI,CAAC;YACJ,OAAO;gBACN,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE;oBAC7B,MAAM;oBACN,QAAQ;oBACR,sBAAsB,EAAE,sBAAsB;wBAC7C,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,sBAAsB,CAAC,UAAU,EAAE,OAAO,CAAC;wBAC1D,CAAC,CAAC,SAAS;iBACZ,CAAC;gBACF,eAAe;aACf,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,CAAC,KAAK,YAAY,6BAA6B,CAAC,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBAC1E,MAAM,KAAK,CAAC;YACb,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACzD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACvB,gBAAgB,CAAC,8BAA8B,CAAC,CAAC;gBACjD,SAAS;YACV,CAAC;YACD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACvB,gBAAgB,CAAC,6BAA6B,CAAC,CAAC;gBAChD,MAAM,WAAW,CAAC,IAAI,EAAE,CAAC;gBACzB,eAAe,GAAG,IAAI,CAAC;gBACvB,SAAS;YACV,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;AAAA,CACD;AAED,6FAA6F;AAC7F,MAAM,UAAU,0BAA0B,CAAC,WAAoD,EAAkB;IAChH,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,CAAC;IAC5B,OAAO,WAAW;SAChB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;SAC/C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;AAAA,CACtE;AAED,MAAM,UAAU,2BAA2B,CAC1C,GAAW,EACX,OAA4B,EAC+B;IAC3D,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,IAAI,wBAAwB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAEvF,OAAO;QACN,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,WAAW,EACV,8bAA8b;QAC/b,aAAa,EAAE,4EAA4E;QAC3F,kGAAgG;QAChG,aAAa,EAAE,YAAY;QAC3B,UAAU,EAAE,aAAa;QACzB,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,CAAC;YAC7D,IAAI,iBAAiB,GAAG,KAAK,CAAC;YAC9B,MAAM,qBAAqB,GAAG,CAAC,OAAgB,EAAE,EAAE,CAAC;gBACnD,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBAChC,iBAAiB,GAAG,OAAO,KAAK,SAAS,CAAC;YAAA,CAC1C,CAAC;YACF,MAAM,qBAAqB,GAAmC,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC1E,qBAAqB,CAAC,OAAO,CAAC,CAAC;gBAC/B,QAAQ,EAAE,CAAC;oBACV,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;oBAC1C,OAAO,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE;iBAC/B,CAAC,CAAC;YAAA,CACH,CAAC;YAEF,IAAI,CAAC;gBACJ,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,eAAe,EAAE,GAAG,MAAM,2BAA2B,CACvE,WAAW,EACX,qBAAqB,EACrB,UAAU,EACV,MAAM,CAAC,IAAI,EACX,MAAM,EACN,CAAC,KAAK,EAAE,EAAE,CAAC;oBACV,QAAQ,EAAE,CAAC;wBACV,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;wBACxC,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;qBACzB,CAAC,CAAC;gBAAA,CACH,EACD,qBAAqB,EACrB,OAAO,EAAE,sBAAsB,EAC/B,GAAG,CACH,CAAC;gBAEF,IAAI,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC;gBACpB,IAAI,CAAC,CAAC,MAAM;oBAAE,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;gBACpD,IAAI,CAAC,CAAC,MAAM;oBAAE,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;gBACpD,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;oBACrC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC3D,CAAC;gBACD,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;oBACxB,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,uCAAuC,CAAC,CAAC,gBAAgB,EAAE,CAAC;gBACxF,CAAC;gBACD,IAAI,eAAe,EAAE,CAAC;oBACrB,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,qBAAqB,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC;gBAC7E,CAAC;gBAED,MAAM,WAAW,GAAG,0BAA0B,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;gBAC9D,MAAM,OAAO,GAAmC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,EAAE,GAAG,WAAW,CAAC,CAAC;gBAErG,OAAO;oBACN,OAAO;oBACP,OAAO,EAAE;wBACR,UAAU,EAAE,CAAC,CAAC,UAAU;wBACxB,MAAM,EAAE,CAAC,CAAC,MAAM;wBAChB,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK;wBAC1B,MAAM,EAAE,CAAC,CAAC,MAAM;wBAChB,MAAM,EAAE,CAAC,CAAC,MAAM;wBAChB,MAAM,EAAE,CAAC,CAAC,MAAM;wBAChB,gBAAgB,EAAE,CAAC,CAAC,gBAAgB;wBACpC,KAAK,EAAE,CAAC,CAAC,KAAK;wBACd,WAAW,EAAE,CAAC,CAAC,WAAW;wBAC1B,iBAAiB,EAAE,CAAC,CAAC,iBAAiB;wBACtC,eAAe;wBACf,KAAK,EAAE,CAAC,CAAC,KAAK;qBACd;oBACD,OAAO,EAAE,CAAC,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;iBACvD,CAAC;YACH,CAAC;oBAAS,CAAC;gBACV,IAAI,iBAAiB,EAAE,CAAC;oBACvB,qBAAqB,EAAE,CAAC;gBACzB,CAAC;YACF,CAAC;QAAA,CACD;KACD,CAAC;AAAA,CACF;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW,EAAE,OAA4B,EAAmC;IAC7G,OAAO,kBAAkB,CAAC,2BAA2B,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;AAAA,CACrE","sourcesContent":["import { existsSync } from \"node:fs\";\nimport type { AgentTool } from \"@asm-agent/agent\";\nimport type { ImageContent, TextContent } from \"@asm-agent/ai\";\nimport { type Static, Type } from \"typebox\";\nimport { IMAGE_MIME_TYPES } from \"../../utils/mime.js\";\nimport { resolveKernelBashShell } from \"../../utils/shell.js\";\nimport type { ExtensionContext, ToolDefinition } from \"../extensions/types.js\";\nimport { withKernelBootPermit } from \"../kernel/boot-gate.js\";\nimport type { KernelBootstrapProgressHandler } from \"../kernel/bootstrap.js\";\nimport {\n\ttype ExecuteResult,\n\ttype HostRequestHandlers,\n\ttype KernelAttachment,\n\tKernelBusyAfterInterruptError,\n\ttype KernelClient,\n\ttype KernelDiffDisplay,\n\ttype KernelSentAgentMessage,\n\tReplKernelManager,\n} from \"../kernel/index.js\";\nimport { manifestPathIn, type RestoreResult, snapshotPathIn } from \"../kernel/state-snapshot.js\";\nimport type { PythonSkillRuntimeInfo } from \"../skills.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\nconst RLM_BOOTSTRAP_HEADER_CODE = `\nimport asyncio\nimport os as _asm_agent_os\n\n_asm_agent_os.environ[\"NO_COLOR\"] = \"1\"\n`.trim();\n\nconst RLM_BOOTSTRAP_RUNTIME_CODE = `\ntry:\n import rlm as _asm_agent_rlm_module\n rlm = _asm_agent_rlm_module.rlm\n bash = _asm_agent_rlm_module.bash\n import rlm.mcp as mcp\nexcept Exception as _asm_agent_rlm_error:\n _ASM_AGENT_RLM_IMPORT_ERROR = str(_asm_agent_rlm_error)\n\n class _AsmAgentMissingRlm:\n def _raise_missing(self):\n raise RuntimeError(\n \"asm-agent-runtime is not installed in this kernel. \"\n \"Remove ~/.asm-agent/kernel-venv so asm-agent can rebuild it, or set \"\n \"ASM_AGENT_KERNEL_PYTHON to a kernel environment with asm-agent-runtime installed. \"\n f\"Import error: {_ASM_AGENT_RLM_IMPORT_ERROR}\"\n )\n\n async def run(self, prompt, **kwargs):\n self._raise_missing()\n\n async def find_models(self, query=\"\", limit=8):\n self._raise_missing()\n\n async def list_subagents(self):\n self._raise_missing()\n\n async def delete_subagent(self, target):\n self._raise_missing()\n\n async def __call__(self, prompt, **kwargs):\n return await self.run(prompt, **kwargs)\n\n rlm = _AsmAgentMissingRlm()\n\n def bash(command):\n rlm._raise_missing()\n`.trim();\n\nexport function buildRlmBootstrapCode(pythonSkills: readonly PythonSkillRuntimeInfo[] = []): string {\n\tconst baseCode = [RLM_BOOTSTRAP_HEADER_CODE, RLM_BOOTSTRAP_RUNTIME_CODE].join(\"\\n\\n\");\n\tconst importNames = [...new Set(pythonSkills.map((skill) => skill.importName))];\n\tif (importNames.length === 0) {\n\t\treturn baseCode;\n\t}\n\n\treturn `\n${baseCode}\n\nimport importlib as _asm_agent_importlib\nimport inspect as _asm_agent_inspect\nimport sys as _asm_agent_sys\nimport types as _asm_agent_types\n\nclass _AsmAgentCallableSkillModule(_asm_agent_types.ModuleType):\n async def __call__(self, *args, **kwargs):\n result = self.run(*args, **kwargs)\n if _asm_agent_inspect.isawaitable(result):\n return await result\n return result\n\nclass _AsmAgentUnavailableSkill:\n def __init__(self, name, error):\n self.__name__ = name\n self._asm_agent_import_error = error\n self.__doc__ = f\"Python skill {name} is unavailable: {error}\"\n\n async def run(self, *args, **kwargs):\n raise RuntimeError(\n f\"Python skill {self.__name__} is unavailable in this kernel. \"\n f\"Import error: {self._asm_agent_import_error}\"\n )\n\n async def __call__(self, *args, **kwargs):\n return await self.run(*args, **kwargs)\n\n def __repr__(self):\n return f\"<unavailable Python skill {self.__name__!r}: {self._asm_agent_import_error}>\"\n\ndef _asm_agent_wrap_skill_module(module):\n run = getattr(module, \"run\", None)\n if not callable(run):\n return module\n if isinstance(module, _AsmAgentCallableSkillModule):\n return module\n wrapped = _AsmAgentCallableSkillModule(module.__name__)\n wrapped.__dict__.update(module.__dict__)\n try:\n wrapped.__signature__ = _asm_agent_inspect.signature(run)\n except Exception:\n pass\n doc = getattr(run, \"__doc__\", None)\n if doc:\n wrapped.__doc__ = doc\n _asm_agent_sys.modules[module.__name__] = wrapped\n return wrapped\n\n_ASM_AGENT_SKILL_IMPORT_ERRORS = {}\n\nfor _asm_agent_skill_name in ${JSON.stringify(importNames)}:\n try:\n globals()[_asm_agent_skill_name] = _asm_agent_wrap_skill_module(\n _asm_agent_importlib.import_module(_asm_agent_skill_name)\n )\n except Exception as _asm_agent_skill_error:\n _ASM_AGENT_SKILL_IMPORT_ERRORS[_asm_agent_skill_name] = str(_asm_agent_skill_error)\n globals()[_asm_agent_skill_name] = _AsmAgentUnavailableSkill(\n _asm_agent_skill_name,\n str(_asm_agent_skill_error),\n )\n`.trim();\n}\n\nconst ipythonSchema = Type.Object({\n\tcode: Type.String({\n\t\tdescription:\n\t\t\t\"Python code to execute in the persistent Python REPL. Use the target project's own environment for project imports, tests, scripts, CLIs, and dependency checks instead of direct kernel imports.\",\n\t}),\n});\n\nconst BUSY_KERNEL_WAIT_CHOICE = \"Wait and preserve state\";\nconst BUSY_KERNEL_KILL_CHOICE = \"Kill kernel and restart\";\nconst BUSY_KERNEL_PROMPT = [\n\t\"Interrupted Python cell is still running\",\n\t\"Ctrl+C sent an interrupt, but the previous cell has not stopped yet. A new command cannot start until it finishes.\",\n\t\"Waiting preserves the current kernel state. Killing restarts the kernel and loses in-memory variables, imports, and running tasks.\",\n].join(\"\\n\");\nconst KERNEL_RESTART_NOTICE = [\n\t\"<ipython_kernel_reset>\",\n\t\"The Python kernel was restarted after a previous interrupted cell kept running. Variables, imports, async tasks, and open resources from before the restart are no longer available; recreate them before using them.\",\n\t\"</ipython_kernel_reset>\",\n].join(\"\\n\");\n\nfunction createAbortError(): Error {\n\treturn new Error(\"Python execution aborted\");\n}\n\nfunction raceWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined, onAbort?: () => void): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\tonAbort?.();\n\t\treturn Promise.reject(createAbortError());\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\tonAbort?.();\n\t\t\treject(createAbortError());\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\nfunction createLinkedAbortSignal(sources: readonly (AbortSignal | undefined)[]): {\n\tsignal: AbortSignal;\n\tcleanup: () => void;\n} {\n\tconst controller = new AbortController();\n\tconst cleanups: Array<() => void> = [];\n\tconst abort = () => controller.abort();\n\tfor (const source of sources) {\n\t\tif (!source) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (source.aborted) {\n\t\t\tcontroller.abort();\n\t\t\tcontinue;\n\t\t}\n\t\tconst listener = () => abort();\n\t\tsource.addEventListener(\"abort\", listener, { once: true });\n\t\tcleanups.push(() => source.removeEventListener(\"abort\", listener));\n\t}\n\treturn {\n\t\tsignal: controller.signal,\n\t\tcleanup: () => {\n\t\t\tfor (const cleanup of cleanups) {\n\t\t\t\tcleanup();\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction setWorkingMessage(ctx: ExtensionContext | undefined, message?: string): void {\n\ttry {\n\t\tctx?.ui.setWorkingMessage(message);\n\t} catch {\n\t\t// Stale UI context; cosmetic only.\n\t}\n}\n\nexport type IpythonToolInput = Static<typeof ipythonSchema>;\n\nexport interface IpythonToolDetails {\n\tdurationMs?: number;\n\tstatus?: \"ok\" | \"error\" | \"aborted\" | \"starting\";\n\terrorEname?: string;\n\tstdout?: string;\n\tstderr?: string;\n\tresult?: string;\n\t/** Output that arrived without this cell's id (threads, other cells' leftovers), shown separately from stdout. */\n\tbackgroundOutput?: string;\n\t/** Diffs streamed from file edits, rendered by the cell view. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments loaded into context (e.g. by the attach-image skill). */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** True when this result came after killing and restarting a busy kernel. */\n\tkernelRestarted?: boolean;\n\terror?: {\n\t\tename: string;\n\t\tevalue: string;\n\t\ttraceback: string[];\n\t};\n}\n\nexport interface IpythonToolOptions {\n\t/** Python override. Must have asm-agent-runtime installed. */\n\tpython?: string;\n\tenv?: Record<string, string>;\n\t/** Command prefix prepended to every bash() command. */\n\tcommandPrefix?: string;\n\t/** Shell used by bash(). */\n\tshellPath?: string;\n\tsessionId?: string;\n\t/** Typed host request handlers for the kernel↔host bridge (rlm.run, goal.*, …). */\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly PythonSkillRuntimeInfo[];\n\t/** Per-session artifact dir where the kernel namespace snapshot is stored. Omit to disable snapshots. */\n\tsnapshotDir?: string;\n\t/** Resolves before this kernel starts — e.g. the previous provisioner's dispose, so a\n\t * /reload's old-kernel snapshot flush can't race the new kernel's restore. */\n\treadyGate?: Promise<unknown>;\n\t/** Filled with the live kernel client after the first kernel start; cleared on construction. */\n\tkernelManagerRef?: { current?: KernelClient };\n\t/**\n\t * Fires once per kernel start when a previous session's namespace was revived\n\t * (some names restored or some failed), so the session can tell the model.\n\t */\n\tonRestore?: (result: RestoreResult) => void;\n\tonLateSentAgentMessage?: (toolCallId: string, message: KernelSentAgentMessage) => void;\n\t/** Shared provisioner owning the kernel lifecycle. When provided, the remaining options are ignored. */\n\tprovisioner?: IpythonKernelProvisioner;\n}\n\n/**\n * Owns the lazy create+start+runtime-bootstrap of one session's Python kernel.\n *\n * Concurrent ensure() calls await the same in-flight startup, a failed startup\n * clears the memo so the next call retries fresh, and progress listeners can\n * attach mid-flight (a tool call racing a background prewarm()).\n */\nexport class IpythonKernelProvisioner {\n\tprivate managerPromise?: Promise<KernelClient>;\n\tprivate startedManager?: KernelClient;\n\tprivate readonly startupListeners = new Set<KernelBootstrapProgressHandler>();\n\tprivate lastStartupMessage?: string;\n\tprivate _lastRestore?: RestoreResult;\n\tprivate readonly disposeController = new AbortController();\n\n\tconstructor(\n\t\tprivate readonly cwd: string,\n\t\tprivate readonly options?: Omit<IpythonToolOptions, \"provisioner\">,\n\t) {\n\t\tif (options?.kernelManagerRef) {\n\t\t\toptions.kernelManagerRef.current = undefined;\n\t\t}\n\t}\n\n\t/** The kernel manager, once a startup has completed successfully. */\n\tget manager(): KernelClient | undefined {\n\t\treturn this.startedManager;\n\t}\n\n\t/** Result of reviving a prior session's namespace on the last kernel start, if any. */\n\tget lastRestore(): RestoreResult | undefined {\n\t\treturn this._lastRestore;\n\t}\n\n\t/** Start the kernel in the background. Failures are swallowed here and surface on the next ensure(). */\n\tprewarm(): void {\n\t\tvoid this.ensure().catch(() => {});\n\t}\n\n\t/** Whether a kernel has finished starting and is currently running. */\n\tget hasRunningKernel(): boolean {\n\t\treturn this.startedManager?.isRunning ?? false;\n\t}\n\n\t/** Remove live variables above the snapshot's per-variable size limit. */\n\tasync pruneOversizedVariables(): Promise<string[] | null> {\n\t\tconst m = this.startedManager ?? (await this.managerPromise?.catch(() => undefined));\n\t\tconst result = await m?.pruneOversizedVariables();\n\t\treturn result ? (result.pruned ?? []) : null;\n\t}\n\n\t/** Live user-defined names in the kernel namespace, or null if listing failed / no kernel. */\n\tasync listNamespaceNames(signal?: AbortSignal): Promise<string[] | null> {\n\t\tconst m = this.startedManager ?? (await this.managerPromise?.catch(() => undefined));\n\t\treturn (await m?.listNamespaceNames(signal)) ?? null;\n\t}\n\n\t/** Dispose the kernel owned by this provisioner, including one still starting up. */\n\tasync dispose(): Promise<void> {\n\t\t// Drops a still-queued boot out of the semaphore and short-circuits an\n\t\t// in-flight startKernel before it spawns, so a disposed session's boot\n\t\t// doesn't waste a slot during a fan-out.\n\t\tthis.disposeController.abort();\n\t\tconst pending = this.managerPromise;\n\t\tthis.managerPromise = undefined;\n\t\tthis.startedManager = undefined;\n\t\tif (this.options?.kernelManagerRef) {\n\t\t\tthis.options.kernelManagerRef.current = undefined;\n\t\t}\n\t\tif (!pending) return;\n\t\ttry {\n\t\t\tconst m = await pending;\n\t\t\tawait m.shutdown({ snapshot: true, drainHostRequests: true });\n\t\t} catch {\n\t\t\t// a failed startup already cleaned up after itself\n\t\t}\n\t}\n\n\tasync kill(): Promise<void> {\n\t\tconst pending = this.managerPromise;\n\t\tthis.managerPromise = undefined;\n\t\tthis.startedManager = undefined;\n\t\tif (this.options?.kernelManagerRef) {\n\t\t\tthis.options.kernelManagerRef.current = undefined;\n\t\t}\n\t\tif (!pending) return;\n\t\ttry {\n\t\t\tconst m = await pending;\n\t\t\tawait m.kill();\n\t\t} catch {\n\t\t\t// a failed startup already cleaned up after itself\n\t\t}\n\t}\n\n\tensure(onProgress?: KernelBootstrapProgressHandler, signal?: AbortSignal): Promise<KernelClient> {\n\t\tif (signal?.aborted) {\n\t\t\treturn Promise.reject(createAbortError());\n\t\t}\n\t\tlet cleanupProgressListener: (() => void) | undefined;\n\t\tif (onProgress && !this.startedManager) {\n\t\t\tthis.startupListeners.add(onProgress);\n\t\t\tcleanupProgressListener = () => {\n\t\t\t\tthis.startupListeners.delete(onProgress);\n\t\t\t\tsignal?.removeEventListener(\"abort\", cleanupProgressListener!);\n\t\t\t};\n\t\t\tsignal?.addEventListener(\"abort\", cleanupProgressListener, { once: true });\n\t\t\t// Joining an in-flight startup: replay the current stage.\n\t\t\tif (this.managerPromise && this.lastStartupMessage) {\n\t\t\t\tonProgress(this.lastStartupMessage);\n\t\t\t}\n\t\t}\n\t\tif (!this.managerPromise) {\n\t\t\tconst startup = this.startKernel(signal);\n\t\t\tthis.managerPromise = startup;\n\t\t\tstartup.then(\n\t\t\t\t(m) => {\n\t\t\t\t\tif (this.managerPromise === startup) {\n\t\t\t\t\t\tthis.startedManager = m;\n\t\t\t\t\t}\n\t\t\t\t\tthis.settleStartup();\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\t// Clear the memo so the next ensure() retries instead of\n\t\t\t\t\t// rethrowing a cached rejection forever.\n\t\t\t\t\tif (this.managerPromise === startup) {\n\t\t\t\t\t\tthis.managerPromise = undefined;\n\t\t\t\t\t}\n\t\t\t\t\tthis.settleStartup();\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn raceWithAbort(this.managerPromise, signal).finally(() => {\n\t\t\tcleanupProgressListener?.();\n\t\t});\n\t}\n\n\tprivate settleStartup(): void {\n\t\tthis.startupListeners.clear();\n\t\tthis.lastStartupMessage = undefined;\n\t}\n\n\tprivate emitStartupProgress(message: string): void {\n\t\tthis.lastStartupMessage = message;\n\t\tfor (const listener of [...this.startupListeners]) {\n\t\t\tlistener(message);\n\t\t}\n\t}\n\n\tprivate async startKernel(signal?: AbortSignal): Promise<KernelClient> {\n\t\tconst startupAbort = createLinkedAbortSignal([this.disposeController.signal, signal]);\n\t\tconst startupSignal = startupAbort.signal;\n\t\t// Wait for a previous provisioner (e.g. on /reload) to finish disposing — and\n\t\t// flushing its final snapshot — before we read that snapshot back, so the two\n\t\t// kernels can't race over the same on-disk file. Guarded so the common\n\t\t// no-gate path stays synchronous (callers rely on prompt startup progress).\n\t\ttry {\n\t\t\tif (this.options?.readyGate) {\n\t\t\t\tawait raceWithAbort(\n\t\t\t\t\tthis.options.readyGate.catch(() => {}),\n\t\t\t\t\tstartupSignal,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst snapshotDir = this.options?.snapshotDir;\n\t\t\t// Always inject an absolute trusted shell (undefined only on win32\n\t\t\t// without bash, where the runtime's teaching error fires instead).\n\t\t\tconst shellPath = resolveKernelBashShell(this.options?.shellPath);\n\t\t\tconst commandPrefix = this.options?.commandPrefix;\n\t\t\tconst bootstrapCode = buildRlmBootstrapCode(this.options?.pythonSkills);\n\t\t\tconst m = new ReplKernelManager({\n\t\t\t\tpython: this.options?.python,\n\t\t\t\tcwd: this.cwd,\n\t\t\t\t// bash() reads these to pick its shell and command prefix.\n\t\t\t\tenv: {\n\t\t\t\t\t...this.options?.env,\n\t\t\t\t\t...(shellPath ? { ASM_AGENT_BASH_SHELL: shellPath } : {}),\n\t\t\t\t\t...(commandPrefix ? { ASM_AGENT_BASH_COMMAND_PREFIX: commandPrefix } : {}),\n\t\t\t\t},\n\t\t\t\tsessionId: this.options?.sessionId,\n\t\t\t\thostHandlers: this.options?.hostHandlers,\n\t\t\t\tpythonSkills: this.options?.pythonSkills,\n\t\t\t\t// Only persistent sessions (which have an artifact dir) get a revivable snapshot.\n\t\t\t\tsnapshot: snapshotDir\n\t\t\t\t\t? { path: snapshotPathIn(snapshotDir), manifestPath: manifestPathIn(snapshotDir) }\n\t\t\t\t\t: undefined,\n\t\t\t\tbootstrapCode,\n\t\t\t});\n\t\t\tlet pendingRestore: RestoreResult | undefined;\n\t\t\ttry {\n\t\t\t\t// Emitted synchronously (before the permit await) so a listener attaching\n\t\t\t\t// mid-flight can replay the current stage.\n\t\t\t\tthis.emitStartupProgress(\"Starting Python kernel...\");\n\t\t\t\t// Only the process spawn + port resolve contends for OS resources under a\n\t\t\t\t// fan-out, and it is bounded by start()'s own timeouts — so the permit\n\t\t\t\t// covers only start(). Restore/bootstrap run per-kernel afterwards and are\n\t\t\t\t// unbounded execute()s; holding the global permit across them could pin it\n\t\t\t\t// forever on a wedged bootstrap and starve every other session's boot.\n\t\t\t\tawait withKernelBootPermit(() => {\n\t\t\t\t\t// Disposed while queued for the permit — don't spawn a kernel nobody wants.\n\t\t\t\t\tif (startupSignal.aborted) throw new Error(\"Kernel provisioner disposed before start\");\n\t\t\t\t\treturn m.start({\n\t\t\t\t\t\tonBootstrapProgress: (message) => this.emitStartupProgress(message),\n\t\t\t\t\t\tsignal: startupSignal,\n\t\t\t\t\t});\n\t\t\t\t}, startupSignal);\n\t\t\t\t// Revive a prior session's namespace before the bootstrap, so the bootstrap\n\t\t\t\t// then overwrites live handles (rlm, skills) on top of anything restored.\n\t\t\t\tif (snapshotDir) {\n\t\t\t\t\tconst snapshotExisted = existsSync(snapshotPathIn(snapshotDir));\n\t\t\t\t\tthis.emitStartupProgress(\"Restoring Python state...\");\n\t\t\t\t\tconst restore = await raceWithAbort(m.restoreState(), startupSignal);\n\t\t\t\t\tif (snapshotExisted) {\n\t\t\t\t\t\tpendingRestore = restore ?? { restored: [], failed: [], path: snapshotPathIn(snapshotDir) };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.emitStartupProgress(\"Preparing Python runtime...\");\n\t\t\t\tconst bootstrap = await m.execute(bootstrapCode, {\n\t\t\t\t\tsignal: startupSignal,\n\t\t\t\t});\n\t\t\t\tif (bootstrap.status !== \"ok\") {\n\t\t\t\t\tconst details = [bootstrap.stderr, bootstrap.error?.traceback.join(\"\\n\")].filter(Boolean).join(\"\\n\");\n\t\t\t\t\tthrow new Error(`Failed to initialize rlm runtime in the Python kernel:\\n${details}`);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Never leak the kernel process if startup fails after spawn — and never\n\t\t\t\t// surface the failure before the teardown (final snapshot flush included)\n\t\t\t\t// finished, or a replacement provisioner gated on this dispose could\n\t\t\t\t// race the still-flushing kernel over the same snapshot files.\n\t\t\t\tawait m.shutdown({ snapshot: true, drainHostRequests: true }).catch(() => undefined);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\t// Only tell the model what was revived once the kernel is actually usable —\n\t\t\t// a notice claiming restored state must never outlive a failed bootstrap.\n\t\t\tif (pendingRestore) {\n\t\t\t\tthis._lastRestore = pendingRestore;\n\t\t\t\tthis.options?.onRestore?.(pendingRestore);\n\t\t\t}\n\t\t\tif (this.options?.kernelManagerRef) {\n\t\t\t\tthis.options.kernelManagerRef.current = m;\n\t\t\t}\n\t\t\treturn m;\n\t\t} finally {\n\t\t\tstartupAbort.cleanup();\n\t\t}\n\t}\n}\n\nasync function chooseBusyKernelAction(\n\tctx: ExtensionContext | undefined,\n\tsignal: AbortSignal | undefined,\n): Promise<\"wait\" | \"kill\" | \"cancel\"> {\n\tif (!ctx?.hasUI) {\n\t\treturn \"cancel\";\n\t}\n\tconst choice = await ctx.ui.select(BUSY_KERNEL_PROMPT, [BUSY_KERNEL_WAIT_CHOICE, BUSY_KERNEL_KILL_CHOICE], {\n\t\tsignal,\n\t});\n\tif (choice === BUSY_KERNEL_WAIT_CHOICE) {\n\t\treturn \"wait\";\n\t}\n\tif (choice === BUSY_KERNEL_KILL_CHOICE) {\n\t\treturn \"kill\";\n\t}\n\treturn \"cancel\";\n}\n\nasync function executeWithBusyKernelChoice(\n\tprovisioner: IpythonKernelProvisioner,\n\treportStartupProgress: KernelBootstrapProgressHandler,\n\ttoolCallId: string,\n\tcode: string,\n\tsignal: AbortSignal | undefined,\n\tonStream: (chunk: string, name: \"stdout\" | \"stderr\") => void,\n\tonWorkingMessage: (message?: string) => void,\n\tonLateSentAgentMessage: ((toolCallId: string, message: KernelSentAgentMessage) => void) | undefined,\n\tctx: ExtensionContext | undefined,\n): Promise<{ result: ExecuteResult; kernelRestarted: boolean }> {\n\tlet kernelRestarted = false;\n\twhile (true) {\n\t\tconst m = await provisioner.ensure(reportStartupProgress, signal);\n\t\ttry {\n\t\t\treturn {\n\t\t\t\tresult: await m.execute(code, {\n\t\t\t\t\tsignal,\n\t\t\t\t\tonStream,\n\t\t\t\t\tonLateSentAgentMessage: onLateSentAgentMessage\n\t\t\t\t\t\t? (message) => onLateSentAgentMessage(toolCallId, message)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t}),\n\t\t\t\tkernelRestarted,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof KernelBusyAfterInterruptError) || signal?.aborted) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst action = await chooseBusyKernelAction(ctx, signal);\n\t\t\tif (action === \"wait\") {\n\t\t\t\tonWorkingMessage(\"Waiting for Python kernel...\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (action === \"kill\") {\n\t\t\t\tonWorkingMessage(\"Restarting Python kernel...\");\n\t\t\t\tawait provisioner.kill();\n\t\t\t\tkernelRestarted = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\n/** Turn kernel image attachments into `ImageContent` blocks; non-image types are dropped. */\nexport function imageBlocksFromAttachments(attachments: readonly KernelAttachment[] | undefined): ImageContent[] {\n\tif (!attachments) return [];\n\treturn attachments\n\t\t.filter((a) => IMAGE_MIME_TYPES.has(a.mimeType))\n\t\t.map((a) => ({ type: \"image\", data: a.data, mimeType: a.mimeType }));\n}\n\nexport function createIpythonToolDefinition(\n\tcwd: string,\n\toptions?: IpythonToolOptions,\n): ToolDefinition<typeof ipythonSchema, IpythonToolDetails> {\n\tconst provisioner = options?.provisioner ?? new IpythonKernelProvisioner(cwd, options);\n\n\treturn {\n\t\tname: \"ipython\",\n\t\tlabel: \"ipython\",\n\t\tdescription:\n\t\t\t\"Execute Python code in a persistent Python REPL. Top-level `await` is supported. Variables, imports, and loaded data persist across calls, and are revived on a best-effort basis when a session is resumed (objects that cannot be serialized are dropped and reported). Run shell commands with `bash('cmd')` / `await bash('cmd')`. Project imports, tests, scripts, CLIs, and dependency checks should run through the target project's own environment.\",\n\t\tpromptSnippet: \"ipython - persistent Python REPL for code, state, and bash() orchestration\",\n\t\t// The kernel is single-threaded — pi must not run two ipython calls in parallel within a batch.\n\t\texecutionMode: \"sequential\",\n\t\tparameters: ipythonSchema,\n\t\texecute: async (toolCallId, params, signal, onUpdate, ctx) => {\n\t\t\tlet hasWorkingMessage = false;\n\t\t\tconst setToolWorkingMessage = (message?: string) => {\n\t\t\t\tsetWorkingMessage(ctx, message);\n\t\t\t\thasWorkingMessage = message !== undefined;\n\t\t\t};\n\t\t\tconst reportStartupProgress: KernelBootstrapProgressHandler = (message) => {\n\t\t\t\tsetToolWorkingMessage(message);\n\t\t\t\tonUpdate?.({\n\t\t\t\t\tcontent: [{ type: \"text\", text: message }],\n\t\t\t\t\tdetails: { status: \"starting\" },\n\t\t\t\t});\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tconst { result: r, kernelRestarted } = await executeWithBusyKernelChoice(\n\t\t\t\t\tprovisioner,\n\t\t\t\t\treportStartupProgress,\n\t\t\t\t\ttoolCallId,\n\t\t\t\t\tparams.code,\n\t\t\t\t\tsignal,\n\t\t\t\t\t(chunk) => {\n\t\t\t\t\t\tonUpdate?.({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: chunk }],\n\t\t\t\t\t\t\tdetails: { status: \"ok\" },\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tsetToolWorkingMessage,\n\t\t\t\t\toptions?.onLateSentAgentMessage,\n\t\t\t\t\tctx,\n\t\t\t\t);\n\n\t\t\t\tlet text = r.stdout;\n\t\t\t\tif (r.stderr) text += (text ? \"\\n\" : \"\") + r.stderr;\n\t\t\t\tif (r.result) text += (text ? \"\\n\" : \"\") + r.result;\n\t\t\t\tif (r.status === \"error\" && r.error) {\n\t\t\t\t\ttext += (text ? \"\\n\" : \"\") + r.error.traceback.join(\"\\n\");\n\t\t\t\t}\n\t\t\t\tif (r.backgroundOutput) {\n\t\t\t\t\ttext += `${text ? \"\\n\" : \"\"}[background output (unattributed)]\\n${r.backgroundOutput}`;\n\t\t\t\t}\n\t\t\t\tif (kernelRestarted) {\n\t\t\t\t\ttext = text ? `${KERNEL_RESTART_NOTICE}\\n\\n${text}` : KERNEL_RESTART_NOTICE;\n\t\t\t\t}\n\n\t\t\t\tconst imageBlocks = imageBlocksFromAttachments(r.attachments);\n\t\t\t\tconst content: (TextContent | ImageContent)[] = [{ type: \"text\", text: text || \"\" }, ...imageBlocks];\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent,\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tdurationMs: r.durationMs,\n\t\t\t\t\t\tstatus: r.status,\n\t\t\t\t\t\terrorEname: r.error?.ename,\n\t\t\t\t\t\tstdout: r.stdout,\n\t\t\t\t\t\tstderr: r.stderr,\n\t\t\t\t\t\tresult: r.result,\n\t\t\t\t\t\tbackgroundOutput: r.backgroundOutput,\n\t\t\t\t\t\tdiffs: r.diffs,\n\t\t\t\t\t\tattachments: r.attachments,\n\t\t\t\t\t\tsentAgentMessages: r.sentAgentMessages,\n\t\t\t\t\t\tkernelRestarted,\n\t\t\t\t\t\terror: r.error,\n\t\t\t\t\t},\n\t\t\t\t\tisError: r.status === \"error\" || r.status === \"aborted\",\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tif (hasWorkingMessage) {\n\t\t\t\t\tsetToolWorkingMessage();\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport function createIpythonTool(cwd: string, options?: IpythonToolOptions): AgentTool<typeof ipythonSchema> {\n\treturn wrapToolDefinition(createIpythonToolDefinition(cwd, options));\n}\n"]}