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
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { scheduleStops } from "../schedule-progress.js";
|
|
2
|
+
import { runIdentity } from "../service/submission-handler.js";
|
|
3
|
+
import { failed, invalidRequest, result } from "../service/replies.js";
|
|
4
|
+
function summary(workflowId, progress) {
|
|
5
|
+
return {
|
|
6
|
+
workflowId,
|
|
7
|
+
everySeconds: progress.everySeconds,
|
|
8
|
+
state: progress.state,
|
|
9
|
+
failuresInRow: progress.failuresInRow,
|
|
10
|
+
failedRunIds: progress.failedRunIds,
|
|
11
|
+
lastSlot: progress.lastSlot,
|
|
12
|
+
nextAt: progress.state === "active" ? progress.nextAt : null,
|
|
13
|
+
activeRunId: progress.activeCommandId === undefined
|
|
14
|
+
? null
|
|
15
|
+
: runIdentity(progress.activeCommandId),
|
|
16
|
+
stoppedAt: progress.stoppedAt ?? null,
|
|
17
|
+
stoppedSlot: progress.stoppedSlot ?? null,
|
|
18
|
+
stops: scheduleStops(progress),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function scheduleConnection() {
|
|
22
|
+
let port;
|
|
23
|
+
return {
|
|
24
|
+
request: (parameters) => port?.(parameters) ??
|
|
25
|
+
Promise.resolve(failed("schedules-unavailable", {})),
|
|
26
|
+
connect: (connected) => {
|
|
27
|
+
port = connected;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async function change(settings, command) {
|
|
32
|
+
const { workflowId, action, now } = command;
|
|
33
|
+
const { journal } = settings;
|
|
34
|
+
let prior = journal.entries.get(workflowId);
|
|
35
|
+
if (prior === undefined)
|
|
36
|
+
return failed("unknown-schedule", { workflowId });
|
|
37
|
+
const state = action === "pause" ? "paused" : "active";
|
|
38
|
+
if (prior.state === state)
|
|
39
|
+
return result("schedule", { schedule: summary(workflowId, prior) });
|
|
40
|
+
if (prior.pending !== undefined) {
|
|
41
|
+
prior = await settings.recoverPending({ workflowId, progress: prior });
|
|
42
|
+
journal.entries.set(workflowId, prior);
|
|
43
|
+
await journal.save();
|
|
44
|
+
}
|
|
45
|
+
const rest = { ...prior };
|
|
46
|
+
delete rest.stoppedAt;
|
|
47
|
+
delete rest.stoppedSlot;
|
|
48
|
+
const progress = action === "pause"
|
|
49
|
+
? { ...prior, state, stops: scheduleStops(prior) }
|
|
50
|
+
: {
|
|
51
|
+
...rest,
|
|
52
|
+
state,
|
|
53
|
+
failuresInRow: 0,
|
|
54
|
+
failedRunIds: [],
|
|
55
|
+
stops: scheduleStops(prior),
|
|
56
|
+
nextAt: now + prior.everySeconds * 1000,
|
|
57
|
+
};
|
|
58
|
+
journal.entries.set(workflowId, progress);
|
|
59
|
+
try {
|
|
60
|
+
await journal.save();
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
journal.entries.set(workflowId, prior);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
return result("schedule", { schedule: summary(workflowId, progress) });
|
|
67
|
+
}
|
|
68
|
+
function request(settings, parameters) {
|
|
69
|
+
const { action, workflowId } = parameters;
|
|
70
|
+
const scheduled = settings.declarations.filter((entry) => entry.schedule !== undefined);
|
|
71
|
+
if (action === "list")
|
|
72
|
+
return Promise.resolve(result("schedules", {
|
|
73
|
+
stops: Array.from(settings.journal.entries, ([workflowId, progress]) => ({
|
|
74
|
+
workflowId,
|
|
75
|
+
stops: scheduleStops(progress),
|
|
76
|
+
})),
|
|
77
|
+
schedules: scheduled.flatMap((entry) => {
|
|
78
|
+
const progress = settings.journal.entries.get(entry.workflowId);
|
|
79
|
+
return progress === undefined
|
|
80
|
+
? []
|
|
81
|
+
: [summary(entry.workflowId, progress)];
|
|
82
|
+
}),
|
|
83
|
+
}));
|
|
84
|
+
if ((action !== "pause" && action !== "resume") ||
|
|
85
|
+
typeof workflowId !== "string")
|
|
86
|
+
return Promise.resolve(invalidRequest("Use list, pause or resume with a workflow ID."));
|
|
87
|
+
if (!scheduled.some((entry) => entry.workflowId === workflowId))
|
|
88
|
+
return Promise.resolve(failed("unknown-schedule", { workflowId }));
|
|
89
|
+
return change(settings, {
|
|
90
|
+
workflowId,
|
|
91
|
+
action,
|
|
92
|
+
now: settings.clock(),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
export function scheduleControl(settings) {
|
|
96
|
+
let running = Promise.resolve();
|
|
97
|
+
const serialized = (operation) => {
|
|
98
|
+
const next = running.then(async () => {
|
|
99
|
+
settings.signal.throwIfAborted();
|
|
100
|
+
return operation();
|
|
101
|
+
});
|
|
102
|
+
running = next.then(() => undefined, () => undefined);
|
|
103
|
+
return next;
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
request: (parameters) => serialized(() => request(settings, parameters)).catch(() => failed("schedules-unavailable", {})),
|
|
107
|
+
tick: () => serialized(settings.tick),
|
|
108
|
+
drain: () => running,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export type PendingSlot
|
|
3
|
-
readonly kind: "run";
|
|
4
|
-
readonly input: Fields;
|
|
5
|
-
} | {
|
|
6
|
-
readonly kind: "skip";
|
|
7
|
-
readonly runId: string;
|
|
8
|
-
};
|
|
9
|
-
export interface ScheduleProgress {
|
|
10
|
-
readonly everySeconds: number;
|
|
11
|
-
readonly nextAt: number;
|
|
12
|
-
readonly lastSlot: number;
|
|
13
|
-
readonly activeCommandId?: string;
|
|
14
|
-
readonly pending?: PendingSlot;
|
|
15
|
-
}
|
|
1
|
+
import type { ScheduleProgress } from "../schedule-progress.ts";
|
|
2
|
+
export type { PendingSlot, ScheduleProgress } from "../schedule-progress.ts";
|
|
16
3
|
export declare function scheduleJournal(directory: string): Promise<{
|
|
17
4
|
readonly entries: Map<string, ScheduleProgress>;
|
|
18
5
|
readonly save: () => Promise<void>;
|
|
@@ -23,10 +23,68 @@ function isProgress(value) {
|
|
|
23
23
|
const fields = record(value);
|
|
24
24
|
if (fields === undefined)
|
|
25
25
|
return false;
|
|
26
|
-
const { activeCommandId
|
|
26
|
+
const { activeCommandId } = fields;
|
|
27
27
|
return (timing(fields) &&
|
|
28
|
-
(
|
|
29
|
-
(
|
|
28
|
+
control(fields) &&
|
|
29
|
+
history(fields) &&
|
|
30
|
+
pendingProgress(fields) &&
|
|
31
|
+
(activeCommandId === undefined || isCommandId(activeCommandId)));
|
|
32
|
+
}
|
|
33
|
+
function pendingProgress(fields) {
|
|
34
|
+
const { pending, state } = fields;
|
|
35
|
+
if (pending === undefined)
|
|
36
|
+
return true;
|
|
37
|
+
return (state === undefined || state === "active") && pendingSlot(pending);
|
|
38
|
+
}
|
|
39
|
+
function stopped(fields) {
|
|
40
|
+
const { state, stoppedAt, stoppedSlot } = fields;
|
|
41
|
+
if (stoppedAt === undefined && stoppedSlot === undefined)
|
|
42
|
+
return state !== "stopped";
|
|
43
|
+
return (whole(stoppedAt) &&
|
|
44
|
+
whole(stoppedSlot) &&
|
|
45
|
+
stoppedSlot <= Number(fields["lastSlot"]));
|
|
46
|
+
}
|
|
47
|
+
function failures(fields) {
|
|
48
|
+
const { failuresInRow, failedRunIds } = fields;
|
|
49
|
+
return (whole(failuresInRow) &&
|
|
50
|
+
failuresInRow <= 3 &&
|
|
51
|
+
Array.isArray(failedRunIds) &&
|
|
52
|
+
failedRunIds.length === failuresInRow &&
|
|
53
|
+
failedRunIds.every(isRecordKey) &&
|
|
54
|
+
new Set(failedRunIds).size === failuresInRow);
|
|
55
|
+
}
|
|
56
|
+
function history(fields) {
|
|
57
|
+
const stops = fields["stops"];
|
|
58
|
+
if (stops === undefined)
|
|
59
|
+
return true;
|
|
60
|
+
if (!Array.isArray(stops))
|
|
61
|
+
return false;
|
|
62
|
+
let previousSlot = -1;
|
|
63
|
+
for (const item of stops) {
|
|
64
|
+
const stop = record(item);
|
|
65
|
+
if (stop === undefined)
|
|
66
|
+
return false;
|
|
67
|
+
const slot = stop["stoppedSlot"];
|
|
68
|
+
if (!whole(slot) || !whole(stop["stoppedAt"]))
|
|
69
|
+
return false;
|
|
70
|
+
if (slot <= previousSlot || slot > Number(fields["lastSlot"]))
|
|
71
|
+
return false;
|
|
72
|
+
if (!failures({ ...stop, failuresInRow: 3 }))
|
|
73
|
+
return false;
|
|
74
|
+
previousSlot = slot;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
function control(fields) {
|
|
79
|
+
const { state, failuresInRow, failedRunIds } = fields;
|
|
80
|
+
if (state === undefined &&
|
|
81
|
+
failuresInRow === undefined &&
|
|
82
|
+
failedRunIds === undefined)
|
|
83
|
+
return (fields["stoppedAt"] === undefined && fields["stoppedSlot"] === undefined);
|
|
84
|
+
return (["active", "paused", "stopped"].includes(String(state)) &&
|
|
85
|
+
failures(fields) &&
|
|
86
|
+
stopped(fields) &&
|
|
87
|
+
(state !== "stopped" || failuresInRow === 3));
|
|
30
88
|
}
|
|
31
89
|
export async function scheduleJournal(directory) {
|
|
32
90
|
const file = join(directory, "schedules.json");
|
|
@@ -42,7 +100,12 @@ export async function scheduleJournal(directory) {
|
|
|
42
100
|
for (const [workflowId, value] of Object.entries(parsed)) {
|
|
43
101
|
if (!isProgress(value))
|
|
44
102
|
throw new Error("invalid-schedule-state");
|
|
45
|
-
entries.set(workflowId,
|
|
103
|
+
entries.set(workflowId, {
|
|
104
|
+
...value,
|
|
105
|
+
state: value.state ?? "active",
|
|
106
|
+
failuresInRow: value.failuresInRow ?? 0,
|
|
107
|
+
failedRunIds: value.failedRunIds ?? [],
|
|
108
|
+
});
|
|
46
109
|
}
|
|
47
110
|
return {
|
|
48
111
|
entries,
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { type DeclaredWorkflow } from "../workflow-declaration.ts";
|
|
2
2
|
import type { Resolver, WorkflowRegistration } from "../service/registry.ts";
|
|
3
|
+
import { type SubmissionContract } from "../submission.ts";
|
|
3
4
|
import type { LocalCommands } from "../command-settings.ts";
|
|
4
|
-
export declare const ticketWorkflow: "develop-ticket";
|
|
5
|
-
export declare const promptWorkflow: "develop-prompt";
|
|
6
|
-
export declare const issueWorkflow: "develop-issue";
|
|
7
|
-
export declare const ticketCapabilities: string[];
|
|
8
5
|
export type TicketRegistrations = {
|
|
9
6
|
readonly kind: "admitted";
|
|
10
7
|
readonly registrations: readonly WorkflowRegistration[];
|
|
@@ -24,10 +21,8 @@ export interface TicketRegistration extends Omit<WorkflowRegistration, "manifest
|
|
|
24
21
|
readonly effectLimit: number;
|
|
25
22
|
readonly capabilities?: readonly string[];
|
|
26
23
|
}
|
|
27
|
-
export declare function ticketResolver(source: TicketSource): Resolver;
|
|
28
|
-
export declare function issueResolver(source: TicketSource): Resolver;
|
|
29
|
-
export declare function promptResolver(repository: string): Resolver;
|
|
30
24
|
export declare function ticketRegistrationTable(source: TicketSource, declarations?: readonly DeclaredWorkflow[]): readonly TicketRegistration[];
|
|
25
|
+
export declare function declaredResolver(source: TicketSource, contract: SubmissionContract): Resolver;
|
|
31
26
|
export declare function ticketRegistrations(manifests: Readonly<Record<string, string | undefined>>, options: {
|
|
32
27
|
readonly table: readonly TicketRegistration[];
|
|
33
28
|
readonly bundleDigest?: string;
|
|
@@ -1,45 +1,34 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import { admitCompiled } from "../cli/validate.js";
|
|
3
2
|
import { isCommandWaits, workflowQueueRefusals, } from "../workflow-declaration.js";
|
|
4
3
|
import { ticketCommandText, ticketText } from "./ticket-source.js";
|
|
5
4
|
import { declaredInput } from "./declared-input.js";
|
|
6
|
-
import { bundledWorkflowIds } from "../workflow-discovery.js";
|
|
7
5
|
import { declaredManifest } from "../execution-pin.js";
|
|
8
6
|
import { record } from "../protocol.js";
|
|
9
7
|
import { hasTicketField } from "../submission.js";
|
|
10
8
|
import { issueIdentity } from "../issue-identity.js";
|
|
11
9
|
import { ticketDependencyWaits, TicketDependencyReadError, } from "./ticket-dependency-waits.js";
|
|
12
|
-
|
|
13
|
-
export const promptWorkflow = bundledWorkflowIds.prompt;
|
|
14
|
-
export const issueWorkflow = bundledWorkflowIds.issue;
|
|
15
|
-
export const ticketCapabilities = ["claude", "github"];
|
|
10
|
+
const defaultCapabilities = ["claude", "github"];
|
|
16
11
|
const ticketIdentifier = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
17
12
|
function rejected(code, detail) {
|
|
18
13
|
return { kind: "rejected", code, detail };
|
|
19
14
|
}
|
|
20
|
-
function digest(commandId) {
|
|
21
|
-
return createHash("sha256").update(commandId).digest("hex");
|
|
22
|
-
}
|
|
23
15
|
async function ticket(source, request) {
|
|
24
|
-
const { command
|
|
16
|
+
const { command } = source;
|
|
25
17
|
const ticketId = String(request.submission["ticket"]);
|
|
26
18
|
if (ticketId === "next")
|
|
27
19
|
return nextTicket(source, request);
|
|
28
20
|
if (!ticketIdentifier.test(ticketId))
|
|
29
21
|
return rejected("invalid-ticket-id", "A ticket ID has letters, digits and the characters . _ : - only.");
|
|
30
22
|
if (command === undefined)
|
|
31
|
-
return rejected("ticket-source-not-configured", "The daemon has no ticket command. Set CLOVING_TICKET_COMMAND
|
|
23
|
+
return rejected("ticket-source-not-configured", "The daemon has no ticket command. Set CLOVING_TICKET_COMMAND.");
|
|
32
24
|
const read = await ticketText(command, { ticketId, signal: request.signal });
|
|
33
25
|
if (read.kind === "failed")
|
|
34
26
|
return rejected("ticket-not-available", read.detail);
|
|
35
|
-
const name = ticketId.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
36
27
|
return {
|
|
37
28
|
kind: "resolved",
|
|
38
29
|
input: {
|
|
39
|
-
cycleId: `${name}-${digest(request.commandId).slice(0, 8)}`,
|
|
40
30
|
ticketId,
|
|
41
31
|
ticket: read.text,
|
|
42
|
-
repository,
|
|
43
32
|
},
|
|
44
33
|
};
|
|
45
34
|
}
|
|
@@ -63,17 +52,10 @@ async function nextTicket(source, request) {
|
|
|
63
52
|
return rejected("invalid-ticket-id", "The next-ticket command must print one valid ticket ID other than next.");
|
|
64
53
|
return ticket(source, { ...request, submission: { ticket: selected.text } });
|
|
65
54
|
}
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
id: "ticket-command",
|
|
69
|
-
version: "1",
|
|
70
|
-
resolve: (request) => ticket(source, request),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
export function issueResolver(source) {
|
|
55
|
+
function ticketResolver(source) {
|
|
74
56
|
const waits = ticketDependencyWaits(source);
|
|
75
57
|
return {
|
|
76
|
-
id: "
|
|
58
|
+
id: "ticket-command",
|
|
77
59
|
version: "1",
|
|
78
60
|
skippedTickets: waits.skipped,
|
|
79
61
|
resolve: (request) => issueTicket({ source, waits }, request).catch((error) => {
|
|
@@ -107,66 +89,18 @@ async function issueTicket(context, request) {
|
|
|
107
89
|
return rejected("ticket-dependency-open", `Ticket ${String(input["ticketId"])} needs open issues: ${dependencies.map((id) => `#${id}`).join(", ")}.`);
|
|
108
90
|
return {
|
|
109
91
|
kind: "resolved",
|
|
110
|
-
input
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
export function promptResolver(repository) {
|
|
114
|
-
return {
|
|
115
|
-
id: "ticket-prompt",
|
|
116
|
-
version: "1",
|
|
117
|
-
resolve: (request) => Promise.resolve({
|
|
118
|
-
kind: "resolved",
|
|
119
|
-
input: {
|
|
120
|
-
cycleId: `task-${digest(request.commandId).slice(0, 12)}`,
|
|
121
|
-
ticket: String(request.submission["prompt"]),
|
|
122
|
-
repository,
|
|
123
|
-
},
|
|
124
|
-
}),
|
|
92
|
+
input,
|
|
125
93
|
};
|
|
126
94
|
}
|
|
127
95
|
export function ticketRegistrationTable(source, declarations = []) {
|
|
128
|
-
return
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
handoffs: [],
|
|
134
|
-
submission: { maximumBytes: 4_096, fields: { ticket: "string" } },
|
|
135
|
-
resolver: ticketResolver(source),
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
workflowId: issueWorkflow,
|
|
139
|
-
functionName: "developTicket",
|
|
140
|
-
effectLimit: 20,
|
|
141
|
-
handoffs: [],
|
|
142
|
-
submission: { maximumBytes: 4_096, fields: { ticket: "string" } },
|
|
143
|
-
resolver: issueResolver(source),
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
workflowId: bundledWorkflowIds.prd,
|
|
147
|
-
functionName: "developPrd",
|
|
148
|
-
effectLimit: 8,
|
|
149
|
-
handoffs: [issueWorkflow],
|
|
150
|
-
submission: { maximumBytes: 65_536, fields: { prompt: "string" } },
|
|
151
|
-
resolver: promptResolver(source.repository),
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
workflowId: promptWorkflow,
|
|
155
|
-
functionName: "developTicket",
|
|
156
|
-
effectLimit: 8,
|
|
157
|
-
handoffs: [],
|
|
158
|
-
submission: { maximumBytes: 65_536, fields: { prompt: "string" } },
|
|
159
|
-
resolver: promptResolver(source.repository),
|
|
160
|
-
},
|
|
161
|
-
...declarations.map(({ input, ...declaration }) => ({
|
|
162
|
-
...declaration,
|
|
163
|
-
submission: input,
|
|
164
|
-
resolver: declaredResolver(source, input),
|
|
165
|
-
})),
|
|
166
|
-
];
|
|
96
|
+
return declarations.map(({ input, ...declaration }) => ({
|
|
97
|
+
...declaration,
|
|
98
|
+
submission: input,
|
|
99
|
+
resolver: declaredResolver(source, input),
|
|
100
|
+
}));
|
|
167
101
|
}
|
|
168
|
-
function declaredResolver(source, contract) {
|
|
169
|
-
const tickets = hasTicketField(contract) ?
|
|
102
|
+
export function declaredResolver(source, contract) {
|
|
103
|
+
const tickets = hasTicketField(contract) ? ticketResolver(source) : undefined;
|
|
170
104
|
return {
|
|
171
105
|
id: "declared-input",
|
|
172
106
|
version: "1",
|
|
@@ -202,7 +136,7 @@ export async function ticketRegistrations(manifests, options) {
|
|
|
202
136
|
return { kind: "rejected", detail: refusal };
|
|
203
137
|
const registrations = [];
|
|
204
138
|
for (const row of options.table) {
|
|
205
|
-
const { functionName, capabilities =
|
|
139
|
+
const { functionName, capabilities = defaultCapabilities, ...registration } = row;
|
|
206
140
|
const manifest = manifests[functionName];
|
|
207
141
|
if (manifest === undefined)
|
|
208
142
|
return {
|
|
@@ -254,7 +188,7 @@ function registrationRefusal(table, commands) {
|
|
|
254
188
|
function commandRegistrationRefusal(row, commands) {
|
|
255
189
|
if (Object.hasOwn(row, "commandWaits") &&
|
|
256
190
|
!isCommandWaits(row.commandWaits, {
|
|
257
|
-
capabilities: row.capabilities ??
|
|
191
|
+
capabilities: row.capabilities ?? defaultCapabilities,
|
|
258
192
|
commands: row.commands ?? [],
|
|
259
193
|
}))
|
|
260
194
|
return `invalid-command-waits: ${row.workflowId}`;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type TurnEnd, type TurnProgress } from "./claude-turn.ts";
|
|
2
|
+
import { attemptCommand, type Attempts } from "./turn-attempts.ts";
|
|
3
|
+
declare function attemptEntry(attempts: Attempts, selected: ReturnType<typeof attemptCommand>): {
|
|
4
|
+
startedAt: string;
|
|
5
|
+
resumedAfter?: "daemon-restart";
|
|
6
|
+
attempt: number;
|
|
7
|
+
fallbackReason?: string;
|
|
8
|
+
budgetUsd?: number;
|
|
9
|
+
turnStartCommit: string | null;
|
|
10
|
+
model: string | null;
|
|
11
|
+
};
|
|
12
|
+
export declare function prepareAttempt(attempts: Attempts): Promise<{
|
|
13
|
+
entry: {
|
|
14
|
+
startedAt: string;
|
|
15
|
+
resumedAfter?: "daemon-restart";
|
|
16
|
+
attempt: number;
|
|
17
|
+
fallbackReason?: string;
|
|
18
|
+
budgetUsd?: number;
|
|
19
|
+
turnStartCommit: string | null;
|
|
20
|
+
model: string | null;
|
|
21
|
+
};
|
|
22
|
+
selected: {
|
|
23
|
+
fallbackReason?: string;
|
|
24
|
+
model: string | null;
|
|
25
|
+
budgetUsd?: number;
|
|
26
|
+
command: readonly string[];
|
|
27
|
+
};
|
|
28
|
+
folder: string;
|
|
29
|
+
} | undefined>;
|
|
30
|
+
export declare function attemptJournal(attempts: Attempts, entry: ReturnType<typeof attemptEntry>): {
|
|
31
|
+
progress: (snapshot: TurnProgress) => Promise<void>;
|
|
32
|
+
running: (identifier: number | null) => Promise<void>;
|
|
33
|
+
};
|
|
34
|
+
export declare function recordedEnd(attempts: Attempts, completion: {
|
|
35
|
+
readonly processId: number | null;
|
|
36
|
+
readonly end: TurnEnd;
|
|
37
|
+
}): Promise<void>;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
import { readRecord, writeRecord } from "./operation-journal.js";
|
|
4
|
+
import { capacityRefusal, } from "./claude-turn.js";
|
|
5
|
+
import { attemptCommand, attemptUsage, attemptCost, } from "./turn-attempts.js";
|
|
6
|
+
function written(folder, entry) {
|
|
7
|
+
return writeRecord(folder, { ...entry, permissions: "private" });
|
|
8
|
+
}
|
|
9
|
+
function attemptEntry(attempts, selected) {
|
|
10
|
+
const attempt = attempts.ends.length + 1;
|
|
11
|
+
const resume = attempts.options.resume;
|
|
12
|
+
return {
|
|
13
|
+
...attempts.options.metadata,
|
|
14
|
+
turnStartCommit: attempts.turnStartCommit,
|
|
15
|
+
model: selected.model,
|
|
16
|
+
...(selected.budgetUsd === undefined
|
|
17
|
+
? {}
|
|
18
|
+
: { budgetUsd: selected.budgetUsd }),
|
|
19
|
+
...(selected.fallbackReason === undefined
|
|
20
|
+
? {}
|
|
21
|
+
: { fallbackReason: selected.fallbackReason }),
|
|
22
|
+
attempt,
|
|
23
|
+
...(resume !== undefined && attempt === resume.ends.length + 1
|
|
24
|
+
? { resumedAfter: "daemon-restart" }
|
|
25
|
+
: {}),
|
|
26
|
+
startedAt: attempt === 1 ? attempts.startedAt : new Date().toISOString(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export async function prepareAttempt(attempts) {
|
|
30
|
+
const selected = attemptCommand(attempts, attempts.ends.length + 1);
|
|
31
|
+
if (selected.budgetUsd === 0) {
|
|
32
|
+
attempts.failure = "claude-turn-budget-exhausted";
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const entry = attemptEntry(attempts, selected);
|
|
36
|
+
const folder = entry.attempt === 1
|
|
37
|
+
? attempts.start.folder
|
|
38
|
+
: join(attempts.start.folder, `attempt-${String(entry.attempt)}`);
|
|
39
|
+
await written(attempts.start.folder, {
|
|
40
|
+
name: "started.json",
|
|
41
|
+
value: {
|
|
42
|
+
...entry,
|
|
43
|
+
startedAt: attempts.startedAt,
|
|
44
|
+
phase: "starting",
|
|
45
|
+
processId: null,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
await mkdir(folder, { recursive: true, mode: 0o700 });
|
|
49
|
+
return { entry, selected, folder };
|
|
50
|
+
}
|
|
51
|
+
function progressFields(attempts, progress) {
|
|
52
|
+
const result = progress?.result;
|
|
53
|
+
const end = { exitCode: null, result };
|
|
54
|
+
if (result === undefined)
|
|
55
|
+
return {
|
|
56
|
+
sessionId: progress?.sessionId ?? null,
|
|
57
|
+
sessionCostUsd: null,
|
|
58
|
+
costUsd: null,
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
sessionId: progress?.sessionId ?? null,
|
|
62
|
+
sessionCostUsd: result.costUsd,
|
|
63
|
+
costUsd: attemptCost([...attempts.ends, end], attempts.ends.length),
|
|
64
|
+
usage: result.usage,
|
|
65
|
+
turns: result.turns,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export function attemptJournal(attempts, entry) {
|
|
69
|
+
let saved = Promise.resolve();
|
|
70
|
+
let progress;
|
|
71
|
+
let processId = null;
|
|
72
|
+
const save = () => {
|
|
73
|
+
const value = {
|
|
74
|
+
...entry,
|
|
75
|
+
phase: "running",
|
|
76
|
+
processId,
|
|
77
|
+
...progressFields(attempts, progress),
|
|
78
|
+
};
|
|
79
|
+
saved = saved.then(() => written(attempts.start.folder, {
|
|
80
|
+
name: `attempt-${String(entry.attempt)}.json`,
|
|
81
|
+
value,
|
|
82
|
+
}));
|
|
83
|
+
return saved;
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
progress: (snapshot) => {
|
|
87
|
+
progress = snapshot;
|
|
88
|
+
return save();
|
|
89
|
+
},
|
|
90
|
+
running: async (identifier) => {
|
|
91
|
+
processId = identifier;
|
|
92
|
+
await written(attempts.start.folder, {
|
|
93
|
+
name: "started.json",
|
|
94
|
+
value: {
|
|
95
|
+
...entry,
|
|
96
|
+
startedAt: attempts.startedAt,
|
|
97
|
+
phase: "running",
|
|
98
|
+
processId,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
await save();
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export async function recordedEnd(attempts, completion) {
|
|
106
|
+
const { start, ends, options } = attempts;
|
|
107
|
+
const { processId, end } = completion;
|
|
108
|
+
const attempt = ends.length;
|
|
109
|
+
const selected = attemptCommand(attempts, attempt);
|
|
110
|
+
const name = `attempt-${String(attempt)}.json`;
|
|
111
|
+
const record = await readRecord(start.folder, name);
|
|
112
|
+
const started = typeof record === "object" && record !== null ? record : {};
|
|
113
|
+
const startedAt = Reflect.get(started, "startedAt");
|
|
114
|
+
if (typeof startedAt !== "string")
|
|
115
|
+
throw new Error("attempt-start-time-missing");
|
|
116
|
+
await written(start.folder, {
|
|
117
|
+
name,
|
|
118
|
+
value: {
|
|
119
|
+
...started,
|
|
120
|
+
...options.metadata,
|
|
121
|
+
turnStartCommit: attempts.turnStartCommit,
|
|
122
|
+
...attemptUsage(attempts, { end, attempt }),
|
|
123
|
+
...(selected.fallbackReason === undefined
|
|
124
|
+
? {}
|
|
125
|
+
: { fallbackReason: selected.fallbackReason }),
|
|
126
|
+
attempt,
|
|
127
|
+
processId,
|
|
128
|
+
startedAt,
|
|
129
|
+
endedAt: new Date().toISOString(),
|
|
130
|
+
exitCode: end.exitCode,
|
|
131
|
+
capacity: capacityRefusal(end.result),
|
|
132
|
+
sessionCostUsd: end.result?.costUsd ?? null,
|
|
133
|
+
turns: end.result?.turns ?? null,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { usageOf, type WorkerAttemptUsage } from "../worker-usage.ts";
|
|
2
|
+
import { type TokenPrices } from "../token-prices.ts";
|
|
3
|
+
import { type TurnEnd, type TurnStart } from "./claude-turn.ts";
|
|
4
|
+
import type { TurnResume } from "./claude-recovery.ts";
|
|
5
|
+
export interface RetrySettings {
|
|
6
|
+
readonly tokenPrices?: TokenPrices;
|
|
7
|
+
readonly fallbackModels?: readonly string[];
|
|
8
|
+
readonly retryDelay?: (milliseconds: number, signal: AbortSignal) => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export interface RetryOptions extends RetrySettings {
|
|
11
|
+
readonly resume?: TurnResume;
|
|
12
|
+
readonly stopping?: AbortController;
|
|
13
|
+
readonly stopMilliseconds?: number;
|
|
14
|
+
readonly metadata: Readonly<Record<string, unknown>>;
|
|
15
|
+
}
|
|
16
|
+
export interface Attempts {
|
|
17
|
+
failure?: string;
|
|
18
|
+
readonly start: TurnStart;
|
|
19
|
+
readonly turnStartCommit: string | null;
|
|
20
|
+
readonly startedAt: string;
|
|
21
|
+
readonly options: RetryOptions;
|
|
22
|
+
readonly stopping: AbortController;
|
|
23
|
+
readonly grace: {
|
|
24
|
+
milliseconds: number;
|
|
25
|
+
};
|
|
26
|
+
readonly ends: TurnEnd[];
|
|
27
|
+
}
|
|
28
|
+
export declare function total(values: readonly (number | null)[]): number | null;
|
|
29
|
+
export declare function combined(attempts: Attempts): TurnEnd;
|
|
30
|
+
export declare function attemptCommand(attempts: Attempts, attempt: number): {
|
|
31
|
+
fallbackReason?: string;
|
|
32
|
+
model: string | null;
|
|
33
|
+
budgetUsd?: number;
|
|
34
|
+
command: readonly string[];
|
|
35
|
+
};
|
|
36
|
+
export declare function attemptUsage(attempts: Attempts, completion: {
|
|
37
|
+
readonly end: TurnEnd;
|
|
38
|
+
readonly attempt: number;
|
|
39
|
+
}): WorkerAttemptUsage;
|
|
40
|
+
export declare function sessionCost(ends: readonly TurnEnd[]): number | null;
|
|
41
|
+
export declare function attemptCost(ends: readonly TurnEnd[], index: number): number | null;
|
|
42
|
+
export declare function attemptEstimate(attempts: Attempts, result: {
|
|
43
|
+
readonly end: TurnEnd;
|
|
44
|
+
readonly model: string | null;
|
|
45
|
+
readonly usage: ReturnType<typeof usageOf>;
|
|
46
|
+
}): number | null;
|
|
47
|
+
export declare function kindOf(attempts: Attempts, end: TurnEnd): import("../worker-usage.ts").RetryKind | null;
|