cloving 1.2.0 → 1.3.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 +525 -185
- package/dist/src/agent-harness-shell.d.ts +12 -0
- package/dist/src/agent-harness-shell.js +90 -0
- package/dist/src/agent-harness-versions.d.ts +8 -0
- package/dist/src/agent-harness-versions.js +49 -0
- package/dist/src/agent-harnesses.d.ts +4 -0
- package/dist/src/agent-harnesses.js +13 -0
- package/dist/src/authority/commands.d.ts +1 -0
- package/dist/src/authority/commands.js +24 -4
- package/dist/src/authority/gate-reduction.d.ts +1 -0
- package/dist/src/authority/gate-reduction.js +18 -3
- package/dist/src/authority/gates.d.ts +2 -1
- package/dist/src/authority/gates.js +5 -1
- package/dist/src/authority/operations.js +1 -1
- package/dist/src/authority/reduction.d.ts +1 -1
- package/dist/src/authority/reduction.js +6 -1
- package/dist/src/authority/status-input.d.ts +1 -1
- package/dist/src/authority/status.d.ts +1 -0
- package/dist/src/authority/status.js +27 -3
- package/dist/src/authority/transitions.d.ts +5 -5
- package/dist/src/authority/transitions.js +6 -0
- package/dist/src/broker/actor/authority-delivery.js +1 -0
- package/dist/src/broker/actor/authority-queries.d.ts +7 -1
- package/dist/src/broker/actor/authority-queries.js +17 -2
- package/dist/src/broker/actor/authority-receipts.js +11 -1
- package/dist/src/broker/actor/authority-session.js +2 -2
- package/dist/src/broker/actor/authority-settings.js +4 -0
- package/dist/src/broker/actor/delivery-intents.d.ts +2 -1
- package/dist/src/broker/actor/delivery-intents.js +4 -4
- package/dist/src/broker/actor/effect-delivery.js +4 -1
- package/dist/src/broker/actor/effect-queries.d.ts +1 -0
- package/dist/src/broker/actor/effect-queries.js +7 -0
- package/dist/src/broker/actor/pin-check.js +1 -1
- package/dist/src/broker/actor/stop-plan.js +1 -1
- package/dist/src/broker/actor/turn-state.js +1 -1
- package/dist/src/broker/authority-admission.js +22 -9
- package/dist/src/broker/authority-restore.js +1 -0
- package/dist/src/broker/decision-restore.js +4 -4
- package/dist/src/broker/socket-address.d.ts +3 -1
- package/dist/src/broker/socket-address.js +2 -2
- package/dist/src/cli/arguments.d.ts +11 -13
- package/dist/src/cli/arguments.js +37 -107
- package/dist/src/cli/command-effects.d.ts +7 -0
- package/dist/src/cli/command-effects.js +39 -0
- package/dist/src/cli/commands.js +11 -4
- package/dist/src/cli/config-arguments.d.ts +11 -0
- package/dist/src/cli/config-arguments.js +59 -0
- package/dist/src/cli/config-command.d.ts +2 -0
- package/dist/src/cli/config-command.js +105 -0
- package/dist/src/cli/config-file.d.ts +3 -0
- package/dist/src/cli/config-file.js +50 -0
- package/dist/src/cli/config-lock.d.ts +7 -0
- package/dist/src/cli/config-lock.js +61 -0
- package/dist/src/cli/config-notice.d.ts +5 -0
- package/dist/src/cli/config-notice.js +46 -0
- package/dist/src/cli/doctor-github.d.ts +9 -0
- package/dist/src/cli/doctor-github.js +138 -0
- package/dist/src/cli/doctor-harness.d.ts +13 -0
- package/dist/src/cli/doctor-harness.js +242 -0
- package/dist/src/cli/doctor-local.d.ts +18 -0
- package/dist/src/cli/doctor-local.js +77 -0
- package/dist/src/cli/doctor-runtime.d.ts +15 -0
- package/dist/src/cli/doctor-runtime.js +176 -0
- package/dist/src/cli/doctor-service.d.ts +1 -0
- package/dist/src/cli/doctor-service.js +11 -0
- package/dist/src/cli/doctor-workflows.d.ts +3 -0
- package/dist/src/cli/doctor-workflows.js +45 -0
- package/dist/src/cli/doctor.d.ts +15 -0
- package/dist/src/cli/doctor.js +190 -0
- package/dist/src/cli/github-ci-progress.d.ts +8 -0
- package/dist/src/cli/github-ci-progress.js +83 -0
- package/dist/src/cli/github-retry.d.ts +6 -0
- package/dist/src/cli/github-retry.js +47 -0
- package/dist/src/cli/harness-probes.d.ts +22 -0
- package/dist/src/cli/harness-probes.js +183 -0
- package/dist/src/cli/main.d.ts +7 -1
- package/dist/src/cli/main.js +39 -61
- package/dist/src/cli/phone-arguments.d.ts +14 -0
- package/dist/src/cli/phone-arguments.js +37 -0
- package/dist/src/cli/phone-client.d.ts +2 -0
- package/dist/src/cli/phone-client.js +32 -0
- package/dist/src/cli/phone-command.d.ts +13 -0
- package/dist/src/cli/phone-command.js +101 -0
- package/dist/src/cli/probe-command.d.ts +36 -0
- package/dist/src/cli/probe-command.js +59 -0
- package/dist/src/cli/project-settings.d.ts +4 -0
- package/dist/src/cli/project-settings.js +33 -0
- package/dist/src/cli/render.d.ts +2 -1
- package/dist/src/cli/render.js +44 -28
- package/dist/src/cli/report-github.d.ts +18 -0
- package/dist/src/cli/report-github.js +200 -0
- package/dist/src/cli/report.d.ts +17 -0
- package/dist/src/cli/report.js +192 -0
- package/dist/src/cli/repository-arguments.d.ts +5 -0
- package/dist/src/cli/repository-arguments.js +34 -0
- package/dist/src/cli/runtime-arguments.d.ts +4 -0
- package/dist/src/cli/runtime-arguments.js +15 -0
- package/dist/src/cli/runtime-directory.d.ts +6 -2
- package/dist/src/cli/runtime-directory.js +72 -14
- package/dist/src/cli/toolchain.d.ts +2 -0
- package/dist/src/cli/toolchain.js +22 -2
- package/dist/src/cli/usage-render.d.ts +5 -0
- package/dist/src/cli/usage-render.js +40 -1
- package/dist/src/cli/usage.d.ts +3 -3
- package/dist/src/cli/usage.js +70 -44
- package/dist/src/cli/watch-actions.d.ts +2 -0
- package/dist/src/cli/watch-actions.js +4 -4
- package/dist/src/cli/watch-board-view.d.ts +1 -1
- package/dist/src/cli/watch-board-view.js +8 -2
- package/dist/src/cli/watch-board.js +23 -10
- package/dist/src/cli/watch-card-forms.js +2 -2
- package/dist/src/cli/watch-client.d.ts +1 -1
- package/dist/src/cli/watch-client.js +7 -2
- package/dist/src/cli/watch-header-view.d.ts +27 -0
- package/dist/src/cli/watch-header-view.js +116 -0
- package/dist/src/cli/watch-model.js +17 -4
- package/dist/src/cli/watch-page.js +48 -12
- package/dist/src/cli/watch-phone-client.d.ts +1 -0
- package/dist/src/cli/watch-phone-client.js +84 -0
- package/dist/src/cli/watch-phone-view.d.ts +56 -0
- package/dist/src/cli/watch-phone-view.js +113 -0
- package/dist/src/cli/watch-phone.d.ts +5 -0
- package/dist/src/cli/watch-phone.js +53 -0
- package/dist/src/cli/watch-pull-request.d.ts +3 -1
- package/dist/src/cli/watch-pull-request.js +4 -0
- package/dist/src/cli/watch-render.d.ts +9 -1
- package/dist/src/cli/watch-server.js +23 -5
- package/dist/src/cli/watch-turn.d.ts +4 -1
- package/dist/src/cli/watch-turn.js +37 -1
- package/dist/src/cli/watch-usage.js +5 -1
- package/dist/src/cli/watch-view.d.ts +6 -20
- package/dist/src/cli/watch-view.js +14 -106
- package/dist/src/cli.js +106 -23
- package/dist/src/command-settings.d.ts +16 -0
- package/dist/src/command-settings.js +42 -0
- package/dist/src/configuration-change.d.ts +11 -0
- package/dist/src/configuration-change.js +62 -0
- package/dist/src/configuration-display.d.ts +21 -0
- package/dist/src/configuration-display.js +69 -0
- package/dist/src/configuration-schema.d.ts +49 -0
- package/dist/src/configuration-schema.js +159 -0
- package/dist/src/daemon/claude-turn.d.ts +19 -5
- package/dist/src/daemon/claude-turn.js +116 -53
- package/dist/src/daemon/claude-worker.d.ts +3 -3
- package/dist/src/daemon/claude-worker.js +65 -76
- package/dist/src/daemon/command-journal.d.ts +5 -0
- package/dist/src/daemon/command-journal.js +50 -0
- package/dist/src/daemon/command-process.d.ts +28 -0
- package/dist/src/daemon/command-process.js +164 -0
- package/dist/src/daemon/command-worker.d.ts +19 -0
- package/dist/src/daemon/command-worker.js +182 -0
- package/dist/src/daemon/cursor-stream.d.ts +4 -0
- package/dist/src/daemon/cursor-stream.js +154 -0
- package/dist/src/daemon/daemon-adapters.d.ts +58 -0
- package/dist/src/daemon/daemon-adapters.js +102 -0
- package/dist/src/daemon/daemon-loops.d.ts +14 -0
- package/dist/src/daemon/daemon-loops.js +39 -0
- package/dist/src/daemon/daemon.d.ts +2 -33
- package/dist/src/daemon/daemon.js +126 -132
- package/dist/src/daemon/declared-input.d.ts +3 -0
- package/dist/src/daemon/declared-input.js +9 -0
- package/dist/src/daemon/delivery-router.js +1 -1
- package/dist/src/daemon/gemini-stream.d.ts +4 -0
- package/dist/src/daemon/gemini-stream.js +129 -0
- package/dist/src/daemon/github-ci-rerun.d.ts +26 -0
- package/dist/src/daemon/github-ci-rerun.js +92 -0
- package/dist/src/daemon/github-ci.js +89 -8
- package/dist/src/daemon/github-merge-call.d.ts +2 -0
- package/dist/src/daemon/github-merge.js +7 -3
- package/dist/src/daemon/github-methods.js +10 -0
- package/dist/src/daemon/github-operation.d.ts +1 -0
- package/dist/src/daemon/github-retry.js +8 -44
- package/dist/src/daemon/issue-queue.d.ts +1 -0
- package/dist/src/daemon/issue-queue.js +3 -26
- package/dist/src/daemon/notify.js +2 -1
- package/dist/src/daemon/opencode-stream.d.ts +5 -0
- package/dist/src/daemon/opencode-stream.js +87 -0
- package/dist/src/daemon/queue-status.d.ts +4 -0
- package/dist/src/daemon/queue-status.js +38 -0
- package/dist/src/daemon/retry-worktree.d.ts +18 -0
- package/dist/src/daemon/retry-worktree.js +139 -0
- package/dist/src/daemon/retrying-turn.d.ts +2 -0
- package/dist/src/daemon/retrying-turn.js +108 -30
- package/dist/src/daemon/schedule-journal.d.ts +19 -0
- package/dist/src/daemon/schedule-journal.js +57 -0
- package/dist/src/daemon/text-output.d.ts +18 -0
- package/dist/src/daemon/text-output.js +44 -0
- package/dist/src/daemon/ticket-registration.d.ts +5 -7
- package/dist/src/daemon/ticket-registration.js +85 -40
- package/dist/src/daemon/turn-command.d.ts +10 -0
- package/dist/src/daemon/turn-command.js +33 -0
- package/dist/src/daemon/turn-report.d.ts +18 -0
- package/dist/src/daemon/turn-report.js +79 -0
- package/dist/src/daemon/work-tree.d.ts +2 -1
- package/dist/src/daemon/work-tree.js +15 -12
- package/dist/src/daemon/workflow-generations.d.ts +32 -0
- package/dist/src/daemon/workflow-generations.js +57 -0
- package/dist/src/daemon/workflow-replay.d.ts +16 -0
- package/dist/src/daemon/workflow-replay.js +58 -0
- package/dist/src/daemon/workflow-schedules.d.ts +27 -0
- package/dist/src/daemon/workflow-schedules.js +185 -0
- package/dist/src/decision-wait.d.ts +1 -0
- package/dist/src/decision-wait.js +3 -0
- package/dist/src/delivery-backoff.d.ts +1 -0
- package/dist/src/delivery-backoff.js +3 -0
- package/dist/src/doctor-report.d.ts +24 -0
- package/dist/src/doctor-report.js +70 -0
- package/dist/src/execution-pin.d.ts +5 -0
- package/dist/src/execution-pin.js +67 -4
- package/dist/src/grok-result.d.ts +12 -0
- package/dist/src/grok-result.js +61 -0
- package/dist/src/harness-defaults.d.ts +35 -0
- package/dist/src/harness-defaults.js +121 -0
- package/dist/src/host/decision-resume.d.ts +35 -0
- package/dist/src/host/decision-resume.js +86 -0
- package/dist/src/host/gate-bridge.d.ts +48 -0
- package/dist/src/host/gate-bridge.js +124 -0
- package/dist/src/host/run-wait.d.ts +26 -0
- package/dist/src/host/run-wait.js +37 -0
- package/dist/src/host/start-delivery.d.ts +1 -1
- package/dist/src/host/start-delivery.js +10 -3
- package/dist/src/host/step-bridge-host.d.ts +6 -20
- package/dist/src/host/step-bridge-host.js +44 -58
- package/dist/src/merge-request.d.ts +19 -0
- package/dist/src/merge-request.js +32 -0
- package/dist/src/phone/canonical-json.d.ts +1 -0
- package/dist/src/phone/canonical-json.js +16 -0
- package/dist/src/phone/phone-link.d.ts +7 -0
- package/dist/src/phone/phone-link.js +21 -0
- package/dist/src/phone/qr-data.d.ts +8 -0
- package/dist/src/phone/qr-data.js +133 -0
- package/dist/src/phone/qr-matrix.d.ts +6 -0
- package/dist/src/phone/qr-matrix.js +175 -0
- package/dist/src/phone/qr-penalty.d.ts +3 -0
- package/dist/src/phone/qr-penalty.js +67 -0
- package/dist/src/phone/qr-picture.d.ts +5 -0
- package/dist/src/phone/qr-picture.js +24 -0
- package/dist/src/private-config.schema.json +263 -0
- package/dist/src/project-config.schema.json +65 -0
- package/dist/src/project-settings.d.ts +19 -0
- package/dist/src/project-settings.js +131 -0
- package/dist/src/relay/answer-check.d.ts +24 -0
- package/dist/src/relay/answer-check.js +145 -0
- package/dist/src/relay/answer-inbox.d.ts +15 -0
- package/dist/src/relay/answer-inbox.js +173 -0
- package/dist/src/relay/daemon-keys.d.ts +16 -0
- package/dist/src/relay/daemon-keys.js +75 -0
- package/dist/src/relay/decision-check.d.ts +3 -0
- package/dist/src/relay/decision-check.js +77 -0
- package/dist/src/relay/decision-close.d.ts +6 -0
- package/dist/src/relay/decision-close.js +72 -0
- package/dist/src/relay/decision-envelopes.d.ts +29 -0
- package/dist/src/relay/decision-envelopes.js +93 -0
- package/dist/src/relay/decision-watch.d.ts +14 -0
- package/dist/src/relay/decision-watch.js +68 -0
- package/dist/src/relay/envelope-post.d.ts +8 -0
- package/dist/src/relay/envelope-post.js +71 -0
- package/dist/src/relay/envelope.d.ts +28 -0
- package/dist/src/relay/envelope.js +53 -0
- package/dist/src/relay/hpke.d.ts +25 -0
- package/dist/src/relay/hpke.js +108 -0
- package/dist/src/relay/pairing-checks.d.ts +42 -0
- package/dist/src/relay/pairing-checks.js +172 -0
- package/dist/src/relay/pairing-follow.d.ts +8 -0
- package/dist/src/relay/pairing-follow.js +95 -0
- package/dist/src/relay/pairing-session.d.ts +29 -0
- package/dist/src/relay/pairing-session.js +87 -0
- package/dist/src/relay/phone-delivery.d.ts +51 -0
- package/dist/src/relay/phone-delivery.js +139 -0
- package/dist/src/relay/phone-devices.d.ts +36 -0
- package/dist/src/relay/phone-devices.js +46 -0
- package/dist/src/relay/phone-inbox.d.ts +18 -0
- package/dist/src/relay/phone-inbox.js +17 -0
- package/dist/src/relay/phone-operations.d.ts +18 -0
- package/dist/src/relay/phone-operations.js +121 -0
- package/dist/src/relay/phone-outbox.d.ts +21 -0
- package/dist/src/relay/phone-outbox.js +25 -0
- package/dist/src/relay/phone-socket.d.ts +6 -0
- package/dist/src/relay/phone-socket.js +63 -0
- package/dist/src/relay/relay-control.d.ts +7 -0
- package/dist/src/relay/relay-control.js +24 -0
- package/dist/src/relay/relay-files.d.ts +2 -0
- package/dist/src/relay/relay-files.js +23 -0
- package/dist/src/relay/relay-heartbeat.d.ts +17 -0
- package/dist/src/relay/relay-heartbeat.js +81 -0
- package/dist/src/relay/relay-http.d.ts +14 -0
- package/dist/src/relay/relay-http.js +63 -0
- package/dist/src/relay/relay-registration.d.ts +15 -0
- package/dist/src/relay/relay-registration.js +63 -0
- package/dist/src/relay/relay-service.d.ts +25 -0
- package/dist/src/relay/relay-service.js +216 -0
- package/dist/src/relay/relay-store.d.ts +48 -0
- package/dist/src/relay/relay-store.js +131 -0
- package/dist/src/relay/sealed-post.d.ts +25 -0
- package/dist/src/relay/sealed-post.js +52 -0
- package/dist/src/relay.d.ts +1 -0
- package/dist/src/relay.js +8 -0
- package/dist/src/report-statistics.d.ts +50 -0
- package/dist/src/report-statistics.js +70 -0
- package/dist/src/report.d.ts +171 -0
- package/dist/src/report.js +119 -0
- package/dist/src/repository-identity.d.ts +4 -0
- package/dist/src/repository-identity.js +4 -0
- package/dist/src/review-subject.d.ts +41 -0
- package/dist/src/review-subject.js +91 -0
- package/dist/src/run-wait.d.ts +23 -0
- package/dist/src/run-wait.js +20 -0
- package/dist/src/service/active-tickets.d.ts +4 -0
- package/dist/src/service/active-tickets.js +49 -0
- package/dist/src/service/command-effects.d.ts +4 -0
- package/dist/src/service/command-effects.js +16 -0
- package/dist/src/service/decision-handler.d.ts +4 -0
- package/dist/src/service/decision-handler.js +39 -4
- package/dist/src/service/gateway.js +18 -3
- package/dist/src/service/registry.d.ts +16 -3
- package/dist/src/service/registry.js +34 -3
- package/dist/src/service/replies.d.ts +9 -1
- package/dist/src/service/replies.js +12 -0
- package/dist/src/service/run-handlers.js +14 -55
- package/dist/src/service/run-metadata.d.ts +8 -0
- package/dist/src/service/run-metadata.js +53 -0
- package/dist/src/service/run-waits.d.ts +3 -0
- package/dist/src/service/run-waits.js +41 -0
- package/dist/src/service/service.d.ts +18 -2
- package/dist/src/service/service.js +23 -7
- package/dist/src/service/submission-handler.d.ts +0 -1
- package/dist/src/service/submission-handler.js +6 -34
- package/dist/src/service/worker-port.d.ts +1 -0
- package/dist/src/service/worker-totals.js +8 -23
- package/dist/src/step-bridge.d.ts +23 -0
- package/dist/src/steps/decision-gate.d.ts +2 -1
- package/dist/src/steps/decision-gate.js +7 -0
- package/dist/src/steps/merge-by-person.d.ts +2 -0
- package/dist/src/steps/merge-by-person.js +18 -0
- package/dist/src/steps/merge-when-green.d.ts +2 -0
- package/dist/src/steps/merge-when-green.js +62 -0
- package/dist/src/steps/review-by-agent.d.ts +5 -0
- package/dist/src/steps/review-by-agent.js +124 -0
- package/dist/src/steps/review-by-person.d.ts +4 -0
- package/dist/src/steps/review-by-person.js +16 -0
- package/dist/src/steps/review-subject.d.ts +2 -0
- package/dist/src/steps/review-subject.js +11 -0
- package/dist/src/steps/run-command.d.ts +2 -0
- package/dist/src/steps/run-command.js +15 -0
- package/dist/src/steps/sleep.d.ts +3 -0
- package/dist/src/steps/sleep.js +19 -0
- package/dist/src/steps/wait-for-decision.d.ts +2 -0
- package/dist/src/steps/wait-for-decision.js +71 -0
- package/dist/src/steps/wait-for-run.d.ts +5 -0
- package/dist/src/steps/wait-for-run.js +9 -0
- package/dist/src/storage/decision-snapshots.d.ts +1 -0
- package/dist/src/storage/decision-snapshots.js +7 -1
- package/dist/src/submission.d.ts +4 -0
- package/dist/src/submission.js +6 -2
- package/dist/src/testing.d.ts +29 -0
- package/dist/src/testing.js +264 -0
- package/dist/src/ticket-status-stream.d.ts +9 -0
- package/dist/src/ticket-status-stream.js +94 -0
- package/dist/src/token-prices.d.ts +12 -4
- package/dist/src/token-prices.js +15 -2
- package/dist/src/turn-profile.d.ts +1 -0
- package/dist/src/turn-profile.js +8 -1
- package/dist/src/worker-usage.d.ts +16 -0
- package/dist/src/worker-usage.js +57 -0
- package/dist/src/workflow-admission.d.ts +5 -0
- package/dist/src/workflow-candidate.d.ts +7 -2
- package/dist/src/workflow-candidate.js +47 -5
- package/dist/src/workflow-declaration.d.ts +21 -3
- package/dist/src/workflow-declaration.js +77 -9
- package/dist/src/workflow-discovery.d.ts +2 -1
- package/dist/src/workflow-discovery.js +4 -1
- package/dist/src/workflow-refusals.js +3 -0
- package/dist/src/workflow-schedule.d.ts +22 -0
- package/dist/src/workflow-schedule.js +53 -0
- package/dist/src/workflows.d.ts +11 -1
- package/dist/src/workflows.js +8 -0
- package/dist/tools/admit-workflow.d.ts +1 -0
- package/dist/tools/admit-workflow.js +18 -5
- package/dist/tools/doctor-adoption.d.ts +1 -0
- package/dist/tools/doctor-adoption.js +11 -0
- package/dist/tools/project-configuration.d.ts +4 -0
- package/dist/tools/project-configuration.js +47 -0
- package/dist/tools/self-host-processes.d.ts +1 -0
- package/dist/tools/self-host-processes.js +33 -22
- package/dist/tools/self-host-project.d.ts +4 -0
- package/dist/tools/self-host-project.js +61 -14
- package/dist/tools/self-host-runtime.d.ts +16 -0
- package/dist/tools/self-host-runtime.js +60 -0
- package/dist/tools/self-host-workflows.d.ts +1 -0
- package/dist/tools/self-host-workflows.js +36 -1
- package/dist/tools/self-host.js +86 -61
- package/dist/tools/workflow-candidate-counts.d.ts +6 -2
- package/dist/tools/workflow-candidate-counts.js +31 -16
- package/dist/tools/workflow-candidate-fields.d.ts +5 -0
- package/dist/tools/workflow-candidate-fields.js +24 -0
- package/dist/tools/workflow-candidate-helper-counts.d.ts +10 -0
- package/dist/tools/workflow-candidate-helper-counts.js +129 -0
- package/dist/tools/workflow-candidate-program.d.ts +2 -0
- package/dist/tools/workflow-candidate-program.js +36 -8
- package/dist/tools/workflow-candidate-source.js +77 -15
- package/dist/tools/workflow-candidate-syntax.d.ts +8 -2
- package/dist/tools/workflow-candidate-syntax.js +70 -26
- package/dist/tools/workflow-command.d.ts +1 -0
- package/dist/tools/workflow-command.js +143 -0
- package/dist/tools/workflow-folder-capture.d.ts +1 -0
- package/dist/tools/workflow-folder-capture.js +47 -2
- package/dist/tools/workflow-folders.d.ts +17 -1
- package/dist/tools/workflow-folders.js +59 -6
- package/dist/tools/workflow-test-hook.d.ts +7 -0
- package/dist/tools/workflow-test-hook.js +16 -0
- package/dist/tools/workflow-test-loader.d.ts +1 -0
- package/dist/tools/workflow-test-loader.js +10 -0
- package/dist/workflows/develop-ticket.js +1 -1
- package/dist/workflows/ticket-prompts.js +5 -1
- package/package.json +46 -4
- package/templates/develop/README.md +78 -0
- package/templates/develop/develop/accept.ts +11 -0
- package/templates/develop/develop/flow-tests.ts +357 -0
- package/templates/develop/develop/local.ts +40 -0
- package/templates/develop/develop/option-tests.ts +174 -0
- package/templates/develop/develop/partial-choice.ts +29 -0
- package/templates/develop/develop/partial.ts +28 -0
- package/templates/develop/develop/pull.ts +152 -0
- package/templates/develop/develop/queue-partial.ts +39 -0
- package/templates/develop/develop/refusal-tests.ts +174 -0
- package/templates/develop/develop/report.ts +156 -0
- package/templates/develop/develop/round.ts +40 -0
- package/templates/develop/develop/scenarios.ts +87 -0
- package/templates/develop/develop/turn.ts +67 -0
- package/templates/develop/develop.declaration.json +24 -0
- package/templates/develop/develop.test.ts +287 -0
- package/templates/develop/develop.ts +30 -0
- package/templates/develop/template.json +153 -0
- package/dist/src/chats/analysis-batches.d.ts +0 -42
- package/dist/src/chats/analysis-batches.js +0 -97
- package/dist/src/chats/analysis-checkpoint.d.ts +0 -29
- package/dist/src/chats/analysis-checkpoint.js +0 -31
- package/dist/src/chats/analysis-contract.d.ts +0 -80
- package/dist/src/chats/analysis-contract.js +0 -16
- package/dist/src/chats/analysis-data.d.ts +0 -3
- package/dist/src/chats/analysis-data.js +0 -85
- package/dist/src/chats/analysis-input.d.ts +0 -18
- package/dist/src/chats/analysis-input.js +0 -97
- package/dist/src/chats/analysis-links.d.ts +0 -2
- package/dist/src/chats/analysis-links.js +0 -25
- package/dist/src/chats/analysis-merge.d.ts +0 -16
- package/dist/src/chats/analysis-merge.js +0 -134
- package/dist/src/chats/analysis-prompt.d.ts +0 -18
- package/dist/src/chats/analysis-prompt.js +0 -42
- package/dist/src/chats/analysis-proposal.d.ts +0 -4
- package/dist/src/chats/analysis-proposal.js +0 -41
- package/dist/src/chats/analysis-provenance.d.ts +0 -4
- package/dist/src/chats/analysis-provenance.js +0 -99
- package/dist/src/chats/analysis-shape.d.ts +0 -4
- package/dist/src/chats/analysis-shape.js +0 -235
- package/dist/src/chats/analysis.d.ts +0 -3
- package/dist/src/chats/analysis.js +0 -42
- package/dist/src/chats/corpus-input.d.ts +0 -5
- package/dist/src/chats/corpus-input.js +0 -288
- package/dist/src/chats/destination.d.ts +0 -4
- package/dist/src/chats/destination.js +0 -96
- package/dist/src/chats/import.d.ts +0 -15
- package/dist/src/chats/import.js +0 -243
- package/dist/src/chats/input-file.d.ts +0 -3
- package/dist/src/chats/input-file.js +0 -32
- package/dist/src/chats/normalize.d.ts +0 -29
- package/dist/src/chats/normalize.js +0 -155
- package/dist/src/chats/previous-import.d.ts +0 -5
- package/dist/src/chats/previous-import.js +0 -34
- package/dist/src/chats/reader.d.ts +0 -21
- package/dist/src/chats/reader.js +0 -118
- package/dist/src/chats/redact-import.d.ts +0 -14
- package/dist/src/chats/redact-import.js +0 -141
- package/dist/src/chats/redaction.d.ts +0 -41
- package/dist/src/chats/redaction.js +0 -161
- package/dist/src/chats/reference-mapping.d.ts +0 -16
- package/dist/src/chats/reference-mapping.js +0 -153
- package/dist/src/chats/revision-input.d.ts +0 -3
- package/dist/src/chats/revision-input.js +0 -180
- package/dist/src/chats/revision.d.ts +0 -48
- package/dist/src/chats/revision.js +0 -94
- package/dist/src/chats/selection.d.ts +0 -25
- package/dist/src/chats/selection.js +0 -109
- package/dist/src/chats/source.d.ts +0 -23
- package/dist/src/chats/source.js +0 -135
- package/dist/src/chats/structure.d.ts +0 -57
- package/dist/src/chats/structure.js +0 -216
- package/dist/src/chats/workflow-generation-projection.d.ts +0 -8
- package/dist/src/chats/workflow-generation-projection.js +0 -75
- package/dist/src/chats/workflow-generation-source.d.ts +0 -25
- package/dist/src/chats/workflow-generation-source.js +0 -124
- package/dist/src/chats/workflow-generation-text.d.ts +0 -23
- package/dist/src/chats/workflow-generation-text.js +0 -74
- package/dist/src/chats/workflow-generation.d.ts +0 -69
- package/dist/src/chats/workflow-generation.js +0 -142
- package/dist/src/daemon/analysis-launch.d.ts +0 -5
- package/dist/src/daemon/analysis-launch.js +0 -81
- package/dist/src/daemon/analysis-token.d.ts +0 -6
- package/dist/src/daemon/analysis-token.js +0 -88
- package/dist/src/daemon/analysis-worker.d.ts +0 -20
- package/dist/src/daemon/analysis-worker.js +0 -271
- package/dist/src/daemon/chats-batch-binding.d.ts +0 -23
- package/dist/src/daemon/chats-batch-binding.js +0 -99
- package/dist/src/daemon/chats-capture.d.ts +0 -18
- package/dist/src/daemon/chats-capture.js +0 -194
- package/dist/src/daemon/chats-checkpoints.d.ts +0 -41
- package/dist/src/daemon/chats-checkpoints.js +0 -186
- package/dist/src/daemon/chats-files.d.ts +0 -42
- package/dist/src/daemon/chats-files.js +0 -252
- package/dist/src/daemon/chats-follow-up.d.ts +0 -38
- package/dist/src/daemon/chats-follow-up.js +0 -180
- package/dist/src/daemon/chats-question.d.ts +0 -15
- package/dist/src/daemon/chats-question.js +0 -93
- package/dist/src/daemon/chats-worker.d.ts +0 -13
- package/dist/src/daemon/chats-worker.js +0 -201
- package/dist/workflows/analyze-chats.d.ts +0 -15
- package/dist/workflows/analyze-chats.declaration.json +0 -22
- package/dist/workflows/analyze-chats.js +0 -250
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { snapshotReviewSubject } from "./review-subject.js";
|
|
2
|
+
import { waitForDecision } from "./wait-for-decision.js";
|
|
3
|
+
export async function reviewByPerson(request) {
|
|
4
|
+
const snapshot = await snapshotReviewSubject(request.subject);
|
|
5
|
+
const decision = await waitForDecision({
|
|
6
|
+
gateId: `review:${snapshot.subjectDigest}`,
|
|
7
|
+
kind: "approval",
|
|
8
|
+
prompt: `Review this subject:\n${snapshot.prompt}`,
|
|
9
|
+
});
|
|
10
|
+
return {
|
|
11
|
+
accepted: decision.decision === "approved",
|
|
12
|
+
feedback: decision.answer ?? "",
|
|
13
|
+
commit: snapshot.commit,
|
|
14
|
+
subjectDigest: snapshot.subjectDigest,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { operationDigest } from "../broker/operation-digest.js";
|
|
2
|
+
import { reviewSubjectText, } from "../review-subject.js";
|
|
3
|
+
export async function snapshotReviewSubject(subject) {
|
|
4
|
+
"use step";
|
|
5
|
+
const prompt = reviewSubjectText(subject);
|
|
6
|
+
return Promise.resolve({
|
|
7
|
+
commit: "draft" in subject ? null : subject.commit,
|
|
8
|
+
subjectDigest: `sha256:${operationDigest(prompt)}`,
|
|
9
|
+
prompt,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isStepBridge, stepBridgeKey, } from "../step-bridge.js";
|
|
2
|
+
export async function runCommand(request) {
|
|
3
|
+
"use step";
|
|
4
|
+
const bridge = Reflect.get(globalThis, Symbol.for(stepBridgeKey));
|
|
5
|
+
if (!isStepBridge(bridge))
|
|
6
|
+
return { kind: "unavailable", reason: "cloving-host-unavailable" };
|
|
7
|
+
return await bridge.effect({
|
|
8
|
+
scope: request.scope,
|
|
9
|
+
capability: "command",
|
|
10
|
+
method: `run:${request.name}`,
|
|
11
|
+
target: request.name,
|
|
12
|
+
payload: JSON.stringify(request.input),
|
|
13
|
+
units: 1,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { sleep as sdkSleep } from "workflow";
|
|
2
|
+
export async function sleep(request) {
|
|
3
|
+
await sdkSleep(sleepSeconds(request) * 1000);
|
|
4
|
+
}
|
|
5
|
+
function sleepSeconds(request) {
|
|
6
|
+
if (typeof request !== "object" ||
|
|
7
|
+
request === null ||
|
|
8
|
+
Reflect.ownKeys(request).length !== 1)
|
|
9
|
+
throw new Error("invalid-sleep-seconds");
|
|
10
|
+
const field = Object.getOwnPropertyDescriptor(request, "seconds") ?? {};
|
|
11
|
+
const seconds = field.value;
|
|
12
|
+
if (field.enumerable !== true ||
|
|
13
|
+
typeof seconds !== "number" ||
|
|
14
|
+
!Number.isInteger(seconds) ||
|
|
15
|
+
seconds < 1 ||
|
|
16
|
+
seconds > 86400)
|
|
17
|
+
throw new Error("invalid-sleep-seconds");
|
|
18
|
+
return seconds;
|
|
19
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
53
|
+
import { createHook } from "workflow";
|
|
54
|
+
import { openDecisionWait } from "./decision-gate.js";
|
|
55
|
+
export async function waitForDecision(request) {
|
|
56
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
57
|
+
try {
|
|
58
|
+
const wait = await openDecisionWait(request);
|
|
59
|
+
if ("decision" in wait)
|
|
60
|
+
return wait;
|
|
61
|
+
const hook = __addDisposableResource(env_1, createHook({ token: wait.token }), false);
|
|
62
|
+
return await hook;
|
|
63
|
+
}
|
|
64
|
+
catch (e_1) {
|
|
65
|
+
env_1.error = e_1;
|
|
66
|
+
env_1.hasError = true;
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
__disposeResources(env_1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { isStepBridge, stepBridgeKey, } from "../step-bridge.js";
|
|
2
|
+
export async function waitForRun(request) {
|
|
3
|
+
"use step";
|
|
4
|
+
const bridge = Reflect.get(globalThis, Symbol.for(stepBridgeKey));
|
|
5
|
+
if (!isStepBridge(bridge) || bridge.waitForRun === undefined)
|
|
6
|
+
throw new Error("cloving-host-unavailable");
|
|
7
|
+
return await bridge.waitForRun(request);
|
|
8
|
+
}
|
|
9
|
+
waitForRun.maxRetries = 86401;
|
|
@@ -12,6 +12,7 @@ export interface DecidedSnapshot extends DomainSnapshot, DecisionRows {
|
|
|
12
12
|
export interface CurrentSnapshot extends DecidedSnapshot {
|
|
13
13
|
readonly effects: EffectRows;
|
|
14
14
|
}
|
|
15
|
+
export declare function loadOperationSnapshot(connection: SqlConnection): Promise<Pick<ProjectionSnapshot, "outbox" | "deliveries">>;
|
|
15
16
|
export declare function loadProjections(connection: SqlConnection): Promise<ProjectionSnapshot>;
|
|
16
17
|
export declare function loadDecided(connection: SqlConnection): Promise<DecidedSnapshot>;
|
|
17
18
|
export declare function loadCurrent(connection: SqlConnection): Promise<CurrentSnapshot>;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { loadDecisions, loadDocuments, } from "./decision-rows.js";
|
|
2
|
-
import { latestSequence, loadAttempts, loadSnapshot, projectionRows, text, } from "./domain-rows.js";
|
|
2
|
+
import { latestSequence, loadAttempts, loadSnapshot, outboxRow, projectionRows, text, } from "./domain-rows.js";
|
|
3
3
|
import { loadEffects } from "./effect-rows.js";
|
|
4
|
+
export async function loadOperationSnapshot(connection) {
|
|
5
|
+
return {
|
|
6
|
+
outbox: (await connection.all("SELECT operation_id, kind, run_id, created_sequence, released_sequence FROM outbox ORDER BY created_sequence, rowid")).map(outboxRow),
|
|
7
|
+
deliveries: await loadAttempts(connection),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
4
10
|
export async function loadProjections(connection) {
|
|
5
11
|
return {
|
|
6
12
|
...(await projectionRows(connection)),
|
package/dist/src/submission.d.ts
CHANGED
|
@@ -5,6 +5,9 @@ export type SubmissionField = {
|
|
|
5
5
|
} | {
|
|
6
6
|
readonly type: "boolean";
|
|
7
7
|
readonly optional?: boolean;
|
|
8
|
+
} | {
|
|
9
|
+
readonly type: "ticket";
|
|
10
|
+
readonly optional?: boolean;
|
|
8
11
|
} | {
|
|
9
12
|
readonly type: "integer";
|
|
10
13
|
readonly optional?: boolean;
|
|
@@ -15,6 +18,7 @@ export interface SubmissionContract {
|
|
|
15
18
|
readonly maximumBytes: number;
|
|
16
19
|
readonly fields: Readonly<Record<string, "string" | SubmissionField>>;
|
|
17
20
|
}
|
|
21
|
+
export declare function hasTicketField(contract: SubmissionContract | undefined): boolean;
|
|
18
22
|
export type SubmissionCheck = {
|
|
19
23
|
readonly kind: "valid";
|
|
20
24
|
readonly normalized: string;
|
package/dist/src/submission.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { record } from "./protocol.js";
|
|
2
|
+
export function hasTicketField(contract) {
|
|
3
|
+
const rule = contract?.fields["ticket"];
|
|
4
|
+
return rule !== undefined && rule !== "string" && rule.type === "ticket";
|
|
5
|
+
}
|
|
2
6
|
function rejected(code, detail) {
|
|
3
7
|
return { kind: "rejected", code, detail };
|
|
4
8
|
}
|
|
@@ -14,11 +18,11 @@ function invalidField(contract, submission) {
|
|
|
14
18
|
return Object.entries(contract.fields).find(([key, rule]) => {
|
|
15
19
|
if (rule !== "string" && rule.optional && !Object.hasOwn(submission, key))
|
|
16
20
|
return false;
|
|
17
|
-
return !validValue(rule, submission[key]);
|
|
21
|
+
return !validValue(rule === "string" ? { type: "string" } : rule, submission[key]);
|
|
18
22
|
})?.[0];
|
|
19
23
|
}
|
|
20
24
|
function validValue(rule, value) {
|
|
21
|
-
if (rule === "string" || rule.type === "
|
|
25
|
+
if (rule.type === "string" || rule.type === "ticket")
|
|
22
26
|
return typeof value === "string" && value.trim().length > 0;
|
|
23
27
|
if (rule.type === "boolean")
|
|
24
28
|
return typeof value === "boolean";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type HandoffValue, type ProtectedResult, type GateResult, type DecisionResult, type RunWaitOutcome, type ProtectedRequest, type GateRequest, type HandoffRequest, type RunWaitRequest } from "./step-bridge.ts";
|
|
2
|
+
export interface StepScenario {
|
|
3
|
+
readonly turn?: ProtectedResult;
|
|
4
|
+
readonly effects?: Readonly<Record<string, readonly ProtectedResult[]>>;
|
|
5
|
+
readonly gate?: GateResult;
|
|
6
|
+
readonly waits?: readonly RunWaitOutcome[];
|
|
7
|
+
readonly decisions?: readonly DecisionResult[];
|
|
8
|
+
readonly effectLimit?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkflowScenario extends StepScenario {
|
|
11
|
+
readonly workflow: string;
|
|
12
|
+
readonly exportName: string;
|
|
13
|
+
readonly input: HandoffValue;
|
|
14
|
+
readonly decision?: HandoffValue;
|
|
15
|
+
}
|
|
16
|
+
export type ScenarioRequest = readonly ["effect", ProtectedRequest] | readonly ["gate", GateRequest] | readonly ["waitForDecision", GateRequest] | readonly ["handoff", HandoffRequest] | readonly ["waitForRun", RunWaitRequest] | readonly ["sleep", {
|
|
17
|
+
readonly seconds: number;
|
|
18
|
+
}];
|
|
19
|
+
export interface ScenarioResult {
|
|
20
|
+
readonly spent?: number;
|
|
21
|
+
readonly outcome: unknown;
|
|
22
|
+
readonly requests: readonly ScenarioRequest[];
|
|
23
|
+
}
|
|
24
|
+
export declare function fakeStepBridge(scenario: StepScenario): {
|
|
25
|
+
requests: ScenarioRequest[];
|
|
26
|
+
readonly spent: number;
|
|
27
|
+
[Symbol.dispose](): void;
|
|
28
|
+
};
|
|
29
|
+
export declare function runScenario(scenario: WorkflowScenario): Promise<ScenarioResult>;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { workflowTestLoader } from "../tools/workflow-test-loader.js";
|
|
7
|
+
import { authorizeEffect } from "./effect-authorization.js";
|
|
8
|
+
import { record } from "./protocol.js";
|
|
9
|
+
import { stepBridgeKey, } from "./step-bridge.js";
|
|
10
|
+
export function fakeStepBridge(scenario) {
|
|
11
|
+
const { bridge, requests, budget } = scenarioBridge(scenario);
|
|
12
|
+
const key = Symbol.for(stepBridgeKey);
|
|
13
|
+
const sleepKey = Symbol.for("WORKFLOW_SLEEP");
|
|
14
|
+
const previous = Object.getOwnPropertyDescriptor(globalThis, key);
|
|
15
|
+
const previousSleep = Object.getOwnPropertyDescriptor(globalThis, sleepKey);
|
|
16
|
+
if (Reflect.get(globalThis, key) !== undefined ||
|
|
17
|
+
Reflect.get(globalThis, sleepKey) !== undefined)
|
|
18
|
+
throw new Error("workflow-testing-host-active");
|
|
19
|
+
if (previous?.configurable === false || previousSleep?.configurable === false)
|
|
20
|
+
throw new Error("workflow-testing-host-locked");
|
|
21
|
+
Object.defineProperty(globalThis, key, { configurable: true, value: bridge });
|
|
22
|
+
Object.defineProperty(globalThis, sleepKey, {
|
|
23
|
+
configurable: true,
|
|
24
|
+
value: (milliseconds) => {
|
|
25
|
+
requests.push(["sleep", { seconds: milliseconds / 1000 }]);
|
|
26
|
+
return Promise.resolve();
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
requests,
|
|
31
|
+
get spent() {
|
|
32
|
+
return budget.spent;
|
|
33
|
+
},
|
|
34
|
+
[Symbol.dispose]() {
|
|
35
|
+
if (previous === undefined)
|
|
36
|
+
Reflect.deleteProperty(globalThis, key);
|
|
37
|
+
else
|
|
38
|
+
Object.defineProperty(globalThis, key, previous);
|
|
39
|
+
if (previousSleep === undefined)
|
|
40
|
+
Reflect.deleteProperty(globalThis, sleepKey);
|
|
41
|
+
else
|
|
42
|
+
Object.defineProperty(globalThis, sleepKey, previousSleep);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function scenarioBridge(scenario) {
|
|
47
|
+
const requests = [];
|
|
48
|
+
const waits = [...(scenario.waits ?? [])];
|
|
49
|
+
const decisions = [...(scenario.decisions ?? [])];
|
|
50
|
+
const budget = { spent: 0 };
|
|
51
|
+
const bridge = {
|
|
52
|
+
...scenarioEffects(scenario, { requests, budget }),
|
|
53
|
+
gate: (request) => {
|
|
54
|
+
requests.push(["gate", request]);
|
|
55
|
+
return Promise.resolve(scenario.gate ?? {
|
|
56
|
+
kind: "unavailable",
|
|
57
|
+
reason: "no-scenario-result",
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
waitForDecision: (request) => {
|
|
61
|
+
requests.push(["waitForDecision", request]);
|
|
62
|
+
const decision = decisions.shift();
|
|
63
|
+
if (decision === undefined)
|
|
64
|
+
return Promise.reject(new Error("no-scenario-result"));
|
|
65
|
+
return Promise.resolve(decision);
|
|
66
|
+
},
|
|
67
|
+
waitForRun: (request) => {
|
|
68
|
+
requests.push(["waitForRun", request]);
|
|
69
|
+
return Promise.resolve(waits.shift() ?? {
|
|
70
|
+
kind: "unavailable",
|
|
71
|
+
reason: "no-scenario-result",
|
|
72
|
+
promotionId: null,
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
return { bridge, requests, budget };
|
|
77
|
+
}
|
|
78
|
+
function scenarioEffects(scenario, state) {
|
|
79
|
+
const { requests, budget } = state;
|
|
80
|
+
const effects = new Map(Object.entries(scenario.effects ?? {}).map(([key, values]) => [
|
|
81
|
+
key,
|
|
82
|
+
[...values],
|
|
83
|
+
]));
|
|
84
|
+
return {
|
|
85
|
+
effect: (request) => {
|
|
86
|
+
requests.push(["effect", request]);
|
|
87
|
+
const refusal = spend(scenario, { budget, request });
|
|
88
|
+
if (refusal !== undefined)
|
|
89
|
+
return Promise.resolve(refusal);
|
|
90
|
+
const result = scenario.effects === undefined
|
|
91
|
+
? scenario.turn
|
|
92
|
+
: effects.get(request.capability + " " + request.method)?.shift();
|
|
93
|
+
return Promise.resolve(result ?? { kind: "unavailable", reason: "no-scenario-result" });
|
|
94
|
+
},
|
|
95
|
+
handoff: (request) => {
|
|
96
|
+
requests.push(["handoff", request]);
|
|
97
|
+
const refusal = spend(scenario, {
|
|
98
|
+
budget,
|
|
99
|
+
request: { capability: "handoff", units: 1 },
|
|
100
|
+
});
|
|
101
|
+
if (refusal !== undefined)
|
|
102
|
+
return Promise.resolve(refusal);
|
|
103
|
+
return Promise.resolve(effects.get("handoff")?.shift() ?? {
|
|
104
|
+
kind: "completed",
|
|
105
|
+
operationId: "handoff",
|
|
106
|
+
receipt: null,
|
|
107
|
+
result: "target-run",
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function spend(scenario, effect) {
|
|
113
|
+
const { budget, request } = effect;
|
|
114
|
+
if (scenario.effectLimit === undefined)
|
|
115
|
+
return undefined;
|
|
116
|
+
const allowed = authorizeEffect({ capabilities: ["claude", "github", "command", "handoff"] }, {
|
|
117
|
+
budget: { limit: scenario.effectLimit, spent: budget.spent },
|
|
118
|
+
request,
|
|
119
|
+
});
|
|
120
|
+
if (allowed.kind === "rejected")
|
|
121
|
+
return { kind: "refused", reason: allowed.reason };
|
|
122
|
+
budget.spent = allowed.spent;
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
function scenarioScript(scenario) {
|
|
126
|
+
const workflow = scenario.workflow.startsWith("file:")
|
|
127
|
+
? scenario.workflow
|
|
128
|
+
: pathToFileURL(resolve(scenario.workflow)).href;
|
|
129
|
+
return `import { writeFile } from "node:fs/promises";
|
|
130
|
+
import { fakeStepBridge } from ${JSON.stringify(import.meta.url)};
|
|
131
|
+
import * as workflows from ${JSON.stringify(workflow)};
|
|
132
|
+
const given = ${JSON.stringify(scenario)};
|
|
133
|
+
const workflow = workflows[given.exportName];
|
|
134
|
+
if (typeof workflow !== "function") throw new Error("workflow-test-export-missing");
|
|
135
|
+
using bridge = fakeStepBridge(given);
|
|
136
|
+
const outcome = await workflow(given.input, given.decision);
|
|
137
|
+
await writeFile(process.argv[2], JSON.stringify({ outcome, requests: bridge.requests,
|
|
138
|
+
...(given.effectLimit === undefined ? {} : { spent: bridge.spent }) }));`;
|
|
139
|
+
}
|
|
140
|
+
function scenarioResult(value) {
|
|
141
|
+
const result = record(value);
|
|
142
|
+
return (result !== undefined &&
|
|
143
|
+
Object.hasOwn(result, "outcome") &&
|
|
144
|
+
Array.isArray(result["requests"]) &&
|
|
145
|
+
result["requests"].every(scenarioRequest) &&
|
|
146
|
+
(result["spent"] === undefined || typeof result["spent"] === "number"));
|
|
147
|
+
}
|
|
148
|
+
function scenarioRequest(value) {
|
|
149
|
+
if (!Array.isArray(value) || value.length !== 2)
|
|
150
|
+
return false;
|
|
151
|
+
const request = record(value[1]);
|
|
152
|
+
if (request === undefined)
|
|
153
|
+
return false;
|
|
154
|
+
switch (value[0]) {
|
|
155
|
+
case "effect":
|
|
156
|
+
return protectedRequest(request);
|
|
157
|
+
case "gate":
|
|
158
|
+
case "waitForDecision":
|
|
159
|
+
return gateRequest(request);
|
|
160
|
+
case "handoff":
|
|
161
|
+
return handoffRequest(request);
|
|
162
|
+
case "waitForRun":
|
|
163
|
+
return runWaitRequest(request);
|
|
164
|
+
case "sleep":
|
|
165
|
+
return sleepRequest(request);
|
|
166
|
+
default:
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function runWaitRequest(request) {
|
|
171
|
+
return (typeof request["runId"] === "string" &&
|
|
172
|
+
typeof request["maximumWaitSeconds"] === "number");
|
|
173
|
+
}
|
|
174
|
+
function sleepRequest(request) {
|
|
175
|
+
const seconds = request["seconds"];
|
|
176
|
+
return (typeof seconds === "number" &&
|
|
177
|
+
Number.isInteger(seconds) &&
|
|
178
|
+
seconds >= 1 &&
|
|
179
|
+
seconds <= 86400);
|
|
180
|
+
}
|
|
181
|
+
function gateRequest(request) {
|
|
182
|
+
return (typeof request["gateId"] === "string" &&
|
|
183
|
+
(request["kind"] === "question" || request["kind"] === "approval") &&
|
|
184
|
+
typeof request["prompt"] === "string" &&
|
|
185
|
+
optionalText(request["expiresAt"]));
|
|
186
|
+
}
|
|
187
|
+
function handoffRequest(request) {
|
|
188
|
+
const input = record(request["input"]);
|
|
189
|
+
return (typeof request["workflowId"] === "string" &&
|
|
190
|
+
input !== undefined &&
|
|
191
|
+
Object.values(input).every(handoffValue));
|
|
192
|
+
}
|
|
193
|
+
function protectedRequest(request) {
|
|
194
|
+
return (protectedScope(request["scope"]) &&
|
|
195
|
+
["capability", "method", "target", "payload"].every((key) => typeof request[key] === "string") &&
|
|
196
|
+
typeof request["units"] === "number" &&
|
|
197
|
+
optionalText(request["resource"]));
|
|
198
|
+
}
|
|
199
|
+
function protectedScope(value) {
|
|
200
|
+
const scope = record(value);
|
|
201
|
+
if (scope === undefined)
|
|
202
|
+
return false;
|
|
203
|
+
const stage = record(scope["stage"]);
|
|
204
|
+
return (typeof scope["phase"] === "string" &&
|
|
205
|
+
typeof scope["operation"] === "string" &&
|
|
206
|
+
typeof scope["round"] === "number" &&
|
|
207
|
+
optionalText(scope["key"]) &&
|
|
208
|
+
(scope["stage"] === undefined ||
|
|
209
|
+
(stage !== undefined &&
|
|
210
|
+
["id", "visit", "branch"].every((key) => typeof stage[key] === "string"))));
|
|
211
|
+
}
|
|
212
|
+
function optionalText(value) {
|
|
213
|
+
return value === undefined || typeof value === "string";
|
|
214
|
+
}
|
|
215
|
+
function handoffValue(value) {
|
|
216
|
+
if (value === null || ["string", "boolean", "number"].includes(typeof value))
|
|
217
|
+
return true;
|
|
218
|
+
if (Array.isArray(value))
|
|
219
|
+
return value.every(handoffValue);
|
|
220
|
+
const fields = record(value);
|
|
221
|
+
return fields !== undefined && Object.values(fields).every(handoffValue);
|
|
222
|
+
}
|
|
223
|
+
export async function runScenario(scenario) {
|
|
224
|
+
const temporary = await mkdtemp(join(tmpdir(), "cloving-scenario-"));
|
|
225
|
+
try {
|
|
226
|
+
const script = join(temporary, "scenario.mjs");
|
|
227
|
+
const resultFile = join(temporary, "result.json");
|
|
228
|
+
await writeFile(script, scenarioScript(scenario));
|
|
229
|
+
await executeScenario(script, resultFile);
|
|
230
|
+
const value = JSON.parse(await readFile(resultFile, "utf8"));
|
|
231
|
+
if (!scenarioResult(value))
|
|
232
|
+
throw new Error("invalid-workflow-scenario-result");
|
|
233
|
+
return value;
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
await rm(temporary, { recursive: true, force: true });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function executeScenario(script, resultFile) {
|
|
240
|
+
return new Promise((settle, reject) => {
|
|
241
|
+
const child = spawn(process.execPath, [
|
|
242
|
+
"--experimental-strip-types",
|
|
243
|
+
"--import",
|
|
244
|
+
workflowTestLoader(),
|
|
245
|
+
script,
|
|
246
|
+
resultFile,
|
|
247
|
+
], {
|
|
248
|
+
env: { ...process.env, NODE_TEST_CONTEXT: undefined },
|
|
249
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
250
|
+
});
|
|
251
|
+
let diagnostics = "";
|
|
252
|
+
child.stderr.setEncoding("utf8");
|
|
253
|
+
child.stderr.on("data", (text) => {
|
|
254
|
+
diagnostics = (diagnostics + text).slice(-65536);
|
|
255
|
+
});
|
|
256
|
+
child.once("error", reject);
|
|
257
|
+
child.once("close", (code, signal) => {
|
|
258
|
+
if (code === 0)
|
|
259
|
+
settle();
|
|
260
|
+
else
|
|
261
|
+
reject(new Error(`workflow-scenario-failed: ${String(code ?? signal)}\n${diagnostics}`));
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type TicketStatus } from "./ticket-status.ts";
|
|
2
|
+
export interface StreamedTicketStatus {
|
|
3
|
+
readonly ticketStatus: TicketStatus;
|
|
4
|
+
readonly ticketStatusTooLarge?: true;
|
|
5
|
+
}
|
|
6
|
+
export declare function ticketStatusStream(limit: number): {
|
|
7
|
+
write: (chunk: string) => void;
|
|
8
|
+
result: () => StreamedTicketStatus;
|
|
9
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ticketStatus } from "./ticket-status.js";
|
|
2
|
+
function emptyLine() {
|
|
3
|
+
return {
|
|
4
|
+
text: "",
|
|
5
|
+
length: 0,
|
|
6
|
+
contentLength: 0,
|
|
7
|
+
breaks: 0,
|
|
8
|
+
trailingCarriage: false,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function remainingLine(stream, line) {
|
|
12
|
+
if (line.length <= stream.limit && line.text === "Remaining:") {
|
|
13
|
+
stream.remaining = [];
|
|
14
|
+
stream.characters = 0;
|
|
15
|
+
stream.valid = true;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (!stream.valid)
|
|
19
|
+
return;
|
|
20
|
+
if (line.breaks > 0 || !/^- \S.*$/.test(line.text)) {
|
|
21
|
+
stream.valid = false;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
stream.characters = Math.min(stream.limit + 1, stream.characters + line.length + 1);
|
|
25
|
+
if (stream.characters <= stream.limit)
|
|
26
|
+
stream.remaining.push(line.text.slice(2));
|
|
27
|
+
}
|
|
28
|
+
function receivedLine(stream) {
|
|
29
|
+
const line = stream.line;
|
|
30
|
+
stream.line = emptyLine();
|
|
31
|
+
if (line.contentLength === 0)
|
|
32
|
+
return;
|
|
33
|
+
if (line.trailingCarriage)
|
|
34
|
+
line.breaks--;
|
|
35
|
+
if (line.length <= stream.limit + 1 && line.trailingCarriage) {
|
|
36
|
+
line.text = line.text.slice(0, -1);
|
|
37
|
+
line.length--;
|
|
38
|
+
}
|
|
39
|
+
if (stream.last !== undefined)
|
|
40
|
+
remainingLine(stream, stream.last);
|
|
41
|
+
if (/^Ticket status:/i.test(line.text))
|
|
42
|
+
stream.statuses = Math.min(2, stream.statuses + 1);
|
|
43
|
+
stream.last = line;
|
|
44
|
+
}
|
|
45
|
+
function writeStatus(stream, chunk) {
|
|
46
|
+
const parts = chunk.split("\n");
|
|
47
|
+
for (const [index, part] of parts.entries()) {
|
|
48
|
+
const { line, limit } = stream;
|
|
49
|
+
const content = part.trimEnd();
|
|
50
|
+
line.breaks = Math.min(2, line.breaks + (part.match(/[\r\u2028\u2029]/g)?.length ?? 0));
|
|
51
|
+
if (part !== "")
|
|
52
|
+
line.trailingCarriage = part.endsWith("\r");
|
|
53
|
+
if (content !== "")
|
|
54
|
+
line.contentLength = Math.min(limit + 1, line.length + content.length);
|
|
55
|
+
line.text = (line.text + part).slice(0, limit + 1);
|
|
56
|
+
line.length = Math.min(limit + 2, line.length + part.length);
|
|
57
|
+
if (index < parts.length - 1)
|
|
58
|
+
receivedLine(stream);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function statusResult(stream) {
|
|
62
|
+
receivedLine(stream);
|
|
63
|
+
const last = stream.last;
|
|
64
|
+
if (stream.statuses !== 1 || last === undefined)
|
|
65
|
+
return { ticketStatus: ticketStatus("") };
|
|
66
|
+
const status = last.contentLength > stream.limit ? "" : last.text.trimEnd();
|
|
67
|
+
if (status !== "Ticket status: partial" || !stream.valid)
|
|
68
|
+
return { ticketStatus: ticketStatus(status) };
|
|
69
|
+
if (stream.characters > stream.limit)
|
|
70
|
+
return {
|
|
71
|
+
ticketStatus: ticketStatus(status),
|
|
72
|
+
ticketStatusTooLarge: true,
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
ticketStatus: ticketStatus(`Remaining:\n${stream.remaining.map((item) => `- ${item}`).join("\n")}\n${status}`),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export function ticketStatusStream(limit) {
|
|
79
|
+
const stream = {
|
|
80
|
+
limit,
|
|
81
|
+
line: emptyLine(),
|
|
82
|
+
last: undefined,
|
|
83
|
+
statuses: 0,
|
|
84
|
+
remaining: [],
|
|
85
|
+
characters: 0,
|
|
86
|
+
valid: false,
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
write: (chunk) => {
|
|
90
|
+
writeStatus(stream, chunk);
|
|
91
|
+
},
|
|
92
|
+
result: () => statusResult(stream),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type WorkerUsage } from "./worker-usage.ts";
|
|
1
|
+
import { type WorkerUsage, type WorkerModelUsage } from "./worker-usage.ts";
|
|
2
|
+
import type { NativeHarness } from "./harness-defaults.ts";
|
|
2
3
|
export interface TokenPrice {
|
|
3
4
|
readonly input: number;
|
|
4
5
|
readonly cachedInput: number;
|
|
@@ -15,7 +16,14 @@ export declare function parseFallbackModels(value: string | undefined): readonly
|
|
|
15
16
|
export declare function priceForModel(prices: TokenPrices, model: string | null): TokenPrice | undefined;
|
|
16
17
|
export declare function commandModel(command: readonly string[]): string | null;
|
|
17
18
|
export declare function replaceCommandModel(command: readonly string[], model: string): readonly string[];
|
|
18
|
-
|
|
19
|
-
readonly worker:
|
|
19
|
+
interface SinglePricing {
|
|
20
|
+
readonly worker: NativeHarness;
|
|
20
21
|
readonly price: TokenPrice | undefined;
|
|
21
|
-
}
|
|
22
|
+
}
|
|
23
|
+
interface ModelPricing {
|
|
24
|
+
readonly worker: "gemini";
|
|
25
|
+
readonly models: readonly WorkerModelUsage[];
|
|
26
|
+
readonly prices: TokenPrices;
|
|
27
|
+
}
|
|
28
|
+
export declare function estimatedCostUsd(usage: WorkerUsage, pricing: SinglePricing | ModelPricing): number | null;
|
|
29
|
+
export {};
|