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,173 @@
|
|
|
1
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
2
|
+
import { record } from "../protocol.js";
|
|
3
|
+
import { answerDecisionId, answerVerdict, } from "./answer-check.js";
|
|
4
|
+
import { receiptEnvelope } from "./decision-envelopes.js";
|
|
5
|
+
import { envelopeAad, relayInfo, unpadPlaintext } from "./envelope.js";
|
|
6
|
+
import { envelopesBody } from "./envelope-post.js";
|
|
7
|
+
import { openFrom } from "./hpke.js";
|
|
8
|
+
import { relayRequest } from "./relay-http.js";
|
|
9
|
+
import { storedRegistration } from "./relay-registration.js";
|
|
10
|
+
import { envelopeExpiry, sealedPost, } from "./sealed-post.js";
|
|
11
|
+
export const inboxTiming = {
|
|
12
|
+
waitSeconds: 25,
|
|
13
|
+
retryMilliseconds: 5000,
|
|
14
|
+
};
|
|
15
|
+
function inboxItem(value) {
|
|
16
|
+
const fields = record(value);
|
|
17
|
+
const answerId = fields?.["answer_id"];
|
|
18
|
+
const deviceId = fields?.["device_id"];
|
|
19
|
+
const ciphertext = fields?.["ciphertext"];
|
|
20
|
+
const sequence = fields?.["sequence"];
|
|
21
|
+
if (typeof answerId !== "string" ||
|
|
22
|
+
typeof deviceId !== "string" ||
|
|
23
|
+
typeof ciphertext !== "string" ||
|
|
24
|
+
typeof sequence !== "number")
|
|
25
|
+
return [];
|
|
26
|
+
return [{ answerId, deviceId, ciphertext, sequence }];
|
|
27
|
+
}
|
|
28
|
+
function inboxItems(body) {
|
|
29
|
+
const answers = record(body)?.["answers"];
|
|
30
|
+
if (!Array.isArray(answers))
|
|
31
|
+
return [];
|
|
32
|
+
return answers
|
|
33
|
+
.flatMap((item) => inboxItem(item))
|
|
34
|
+
.sort((left, right) => left.sequence - right.sequence);
|
|
35
|
+
}
|
|
36
|
+
function openedAnswer(answering) {
|
|
37
|
+
const { registration, item } = answering;
|
|
38
|
+
try {
|
|
39
|
+
const padded = openFrom(registration.keys.agreementPrivateKey, {
|
|
40
|
+
info: relayInfo,
|
|
41
|
+
aad: envelopeAad({
|
|
42
|
+
daemonId: registration.link.daemonId,
|
|
43
|
+
deviceId: item.deviceId,
|
|
44
|
+
kind: "answer",
|
|
45
|
+
}),
|
|
46
|
+
sealed: Buffer.from(item.ciphertext, "base64url"),
|
|
47
|
+
});
|
|
48
|
+
const message = record(JSON.parse(unpadPlaintext(padded).toString("utf8")));
|
|
49
|
+
const payload = message?.["payload"];
|
|
50
|
+
const signature = message?.["signature"];
|
|
51
|
+
if (typeof payload !== "string" || typeof signature !== "string")
|
|
52
|
+
return undefined;
|
|
53
|
+
return { payload, signature };
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function verdictOf(context, answering) {
|
|
60
|
+
const message = openedAnswer(answering);
|
|
61
|
+
if (message === undefined)
|
|
62
|
+
return { kind: "refused", code: "answer-invalid", decisionId: null };
|
|
63
|
+
const decisionId = answerDecisionId(message);
|
|
64
|
+
return answerVerdict(message, {
|
|
65
|
+
daemonId: answering.registration.link.daemonId,
|
|
66
|
+
routedDeviceId: answering.item.deviceId,
|
|
67
|
+
devices: await context.store.devices(),
|
|
68
|
+
now: context.now(),
|
|
69
|
+
sentPromptDigest: decisionId === null
|
|
70
|
+
? null
|
|
71
|
+
: await context.store.delivery.promptDigest(decisionId),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function receiptBody(base, answered) {
|
|
75
|
+
if (answered.kind === "result")
|
|
76
|
+
return {
|
|
77
|
+
...base,
|
|
78
|
+
code: String(answered.result["kind"]),
|
|
79
|
+
outcome: answered.result["decision"] ?? null,
|
|
80
|
+
receipt: answered.result["receipt"] ?? null,
|
|
81
|
+
};
|
|
82
|
+
const code = answered.kind === "refused" ? answered.code : answered.error.code;
|
|
83
|
+
return { ...base, code, outcome: null, receipt: null };
|
|
84
|
+
}
|
|
85
|
+
async function receiptWork(context, answering) {
|
|
86
|
+
const { registration, item } = answering;
|
|
87
|
+
const verdict = await verdictOf(context, answering);
|
|
88
|
+
const answered = verdict.kind === "decision"
|
|
89
|
+
? await context.control({
|
|
90
|
+
operation: "decision",
|
|
91
|
+
parameters: verdict.parameters,
|
|
92
|
+
})
|
|
93
|
+
: verdict;
|
|
94
|
+
const receipt = receiptBody({ answerId: item.answerId, decisionId: verdict.decisionId }, answered);
|
|
95
|
+
const device = (await context.store.devices()).find((known) => known.deviceId === item.deviceId);
|
|
96
|
+
const issued = context.now();
|
|
97
|
+
const envelope = device === undefined
|
|
98
|
+
? null
|
|
99
|
+
: await sealedPost(context, {
|
|
100
|
+
registration,
|
|
101
|
+
device,
|
|
102
|
+
decisionId: verdict.decisionId ?? item.answerId,
|
|
103
|
+
issuedAt: new Date(issued).toISOString(),
|
|
104
|
+
expiresAt: envelopeExpiry(null, issued),
|
|
105
|
+
build: (target) => receiptEnvelope({ receipt, target }),
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
answerId: item.answerId,
|
|
109
|
+
sequence: item.sequence,
|
|
110
|
+
deviceId: item.deviceId,
|
|
111
|
+
envelope,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function handled(context, answering) {
|
|
115
|
+
const { item } = answering;
|
|
116
|
+
const { link } = answering.registration;
|
|
117
|
+
const work = (await context.store.inbox.work(item.answerId)) ??
|
|
118
|
+
(await context.store.inbox.record(await receiptWork(context, answering)));
|
|
119
|
+
if (work.envelope !== null) {
|
|
120
|
+
const posted = await relayRequest(link.origin, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
path: "/api/v1/relay/envelopes",
|
|
123
|
+
token: link.token,
|
|
124
|
+
body: envelopesBody(work.deviceId, [work.envelope]),
|
|
125
|
+
});
|
|
126
|
+
if (posted.status !== 201)
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const acknowledged = await relayRequest(link.origin, {
|
|
130
|
+
method: "POST",
|
|
131
|
+
path: "/api/v1/relay/inbox/acks",
|
|
132
|
+
token: link.token,
|
|
133
|
+
body: { answer_ids: [item.answerId] },
|
|
134
|
+
});
|
|
135
|
+
if (acknowledged.status !== 200)
|
|
136
|
+
return false;
|
|
137
|
+
await context.store.inbox.complete({
|
|
138
|
+
answerId: item.answerId,
|
|
139
|
+
sequence: item.sequence,
|
|
140
|
+
});
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
export async function readAnswers(context, reading) {
|
|
144
|
+
const registration = await storedRegistration(context.phoneDirectory);
|
|
145
|
+
if (registration === undefined)
|
|
146
|
+
return false;
|
|
147
|
+
const { link } = registration;
|
|
148
|
+
const cursor = await context.store.inbox.cursor();
|
|
149
|
+
const reply = await relayRequest(link.origin, {
|
|
150
|
+
method: "GET",
|
|
151
|
+
path: `/api/v1/relay/inbox?after=${String(cursor)}&wait=${String(reading.waitSeconds)}`,
|
|
152
|
+
token: link.token,
|
|
153
|
+
timeoutMilliseconds: (reading.waitSeconds + 10) * 1000,
|
|
154
|
+
signal: reading.signal,
|
|
155
|
+
});
|
|
156
|
+
if (reply.status !== 200)
|
|
157
|
+
throw new Error(`inbox-failed: cloving.ai answered ${String(reply.status)}.`);
|
|
158
|
+
for (const item of inboxItems(reply.body))
|
|
159
|
+
if (!(await handled(context, { registration, item })))
|
|
160
|
+
return false;
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
export async function runInbox(context, running) {
|
|
164
|
+
const { signal, timing } = running;
|
|
165
|
+
while (!signal.aborted) {
|
|
166
|
+
const polled = await readAnswers(context, {
|
|
167
|
+
waitSeconds: timing.waitSeconds,
|
|
168
|
+
signal,
|
|
169
|
+
}).catch(() => false);
|
|
170
|
+
if (!polled)
|
|
171
|
+
await delay(timing.retryMilliseconds, undefined, { signal }).catch(() => undefined);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type KeyObject } from "node:crypto";
|
|
2
|
+
export interface DaemonKeys {
|
|
3
|
+
readonly agreementPrivateKey: string;
|
|
4
|
+
readonly agreementPublicKey: string;
|
|
5
|
+
readonly signingPrivateKey: string;
|
|
6
|
+
readonly signingPublicKey: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function publicPoint(key: KeyObject): string;
|
|
9
|
+
export declare function newDaemonKeys(): DaemonKeys;
|
|
10
|
+
export declare function parseDaemonKeys(text: string | undefined): DaemonKeys | undefined;
|
|
11
|
+
export declare function keyFingerprint(keys: DaemonKeys): Buffer;
|
|
12
|
+
export declare function pairingIdentity(secret: Buffer): string;
|
|
13
|
+
export declare function pairingLink(origin: string, values: {
|
|
14
|
+
readonly secret: Buffer;
|
|
15
|
+
readonly keys: DaemonKeys;
|
|
16
|
+
}): string;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createHash, generateKeyPairSync } from "node:crypto";
|
|
2
|
+
import { canonicalJson } from "../phone/canonical-json.js";
|
|
3
|
+
const fields = [
|
|
4
|
+
"agreementPrivateKey",
|
|
5
|
+
"agreementPublicKey",
|
|
6
|
+
"signingPrivateKey",
|
|
7
|
+
"signingPublicKey",
|
|
8
|
+
];
|
|
9
|
+
export function publicPoint(key) {
|
|
10
|
+
const jwk = key.export({ format: "jwk" });
|
|
11
|
+
return Buffer.concat([
|
|
12
|
+
Buffer.from([4]),
|
|
13
|
+
Buffer.from(String(jwk.x), "base64url"),
|
|
14
|
+
Buffer.from(String(jwk.y), "base64url"),
|
|
15
|
+
]).toString("base64url");
|
|
16
|
+
}
|
|
17
|
+
function pair() {
|
|
18
|
+
const { privateKey, publicKey } = generateKeyPairSync("ec", {
|
|
19
|
+
namedCurve: "prime256v1",
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
privateKey: privateKey
|
|
23
|
+
.export({ format: "der", type: "pkcs8" })
|
|
24
|
+
.toString("base64url"),
|
|
25
|
+
publicKey: publicPoint(publicKey),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function newDaemonKeys() {
|
|
29
|
+
const agreement = pair();
|
|
30
|
+
const signing = pair();
|
|
31
|
+
return {
|
|
32
|
+
agreementPrivateKey: agreement.privateKey,
|
|
33
|
+
agreementPublicKey: agreement.publicKey,
|
|
34
|
+
signingPrivateKey: signing.privateKey,
|
|
35
|
+
signingPublicKey: signing.publicKey,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function parseDaemonKeys(text) {
|
|
39
|
+
if (text === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
const value = JSON.parse(text);
|
|
42
|
+
if (typeof value !== "object" || value === null)
|
|
43
|
+
return undefined;
|
|
44
|
+
const complete = fields.every((name) => {
|
|
45
|
+
const field = Reflect.get(value, name);
|
|
46
|
+
return typeof field === "string" && field !== "";
|
|
47
|
+
});
|
|
48
|
+
if (!complete)
|
|
49
|
+
return undefined;
|
|
50
|
+
return {
|
|
51
|
+
agreementPrivateKey: String(Reflect.get(value, "agreementPrivateKey")),
|
|
52
|
+
agreementPublicKey: String(Reflect.get(value, "agreementPublicKey")),
|
|
53
|
+
signingPrivateKey: String(Reflect.get(value, "signingPrivateKey")),
|
|
54
|
+
signingPublicKey: String(Reflect.get(value, "signingPublicKey")),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function keyFingerprint(keys) {
|
|
58
|
+
const text = canonicalJson({
|
|
59
|
+
agreementKey: keys.agreementPublicKey,
|
|
60
|
+
signingKey: keys.signingPublicKey,
|
|
61
|
+
});
|
|
62
|
+
return createHash("sha256").update(text).digest().subarray(0, 16);
|
|
63
|
+
}
|
|
64
|
+
export function pairingIdentity(secret) {
|
|
65
|
+
return createHash("sha256")
|
|
66
|
+
.update("cloving-pairing-id|")
|
|
67
|
+
.update(secret)
|
|
68
|
+
.digest()
|
|
69
|
+
.subarray(0, 16)
|
|
70
|
+
.toString("base64url");
|
|
71
|
+
}
|
|
72
|
+
export function pairingLink(origin, values) {
|
|
73
|
+
const payload = Buffer.concat([values.secret, keyFingerprint(values.keys)]);
|
|
74
|
+
return `${origin}/pair#v1.${payload.toString("base64url")}`;
|
|
75
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { record } from "../protocol.js";
|
|
2
|
+
import { closeDecisions } from "./decision-close.js";
|
|
3
|
+
import { fullEnvelope, previewEnvelope, promptDigest, } from "./decision-envelopes.js";
|
|
4
|
+
import { storedRegistration } from "./relay-registration.js";
|
|
5
|
+
import { envelopeExpiry, resultOf, rows, sealedPost, } from "./sealed-post.js";
|
|
6
|
+
async function devicePosts(context, plan) {
|
|
7
|
+
const { decision, workflowId } = plan;
|
|
8
|
+
const issued = context.now();
|
|
9
|
+
const posts = [];
|
|
10
|
+
for (const build of [previewEnvelope, fullEnvelope])
|
|
11
|
+
posts.push(await sealedPost(context, {
|
|
12
|
+
registration: plan.registration,
|
|
13
|
+
device: plan.device,
|
|
14
|
+
decisionId: String(decision["decisionId"]),
|
|
15
|
+
issuedAt: new Date(issued).toISOString(),
|
|
16
|
+
expiresAt: envelopeExpiry(decision["expiresAt"], issued),
|
|
17
|
+
build: (target) => build({ decision, workflowId, target }),
|
|
18
|
+
}));
|
|
19
|
+
return posts;
|
|
20
|
+
}
|
|
21
|
+
async function workflowOf(context, lookup) {
|
|
22
|
+
const known = lookup.known.get(lookup.runId);
|
|
23
|
+
if (known !== undefined)
|
|
24
|
+
return known;
|
|
25
|
+
const status = await resultOf(context, {
|
|
26
|
+
operation: "status",
|
|
27
|
+
parameters: { runId: lookup.runId },
|
|
28
|
+
});
|
|
29
|
+
const workflowId = record(status?.["run"])?.["workflowId"];
|
|
30
|
+
if (typeof workflowId !== "string")
|
|
31
|
+
return undefined;
|
|
32
|
+
lookup.known.set(lookup.runId, workflowId);
|
|
33
|
+
return workflowId;
|
|
34
|
+
}
|
|
35
|
+
async function offerDecision(context, plan) {
|
|
36
|
+
const decisionId = String(plan.decision["decisionId"]);
|
|
37
|
+
const delivered = new Set((await context.store.delivery.list(decisionId))
|
|
38
|
+
.filter((item) => item.topic === "decision")
|
|
39
|
+
.map((item) => item.deviceId));
|
|
40
|
+
for (const device of plan.devices.filter((item) => !delivered.has(item.deviceId)))
|
|
41
|
+
await context.store.delivery.offer(decisionId, {
|
|
42
|
+
runId: String(plan.decision["runId"]),
|
|
43
|
+
promptDigest: promptDigest(String(plan.decision["prompt"])),
|
|
44
|
+
deviceId: device.deviceId,
|
|
45
|
+
envelopes: await devicePosts(context, { ...plan, device }),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export async function checkDecisions(context) {
|
|
49
|
+
const registration = await storedRegistration(context.phoneDirectory);
|
|
50
|
+
if (registration === undefined)
|
|
51
|
+
return;
|
|
52
|
+
const questions = await resultOf(context, {
|
|
53
|
+
operation: "questions",
|
|
54
|
+
parameters: {},
|
|
55
|
+
});
|
|
56
|
+
if (questions === undefined)
|
|
57
|
+
return;
|
|
58
|
+
const decisions = rows(questions["decisions"]);
|
|
59
|
+
const devices = (await context.store.devices()).filter((device) => device.state === "connected" && device.removedAt === null);
|
|
60
|
+
const known = new Map();
|
|
61
|
+
for (const decision of devices.length === 0 ? [] : decisions) {
|
|
62
|
+
const runId = String(decision["runId"]);
|
|
63
|
+
const workflowId = await workflowOf(context, { runId, known });
|
|
64
|
+
if (workflowId !== undefined)
|
|
65
|
+
await offerDecision(context, {
|
|
66
|
+
decision,
|
|
67
|
+
workflowId,
|
|
68
|
+
registration,
|
|
69
|
+
devices,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
await closeDecisions(context, {
|
|
73
|
+
registration,
|
|
74
|
+
pending: new Set(decisions.map((decision) => decision["decisionId"])),
|
|
75
|
+
});
|
|
76
|
+
await context.store.delivery.sweep();
|
|
77
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Registration } from "./relay-registration.ts";
|
|
2
|
+
import { type CheckContext } from "./sealed-post.ts";
|
|
3
|
+
export declare function closeDecisions(context: CheckContext, scope: {
|
|
4
|
+
readonly registration: Registration;
|
|
5
|
+
readonly pending: ReadonlySet<unknown>;
|
|
6
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { record } from "../protocol.js";
|
|
2
|
+
import { closedEnvelope } from "./decision-envelopes.js";
|
|
3
|
+
import { envelopeExpiry, rows, sealedPost, } from "./sealed-post.js";
|
|
4
|
+
const outcomes = new Set([
|
|
5
|
+
"answered",
|
|
6
|
+
"approved",
|
|
7
|
+
"denied",
|
|
8
|
+
"expired",
|
|
9
|
+
]);
|
|
10
|
+
async function runOf(context, runId) {
|
|
11
|
+
const reply = await context.control({
|
|
12
|
+
operation: "status",
|
|
13
|
+
parameters: { runId },
|
|
14
|
+
});
|
|
15
|
+
if (reply.kind === "result")
|
|
16
|
+
return { run: record(reply.result["run"]) };
|
|
17
|
+
return reply.error.code === "unknown-run" ? { run: undefined } : undefined;
|
|
18
|
+
}
|
|
19
|
+
function endingFrom(open, run) {
|
|
20
|
+
const decided = rows(run?.["decisions"]).find((item) => item["decisionId"] === open.decisionId);
|
|
21
|
+
const outcome = decided?.["decision"];
|
|
22
|
+
const actor = decided?.["actor"];
|
|
23
|
+
return {
|
|
24
|
+
decisionId: open.decisionId,
|
|
25
|
+
outcome: typeof outcome === "string" && outcomes.has(outcome)
|
|
26
|
+
? outcome
|
|
27
|
+
: "unknown",
|
|
28
|
+
actor: typeof actor === "string" ? actor : null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async function endingOf(context, open) {
|
|
32
|
+
const found = await runOf(context, open.runId);
|
|
33
|
+
return found === undefined ? undefined : endingFrom(open, found.run);
|
|
34
|
+
}
|
|
35
|
+
async function closeDecision(context, scope) {
|
|
36
|
+
const { decisionId } = scope.open;
|
|
37
|
+
const ending = await endingOf(context, scope.open);
|
|
38
|
+
if (ending === undefined)
|
|
39
|
+
return;
|
|
40
|
+
const reached = new Set((await context.store.delivery.list(decisionId))
|
|
41
|
+
.filter((item) => item.topic === "decision" &&
|
|
42
|
+
(item.state === "sent" || item.state === "pending"))
|
|
43
|
+
.map((item) => item.deviceId));
|
|
44
|
+
const devices = (await context.store.devices()).filter((device) => reached.has(device.deviceId) &&
|
|
45
|
+
device.state === "connected" &&
|
|
46
|
+
device.removedAt === null);
|
|
47
|
+
const issued = context.now();
|
|
48
|
+
const closing = [];
|
|
49
|
+
for (const device of devices) {
|
|
50
|
+
const post = await sealedPost(context, {
|
|
51
|
+
registration: scope.registration,
|
|
52
|
+
device,
|
|
53
|
+
decisionId,
|
|
54
|
+
issuedAt: new Date(issued).toISOString(),
|
|
55
|
+
expiresAt: envelopeExpiry(null, issued),
|
|
56
|
+
build: (target) => closedEnvelope({ ending, target }),
|
|
57
|
+
});
|
|
58
|
+
closing.push({ deviceId: device.deviceId, envelopes: [post] });
|
|
59
|
+
}
|
|
60
|
+
await context.store.delivery.close(decisionId, {
|
|
61
|
+
devices: closing,
|
|
62
|
+
at: issued,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export async function closeDecisions(context, scope) {
|
|
66
|
+
for (const open of await context.store.delivery.open())
|
|
67
|
+
if (!scope.pending.has(open.decisionId))
|
|
68
|
+
await closeDecision(context, {
|
|
69
|
+
registration: scope.registration,
|
|
70
|
+
open,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Fields } from "../protocol.ts";
|
|
2
|
+
import { type Envelope } from "./envelope.ts";
|
|
3
|
+
export interface EnvelopeTarget {
|
|
4
|
+
readonly daemonId: string;
|
|
5
|
+
readonly deviceId: string;
|
|
6
|
+
readonly sequence: number;
|
|
7
|
+
readonly issuedAt: string;
|
|
8
|
+
readonly expiresAt: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DecisionSource {
|
|
11
|
+
readonly decision: Fields;
|
|
12
|
+
readonly workflowId: string;
|
|
13
|
+
readonly target: EnvelopeTarget;
|
|
14
|
+
}
|
|
15
|
+
export declare function promptDigest(prompt: string): string;
|
|
16
|
+
export declare function receiptEnvelope(source: {
|
|
17
|
+
readonly receipt: Fields;
|
|
18
|
+
readonly target: EnvelopeTarget;
|
|
19
|
+
}): Envelope;
|
|
20
|
+
export declare function closedEnvelope(source: {
|
|
21
|
+
readonly ending: {
|
|
22
|
+
readonly decisionId: string;
|
|
23
|
+
readonly outcome: string;
|
|
24
|
+
readonly actor: string | null;
|
|
25
|
+
};
|
|
26
|
+
readonly target: EnvelopeTarget;
|
|
27
|
+
}): Envelope;
|
|
28
|
+
export declare function previewEnvelope(source: DecisionSource): Envelope;
|
|
29
|
+
export declare function fullEnvelope(source: DecisionSource): Envelope;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { operationalDecision } from "../decision-visibility.js";
|
|
3
|
+
import { plaintextBytes, } from "./envelope.js";
|
|
4
|
+
const previewBytes = 2048;
|
|
5
|
+
const fullBytes = 65_535;
|
|
6
|
+
export function promptDigest(prompt) {
|
|
7
|
+
return `sha256:${createHash("sha256").update(prompt).digest("hex")}`;
|
|
8
|
+
}
|
|
9
|
+
function prefix(text, bytes) {
|
|
10
|
+
let used = 0;
|
|
11
|
+
let end = 0;
|
|
12
|
+
for (const character of text) {
|
|
13
|
+
const size = Buffer.byteLength(character);
|
|
14
|
+
if (used + size > bytes)
|
|
15
|
+
break;
|
|
16
|
+
used += size;
|
|
17
|
+
end += character.length;
|
|
18
|
+
}
|
|
19
|
+
return text.slice(0, end);
|
|
20
|
+
}
|
|
21
|
+
function fitted(prompt, sizing) {
|
|
22
|
+
let shown = prompt;
|
|
23
|
+
let envelope = sizing.build(shown);
|
|
24
|
+
while (plaintextBytes(envelope) > sizing.limit && shown !== "") {
|
|
25
|
+
const excess = plaintextBytes(envelope) - sizing.limit;
|
|
26
|
+
shown = prefix(shown, Buffer.byteLength(shown) - excess);
|
|
27
|
+
envelope = sizing.build(shown);
|
|
28
|
+
}
|
|
29
|
+
return envelope;
|
|
30
|
+
}
|
|
31
|
+
function envelopeOf(source, content) {
|
|
32
|
+
return { version: 1, ...source.target, ...content };
|
|
33
|
+
}
|
|
34
|
+
export function receiptEnvelope(source) {
|
|
35
|
+
return {
|
|
36
|
+
version: 1,
|
|
37
|
+
...source.target,
|
|
38
|
+
kind: "receipt",
|
|
39
|
+
body: source.receipt,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function closedEnvelope(source) {
|
|
43
|
+
return {
|
|
44
|
+
version: 1,
|
|
45
|
+
...source.target,
|
|
46
|
+
kind: "closed",
|
|
47
|
+
body: source.ending,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function previewEnvelope(source) {
|
|
51
|
+
const { decision, workflowId } = source;
|
|
52
|
+
const prompt = String(operationalDecision(decision, workflowId)["prompt"]);
|
|
53
|
+
return fitted(prompt, {
|
|
54
|
+
limit: previewBytes,
|
|
55
|
+
build: (shown) => envelopeOf(source, {
|
|
56
|
+
kind: "preview",
|
|
57
|
+
body: {
|
|
58
|
+
decisionId: decision["decisionId"],
|
|
59
|
+
label: decision["label"] ?? null,
|
|
60
|
+
type: decision["type"],
|
|
61
|
+
workflowId,
|
|
62
|
+
expiresAt: decision["expiresAt"] ?? null,
|
|
63
|
+
prompt: shown,
|
|
64
|
+
truncated: shown.length < prompt.length,
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export function fullEnvelope(source) {
|
|
70
|
+
const { decision, workflowId } = source;
|
|
71
|
+
const prompt = String(decision["prompt"]);
|
|
72
|
+
return fitted(prompt, {
|
|
73
|
+
limit: fullBytes,
|
|
74
|
+
build: (shown) => envelopeOf(source, {
|
|
75
|
+
kind: "full",
|
|
76
|
+
body: {
|
|
77
|
+
decisionId: decision["decisionId"],
|
|
78
|
+
label: decision["label"] ?? null,
|
|
79
|
+
type: decision["type"],
|
|
80
|
+
runId: decision["runId"],
|
|
81
|
+
workflowId,
|
|
82
|
+
prompt: shown,
|
|
83
|
+
allowed: decision["allowed"],
|
|
84
|
+
revision: decision["revision"],
|
|
85
|
+
generation: decision["generation"],
|
|
86
|
+
subjectDigest: decision["subjectDigest"],
|
|
87
|
+
promptDigest: promptDigest(prompt),
|
|
88
|
+
expiresAt: decision["expiresAt"] ?? null,
|
|
89
|
+
truncated: shown.length < prompt.length,
|
|
90
|
+
},
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type CheckContext } from "./decision-check.ts";
|
|
2
|
+
export interface WatchTiming {
|
|
3
|
+
readonly pollMilliseconds: number;
|
|
4
|
+
readonly reconnectMilliseconds: number;
|
|
5
|
+
}
|
|
6
|
+
export declare const watchTiming: WatchTiming;
|
|
7
|
+
export interface WatchContext extends CheckContext {
|
|
8
|
+
readonly runtimeDirectory: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DecisionWatch {
|
|
11
|
+
readonly wake: () => void;
|
|
12
|
+
readonly close: () => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare function watchDecisions(context: WatchContext, timing: WatchTiming): DecisionWatch;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
2
|
+
import { checkDecisions } from "./decision-check.js";
|
|
3
|
+
import { relayEvents } from "./relay-control.js";
|
|
4
|
+
export const watchTiming = {
|
|
5
|
+
pollMilliseconds: 60_000,
|
|
6
|
+
reconnectMilliseconds: 5000,
|
|
7
|
+
};
|
|
8
|
+
const gateEvents = new Set([
|
|
9
|
+
"gate.opened",
|
|
10
|
+
"gate.decided",
|
|
11
|
+
"gate.expired",
|
|
12
|
+
]);
|
|
13
|
+
function pause(milliseconds, signal) {
|
|
14
|
+
return delay(milliseconds, undefined, { signal }).catch(() => undefined);
|
|
15
|
+
}
|
|
16
|
+
async function followGates(context, stream) {
|
|
17
|
+
const page = await context.control({
|
|
18
|
+
operation: "events",
|
|
19
|
+
parameters: { after: 0, limit: 1 },
|
|
20
|
+
});
|
|
21
|
+
const latest = page.kind === "result" ? page.result["latestSequence"] : null;
|
|
22
|
+
if (typeof latest !== "number")
|
|
23
|
+
return;
|
|
24
|
+
stream.wake();
|
|
25
|
+
await relayEvents(context.runtimeDirectory, {
|
|
26
|
+
after: latest,
|
|
27
|
+
onEvent: (event) => {
|
|
28
|
+
if (gateEvents.has(String(event["type"])))
|
|
29
|
+
stream.wake();
|
|
30
|
+
},
|
|
31
|
+
stopped: stream.stopped,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function watchDecisions(context, timing) {
|
|
35
|
+
const stop = new AbortController();
|
|
36
|
+
const stopped = new Promise((settle) => {
|
|
37
|
+
stop.signal.addEventListener("abort", () => {
|
|
38
|
+
settle();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
const waking = { controller: new AbortController() };
|
|
42
|
+
const wake = () => {
|
|
43
|
+
waking.controller.abort();
|
|
44
|
+
};
|
|
45
|
+
const checking = (async () => {
|
|
46
|
+
while (!stop.signal.aborted) {
|
|
47
|
+
const current = waking.controller;
|
|
48
|
+
await checkDecisions(context).catch(() => undefined);
|
|
49
|
+
await pause(timing.pollMilliseconds, AbortSignal.any([stop.signal, current.signal]));
|
|
50
|
+
if (current.signal.aborted)
|
|
51
|
+
waking.controller = new AbortController();
|
|
52
|
+
}
|
|
53
|
+
})();
|
|
54
|
+
const following = (async () => {
|
|
55
|
+
while (!stop.signal.aborted) {
|
|
56
|
+
await followGates(context, { wake, stopped }).catch(() => undefined);
|
|
57
|
+
wake();
|
|
58
|
+
await pause(timing.reconnectMilliseconds, stop.signal);
|
|
59
|
+
}
|
|
60
|
+
})();
|
|
61
|
+
return {
|
|
62
|
+
wake,
|
|
63
|
+
close: async () => {
|
|
64
|
+
stop.abort();
|
|
65
|
+
await Promise.all([checking, following]);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JsonObject } from "solid-objects";
|
|
2
|
+
import type { PostedEnvelope, PostOutcome } from "./phone-delivery.ts";
|
|
3
|
+
export interface PostContext {
|
|
4
|
+
readonly phoneDirectory: string;
|
|
5
|
+
readonly now: () => number;
|
|
6
|
+
}
|
|
7
|
+
export declare function envelopesBody(deviceId: string, envelopes: readonly PostedEnvelope[]): Readonly<Record<string, unknown>>;
|
|
8
|
+
export declare function postEnvelopes(context: PostContext, request: JsonObject): Promise<PostOutcome>;
|