@calliopelabs/cli 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -7
- package/dist/acp.d.ts +1 -4
- package/dist/acp.d.ts.map +1 -1
- package/dist/acp.js +118 -217
- package/dist/acp.js.map +1 -1
- package/dist/approvals/commands.d.ts +35 -0
- package/dist/approvals/commands.d.ts.map +1 -0
- package/dist/approvals/commands.js +51 -0
- package/dist/approvals/commands.js.map +1 -0
- package/dist/approvals/index.d.ts +6 -0
- package/dist/approvals/index.d.ts.map +1 -0
- package/dist/approvals/index.js +6 -0
- package/dist/approvals/index.js.map +1 -0
- package/dist/approvals/queue.d.ts +18 -0
- package/dist/approvals/queue.d.ts.map +1 -0
- package/dist/approvals/queue.js +46 -0
- package/dist/approvals/queue.js.map +1 -0
- package/dist/approvals/request.d.ts +13 -0
- package/dist/approvals/request.d.ts.map +1 -0
- package/dist/approvals/request.js +102 -0
- package/dist/approvals/request.js.map +1 -0
- package/dist/approvals/store.d.ts +24 -0
- package/dist/approvals/store.d.ts.map +1 -0
- package/dist/approvals/store.js +229 -0
- package/dist/approvals/store.js.map +1 -0
- package/dist/approvals/types.d.ts +27 -0
- package/dist/approvals/types.d.ts.map +1 -0
- package/dist/approvals/types.js +4 -0
- package/dist/approvals/types.js.map +1 -0
- package/dist/auto-compressor.d.ts +3 -2
- package/dist/auto-compressor.d.ts.map +1 -1
- package/dist/auto-compressor.js +9 -9
- package/dist/auto-compressor.js.map +1 -1
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +125 -22
- package/dist/bin.js.map +1 -1
- package/dist/brain/access.d.ts +65 -0
- package/dist/brain/access.d.ts.map +1 -0
- package/dist/brain/access.js +216 -0
- package/dist/brain/access.js.map +1 -0
- package/dist/brain/actions.d.ts +120 -0
- package/dist/brain/actions.d.ts.map +1 -0
- package/dist/brain/actions.js +270 -0
- package/dist/brain/actions.js.map +1 -0
- package/dist/brain/cli.d.ts +17 -0
- package/dist/brain/cli.d.ts.map +1 -0
- package/dist/brain/cli.js +206 -0
- package/dist/brain/cli.js.map +1 -0
- package/dist/brain/index.d.ts +16 -0
- package/dist/brain/index.d.ts.map +1 -0
- package/dist/brain/index.js +16 -0
- package/dist/brain/index.js.map +1 -0
- package/dist/brain/journal.d.ts +17 -0
- package/dist/brain/journal.d.ts.map +1 -0
- package/dist/brain/journal.js +189 -0
- package/dist/brain/journal.js.map +1 -0
- package/dist/brain/kg.d.ts +23 -0
- package/dist/brain/kg.d.ts.map +1 -0
- package/dist/brain/kg.js +36 -0
- package/dist/brain/kg.js.map +1 -0
- package/dist/brain/presentation.d.ts +3 -0
- package/dist/brain/presentation.d.ts.map +1 -0
- package/dist/brain/presentation.js +48 -0
- package/dist/brain/presentation.js.map +1 -0
- package/dist/brain/queries.d.ts +179 -0
- package/dist/brain/queries.d.ts.map +1 -0
- package/dist/brain/queries.js +210 -0
- package/dist/brain/queries.js.map +1 -0
- package/dist/brain/run-ingest.d.ts +29 -0
- package/dist/brain/run-ingest.d.ts.map +1 -0
- package/dist/brain/run-ingest.js +200 -0
- package/dist/brain/run-ingest.js.map +1 -0
- package/dist/brain/sqlite.d.ts +19 -0
- package/dist/brain/sqlite.d.ts.map +1 -0
- package/dist/brain/sqlite.js +252 -0
- package/dist/brain/sqlite.js.map +1 -0
- package/dist/brain/store.d.ts +31 -0
- package/dist/brain/store.d.ts.map +1 -0
- package/dist/brain/store.js +324 -0
- package/dist/brain/store.js.map +1 -0
- package/dist/brain/tool-query.d.ts +6 -0
- package/dist/brain/tool-query.d.ts.map +1 -0
- package/dist/brain/tool-query.js +42 -0
- package/dist/brain/tool-query.js.map +1 -0
- package/dist/brain/tools.d.ts +13 -0
- package/dist/brain/tools.d.ts.map +1 -0
- package/dist/brain/tools.js +20 -0
- package/dist/brain/tools.js.map +1 -0
- package/dist/brain/transfer.d.ts +57 -0
- package/dist/brain/transfer.d.ts.map +1 -0
- package/dist/brain/transfer.js +266 -0
- package/dist/brain/transfer.js.map +1 -0
- package/dist/brain/types.d.ts +145 -0
- package/dist/brain/types.d.ts.map +1 -0
- package/dist/brain/types.js +38 -0
- package/dist/brain/types.js.map +1 -0
- package/dist/brain/validation.d.ts +15 -0
- package/dist/brain/validation.d.ts.map +1 -0
- package/dist/brain/validation.js +163 -0
- package/dist/brain/validation.js.map +1 -0
- package/dist/budget.d.ts +5 -4
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +16 -38
- package/dist/budget.js.map +1 -1
- package/dist/cancellation.d.ts +8 -0
- package/dist/cancellation.d.ts.map +1 -0
- package/dist/cancellation.js +42 -0
- package/dist/cancellation.js.map +1 -0
- package/dist/checkpoint.d.ts +2 -2
- package/dist/checkpoint.d.ts.map +1 -1
- package/dist/checkpoint.js +5 -5
- package/dist/checkpoint.js.map +1 -1
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +32 -8
- package/dist/config.js.map +1 -1
- package/dist/doctor.d.ts +30 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +124 -0
- package/dist/doctor.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +33 -9
- package/dist/errors.js.map +1 -1
- package/dist/execution/attribution.d.ts +17 -0
- package/dist/execution/attribution.d.ts.map +1 -0
- package/dist/execution/attribution.js +21 -0
- package/dist/execution/attribution.js.map +1 -0
- package/dist/execution/authority.d.ts +22 -0
- package/dist/execution/authority.d.ts.map +1 -0
- package/dist/execution/authority.js +134 -0
- package/dist/execution/authority.js.map +1 -0
- package/dist/execution/billing.d.ts +100 -0
- package/dist/execution/billing.d.ts.map +1 -0
- package/dist/execution/billing.js +166 -0
- package/dist/execution/billing.js.map +1 -0
- package/dist/execution/child-grants.d.ts +9 -0
- package/dist/execution/child-grants.d.ts.map +1 -0
- package/dist/execution/child-grants.js +55 -0
- package/dist/execution/child-grants.js.map +1 -0
- package/dist/execution/files.d.ts +6 -0
- package/dist/execution/files.d.ts.map +1 -0
- package/dist/execution/files.js +159 -0
- package/dist/execution/files.js.map +1 -0
- package/dist/execution/guard.d.ts +50 -0
- package/dist/execution/guard.d.ts.map +1 -0
- package/dist/execution/guard.js +158 -0
- package/dist/execution/guard.js.map +1 -0
- package/dist/execution/index.d.ts +12 -0
- package/dist/execution/index.d.ts.map +1 -0
- package/dist/execution/index.js +12 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/execution/ledger.d.ts +23 -0
- package/dist/execution/ledger.d.ts.map +1 -0
- package/dist/execution/ledger.js +379 -0
- package/dist/execution/ledger.js.map +1 -0
- package/dist/execution/project-spend.d.ts +45 -0
- package/dist/execution/project-spend.d.ts.map +1 -0
- package/dist/execution/project-spend.js +274 -0
- package/dist/execution/project-spend.js.map +1 -0
- package/dist/execution/quote.d.ts +19 -0
- package/dist/execution/quote.d.ts.map +1 -0
- package/dist/execution/quote.js +116 -0
- package/dist/execution/quote.js.map +1 -0
- package/dist/execution/types.d.ts +113 -0
- package/dist/execution/types.d.ts.map +1 -0
- package/dist/execution/types.js +9 -0
- package/dist/execution/types.js.map +1 -0
- package/dist/execution/writer-recovery.d.ts +3 -0
- package/dist/execution/writer-recovery.d.ts.map +1 -0
- package/dist/execution/writer-recovery.js +85 -0
- package/dist/execution/writer-recovery.js.map +1 -0
- package/dist/goals/accounting.d.ts +69 -0
- package/dist/goals/accounting.d.ts.map +1 -0
- package/dist/goals/accounting.js +100 -0
- package/dist/goals/accounting.js.map +1 -0
- package/dist/goals/actions.d.ts +33 -0
- package/dist/goals/actions.d.ts.map +1 -0
- package/dist/goals/actions.js +261 -0
- package/dist/goals/actions.js.map +1 -0
- package/dist/goals/authority.d.ts +9 -0
- package/dist/goals/authority.d.ts.map +1 -0
- package/dist/goals/authority.js +47 -0
- package/dist/goals/authority.js.map +1 -0
- package/dist/goals/cli.d.ts +10 -0
- package/dist/goals/cli.d.ts.map +1 -0
- package/dist/goals/cli.js +169 -0
- package/dist/goals/cli.js.map +1 -0
- package/dist/goals/contracts.d.ts +18 -0
- package/dist/goals/contracts.d.ts.map +1 -0
- package/dist/goals/contracts.js +127 -0
- package/dist/goals/contracts.js.map +1 -0
- package/dist/goals/index.d.ts +17 -0
- package/dist/goals/index.d.ts.map +1 -0
- package/dist/goals/index.js +16 -0
- package/dist/goals/index.js.map +1 -0
- package/dist/goals/journal.d.ts +5 -0
- package/dist/goals/journal.d.ts.map +1 -0
- package/dist/goals/journal.js +142 -0
- package/dist/goals/journal.js.map +1 -0
- package/dist/goals/metrics.d.ts +53 -0
- package/dist/goals/metrics.d.ts.map +1 -0
- package/dist/goals/metrics.js +141 -0
- package/dist/goals/metrics.js.map +1 -0
- package/dist/goals/repair.d.ts +10 -0
- package/dist/goals/repair.d.ts.map +1 -0
- package/dist/goals/repair.js +50 -0
- package/dist/goals/repair.js.map +1 -0
- package/dist/goals/routing.d.ts +18 -0
- package/dist/goals/routing.d.ts.map +1 -0
- package/dist/goals/routing.js +60 -0
- package/dist/goals/routing.js.map +1 -0
- package/dist/goals/store.d.ts +30 -0
- package/dist/goals/store.d.ts.map +1 -0
- package/dist/goals/store.js +314 -0
- package/dist/goals/store.js.map +1 -0
- package/dist/goals/supervised-cli.d.ts +5 -0
- package/dist/goals/supervised-cli.d.ts.map +1 -0
- package/dist/goals/supervised-cli.js +44 -0
- package/dist/goals/supervised-cli.js.map +1 -0
- package/dist/goals/supervised.d.ts +8 -0
- package/dist/goals/supervised.d.ts.map +1 -0
- package/dist/goals/supervised.js +65 -0
- package/dist/goals/supervised.js.map +1 -0
- package/dist/goals/team.d.ts +8 -0
- package/dist/goals/team.d.ts.map +1 -0
- package/dist/goals/team.js +64 -0
- package/dist/goals/team.js.map +1 -0
- package/dist/goals/types.d.ts +174 -0
- package/dist/goals/types.d.ts.map +1 -0
- package/dist/goals/types.js +2 -0
- package/dist/goals/types.js.map +1 -0
- package/dist/goals/validation.d.ts +17 -0
- package/dist/goals/validation.d.ts.map +1 -0
- package/dist/goals/validation.js +172 -0
- package/dist/goals/validation.js.map +1 -0
- package/dist/headless.d.ts +1 -0
- package/dist/headless.d.ts.map +1 -1
- package/dist/headless.js +98 -275
- package/dist/headless.js.map +1 -1
- package/dist/health/index.d.ts +5 -0
- package/dist/health/index.d.ts.map +1 -0
- package/dist/health/index.js +5 -0
- package/dist/health/index.js.map +1 -0
- package/dist/health/store.d.ts +24 -0
- package/dist/health/store.d.ts.map +1 -0
- package/dist/health/store.js +190 -0
- package/dist/health/store.js.map +1 -0
- package/dist/health/summary.d.ts +10 -0
- package/dist/health/summary.d.ts.map +1 -0
- package/dist/health/summary.js +95 -0
- package/dist/health/summary.js.map +1 -0
- package/dist/health/target.d.ts +3 -0
- package/dist/health/target.d.ts.map +1 -0
- package/dist/health/target.js +53 -0
- package/dist/health/target.js.map +1 -0
- package/dist/health/types.d.ts +72 -0
- package/dist/health/types.d.ts.map +1 -0
- package/dist/health/types.js +2 -0
- package/dist/health/types.js.map +1 -0
- package/dist/hooks.d.ts +7 -2
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +35 -17
- package/dist/hooks.js.map +1 -1
- package/dist/improvement/actions.d.ts +27 -0
- package/dist/improvement/actions.d.ts.map +1 -0
- package/dist/improvement/actions.js +116 -0
- package/dist/improvement/actions.js.map +1 -0
- package/dist/improvement/cli.d.ts +9 -0
- package/dist/improvement/cli.d.ts.map +1 -0
- package/dist/improvement/cli.js +58 -0
- package/dist/improvement/cli.js.map +1 -0
- package/dist/improvement/feedback.d.ts +30 -0
- package/dist/improvement/feedback.d.ts.map +1 -0
- package/dist/improvement/feedback.js +7 -0
- package/dist/improvement/feedback.js.map +1 -0
- package/dist/improvement/index.d.ts +7 -0
- package/dist/improvement/index.d.ts.map +1 -0
- package/dist/improvement/index.js +7 -0
- package/dist/improvement/index.js.map +1 -0
- package/dist/improvement/metrics.d.ts +4 -0
- package/dist/improvement/metrics.d.ts.map +1 -0
- package/dist/improvement/metrics.js +38 -0
- package/dist/improvement/metrics.js.map +1 -0
- package/dist/improvement/projection.d.ts +7 -0
- package/dist/improvement/projection.d.ts.map +1 -0
- package/dist/improvement/projection.js +135 -0
- package/dist/improvement/projection.js.map +1 -0
- package/dist/improvement/types.d.ts +115 -0
- package/dist/improvement/types.d.ts.map +1 -0
- package/dist/improvement/types.js +2 -0
- package/dist/improvement/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +10 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +93 -0
- package/dist/instructions.js.map +1 -0
- package/dist/isolation/contracts.d.ts +42 -0
- package/dist/isolation/contracts.d.ts.map +1 -0
- package/dist/isolation/contracts.js +33 -0
- package/dist/isolation/contracts.js.map +1 -0
- package/dist/isolation/coordinator.d.ts +10 -0
- package/dist/isolation/coordinator.d.ts.map +1 -0
- package/dist/isolation/coordinator.js +79 -0
- package/dist/isolation/coordinator.js.map +1 -0
- package/dist/isolation/index.d.ts +5 -0
- package/dist/isolation/index.d.ts.map +1 -0
- package/dist/isolation/index.js +5 -0
- package/dist/isolation/index.js.map +1 -0
- package/dist/isolation/process.d.ts +11 -0
- package/dist/isolation/process.d.ts.map +1 -0
- package/dist/isolation/process.js +131 -0
- package/dist/isolation/process.js.map +1 -0
- package/dist/isolation/worktree.d.ts +25 -0
- package/dist/isolation/worktree.d.ts.map +1 -0
- package/dist/isolation/worktree.js +282 -0
- package/dist/isolation/worktree.js.map +1 -0
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +5 -1
- package/dist/memory.js.map +1 -1
- package/dist/model-detection.d.ts +14 -18
- package/dist/model-detection.d.ts.map +1 -1
- package/dist/model-detection.js +287 -149
- package/dist/model-detection.js.map +1 -1
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +3 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/metadata.d.ts +43 -0
- package/dist/models/metadata.d.ts.map +1 -0
- package/dist/models/metadata.js +72 -0
- package/dist/models/metadata.js.map +1 -0
- package/dist/models/openrouter.d.ts +6 -0
- package/dist/models/openrouter.d.ts.map +1 -0
- package/dist/models/openrouter.js +38 -0
- package/dist/models/openrouter.js.map +1 -0
- package/dist/orchestration/accounting.d.ts +35 -0
- package/dist/orchestration/accounting.d.ts.map +1 -0
- package/dist/orchestration/accounting.js +27 -0
- package/dist/orchestration/accounting.js.map +1 -0
- package/dist/orchestration/actions.d.ts +23 -0
- package/dist/orchestration/actions.d.ts.map +1 -0
- package/dist/orchestration/actions.js +65 -0
- package/dist/orchestration/actions.js.map +1 -0
- package/dist/orchestration/cli.d.ts +28 -0
- package/dist/orchestration/cli.d.ts.map +1 -0
- package/dist/orchestration/cli.js +178 -0
- package/dist/orchestration/cli.js.map +1 -0
- package/dist/orchestration/coordinator-actions.d.ts +15 -0
- package/dist/orchestration/coordinator-actions.d.ts.map +1 -0
- package/dist/orchestration/coordinator-actions.js +79 -0
- package/dist/orchestration/coordinator-actions.js.map +1 -0
- package/dist/orchestration/coordinator-types.d.ts +193 -0
- package/dist/orchestration/coordinator-types.d.ts.map +1 -0
- package/dist/orchestration/coordinator-types.js +2 -0
- package/dist/orchestration/coordinator-types.js.map +1 -0
- package/dist/orchestration/coordinator.d.ts +32 -0
- package/dist/orchestration/coordinator.d.ts.map +1 -0
- package/dist/orchestration/coordinator.js +405 -0
- package/dist/orchestration/coordinator.js.map +1 -0
- package/dist/orchestration/execution-cli.d.ts +11 -0
- package/dist/orchestration/execution-cli.d.ts.map +1 -0
- package/dist/orchestration/execution-cli.js +183 -0
- package/dist/orchestration/execution-cli.js.map +1 -0
- package/dist/orchestration/execution-journal.d.ts +15 -0
- package/dist/orchestration/execution-journal.d.ts.map +1 -0
- package/dist/orchestration/execution-journal.js +477 -0
- package/dist/orchestration/execution-journal.js.map +1 -0
- package/dist/orchestration/execution-store.d.ts +39 -0
- package/dist/orchestration/execution-store.d.ts.map +1 -0
- package/dist/orchestration/execution-store.js +325 -0
- package/dist/orchestration/execution-store.js.map +1 -0
- package/dist/orchestration/execution.d.ts +8 -0
- package/dist/orchestration/execution.d.ts.map +1 -0
- package/dist/orchestration/execution.js +47 -0
- package/dist/orchestration/execution.js.map +1 -0
- package/dist/orchestration/index.d.ts +20 -0
- package/dist/orchestration/index.d.ts.map +1 -0
- package/dist/orchestration/index.js +17 -0
- package/dist/orchestration/index.js.map +1 -0
- package/dist/orchestration/progress.d.ts +19 -0
- package/dist/orchestration/progress.d.ts.map +1 -0
- package/dist/orchestration/progress.js +20 -0
- package/dist/orchestration/progress.js.map +1 -0
- package/dist/orchestration/recovery.d.ts +4 -0
- package/dist/orchestration/recovery.d.ts.map +1 -0
- package/dist/orchestration/recovery.js +121 -0
- package/dist/orchestration/recovery.js.map +1 -0
- package/dist/orchestration/request-accounting.d.ts +35 -0
- package/dist/orchestration/request-accounting.d.ts.map +1 -0
- package/dist/orchestration/request-accounting.js +71 -0
- package/dist/orchestration/request-accounting.js.map +1 -0
- package/dist/orchestration/routing.d.ts +20 -0
- package/dist/orchestration/routing.d.ts.map +1 -0
- package/dist/orchestration/routing.js +38 -0
- package/dist/orchestration/routing.js.map +1 -0
- package/dist/orchestration/store.d.ts +26 -0
- package/dist/orchestration/store.d.ts.map +1 -0
- package/dist/orchestration/store.js +381 -0
- package/dist/orchestration/store.js.map +1 -0
- package/dist/orchestration/types.d.ts +201 -0
- package/dist/orchestration/types.d.ts.map +1 -0
- package/dist/orchestration/types.js +11 -0
- package/dist/orchestration/types.js.map +1 -0
- package/dist/orchestration/validation.d.ts +29 -0
- package/dist/orchestration/validation.d.ts.map +1 -0
- package/dist/orchestration/validation.js +440 -0
- package/dist/orchestration/validation.js.map +1 -0
- package/dist/orchestration/verification.d.ts +30 -0
- package/dist/orchestration/verification.d.ts.map +1 -0
- package/dist/orchestration/verification.js +204 -0
- package/dist/orchestration/verification.js.map +1 -0
- package/dist/orchestration/worker-context.d.ts +33 -0
- package/dist/orchestration/worker-context.d.ts.map +1 -0
- package/dist/orchestration/worker-context.js +31 -0
- package/dist/orchestration/worker-context.js.map +1 -0
- package/dist/policy.d.ts +1 -0
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +25 -5
- package/dist/policy.js.map +1 -1
- package/dist/preferences/controls.d.ts +21 -0
- package/dist/preferences/controls.d.ts.map +1 -0
- package/dist/preferences/controls.js +36 -0
- package/dist/preferences/controls.js.map +1 -0
- package/dist/preferences/flags.d.ts +8 -0
- package/dist/preferences/flags.d.ts.map +1 -0
- package/dist/preferences/flags.js +27 -0
- package/dist/preferences/flags.js.map +1 -0
- package/dist/preferences/index.d.ts +9 -0
- package/dist/preferences/index.d.ts.map +1 -0
- package/dist/preferences/index.js +9 -0
- package/dist/preferences/index.js.map +1 -0
- package/dist/preferences/once.d.ts +8 -0
- package/dist/preferences/once.d.ts.map +1 -0
- package/dist/preferences/once.js +28 -0
- package/dist/preferences/once.js.map +1 -0
- package/dist/preferences/resolve.d.ts +14 -0
- package/dist/preferences/resolve.d.ts.map +1 -0
- package/dist/preferences/resolve.js +58 -0
- package/dist/preferences/resolve.js.map +1 -0
- package/dist/preferences/store.d.ts +19 -0
- package/dist/preferences/store.d.ts.map +1 -0
- package/dist/preferences/store.js +147 -0
- package/dist/preferences/store.js.map +1 -0
- package/dist/preferences/submission.d.ts +16 -0
- package/dist/preferences/submission.d.ts.map +1 -0
- package/dist/preferences/submission.js +29 -0
- package/dist/preferences/submission.js.map +1 -0
- package/dist/preferences/types.d.ts +23 -0
- package/dist/preferences/types.d.ts.map +1 -0
- package/dist/preferences/types.js +2 -0
- package/dist/preferences/types.js.map +1 -0
- package/dist/preferences/validate.d.ts +3 -0
- package/dist/preferences/validate.d.ts.map +1 -0
- package/dist/preferences/validate.js +15 -0
- package/dist/preferences/validate.js.map +1 -0
- package/dist/process-cancellation.d.ts +6 -0
- package/dist/process-cancellation.d.ts.map +1 -0
- package/dist/process-cancellation.js +39 -0
- package/dist/process-cancellation.js.map +1 -0
- package/dist/providers/anthropic.d.ts +7 -2
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +72 -41
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/bedrock.d.ts +1 -1
- package/dist/providers/bedrock.d.ts.map +1 -1
- package/dist/providers/bedrock.js +39 -23
- package/dist/providers/bedrock.js.map +1 -1
- package/dist/providers/compat.d.ts +4 -3
- package/dist/providers/compat.d.ts.map +1 -1
- package/dist/providers/compat.js +49 -31
- package/dist/providers/compat.js.map +1 -1
- package/dist/providers/deepseek-reasoning.d.ts +64 -0
- package/dist/providers/deepseek-reasoning.d.ts.map +1 -0
- package/dist/providers/deepseek-reasoning.js +28 -0
- package/dist/providers/deepseek-reasoning.js.map +1 -0
- package/dist/providers/google.d.ts +4 -3
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +128 -19
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +216 -36
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/ollama.d.ts.map +1 -1
- package/dist/providers/ollama.js +79 -43
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +50 -46
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter-bounds.d.ts +22 -0
- package/dist/providers/openrouter-bounds.d.ts.map +1 -0
- package/dist/providers/openrouter-bounds.js +16 -0
- package/dist/providers/openrouter-bounds.js.map +1 -0
- package/dist/providers/stream-attempt.d.ts +29 -0
- package/dist/providers/stream-attempt.d.ts.map +1 -0
- package/dist/providers/stream-attempt.js +63 -0
- package/dist/providers/stream-attempt.js.map +1 -0
- package/dist/providers/types.d.ts +40 -4
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js +19 -0
- package/dist/providers/types.js.map +1 -1
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +16 -0
- package/dist/replay.js.map +1 -1
- package/dist/risk.d.ts.map +1 -1
- package/dist/risk.js +5 -0
- package/dist/risk.js.map +1 -1
- package/dist/router.d.ts +7 -110
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +0 -451
- package/dist/router.js.map +1 -1
- package/dist/routing/index.d.ts +6 -0
- package/dist/routing/index.d.ts.map +1 -0
- package/dist/routing/index.js +5 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/routing/prompt.d.ts +4 -0
- package/dist/routing/prompt.d.ts.map +1 -0
- package/dist/routing/prompt.js +14 -0
- package/dist/routing/prompt.js.map +1 -0
- package/dist/routing/select.d.ts +9 -0
- package/dist/routing/select.d.ts.map +1 -0
- package/dist/routing/select.js +279 -0
- package/dist/routing/select.js.map +1 -0
- package/dist/routing/smart.d.ts +31 -0
- package/dist/routing/smart.d.ts.map +1 -0
- package/dist/routing/smart.js +46 -0
- package/dist/routing/smart.js.map +1 -0
- package/dist/routing/types.d.ts +79 -0
- package/dist/routing/types.d.ts.map +1 -0
- package/dist/routing/types.js +2 -0
- package/dist/routing/types.js.map +1 -0
- package/dist/runlog.d.ts +32 -4
- package/dist/runlog.d.ts.map +1 -1
- package/dist/runlog.js +22 -6
- package/dist/runlog.js.map +1 -1
- package/dist/runtime/index.d.ts +6 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +6 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/permissions.d.ts +21 -0
- package/dist/runtime/permissions.d.ts.map +1 -0
- package/dist/runtime/permissions.js +110 -0
- package/dist/runtime/permissions.js.map +1 -0
- package/dist/runtime/repair.d.ts +20 -0
- package/dist/runtime/repair.d.ts.map +1 -0
- package/dist/runtime/repair.js +50 -0
- package/dist/runtime/repair.js.map +1 -0
- package/dist/runtime/tool-retry.d.ts +3 -0
- package/dist/runtime/tool-retry.d.ts.map +1 -0
- package/dist/runtime/tool-retry.js +12 -0
- package/dist/runtime/tool-retry.js.map +1 -0
- package/dist/runtime/turn.d.ts +97 -0
- package/dist/runtime/turn.d.ts.map +1 -0
- package/dist/runtime/turn.js +437 -0
- package/dist/runtime/turn.js.map +1 -0
- package/dist/runtime/types.d.ts +11 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +5 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/sandbox/docker.d.ts +4 -4
- package/dist/sandbox/docker.d.ts.map +1 -1
- package/dist/sandbox/docker.js +55 -16
- package/dist/sandbox/docker.js.map +1 -1
- package/dist/sandbox/native.d.ts +1 -0
- package/dist/sandbox/native.d.ts.map +1 -1
- package/dist/sandbox/native.js +11 -2
- package/dist/sandbox/native.js.map +1 -1
- package/dist/scope.d.ts +5 -9
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +43 -20
- package/dist/scope.js.map +1 -1
- package/dist/session-management/actions.d.ts +28 -0
- package/dist/session-management/actions.d.ts.map +1 -0
- package/dist/session-management/actions.js +78 -0
- package/dist/session-management/actions.js.map +1 -0
- package/dist/session-management/cli.d.ts +24 -0
- package/dist/session-management/cli.d.ts.map +1 -0
- package/dist/session-management/cli.js +92 -0
- package/dist/session-management/cli.js.map +1 -0
- package/dist/session-management/files.d.ts +5 -0
- package/dist/session-management/files.d.ts.map +1 -0
- package/dist/session-management/files.js +92 -0
- package/dist/session-management/files.js.map +1 -0
- package/dist/session-management/index.d.ts +7 -0
- package/dist/session-management/index.d.ts.map +1 -0
- package/dist/session-management/index.js +7 -0
- package/dist/session-management/index.js.map +1 -0
- package/dist/session-management/permissions.d.ts +12 -0
- package/dist/session-management/permissions.d.ts.map +1 -0
- package/dist/session-management/permissions.js +33 -0
- package/dist/session-management/permissions.js.map +1 -0
- package/dist/session-management/presentation.d.ts +5 -0
- package/dist/session-management/presentation.d.ts.map +1 -0
- package/dist/session-management/presentation.js +10 -0
- package/dist/session-management/presentation.js.map +1 -0
- package/dist/session-management/transfer.d.ts +9 -0
- package/dist/session-management/transfer.d.ts.map +1 -0
- package/dist/session-management/transfer.js +39 -0
- package/dist/session-management/transfer.js.map +1 -0
- package/dist/sessions/context.d.ts +4 -0
- package/dist/sessions/context.d.ts.map +1 -0
- package/dist/sessions/context.js +6 -0
- package/dist/sessions/context.js.map +1 -0
- package/dist/sessions/format.d.ts +38 -0
- package/dist/sessions/format.d.ts.map +1 -0
- package/dist/sessions/format.js +116 -0
- package/dist/sessions/format.js.map +1 -0
- package/dist/sessions/history.d.ts +37 -0
- package/dist/sessions/history.d.ts.map +1 -0
- package/dist/sessions/history.js +197 -0
- package/dist/sessions/history.js.map +1 -0
- package/dist/sessions/index.d.ts +8 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/sessions/index.js +8 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/sessions/output.d.ts +31 -0
- package/dist/sessions/output.d.ts.map +1 -0
- package/dist/sessions/output.js +115 -0
- package/dist/sessions/output.js.map +1 -0
- package/dist/sessions/portable.d.ts +26 -0
- package/dist/sessions/portable.d.ts.map +1 -0
- package/dist/sessions/portable.js +105 -0
- package/dist/sessions/portable.js.map +1 -0
- package/dist/sessions/snapshot.d.ts +11 -0
- package/dist/sessions/snapshot.d.ts.map +1 -0
- package/dist/sessions/snapshot.js +118 -0
- package/dist/sessions/snapshot.js.map +1 -0
- package/dist/sessions/tool-state.d.ts +11 -0
- package/dist/sessions/tool-state.d.ts.map +1 -0
- package/dist/sessions/tool-state.js +152 -0
- package/dist/sessions/tool-state.js.map +1 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +13 -5
- package/dist/setup.js.map +1 -1
- package/dist/spawning/actions.d.ts +23 -0
- package/dist/spawning/actions.d.ts.map +1 -0
- package/dist/spawning/actions.js +120 -0
- package/dist/spawning/actions.js.map +1 -0
- package/dist/spawning/authority.d.ts +15 -0
- package/dist/spawning/authority.d.ts.map +1 -0
- package/dist/spawning/authority.js +16 -0
- package/dist/spawning/authority.js.map +1 -0
- package/dist/spawning/cli.d.ts +4 -0
- package/dist/spawning/cli.d.ts.map +1 -0
- package/dist/spawning/cli.js +46 -0
- package/dist/spawning/cli.js.map +1 -0
- package/dist/spawning/execution.d.ts +13 -0
- package/dist/spawning/execution.d.ts.map +1 -0
- package/dist/spawning/execution.js +59 -0
- package/dist/spawning/execution.js.map +1 -0
- package/dist/spawning/index.d.ts +8 -0
- package/dist/spawning/index.d.ts.map +1 -0
- package/dist/spawning/index.js +8 -0
- package/dist/spawning/index.js.map +1 -0
- package/dist/spawning/store.d.ts +12 -0
- package/dist/spawning/store.d.ts.map +1 -0
- package/dist/spawning/store.js +93 -0
- package/dist/spawning/store.js.map +1 -0
- package/dist/spawning/types.d.ts +34 -0
- package/dist/spawning/types.d.ts.map +1 -0
- package/dist/spawning/types.js +2 -0
- package/dist/spawning/types.js.map +1 -0
- package/dist/spawning/validation.d.ts +14 -0
- package/dist/spawning/validation.d.ts.map +1 -0
- package/dist/spawning/validation.js +95 -0
- package/dist/spawning/validation.js.map +1 -0
- package/dist/storage.d.ts +29 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +112 -40
- package/dist/storage.js.map +1 -1
- package/dist/supervision/approval.d.ts +4 -0
- package/dist/supervision/approval.d.ts.map +1 -0
- package/dist/supervision/approval.js +7 -0
- package/dist/supervision/approval.js.map +1 -0
- package/dist/supervision/availability.d.ts +34 -0
- package/dist/supervision/availability.d.ts.map +1 -0
- package/dist/supervision/availability.js +44 -0
- package/dist/supervision/availability.js.map +1 -0
- package/dist/supervision/context.d.ts +36 -0
- package/dist/supervision/context.d.ts.map +1 -0
- package/dist/supervision/context.js +52 -0
- package/dist/supervision/context.js.map +1 -0
- package/dist/supervision/contracts.d.ts +7 -0
- package/dist/supervision/contracts.d.ts.map +1 -0
- package/dist/supervision/contracts.js +86 -0
- package/dist/supervision/contracts.js.map +1 -0
- package/dist/supervision/effects.d.ts +15 -0
- package/dist/supervision/effects.d.ts.map +1 -0
- package/dist/supervision/effects.js +19 -0
- package/dist/supervision/effects.js.map +1 -0
- package/dist/supervision/evidence.d.ts +32 -0
- package/dist/supervision/evidence.d.ts.map +1 -0
- package/dist/supervision/evidence.js +68 -0
- package/dist/supervision/evidence.js.map +1 -0
- package/dist/supervision/health.d.ts +16 -0
- package/dist/supervision/health.d.ts.map +1 -0
- package/dist/supervision/health.js +86 -0
- package/dist/supervision/health.js.map +1 -0
- package/dist/supervision/index.d.ts +13 -0
- package/dist/supervision/index.d.ts.map +1 -0
- package/dist/supervision/index.js +13 -0
- package/dist/supervision/index.js.map +1 -0
- package/dist/supervision/journal.d.ts +16 -0
- package/dist/supervision/journal.d.ts.map +1 -0
- package/dist/supervision/journal.js +311 -0
- package/dist/supervision/journal.js.map +1 -0
- package/dist/supervision/receipts.d.ts +4 -0
- package/dist/supervision/receipts.d.ts.map +1 -0
- package/dist/supervision/receipts.js +21 -0
- package/dist/supervision/receipts.js.map +1 -0
- package/dist/supervision/reply.d.ts +12 -0
- package/dist/supervision/reply.d.ts.map +1 -0
- package/dist/supervision/reply.js +50 -0
- package/dist/supervision/reply.js.map +1 -0
- package/dist/supervision/runtime.d.ts +8 -0
- package/dist/supervision/runtime.d.ts.map +1 -0
- package/dist/supervision/runtime.js +188 -0
- package/dist/supervision/runtime.js.map +1 -0
- package/dist/supervision/types.d.ts +174 -0
- package/dist/supervision/types.d.ts.map +1 -0
- package/dist/supervision/types.js +2 -0
- package/dist/supervision/types.js.map +1 -0
- package/dist/tools.d.ts +12 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +131 -23
- package/dist/tools.js.map +1 -1
- package/dist/turn-controller.d.ts +12 -0
- package/dist/turn-controller.d.ts.map +1 -0
- package/dist/turn-controller.js +51 -0
- package/dist/turn-controller.js.map +1 -0
- package/dist/types.d.ts +7 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -1
- package/dist/ui/agent.d.ts +11 -10
- package/dist/ui/agent.d.ts.map +1 -1
- package/dist/ui/agent.js +293 -897
- package/dist/ui/agent.js.map +1 -1
- package/dist/ui/commands.d.ts +20 -2
- package/dist/ui/commands.d.ts.map +1 -1
- package/dist/ui/commands.js +208 -155
- package/dist/ui/commands.js.map +1 -1
- package/dist/ui/completions.d.ts.map +1 -1
- package/dist/ui/completions.js +16 -5
- package/dist/ui/completions.js.map +1 -1
- package/dist/ui/index.d.ts +6 -2
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +13 -11
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/messages.d.ts.map +1 -1
- package/dist/ui/messages.js +5 -0
- package/dist/ui/messages.js.map +1 -1
- package/dist/ui/modals/index.d.ts +1 -0
- package/dist/ui/modals/index.d.ts.map +1 -1
- package/dist/ui/modals/index.js +1 -0
- package/dist/ui/modals/index.js.map +1 -1
- package/dist/ui/modals/prompts.d.ts +4 -7
- package/dist/ui/modals/prompts.d.ts.map +1 -1
- package/dist/ui/modals/prompts.js +14 -10
- package/dist/ui/modals/prompts.js.map +1 -1
- package/dist/ui/modals/selectors.d.ts +1 -0
- package/dist/ui/modals/selectors.d.ts.map +1 -1
- package/dist/ui/modals/selectors.js +3 -2
- package/dist/ui/modals/selectors.js.map +1 -1
- package/dist/ui/modals/tool-output.d.ts +8 -0
- package/dist/ui/modals/tool-output.d.ts.map +1 -0
- package/dist/ui/modals/tool-output.js +26 -0
- package/dist/ui/modals/tool-output.js.map +1 -0
- package/dist/ui/model-commands.d.ts +4 -0
- package/dist/ui/model-commands.d.ts.map +1 -0
- package/dist/ui/model-commands.js +69 -0
- package/dist/ui/model-commands.js.map +1 -0
- package/dist/ui/regions/modal-host.d.ts +3 -0
- package/dist/ui/regions/modal-host.d.ts.map +1 -1
- package/dist/ui/regions/modal-host.js +7 -1
- package/dist/ui/regions/modal-host.js.map +1 -1
- package/dist/ui/regions/status-region.d.ts +2 -1
- package/dist/ui/regions/status-region.d.ts.map +1 -1
- package/dist/ui/regions/status-region.js +2 -2
- package/dist/ui/regions/status-region.js.map +1 -1
- package/dist/ui/regions/tool-activity.d.ts +6 -0
- package/dist/ui/regions/tool-activity.d.ts.map +1 -0
- package/dist/ui/regions/tool-activity.js +9 -0
- package/dist/ui/regions/tool-activity.js.map +1 -0
- package/dist/ui/regions/transcript-region.d.ts.map +1 -1
- package/dist/ui/regions/transcript-region.js +2 -1
- package/dist/ui/regions/transcript-region.js.map +1 -1
- package/dist/ui/regions/workflow-region.d.ts +9 -0
- package/dist/ui/regions/workflow-region.d.ts.map +1 -0
- package/dist/ui/regions/workflow-region.js +11 -0
- package/dist/ui/regions/workflow-region.js.map +1 -0
- package/dist/ui/session-commands.d.ts +3 -0
- package/dist/ui/session-commands.d.ts.map +1 -0
- package/dist/ui/session-commands.js +141 -0
- package/dist/ui/session-commands.js.map +1 -0
- package/dist/ui/state/use-approval-state.d.ts +9 -0
- package/dist/ui/state/use-approval-state.d.ts.map +1 -0
- package/dist/ui/state/use-approval-state.js +18 -0
- package/dist/ui/state/use-approval-state.js.map +1 -0
- package/dist/ui/state/use-chat-controller.d.ts +4 -1
- package/dist/ui/state/use-chat-controller.d.ts.map +1 -1
- package/dist/ui/state/use-chat-controller.js +256 -180
- package/dist/ui/state/use-chat-controller.js.map +1 -1
- package/dist/ui/state/use-modal-state.d.ts +6 -8
- package/dist/ui/state/use-modal-state.d.ts.map +1 -1
- package/dist/ui/state/use-modal-state.js +3 -4
- package/dist/ui/state/use-modal-state.js.map +1 -1
- package/dist/ui/state/use-model-state.d.ts +7 -3
- package/dist/ui/state/use-model-state.d.ts.map +1 -1
- package/dist/ui/state/use-model-state.js +24 -17
- package/dist/ui/state/use-model-state.js.map +1 -1
- package/dist/ui/state/use-queue-state.d.ts +4 -3
- package/dist/ui/state/use-queue-state.d.ts.map +1 -1
- package/dist/ui/state/use-queue-state.js +11 -8
- package/dist/ui/state/use-queue-state.js.map +1 -1
- package/dist/ui/state/use-session-init.d.ts +4 -0
- package/dist/ui/state/use-session-init.d.ts.map +1 -1
- package/dist/ui/state/use-session-init.js +19 -3
- package/dist/ui/state/use-session-init.js.map +1 -1
- package/dist/ui/state/use-transcript-state.d.ts +6 -3
- package/dist/ui/state/use-transcript-state.d.ts.map +1 -1
- package/dist/ui/state/use-transcript-state.js +27 -15
- package/dist/ui/state/use-transcript-state.js.map +1 -1
- package/dist/ui/status-bar.d.ts +2 -1
- package/dist/ui/status-bar.d.ts.map +1 -1
- package/dist/ui/status-bar.js +2 -2
- package/dist/ui/status-bar.js.map +1 -1
- package/dist/ui/tool-output-cache.d.ts +12 -0
- package/dist/ui/tool-output-cache.d.ts.map +1 -0
- package/dist/ui/tool-output-cache.js +27 -0
- package/dist/ui/tool-output-cache.js.map +1 -0
- package/dist/ui/tool-output-commands.d.ts +3 -0
- package/dist/ui/tool-output-commands.d.ts.map +1 -0
- package/dist/ui/tool-output-commands.js +37 -0
- package/dist/ui/tool-output-commands.js.map +1 -0
- package/dist/ui/tool-output-wrap.d.ts +3 -0
- package/dist/ui/tool-output-wrap.d.ts.map +1 -0
- package/dist/ui/tool-output-wrap.js +24 -0
- package/dist/ui/tool-output-wrap.js.map +1 -0
- package/dist/ui/tool-progress.d.ts +23 -0
- package/dist/ui/tool-progress.d.ts.map +1 -0
- package/dist/ui/tool-progress.js +42 -0
- package/dist/ui/tool-progress.js.map +1 -0
- package/dist/ui/types.d.ts +8 -0
- package/dist/ui/types.d.ts.map +1 -1
- package/dist/ui/workflow-progress.d.ts +21 -0
- package/dist/ui/workflow-progress.d.ts.map +1 -0
- package/dist/ui/workflow-progress.js +107 -0
- package/dist/ui/workflow-progress.js.map +1 -0
- package/package.json +13 -4
- package/scripts/verify-runlog.mjs +208 -0
package/dist/ui/agent.js
CHANGED
|
@@ -1,55 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Core agent execution loop, tool handling, and message validation.
|
|
5
|
-
* Extracted from TerminalChat using an AgentContext state bag.
|
|
6
|
-
*/
|
|
1
|
+
import { approvalDisplayText } from '../approvals/index.js';
|
|
2
|
+
import { runTurn } from '../runtime/index.js';
|
|
3
|
+
import { cancellableDelay, isCancellation, throwIfCancelled } from '../cancellation.js';
|
|
7
4
|
import * as config from '../config.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { getModelContextLimit } from '../model-detection.js';
|
|
13
|
-
import { assessToolRisk, requiresConfirmation } from '../risk.js';
|
|
5
|
+
import { estimateContextUsage } from '../providers/types.js';
|
|
6
|
+
import { getTools } from '../tools.js';
|
|
7
|
+
import { RISK_CONFIG } from '../types.js';
|
|
8
|
+
import { assessToolRisk } from '../risk.js';
|
|
14
9
|
import { formatError, classifyError } from '../errors.js';
|
|
15
10
|
import { getAvailableProviders } from '../providers/index.js';
|
|
16
11
|
import * as storage from '../storage.js';
|
|
17
|
-
import
|
|
18
|
-
import * as router from '../router.js';
|
|
12
|
+
import { formatRoutingDecision } from '../routing/index.js';
|
|
19
13
|
import { fleetActive, fleetMirrorAssistant } from '../fleet.js';
|
|
20
14
|
import * as summarization from '../summarization.js';
|
|
21
|
-
import { autoCompress } from '../auto-compressor.js';
|
|
22
15
|
import { createStreamFlusher } from '../streaming.js';
|
|
23
|
-
import {
|
|
16
|
+
import { ToolProgress } from './tool-progress.js';
|
|
24
17
|
import { checkAndWarnContextLimit } from './context.js';
|
|
25
|
-
import {
|
|
26
|
-
import { isLocalBackend, detectMalformedToolCall, buildRepairMessage, buildToolCallEnvelopeSchema, extractRepairedToolCall, getLocalModelProfile, } from '../local-model.js';
|
|
18
|
+
import { SessionRecoveryError } from '../sessions/index.js';
|
|
27
19
|
import { shouldCheckpoint, createCheckpoint } from '../checkpoint.js';
|
|
28
20
|
import { startPreventSleep, stopPreventSleep } from '../prevent-sleep.js';
|
|
29
|
-
import { resolveIterationLimit, formatIterationProgress, isFiniteIterationLimit
|
|
30
|
-
import {
|
|
31
|
-
import { getBudgetCaps, evaluateBudget, hasBudgetCaps, recordProjectSpend, loadProjectSpend, formatBudgetHalt, } from '../budget.js';
|
|
32
|
-
import { evaluatePolicy, isPolicyEnabled } from '../policy.js';
|
|
33
|
-
// ============================================================================
|
|
34
|
-
// Tool Result Truncation
|
|
35
|
-
// ============================================================================
|
|
36
|
-
/**
|
|
37
|
-
* Truncate tool result content to fit within available context.
|
|
38
|
-
* For small models (< 16K), aggressively cap tool output to prevent context overflow.
|
|
39
|
-
*/
|
|
40
|
-
function truncateToolResult(content, modelLimit) {
|
|
41
|
-
// Scale max tool result size based on model context
|
|
42
|
-
// Small models: 25% of context, large models: up to 50K chars
|
|
43
|
-
const maxChars = modelLimit < 8000 ? Math.floor(modelLimit * 0.6) // ~2.4K chars for 4K model
|
|
44
|
-
: modelLimit < 16000 ? Math.floor(modelLimit * 0.8) // ~12K chars for 16K model
|
|
45
|
-
: modelLimit < 32000 ? 20000
|
|
46
|
-
: 50000;
|
|
47
|
-
if (content.length <= maxChars)
|
|
48
|
-
return content;
|
|
49
|
-
const half = Math.floor(maxChars / 2);
|
|
50
|
-
const trimmed = content.slice(0, half) + `\n\n... [truncated ${content.length - maxChars} chars] ...\n\n` + content.slice(-half);
|
|
51
|
-
return trimmed;
|
|
52
|
-
}
|
|
21
|
+
import { resolveIterationLimit, formatIterationProgress, isFiniteIterationLimit } from '../iteration-limit.js';
|
|
22
|
+
import { formatBudgetHalt } from '../budget.js';
|
|
53
23
|
function summarizeMessageContent(content) {
|
|
54
24
|
if (typeof content === 'string') {
|
|
55
25
|
return content;
|
|
@@ -103,148 +73,21 @@ export function validateAndRepairMessagesImpl(ctx) {
|
|
|
103
73
|
}
|
|
104
74
|
return repaired;
|
|
105
75
|
}
|
|
106
|
-
// ============================================================================
|
|
107
|
-
// Local-model tool-call repair (features 2 & 3)
|
|
108
|
-
// ============================================================================
|
|
109
|
-
/**
|
|
110
|
-
* When a LOCAL backend returns a malformed tool call, spend ONE corrective
|
|
111
|
-
* round-trip trying to fix it before the error surfaces naturally through
|
|
112
|
-
* executeTool. At most one call is repaired per response (cost control on
|
|
113
|
-
* self-hosted hardware); each call id gets at most one repair (`repairedCallIds`).
|
|
114
|
-
*
|
|
115
|
-
* On Ollama we grammar-constrain the repair reply with the tool-call envelope
|
|
116
|
-
* schema (the `format` param) — but only here, never on normal prose turns.
|
|
117
|
-
*
|
|
118
|
-
* Cloud backends and well-formed responses pass straight through unchanged.
|
|
119
|
-
*/
|
|
120
|
-
async function maybeRepairLocalToolCalls(ctx, baseMessages, response, effectiveModel, repairedCallIds) {
|
|
121
|
-
if (!response.toolCalls?.length)
|
|
122
|
-
return response;
|
|
123
|
-
const provider = ctx.actualProvider;
|
|
124
|
-
if (!isLocalBackend(provider))
|
|
125
|
-
return response;
|
|
126
|
-
const tools = getTools();
|
|
127
|
-
let target;
|
|
128
|
-
for (const call of response.toolCalls) {
|
|
129
|
-
if (repairedCallIds.has(call.id))
|
|
130
|
-
continue;
|
|
131
|
-
const fault = detectMalformedToolCall(call, tools);
|
|
132
|
-
if (fault) {
|
|
133
|
-
target = { call, reason: fault.reason };
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (!target)
|
|
138
|
-
return response;
|
|
139
|
-
const { call, reason } = target;
|
|
140
|
-
repairedCallIds.add(call.id); // one repair per tool call, whatever the outcome
|
|
141
|
-
ctx.debugLog('repair', 'malformed local tool call', call.name, reason);
|
|
142
|
-
ctx.addMessage('system', `🔧 Malformed tool call from local model (${reason}); attempting one repair…`);
|
|
143
|
-
// Grammar-constrain the reply when the backend supports JSON-schema format.
|
|
144
|
-
let format;
|
|
145
|
-
try {
|
|
146
|
-
const profile = await getLocalModelProfile(provider, effectiveModel || ctx.actualModel);
|
|
147
|
-
if (profile.supportsJsonSchemaFormat) {
|
|
148
|
-
format = buildToolCallEnvelopeSchema(tools.map(t => t.name));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
// No format constraint — the corrective message alone still helps.
|
|
153
|
-
}
|
|
154
|
-
const repairMessages = [
|
|
155
|
-
...baseMessages,
|
|
156
|
-
{ role: 'assistant', content: typeof response.content === 'string' ? response.content : '', toolCalls: response.toolCalls },
|
|
157
|
-
{ role: 'user', content: buildRepairMessage(call, { reason }) },
|
|
158
|
-
];
|
|
159
|
-
let repaired;
|
|
160
|
-
try {
|
|
161
|
-
repaired = await chat(ctx.provider, repairMessages, tools, effectiveModel, undefined, undefined, format ? { format } : undefined);
|
|
162
|
-
}
|
|
163
|
-
catch (err) {
|
|
164
|
-
ctx.ledger?.recordAction('repair', { tool: call.name }, 'error', reason);
|
|
165
|
-
ctx.debugLog('repair', 'repair round-trip threw', err instanceof Error ? err.message : String(err));
|
|
166
|
-
return response; // let the original error surface through executeTool
|
|
167
|
-
}
|
|
168
|
-
const corrected = extractRepairedToolCall(typeof repaired.content === 'string' ? repaired.content : '', repaired.toolCalls, call.id);
|
|
169
|
-
if (!corrected) {
|
|
170
|
-
ctx.ledger?.recordAction('repair', { tool: call.name }, 'error', reason);
|
|
171
|
-
ctx.addMessage('system', '🔧 Repair produced no usable tool call; surfacing the original error.');
|
|
172
|
-
return response;
|
|
173
|
-
}
|
|
174
|
-
const stillBad = detectMalformedToolCall(corrected, tools);
|
|
175
|
-
ctx.ledger?.recordAction('repair', { tool: call.name, to: corrected.name }, stillBad ? 'error' : 'ok', stillBad?.reason);
|
|
176
|
-
ctx.addMessage('system', stillBad
|
|
177
|
-
? '🔧 Repair did not resolve the malformed call; surfacing the error.'
|
|
178
|
-
: `🔧 Repaired tool call → ${corrected.name}`);
|
|
179
|
-
return { ...response, toolCalls: response.toolCalls.map(tc => (tc.id === call.id ? corrected : tc)) };
|
|
180
|
-
}
|
|
181
|
-
// ============================================================================
|
|
182
|
-
// Provider warnings
|
|
183
|
-
// ============================================================================
|
|
184
|
-
// Provider warnings already surfaced this process, keyed by sessionId::warning,
|
|
185
|
-
// so a repeated model substitution across turns/iterations isn't shown twice
|
|
186
|
-
// (#217). Non-fatal notices must be surfaced, never swallowed.
|
|
187
76
|
const surfacedWarnings = new Set();
|
|
188
|
-
function surfaceResponseWarnings(ctx, response) {
|
|
189
|
-
if (!response.warnings?.length)
|
|
190
|
-
return;
|
|
191
|
-
const sessionId = ctx.sessionRef.current?.id ?? 'session_adhoc';
|
|
192
|
-
for (const warning of response.warnings) {
|
|
193
|
-
const key = `${sessionId}::${warning}`;
|
|
194
|
-
if (surfacedWarnings.has(key))
|
|
195
|
-
continue;
|
|
196
|
-
surfacedWarnings.add(key);
|
|
197
|
-
ctx.addMessage('system', warning);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
// ============================================================================
|
|
201
|
-
// Run Agent
|
|
202
|
-
// ============================================================================
|
|
203
|
-
/**
|
|
204
|
-
* Run agent with user prompt. Core execution loop that handles LLM calls,
|
|
205
|
-
* tool execution (parallel + sequential), auto-compaction, and queued messages.
|
|
206
|
-
*/
|
|
207
|
-
// #231: remember the previous turn's mode so the first work-mode turn after
|
|
208
|
-
// a plan-mode turn can carry an explicit execution directive. Module scope is
|
|
209
|
-
// intentional — mode itself outlives session resets.
|
|
210
77
|
let previousTurnMode = null;
|
|
211
78
|
/** Test seam: clear the plan-to-work transition tracking. */
|
|
212
79
|
export function _resetModeTracking() {
|
|
213
80
|
previousTurnMode = null;
|
|
214
81
|
}
|
|
215
82
|
export async function runAgentImpl(ctx, content) {
|
|
83
|
+
throwIfCancelled(ctx.signal);
|
|
216
84
|
ctx.debugLog('runAgent', 'ENTER', typeof content === 'string' ? content.substring(0, 50) : '[complex]');
|
|
217
85
|
// Validate message history before adding new content
|
|
218
86
|
ctx.validateAndRepairMessages();
|
|
219
87
|
ctx.llmMessages.current.push({ role: 'user', content });
|
|
220
88
|
ctx.setStats(s => ({ ...s, messageCount: s.messageCount + 1 }));
|
|
221
89
|
ctx.setStreamingResponse('');
|
|
222
|
-
// Smart routing (cross-provider) takes precedence over autoRoute (single-provider)
|
|
223
|
-
let effectiveModel = ctx.model;
|
|
224
|
-
let effectiveProvider = ctx.provider;
|
|
225
|
-
if (ctx.smartRouteActive && ctx.smartRoutingConfig?.enabled && typeof content === 'string') {
|
|
226
|
-
const decision = smartRoute(content, ctx.smartRoutingConfig, {
|
|
227
|
-
messageCount: ctx.stats.messageCount,
|
|
228
|
-
hasCode: content.includes('```') || /\.(ts|js|py|go|rs|java)/.test(content),
|
|
229
|
-
});
|
|
230
|
-
effectiveModel = decision.selected.model;
|
|
231
|
-
effectiveProvider = decision.selected.provider;
|
|
232
|
-
if (effectiveModel !== ctx.model || effectiveProvider !== ctx.provider) {
|
|
233
|
-
ctx.addMessage('system', `[Smart route: ${decision.selected.provider}/${decision.selected.tier} - ${decision.taskType}/${decision.complexity}]`);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
else if (ctx.autoRoute && typeof content === 'string') {
|
|
237
|
-
const routeDecision = router.routeRequest(content, ctx.provider, {
|
|
238
|
-
messageCount: ctx.stats.messageCount,
|
|
239
|
-
hasCode: content.includes('```') || /\.(ts|js|py|go|rs|java)/.test(content),
|
|
240
|
-
});
|
|
241
|
-
effectiveModel = routeDecision.model.model;
|
|
242
|
-
if (effectiveModel !== ctx.model) {
|
|
243
|
-
ctx.addMessage('system', `[Auto-route: ${routeDecision.tier} tier - ${routeDecision.reason}]`);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
90
|
const maxIterations = resolveIterationLimit(config.get('maxIterations'));
|
|
247
|
-
let completedNaturally = false;
|
|
248
91
|
const hasParentRun = Boolean(ctx.ledger?.getActiveRun('loop') ||
|
|
249
92
|
ctx.ledger?.getActiveRun('workflow') ||
|
|
250
93
|
ctx.ledger?.getActiveRun('swarm') ||
|
|
@@ -256,758 +99,302 @@ export async function runAgentImpl(ctx, content) {
|
|
|
256
99
|
: undefined;
|
|
257
100
|
let runStatus;
|
|
258
101
|
let runErrorSummary;
|
|
259
|
-
// Local-backend behaviours (features 2 & 4): whether to append anchor hashes
|
|
260
|
-
// to read_file output, and which tool-call ids have already spent their one
|
|
261
|
-
// repair round-trip.
|
|
262
|
-
const localBackend = isLocalBackend(ctx.actualProvider);
|
|
263
|
-
const executeOptions = { appendAnchorHash: localBackend };
|
|
264
|
-
const repairedCallIds = new Set();
|
|
265
|
-
// Governance (#189): audit run log, budget caps, policy hook. The run log is a
|
|
266
|
-
// per-session append-only JSONL trace; each agent turn is one run within it.
|
|
267
102
|
const sessionId = ctx.sessionRef.current?.id ?? 'session_adhoc';
|
|
268
103
|
const projectDir = ctx.sessionRef.current?.projectPath ?? process.cwd();
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
// Only maintain the cross-run project ledger when a project cap is active.
|
|
272
|
-
const trackProjectBudget = typeof budgetCaps.maxCostPerProject === 'number';
|
|
273
|
-
const runStartedAt = Date.now();
|
|
274
|
-
let runInputTokens = 0;
|
|
275
|
-
let runOutputTokens = 0;
|
|
276
|
-
let runCostUsd = 0;
|
|
277
|
-
let runToolCalls = 0;
|
|
278
|
-
let runEnded = false;
|
|
279
|
-
let runExitReason;
|
|
280
|
-
let budgetVerdict;
|
|
281
|
-
runlog.runStart({
|
|
282
|
-
session: sessionId,
|
|
283
|
-
cwd: projectDir,
|
|
284
|
-
provider: effectiveProvider,
|
|
285
|
-
model: effectiveModel || ctx.actualModel,
|
|
286
|
-
config: config.getConfig(),
|
|
287
|
-
});
|
|
288
|
-
runlog.userPrompt(summarizeMessageContent(content));
|
|
289
|
-
// #231: a terse reply right after leaving plan mode is an approval, not a
|
|
290
|
-
// conversation — bind it to execution explicitly or weak models answer
|
|
291
|
-
// "done" without doing anything.
|
|
104
|
+
let provider = ctx.actualProvider;
|
|
105
|
+
let model = ctx.actualModel;
|
|
292
106
|
const justLeftPlanMode = previousTurnMode === 'plan' && ctx.mode !== 'plan';
|
|
293
107
|
previousTurnMode = ctx.mode;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
108
|
+
let flusher;
|
|
109
|
+
let iteration = 0;
|
|
110
|
+
let streamStarted = false;
|
|
111
|
+
let errorShown = false;
|
|
112
|
+
let finalResponse = false;
|
|
113
|
+
let lastResponseCost;
|
|
114
|
+
const blocked = new Set();
|
|
115
|
+
const toolProgress = new ToolProgress();
|
|
116
|
+
const showToolProgress = () => ctx.setActivityState(toolProgress.snapshot());
|
|
117
|
+
const failed = ctx.ledger?.getFailedApproachesMessage();
|
|
118
|
+
if (failed)
|
|
119
|
+
ctx.llmMessages.current.push({ role: 'user', content: failed });
|
|
120
|
+
const clearDisplay = () => {
|
|
121
|
+
toolProgress.clear();
|
|
122
|
+
flusher?.destroy();
|
|
123
|
+
ctx.setThinkingState(null);
|
|
124
|
+
ctx.setActivityState(null);
|
|
125
|
+
ctx.setStreamingResponse('');
|
|
309
126
|
};
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
ctx.addMessage('system', `⚠️ Context at ${Math.round(contextPercentage)}% capacity (${Math.round(currentContextTokens / 1000)}K/${Math.round(modelLimit / 1000)}K tokens)
|
|
328
|
-
Consider: /compact | /clear | shorter messages`);
|
|
329
|
-
}
|
|
330
|
-
// Inject failed approaches into context so the agent avoids repeating mistakes
|
|
331
|
-
if (ctx.ledger) {
|
|
332
|
-
const failedMsg = ctx.ledger.getFailedApproachesMessage();
|
|
333
|
-
if (failedMsg) {
|
|
334
|
-
ctx.llmMessages.current.push({ role: 'user', content: failedMsg });
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
for (let i = 0; i < maxIterations; i++) {
|
|
338
|
-
// Start ledger tracking for this iteration
|
|
339
|
-
if (ctx.ledger) {
|
|
340
|
-
ctx.ledger.startIteration(ctx.ledger.getNextIterationNumber());
|
|
341
|
-
}
|
|
342
|
-
// Safety check at start of each iteration - context may have grown from tool results
|
|
343
|
-
if (i > 0) {
|
|
344
|
-
const iterContextTokens = ctx.estimateContextTokens();
|
|
345
|
-
const iterAutoCompressResult = await autoCompress(ctx.llmMessages.current, modelLimit, ctx.provider, effectiveModel);
|
|
346
|
-
if (iterAutoCompressResult.compressed) {
|
|
347
|
-
ctx.llmMessages.current = iterAutoCompressResult.messages;
|
|
348
|
-
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
349
|
-
ctx.addMessage('system', `🔄 Auto-compressed ${iterAutoCompressResult.summarizedCount} messages during iteration ${i + 1} (${iterAutoCompressResult.method})`);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
// Frame-bounded streaming: coalesce provider tokens so setStreamingResponse
|
|
353
|
-
// fires at most ~30fps instead of once per token (which re-rendered the
|
|
354
|
-
// transcript region on every token). The flusher is per-iteration; its
|
|
355
|
-
// pending timer is drained by flush() on success and cleared by destroy()
|
|
356
|
-
// on the error/cancel path below.
|
|
357
|
-
const streamFlusher = createStreamFlusher((delta) => {
|
|
358
|
-
ctx.setStreamingResponse(prev => prev + delta);
|
|
359
|
-
});
|
|
360
|
-
try {
|
|
361
|
-
// Update thinking state for LLM call
|
|
362
|
-
ctx.setThinkingState({
|
|
363
|
-
status: i === 0 ? 'Analyzing request...' : 'Processing response...',
|
|
364
|
-
detail: `Iteration ${formatIterationProgress(i + 1, maxIterations)}`,
|
|
365
|
-
iteration: i + 1,
|
|
366
|
-
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
|
|
367
|
-
});
|
|
368
|
-
ctx.setActivityState({
|
|
369
|
-
action: i === 0 ? 'Analyzing request' : 'Processing',
|
|
370
|
-
target: `iteration ${i + 1}`,
|
|
371
|
-
startTime: Date.now(),
|
|
372
|
-
});
|
|
373
|
-
// Streaming callback for final response. Clear the thinking indicator on
|
|
374
|
-
// the first token, then hand the token to the frame-bounded flusher.
|
|
375
|
-
let streamStarted = false;
|
|
376
|
-
const onToken = (token) => {
|
|
377
|
-
if (!streamStarted) {
|
|
378
|
-
ctx.setThinkingState(null); // Clear thinking when streaming starts
|
|
379
|
-
streamStarted = true;
|
|
127
|
+
ctx.signal?.addEventListener('abort', clearDisplay, { once: true });
|
|
128
|
+
try {
|
|
129
|
+
const result = await runTurn({
|
|
130
|
+
captureToolOutput: true,
|
|
131
|
+
onCheckpoint: ctx.onCheckpoint,
|
|
132
|
+
onSafetyBranch: ctx.onCheckpoint ? async () => {
|
|
133
|
+
const { branchSession } = await import('../session-management/index.js');
|
|
134
|
+
const branch = await branchSession(sessionId, { kind: 'safety', signal: ctx.signal, mode: ctx.mode });
|
|
135
|
+
ctx.addMessage('system', `Recovery conversation branch: ${branch.session.id}. Use /checkout to recover its conversation and tool state.`);
|
|
136
|
+
} : undefined,
|
|
137
|
+
client: 'terminal', sessionId, cwd: projectDir, provider: ctx.provider, model: ctx.model,
|
|
138
|
+
preferenceSources: ctx.preferenceSources,
|
|
139
|
+
routing: { ...ctx.smartRoutingConfig, ...config.get('routing') },
|
|
140
|
+
onRoute: decision => {
|
|
141
|
+
if (decision.selected) {
|
|
142
|
+
provider = decision.selected.provider;
|
|
143
|
+
model = decision.selected.model;
|
|
380
144
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
145
|
+
ctx.onRoute?.(decision);
|
|
146
|
+
ctx.addMessage(decision.selected ? 'system' : 'error', formatRoutingDecision(decision));
|
|
147
|
+
},
|
|
148
|
+
prompt: summarizeMessageContent(content), messages: ctx.llmMessages,
|
|
149
|
+
signal: ctx.signal, mode: ctx.mode, maxIterations,
|
|
150
|
+
inheritScope: true, parallel: true, continueOnLength: true, tools: getTools,
|
|
151
|
+
confirmation: ctx.confirmMode ? 'interactive' : 'none',
|
|
152
|
+
approvals: ctx.approvals,
|
|
153
|
+
approve: ctx.approve ? (_call, decision) => ctx.approve(decision, ctx.signal) : undefined,
|
|
154
|
+
prepare: async (request, index) => {
|
|
155
|
+
iteration = index;
|
|
156
|
+
finalResponse = false;
|
|
157
|
+
ctx.ledger?.startIteration(ctx.ledger.getNextIterationNumber());
|
|
158
|
+
flusher?.destroy();
|
|
159
|
+
flusher = createStreamFlusher(delta => { if (!ctx.signal?.aborted)
|
|
160
|
+
ctx.setStreamingResponse(prev => prev + delta); });
|
|
161
|
+
streamStarted = false;
|
|
162
|
+
ctx.setStreamingResponse('');
|
|
163
|
+
ctx.setThinkingState({ status: index === 1 ? 'Analyzing request...' : 'Processing response...',
|
|
164
|
+
detail: `Iteration ${formatIterationProgress(index, maxIterations)}`, iteration: index,
|
|
165
|
+
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined });
|
|
166
|
+
ctx.setActivityState({ action: index === 1 ? 'Analyzing request' : 'Processing', target: `iteration ${index}`, startTime: Date.now() });
|
|
167
|
+
ctx.llmMessages.current = summarization.validateMessageHistory(ctx.llmMessages.current);
|
|
168
|
+
const context = estimateContextUsage(request.provider, request.model, ctx.llmMessages.current, request.tools);
|
|
169
|
+
if (context.needsSummarization) {
|
|
170
|
+
const compact = summarization.summarizeConversation(ctx.llmMessages.current, { maxTokens: Math.floor(context.limit * 0.6) });
|
|
171
|
+
if (compact.summarizedCount > 0)
|
|
172
|
+
ctx.llmMessages.current = compact.messages;
|
|
173
|
+
}
|
|
174
|
+
// Mode directives are transient; compaction never writes them into history.
|
|
175
|
+
let messages = [...ctx.llmMessages.current];
|
|
176
|
+
if (ctx.mode === 'plan')
|
|
177
|
+
messages.push({ role: 'system', content: 'You are in PLAN mode: no mutating tools will execute. '
|
|
408
178
|
+ 'Read-only tools (read_file, list_files, think, create_plan, ask_question) ARE available — use them. '
|
|
409
179
|
+ 'Before proposing any plan, read the files you intend to change and cite file:line for every claim. '
|
|
410
180
|
+ 'State explicitly what you verified versus what you assume. '
|
|
411
|
-
+ 'A plan produced without reading anything will be marked unverified.'
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
if (justLeftPlanMode && i === 0) {
|
|
416
|
-
validatedMessages = [
|
|
417
|
-
...validatedMessages,
|
|
418
|
-
{
|
|
419
|
-
role: 'system',
|
|
420
|
-
content: 'The user has just switched from plan mode to work mode and replied. '
|
|
181
|
+
+ 'A plan produced without reading anything will be marked unverified.' });
|
|
182
|
+
if (justLeftPlanMode && index === 1)
|
|
183
|
+
messages.push({ role: 'system', content: 'The user has just switched from plan mode to work mode and replied. '
|
|
421
184
|
+ 'Treat their message as approval to execute the plan discussed above. '
|
|
422
185
|
+ 'Carry it out now using tools, step by step. '
|
|
423
|
-
+ 'Never state that work is done unless you performed it with tool calls in this turn.'
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
186
|
+
+ 'Never state that work is done unless you performed it with tool calls in this turn.' });
|
|
187
|
+
return { ...request, messages };
|
|
188
|
+
},
|
|
189
|
+
onCompression: result => {
|
|
190
|
+
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
191
|
+
ctx.addMessage('system', `🔄 Auto-compressed ${result.summarizedCount} messages using ${result.method} (${Math.round(result.originalTokens / 1000)}K → ${Math.round(result.compressedTokens / 1000)}K tokens)`);
|
|
192
|
+
},
|
|
193
|
+
onStreamReset: () => {
|
|
194
|
+
flusher?.destroy();
|
|
195
|
+
flusher = createStreamFlusher(delta => { if (!ctx.signal?.aborted)
|
|
196
|
+
ctx.setStreamingResponse(prev => prev + delta); });
|
|
197
|
+
streamStarted = false;
|
|
198
|
+
ctx.setStreamingResponse('');
|
|
199
|
+
},
|
|
200
|
+
onToken: token => {
|
|
201
|
+
if (!streamStarted) {
|
|
202
|
+
ctx.setThinkingState(null);
|
|
203
|
+
streamStarted = true;
|
|
438
204
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
ctx.
|
|
205
|
+
flusher?.push(token);
|
|
206
|
+
},
|
|
207
|
+
onRetry: (attempt, error, delayMs) => ctx.setThinkingState({ status: `Retrying... (attempt ${attempt + 1})`,
|
|
208
|
+
detail: `${approvalDisplayText(classifyError(error).message)} Waiting ${Math.round(delayMs / 1000)}s`, iteration,
|
|
209
|
+
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined }),
|
|
210
|
+
onUsage: (response, request, cost) => {
|
|
211
|
+
lastResponseCost = response.usage ? cost : undefined;
|
|
212
|
+
if (!response.usage)
|
|
213
|
+
return;
|
|
214
|
+
const { inputTokens, outputTokens } = response.usage;
|
|
215
|
+
ctx.setStats(s => ({ ...s, inputTokens: s.inputTokens + inputTokens, outputTokens: s.outputTokens + outputTokens, cost: s.cost + cost }));
|
|
216
|
+
ctx.ledger?.recordTokens(inputTokens, outputTokens, cost);
|
|
217
|
+
storage.recordCost(cost, request.provider, sessionId);
|
|
218
|
+
},
|
|
219
|
+
onWarning: warning => {
|
|
220
|
+
const key = `${sessionId}::${warning}`;
|
|
221
|
+
if (!surfacedWarnings.has(key)) {
|
|
222
|
+
surfacedWarnings.add(key);
|
|
223
|
+
ctx.addMessage('system', warning);
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
onResponse: (response, index) => {
|
|
227
|
+
flusher?.flush();
|
|
228
|
+
if (ctx.circuitBreaker) {
|
|
229
|
+
const breaker = ctx.circuitBreaker.check({ iteration: index, inputTokens: response.usage?.inputTokens,
|
|
230
|
+
outputTokens: response.usage?.outputTokens,
|
|
231
|
+
cost: lastResponseCost,
|
|
232
|
+
toolCalls: response.toolCalls?.map(call => ({ name: call.name, arguments: call.arguments })),
|
|
233
|
+
content: response.content, timestamp: new Date() });
|
|
234
|
+
ctx.setBreakerHealth?.(ctx.circuitBreaker.getHealth());
|
|
235
|
+
if (breaker.tripped) {
|
|
236
|
+
ctx.addMessage('system', `⚠️ Circuit breaker tripped: ${breaker.breaker}\n${breaker.message}\n\nUse /breaker resume to continue, /breaker status for details.`);
|
|
237
|
+
runErrorSummary = breaker.message;
|
|
238
|
+
return 'stop';
|
|
471
239
|
}
|
|
472
240
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
runOutputTokens += response.usage.outputTokens;
|
|
482
|
-
runCostUsd += turnCost;
|
|
483
|
-
if (trackProjectBudget)
|
|
484
|
-
recordProjectSpend(projectDir, turnCost);
|
|
241
|
+
if (fleetActive() && response.content)
|
|
242
|
+
fleetMirrorAssistant(response.content);
|
|
243
|
+
if (response.toolCalls?.length) {
|
|
244
|
+
if (response.content)
|
|
245
|
+
ctx.addMessage('assistant', response.content);
|
|
246
|
+
ctx.setStreamingResponse('');
|
|
247
|
+
ctx.setThinkingState(null);
|
|
248
|
+
return;
|
|
485
249
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
250
|
+
finalResponse = true;
|
|
251
|
+
ctx.setThinkingState(null);
|
|
252
|
+
ctx.addMessage('assistant', response.content);
|
|
253
|
+
ctx.setStreamingResponse('');
|
|
254
|
+
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
255
|
+
checkAndWarnContextLimit(provider, model, ctx.estimateContextTokens(), ctx.addMessage);
|
|
256
|
+
if (response.finishReason === 'length')
|
|
257
|
+
ctx.addMessage('system', '(auto-continuing...)');
|
|
258
|
+
else if (!ctx.onCheckpoint)
|
|
259
|
+
storage.saveMessageHistory(ctx.llmMessages.current);
|
|
260
|
+
return undefined;
|
|
261
|
+
},
|
|
262
|
+
onRepair: event => {
|
|
263
|
+
if (event.status === 'started') {
|
|
264
|
+
ctx.addMessage('system', `🔧 Malformed tool call from local model (${event.reason}); attempting one repair…`);
|
|
499
265
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
iteration: i + 1,
|
|
505
|
-
inputTokens: response.usage?.inputTokens,
|
|
506
|
-
outputTokens: response.usage?.outputTokens,
|
|
507
|
-
cost: response.usage ? calculateCost(ctx.model || DEFAULT_MODELS[ctx.provider], response.usage.inputTokens, response.usage.outputTokens) : undefined,
|
|
508
|
-
toolCalls: response.toolCalls?.map(tc => ({ name: tc.name, arguments: tc.arguments })),
|
|
509
|
-
content: response.content,
|
|
510
|
-
timestamp: new Date(),
|
|
511
|
-
};
|
|
512
|
-
const breakerResult = ctx.circuitBreaker.check(iterData);
|
|
513
|
-
ctx.setBreakerHealth?.(ctx.circuitBreaker.getHealth());
|
|
514
|
-
if (breakerResult.tripped) {
|
|
515
|
-
ctx.addMessage('system', `\u26a0\ufe0f Circuit breaker tripped: ${breakerResult.breaker}\n${breakerResult.message}\n\nUse /breaker resume to continue, /breaker status for details.`);
|
|
516
|
-
runStatus = 'stopped';
|
|
517
|
-
runErrorSummary = breakerResult.message;
|
|
518
|
-
completedNaturally = true;
|
|
519
|
-
break;
|
|
266
|
+
else {
|
|
267
|
+
ctx.ledger?.recordAction('repair', { tool: event.call.name, ...(event.corrected ? { to: event.corrected.name } : {}) }, event.status, event.status === 'ok' ? undefined : event.reason);
|
|
268
|
+
ctx.addMessage('system', event.status === 'ok' ? `🔧 Repaired tool call → ${event.corrected.name}`
|
|
269
|
+
: event.corrected ? '🔧 Repair did not resolve the malformed call; surfacing the error.' : '🔧 Repair produced no usable tool call; surfacing the original error.');
|
|
520
270
|
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
271
|
+
},
|
|
272
|
+
onToolStart: call => { toolProgress.start(call.id, call.name); showToolProgress(); },
|
|
273
|
+
onToolRetry: call => { toolProgress.retry(call.id); showToolProgress(); },
|
|
274
|
+
onPermission: (call, decision) => {
|
|
275
|
+
const risk = assessToolRisk(call);
|
|
276
|
+
const display = risk.level !== 'none' ? ` [${RISK_CONFIG[risk.level].bar}]` : '';
|
|
277
|
+
const preview = approvalDisplayText(String(call.arguments.command || call.arguments.path || '...'));
|
|
278
|
+
if (decision.decision !== 'allow') {
|
|
279
|
+
blocked.add(call.id);
|
|
280
|
+
ctx.addMessage('tool', `${call.name}: ${preview}${display}\n${decision.reason}`);
|
|
281
|
+
ctx.ledger?.recordAction(call.name, call.arguments, 'blocked', decision.reason);
|
|
532
282
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
// Check blocking conditions
|
|
552
|
-
const PLAN_MODE_ALLOWED = new Set(['think', 'ask_question', 'create_plan', 'read_file', 'list_files']);
|
|
553
|
-
if (ctx.mode === 'plan' && !PLAN_MODE_ALLOWED.has(toolCall.name)) {
|
|
554
|
-
preCheck.blocked = true;
|
|
555
|
-
preCheck.blockReason = 'plan mode';
|
|
556
|
-
preCheck.blockContent = '[Plan mode: Tool not executed. Describe what this would do.]';
|
|
557
|
-
ctx.addMessage('tool', `📋 ${toolCall.name}: ${toolPreview}${riskDisplay} (plan mode - not executed)`);
|
|
558
|
-
}
|
|
559
|
-
else if (ctx.confirmMode && requiresConfirmation(risk, false) && toolCall.name !== 'think') {
|
|
560
|
-
preCheck.blocked = true;
|
|
561
|
-
preCheck.blockReason = 'confirmation required';
|
|
562
|
-
preCheck.blockContent = `[Operation blocked - ${risk.level} risk: ${risk.reason}. User confirmation required.]`;
|
|
563
|
-
const riskIcon = risk.level === 'critical' ? '🛑' : '⚠️';
|
|
564
|
-
ctx.addMessage('tool', `${riskIcon} ${toolCall.name}: ${toolPreview}${riskDisplay}\n → Requires confirmation (use /confirm off to disable)`);
|
|
565
|
-
}
|
|
566
|
-
else {
|
|
567
|
-
// Check pre-tool hooks
|
|
568
|
-
const preHookResult = await hooks.checkHooksAllow('pre-tool', {
|
|
569
|
-
tool: toolCall.name,
|
|
570
|
-
toolArgs: args,
|
|
571
|
-
});
|
|
572
|
-
if (!preHookResult.allowed) {
|
|
573
|
-
preCheck.blocked = true;
|
|
574
|
-
preCheck.blockReason = 'blocked by hook';
|
|
575
|
-
preCheck.blockContent = `[Blocked by hook: ${preHookResult.reason}]`;
|
|
576
|
-
ctx.addMessage('tool', `⚡ ${toolCall.name}: ${toolPreview}${riskDisplay}`);
|
|
577
|
-
ctx.addMessage('tool', `🛑 Blocked by hook: ${preHookResult.reason}`);
|
|
578
|
-
}
|
|
579
|
-
else {
|
|
580
|
-
// Pre-tool policy gate (fail closed). Distinct from the hook above:
|
|
581
|
-
// the policy engine receives the tool-call JSON on stdin and denies
|
|
582
|
-
// via a non-zero exit code (see policy.ts / docs/governance.md).
|
|
583
|
-
const policyResult = isPolicyEnabled() ? await evaluatePolicy(toolCall) : undefined;
|
|
584
|
-
if (policyResult) {
|
|
585
|
-
runlog.policyEvent({
|
|
586
|
-
tool: toolCall.name,
|
|
587
|
-
decision: policyResult.decision,
|
|
588
|
-
source: policyResult.source,
|
|
589
|
-
reason: policyResult.reason,
|
|
590
|
-
durationMs: policyResult.durationMs,
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
|
-
if (policyResult && policyResult.decision === 'deny') {
|
|
594
|
-
preCheck.blocked = true;
|
|
595
|
-
preCheck.blockReason = 'denied by policy';
|
|
596
|
-
preCheck.blockContent = `[Denied by policy: ${policyResult.reason || 'no reason given'}]`;
|
|
597
|
-
ctx.addMessage('tool', `⚡ ${toolCall.name}: ${toolPreview}${riskDisplay}`);
|
|
598
|
-
ctx.addMessage('tool', `⛨ Denied by policy: ${policyResult.reason || 'no reason given'}`);
|
|
599
|
-
}
|
|
600
|
-
else {
|
|
601
|
-
// Tool can be executed
|
|
602
|
-
executableTools.push(toolCall);
|
|
603
|
-
ctx.addMessage('tool', `⚡ ${toolCall.name}: ${toolPreview}${riskDisplay}`);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
preChecks.push(preCheck);
|
|
608
|
-
// Record blocked tools in ledger and add to LLM messages
|
|
609
|
-
if (preCheck.blocked) {
|
|
610
|
-
ctx.ledger?.recordAction(toolCall.name, args, 'blocked', preCheck.blockReason);
|
|
611
|
-
runlog.toolResult({ id: toolCall.id, result: preCheck.blockContent, isError: true, durationMs: 0 });
|
|
612
|
-
ctx.llmMessages.current.push({
|
|
613
|
-
role: 'tool',
|
|
614
|
-
content: preCheck.blockContent,
|
|
615
|
-
toolCallId: toolCall.id,
|
|
616
|
-
});
|
|
617
|
-
}
|
|
283
|
+
else
|
|
284
|
+
ctx.addMessage('tool', `⚡ ${call.name}: ${preview}${display}`);
|
|
285
|
+
},
|
|
286
|
+
beforeTool: call => {
|
|
287
|
+
const args = call.arguments;
|
|
288
|
+
const thought = call.name === 'think' ? String(args.thought || '') : undefined;
|
|
289
|
+
toolProgress.running(call.id);
|
|
290
|
+
showToolProgress();
|
|
291
|
+
ctx.setThinkingState({ status: thought ? 'Reasoning...' : `Executing ${call.name}...`, thinking: thought,
|
|
292
|
+
detail: (thought || String(args.path || args.command || '...')).substring(0, 60), iteration,
|
|
293
|
+
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined });
|
|
294
|
+
if (shouldCheckpoint(call.name, args, projectDir))
|
|
295
|
+
createCheckpoint(call.name, args, projectDir);
|
|
296
|
+
},
|
|
297
|
+
onToolOutput: (call, chunk) => {
|
|
298
|
+
if (!ctx.signal?.aborted) {
|
|
299
|
+
toolProgress.output(call.id, chunk);
|
|
300
|
+
showToolProgress();
|
|
618
301
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
startTime: Date.now(),
|
|
637
|
-
});
|
|
638
|
-
// Execute in parallel using dependency-aware staging
|
|
639
|
-
ctx.debugLog('tools', 'PARALLEL exec start', `count=${executableTools.length}`);
|
|
640
|
-
const results = await executeParallel(executableTools, async (call) => {
|
|
641
|
-
const result = await executeTool(call, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, undefined, executeOptions);
|
|
642
|
-
return { result: result.result, isError: result.isError };
|
|
643
|
-
}, (completed, total, current) => {
|
|
644
|
-
const args = current.arguments;
|
|
645
|
-
const target = args.path || args.command?.substring(0, 30) || current.name;
|
|
646
|
-
ctx.setActivityState({
|
|
647
|
-
action: `Running ${current.name}`,
|
|
648
|
-
target: target,
|
|
649
|
-
startTime: Date.now(),
|
|
650
|
-
});
|
|
651
|
-
ctx.setThinkingState({
|
|
652
|
-
status: `Executing tools... (${completed + 1}/${total})`,
|
|
653
|
-
detail: current.name,
|
|
654
|
-
iteration: i + 1,
|
|
655
|
-
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
|
|
656
|
-
});
|
|
657
|
-
});
|
|
658
|
-
ctx.debugLog('tools', 'PARALLEL exec done', `results=${results.length}`);
|
|
659
|
-
// Process results sequentially for UI and LLM messages
|
|
660
|
-
for (const result of results) {
|
|
661
|
-
const toolCall = result.toolCall;
|
|
662
|
-
const args = toolCall.arguments;
|
|
663
|
-
// A tool-level failure (result.isError) or a thrown exception (result.error)
|
|
664
|
-
// both count as a failure — mirror the sequential branch.
|
|
665
|
-
const failed = result.isError || !!result.error;
|
|
666
|
-
const failureText = result.error || result.result;
|
|
667
|
-
// Record in iteration ledger
|
|
668
|
-
ctx.ledger?.recordAction(toolCall.name, args, failed ? 'error' : 'ok', failed ? failureText : undefined);
|
|
669
|
-
// Audit the tool result (parallel exec has no per-call duration).
|
|
670
|
-
runlog.toolResult({ id: toolCall.id, result: failed ? failureText : result.result, isError: failed, durationMs: 0 });
|
|
671
|
-
// Execute post-tool hooks
|
|
672
|
-
hooks.executeHooks('post-tool', {
|
|
673
|
-
tool: toolCall.name,
|
|
674
|
-
toolArgs: args,
|
|
675
|
-
toolResult: result.result,
|
|
676
|
-
}).catch((err) => {
|
|
677
|
-
ctx.debugLog('hooks', `post-tool hook failed for ${toolCall.name}:`, err instanceof Error ? err.message : err);
|
|
678
|
-
});
|
|
679
|
-
// Display result
|
|
680
|
-
if (toolCall.name === 'think') {
|
|
681
|
-
const thought = String(args.thought || '');
|
|
682
|
-
ctx.addMessage('tool', thought);
|
|
683
|
-
}
|
|
684
|
-
else if (result.error) {
|
|
685
|
-
ctx.addMessage('tool', `Error: ${result.error}`, true);
|
|
686
|
-
}
|
|
687
|
-
else {
|
|
688
|
-
const preview = result.result.split('\n').slice(0, 3).join('\n');
|
|
689
|
-
ctx.addMessage('tool', preview + (result.result.split('\n').length > 3 ? '\n...' : ''), failed);
|
|
690
|
-
}
|
|
691
|
-
ctx.llmMessages.current.push({
|
|
692
|
-
role: 'tool',
|
|
693
|
-
content: truncateToolResult(result.error ? `Error: ${result.error}` : result.result, modelLimit),
|
|
694
|
-
toolCallId: toolCall.id,
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
else {
|
|
699
|
-
// Sequential execution (single tool or dependencies prevent parallelization)
|
|
700
|
-
ctx.debugLog('tools', 'SEQUENTIAL exec start', `count=${executableTools.length}`);
|
|
701
|
-
for (const toolCall of executableTools) {
|
|
702
|
-
const args = toolCall.arguments;
|
|
703
|
-
const toolPreview = String(args.command || args.path || args.content?.toString().substring(0, 30) || '...');
|
|
704
|
-
// Set activity state for streaming indicator
|
|
705
|
-
const actionMap = {
|
|
706
|
-
read_file: 'Reading',
|
|
707
|
-
write_file: 'Writing',
|
|
708
|
-
edit_file: 'Editing',
|
|
709
|
-
bash: 'Running',
|
|
710
|
-
search: 'Searching',
|
|
711
|
-
glob: 'Finding',
|
|
712
|
-
think: 'Thinking',
|
|
713
|
-
};
|
|
714
|
-
const action = actionMap[toolCall.name] || `Executing ${toolCall.name}`;
|
|
715
|
-
const target = toolCall.name === 'bash'
|
|
716
|
-
? args.command?.substring(0, 40) + (args.command?.length > 40 ? '...' : '')
|
|
717
|
-
: toolCall.name === 'think'
|
|
718
|
-
? undefined
|
|
719
|
-
: args.path || args.pattern;
|
|
720
|
-
ctx.setActivityState({ action, target, startTime: Date.now() });
|
|
721
|
-
// Special handling for think tool UI
|
|
722
|
-
if (toolCall.name === 'think') {
|
|
723
|
-
const thought = String(args.thought || '');
|
|
724
|
-
ctx.setThinkingState({
|
|
725
|
-
status: 'Reasoning...',
|
|
726
|
-
detail: thought.substring(0, 60) + (thought.length > 60 ? '...' : ''),
|
|
727
|
-
thinking: thought,
|
|
728
|
-
iteration: i + 1,
|
|
729
|
-
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
else {
|
|
733
|
-
ctx.setThinkingState({
|
|
734
|
-
status: `Executing ${toolCall.name}...`,
|
|
735
|
-
detail: toolPreview.substring(0, 60),
|
|
736
|
-
thinking: undefined,
|
|
737
|
-
iteration: i + 1,
|
|
738
|
-
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
|
|
739
|
-
});
|
|
740
|
-
}
|
|
741
|
-
ctx.debugLog('tools', 'EXEC', toolCall.name, toolPreview.substring(0, 30));
|
|
742
|
-
// Auto-checkpoint before destructive operations
|
|
743
|
-
if (shouldCheckpoint(toolCall.name, args)) {
|
|
744
|
-
const hash = createCheckpoint(toolCall.name, args);
|
|
745
|
-
if (hash) {
|
|
746
|
-
ctx.debugLog('checkpoint', `checkpoint ${hash} before ${toolCall.name}`);
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
// Stream shell output in real-time (#15)
|
|
750
|
-
const shellStreamCallback = toolCall.name === 'shell' ? (chunk) => {
|
|
751
|
-
ctx.setActivityState({
|
|
752
|
-
action: 'Running shell',
|
|
753
|
-
target: args.command?.substring(0, 40),
|
|
754
|
-
startTime: Date.now(),
|
|
755
|
-
detail: chunk.trimEnd().split('\n').pop()?.substring(0, 60),
|
|
756
|
-
});
|
|
757
|
-
} : undefined;
|
|
758
|
-
const seqStart = Date.now();
|
|
759
|
-
const result = await executeTool(toolCall, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, shellStreamCallback, executeOptions);
|
|
760
|
-
ctx.debugLog('tools', 'DONE', toolCall.name);
|
|
761
|
-
// Record in iteration ledger
|
|
762
|
-
ctx.ledger?.recordAction(toolCall.name, args, result.isError ? 'error' : 'ok', result.isError ? result.result : undefined);
|
|
763
|
-
// Audit the tool result.
|
|
764
|
-
runlog.toolResult({ id: toolCall.id, result: result.result, isError: result.isError || false, durationMs: Date.now() - seqStart });
|
|
765
|
-
// Execute post-tool hooks
|
|
766
|
-
hooks.executeHooks('post-tool', {
|
|
767
|
-
tool: toolCall.name,
|
|
768
|
-
toolArgs: args,
|
|
769
|
-
toolResult: result.result,
|
|
770
|
-
}).catch((err) => {
|
|
771
|
-
ctx.debugLog('hooks', `post-tool hook failed for ${toolCall.name}:`, err instanceof Error ? err.message : err);
|
|
772
|
-
});
|
|
773
|
-
// Display result - use displayResult for UI, full result for LLM (#25)
|
|
774
|
-
if (toolCall.name === 'think') {
|
|
775
|
-
const thought = String(args.thought || '');
|
|
776
|
-
ctx.addMessage('tool', thought);
|
|
777
|
-
}
|
|
778
|
-
else if (toolCall.name === 'ask_question') {
|
|
779
|
-
// Display question prominently (#42)
|
|
780
|
-
const question = String(args.question || '');
|
|
781
|
-
const options = Array.isArray(args.options) ? args.options : undefined;
|
|
782
|
-
const contextNote = typeof args.context === 'string' ? args.context : undefined;
|
|
783
|
-
let questionMsg = `❓ ${question}`;
|
|
784
|
-
if (contextNote)
|
|
785
|
-
questionMsg += `\n ${contextNote}`;
|
|
786
|
-
if (options)
|
|
787
|
-
questionMsg += '\n' + options.map((o, i) => ` ${i + 1}. ${o}`).join('\n');
|
|
788
|
-
ctx.addMessage('assistant', questionMsg);
|
|
789
|
-
// Tell the LLM that we're waiting for user input
|
|
790
|
-
ctx.llmMessages.current.push({
|
|
791
|
-
role: 'tool',
|
|
792
|
-
content: '[Waiting for user response. The user will reply with their answer.]',
|
|
793
|
-
toolCallId: toolCall.id,
|
|
794
|
-
});
|
|
795
|
-
// Break out of tool loop - let user respond naturally
|
|
796
|
-
completedNaturally = true;
|
|
797
|
-
}
|
|
798
|
-
else if (toolCall.name === 'create_plan') {
|
|
799
|
-
// Display plan as a checklist for user approval (#19)
|
|
800
|
-
const planTitle = String(args.title || 'Plan');
|
|
801
|
-
const planSteps = Array.isArray(args.steps) ? args.steps : [];
|
|
802
|
-
const planReasoning = typeof args.reasoning === 'string' ? args.reasoning : undefined;
|
|
803
|
-
let planMsg = `📋 Plan: ${planTitle}\n`;
|
|
804
|
-
if (planReasoning)
|
|
805
|
-
planMsg += `\n ${planReasoning}\n`;
|
|
806
|
-
planMsg += '\n' + planSteps.map((s, idx) => ` ${idx + 1}. [ ] ${s}`).join('\n');
|
|
807
|
-
planMsg += '\n\n Switch to work mode (Shift+Tab) and reply to execute, or give feedback to revise.';
|
|
808
|
-
ctx.addMessage('assistant', planMsg);
|
|
809
|
-
// Tell the LLM to wait for user approval
|
|
810
|
-
ctx.llmMessages.current.push({
|
|
811
|
-
role: 'tool',
|
|
812
|
-
content: '[Plan displayed to user. Waiting for approval. The user will reply to approve and execute the plan (they may switch to work mode first), or provide feedback to revise it. Do NOT proceed with execution until the user approves.]',
|
|
813
|
-
toolCallId: toolCall.id,
|
|
814
|
-
});
|
|
815
|
-
// Break out of tool loop - wait for user approval
|
|
816
|
-
completedNaturally = true;
|
|
817
|
-
}
|
|
818
|
-
else {
|
|
819
|
-
const display = result.displayResult || result.result;
|
|
820
|
-
const preview = display.split('\n').slice(0, 5).join('\n');
|
|
821
|
-
ctx.addMessage('tool', preview + (display.split('\n').length > 5 ? '\n...' : ''), result.isError);
|
|
822
|
-
}
|
|
823
|
-
if (toolCall.name !== 'ask_question') {
|
|
824
|
-
ctx.llmMessages.current.push({
|
|
825
|
-
role: 'tool',
|
|
826
|
-
content: truncateToolResult(result.result, modelLimit),
|
|
827
|
-
toolCallId: toolCall.id,
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
}
|
|
302
|
+
},
|
|
303
|
+
onToolResult: (call, result, _iteration, output) => {
|
|
304
|
+
toolProgress.finish(call.id);
|
|
305
|
+
showToolProgress();
|
|
306
|
+
if (blocked.has(call.id))
|
|
307
|
+
return;
|
|
308
|
+
const args = call.arguments;
|
|
309
|
+
ctx.ledger?.recordAction(call.name, args, result.isError ? 'error' : 'ok', result.isError ? result.result : undefined);
|
|
310
|
+
if (call.name === 'think' && !result.isError)
|
|
311
|
+
ctx.addMessage('tool', output?.record.content ?? approvalDisplayText(String(args.thought || '')), false, output);
|
|
312
|
+
else if (call.name === 'ask_question' && !result.isError) {
|
|
313
|
+
let question = `❓ ${String(args.question || '')}`;
|
|
314
|
+
if (typeof args.context === 'string')
|
|
315
|
+
question += `\n ${args.context}`;
|
|
316
|
+
if (Array.isArray(args.options))
|
|
317
|
+
question += '\n' + args.options.map((o, i) => ` ${i + 1}. ${o}`).join('\n');
|
|
318
|
+
ctx.addMessage('assistant', question);
|
|
832
319
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
kind: budgetVerdict.kind ?? 'cost',
|
|
841
|
-
spent: budgetVerdict.spent ?? 0,
|
|
842
|
-
cap: budgetVerdict.cap ?? 0,
|
|
843
|
-
message: budgetVerdict.message ?? summary,
|
|
844
|
-
});
|
|
845
|
-
ctx.addMessage('system', `‖ ${summary}`);
|
|
846
|
-
runStatus = 'stopped';
|
|
847
|
-
runErrorSummary = summary;
|
|
848
|
-
runExitReason = 'budget';
|
|
849
|
-
completedNaturally = true;
|
|
850
|
-
break;
|
|
320
|
+
else if (call.name === 'create_plan' && !result.isError) {
|
|
321
|
+
let plan = `📋 Plan: ${String(args.title || 'Plan')}\n`;
|
|
322
|
+
if (typeof args.reasoning === 'string')
|
|
323
|
+
plan += `\n ${args.reasoning}\n`;
|
|
324
|
+
if (Array.isArray(args.steps))
|
|
325
|
+
plan += '\n' + args.steps.map((step, i) => ` ${i + 1}. [ ] ${step}`).join('\n');
|
|
326
|
+
ctx.addMessage('assistant', plan + '\n\n Switch to work mode (Shift+Tab) and reply to execute, or give feedback to revise.');
|
|
851
327
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
}
|
|
856
|
-
// Final response - move streaming content to message history
|
|
857
|
-
ctx.setThinkingState(null);
|
|
858
|
-
ctx.llmMessages.current.push({ role: 'assistant', content: response.content });
|
|
859
|
-
ctx.addMessage('assistant', response.content);
|
|
860
|
-
// Mirror assistant message to the fleet channel
|
|
861
|
-
if (fleetActive()) {
|
|
862
|
-
fleetMirrorAssistant(response.content);
|
|
863
|
-
}
|
|
864
|
-
ctx.setStreamingResponse('');
|
|
865
|
-
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
866
|
-
checkAndWarnContextLimit(ctx.actualProvider, ctx.actualModel, ctx.estimateContextTokens(), ctx.addMessage);
|
|
867
|
-
// Auto-continue if response was truncated due to length
|
|
868
|
-
if (response.finishReason === 'length') {
|
|
869
|
-
ctx.addMessage('system', '(auto-continuing...)');
|
|
870
|
-
ctx.llmMessages.current.push({ role: 'user', content: 'Please continue where you left off.' });
|
|
871
|
-
continue; // Loop again to get continuation
|
|
872
|
-
}
|
|
873
|
-
completedNaturally = true;
|
|
874
|
-
runStatus = 'completed';
|
|
875
|
-
// The answer is already shown; if this final turn crossed a budget cap,
|
|
876
|
-
// still record it so the audit trail reflects why spend stopped here.
|
|
877
|
-
if (budgetVerdict?.exceeded) {
|
|
878
|
-
const summary = formatBudgetHalt(budgetVerdict);
|
|
879
|
-
runlog.budgetEvent({
|
|
880
|
-
scope: budgetVerdict.scope ?? 'run',
|
|
881
|
-
kind: budgetVerdict.kind ?? 'cost',
|
|
882
|
-
spent: budgetVerdict.spent ?? 0,
|
|
883
|
-
cap: budgetVerdict.cap ?? 0,
|
|
884
|
-
message: budgetVerdict.message ?? summary,
|
|
885
|
-
});
|
|
886
|
-
ctx.addMessage('system', `‖ ${summary}`);
|
|
887
|
-
runExitReason = 'budget';
|
|
888
|
-
}
|
|
889
|
-
// End iteration ledger entry
|
|
890
|
-
ctx.ledger?.endIteration('success');
|
|
891
|
-
// Auto-save full message history for session persistence
|
|
892
|
-
storage.saveMessageHistory(ctx.llmMessages.current);
|
|
893
|
-
break;
|
|
894
|
-
}
|
|
895
|
-
catch (error) {
|
|
896
|
-
streamFlusher.destroy(); // stop any pending flush timer on error/cancel
|
|
897
|
-
ctx.setThinkingState(null);
|
|
898
|
-
ctx.setActivityState(null);
|
|
899
|
-
ctx.setStreamingResponse('');
|
|
900
|
-
// End iteration ledger entry with error
|
|
901
|
-
ctx.ledger?.endIteration('error');
|
|
902
|
-
// Format error with provider context for better suggestions
|
|
903
|
-
const errorMsg = formatError(error, { provider: ctx.actualProvider });
|
|
904
|
-
ctx.addMessage('error', errorMsg);
|
|
905
|
-
// Classify error to provide additional recovery suggestions
|
|
906
|
-
const classified = classifyError(error);
|
|
907
|
-
const availableProviders = getAvailableProviders();
|
|
908
|
-
const otherProviders = availableProviders.filter(p => p !== ctx.actualProvider);
|
|
909
|
-
// Feed error to circuit breaker
|
|
910
|
-
if (ctx.circuitBreaker) {
|
|
911
|
-
const errorIterData = {
|
|
912
|
-
iteration: i + 1,
|
|
913
|
-
error: errorMsg,
|
|
914
|
-
timestamp: new Date(),
|
|
915
|
-
};
|
|
916
|
-
const breakerResult = ctx.circuitBreaker.check(errorIterData);
|
|
917
|
-
ctx.setBreakerHealth?.(ctx.circuitBreaker.getHealth());
|
|
918
|
-
if (breakerResult.tripped) {
|
|
919
|
-
ctx.addMessage('system', `\u26a0\ufe0f Circuit breaker tripped: ${breakerResult.breaker}\n${breakerResult.message}\n\nUse /breaker resume to continue.`);
|
|
920
|
-
runStatus = 'stopped';
|
|
921
|
-
runErrorSummary = breakerResult.message;
|
|
922
|
-
completedNaturally = true;
|
|
923
|
-
break;
|
|
328
|
+
else {
|
|
329
|
+
const display = output?.record.content ?? approvalDisplayText((result.displayResult || result.result).slice(0, 65536));
|
|
330
|
+
ctx.addMessage('tool', display, result.isError, output);
|
|
924
331
|
}
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
332
|
+
},
|
|
333
|
+
onIterationEnd: () => { ctx.ledger?.endIteration(finalResponse ? 'success' : undefined); },
|
|
334
|
+
onError: (error, index) => {
|
|
335
|
+
clearDisplay();
|
|
336
|
+
ctx.ledger?.endIteration('error');
|
|
337
|
+
const message = approvalDisplayText(formatError(error, { provider, retrying: false }));
|
|
338
|
+
ctx.addMessage('error', message);
|
|
339
|
+
errorShown = true;
|
|
340
|
+
runErrorSummary = message;
|
|
341
|
+
if (ctx.circuitBreaker) {
|
|
342
|
+
const breaker = ctx.circuitBreaker.check({ iteration: index, error: message, timestamp: new Date() });
|
|
343
|
+
ctx.setBreakerHealth?.(ctx.circuitBreaker.getHealth());
|
|
344
|
+
if (breaker.tripped) {
|
|
345
|
+
ctx.addMessage('system', `⚠️ Circuit breaker tripped: ${breaker.breaker}\n${breaker.message}\n\nUse /breaker resume to continue.`);
|
|
346
|
+
return 'stop';
|
|
347
|
+
}
|
|
932
348
|
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
ctx.addMessage('system', `\u26a0\ufe0f Cleared ${currentQueuedOnError.length} queued message(s) due to error. Use /clear to reset conversation.`);
|
|
954
|
-
ctx.setQueuedMessages([]);
|
|
955
|
-
}
|
|
956
|
-
if (runId && runStatus) {
|
|
957
|
-
ctx.ledger?.finishRun(runId, runStatus, { errorSummary: runErrorSummary });
|
|
958
|
-
}
|
|
959
|
-
finalizeRun(runExitReason ?? runStatus ?? 'error');
|
|
960
|
-
return; // Exit early on error - don't process queued messages
|
|
349
|
+
const { category } = classifyError(error);
|
|
350
|
+
const others = getAvailableProviders().filter(p => p !== provider);
|
|
351
|
+
if (['rate_limit', 'server'].includes(category) && others.length)
|
|
352
|
+
ctx.addMessage('system', `💡 Try switching providers: /provider ${others[0]} or /model to see alternatives`);
|
|
353
|
+
else if (['timeout', 'network'].includes(category))
|
|
354
|
+
ctx.addMessage('system', '💡 Network issue detected. Check connection and try again, or use /provider to switch.');
|
|
355
|
+
else if (category === 'auth')
|
|
356
|
+
ctx.addMessage('system', "💡 Run 'calliope --setup' to reconfigure API keys.");
|
|
357
|
+
if (ctx.circuitBreaker && ['rate_limit', 'server', 'timeout', 'network'].includes(category)) {
|
|
358
|
+
ctx.addMessage('system', `Retrying... (circuit breaker will pause after ${ctx.circuitBreaker.getConfig().breakers['repeated-failure'].maxConsecutiveErrors} consecutive failures)`);
|
|
359
|
+
return 'retry';
|
|
360
|
+
}
|
|
361
|
+
return undefined;
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
runStatus = ['completed', 'waiting_for_user'].includes(result.reason) ? 'completed' : 'stopped';
|
|
365
|
+
if (result.reason === 'cancelled') {
|
|
366
|
+
runErrorSummary = 'Operation cancelled';
|
|
367
|
+
ctx.ledger?.endIteration('error');
|
|
368
|
+
ctx.validateAndRepairMessages();
|
|
961
369
|
}
|
|
962
|
-
|
|
963
|
-
// Only show warning if we actually hit the iteration limit (not errors or natural completion)
|
|
964
|
-
if (!completedNaturally && isFiniteIterationLimit(maxIterations)) {
|
|
965
|
-
ctx.addMessage('system', `⚠️ Reached ${maxIterations} iterations limit. Task may be incomplete. Adjust with /set maxIterations <number>.`);
|
|
966
|
-
if (!runStatus) {
|
|
967
|
-
runStatus = 'stopped';
|
|
370
|
+
else if (result.reason === 'iteration_limit') {
|
|
968
371
|
runErrorSummary = `Reached ${maxIterations} iterations limit`;
|
|
372
|
+
ctx.addMessage('system', `⚠️ Reached ${maxIterations} iterations limit. Task may be incomplete. Adjust with /set maxIterations <number>.`);
|
|
969
373
|
}
|
|
374
|
+
else if (result.reason === 'budget' && result.budget) {
|
|
375
|
+
runErrorSummary = formatBudgetHalt(result.budget);
|
|
376
|
+
ctx.addMessage('system', `‖ ${runErrorSummary}`);
|
|
377
|
+
}
|
|
378
|
+
if (runId)
|
|
379
|
+
ctx.ledger?.finishRun(runId, runStatus, { errorSummary: runErrorSummary });
|
|
380
|
+
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
381
|
+
if (ctx.mode === 'plan' && result.totals.toolCalls === 0 && result.reason !== 'cancelled')
|
|
382
|
+
ctx.addMessage('system', '⚠ Unverified plan — the agent read nothing to produce this. Ask it to verify (it can read files in plan mode), or treat claims as assumptions.');
|
|
383
|
+
return ['completed', 'waiting_for_user'].includes(result.reason);
|
|
970
384
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
// #224: a plan produced with zero tool calls carries no evidence — say so.
|
|
980
|
-
if (ctx.mode === 'plan' && runToolCalls === 0) {
|
|
981
|
-
ctx.addMessage('system', '⚠ Unverified plan — the agent read nothing to produce this. Ask it to verify (it can read files in plan mode), or treat claims as assumptions.');
|
|
385
|
+
catch (error) {
|
|
386
|
+
const recoveryFailure = error instanceof SessionRecoveryError;
|
|
387
|
+
const cancelled = !recoveryFailure && (ctx.signal?.aborted || isCancellation(error));
|
|
388
|
+
if (recoveryFailure || !cancelled && !errorShown)
|
|
389
|
+
ctx.addMessage('error', approvalDisplayText(formatError(error, { provider, retrying: false })));
|
|
390
|
+
if (runId)
|
|
391
|
+
ctx.ledger?.finishRun(runId, cancelled ? 'stopped' : 'failed', { errorSummary: cancelled ? 'Operation cancelled' : String(error) });
|
|
392
|
+
return false;
|
|
982
393
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
const queued = [...currentQueued];
|
|
987
|
-
ctx.setQueuedMessages([]); // Clear the queue
|
|
988
|
-
ctx.queuedMessagesRef.current = []; // Also clear ref immediately
|
|
989
|
-
// Combine queued messages into a single follow-up
|
|
990
|
-
const followUp = queued.length === 1
|
|
991
|
-
? queued[0]
|
|
992
|
-
: `[Multiple follow-up messages from user:]\n${queued.map((m, i) => `${i + 1}. ${m}`).join('\n')}`;
|
|
993
|
-
ctx.addMessage('system', `📨 Processing ${queued.length} queued message${queued.length > 1 ? 's' : ''}...`);
|
|
994
|
-
// Recursively run agent with follow-up
|
|
995
|
-
// Use setTimeout to avoid stack overflow and allow UI to update
|
|
996
|
-
// Note: handleSubmit's finally will set isProcessing=false, so we need to re-enable it
|
|
997
|
-
ctx.debugLog('runAgent', 'SCHEDULING recursive call for queued messages');
|
|
998
|
-
setTimeout(() => {
|
|
999
|
-
ctx.debugLog('runAgent', 'RECURSIVE call starting');
|
|
1000
|
-
ctx.setIsProcessing(true);
|
|
1001
|
-
runAgentImpl(ctx, followUp).finally(() => {
|
|
1002
|
-
ctx.setIsProcessing(false);
|
|
1003
|
-
ctx.setThinkingState(null);
|
|
1004
|
-
ctx.setActivityState(null);
|
|
1005
|
-
ctx.setStreamingResponse('');
|
|
1006
|
-
ctx.setEditingQueueIndex(null);
|
|
1007
|
-
});
|
|
1008
|
-
}, 100);
|
|
394
|
+
finally {
|
|
395
|
+
ctx.signal?.removeEventListener('abort', clearDisplay);
|
|
396
|
+
clearDisplay();
|
|
1009
397
|
}
|
|
1010
|
-
ctx.debugLog('runAgent', 'RETURN');
|
|
1011
398
|
}
|
|
1012
399
|
// ============================================================================
|
|
1013
400
|
// Run Loop
|
|
@@ -1029,7 +416,7 @@ export async function runLoopImpl(ctx, prompt, maxIter, completionPromise) {
|
|
|
1029
416
|
try {
|
|
1030
417
|
for (let i = 0; i < maxIter; i++) {
|
|
1031
418
|
// Check if cancelled
|
|
1032
|
-
if (ctx.loopCancelledRef.current) {
|
|
419
|
+
if (ctx.loopCancelledRef.current || ctx.signal?.aborted) {
|
|
1033
420
|
ctx.addMessage('system', '🛑 Loop cancelled by user');
|
|
1034
421
|
loopOutcome = 'cancelled';
|
|
1035
422
|
break;
|
|
@@ -1043,7 +430,11 @@ export async function runLoopImpl(ctx, prompt, maxIter, completionPromise) {
|
|
|
1043
430
|
: `Continue working on the task: "${prompt}"\n\nThis is iteration ${i + 1}. Review what you've done so far and continue making progress.`;
|
|
1044
431
|
try {
|
|
1045
432
|
// Run the agent
|
|
1046
|
-
await runAgentImpl(ctx, iterationPrompt)
|
|
433
|
+
if (!await runAgentImpl(ctx, iterationPrompt) || ctx.afterLoopTurn && !await ctx.afterLoopTurn()) {
|
|
434
|
+
loopOutcome = ctx.signal?.aborted ? 'cancelled' : 'error';
|
|
435
|
+
loopErrorSummary = 'Turn stopped; queued work and remaining loop iterations are paused';
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
1047
438
|
// Check for completion promise in the last assistant message
|
|
1048
439
|
if (completionPromise) {
|
|
1049
440
|
const lastMessage = ctx.llmMessages.current[ctx.llmMessages.current.length - 1];
|
|
@@ -1059,17 +450,22 @@ export async function runLoopImpl(ctx, prompt, maxIter, completionPromise) {
|
|
|
1059
450
|
}
|
|
1060
451
|
}
|
|
1061
452
|
// Check cancelled again after agent run
|
|
1062
|
-
if (ctx.loopCancelledRef.current) {
|
|
453
|
+
if (ctx.loopCancelledRef.current || ctx.signal?.aborted) {
|
|
1063
454
|
ctx.addMessage('system', '🛑 Loop cancelled by user');
|
|
1064
455
|
loopOutcome = 'cancelled';
|
|
1065
456
|
break;
|
|
1066
457
|
}
|
|
1067
458
|
// Small delay between iterations
|
|
1068
459
|
if (i + 1 < maxIter) {
|
|
1069
|
-
await
|
|
460
|
+
await cancellableDelay(500, ctx.signal);
|
|
1070
461
|
}
|
|
1071
462
|
}
|
|
1072
463
|
catch (error) {
|
|
464
|
+
if (ctx.signal?.aborted || isCancellation(error)) {
|
|
465
|
+
ctx.addMessage('system', '🛑 Loop cancelled by user');
|
|
466
|
+
loopOutcome = 'cancelled';
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
1073
469
|
loopErrorSummary = error instanceof Error ? error.message : String(error);
|
|
1074
470
|
ctx.addMessage('error', `Loop error: ${loopErrorSummary}`);
|
|
1075
471
|
loopOutcome = 'error';
|