@caupulican/pi-adaptative 0.85.3 → 0.85.4

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 (192) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/core/agent-session-services.d.ts.map +1 -1
  3. package/dist/core/agent-session-services.js +2 -0
  4. package/dist/core/agent-session-services.js.map +1 -1
  5. package/dist/core/agent-session.d.ts +6 -5
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +76 -87
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/auth-guidance.d.ts.map +1 -1
  10. package/dist/core/auth-guidance.js +3 -0
  11. package/dist/core/auth-guidance.js.map +1 -1
  12. package/dist/core/autonomy/lane-tool-surface.d.ts +2 -0
  13. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  14. package/dist/core/autonomy/lane-tool-surface.js +4 -3
  15. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  16. package/dist/core/background-lane-controller.d.ts +4 -0
  17. package/dist/core/background-lane-controller.d.ts.map +1 -1
  18. package/dist/core/background-lane-controller.js +2 -0
  19. package/dist/core/background-lane-controller.js.map +1 -1
  20. package/dist/core/background-tool-task-controller.d.ts +12 -7
  21. package/dist/core/background-tool-task-controller.d.ts.map +1 -1
  22. package/dist/core/background-tool-task-controller.js +55 -19
  23. package/dist/core/background-tool-task-controller.js.map +1 -1
  24. package/dist/core/bedrock-scope.d.ts +26 -0
  25. package/dist/core/bedrock-scope.d.ts.map +1 -0
  26. package/dist/core/bedrock-scope.js +178 -0
  27. package/dist/core/bedrock-scope.js.map +1 -0
  28. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  29. package/dist/core/delegation/worker-delegation-controller.js +6 -0
  30. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  31. package/dist/core/extensions/loader.d.ts.map +1 -1
  32. package/dist/core/extensions/loader.js +3 -0
  33. package/dist/core/extensions/loader.js.map +1 -1
  34. package/dist/core/foreground-recovery-controller.d.ts +25 -2
  35. package/dist/core/foreground-recovery-controller.d.ts.map +1 -1
  36. package/dist/core/foreground-recovery-controller.js +111 -2
  37. package/dist/core/foreground-recovery-controller.js.map +1 -1
  38. package/dist/core/foreground-terminal-handoff-controller.d.ts +27 -0
  39. package/dist/core/foreground-terminal-handoff-controller.d.ts.map +1 -0
  40. package/dist/core/foreground-terminal-handoff-controller.js +56 -0
  41. package/dist/core/foreground-terminal-handoff-controller.js.map +1 -0
  42. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  43. package/dist/core/goal-loop-controller.js +5 -0
  44. package/dist/core/goal-loop-controller.js.map +1 -1
  45. package/dist/core/goals/goal-auto-continue-controller.d.ts +3 -0
  46. package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -1
  47. package/dist/core/goals/goal-auto-continue-controller.js +28 -7
  48. package/dist/core/goals/goal-auto-continue-controller.js.map +1 -1
  49. package/dist/core/human-input-controller.d.ts +9 -0
  50. package/dist/core/human-input-controller.d.ts.map +1 -1
  51. package/dist/core/human-input-controller.js +60 -5
  52. package/dist/core/human-input-controller.js.map +1 -1
  53. package/dist/core/human-input.d.ts +12 -0
  54. package/dist/core/human-input.d.ts.map +1 -1
  55. package/dist/core/human-input.js +59 -12
  56. package/dist/core/human-input.js.map +1 -1
  57. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  58. package/dist/core/local-runtime-controller.js +15 -9
  59. package/dist/core/local-runtime-controller.js.map +1 -1
  60. package/dist/core/memory/providers/file-store.d.ts +1 -0
  61. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  62. package/dist/core/memory/providers/file-store.js +252 -125
  63. package/dist/core/memory/providers/file-store.js.map +1 -1
  64. package/dist/core/model-capability.d.ts +15 -1
  65. package/dist/core/model-capability.d.ts.map +1 -1
  66. package/dist/core/model-capability.js +24 -1
  67. package/dist/core/model-capability.js.map +1 -1
  68. package/dist/core/model-registry.d.ts +5 -0
  69. package/dist/core/model-registry.d.ts.map +1 -1
  70. package/dist/core/model-registry.js +18 -0
  71. package/dist/core/model-registry.js.map +1 -1
  72. package/dist/core/model-router-controller.d.ts +6 -6
  73. package/dist/core/model-router-controller.d.ts.map +1 -1
  74. package/dist/core/model-router-controller.js +5 -5
  75. package/dist/core/model-router-controller.js.map +1 -1
  76. package/dist/core/models/context-sizing.d.ts +1 -0
  77. package/dist/core/models/context-sizing.d.ts.map +1 -1
  78. package/dist/core/models/context-sizing.js +4 -1
  79. package/dist/core/models/context-sizing.js.map +1 -1
  80. package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
  81. package/dist/core/models/default-model-suggestions.js +20 -10
  82. package/dist/core/models/default-model-suggestions.js.map +1 -1
  83. package/dist/core/models/llamacpp-runtime.d.ts +32 -11
  84. package/dist/core/models/llamacpp-runtime.d.ts.map +1 -1
  85. package/dist/core/models/llamacpp-runtime.js +79 -28
  86. package/dist/core/models/llamacpp-runtime.js.map +1 -1
  87. package/dist/core/models/local-execution-planner.d.ts +24 -0
  88. package/dist/core/models/local-execution-planner.d.ts.map +1 -0
  89. package/dist/core/models/local-execution-planner.js +119 -0
  90. package/dist/core/models/local-execution-planner.js.map +1 -0
  91. package/dist/core/models/local-inference-profile.d.ts +34 -0
  92. package/dist/core/models/local-inference-profile.d.ts.map +1 -0
  93. package/dist/core/models/local-inference-profile.js +74 -0
  94. package/dist/core/models/local-inference-profile.js.map +1 -0
  95. package/dist/core/models/local-runtime.d.ts +23 -4
  96. package/dist/core/models/local-runtime.d.ts.map +1 -1
  97. package/dist/core/models/local-runtime.js +11 -8
  98. package/dist/core/models/local-runtime.js.map +1 -1
  99. package/dist/core/models/managed-ollama-model.d.ts +26 -0
  100. package/dist/core/models/managed-ollama-model.d.ts.map +1 -0
  101. package/dist/core/models/managed-ollama-model.js +59 -0
  102. package/dist/core/models/managed-ollama-model.js.map +1 -0
  103. package/dist/core/models/model-ref.js +11 -11
  104. package/dist/core/models/model-ref.js.map +1 -1
  105. package/dist/core/models/prism-llamacpp-lifecycle.d.ts +10 -20
  106. package/dist/core/models/prism-llamacpp-lifecycle.d.ts.map +1 -1
  107. package/dist/core/models/prism-llamacpp-lifecycle.js +11 -31
  108. package/dist/core/models/prism-llamacpp-lifecycle.js.map +1 -1
  109. package/dist/core/models/prism-llamacpp-server-probe.d.ts +10 -0
  110. package/dist/core/models/prism-llamacpp-server-probe.d.ts.map +1 -0
  111. package/dist/core/models/prism-llamacpp-server-probe.js +36 -0
  112. package/dist/core/models/prism-llamacpp-server-probe.js.map +1 -0
  113. package/dist/core/models/runtime-arbiter.d.ts +3 -0
  114. package/dist/core/models/runtime-arbiter.d.ts.map +1 -1
  115. package/dist/core/models/runtime-arbiter.js +2 -1
  116. package/dist/core/models/runtime-arbiter.js.map +1 -1
  117. package/dist/core/research/research-lane-controller.d.ts.map +1 -1
  118. package/dist/core/research/research-lane-controller.js +14 -5
  119. package/dist/core/research/research-lane-controller.js.map +1 -1
  120. package/dist/core/runtime-builder.d.ts +5 -1
  121. package/dist/core/runtime-builder.d.ts.map +1 -1
  122. package/dist/core/runtime-builder.js +37 -4
  123. package/dist/core/runtime-builder.js.map +1 -1
  124. package/dist/core/scout-controller.d.ts +3 -2
  125. package/dist/core/scout-controller.d.ts.map +1 -1
  126. package/dist/core/scout-controller.js +1 -0
  127. package/dist/core/scout-controller.js.map +1 -1
  128. package/dist/core/sdk.d.ts.map +1 -1
  129. package/dist/core/sdk.js +10 -8
  130. package/dist/core/sdk.js.map +1 -1
  131. package/dist/core/settings-manager.d.ts +13 -0
  132. package/dist/core/settings-manager.d.ts.map +1 -1
  133. package/dist/core/settings-manager.js +59 -0
  134. package/dist/core/settings-manager.js.map +1 -1
  135. package/dist/core/system-prompt-builder.d.ts +5 -2
  136. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  137. package/dist/core/system-prompt-builder.js +22 -10
  138. package/dist/core/system-prompt-builder.js.map +1 -1
  139. package/dist/core/system-prompt.d.ts +3 -0
  140. package/dist/core/system-prompt.d.ts.map +1 -1
  141. package/dist/core/system-prompt.js +181 -55
  142. package/dist/core/system-prompt.js.map +1 -1
  143. package/dist/core/tool-protocol-controller.d.ts.map +1 -1
  144. package/dist/core/tool-protocol-controller.js +5 -3
  145. package/dist/core/tool-protocol-controller.js.map +1 -1
  146. package/dist/core/tools/edit-diff.d.ts +2 -0
  147. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  148. package/dist/core/tools/edit-diff.js +9 -1
  149. package/dist/core/tools/edit-diff.js.map +1 -1
  150. package/dist/core/tools/edit.d.ts +5 -8
  151. package/dist/core/tools/edit.d.ts.map +1 -1
  152. package/dist/core/tools/edit.js +88 -55
  153. package/dist/core/tools/edit.js.map +1 -1
  154. package/dist/core/tools/file-mutation-intent.d.ts +42 -15
  155. package/dist/core/tools/file-mutation-intent.d.ts.map +1 -1
  156. package/dist/core/tools/file-mutation-intent.js +271 -77
  157. package/dist/core/tools/file-mutation-intent.js.map +1 -1
  158. package/dist/core/tools/index.d.ts +1 -1
  159. package/dist/core/tools/index.d.ts.map +1 -1
  160. package/dist/core/tools/index.js +1 -1
  161. package/dist/core/tools/index.js.map +1 -1
  162. package/dist/core/tools/write.d.ts +5 -10
  163. package/dist/core/tools/write.d.ts.map +1 -1
  164. package/dist/core/tools/write.js +92 -73
  165. package/dist/core/tools/write.js.map +1 -1
  166. package/dist/index.d.ts +1 -1
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +1 -1
  169. package/dist/index.js.map +1 -1
  170. package/dist/modes/interactive/auth-dialogs-controller.d.ts +6 -0
  171. package/dist/modes/interactive/auth-dialogs-controller.d.ts.map +1 -1
  172. package/dist/modes/interactive/auth-dialogs-controller.js +59 -12
  173. package/dist/modes/interactive/auth-dialogs-controller.js.map +1 -1
  174. package/dist/modes/interactive/local-model-commands.d.ts +1 -1
  175. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  176. package/dist/modes/interactive/local-model-commands.js +51 -8
  177. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  178. package/docs/providers.md +34 -10
  179. package/docs/tool-boundary-performance-roadmap-2026-08-02.md +21 -18
  180. package/examples/extensions/built-in-tool-renderer.ts +1 -1
  181. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  182. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  183. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  184. package/examples/extensions/minimal-mode.ts +1 -1
  185. package/examples/extensions/prompt-customizer.ts +2 -2
  186. package/examples/extensions/sandbox/package-lock.json +2 -2
  187. package/examples/extensions/sandbox/package.json +1 -1
  188. package/examples/extensions/ssh.ts +17 -0
  189. package/examples/extensions/with-deps/package-lock.json +2 -2
  190. package/examples/extensions/with-deps/package.json +1 -1
  191. package/npm-shrinkwrap.json +240 -260
  192. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"foreground-recovery-controller.js","sourceRoot":"","sources":["../../src/core/foreground-recovery-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent, AgentEvent, ClassifiedError } from \"@caupulican/pi-agent-core\";\nimport { classifyFailure, DEFAULT_RETRY_POLICY, RetryController } from \"@caupulican/pi-agent-core\";\nimport type { AssistantMessage } from \"@caupulican/pi-ai\";\nimport { isContextOverflow } from \"@caupulican/pi-ai\";\nimport { BillingFailoverController, ExhaustedProviderRegistry } from \"./billing-failover-controller.ts\";\nimport type { FailureCorpusRecorder } from \"./failure-corpus.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport type { ModelRouterFailoverStatus } from \"./model-router/status.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\ntype ForegroundRecoveryEvent =\n\t| { type: \"warning\"; message: string }\n\t| { type: \"auto_retry_start\"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }\n\t| { type: \"auto_retry_end\"; success: boolean; attempt: number; finalError?: string };\n\nexport interface ForegroundRecoveryControllerDeps {\n\tagent: Agent;\n\tsettingsManager: SettingsManager;\n\tmodelRegistry: ModelRegistry;\n\tfailureCorpus: FailureCorpusRecorder;\n\tgetContextWindow(): number;\n\temit(event: ForegroundRecoveryEvent): void;\n\tcheckCompaction(message: AssistantMessage): Promise<boolean>;\n\tonSuccessfulAssistant(): void;\n}\n\n/** Owns foreground retry/failover/compaction recovery ordering and its response latch. */\nexport class ForegroundRecoveryController {\n\tprivate readonly retry: RetryController;\n\tprivate readonly billingFailover: BillingFailoverController;\n\tprivate lastAssistantMessage: AssistantMessage | undefined;\n\tprivate readonly deps: ForegroundRecoveryControllerDeps;\n\n\tconstructor(deps: ForegroundRecoveryControllerDeps) {\n\t\tthis.deps = deps;\n\t\tthis.retry = new RetryController(\n\t\t\tdeps.agent,\n\t\t\t() => {\n\t\t\t\tconst retry = deps.settingsManager.getRetrySettings();\n\t\t\t\treturn {\n\t\t\t\t\tenabled: retry.enabled,\n\t\t\t\t\tmaxAttempts: retry.maxRetries,\n\t\t\t\t\tbaseDelayMs: retry.baseDelayMs,\n\t\t\t\t\tmaxDelayMs: DEFAULT_RETRY_POLICY.maxDelayMs,\n\t\t\t\t\tjitterRatio: 0,\n\t\t\t\t};\n\t\t\t},\n\t\t\t{\n\t\t\t\tonRetryStart: (info) => deps.emit({ type: \"auto_retry_start\", ...info }),\n\t\t\t\tonRetryEnd: (info) => deps.emit({ type: \"auto_retry_end\", ...info }),\n\t\t\t},\n\t\t\tdeps.getContextWindow,\n\t\t);\n\t\tthis.billingFailover = new BillingFailoverController({\n\t\t\tagent: deps.agent,\n\t\t\tmodelRegistry: deps.modelRegistry,\n\t\t\texhausted: new ExhaustedProviderRegistry(),\n\t\t\tsubscriptionHop: deps.settingsManager.getFailoverSettings().subscriptionHop,\n\t\t\temit: deps.emit,\n\t\t\trecordFailure: (record) => deps.failureCorpus.record(record),\n\t\t});\n\t}\n\n\tget attempt(): number {\n\t\treturn this.retry.attempt;\n\t}\n\n\tget isRetrying(): boolean {\n\t\treturn this.retry.isRetrying;\n\t}\n\n\tabortRetry(): void {\n\t\tthis.retry.abort();\n\t}\n\n\tisModelExhausted(ref: string): boolean {\n\t\treturn this.billingFailover.isExhausted(ref);\n\t}\n\n\tgetFailoverStatus(): ModelRouterFailoverStatus {\n\t\treturn this.billingFailover.getStatus();\n\t}\n\n\tobserveAssistant(message: AssistantMessage): void {\n\t\tthis.lastAssistantMessage = message;\n\t\tif (message.stopReason === \"error\") return;\n\t\tif (message.stopReason === \"aborted\") {\n\t\t\tthis.finishRetry(false, message.errorMessage ?? \"Retry aborted\");\n\t\t\treturn;\n\t\t}\n\t\tthis.deps.onSuccessfulAssistant();\n\t\tthis.finishRetry(true);\n\t}\n\n\twillRetryAfterAgentEnd(event: Extract<AgentEvent, { type: \"agent_end\" }>): boolean {\n\t\tconst settings = this.deps.settingsManager.getRetrySettings();\n\t\tif (!settings.enabled || this.retry.attempt >= settings.maxRetries) return false;\n\t\tfor (let index = event.messages.length - 1; index >= 0; index--) {\n\t\t\tconst message = event.messages[index];\n\t\t\tif (message.role === \"assistant\") return this.classifyAssistantError(message)?.retryable ?? false;\n\t\t}\n\t\treturn false;\n\t}\n\n\tasync handlePostAgentRun(): Promise<boolean> {\n\t\tconst message = this.lastAssistantMessage;\n\t\tthis.lastAssistantMessage = undefined;\n\t\tif (!message) return false;\n\n\t\tconst classified = this.classifyAssistantError(message);\n\t\tif (classified) {\n\t\t\tthis.deps.failureCorpus.record({\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodelId: message.model,\n\t\t\t\tmessage: message.errorMessage ?? \"\",\n\t\t\t\tclassified,\n\t\t\t});\n\t\t}\n\t\tif (classified?.retryable && (await this.retry.prepareRetry(message))) return true;\n\t\tif (await this.billingFailover.handleAssistantError(message, classified)) return false;\n\n\t\tif (message.stopReason === \"error\") this.finishRetry(false, message.errorMessage);\n\t\tif (await this.deps.checkCompaction(message)) return true;\n\t\treturn this.deps.agent.hasQueuedMessages();\n\t}\n\n\tprivate finishRetry(success: boolean, finalError?: string): void {\n\t\tif (this.retry.attempt === 0) return;\n\t\tthis.deps.emit({ type: \"auto_retry_end\", success, attempt: this.retry.attempt, finalError });\n\t\tthis.retry.reset();\n\t}\n\n\tprivate classifyAssistantError(message: AssistantMessage): ClassifiedError | undefined {\n\t\tif (message.stopReason !== \"error\" || !message.errorMessage) return undefined;\n\t\treturn classifyFailure({\n\t\t\tmessage: message.errorMessage,\n\t\t\tcontextOverflow: isContextOverflow(message, this.deps.getContextWindow()),\n\t\t\tprovider: message.provider,\n\t\t});\n\t}\n}\n"]}
1
+ {"version":3,"file":"foreground-recovery-controller.js","sourceRoot":"","sources":["../../src/core/foreground-recovery-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent, AgentEvent, AgentMessage, ClassifiedError } from \"@caupulican/pi-agent-core\";\nimport { AgentBusyError, classifyFailure, DEFAULT_RETRY_POLICY, RetryController } from \"@caupulican/pi-agent-core\";\nimport type { AssistantMessage } from \"@caupulican/pi-ai\";\nimport { isContextOverflow } from \"@caupulican/pi-ai\";\nimport { BillingFailoverController, ExhaustedProviderRegistry } from \"./billing-failover-controller.ts\";\nimport type { FailureCorpusRecorder } from \"./failure-corpus.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport type { ModelRouterFailoverStatus } from \"./model-router/status.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\ntype ForegroundRecoveryEvent =\n\t| { type: \"warning\"; message: string }\n\t| { type: \"auto_retry_start\"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }\n\t| { type: \"auto_retry_end\"; success: boolean; attempt: number; finalError?: string };\n\nconst foregroundSubmissionLeaseMarker: unique symbol = Symbol(\"foregroundSubmissionLease\");\n\n/** Identity-bound authority to prepare and execute one logical foreground submission. */\nexport interface ForegroundSubmissionLease {\n\treadonly [foregroundSubmissionLeaseMarker]: true;\n}\n\nexport interface ForegroundRecoveryControllerDeps {\n\tagent: Agent;\n\tsettingsManager: SettingsManager;\n\tmodelRegistry: ModelRegistry;\n\tfailureCorpus: FailureCorpusRecorder;\n\tgetContextWindow(): number;\n\temit(event: ForegroundRecoveryEvent): void;\n\tcheckCompaction(message: AssistantMessage): Promise<boolean>;\n\tonSuccessfulAssistant(): void;\n\tprepareRun(): Promise<void>;\n\tafterRun(): Promise<void>;\n}\n\n/** Owns the complete logical foreground run plus retry/failover/compaction recovery ordering. */\nexport class ForegroundRecoveryController {\n\tprivate readonly retry: RetryController;\n\tprivate readonly billingFailover: BillingFailoverController;\n\tprivate lastAssistantMessage: AssistantMessage | undefined;\n\tprivate activeRuns = 0;\n\tprivate submissionLease: ForegroundSubmissionLease | undefined;\n\tprivate shutdownReason: Error | undefined;\n\tprivate readonly idleWaiters = new Set<() => void>();\n\tprivate readonly deps: ForegroundRecoveryControllerDeps;\n\n\tconstructor(deps: ForegroundRecoveryControllerDeps) {\n\t\tthis.deps = deps;\n\t\tthis.retry = new RetryController(\n\t\t\tdeps.agent,\n\t\t\t() => {\n\t\t\t\tconst retry = deps.settingsManager.getRetrySettings();\n\t\t\t\treturn {\n\t\t\t\t\tenabled: retry.enabled,\n\t\t\t\t\tmaxAttempts: retry.maxRetries,\n\t\t\t\t\tbaseDelayMs: retry.baseDelayMs,\n\t\t\t\t\tmaxDelayMs: DEFAULT_RETRY_POLICY.maxDelayMs,\n\t\t\t\t\tjitterRatio: 0,\n\t\t\t\t};\n\t\t\t},\n\t\t\t{\n\t\t\t\tonRetryStart: (info) => deps.emit({ type: \"auto_retry_start\", ...info }),\n\t\t\t\tonRetryEnd: (info) => deps.emit({ type: \"auto_retry_end\", ...info }),\n\t\t\t},\n\t\t\tdeps.getContextWindow,\n\t\t);\n\t\tthis.billingFailover = new BillingFailoverController({\n\t\t\tagent: deps.agent,\n\t\t\tmodelRegistry: deps.modelRegistry,\n\t\t\texhausted: new ExhaustedProviderRegistry(),\n\t\t\tsubscriptionHop: deps.settingsManager.getFailoverSettings().subscriptionHop,\n\t\t\temit: deps.emit,\n\t\t\trecordFailure: (record) => deps.failureCorpus.record(record),\n\t\t});\n\t}\n\n\tget attempt(): number {\n\t\treturn this.retry.attempt;\n\t}\n\n\tget isRetrying(): boolean {\n\t\treturn this.retry.isRetrying;\n\t}\n\n\tget isRunActive(): boolean {\n\t\treturn this.activeRuns > 0;\n\t}\n\n\tget isBusy(): boolean {\n\t\treturn (\n\t\t\tthis.submissionLease !== undefined || this.isRunActive || this.deps.agent.state.isStreaming || this.isRetrying\n\t\t);\n\t}\n\n\t/** Atomically reserve the full foreground lifecycle, including asynchronous prompt preparation. */\n\ttryAcquireSubmission(): ForegroundSubmissionLease | undefined {\n\t\tif (this.shutdownReason || this.isBusy) return undefined;\n\t\tconst lease: ForegroundSubmissionLease = { [foregroundSubmissionLeaseMarker]: true };\n\t\tthis.submissionLease = lease;\n\t\treturn lease;\n\t}\n\n\t/** Wait until foreground ownership can be acquired without a check-then-act gap. */\n\tasync acquireSubmission(): Promise<ForegroundSubmissionLease> {\n\t\twhile (true) {\n\t\t\tif (this.shutdownReason) throw this.shutdownReason;\n\t\t\tconst lease = this.tryAcquireSubmission();\n\t\t\tif (lease) return lease;\n\t\t\tawait this.waitForIdle();\n\t\t}\n\t}\n\n\townsSubmission(lease: ForegroundSubmissionLease | undefined): boolean {\n\t\treturn lease !== undefined && lease === this.submissionLease;\n\t}\n\n\treleaseSubmission(lease: ForegroundSubmissionLease): void {\n\t\tif (!this.ownsSubmission(lease)) {\n\t\t\tthrow new Error(\"Cannot release foreground submission authority owned by another caller\");\n\t\t}\n\t\tif (this.activeRuns > 0) {\n\t\t\tthrow new Error(\"Cannot release foreground submission authority while its agent run is active\");\n\t\t}\n\t\tthis.submissionLease = undefined;\n\t\tthis.resolveIdleWaiters();\n\t}\n\n\tasync runAgentPrompt(\n\t\tmessages: AgentMessage | AgentMessage[],\n\t\tsubmissionLease?: ForegroundSubmissionLease,\n\t): Promise<void> {\n\t\tlet lease = submissionLease;\n\t\tlet releaseLease = false;\n\t\tif (lease) {\n\t\t\tif (!this.ownsSubmission(lease)) {\n\t\t\t\tthrow new AgentBusyError(\"Foreground submission authority is no longer active.\");\n\t\t\t}\n\t\t} else {\n\t\t\tlease = this.tryAcquireSubmission();\n\t\t\tif (!lease) throw new AgentBusyError(\"Agent is already processing.\");\n\t\t\treleaseLease = true;\n\t\t}\n\t\tif (this.activeRuns > 0) {\n\t\t\tif (releaseLease) this.releaseSubmission(lease);\n\t\t\tthrow new AgentBusyError(\"Agent is already processing.\");\n\t\t}\n\n\t\tthis.activeRuns++;\n\t\ttry {\n\t\t\tconst maxGoalLoopRounds = this.deps.settingsManager.getAutonomySettings().maxStallTurns;\n\t\t\tthis.deps.agent.maxStallTurns = maxGoalLoopRounds;\n\t\t\tawait this.deps.prepareRun();\n\t\t\tlet goalLoopRounds = 1;\n\t\t\tawait this.deps.agent.prompt(messages);\n\t\t\twhile ((maxGoalLoopRounds === 0 || goalLoopRounds < maxGoalLoopRounds) && (await this.handlePostAgentRun())) {\n\t\t\t\tawait this.deps.agent.continue();\n\t\t\t\tgoalLoopRounds++;\n\t\t\t}\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tawait this.deps.afterRun();\n\t\t\t} finally {\n\t\t\t\tthis.activeRuns--;\n\t\t\t\tif (releaseLease) this.releaseSubmission(lease);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync waitForIdle(): Promise<void> {\n\t\twhile (true) {\n\t\t\tif (this.shutdownReason) return;\n\t\t\tif (this.submissionLease !== undefined || this.activeRuns > 0) {\n\t\t\t\tawait new Promise<void>((resolve) => this.idleWaiters.add(resolve));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait this.deps.agent.waitForIdle();\n\t\t\tif (!this.isBusy) return;\n\t\t}\n\t}\n\n\tshutdown(): void {\n\t\tthis.shutdownReason ??= new Error(\"Session disposed before foreground submission authority was acquired\");\n\t\tfor (const resolve of this.idleWaiters) resolve();\n\t\tthis.idleWaiters.clear();\n\t}\n\n\tprivate resolveIdleWaiters(): void {\n\t\tif (this.submissionLease !== undefined || this.activeRuns > 0) return;\n\t\tfor (const resolve of this.idleWaiters) resolve();\n\t\tthis.idleWaiters.clear();\n\t}\n\n\tabortRetry(): void {\n\t\tthis.retry.abort();\n\t}\n\n\tisModelExhausted(ref: string): boolean {\n\t\treturn this.billingFailover.isExhausted(ref);\n\t}\n\n\tgetFailoverStatus(): ModelRouterFailoverStatus {\n\t\treturn this.billingFailover.getStatus();\n\t}\n\n\tobserveAssistant(message: AssistantMessage): void {\n\t\tthis.lastAssistantMessage = message;\n\t\tif (message.stopReason === \"error\") return;\n\t\tif (message.stopReason === \"aborted\") {\n\t\t\tthis.finishRetry(false, message.errorMessage ?? \"Retry aborted\");\n\t\t\treturn;\n\t\t}\n\t\tthis.deps.onSuccessfulAssistant();\n\t\tthis.finishRetry(true);\n\t}\n\n\twillRetryAfterAgentEnd(event: Extract<AgentEvent, { type: \"agent_end\" }>): boolean {\n\t\tconst settings = this.deps.settingsManager.getRetrySettings();\n\t\tif (!settings.enabled || this.retry.attempt >= settings.maxRetries) return false;\n\t\tfor (let index = event.messages.length - 1; index >= 0; index--) {\n\t\t\tconst message = event.messages[index];\n\t\t\tif (message.role === \"assistant\") return this.classifyAssistantError(message)?.retryable ?? false;\n\t\t}\n\t\treturn false;\n\t}\n\n\tasync handlePostAgentRun(): Promise<boolean> {\n\t\tconst message = this.lastAssistantMessage;\n\t\tthis.lastAssistantMessage = undefined;\n\t\tif (!message) return false;\n\n\t\tconst classified = this.classifyAssistantError(message);\n\t\tif (classified) {\n\t\t\tthis.deps.failureCorpus.record({\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodelId: message.model,\n\t\t\t\tmessage: message.errorMessage ?? \"\",\n\t\t\t\tclassified,\n\t\t\t});\n\t\t}\n\t\tif (classified?.retryable && (await this.retry.prepareRetry(message))) return true;\n\t\tif (await this.billingFailover.handleAssistantError(message, classified)) return false;\n\n\t\tif (message.stopReason === \"error\") this.finishRetry(false, message.errorMessage);\n\t\tif (await this.deps.checkCompaction(message)) return true;\n\t\treturn this.deps.agent.hasQueuedMessages();\n\t}\n\n\tprivate finishRetry(success: boolean, finalError?: string): void {\n\t\tif (this.retry.attempt === 0) return;\n\t\tthis.deps.emit({ type: \"auto_retry_end\", success, attempt: this.retry.attempt, finalError });\n\t\tthis.retry.reset();\n\t}\n\n\tprivate classifyAssistantError(message: AssistantMessage): ClassifiedError | undefined {\n\t\tif (message.stopReason !== \"error\" || !message.errorMessage) return undefined;\n\t\treturn classifyFailure({\n\t\t\tmessage: message.errorMessage,\n\t\t\tcontextOverflow: isContextOverflow(message, this.deps.getContextWindow()),\n\t\t\tprovider: message.provider,\n\t\t});\n\t}\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import type { CustomMessage } from "@caupulican/pi-agent-core";
2
+ import type { LaneTerminalStatus } from "./autonomy/lane-tracker.ts";
3
+ import { type BackgroundToolTaskRecord } from "./background-tool-task-controller.ts";
4
+ import type { ForegroundRecoveryController, ForegroundSubmissionLease } from "./foreground-recovery-controller.ts";
5
+ interface ForegroundTerminalHandoffControllerDeps {
6
+ foreground: ForegroundRecoveryController;
7
+ isDisposed(): boolean;
8
+ workerInputsWillWakeParent(workerRequestIds: readonly string[]): boolean;
9
+ sendCustomMessage(message: Pick<CustomMessage<unknown>, "customType" | "content" | "display" | "details">, options: {
10
+ triggerTurn: boolean;
11
+ deliverAs: "followUp";
12
+ }, lease: ForegroundSubmissionLease): Promise<void>;
13
+ }
14
+ /** Serializes durable background terminal delivery through the foreground submission owner. */
15
+ export declare class ForegroundTerminalHandoffController {
16
+ private readonly deps;
17
+ constructor(deps: ForegroundTerminalHandoffControllerDeps);
18
+ notifyWorkers(records: readonly {
19
+ laneId: string;
20
+ status: LaneTerminalStatus;
21
+ reasonCode?: string;
22
+ }[]): Promise<void>;
23
+ notifyTools(records: readonly BackgroundToolTaskRecord[], wakeParent: boolean): Promise<void>;
24
+ private assertLive;
25
+ }
26
+ export {};
27
+ //# sourceMappingURL=foreground-terminal-handoff-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foreground-terminal-handoff-controller.d.ts","sourceRoot":"","sources":["../../src/core/foreground-terminal-handoff-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EACN,KAAK,wBAAwB,EAE7B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,4BAA4B,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAEnH,UAAU,uCAAuC;IAChD,UAAU,EAAE,4BAA4B,CAAC;IACzC,UAAU,IAAI,OAAO,CAAC;IACtB,0BAA0B,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC;IACzE,iBAAiB,CAChB,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC,EACvF,OAAO,EAAE;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,UAAU,CAAA;KAAE,EACxD,KAAK,EAAE,yBAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAAC;CACjB;AAED,+FAA+F;AAC/F,qBAAa,mCAAmC;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA0C;IAE/D,YAAY,IAAI,EAAE,uCAAuC,EAExD;IAEK,aAAa,CAClB,OAAO,EAAE,SAAS;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,kBAAkB,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,GACrF,OAAO,CAAC,IAAI,CAAC,CAkCf;IAEK,WAAW,CAAC,OAAO,EAAE,SAAS,wBAAwB,EAAE,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAclG;IAED,OAAO,CAAC,UAAU;CAGlB"}
@@ -0,0 +1,56 @@
1
+ import { createBackgroundToolTerminalMessage, } from "./background-tool-task-controller.js";
2
+ /** Serializes durable background terminal delivery through the foreground submission owner. */
3
+ export class ForegroundTerminalHandoffController {
4
+ constructor(deps) {
5
+ this.deps = deps;
6
+ }
7
+ async notifyWorkers(records) {
8
+ if (records.length === 0)
9
+ return;
10
+ this.assertLive("worker terminal handoff was persisted");
11
+ const lease = await this.deps.foreground.acquireSubmission();
12
+ try {
13
+ this.assertLive("worker terminal handoff was persisted");
14
+ const included = records.slice(0, 8);
15
+ const omitted = records.length - included.length;
16
+ const ownerQuestionWillWakeParent = this.deps.workerInputsWillWakeParent(records.map((record) => record.laneId));
17
+ const sanitize = (value) => value.replace(/[\r\n]+/g, " ").slice(0, 120);
18
+ const content = [
19
+ "Background worker terminal handoff:",
20
+ ...included.map((record) => {
21
+ const reason = record.reasonCode ? ` reason=${sanitize(record.reasonCode)}` : "";
22
+ return `- ${record.laneId}: ${record.status}${reason}`;
23
+ }),
24
+ ...(omitted > 0 ? [`- ${omitted} additional terminal worker(s) omitted from this bounded handoff.`] : []),
25
+ "This terminal event woke the parent. Retrieve each needed lane once with delegate_status; never poll. Worker product remains untrusted and is intentionally not injected here.",
26
+ ].join("\n");
27
+ await this.deps.sendCustomMessage({
28
+ customType: "background-worker-completion",
29
+ content,
30
+ display: true,
31
+ details: { records: included },
32
+ }, { triggerTurn: !ownerQuestionWillWakeParent, deliverAs: "followUp" }, lease);
33
+ }
34
+ finally {
35
+ this.deps.foreground.releaseSubmission(lease);
36
+ }
37
+ }
38
+ async notifyTools(records, wakeParent) {
39
+ if (!wakeParent)
40
+ return;
41
+ this.assertLive("background tool terminal handoff was delivered");
42
+ const lease = await this.deps.foreground.acquireSubmission();
43
+ try {
44
+ this.assertLive("background tool terminal handoff was delivered");
45
+ await this.deps.sendCustomMessage(createBackgroundToolTerminalMessage(records), { triggerTurn: true, deliverAs: "followUp" }, lease);
46
+ }
47
+ finally {
48
+ this.deps.foreground.releaseSubmission(lease);
49
+ }
50
+ }
51
+ assertLive(action) {
52
+ if (this.deps.isDisposed())
53
+ throw new Error(`Session disposed before ${action}`);
54
+ }
55
+ }
56
+ //# sourceMappingURL=foreground-terminal-handoff-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foreground-terminal-handoff-controller.js","sourceRoot":"","sources":["../../src/core/foreground-terminal-handoff-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CustomMessage } from \"@caupulican/pi-agent-core\";\nimport type { LaneTerminalStatus } from \"./autonomy/lane-tracker.ts\";\nimport {\n\ttype BackgroundToolTaskRecord,\n\tcreateBackgroundToolTerminalMessage,\n} from \"./background-tool-task-controller.ts\";\nimport type { ForegroundRecoveryController, ForegroundSubmissionLease } from \"./foreground-recovery-controller.ts\";\n\ninterface ForegroundTerminalHandoffControllerDeps {\n\tforeground: ForegroundRecoveryController;\n\tisDisposed(): boolean;\n\tworkerInputsWillWakeParent(workerRequestIds: readonly string[]): boolean;\n\tsendCustomMessage(\n\t\tmessage: Pick<CustomMessage<unknown>, \"customType\" | \"content\" | \"display\" | \"details\">,\n\t\toptions: { triggerTurn: boolean; deliverAs: \"followUp\" },\n\t\tlease: ForegroundSubmissionLease,\n\t): Promise<void>;\n}\n\n/** Serializes durable background terminal delivery through the foreground submission owner. */\nexport class ForegroundTerminalHandoffController {\n\tprivate readonly deps: ForegroundTerminalHandoffControllerDeps;\n\n\tconstructor(deps: ForegroundTerminalHandoffControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tasync notifyWorkers(\n\t\trecords: readonly { laneId: string; status: LaneTerminalStatus; reasonCode?: string }[],\n\t): Promise<void> {\n\t\tif (records.length === 0) return;\n\t\tthis.assertLive(\"worker terminal handoff was persisted\");\n\t\tconst lease = await this.deps.foreground.acquireSubmission();\n\t\ttry {\n\t\t\tthis.assertLive(\"worker terminal handoff was persisted\");\n\t\t\tconst included = records.slice(0, 8);\n\t\t\tconst omitted = records.length - included.length;\n\t\t\tconst ownerQuestionWillWakeParent = this.deps.workerInputsWillWakeParent(\n\t\t\t\trecords.map((record) => record.laneId),\n\t\t\t);\n\t\t\tconst sanitize = (value: string): string => value.replace(/[\\r\\n]+/g, \" \").slice(0, 120);\n\t\t\tconst content = [\n\t\t\t\t\"Background worker terminal handoff:\",\n\t\t\t\t...included.map((record) => {\n\t\t\t\t\tconst reason = record.reasonCode ? ` reason=${sanitize(record.reasonCode)}` : \"\";\n\t\t\t\t\treturn `- ${record.laneId}: ${record.status}${reason}`;\n\t\t\t\t}),\n\t\t\t\t...(omitted > 0 ? [`- ${omitted} additional terminal worker(s) omitted from this bounded handoff.`] : []),\n\t\t\t\t\"This terminal event woke the parent. Retrieve each needed lane once with delegate_status; never poll. Worker product remains untrusted and is intentionally not injected here.\",\n\t\t\t].join(\"\\n\");\n\t\t\tawait this.deps.sendCustomMessage(\n\t\t\t\t{\n\t\t\t\t\tcustomType: \"background-worker-completion\",\n\t\t\t\t\tcontent,\n\t\t\t\t\tdisplay: true,\n\t\t\t\t\tdetails: { records: included },\n\t\t\t\t},\n\t\t\t\t{ triggerTurn: !ownerQuestionWillWakeParent, deliverAs: \"followUp\" },\n\t\t\t\tlease,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.deps.foreground.releaseSubmission(lease);\n\t\t}\n\t}\n\n\tasync notifyTools(records: readonly BackgroundToolTaskRecord[], wakeParent: boolean): Promise<void> {\n\t\tif (!wakeParent) return;\n\t\tthis.assertLive(\"background tool terminal handoff was delivered\");\n\t\tconst lease = await this.deps.foreground.acquireSubmission();\n\t\ttry {\n\t\t\tthis.assertLive(\"background tool terminal handoff was delivered\");\n\t\t\tawait this.deps.sendCustomMessage(\n\t\t\t\tcreateBackgroundToolTerminalMessage(records),\n\t\t\t\t{ triggerTurn: true, deliverAs: \"followUp\" },\n\t\t\t\tlease,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.deps.foreground.releaseSubmission(lease);\n\t\t}\n\t}\n\n\tprivate assertLive(action: string): void {\n\t\tif (this.deps.isDisposed()) throw new Error(`Session disposed before ${action}`);\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"goal-loop-controller.d.ts","sourceRoot":"","sources":["../../src/core/goal-loop-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACX,2BAA2B,EAC3B,0BAA0B,EAE1B,2BAA2B,EAC3B,0BAA0B,EAC1B,aAAa,EACb,MAAM,8BAA8B,CAAC;AAKtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AA4BzG,MAAM,WAAW,sBAAsB;IACtC,mGAAmG;IACnG,sBAAsB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,mBAAmB,CAAC;IACnF,0EAA0E;IAC1E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,sFAAsF;IACtF,kBAAkB,IAAI,MAAM,GAAG,IAAI,CAAC;IACpC;;;;;OAKG;IACH,0BAA0B,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3G,uFAAuF;IACvF,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACpD,gFAAgF;IAChF,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5C;AAED,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyB;IAE9C,YAAY,IAAI,EAAE,sBAAsB,EAEvC;IAEK,gBAAgB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAgBhG;IAEK,gBAAgB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAsEhG;CACD"}
1
+ {"version":3,"file":"goal-loop-controller.d.ts","sourceRoot":"","sources":["../../src/core/goal-loop-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EACX,2BAA2B,EAC3B,0BAA0B,EAE1B,2BAA2B,EAC3B,0BAA0B,EAC1B,aAAa,EACb,MAAM,8BAA8B,CAAC;AAKtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AA4BzG,MAAM,WAAW,sBAAsB;IACtC,mGAAmG;IACnG,sBAAsB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,mBAAmB,CAAC;IACnF,0EAA0E;IAC1E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,sFAAsF;IACtF,kBAAkB,IAAI,MAAM,GAAG,IAAI,CAAC;IACpC;;;;;OAKG;IACH,0BAA0B,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3G,uFAAuF;IACvF,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACpD,gFAAgF;IAChF,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5C;AAED,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyB;IAE9C,YAAY,IAAI,EAAE,sBAAsB,EAEvC;IAEK,gBAAgB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAgBhG;IAEK,gBAAgB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAyEhG;CACD"}
@@ -11,6 +11,7 @@
11
11
  * for observability. Progress is inferred from real work and terminal goal updates; telemetry never
12
12
  * becomes an implicit execution limit.
13
13
  */
14
+ import { AgentBusyError } from "@caupulican/pi-agent-core";
14
15
  import { buildGoalContinuationPrompt, GOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE, } from "./goals/goal-continuation-prompt.js";
15
16
  /**
16
17
  * Resolve an owner-supplied durable goal budget. Wall-clock and spend counters are deliberately
@@ -89,6 +90,10 @@ export class GoalLoopController {
89
90
  result = await this.continueGoalOnce(options);
90
91
  }
91
92
  catch (error) {
93
+ // Prompt admission did not happen. A concurrent foreground owner is transient session
94
+ // coordination, not a consumed goal turn and not evidence that the goal is blocked.
95
+ if (error instanceof AgentBusyError)
96
+ throw error;
92
97
  turnsSubmitted++;
93
98
  this.deps.recordGoalContinuationPass({ turns: 1, wallClockMs: now() - passStartedAt, usageCursor });
94
99
  this.deps.recordGoalContinuationFailure(error);
@@ -1 +1 @@
1
- {"version":3,"file":"goal-loop-controller.js","sourceRoot":"","sources":["../../src/core/goal-loop-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Goal auto-continuation loop.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Drives the bounded \"keep the\n * active goal moving\" loop: each pass reads the goal runtime snapshot, and — only while the snapshot\n * says `continue` — submits one continuation prompt back through the session's own prompt path. It\n * owns no state; the goal state lives in the session log and is read fresh every pass. Termination is\n * gated only by real terminal conditions and limits the owner explicitly supplied: an optional\n * per-invocation turn cap, an optional per-invocation wall-clock cap, and an optional durable token\n * budget on the goal. Each submitted pass still reports exact token/wall-clock/spend contribution\n * for observability. Progress is inferred from real work and terminal goal updates; telemetry never\n * becomes an implicit execution limit.\n */\n\nimport type {\n\tGoalContinuationLoopOptions,\n\tGoalContinuationLoopResult,\n\tGoalContinuationLoopStopReason,\n\tGoalContinuationOnceOptions,\n\tGoalContinuationOnceResult,\n\tPromptOptions,\n} from \"./agent-session-contracts.ts\";\nimport {\n\tbuildGoalContinuationPrompt,\n\tGOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE,\n} from \"./goals/goal-continuation-prompt.ts\";\nimport type { GoalRuntimeSnapshot, GoalRuntimeSnapshotSettings } from \"./goals/goal-runtime-snapshot.ts\";\nimport type { GoalState } from \"./goals/goal-state.ts\";\n\n/**\n * Resolve an owner-supplied durable goal budget. Wall-clock and spend counters are deliberately\n * absent here: they are telemetry unless the public goal contract grows matching explicit limits.\n * Execution limits are policy inputs, not hidden runtime defaults.\n */\nfunction getExplicitGoalBudgetExhaustion(state: GoalState | undefined): string | undefined {\n\tif (!state || state.status !== \"active\") return undefined;\n\tif (state.tokenBudget !== undefined && (state.tokensUsed ?? 0) >= state.tokenBudget) {\n\t\treturn `token budget exhausted (${state.tokensUsed ?? 0}/${state.tokenBudget})`;\n\t}\n\treturn undefined;\n}\n\n/**\n * Maps a non-\"continue\" continuation action onto the loop's own stopReason vocabulary.\n * `\"waiting\"` (a worker is dispatched against an open requirement) gets its OWN benign stopReason so\n * a wait is never misreported as `continuation_not_allowed` (which reads as a terminal refusal) —\n * callers/telemetry can tell \"paused, will resume on its own\" apart from \"stopped, needs a human or a\n * new decision.\" Every other non-continue action (ask-user/finalize/stop) keeps the existing\n * `continuation_not_allowed` stopReason unchanged.\n */\nfunction nonContinueStopReason(snapshot: GoalRuntimeSnapshot): GoalContinuationLoopStopReason {\n\treturn snapshot.continuation.action === \"waiting\" ? \"worker_in_flight\" : \"continuation_not_allowed\";\n}\n\nexport interface GoalLoopControllerDeps {\n\t/** Read the current goal runtime snapshot (continuation decision + goal state) fresh each pass. */\n\tgetGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot;\n\t/** Submit a continuation prompt through the session's own prompt path. */\n\tprompt(text: string, options?: PromptOptions): Promise<void>;\n\t/** Capture the append-only branch cursor immediately before a continuation prompt. */\n\tcaptureUsageCursor(): string | null;\n\t/**\n\t * Persist one submitted pass's contribution to the active goal's durable cumulative accounting\n\t * (turns + active wall-clock; USD is attributed by the implementation from the session's own\n\t * spend, not passed in here — see `AgentSession.recordGoalContinuationPass`). Called once per\n\t * pass actually SUBMITTED (never for a no-op `continueGoalOnce` call).\n\t */\n\trecordGoalContinuationPass(pass: { turns: number; wallClockMs: number; usageCursor: string | null }): void;\n\t/** Persist an exhausted/non-retryable continuation failure as a stopped goal state. */\n\trecordGoalContinuationFailure(error: unknown): void;\n\t/** Persist a reason-specific budget terminal state before returning control. */\n\tmarkGoalBudgetLimited(reason: string): void;\n}\n\nexport class GoalLoopController {\n\tprivate readonly deps: GoalLoopControllerDeps;\n\n\tconstructor(deps: GoalLoopControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tasync continueGoalOnce(options: GoalContinuationOnceOptions): Promise<GoalContinuationOnceResult> {\n\t\tconst snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns: options.maxStallTurns });\n\n\t\tif (snapshot.continuation.action !== \"continue\") {\n\t\t\treturn { submitted: false, snapshot };\n\t\t}\n\n\t\tconst prompt = buildGoalContinuationPrompt();\n\t\tawait this.deps.prompt(prompt.text, {\n\t\t\texpandPromptTemplates: false,\n\t\t\tprocessSlashCommands: false,\n\t\t\tautoContinueGoal: false,\n\t\t\tinternalContextType: GOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE,\n\t\t});\n\n\t\treturn { submitted: true, snapshot, prompt };\n\t}\n\n\tasync continueGoalLoop(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult> {\n\t\tif (!Number.isSafeInteger(options.maxTurns) || options.maxTurns < 0) {\n\t\t\tthrow new Error(\"Goal continuation maxTurns must be a non-negative safe integer; 0 means unbounded.\");\n\t\t}\n\t\tlet turnsSubmitted = 0;\n\t\tconst hasExplicitTurnLimit = options.maxTurns > 0;\n\t\tconst now = options.now ?? Date.now;\n\t\tconst maxWallClockMs =\n\t\t\ttypeof options.maxWallClockMinutes === \"number\" && options.maxWallClockMinutes > 0\n\t\t\t\t? options.maxWallClockMinutes * 60_000\n\t\t\t\t: undefined;\n\t\tconst startedAt = now();\n\t\tconst hasReachedWallClockBudget = () => maxWallClockMs !== undefined && now() - startedAt >= maxWallClockMs;\n\t\tconst snapshot = () => this.deps.getGoalRuntimeSnapshot({ maxStallTurns: options.maxStallTurns });\n\n\t\tif (hasReachedWallClockBudget()) {\n\t\t\treturn { turnsSubmitted, stopReason: \"wall_clock_budget_reached\", finalSnapshot: snapshot() };\n\t\t}\n\n\t\twhile (!hasExplicitTurnLimit || turnsSubmitted < options.maxTurns) {\n\t\t\tconst beforeSnapshot = snapshot();\n\t\t\tif (beforeSnapshot.continuation.action !== \"continue\") {\n\t\t\t\treturn { turnsSubmitted, stopReason: nonContinueStopReason(beforeSnapshot), finalSnapshot: beforeSnapshot };\n\t\t\t}\n\n\t\t\t// Owner-supplied durable token budget — read fresh every pass so an invocation stops as\n\t\t\t// soon as its own latest persisted usage crosses the explicit ceiling.\n\t\t\tconst beforeBudgetExhaustion = getExplicitGoalBudgetExhaustion(beforeSnapshot.goalState);\n\t\t\tif (beforeBudgetExhaustion) {\n\t\t\t\tthis.deps.markGoalBudgetLimited(beforeBudgetExhaustion);\n\t\t\t\treturn { turnsSubmitted, stopReason: \"goal_budget_exhausted\", finalSnapshot: snapshot() };\n\t\t\t}\n\n\t\t\tconst passStartedAt = now();\n\t\t\tconst usageCursor = this.deps.captureUsageCursor();\n\t\t\tlet result: GoalContinuationOnceResult;\n\t\t\ttry {\n\t\t\t\tresult = await this.continueGoalOnce(options);\n\t\t\t} catch (error) {\n\t\t\t\tturnsSubmitted++;\n\t\t\t\tthis.deps.recordGoalContinuationPass({ turns: 1, wallClockMs: now() - passStartedAt, usageCursor });\n\t\t\t\tthis.deps.recordGoalContinuationFailure(error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tif (result.submitted) {\n\t\t\t\tturnsSubmitted++;\n\t\t\t\tthis.deps.recordGoalContinuationPass({ turns: 1, wallClockMs: now() - passStartedAt, usageCursor });\n\t\t\t}\n\n\t\t\tif (hasReachedWallClockBudget()) {\n\t\t\t\treturn { turnsSubmitted, stopReason: \"wall_clock_budget_reached\", finalSnapshot: snapshot() };\n\t\t\t}\n\n\t\t\tlet afterSnapshot = snapshot();\n\t\t\tconst afterBudgetExhaustion = getExplicitGoalBudgetExhaustion(afterSnapshot.goalState);\n\t\t\tif (afterBudgetExhaustion) {\n\t\t\t\tthis.deps.markGoalBudgetLimited(afterBudgetExhaustion);\n\t\t\t\tafterSnapshot = snapshot();\n\t\t\t\treturn { turnsSubmitted, stopReason: \"goal_budget_exhausted\", finalSnapshot: afterSnapshot };\n\t\t\t}\n\t\t\tif (afterSnapshot.continuation.action !== \"continue\") {\n\t\t\t\treturn { turnsSubmitted, stopReason: nonContinueStopReason(afterSnapshot), finalSnapshot: afterSnapshot };\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tturnsSubmitted,\n\t\t\tstopReason: \"max_turns_reached\",\n\t\t\tfinalSnapshot: snapshot(),\n\t\t};\n\t}\n}\n"]}
1
+ {"version":3,"file":"goal-loop-controller.js","sourceRoot":"","sources":["../../src/core/goal-loop-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Goal auto-continuation loop.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Drives the bounded \"keep the\n * active goal moving\" loop: each pass reads the goal runtime snapshot, and — only while the snapshot\n * says `continue` — submits one continuation prompt back through the session's own prompt path. It\n * owns no state; the goal state lives in the session log and is read fresh every pass. Termination is\n * gated only by real terminal conditions and limits the owner explicitly supplied: an optional\n * per-invocation turn cap, an optional per-invocation wall-clock cap, and an optional durable token\n * budget on the goal. Each submitted pass still reports exact token/wall-clock/spend contribution\n * for observability. Progress is inferred from real work and terminal goal updates; telemetry never\n * becomes an implicit execution limit.\n */\n\nimport { AgentBusyError } from \"@caupulican/pi-agent-core\";\nimport type {\n\tGoalContinuationLoopOptions,\n\tGoalContinuationLoopResult,\n\tGoalContinuationLoopStopReason,\n\tGoalContinuationOnceOptions,\n\tGoalContinuationOnceResult,\n\tPromptOptions,\n} from \"./agent-session-contracts.ts\";\nimport {\n\tbuildGoalContinuationPrompt,\n\tGOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE,\n} from \"./goals/goal-continuation-prompt.ts\";\nimport type { GoalRuntimeSnapshot, GoalRuntimeSnapshotSettings } from \"./goals/goal-runtime-snapshot.ts\";\nimport type { GoalState } from \"./goals/goal-state.ts\";\n\n/**\n * Resolve an owner-supplied durable goal budget. Wall-clock and spend counters are deliberately\n * absent here: they are telemetry unless the public goal contract grows matching explicit limits.\n * Execution limits are policy inputs, not hidden runtime defaults.\n */\nfunction getExplicitGoalBudgetExhaustion(state: GoalState | undefined): string | undefined {\n\tif (!state || state.status !== \"active\") return undefined;\n\tif (state.tokenBudget !== undefined && (state.tokensUsed ?? 0) >= state.tokenBudget) {\n\t\treturn `token budget exhausted (${state.tokensUsed ?? 0}/${state.tokenBudget})`;\n\t}\n\treturn undefined;\n}\n\n/**\n * Maps a non-\"continue\" continuation action onto the loop's own stopReason vocabulary.\n * `\"waiting\"` (a worker is dispatched against an open requirement) gets its OWN benign stopReason so\n * a wait is never misreported as `continuation_not_allowed` (which reads as a terminal refusal) —\n * callers/telemetry can tell \"paused, will resume on its own\" apart from \"stopped, needs a human or a\n * new decision.\" Every other non-continue action (ask-user/finalize/stop) keeps the existing\n * `continuation_not_allowed` stopReason unchanged.\n */\nfunction nonContinueStopReason(snapshot: GoalRuntimeSnapshot): GoalContinuationLoopStopReason {\n\treturn snapshot.continuation.action === \"waiting\" ? \"worker_in_flight\" : \"continuation_not_allowed\";\n}\n\nexport interface GoalLoopControllerDeps {\n\t/** Read the current goal runtime snapshot (continuation decision + goal state) fresh each pass. */\n\tgetGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot;\n\t/** Submit a continuation prompt through the session's own prompt path. */\n\tprompt(text: string, options?: PromptOptions): Promise<void>;\n\t/** Capture the append-only branch cursor immediately before a continuation prompt. */\n\tcaptureUsageCursor(): string | null;\n\t/**\n\t * Persist one submitted pass's contribution to the active goal's durable cumulative accounting\n\t * (turns + active wall-clock; USD is attributed by the implementation from the session's own\n\t * spend, not passed in here — see `AgentSession.recordGoalContinuationPass`). Called once per\n\t * pass actually SUBMITTED (never for a no-op `continueGoalOnce` call).\n\t */\n\trecordGoalContinuationPass(pass: { turns: number; wallClockMs: number; usageCursor: string | null }): void;\n\t/** Persist an exhausted/non-retryable continuation failure as a stopped goal state. */\n\trecordGoalContinuationFailure(error: unknown): void;\n\t/** Persist a reason-specific budget terminal state before returning control. */\n\tmarkGoalBudgetLimited(reason: string): void;\n}\n\nexport class GoalLoopController {\n\tprivate readonly deps: GoalLoopControllerDeps;\n\n\tconstructor(deps: GoalLoopControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tasync continueGoalOnce(options: GoalContinuationOnceOptions): Promise<GoalContinuationOnceResult> {\n\t\tconst snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns: options.maxStallTurns });\n\n\t\tif (snapshot.continuation.action !== \"continue\") {\n\t\t\treturn { submitted: false, snapshot };\n\t\t}\n\n\t\tconst prompt = buildGoalContinuationPrompt();\n\t\tawait this.deps.prompt(prompt.text, {\n\t\t\texpandPromptTemplates: false,\n\t\t\tprocessSlashCommands: false,\n\t\t\tautoContinueGoal: false,\n\t\t\tinternalContextType: GOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE,\n\t\t});\n\n\t\treturn { submitted: true, snapshot, prompt };\n\t}\n\n\tasync continueGoalLoop(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult> {\n\t\tif (!Number.isSafeInteger(options.maxTurns) || options.maxTurns < 0) {\n\t\t\tthrow new Error(\"Goal continuation maxTurns must be a non-negative safe integer; 0 means unbounded.\");\n\t\t}\n\t\tlet turnsSubmitted = 0;\n\t\tconst hasExplicitTurnLimit = options.maxTurns > 0;\n\t\tconst now = options.now ?? Date.now;\n\t\tconst maxWallClockMs =\n\t\t\ttypeof options.maxWallClockMinutes === \"number\" && options.maxWallClockMinutes > 0\n\t\t\t\t? options.maxWallClockMinutes * 60_000\n\t\t\t\t: undefined;\n\t\tconst startedAt = now();\n\t\tconst hasReachedWallClockBudget = () => maxWallClockMs !== undefined && now() - startedAt >= maxWallClockMs;\n\t\tconst snapshot = () => this.deps.getGoalRuntimeSnapshot({ maxStallTurns: options.maxStallTurns });\n\n\t\tif (hasReachedWallClockBudget()) {\n\t\t\treturn { turnsSubmitted, stopReason: \"wall_clock_budget_reached\", finalSnapshot: snapshot() };\n\t\t}\n\n\t\twhile (!hasExplicitTurnLimit || turnsSubmitted < options.maxTurns) {\n\t\t\tconst beforeSnapshot = snapshot();\n\t\t\tif (beforeSnapshot.continuation.action !== \"continue\") {\n\t\t\t\treturn { turnsSubmitted, stopReason: nonContinueStopReason(beforeSnapshot), finalSnapshot: beforeSnapshot };\n\t\t\t}\n\n\t\t\t// Owner-supplied durable token budget — read fresh every pass so an invocation stops as\n\t\t\t// soon as its own latest persisted usage crosses the explicit ceiling.\n\t\t\tconst beforeBudgetExhaustion = getExplicitGoalBudgetExhaustion(beforeSnapshot.goalState);\n\t\t\tif (beforeBudgetExhaustion) {\n\t\t\t\tthis.deps.markGoalBudgetLimited(beforeBudgetExhaustion);\n\t\t\t\treturn { turnsSubmitted, stopReason: \"goal_budget_exhausted\", finalSnapshot: snapshot() };\n\t\t\t}\n\n\t\t\tconst passStartedAt = now();\n\t\t\tconst usageCursor = this.deps.captureUsageCursor();\n\t\t\tlet result: GoalContinuationOnceResult;\n\t\t\ttry {\n\t\t\t\tresult = await this.continueGoalOnce(options);\n\t\t\t} catch (error) {\n\t\t\t\t// Prompt admission did not happen. A concurrent foreground owner is transient session\n\t\t\t\t// coordination, not a consumed goal turn and not evidence that the goal is blocked.\n\t\t\t\tif (error instanceof AgentBusyError) throw error;\n\t\t\t\tturnsSubmitted++;\n\t\t\t\tthis.deps.recordGoalContinuationPass({ turns: 1, wallClockMs: now() - passStartedAt, usageCursor });\n\t\t\t\tthis.deps.recordGoalContinuationFailure(error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tif (result.submitted) {\n\t\t\t\tturnsSubmitted++;\n\t\t\t\tthis.deps.recordGoalContinuationPass({ turns: 1, wallClockMs: now() - passStartedAt, usageCursor });\n\t\t\t}\n\n\t\t\tif (hasReachedWallClockBudget()) {\n\t\t\t\treturn { turnsSubmitted, stopReason: \"wall_clock_budget_reached\", finalSnapshot: snapshot() };\n\t\t\t}\n\n\t\t\tlet afterSnapshot = snapshot();\n\t\t\tconst afterBudgetExhaustion = getExplicitGoalBudgetExhaustion(afterSnapshot.goalState);\n\t\t\tif (afterBudgetExhaustion) {\n\t\t\t\tthis.deps.markGoalBudgetLimited(afterBudgetExhaustion);\n\t\t\t\tafterSnapshot = snapshot();\n\t\t\t\treturn { turnsSubmitted, stopReason: \"goal_budget_exhausted\", finalSnapshot: afterSnapshot };\n\t\t\t}\n\t\t\tif (afterSnapshot.continuation.action !== \"continue\") {\n\t\t\t\treturn { turnsSubmitted, stopReason: nonContinueStopReason(afterSnapshot), finalSnapshot: afterSnapshot };\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tturnsSubmitted,\n\t\t\tstopReason: \"max_turns_reached\",\n\t\t\tfinalSnapshot: snapshot(),\n\t\t};\n\t}\n}\n"]}
@@ -10,6 +10,8 @@ export interface GoalAutoContinueControllerDeps {
10
10
  getGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot;
11
11
  hasInFlightLaneForGoal(goalId: string): boolean;
12
12
  continueGoalLoop(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult>;
13
+ isForegroundBusy(): boolean;
14
+ waitForForegroundIdle(): Promise<void>;
13
15
  markGoalToolUnavailable(): void;
14
16
  emit(event: AgentSessionEvent): void;
15
17
  }
@@ -22,6 +24,7 @@ export declare class GoalAutoContinueController {
22
24
  clearTimer(): void;
23
25
  scheduleFromIdle(options?: PromptOptions): void;
24
26
  continueExclusive(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult>;
27
+ private unavailableResult;
25
28
  private runScheduled;
26
29
  private skippedResult;
27
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"goal-auto-continue-controller.d.ts","sourceRoot":"","sources":["../../../src/core/goals/goal-auto-continue-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,iBAAiB,EACjB,2BAA2B,EAC3B,0BAA0B,EAC1B,aAAa,EACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAEnG,MAAM,WAAW,8BAA8B;IAC9C,UAAU,IAAI,OAAO,CAAC;IACtB,gBAAgB,IAAI,OAAO,CAAC;IAC5B,kBAAkB,IAAI,eAAe,CAAC;IACtC,yBAAyB,IAAI,sBAAsB,CAAC;IACpD,sBAAsB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,mBAAmB,CAAC;IACnF,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAChD,gBAAgB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC5F,uBAAuB,IAAI,IAAI,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACrC;AAED,mFAAmF;AACnF,qBAAa,0BAA0B;IACtC,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;IAEtD,YAAY,IAAI,EAAE,8BAA8B,EAE/C;IAED,UAAU,IAAI,IAAI,CAKjB;IAED,gBAAgB,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAuB9C;IAEK,iBAAiB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAajG;YAEa,YAAY;IAoB1B,OAAO,CAAC,aAAa;CAUrB"}
1
+ {"version":3,"file":"goal-auto-continue-controller.d.ts","sourceRoot":"","sources":["../../../src/core/goals/goal-auto-continue-controller.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,iBAAiB,EACjB,2BAA2B,EAC3B,0BAA0B,EAC1B,aAAa,EACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAEnG,MAAM,WAAW,8BAA8B;IAC9C,UAAU,IAAI,OAAO,CAAC;IACtB,gBAAgB,IAAI,OAAO,CAAC;IAC5B,kBAAkB,IAAI,eAAe,CAAC;IACtC,yBAAyB,IAAI,sBAAsB,CAAC;IACpD,sBAAsB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,mBAAmB,CAAC;IACnF,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAChD,gBAAgB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC5F,gBAAgB,IAAI,OAAO,CAAC;IAC5B,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,uBAAuB,IAAI,IAAI,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACrC;AAED,mFAAmF;AACnF,qBAAa,0BAA0B;IACtC,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;IAEtD,YAAY,IAAI,EAAE,8BAA8B,EAE/C;IAED,UAAU,IAAI,IAAI,CAKjB;IAED,gBAAgB,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAuB9C;IAEK,iBAAiB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAqBjG;IAED,OAAO,CAAC,iBAAiB;YAOX,YAAY;IAoB1B,OAAO,CAAC,aAAa;CAUrB"}
@@ -1,3 +1,4 @@
1
+ import { AgentBusyError } from "@caupulican/pi-agent-core";
1
2
  /** Owns the single-flight goal continuation loop and its foreground-idle timer. */
2
3
  export class GoalAutoContinueController {
3
4
  constructor(deps) {
@@ -40,20 +41,40 @@ export class GoalAutoContinueController {
40
41
  async continueExclusive(options) {
41
42
  if (this._isContinuing)
42
43
  return this.skippedResult(options, "already_continuing");
43
- if (this.deps.isDisposed())
44
- return this.skippedResult(options, "session_disposed");
45
- if (!this.deps.isGoalToolActive()) {
46
- this.deps.markGoalToolUnavailable();
47
- return this.skippedResult(options, "goal_tool_unavailable");
48
- }
44
+ const initialGuard = this.unavailableResult(options);
45
+ if (initialGuard)
46
+ return initialGuard;
49
47
  this._isContinuing = true;
50
48
  try {
51
- return await this.deps.continueGoalLoop(options);
49
+ while (true) {
50
+ if (this.deps.isForegroundBusy())
51
+ await this.deps.waitForForegroundIdle();
52
+ const postWaitGuard = this.unavailableResult(options);
53
+ if (postWaitGuard)
54
+ return postWaitGuard;
55
+ try {
56
+ return await this.deps.continueGoalLoop(options);
57
+ }
58
+ catch (error) {
59
+ // A different foreground owner can acquire the Agent after the idle event but before
60
+ // prompt admission. Wait for that exact run and retry without terminalizing the goal.
61
+ if (!(error instanceof AgentBusyError))
62
+ throw error;
63
+ }
64
+ }
52
65
  }
53
66
  finally {
54
67
  this._isContinuing = false;
55
68
  }
56
69
  }
70
+ unavailableResult(options) {
71
+ if (this.deps.isDisposed())
72
+ return this.skippedResult(options, "session_disposed");
73
+ if (this.deps.isGoalToolActive())
74
+ return undefined;
75
+ this.deps.markGoalToolUnavailable();
76
+ return this.skippedResult(options, "goal_tool_unavailable");
77
+ }
57
78
  async runScheduled() {
58
79
  if (this._isContinuing || this.deps.isDisposed())
59
80
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"goal-auto-continue-controller.js","sourceRoot":"","sources":["../../../src/core/goals/goal-auto-continue-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tAgentSessionEvent,\n\tGoalContinuationLoopOptions,\n\tGoalContinuationLoopResult,\n\tPromptOptions,\n} from \"../agent-session.ts\";\nimport type { ModelCapabilityProfile } from \"../model-capability.ts\";\nimport type { SettingsManager } from \"../settings-manager.ts\";\nimport type { GoalRuntimeSnapshot, GoalRuntimeSnapshotSettings } from \"./goal-runtime-snapshot.ts\";\n\nexport interface GoalAutoContinueControllerDeps {\n\tisDisposed(): boolean;\n\tisGoalToolActive(): boolean;\n\tgetSettingsManager(): SettingsManager;\n\tgetModelCapabilityProfile(): ModelCapabilityProfile;\n\tgetGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot;\n\thasInFlightLaneForGoal(goalId: string): boolean;\n\tcontinueGoalLoop(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult>;\n\tmarkGoalToolUnavailable(): void;\n\temit(event: AgentSessionEvent): void;\n}\n\n/** Owns the single-flight goal continuation loop and its foreground-idle timer. */\nexport class GoalAutoContinueController {\n\tprivate _timer: ReturnType<typeof setTimeout> | undefined;\n\tprivate _isContinuing = false;\n\tprivate readonly deps: GoalAutoContinueControllerDeps;\n\n\tconstructor(deps: GoalAutoContinueControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tclearTimer(): void {\n\t\tif (this._timer !== undefined) {\n\t\t\tclearTimeout(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\tscheduleFromIdle(options?: PromptOptions): void {\n\t\tif (options?.autoContinueGoal === false || this._isContinuing || this.deps.isDisposed()) return;\n\t\tif (!this.deps.getModelCapabilityProfile().backgroundLanesEnabled) return;\n\n\t\tconst { maxStallTurns, goalAutoContinue, goalAutoContinueDelayMs } = this.deps\n\t\t\t.getSettingsManager()\n\t\t\t.getAutonomySettings();\n\t\tif (!goalAutoContinue) return;\n\t\tconst snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });\n\t\tif (snapshot.continuation.action !== \"continue\") return;\n\t\tconst activeGoalId = snapshot.goalState?.goalId;\n\t\tif (activeGoalId !== undefined && this.deps.hasInFlightLaneForGoal(activeGoalId)) return;\n\n\t\tthis.clearTimer();\n\t\tthis._timer = setTimeout(() => {\n\t\t\tthis._timer = undefined;\n\t\t\tvoid this.runScheduled();\n\t\t}, goalAutoContinueDelayMs);\n\t\tconst timer = this._timer;\n\t\tif (typeof timer === \"object\" && timer && \"unref\" in timer) {\n\t\t\tconst { unref } = timer as { unref?: () => void };\n\t\t\tunref?.call(timer);\n\t\t}\n\t}\n\n\tasync continueExclusive(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult> {\n\t\tif (this._isContinuing) return this.skippedResult(options, \"already_continuing\");\n\t\tif (this.deps.isDisposed()) return this.skippedResult(options, \"session_disposed\");\n\t\tif (!this.deps.isGoalToolActive()) {\n\t\t\tthis.deps.markGoalToolUnavailable();\n\t\t\treturn this.skippedResult(options, \"goal_tool_unavailable\");\n\t\t}\n\t\tthis._isContinuing = true;\n\t\ttry {\n\t\t\treturn await this.deps.continueGoalLoop(options);\n\t\t} finally {\n\t\t\tthis._isContinuing = false;\n\t\t}\n\t}\n\n\tprivate async runScheduled(): Promise<void> {\n\t\tif (this._isContinuing || this.deps.isDisposed()) return;\n\t\tconst { maxStallTurns, goalContinueTurns, goalContinueMaxWallClockMinutes, goalAutoContinue } = this.deps\n\t\t\t.getSettingsManager()\n\t\t\t.getAutonomySettings();\n\t\tif (!goalAutoContinue) return;\n\t\tconst snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });\n\t\tif (snapshot.continuation.action !== \"continue\") return;\n\t\ttry {\n\t\t\tawait this.continueExclusive({\n\t\t\t\tmaxTurns: goalContinueTurns,\n\t\t\t\tmaxStallTurns,\n\t\t\t\tmaxWallClockMinutes: goalContinueMaxWallClockMinutes,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthis.deps.emit({ type: \"warning\", message: `Goal auto-continuation failed: ${message}` });\n\t\t}\n\t}\n\n\tprivate skippedResult(\n\t\toptions: GoalContinuationLoopOptions,\n\t\tstopReason: \"already_continuing\" | \"session_disposed\" | \"goal_tool_unavailable\",\n\t): GoalContinuationLoopResult {\n\t\treturn {\n\t\t\tturnsSubmitted: 0,\n\t\t\tstopReason,\n\t\t\tfinalSnapshot: this.deps.getGoalRuntimeSnapshot({ maxStallTurns: options.maxStallTurns }),\n\t\t};\n\t}\n}\n"]}
1
+ {"version":3,"file":"goal-auto-continue-controller.js","sourceRoot":"","sources":["../../../src/core/goals/goal-auto-continue-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import { AgentBusyError } from \"@caupulican/pi-agent-core\";\nimport type {\n\tAgentSessionEvent,\n\tGoalContinuationLoopOptions,\n\tGoalContinuationLoopResult,\n\tPromptOptions,\n} from \"../agent-session.ts\";\nimport type { ModelCapabilityProfile } from \"../model-capability.ts\";\nimport type { SettingsManager } from \"../settings-manager.ts\";\nimport type { GoalRuntimeSnapshot, GoalRuntimeSnapshotSettings } from \"./goal-runtime-snapshot.ts\";\n\nexport interface GoalAutoContinueControllerDeps {\n\tisDisposed(): boolean;\n\tisGoalToolActive(): boolean;\n\tgetSettingsManager(): SettingsManager;\n\tgetModelCapabilityProfile(): ModelCapabilityProfile;\n\tgetGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot;\n\thasInFlightLaneForGoal(goalId: string): boolean;\n\tcontinueGoalLoop(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult>;\n\tisForegroundBusy(): boolean;\n\twaitForForegroundIdle(): Promise<void>;\n\tmarkGoalToolUnavailable(): void;\n\temit(event: AgentSessionEvent): void;\n}\n\n/** Owns the single-flight goal continuation loop and its foreground-idle timer. */\nexport class GoalAutoContinueController {\n\tprivate _timer: ReturnType<typeof setTimeout> | undefined;\n\tprivate _isContinuing = false;\n\tprivate readonly deps: GoalAutoContinueControllerDeps;\n\n\tconstructor(deps: GoalAutoContinueControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tclearTimer(): void {\n\t\tif (this._timer !== undefined) {\n\t\t\tclearTimeout(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\tscheduleFromIdle(options?: PromptOptions): void {\n\t\tif (options?.autoContinueGoal === false || this._isContinuing || this.deps.isDisposed()) return;\n\t\tif (!this.deps.getModelCapabilityProfile().backgroundLanesEnabled) return;\n\n\t\tconst { maxStallTurns, goalAutoContinue, goalAutoContinueDelayMs } = this.deps\n\t\t\t.getSettingsManager()\n\t\t\t.getAutonomySettings();\n\t\tif (!goalAutoContinue) return;\n\t\tconst snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });\n\t\tif (snapshot.continuation.action !== \"continue\") return;\n\t\tconst activeGoalId = snapshot.goalState?.goalId;\n\t\tif (activeGoalId !== undefined && this.deps.hasInFlightLaneForGoal(activeGoalId)) return;\n\n\t\tthis.clearTimer();\n\t\tthis._timer = setTimeout(() => {\n\t\t\tthis._timer = undefined;\n\t\t\tvoid this.runScheduled();\n\t\t}, goalAutoContinueDelayMs);\n\t\tconst timer = this._timer;\n\t\tif (typeof timer === \"object\" && timer && \"unref\" in timer) {\n\t\t\tconst { unref } = timer as { unref?: () => void };\n\t\t\tunref?.call(timer);\n\t\t}\n\t}\n\n\tasync continueExclusive(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult> {\n\t\tif (this._isContinuing) return this.skippedResult(options, \"already_continuing\");\n\t\tconst initialGuard = this.unavailableResult(options);\n\t\tif (initialGuard) return initialGuard;\n\t\tthis._isContinuing = true;\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tif (this.deps.isForegroundBusy()) await this.deps.waitForForegroundIdle();\n\t\t\t\tconst postWaitGuard = this.unavailableResult(options);\n\t\t\t\tif (postWaitGuard) return postWaitGuard;\n\t\t\t\ttry {\n\t\t\t\t\treturn await this.deps.continueGoalLoop(options);\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// A different foreground owner can acquire the Agent after the idle event but before\n\t\t\t\t\t// prompt admission. Wait for that exact run and retry without terminalizing the goal.\n\t\t\t\t\tif (!(error instanceof AgentBusyError)) throw error;\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._isContinuing = false;\n\t\t}\n\t}\n\n\tprivate unavailableResult(options: GoalContinuationLoopOptions): GoalContinuationLoopResult | undefined {\n\t\tif (this.deps.isDisposed()) return this.skippedResult(options, \"session_disposed\");\n\t\tif (this.deps.isGoalToolActive()) return undefined;\n\t\tthis.deps.markGoalToolUnavailable();\n\t\treturn this.skippedResult(options, \"goal_tool_unavailable\");\n\t}\n\n\tprivate async runScheduled(): Promise<void> {\n\t\tif (this._isContinuing || this.deps.isDisposed()) return;\n\t\tconst { maxStallTurns, goalContinueTurns, goalContinueMaxWallClockMinutes, goalAutoContinue } = this.deps\n\t\t\t.getSettingsManager()\n\t\t\t.getAutonomySettings();\n\t\tif (!goalAutoContinue) return;\n\t\tconst snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });\n\t\tif (snapshot.continuation.action !== \"continue\") return;\n\t\ttry {\n\t\t\tawait this.continueExclusive({\n\t\t\t\tmaxTurns: goalContinueTurns,\n\t\t\t\tmaxStallTurns,\n\t\t\t\tmaxWallClockMinutes: goalContinueMaxWallClockMinutes,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthis.deps.emit({ type: \"warning\", message: `Goal auto-continuation failed: ${message}` });\n\t\t}\n\t}\n\n\tprivate skippedResult(\n\t\toptions: GoalContinuationLoopOptions,\n\t\tstopReason: \"already_continuing\" | \"session_disposed\" | \"goal_tool_unavailable\",\n\t): GoalContinuationLoopResult {\n\t\treturn {\n\t\t\tturnsSubmitted: 0,\n\t\t\tstopReason,\n\t\t\tfinalSnapshot: this.deps.getGoalRuntimeSnapshot({ maxStallTurns: options.maxStallTurns }),\n\t\t};\n\t}\n}\n"]}
@@ -36,11 +36,20 @@ export declare class HumanInputController {
36
36
  private readonly deps;
37
37
  private tail;
38
38
  private readonly activeRequestIds;
39
+ private indexedSessionManager;
40
+ private indexedLeafId;
41
+ private workerInputIndexInitialized;
42
+ private readonly workerInputProjection;
39
43
  constructor(deps: HumanInputControllerDeps);
40
44
  queueWorkerInput(request: WorkerHumanInputRequest): void;
41
45
  schedulePendingWorkerInputs(): void;
46
+ /** True when every terminal lane already has a durable owner question scheduled for delivery. */
47
+ workerInputsWillWakeParent(workerRequestIds: readonly string[]): boolean;
42
48
  resumePending(): Promise<boolean>;
43
49
  private scheduleWorkerInput;
50
+ private getWorkerInputsRequiringDelivery;
51
+ private synchronizeWorkerInputIndex;
52
+ private rebuildWorkerInputIndex;
44
53
  private resolveWorkerInput;
45
54
  }
46
55
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"human-input-controller.d.ts","sourceRoot":"","sources":["../../src/core/human-input-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,GAAG,EAAgB,KAAK,EAAqB,MAAM,mBAAmB,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAalF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,MAAM,WAAW,uBAAuB;IACvC,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,UAAU,wBAAwB;IACjC,iBAAiB,IAAI,cAAc,CAAC;IACpC,YAAY,IAAI,kBAAkB,GAAG,SAAS,CAAC;IAC/C,gBAAgB,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC7C,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,UAAU,IAAI,OAAO,CAAC;IACtB,WAAW,IAAI,OAAO,CAAC;IACvB,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACnC,gBAAgB,IAAI,aAAa,CAAC;IAClC,aAAa,IAAI,iBAAiB,GAAG,SAAS,CAAC;IAC/C,cAAc,CAAC,QAAQ,EAAE,YAAY,GAAG,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,iBAAiB,CAChB,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,EACrF,OAAO,EAAE;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,UAAU,CAAA;KAAE,GACtD,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,qFAAqF;AACrF,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2B;IAChD,OAAO,CAAC,IAAI,CAAoC;IAChD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IAEtD,YAAY,IAAI,EAAE,wBAAwB,EAEzC;IAED,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAkCvD;IAED,2BAA2B,IAAI,IAAI,CAIlC;IAEK,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CA8EtC;IAED,OAAO,CAAC,mBAAmB;YAoBb,kBAAkB;CAmChC"}
1
+ {"version":3,"file":"human-input-controller.d.ts","sourceRoot":"","sources":["../../src/core/human-input-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACnF,OAAO,KAAK,EAAE,GAAG,EAAgB,KAAK,EAAqB,MAAM,mBAAmB,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAYlF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,MAAM,WAAW,uBAAuB;IACvC,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,UAAU,wBAAwB;IACjC,iBAAiB,IAAI,cAAc,CAAC;IACpC,YAAY,IAAI,kBAAkB,GAAG,SAAS,CAAC;IAC/C,gBAAgB,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC7C,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,UAAU,IAAI,OAAO,CAAC;IACtB,WAAW,IAAI,OAAO,CAAC;IACvB,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACnC,gBAAgB,IAAI,aAAa,CAAC;IAClC,aAAa,IAAI,iBAAiB,GAAG,SAAS,CAAC;IAC/C,cAAc,CAAC,QAAQ,EAAE,YAAY,GAAG,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,iBAAiB,CAChB,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,EACrF,OAAO,EAAE;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,UAAU,CAAA;KAAE,GACtD,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,qFAAqF;AACrF,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2B;IAChD,OAAO,CAAC,IAAI,CAAoC;IAChD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,qBAAqB,CAA6B;IAC1D,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,2BAA2B,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAoC;IAE1E,YAAY,IAAI,EAAE,wBAAwB,EAEzC;IAED,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAiCvD;IAED,2BAA2B,IAAI,IAAI,CAIlC;IAED,iGAAiG;IACjG,0BAA0B,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAUvE;IAEK,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CA8EtC;IAED,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,gCAAgC;IAKxC,OAAO,CAAC,2BAA2B;IA8BnC,OAAO,CAAC,uBAAuB;YAOjB,kBAAkB;CAqChC"}
@@ -1,15 +1,18 @@
1
- import { beginHumanInputRequest, createHumanInputRequest, formatHumanInputAnswerText, getLatestHumanInputSnapshots, getResumableHumanInputSnapshot, getWorkerHumanInputsRequiringDelivery, HUMAN_INPUT_WORKER_RESPONSE_CUSTOM_TYPE, resolveHumanInput, } from "./human-input.js";
1
+ import { beginHumanInputRequest, createHumanInputRequest, formatHumanInputAnswerText, getResumableHumanInputSnapshot, HUMAN_INPUT_WORKER_RESPONSE_CUSTOM_TYPE, resolveHumanInput, WorkerHumanInputProjection, } from "./human-input.js";
2
2
  /** Owns durable question replay and serialized worker-to-owner question delivery. */
3
3
  export class HumanInputController {
4
4
  constructor(deps) {
5
5
  this.tail = Promise.resolve();
6
6
  this.activeRequestIds = new Set();
7
+ this.indexedLeafId = null;
8
+ this.workerInputIndexInitialized = false;
9
+ this.workerInputProjection = new WorkerHumanInputProjection();
7
10
  this.deps = deps;
8
11
  }
9
12
  queueWorkerInput(request) {
10
13
  const sessionManager = this.deps.getSessionManager();
11
- const existing = getLatestHumanInputSnapshots(sessionManager).find((snapshot) => snapshot.request.workerRequestId === request.workerRequestId);
12
- if (existing)
14
+ this.synchronizeWorkerInputIndex();
15
+ if (this.workerInputProjection.hasKnownWorkerRequest(request.workerRequestId))
13
16
  return;
14
17
  const blockerText = request.blockers
15
18
  .slice(0, 4)
@@ -38,14 +41,25 @@ export class HumanInputController {
38
41
  acceptsImages: false,
39
42
  });
40
43
  beginHumanInputRequest(sessionManager, question);
44
+ this.synchronizeWorkerInputIndex();
41
45
  if (!this.deps.isStreaming())
42
46
  this.scheduleWorkerInput(question);
43
47
  }
44
48
  schedulePendingWorkerInputs() {
45
- for (const snapshot of getWorkerHumanInputsRequiringDelivery(this.deps.getSessionManager())) {
49
+ for (const snapshot of this.getWorkerInputsRequiringDelivery()) {
46
50
  this.scheduleWorkerInput(snapshot);
47
51
  }
48
52
  }
53
+ /** True when every terminal lane already has a durable owner question scheduled for delivery. */
54
+ workerInputsWillWakeParent(workerRequestIds) {
55
+ if (workerRequestIds.length === 0 ||
56
+ this.deps.getUIContext() === undefined ||
57
+ this.deps.getExtensionMode() === "print") {
58
+ return false;
59
+ }
60
+ this.synchronizeWorkerInputIndex();
61
+ return workerRequestIds.every((workerRequestId) => this.workerInputProjection.requiresDelivery(workerRequestId));
62
+ }
49
63
  async resumePending() {
50
64
  if (this.deps.isDisposed() || this.deps.isStreaming())
51
65
  return false;
@@ -112,7 +126,7 @@ export class HumanInputController {
112
126
  await this.deps.runAgentPrompt(toolResult);
113
127
  resumed = true;
114
128
  }
115
- for (const workerInput of getWorkerHumanInputsRequiringDelivery(sessionManager)) {
129
+ for (const workerInput of this.getWorkerInputsRequiringDelivery()) {
116
130
  if (this.activeRequestIds.has(workerInput.request.requestId))
117
131
  continue;
118
132
  this.activeRequestIds.add(workerInput.request.requestId);
@@ -142,6 +156,45 @@ export class HumanInputController {
142
156
  this.activeRequestIds.delete(requestId);
143
157
  });
144
158
  }
159
+ getWorkerInputsRequiringDelivery() {
160
+ this.synchronizeWorkerInputIndex();
161
+ return this.workerInputProjection.getRequiringDelivery();
162
+ }
163
+ synchronizeWorkerInputIndex() {
164
+ const sessionManager = this.deps.getSessionManager();
165
+ const currentLeafId = sessionManager.getLeafId();
166
+ if (!this.workerInputIndexInitialized || this.indexedSessionManager !== sessionManager) {
167
+ this.rebuildWorkerInputIndex(sessionManager);
168
+ return;
169
+ }
170
+ if (currentLeafId === this.indexedLeafId)
171
+ return;
172
+ const appendedEntries = [];
173
+ let cursor = currentLeafId;
174
+ while (cursor !== null && cursor !== this.indexedLeafId) {
175
+ const entry = sessionManager.getEntry(cursor);
176
+ if (!entry) {
177
+ this.rebuildWorkerInputIndex(sessionManager);
178
+ return;
179
+ }
180
+ appendedEntries.push(entry);
181
+ cursor = entry.parentId;
182
+ }
183
+ if (cursor !== this.indexedLeafId) {
184
+ this.rebuildWorkerInputIndex(sessionManager);
185
+ return;
186
+ }
187
+ for (let index = appendedEntries.length - 1; index >= 0; index--) {
188
+ this.workerInputProjection.apply(appendedEntries[index]);
189
+ }
190
+ this.indexedLeafId = currentLeafId;
191
+ }
192
+ rebuildWorkerInputIndex(sessionManager) {
193
+ this.workerInputProjection.reset(sessionManager.getBranch());
194
+ this.indexedSessionManager = sessionManager;
195
+ this.indexedLeafId = sessionManager.getLeafId();
196
+ this.workerInputIndexInitialized = true;
197
+ }
145
198
  async resolveWorkerInput(initial) {
146
199
  const ui = this.deps.getUIContext();
147
200
  if (!ui || this.deps.getExtensionMode() === "print")
@@ -158,6 +211,7 @@ export class HumanInputController {
158
211
  getImageStore: () => this.deps.getImageStore(),
159
212
  })
160
213
  : { snapshot: initial, imageContents: [] };
214
+ this.synchronizeWorkerInputIndex();
161
215
  await this.deps.waitForIdle();
162
216
  if (this.deps.isDisposed())
163
217
  return false;
@@ -174,6 +228,7 @@ export class HumanInputController {
174
228
  workerRequestId: initial.request.workerRequestId,
175
229
  },
176
230
  }, { triggerTurn: true, deliverAs: "followUp" });
231
+ this.synchronizeWorkerInputIndex();
177
232
  return true;
178
233
  }
179
234
  }