cloving 1.3.0 → 1.4.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 +235 -206
- package/dist/src/agent-harness-shell.d.ts +2 -1
- package/dist/src/agent-harness-shell.js +8 -5
- package/dist/src/authority/command-wait-checks.d.ts +8 -0
- package/dist/src/authority/command-wait-checks.js +88 -0
- package/dist/src/authority/command-wait-results.d.ts +15 -0
- package/dist/src/authority/command-wait-results.js +73 -0
- package/dist/src/authority/command-wait-view.d.ts +21 -0
- package/dist/src/authority/command-wait-view.js +34 -0
- package/dist/src/authority/command-waits.d.ts +39 -0
- package/dist/src/authority/command-waits.js +97 -0
- package/dist/src/authority/commands.js +22 -1
- package/dist/src/authority/dispatch-decision.d.ts +8 -0
- package/dist/src/authority/dispatch-decision.js +26 -1
- package/dist/src/authority/effect-requests.js +6 -3
- package/dist/src/authority/effect-results.js +4 -2
- package/dist/src/authority/history-state.d.ts +2 -0
- package/dist/src/authority/history-state.js +13 -1
- package/dist/src/authority/instants.d.ts +1 -0
- package/dist/src/authority/instants.js +21 -0
- package/dist/src/authority/reduction-state.d.ts +2 -0
- package/dist/src/authority/reduction-state.js +5 -0
- package/dist/src/authority/reduction.js +24 -0
- package/dist/src/authority/status-input.d.ts +2 -0
- package/dist/src/authority/status.d.ts +2 -0
- package/dist/src/authority/status.js +1 -0
- package/dist/src/authority/stop-sequence.d.ts +1 -0
- package/dist/src/authority/stop-sequence.js +2 -1
- package/dist/src/authority/transitions.d.ts +6 -4
- package/dist/src/authority/transitions.js +7 -0
- package/dist/src/broker/actor/authority-queries.d.ts +2 -1
- package/dist/src/broker/actor/authority-queries.js +14 -5
- package/dist/src/broker/actor/authority-receipts.js +1 -1
- package/dist/src/broker/actor/authority-settings.js +2 -2
- package/dist/src/broker/actor/authority-upgrades.d.ts +19 -2
- package/dist/src/broker/actor/authority-upgrades.js +7 -2
- package/dist/src/broker/actor/delivery-intents.js +2 -0
- package/dist/src/broker/actor/effect-delivery.d.ts +1 -1
- package/dist/src/broker/actor/effect-delivery.js +15 -2
- package/dist/src/broker/actor/effect-plan.js +11 -2
- package/dist/src/broker/actor/effect-queries.d.ts +4 -0
- package/dist/src/broker/actor/effect-queries.js +9 -1
- package/dist/src/broker/actor/effect-turn.js +2 -0
- package/dist/src/broker/actor/stop-plan.js +9 -3
- package/dist/src/broker/actor/stop-turn.d.ts +2 -0
- package/dist/src/broker/actor/stop-turn.js +24 -11
- package/dist/src/broker/actor/turn-draft.d.ts +1 -0
- package/dist/src/broker/actor/turn-draft.js +4 -0
- package/dist/src/broker/actor/turn-state.js +5 -1
- package/dist/src/broker/authority-admission.js +60 -0
- package/dist/src/broker/authority-restore.d.ts +2 -0
- package/dist/src/broker/authority-restore.js +35 -1
- package/dist/src/broker/command-wait-restore.d.ts +31 -0
- package/dist/src/broker/command-wait-restore.js +86 -0
- package/dist/src/broker/decision-restore.d.ts +1 -1
- package/dist/src/broker/decision-restore.js +1 -0
- package/dist/src/broker/effect-upgrade.d.ts +2 -0
- package/dist/src/broker/effect-upgrade.js +8 -2
- package/dist/src/cli/arguments.d.ts +13 -0
- package/dist/src/cli/arguments.js +42 -3
- package/dist/src/cli/chat-stores.d.ts +2 -0
- package/dist/src/cli/chat-stores.js +113 -0
- package/dist/src/cli/client.d.ts +4 -1
- package/dist/src/cli/client.js +9 -3
- package/dist/src/cli/command-requests.d.ts +17 -0
- package/dist/src/cli/command-requests.js +43 -0
- package/dist/src/cli/command-waits.d.ts +9 -0
- package/dist/src/cli/command-waits.js +51 -0
- package/dist/src/cli/commands.js +14 -41
- package/dist/src/cli/config-file.d.ts +2 -2
- package/dist/src/cli/config-file.js +4 -2
- package/dist/src/cli/doctor-workflows.d.ts +5 -2
- package/dist/src/cli/doctor-workflows.js +12 -7
- package/dist/src/cli/doctor.js +6 -9
- package/dist/src/cli/harness-probes.d.ts +2 -0
- package/dist/src/cli/harness-probes.js +1 -1
- package/dist/src/cli/harness-processes.d.ts +6 -0
- package/dist/src/cli/harness-processes.js +36 -0
- package/dist/src/cli/init-command.d.ts +9 -0
- package/dist/src/cli/init-command.js +52 -0
- package/dist/src/cli/init-prompt-command.d.ts +10 -0
- package/dist/src/cli/init-prompt-command.js +145 -0
- package/dist/src/cli/init-prompt.d.ts +35 -0
- package/dist/src/cli/init-prompt.js +74 -0
- package/dist/src/cli/init-selection.d.ts +13 -0
- package/dist/src/cli/init-selection.js +75 -0
- package/dist/src/cli/main.d.ts +2 -0
- package/dist/src/cli/main.js +37 -7
- package/dist/src/cli/mcp-server.d.ts +1 -0
- package/dist/src/cli/mcp-server.js +156 -0
- package/dist/src/cli/mcp-tools.d.ts +15 -0
- package/dist/src/cli/mcp-tools.js +80 -0
- package/dist/src/cli/render.d.ts +1 -1
- package/dist/src/cli/render.js +17 -30
- package/dist/src/cli/report-ci-jobs.d.ts +23 -0
- package/dist/src/cli/report-ci-jobs.js +103 -0
- package/dist/src/cli/report-ci-usage.d.ts +9 -0
- package/dist/src/cli/report-ci-usage.js +185 -0
- package/dist/src/cli/report-ci.d.ts +13 -0
- package/dist/src/cli/report-ci.js +74 -0
- package/dist/src/cli/report-closures.d.ts +6 -0
- package/dist/src/cli/report-closures.js +97 -0
- package/dist/src/cli/report-flow.d.ts +7 -0
- package/dist/src/cli/report-flow.js +122 -0
- package/dist/src/cli/report-github-reader.d.ts +29 -0
- package/dist/src/cli/report-github-reader.js +157 -0
- package/dist/src/cli/report-github.d.ts +15 -8
- package/dist/src/cli/report-github.js +131 -150
- package/dist/src/cli/report-operator.d.ts +3 -0
- package/dist/src/cli/report-operator.js +40 -0
- package/dist/src/cli/report-ready.d.ts +3 -0
- package/dist/src/cli/report-ready.js +96 -0
- package/dist/src/cli/report-text.d.ts +9 -0
- package/dist/src/cli/report-text.js +33 -0
- package/dist/src/cli/report.d.ts +1 -0
- package/dist/src/cli/report.js +104 -27
- package/dist/src/cli/runtime-arguments.d.ts +2 -1
- package/dist/src/cli/runtime-arguments.js +7 -2
- package/dist/src/cli/schedule-arguments.d.ts +14 -0
- package/dist/src/cli/schedule-arguments.js +26 -0
- package/dist/src/cli/schedule-render.d.ts +22 -0
- package/dist/src/cli/schedule-render.js +73 -0
- package/dist/src/cli/terminal-text.d.ts +3 -0
- package/dist/src/cli/terminal-text.js +28 -0
- package/dist/src/cli/usage-render.d.ts +1 -0
- package/dist/src/cli/usage-render.js +28 -1
- package/dist/src/cli/usage.d.ts +1 -1
- package/dist/src/cli/usage.js +50 -7
- package/dist/src/cli/watch-actions.js +2 -2
- package/dist/src/cli/watch-board-view.js +19 -2
- package/dist/src/cli/watch-board.d.ts +4 -2
- package/dist/src/cli/watch-board.js +53 -43
- package/dist/src/cli/watch-card-forms.d.ts +1 -1
- package/dist/src/cli/watch-card-forms.js +11 -10
- package/dist/src/cli/watch-client.d.ts +1 -1
- package/dist/src/cli/watch-client.js +2 -11
- package/dist/src/cli/watch-header-view.d.ts +0 -5
- package/dist/src/cli/watch-header-view.js +8 -20
- package/dist/src/cli/watch-model.d.ts +4 -1
- package/dist/src/cli/watch-model.js +17 -11
- package/dist/src/cli/watch-page.js +12 -14
- package/dist/src/cli/watch-phases.d.ts +12 -3
- package/dist/src/cli/watch-phases.js +68 -11
- package/dist/src/cli/watch-pr-board.js +1 -1
- package/dist/src/cli/watch-pr-command.d.ts +1 -0
- package/dist/src/cli/watch-pull-request.js +18 -8
- package/dist/src/cli/watch-reads.d.ts +2 -0
- package/dist/src/cli/watch-reads.js +17 -16
- package/dist/src/cli/watch-render.d.ts +14 -0
- package/dist/src/cli/watch-run-input.d.ts +0 -1
- package/dist/src/cli/watch-run-input.js +0 -13
- package/dist/src/cli/watch-server.js +6 -6
- package/dist/src/cli/watch-view.d.ts +0 -3
- package/dist/src/cli/watch-view.js +13 -13
- package/dist/src/cli.js +59 -5
- package/dist/src/command-wait.d.ts +4 -0
- package/dist/src/command-wait.js +45 -0
- package/dist/src/configuration-change.d.ts +7 -0
- package/dist/src/configuration-change.js +14 -0
- package/dist/src/configuration-schema.d.ts +9 -0
- package/dist/src/configuration-schema.js +6 -2
- package/dist/src/daemon/attachment-file.d.ts +6 -0
- package/dist/src/daemon/attachment-file.js +86 -0
- package/dist/src/daemon/attachments.d.ts +14 -0
- package/dist/src/daemon/attachments.js +17 -0
- package/dist/src/daemon/claude-recovery.d.ts +50 -0
- package/dist/src/daemon/claude-recovery.js +204 -0
- package/dist/src/daemon/claude-stream.d.ts +27 -0
- package/dist/src/daemon/claude-stream.js +63 -0
- package/dist/src/daemon/claude-turn.d.ts +11 -25
- package/dist/src/daemon/claude-turn.js +64 -81
- package/dist/src/daemon/claude-worker.d.ts +2 -1
- package/dist/src/daemon/claude-worker.js +124 -122
- package/dist/src/daemon/daemon-adapters.d.ts +2 -0
- package/dist/src/daemon/daemon-adapters.js +26 -2
- package/dist/src/daemon/daemon-loops.d.ts +3 -1
- package/dist/src/daemon/daemon-loops.js +3 -1
- package/dist/src/daemon/daemon.js +14 -6
- package/dist/src/daemon/github-attach.d.ts +2 -0
- package/dist/src/daemon/github-attach.js +149 -0
- package/dist/src/daemon/github-ci-filters.d.ts +6 -0
- package/dist/src/daemon/github-ci-filters.js +138 -0
- package/dist/src/daemon/github-ci-workflows.d.ts +10 -0
- package/dist/src/daemon/github-ci-workflows.js +148 -0
- package/dist/src/daemon/github-ci.js +22 -4
- package/dist/src/daemon/github-issue-methods.d.ts +2 -0
- package/dist/src/daemon/github-issue-methods.js +37 -2
- package/dist/src/daemon/github-merge-call.d.ts +2 -1
- package/dist/src/daemon/github-merge-call.js +10 -3
- package/dist/src/daemon/github-merge.js +52 -24
- package/dist/src/daemon/github-methods.js +12 -2
- package/dist/src/daemon/github-operation.d.ts +4 -0
- package/dist/src/daemon/github-operation.js +6 -0
- package/dist/src/daemon/github-worker.d.ts +4 -0
- package/dist/src/daemon/github-worker.js +6 -0
- package/dist/src/daemon/issue-queue.d.ts +1 -1
- package/dist/src/daemon/issue-queue.js +16 -11
- package/dist/src/daemon/notify-events.d.ts +6 -0
- package/dist/src/daemon/notify-events.js +84 -0
- package/dist/src/daemon/notify-journal.js +1 -1
- package/dist/src/daemon/notify-message.d.ts +4 -1
- package/dist/src/daemon/notify-message.js +36 -0
- package/dist/src/daemon/notify.d.ts +1 -1
- package/dist/src/daemon/notify.js +5 -77
- package/dist/src/daemon/operation-journal.d.ts +1 -0
- package/dist/src/daemon/queue-status.d.ts +9 -3
- package/dist/src/daemon/queue-status.js +17 -4
- package/dist/src/daemon/resume-index-lock.d.ts +13 -0
- package/dist/src/daemon/resume-index-lock.js +160 -0
- package/dist/src/daemon/resume-worktree.d.ts +7 -0
- package/dist/src/daemon/resume-worktree.js +29 -0
- package/dist/src/daemon/retrying-turn.d.ts +2 -12
- package/dist/src/daemon/retrying-turn.js +98 -172
- package/dist/src/daemon/schedule-control.d.ts +26 -0
- package/dist/src/daemon/schedule-control.js +110 -0
- package/dist/src/daemon/schedule-journal.d.ts +2 -15
- package/dist/src/daemon/schedule-journal.js +67 -4
- package/dist/src/daemon/ticket-registration.d.ts +2 -7
- package/dist/src/daemon/ticket-registration.js +15 -81
- package/dist/src/daemon/turn-attempt-journal.d.ts +38 -0
- package/dist/src/daemon/turn-attempt-journal.js +136 -0
- package/dist/src/daemon/turn-attempts.d.ts +47 -0
- package/dist/src/daemon/turn-attempts.js +166 -0
- package/dist/src/daemon/turn-report.d.ts +8 -1
- package/dist/src/daemon/turn-report.js +39 -0
- package/dist/src/daemon/work-tree.d.ts +9 -0
- package/dist/src/daemon/work-tree.js +20 -0
- package/dist/src/daemon/worker-profile.d.ts +8 -0
- package/dist/src/daemon/worker-profile.js +40 -0
- package/dist/src/daemon/workflow-schedules.d.ts +3 -0
- package/dist/src/daemon/workflow-schedules.js +98 -45
- package/dist/src/daemon/worktree-processes.d.ts +9 -0
- package/dist/src/daemon/worktree-processes.js +151 -0
- package/dist/src/doctor-report.d.ts +3 -5
- package/dist/src/doctor-report.js +6 -13
- package/dist/src/harness-start.d.ts +15 -0
- package/dist/src/harness-start.js +37 -0
- package/dist/src/host/command-wait.d.ts +38 -0
- package/dist/src/host/command-wait.js +115 -0
- package/dist/src/host/start-delivery.d.ts +5 -7
- package/dist/src/host/start-delivery.js +3 -15
- package/dist/src/host/step-bridge-host.d.ts +2 -0
- package/dist/src/host/step-bridge-host.js +21 -0
- package/dist/src/operation-identity.d.ts +1 -0
- package/dist/src/operation-identity.js +4 -0
- package/dist/src/private-config.schema.json +40 -0
- package/dist/src/project-config.schema.json +40 -0
- package/dist/src/project-settings.d.ts +2 -2
- package/dist/src/project-settings.js +3 -1
- package/dist/src/queue-spend.d.ts +4 -0
- package/dist/src/queue-spend.js +21 -0
- package/dist/src/relay/relay-service.d.ts +3 -0
- package/dist/src/relay/relay-service.js +5 -3
- package/dist/src/report-ci.d.ts +164 -0
- package/dist/src/report-ci.js +32 -0
- package/dist/src/report-effects.d.ts +15 -0
- package/dist/src/report-effects.js +212 -0
- package/dist/src/report-flow-types.d.ts +38 -0
- package/dist/src/report-flow-types.js +7 -0
- package/dist/src/report-flow.d.ts +43 -0
- package/dist/src/report-flow.js +193 -0
- package/dist/src/report-operator.d.ts +67 -0
- package/dist/src/report-operator.js +25 -0
- package/dist/src/report-quality.d.ts +221 -0
- package/dist/src/report-quality.js +94 -0
- package/dist/src/report-ready.d.ts +23 -0
- package/dist/src/report-ready.js +117 -0
- package/dist/src/report-statistics.d.ts +2 -1
- package/dist/src/report-statistics.js +40 -4
- package/dist/src/report.d.ts +588 -47
- package/dist/src/report.js +156 -41
- package/dist/src/schedule-progress.d.ts +33 -0
- package/dist/src/schedule-progress.js +45 -0
- package/dist/src/service/active-tickets.js +6 -6
- package/dist/src/service/command-waits.d.ts +34 -0
- package/dist/src/service/command-waits.js +86 -0
- package/dist/src/service/expiry.js +2 -0
- package/dist/src/service/gateway.js +3 -1
- package/dist/src/service/queue-spend.d.ts +6 -0
- package/dist/src/service/queue-spend.js +68 -0
- package/dist/src/service/replies.d.ts +8 -1
- package/dist/src/service/replies.js +11 -0
- package/dist/src/service/run-handlers.js +22 -2
- package/dist/src/service/run-metadata.d.ts +15 -1
- package/dist/src/service/run-metadata.js +93 -8
- package/dist/src/service/service.d.ts +4 -0
- package/dist/src/service/service.js +8 -8
- package/dist/src/service/submission-handler.js +18 -4
- package/dist/src/service/worker-port.js +9 -1
- package/dist/src/service/worker-totals.d.ts +2 -0
- package/dist/src/service/worker-totals.js +1 -1
- package/dist/src/step-bridge.d.ts +20 -0
- package/dist/src/steps/command-wait.d.ts +12 -0
- package/dist/src/steps/command-wait.js +26 -0
- package/dist/src/steps/open-decision-wait.d.ts +2 -0
- package/dist/src/steps/{decision-gate.js → open-decision-wait.js} +0 -7
- package/dist/src/steps/review-by-person.js +4 -1
- package/dist/src/steps/wait-for-command.d.ts +3 -0
- package/dist/src/steps/wait-for-command.js +17 -0
- package/dist/src/steps/wait-for-decision.js +1 -1
- package/dist/src/storage/command-wait-rows.d.ts +30 -0
- package/dist/src/storage/command-wait-rows.js +96 -0
- package/dist/src/storage/decision-snapshots.d.ts +8 -1
- package/dist/src/storage/decision-snapshots.js +15 -1
- package/dist/src/storage/domain-changes.d.ts +2 -0
- package/dist/src/storage/domain-changes.js +3 -0
- package/dist/src/storage/domain-schema.d.ts +13 -1
- package/dist/src/storage/domain-schema.js +31 -4
- package/dist/src/storage/effect-schema.d.ts +1 -0
- package/dist/src/storage/effect-schema.js +5 -0
- package/dist/src/testing-requests.d.ts +15 -0
- package/dist/src/testing-requests.js +93 -0
- package/dist/src/testing.d.ts +4 -10
- package/dist/src/testing.js +33 -86
- package/dist/src/worker-usage.d.ts +1 -0
- package/dist/src/worker-usage.js +3 -0
- package/dist/src/workflow-candidate.d.ts +1 -0
- package/dist/src/workflow-candidate.js +12 -1
- package/dist/src/workflow-contracts.d.ts +1 -0
- package/dist/src/workflow-contracts.js +9 -2
- package/dist/src/workflow-discovery.d.ts +2 -13
- package/dist/src/workflow-discovery.js +1 -13
- package/dist/src/workflow-refusals.js +0 -1
- package/dist/src/workflows.d.ts +1 -1
- package/dist/src/workflows.js +1 -1
- package/dist/tools/red-green-command.d.ts +1 -0
- package/dist/tools/red-green-command.js +174 -0
- package/dist/tools/red-green-process.d.ts +10 -0
- package/dist/tools/red-green-process.js +59 -0
- package/dist/tools/self-host-processes.d.ts +2 -0
- package/dist/tools/self-host-processes.js +32 -13
- package/dist/tools/self-host-project.js +13 -78
- package/dist/tools/self-host-runtime.js +1 -1
- package/dist/tools/self-host.js +42 -10
- package/dist/tools/template-command.d.ts +1 -0
- package/dist/tools/template-command.js +160 -0
- package/dist/tools/template-copy.d.ts +11 -0
- package/dist/tools/template-copy.js +130 -0
- package/dist/tools/template-files.d.ts +17 -0
- package/dist/tools/template-files.js +74 -0
- package/dist/tools/workflow-bundles.js +1 -0
- package/dist/tools/workflow-candidate-counts.js +2 -2
- package/dist/tools/workflow-candidate-shape.d.ts +3 -0
- package/dist/tools/workflow-candidate-shape.js +90 -0
- package/dist/tools/workflow-candidate-source.js +15 -1
- package/dist/tools/workflow-candidate-syntax.d.ts +2 -2
- package/dist/tools/workflow-candidate-syntax.js +24 -94
- package/dist/tools/workflow-command.js +1 -2
- package/dist/tools/workflow-folders.d.ts +2 -3
- package/dist/tools/workflow-folders.js +1 -1
- package/package.json +16 -8
- package/templates/develop/README.md +12 -0
- package/templates/develop/develop/flow-tests.ts +37 -0
- package/templates/develop/develop/pull.ts +5 -1
- package/templates/develop/develop/refusal-tests.ts +77 -0
- package/templates/develop/develop/turn.ts +2 -1
- package/templates/idea-to-issue/README.md +27 -0
- package/templates/idea-to-issue/idea-to-issue/effects.ts +52 -0
- package/templates/idea-to-issue/idea-to-issue/leave.ts +10 -0
- package/templates/idea-to-issue/idea-to-issue/next.ts +29 -0
- package/templates/idea-to-issue/idea-to-issue/post-person.ts +18 -0
- package/templates/idea-to-issue/idea-to-issue/post.ts +50 -0
- package/templates/idea-to-issue/idea-to-issue/queue.ts +27 -0
- package/templates/idea-to-issue/idea-to-issue/review.ts +42 -0
- package/templates/idea-to-issue/idea-to-issue/scenarios.ts +50 -0
- package/templates/idea-to-issue/idea-to-issue/turn.ts +40 -0
- package/templates/idea-to-issue/idea-to-issue.declaration.json +16 -0
- package/templates/idea-to-issue/idea-to-issue.test.ts +79 -0
- package/templates/idea-to-issue/idea-to-issue.ts +49 -0
- package/templates/idea-to-issue/template.json +81 -0
- package/templates/review-day/README.md +97 -0
- package/templates/review-day/review-day/critique.ts +58 -0
- package/templates/review-day/review-day/decision.ts +29 -0
- package/templates/review-day/review-day/facts.ts +69 -0
- package/templates/review-day/review-day/label.ts +29 -0
- package/templates/review-day/review-day/next.ts +28 -0
- package/templates/review-day/review-day/post.ts +27 -0
- package/templates/review-day/review-day/report.ts +98 -0
- package/templates/review-day/review-day/rules.ts +12 -0
- package/templates/review-day/review-day/scenarios.ts +76 -0
- package/templates/review-day/review-day/turn.ts +73 -0
- package/templates/review-day/review-day.declaration.json +16 -0
- package/templates/review-day/review-day.test.ts +362 -0
- package/templates/review-day/review-day.ts +37 -0
- package/templates/review-day/template.json +56 -0
- package/dist/src/cli/watch-start-view.d.ts +0 -18
- package/dist/src/cli/watch-start-view.js +0 -77
- package/dist/src/steps/decision-gate.d.ts +0 -3
- package/dist/workflows/develop-prd.d.ts +0 -7
- package/dist/workflows/develop-prd.js +0 -165
- package/dist/workflows/develop-ticket.d.ts +0 -9
- package/dist/workflows/develop-ticket.js +0 -207
- package/dist/workflows/issue-labels.d.ts +0 -16
- package/dist/workflows/issue-labels.js +0 -70
- package/dist/workflows/ticket-prompts.d.ts +0 -35
- package/dist/workflows/ticket-prompts.js +0 -147
|
@@ -6,75 +6,18 @@ import { codexStream } from "./codex-stream.js";
|
|
|
6
6
|
import { cursorStream } from "./cursor-stream.js";
|
|
7
7
|
import { opencodeStream } from "./opencode-stream.js";
|
|
8
8
|
import { textOutput } from "./text-output.js";
|
|
9
|
-
import {
|
|
9
|
+
import { grokUsageWarnings } from "../grok-result.js";
|
|
10
10
|
import { geminiStream } from "./gemini-stream.js";
|
|
11
|
-
import {
|
|
11
|
+
import { parsed, textOf, streamResult, } from "./claude-stream.js";
|
|
12
|
+
import { usageOf } from "../worker-usage.js";
|
|
13
|
+
export { streamResult, capacityRefusal, retryKind } from "./claude-stream.js";
|
|
12
14
|
import { commandModel } from "../token-prices.js";
|
|
13
15
|
import { shellTurnScript } from "../agent-harness-shell.js";
|
|
14
|
-
function parsed(line) {
|
|
15
|
-
try {
|
|
16
|
-
const value = JSON.parse(line);
|
|
17
|
-
return value;
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function numberOf(value) {
|
|
24
|
-
return typeof value === "number" ? value : null;
|
|
25
|
-
}
|
|
26
|
-
function textOf(value) {
|
|
27
|
-
return typeof value === "string" ? value : null;
|
|
28
|
-
}
|
|
29
|
-
function networkFailure(result) {
|
|
30
|
-
return (result?.failed === true &&
|
|
31
|
-
result.text.startsWith("API Error: Can't reach the API server"));
|
|
32
|
-
}
|
|
33
|
-
export function capacityRefusal(result) {
|
|
34
|
-
if (networkFailure(result))
|
|
35
|
-
return false;
|
|
36
|
-
return (result?.failed === true &&
|
|
37
|
-
["success", "error_during_execution"].includes(result.subtype ?? "error_during_execution") &&
|
|
38
|
-
(result.text ===
|
|
39
|
-
"Selected model is at capacity. Please try a different model." ||
|
|
40
|
-
result.errorType === "overloaded"));
|
|
41
|
-
}
|
|
42
|
-
export function retryKind(result) {
|
|
43
|
-
if (networkFailure(result))
|
|
44
|
-
return "network";
|
|
45
|
-
return capacityRefusal(result) ? "capacity" : null;
|
|
46
|
-
}
|
|
47
16
|
export function succeeded(end) {
|
|
48
17
|
return (end.reason === undefined &&
|
|
49
18
|
end.exitCode === 0 &&
|
|
50
19
|
end.result?.failed === false);
|
|
51
20
|
}
|
|
52
|
-
export function streamResult(line, worker) {
|
|
53
|
-
if (!line.includes('"result"'))
|
|
54
|
-
return undefined;
|
|
55
|
-
const record = parsed(line);
|
|
56
|
-
const field = (name) => Reflect.get(Object(record), name);
|
|
57
|
-
if (field("type") !== "result")
|
|
58
|
-
return undefined;
|
|
59
|
-
const models = modelUsageOf(field("models"));
|
|
60
|
-
return {
|
|
61
|
-
...(models === undefined ? {} : { models }),
|
|
62
|
-
...(typeof field("subtype") === "string"
|
|
63
|
-
? { subtype: String(field("subtype")) }
|
|
64
|
-
: {}),
|
|
65
|
-
...(typeof field("error") === "string"
|
|
66
|
-
? { errorType: String(field("error")) }
|
|
67
|
-
: {}),
|
|
68
|
-
text: textOf(field("result")) ?? "",
|
|
69
|
-
failed: field("is_error") === true,
|
|
70
|
-
costUsd: priceOf(field("total_cost_usd")),
|
|
71
|
-
usage: usageOf(field("usage")),
|
|
72
|
-
model: textOf(field("model")),
|
|
73
|
-
sessionId: textOf(field("session_id")),
|
|
74
|
-
turns: numberOf(field("num_turns")),
|
|
75
|
-
...(worker === "grok" ? grokResultFields(record) : {}),
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
21
|
function ended(stream) {
|
|
79
22
|
return new Promise((settle) => {
|
|
80
23
|
stream.end(settle);
|
|
@@ -129,33 +72,65 @@ function spawnTurn(start) {
|
|
|
129
72
|
throw error;
|
|
130
73
|
}
|
|
131
74
|
}
|
|
75
|
+
function streamMetadata(stream, event) {
|
|
76
|
+
if (typeof event !== "object" || event === null)
|
|
77
|
+
return false;
|
|
78
|
+
const field = (name) => Reflect.get(event, name);
|
|
79
|
+
const type = field("type");
|
|
80
|
+
const sessionId = textOf(field("session_id"));
|
|
81
|
+
const discovered = stream.sessionId === null && sessionId !== null;
|
|
82
|
+
if (discovered)
|
|
83
|
+
stream.sessionId = sessionId;
|
|
84
|
+
if (type === "assistant" && field("parent_tool_use_id") == null)
|
|
85
|
+
stream.errorType = textOf(field("error"));
|
|
86
|
+
if (type === "system" || type === "result")
|
|
87
|
+
stream.model = textOf(field("model")) ?? stream.model;
|
|
88
|
+
return discovered;
|
|
89
|
+
}
|
|
132
90
|
function receivedLines(stream, input) {
|
|
133
91
|
const { lines, worker } = input;
|
|
134
92
|
for (const line of lines) {
|
|
135
|
-
const
|
|
136
|
-
const field = (name) => Reflect.get(Object(event), name);
|
|
137
|
-
const type = field("type");
|
|
138
|
-
if (type === "assistant" && field("parent_tool_use_id") == null)
|
|
139
|
-
stream.errorType = textOf(field("error"));
|
|
140
|
-
if (type === "system" || type === "result")
|
|
141
|
-
stream.model = textOf(field("model")) ?? stream.model;
|
|
93
|
+
const discovered = streamMetadata(stream, parsed(line));
|
|
142
94
|
const result = streamResult(line, worker);
|
|
143
95
|
if (result !== undefined) {
|
|
144
96
|
const errorType = result.errorType ?? stream.errorType;
|
|
145
97
|
stream.result = {
|
|
146
98
|
...result,
|
|
99
|
+
sessionId: result.sessionId ?? stream.sessionId,
|
|
147
100
|
...(errorType === null ? {} : { errorType }),
|
|
148
101
|
};
|
|
149
102
|
}
|
|
103
|
+
if (discovered || result !== undefined) {
|
|
104
|
+
saveProgress(stream, input);
|
|
105
|
+
}
|
|
150
106
|
}
|
|
151
107
|
}
|
|
108
|
+
function saveProgress(stream, input) {
|
|
109
|
+
const progress = { sessionId: stream.sessionId, result: stream.result };
|
|
110
|
+
stream.saved = stream.saved
|
|
111
|
+
.then(() => {
|
|
112
|
+
if (stream.failure === undefined)
|
|
113
|
+
return input.progress?.(progress);
|
|
114
|
+
return undefined;
|
|
115
|
+
})
|
|
116
|
+
.catch((error) => {
|
|
117
|
+
stream.failure = { error };
|
|
118
|
+
input.failed();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
152
121
|
function activityOutput(start) {
|
|
153
122
|
const output = createWriteStream(join(start.activityFolder ?? start.folder, "turn.jsonl"), { flags: start.activityFolder === undefined ? "w" : "a" });
|
|
154
123
|
if (start.activityFolder !== undefined)
|
|
155
124
|
output.write("\n");
|
|
156
125
|
return output;
|
|
157
126
|
}
|
|
158
|
-
function streamAdapter(worker) {
|
|
127
|
+
function streamAdapter(worker, stderr) {
|
|
128
|
+
if (worker === "opencode") {
|
|
129
|
+
const translated = opencodeStream();
|
|
130
|
+
stderr.setEncoding("utf8");
|
|
131
|
+
stderr.on("data", translated.stderr);
|
|
132
|
+
return translated;
|
|
133
|
+
}
|
|
159
134
|
if (worker === "codex")
|
|
160
135
|
return codexStream();
|
|
161
136
|
if (worker === "cursor-agent")
|
|
@@ -164,26 +139,31 @@ function streamAdapter(worker) {
|
|
|
164
139
|
return geminiStream();
|
|
165
140
|
return undefined;
|
|
166
141
|
}
|
|
167
|
-
function
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const adapter = translated ?? streamAdapter(start.worker);
|
|
173
|
-
const { stdout, stderr } = child;
|
|
174
|
-
if (translated !== undefined) {
|
|
175
|
-
stderr.setEncoding("utf8");
|
|
176
|
-
stderr.on("data", translated.stderr);
|
|
177
|
-
}
|
|
178
|
-
const stream = {
|
|
142
|
+
function streamState() {
|
|
143
|
+
return {
|
|
144
|
+
failure: undefined,
|
|
145
|
+
sessionId: null,
|
|
146
|
+
saved: Promise.resolve(),
|
|
179
147
|
pending: "",
|
|
180
148
|
result: undefined,
|
|
181
149
|
model: null,
|
|
182
150
|
errorType: null,
|
|
183
151
|
};
|
|
152
|
+
}
|
|
153
|
+
function turnOutput(start, child) {
|
|
154
|
+
const output = activityOutput(start);
|
|
155
|
+
if (start.output === "text")
|
|
156
|
+
return textOutput(child, { output, limit: start.resultBytes ?? 48_000 });
|
|
157
|
+
const { stdout, stderr } = child;
|
|
158
|
+
const adapter = streamAdapter(start.worker, stderr);
|
|
159
|
+
const stream = streamState();
|
|
184
160
|
const received = (lines) => {
|
|
185
161
|
receivedLines(stream, {
|
|
186
162
|
lines,
|
|
163
|
+
progress: start.progress,
|
|
164
|
+
failed: () => {
|
|
165
|
+
signalTurn({ child, start }, "SIGKILL");
|
|
166
|
+
},
|
|
187
167
|
...(start.worker === undefined ? {} : { worker: start.worker }),
|
|
188
168
|
});
|
|
189
169
|
};
|
|
@@ -212,6 +192,9 @@ function turnOutput(start, child) {
|
|
|
212
192
|
await ended(output);
|
|
213
193
|
if (adapter === undefined)
|
|
214
194
|
received([stream.pending]);
|
|
195
|
+
await stream.saved;
|
|
196
|
+
if (stream.failure !== undefined)
|
|
197
|
+
throw stream.failure.error;
|
|
215
198
|
const result = stream.result;
|
|
216
199
|
return result === undefined
|
|
217
200
|
? undefined
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type HarnessSettings } from "../harness-defaults.ts";
|
|
2
2
|
import { type TurnProfiles } from "../turn-profile.ts";
|
|
3
|
-
import type { Delivery } from "../broker/actor/delivery-intents.ts";
|
|
3
|
+
import type { Delivery, DeliveryIntent } from "../broker/actor/delivery-intents.ts";
|
|
4
4
|
import { type RetrySettings } from "./retrying-turn.ts";
|
|
5
5
|
import { type WorkerReport } from "./operation-journal.ts";
|
|
6
6
|
export interface ClaudeWorkerSettings extends RetrySettings, HarnessSettings {
|
|
@@ -15,6 +15,7 @@ export interface ClaudeWorkerSettings extends RetrySettings, HarnessSettings {
|
|
|
15
15
|
readonly stopMilliseconds?: number;
|
|
16
16
|
}
|
|
17
17
|
export interface ClaudeWorker extends Delivery {
|
|
18
|
+
readonly recover: (intent: DeliveryIntent) => Promise<void>;
|
|
18
19
|
readonly harness: (operationId: string) => Promise<string | undefined>;
|
|
19
20
|
readonly stop: NonNullable<Delivery["stop"]>;
|
|
20
21
|
readonly close: () => Promise<void>;
|
|
@@ -1,94 +1,111 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
1
|
import { turnHarness } from "../harness-defaults.js";
|
|
2
|
+
import { attachmentEnvironment } from "./attachments.js";
|
|
3
3
|
import { profileCommand, } from "../turn-profile.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import {} from "./claude-turn.js";
|
|
5
|
+
import { turnOutcome } from "./turn-report.js";
|
|
6
6
|
import { startRetriedTurn } from "./retrying-turn.js";
|
|
7
|
-
import {
|
|
8
|
-
import { treeFailure, workTree } from "./work-tree.js";
|
|
7
|
+
import { located, readRecord as read, replied, reportOf, writeRecord as written, } from "./operation-journal.js";
|
|
8
|
+
import { firstTurn, treeFailure, workTree, } from "./work-tree.js";
|
|
9
|
+
import { recordedHarness, interruptedReply, processAlive, uncertainProcess, resumePlan, recordedField, resumableHarness, recoverTurn, turnIntent, } from "./claude-recovery.js";
|
|
10
|
+
import { selectedProfile } from "./worker-profile.js";
|
|
9
11
|
export const claudeLimits = {
|
|
10
12
|
summaryCharacters: 16_000,
|
|
11
13
|
resultBytes: 48_000,
|
|
12
|
-
stopMilliseconds:
|
|
14
|
+
stopMilliseconds: 15_000,
|
|
13
15
|
};
|
|
14
16
|
const worker = "claude";
|
|
15
|
-
|
|
16
|
-
const {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
return Reflect.get(Object(error), "code") === "EPERM";
|
|
30
|
-
}
|
|
17
|
+
function reconciled(parts, call) {
|
|
18
|
+
const { intent } = call;
|
|
19
|
+
const { key } = located(parts.settings.journal, intent.operationId);
|
|
20
|
+
const pending = parts.reconciling.get(key);
|
|
21
|
+
if (pending !== undefined)
|
|
22
|
+
return pending;
|
|
23
|
+
const reconciliation = reconcileOperation(parts, call).finally(() => {
|
|
24
|
+
if (parts.reconciling.get(key) === reconciliation)
|
|
25
|
+
parts.reconciling.delete(key);
|
|
26
|
+
});
|
|
27
|
+
parts.reconciling.set(key, reconciliation);
|
|
28
|
+
return reconciliation;
|
|
31
29
|
}
|
|
32
|
-
async function
|
|
30
|
+
async function reconcileOperation(parts, call) {
|
|
31
|
+
const { intent, signal } = call;
|
|
33
32
|
const { key, folder } = located(parts.settings.journal, intent.operationId);
|
|
34
33
|
const result = await read(folder, "result.json");
|
|
35
|
-
if (result !== undefined
|
|
34
|
+
if (result !== undefined &&
|
|
35
|
+
(recordedField(result, "stoppedBy") !== "daemon-shutdown" ||
|
|
36
|
+
parts.stopping.signal.aborted))
|
|
36
37
|
return replied({ receipt: key, worker }, result);
|
|
37
38
|
if (parts.active.has(key))
|
|
38
39
|
return { kind: "accepted", receipt: key, worker };
|
|
39
40
|
const started = await read(folder, "started.json");
|
|
40
41
|
if (started === undefined)
|
|
41
42
|
return { kind: "absent" };
|
|
42
|
-
if (
|
|
43
|
+
if (await uncertainProcess(started))
|
|
43
44
|
return { kind: "unknown", evidence: `claude-turn:${key}` };
|
|
44
|
-
|
|
45
|
-
await written(folder, { name: "result.json", value: ending });
|
|
46
|
-
return replied({ receipt: key, worker }, ending);
|
|
45
|
+
return resumed(parts, { intent, key, folder, started, signal });
|
|
47
46
|
}
|
|
48
|
-
async function
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
47
|
+
async function resumed(parts, request) {
|
|
48
|
+
const { intent, started, key, folder, signal } = request;
|
|
49
|
+
const aborted = () => signal.aborted;
|
|
50
|
+
if (!turnIntent(intent))
|
|
51
|
+
return interruptedReply(request, "claude-turn-interrupted");
|
|
52
|
+
if (aborted())
|
|
53
|
+
return failure("claude-start-aborted");
|
|
54
|
+
const plan = await resumePlan(parts.settings, {
|
|
55
|
+
folder,
|
|
56
|
+
started,
|
|
57
|
+
intent,
|
|
58
|
+
signal,
|
|
59
|
+
});
|
|
60
|
+
if (aborted())
|
|
61
|
+
return failure("claude-start-aborted");
|
|
62
|
+
if (typeof plan === "string")
|
|
63
|
+
return interruptedReply(request, plan);
|
|
64
|
+
const prompt = await parts.settings.document(intent.payload);
|
|
65
|
+
if (aborted())
|
|
66
|
+
return failure("claude-start-aborted");
|
|
67
|
+
if (prompt === undefined)
|
|
68
|
+
return interruptedReply(request, "claude-turn-interrupted");
|
|
69
|
+
await begun(parts, {
|
|
70
|
+
key,
|
|
71
|
+
folder,
|
|
72
|
+
intent,
|
|
73
|
+
...plan,
|
|
74
|
+
signal,
|
|
75
|
+
prompt,
|
|
76
|
+
});
|
|
77
|
+
return { kind: "accepted", receipt: key, worker };
|
|
70
78
|
}
|
|
71
79
|
async function concluded(parts, running) {
|
|
72
80
|
const { intent, key } = running;
|
|
73
|
-
const recorded = await
|
|
81
|
+
const recorded = await turnOutcome({ ...running, limits: claudeLimits }, await running.turn.ended);
|
|
74
82
|
await written(running.folder, { name: "result.json", value: recorded });
|
|
83
|
+
if (recorded.stoppedBy === "daemon-shutdown")
|
|
84
|
+
return;
|
|
75
85
|
await parts.settings.report(reportOf({ intent, commandId: `claude-turn:${key}`, worker }, recorded));
|
|
76
86
|
}
|
|
77
87
|
async function begun(parts, start) {
|
|
78
88
|
const { intent, key, folder, tree, prompt } = start;
|
|
79
89
|
const { harness } = start;
|
|
80
90
|
const stopping = { requested: false, controller: new AbortController() };
|
|
91
|
+
const cancel = () => {
|
|
92
|
+
stopping.controller.abort();
|
|
93
|
+
};
|
|
94
|
+
start.signal.addEventListener("abort", cancel, { once: true });
|
|
95
|
+
if (start.signal.aborted)
|
|
96
|
+
cancel();
|
|
81
97
|
const turn = startRetriedTurn({
|
|
82
98
|
...harness,
|
|
83
99
|
resultBytes: claudeLimits.resultBytes,
|
|
84
100
|
command: profileCommand(harness.command, start.profile),
|
|
85
|
-
environment:
|
|
101
|
+
environment: await attachmentEnvironment(parts.settings, start),
|
|
86
102
|
turnId: `${intent.runId}-${key}`,
|
|
87
103
|
directory: tree.path,
|
|
88
104
|
folder,
|
|
89
105
|
prompt,
|
|
90
106
|
}, {
|
|
91
107
|
...parts.settings,
|
|
108
|
+
...(start.resume === undefined ? {} : { resume: start.resume }),
|
|
92
109
|
stopping: stopping.controller,
|
|
93
110
|
metadata: {
|
|
94
111
|
harness: harness.worker,
|
|
@@ -100,6 +117,7 @@ async function begun(parts, start) {
|
|
|
100
117
|
},
|
|
101
118
|
});
|
|
102
119
|
parts.active.set(key, {
|
|
120
|
+
worker: harness.worker,
|
|
103
121
|
turn,
|
|
104
122
|
stopping,
|
|
105
123
|
concluded: turn
|
|
@@ -107,74 +125,13 @@ async function begun(parts, start) {
|
|
|
107
125
|
.catch(() => undefined)
|
|
108
126
|
.finally(() => parts.active.delete(key)),
|
|
109
127
|
});
|
|
110
|
-
await turn
|
|
111
|
-
|
|
112
|
-
async function firstTurn(settings, opening) {
|
|
113
|
-
const { tree, intent } = opening;
|
|
114
|
-
const { target, runId } = intent;
|
|
115
|
-
const folder = target.startsWith("branch:")
|
|
116
|
-
? join(settings.journal, "runs", operationKey(runId), "targets")
|
|
117
|
-
: join(settings.journal, "targets");
|
|
118
|
-
const name = `${operationKey(target)}.json`;
|
|
119
|
-
const startCommit = Reflect.get(Object(await read(folder, name)), "startCommit");
|
|
120
|
-
if (typeof startCommit === "string")
|
|
121
|
-
return { ...tree, startCommit };
|
|
122
|
-
await written(folder, {
|
|
123
|
-
name,
|
|
124
|
-
value: { target, startCommit: tree.startCommit },
|
|
128
|
+
await turn.finally(() => {
|
|
129
|
+
start.signal.removeEventListener("abort", cancel);
|
|
125
130
|
});
|
|
126
|
-
return tree;
|
|
127
|
-
}
|
|
128
|
-
function turnIntent(intent) {
|
|
129
|
-
return (intent.capability === worker &&
|
|
130
|
-
(intent.method === "run-turn" ||
|
|
131
|
-
/^run-turn:[A-Za-z0-9_-]{1,119}$/.test(intent.method ?? "")) &&
|
|
132
|
-
intent.target !== undefined &&
|
|
133
|
-
intent.payload !== undefined &&
|
|
134
|
-
intent.leaseGeneration !== undefined);
|
|
135
131
|
}
|
|
136
132
|
function failure(reason) {
|
|
137
133
|
return { kind: "failed", reason };
|
|
138
134
|
}
|
|
139
|
-
async function runWorkflow(settings, runId) {
|
|
140
|
-
try {
|
|
141
|
-
return await settings.workflowId?.(runId);
|
|
142
|
-
}
|
|
143
|
-
catch {
|
|
144
|
-
return undefined;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
function unsupportedProfile(profile, harness) {
|
|
148
|
-
if (harness.output === "text" && profile.model !== undefined)
|
|
149
|
-
return "profile-model-not-supported";
|
|
150
|
-
if (harness.worker === "claude")
|
|
151
|
-
return undefined;
|
|
152
|
-
return [
|
|
153
|
-
profile.allowedTools,
|
|
154
|
-
profile.permissionMode,
|
|
155
|
-
profile.maxBudgetUsd,
|
|
156
|
-
].some((value) => value !== undefined)
|
|
157
|
-
? "profile-needs-claude"
|
|
158
|
-
: undefined;
|
|
159
|
-
}
|
|
160
|
-
async function selectedProfile(settings, intent) {
|
|
161
|
-
if (intent.method === "run-turn")
|
|
162
|
-
return {};
|
|
163
|
-
const name = intent.method.slice("run-turn:".length);
|
|
164
|
-
const profiles = settings.profiles ?? {};
|
|
165
|
-
const profile = Object.hasOwn(profiles, name) ? profiles[name] : undefined;
|
|
166
|
-
if (profile === undefined)
|
|
167
|
-
return "unknown-profile";
|
|
168
|
-
const unsupported = unsupportedProfile(profile, turnHarness(settings, profile));
|
|
169
|
-
if (unsupported !== undefined)
|
|
170
|
-
return unsupported;
|
|
171
|
-
if (profile.workflows === undefined)
|
|
172
|
-
return profile;
|
|
173
|
-
const workflowId = await runWorkflow(settings, intent.runId);
|
|
174
|
-
if (workflowId === undefined || !profile.workflows.includes(workflowId))
|
|
175
|
-
return "profile-not-allowed";
|
|
176
|
-
return profile;
|
|
177
|
-
}
|
|
178
135
|
async function delivered(parts, call) {
|
|
179
136
|
const { intent, signal } = call;
|
|
180
137
|
const { settings } = parts;
|
|
@@ -183,7 +140,7 @@ async function delivered(parts, call) {
|
|
|
183
140
|
return failure("unsupported-effect");
|
|
184
141
|
if (aborted())
|
|
185
142
|
return failure("claude-start-aborted");
|
|
186
|
-
const known = await reconciled(parts,
|
|
143
|
+
const known = await reconciled(parts, call);
|
|
187
144
|
if (known.kind !== "absent")
|
|
188
145
|
return known;
|
|
189
146
|
const profile = await selectedProfile(settings, intent);
|
|
@@ -213,6 +170,7 @@ async function delivered(parts, call) {
|
|
|
213
170
|
prompt,
|
|
214
171
|
profile,
|
|
215
172
|
harness: turnHarness(settings, profile),
|
|
173
|
+
signal,
|
|
216
174
|
});
|
|
217
175
|
return { receipt: place.key, worker };
|
|
218
176
|
}
|
|
@@ -224,11 +182,44 @@ async function halted(parts, active) {
|
|
|
224
182
|
await active.concluded;
|
|
225
183
|
}
|
|
226
184
|
async function stopped(parts, intent) {
|
|
227
|
-
const { key } = located(parts.settings.journal, intent.operationId);
|
|
185
|
+
const { key, folder } = located(parts.settings.journal, intent.operationId);
|
|
228
186
|
const active = parts.active.get(key);
|
|
229
|
-
if (active !== undefined)
|
|
187
|
+
if (active !== undefined) {
|
|
188
|
+
delete active.stopping.stoppedBy;
|
|
230
189
|
await halted(parts, active);
|
|
231
|
-
|
|
190
|
+
}
|
|
191
|
+
const result = await read(folder, "result.json");
|
|
192
|
+
if (result !== undefined &&
|
|
193
|
+
recordedField(result, "stoppedBy") !== "daemon-shutdown")
|
|
194
|
+
return replied({ receipt: key, worker }, result);
|
|
195
|
+
const started = await read(folder, "started.json");
|
|
196
|
+
if (started === undefined)
|
|
197
|
+
return { kind: "absent" };
|
|
198
|
+
if (!resumableHarness(recordedField(started, "harness")))
|
|
199
|
+
return reconcileOperation(parts, { intent, signal: parts.stopping.signal });
|
|
200
|
+
if (processAlive(recordedField(started, "processId")))
|
|
201
|
+
return { kind: "unknown", evidence: `claude-turn:${key}` };
|
|
202
|
+
if (!turnIntent(intent))
|
|
203
|
+
return failure("unsupported-effect");
|
|
204
|
+
const recorded = {
|
|
205
|
+
outcome: "stopped",
|
|
206
|
+
endedAt: new Date().toISOString(),
|
|
207
|
+
};
|
|
208
|
+
await written(folder, { name: "result.json", value: recorded });
|
|
209
|
+
await parts.settings.report(reportOf({ intent, commandId: `claude-turn:${key}`, worker }, recorded));
|
|
210
|
+
return replied({ receipt: key, worker }, recorded);
|
|
211
|
+
}
|
|
212
|
+
function stoppingOperation(parts, intent) {
|
|
213
|
+
const { key } = located(parts.settings.journal, intent.operationId);
|
|
214
|
+
const pending = parts.reconciling.get(key);
|
|
215
|
+
const stop = () => stopped(parts, intent);
|
|
216
|
+
const stopping = pending === undefined ? stop() : pending.then(stop, stop);
|
|
217
|
+
const settled = stopping.finally(() => {
|
|
218
|
+
if (parts.reconciling.get(key) === settled)
|
|
219
|
+
parts.reconciling.delete(key);
|
|
220
|
+
});
|
|
221
|
+
parts.reconciling.set(key, settled);
|
|
222
|
+
return settled;
|
|
232
223
|
}
|
|
233
224
|
export function claudeWorker(settings) {
|
|
234
225
|
if (settings.command.length === 0)
|
|
@@ -239,22 +230,33 @@ export function claudeWorker(settings) {
|
|
|
239
230
|
const parts = {
|
|
240
231
|
settings,
|
|
241
232
|
active: new Map(),
|
|
233
|
+
reconciling: new Map(),
|
|
242
234
|
stopping: new AbortController(),
|
|
243
235
|
};
|
|
244
236
|
const guarded = (intent, run) => intent.method === "run-turn:analyze-chats"
|
|
245
237
|
? Promise.resolve(failure("unsupported-effect"))
|
|
246
238
|
: run();
|
|
247
239
|
return {
|
|
240
|
+
recover: (intent) => turnIntent(intent)
|
|
241
|
+
? recoverTurn({ ...settings, intent, signal: parts.stopping.signal }, () => reconciled(parts, { intent, signal: parts.stopping.signal }))
|
|
242
|
+
: Promise.resolve(),
|
|
248
243
|
harness: (operationId) => recordedHarness(settings.journal, operationId),
|
|
249
244
|
deliver: (intent, signal) => guarded(intent, () => delivered(parts, {
|
|
250
245
|
intent,
|
|
251
246
|
signal: AbortSignal.any([signal, parts.stopping.signal]),
|
|
252
247
|
})),
|
|
253
|
-
reconcile: (intent) => guarded(intent, () => reconciled(parts,
|
|
254
|
-
|
|
248
|
+
reconcile: (intent, signal) => guarded(intent, () => reconciled(parts, {
|
|
249
|
+
intent,
|
|
250
|
+
signal: AbortSignal.any([signal, parts.stopping.signal]),
|
|
251
|
+
})),
|
|
252
|
+
stop: (intent) => guarded(intent, () => stoppingOperation(parts, intent)),
|
|
255
253
|
close: async () => {
|
|
256
254
|
parts.stopping.abort();
|
|
257
|
-
await Promise.all([...parts.active.values()].map((active) =>
|
|
255
|
+
await Promise.all([...parts.active.values()].map((active) => {
|
|
256
|
+
if (resumableHarness(active.worker) && !active.stopping.requested)
|
|
257
|
+
active.stopping.stoppedBy = "daemon-shutdown";
|
|
258
|
+
return halted(parts, active);
|
|
259
|
+
}));
|
|
258
260
|
},
|
|
259
261
|
};
|
|
260
262
|
}
|
|
@@ -37,6 +37,7 @@ export interface DaemonSettings extends HarnessSettings {
|
|
|
37
37
|
readonly scheduleClock?: () => number;
|
|
38
38
|
readonly schedules?: unknown;
|
|
39
39
|
readonly queueLanes?: number;
|
|
40
|
+
readonly queueDailySpendLimitUsd?: number;
|
|
40
41
|
readonly resetQueue?: boolean;
|
|
41
42
|
readonly githubCommand?: readonly string[];
|
|
42
43
|
readonly requiredChecks?: readonly string[] | null;
|
|
@@ -56,3 +57,4 @@ export declare function adapters(settings: DaemonSettings, options: {
|
|
|
56
57
|
readonly generations: ReturnType<typeof workflowGenerations>;
|
|
57
58
|
}): Adapters;
|
|
58
59
|
export declare function recoverCommands(commands: CommandWorker, ports: HostPorts): Promise<void>;
|
|
60
|
+
export declare function recoverClaude(worker: Pick<ClaudeWorker, "harness" | "recover">, ports: Pick<HostPorts, "state" | "operations">): Promise<void>;
|
|
@@ -6,11 +6,13 @@ import { claudeWorker } from "./claude-worker.js";
|
|
|
6
6
|
import { githubWorker } from "./github-worker.js";
|
|
7
7
|
import { commandWorker } from "./command-worker.js";
|
|
8
8
|
import { handoffWorker } from "./handoff-worker.js";
|
|
9
|
+
import { resumableHarness } from "./claude-recovery.js";
|
|
9
10
|
function githubOf(settings, given) {
|
|
10
11
|
const { directory, repository, checkCommand } = settings;
|
|
11
12
|
return githubWorker({
|
|
12
13
|
document: async (digest) => (await given).document(digest),
|
|
13
14
|
report: async (effect) => (await given).reportEffect(effect),
|
|
15
|
+
observe: async (observation) => (await given).submit({ kind: "report-observation", ...observation }),
|
|
14
16
|
operationStatus: async (operationId) => (await (await given).operation(operationId))?.status,
|
|
15
17
|
runStatus: async (runId) => (await (await given).state()).runs[runId]?.status,
|
|
16
18
|
repository,
|
|
@@ -81,11 +83,33 @@ function handoffOf(settings, given) {
|
|
|
81
83
|
});
|
|
82
84
|
}
|
|
83
85
|
export async function recoverCommands(commands, ports) {
|
|
86
|
+
await commands.recover(await recoveryCalls(ports, "command"));
|
|
87
|
+
}
|
|
88
|
+
export async function recoverClaude(worker, ports) {
|
|
89
|
+
for (const intent of await recoveryCalls(ports, "claude")) {
|
|
90
|
+
if (!resumableHarness(await worker.harness(intent.operationId)))
|
|
91
|
+
continue;
|
|
92
|
+
if (!(await stillRecoverable(ports, intent)))
|
|
93
|
+
continue;
|
|
94
|
+
await worker.recover(intent);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function stillRecoverable(ports, intent) {
|
|
98
|
+
const operation = (await ports.operations(intent.runId)).find((current) => current.operationId === intent.operationId);
|
|
99
|
+
const run = (await ports.state()).runs[intent.runId];
|
|
100
|
+
return (run?.status === "running" &&
|
|
101
|
+
operation !== undefined &&
|
|
102
|
+
operation.leaseGeneration === intent.leaseGeneration &&
|
|
103
|
+
["running", "recovering", "dispatch-pending"].includes(operation.status));
|
|
104
|
+
}
|
|
105
|
+
async function recoveryCalls(ports, capability) {
|
|
84
106
|
const state = await ports.state();
|
|
85
107
|
const calls = [];
|
|
86
108
|
for (const run of Object.values(state.runs)) {
|
|
109
|
+
if (capability === "claude" && run.status !== "running")
|
|
110
|
+
continue;
|
|
87
111
|
for (const operation of await ports.operations(run.runId)) {
|
|
88
|
-
if (operation.capability !==
|
|
112
|
+
if (operation.capability !== capability ||
|
|
89
113
|
operation.leaseGeneration === null ||
|
|
90
114
|
!["running", "recovering", "dispatch-pending"].includes(operation.status))
|
|
91
115
|
continue;
|
|
@@ -98,5 +122,5 @@ export async function recoverCommands(commands, ports) {
|
|
|
98
122
|
});
|
|
99
123
|
}
|
|
100
124
|
}
|
|
101
|
-
|
|
125
|
+
return calls;
|
|
102
126
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import type { scheduleConnection } from "./schedule-control.ts";
|
|
1
2
|
import { workflowSchedules } from "./workflow-schedules.ts";
|
|
2
3
|
interface LoopSettings extends ParametersOfSchedules {
|
|
4
|
+
readonly scheduleConnection?: ReturnType<typeof scheduleConnection>;
|
|
3
5
|
readonly notifyCommand?: readonly string[];
|
|
4
6
|
readonly notifyMilliseconds?: number;
|
|
5
7
|
readonly queueMilliseconds?: number;
|
|
6
8
|
readonly queueLanes?: number;
|
|
7
9
|
readonly resetQueue?: boolean;
|
|
8
|
-
readonly queueWorkflowId: string;
|
|
10
|
+
readonly queueWorkflowId: string | undefined;
|
|
9
11
|
}
|
|
10
12
|
type ParametersOfSchedules = Parameters<typeof workflowSchedules>[0];
|
|
11
13
|
export declare function daemonLoops(settings: LoopSettings): Promise<{
|