@code-yeongyu/senpi 2026.8.19 → 2026.8.20

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 (219) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/dist/cli-main.js +4 -0
  3. package/dist/cli-main.js.map +1 -1
  4. package/dist/cli.js +31 -3
  5. package/dist/cli.js.map +1 -1
  6. package/dist/compile-cache.d.ts +2 -0
  7. package/dist/compile-cache.d.ts.map +1 -0
  8. package/dist/compile-cache.js +33 -0
  9. package/dist/compile-cache.js.map +1 -0
  10. package/dist/core/agent-session.d.ts +5 -0
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +94 -12
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/compaction/compaction.d.ts +6 -0
  15. package/dist/core/compaction/compaction.d.ts.map +1 -1
  16. package/dist/core/compaction/compaction.js +13 -0
  17. package/dist/core/compaction/compaction.js.map +1 -1
  18. package/dist/core/cursor-exec-bridge-session.d.ts +2 -1
  19. package/dist/core/cursor-exec-bridge-session.d.ts.map +1 -1
  20. package/dist/core/cursor-exec-bridge-session.js +5 -2
  21. package/dist/core/cursor-exec-bridge-session.js.map +1 -1
  22. package/dist/core/cursor-exec-bridge.d.ts +9 -1
  23. package/dist/core/cursor-exec-bridge.d.ts.map +1 -1
  24. package/dist/core/cursor-exec-bridge.js +26 -2
  25. package/dist/core/cursor-exec-bridge.js.map +1 -1
  26. package/dist/core/extensions/builtin/claude-sdk-oauth/custom-tools.d.ts +1 -1
  27. package/dist/core/extensions/builtin/claude-sdk-oauth/custom-tools.d.ts.map +1 -1
  28. package/dist/core/extensions/builtin/claude-sdk-oauth/custom-tools.js +5 -2
  29. package/dist/core/extensions/builtin/claude-sdk-oauth/custom-tools.js.map +1 -1
  30. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.d.ts +6 -4
  31. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.js +24 -2
  33. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.js.map +1 -1
  34. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.lazy.d.ts +39 -0
  35. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.lazy.d.ts.map +1 -0
  36. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.lazy.js +31 -0
  37. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.lazy.js.map +1 -0
  38. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts +2 -0
  39. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts.map +1 -1
  40. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js +35 -0
  41. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js.map +1 -1
  42. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.d.ts +1 -1
  43. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.d.ts.map +1 -1
  44. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.js +1 -0
  45. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.js.map +1 -1
  46. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.d.ts +8 -0
  47. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.d.ts.map +1 -1
  48. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js +4 -1
  49. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js.map +1 -1
  50. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.d.ts.map +1 -1
  51. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js +28 -1
  52. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js.map +1 -1
  53. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.d.ts.map +1 -1
  54. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.js +5 -2
  55. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.js.map +1 -1
  56. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  57. package/dist/core/extensions/builtin/compaction/index.js +5 -0
  58. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  59. package/dist/core/extensions/builtin/compaction/log.d.ts +1 -1
  60. package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/compaction/log.js +1 -0
  62. package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
  63. package/dist/core/extensions/builtin/compaction/speculative.d.ts +1 -0
  64. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/compaction/speculative.js +4 -0
  66. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  67. package/dist/core/extensions/builtin/config-reload/index.d.ts.map +1 -1
  68. package/dist/core/extensions/builtin/config-reload/index.js +13 -10
  69. package/dist/core/extensions/builtin/config-reload/index.js.map +1 -1
  70. package/dist/core/extensions/builtin/config-reload/watch-engine.d.ts +8 -1
  71. package/dist/core/extensions/builtin/config-reload/watch-engine.d.ts.map +1 -1
  72. package/dist/core/extensions/builtin/config-reload/watch-engine.js +98 -18
  73. package/dist/core/extensions/builtin/config-reload/watch-engine.js.map +1 -1
  74. package/dist/core/extensions/builtin/config-reload/watch-event-source.d.ts +1 -1
  75. package/dist/core/extensions/builtin/config-reload/watch-event-source.d.ts.map +1 -1
  76. package/dist/core/extensions/builtin/config-reload/watch-event-source.js +7 -2
  77. package/dist/core/extensions/builtin/config-reload/watch-event-source.js.map +1 -1
  78. package/dist/core/extensions/builtin/goal/elapsed-ticker.d.ts.map +1 -1
  79. package/dist/core/extensions/builtin/goal/elapsed-ticker.js +15 -1
  80. package/dist/core/extensions/builtin/goal/elapsed-ticker.js.map +1 -1
  81. package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
  82. package/dist/core/extensions/builtin/goal/index.js +7 -22
  83. package/dist/core/extensions/builtin/goal/index.js.map +1 -1
  84. package/dist/core/extensions/builtin/goal/stale-context.d.ts +9 -0
  85. package/dist/core/extensions/builtin/goal/stale-context.d.ts.map +1 -0
  86. package/dist/core/extensions/builtin/goal/stale-context.js +11 -0
  87. package/dist/core/extensions/builtin/goal/stale-context.js.map +1 -0
  88. package/dist/core/extensions/builtin/goal/wait-ticker.d.ts +8 -0
  89. package/dist/core/extensions/builtin/goal/wait-ticker.d.ts.map +1 -1
  90. package/dist/core/extensions/builtin/goal/wait-ticker.js +37 -3
  91. package/dist/core/extensions/builtin/goal/wait-ticker.js.map +1 -1
  92. package/dist/core/extensions/builtin/mcp/index.d.ts.map +1 -1
  93. package/dist/core/extensions/builtin/mcp/index.js +12 -1
  94. package/dist/core/extensions/builtin/mcp/index.js.map +1 -1
  95. package/dist/core/extensions/builtin/prompt-url-widget.d.ts.map +1 -1
  96. package/dist/core/extensions/builtin/prompt-url-widget.js +7 -15
  97. package/dist/core/extensions/builtin/prompt-url-widget.js.map +1 -1
  98. package/dist/core/extensions/builtin/rules/ui/rules-banner.d.ts.map +1 -1
  99. package/dist/core/extensions/builtin/rules/ui/rules-banner.js +21 -25
  100. package/dist/core/extensions/builtin/rules/ui/rules-banner.js.map +1 -1
  101. package/dist/core/extensions/builtin/todotools/index.d.ts.map +1 -1
  102. package/dist/core/extensions/builtin/todotools/index.js +20 -0
  103. package/dist/core/extensions/builtin/todotools/index.js.map +1 -1
  104. package/dist/core/extensions/builtin/todotools/native-todo-mirror.d.ts +3 -0
  105. package/dist/core/extensions/builtin/todotools/native-todo-mirror.d.ts.map +1 -0
  106. package/dist/core/extensions/builtin/todotools/native-todo-mirror.js +22 -0
  107. package/dist/core/extensions/builtin/todotools/native-todo-mirror.js.map +1 -0
  108. package/dist/core/extensions/builtin/webfetch/webfetch/content.lazy.d.ts +17 -0
  109. package/dist/core/extensions/builtin/webfetch/webfetch/content.lazy.d.ts.map +1 -0
  110. package/dist/core/extensions/builtin/webfetch/webfetch/content.lazy.js +22 -0
  111. package/dist/core/extensions/builtin/webfetch/webfetch/content.lazy.js.map +1 -0
  112. package/dist/core/extensions/builtin/webfetch/webfetch/tool.js +3 -3
  113. package/dist/core/extensions/builtin/webfetch/webfetch/tool.js.map +1 -1
  114. package/dist/core/messages.d.ts +9 -2
  115. package/dist/core/messages.d.ts.map +1 -1
  116. package/dist/core/messages.js +13 -18
  117. package/dist/core/messages.js.map +1 -1
  118. package/dist/core/retry-fallback/hint-policy.d.ts +3 -2
  119. package/dist/core/retry-fallback/hint-policy.d.ts.map +1 -1
  120. package/dist/core/retry-fallback/hint-policy.js +19 -8
  121. package/dist/core/retry-fallback/hint-policy.js.map +1 -1
  122. package/dist/core/retry-fallback/settings.js.map +1 -1
  123. package/dist/core/session-discovery.d.ts +16 -0
  124. package/dist/core/session-discovery.d.ts.map +1 -0
  125. package/dist/core/session-discovery.js +101 -0
  126. package/dist/core/session-discovery.js.map +1 -0
  127. package/dist/core/session-manager.d.ts +2 -1
  128. package/dist/core/session-manager.d.ts.map +1 -1
  129. package/dist/core/session-manager.js +4 -170
  130. package/dist/core/session-manager.js.map +1 -1
  131. package/dist/core/session-record.d.ts +14 -0
  132. package/dist/core/session-record.d.ts.map +1 -0
  133. package/dist/core/session-record.js +47 -0
  134. package/dist/core/session-record.js.map +1 -0
  135. package/dist/core/session-summary-cache.d.ts +40 -0
  136. package/dist/core/session-summary-cache.d.ts.map +1 -0
  137. package/dist/core/session-summary-cache.js +79 -0
  138. package/dist/core/session-summary-cache.js.map +1 -0
  139. package/dist/core/session-summary-lru.d.ts +54 -0
  140. package/dist/core/session-summary-lru.d.ts.map +1 -0
  141. package/dist/core/session-summary-lru.js +72 -0
  142. package/dist/core/session-summary-lru.js.map +1 -0
  143. package/dist/core/session-summary.d.ts +29 -0
  144. package/dist/core/session-summary.d.ts.map +1 -0
  145. package/dist/core/session-summary.js +90 -0
  146. package/dist/core/session-summary.js.map +1 -0
  147. package/dist/index.d.ts +1 -0
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +2 -0
  150. package/dist/index.js.map +1 -1
  151. package/dist/inspector-policy.d.ts +6 -0
  152. package/dist/inspector-policy.d.ts.map +1 -1
  153. package/dist/inspector-policy.js +6 -1
  154. package/dist/inspector-policy.js.map +1 -1
  155. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  156. package/dist/modes/interactive/components/earendil-announcement.js +8 -8
  157. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
  158. package/dist/modes/interactive/components/progressive-transcript-container.d.ts +72 -0
  159. package/dist/modes/interactive/components/progressive-transcript-container.d.ts.map +1 -0
  160. package/dist/modes/interactive/components/progressive-transcript-container.js +150 -0
  161. package/dist/modes/interactive/components/progressive-transcript-container.js.map +1 -0
  162. package/dist/modes/interactive/components/status-indicator.d.ts +1 -1
  163. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  164. package/dist/modes/interactive/components/status-indicator.js +8 -2
  165. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  166. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  167. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  168. package/dist/modes/interactive/interactive-mode.js +127 -65
  169. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  170. package/dist/modes/interactive/split-trailing-assistant-text.d.ts +7 -0
  171. package/dist/modes/interactive/split-trailing-assistant-text.d.ts.map +1 -0
  172. package/dist/modes/interactive/split-trailing-assistant-text.js +19 -0
  173. package/dist/modes/interactive/split-trailing-assistant-text.js.map +1 -0
  174. package/dist/senpi +31 -3
  175. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +12 -0
  176. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  177. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  178. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +38 -3
  179. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  180. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.d.ts +8 -1
  181. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.d.ts.map +1 -1
  182. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js +25 -1
  183. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js.map +1 -1
  184. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  185. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
  186. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  187. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +284 -209
  188. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  189. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.d.ts +37 -0
  190. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.d.ts.map +1 -0
  191. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.js +110 -0
  192. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.js.map +1 -0
  193. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-task-args.d.ts +3 -0
  194. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-task-args.d.ts.map +1 -0
  195. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-task-args.js +17 -0
  196. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-task-args.js.map +1 -0
  197. package/node_modules/@earendil-works/pi-ai/dist/cursor/selection-descriptor.d.ts.map +1 -1
  198. package/node_modules/@earendil-works/pi-ai/dist/cursor/selection-descriptor.js +27 -11
  199. package/node_modules/@earendil-works/pi-ai/dist/cursor/selection-descriptor.js.map +1 -1
  200. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  201. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  202. package/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
  203. package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
  204. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  205. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  206. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  207. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  208. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.d.ts.map +1 -1
  209. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.js +2 -0
  210. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.js.map +1 -1
  211. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +30 -0
  212. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  213. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +33 -0
  214. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  215. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  216. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  217. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  218. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  219. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"cursor-exec-bridge.js","sourceRoot":"","sources":["../../src/core/cursor-exec-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Cursor exec-channel → local tool bridge.\n *\n * Cursor's agent protocol executes tools server-drivenly: mid-stream, the\n * server sends exec frames and blocks until the client answers in band. The\n * `cursor-agent` API dispatches those frames onto this bridge, which maps\n * each frame's args onto the session's real tools (`read`, `bash`, `edit`,\n * `write`, `grep`, `find`, `ls`, plus MCP/extension tools by name) and runs\n * them through the same wrapped `AgentTool.execute` path model-issued calls\n * use — so approvals, sandboxing, truncation, and rendering behavior stay\n * identical.\n *\n * The arg translations here MUST mirror the display blocks the API\n * synthesizes (`synthesizeCursorExecToolCall` call sites): the transcript\n * shows one operation, so a different one must not run.\n */\n\nimport type { AgentEvent, AgentTool, AgentToolCall } from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype CursorExecHandlers,\n\tcomposeCursorShellCommand,\n\tcursorPiLimit,\n\tcursorPiLsPath,\n\tcursorPiReadArgs,\n\tcursorPiTimeout,\n\tomitUndefinedCursorArgs,\n\ttype ToolResultMessage,\n\tvalidateToolArguments,\n} from \"@earendil-works/pi-ai\";\nimport type { ToolCallEvent, ToolCallEventResult } from \"./extensions/types.ts\";\n\nexport interface CursorExecBridgeOptions {\n\t/**\n\t * Resolve an executable tool by name. Backed by the session's full tool\n\t * registry (builtin + extension tools), not only the active set: Cursor\n\t * drives its native tools (read/bash/grep/ls/write) over the exec channel\n\t * regardless of which tools the request advertised.\n\t */\n\tgetTool: (name: string) => AgentTool | undefined;\n\t/**\n\t * Surface tool lifecycle events to the host UI. Bridge-run tools bypass\n\t * the agent loop's executor, so without these the live tool card for a\n\t * synthesized call never resolves.\n\t */\n\temitEvent: (event: AgentEvent, runSignal: AbortSignal) => Promise<void>;\n\t/** Run the session's vetoable extension preflight before tool execution. */\n\tpreflightToolCall?: (event: ToolCallEvent) => Promise<ToolCallEventResult | undefined>;\n\t/** Abort signal for in-flight bridge executions (the active run's signal). */\n\tgetAbortSignal: () => AbortSignal | undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction errorResult(toolCallId: string, toolName: string, text: string): ToolResultMessage {\n\treturn {\n\t\trole: \"toolResult\",\n\t\ttoolCallId,\n\t\ttoolName,\n\t\tcontent: [{ type: \"text\", text }],\n\t\tisError: true,\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction emptyResult(toolCallId: string, toolName: string): ToolResultMessage {\n\treturn {\n\t\trole: \"toolResult\",\n\t\ttoolCallId,\n\t\ttoolName,\n\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\tisError: false,\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nasync function executeTool(\n\toptions: CursorExecBridgeOptions,\n\ttoolName: string,\n\ttoolCallId: string,\n\targs: Record<string, unknown>,\n): Promise<ToolResultMessage> {\n\tconst runSignal = options.getAbortSignal();\n\tif (!runSignal) {\n\t\treturn errorResult(toolCallId, toolName, \"Tool execution has no active run\");\n\t}\n\n\tconst tool = options.getTool(toolName);\n\tif (!tool) {\n\t\treturn errorResult(toolCallId, toolName, `Tool \"${toolName}\" is not available in this session`);\n\t}\n\n\t// Exec-frame translators write forms like `path: args.path || undefined`;\n\t// a present `undefined` fails schema validation on optional fields even\n\t// though omitting the key is valid.\n\tconst cleanArgs = omitUndefinedCursorArgs(args);\n\tconst toolCall: AgentToolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: toolCallId,\n\t\tname: toolName,\n\t\targuments: cleanArgs,\n\t};\n\n\tlet params: unknown;\n\ttry {\n\t\tparams = validateToolArguments(tool, toolCall);\n\t\tif (tool.prepareArguments) {\n\t\t\tparams = tool.prepareArguments(params);\n\t\t}\n\t} catch (error) {\n\t\treturn errorResult(toolCallId, toolName, error instanceof Error ? error.message : String(error));\n\t}\n\n\tawait options.emitEvent({ type: \"tool_execution_start\", toolCallId, toolName, args: cleanArgs }, runSignal);\n\tlet toolResult: ToolResultMessage;\n\tlet endEvent: AgentEvent;\n\ttry {\n\t\tif (!isRecord(params)) {\n\t\t\tthrow new Error(`Tool \"${toolName}\" prepared non-object arguments`);\n\t\t}\n\t\tconst preflight = await options.preflightToolCall?.({\n\t\t\ttype: \"tool_call\",\n\t\t\ttoolCallId,\n\t\t\ttoolName,\n\t\t\tinput: params,\n\t\t});\n\t\tif (preflight?.block) {\n\t\t\tconst message = preflight.reason || \"Tool execution was blocked\";\n\t\t\ttoolResult = errorResult(toolCallId, toolName, message);\n\t\t\tendEvent = {\n\t\t\t\ttype: \"tool_execution_end\",\n\t\t\t\ttoolCallId,\n\t\t\t\ttoolName,\n\t\t\t\tresult: { content: [{ type: \"text\", text: message }], details: undefined },\n\t\t\t\tisError: true,\n\t\t\t};\n\t\t} else {\n\t\t\tconst result = await tool.execute(toolCallId, params, runSignal, undefined);\n\t\t\ttoolResult = {\n\t\t\t\trole: \"toolResult\",\n\t\t\t\ttoolCallId,\n\t\t\t\ttoolName,\n\t\t\t\tcontent: result.content ?? [],\n\t\t\t\tdetails: result.details,\n\t\t\t\tusage: result.usage,\n\t\t\t\tisError: false,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\t\t\tendEvent = { type: \"tool_execution_end\", toolCallId, toolName, result, isError: false };\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\ttoolResult = errorResult(toolCallId, toolName, message);\n\t\tendEvent = {\n\t\t\ttype: \"tool_execution_end\",\n\t\t\ttoolCallId,\n\t\t\ttoolName,\n\t\t\tresult: { content: [{ type: \"text\", text: message }], details: undefined },\n\t\t\tisError: true,\n\t\t};\n\t}\n\tawait options.emitEvent(endEvent, runSignal);\n\treturn toolResult;\n}\n\n/**\n * Build the exec handlers passed to the `cursor-agent` API through the agent\n * loop config. Handlers return bare `ToolResultMessage`s; the API derives the\n * wire protobuf answer from them and pairs the transcript result.\n *\n * Deliberately absent handlers (the API answers with a typed refusal):\n * - `delete`: senpi has no delete tool and the bridge must not mutate the\n * filesystem outside the tool/approval path.\n * - `diagnostics`: no LSP tool in this fork.\n * - `mcpApprovalPreflight`: approval decisions cannot be resolved without an\n * interactive prompt, so smart-mode probes are conservatively refused.\n */\nexport function createCursorExecBridge(options: CursorExecBridgeOptions): CursorExecHandlers {\n\treturn {\n\t\tread: async (args) =>\n\t\t\texecuteTool(options, \"read\", args.toolCallId, {\n\t\t\t\tpath: args.path,\n\t\t\t\toffset: args.offset,\n\t\t\t\tlimit: args.limit,\n\t\t\t}),\n\n\t\tls: async (args) => executeTool(options, \"ls\", args.toolCallId, { path: cursorPiLsPath(args.path) }),\n\n\t\tgrep: async (args) =>\n\t\t\texecuteTool(options, \"grep\", args.toolCallId, {\n\t\t\t\tpattern: args.pattern,\n\t\t\t\tpath: args.path || undefined,\n\t\t\t\tglob: args.glob || undefined,\n\t\t\t\tignoreCase: args.caseInsensitive === true ? true : undefined,\n\t\t\t}),\n\n\t\twrite: async (args) =>\n\t\t\texecuteTool(options, \"write\", args.toolCallId, {\n\t\t\t\tpath: args.path,\n\t\t\t\tcontent: args.fileText ?? new TextDecoder().decode(args.fileBytes ?? new Uint8Array()),\n\t\t\t}),\n\n\t\tshell: async (args) =>\n\t\t\texecuteTool(options, \"bash\", args.toolCallId, {\n\t\t\t\tcommand: composeCursorShellCommand(args.command, args.workingDirectory || undefined),\n\t\t\t\ttimeout: args.timeout && args.timeout > 0 ? args.timeout : undefined,\n\t\t\t}),\n\n\t\tmcp: async (call) => {\n\t\t\tconst toolName = call.toolName || call.name;\n\t\t\tconst tool = options.getTool(toolName);\n\t\t\tif (!tool) {\n\t\t\t\treturn errorResult(call.toolCallId, toolName, `Tool \"${toolName}\" is not registered in this session`);\n\t\t\t}\n\t\t\treturn executeTool(options, toolName, call.toolCallId, call.args);\n\t\t},\n\n\t\tpiRead: async (call) => {\n\t\t\tconst readArgs = cursorPiReadArgs(call.args.path, call.args.offset, call.args.limit);\n\t\t\t// A present `limit: 0` reads zero lines; answer with empty output\n\t\t\t// without running the tool rather than degrading into a full read.\n\t\t\tif (readArgs === null) return emptyResult(call.toolCallId, \"read\");\n\t\t\treturn executeTool(options, \"read\", call.toolCallId, readArgs);\n\t\t},\n\n\t\tpiBash: async (call) =>\n\t\t\texecuteTool(options, \"bash\", call.toolCallId, {\n\t\t\t\tcommand: call.args.command,\n\t\t\t\ttimeout: cursorPiTimeout(call.args.timeout),\n\t\t\t}),\n\n\t\tpiEdit: async (call) =>\n\t\t\texecuteTool(options, \"edit\", call.toolCallId, {\n\t\t\t\tpath: call.args.path,\n\t\t\t\tedits: call.args.edits.map((edit) => ({ oldText: edit.oldText, newText: edit.newText })),\n\t\t\t}),\n\n\t\tpiWrite: async (call) =>\n\t\t\texecuteTool(options, \"write\", call.toolCallId, {\n\t\t\t\tpath: call.args.path,\n\t\t\t\tcontent: call.args.content,\n\t\t\t}),\n\n\t\tpiGrep: async (call) =>\n\t\t\texecuteTool(options, \"grep\", call.toolCallId, {\n\t\t\t\tpattern: call.args.pattern,\n\t\t\t\tpath: call.args.path || undefined,\n\t\t\t\tglob: call.args.glob || undefined,\n\t\t\t\tignoreCase: call.args.ignoreCase === true ? true : undefined,\n\t\t\t\tliteral: call.args.literal === true ? true : undefined,\n\t\t\t\tcontext: call.args.context,\n\t\t\t\tlimit: cursorPiLimit(call.args.limit),\n\t\t\t}),\n\n\t\tpiFind: async (call) =>\n\t\t\texecuteTool(options, \"find\", call.toolCallId, {\n\t\t\t\tpattern: call.args.pattern,\n\t\t\t\tpath: call.args.path || undefined,\n\t\t\t\tlimit: cursorPiLimit(call.args.limit),\n\t\t\t}),\n\n\t\tpiLs: async (call) =>\n\t\t\texecuteTool(options, \"ls\", call.toolCallId, {\n\t\t\t\tpath: cursorPiLsPath(call.args.path),\n\t\t\t\tlimit: cursorPiLimit(call.args.limit),\n\t\t\t}),\n\t};\n}\n"]}
1
+ {"version":3,"file":"cursor-exec-bridge.js","sourceRoot":"","sources":["../../src/core/cursor-exec-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Cursor exec-channel → local tool bridge.\n *\n * Cursor's agent protocol executes tools server-drivenly: mid-stream, the\n * server sends exec frames and blocks until the client answers in band. The\n * `cursor-agent` API dispatches those frames onto this bridge, which maps\n * each frame's args onto the session's real tools (`read`, `bash`, `edit`,\n * `write`, `grep`, `find`, `ls`, plus MCP/extension tools by name) and runs\n * them through the same wrapped `AgentTool.execute` path model-issued calls\n * use — so approvals, sandboxing, truncation, and rendering behavior stay\n * identical.\n *\n * The arg translations here MUST mirror the display blocks the API\n * synthesizes (`synthesizeCursorExecToolCall` call sites): the transcript\n * shows one operation, so a different one must not run.\n */\n\nimport type { AgentEvent, AgentTool, AgentToolCall, AgentToolResult } from \"@earendil-works/pi-agent-core\";\nimport {\n\ttype CursorExecHandlers,\n\tcomposeCursorShellCommand,\n\tcursorPiLimit,\n\tcursorPiLsPath,\n\tcursorPiReadArgs,\n\tcursorPiTimeout,\n\tomitUndefinedCursorArgs,\n\ttype ToolResultMessage,\n\tvalidateToolArguments,\n} from \"@earendil-works/pi-ai\";\nimport type { ToolCallEvent, ToolCallEventResult } from \"./extensions/types.ts\";\n\nexport interface CursorExecBridgeOptions {\n\t/**\n\t * Resolve an executable tool by name. Backed by the session's full tool\n\t * registry (builtin + extension tools), not only the active set: Cursor\n\t * drives its native tools (read/bash/grep/ls/write) over the exec channel\n\t * regardless of which tools the request advertised.\n\t */\n\tgetTool: (name: string) => AgentTool | undefined;\n\t/**\n\t * Surface tool lifecycle events to the host UI. Bridge-run tools bypass\n\t * the agent loop's executor, so without these the live tool card for a\n\t * synthesized call never resolves.\n\t */\n\temitEvent: (event: AgentEvent, runSignal: AbortSignal) => Promise<void>;\n\t/** Same tool_result hook the local tool loop emits; plan-touch trackers listen here. */\n\temitToolResult?: (event: {\n\t\ttoolName: string;\n\t\ttoolCallId: string;\n\t\targs: unknown;\n\t\tresult: AgentToolResult<unknown>;\n\t\tisError: boolean;\n\t}) => Promise<void>;\n\t/** Run the session's vetoable extension preflight before tool execution. */\n\tpreflightToolCall?: (event: ToolCallEvent) => Promise<ToolCallEventResult | undefined>;\n\t/** Abort signal for in-flight bridge executions (the active run's signal). */\n\tgetAbortSignal: () => AbortSignal | undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction errorResult(toolCallId: string, toolName: string, text: string): ToolResultMessage {\n\treturn {\n\t\trole: \"toolResult\",\n\t\ttoolCallId,\n\t\ttoolName,\n\t\tcontent: [{ type: \"text\", text }],\n\t\tisError: true,\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction emptyResult(toolCallId: string, toolName: string): ToolResultMessage {\n\treturn {\n\t\trole: \"toolResult\",\n\t\ttoolCallId,\n\t\ttoolName,\n\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\tisError: false,\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nasync function executeTool(\n\toptions: CursorExecBridgeOptions,\n\ttoolName: string,\n\ttoolCallId: string,\n\targs: Record<string, unknown>,\n): Promise<ToolResultMessage> {\n\tconst runSignal = options.getAbortSignal();\n\tif (!runSignal || runSignal.aborted) {\n\t\treturn errorResult(toolCallId, toolName, \"Tool execution has no active run\");\n\t}\n\n\tconst tool = options.getTool(toolName);\n\tif (!tool) {\n\t\treturn errorResult(toolCallId, toolName, `Tool \"${toolName}\" is not available in this session`);\n\t}\n\n\t// Exec-frame translators write forms like `path: args.path || undefined`;\n\t// a present `undefined` fails schema validation on optional fields even\n\t// though omitting the key is valid.\n\tconst cleanArgs = omitUndefinedCursorArgs(args);\n\tconst toolCall: AgentToolCall = {\n\t\ttype: \"toolCall\",\n\t\tid: toolCallId,\n\t\tname: toolName,\n\t\targuments: cleanArgs,\n\t};\n\n\tlet params: unknown;\n\ttry {\n\t\tparams = validateToolArguments(tool, toolCall);\n\t\tif (tool.prepareArguments) {\n\t\t\tparams = tool.prepareArguments(params);\n\t\t}\n\t} catch (error) {\n\t\treturn errorResult(toolCallId, toolName, error instanceof Error ? error.message : String(error));\n\t}\n\n\tawait options.emitEvent({ type: \"tool_execution_start\", toolCallId, toolName, args: cleanArgs }, runSignal);\n\tlet toolResult: ToolResultMessage;\n\tlet endEvent: AgentEvent;\n\ttry {\n\t\tif (!isRecord(params)) {\n\t\t\tthrow new Error(`Tool \"${toolName}\" prepared non-object arguments`);\n\t\t}\n\t\tconst preflight = await options.preflightToolCall?.({\n\t\t\ttype: \"tool_call\",\n\t\t\ttoolCallId,\n\t\t\ttoolName,\n\t\t\tinput: params,\n\t\t});\n\t\tif (runSignal.aborted || options.getAbortSignal() !== runSignal) {\n\t\t\tconst message = \"Tool execution has no active run\";\n\t\t\ttoolResult = errorResult(toolCallId, toolName, message);\n\t\t\tendEvent = {\n\t\t\t\ttype: \"tool_execution_end\",\n\t\t\t\ttoolCallId,\n\t\t\t\ttoolName,\n\t\t\t\tresult: { content: [{ type: \"text\", text: message }], details: undefined },\n\t\t\t\tisError: true,\n\t\t\t};\n\t\t} else if (preflight?.block) {\n\t\t\tconst message = preflight.reason || \"Tool execution was blocked\";\n\t\t\ttoolResult = errorResult(toolCallId, toolName, message);\n\t\t\tendEvent = {\n\t\t\t\ttype: \"tool_execution_end\",\n\t\t\t\ttoolCallId,\n\t\t\t\ttoolName,\n\t\t\t\tresult: { content: [{ type: \"text\", text: message }], details: undefined },\n\t\t\t\tisError: true,\n\t\t\t};\n\t\t} else {\n\t\t\tconst result = await tool.execute(toolCallId, params, runSignal, undefined);\n\t\t\ttoolResult = {\n\t\t\t\trole: \"toolResult\",\n\t\t\t\ttoolCallId,\n\t\t\t\ttoolName,\n\t\t\t\tcontent: result.content ?? [],\n\t\t\t\tdetails: result.details,\n\t\t\t\tusage: result.usage,\n\t\t\t\tisError: false,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\t\t\tendEvent = { type: \"tool_execution_end\", toolCallId, toolName, result, isError: false };\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\ttoolResult = errorResult(toolCallId, toolName, message);\n\t\tendEvent = {\n\t\t\ttype: \"tool_execution_end\",\n\t\t\ttoolCallId,\n\t\t\ttoolName,\n\t\t\tresult: { content: [{ type: \"text\", text: message }], details: undefined },\n\t\t\tisError: true,\n\t\t};\n\t}\n\tawait options.emitEvent(endEvent, runSignal);\n\tif (options.emitToolResult) {\n\t\tawait options.emitToolResult({\n\t\t\ttoolName,\n\t\t\ttoolCallId,\n\t\t\targs: params ?? cleanArgs,\n\t\t\tresult: {\n\t\t\t\tcontent: toolResult.content,\n\t\t\t\tdetails: toolResult.details,\n\t\t\t\tusage: toolResult.usage,\n\t\t\t},\n\t\t\tisError: toolResult.isError === true,\n\t\t});\n\t}\n\treturn toolResult;\n}\n\n/**\n * Build the exec handlers passed to the `cursor-agent` API through the agent\n * loop config. Handlers return bare `ToolResultMessage`s; the API derives the\n * wire protobuf answer from them and pairs the transcript result.\n *\n * Deliberately absent handlers (the API answers with a typed refusal):\n * - `delete`: senpi has no delete tool and the bridge must not mutate the\n * filesystem outside the tool/approval path.\n * - `diagnostics`: no LSP tool in this fork.\n * - `mcpApprovalPreflight`: approval decisions cannot be resolved without an\n * interactive prompt, so smart-mode probes are conservatively refused.\n */\nexport function createCursorExecBridge(options: CursorExecBridgeOptions): CursorExecHandlers {\n\treturn {\n\t\tread: async (args) =>\n\t\t\texecuteTool(options, \"read\", args.toolCallId, {\n\t\t\t\tpath: args.path,\n\t\t\t\toffset: args.offset,\n\t\t\t\tlimit: args.limit,\n\t\t\t}),\n\n\t\tls: async (args) => executeTool(options, \"ls\", args.toolCallId, { path: cursorPiLsPath(args.path) }),\n\n\t\tgrep: async (args) =>\n\t\t\texecuteTool(options, \"grep\", args.toolCallId, {\n\t\t\t\tpattern: args.pattern,\n\t\t\t\tpath: args.path || undefined,\n\t\t\t\tglob: args.glob || undefined,\n\t\t\t\tignoreCase: args.caseInsensitive === true ? true : undefined,\n\t\t\t}),\n\n\t\twrite: async (args) =>\n\t\t\texecuteTool(options, \"write\", args.toolCallId, {\n\t\t\t\tpath: args.path,\n\t\t\t\tcontent: args.fileText ?? new TextDecoder().decode(args.fileBytes ?? new Uint8Array()),\n\t\t\t}),\n\n\t\tshell: async (args) =>\n\t\t\texecuteTool(options, \"bash\", args.toolCallId, {\n\t\t\t\tcommand: composeCursorShellCommand(args.command, args.workingDirectory || undefined),\n\t\t\t\ttimeout: args.timeout && args.timeout > 0 ? args.timeout : undefined,\n\t\t\t}),\n\n\t\tmcp: async (call) => {\n\t\t\tconst toolName = call.toolName || call.name;\n\t\t\tconst tool = options.getTool(toolName);\n\t\t\tif (!tool) {\n\t\t\t\treturn errorResult(call.toolCallId, toolName, `Tool \"${toolName}\" is not registered in this session`);\n\t\t\t}\n\t\t\treturn executeTool(options, toolName, call.toolCallId, call.args);\n\t\t},\n\n\t\tpiRead: async (call) => {\n\t\t\tconst readArgs = cursorPiReadArgs(call.args.path, call.args.offset, call.args.limit);\n\t\t\t// A present `limit: 0` reads zero lines; answer with empty output\n\t\t\t// without running the tool rather than degrading into a full read.\n\t\t\tif (readArgs === null) return emptyResult(call.toolCallId, \"read\");\n\t\t\treturn executeTool(options, \"read\", call.toolCallId, readArgs);\n\t\t},\n\n\t\tpiBash: async (call) =>\n\t\t\texecuteTool(options, \"bash\", call.toolCallId, {\n\t\t\t\tcommand: call.args.command,\n\t\t\t\ttimeout: cursorPiTimeout(call.args.timeout),\n\t\t\t}),\n\n\t\tpiEdit: async (call) =>\n\t\t\texecuteTool(options, \"edit\", call.toolCallId, {\n\t\t\t\tpath: call.args.path,\n\t\t\t\tedits: call.args.edits.map((edit) => ({ oldText: edit.oldText, newText: edit.newText })),\n\t\t\t}),\n\n\t\tpiWrite: async (call) =>\n\t\t\texecuteTool(options, \"write\", call.toolCallId, {\n\t\t\t\tpath: call.args.path,\n\t\t\t\tcontent: call.args.content,\n\t\t\t}),\n\n\t\tpiGrep: async (call) =>\n\t\t\texecuteTool(options, \"grep\", call.toolCallId, {\n\t\t\t\tpattern: call.args.pattern,\n\t\t\t\tpath: call.args.path || undefined,\n\t\t\t\tglob: call.args.glob || undefined,\n\t\t\t\tignoreCase: call.args.ignoreCase === true ? true : undefined,\n\t\t\t\tliteral: call.args.literal === true ? true : undefined,\n\t\t\t\tcontext: call.args.context,\n\t\t\t\tlimit: cursorPiLimit(call.args.limit),\n\t\t\t}),\n\n\t\tpiFind: async (call) =>\n\t\t\texecuteTool(options, \"find\", call.toolCallId, {\n\t\t\t\tpattern: call.args.pattern,\n\t\t\t\tpath: call.args.path || undefined,\n\t\t\t\tlimit: cursorPiLimit(call.args.limit),\n\t\t\t}),\n\n\t\tpiLs: async (call) =>\n\t\t\texecuteTool(options, \"ls\", call.toolCallId, {\n\t\t\t\tpath: cursorPiLsPath(call.args.path),\n\t\t\t\tlimit: cursorPiLimit(call.args.limit),\n\t\t\t}),\n\t};\n}\n"]}
@@ -12,6 +12,6 @@ export declare function denyCustomToolExecution(): Promise<{
12
12
  * Exposes senpi-only tools to Claude Code's planner. The SDK handler is deliberately
13
13
  * unusable: senpi receives the matching streamed tool call and executes it itself.
14
14
  */
15
- export declare function buildCustomToolServers(customTools: readonly Tool[]): Record<string, CustomToolsMcpServer> | undefined;
15
+ export declare function buildCustomToolServers(customTools: readonly Tool[]): Promise<Record<string, CustomToolsMcpServer> | undefined>;
16
16
  export {};
17
17
  //# sourceMappingURL=custom-tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"custom-tools.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/custom-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAElD,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrE,KAAK,oBAAoB,GAAG,UAAU,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC,CAAC;AAE1E,wBAAsB,uBAAuB;;;;;;GAK5C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,SAAS,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,GAAG,SAAS,CAarH"}
1
+ {"version":3,"file":"custom-tools.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/custom-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAElD,OAAO,EAAsC,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGzF,KAAK,oBAAoB,GAAG,UAAU,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC,CAAC;AAE1E,wBAAsB,uBAAuB;;;;;;GAK5C;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC3C,WAAW,EAAE,SAAS,IAAI,EAAE,GAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,GAAG,SAAS,CAAC,CAgB3D"}
@@ -1,5 +1,5 @@
1
1
  import { jsonSchemaToZodShape } from "./custom-tools-schema.js";
2
- import { getSdkBoundary } from "./sdk-boundary.js";
2
+ import { getSdkBoundary, loadClaudeAgentSdk } from "./sdk-boundary.js";
3
3
  import { CUSTOM_TOOLS_MCP_SERVER_NAME, TOOL_EXECUTION_DENIED_MESSAGE } from "./tools.js";
4
4
  export async function denyCustomToolExecution() {
5
5
  return {
@@ -11,9 +11,12 @@ export async function denyCustomToolExecution() {
11
11
  * Exposes senpi-only tools to Claude Code's planner. The SDK handler is deliberately
12
12
  * unusable: senpi receives the matching streamed tool call and executes it itself.
13
13
  */
14
- export function buildCustomToolServers(customTools) {
14
+ export async function buildCustomToolServers(customTools) {
15
15
  if (customTools.length === 0)
16
16
  return undefined;
17
+ // `createSdkMcpServer` is a synchronous SDK member behind a lazy boundary;
18
+ // awaiting here is what makes this call site self-sufficient.
19
+ await loadClaudeAgentSdk();
17
20
  const server = getSdkBoundary().createSdkMcpServer({
18
21
  name: CUSTOM_TOOLS_MCP_SERVER_NAME,
19
22
  version: "1.0.0",
@@ -1 +1 @@
1
- {"version":3,"file":"custom-tools.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/custom-tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAoB,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,4BAA4B,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAIzF,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC5C,OAAO;QACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC;QACzE,OAAO,EAAE,IAAI;KACb,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,WAA4B;IAClE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC,kBAAkB,CAAC;QAClD,IAAI,EAAE,4BAA4B;QAClC,OAAO,EAAE,OAAO;QAChB,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACjC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC;YAClD,OAAO,EAAE,uBAAuB;SAChC,CAAC,CAAC;KACH,CAAC,CAAC;IACH,OAAO,EAAE,CAAC,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;AACnD,CAAC","sourcesContent":["import type { Tool } from \"@earendil-works/pi-ai\";\nimport { jsonSchemaToZodShape } from \"./custom-tools-schema.ts\";\nimport { getSdkBoundary, type SdkBoundary } from \"./sdk-boundary.ts\";\nimport { CUSTOM_TOOLS_MCP_SERVER_NAME, TOOL_EXECUTION_DENIED_MESSAGE } from \"./tools.ts\";\n\ntype CustomToolsMcpServer = ReturnType<SdkBoundary[\"createSdkMcpServer\"]>;\n\nexport async function denyCustomToolExecution() {\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text: TOOL_EXECUTION_DENIED_MESSAGE }],\n\t\tisError: true,\n\t};\n}\n\n/**\n * Exposes senpi-only tools to Claude Code's planner. The SDK handler is deliberately\n * unusable: senpi receives the matching streamed tool call and executes it itself.\n */\nexport function buildCustomToolServers(customTools: readonly Tool[]): Record<string, CustomToolsMcpServer> | undefined {\n\tif (customTools.length === 0) return undefined;\n\tconst server = getSdkBoundary().createSdkMcpServer({\n\t\tname: CUSTOM_TOOLS_MCP_SERVER_NAME,\n\t\tversion: \"1.0.0\",\n\t\ttools: customTools.map((tool) => ({\n\t\t\tname: tool.name,\n\t\t\tdescription: tool.description,\n\t\t\tinputSchema: jsonSchemaToZodShape(tool.parameters),\n\t\t\thandler: denyCustomToolExecution,\n\t\t})),\n\t});\n\treturn { [CUSTOM_TOOLS_MCP_SERVER_NAME]: server };\n}\n"]}
1
+ {"version":3,"file":"custom-tools.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/custom-tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAoB,MAAM,mBAAmB,CAAC;AACzF,OAAO,EAAE,4BAA4B,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAIzF,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC5C,OAAO;QACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC;QACzE,OAAO,EAAE,IAAI;KACb,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC3C,WAA4B;IAE5B,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,2EAA2E;IAC3E,8DAA8D;IAC9D,MAAM,kBAAkB,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC,kBAAkB,CAAC;QAClD,IAAI,EAAE,4BAA4B;QAClC,OAAO,EAAE,OAAO;QAChB,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACjC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC;YAClD,OAAO,EAAE,uBAAuB;SAChC,CAAC,CAAC;KACH,CAAC,CAAC;IACH,OAAO,EAAE,CAAC,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;AACnD,CAAC","sourcesContent":["import type { Tool } from \"@earendil-works/pi-ai\";\nimport { jsonSchemaToZodShape } from \"./custom-tools-schema.ts\";\nimport { getSdkBoundary, loadClaudeAgentSdk, type SdkBoundary } from \"./sdk-boundary.ts\";\nimport { CUSTOM_TOOLS_MCP_SERVER_NAME, TOOL_EXECUTION_DENIED_MESSAGE } from \"./tools.ts\";\n\ntype CustomToolsMcpServer = ReturnType<SdkBoundary[\"createSdkMcpServer\"]>;\n\nexport async function denyCustomToolExecution() {\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text: TOOL_EXECUTION_DENIED_MESSAGE }],\n\t\tisError: true,\n\t};\n}\n\n/**\n * Exposes senpi-only tools to Claude Code's planner. The SDK handler is deliberately\n * unusable: senpi receives the matching streamed tool call and executes it itself.\n */\nexport async function buildCustomToolServers(\n\tcustomTools: readonly Tool[],\n): Promise<Record<string, CustomToolsMcpServer> | undefined> {\n\tif (customTools.length === 0) return undefined;\n\t// `createSdkMcpServer` is a synchronous SDK member behind a lazy boundary;\n\t// awaiting here is what makes this call site self-sufficient.\n\tawait loadClaudeAgentSdk();\n\tconst server = getSdkBoundary().createSdkMcpServer({\n\t\tname: CUSTOM_TOOLS_MCP_SERVER_NAME,\n\t\tversion: \"1.0.0\",\n\t\ttools: customTools.map((tool) => ({\n\t\t\tname: tool.name,\n\t\t\tdescription: tool.description,\n\t\t\tinputSchema: jsonSchemaToZodShape(tool.parameters),\n\t\t\thandler: denyCustomToolExecution,\n\t\t})),\n\t});\n\treturn { [CUSTOM_TOOLS_MCP_SERVER_NAME]: server };\n}\n"]}
@@ -1,8 +1,10 @@
1
1
  import type { EffortLevel, Options, SDKMessage, SDKUserMessage, SessionMessage, SettingSource, ThinkingConfig } from "@anthropic-ai/claude-agent-sdk";
2
- import { createSdkMcpServer, getSessionMessages, query } from "@anthropic-ai/claude-agent-sdk";
3
2
  import type { Base64ImageSource, ContentBlockParam } from "@anthropic-ai/sdk/resources";
3
+ import { loadClaudeAgentSdk } from "./sdk-boundary.lazy.ts";
4
+ export { loadClaudeAgentSdk } from "./sdk-boundary.lazy.ts";
4
5
  export type { Base64ImageSource, ContentBlockParam, EffortLevel, Options, SDKMessage, SDKUserMessage, SessionMessage, SettingSource, ThinkingConfig, };
5
- export type SdkQueryInput = Parameters<typeof query>[0];
6
+ type SdkModule = Awaited<ReturnType<typeof loadClaudeAgentSdk>>;
7
+ export type SdkQueryInput = Parameters<SdkModule["query"]>[0];
6
8
  export type SdkQueryHandle = AsyncIterable<SDKMessage> & {
7
9
  interrupt(): Promise<unknown>;
8
10
  setModel?: (model?: string) => Promise<void>;
@@ -12,8 +14,8 @@ export type SdkQueryHandle = AsyncIterable<SDKMessage> & {
12
14
  export type SdkQuery = (input: SdkQueryInput) => SdkQueryHandle;
13
15
  export type SdkBoundary = {
14
16
  query: SdkQuery;
15
- createSdkMcpServer: typeof createSdkMcpServer;
16
- getSessionMessages: typeof getSessionMessages;
17
+ createSdkMcpServer: SdkModule["createSdkMcpServer"];
18
+ getSessionMessages: SdkModule["getSessionMessages"];
17
19
  };
18
20
  declare const defaultSdkBoundary: SdkBoundary;
19
21
  export declare function getSdkBoundary(): SdkBoundary;
@@ -1 +1 @@
1
- {"version":3,"file":"sdk-boundary.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,WAAW,EACX,OAAO,EACP,UAAU,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,cAAc,EACd,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AAC/F,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAExF,YAAY,EACX,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,OAAO,EACP,UAAU,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,cAAc,GACd,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,MAAM,MAAM,cAAc,GAAG,aAAa,CAAC,UAAU,CAAC,GAAG;IACxD,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,KAAK,IAAI,IAAI,CAAC;IACd,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9C,CAAC;AACF,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,cAAc,CAAC;AAEhE,MAAM,MAAM,WAAW,GAAG;IACzB,KAAK,EAAE,QAAQ,CAAC;IAChB,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;CAC9C,CAAC;AAEF,QAAA,MAAM,kBAAkB,EAAE,WAA+D,CAAC;AAG1F,wBAAgB,cAAc,IAAI,WAAW,CAE5C;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAExE;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"sdk-boundary.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,WAAW,EACX,OAAO,EACP,UAAU,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,cAAc,EACd,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAwB,MAAM,wBAAwB,CAAC;AAElF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EACX,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,OAAO,EACP,UAAU,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,cAAc,GACd,CAAC;AAEF,KAAK,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC;AAEhE,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9D,MAAM,MAAM,cAAc,GAAG,aAAa,CAAC,UAAU,CAAC,GAAG;IACxD,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,KAAK,IAAI,IAAI,CAAC;IACd,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9C,CAAC;AACF,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,cAAc,CAAC;AAEhE,MAAM,MAAM,WAAW,GAAG;IACzB,KAAK,EAAE,QAAQ,CAAC;IAChB,kBAAkB,EAAE,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACpD,kBAAkB,EAAE,SAAS,CAAC,oBAAoB,CAAC,CAAC;CACpD,CAAC;AAsBF,QAAA,MAAM,kBAAkB,EAAE,WAKzB,CAAC;AAGF,wBAAgB,cAAc,IAAI,WAAW,CAE5C;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAExE;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,eAAe,kBAAkB,CAAC"}
@@ -1,5 +1,27 @@
1
- import { createSdkMcpServer, getSessionMessages, query } from "@anthropic-ai/claude-agent-sdk";
2
- const defaultSdkBoundary = { query, createSdkMcpServer, getSessionMessages };
1
+ import { loadClaudeAgentSdk, loadedClaudeAgentSdk } from "./sdk-boundary.lazy.js";
2
+ export { loadClaudeAgentSdk } from "./sdk-boundary.lazy.js";
3
+ /**
4
+ * Reads the deferred SDK module for a synchronous boundary member.
5
+ *
6
+ * `query` and `createSdkMcpServer` are synchronous SDK functions, so the
7
+ * default boundary cannot await here. Every caller reaches these through an
8
+ * async entry point that awaits `loadClaudeAgentSdk()` first, which makes an
9
+ * unloaded module a wiring defect rather than a runtime condition — and this
10
+ * names it at the exact call that skipped the preload.
11
+ */
12
+ function requireLoadedSdk(member) {
13
+ const sdk = loadedClaudeAgentSdk();
14
+ if (!sdk) {
15
+ throw new Error(`Claude Agent SDK was not preloaded before '${member}'. ` +
16
+ `Await loadClaudeAgentSdk() on the entry path before reaching the SDK boundary.`);
17
+ }
18
+ return sdk;
19
+ }
20
+ const defaultSdkBoundary = {
21
+ query: (input) => requireLoadedSdk("query").query(input),
22
+ createSdkMcpServer: (options) => requireLoadedSdk("createSdkMcpServer").createSdkMcpServer(options),
23
+ getSessionMessages: async (sessionId, options) => (await loadClaudeAgentSdk()).getSessionMessages(sessionId, options),
24
+ };
3
25
  let activeSdkBoundary = defaultSdkBoundary;
4
26
  export function getSdkBoundary() {
5
27
  return activeSdkBoundary;
@@ -1 +1 @@
1
- {"version":3,"file":"sdk-boundary.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AA8B/F,MAAM,kBAAkB,GAAgB,EAAE,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;AAC1F,IAAI,iBAAiB,GAAG,kBAAkB,CAAC;AAE3C,MAAM,UAAU,cAAc;IAC7B,OAAO,iBAAiB,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAA8B;IACjE,iBAAiB,GAAG,EAAE,GAAG,kBAAkB,EAAE,GAAG,QAAQ,EAAE,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC/B,iBAAiB,GAAG,kBAAkB,CAAC;AACxC,CAAC;AAED,eAAe,kBAAkB,CAAC","sourcesContent":["import type {\n\tEffortLevel,\n\tOptions,\n\tSDKMessage,\n\tSDKUserMessage,\n\tSessionMessage,\n\tSettingSource,\n\tThinkingConfig,\n} from \"@anthropic-ai/claude-agent-sdk\";\nimport { createSdkMcpServer, getSessionMessages, query } from \"@anthropic-ai/claude-agent-sdk\";\nimport type { Base64ImageSource, ContentBlockParam } from \"@anthropic-ai/sdk/resources\";\n\nexport type {\n\tBase64ImageSource,\n\tContentBlockParam,\n\tEffortLevel,\n\tOptions,\n\tSDKMessage,\n\tSDKUserMessage,\n\tSessionMessage,\n\tSettingSource,\n\tThinkingConfig,\n};\n\nexport type SdkQueryInput = Parameters<typeof query>[0];\nexport type SdkQueryHandle = AsyncIterable<SDKMessage> & {\n\tinterrupt(): Promise<unknown>;\n\tsetModel?: (model?: string) => Promise<void>;\n\tclose(): void;\n\tinitializationResult?: () => Promise<unknown>;\n};\nexport type SdkQuery = (input: SdkQueryInput) => SdkQueryHandle;\n\nexport type SdkBoundary = {\n\tquery: SdkQuery;\n\tcreateSdkMcpServer: typeof createSdkMcpServer;\n\tgetSessionMessages: typeof getSessionMessages;\n};\n\nconst defaultSdkBoundary: SdkBoundary = { query, createSdkMcpServer, getSessionMessages };\nlet activeSdkBoundary = defaultSdkBoundary;\n\nexport function getSdkBoundary(): SdkBoundary {\n\treturn activeSdkBoundary;\n}\n\nexport function overrideSdkBoundary(override: Partial<SdkBoundary>): void {\n\tactiveSdkBoundary = { ...defaultSdkBoundary, ...override };\n}\n\nexport function resetSdkBoundary(): void {\n\tactiveSdkBoundary = defaultSdkBoundary;\n}\n\nexport default defaultSdkBoundary;\n"]}
1
+ {"version":3,"file":"sdk-boundary.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AA8B5D;;;;;;;;GAQG;AACH,SAAS,gBAAgB,CAAC,MAAyB;IAClD,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;IACnC,IAAI,CAAC,GAAG,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACd,8CAA8C,MAAM,KAAK;YACxD,gFAAgF,CACjF,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,MAAM,kBAAkB,GAAgB;IACvC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IACxD,kBAAkB,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC;IACnG,kBAAkB,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,CAChD,CAAC,MAAM,kBAAkB,EAAE,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC;CACpE,CAAC;AACF,IAAI,iBAAiB,GAAG,kBAAkB,CAAC;AAE3C,MAAM,UAAU,cAAc;IAC7B,OAAO,iBAAiB,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAA8B;IACjE,iBAAiB,GAAG,EAAE,GAAG,kBAAkB,EAAE,GAAG,QAAQ,EAAE,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC/B,iBAAiB,GAAG,kBAAkB,CAAC;AACxC,CAAC;AAED,eAAe,kBAAkB,CAAC","sourcesContent":["import type {\n\tEffortLevel,\n\tOptions,\n\tSDKMessage,\n\tSDKUserMessage,\n\tSessionMessage,\n\tSettingSource,\n\tThinkingConfig,\n} from \"@anthropic-ai/claude-agent-sdk\";\nimport type { Base64ImageSource, ContentBlockParam } from \"@anthropic-ai/sdk/resources\";\nimport { loadClaudeAgentSdk, loadedClaudeAgentSdk } from \"./sdk-boundary.lazy.ts\";\n\nexport { loadClaudeAgentSdk } from \"./sdk-boundary.lazy.ts\";\nexport type {\n\tBase64ImageSource,\n\tContentBlockParam,\n\tEffortLevel,\n\tOptions,\n\tSDKMessage,\n\tSDKUserMessage,\n\tSessionMessage,\n\tSettingSource,\n\tThinkingConfig,\n};\n\ntype SdkModule = Awaited<ReturnType<typeof loadClaudeAgentSdk>>;\n\nexport type SdkQueryInput = Parameters<SdkModule[\"query\"]>[0];\nexport type SdkQueryHandle = AsyncIterable<SDKMessage> & {\n\tinterrupt(): Promise<unknown>;\n\tsetModel?: (model?: string) => Promise<void>;\n\tclose(): void;\n\tinitializationResult?: () => Promise<unknown>;\n};\nexport type SdkQuery = (input: SdkQueryInput) => SdkQueryHandle;\n\nexport type SdkBoundary = {\n\tquery: SdkQuery;\n\tcreateSdkMcpServer: SdkModule[\"createSdkMcpServer\"];\n\tgetSessionMessages: SdkModule[\"getSessionMessages\"];\n};\n\n/**\n * Reads the deferred SDK module for a synchronous boundary member.\n *\n * `query` and `createSdkMcpServer` are synchronous SDK functions, so the\n * default boundary cannot await here. Every caller reaches these through an\n * async entry point that awaits `loadClaudeAgentSdk()` first, which makes an\n * unloaded module a wiring defect rather than a runtime condition — and this\n * names it at the exact call that skipped the preload.\n */\nfunction requireLoadedSdk(member: keyof SdkBoundary): SdkModule {\n\tconst sdk = loadedClaudeAgentSdk();\n\tif (!sdk) {\n\t\tthrow new Error(\n\t\t\t`Claude Agent SDK was not preloaded before '${member}'. ` +\n\t\t\t\t`Await loadClaudeAgentSdk() on the entry path before reaching the SDK boundary.`,\n\t\t);\n\t}\n\treturn sdk;\n}\n\nconst defaultSdkBoundary: SdkBoundary = {\n\tquery: (input) => requireLoadedSdk(\"query\").query(input),\n\tcreateSdkMcpServer: (options) => requireLoadedSdk(\"createSdkMcpServer\").createSdkMcpServer(options),\n\tgetSessionMessages: async (sessionId, options) =>\n\t\t(await loadClaudeAgentSdk()).getSessionMessages(sessionId, options),\n};\nlet activeSdkBoundary = defaultSdkBoundary;\n\nexport function getSdkBoundary(): SdkBoundary {\n\treturn activeSdkBoundary;\n}\n\nexport function overrideSdkBoundary(override: Partial<SdkBoundary>): void {\n\tactiveSdkBoundary = { ...defaultSdkBoundary, ...override };\n}\n\nexport function resetSdkBoundary(): void {\n\tactiveSdkBoundary = defaultSdkBoundary;\n}\n\nexport default defaultSdkBoundary;\n"]}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Lazy boundary for the Claude Agent SDK runtime module.
3
+ *
4
+ * `@anthropic-ai/claude-agent-sdk`'s entrypoint is a ~1.2 MB single-file
5
+ * bundle that every CLI start parses and evaluates today, even though only the
6
+ * claude-sdk-oauth streaming lane ever calls into it. This module owns the one
7
+ * deferred import; `sdk-boundary.ts` keeps the synchronous `getSdkBoundary()`
8
+ * surface its callers depend on and simply refuses to hand out SDK functions
9
+ * before {@link loadClaudeAgentSdk} has resolved.
10
+ *
11
+ * That split is what keeps the deferral honest: the SDK's `query` and
12
+ * `createSdkMcpServer` are synchronous, so their call sites cannot await. Every
13
+ * one of them is reached from an async entry point that awaits this loader
14
+ * first (the `streamClaudeSdkOauth` async body, `reattachSession`,
15
+ * `verifyRestoredTranscript`), so the module is always resident by the time a
16
+ * synchronous member is read — and a future call site that forgets to preload
17
+ * fails loudly and locally instead of silently regressing startup.
18
+ *
19
+ * Follows the repository's documented lazy-boundary pattern
20
+ * (`packages/ai/src/api/*.lazy.ts`); `test/startup-import-graph.test.ts` fails
21
+ * if a static edge to the SDK reappears.
22
+ */
23
+ import type { createSdkMcpServer, getSessionMessages, query } from "@anthropic-ai/claude-agent-sdk";
24
+ export type ClaudeAgentSdkModule = {
25
+ query: typeof query;
26
+ createSdkMcpServer: typeof createSdkMcpServer;
27
+ getSessionMessages: typeof getSessionMessages;
28
+ };
29
+ /**
30
+ * Loads the SDK once and caches it. Concurrent callers share the in-flight
31
+ * promise; a failed load is not cached, so a later attempt can retry.
32
+ */
33
+ export declare function loadClaudeAgentSdk(): Promise<ClaudeAgentSdkModule>;
34
+ /**
35
+ * The loaded SDK, or `undefined` when nothing has awaited
36
+ * {@link loadClaudeAgentSdk} yet.
37
+ */
38
+ export declare function loadedClaudeAgentSdk(): ClaudeAgentSdkModule | undefined;
39
+ //# sourceMappingURL=sdk-boundary.lazy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-boundary.lazy.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.lazy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AAEpG,MAAM,MAAM,oBAAoB,GAAG;IAClC,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;CAC9C,CAAC;AAKF;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAelE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,oBAAoB,GAAG,SAAS,CAEvE"}
@@ -0,0 +1,31 @@
1
+ let loaded;
2
+ let loading;
3
+ /**
4
+ * Loads the SDK once and caches it. Concurrent callers share the in-flight
5
+ * promise; a failed load is not cached, so a later attempt can retry.
6
+ */
7
+ export function loadClaudeAgentSdk() {
8
+ if (loaded)
9
+ return Promise.resolve(loaded);
10
+ loading ??= import("@anthropic-ai/claude-agent-sdk")
11
+ .then((module) => {
12
+ loaded = {
13
+ query: module.query,
14
+ createSdkMcpServer: module.createSdkMcpServer,
15
+ getSessionMessages: module.getSessionMessages,
16
+ };
17
+ return loaded;
18
+ })
19
+ .finally(() => {
20
+ loading = undefined;
21
+ });
22
+ return loading;
23
+ }
24
+ /**
25
+ * The loaded SDK, or `undefined` when nothing has awaited
26
+ * {@link loadClaudeAgentSdk} yet.
27
+ */
28
+ export function loadedClaudeAgentSdk() {
29
+ return loaded;
30
+ }
31
+ //# sourceMappingURL=sdk-boundary.lazy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-boundary.lazy.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.lazy.ts"],"names":[],"mappings":"AA8BA,IAAI,MAAwC,CAAC;AAC7C,IAAI,OAAkD,CAAC;AAEvD;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IACjC,IAAI,MAAM;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,OAAO,KAAK,MAAM,CAAC,gCAAgC,CAAC;SAClD,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;QAChB,MAAM,GAAG;YACR,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;YAC7C,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;SAC7C,CAAC;QACF,OAAO,MAAM,CAAC;IACf,CAAC,CAAC;SACD,OAAO,CAAC,GAAG,EAAE;QACb,OAAO,GAAG,SAAS,CAAC;IACrB,CAAC,CAAC,CAAC;IACJ,OAAO,OAAO,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IACnC,OAAO,MAAM,CAAC;AACf,CAAC","sourcesContent":["/**\n * Lazy boundary for the Claude Agent SDK runtime module.\n *\n * `@anthropic-ai/claude-agent-sdk`'s entrypoint is a ~1.2 MB single-file\n * bundle that every CLI start parses and evaluates today, even though only the\n * claude-sdk-oauth streaming lane ever calls into it. This module owns the one\n * deferred import; `sdk-boundary.ts` keeps the synchronous `getSdkBoundary()`\n * surface its callers depend on and simply refuses to hand out SDK functions\n * before {@link loadClaudeAgentSdk} has resolved.\n *\n * That split is what keeps the deferral honest: the SDK's `query` and\n * `createSdkMcpServer` are synchronous, so their call sites cannot await. Every\n * one of them is reached from an async entry point that awaits this loader\n * first (the `streamClaudeSdkOauth` async body, `reattachSession`,\n * `verifyRestoredTranscript`), so the module is always resident by the time a\n * synchronous member is read — and a future call site that forgets to preload\n * fails loudly and locally instead of silently regressing startup.\n *\n * Follows the repository's documented lazy-boundary pattern\n * (`packages/ai/src/api/*.lazy.ts`); `test/startup-import-graph.test.ts` fails\n * if a static edge to the SDK reappears.\n */\nimport type { createSdkMcpServer, getSessionMessages, query } from \"@anthropic-ai/claude-agent-sdk\";\n\nexport type ClaudeAgentSdkModule = {\n\tquery: typeof query;\n\tcreateSdkMcpServer: typeof createSdkMcpServer;\n\tgetSessionMessages: typeof getSessionMessages;\n};\n\nlet loaded: ClaudeAgentSdkModule | undefined;\nlet loading: Promise<ClaudeAgentSdkModule> | undefined;\n\n/**\n * Loads the SDK once and caches it. Concurrent callers share the in-flight\n * promise; a failed load is not cached, so a later attempt can retry.\n */\nexport function loadClaudeAgentSdk(): Promise<ClaudeAgentSdkModule> {\n\tif (loaded) return Promise.resolve(loaded);\n\tloading ??= import(\"@anthropic-ai/claude-agent-sdk\")\n\t\t.then((module) => {\n\t\t\tloaded = {\n\t\t\t\tquery: module.query,\n\t\t\t\tcreateSdkMcpServer: module.createSdkMcpServer,\n\t\t\t\tgetSessionMessages: module.getSessionMessages,\n\t\t\t};\n\t\t\treturn loaded;\n\t\t})\n\t\t.finally(() => {\n\t\t\tloading = undefined;\n\t\t});\n\treturn loading;\n}\n\n/**\n * The loaded SDK, or `undefined` when nothing has awaited\n * {@link loadClaudeAgentSdk} yet.\n */\nexport function loadedClaudeAgentSdk(): ClaudeAgentSdkModule | undefined {\n\treturn loaded;\n}\n"]}
@@ -22,6 +22,8 @@ export type ContinuityBindingSnapshot = {
22
22
  modelId: string;
23
23
  systemPromptHash: string;
24
24
  toolsetHash: string;
25
+ /** Sent-stream digest of a turn that was pushed but never answered (retry checkpoint). */
26
+ unansweredTurnDigest?: string;
25
27
  };
26
28
  export type ContinuityDecisionInput = {
27
29
  entry: ContinuityEntrySnapshot | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"session-continuity.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-continuity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAGnE,MAAM,MAAM,uBAAuB,GAAG;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACrC,KAAK,EAAE,uBAAuB,GAAG,SAAS,CAAC;IAC3C,OAAO,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAC/C,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC3B;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAClF;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAC9F;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAyFjD,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,SAAS,GAAG,YAAY,CAAC;AAEpE,MAAM,MAAM,uBAAuB,GAAG;IACrC,QAAQ,EAAE,YAAY,CAAC;IACvB,2BAA2B,EAAE,OAAO,CAAC;IACrC,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACrF,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,uBAAuB,GAAG,kBAAkB,CAmB3F;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,uBAAuB,GAAG,kBAAkB,CA8BzF"}
1
+ {"version":3,"file":"session-continuity.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-continuity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAGnE,MAAM,MAAM,uBAAuB,GAAG;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACrC,KAAK,EAAE,uBAAuB,GAAG,SAAS,CAAC;IAC3C,OAAO,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAC/C,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC3B;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAClF;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAC9F;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,CAAC;AA4HjD,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,SAAS,GAAG,YAAY,CAAC;AAEpE,MAAM,MAAM,uBAAuB,GAAG;IACrC,QAAQ,EAAE,YAAY,CAAC;IACvB,2BAA2B,EAAE,OAAO,CAAC;IACrC,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACrF,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,uBAAuB,GAAG,kBAAkB,CAmB3F;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,uBAAuB,GAAG,kBAAkB,CA8BzF"}
@@ -46,12 +46,47 @@ function identityDrift(input, entry) {
46
46
  return "options_changed";
47
47
  return null;
48
48
  }
49
+ /**
50
+ * Same-turn retry after a stream-start timeout: the abandoned attempt already
51
+ * appended its user message to the lineage, so re-attaching would append it a
52
+ * SECOND time and re-bill the whole conversation. Forking at the pre-turn
53
+ * assistant boundary rewinds past the un-answered message, so the retry's
54
+ * request byte-layout matches the failed attempt's (prefix cache read).
55
+ * Requires the FULL current turn to hash-match the checkpoint, so a different
56
+ * turn falls through to the ordinary branches below.
57
+ */
58
+ function retryCheckpointDecision(input, binding) {
59
+ if (binding.unansweredTurnDigest === undefined)
60
+ return undefined;
61
+ if (sentHashPrefixDigest(input.currentHashes, input.currentHashes.length) !== binding.unansweredTurnDigest) {
62
+ return undefined;
63
+ }
64
+ if (input.currentHashes.length < binding.sentCount)
65
+ return undefined;
66
+ const prefixMatches = binding.sentPrefixHash !== undefined
67
+ ? sentHashPrefixDigest(input.currentHashes, binding.sentCount) === binding.sentPrefixHash
68
+ : commonPrefixLength(binding.sentHashes, input.currentHashes) === binding.sentCount;
69
+ if (!prefixMatches)
70
+ return undefined;
71
+ if (!binding.lastAssistantUuid)
72
+ return { kind: "flatten", reason: "timeout_retry" };
73
+ return {
74
+ kind: "fork",
75
+ sdkSessionId: binding.sdkSessionId,
76
+ atUuid: binding.lastAssistantUuid,
77
+ from: binding.sentCount,
78
+ reason: "timeout_retry",
79
+ };
80
+ }
49
81
  function decideFromBinding(input, binding) {
50
82
  if (!input.transcriptAvailable)
51
83
  return { kind: "flatten", reason: "transcript_missing" };
52
84
  const drift = identityDrift(input, binding);
53
85
  if (drift)
54
86
  return { kind: "flatten", reason: drift };
87
+ const retry = retryCheckpointDecision(input, binding);
88
+ if (retry)
89
+ return retry;
55
90
  if (binding.sentPrefixHash !== undefined) {
56
91
  const prefixMatches = input.currentHashes.length >= binding.sentCount &&
57
92
  sentHashPrefixDigest(input.currentHashes, binding.sentCount) === binding.sentPrefixHash;
@@ -1 +1 @@
1
- {"version":3,"file":"session-continuity.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-continuity.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContinuityReason } from \"./session-observability.ts\";\nimport { sentHashPrefixDigest } from \"./session-sync.ts\";\n\nexport type ContinuityEntrySnapshot = {\n\tsdkSessionId: string;\n\taccountName: string;\n\tmodelId: string;\n\tsystemPromptHash: string;\n\ttoolsetHash: string;\n\tsentCount: number;\n\tsentHashes: readonly string[];\n\tlastAssistantUuid: string | null;\n\tassistantUuidByIndex: ReadonlyMap<number, string>;\n\tpendingForkReason: string | null;\n\ttaintedReason?: string | null;\n};\n\nexport type ContinuityBindingSnapshot = {\n\tsdkSessionId: string;\n\tsentCount: number;\n\tsentHashes: readonly string[];\n\tsentPrefixHash?: string;\n\tlastAssistantUuid: string | null;\n\taccountName: string;\n\tmodelId: string;\n\tsystemPromptHash: string;\n\ttoolsetHash: string;\n};\n\nexport type ContinuityDecisionInput = {\n\tentry: ContinuityEntrySnapshot | undefined;\n\tbinding: ContinuityBindingSnapshot | undefined;\n\tcurrentHashes: readonly string[];\n\taccountName: string;\n\tmodelId: string;\n\tfingerprint: { systemPromptHash: string; toolsetHash: string };\n\ttranscriptAvailable: boolean;\n\tidleExpired?: boolean;\n};\n\nexport type ContinuityDecision =\n\t| { kind: \"bootstrap\" }\n\t| { kind: \"delta\"; from: number }\n\t| { kind: \"reattach\"; sdkSessionId: string; from: number; reason: ContinuityReason }\n\t| { kind: \"fork\"; sdkSessionId: string; atUuid: string; from: number; reason: ContinuityReason }\n\t| { kind: \"flatten\"; reason: ContinuityReason };\n\nconst PENDING_FORK_REASONS: Readonly<Record<string, ContinuityReason>> = {\n\tassistant_rewritten: \"assistant_rewritten\",\n\tcompaction: \"tainted_compaction\",\n};\n\nfunction commonPrefixLength(left: readonly string[], right: readonly string[]): number {\n\tconst limit = Math.min(left.length, right.length);\n\tlet index = 0;\n\twhile (index < limit && left[index] === right[index]) index += 1;\n\treturn index;\n}\n\n/**\n * The fork point is the last assistant boundary STRICTLY BEFORE the divergence:\n * forking at the diverged turn itself would carry the stale assistant into the new\n * branch and leave nothing to re-send.\n */\nfunction boundaryBefore(entry: ContinuityEntrySnapshot, count: number): { index: number; uuid: string } | undefined {\n\tfor (let candidate = count - 1; candidate >= 1; candidate -= 1) {\n\t\tconst uuid = entry.assistantUuidByIndex.get(candidate);\n\t\tif (uuid) return { index: candidate, uuid };\n\t}\n\treturn undefined;\n}\n\nfunction forkOrFlatten(\n\tentry: ContinuityEntrySnapshot,\n\tdivergesAt: number,\n\treason: ContinuityReason,\n): ContinuityDecision {\n\tconst boundary = boundaryBefore(entry, divergesAt);\n\tif (!boundary) return { kind: \"flatten\", reason };\n\treturn {\n\t\tkind: \"fork\",\n\t\tsdkSessionId: entry.sdkSessionId,\n\t\tatUuid: boundary.uuid,\n\t\tfrom: boundary.index,\n\t\treason,\n\t};\n}\n\nfunction identityDrift(\n\tinput: ContinuityDecisionInput,\n\tentry: Pick<ContinuityEntrySnapshot, \"accountName\" | \"modelId\" | \"systemPromptHash\" | \"toolsetHash\">,\n): ContinuityReason | null {\n\tif (entry.accountName !== input.accountName) return \"account_changed\";\n\tif (entry.modelId !== input.modelId) return \"model_changed\";\n\tif (entry.systemPromptHash !== input.fingerprint.systemPromptHash) return \"options_changed\";\n\tif (entry.toolsetHash !== input.fingerprint.toolsetHash) return \"options_changed\";\n\treturn null;\n}\n\nfunction decideFromBinding(input: ContinuityDecisionInput, binding: ContinuityBindingSnapshot): ContinuityDecision {\n\tif (!input.transcriptAvailable) return { kind: \"flatten\", reason: \"transcript_missing\" };\n\tconst drift = identityDrift(input, binding);\n\tif (drift) return { kind: \"flatten\", reason: drift };\n\tif (binding.sentPrefixHash !== undefined) {\n\t\tconst prefixMatches =\n\t\t\tinput.currentHashes.length >= binding.sentCount &&\n\t\t\tsentHashPrefixDigest(input.currentHashes, binding.sentCount) === binding.sentPrefixHash;\n\t\tif (prefixMatches) {\n\t\t\treturn {\n\t\t\t\tkind: \"reattach\",\n\t\t\t\tsdkSessionId: binding.sdkSessionId,\n\t\t\t\tfrom: binding.sentCount,\n\t\t\t\treason: \"registry_miss\",\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tkind: \"flatten\",\n\t\t\treason: input.currentHashes.length < binding.sentCount ? \"history_rolled_back\" : \"sent_stream_diverged\",\n\t\t};\n\t}\n\tconst shared = commonPrefixLength(binding.sentHashes, input.currentHashes);\n\tif (shared === binding.sentCount) {\n\t\treturn { kind: \"reattach\", sdkSessionId: binding.sdkSessionId, from: binding.sentCount, reason: \"registry_miss\" };\n\t}\n\tif (!binding.lastAssistantUuid) return { kind: \"flatten\", reason: \"registry_miss\" };\n\treturn {\n\t\tkind: \"fork\",\n\t\tsdkSessionId: binding.sdkSessionId,\n\t\tatUuid: binding.lastAssistantUuid,\n\t\tfrom: shared,\n\t\treason: shared < binding.sentCount ? \"history_rolled_back\" : \"sent_stream_diverged\",\n\t};\n}\n\nexport type FailoverLane = \"oauth-slots\" | \"ambient\" | \"config-dir\";\n\nexport type FailoverContinuityInput = {\n\tauthLane: FailoverLane;\n\tcrossAccountResumeSupported: boolean;\n\tentry: { sdkSessionId: string; sentCount: number; lastAssistantUuid: string | null };\n};\n\n/**\n * The config-dir lane keeps each account's credentials inside its own\n * CLAUDE_CONFIG_DIR, and no official SDK API moves a transcript across roots, so\n * its failover is the one declared residual that must still flatten.\n */\nexport function decideFailoverContinuity(input: FailoverContinuityInput): ContinuityDecision {\n\tconst { entry } = input;\n\tif (input.authLane === \"config-dir\") return { kind: \"flatten\", reason: \"cross_root_unsupported\" };\n\tif (input.crossAccountResumeSupported) {\n\t\treturn {\n\t\t\tkind: \"reattach\",\n\t\t\tsdkSessionId: entry.sdkSessionId,\n\t\t\tfrom: entry.sentCount,\n\t\t\treason: \"account_changed\",\n\t\t};\n\t}\n\tif (!entry.lastAssistantUuid) return { kind: \"flatten\", reason: \"branch_boundary_unavailable\" };\n\treturn {\n\t\tkind: \"fork\",\n\t\tsdkSessionId: entry.sdkSessionId,\n\t\tatUuid: entry.lastAssistantUuid,\n\t\tfrom: entry.sentCount,\n\t\treason: \"account_changed\",\n\t};\n}\n\n/**\n * Resume-first: a live session is never abandoned for a flattened re-send. Only a\n * missing transcript or an unrecoverable boundary reaches `flatten`; every other\n * divergence resolves to `fork` (same lineage, new branch) or `reattach` (same\n * session, new query).\n */\nexport function decideNativeContinuity(input: ContinuityDecisionInput): ContinuityDecision {\n\tconst { entry, binding } = input;\n\tif (!entry) {\n\t\tif (!binding) return { kind: \"bootstrap\" };\n\t\treturn decideFromBinding(input, binding);\n\t}\n\n\tconst divergence = entry.pendingForkReason ?? entry.taintedReason;\n\tif (divergence) {\n\t\treturn forkOrFlatten(entry, entry.sentCount, PENDING_FORK_REASONS[divergence] ?? \"other\");\n\t}\n\n\tconst shared = commonPrefixLength(entry.sentHashes, input.currentHashes);\n\tif (shared < entry.sentCount) {\n\t\tconst rolledBack = input.currentHashes.length < entry.sentCount && shared === input.currentHashes.length;\n\t\treturn rolledBack\n\t\t\t? forkOrFlatten(entry, input.currentHashes.length, \"history_rolled_back\")\n\t\t\t: forkOrFlatten(entry, shared + 1, \"sent_stream_diverged\");\n\t}\n\n\tif (input.idleExpired) {\n\t\treturn { kind: \"reattach\", sdkSessionId: entry.sdkSessionId, from: entry.sentCount, reason: \"idle_ttl\" };\n\t}\n\n\tconst drift = identityDrift(input, entry);\n\tif (drift) {\n\t\treturn { kind: \"reattach\", sdkSessionId: entry.sdkSessionId, from: entry.sentCount, reason: drift };\n\t}\n\n\treturn { kind: \"delta\", from: entry.sentCount };\n}\n"]}
1
+ {"version":3,"file":"session-continuity.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-continuity.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ContinuityReason } from \"./session-observability.ts\";\nimport { sentHashPrefixDigest } from \"./session-sync.ts\";\n\nexport type ContinuityEntrySnapshot = {\n\tsdkSessionId: string;\n\taccountName: string;\n\tmodelId: string;\n\tsystemPromptHash: string;\n\ttoolsetHash: string;\n\tsentCount: number;\n\tsentHashes: readonly string[];\n\tlastAssistantUuid: string | null;\n\tassistantUuidByIndex: ReadonlyMap<number, string>;\n\tpendingForkReason: string | null;\n\ttaintedReason?: string | null;\n};\n\nexport type ContinuityBindingSnapshot = {\n\tsdkSessionId: string;\n\tsentCount: number;\n\tsentHashes: readonly string[];\n\tsentPrefixHash?: string;\n\tlastAssistantUuid: string | null;\n\taccountName: string;\n\tmodelId: string;\n\tsystemPromptHash: string;\n\ttoolsetHash: string;\n\t/** Sent-stream digest of a turn that was pushed but never answered (retry checkpoint). */\n\tunansweredTurnDigest?: string;\n};\n\nexport type ContinuityDecisionInput = {\n\tentry: ContinuityEntrySnapshot | undefined;\n\tbinding: ContinuityBindingSnapshot | undefined;\n\tcurrentHashes: readonly string[];\n\taccountName: string;\n\tmodelId: string;\n\tfingerprint: { systemPromptHash: string; toolsetHash: string };\n\ttranscriptAvailable: boolean;\n\tidleExpired?: boolean;\n};\n\nexport type ContinuityDecision =\n\t| { kind: \"bootstrap\" }\n\t| { kind: \"delta\"; from: number }\n\t| { kind: \"reattach\"; sdkSessionId: string; from: number; reason: ContinuityReason }\n\t| { kind: \"fork\"; sdkSessionId: string; atUuid: string; from: number; reason: ContinuityReason }\n\t| { kind: \"flatten\"; reason: ContinuityReason };\n\nconst PENDING_FORK_REASONS: Readonly<Record<string, ContinuityReason>> = {\n\tassistant_rewritten: \"assistant_rewritten\",\n\tcompaction: \"tainted_compaction\",\n};\n\nfunction commonPrefixLength(left: readonly string[], right: readonly string[]): number {\n\tconst limit = Math.min(left.length, right.length);\n\tlet index = 0;\n\twhile (index < limit && left[index] === right[index]) index += 1;\n\treturn index;\n}\n\n/**\n * The fork point is the last assistant boundary STRICTLY BEFORE the divergence:\n * forking at the diverged turn itself would carry the stale assistant into the new\n * branch and leave nothing to re-send.\n */\nfunction boundaryBefore(entry: ContinuityEntrySnapshot, count: number): { index: number; uuid: string } | undefined {\n\tfor (let candidate = count - 1; candidate >= 1; candidate -= 1) {\n\t\tconst uuid = entry.assistantUuidByIndex.get(candidate);\n\t\tif (uuid) return { index: candidate, uuid };\n\t}\n\treturn undefined;\n}\n\nfunction forkOrFlatten(\n\tentry: ContinuityEntrySnapshot,\n\tdivergesAt: number,\n\treason: ContinuityReason,\n): ContinuityDecision {\n\tconst boundary = boundaryBefore(entry, divergesAt);\n\tif (!boundary) return { kind: \"flatten\", reason };\n\treturn {\n\t\tkind: \"fork\",\n\t\tsdkSessionId: entry.sdkSessionId,\n\t\tatUuid: boundary.uuid,\n\t\tfrom: boundary.index,\n\t\treason,\n\t};\n}\n\nfunction identityDrift(\n\tinput: ContinuityDecisionInput,\n\tentry: Pick<ContinuityEntrySnapshot, \"accountName\" | \"modelId\" | \"systemPromptHash\" | \"toolsetHash\">,\n): ContinuityReason | null {\n\tif (entry.accountName !== input.accountName) return \"account_changed\";\n\tif (entry.modelId !== input.modelId) return \"model_changed\";\n\tif (entry.systemPromptHash !== input.fingerprint.systemPromptHash) return \"options_changed\";\n\tif (entry.toolsetHash !== input.fingerprint.toolsetHash) return \"options_changed\";\n\treturn null;\n}\n\n/**\n * Same-turn retry after a stream-start timeout: the abandoned attempt already\n * appended its user message to the lineage, so re-attaching would append it a\n * SECOND time and re-bill the whole conversation. Forking at the pre-turn\n * assistant boundary rewinds past the un-answered message, so the retry's\n * request byte-layout matches the failed attempt's (prefix cache read).\n * Requires the FULL current turn to hash-match the checkpoint, so a different\n * turn falls through to the ordinary branches below.\n */\nfunction retryCheckpointDecision(\n\tinput: ContinuityDecisionInput,\n\tbinding: ContinuityBindingSnapshot,\n): ContinuityDecision | undefined {\n\tif (binding.unansweredTurnDigest === undefined) return undefined;\n\tif (sentHashPrefixDigest(input.currentHashes, input.currentHashes.length) !== binding.unansweredTurnDigest) {\n\t\treturn undefined;\n\t}\n\tif (input.currentHashes.length < binding.sentCount) return undefined;\n\tconst prefixMatches =\n\t\tbinding.sentPrefixHash !== undefined\n\t\t\t? sentHashPrefixDigest(input.currentHashes, binding.sentCount) === binding.sentPrefixHash\n\t\t\t: commonPrefixLength(binding.sentHashes, input.currentHashes) === binding.sentCount;\n\tif (!prefixMatches) return undefined;\n\tif (!binding.lastAssistantUuid) return { kind: \"flatten\", reason: \"timeout_retry\" };\n\treturn {\n\t\tkind: \"fork\",\n\t\tsdkSessionId: binding.sdkSessionId,\n\t\tatUuid: binding.lastAssistantUuid,\n\t\tfrom: binding.sentCount,\n\t\treason: \"timeout_retry\",\n\t};\n}\n\nfunction decideFromBinding(input: ContinuityDecisionInput, binding: ContinuityBindingSnapshot): ContinuityDecision {\n\tif (!input.transcriptAvailable) return { kind: \"flatten\", reason: \"transcript_missing\" };\n\tconst drift = identityDrift(input, binding);\n\tif (drift) return { kind: \"flatten\", reason: drift };\n\tconst retry = retryCheckpointDecision(input, binding);\n\tif (retry) return retry;\n\tif (binding.sentPrefixHash !== undefined) {\n\t\tconst prefixMatches =\n\t\t\tinput.currentHashes.length >= binding.sentCount &&\n\t\t\tsentHashPrefixDigest(input.currentHashes, binding.sentCount) === binding.sentPrefixHash;\n\t\tif (prefixMatches) {\n\t\t\treturn {\n\t\t\t\tkind: \"reattach\",\n\t\t\t\tsdkSessionId: binding.sdkSessionId,\n\t\t\t\tfrom: binding.sentCount,\n\t\t\t\treason: \"registry_miss\",\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tkind: \"flatten\",\n\t\t\treason: input.currentHashes.length < binding.sentCount ? \"history_rolled_back\" : \"sent_stream_diverged\",\n\t\t};\n\t}\n\tconst shared = commonPrefixLength(binding.sentHashes, input.currentHashes);\n\tif (shared === binding.sentCount) {\n\t\treturn { kind: \"reattach\", sdkSessionId: binding.sdkSessionId, from: binding.sentCount, reason: \"registry_miss\" };\n\t}\n\tif (!binding.lastAssistantUuid) return { kind: \"flatten\", reason: \"registry_miss\" };\n\treturn {\n\t\tkind: \"fork\",\n\t\tsdkSessionId: binding.sdkSessionId,\n\t\tatUuid: binding.lastAssistantUuid,\n\t\tfrom: shared,\n\t\treason: shared < binding.sentCount ? \"history_rolled_back\" : \"sent_stream_diverged\",\n\t};\n}\n\nexport type FailoverLane = \"oauth-slots\" | \"ambient\" | \"config-dir\";\n\nexport type FailoverContinuityInput = {\n\tauthLane: FailoverLane;\n\tcrossAccountResumeSupported: boolean;\n\tentry: { sdkSessionId: string; sentCount: number; lastAssistantUuid: string | null };\n};\n\n/**\n * The config-dir lane keeps each account's credentials inside its own\n * CLAUDE_CONFIG_DIR, and no official SDK API moves a transcript across roots, so\n * its failover is the one declared residual that must still flatten.\n */\nexport function decideFailoverContinuity(input: FailoverContinuityInput): ContinuityDecision {\n\tconst { entry } = input;\n\tif (input.authLane === \"config-dir\") return { kind: \"flatten\", reason: \"cross_root_unsupported\" };\n\tif (input.crossAccountResumeSupported) {\n\t\treturn {\n\t\t\tkind: \"reattach\",\n\t\t\tsdkSessionId: entry.sdkSessionId,\n\t\t\tfrom: entry.sentCount,\n\t\t\treason: \"account_changed\",\n\t\t};\n\t}\n\tif (!entry.lastAssistantUuid) return { kind: \"flatten\", reason: \"branch_boundary_unavailable\" };\n\treturn {\n\t\tkind: \"fork\",\n\t\tsdkSessionId: entry.sdkSessionId,\n\t\tatUuid: entry.lastAssistantUuid,\n\t\tfrom: entry.sentCount,\n\t\treason: \"account_changed\",\n\t};\n}\n\n/**\n * Resume-first: a live session is never abandoned for a flattened re-send. Only a\n * missing transcript or an unrecoverable boundary reaches `flatten`; every other\n * divergence resolves to `fork` (same lineage, new branch) or `reattach` (same\n * session, new query).\n */\nexport function decideNativeContinuity(input: ContinuityDecisionInput): ContinuityDecision {\n\tconst { entry, binding } = input;\n\tif (!entry) {\n\t\tif (!binding) return { kind: \"bootstrap\" };\n\t\treturn decideFromBinding(input, binding);\n\t}\n\n\tconst divergence = entry.pendingForkReason ?? entry.taintedReason;\n\tif (divergence) {\n\t\treturn forkOrFlatten(entry, entry.sentCount, PENDING_FORK_REASONS[divergence] ?? \"other\");\n\t}\n\n\tconst shared = commonPrefixLength(entry.sentHashes, input.currentHashes);\n\tif (shared < entry.sentCount) {\n\t\tconst rolledBack = input.currentHashes.length < entry.sentCount && shared === input.currentHashes.length;\n\t\treturn rolledBack\n\t\t\t? forkOrFlatten(entry, input.currentHashes.length, \"history_rolled_back\")\n\t\t\t: forkOrFlatten(entry, shared + 1, \"sent_stream_diverged\");\n\t}\n\n\tif (input.idleExpired) {\n\t\treturn { kind: \"reattach\", sdkSessionId: entry.sdkSessionId, from: entry.sentCount, reason: \"idle_ttl\" };\n\t}\n\n\tconst drift = identityDrift(input, entry);\n\tif (drift) {\n\t\treturn { kind: \"reattach\", sdkSessionId: entry.sdkSessionId, from: entry.sentCount, reason: drift };\n\t}\n\n\treturn { kind: \"delta\", from: entry.sentCount };\n}\n"]}
@@ -9,7 +9,7 @@
9
9
  */
10
10
  export type ContinuityKind = "bootstrap" | "delta" | "reattach" | "fork" | "flatten" | "disabled";
11
11
  /** Fixed vocabulary — arbitrary error text never reaches an observation. */
12
- export type ContinuityReason = "prefix_matched" | "registry_miss" | "idle_ttl" | "capacity" | "model_selected" | "thinking_level_selected" | "bound_account_token_expiring" | "account_changed" | "model_changed" | "toolset_changed" | "system_prompt_changed" | "assistant_stream_diverged" | "sent_stream_diverged" | "options_changed" | "history_rolled_back" | "assistant_rewritten" | "transcript_missing" | "cross_root_unsupported" | "branch_diverged" | "branch_boundary_unavailable" | "branch_resume" | "tainted_compaction" | "tainted_fork" | "tainted_abort" | "tainted_assistant_provenance_unverified" | "resume_initialization_failed" | "resume_initialization_aborted" | "resume_mode_off" | "query_failed" | "turn_attribution_failed" | "abort_timeout" | "extensions_removed" | "session_shutdown" | "other";
12
+ export type ContinuityReason = "prefix_matched" | "registry_miss" | "idle_ttl" | "capacity" | "model_selected" | "thinking_level_selected" | "bound_account_token_expiring" | "account_changed" | "model_changed" | "toolset_changed" | "system_prompt_changed" | "assistant_stream_diverged" | "sent_stream_diverged" | "options_changed" | "history_rolled_back" | "assistant_rewritten" | "transcript_missing" | "cross_root_unsupported" | "branch_diverged" | "branch_boundary_unavailable" | "branch_resume" | "tainted_compaction" | "tainted_fork" | "tainted_abort" | "tainted_assistant_provenance_unverified" | "resume_initialization_failed" | "resume_initialization_aborted" | "resume_mode_off" | "query_failed" | "turn_attribution_failed" | "abort_timeout" | "extensions_removed" | "session_shutdown" | "timeout_retry" | "other";
13
13
  export type ContinuityObservation = {
14
14
  kind: ContinuityKind;
15
15
  reason: ContinuityReason;
@@ -1 +1 @@
1
- {"version":3,"file":"session-observability.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-observability.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-observability.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-observability.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,7 @@ const SANITIZED_REASONS = new Set([
34
34
  "abort_timeout",
35
35
  "extensions_removed",
36
36
  "session_shutdown",
37
+ "timeout_retry",
37
38
  "other",
38
39
  ]);
39
40
  let cachedLogger;
@@ -1 +1 @@
1
- {"version":3,"file":"session-observability.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-observability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAsB,MAAM,yBAAyB,CAAC;AA+DlF,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAS;IACzC,gBAAgB;IAChB,eAAe;IACf,UAAU;IACV,UAAU;IACV,gBAAgB;IAChB,yBAAyB;IACzB,8BAA8B;IAC9B,iBAAiB;IACjB,eAAe;IACf,iBAAiB;IACjB,uBAAuB;IACvB,2BAA2B;IAC3B,iBAAiB;IACjB,qBAAqB;IACrB,qBAAqB;IACrB,oBAAoB;IACpB,wBAAwB;IACxB,sBAAsB;IACtB,iBAAiB;IACjB,6BAA6B;IAC7B,eAAe;IACf,oBAAoB;IACpB,cAAc;IACd,eAAe;IACf,yCAAyC;IACzC,8BAA8B;IAC9B,+BAA+B;IAC/B,iBAAiB;IACjB,cAAc;IACd,yBAAyB;IACzB,eAAe;IACf,oBAAoB;IACpB,kBAAkB;IAClB,OAAO;CACP,CAAC,CAAC;AAEH,IAAI,YAAuC,CAAC;AAE5C,SAAS,aAAa;IACrB,YAAY,KAAK,mBAAmB,CAAC,WAAW,EAAE,CAAC,CAAC;IACpD,OAAO,YAAY,CAAC;AACrB,CAAC;AAED,MAAM,eAAe,GAAoC;IACxD,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,GAAG,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC;CACvD,CAAC;AACF,IAAI,cAAc,GAAG,eAAe,CAAC;AAErC,MAAM,UAAU,uCAAuC,CAAC,QAAkD;IACzG,cAAc,GAAG,EAAE,GAAG,eAAe,EAAE,GAAG,QAAQ,EAAE,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,oCAAoC;IACnD,cAAc,GAAG,eAAe,CAAC;AAClC,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAAC,KAAc;IAC5C,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7F,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,IAAwB,CAAC;IACjE,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,OAAO,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,WAAW,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,IAAI,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,MAA0B,CAAC;IACtE,CAAC;IACD,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAChD,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7F,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,IAAwB,CAAC;IACjE,IAAI,qCAAqC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,eAAe,CAAC;IAC7E,IAAI,gGAAgG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjH,OAAO,yBAAyB,CAAC;IAClC,CAAC;IACD,IAAI,wDAAwD,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,cAAc,CAAC;IAC/F,OAAO,OAAO,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACrD,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACxC,IAAI,KAAK,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IACpC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC;AAC1D,CAAC;AAED;;;;GAIG;AACH,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAA4B,CAAC;AAC/D,8EAA8E;AAC9E,8EAA8E;AAC9E,8DAA8D;AAC9D,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAEtC,MAAM,UAAU,uBAAuB,CAAC,cAAsB,EAAE,MAAe;IAC9E,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACrC,oEAAoE;IACpE,kBAAkB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAC1C,IAAI,kBAAkB,CAAC,IAAI,IAAI,yBAAyB,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QACtD,IAAI,MAAM,KAAK,SAAS;YAAE,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;IACD,kBAAkB,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IAC9C,cAAc,CAAC,GAAG,CAAC,gCAAgC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,qBAAqB,CAAC,cAAsB;IAC3D,OAAO,kBAAkB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,cAAsB;IAC9D,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACrD,kBAAkB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAC1C,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAQ1C;IACA,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC;IACxF,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,aAAa,GAClB,KAAK,CAAC,MAAM,KAAK,eAAe,CAAC,CAAC,CAAC,wBAAwB,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/F,OAAO;YACN,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,aAAa,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACtG,aAAa,EAAE,KAAK,CAAC,aAAa;SAClC,CAAC;IACH,CAAC;IACD,qEAAqE;IACrE,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,KAAK,eAAe,CAAC,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5G,MAAM,MAAM,GAAG,QAAQ,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO;QACN,IAAI,EAAE,KAAK,CAAC,SAAS,IAAI,MAAM,KAAK,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;QAC7E,MAAM;QACN,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,GAAG,CAAC,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,GAAG,CAAC,KAAK,CAAC,mBAAmB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,KAAK,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtG,CAAC;AACH,CAAC;AAOD,oFAAoF;AACpF,MAAM,UAAU,uBAAuB,CACtC,WAAkC,EAClC,UAAyD,EACzD,UAAuB;IAEvB,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,OAAO;QACN,WAAW;QACX,IAAI,EAAE,GAAG,EAAE;YACV,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,UAAU,EAAE,EAAE,CAAC;YACf,yBAAyB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QACpD,CAAC;KACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yBAAyB,CACxC,WAAkC,EAClC,UAAyD;IAEzD,UAAU,EAAE,CAAC,WAAW,CAAC,CAAC;IAC1B,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACjC,cAAc,CAAC,GAAG,CAAC,qCAAqC,EAAE;QACzD,IAAI,EAAE,WAAW,CAAC,IAAI;QACtB,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,GAAG,CAAC,WAAW,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,aAAa,EAAE,CAAC;KACxF,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { getAgentDir } from \"../../../../config.ts\";\nimport { createSessionLogger, type SessionLogger } from \"../../../session-log.ts\";\n\n/**\n * Per-turn continuity observability for the claude-sdk-oauth lane.\n *\n * Emission semantics: a decision is STAGED per attempt at its final outcome\n * point and EMITTED only when the auth-lane retain/discard wrapper retains that\n * attempt (auth-attempt.ts). A discarded attempt emits nothing, so every\n * completed turn yields exactly one observation; a turn where every attempt\n * fails yields one terminal error observation instead.\n */\nexport type ContinuityKind = \"bootstrap\" | \"delta\" | \"reattach\" | \"fork\" | \"flatten\" | \"disabled\";\n\n/** Fixed vocabulary — arbitrary error text never reaches an observation. */\nexport type ContinuityReason =\n\t| \"prefix_matched\"\n\t| \"registry_miss\"\n\t| \"idle_ttl\"\n\t| \"capacity\"\n\t| \"model_selected\"\n\t| \"thinking_level_selected\"\n\t| \"bound_account_token_expiring\"\n\t| \"account_changed\"\n\t| \"model_changed\"\n\t| \"toolset_changed\"\n\t| \"system_prompt_changed\"\n\t| \"assistant_stream_diverged\"\n\t| \"sent_stream_diverged\"\n\t| \"options_changed\"\n\t| \"history_rolled_back\"\n\t| \"assistant_rewritten\"\n\t| \"transcript_missing\"\n\t| \"cross_root_unsupported\"\n\t| \"branch_diverged\"\n\t| \"branch_boundary_unavailable\"\n\t| \"branch_resume\"\n\t| \"tainted_compaction\"\n\t| \"tainted_fork\"\n\t| \"tainted_abort\"\n\t| \"tainted_assistant_provenance_unverified\"\n\t| \"resume_initialization_failed\"\n\t| \"resume_initialization_aborted\"\n\t| \"resume_mode_off\"\n\t| \"query_failed\"\n\t| \"turn_attribution_failed\"\n\t| \"abort_timeout\"\n\t| \"extensions_removed\"\n\t| \"session_shutdown\"\n\t| \"other\";\n\nexport type ContinuityObservation = {\n\tkind: ContinuityKind;\n\treason: ContinuityReason;\n\tdeltaMessages?: number;\n\tpayloadBytes?: number;\n\tcollapsedDirectives?: number;\n};\n\nexport type ContinuityObservabilityBoundary = {\n\temit: (observation: ContinuityObservation) => void;\n\tlog: (event: string, data: Record<string, unknown>) => void;\n};\n\nconst SANITIZED_REASONS = new Set<string>([\n\t\"prefix_matched\",\n\t\"registry_miss\",\n\t\"idle_ttl\",\n\t\"capacity\",\n\t\"model_selected\",\n\t\"thinking_level_selected\",\n\t\"bound_account_token_expiring\",\n\t\"account_changed\",\n\t\"model_changed\",\n\t\"toolset_changed\",\n\t\"system_prompt_changed\",\n\t\"assistant_stream_diverged\",\n\t\"options_changed\",\n\t\"history_rolled_back\",\n\t\"assistant_rewritten\",\n\t\"transcript_missing\",\n\t\"cross_root_unsupported\",\n\t\"sent_stream_diverged\",\n\t\"branch_diverged\",\n\t\"branch_boundary_unavailable\",\n\t\"branch_resume\",\n\t\"tainted_compaction\",\n\t\"tainted_fork\",\n\t\"tainted_abort\",\n\t\"tainted_assistant_provenance_unverified\",\n\t\"resume_initialization_failed\",\n\t\"resume_initialization_aborted\",\n\t\"resume_mode_off\",\n\t\"query_failed\",\n\t\"turn_attribution_failed\",\n\t\"abort_timeout\",\n\t\"extensions_removed\",\n\t\"session_shutdown\",\n\t\"other\",\n]);\n\nlet cachedLogger: SessionLogger | undefined;\n\nfunction sessionLogger(): SessionLogger {\n\tcachedLogger ??= createSessionLogger(getAgentDir());\n\treturn cachedLogger;\n}\n\nconst defaultBoundary: ContinuityObservabilityBoundary = {\n\temit: () => {},\n\tlog: (event, data) => sessionLogger().info(event, data),\n};\nlet activeBoundary = defaultBoundary;\n\nexport function overrideContinuityObservabilityBoundary(override: Partial<ContinuityObservabilityBoundary>): void {\n\tactiveBoundary = { ...defaultBoundary, ...override };\n}\n\nexport function resetContinuityObservabilityBoundary(): void {\n\tactiveBoundary = defaultBoundary;\n}\n\n/** Maps a fixed vocabulary member through, and anything else to a bucketed cause. */\nexport function sanitizeReason(value: unknown): ContinuityReason {\n\tconst text = typeof value === \"string\" ? value : value instanceof Error ? value.message : \"\";\n\tif (SANITIZED_REASONS.has(text)) return text as ContinuityReason;\n\tconst tainted = /^tainted:(.+)$/.exec(text);\n\tif (tainted) {\n\t\tconst mapped = `tainted_${tainted[1]}`;\n\t\tif (SANITIZED_REASONS.has(mapped)) return mapped as ContinuityReason;\n\t}\n\treturn sanitizeCloseCause(value);\n}\n\n/** Buckets arbitrary close/pump failures into the fixed close-cause vocabulary. */\nexport function sanitizeCloseCause(value: unknown): ContinuityReason {\n\tconst text = typeof value === \"string\" ? value : value instanceof Error ? value.message : \"\";\n\tif (SANITIZED_REASONS.has(text)) return text as ContinuityReason;\n\tif (/did not terminate|interrupt failed/i.test(text)) return \"abort_timeout\";\n\tif (/user_message_uuid did not match|result arrived before replay claim|pre-replay buffer overflow/i.test(text)) {\n\t\treturn \"turn_attribution_failed\";\n\t}\n\tif (/query ended before|Claude SDK OAuth query|Claude Code/i.test(text)) return \"query_failed\";\n\treturn \"other\";\n}\n\n/**\n * Classifies the failure that ended a turn after every auth attempt failed.\n * Anything reaching here came out of the SDK query path, so an Error is a query\n * failure unless it matches a more specific bucket; the raw text never escapes.\n */\nexport function sanitizeTerminalFailure(value: unknown): ContinuityReason {\n\tconst cause = sanitizeCloseCause(value);\n\tif (cause !== \"other\") return cause;\n\treturn value instanceof Error ? \"query_failed\" : \"other\";\n}\n\n/**\n * One-shot pending close causes keyed by senpi session id. `closeSession`\n * records the cause here; the next admission consumes it so a first-turn\n * `registry_miss` is attributed to the real cause instead.\n */\nconst pendingCloseCauses = new Map<string, ContinuityReason>();\n// Long-running processes can stage many closes: the map is FIFO-bounded so it\n// cannot grow without limit. 256 pending causes per process is far beyond any\n// realistic session count; the oldest entry is evicted first.\nconst PENDING_CLOSE_CAUSE_LIMIT = 256;\n\nexport function recordPendingCloseCause(senpiSessionId: string, reason: unknown): void {\n\tconst cause = sanitizeReason(reason);\n\t// Delete first so a re-recorded cause moves to the newest position.\n\tpendingCloseCauses.delete(senpiSessionId);\n\tif (pendingCloseCauses.size >= PENDING_CLOSE_CAUSE_LIMIT) {\n\t\tconst oldest = pendingCloseCauses.keys().next().value;\n\t\tif (oldest !== undefined) pendingCloseCauses.delete(oldest);\n\t}\n\tpendingCloseCauses.set(senpiSessionId, cause);\n\tactiveBoundary.log(\"claude_sdk_oauth_session_close\", { reason: cause });\n}\n\n/** Read the pending close cause WITHOUT consuming it (consumed at emit time). */\nexport function peekPendingCloseCause(senpiSessionId: string): ContinuityReason | undefined {\n\treturn pendingCloseCauses.get(senpiSessionId);\n}\n\nexport function consumePendingCloseCause(senpiSessionId: string): ContinuityReason | undefined {\n\tconst cause = pendingCloseCauses.get(senpiSessionId);\n\tpendingCloseCauses.delete(senpiSessionId);\n\treturn cause;\n}\n\n/**\n * Maps the continuity decision families onto the observation vocabulary:\n * incremental→delta, resume→fork, cold-seed→flatten.\n */\nexport function observeSessionSyncDecision(input: {\n\tkind: \"incremental\" | \"resume\" | \"cold-seed\";\n\treason?: string;\n\tdeltaMessages: number;\n\tfirstTurn: boolean;\n\tsenpiSessionId: string;\n\tpayloadBytes?: number;\n\tcollapsedDirectives?: number;\n}): ContinuityObservation {\n\tif (input.kind === \"incremental\") {\n\t\treturn { kind: \"delta\", reason: \"prefix_matched\", deltaMessages: input.deltaMessages };\n\t}\n\tif (input.kind === \"resume\") {\n\t\tconst retainedCause =\n\t\t\tinput.reason === \"registry_miss\" ? consumePendingCloseCause(input.senpiSessionId) : undefined;\n\t\treturn {\n\t\t\tkind: \"fork\",\n\t\t\treason: retainedCause ?? (input.reason === undefined ? \"branch_resume\" : sanitizeReason(input.reason)),\n\t\t\tdeltaMessages: input.deltaMessages,\n\t\t};\n\t}\n\t// Peek, not consume: the staged observation is emitted only when the\n\t// auth-lane RETAINS this attempt. Consuming here would lose the cause when\n\t// the attempt is discarded; the emit path consumes it (session-stream.ts).\n\tconst retained = input.reason === \"registry_miss\" ? peekPendingCloseCause(input.senpiSessionId) : undefined;\n\tconst reason = retained ?? sanitizeReason(input.reason);\n\treturn {\n\t\tkind: input.firstTurn && reason === \"registry_miss\" ? \"bootstrap\" : \"flatten\",\n\t\treason,\n\t\tdeltaMessages: input.deltaMessages,\n\t\t...(input.payloadBytes !== undefined ? { payloadBytes: input.payloadBytes } : {}),\n\t\t...(input.collapsedDirectives !== undefined ? { collapsedDirectives: input.collapsedDirectives } : {}),\n\t};\n}\n\nexport type StagedContinuityDecision = {\n\tobservation: ContinuityObservation;\n\temit: () => void;\n};\n\n/** Stages an attempt's decision; `emit` fires only when the attempt is retained. */\nexport function stageContinuityDecision(\n\tobservation: ContinuityObservation,\n\tonDecision?: (observation: ContinuityObservation) => void,\n\tbeforeEmit?: () => void,\n): StagedContinuityDecision {\n\tlet emitted = false;\n\treturn {\n\t\tobservation,\n\t\temit: () => {\n\t\t\tif (emitted) return;\n\t\t\temitted = true;\n\t\t\tbeforeEmit?.();\n\t\t\temitContinuityObservation(observation, onDecision);\n\t\t},\n\t};\n}\n\nexport function emitContinuityObservation(\n\tobservation: ContinuityObservation,\n\tonDecision?: (observation: ContinuityObservation) => void,\n): void {\n\tonDecision?.(observation);\n\tactiveBoundary.emit(observation);\n\tactiveBoundary.log(\"claude_sdk_oauth_session_continuity\", {\n\t\tkind: observation.kind,\n\t\treason: observation.reason,\n\t\t...(observation.deltaMessages === undefined ? {} : { count: observation.deltaMessages }),\n\t});\n}\n"]}
1
+ {"version":3,"file":"session-observability.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-observability.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAgentDir } from \"../../../../config.ts\";\nimport { createSessionLogger, type SessionLogger } from \"../../../session-log.ts\";\n\n/**\n * Per-turn continuity observability for the claude-sdk-oauth lane.\n *\n * Emission semantics: a decision is STAGED per attempt at its final outcome\n * point and EMITTED only when the auth-lane retain/discard wrapper retains that\n * attempt (auth-attempt.ts). A discarded attempt emits nothing, so every\n * completed turn yields exactly one observation; a turn where every attempt\n * fails yields one terminal error observation instead.\n */\nexport type ContinuityKind = \"bootstrap\" | \"delta\" | \"reattach\" | \"fork\" | \"flatten\" | \"disabled\";\n\n/** Fixed vocabulary — arbitrary error text never reaches an observation. */\nexport type ContinuityReason =\n\t| \"prefix_matched\"\n\t| \"registry_miss\"\n\t| \"idle_ttl\"\n\t| \"capacity\"\n\t| \"model_selected\"\n\t| \"thinking_level_selected\"\n\t| \"bound_account_token_expiring\"\n\t| \"account_changed\"\n\t| \"model_changed\"\n\t| \"toolset_changed\"\n\t| \"system_prompt_changed\"\n\t| \"assistant_stream_diverged\"\n\t| \"sent_stream_diverged\"\n\t| \"options_changed\"\n\t| \"history_rolled_back\"\n\t| \"assistant_rewritten\"\n\t| \"transcript_missing\"\n\t| \"cross_root_unsupported\"\n\t| \"branch_diverged\"\n\t| \"branch_boundary_unavailable\"\n\t| \"branch_resume\"\n\t| \"tainted_compaction\"\n\t| \"tainted_fork\"\n\t| \"tainted_abort\"\n\t| \"tainted_assistant_provenance_unverified\"\n\t| \"resume_initialization_failed\"\n\t| \"resume_initialization_aborted\"\n\t| \"resume_mode_off\"\n\t| \"query_failed\"\n\t| \"turn_attribution_failed\"\n\t| \"abort_timeout\"\n\t| \"extensions_removed\"\n\t| \"session_shutdown\"\n\t| \"timeout_retry\"\n\t| \"other\";\n\nexport type ContinuityObservation = {\n\tkind: ContinuityKind;\n\treason: ContinuityReason;\n\tdeltaMessages?: number;\n\tpayloadBytes?: number;\n\tcollapsedDirectives?: number;\n};\n\nexport type ContinuityObservabilityBoundary = {\n\temit: (observation: ContinuityObservation) => void;\n\tlog: (event: string, data: Record<string, unknown>) => void;\n};\n\nconst SANITIZED_REASONS = new Set<string>([\n\t\"prefix_matched\",\n\t\"registry_miss\",\n\t\"idle_ttl\",\n\t\"capacity\",\n\t\"model_selected\",\n\t\"thinking_level_selected\",\n\t\"bound_account_token_expiring\",\n\t\"account_changed\",\n\t\"model_changed\",\n\t\"toolset_changed\",\n\t\"system_prompt_changed\",\n\t\"assistant_stream_diverged\",\n\t\"options_changed\",\n\t\"history_rolled_back\",\n\t\"assistant_rewritten\",\n\t\"transcript_missing\",\n\t\"cross_root_unsupported\",\n\t\"sent_stream_diverged\",\n\t\"branch_diverged\",\n\t\"branch_boundary_unavailable\",\n\t\"branch_resume\",\n\t\"tainted_compaction\",\n\t\"tainted_fork\",\n\t\"tainted_abort\",\n\t\"tainted_assistant_provenance_unverified\",\n\t\"resume_initialization_failed\",\n\t\"resume_initialization_aborted\",\n\t\"resume_mode_off\",\n\t\"query_failed\",\n\t\"turn_attribution_failed\",\n\t\"abort_timeout\",\n\t\"extensions_removed\",\n\t\"session_shutdown\",\n\t\"timeout_retry\",\n\t\"other\",\n]);\n\nlet cachedLogger: SessionLogger | undefined;\n\nfunction sessionLogger(): SessionLogger {\n\tcachedLogger ??= createSessionLogger(getAgentDir());\n\treturn cachedLogger;\n}\n\nconst defaultBoundary: ContinuityObservabilityBoundary = {\n\temit: () => {},\n\tlog: (event, data) => sessionLogger().info(event, data),\n};\nlet activeBoundary = defaultBoundary;\n\nexport function overrideContinuityObservabilityBoundary(override: Partial<ContinuityObservabilityBoundary>): void {\n\tactiveBoundary = { ...defaultBoundary, ...override };\n}\n\nexport function resetContinuityObservabilityBoundary(): void {\n\tactiveBoundary = defaultBoundary;\n}\n\n/** Maps a fixed vocabulary member through, and anything else to a bucketed cause. */\nexport function sanitizeReason(value: unknown): ContinuityReason {\n\tconst text = typeof value === \"string\" ? value : value instanceof Error ? value.message : \"\";\n\tif (SANITIZED_REASONS.has(text)) return text as ContinuityReason;\n\tconst tainted = /^tainted:(.+)$/.exec(text);\n\tif (tainted) {\n\t\tconst mapped = `tainted_${tainted[1]}`;\n\t\tif (SANITIZED_REASONS.has(mapped)) return mapped as ContinuityReason;\n\t}\n\treturn sanitizeCloseCause(value);\n}\n\n/** Buckets arbitrary close/pump failures into the fixed close-cause vocabulary. */\nexport function sanitizeCloseCause(value: unknown): ContinuityReason {\n\tconst text = typeof value === \"string\" ? value : value instanceof Error ? value.message : \"\";\n\tif (SANITIZED_REASONS.has(text)) return text as ContinuityReason;\n\tif (/did not terminate|interrupt failed/i.test(text)) return \"abort_timeout\";\n\tif (/user_message_uuid did not match|result arrived before replay claim|pre-replay buffer overflow/i.test(text)) {\n\t\treturn \"turn_attribution_failed\";\n\t}\n\tif (/query ended before|Claude SDK OAuth query|Claude Code/i.test(text)) return \"query_failed\";\n\treturn \"other\";\n}\n\n/**\n * Classifies the failure that ended a turn after every auth attempt failed.\n * Anything reaching here came out of the SDK query path, so an Error is a query\n * failure unless it matches a more specific bucket; the raw text never escapes.\n */\nexport function sanitizeTerminalFailure(value: unknown): ContinuityReason {\n\tconst cause = sanitizeCloseCause(value);\n\tif (cause !== \"other\") return cause;\n\treturn value instanceof Error ? \"query_failed\" : \"other\";\n}\n\n/**\n * One-shot pending close causes keyed by senpi session id. `closeSession`\n * records the cause here; the next admission consumes it so a first-turn\n * `registry_miss` is attributed to the real cause instead.\n */\nconst pendingCloseCauses = new Map<string, ContinuityReason>();\n// Long-running processes can stage many closes: the map is FIFO-bounded so it\n// cannot grow without limit. 256 pending causes per process is far beyond any\n// realistic session count; the oldest entry is evicted first.\nconst PENDING_CLOSE_CAUSE_LIMIT = 256;\n\nexport function recordPendingCloseCause(senpiSessionId: string, reason: unknown): void {\n\tconst cause = sanitizeReason(reason);\n\t// Delete first so a re-recorded cause moves to the newest position.\n\tpendingCloseCauses.delete(senpiSessionId);\n\tif (pendingCloseCauses.size >= PENDING_CLOSE_CAUSE_LIMIT) {\n\t\tconst oldest = pendingCloseCauses.keys().next().value;\n\t\tif (oldest !== undefined) pendingCloseCauses.delete(oldest);\n\t}\n\tpendingCloseCauses.set(senpiSessionId, cause);\n\tactiveBoundary.log(\"claude_sdk_oauth_session_close\", { reason: cause });\n}\n\n/** Read the pending close cause WITHOUT consuming it (consumed at emit time). */\nexport function peekPendingCloseCause(senpiSessionId: string): ContinuityReason | undefined {\n\treturn pendingCloseCauses.get(senpiSessionId);\n}\n\nexport function consumePendingCloseCause(senpiSessionId: string): ContinuityReason | undefined {\n\tconst cause = pendingCloseCauses.get(senpiSessionId);\n\tpendingCloseCauses.delete(senpiSessionId);\n\treturn cause;\n}\n\n/**\n * Maps the continuity decision families onto the observation vocabulary:\n * incremental→delta, resume→fork, cold-seed→flatten.\n */\nexport function observeSessionSyncDecision(input: {\n\tkind: \"incremental\" | \"resume\" | \"cold-seed\";\n\treason?: string;\n\tdeltaMessages: number;\n\tfirstTurn: boolean;\n\tsenpiSessionId: string;\n\tpayloadBytes?: number;\n\tcollapsedDirectives?: number;\n}): ContinuityObservation {\n\tif (input.kind === \"incremental\") {\n\t\treturn { kind: \"delta\", reason: \"prefix_matched\", deltaMessages: input.deltaMessages };\n\t}\n\tif (input.kind === \"resume\") {\n\t\tconst retainedCause =\n\t\t\tinput.reason === \"registry_miss\" ? consumePendingCloseCause(input.senpiSessionId) : undefined;\n\t\treturn {\n\t\t\tkind: \"fork\",\n\t\t\treason: retainedCause ?? (input.reason === undefined ? \"branch_resume\" : sanitizeReason(input.reason)),\n\t\t\tdeltaMessages: input.deltaMessages,\n\t\t};\n\t}\n\t// Peek, not consume: the staged observation is emitted only when the\n\t// auth-lane RETAINS this attempt. Consuming here would lose the cause when\n\t// the attempt is discarded; the emit path consumes it (session-stream.ts).\n\tconst retained = input.reason === \"registry_miss\" ? peekPendingCloseCause(input.senpiSessionId) : undefined;\n\tconst reason = retained ?? sanitizeReason(input.reason);\n\treturn {\n\t\tkind: input.firstTurn && reason === \"registry_miss\" ? \"bootstrap\" : \"flatten\",\n\t\treason,\n\t\tdeltaMessages: input.deltaMessages,\n\t\t...(input.payloadBytes !== undefined ? { payloadBytes: input.payloadBytes } : {}),\n\t\t...(input.collapsedDirectives !== undefined ? { collapsedDirectives: input.collapsedDirectives } : {}),\n\t};\n}\n\nexport type StagedContinuityDecision = {\n\tobservation: ContinuityObservation;\n\temit: () => void;\n};\n\n/** Stages an attempt's decision; `emit` fires only when the attempt is retained. */\nexport function stageContinuityDecision(\n\tobservation: ContinuityObservation,\n\tonDecision?: (observation: ContinuityObservation) => void,\n\tbeforeEmit?: () => void,\n): StagedContinuityDecision {\n\tlet emitted = false;\n\treturn {\n\t\tobservation,\n\t\temit: () => {\n\t\t\tif (emitted) return;\n\t\t\temitted = true;\n\t\t\tbeforeEmit?.();\n\t\t\temitContinuityObservation(observation, onDecision);\n\t\t},\n\t};\n}\n\nexport function emitContinuityObservation(\n\tobservation: ContinuityObservation,\n\tonDecision?: (observation: ContinuityObservation) => void,\n): void {\n\tonDecision?.(observation);\n\tactiveBoundary.emit(observation);\n\tactiveBoundary.log(\"claude_sdk_oauth_session_continuity\", {\n\t\tkind: observation.kind,\n\t\treason: observation.reason,\n\t\t...(observation.deltaMessages === undefined ? {} : { count: observation.deltaMessages }),\n\t});\n}\n"]}
@@ -14,6 +14,14 @@ export type ContinuityBinding = {
14
14
  toolsetHash: string;
15
15
  /** Assistant boundaries kept as entries so a later fork still has a resume point. */
16
16
  assistantUuidByIndex?: readonly (readonly [number, string])[];
17
+ /**
18
+ * Digest of the FULL sent stream an attempt pushed but never got answered
19
+ * (stream-start timeout abort/failure). Purely in-memory: it lets the SAME
20
+ * turn's retry fork at the pre-turn boundary instead of re-appending its user
21
+ * message to a lineage that already carries it. Never persisted — the sidecar
22
+ * schema is fixed at schemaVersion 1 and restart retries are out of scope.
23
+ */
24
+ unansweredTurnDigest?: string;
17
25
  };
18
26
  export type ReattachInput = {
19
27
  binding: ContinuityBinding;