@caupulican/pi-adaptative 0.90.7 → 0.90.11

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 (207) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/bundled-resources/skills/tool-call-repair/SKILL.md +5 -4
  3. package/dist/bundled-resources/skills/tool-call-repair/references/failure-grammar.md +6 -3
  4. package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +1 -0
  5. package/dist/core/agent-session-contracts.d.ts +2 -0
  6. package/dist/core/agent-session-contracts.d.ts.map +1 -1
  7. package/dist/core/agent-session-contracts.js.map +1 -1
  8. package/dist/core/agent-session.d.ts +8 -7
  9. package/dist/core/agent-session.d.ts.map +1 -1
  10. package/dist/core/agent-session.js +80 -196
  11. package/dist/core/agent-session.js.map +1 -1
  12. package/dist/core/autonomy/bounded-completion.d.ts +7 -1
  13. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  14. package/dist/core/autonomy/bounded-completion.js +8 -2
  15. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  16. package/dist/core/autonomy/contracts.d.ts +1 -1
  17. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  18. package/dist/core/autonomy/contracts.js.map +1 -1
  19. package/dist/core/background-lane-controller.d.ts +14 -0
  20. package/dist/core/background-lane-controller.d.ts.map +1 -1
  21. package/dist/core/background-lane-controller.js +32 -0
  22. package/dist/core/background-lane-controller.js.map +1 -1
  23. package/dist/core/billing-failover-controller.js +1 -1
  24. package/dist/core/billing-failover-controller.js.map +1 -1
  25. package/dist/core/context/current-work-memory.d.ts +1 -1
  26. package/dist/core/context/current-work-memory.d.ts.map +1 -1
  27. package/dist/core/context/current-work-memory.js +2 -1
  28. package/dist/core/context/current-work-memory.js.map +1 -1
  29. package/dist/core/context/long-term-memory-trigger.d.ts +1 -1
  30. package/dist/core/context/long-term-memory-trigger.d.ts.map +1 -1
  31. package/dist/core/context/long-term-memory-trigger.js +2 -1
  32. package/dist/core/context/long-term-memory-trigger.js.map +1 -1
  33. package/dist/core/delegation/managed-lane-controller.js +2 -2
  34. package/dist/core/delegation/managed-lane-controller.js.map +1 -1
  35. package/dist/core/delegation/worker-agent-control-coordinator.d.ts +13 -0
  36. package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
  37. package/dist/core/delegation/worker-agent-control-coordinator.js +71 -8
  38. package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
  39. package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
  40. package/dist/core/delegation/worker-attempt-executor.js +13 -8
  41. package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
  42. package/dist/core/delegation/worker-claim.d.ts.map +1 -1
  43. package/dist/core/delegation/worker-claim.js +14 -1
  44. package/dist/core/delegation/worker-claim.js.map +1 -1
  45. package/dist/core/delegation/worker-delegation-controller.d.ts +3 -2
  46. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  47. package/dist/core/delegation/worker-delegation-controller.js +95 -42
  48. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  49. package/dist/core/delegation/worker-fleet-limits.d.ts.map +1 -1
  50. package/dist/core/delegation/worker-fleet-limits.js +5 -1
  51. package/dist/core/delegation/worker-fleet-limits.js.map +1 -1
  52. package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -1
  53. package/dist/core/delegation/worker-lane-projection.js +7 -4
  54. package/dist/core/delegation/worker-lane-projection.js.map +1 -1
  55. package/dist/core/delegation/worker-lifecycle.d.ts +8 -0
  56. package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
  57. package/dist/core/delegation/worker-lifecycle.js.map +1 -1
  58. package/dist/core/delegation/worker-notification-coordinator.d.ts +21 -0
  59. package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -1
  60. package/dist/core/delegation/worker-notification-coordinator.js +65 -2
  61. package/dist/core/delegation/worker-notification-coordinator.js.map +1 -1
  62. package/dist/core/delegation/worker-runner.d.ts +7 -0
  63. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  64. package/dist/core/delegation/worker-runner.js +24 -7
  65. package/dist/core/delegation/worker-runner.js.map +1 -1
  66. package/dist/core/delegation/worker-system-prompt.d.ts +54 -0
  67. package/dist/core/delegation/worker-system-prompt.d.ts.map +1 -0
  68. package/dist/core/delegation/worker-system-prompt.js +85 -0
  69. package/dist/core/delegation/worker-system-prompt.js.map +1 -0
  70. package/dist/core/delegation/worker-tree-budget-coordinator.d.ts.map +1 -1
  71. package/dist/core/delegation/worker-tree-budget-coordinator.js +4 -0
  72. package/dist/core/delegation/worker-tree-budget-coordinator.js.map +1 -1
  73. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts +4 -0
  74. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts.map +1 -1
  75. package/dist/core/delegation/worker-write-reservation-coordinator.js +28 -1
  76. package/dist/core/delegation/worker-write-reservation-coordinator.js.map +1 -1
  77. package/dist/core/durable-custom-message-turn-controller.d.ts.map +1 -1
  78. package/dist/core/durable-custom-message-turn-controller.js +14 -3
  79. package/dist/core/durable-custom-message-turn-controller.js.map +1 -1
  80. package/dist/core/extension-binding-controller.d.ts +107 -0
  81. package/dist/core/extension-binding-controller.d.ts.map +1 -0
  82. package/dist/core/extension-binding-controller.js +224 -0
  83. package/dist/core/extension-binding-controller.js.map +1 -0
  84. package/dist/core/extensions/types.d.ts +2 -0
  85. package/dist/core/extensions/types.d.ts.map +1 -1
  86. package/dist/core/extensions/types.js.map +1 -1
  87. package/dist/core/foreground-terminal-handoff-controller.d.ts +9 -0
  88. package/dist/core/foreground-terminal-handoff-controller.d.ts.map +1 -1
  89. package/dist/core/foreground-terminal-handoff-controller.js +35 -9
  90. package/dist/core/foreground-terminal-handoff-controller.js.map +1 -1
  91. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  92. package/dist/core/goal-loop-controller.js +10 -1
  93. package/dist/core/goal-loop-controller.js.map +1 -1
  94. package/dist/core/goals/compact-goal-context.d.ts +1 -1
  95. package/dist/core/goals/compact-goal-context.d.ts.map +1 -1
  96. package/dist/core/goals/compact-goal-context.js +2 -1
  97. package/dist/core/goals/compact-goal-context.js.map +1 -1
  98. package/dist/core/goals/goal-execution-errors.d.ts +18 -0
  99. package/dist/core/goals/goal-execution-errors.d.ts.map +1 -0
  100. package/dist/core/goals/goal-execution-errors.js +21 -0
  101. package/dist/core/goals/goal-execution-errors.js.map +1 -0
  102. package/dist/core/goals/goal-lifecycle.d.ts.map +1 -1
  103. package/dist/core/goals/goal-lifecycle.js +3 -3
  104. package/dist/core/goals/goal-lifecycle.js.map +1 -1
  105. package/dist/core/goals/goal-session-controller.d.ts +11 -0
  106. package/dist/core/goals/goal-session-controller.d.ts.map +1 -1
  107. package/dist/core/goals/goal-session-controller.js +74 -14
  108. package/dist/core/goals/goal-session-controller.js.map +1 -1
  109. package/dist/core/goals/goal-state.js +1 -1
  110. package/dist/core/goals/goal-state.js.map +1 -1
  111. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  112. package/dist/core/goals/goal-tool-core.js +2 -2
  113. package/dist/core/goals/goal-tool-core.js.map +1 -1
  114. package/dist/core/goals/natural-language-goal.d.ts +18 -0
  115. package/dist/core/goals/natural-language-goal.d.ts.map +1 -1
  116. package/dist/core/goals/natural-language-goal.js +99 -16
  117. package/dist/core/goals/natural-language-goal.js.map +1 -1
  118. package/dist/core/model-registry.d.ts +5 -0
  119. package/dist/core/model-registry.d.ts.map +1 -1
  120. package/dist/core/model-registry.js +10 -0
  121. package/dist/core/model-registry.js.map +1 -1
  122. package/dist/core/model-resolver.d.ts.map +1 -1
  123. package/dist/core/model-resolver.js +3 -1
  124. package/dist/core/model-resolver.js.map +1 -1
  125. package/dist/core/orchestration/capability-gateway.d.ts +1 -1
  126. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
  127. package/dist/core/orchestration/capability-gateway.js +4 -4
  128. package/dist/core/orchestration/capability-gateway.js.map +1 -1
  129. package/dist/core/orchestration/event-store.d.ts +8 -0
  130. package/dist/core/orchestration/event-store.d.ts.map +1 -1
  131. package/dist/core/orchestration/event-store.js +9 -7
  132. package/dist/core/orchestration/event-store.js.map +1 -1
  133. package/dist/core/orchestration/task-runtime-reducer.js +1 -1
  134. package/dist/core/orchestration/task-runtime-reducer.js.map +1 -1
  135. package/dist/core/orchestration/worker-model-pins.d.ts +7 -0
  136. package/dist/core/orchestration/worker-model-pins.d.ts.map +1 -1
  137. package/dist/core/orchestration/worker-model-pins.js +17 -1
  138. package/dist/core/orchestration/worker-model-pins.js.map +1 -1
  139. package/dist/core/provider-request-runtime-controller.d.ts +9 -0
  140. package/dist/core/provider-request-runtime-controller.d.ts.map +1 -1
  141. package/dist/core/provider-request-runtime-controller.js +17 -3
  142. package/dist/core/provider-request-runtime-controller.js.map +1 -1
  143. package/dist/core/provider-tool-text.d.ts +15 -1
  144. package/dist/core/provider-tool-text.d.ts.map +1 -1
  145. package/dist/core/provider-tool-text.js +42 -10
  146. package/dist/core/provider-tool-text.js.map +1 -1
  147. package/dist/core/research/research-lane-controller.d.ts +1 -1
  148. package/dist/core/research/research-lane-controller.d.ts.map +1 -1
  149. package/dist/core/research/research-lane-controller.js +2 -1
  150. package/dist/core/research/research-lane-controller.js.map +1 -1
  151. package/dist/core/resource-loader.d.ts +3 -0
  152. package/dist/core/resource-loader.d.ts.map +1 -1
  153. package/dist/core/resource-loader.js +8 -0
  154. package/dist/core/resource-loader.js.map +1 -1
  155. package/dist/core/runtime-builder.d.ts +8 -0
  156. package/dist/core/runtime-builder.d.ts.map +1 -1
  157. package/dist/core/runtime-builder.js +6 -0
  158. package/dist/core/runtime-builder.js.map +1 -1
  159. package/dist/core/session-supervision-runtime.d.ts.map +1 -1
  160. package/dist/core/session-supervision-runtime.js +2 -1
  161. package/dist/core/session-supervision-runtime.js.map +1 -1
  162. package/dist/core/settings-manager.d.ts +2 -2
  163. package/dist/core/settings-manager.d.ts.map +1 -1
  164. package/dist/core/settings-manager.js +7 -2
  165. package/dist/core/settings-manager.js.map +1 -1
  166. package/dist/core/tools/delegate.d.ts +13 -0
  167. package/dist/core/tools/delegate.d.ts.map +1 -1
  168. package/dist/core/tools/delegate.js +20 -18
  169. package/dist/core/tools/delegate.js.map +1 -1
  170. package/dist/core/util/atomic-file.d.ts +7 -0
  171. package/dist/core/util/atomic-file.d.ts.map +1 -1
  172. package/dist/core/util/atomic-file.js +8 -6
  173. package/dist/core/util/atomic-file.js.map +1 -1
  174. package/dist/core/util/faultable-fs.d.ts +30 -0
  175. package/dist/core/util/faultable-fs.d.ts.map +1 -0
  176. package/dist/core/util/faultable-fs.js +30 -0
  177. package/dist/core/util/faultable-fs.js.map +1 -0
  178. package/dist/modes/interactive/auth-dialogs-controller.d.ts.map +1 -1
  179. package/dist/modes/interactive/auth-dialogs-controller.js +1 -0
  180. package/dist/modes/interactive/auth-dialogs-controller.js.map +1 -1
  181. package/dist/modes/interactive/components/activity-lane.d.ts.map +1 -1
  182. package/dist/modes/interactive/components/activity-lane.js +4 -3
  183. package/dist/modes/interactive/components/activity-lane.js.map +1 -1
  184. package/dist/modes/interactive/components/footer.js +1 -1
  185. package/dist/modes/interactive/components/footer.js.map +1 -1
  186. package/dist/modes/interactive/components/oauth-selector.d.ts +2 -0
  187. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  188. package/dist/modes/interactive/components/oauth-selector.js +3 -2
  189. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  190. package/dist/modes/interactive/report-commands.js +1 -1
  191. package/dist/modes/interactive/report-commands.js.map +1 -1
  192. package/dist/modes/interactive/session-flow-commands.d.ts +1 -1
  193. package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
  194. package/dist/modes/interactive/session-flow-commands.js +3 -2
  195. package/dist/modes/interactive/session-flow-commands.js.map +1 -1
  196. package/docs/providers.md +3 -0
  197. package/docs/settings.md +4 -2
  198. package/docs/tool-repair.md +1 -0
  199. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  200. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  201. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  202. package/examples/extensions/sandbox/package-lock.json +2 -2
  203. package/examples/extensions/sandbox/package.json +1 -1
  204. package/examples/extensions/with-deps/package-lock.json +2 -2
  205. package/examples/extensions/with-deps/package.json +1 -1
  206. package/npm-shrinkwrap.json +12 -12
  207. package/package.json +9 -4
@@ -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 turn/wall-clock telemetry; provider usage\n * and spend are attributed at the response boundary. Progress is inferred from real work and\n * terminal goal updates; telemetry never becomes an implicit execution limit.\n */\n\nimport { AgentBusyError } from \"@caupulican/pi-agent-core/agent\";\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/**\n\t * Persist one submitted pass's turn and active-wall-clock contribution. Provider usage is charged\n\t * at each assistant response before another request can be admitted. Called once per\n\t * pass actually SUBMITTED (never for a no-op `continueGoalOnce` call).\n\t */\n\trecordGoalContinuationPass(pass: { turns: number; wallClockMs: number }): 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\tgoalExecutionId: snapshot.goalState?.goalId,\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\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 });\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 });\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\tif (afterSnapshot.goalState?.status === \"budget_limited\") {\n\t\t\t\treturn { turnsSubmitted, stopReason: \"goal_budget_exhausted\", finalSnapshot: afterSnapshot };\n\t\t\t}\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 turn/wall-clock telemetry; provider usage\n * and spend are attributed at the response boundary. Progress is inferred from real work and\n * terminal goal updates; telemetry never becomes an implicit execution limit.\n */\n\nimport { AgentBusyError } from \"@caupulican/pi-agent-core/agent\";\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 { GoalBudgetExhaustedError } from \"./goals/goal-execution-errors.ts\";\nimport type { GoalRuntimeSnapshot, GoalRuntimeSnapshotSettings } from \"./goals/goal-runtime-snapshot.ts\";\nimport { type GoalState, isGoalExecutionActive } 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 || !isGoalExecutionActive(state.status)) 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/**\n\t * Persist one submitted pass's turn and active-wall-clock contribution. Provider usage is charged\n\t * at each assistant response before another request can be admitted. Called once per\n\t * pass actually SUBMITTED (never for a no-op `continueGoalOnce` call).\n\t */\n\trecordGoalContinuationPass(pass: { turns: number; wallClockMs: number }): 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\tgoalExecutionId: snapshot.goalState?.goalId,\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\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 });\n\t\t\t\tif (error instanceof GoalBudgetExhaustedError) {\n\t\t\t\t\t// The goal already stopped itself durably (markBudgetLimited ran synchronously before\n\t\t\t\t\t// this signal was thrown) — surface the existing clean stop instead of reclassifying an\n\t\t\t\t\t// intentional, already-recorded stop as a generic continuation failure and rejecting\n\t\t\t\t\t// the whole loop promise.\n\t\t\t\t\treturn { turnsSubmitted, stopReason: \"goal_budget_exhausted\", finalSnapshot: snapshot() };\n\t\t\t\t}\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 });\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\tif (afterSnapshot.goalState?.status === \"budget_limited\") {\n\t\t\t\treturn { turnsSubmitted, stopReason: \"goal_budget_exhausted\", finalSnapshot: afterSnapshot };\n\t\t\t}\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,5 +1,5 @@
1
1
  import type { AgentMessage } from "@caupulican/pi-agent-core/types";
2
- import type { GoalState } from "./goal-state.ts";
2
+ import { type GoalState } from "./goal-state.ts";
3
3
  export declare const ACTIVE_GOAL_CONTEXT_CUSTOM_TYPE = "active_goal_context";
4
4
  export declare function formatCompactGoalContext(state: GoalState, continuationTurn: boolean): string;
5
5
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"compact-goal-context.d.ts","sourceRoot":"","sources":["../../../src/core/goals/compact-goal-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAIpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,eAAO,MAAM,+BAA+B,wBAAwB,CAAC;AAsBrE,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,OAAO,GAAG,MAAM,CAqB5F;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,YAAY,EAAE,CAkB/G"}
1
+ {"version":3,"file":"compact-goal-context.d.ts","sourceRoot":"","sources":["../../../src/core/goals/compact-goal-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAIpE,OAAO,EAAE,KAAK,SAAS,EAAyB,MAAM,iBAAiB,CAAC;AAExE,eAAO,MAAM,+BAA+B,wBAAwB,CAAC;AAsBrE,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,OAAO,GAAG,MAAM,CAqB5F;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,YAAY,EAAE,CAkB/G"}
@@ -1,6 +1,7 @@
1
1
  import { createCustomMessage } from "@caupulican/pi-agent-core/messages";
2
2
  import { GOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE } from "./goal-continuation-prompt.js";
3
3
  import { projectGoalRecord } from "./goal-record.js";
4
+ import { isGoalExecutionActive } from "./goal-state.js";
4
5
  export const ACTIVE_GOAL_CONTEXT_CUSTOM_TYPE = "active_goal_context";
5
6
  const LEGACY_GOAL_CONTINUATION_PREFIX = "Goal continuation context\n=========================";
6
7
  function messageText(message) {
@@ -51,7 +52,7 @@ export function injectCompactGoalContext(messages, state) {
51
52
  const continuationTurn = messages.some((message) => (message.role === "custom" && message.customType === GOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE) ||
52
53
  messageText(message).startsWith(LEGACY_GOAL_CONTINUATION_PREFIX));
53
54
  const filtered = messages.filter((message) => !isGoalContextMessage(message));
54
- if (!state || state.status !== "active")
55
+ if (!state || !isGoalExecutionActive(state.status))
55
56
  return filtered;
56
57
  return [
57
58
  ...filtered,
@@ -1 +1 @@
1
- {"version":3,"file":"compact-goal-context.js","sourceRoot":"","sources":["../../../src/core/goals/compact-goal-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAGzE,OAAO,EAAE,qCAAqC,EAAE,MAAM,+BAA+B,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,MAAM,CAAC,MAAM,+BAA+B,GAAG,qBAAqB,CAAC;AACrE,MAAM,+BAA+B,GAAG,sDAAsD,CAAC;AAE/F,SAAS,WAAW,CAAC,OAAqB;IACzC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,EAAE,CAAC;IACvC,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC;IAChE,OAAO,OAAO,CAAC,OAAO;SACpB,MAAM,CAAC,CAAC,IAAI,EAAuB,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;SAC3D,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;SACxB,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC;AAED,SAAS,oBAAoB,CAAC,OAAqB;IAClD,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,CACN,OAAO,CAAC,UAAU,KAAK,qCAAqC;YAC5D,OAAO,CAAC,UAAU,KAAK,+BAA+B,CACtD,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,+BAA+B,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAgB,EAAE,gBAAyB;IACnF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC/F,MAAM,aAAa,GAAG,MAAM,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAC1G,MAAM,WAAW,GAAG,gBAAgB;QACnC,CAAC,CAAC,0FAA0F;QAC5F,CAAC,CAAC,iFAAiF,CAAC;IACrF,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC;QACpC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,WAAW,EAAE,UAAU;QACvB,eAAe,EAAE,aAAa;KAC9B,CAAC;SACA,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC;SAC1B,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC7B,OAAO;QACN,aAAa;QACb,aAAa;QACb,WAAW;QACX,mLAAmL;KACnL,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,QAAwB,EAAE,KAA4B;IAC9F,MAAM,gBAAgB,GAAG,QAAQ,CAAC,IAAI,CACrC,CAAC,OAAO,EAAE,EAAE,CACX,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,UAAU,KAAK,qCAAqC,CAAC;QAC3F,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,+BAA+B,CAAC,CACjE,CAAC;IACF,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9E,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IACzD,OAAO;QACN,GAAG,QAAQ;QACX,mBAAmB,CAClB,+BAA+B,EAC/B,wBAAwB,CAAC,KAAK,EAAE,gBAAgB,CAAC,EACjD,KAAK,EACL,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,CAAC,EAAE,EACvD,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CACxB;KACD,CAAC;AACH,CAAC","sourcesContent":["import { createCustomMessage } from \"@caupulican/pi-agent-core/messages\";\nimport type { AgentMessage } from \"@caupulican/pi-agent-core/types\";\nimport type { TextContent } from \"@caupulican/pi-ai\";\nimport { GOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE } from \"./goal-continuation-prompt.ts\";\nimport { projectGoalRecord } from \"./goal-record.ts\";\nimport type { GoalState } from \"./goal-state.ts\";\n\nexport const ACTIVE_GOAL_CONTEXT_CUSTOM_TYPE = \"active_goal_context\";\nconst LEGACY_GOAL_CONTINUATION_PREFIX = \"Goal continuation context\\n=========================\";\n\nfunction messageText(message: AgentMessage): string {\n\tif (message.role !== \"user\") return \"\";\n\tif (typeof message.content === \"string\") return message.content;\n\treturn message.content\n\t\t.filter((part): part is TextContent => part.type === \"text\")\n\t\t.map((part) => part.text)\n\t\t.join(\"\\n\");\n}\n\nfunction isGoalContextMessage(message: AgentMessage): boolean {\n\tif (message.role === \"custom\") {\n\t\treturn (\n\t\t\tmessage.customType === GOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE ||\n\t\t\tmessage.customType === ACTIVE_GOAL_CONTEXT_CUSTOM_TYPE\n\t\t);\n\t}\n\treturn messageText(message).startsWith(LEGACY_GOAL_CONTINUATION_PREFIX);\n}\n\nexport function formatCompactGoalContext(state: GoalState, continuationTurn: boolean): string {\n\tconst record = projectGoalRecord(state);\n\tconst budgetText = record.tokenBudget === undefined ? \"unbounded\" : String(record.tokenBudget);\n\tconst remainingText = record.tokensRemaining === undefined ? \"unbounded\" : String(record.tokensRemaining);\n\tconst instruction = continuationTurn\n\t\t? \"Continue now: inspect authoritative state, make concrete progress toward full objective.\"\n\t\t: \"Goal persists; current user message steers this turn, never replaces objective.\";\n\tconst encodedRecord = JSON.stringify({\n\t\tobjective: record.objective,\n\t\ttokensUsed: record.tokensUsed,\n\t\ttokenBudget: budgetText,\n\t\ttokensRemaining: remainingText,\n\t})\n\t\t.replaceAll(\"<\", \"\\\\u003c\")\n\t\t.replaceAll(\">\", \"\\\\u003e\");\n\treturn [\n\t\t\"ACTIVE GOAL\",\n\t\tencodedRecord,\n\t\tinstruction,\n\t\t\"Use task_steps for decomposition, delegate for workers, tool/artifact results as evidence. Keep active unless completion is proven or same genuine blocker persists 3 goal turns.\",\n\t].join(\"\\n\");\n}\n\n/**\n * Replace every historical goal continuation payload with one current compact projection. The\n * projection exists only in the provider request returned from context assembly; it is never\n * appended to the transcript or session log.\n */\nexport function injectCompactGoalContext(messages: AgentMessage[], state: GoalState | undefined): AgentMessage[] {\n\tconst continuationTurn = messages.some(\n\t\t(message) =>\n\t\t\t(message.role === \"custom\" && message.customType === GOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE) ||\n\t\t\tmessageText(message).startsWith(LEGACY_GOAL_CONTINUATION_PREFIX),\n\t);\n\tconst filtered = messages.filter((message) => !isGoalContextMessage(message));\n\tif (!state || state.status !== \"active\") return filtered;\n\treturn [\n\t\t...filtered,\n\t\tcreateCustomMessage(\n\t\t\tACTIVE_GOAL_CONTEXT_CUSTOM_TYPE,\n\t\t\tformatCompactGoalContext(state, continuationTurn),\n\t\t\tfalse,\n\t\t\t{ goalId: state.goalId, revision: state.revision ?? 0 },\n\t\t\tnew Date().toISOString(),\n\t\t),\n\t];\n}\n"]}
1
+ {"version":3,"file":"compact-goal-context.js","sourceRoot":"","sources":["../../../src/core/goals/compact-goal-context.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCustomMessage } from \"@caupulican/pi-agent-core/messages\";\nimport type { AgentMessage } from \"@caupulican/pi-agent-core/types\";\nimport type { TextContent } from \"@caupulican/pi-ai\";\nimport { GOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE } from \"./goal-continuation-prompt.ts\";\nimport { projectGoalRecord } from \"./goal-record.ts\";\nimport { type GoalState, isGoalExecutionActive } from \"./goal-state.ts\";\n\nexport const ACTIVE_GOAL_CONTEXT_CUSTOM_TYPE = \"active_goal_context\";\nconst LEGACY_GOAL_CONTINUATION_PREFIX = \"Goal continuation context\\n=========================\";\n\nfunction messageText(message: AgentMessage): string {\n\tif (message.role !== \"user\") return \"\";\n\tif (typeof message.content === \"string\") return message.content;\n\treturn message.content\n\t\t.filter((part): part is TextContent => part.type === \"text\")\n\t\t.map((part) => part.text)\n\t\t.join(\"\\n\");\n}\n\nfunction isGoalContextMessage(message: AgentMessage): boolean {\n\tif (message.role === \"custom\") {\n\t\treturn (\n\t\t\tmessage.customType === GOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE ||\n\t\t\tmessage.customType === ACTIVE_GOAL_CONTEXT_CUSTOM_TYPE\n\t\t);\n\t}\n\treturn messageText(message).startsWith(LEGACY_GOAL_CONTINUATION_PREFIX);\n}\n\nexport function formatCompactGoalContext(state: GoalState, continuationTurn: boolean): string {\n\tconst record = projectGoalRecord(state);\n\tconst budgetText = record.tokenBudget === undefined ? \"unbounded\" : String(record.tokenBudget);\n\tconst remainingText = record.tokensRemaining === undefined ? \"unbounded\" : String(record.tokensRemaining);\n\tconst instruction = continuationTurn\n\t\t? \"Continue now: inspect authoritative state, make concrete progress toward full objective.\"\n\t\t: \"Goal persists; current user message steers this turn, never replaces objective.\";\n\tconst encodedRecord = JSON.stringify({\n\t\tobjective: record.objective,\n\t\ttokensUsed: record.tokensUsed,\n\t\ttokenBudget: budgetText,\n\t\ttokensRemaining: remainingText,\n\t})\n\t\t.replaceAll(\"<\", \"\\\\u003c\")\n\t\t.replaceAll(\">\", \"\\\\u003e\");\n\treturn [\n\t\t\"ACTIVE GOAL\",\n\t\tencodedRecord,\n\t\tinstruction,\n\t\t\"Use task_steps for decomposition, delegate for workers, tool/artifact results as evidence. Keep active unless completion is proven or same genuine blocker persists 3 goal turns.\",\n\t].join(\"\\n\");\n}\n\n/**\n * Replace every historical goal continuation payload with one current compact projection. The\n * projection exists only in the provider request returned from context assembly; it is never\n * appended to the transcript or session log.\n */\nexport function injectCompactGoalContext(messages: AgentMessage[], state: GoalState | undefined): AgentMessage[] {\n\tconst continuationTurn = messages.some(\n\t\t(message) =>\n\t\t\t(message.role === \"custom\" && message.customType === GOAL_CONTINUATION_TRIGGER_CUSTOM_TYPE) ||\n\t\t\tmessageText(message).startsWith(LEGACY_GOAL_CONTINUATION_PREFIX),\n\t);\n\tconst filtered = messages.filter((message) => !isGoalContextMessage(message));\n\tif (!state || !isGoalExecutionActive(state.status)) return filtered;\n\treturn [\n\t\t...filtered,\n\t\tcreateCustomMessage(\n\t\t\tACTIVE_GOAL_CONTEXT_CUSTOM_TYPE,\n\t\t\tformatCompactGoalContext(state, continuationTurn),\n\t\t\tfalse,\n\t\t\t{ goalId: state.goalId, revision: state.revision ?? 0 },\n\t\t\tnew Date().toISOString(),\n\t\t),\n\t];\n}\n"]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Typed execution-admission signals, kept in their own module so both the goal session controller
3
+ * (which throws them) and the goal continuation loop (which must recognize them) can import without
4
+ * creating a cycle between `goal-session-controller.ts` and `../goal-loop-controller.ts`.
5
+ */
6
+ /**
7
+ * Thrown by `GoalSessionController#admitProviderRequest` when a goal's token budget is exhausted —
8
+ * either already recorded (`status: "budget_limited"`) or just crossed by this very admission. The
9
+ * durable stop (`markBudgetLimited`) is always applied synchronously BEFORE this is thrown, so by the
10
+ * time a caller observes it the goal state already reflects the clean stop. Callers that own a
11
+ * bounded "keep going until told to stop" contract (the goal continuation loop) must recognize this
12
+ * distinctly from a genuine unexpected failure and report their own clean stop reason instead of
13
+ * reclassifying an intentional, already-recorded stop as a continuation failure.
14
+ */
15
+ export declare class GoalBudgetExhaustedError extends Error {
16
+ constructor(message: string);
17
+ }
18
+ //# sourceMappingURL=goal-execution-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-execution-errors.d.ts","sourceRoot":"","sources":["../../../src/core/goals/goal-execution-errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;GAQG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IAClD,YAAY,OAAO,EAAE,MAAM,EAG1B;CACD"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Typed execution-admission signals, kept in their own module so both the goal session controller
3
+ * (which throws them) and the goal continuation loop (which must recognize them) can import without
4
+ * creating a cycle between `goal-session-controller.ts` and `../goal-loop-controller.ts`.
5
+ */
6
+ /**
7
+ * Thrown by `GoalSessionController#admitProviderRequest` when a goal's token budget is exhausted —
8
+ * either already recorded (`status: "budget_limited"`) or just crossed by this very admission. The
9
+ * durable stop (`markBudgetLimited`) is always applied synchronously BEFORE this is thrown, so by the
10
+ * time a caller observes it the goal state already reflects the clean stop. Callers that own a
11
+ * bounded "keep going until told to stop" contract (the goal continuation loop) must recognize this
12
+ * distinctly from a genuine unexpected failure and report their own clean stop reason instead of
13
+ * reclassifying an intentional, already-recorded stop as a continuation failure.
14
+ */
15
+ export class GoalBudgetExhaustedError extends Error {
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = "GoalBudgetExhaustedError";
19
+ }
20
+ }
21
+ //# sourceMappingURL=goal-execution-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-execution-errors.js","sourceRoot":"","sources":["../../../src/core/goals/goal-execution-errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;GAQG;AACH,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IAClD,YAAY,OAAe;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACxC,CAAC;CACD","sourcesContent":["/**\n * Typed execution-admission signals, kept in their own module so both the goal session controller\n * (which throws them) and the goal continuation loop (which must recognize them) can import without\n * creating a cycle between `goal-session-controller.ts` and `../goal-loop-controller.ts`.\n */\n\n/**\n * Thrown by `GoalSessionController#admitProviderRequest` when a goal's token budget is exhausted —\n * either already recorded (`status: \"budget_limited\"`) or just crossed by this very admission. The\n * durable stop (`markBudgetLimited`) is always applied synchronously BEFORE this is thrown, so by the\n * time a caller observes it the goal state already reflects the clean stop. Callers that own a\n * bounded \"keep going until told to stop\" contract (the goal continuation loop) must recognize this\n * distinctly from a genuine unexpected failure and report their own clean stop reason instead of\n * reclassifying an intentional, already-recorded stop as a continuation failure.\n */\nexport class GoalBudgetExhaustedError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"GoalBudgetExhaustedError\";\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"goal-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/core/goals/goal-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,SAAS,EAId,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAmB,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAErG,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,iBAAiB,CAExE;AAED,qFAAqF;AACrF,MAAM,WAAW,sBAAsB;IACtC,oBAAoB,IAAI,SAAS,GAAG,SAAS,CAAC;IAC9C,qBAAqB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;IAC9E,sBAAsB,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/D;AAgBD,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAMvF;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CASxF;AAED,wBAAgB,QAAQ,CACvB,OAAO,EAAE,SAAS,GAAG,SAAS,EAC9B,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAChD,GAAG,EAAE,MAAM,GACT,gBAAgB,CAiBlB;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAMxF;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CAC1B,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAChE,GAAG,EAAE,MAAM,GACT,gBAAgB,CAElB;AAED,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,SAAS,GAAG,SAAS,EAC9B,IAAI,EAAE;IAAE,MAAM,EAAE,SAAS,GAAG,eAAe,GAAG,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAChF,GAAG,EAAE,MAAM,GACT,gBAAgB,CAQlB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,EAAE,GAAG,SAA2B,GAAG,gBAAgB,CAGjH;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,sBAAsB,EAAE,GAAG,SAA2B,GAAG,gBAAgB,CAGlH;AAED,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,sBAAsB,EAC5B,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAChD,GAAG,SAA2B,GAC5B,gBAAgB,CAGlB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,sBAAsB,EAAE,GAAG,SAA2B,GAAG,gBAAgB,CAGlH;AAED,MAAM,MAAM,wBAAwB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,iBAAiB,CAAC;AAE1F,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,sBAAsB,EAC5B,GAAG,SAA2B,GAC5B,wBAAwB,CAM1B"}
1
+ {"version":3,"file":"goal-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/core/goals/goal-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,SAAS,EAKd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAmB,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAErG,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,iBAAiB,CAExE;AAED,qFAAqF;AACrF,MAAM,WAAW,sBAAsB;IACtC,oBAAoB,IAAI,SAAS,GAAG,SAAS,CAAC;IAC9C,qBAAqB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;IAC9E,sBAAsB,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/D;AAgBD,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAMvF;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CASxF;AAED,wBAAgB,QAAQ,CACvB,OAAO,EAAE,SAAS,GAAG,SAAS,EAC9B,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAChD,GAAG,EAAE,MAAM,GACT,gBAAgB,CAiBlB;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAMxF;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CAC1B,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAChE,GAAG,EAAE,MAAM,GACT,gBAAgB,CAElB;AAED,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,SAAS,GAAG,SAAS,EAC9B,IAAI,EAAE;IAAE,MAAM,EAAE,SAAS,GAAG,eAAe,GAAG,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAChF,GAAG,EAAE,MAAM,GACT,gBAAgB,CAQlB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,EAAE,GAAG,SAA2B,GAAG,gBAAgB,CAGjH;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,sBAAsB,EAAE,GAAG,SAA2B,GAAG,gBAAgB,CAGlH;AAED,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,sBAAsB,EAC5B,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAChD,GAAG,SAA2B,GAC5B,gBAAgB,CAGlB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,sBAAsB,EAAE,GAAG,SAA2B,GAAG,gBAAgB,CAGlH;AAED,MAAM,MAAM,wBAAwB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,iBAAiB,CAAC;AAE1F,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,sBAAsB,EAC5B,GAAG,SAA2B,GAC5B,wBAAwB,CAM1B"}
@@ -1,4 +1,4 @@
1
- import { applyGoalEvent, isGoalResumableStatus, isGoalUnfinishedStatus, MAX_GOAL_OBJECTIVE_LENGTH, } from "./goal-state.js";
1
+ import { applyGoalEvent, isGoalExecutionActive, isGoalResumableStatus, isGoalUnfinishedStatus, MAX_GOAL_OBJECTIVE_LENGTH, } from "./goal-state.js";
2
2
  import { applyGoalAction } from "./goal-tool-core.js";
3
3
  export function getGoalStateRevision(state) {
4
4
  return { goalId: state.goalId, revision: state.revision ?? 0 };
@@ -15,7 +15,7 @@ function persistResult(host, current, result) {
15
15
  export function pauseGoal(current, now) {
16
16
  if (!current)
17
17
  return missingGoal("pause");
18
- if (current.status !== "active") {
18
+ if (!isGoalExecutionActive(current.status)) {
19
19
  return { ok: false, error: `Goal '${current.goalId}' is ${current.status}; only active goals can be paused.` };
20
20
  }
21
21
  return { ok: true, state: applyGoalEvent(current, { type: "pause_goal", now }) };
@@ -66,7 +66,7 @@ export function replaceGoal(args, now) {
66
66
  export function stopGoalFromSystem(current, args, now) {
67
67
  if (!current)
68
68
  return missingGoal("stop");
69
- if (current.status !== "active") {
69
+ if (!isGoalExecutionActive(current.status)) {
70
70
  return { ok: false, error: `Goal '${current.goalId}' is ${current.status}; no system stop is needed.` };
71
71
  }
72
72
  const reason = args.reason.trim();
@@ -1 +1 @@
1
- {"version":3,"file":"goal-lifecycle.js","sourceRoot":"","sources":["../../../src/core/goals/goal-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tapplyGoalEvent,\n\ttype GoalState,\n\tisGoalResumableStatus,\n\tisGoalUnfinishedStatus,\n\tMAX_GOAL_OBJECTIVE_LENGTH,\n} from \"./goal-state.ts\";\nimport { applyGoalAction, type GoalActionFailure, type GoalActionResult } from \"./goal-tool-core.ts\";\n\nexport interface GoalStateRevision {\n\tgoalId: string;\n\trevision: number;\n}\n\nexport function getGoalStateRevision(state: GoalState): GoalStateRevision {\n\treturn { goalId: state.goalId, revision: state.revision ?? 0 };\n}\n\n/** Minimal durable seam shared by CLI, tools, and in-process session restoration. */\nexport interface PersistedGoalStateHost {\n\tgetGoalStateSnapshot(): GoalState | undefined;\n\tsaveGoalStateSnapshot(state: GoalState, expected?: GoalStateRevision): string;\n\tclearGoalStateSnapshot?(state: GoalState, now: string): string;\n}\n\nfunction missingGoal(action: string): GoalActionFailure {\n\treturn { ok: false, error: `No goal exists to ${action}.` };\n}\n\nfunction persistResult(\n\thost: PersistedGoalStateHost,\n\tcurrent: GoalState | undefined,\n\tresult: GoalActionResult,\n): GoalActionResult {\n\tif (!result.ok) return result;\n\thost.saveGoalStateSnapshot(result.state, current ? getGoalStateRevision(current) : undefined);\n\treturn result;\n}\n\nexport function pauseGoal(current: GoalState | undefined, now: string): GoalActionResult {\n\tif (!current) return missingGoal(\"pause\");\n\tif (current.status !== \"active\") {\n\t\treturn { ok: false, error: `Goal '${current.goalId}' is ${current.status}; only active goals can be paused.` };\n\t}\n\treturn { ok: true, state: applyGoalEvent(current, { type: \"pause_goal\", now }) };\n}\n\nexport function resumeGoal(current: GoalState | undefined, now: string): GoalActionResult {\n\tif (!current) return missingGoal(\"resume\");\n\tif (!isGoalResumableStatus(current.status)) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: `Goal '${current.goalId}' is ${current.status}; only paused, blocked, or usage-limited goals can be resumed.`,\n\t\t};\n\t}\n\treturn { ok: true, state: applyGoalEvent(current, { type: \"resume_goal\", now }) };\n}\n\nexport function editGoal(\n\tcurrent: GoalState | undefined,\n\targs: { userGoal: string; tokenBudget?: number },\n\tnow: string,\n): GoalActionResult {\n\tif (!current) return missingGoal(\"edit\");\n\tif (current.status === \"cancelled\") {\n\t\treturn { ok: false, error: `Goal '${current.goalId}' is cancelled; start a new goal instead.` };\n\t}\n\tconst userGoal = args.userGoal.trim();\n\tif (!userGoal) return { ok: false, error: \"Goal objective must not be empty.\" };\n\tif (userGoal.length > MAX_GOAL_OBJECTIVE_LENGTH) {\n\t\treturn { ok: false, error: `Goal objective must be at most ${MAX_GOAL_OBJECTIVE_LENGTH} characters.` };\n\t}\n\tif (args.tokenBudget !== undefined && (!Number.isSafeInteger(args.tokenBudget) || args.tokenBudget <= 0)) {\n\t\treturn { ok: false, error: \"Goal token budget must be a positive integer when provided.\" };\n\t}\n\treturn {\n\t\tok: true,\n\t\tstate: applyGoalEvent(current, { type: \"edit_goal\", userGoal, tokenBudget: args.tokenBudget, now }),\n\t};\n}\n\nexport function cancelGoal(current: GoalState | undefined, now: string): GoalActionResult {\n\tif (!current) return missingGoal(\"cancel\");\n\tif (!isGoalUnfinishedStatus(current.status)) {\n\t\treturn { ok: false, error: `Goal '${current.goalId}' is already ${current.status}.` };\n\t}\n\treturn { ok: true, state: applyGoalEvent(current, { type: \"cancel_goal\", now }) };\n}\n\n/** Explicit owner replacement. The old and new states cross one persistence boundary. */\nexport function replaceGoal(\n\targs: { goalId: string; userGoal: string; tokenBudget?: number },\n\tnow: string,\n): GoalActionResult {\n\treturn applyGoalAction(undefined, { action: \"start\", ...args }, now);\n}\n\nexport function stopGoalFromSystem(\n\tcurrent: GoalState | undefined,\n\targs: { status: \"blocked\" | \"usage_limited\" | \"budget_limited\"; reason: string },\n\tnow: string,\n): GoalActionResult {\n\tif (!current) return missingGoal(\"stop\");\n\tif (current.status !== \"active\") {\n\t\treturn { ok: false, error: `Goal '${current.goalId}' is ${current.status}; no system stop is needed.` };\n\t}\n\tconst reason = args.reason.trim();\n\tif (!reason) return { ok: false, error: \"System goal stop requires a reason.\" };\n\treturn { ok: true, state: applyGoalEvent(current, { type: \"system_stop_goal\", ...args, reason, now }) };\n}\n\nexport function pausePersistedGoal(host: PersistedGoalStateHost, now = new Date().toISOString()): GoalActionResult {\n\tconst current = host.getGoalStateSnapshot();\n\treturn persistResult(host, current, pauseGoal(current, now));\n}\n\nexport function resumePersistedGoal(host: PersistedGoalStateHost, now = new Date().toISOString()): GoalActionResult {\n\tconst current = host.getGoalStateSnapshot();\n\treturn persistResult(host, current, resumeGoal(current, now));\n}\n\nexport function editPersistedGoal(\n\thost: PersistedGoalStateHost,\n\targs: { userGoal: string; tokenBudget?: number },\n\tnow = new Date().toISOString(),\n): GoalActionResult {\n\tconst current = host.getGoalStateSnapshot();\n\treturn persistResult(host, current, editGoal(current, args, now));\n}\n\nexport function cancelPersistedGoal(host: PersistedGoalStateHost, now = new Date().toISOString()): GoalActionResult {\n\tconst current = host.getGoalStateSnapshot();\n\treturn persistResult(host, current, cancelGoal(current, now));\n}\n\nexport type ClearPersistedGoalResult = { ok: true; cleared: boolean } | GoalActionFailure;\n\nexport function clearPersistedGoal(\n\thost: PersistedGoalStateHost,\n\tnow = new Date().toISOString(),\n): ClearPersistedGoalResult {\n\tconst current = host.getGoalStateSnapshot();\n\tif (!current) return { ok: true, cleared: false };\n\tif (!host.clearGoalStateSnapshot) return { ok: false, error: \"This session does not support clearing goals.\" };\n\thost.clearGoalStateSnapshot(current, now);\n\treturn { ok: true, cleared: true };\n}\n"]}
1
+ {"version":3,"file":"goal-lifecycle.js","sourceRoot":"","sources":["../../../src/core/goals/goal-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tapplyGoalEvent,\n\ttype GoalState,\n\tisGoalExecutionActive,\n\tisGoalResumableStatus,\n\tisGoalUnfinishedStatus,\n\tMAX_GOAL_OBJECTIVE_LENGTH,\n} from \"./goal-state.ts\";\nimport { applyGoalAction, type GoalActionFailure, type GoalActionResult } from \"./goal-tool-core.ts\";\n\nexport interface GoalStateRevision {\n\tgoalId: string;\n\trevision: number;\n}\n\nexport function getGoalStateRevision(state: GoalState): GoalStateRevision {\n\treturn { goalId: state.goalId, revision: state.revision ?? 0 };\n}\n\n/** Minimal durable seam shared by CLI, tools, and in-process session restoration. */\nexport interface PersistedGoalStateHost {\n\tgetGoalStateSnapshot(): GoalState | undefined;\n\tsaveGoalStateSnapshot(state: GoalState, expected?: GoalStateRevision): string;\n\tclearGoalStateSnapshot?(state: GoalState, now: string): string;\n}\n\nfunction missingGoal(action: string): GoalActionFailure {\n\treturn { ok: false, error: `No goal exists to ${action}.` };\n}\n\nfunction persistResult(\n\thost: PersistedGoalStateHost,\n\tcurrent: GoalState | undefined,\n\tresult: GoalActionResult,\n): GoalActionResult {\n\tif (!result.ok) return result;\n\thost.saveGoalStateSnapshot(result.state, current ? getGoalStateRevision(current) : undefined);\n\treturn result;\n}\n\nexport function pauseGoal(current: GoalState | undefined, now: string): GoalActionResult {\n\tif (!current) return missingGoal(\"pause\");\n\tif (!isGoalExecutionActive(current.status)) {\n\t\treturn { ok: false, error: `Goal '${current.goalId}' is ${current.status}; only active goals can be paused.` };\n\t}\n\treturn { ok: true, state: applyGoalEvent(current, { type: \"pause_goal\", now }) };\n}\n\nexport function resumeGoal(current: GoalState | undefined, now: string): GoalActionResult {\n\tif (!current) return missingGoal(\"resume\");\n\tif (!isGoalResumableStatus(current.status)) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: `Goal '${current.goalId}' is ${current.status}; only paused, blocked, or usage-limited goals can be resumed.`,\n\t\t};\n\t}\n\treturn { ok: true, state: applyGoalEvent(current, { type: \"resume_goal\", now }) };\n}\n\nexport function editGoal(\n\tcurrent: GoalState | undefined,\n\targs: { userGoal: string; tokenBudget?: number },\n\tnow: string,\n): GoalActionResult {\n\tif (!current) return missingGoal(\"edit\");\n\tif (current.status === \"cancelled\") {\n\t\treturn { ok: false, error: `Goal '${current.goalId}' is cancelled; start a new goal instead.` };\n\t}\n\tconst userGoal = args.userGoal.trim();\n\tif (!userGoal) return { ok: false, error: \"Goal objective must not be empty.\" };\n\tif (userGoal.length > MAX_GOAL_OBJECTIVE_LENGTH) {\n\t\treturn { ok: false, error: `Goal objective must be at most ${MAX_GOAL_OBJECTIVE_LENGTH} characters.` };\n\t}\n\tif (args.tokenBudget !== undefined && (!Number.isSafeInteger(args.tokenBudget) || args.tokenBudget <= 0)) {\n\t\treturn { ok: false, error: \"Goal token budget must be a positive integer when provided.\" };\n\t}\n\treturn {\n\t\tok: true,\n\t\tstate: applyGoalEvent(current, { type: \"edit_goal\", userGoal, tokenBudget: args.tokenBudget, now }),\n\t};\n}\n\nexport function cancelGoal(current: GoalState | undefined, now: string): GoalActionResult {\n\tif (!current) return missingGoal(\"cancel\");\n\tif (!isGoalUnfinishedStatus(current.status)) {\n\t\treturn { ok: false, error: `Goal '${current.goalId}' is already ${current.status}.` };\n\t}\n\treturn { ok: true, state: applyGoalEvent(current, { type: \"cancel_goal\", now }) };\n}\n\n/** Explicit owner replacement. The old and new states cross one persistence boundary. */\nexport function replaceGoal(\n\targs: { goalId: string; userGoal: string; tokenBudget?: number },\n\tnow: string,\n): GoalActionResult {\n\treturn applyGoalAction(undefined, { action: \"start\", ...args }, now);\n}\n\nexport function stopGoalFromSystem(\n\tcurrent: GoalState | undefined,\n\targs: { status: \"blocked\" | \"usage_limited\" | \"budget_limited\"; reason: string },\n\tnow: string,\n): GoalActionResult {\n\tif (!current) return missingGoal(\"stop\");\n\tif (!isGoalExecutionActive(current.status)) {\n\t\treturn { ok: false, error: `Goal '${current.goalId}' is ${current.status}; no system stop is needed.` };\n\t}\n\tconst reason = args.reason.trim();\n\tif (!reason) return { ok: false, error: \"System goal stop requires a reason.\" };\n\treturn { ok: true, state: applyGoalEvent(current, { type: \"system_stop_goal\", ...args, reason, now }) };\n}\n\nexport function pausePersistedGoal(host: PersistedGoalStateHost, now = new Date().toISOString()): GoalActionResult {\n\tconst current = host.getGoalStateSnapshot();\n\treturn persistResult(host, current, pauseGoal(current, now));\n}\n\nexport function resumePersistedGoal(host: PersistedGoalStateHost, now = new Date().toISOString()): GoalActionResult {\n\tconst current = host.getGoalStateSnapshot();\n\treturn persistResult(host, current, resumeGoal(current, now));\n}\n\nexport function editPersistedGoal(\n\thost: PersistedGoalStateHost,\n\targs: { userGoal: string; tokenBudget?: number },\n\tnow = new Date().toISOString(),\n): GoalActionResult {\n\tconst current = host.getGoalStateSnapshot();\n\treturn persistResult(host, current, editGoal(current, args, now));\n}\n\nexport function cancelPersistedGoal(host: PersistedGoalStateHost, now = new Date().toISOString()): GoalActionResult {\n\tconst current = host.getGoalStateSnapshot();\n\treturn persistResult(host, current, cancelGoal(current, now));\n}\n\nexport type ClearPersistedGoalResult = { ok: true; cleared: boolean } | GoalActionFailure;\n\nexport function clearPersistedGoal(\n\thost: PersistedGoalStateHost,\n\tnow = new Date().toISOString(),\n): ClearPersistedGoalResult {\n\tconst current = host.getGoalStateSnapshot();\n\tif (!current) return { ok: true, cleared: false };\n\tif (!host.clearGoalStateSnapshot) return { ok: false, error: \"This session does not support clearing goals.\" };\n\thost.clearGoalStateSnapshot(current, now);\n\treturn { ok: true, cleared: true };\n}\n"]}
@@ -70,6 +70,17 @@ export declare class GoalSessionController {
70
70
  authorizeStartFromTool(input: {
71
71
  tokenBudget?: number;
72
72
  }): string | undefined;
73
+ /**
74
+ * True once the currently held execution lease's goal has crossed into `budget_limited` — set
75
+ * synchronously by `chargeExecutionUsage` as soon as a just-charged response crosses the ceiling.
76
+ * Wired to the agent's `shouldStopAfterTurn` hook so a mid-turn budget stop ends the loop
77
+ * gracefully BEFORE the next provider request is even planned, instead of only being caught by
78
+ * `admitProviderRequest`'s throw — which still exists as the backstop for a fresh turn or an
79
+ * external admission starting against an already budget_limited goal, but would otherwise also
80
+ * fire for a request this same lease was already mid-flight for, surfacing a synthetic error
81
+ * message where a clean turn end belongs.
82
+ */
83
+ hasExecutionLeaseCrossedBudgetLimit(): boolean;
73
84
  /** Reserve only remaining output capacity; actual provider usage is charged on response. */
74
85
  admitProviderRequest(): number | undefined;
75
86
  private resolveExecutionState;
@@ -1 +1 @@
1
- {"version":3,"file":"goal-session-controller.d.ts","sourceRoot":"","sources":["../../../src/core/goals/goal-session-controller.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EACX,2BAA2B,EAC3B,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,EAC1B,aAAa,EACb,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAG9D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,EAAE,KAAK,iBAAiB,EAA4C,MAAM,qBAAqB,CAAC;AACvG,OAAO,EAEN,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,EAChC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAkB,KAAK,SAAS,EAA0B,MAAM,iBAAiB,CAAC;AAEzF,OAAO,EAAE,KAAK,0BAA0B,EAAyB,MAAM,4BAA4B,CAAC;AAOpG,MAAM,WAAW,yBAAyB;IACzC,iBAAiB,IAAI,cAAc,CAAC;IACpC,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAAC;IACvC,cAAc,IAAI,SAAS,UAAU,EAAE,CAAC;IACxC,sBAAsB,IAAI,qBAAqB,GAAG,SAAS,CAAC;IAC5D,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7C,gCAAgC,IAAI,IAAI,CAAC;IACzC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,MAAM,iBAAiB,GAC1B;IAAE,MAAM,EAAE,cAAc,CAAA;CAAE,GAC1B;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACvC;IAAE,MAAM,EAAE,wBAAwB,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAE1D,QAAA,MAAM,wBAAwB,EAAE,OAAO,MAAqC,CAAC;AAE7E,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,CAAC,wBAAwB,CAAC,EAAE,IAAI,CAAC;CAC1C;AAWD;;;GAGG;AACH,qBAAa,qBAAqB;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAC1C,OAAO,CAAC,cAAc,CAAwC;IAC9D,OAAO,CAAC,cAAc,CAAyC;IAE/D,YAAY,IAAI,EAAE,yBAAyB,EAS1C;IAED,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAmBhE;IAED,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAUhD;IAED,QAAQ,IAAI,SAAS,GAAG,SAAS,CAEhC;IAED;;;;OAIG;IACH,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAA2B,GAAG,iBAAiB,CAwBrF;IAED,6FAA6F;IAC7F,sBAAsB,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAYzE;IAED,gGAAgG;IAChG,cAAc,CACb,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,GAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAC;QAAC,sBAAsB,CAAC,EAAE,MAAM,CAAA;KAAO,GACvE,kBAAkB,GAAG,SAAS,CAkBhC;IAED,YAAY,CAAC,KAAK,EAAE,kBAAkB,GAAG,SAAS,GAAG,IAAI,CAKxD;IAED,iHAAiH;IACjH,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAmBpD;IAED,OAAO,CAAC,oBAAoB;IA4B5B,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAaxD;IAED,iBAAiB,CAAC,SAAS,EAAE,0BAA0B,GAAG,SAAS,GAAG,IAAI,CAEzE;IAED,sBAAsB,CAAC,KAAK,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,GAAG,SAAS,CAU1E;IAED,4FAA4F;IAC5F,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAoBzC;IAED,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,0BAA0B;IAOlC,mBAAmB,IAAI,IAAI,CAK1B;IAED,kBAAkB,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAKxE;IAED,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAKxD;IAED,kBAAkB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,mBAAmB,CAO7E;IAED,YAAY,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAEtF;IAED,YAAY,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAEtF;IAED,kBAAkB,IAAI,IAAI,CAEzB;IAED,OAAO,CAAC,yBAAyB;IASjC,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,cAAc;CAOtB"}
1
+ {"version":3,"file":"goal-session-controller.d.ts","sourceRoot":"","sources":["../../../src/core/goals/goal-session-controller.ts"],"names":[],"mappings":"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"}
@@ -2,12 +2,25 @@ import { createHash } from "node:crypto";
2
2
  import { classifyFailure } from "@caupulican/pi-agent-core/reliability";
3
3
  import { GoalLoopController } from "../goal-loop-controller.js";
4
4
  import { budgetedTokens } from "../orchestration/capability-gateway.js";
5
+ import { GoalBudgetExhaustedError } from "./goal-execution-errors.js";
5
6
  import { getGoalStateRevision, stopGoalFromSystem } from "./goal-lifecycle.js";
6
7
  import { buildGoalRuntimeSnapshot, } from "./goal-runtime-snapshot.js";
7
- import { applyGoalEvent, isGoalUnfinishedStatus } from "./goal-state.js";
8
+ import { applyGoalEvent, isGoalExecutionActive, isGoalUnfinishedStatus } from "./goal-state.js";
8
9
  import { applyGoalAction } from "./goal-tool-core.js";
9
10
  import { parseExplicitChatGoal } from "./natural-language-goal.js";
10
11
  import { appendGoalClearedSnapshot, appendGoalStateSnapshot, getLatestGoalStateSnapshot, } from "./session-goal-state.js";
12
+ /**
13
+ * Smallest remaining goal budget worth handing to the provider as one more request's OUTPUT cap.
14
+ * `admitProviderRequest` returns the goal's remaining TOTAL budget (input+output, cache-weighted) as
15
+ * that cap — correct only because it also narrows further downstream against the model's real output
16
+ * limit. When remaining drops below this floor (e.g. 200k budget, 199.4k used → 600 remaining), handing
17
+ * 600 to the provider as maxTokens produces a request that pays the FULL input cost of the turn only to
18
+ * get cut off mid-generation (`stopReason: "length"`) — a doomed, wasted turn. Below the floor, stop
19
+ * the goal cleanly BEFORE sending instead of gambling on a truncated one. Above it, the remaining-total
20
+ * value is a generous (imprecise but safe) protective ceiling — a modest final-turn overrun is
21
+ * acceptable; a truncated turn is not.
22
+ */
23
+ const MIN_VIABLE_GOAL_TURN_OUTPUT_TOKENS = 1_000;
11
24
  const goalExecutionLeaseMarker = Symbol("goalExecutionLease");
12
25
  /**
13
26
  * Owns durable goal state, exact continuation accounting, and the raw continuation loop. The
@@ -103,7 +116,7 @@ export class GoalSessionController {
103
116
  if (this.executionLease)
104
117
  throw new Error("Goal execution attribution is already active");
105
118
  const state = this.getState();
106
- if (goalId && (!state || state.goalId !== goalId || state.status !== "active"))
119
+ if (goalId && (!state || state.goalId !== goalId || !isGoalExecutionActive(state.status)))
107
120
  return undefined;
108
121
  const lease = {
109
122
  ...(goalId ? { goalId } : {}),
@@ -115,6 +128,7 @@ export class GoalSessionController {
115
128
  : {}),
116
129
  pendingTokens: 0,
117
130
  pendingSpendUsd: 0,
131
+ admittedWhileActive: false,
118
132
  };
119
133
  this.executionLease = lease;
120
134
  return lease;
@@ -162,7 +176,7 @@ export class GoalSessionController {
162
176
  this.saveState(updated, getGoalStateRevision(state));
163
177
  lease.pendingTokens = 0;
164
178
  lease.pendingSpendUsd = 0;
165
- if (updated.status === "active" &&
179
+ if (isGoalExecutionActive(updated.status) &&
166
180
  updated.tokenBudget !== undefined &&
167
181
  (updated.tokensUsed ?? 0) >= updated.tokenBudget) {
168
182
  this.markBudgetLimited(`token budget exhausted (${updated.tokensUsed ?? 0}/${updated.tokenBudget}) during goal execution`);
@@ -173,7 +187,7 @@ export class GoalSessionController {
173
187
  if (!lease)
174
188
  return false;
175
189
  const state = this.resolveExecutionState(lease);
176
- if (!state || state.status !== "active")
190
+ if (!state || !isGoalExecutionActive(state.status))
177
191
  return false;
178
192
  const stopped = stopGoalFromSystem(state, { status: "blocked", reason: errorMessage.slice(0, 500) }, new Date().toISOString());
179
193
  if (!stopped.ok)
@@ -196,6 +210,22 @@ export class GoalSessionController {
196
210
  }
197
211
  return undefined;
198
212
  }
213
+ /**
214
+ * True once the currently held execution lease's goal has crossed into `budget_limited` — set
215
+ * synchronously by `chargeExecutionUsage` as soon as a just-charged response crosses the ceiling.
216
+ * Wired to the agent's `shouldStopAfterTurn` hook so a mid-turn budget stop ends the loop
217
+ * gracefully BEFORE the next provider request is even planned, instead of only being caught by
218
+ * `admitProviderRequest`'s throw — which still exists as the backstop for a fresh turn or an
219
+ * external admission starting against an already budget_limited goal, but would otherwise also
220
+ * fire for a request this same lease was already mid-flight for, surfacing a synthetic error
221
+ * message where a clean turn end belongs.
222
+ */
223
+ hasExecutionLeaseCrossedBudgetLimit() {
224
+ const lease = this.executionLease;
225
+ if (!lease)
226
+ return false;
227
+ return this.resolveExecutionState(lease)?.status === "budget_limited";
228
+ }
199
229
  /** Reserve only remaining output capacity; actual provider usage is charged on response. */
200
230
  admitProviderRequest() {
201
231
  const lease = this.executionLease;
@@ -203,21 +233,39 @@ export class GoalSessionController {
203
233
  return undefined;
204
234
  this.flushPendingExecutionUsage(lease);
205
235
  const state = this.resolveExecutionState(lease);
206
- const tokenBudget = state?.tokenBudget ?? lease.provisionalTokenBudget;
207
- if (tokenBudget === undefined)
208
- return undefined;
209
236
  if (state?.status === "budget_limited") {
210
- throw new Error(`goal_token_budget_exhausted: ${state.blockedReason ?? lease.goalId}`);
237
+ throw new GoalBudgetExhaustedError(`goal_token_budget_exhausted: ${state.blockedReason ?? lease.goalId}`);
211
238
  }
212
- if (state && state.status !== "active")
239
+ if (state && !isGoalExecutionActive(state.status)) {
240
+ if (!lease.admittedWhileActive) {
241
+ // A fresh turn/continuation is being started against a goal that is already done —
242
+ // keep this denied so a dead goal cannot restart itself.
243
+ throw new Error(`goal_execution_not_active: ${state.goalId} is ${state.status}`);
244
+ }
245
+ // This lease already admitted at least one request while the goal was active, so the
246
+ // goal ended mid-turn (e.g. the model itself just called `goal complete`/`block`). Let
247
+ // the already-in-flight turn drain to its closing response instead of throwing an error
248
+ // at the user right after a successful stop.
249
+ return undefined;
250
+ }
251
+ if (!state && lease.goalId !== undefined) {
252
+ throw new Error(`goal_execution_not_active: ${lease.goalId} no longer exists`);
253
+ }
254
+ lease.admittedWhileActive = true;
255
+ const tokenBudget = state?.tokenBudget ?? lease.provisionalTokenBudget;
256
+ if (tokenBudget === undefined)
213
257
  return undefined;
214
258
  const tokensUsed = state?.tokensUsed ?? lease.pendingTokens;
215
259
  const remaining = Math.max(0, tokenBudget - tokensUsed);
216
- if (remaining === 0) {
260
+ if (remaining < MIN_VIABLE_GOAL_TURN_OUTPUT_TOKENS) {
261
+ // Below the floor, remaining is too small to hand to the provider as an output cap without
262
+ // dooming the turn to a mid-generation truncation that still burns the full input cost.
263
+ // Stop cleanly now instead of sending a request that cannot possibly complete.
217
264
  if (state) {
218
- this.markBudgetLimited(`token budget exhausted (${tokensUsed}/${tokenBudget}) before provider request`);
265
+ this.markBudgetLimited(`token budget nearly exhausted (${tokensUsed}/${tokenBudget}, ${remaining} remaining is below the ` +
266
+ `${MIN_VIABLE_GOAL_TURN_OUTPUT_TOKENS}-token minimum viable turn) before provider request`);
219
267
  }
220
- throw new Error(`goal_token_budget_exhausted: no tokens remain for provider output`);
268
+ throw new GoalBudgetExhaustedError(`goal_token_budget_exhausted: ${remaining} tokens remain, below the ${MIN_VIABLE_GOAL_TURN_OUTPUT_TOKENS}-token minimum viable turn`);
221
269
  }
222
270
  return remaining;
223
271
  }
@@ -238,8 +286,20 @@ export class GoalSessionController {
238
286
  if (lease.pendingTokens === 0 && lease.pendingSpendUsd === 0)
239
287
  return;
240
288
  const state = this.resolveExecutionState(lease);
241
- if (!state)
289
+ if (!state) {
290
+ // A speculative adopt-new-goal lease (lease.goalId still undefined) that never actually
291
+ // adopted a goal this turn has nowhere to attribute usage — that is normal, not a loss.
292
+ // A lease that WAS bound to a real goal but can no longer resolve it is a genuine loss of
293
+ // buffered spend; fail loudly instead of silently discarding it (matches the cursor-based
294
+ // accounting this replaced, which stopped the goal on `goal_usage_cursor_lost`).
295
+ if (lease.goalId !== undefined) {
296
+ this.deps.emitWarning(`Goal usage cursor is no longer resolvable for '${lease.goalId}'; ${lease.pendingTokens} pending tokens and $${lease.pendingSpendUsd.toFixed(6)} pending spend could not be attributed and were dropped.`);
297
+ this.recordContinuationFailure(new Error("goal_usage_cursor_lost"));
298
+ }
299
+ lease.pendingTokens = 0;
300
+ lease.pendingSpendUsd = 0;
242
301
  return;
302
+ }
243
303
  this.chargeExecutionUsage(lease, state, lease.pendingTokens, lease.pendingSpendUsd);
244
304
  }
245
305
  markToolUnavailable() {
@@ -270,7 +330,7 @@ export class GoalSessionController {
270
330
  }
271
331
  recordContinuationFailure(error) {
272
332
  const state = this.getState();
273
- if (!state || state.status !== "active")
333
+ if (!state || !isGoalExecutionActive(state.status))
274
334
  return;
275
335
  const message = error instanceof Error ? error.message : String(error);
276
336
  const classified = classifyFailure({ message, provider: this.deps.getModelProvider() });