@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,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { basename, dirname } from "node:path";
4
- import { compactToolResultDetailsForRetention, createCustomMessage, DEFAULT_STREAM_IDLE, withStreamIdleWatchdog, } from "@caupulican/pi-agent-core";
4
+ import { AgentBusyError, compactToolResultDetailsForRetention, createCustomMessage, DEFAULT_STREAM_IDLE, withStreamIdleWatchdog, } from "@caupulican/pi-agent-core";
5
5
  import { cleanupSessionResources, getSupportedThinkingLevels, modelsAreEqual, streamSimple } from "@caupulican/pi-ai";
6
6
  import { getAgentDir } from "../config.js";
7
7
  import { stripFrontmatter } from "../utils/frontmatter.js";
@@ -11,7 +11,7 @@ import { buildForegroundEnvelope, formatForegroundEnvelopeObservation } from "./
11
11
  import { evaluateToolGate } from "./autonomy/gates.js";
12
12
  import { AutonomyTelemetry } from "./autonomy-telemetry.js";
13
13
  import { BackgroundLaneController } from "./background-lane-controller.js";
14
- import { BACKGROUND_TOOL_TASK_CUSTOM_TYPE, BackgroundToolTaskController, createBackgroundToolTerminalMessage, DEFAULT_BACKGROUND_TOOL_CALL_AFTER_MS, loadBackgroundToolTaskRecordsNewestFirst, } from "./background-tool-task-controller.js";
14
+ import { BACKGROUND_TOOL_TASK_CUSTOM_TYPE, BackgroundToolTaskController, DEFAULT_BACKGROUND_TOOL_CALL_AFTER_MS, loadBackgroundToolTaskRecordsNewestFirst, } from "./background-tool-task-controller.js";
15
15
  import { BashExecutionController } from "./bash-execution-controller.js";
16
16
  import { CompactionController } from "./compaction-controller.js";
17
17
  import { CompactionSupport } from "./compaction-support.js";
@@ -21,11 +21,11 @@ import { downgradeReasoning, estimateTurnCostUsd, evaluateCostGuard } from "./co
21
21
  import { appendWorkerClaimSnapshot, getWorkerClaimSnapshots } from "./delegation/session-worker-claim.js";
22
22
  import { FailureCorpusRecorder } from "./failure-corpus.js";
23
23
  import { ForegroundRecoveryController } from "./foreground-recovery-controller.js";
24
+ import { ForegroundTerminalHandoffController } from "./foreground-terminal-handoff-controller.js";
24
25
  import { GatewayRegistry } from "./gateways/channel-provider.js";
25
26
  import { injectCompactGoalContext } from "./goals/compact-goal-context.js";
26
27
  import { GoalSessionController } from "./goals/goal-session-controller.js";
27
28
  import { constrainStreamIdleToHttpTimeout } from "./http-dispatcher.js";
28
- import { getWorkerHumanInputsRequiringDelivery } from "./human-input.js";
29
29
  import { HumanInputController } from "./human-input-controller.js";
30
30
  import { appendLearningDecisionSnapshot, getLearningDecisionSnapshots } from "./learning/session-learning-decision.js";
31
31
  import { isPromotedFrontmatter, SkillCurator } from "./learning/skill-curator.js";
@@ -335,7 +335,7 @@ export class AgentSession {
335
335
  getToolPromptGuidelines: (name) => this._runtimeBuilder.getToolPromptGuidelines(name),
336
336
  getModelAdaptationRules: () => this._toolProtocol.getAdaptationRulesForPrompt(),
337
337
  getActiveExtensions: () => this._extensionRunner.activeExtensions,
338
- getContextWindow: () => this.model?.contextWindow,
338
+ getModelCapabilityProfile: () => this.getModelCapabilityProfile(),
339
339
  getThinkingLevel: () => this.thinkingLevel,
340
340
  // The evidence-gated tool-selection hint block — self-gated by kill switch/evidence
341
341
  // thresholds inside getActiveHints() itself, so this is a plain always-on pass-through.
@@ -358,13 +358,13 @@ export class AgentSession {
358
358
  getSessionManager: () => this.sessionManager,
359
359
  getUIContext: () => this._extensionUIContext,
360
360
  getExtensionMode: () => this._extensionMode,
361
- waitForIdle: () => this.agent.waitForIdle(),
361
+ waitForIdle: () => this._foregroundRecovery.waitForIdle(),
362
362
  isDisposed: () => this._disposed,
363
363
  isStreaming: () => this.isStreaming,
364
364
  getModel: () => this.model,
365
365
  getArtifactStore: () => this._getToolArtifactStore(),
366
366
  getImageStore: () => this._getSessionImageStore(),
367
- runAgentPrompt: (messages) => this._runAgentPrompt(messages),
367
+ runAgentPrompt: (messages) => this._foregroundRecovery.runAgentPrompt(messages),
368
368
  sendCustomMessage: (message, options) => this.sendCustomMessage(message, options),
369
369
  emitWarning: (message) => this._emit({ type: "warning", message }),
370
370
  });
@@ -396,7 +396,7 @@ export class AgentSession {
396
396
  getCapabilityEnvelope: () => this.capabilityEnvelope,
397
397
  getModelCapabilityProfile: () => this.getModelCapabilityProfile(),
398
398
  emit: (event) => this._emit(event),
399
- notifyWorkerTerminalHandoff: (records) => this._notifyWorkerTerminalHandoff(records),
399
+ notifyWorkerTerminalHandoff: (records) => this._terminalHandoffs.notifyWorkers(records),
400
400
  emitAutonomyTelemetry: (event) => this._emitAutonomyTelemetry(event),
401
401
  getGoalStateSnapshot: () => this.getGoalStateSnapshot(),
402
402
  getGoalRuntimeSnapshot: (settings) => this.getGoalRuntimeSnapshot(settings),
@@ -412,6 +412,8 @@ export class AgentSession {
412
412
  // to this controller's own `continueGoalLoopExclusive` guard, so routing through it here would
413
413
  // recurse into the guard from inside itself instead of driving the actual continuation pass.
414
414
  continueGoalLoop: (options) => this._goals.continueLoop(options),
415
+ isForegroundBusy: () => this._foregroundRecovery.isBusy,
416
+ waitForForegroundIdle: () => this._foregroundRecovery.waitForIdle(),
415
417
  collectWorkspaceSources: (args) => this._collectWorkspaceSources(args),
416
418
  });
417
419
  this._memory = new MemoryController({
@@ -461,7 +463,7 @@ export class AgentSession {
461
463
  getArtifactStore: () => this._getToolArtifactStore(),
462
464
  loadPersistedRecordsNewestFirst: () => loadBackgroundToolTaskRecordsNewestFirst(this.sessionManager),
463
465
  persist: (record) => this.sessionManager.appendCustomEntry(BACKGROUND_TOOL_TASK_CUSTOM_TYPE, record),
464
- notifyTerminal: (record, options) => this._notifyBackgroundToolTerminal(record, options.wakeParent),
466
+ notifyTerminal: (records, options) => this._terminalHandoffs.notifyTools(records, options.wakeParent),
465
467
  onLiveTasksChanged: (tasks) => this._emit({ type: "background_tools", tasks }),
466
468
  recordUsage: (taskId, usage) => {
467
469
  this.addSpawnedUsage(usage, {
@@ -523,6 +525,20 @@ export class AgentSession {
523
525
  emit: (event) => this._emit(event),
524
526
  checkCompaction: (message) => this._checkCompaction(message),
525
527
  onSuccessfulAssistant: () => this._compaction.resetOverflowRecovery(),
528
+ prepareRun: async () => {
529
+ this.agent.state.systemPrompt = this._systemPromptBuilder.enforceSystemPromptBudget(this.systemPrompt);
530
+ await this._toolProtocol.ensureActiveModelProtocol();
531
+ },
532
+ afterRun: async () => {
533
+ this._flushPendingBashMessages();
534
+ await this._drainQueuedExtensionCommands();
535
+ },
536
+ });
537
+ this._terminalHandoffs = new ForegroundTerminalHandoffController({
538
+ foreground: this._foregroundRecovery,
539
+ isDisposed: () => this._disposed,
540
+ workerInputsWillWakeParent: (workerRequestIds) => this._humanInput.workerInputsWillWakeParent(workerRequestIds),
541
+ sendCustomMessage: (message, options, lease) => this._sendCustomMessage(message, options, lease),
526
542
  });
527
543
  this._modelRouter = new ModelRouterController({
528
544
  getAgent: () => this.agent,
@@ -538,7 +554,7 @@ export class AgentSession {
538
554
  getAgentDir: () => this._agentDir,
539
555
  getReflectionSignal: () => this._reflectionAbort.signal,
540
556
  getBaseSystemPrompt: () => this._baseSystemPrompt,
541
- runAgentPrompt: (messages) => this._runAgentPrompt(messages),
557
+ runAgentPrompt: (messages) => this._foregroundRecovery.runAgentPrompt(messages, this._foregroundPromptLease),
542
558
  buildSystemPromptForToolNames: (toolNames) => this._buildSystemPromptForToolNames(toolNames),
543
559
  refreshCurrentModelFromRegistry: () => this._refreshCurrentModelFromRegistry(),
544
560
  runIsolatedCompletion: (opts) => this.runIsolatedCompletion(opts),
@@ -942,7 +958,10 @@ export class AgentSession {
942
958
  const previousTransformContext = this.agent.transformContext?.bind(this.agent);
943
959
  this.agent.transformContext = async (messages, signal) => {
944
960
  const transformed = previousTransformContext ? await previousTransformContext(messages, signal) : messages;
945
- const authoritativeMessages = this.agent.state.messages.length > 0 ? this.agent.state.messages : transformed;
961
+ // The agent loop may deliberately project a safer request context (for example, removing a
962
+ // failed mutation attempt after retaining its payload). Replacing that projection with raw
963
+ // session state would disclose the discarded attempt and defeat tool-failure repair.
964
+ const authoritativeMessages = transformed;
946
965
  let currentMessages = authoritativeMessages;
947
966
  try {
948
967
  if (!this.isCompacting &&
@@ -1409,53 +1428,6 @@ export class AgentSession {
1409
1428
  l(event);
1410
1429
  }
1411
1430
  }
1412
- async _notifyWorkerTerminalHandoff(records) {
1413
- if (records.length === 0)
1414
- return;
1415
- if (this._disposed)
1416
- throw new Error("Session disposed before worker terminal handoff was persisted");
1417
- // sendCustomMessage only queues in process memory while the foreground is streaming. Wait for
1418
- // the owning run to settle so a successful return proves the handoff reached session storage
1419
- // before the durable notification outbox marks it delivered.
1420
- await this.agent.waitForIdle();
1421
- if (this._disposed)
1422
- throw new Error("Session disposed before worker terminal handoff was persisted");
1423
- const included = records.slice(0, 8);
1424
- const sanitize = (value) => value.replace(/[\r\n]+/g, " ").slice(0, 120);
1425
- const omitted = records.length - included.length;
1426
- const workerRequestsAwaitingDelivery = new Set(getWorkerHumanInputsRequiringDelivery(this.sessionManager).flatMap((snapshot) => snapshot.request.workerRequestId ? [snapshot.request.workerRequestId] : []));
1427
- const ownerQuestionWillWakeParent = this._extensionUIContext !== undefined &&
1428
- this._extensionMode !== "print" &&
1429
- records.every((record) => workerRequestsAwaitingDelivery.has(record.laneId));
1430
- const content = [
1431
- "Background worker terminal handoff:",
1432
- ...included.map((record) => {
1433
- const reason = record.reasonCode ? ` reason=${sanitize(record.reasonCode)}` : "";
1434
- return `- ${record.laneId}: ${record.status}${reason}`;
1435
- }),
1436
- ...(omitted > 0 ? [`- ${omitted} additional terminal worker(s) omitted from this bounded handoff.`] : []),
1437
- "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.",
1438
- ].join("\n");
1439
- await this.sendCustomMessage({
1440
- customType: "background-worker-completion",
1441
- content,
1442
- display: true,
1443
- details: { records: included },
1444
- }, { triggerTurn: !ownerQuestionWillWakeParent, deliverAs: "followUp" });
1445
- }
1446
- async _notifyBackgroundToolTerminal(record, wakeParent) {
1447
- if (!wakeParent)
1448
- return;
1449
- if (this._disposed)
1450
- throw new Error("Session disposed before background tool terminal handoff was delivered");
1451
- await this.agent.waitForIdle();
1452
- if (this._disposed)
1453
- throw new Error("Session disposed before background tool terminal handoff was delivered");
1454
- await this.sendCustomMessage(createBackgroundToolTerminalMessage(record), {
1455
- triggerTurn: true,
1456
- deliverAs: "followUp",
1457
- });
1458
- }
1459
1431
  _emitQueueUpdate() {
1460
1432
  this._emit({
1461
1433
  type: "queue_update",
@@ -1675,6 +1647,7 @@ export class AgentSession {
1675
1647
  };
1676
1648
  safely(() => this._backgroundLanes.clearGoalAutoContinueTimer());
1677
1649
  safely(() => this._backgroundLanes.clearResearchLaneTimer());
1650
+ safely(() => this._foregroundRecovery.shutdown());
1678
1651
  safely(() => this.abortRetry());
1679
1652
  safely(() => this.abortCompaction());
1680
1653
  safely(() => this.abortBranchSummary());
@@ -1684,6 +1657,7 @@ export class AgentSession {
1684
1657
  safely(() => this.agent.abort());
1685
1658
  track(() => this._gatewayRegistry.stop());
1686
1659
  track(() => this._backgroundToolTasks.shutdown());
1660
+ track(() => this._runtimeBuilder.dispose());
1687
1661
  safely(() => this._reflection.cancelScheduled());
1688
1662
  safely(() => this._reflectionAbort.abort());
1689
1663
  safely(() => this._backgroundLanes.abortInFlightLanes());
@@ -1918,23 +1892,6 @@ export class AgentSession {
1918
1892
  // =========================================================================
1919
1893
  // Prompting
1920
1894
  // =========================================================================
1921
- async _runAgentPrompt(messages) {
1922
- try {
1923
- const maxGoalLoopRounds = this.settingsManager.getAutonomySettings().maxStallTurns;
1924
- this.agent.maxStallTurns = maxGoalLoopRounds;
1925
- await this._toolProtocol.ensureActiveModelProtocol();
1926
- let goalLoopRounds = 1;
1927
- await this.agent.prompt(messages);
1928
- while ((maxGoalLoopRounds === 0 || goalLoopRounds < maxGoalLoopRounds) && (await this._handlePostAgentRun())) {
1929
- await this.agent.continue();
1930
- goalLoopRounds++;
1931
- }
1932
- }
1933
- finally {
1934
- this._flushPendingBashMessages();
1935
- await this._drainQueuedExtensionCommands();
1936
- }
1937
- }
1938
1895
  /**
1939
1896
  * Re-enter an interrupted ask_question call from its durable request snapshot. Pending requests
1940
1897
  * are presented again; already-checkpointed answers are replayed without asking twice. The
@@ -1999,9 +1956,6 @@ export class AgentSession {
1999
1956
  getModelRouterStatus(formatLabel) {
2000
1957
  return this._modelRouter.getStatus(formatLabel);
2001
1958
  }
2002
- async _handlePostAgentRun() {
2003
- return this._foregroundRecovery.handlePostAgentRun();
2004
- }
2005
1959
  /**
2006
1960
  * Send a prompt to the agent.
2007
1961
  * - Handles extension commands (registered via pi.registerCommand) immediately, even during streaming
@@ -2015,14 +1969,30 @@ export class AgentSession {
2015
1969
  if (options?.autoContinueGoal !== false) {
2016
1970
  this._backgroundLanes.clearGoalAutoContinueTimer();
2017
1971
  }
2018
- if ((this.isStreaming || this.isRetrying) && options?.streamingBehavior) {
2019
- const run = this._streamingPromptSubmissionTail.then(() => this._promptUnserialized(text, options), () => this._promptUnserialized(text, options));
1972
+ const submissionLease = this._foregroundRecovery.tryAcquireSubmission();
1973
+ if (!submissionLease && this._foregroundRecovery.isBusy && options?.streamingBehavior) {
1974
+ const run = this._streamingPromptSubmissionTail.then(() => this._runPromptSubmission(text, options), () => this._runPromptSubmission(text, options));
2020
1975
  this._streamingPromptSubmissionTail = run.catch(() => { });
2021
1976
  return run;
2022
1977
  }
2023
- return this._promptUnserialized(text, options);
1978
+ return this._runPromptSubmission(text, options, submissionLease);
2024
1979
  }
2025
- async _promptUnserialized(text, options) {
1980
+ async _runPromptSubmission(text, options, initialSubmissionLease) {
1981
+ const submission = { lease: initialSubmissionLease };
1982
+ if (submission.lease)
1983
+ this._foregroundPromptLease = submission.lease;
1984
+ try {
1985
+ await this._promptUnserialized(text, options, submission);
1986
+ }
1987
+ finally {
1988
+ if (submission.lease) {
1989
+ if (this._foregroundPromptLease === submission.lease)
1990
+ this._foregroundPromptLease = undefined;
1991
+ this._foregroundRecovery.releaseSubmission(submission.lease);
1992
+ }
1993
+ }
1994
+ }
1995
+ async _promptUnserialized(text, options, submission) {
2026
1996
  this._toolProtocol.applyRepairLayerSettings();
2027
1997
  this._cancelPrefixWarm();
2028
1998
  const expandPromptTemplates = options?.expandPromptTemplates ?? true;
@@ -2094,9 +2064,12 @@ export class AgentSession {
2094
2064
  // If streaming — or waiting out a retry backoff, which is still an active
2095
2065
  // operation — queue via steer() or followUp() instead of starting a
2096
2066
  // concurrent run that would race the pending retry continuation.
2097
- if (this.isStreaming || this.isRetrying) {
2067
+ if (!submission.lease) {
2068
+ submission.lease = this._foregroundRecovery.tryAcquireSubmission();
2069
+ }
2070
+ if (!submission.lease) {
2098
2071
  if (!options?.streamingBehavior) {
2099
- throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
2072
+ throw new AgentBusyError("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
2100
2073
  }
2101
2074
  if (options.streamingBehavior === "followUp") {
2102
2075
  await this._queueFollowUp(expandedText, currentImages);
@@ -2107,6 +2080,7 @@ export class AgentSession {
2107
2080
  preflightResult?.(true);
2108
2081
  return;
2109
2082
  }
2083
+ this._foregroundPromptLease = submission.lease;
2110
2084
  // This submission is starting a new foreground turn. Queued steer/follow-up messages above
2111
2085
  // remain part of the already-active turn and must not erase its spawned-cost baseline.
2112
2086
  this._costGuardTurnBaselineUsd = this.getSpawnedUsage().cost;
@@ -2247,7 +2221,7 @@ export class AgentSession {
2247
2221
  this._pendingNextTurnMessages.splice(0, pendingNextTurnCount);
2248
2222
  }
2249
2223
  catch (error) {
2250
- // The turn never reached _runAgentPrompt, so the authoritative message_start that would
2224
+ // The turn never reached the foreground run, so the authoritative message_start that would
2251
2225
  // normally consume this entry (see _handleAgentEvent) never fires — un-register it here
2252
2226
  // instead of leaking the reference.
2253
2227
  if (userMessage) {
@@ -2463,6 +2437,21 @@ export class AgentSession {
2463
2437
  * @param options.deliverAs Delivery mode: "steer", "followUp", or "nextTurn"
2464
2438
  */
2465
2439
  async sendCustomMessage(message, options) {
2440
+ if (options?.deliverAs === "nextTurn") {
2441
+ return this._sendCustomMessage(message, options);
2442
+ }
2443
+ const submissionLease = this._foregroundRecovery.tryAcquireSubmission();
2444
+ if (!submissionLease) {
2445
+ return this._sendCustomMessage(message, options);
2446
+ }
2447
+ try {
2448
+ await this._sendCustomMessage(message, options, submissionLease);
2449
+ }
2450
+ finally {
2451
+ this._foregroundRecovery.releaseSubmission(submissionLease);
2452
+ }
2453
+ }
2454
+ async _sendCustomMessage(message, options, submissionLease) {
2466
2455
  const appMessage = {
2467
2456
  role: "custom",
2468
2457
  customType: message.customType,
@@ -2474,7 +2463,7 @@ export class AgentSession {
2474
2463
  if (options?.deliverAs === "nextTurn") {
2475
2464
  this._pendingNextTurnMessages.push(appMessage);
2476
2465
  }
2477
- else if (this.isStreaming) {
2466
+ else if (this._foregroundRecovery.isBusy && !this._foregroundRecovery.ownsSubmission(submissionLease)) {
2478
2467
  if (options?.deliverAs === "followUp") {
2479
2468
  this.agent.followUp(appMessage);
2480
2469
  }
@@ -2483,7 +2472,7 @@ export class AgentSession {
2483
2472
  }
2484
2473
  }
2485
2474
  else if (options?.triggerTurn) {
2486
- await this._runAgentPrompt(appMessage);
2475
+ await this._foregroundRecovery.runAgentPrompt(appMessage, submissionLease);
2487
2476
  }
2488
2477
  else {
2489
2478
  this.agent.state.messages.push(appMessage);
@@ -2879,7 +2868,7 @@ export class AgentSession {
2879
2868
  },
2880
2869
  }, {
2881
2870
  getModel: () => this.model,
2882
- isIdle: () => !this.isStreaming,
2871
+ isIdle: () => !this._foregroundRecovery.isBusy,
2883
2872
  getSignal: () => this.agent.signal,
2884
2873
  abort: () => {
2885
2874
  if (this._extensionAbortHandler) {