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,166 @@
|
|
|
1
|
+
import { priceOf, sumUsage, usageOf, } from "../worker-usage.js";
|
|
2
|
+
import { commandModel, replaceCommandModel, estimatedCostUsd, priceForModel, } from "../token-prices.js";
|
|
3
|
+
import { capacityRefusal, retryKind, } from "./claude-turn.js";
|
|
4
|
+
import { nativeHarness } from "../harness-defaults.js";
|
|
5
|
+
import { profileCommand } from "../turn-profile.js";
|
|
6
|
+
export function total(values) {
|
|
7
|
+
if (values.includes(null))
|
|
8
|
+
return null;
|
|
9
|
+
return priceOf(values.reduce((sum, value) => sum + (value ?? 0), 0));
|
|
10
|
+
}
|
|
11
|
+
export function combined(attempts) {
|
|
12
|
+
const { ends } = attempts;
|
|
13
|
+
const last = ends.at(-1) ?? { exitCode: null, result: undefined };
|
|
14
|
+
const result = last.result ?? ends.find((end) => end.result)?.result;
|
|
15
|
+
if (result === undefined)
|
|
16
|
+
return last;
|
|
17
|
+
return {
|
|
18
|
+
...last,
|
|
19
|
+
result: {
|
|
20
|
+
...result,
|
|
21
|
+
attempts: ends.map((end, index) => attemptUsage(attempts, { end, attempt: index + 1 })),
|
|
22
|
+
...(last.result === undefined ? { failed: true, text: "" } : {}),
|
|
23
|
+
usage: sumUsage(ends.map((end) => end.result?.usage ?? usageOf(null))),
|
|
24
|
+
costUsd: (attempts.start.worker ?? "claude") === "claude"
|
|
25
|
+
? sessionCost(ends)
|
|
26
|
+
: total(ends.map((end) => end.result?.costUsd ?? null)),
|
|
27
|
+
turns: total(ends.map((end) => end.result?.turns ?? null)),
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function selectedCommand(attempts, attempt) {
|
|
32
|
+
const { start, options } = attempts;
|
|
33
|
+
const original = start.command;
|
|
34
|
+
const previous = attempts.ends[attempt - 2];
|
|
35
|
+
if (options.resume !== undefined &&
|
|
36
|
+
attempt === options.resume.ends.length + 1) {
|
|
37
|
+
const model = previous?.result?.model;
|
|
38
|
+
return {
|
|
39
|
+
command: model == null ? original : replaceCommandModel(original, model),
|
|
40
|
+
fallback: undefined,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const ordinal = attempt -
|
|
44
|
+
attempts.ends
|
|
45
|
+
.slice(0, attempt - 1)
|
|
46
|
+
.filter((end) => end.resumedAfter !== undefined).length;
|
|
47
|
+
const fallback = previous !== undefined && kindOf(attempts, previous) === "capacity"
|
|
48
|
+
? options.fallbackModels?.[ordinal - 3]
|
|
49
|
+
: undefined;
|
|
50
|
+
return {
|
|
51
|
+
command: fallback === undefined
|
|
52
|
+
? original
|
|
53
|
+
: replaceCommandModel(original, fallback),
|
|
54
|
+
fallback,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function attemptCommand(attempts, attempt) {
|
|
58
|
+
const { start } = attempts;
|
|
59
|
+
const { command: selected, fallback } = selectedCommand(attempts, attempt);
|
|
60
|
+
const budgetUsd = remainingBudget(attempts);
|
|
61
|
+
const command = budgetUsd === undefined
|
|
62
|
+
? selected
|
|
63
|
+
: profileCommand(selected, { maxBudgetUsd: budgetUsd });
|
|
64
|
+
const model = start.output === "text" ? null : commandModel(command);
|
|
65
|
+
return {
|
|
66
|
+
command,
|
|
67
|
+
...(budgetUsd === undefined ? {} : { budgetUsd }),
|
|
68
|
+
model,
|
|
69
|
+
...(fallback !== undefined && model === null
|
|
70
|
+
? { fallbackReason: "fallback-model-unavailable" }
|
|
71
|
+
: {}),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function attemptUsage(attempts, completion) {
|
|
75
|
+
const { end, attempt } = completion;
|
|
76
|
+
const result = end.result ?? {
|
|
77
|
+
model: null,
|
|
78
|
+
usage: usageOf(null),
|
|
79
|
+
costUsd: null,
|
|
80
|
+
pricingModel: undefined,
|
|
81
|
+
};
|
|
82
|
+
const model = result.model ?? attemptCommand(attempts, attempt).model;
|
|
83
|
+
const usage = result.usage;
|
|
84
|
+
return {
|
|
85
|
+
attempt,
|
|
86
|
+
...(end.resumedAfter === undefined
|
|
87
|
+
? {}
|
|
88
|
+
: { resumedAfter: end.resumedAfter }),
|
|
89
|
+
retryKind: kindOf(attempts, end),
|
|
90
|
+
model,
|
|
91
|
+
...(end.result?.models === undefined ? {} : { models: end.result.models }),
|
|
92
|
+
usage,
|
|
93
|
+
costUsd: (attempts.start.worker ?? "claude") === "claude"
|
|
94
|
+
? attemptCost(attempts.ends, attempt - 1)
|
|
95
|
+
: result.costUsd,
|
|
96
|
+
estimatedCostUsd: attemptEstimate(attempts, { end, model, usage }),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export function sessionCost(ends) {
|
|
100
|
+
const sessions = new Map();
|
|
101
|
+
ends.forEach((end, index) => {
|
|
102
|
+
sessions.set(end.result?.sessionId ?? index, end.result?.costUsd ?? null);
|
|
103
|
+
});
|
|
104
|
+
return total([...sessions.values()]);
|
|
105
|
+
}
|
|
106
|
+
export function attemptCost(ends, index) {
|
|
107
|
+
const result = ends[index]?.result;
|
|
108
|
+
if (result?.costUsd == null)
|
|
109
|
+
return null;
|
|
110
|
+
const earlier = result.sessionId === null
|
|
111
|
+
? undefined
|
|
112
|
+
: ends
|
|
113
|
+
.slice(0, index)
|
|
114
|
+
.findLast((end) => end.result?.sessionId === result.sessionId);
|
|
115
|
+
if (earlier === undefined)
|
|
116
|
+
return result.costUsd;
|
|
117
|
+
const cost = earlier.result?.costUsd;
|
|
118
|
+
return cost == null
|
|
119
|
+
? null
|
|
120
|
+
: priceOf(Number((result.costUsd - cost).toFixed(12)));
|
|
121
|
+
}
|
|
122
|
+
function remainingBudget(attempts) {
|
|
123
|
+
if ((attempts.start.worker ?? "claude") !== "claude" ||
|
|
124
|
+
attempts.start.output === "text")
|
|
125
|
+
return undefined;
|
|
126
|
+
const budget = commandBudget(attempts.start.command);
|
|
127
|
+
if (budget === undefined)
|
|
128
|
+
return undefined;
|
|
129
|
+
const spent = sessionCost(attempts.ends);
|
|
130
|
+
return Math.max(0, Number((budget - (spent ?? 0)).toFixed(12)));
|
|
131
|
+
}
|
|
132
|
+
function commandBudget(original) {
|
|
133
|
+
const separator = original.indexOf("--");
|
|
134
|
+
const command = separator < 0 ? original : original.slice(0, separator);
|
|
135
|
+
const index = command.findLastIndex((part) => part === "--max-budget-usd" || part.startsWith("--max-budget-usd="));
|
|
136
|
+
if (index < 0)
|
|
137
|
+
return undefined;
|
|
138
|
+
const argument = command[index] ?? "";
|
|
139
|
+
const budget = Number(argument.includes("=") ? argument.split("=")[1] : command[index + 1]);
|
|
140
|
+
if (!Number.isFinite(budget) || budget < 0)
|
|
141
|
+
return undefined;
|
|
142
|
+
return budget;
|
|
143
|
+
}
|
|
144
|
+
export function attemptEstimate(attempts, result) {
|
|
145
|
+
const worker = attempts.start.worker ?? "claude";
|
|
146
|
+
if (!nativeHarness(worker))
|
|
147
|
+
return null;
|
|
148
|
+
const models = result.end.result?.models;
|
|
149
|
+
const prices = attempts.options.tokenPrices ?? {};
|
|
150
|
+
return estimatedCostUsd(result.usage, worker === "gemini" && models !== undefined
|
|
151
|
+
? { worker, models, prices }
|
|
152
|
+
: {
|
|
153
|
+
worker,
|
|
154
|
+
price: priceForModel(prices, result.end.result?.pricingModel === undefined
|
|
155
|
+
? result.model
|
|
156
|
+
: result.end.result.pricingModel),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
export function kindOf(attempts, end) {
|
|
160
|
+
const { worker, output } = attempts.start;
|
|
161
|
+
if (output === "text" || !["claude", "codex"].includes(worker ?? "claude"))
|
|
162
|
+
return null;
|
|
163
|
+
if (worker !== "codex")
|
|
164
|
+
return end.retryKind ?? retryKind(end.result);
|
|
165
|
+
return capacityRefusal(end.result) ? "capacity" : null;
|
|
166
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { Recorded } from "./operation-journal.ts";
|
|
1
2
|
import { type WorkTree } from "./work-tree.ts";
|
|
2
|
-
import type
|
|
3
|
+
import { type StreamResult, type TurnEnd } from "./claude-turn.ts";
|
|
3
4
|
interface ReportLimits {
|
|
4
5
|
readonly summaryCharacters: number;
|
|
5
6
|
readonly resultBytes: number;
|
|
@@ -15,4 +16,10 @@ export declare function turnReport(start: {
|
|
|
15
16
|
readonly text?: string;
|
|
16
17
|
readonly reason?: string;
|
|
17
18
|
}>;
|
|
19
|
+
export declare function turnOutcome(running: Parameters<typeof turnReport>[0] & {
|
|
20
|
+
readonly stopping: {
|
|
21
|
+
readonly requested: boolean;
|
|
22
|
+
readonly stoppedBy?: "daemon-shutdown";
|
|
23
|
+
};
|
|
24
|
+
}, end: TurnEnd): Promise<Recorded>;
|
|
18
25
|
export {};
|
|
@@ -2,6 +2,7 @@ import { ticketStatus } from "../ticket-status.js";
|
|
|
2
2
|
import { reviewReport } from "../review-subject.js";
|
|
3
3
|
import { sumPrices } from "../worker-usage.js";
|
|
4
4
|
import { changes } from "./work-tree.js";
|
|
5
|
+
import { succeeded } from "./claude-turn.js";
|
|
5
6
|
function tailReport(fields, limit) {
|
|
6
7
|
let text = JSON.stringify({ ...fields, summary: "" });
|
|
7
8
|
if (Buffer.byteLength(text) > limit)
|
|
@@ -77,3 +78,41 @@ export async function turnReport(start, result) {
|
|
|
77
78
|
turns: result.turns,
|
|
78
79
|
}, { ...harness, limit: limits.resultBytes });
|
|
79
80
|
}
|
|
81
|
+
export async function turnOutcome(running, end) {
|
|
82
|
+
const endedAt = new Date().toISOString();
|
|
83
|
+
if (end.stopped === true && running.stopping.stoppedBy === "daemon-shutdown")
|
|
84
|
+
return stoppedOutcome(running.stopping, endedAt);
|
|
85
|
+
const { result } = end;
|
|
86
|
+
const report = result === undefined
|
|
87
|
+
? {}
|
|
88
|
+
: await turnReport({
|
|
89
|
+
tree: running.tree,
|
|
90
|
+
harness: running.harness,
|
|
91
|
+
limits: running.limits,
|
|
92
|
+
}, result);
|
|
93
|
+
const { text, reason } = report;
|
|
94
|
+
if (succeeded(end) && reason === undefined && text !== undefined)
|
|
95
|
+
return { outcome: "completed", result: text, endedAt };
|
|
96
|
+
if (running.stopping.requested)
|
|
97
|
+
return stoppedOutcome(running.stopping, endedAt);
|
|
98
|
+
return failedOutcome(end, { ...report, endedAt });
|
|
99
|
+
}
|
|
100
|
+
function failedOutcome(end, report) {
|
|
101
|
+
const { text, reason, endedAt } = report;
|
|
102
|
+
return {
|
|
103
|
+
outcome: "failed",
|
|
104
|
+
reason: end.reason ?? reason ?? "claude-turn-failed",
|
|
105
|
+
...(end.detail === undefined ? {} : { detail: end.detail }),
|
|
106
|
+
...(text === undefined ? {} : { result: text }),
|
|
107
|
+
endedAt,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function stoppedOutcome(stopping, endedAt) {
|
|
111
|
+
return {
|
|
112
|
+
outcome: "stopped",
|
|
113
|
+
endedAt,
|
|
114
|
+
...(stopping.stoppedBy === undefined
|
|
115
|
+
? {}
|
|
116
|
+
: { stoppedBy: stopping.stoppedBy }),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -25,3 +25,12 @@ export declare function treePlace(request: Pick<TreeRequest, "worktrees" | "targ
|
|
|
25
25
|
export declare function workTree(request: TreeRequest): Promise<WorkTree>;
|
|
26
26
|
export declare function treeFailure(error: unknown): string;
|
|
27
27
|
export declare function changes(tree: WorkTree): Promise<Changes>;
|
|
28
|
+
export declare function firstTurn(settings: {
|
|
29
|
+
readonly journal: string;
|
|
30
|
+
}, opening: {
|
|
31
|
+
readonly tree: WorkTree;
|
|
32
|
+
readonly intent: {
|
|
33
|
+
readonly target: string;
|
|
34
|
+
readonly runId: string;
|
|
35
|
+
};
|
|
36
|
+
}): Promise<WorkTree>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readRecord, writeRecord, operationKey } from "./operation-journal.js";
|
|
1
2
|
import { execFile, spawn } from "node:child_process";
|
|
2
3
|
import { existsSync } from "node:fs";
|
|
3
4
|
import { mkdir, realpath } from "node:fs/promises";
|
|
@@ -207,3 +208,22 @@ async function changeLines(call) {
|
|
|
207
208
|
.slice(0, lineLimit)
|
|
208
209
|
.map((line) => line.slice(0, lineLength));
|
|
209
210
|
}
|
|
211
|
+
export async function firstTurn(settings, opening) {
|
|
212
|
+
const { tree, intent } = opening;
|
|
213
|
+
const { target, runId } = intent;
|
|
214
|
+
const folder = target.startsWith("branch:")
|
|
215
|
+
? join(settings.journal, "runs", operationKey(runId), "targets")
|
|
216
|
+
: join(settings.journal, "targets");
|
|
217
|
+
const name = `${operationKey(target)}.json`;
|
|
218
|
+
const record = await readRecord(folder, name);
|
|
219
|
+
const startCommit = typeof record === "object" && record !== null
|
|
220
|
+
? Reflect.get(record, "startCommit")
|
|
221
|
+
: undefined;
|
|
222
|
+
if (typeof startCommit === "string")
|
|
223
|
+
return { ...tree, startCommit };
|
|
224
|
+
await writeRecord(folder, {
|
|
225
|
+
name,
|
|
226
|
+
value: { target, startCommit: tree.startCommit },
|
|
227
|
+
});
|
|
228
|
+
return tree;
|
|
229
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type HarnessSettings } from "../harness-defaults.ts";
|
|
2
|
+
import type { TurnProfile, TurnProfiles } from "../turn-profile.ts";
|
|
3
|
+
import type { ProtectedIntent } from "../broker/actor/delivery-intents.ts";
|
|
4
|
+
export interface WorkerProfiles extends HarnessSettings {
|
|
5
|
+
readonly profiles?: TurnProfiles;
|
|
6
|
+
readonly workflowId?: (runId: string) => Promise<string | undefined>;
|
|
7
|
+
}
|
|
8
|
+
export declare function selectedProfile(settings: WorkerProfiles, intent: ProtectedIntent): Promise<TurnProfile | string>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { turnHarness } from "../harness-defaults.js";
|
|
2
|
+
async function runWorkflow(settings, runId) {
|
|
3
|
+
try {
|
|
4
|
+
return await settings.workflowId?.(runId);
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function unsupportedProfile(profile, harness) {
|
|
11
|
+
if (harness.output === "text" && profile.model !== undefined)
|
|
12
|
+
return "profile-model-not-supported";
|
|
13
|
+
if (harness.worker === "claude")
|
|
14
|
+
return undefined;
|
|
15
|
+
return [
|
|
16
|
+
profile.allowedTools,
|
|
17
|
+
profile.permissionMode,
|
|
18
|
+
profile.maxBudgetUsd,
|
|
19
|
+
].some((value) => value !== undefined)
|
|
20
|
+
? "profile-needs-claude"
|
|
21
|
+
: undefined;
|
|
22
|
+
}
|
|
23
|
+
export async function selectedProfile(settings, intent) {
|
|
24
|
+
if (intent.method === "run-turn")
|
|
25
|
+
return {};
|
|
26
|
+
const name = intent.method.slice("run-turn:".length);
|
|
27
|
+
const profiles = settings.profiles ?? {};
|
|
28
|
+
const profile = Object.hasOwn(profiles, name) ? profiles[name] : undefined;
|
|
29
|
+
if (profile === undefined)
|
|
30
|
+
return "unknown-profile";
|
|
31
|
+
const unsupported = unsupportedProfile(profile, turnHarness(settings, profile));
|
|
32
|
+
if (unsupported !== undefined)
|
|
33
|
+
return unsupported;
|
|
34
|
+
if (profile.workflows === undefined)
|
|
35
|
+
return profile;
|
|
36
|
+
const workflowId = await runWorkflow(settings, intent.runId);
|
|
37
|
+
if (workflowId === undefined || !profile.workflows.includes(workflowId))
|
|
38
|
+
return "profile-not-allowed";
|
|
39
|
+
return profile;
|
|
40
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { WorkerPort } from "../service/worker-port.ts";
|
|
2
2
|
import type { DeclaredWorkflow } from "../workflow-declaration.ts";
|
|
3
|
+
import type { SchedulePort } from "../schedule-progress.ts";
|
|
3
4
|
interface ScheduleSettings {
|
|
4
5
|
readonly directory: string;
|
|
5
6
|
readonly runtimeDirectory: string;
|
|
6
7
|
readonly declarations?: readonly DeclaredWorkflow[];
|
|
7
8
|
readonly scheduleClock?: () => number;
|
|
9
|
+
readonly scheduleObservationClock?: () => number;
|
|
8
10
|
readonly submit: WorkerPort["submit"];
|
|
9
11
|
}
|
|
10
12
|
export declare function scheduledDeclarations(settings: {
|
|
@@ -23,5 +25,6 @@ export declare function scheduledDeclarations(settings: {
|
|
|
23
25
|
};
|
|
24
26
|
export declare function workflowSchedules(settings: ScheduleSettings): Promise<{
|
|
25
27
|
readonly stop: () => Promise<void>;
|
|
28
|
+
readonly request: SchedulePort;
|
|
26
29
|
}>;
|
|
27
30
|
export {};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { setTimeout as delay } from "node:timers/promises";
|
|
3
3
|
import { mutate } from "../cli/client.js";
|
|
4
|
-
import { record } from "../protocol.js";
|
|
4
|
+
import { record, } from "../protocol.js";
|
|
5
5
|
import { runIdentity } from "../service/submission-handler.js";
|
|
6
6
|
import { configuredSchedules } from "../workflow-schedule.js";
|
|
7
7
|
import { declaredInput } from "./declared-input.js";
|
|
8
|
+
import { finishedScheduleRun } from "../schedule-progress.js";
|
|
9
|
+
import { scheduleControl } from "./schedule-control.js";
|
|
8
10
|
import { scheduleJournal, } from "./schedule-journal.js";
|
|
9
11
|
export function scheduledDeclarations(settings, limits) {
|
|
10
12
|
try {
|
|
@@ -60,6 +62,8 @@ async function pending(settings, entry) {
|
|
|
60
62
|
const { pending: planned, ...rest } = progress;
|
|
61
63
|
if (planned === undefined)
|
|
62
64
|
return progress;
|
|
65
|
+
if (progress.state !== "active")
|
|
66
|
+
throw new Error("invalid-schedule-state");
|
|
63
67
|
const identity = commandId(workflowId, progress.lastSlot);
|
|
64
68
|
if (planned.kind === "skip") {
|
|
65
69
|
const receipt = await settings.submit({
|
|
@@ -86,10 +90,7 @@ async function pending(settings, entry) {
|
|
|
86
90
|
throw new Error(reply.error.code);
|
|
87
91
|
return { ...rest, activeCommandId: identity };
|
|
88
92
|
}
|
|
89
|
-
async function
|
|
90
|
-
if (progress.activeCommandId === undefined)
|
|
91
|
-
return undefined;
|
|
92
|
-
const runId = runIdentity(progress.activeCommandId);
|
|
93
|
+
async function scheduledRunStatus(settings, runId) {
|
|
93
94
|
const reply = await request(settings, {
|
|
94
95
|
operation: "status",
|
|
95
96
|
parameters: { runId },
|
|
@@ -99,45 +100,76 @@ async function active(settings, progress) {
|
|
|
99
100
|
const run = record(reply.result["run"]);
|
|
100
101
|
if (typeof run?.["state"] !== "string")
|
|
101
102
|
throw new Error("invalid-schedule-run");
|
|
103
|
+
return run;
|
|
104
|
+
}
|
|
105
|
+
async function active(settings, progress) {
|
|
106
|
+
if (progress.activeCommandId === undefined)
|
|
107
|
+
return progress;
|
|
108
|
+
const now = (settings.scheduleClock ?? Date.now)();
|
|
109
|
+
const observedAt = settings.observationClock();
|
|
110
|
+
const next = settings.nextObservations.get(progress.activeCommandId) ?? 0;
|
|
111
|
+
const due = progress.state === "active" && now >= progress.nextAt;
|
|
112
|
+
if (!due && observedAt < next)
|
|
113
|
+
return progress;
|
|
114
|
+
const runId = runIdentity(progress.activeCommandId);
|
|
115
|
+
const run = await scheduledRunStatus(settings, runId);
|
|
116
|
+
settings.nextObservations.set(progress.activeCommandId, observedAt + 5000);
|
|
102
117
|
if (run["condition"] === "cancelling")
|
|
103
|
-
return
|
|
104
|
-
|
|
118
|
+
return progress;
|
|
119
|
+
if (!["completed", "failed", "cancelled"].includes(String(run["state"])))
|
|
120
|
+
return progress;
|
|
121
|
+
settings.nextObservations.delete(progress.activeCommandId);
|
|
122
|
+
return finishedScheduleRun(progress, {
|
|
123
|
+
runId,
|
|
124
|
+
status: run,
|
|
125
|
+
now: (settings.scheduleClock ?? Date.now)(),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
async function advance(settings, declaration) {
|
|
129
|
+
settings.signal.throwIfAborted();
|
|
130
|
+
const { workflowId, schedule } = declaration;
|
|
131
|
+
if (schedule === undefined)
|
|
132
|
+
return;
|
|
133
|
+
let progress = settings.journal.entries.get(workflowId);
|
|
134
|
+
if (progress === undefined)
|
|
135
|
+
throw new Error("missing-schedule-state");
|
|
136
|
+
if (progress.pending !== undefined) {
|
|
137
|
+
progress = await pending(settings, { workflowId, progress });
|
|
138
|
+
settings.journal.entries.set(workflowId, progress);
|
|
139
|
+
await settings.journal.save();
|
|
140
|
+
if (settings.now < progress.nextAt)
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const observed = await active(settings, progress);
|
|
144
|
+
if (observed !== progress) {
|
|
145
|
+
progress = observed;
|
|
146
|
+
settings.journal.entries.set(workflowId, progress);
|
|
147
|
+
await settings.journal.save();
|
|
148
|
+
}
|
|
149
|
+
if (progress.state !== "active" || settings.now < progress.nextAt)
|
|
150
|
+
return;
|
|
151
|
+
const runId = progress.activeCommandId === undefined
|
|
105
152
|
? undefined
|
|
106
|
-
:
|
|
153
|
+
: runIdentity(progress.activeCommandId);
|
|
154
|
+
const planned = runId === undefined
|
|
155
|
+
? { kind: "run", input: schedule.input }
|
|
156
|
+
: { kind: "skip", runId };
|
|
157
|
+
const elapsed = Math.floor((settings.now - progress.nextAt) / (schedule.everySeconds * 1000)) + 1;
|
|
158
|
+
progress = {
|
|
159
|
+
...progress,
|
|
160
|
+
lastSlot: progress.lastSlot + elapsed,
|
|
161
|
+
nextAt: progress.nextAt + elapsed * schedule.everySeconds * 1000,
|
|
162
|
+
pending: planned,
|
|
163
|
+
};
|
|
164
|
+
settings.journal.entries.set(workflowId, progress);
|
|
165
|
+
await settings.journal.save();
|
|
166
|
+
settings.journal.entries.set(workflowId, await pending(settings, { workflowId, progress }));
|
|
167
|
+
await settings.journal.save();
|
|
107
168
|
}
|
|
108
169
|
async function tick(settings, journal) {
|
|
109
170
|
const now = (settings.scheduleClock ?? Date.now)();
|
|
110
|
-
for (const declaration of settings.declarations ?? [])
|
|
111
|
-
settings
|
|
112
|
-
const { workflowId, schedule } = declaration;
|
|
113
|
-
if (schedule === undefined)
|
|
114
|
-
continue;
|
|
115
|
-
let progress = journal.entries.get(workflowId);
|
|
116
|
-
if (progress === undefined)
|
|
117
|
-
throw new Error("missing-schedule-state");
|
|
118
|
-
if (progress.pending !== undefined) {
|
|
119
|
-
progress = await pending(settings, { workflowId, progress });
|
|
120
|
-
journal.entries.set(workflowId, progress);
|
|
121
|
-
await journal.save();
|
|
122
|
-
}
|
|
123
|
-
if (now < progress.nextAt)
|
|
124
|
-
continue;
|
|
125
|
-
const runId = await active(settings, progress);
|
|
126
|
-
const planned = runId === undefined
|
|
127
|
-
? { kind: "run", input: schedule.input }
|
|
128
|
-
: { kind: "skip", runId };
|
|
129
|
-
const elapsed = Math.floor((now - progress.nextAt) / (schedule.everySeconds * 1000)) + 1;
|
|
130
|
-
progress = {
|
|
131
|
-
...progress,
|
|
132
|
-
lastSlot: progress.lastSlot + elapsed,
|
|
133
|
-
nextAt: progress.nextAt + elapsed * schedule.everySeconds * 1000,
|
|
134
|
-
pending: planned,
|
|
135
|
-
};
|
|
136
|
-
journal.entries.set(workflowId, progress);
|
|
137
|
-
await journal.save();
|
|
138
|
-
journal.entries.set(workflowId, await pending(settings, { workflowId, progress }));
|
|
139
|
-
await journal.save();
|
|
140
|
-
}
|
|
171
|
+
for (const declaration of settings.declarations ?? [])
|
|
172
|
+
await advance({ ...settings, journal, now }, declaration);
|
|
141
173
|
}
|
|
142
174
|
async function initialized(settings) {
|
|
143
175
|
const journal = await scheduleJournal(join(settings.directory, "state"));
|
|
@@ -150,36 +182,57 @@ async function initialized(settings) {
|
|
|
150
182
|
if (prior?.everySeconds === schedule.everySeconds)
|
|
151
183
|
continue;
|
|
152
184
|
journal.entries.set(workflowId, {
|
|
153
|
-
...prior
|
|
185
|
+
...(prior ?? {
|
|
186
|
+
lastSlot: 0,
|
|
187
|
+
state: "active",
|
|
188
|
+
failuresInRow: 0,
|
|
189
|
+
failedRunIds: [],
|
|
190
|
+
}),
|
|
154
191
|
everySeconds: schedule.everySeconds,
|
|
155
|
-
lastSlot: prior?.lastSlot ?? 0,
|
|
156
192
|
nextAt: now + schedule.everySeconds * 1000,
|
|
157
193
|
});
|
|
158
194
|
}
|
|
159
|
-
|
|
195
|
+
if (settings.declarations?.some((entry) => entry.schedule !== undefined))
|
|
196
|
+
await journal.save();
|
|
160
197
|
return journal;
|
|
161
198
|
}
|
|
162
199
|
export async function workflowSchedules(settings) {
|
|
163
|
-
|
|
164
|
-
return { stop: () => Promise.resolve() };
|
|
200
|
+
const declarations = settings.declarations ?? [];
|
|
165
201
|
const journal = await initialized(settings);
|
|
166
202
|
const stopping = new AbortController();
|
|
167
203
|
const stopped = () => stopping.signal.aborted;
|
|
204
|
+
const context = {
|
|
205
|
+
...settings,
|
|
206
|
+
signal: stopping.signal,
|
|
207
|
+
nextObservations: new Map(),
|
|
208
|
+
observationClock: settings.scheduleObservationClock ?? (() => performance.now()),
|
|
209
|
+
};
|
|
210
|
+
const control = scheduleControl({
|
|
211
|
+
journal,
|
|
212
|
+
declarations,
|
|
213
|
+
clock: settings.scheduleClock ?? Date.now,
|
|
214
|
+
signal: stopping.signal,
|
|
215
|
+
recoverPending: (entry) => pending(context, entry),
|
|
216
|
+
tick: () => tick(context, journal),
|
|
217
|
+
});
|
|
168
218
|
const running = (async () => {
|
|
169
219
|
while (!stopped()) {
|
|
170
220
|
await delay(250, undefined, { signal: stopping.signal }).catch(() => undefined);
|
|
171
221
|
if (stopped())
|
|
172
222
|
return;
|
|
173
|
-
await tick(
|
|
223
|
+
await control.tick();
|
|
174
224
|
}
|
|
175
225
|
})().catch((error) => {
|
|
176
226
|
if (!stopped())
|
|
177
227
|
process.stderr.write(`Schedules stopped: ${String(error)}\n`);
|
|
228
|
+
stopping.abort();
|
|
178
229
|
});
|
|
179
230
|
return {
|
|
231
|
+
request: control.request,
|
|
180
232
|
stop: async () => {
|
|
181
233
|
stopping.abort();
|
|
182
234
|
await running;
|
|
235
|
+
await control.drain();
|
|
183
236
|
},
|
|
184
237
|
};
|
|
185
238
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function recordedProcessDirectory(processId: number): Promise<string | undefined>;
|
|
2
|
+
interface StopRequest {
|
|
3
|
+
readonly directory: string;
|
|
4
|
+
readonly folder: string;
|
|
5
|
+
readonly signal: AbortSignal;
|
|
6
|
+
readonly stopMilliseconds?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function stopTreeProcesses(request: StopRequest): Promise<boolean>;
|
|
9
|
+
export {};
|