@kya-os/cli 1.8.1 → 1.8.3
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/dist/commands/agent.d.ts +35 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +321 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/build.js +2 -2
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/check.js +3 -3
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/checkpoint-init.d.ts +1 -1
- package/dist/commands/checkpoint-init.d.ts.map +1 -1
- package/dist/commands/checkpoint-init.js +8 -41
- package/dist/commands/checkpoint-init.js.map +1 -1
- package/dist/commands/claim.js +2 -2
- package/dist/commands/claim.js.map +1 -1
- package/dist/commands/dco.d.ts +59 -0
- package/dist/commands/dco.d.ts.map +1 -1
- package/dist/commands/dco.js +280 -5
- package/dist/commands/dco.js.map +1 -1
- package/dist/commands/dev.js +1 -1
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +18 -24
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/env.js +6 -6
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/identity.js +3 -3
- package/dist/commands/identity.js.map +1 -1
- package/dist/commands/init-stages.d.ts +141 -0
- package/dist/commands/init-stages.d.ts.map +1 -0
- package/dist/commands/init-stages.js +684 -0
- package/dist/commands/init-stages.js.map +1 -0
- package/dist/commands/init.d.ts +46 -16
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +229 -279
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/orchestrator.d.ts +18 -0
- package/dist/commands/orchestrator.d.ts.map +1 -0
- package/dist/commands/orchestrator.js +251 -0
- package/dist/commands/orchestrator.js.map +1 -0
- package/dist/commands/register.d.ts +1 -1
- package/dist/commands/register.d.ts.map +1 -1
- package/dist/commands/register.js +35 -7
- package/dist/commands/register.js.map +1 -1
- package/dist/commands/review.d.ts +48 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +220 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/rotate.js +4 -4
- package/dist/commands/rotate.js.map +1 -1
- package/dist/commands/score.d.ts +15 -0
- package/dist/commands/score.d.ts.map +1 -0
- package/dist/commands/score.js +471 -0
- package/dist/commands/score.js.map +1 -0
- package/dist/commands/start.js +3 -3
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.js +1 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/verify.js +6 -6
- package/dist/commands/verify.js.map +1 -1
- package/dist/dco.d.ts +4 -0
- package/dist/dco.d.ts.map +1 -0
- package/dist/dco.js +23 -0
- package/dist/dco.js.map +1 -0
- package/dist/index.js +52 -9
- package/dist/orchestrator/daemon.d.ts +137 -0
- package/dist/orchestrator/daemon.d.ts.map +1 -0
- package/dist/orchestrator/daemon.js +242 -0
- package/dist/orchestrator/daemon.js.map +1 -0
- package/dist/orchestrator/db/dispatches.d.ts +108 -0
- package/dist/orchestrator/db/dispatches.d.ts.map +1 -0
- package/dist/orchestrator/db/dispatches.js +122 -0
- package/dist/orchestrator/db/dispatches.js.map +1 -0
- package/dist/orchestrator/db/index.d.ts +50 -0
- package/dist/orchestrator/db/index.d.ts.map +1 -0
- package/dist/orchestrator/db/index.js +116 -0
- package/dist/orchestrator/db/index.js.map +1 -0
- package/dist/orchestrator/db/journal.d.ts +93 -0
- package/dist/orchestrator/db/journal.d.ts.map +1 -0
- package/dist/orchestrator/db/journal.js +91 -0
- package/dist/orchestrator/db/journal.js.map +1 -0
- package/dist/orchestrator/db/liveness.d.ts +97 -0
- package/dist/orchestrator/db/liveness.d.ts.map +1 -0
- package/dist/orchestrator/db/liveness.js +105 -0
- package/dist/orchestrator/db/liveness.js.map +1 -0
- package/dist/orchestrator/db/mission.d.ts +78 -0
- package/dist/orchestrator/db/mission.d.ts.map +1 -0
- package/dist/orchestrator/db/mission.js +99 -0
- package/dist/orchestrator/db/mission.js.map +1 -0
- package/dist/orchestrator/db/schema.d.ts +14 -0
- package/dist/orchestrator/db/schema.d.ts.map +1 -0
- package/dist/orchestrator/db/schema.js +169 -0
- package/dist/orchestrator/db/schema.js.map +1 -0
- package/dist/orchestrator/db/tickets.d.ts +147 -0
- package/dist/orchestrator/db/tickets.d.ts.map +1 -0
- package/dist/orchestrator/db/tickets.js +238 -0
- package/dist/orchestrator/db/tickets.js.map +1 -0
- package/dist/orchestrator/db/verdicts.d.ts +187 -0
- package/dist/orchestrator/db/verdicts.d.ts.map +1 -0
- package/dist/orchestrator/db/verdicts.js +197 -0
- package/dist/orchestrator/db/verdicts.js.map +1 -0
- package/dist/orchestrator/events.d.ts +69 -0
- package/dist/orchestrator/events.d.ts.map +1 -0
- package/dist/orchestrator/events.js +20 -0
- package/dist/orchestrator/events.js.map +1 -0
- package/dist/orchestrator/github-credential-anchor-sender.d.ts +159 -0
- package/dist/orchestrator/github-credential-anchor-sender.d.ts.map +1 -0
- package/dist/orchestrator/github-credential-anchor-sender.js +313 -0
- package/dist/orchestrator/github-credential-anchor-sender.js.map +1 -0
- package/dist/orchestrator/hub-dispatch.d.ts +120 -0
- package/dist/orchestrator/hub-dispatch.d.ts.map +1 -0
- package/dist/orchestrator/hub-dispatch.js +257 -0
- package/dist/orchestrator/hub-dispatch.js.map +1 -0
- package/dist/orchestrator/hub-observer.d.ts +201 -0
- package/dist/orchestrator/hub-observer.d.ts.map +1 -0
- package/dist/orchestrator/hub-observer.js +474 -0
- package/dist/orchestrator/hub-observer.js.map +1 -0
- package/dist/orchestrator/journal-writer.d.ts +80 -0
- package/dist/orchestrator/journal-writer.d.ts.map +1 -0
- package/dist/orchestrator/journal-writer.js +113 -0
- package/dist/orchestrator/journal-writer.js.map +1 -0
- package/dist/orchestrator/mcp/server.d.ts +136 -0
- package/dist/orchestrator/mcp/server.d.ts.map +1 -0
- package/dist/orchestrator/mcp/server.js +582 -0
- package/dist/orchestrator/mcp/server.js.map +1 -0
- package/dist/orchestrator/mcp/stdio.d.ts +22 -0
- package/dist/orchestrator/mcp/stdio.d.ts.map +1 -0
- package/dist/orchestrator/mcp/stdio.js +51 -0
- package/dist/orchestrator/mcp/stdio.js.map +1 -0
- package/dist/orchestrator/review.d.ts +131 -0
- package/dist/orchestrator/review.d.ts.map +1 -0
- package/dist/orchestrator/review.js +322 -0
- package/dist/orchestrator/review.js.map +1 -0
- package/dist/tui/app.d.ts +33 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +71 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/components.d.ts +110 -0
- package/dist/tui/components.d.ts.map +1 -0
- package/dist/tui/components.js +118 -0
- package/dist/tui/components.js.map +1 -0
- package/dist/tui/index.d.ts +30 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +95 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/keymap.d.ts +33 -0
- package/dist/tui/keymap.d.ts.map +1 -0
- package/dist/tui/keymap.js +49 -0
- package/dist/tui/keymap.js.map +1 -0
- package/dist/tui/nav.d.ts +29 -0
- package/dist/tui/nav.d.ts.map +1 -0
- package/dist/tui/nav.js +39 -0
- package/dist/tui/nav.js.map +1 -0
- package/dist/tui/sanitize.d.ts +25 -0
- package/dist/tui/sanitize.d.ts.map +1 -0
- package/dist/tui/sanitize.js +36 -0
- package/dist/tui/sanitize.js.map +1 -0
- package/dist/tui/theme.d.ts +32 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +63 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/tui/views/agents.d.ts +67 -0
- package/dist/tui/views/agents.d.ts.map +1 -0
- package/dist/tui/views/agents.js +231 -0
- package/dist/tui/views/agents.js.map +1 -0
- package/dist/tui/views/index.d.ts +93 -0
- package/dist/tui/views/index.d.ts.map +1 -0
- package/dist/tui/views/index.js +150 -0
- package/dist/tui/views/index.js.map +1 -0
- package/dist/tui/views/registry.d.ts +22 -0
- package/dist/tui/views/registry.d.ts.map +1 -0
- package/dist/tui/views/registry.js +110 -0
- package/dist/tui/views/registry.js.map +1 -0
- package/dist/utils/agent/backoff.d.ts +31 -0
- package/dist/utils/agent/backoff.d.ts.map +1 -0
- package/dist/utils/agent/backoff.js +51 -0
- package/dist/utils/agent/backoff.js.map +1 -0
- package/dist/utils/agent/markers.d.ts +87 -0
- package/dist/utils/agent/markers.d.ts.map +1 -0
- package/dist/utils/agent/markers.js +147 -0
- package/dist/utils/agent/markers.js.map +1 -0
- package/dist/utils/agent/review.d.ts +67 -0
- package/dist/utils/agent/review.d.ts.map +1 -0
- package/dist/utils/agent/review.js +305 -0
- package/dist/utils/agent/review.js.map +1 -0
- package/dist/utils/agent/spawn-agent.d.ts +117 -0
- package/dist/utils/agent/spawn-agent.d.ts.map +1 -0
- package/dist/utils/agent/spawn-agent.js +177 -0
- package/dist/utils/agent/spawn-agent.js.map +1 -0
- package/dist/utils/agent/task.d.ts +101 -0
- package/dist/utils/agent/task.d.ts.map +1 -0
- package/dist/utils/agent/task.js +357 -0
- package/dist/utils/agent/task.js.map +1 -0
- package/dist/utils/agent/verify.d.ts +47 -0
- package/dist/utils/agent/verify.d.ts.map +1 -0
- package/dist/utils/agent/verify.js +144 -0
- package/dist/utils/agent/verify.js.map +1 -0
- package/dist/utils/agent-detector.d.ts +48 -0
- package/dist/utils/agent-detector.d.ts.map +1 -0
- package/dist/utils/agent-detector.js +74 -0
- package/dist/utils/agent-detector.js.map +1 -0
- package/dist/utils/agent-profile-stage.d.ts +70 -0
- package/dist/utils/agent-profile-stage.d.ts.map +1 -0
- package/dist/utils/agent-profile-stage.js +153 -0
- package/dist/utils/agent-profile-stage.js.map +1 -0
- package/dist/utils/agent-profiles.d.ts +27 -0
- package/dist/utils/agent-profiles.d.ts.map +1 -0
- package/dist/utils/agent-profiles.js +61 -0
- package/dist/utils/agent-profiles.js.map +1 -0
- package/dist/utils/checkpoint-api.d.ts +1 -1
- package/dist/utils/checkpoint-api.js +1 -1
- package/dist/utils/checkpoint-env.d.ts +1 -1
- package/dist/utils/checkpoint-env.js +2 -2
- package/dist/utils/checkpoint-env.js.map +1 -1
- package/dist/utils/checkpoint-scaffold.d.ts +1 -1
- package/dist/utils/checkpoint-scaffold.js +1 -1
- package/dist/utils/cli-kit/browser.d.ts +24 -0
- package/dist/utils/cli-kit/browser.d.ts.map +1 -0
- package/dist/utils/cli-kit/browser.js +58 -0
- package/dist/utils/cli-kit/browser.js.map +1 -0
- package/dist/utils/cli-kit/claim-polling.d.ts +18 -0
- package/dist/utils/cli-kit/claim-polling.d.ts.map +1 -0
- package/dist/utils/{claim-polling.js → cli-kit/claim-polling.js} +19 -26
- package/dist/utils/cli-kit/claim-polling.js.map +1 -0
- package/dist/utils/cli-kit/diagnostics.d.ts +9 -0
- package/dist/utils/cli-kit/diagnostics.d.ts.map +1 -0
- package/dist/utils/cli-kit/diagnostics.js +11 -0
- package/dist/utils/cli-kit/diagnostics.js.map +1 -0
- package/dist/utils/cli-kit/fs-safe.d.ts +14 -0
- package/dist/utils/cli-kit/fs-safe.d.ts.map +1 -0
- package/dist/utils/cli-kit/fs-safe.js +20 -0
- package/dist/utils/cli-kit/fs-safe.js.map +1 -0
- package/dist/utils/cli-kit/index.d.ts +15 -0
- package/dist/utils/cli-kit/index.d.ts.map +1 -0
- package/dist/utils/cli-kit/index.js +10 -0
- package/dist/utils/cli-kit/index.js.map +1 -0
- package/dist/utils/cli-kit/json-file.d.ts +21 -0
- package/dist/utils/cli-kit/json-file.d.ts.map +1 -0
- package/dist/utils/cli-kit/json-file.js +51 -0
- package/dist/utils/cli-kit/json-file.js.map +1 -0
- package/dist/utils/cli-kit/marker-block.d.ts +26 -0
- package/dist/utils/cli-kit/marker-block.d.ts.map +1 -0
- package/dist/utils/cli-kit/marker-block.js +61 -0
- package/dist/utils/cli-kit/marker-block.js.map +1 -0
- package/dist/utils/cli-kit/paths.d.ts +15 -0
- package/dist/utils/cli-kit/paths.d.ts.map +1 -0
- package/dist/utils/cli-kit/paths.js +21 -0
- package/dist/utils/cli-kit/paths.js.map +1 -0
- package/dist/utils/cli-kit/secure-storage.d.ts +14 -0
- package/dist/utils/cli-kit/secure-storage.d.ts.map +1 -0
- package/dist/utils/cli-kit/secure-storage.js +20 -0
- package/dist/utils/cli-kit/secure-storage.js.map +1 -0
- package/dist/utils/cli-kit/step-report.d.ts +16 -0
- package/dist/utils/cli-kit/step-report.d.ts.map +1 -0
- package/dist/utils/cli-kit/step-report.js +36 -0
- package/dist/utils/cli-kit/step-report.js.map +1 -0
- package/dist/utils/dco/agent-metadata.d.ts +1 -1
- package/dist/utils/dco/agent-metadata.js +1 -1
- package/dist/utils/dco/agent-metadata.js.map +1 -1
- package/dist/utils/dco/claude-config.d.ts +18 -14
- package/dist/utils/dco/claude-config.d.ts.map +1 -1
- package/dist/utils/dco/claude-config.js +31 -97
- package/dist/utils/dco/claude-config.js.map +1 -1
- package/dist/utils/dco/dispatch.d.ts +134 -0
- package/dist/utils/dco/dispatch.d.ts.map +1 -0
- package/dist/utils/dco/dispatch.js +399 -0
- package/dist/utils/dco/dispatch.js.map +1 -0
- package/dist/utils/dco/fix.d.ts +122 -0
- package/dist/utils/dco/fix.d.ts.map +1 -0
- package/dist/utils/dco/fix.js +560 -0
- package/dist/utils/dco/fix.js.map +1 -0
- package/dist/utils/dco/git-config.d.ts.map +1 -1
- package/dist/utils/dco/git-config.js +5 -3
- package/dist/utils/dco/git-config.js.map +1 -1
- package/dist/utils/dco/github.d.ts +101 -0
- package/dist/utils/dco/github.d.ts.map +1 -1
- package/dist/utils/dco/github.js +133 -0
- package/dist/utils/dco/github.js.map +1 -1
- package/dist/utils/dco/paths.d.ts +1 -8
- package/dist/utils/dco/paths.d.ts.map +1 -1
- package/dist/utils/dco/paths.js +3 -12
- package/dist/utils/dco/paths.js.map +1 -1
- package/dist/utils/dco/render.d.ts +1 -1
- package/dist/utils/dco/render.d.ts.map +1 -1
- package/dist/utils/dco/render.js +4 -21
- package/dist/utils/dco/render.js.map +1 -1
- package/dist/utils/dco/resolve-agent.d.ts +54 -0
- package/dist/utils/dco/resolve-agent.d.ts.map +1 -0
- package/dist/utils/dco/resolve-agent.js +164 -0
- package/dist/utils/dco/resolve-agent.js.map +1 -0
- package/dist/utils/dco/setup.d.ts +10 -7
- package/dist/utils/dco/setup.d.ts.map +1 -1
- package/dist/utils/dco/setup.js +264 -200
- package/dist/utils/dco/setup.js.map +1 -1
- package/dist/utils/dco/signing-key-writer.d.ts +40 -0
- package/dist/utils/dco/signing-key-writer.d.ts.map +1 -0
- package/dist/utils/dco/signing-key-writer.js +76 -0
- package/dist/utils/dco/signing-key-writer.js.map +1 -0
- package/dist/utils/dco/ssh-key.d.ts +50 -0
- package/dist/utils/dco/ssh-key.d.ts.map +1 -1
- package/dist/utils/dco/ssh-key.js +129 -8
- package/dist/utils/dco/ssh-key.js.map +1 -1
- package/dist/utils/dco/status.d.ts +1 -1
- package/dist/utils/dco/status.d.ts.map +1 -1
- package/dist/utils/dco/status.js +3 -3
- package/dist/utils/dco/status.js.map +1 -1
- package/dist/utils/dco/verify.d.ts +170 -0
- package/dist/utils/dco/verify.d.ts.map +1 -0
- package/dist/utils/dco/verify.js +550 -0
- package/dist/utils/dco/verify.js.map +1 -0
- package/dist/utils/dependency-installer.d.ts +37 -0
- package/dist/utils/dependency-installer.d.ts.map +1 -0
- package/dist/utils/dependency-installer.js +32 -0
- package/dist/utils/dependency-installer.js.map +1 -0
- package/dist/utils/github-credential-stage.d.ts +87 -0
- package/dist/utils/github-credential-stage.d.ts.map +1 -0
- package/dist/utils/github-credential-stage.js +168 -0
- package/dist/utils/github-credential-stage.js.map +1 -0
- package/dist/utils/github-credential.d.ts +160 -0
- package/dist/utils/github-credential.d.ts.map +1 -0
- package/dist/utils/github-credential.js +308 -0
- package/dist/utils/github-credential.js.map +1 -0
- package/dist/utils/gitignore.d.ts +17 -0
- package/dist/utils/gitignore.d.ts.map +1 -0
- package/dist/utils/gitignore.js +40 -0
- package/dist/utils/gitignore.js.map +1 -0
- package/dist/utils/hub-config.d.ts +21 -0
- package/dist/utils/hub-config.d.ts.map +1 -0
- package/dist/utils/hub-config.js +28 -0
- package/dist/utils/hub-config.js.map +1 -0
- package/dist/utils/identity-heal.d.ts +16 -0
- package/dist/utils/identity-heal.d.ts.map +1 -0
- package/dist/utils/identity-heal.js +37 -0
- package/dist/utils/identity-heal.js.map +1 -0
- package/dist/utils/init/pipeline.d.ts +74 -0
- package/dist/utils/init/pipeline.d.ts.map +1 -0
- package/dist/utils/init/pipeline.js +96 -0
- package/dist/utils/init/pipeline.js.map +1 -0
- package/dist/utils/poll.d.ts +15 -1
- package/dist/utils/poll.d.ts.map +1 -1
- package/dist/utils/poll.js +6 -3
- package/dist/utils/poll.js.map +1 -1
- package/dist/utils/register-agent-identity.d.ts +67 -0
- package/dist/utils/register-agent-identity.d.ts.map +1 -0
- package/dist/utils/register-agent-identity.js +34 -0
- package/dist/utils/register-agent-identity.js.map +1 -0
- package/dist/utils/review/did-web-key.d.ts +34 -0
- package/dist/utils/review/did-web-key.d.ts.map +1 -0
- package/dist/utils/review/did-web-key.js +104 -0
- package/dist/utils/review/did-web-key.js.map +1 -0
- package/dist/utils/review/load-registry.d.ts +33 -0
- package/dist/utils/review/load-registry.d.ts.map +1 -0
- package/dist/utils/review/load-registry.js +62 -0
- package/dist/utils/review/load-registry.js.map +1 -0
- package/dist/utils/review/verdict-verify.d.ts +43 -0
- package/dist/utils/review/verdict-verify.d.ts.map +1 -0
- package/dist/utils/review/verdict-verify.js +53 -0
- package/dist/utils/review/verdict-verify.js.map +1 -0
- package/dist/utils/review-registry-writer.d.ts +52 -0
- package/dist/utils/review-registry-writer.d.ts.map +1 -0
- package/dist/utils/review-registry-writer.js +79 -0
- package/dist/utils/review-registry-writer.js.map +1 -0
- package/dist/utils/should-install-dependency.d.ts +17 -0
- package/dist/utils/should-install-dependency.d.ts.map +1 -0
- package/dist/utils/should-install-dependency.js +25 -0
- package/dist/utils/should-install-dependency.js.map +1 -0
- package/dist/utils/tool-readiness.d.ts +57 -0
- package/dist/utils/tool-readiness.d.ts.map +1 -0
- package/dist/utils/tool-readiness.js +114 -0
- package/dist/utils/tool-readiness.js.map +1 -0
- package/dist/utils/which.d.ts +2 -0
- package/dist/utils/which.d.ts.map +1 -0
- package/dist/utils/which.js +22 -0
- package/dist/utils/which.js.map +1 -0
- package/dist/version.d.ts +15 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +29 -0
- package/dist/version.js.map +1 -0
- package/package.json +19 -6
- package/dist/utils/claim-polling.d.ts +0 -21
- package/dist/utils/claim-polling.d.ts.map +0 -1
- package/dist/utils/claim-polling.js.map +0 -1
- package/dist/utils/dco/fs-safe.d.ts +0 -14
- package/dist/utils/dco/fs-safe.d.ts.map +0 -1
- package/dist/utils/dco/fs-safe.js +0 -20
- package/dist/utils/dco/fs-safe.js.map +0 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon liveness.
|
|
3
|
+
*
|
|
4
|
+
* Answers one question: is the single writer of this ledger still running? That
|
|
5
|
+
* matters because everything else here is designed around there being exactly
|
|
6
|
+
* one. A TUI showing tickets from a ledger whose daemon died an hour ago looks
|
|
7
|
+
* identical to one showing a quiet system, and the difference is whether
|
|
8
|
+
* anything is still being recorded.
|
|
9
|
+
*
|
|
10
|
+
* Staleness is decided by the READER, not stored. A row cannot know it has gone
|
|
11
|
+
* stale, and a daemon that dies cannot write "I died" -- which is the whole
|
|
12
|
+
* reason liveness is a timestamp to compare against rather than a status field
|
|
13
|
+
* to trust.
|
|
14
|
+
*
|
|
15
|
+
* @module cli/orchestrator/db/liveness
|
|
16
|
+
*/
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
import type { Ledger } from "./index.js";
|
|
19
|
+
export declare const LivenessRowSchema: z.ZodObject<{
|
|
20
|
+
id: z.ZodLiteral<"daemon">;
|
|
21
|
+
started_at: z.ZodString;
|
|
22
|
+
last_beat_at: z.ZodString;
|
|
23
|
+
pid: z.ZodNullable<z.ZodNumber>;
|
|
24
|
+
host: z.ZodNullable<z.ZodString>;
|
|
25
|
+
}, "strip", z.ZodTypeAny, {
|
|
26
|
+
id: "daemon";
|
|
27
|
+
host: string | null;
|
|
28
|
+
started_at: string;
|
|
29
|
+
last_beat_at: string;
|
|
30
|
+
pid: number | null;
|
|
31
|
+
}, {
|
|
32
|
+
id: "daemon";
|
|
33
|
+
host: string | null;
|
|
34
|
+
started_at: string;
|
|
35
|
+
last_beat_at: string;
|
|
36
|
+
pid: number | null;
|
|
37
|
+
}>;
|
|
38
|
+
export type LivenessRow = z.infer<typeof LivenessRowSchema>;
|
|
39
|
+
export interface BeatInput {
|
|
40
|
+
/** Set on the first beat of a run; later beats leave it alone. */
|
|
41
|
+
startedAt?: string;
|
|
42
|
+
pid?: number;
|
|
43
|
+
host?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Record a beat.
|
|
47
|
+
*
|
|
48
|
+
* Upserts the single row. `started_at` is preserved across beats within a run,
|
|
49
|
+
* so the pair (started_at, last_beat_at) tells a reader both how long the daemon
|
|
50
|
+
* has been up and how recently it was alive. Overwriting started_at on every
|
|
51
|
+
* beat would collapse those into one useless number.
|
|
52
|
+
*/
|
|
53
|
+
export declare function beat(db: Ledger, input?: BeatInput): LivenessRow;
|
|
54
|
+
/** The current liveness row, or undefined if no daemon has ever run. */
|
|
55
|
+
export declare function readLiveness(db: Ledger): LivenessRow | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Erase the liveness row.
|
|
58
|
+
*
|
|
59
|
+
* Called on a GRACEFUL stop, and only then. A daemon that crashes cannot
|
|
60
|
+
* write "I died" -- the entire reason staleness exists as a concept -- but
|
|
61
|
+
* one that stops on purpose can, and should: without this, a fresh restart
|
|
62
|
+
* inside the staleness window would find its OWN last beat still looking
|
|
63
|
+
* recent and could mistake it for a still-live second writer.
|
|
64
|
+
*/
|
|
65
|
+
export declare function clearLiveness(db: Ledger): void;
|
|
66
|
+
/**
|
|
67
|
+
* Is a process with this pid currently running?
|
|
68
|
+
*
|
|
69
|
+
* `process.kill(pid, 0)` sends no signal; it only asks the OS whether the pid
|
|
70
|
+
* exists and is reachable. ESRCH means it does not. EPERM means it does, just
|
|
71
|
+
* owned by someone else, which still counts as alive for this purpose. Any
|
|
72
|
+
* other failure is treated as "cannot tell", which conservatively answers
|
|
73
|
+
* alive rather than letting a shaky syscall clear a check it should not.
|
|
74
|
+
*/
|
|
75
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
76
|
+
/** How long a daemon may go quiet before a reader should stop trusting it. */
|
|
77
|
+
export declare const DEFAULT_STALE_AFTER_MS = 90000;
|
|
78
|
+
export interface LivenessVerdict {
|
|
79
|
+
/** Absent means no daemon has ever run against this ledger. */
|
|
80
|
+
present: boolean;
|
|
81
|
+
stale: boolean;
|
|
82
|
+
/** Milliseconds since the last beat, or null when there is no row. */
|
|
83
|
+
ageMs: number | null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Judge a liveness row against a clock.
|
|
87
|
+
*
|
|
88
|
+
* Takes `now` rather than reading the clock itself so a caller can test the
|
|
89
|
+
* boundary, and so a TUI rendering a snapshot judges against the time it
|
|
90
|
+
* rendered rather than drifting as it sits open.
|
|
91
|
+
*
|
|
92
|
+
* A row whose timestamp does not parse counts as STALE, not fresh: an
|
|
93
|
+
* unreadable beat is not evidence of life, and defaulting the other way would
|
|
94
|
+
* report a corrupt row as a healthy daemon.
|
|
95
|
+
*/
|
|
96
|
+
export declare function judgeLiveness(row: LivenessRow | undefined, now: number, staleAfterMs?: number): LivenessVerdict;
|
|
97
|
+
//# sourceMappingURL=liveness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liveness.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/db/liveness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;EAM5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,MAAM,WAAW,SAAS;IACxB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,GAAE,SAAc,GAAG,WAAW,CAcnE;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAIhE;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,sEAAsE;IACtE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,WAAW,GAAG,SAAS,EAC5B,GAAG,EAAE,MAAM,EACX,YAAY,GAAE,MAA+B,GAC5C,eAAe,CAMjB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon liveness.
|
|
3
|
+
*
|
|
4
|
+
* Answers one question: is the single writer of this ledger still running? That
|
|
5
|
+
* matters because everything else here is designed around there being exactly
|
|
6
|
+
* one. A TUI showing tickets from a ledger whose daemon died an hour ago looks
|
|
7
|
+
* identical to one showing a quiet system, and the difference is whether
|
|
8
|
+
* anything is still being recorded.
|
|
9
|
+
*
|
|
10
|
+
* Staleness is decided by the READER, not stored. A row cannot know it has gone
|
|
11
|
+
* stale, and a daemon that dies cannot write "I died" -- which is the whole
|
|
12
|
+
* reason liveness is a timestamp to compare against rather than a status field
|
|
13
|
+
* to trust.
|
|
14
|
+
*
|
|
15
|
+
* @module cli/orchestrator/db/liveness
|
|
16
|
+
*/
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
export const LivenessRowSchema = z.object({
|
|
19
|
+
id: z.literal("daemon"),
|
|
20
|
+
started_at: z.string(),
|
|
21
|
+
last_beat_at: z.string(),
|
|
22
|
+
pid: z.number().nullable(),
|
|
23
|
+
host: z.string().nullable(),
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Record a beat.
|
|
27
|
+
*
|
|
28
|
+
* Upserts the single row. `started_at` is preserved across beats within a run,
|
|
29
|
+
* so the pair (started_at, last_beat_at) tells a reader both how long the daemon
|
|
30
|
+
* has been up and how recently it was alive. Overwriting started_at on every
|
|
31
|
+
* beat would collapse those into one useless number.
|
|
32
|
+
*/
|
|
33
|
+
export function beat(db, input = {}) {
|
|
34
|
+
const now = new Date().toISOString();
|
|
35
|
+
db.prepare(`INSERT INTO daemon_liveness (id, started_at, last_beat_at, pid, host)
|
|
36
|
+
VALUES ('daemon', ?, ?, ?, ?)
|
|
37
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
38
|
+
last_beat_at = excluded.last_beat_at,
|
|
39
|
+
pid = excluded.pid,
|
|
40
|
+
host = excluded.host,
|
|
41
|
+
-- Only advance started_at when the caller declares a new run. A plain
|
|
42
|
+
-- beat must not look like a restart.
|
|
43
|
+
started_at = COALESCE(?, daemon_liveness.started_at)`).run(input.startedAt ?? now, now, input.pid ?? null, input.host ?? null, input.startedAt ?? null);
|
|
44
|
+
return readLiveness(db);
|
|
45
|
+
}
|
|
46
|
+
/** The current liveness row, or undefined if no daemon has ever run. */
|
|
47
|
+
export function readLiveness(db) {
|
|
48
|
+
const row = db.prepare(`SELECT * FROM daemon_liveness WHERE id = 'daemon'`).get();
|
|
49
|
+
if (row === undefined || row === null)
|
|
50
|
+
return undefined;
|
|
51
|
+
return LivenessRowSchema.parse(row);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Erase the liveness row.
|
|
55
|
+
*
|
|
56
|
+
* Called on a GRACEFUL stop, and only then. A daemon that crashes cannot
|
|
57
|
+
* write "I died" -- the entire reason staleness exists as a concept -- but
|
|
58
|
+
* one that stops on purpose can, and should: without this, a fresh restart
|
|
59
|
+
* inside the staleness window would find its OWN last beat still looking
|
|
60
|
+
* recent and could mistake it for a still-live second writer.
|
|
61
|
+
*/
|
|
62
|
+
export function clearLiveness(db) {
|
|
63
|
+
db.prepare(`DELETE FROM daemon_liveness WHERE id = 'daemon'`).run();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Is a process with this pid currently running?
|
|
67
|
+
*
|
|
68
|
+
* `process.kill(pid, 0)` sends no signal; it only asks the OS whether the pid
|
|
69
|
+
* exists and is reachable. ESRCH means it does not. EPERM means it does, just
|
|
70
|
+
* owned by someone else, which still counts as alive for this purpose. Any
|
|
71
|
+
* other failure is treated as "cannot tell", which conservatively answers
|
|
72
|
+
* alive rather than letting a shaky syscall clear a check it should not.
|
|
73
|
+
*/
|
|
74
|
+
export function isProcessAlive(pid) {
|
|
75
|
+
try {
|
|
76
|
+
process.kill(pid, 0);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
return err.code !== "ESRCH";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** How long a daemon may go quiet before a reader should stop trusting it. */
|
|
84
|
+
export const DEFAULT_STALE_AFTER_MS = 90000;
|
|
85
|
+
/**
|
|
86
|
+
* Judge a liveness row against a clock.
|
|
87
|
+
*
|
|
88
|
+
* Takes `now` rather than reading the clock itself so a caller can test the
|
|
89
|
+
* boundary, and so a TUI rendering a snapshot judges against the time it
|
|
90
|
+
* rendered rather than drifting as it sits open.
|
|
91
|
+
*
|
|
92
|
+
* A row whose timestamp does not parse counts as STALE, not fresh: an
|
|
93
|
+
* unreadable beat is not evidence of life, and defaulting the other way would
|
|
94
|
+
* report a corrupt row as a healthy daemon.
|
|
95
|
+
*/
|
|
96
|
+
export function judgeLiveness(row, now, staleAfterMs = DEFAULT_STALE_AFTER_MS) {
|
|
97
|
+
if (row === undefined)
|
|
98
|
+
return { present: false, stale: true, ageMs: null };
|
|
99
|
+
const beatAt = Date.parse(row.last_beat_at);
|
|
100
|
+
if (!Number.isFinite(beatAt))
|
|
101
|
+
return { present: true, stale: true, ageMs: null };
|
|
102
|
+
const ageMs = now - beatAt;
|
|
103
|
+
return { present: true, stale: ageMs > staleAfterMs, ageMs };
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=liveness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liveness.js","sourceRoot":"","sources":["../../../src/orchestrator/db/liveness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IACvB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAC;AAUH;;;;;;;GAOG;AACH,MAAM,UAAU,IAAI,CAAC,EAAU,EAAE,QAAmB,EAAE;IACpD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,EAAE,CAAC,OAAO,CACR;;;;;;;;8DAQ0D,CAC3D,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC;IACnG,OAAO,YAAY,CAAC,EAAE,CAAE,CAAC;AAC3B,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC,GAAG,EAAE,CAAC;IAClF,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACxD,OAAO,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,EAAE,CAAC,OAAO,CAAC,iDAAiD,CAAC,CAAC,GAAG,EAAE,CAAC;AACtE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAQ,GAA6B,CAAC,IAAI,KAAK,OAAO,CAAC;IACzD,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAM,CAAC;AAU7C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAC3B,GAA4B,EAC5B,GAAW,EACX,eAAuB,sBAAsB;IAE7C,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC3E,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACjF,MAAM,KAAK,GAAG,GAAG,GAAG,MAAM,CAAC;IAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,GAAG,YAAY,EAAE,KAAK,EAAE,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mission note.
|
|
3
|
+
*
|
|
4
|
+
* One paragraph of standing intent that an orchestrator session reads on waking.
|
|
5
|
+
* The ledger records what happened in exhaustive mechanical detail; none of it
|
|
6
|
+
* says what the operator is trying to achieve. Without that, a session resuming
|
|
7
|
+
* after a gap has to infer intent from a list of dispatches, and inferring
|
|
8
|
+
* intent is exactly the thing it will get confidently wrong.
|
|
9
|
+
*
|
|
10
|
+
* Append-only. Rewriting the note in place would lose the previous one, and
|
|
11
|
+
* changing the mission is a decision worth reading back later -- which is the
|
|
12
|
+
* same reason tickets soft-close instead of being deleted. The newest row is
|
|
13
|
+
* current; older rows are history.
|
|
14
|
+
*
|
|
15
|
+
* @module cli/orchestrator/db/mission
|
|
16
|
+
*/
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
import type { Ledger } from "./index.js";
|
|
19
|
+
export declare const MissionNoteSchema: z.ZodObject<{
|
|
20
|
+
id: z.ZodString;
|
|
21
|
+
body: z.ZodString;
|
|
22
|
+
author: z.ZodString;
|
|
23
|
+
written_at: z.ZodString;
|
|
24
|
+
}, "strip", z.ZodTypeAny, {
|
|
25
|
+
id: string;
|
|
26
|
+
body: string;
|
|
27
|
+
author: string;
|
|
28
|
+
written_at: string;
|
|
29
|
+
}, {
|
|
30
|
+
id: string;
|
|
31
|
+
body: string;
|
|
32
|
+
author: string;
|
|
33
|
+
written_at: string;
|
|
34
|
+
}>;
|
|
35
|
+
export type MissionNote = z.infer<typeof MissionNoteSchema>;
|
|
36
|
+
export interface WriteMissionNoteInput {
|
|
37
|
+
body: string;
|
|
38
|
+
/** Who set it: an operator's name, or an agent DID. */
|
|
39
|
+
author: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Append a mission note, making it current.
|
|
43
|
+
*
|
|
44
|
+
* Rejects an empty or whitespace-only body rather than storing it. A blank
|
|
45
|
+
* mission is worse than none: a session reading one has been told there IS a
|
|
46
|
+
* standing objective and handed nothing, which reads as an instruction to guess.
|
|
47
|
+
*/
|
|
48
|
+
export declare function writeMissionNote(db: Ledger, input: WriteMissionNoteInput): MissionNote;
|
|
49
|
+
/**
|
|
50
|
+
* The current mission note, or undefined when none has been set.
|
|
51
|
+
*
|
|
52
|
+
* Ties break on rowid, meaning insertion order. written_at has millisecond
|
|
53
|
+
* resolution, so two notes set in quick succession share it, and the id is a
|
|
54
|
+
* random UUID -- tiebreaking on that returns whichever uuid happens to sort
|
|
55
|
+
* higher, so the OLDER note becomes "current" about half the time. This is the
|
|
56
|
+
* same trap as the dispatch lookup: an id that is not monotonic cannot stand in
|
|
57
|
+
* for one that is.
|
|
58
|
+
*/
|
|
59
|
+
export declare function currentMissionNote(db: Ledger): MissionNote | undefined;
|
|
60
|
+
/** Every mission note, newest first. The history of what was being attempted. */
|
|
61
|
+
export declare function listMissionNotes(db: Ledger, limit?: number): MissionNote[];
|
|
62
|
+
/**
|
|
63
|
+
* Who to attribute a note to when the operator did not say.
|
|
64
|
+
*
|
|
65
|
+
* `os.userInfo()` rather than an environment variable: USER is POSIX-only and
|
|
66
|
+
* unset on Windows, where the equivalent is USERNAME, so defaulting to USER
|
|
67
|
+
* alone attributed every Windows note to "unknown" while the help text promised
|
|
68
|
+
* the shell user. userInfo reads the account directly on both.
|
|
69
|
+
*
|
|
70
|
+
* It throws when the uid has no entry in the password database -- some slim
|
|
71
|
+
* containers -- so the env vars remain a fallback rather than the source.
|
|
72
|
+
*
|
|
73
|
+
* `lookup` is injectable because otherwise the fallback is untestable on any
|
|
74
|
+
* machine where userInfo succeeds: the assertion passes via the real username
|
|
75
|
+
* and keeps passing with the fallback deleted.
|
|
76
|
+
*/
|
|
77
|
+
export declare function defaultAuthor(env?: NodeJS.ProcessEnv, lookup?: () => string): string;
|
|
78
|
+
//# sourceMappingURL=mission.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mission.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/db/mission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;EAK5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,WAAW,CAetF;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAMtE;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,WAAW,EAAE,CAKtE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,MAAM,GAAE,MAAM,MAAkC,GAC/C,MAAM,CASR"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mission note.
|
|
3
|
+
*
|
|
4
|
+
* One paragraph of standing intent that an orchestrator session reads on waking.
|
|
5
|
+
* The ledger records what happened in exhaustive mechanical detail; none of it
|
|
6
|
+
* says what the operator is trying to achieve. Without that, a session resuming
|
|
7
|
+
* after a gap has to infer intent from a list of dispatches, and inferring
|
|
8
|
+
* intent is exactly the thing it will get confidently wrong.
|
|
9
|
+
*
|
|
10
|
+
* Append-only. Rewriting the note in place would lose the previous one, and
|
|
11
|
+
* changing the mission is a decision worth reading back later -- which is the
|
|
12
|
+
* same reason tickets soft-close instead of being deleted. The newest row is
|
|
13
|
+
* current; older rows are history.
|
|
14
|
+
*
|
|
15
|
+
* @module cli/orchestrator/db/mission
|
|
16
|
+
*/
|
|
17
|
+
import { randomUUID } from "node:crypto";
|
|
18
|
+
import { userInfo } from "node:os";
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
export const MissionNoteSchema = z.object({
|
|
21
|
+
id: z.string(),
|
|
22
|
+
body: z.string(),
|
|
23
|
+
author: z.string(),
|
|
24
|
+
written_at: z.string(),
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Append a mission note, making it current.
|
|
28
|
+
*
|
|
29
|
+
* Rejects an empty or whitespace-only body rather than storing it. A blank
|
|
30
|
+
* mission is worse than none: a session reading one has been told there IS a
|
|
31
|
+
* standing objective and handed nothing, which reads as an instruction to guess.
|
|
32
|
+
*/
|
|
33
|
+
export function writeMissionNote(db, input) {
|
|
34
|
+
const body = input.body.trim();
|
|
35
|
+
if (body.length === 0) {
|
|
36
|
+
throw new Error("orchestrator: a mission note cannot be empty");
|
|
37
|
+
}
|
|
38
|
+
const author = input.author.trim();
|
|
39
|
+
if (author.length === 0) {
|
|
40
|
+
throw new Error("orchestrator: a mission note needs an author");
|
|
41
|
+
}
|
|
42
|
+
const id = randomUUID();
|
|
43
|
+
const writtenAt = new Date().toISOString();
|
|
44
|
+
db.prepare(`INSERT INTO mission_notes (id, body, author, written_at) VALUES (?, ?, ?, ?)`).run(id, body, author, writtenAt);
|
|
45
|
+
return { id, body, author, written_at: writtenAt };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The current mission note, or undefined when none has been set.
|
|
49
|
+
*
|
|
50
|
+
* Ties break on rowid, meaning insertion order. written_at has millisecond
|
|
51
|
+
* resolution, so two notes set in quick succession share it, and the id is a
|
|
52
|
+
* random UUID -- tiebreaking on that returns whichever uuid happens to sort
|
|
53
|
+
* higher, so the OLDER note becomes "current" about half the time. This is the
|
|
54
|
+
* same trap as the dispatch lookup: an id that is not monotonic cannot stand in
|
|
55
|
+
* for one that is.
|
|
56
|
+
*/
|
|
57
|
+
export function currentMissionNote(db) {
|
|
58
|
+
const row = db
|
|
59
|
+
.prepare(`SELECT * FROM mission_notes ORDER BY written_at DESC, rowid DESC LIMIT 1`)
|
|
60
|
+
.get();
|
|
61
|
+
if (row === undefined || row === null)
|
|
62
|
+
return undefined;
|
|
63
|
+
return MissionNoteSchema.parse(row);
|
|
64
|
+
}
|
|
65
|
+
/** Every mission note, newest first. The history of what was being attempted. */
|
|
66
|
+
export function listMissionNotes(db, limit = 20) {
|
|
67
|
+
const rows = db
|
|
68
|
+
.prepare(`SELECT * FROM mission_notes ORDER BY written_at DESC, rowid DESC LIMIT ?`)
|
|
69
|
+
.all(limit);
|
|
70
|
+
return z.array(MissionNoteSchema).parse(rows);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Who to attribute a note to when the operator did not say.
|
|
74
|
+
*
|
|
75
|
+
* `os.userInfo()` rather than an environment variable: USER is POSIX-only and
|
|
76
|
+
* unset on Windows, where the equivalent is USERNAME, so defaulting to USER
|
|
77
|
+
* alone attributed every Windows note to "unknown" while the help text promised
|
|
78
|
+
* the shell user. userInfo reads the account directly on both.
|
|
79
|
+
*
|
|
80
|
+
* It throws when the uid has no entry in the password database -- some slim
|
|
81
|
+
* containers -- so the env vars remain a fallback rather than the source.
|
|
82
|
+
*
|
|
83
|
+
* `lookup` is injectable because otherwise the fallback is untestable on any
|
|
84
|
+
* machine where userInfo succeeds: the assertion passes via the real username
|
|
85
|
+
* and keeps passing with the fallback deleted.
|
|
86
|
+
*/
|
|
87
|
+
export function defaultAuthor(env = process.env, lookup = () => userInfo().username) {
|
|
88
|
+
try {
|
|
89
|
+
const name = lookup();
|
|
90
|
+
if (name)
|
|
91
|
+
return name;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// No passwd entry. Fall through: an env var or "unknown" is a better answer
|
|
95
|
+
// than failing to record the note at all.
|
|
96
|
+
}
|
|
97
|
+
return env["USER"] || env["USERNAME"] || "unknown";
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=mission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mission.js","sourceRoot":"","sources":["../../../src/orchestrator/db/mission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;CACvB,CAAC,CAAC;AASH;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAU,EAAE,KAA4B;IACvE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IACxB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,EAAE,CAAC,OAAO,CACR,8EAA8E,CAC/E,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IACnC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AACrD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAU;IAC3C,MAAM,GAAG,GAAG,EAAE;SACX,OAAO,CAAC,0EAA0E,CAAC;SACnF,GAAG,EAAE,CAAC;IACT,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACxD,OAAO,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACtC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,EAAU,EAAE,KAAK,GAAG,EAAE;IACrD,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CAAC,0EAA0E,CAAC;SACnF,GAAG,CAAC,KAAK,CAAC,CAAC;IACd,OAAO,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAyB,OAAO,CAAC,GAAG,EACpC,SAAuB,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ;IAEhD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC;QACtB,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,4EAA4E;QAC5E,0CAA0C;IAC5C,CAAC;IACD,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,SAAS,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator ledger schema.
|
|
3
|
+
*
|
|
4
|
+
* Inlined as a TypeScript constant rather than shipped as a `.sql` asset.
|
|
5
|
+
* `@kya-os/cli` builds with a plain `tsc` chain that copies nothing, so a
|
|
6
|
+
* separate file would need a build step to place it beside the compiled
|
|
7
|
+
* output plus a runtime search to find it, and would fail only once, in a
|
|
8
|
+
* published artifact, after passing every local test. A template literal has
|
|
9
|
+
* none of those failure modes.
|
|
10
|
+
*
|
|
11
|
+
* @module cli/orchestrator/db/schema
|
|
12
|
+
*/
|
|
13
|
+
export declare const LEDGER_SCHEMA = "\n-- Two invariants are enforced here in SQL rather than in a prompt, because a\n-- prompt cannot be atomic and a language model cannot be relied on to check a\n-- precondition it was merely told about:\n--\n-- 1. Claiming a ticket is one UPDATE (see db/tickets.ts claimNextTicket), so\n-- two orchestrator ticks, or two orchestrators against one ledger, cannot\n-- both staff the same ticket into two checkouts.\n-- 2. There is no delete path. A bad autonomous close and a clean backlog are\n-- indistinguishable once the row is gone, so closing sets closed_reason\n-- and leaves the row selectable.\n\n-- Tickets: agent-native entries AND mirrored Linear issues in one table.\n-- linear_id NULL -> agent-native. Surfaced for a human to promote by hand;\n-- never auto-filed into Linear.\n-- linear_id set -> mirrored. The agent may comment and move status on it,\n-- and may never create one.\nCREATE TABLE IF NOT EXISTS tickets (\n id TEXT PRIMARY KEY,\n linear_id TEXT UNIQUE,\n title TEXT NOT NULL,\n body TEXT,\n status TEXT NOT NULL DEFAULT 'open',\n severity TEXT,\n source TEXT NOT NULL,\n claimed_by TEXT,\n claimed_at TEXT,\n closed_reason TEXT,\n superseded_by TEXT REFERENCES tickets(id),\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n -- 'blocked' mirrors the DIDComm TaskStatus vocabulary and is the honest\n -- resting place for work stalled on a human decision. Without it that\n -- state has to be smuggled into prose, where nothing can query it.\n CHECK (status IN ('open','claimed','in-progress','in-review','blocked','ready','closed')),\n CHECK (severity IS NULL OR severity IN ('blocking','major','minor','nit')),\n CHECK (source IN ('linear','log_scan','review_finding','manual')),\n -- A ticket cannot supersede itself. Longer cycles are the store layer's\n -- problem; this catches the trivial one for free.\n CHECK (superseded_by IS NULL OR superseded_by <> id)\n);\nCREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status);\nCREATE INDEX IF NOT EXISTS idx_tickets_linear ON tickets(linear_id);\n-- Serves the newest-first window listTickets takes before it re-sorts. See the\n-- note on idx_dispatches_dispatched: an unindexed ORDER BY makes the row cap\n-- bound the response without bounding the scan behind it.\nCREATE INDEX IF NOT EXISTS idx_tickets_created ON tickets(created_at DESC, id DESC);\n\n-- Journal: what the daemon OBSERVED, written from verified protocol events\n-- rather than from a model narrating its own behaviour.\n--\n-- event_type is TEXT, deliberately not a CHECK constraint: the hub's event\n-- union widens over time (review_verdict and pr_quorum_reached arrived with\n-- PR #661), and a ledger that rejects an event type it has not been taught is\n-- a ledger that loses exactly the events worth having.\nCREATE TABLE IF NOT EXISTS journal (\n id TEXT PRIMARY KEY,\n ticket_id TEXT REFERENCES tickets(id),\n event_type TEXT NOT NULL,\n agent_did TEXT,\n agent_name TEXT,\n data TEXT NOT NULL,\n observed_at TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_journal_ticket ON journal(ticket_id, observed_at DESC);\nCREATE INDEX IF NOT EXISTS idx_journal_observed ON journal(observed_at DESC);\n\n-- Dispatches: one row per task handed to a worker.\nCREATE TABLE IF NOT EXISTS dispatches (\n task_id TEXT PRIMARY KEY,\n ticket_id TEXT NOT NULL REFERENCES tickets(id),\n worker_did TEXT NOT NULL,\n repo TEXT,\n branch TEXT,\n pr_number INTEGER,\n status TEXT NOT NULL,\n dispatched_at TEXT NOT NULL,\n completed_at TEXT,\n CHECK (status IN ('dispatched','in-progress','completed','failed'))\n);\nCREATE INDEX IF NOT EXISTS idx_dispatches_pr ON dispatches(repo, pr_number);\n-- Serves the newest-first read. Without it a capped list is a full scan plus a\n-- temp B-tree sort, so LIMIT bounds the answer while the work grows with a table\n-- nothing ever deletes from. The journal's idx_journal_observed exists for the\n-- same reason; these two were simply missed.\nCREATE INDEX IF NOT EXISTS idx_dispatches_dispatched\n ON dispatches(dispatched_at DESC, task_id DESC);\n\n-- Verdicts: populated once the review loop is wired. Created now so that is a\n-- pure wire-up with no migration of its own.\n--\n-- The UNIQUE is what makes quorum counting idempotent under replay: one\n-- verdict per reviewer per head SHA, so a redelivered event updates rather\n-- than double-counts.\nCREATE TABLE IF NOT EXISTS verdicts (\n id TEXT PRIMARY KEY,\n ticket_id TEXT REFERENCES tickets(id),\n repo TEXT NOT NULL,\n pr_number INTEGER NOT NULL,\n head_sha TEXT NOT NULL,\n reviewer_did TEXT NOT NULL,\n verdict TEXT NOT NULL,\n findings TEXT,\n counted INTEGER NOT NULL DEFAULT 0,\n received_at TEXT NOT NULL,\n UNIQUE (repo, pr_number, head_sha, reviewer_did),\n CHECK (verdict IN ('approve','request-changes','comment'))\n);\nCREATE INDEX IF NOT EXISTS idx_verdicts_pr ON verdicts(repo, pr_number, head_sha);\n\n-- Quorum reached, at the grain quorum actually has: a property of one PR head,\n-- not of a reviewer. It cannot live in the verdicts table, whose natural key\n-- includes reviewer_did. The hub is the authority on whether quorum was met: it\n-- owns verdict validation, so re-deriving the threshold here could disagree\n-- with it. Recording the hub's conclusion under its own natural key is what\n-- makes acting on it idempotent without a synthetic marker.\n-- Daemon liveness. Deliberately NOT called a heartbeat: compute already owns\n-- that word for agent liveness posted to AgentShield, which is a different\n-- process reporting a different thing to a different consumer. Conflating them\n-- would also drag the daemon toward compute, against the placement decision\n-- that keeps the single writer of this ledger out of a Fly worker's PID 1.\n--\n-- ONE row, replaced in place, and the only table here that is not append-only.\n-- Liveness is a current fact: \"is the daemon up\" has one answer, and a history\n-- of beats grows without bound while answering nothing the journal cannot.\nCREATE TABLE IF NOT EXISTS daemon_liveness (\n id TEXT PRIMARY KEY CHECK (id = 'daemon'),\n started_at TEXT NOT NULL,\n last_beat_at TEXT NOT NULL,\n pid INTEGER,\n host TEXT\n);\n\n-- The standing objective an orchestrator session reads on waking, so it can\n-- resume intent rather than re-derive it from a ledger full of mechanics.\n--\n-- Append-only, unlike liveness: changing the mission IS a decision, and the\n-- previous one is worth being able to read back. Newest row is current.\nCREATE TABLE IF NOT EXISTS mission_notes (\n id TEXT PRIMARY KEY,\n body TEXT NOT NULL,\n author TEXT NOT NULL,\n written_at TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_mission_written ON mission_notes(written_at DESC);\n\nCREATE TABLE IF NOT EXISTS pr_quorum (\n repo TEXT NOT NULL,\n pr_number INTEGER NOT NULL,\n head_sha TEXT NOT NULL,\n ticket_id TEXT REFERENCES tickets(id),\n quorum_n INTEGER,\n reached_at TEXT NOT NULL,\n PRIMARY KEY (repo, pr_number, head_sha)\n);\n";
|
|
14
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/db/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,aAAa,2qOA2JzB,CAAC"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator ledger schema.
|
|
3
|
+
*
|
|
4
|
+
* Inlined as a TypeScript constant rather than shipped as a `.sql` asset.
|
|
5
|
+
* `@kya-os/cli` builds with a plain `tsc` chain that copies nothing, so a
|
|
6
|
+
* separate file would need a build step to place it beside the compiled
|
|
7
|
+
* output plus a runtime search to find it, and would fail only once, in a
|
|
8
|
+
* published artifact, after passing every local test. A template literal has
|
|
9
|
+
* none of those failure modes.
|
|
10
|
+
*
|
|
11
|
+
* @module cli/orchestrator/db/schema
|
|
12
|
+
*/
|
|
13
|
+
export const LEDGER_SCHEMA = `
|
|
14
|
+
-- Two invariants are enforced here in SQL rather than in a prompt, because a
|
|
15
|
+
-- prompt cannot be atomic and a language model cannot be relied on to check a
|
|
16
|
+
-- precondition it was merely told about:
|
|
17
|
+
--
|
|
18
|
+
-- 1. Claiming a ticket is one UPDATE (see db/tickets.ts claimNextTicket), so
|
|
19
|
+
-- two orchestrator ticks, or two orchestrators against one ledger, cannot
|
|
20
|
+
-- both staff the same ticket into two checkouts.
|
|
21
|
+
-- 2. There is no delete path. A bad autonomous close and a clean backlog are
|
|
22
|
+
-- indistinguishable once the row is gone, so closing sets closed_reason
|
|
23
|
+
-- and leaves the row selectable.
|
|
24
|
+
|
|
25
|
+
-- Tickets: agent-native entries AND mirrored Linear issues in one table.
|
|
26
|
+
-- linear_id NULL -> agent-native. Surfaced for a human to promote by hand;
|
|
27
|
+
-- never auto-filed into Linear.
|
|
28
|
+
-- linear_id set -> mirrored. The agent may comment and move status on it,
|
|
29
|
+
-- and may never create one.
|
|
30
|
+
CREATE TABLE IF NOT EXISTS tickets (
|
|
31
|
+
id TEXT PRIMARY KEY,
|
|
32
|
+
linear_id TEXT UNIQUE,
|
|
33
|
+
title TEXT NOT NULL,
|
|
34
|
+
body TEXT,
|
|
35
|
+
status TEXT NOT NULL DEFAULT 'open',
|
|
36
|
+
severity TEXT,
|
|
37
|
+
source TEXT NOT NULL,
|
|
38
|
+
claimed_by TEXT,
|
|
39
|
+
claimed_at TEXT,
|
|
40
|
+
closed_reason TEXT,
|
|
41
|
+
superseded_by TEXT REFERENCES tickets(id),
|
|
42
|
+
created_at TEXT NOT NULL,
|
|
43
|
+
updated_at TEXT NOT NULL,
|
|
44
|
+
-- 'blocked' mirrors the DIDComm TaskStatus vocabulary and is the honest
|
|
45
|
+
-- resting place for work stalled on a human decision. Without it that
|
|
46
|
+
-- state has to be smuggled into prose, where nothing can query it.
|
|
47
|
+
CHECK (status IN ('open','claimed','in-progress','in-review','blocked','ready','closed')),
|
|
48
|
+
CHECK (severity IS NULL OR severity IN ('blocking','major','minor','nit')),
|
|
49
|
+
CHECK (source IN ('linear','log_scan','review_finding','manual')),
|
|
50
|
+
-- A ticket cannot supersede itself. Longer cycles are the store layer's
|
|
51
|
+
-- problem; this catches the trivial one for free.
|
|
52
|
+
CHECK (superseded_by IS NULL OR superseded_by <> id)
|
|
53
|
+
);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status);
|
|
55
|
+
CREATE INDEX IF NOT EXISTS idx_tickets_linear ON tickets(linear_id);
|
|
56
|
+
-- Serves the newest-first window listTickets takes before it re-sorts. See the
|
|
57
|
+
-- note on idx_dispatches_dispatched: an unindexed ORDER BY makes the row cap
|
|
58
|
+
-- bound the response without bounding the scan behind it.
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_tickets_created ON tickets(created_at DESC, id DESC);
|
|
60
|
+
|
|
61
|
+
-- Journal: what the daemon OBSERVED, written from verified protocol events
|
|
62
|
+
-- rather than from a model narrating its own behaviour.
|
|
63
|
+
--
|
|
64
|
+
-- event_type is TEXT, deliberately not a CHECK constraint: the hub's event
|
|
65
|
+
-- union widens over time (review_verdict and pr_quorum_reached arrived with
|
|
66
|
+
-- PR #661), and a ledger that rejects an event type it has not been taught is
|
|
67
|
+
-- a ledger that loses exactly the events worth having.
|
|
68
|
+
CREATE TABLE IF NOT EXISTS journal (
|
|
69
|
+
id TEXT PRIMARY KEY,
|
|
70
|
+
ticket_id TEXT REFERENCES tickets(id),
|
|
71
|
+
event_type TEXT NOT NULL,
|
|
72
|
+
agent_did TEXT,
|
|
73
|
+
agent_name TEXT,
|
|
74
|
+
data TEXT NOT NULL,
|
|
75
|
+
observed_at TEXT NOT NULL
|
|
76
|
+
);
|
|
77
|
+
CREATE INDEX IF NOT EXISTS idx_journal_ticket ON journal(ticket_id, observed_at DESC);
|
|
78
|
+
CREATE INDEX IF NOT EXISTS idx_journal_observed ON journal(observed_at DESC);
|
|
79
|
+
|
|
80
|
+
-- Dispatches: one row per task handed to a worker.
|
|
81
|
+
CREATE TABLE IF NOT EXISTS dispatches (
|
|
82
|
+
task_id TEXT PRIMARY KEY,
|
|
83
|
+
ticket_id TEXT NOT NULL REFERENCES tickets(id),
|
|
84
|
+
worker_did TEXT NOT NULL,
|
|
85
|
+
repo TEXT,
|
|
86
|
+
branch TEXT,
|
|
87
|
+
pr_number INTEGER,
|
|
88
|
+
status TEXT NOT NULL,
|
|
89
|
+
dispatched_at TEXT NOT NULL,
|
|
90
|
+
completed_at TEXT,
|
|
91
|
+
CHECK (status IN ('dispatched','in-progress','completed','failed'))
|
|
92
|
+
);
|
|
93
|
+
CREATE INDEX IF NOT EXISTS idx_dispatches_pr ON dispatches(repo, pr_number);
|
|
94
|
+
-- Serves the newest-first read. Without it a capped list is a full scan plus a
|
|
95
|
+
-- temp B-tree sort, so LIMIT bounds the answer while the work grows with a table
|
|
96
|
+
-- nothing ever deletes from. The journal's idx_journal_observed exists for the
|
|
97
|
+
-- same reason; these two were simply missed.
|
|
98
|
+
CREATE INDEX IF NOT EXISTS idx_dispatches_dispatched
|
|
99
|
+
ON dispatches(dispatched_at DESC, task_id DESC);
|
|
100
|
+
|
|
101
|
+
-- Verdicts: populated once the review loop is wired. Created now so that is a
|
|
102
|
+
-- pure wire-up with no migration of its own.
|
|
103
|
+
--
|
|
104
|
+
-- The UNIQUE is what makes quorum counting idempotent under replay: one
|
|
105
|
+
-- verdict per reviewer per head SHA, so a redelivered event updates rather
|
|
106
|
+
-- than double-counts.
|
|
107
|
+
CREATE TABLE IF NOT EXISTS verdicts (
|
|
108
|
+
id TEXT PRIMARY KEY,
|
|
109
|
+
ticket_id TEXT REFERENCES tickets(id),
|
|
110
|
+
repo TEXT NOT NULL,
|
|
111
|
+
pr_number INTEGER NOT NULL,
|
|
112
|
+
head_sha TEXT NOT NULL,
|
|
113
|
+
reviewer_did TEXT NOT NULL,
|
|
114
|
+
verdict TEXT NOT NULL,
|
|
115
|
+
findings TEXT,
|
|
116
|
+
counted INTEGER NOT NULL DEFAULT 0,
|
|
117
|
+
received_at TEXT NOT NULL,
|
|
118
|
+
UNIQUE (repo, pr_number, head_sha, reviewer_did),
|
|
119
|
+
CHECK (verdict IN ('approve','request-changes','comment'))
|
|
120
|
+
);
|
|
121
|
+
CREATE INDEX IF NOT EXISTS idx_verdicts_pr ON verdicts(repo, pr_number, head_sha);
|
|
122
|
+
|
|
123
|
+
-- Quorum reached, at the grain quorum actually has: a property of one PR head,
|
|
124
|
+
-- not of a reviewer. It cannot live in the verdicts table, whose natural key
|
|
125
|
+
-- includes reviewer_did. The hub is the authority on whether quorum was met: it
|
|
126
|
+
-- owns verdict validation, so re-deriving the threshold here could disagree
|
|
127
|
+
-- with it. Recording the hub's conclusion under its own natural key is what
|
|
128
|
+
-- makes acting on it idempotent without a synthetic marker.
|
|
129
|
+
-- Daemon liveness. Deliberately NOT called a heartbeat: compute already owns
|
|
130
|
+
-- that word for agent liveness posted to AgentShield, which is a different
|
|
131
|
+
-- process reporting a different thing to a different consumer. Conflating them
|
|
132
|
+
-- would also drag the daemon toward compute, against the placement decision
|
|
133
|
+
-- that keeps the single writer of this ledger out of a Fly worker's PID 1.
|
|
134
|
+
--
|
|
135
|
+
-- ONE row, replaced in place, and the only table here that is not append-only.
|
|
136
|
+
-- Liveness is a current fact: "is the daemon up" has one answer, and a history
|
|
137
|
+
-- of beats grows without bound while answering nothing the journal cannot.
|
|
138
|
+
CREATE TABLE IF NOT EXISTS daemon_liveness (
|
|
139
|
+
id TEXT PRIMARY KEY CHECK (id = 'daemon'),
|
|
140
|
+
started_at TEXT NOT NULL,
|
|
141
|
+
last_beat_at TEXT NOT NULL,
|
|
142
|
+
pid INTEGER,
|
|
143
|
+
host TEXT
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
-- The standing objective an orchestrator session reads on waking, so it can
|
|
147
|
+
-- resume intent rather than re-derive it from a ledger full of mechanics.
|
|
148
|
+
--
|
|
149
|
+
-- Append-only, unlike liveness: changing the mission IS a decision, and the
|
|
150
|
+
-- previous one is worth being able to read back. Newest row is current.
|
|
151
|
+
CREATE TABLE IF NOT EXISTS mission_notes (
|
|
152
|
+
id TEXT PRIMARY KEY,
|
|
153
|
+
body TEXT NOT NULL,
|
|
154
|
+
author TEXT NOT NULL,
|
|
155
|
+
written_at TEXT NOT NULL
|
|
156
|
+
);
|
|
157
|
+
CREATE INDEX IF NOT EXISTS idx_mission_written ON mission_notes(written_at DESC);
|
|
158
|
+
|
|
159
|
+
CREATE TABLE IF NOT EXISTS pr_quorum (
|
|
160
|
+
repo TEXT NOT NULL,
|
|
161
|
+
pr_number INTEGER NOT NULL,
|
|
162
|
+
head_sha TEXT NOT NULL,
|
|
163
|
+
ticket_id TEXT REFERENCES tickets(id),
|
|
164
|
+
quorum_n INTEGER,
|
|
165
|
+
reached_at TEXT NOT NULL,
|
|
166
|
+
PRIMARY KEY (repo, pr_number, head_sha)
|
|
167
|
+
);
|
|
168
|
+
`;
|
|
169
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/orchestrator/db/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2J5B,CAAC"}
|