@humwick/server 0.130.0 → 0.130.2
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 +5 -0
- package/docs/guides/tools-administration.md +6 -1
- package/docs/guides/tools-coordination.md +1 -0
- package/docs/guides/tools-tasks.md +8 -0
- package/docs/guides/workflows.md +58 -0
- package/docs/openapi.json +414 -1
- package/package.json +8 -17
- package/public/assets/{MessageBubble-BWpoBjl4.js → MessageBubble-BWLqMGKG.js} +2 -2
- package/public/assets/{PlanGraphPanel-C0auck3n.js → PlanGraphPanel-3TlIQpjk.js} +2 -2
- package/public/assets/{TraceView-DBnOx7AQ.js → TraceView-Wimm0Czv.js} +2 -2
- package/public/assets/{VaultDecisionCard-0kp2oNBC.js → VaultDecisionCard-Bau_vNax.js} +2 -2
- package/public/assets/{activity-yLztXEBt.js → activity-CrSurn-r.js} +2 -2
- package/public/assets/{agent-lineage-BbJDR1dW.js → agent-lineage-CknAJKcb.js} +2 -2
- package/public/assets/agent-liveness-CaNwK0Zf.js +2 -0
- package/public/assets/{agent-name-CTZBrSD9.js → agent-name-DKbiIhBo.js} +2 -2
- package/public/assets/agent-profiles-DwF6EQ4R.js +2 -0
- package/public/assets/{agent-project-badge-9TqzUUYE.js → agent-project-badge-Diz4Sjjb.js} +2 -2
- package/public/assets/{agent-tools-D4n40pn8.js → agent-tools-CRhpRQza.js} +2 -2
- package/public/assets/{agent-type-icon-D6FUarOf.js → agent-type-icon-LrUEU3lB.js} +2 -2
- package/public/assets/{agents-BYDslkJc.js → agents-B-v66C-4.js} +2 -2
- package/public/assets/{analytics-B-XSbEMC.js → analytics-BqCb-LKi.js} +2 -2
- package/public/assets/{artifact-viewer-DdWIfV6M.js → artifact-viewer-kf4pGuAR.js} +2 -2
- package/public/assets/{artifacts-oBOqks-2.js → artifacts-D32zyw4Y.js} +2 -2
- package/public/assets/{artifacts-DPN1q4pA.js → artifacts-DJevXcvg.js} +2 -2
- package/public/assets/{automation-4KcRFXEu.js → automation-BpRajx_5.js} +2 -2
- package/public/assets/{backlog-Cw9xPTCF.js → backlog-D_UjTdzX.js} +2 -2
- package/public/assets/{busy-health-CTu32MZt.js → busy-health-N3VEh4ao.js} +2 -2
- package/public/assets/{channels-BiXzg1G9.js → channels-9jopungq.js} +2 -2
- package/public/assets/{chat-Ctk2qwvA.js → chat-CSXrTB3C.js} +2 -2
- package/public/assets/{code-preview-Bm1wWqAp.js → code-preview-p1NnmqK0.js} +2 -2
- package/public/assets/{connectors-C36ax4Vw.js → connectors-CZdqlDNL.js} +2 -2
- package/public/assets/{cosmos-Jj9LK2Wf.js → cosmos-BgdsE4dG.js} +2 -2
- package/public/assets/{crate-agent-H6S7-B9T.js → crate-agent-30aXTKva.js} +2 -2
- package/public/assets/{dependency-graph-Cd1E23wa.js → dependency-graph-B_jvw2GE.js} +2 -2
- package/public/assets/{deploys-Bwfq146q.js → deploys-DoN6bAb5.js} +2 -2
- package/public/assets/{display-CfO7rTuF.js → display-BrKWFGJz.js} +2 -2
- package/public/assets/{files-DhhZI2Ih.js → files-D6XEu8Ng.js} +2 -2
- package/public/assets/fleet-C7jQWbw2.js +2 -0
- package/public/assets/{fleet-metrics-BkHP-P3R.js → fleet-metrics-BiDs3MLx.js} +2 -2
- package/public/assets/{formatted-body-Cm1RrzmN.js → formatted-body-CQBwc1fb.js} +3 -3
- package/public/assets/{formatted-body-impl-DQiOuOD1.js → formatted-body-impl-DyG0WsQS.js} +2 -2
- package/public/assets/{host-providers-dialog-CHqwXTov.js → host-providers-dialog-DKaW31dN.js} +2 -2
- package/public/assets/index-CRHep3M9.js +15 -0
- package/public/assets/index-MYAIwYTX.css +2 -0
- package/public/assets/{insights-fH1EL-qZ.js → insights-CzrtkpOE.js} +2 -2
- package/public/assets/{integrations-C5Oix6ng.js → integrations-aVsUmigt.js} +2 -2
- package/public/assets/{issue-ref-chip-ODyondIa.js → issue-ref-chip-BnSlM_95.js} +2 -2
- package/public/assets/{log-viewer-Cvfy6PW7.js → log-viewer-qe7Ee47Z.js} +2 -2
- package/public/assets/{maintenance-D8CDH5wZ.js → maintenance-NfjqFZAk.js} +2 -2
- package/public/assets/{managed-agents-egllbpij.js → managed-agents-BiH0HgaE.js} +2 -2
- package/public/assets/{markdown-preview-CYSFgVgK.js → markdown-preview-BixaT3Xr.js} +3 -3
- package/public/assets/{markdown-preview-impl-BteVIjX4.js → markdown-preview-impl-DNGlWIS3.js} +2 -2
- package/public/assets/{memory-C8WmXsyr.js → memory-DNsCl6UX.js} +2 -2
- package/public/assets/{messages-BzQdqlO6.js → messages-DopU1L1X.js} +2 -2
- package/public/assets/{orchestrators-B1HxzkNw.js → orchestrators-Cq7ckLQT.js} +2 -2
- package/public/assets/{overview-DCUnI4Z7.js → overview-C1DCTzzV.js} +2 -2
- package/public/assets/{pairs-ftCd2XcD.js → pairs-DE_m6rrE.js} +2 -2
- package/public/assets/{profile-icon-B2zH8DMH.js → profile-icon-B3rfh77F.js} +2 -2
- package/public/assets/project-profile-instructions-DQWZE0kA.js +2 -0
- package/public/assets/projects-Br2Bd2h1.js +2 -0
- package/public/assets/{prompt-settings-Bftq9czX.js → prompt-settings-BKvFsvGD.js} +2 -2
- package/public/assets/provider-display-DsuWMvVL.js +2 -0
- package/public/assets/{registry-DWCU5c8q.js → registry-Cw38_6gW.js} +2 -2
- package/public/assets/run-card-CpKqRbhg.js +2 -0
- package/public/assets/scroll-text-BKXS1ahr.js +2 -0
- package/public/assets/{secrets-C7GIUATz.js → secrets-DxI-vzSq.js} +2 -2
- package/public/assets/{service-access-settings-C3aujqTG.js → service-access-settings-D9s_VJbd.js} +2 -2
- package/public/assets/{settings-C6sZ9eBa.js → settings-CKZTVGIx.js} +2 -2
- package/public/assets/{shipping-Bu8p1gEw.js → shipping-B9YtnwtG.js} +2 -2
- package/public/assets/{stats-V9Z-GxUd.js → stats-DyUBKyWI.js} +2 -2
- package/public/assets/{status-dot-CVv0iGHL.js → status-dot-DYqlEUhJ.js} +2 -2
- package/public/assets/{store-BvtFdlGX.js → store-DcClXLH0.js} +3 -3
- package/public/assets/{tasks-CXU75HRF.js → tasks-Bh7Chr4-.js} +2 -2
- package/public/assets/{teams-BPf976zb.js → teams-bowI7qVY.js} +2 -2
- package/public/assets/{terminal-viewer-DE9BOe-8.js → terminal-viewer-BgVLHdsU.js} +3 -3
- package/public/assets/{terminal-viewer-impl-D04pjmGh.js → terminal-viewer-impl-Dz22UPy6.js} +2 -2
- package/public/assets/token-estimate-Co-Jdphh.js +2 -0
- package/public/assets/{use-agent-terminal-B1yR456R.js → use-agent-terminal-DSQQ-iY7.js} +2 -2
- package/public/assets/{use-decisions-D-S4tpDf.js → use-decisions-samMQhQf.js} +2 -2
- package/public/assets/use-project-list-59LheVP7.js +2 -0
- package/public/assets/{use-selectors-CnrAk2YP.js → use-selectors-BZa25xEB.js} +2 -2
- package/public/assets/{user-avatar-nBFIcR4M.js → user-avatar-DHxlvUvV.js} +2 -2
- package/public/assets/versioned-prompt-editor-BmED5DnJ.js +8 -0
- package/public/assets/{work-queue-TCjJxeT-.js → work-queue-CSvpuNLz.js} +2 -2
- package/public/assets/{workflow-marker-Cf3Hf2fN.js → workflow-marker-DAZcj5qt.js} +2 -2
- package/public/assets/{workflows-CXQX20Qj.js → workflows-DdmxRSyg.js} +2 -2
- package/public/assets/{workspaces-qSYzKYMA.js → workspaces-BxeKfVuF.js} +2 -2
- package/public/cosmos.html +4 -4
- package/public/index.html +31 -29
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/plugins/claude/hooks/gate-run-guard.provisioned.mjs +31 -1
- package/runner/src/adapters/claude-effort.ts +2 -4
- package/runner/src/adapters/codex-launch-config.ts +4 -5
- package/runner/src/adapters/codex.ts +1 -1
- package/runner/src/adapters/opencode-provider.ts +7 -22
- package/runner/src/adapters/opencode.ts +4 -4
- package/runner/src/agent-runner/credentials.ts +42 -12
- package/runner/src/agent-runner/inbox.ts +27 -0
- package/runner/src/agent-runner/lifecycle-milestones.ts +0 -2
- package/runner/src/bin/claude.ts +4 -0
- package/runner/src/bin/codex.ts +4 -0
- package/runner/src/bin/opencode.ts +4 -0
- package/runner/src/index.ts +5 -2
- package/runner/src/provider-setup.ts +34 -0
- package/runner/src/providers.ts +2 -1
- package/runner/src/runner-core.ts +18 -23
- package/runner/src/spawn-input.ts +2 -1
- package/runner/src/test-run-dedup.ts +59 -56
- package/scripts/cd-config.ts +24 -3
- package/scripts/gate-run-lease.ts +10 -7
- package/scripts/gate.sh +5 -1
- package/src/branch-land-notice.ts +93 -0
- package/src/branch-landed.ts +7 -76
- package/src/bus.ts +19 -20
- package/src/cd-deploy-notify.ts +30 -2
- package/src/cd-host-lag-notify.ts +7 -6
- package/src/ci/incidents.ts +2 -2
- package/src/ci/monitor.ts +5 -8
- package/src/ci/root-missing.ts +2 -7
- package/src/ci/transition-notify.ts +7 -39
- package/src/cli/cd-pauses.ts +19 -0
- package/src/cli/orchestrator.ts +1 -1
- package/src/cli/state.ts +2 -19
- package/src/cli/task-edit.ts +22 -0
- package/src/cli/task.ts +3 -1
- package/src/cli/workflow.ts +8 -2
- package/src/commands-db.ts +10 -6
- package/src/commands-write.ts +8 -0
- package/src/config-store-write.ts +13 -1
- package/src/config-store.ts +25 -14
- package/src/db/activity-async.ts +6 -1
- package/src/db/activity-write-ops.ts +13 -1
- package/src/db/activity.ts +1 -14
- package/src/db/agent-lifecycle-write-ops.ts +1 -0
- package/src/db/agent-meta.ts +2 -2
- package/src/db/agent-ownership-write-ops.ts +4 -0
- package/src/db/agent-search.ts +1 -15
- package/src/db/agents-async.ts +58 -7
- package/src/db/agents-write-ops.ts +28 -5
- package/src/db/agents.ts +81 -54
- package/src/db/artifacts-write-ops.ts +8 -3
- package/src/db/bus-outbox-write-ops.ts +1 -0
- package/src/db/channels-write-ops.ts +4 -0
- package/src/db/ci-incidents-async.ts +5 -5
- package/src/db/ci-incidents-write-ops.ts +2 -2
- package/src/db/claude-api-budget-ops.ts +5 -5
- package/src/db/claude-app-oauth.ts +4 -4
- package/src/db/codex-reset-consumes-write-ops.ts +3 -0
- package/src/db/command-row.ts +10 -2
- package/src/db/commands-list-query.ts +2 -0
- package/src/db/commands-spawn-read.ts +1 -1
- package/src/db/commands-write-ops.ts +21 -0
- package/src/db/config-store-write-ops.ts +9 -2
- package/src/db/continuation-archives-async.ts +5 -0
- package/src/db/continuation-archives-write-ops.ts +9 -0
- package/src/db/continuations.ts +2 -2
- package/src/db/db-writer-worker.ts +8 -1
- package/src/db/delivery-write-ops.ts +5 -0
- package/src/db/deploy-events-async.ts +1 -0
- package/src/db/deploy-events-read-ops.ts +2 -1
- package/src/db/deploy-events.ts +35 -4
- package/src/db/hosts-registration.ts +10 -2
- package/src/db/hosts-write-ops.ts +14 -0
- package/src/db/hosts.ts +9 -6
- package/src/db/inbox-write-ops.ts +4 -0
- package/src/db/inbox.ts +0 -14
- package/src/db/index.ts +2 -0
- package/src/db/insights-write-ops.ts +1 -0
- package/src/db/integrations-async.ts +8 -0
- package/src/db/integrations-ops.ts +15 -0
- package/src/db/integrations.ts +15 -21
- package/src/db/issue-lifecycle.ts +3 -4
- package/src/db/issue-refs-async.ts +20 -0
- package/src/db/issue-refs-write-ops.ts +29 -0
- package/src/db/issue-refs.ts +7 -8
- package/src/db/long-running-work.ts +2 -2
- package/src/db/macos-privacy.ts +1 -1
- package/src/db/main-connection-writes.ts +23 -2
- package/src/db/maintenance-job-reconcile.ts +61 -0
- package/src/db/maintenance-job-runs-async.ts +14 -0
- package/src/db/maintenance-job-write-ops.ts +9 -0
- package/src/db/mappers.ts +2 -14
- package/src/db/merge-lease.ts +1 -15
- package/src/db/message-reads-write-ops.ts +3 -0
- package/src/db/message-write-ops.ts +4 -0
- package/src/db/migrations.ts +7 -15
- package/src/db/orchestrators-async.ts +7 -3
- package/src/db/orchestrators-write-ops.ts +7 -1
- package/src/db/orchestrators.ts +21 -77
- package/src/db/pairs.ts +1 -13
- package/src/db/project-profile-instructions.ts +50 -0
- package/src/db/prompt-versions-ops.ts +1 -0
- package/src/db/prompt-versions.ts +9 -2
- package/src/db/provider-catalog-write-ops.ts +4 -4
- package/src/db/provider-config-history.ts +8 -3
- package/src/db/provider-flap-messages-write-ops.ts +1 -0
- package/src/db/provider-model-seed.ts +34 -0
- package/src/db/provider-quota-lease-write-ops.ts +1 -0
- package/src/db/provider-quota-write-ops.ts +2 -0
- package/src/db/provisioning-write-ops.ts +9 -8
- package/src/db/read-op-marshaling-fixtures.ts +5 -0
- package/src/db/read-ops.ts +4 -0
- package/src/db/routing-policy.ts +3 -4
- package/src/db/schema.ts +9 -3
- package/src/db/secrets-broker-write-ops.ts +5 -5
- package/src/db/secrets-vault-write.ts +7 -7
- package/src/db/services-write-ops.ts +3 -0
- package/src/db/spawn-delegation-state.ts +11 -4
- package/src/db/spawn-tree-cleanup.ts +3 -0
- package/src/db/stats-async.ts +4 -3
- package/src/db/task-entity.ts +23 -2
- package/src/db/task-list.ts +3 -1
- package/src/db/tasks.ts +1 -17
- package/src/db/team-spawn-delegation.ts +1 -1
- package/src/db/teams-write-ops.ts +4 -4
- package/src/db/timer-cutover-write-ops.ts +7 -0
- package/src/db/timer-ingress-write-ops.ts +9 -0
- package/src/db/timer-ingress.ts +7 -1
- package/src/db/timer-operator-write-ops.ts +14 -0
- package/src/db/timer-transfer-write-ops.ts +4 -0
- package/src/db/timer-write-ops.ts +14 -0
- package/src/db/token-db-write-ops.ts +21 -0
- package/src/db/token-standing.ts +15 -11
- package/src/db/user-settings-write-ops.ts +1 -0
- package/src/db/vault-backup.ts +2 -2
- package/src/db/vault-expiry.ts +2 -2
- package/src/db/vault-health.ts +3 -3
- package/src/db/vault-token-ops.ts +8 -8
- package/src/db/web-tool-call-ops.ts +3 -3
- package/src/db/workflow-contact.ts +1 -1
- package/src/db/workflow-task-read-ops.ts +30 -0
- package/src/db/write-ops.ts +15 -1
- package/src/db/writer-fault-seam.ts +5 -2
- package/src/db/writer-gateway.ts +21 -12
- package/src/dispatch-availability.ts +10 -6
- package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts} +4 -2
- package/src/host/disk-alert.ts +121 -0
- package/src/lifecycle-manager.ts +3 -2
- package/src/maintenance/cd-watcher.ts +3 -3
- package/src/maintenance/critical-job-watchdog.ts +16 -17
- package/src/maintenance/index.ts +1 -0
- package/src/maintenance/jobs.ts +3 -2
- package/src/maintenance/scheduler.ts +84 -128
- package/src/maintenance/stalled-delivery-pager.ts +6 -5
- package/src/maintenance/workspaces.ts +1 -1
- package/src/managed-policy.ts +31 -27
- package/src/mcp/index.ts +5 -2
- package/src/mcp/output-descriptions.ts +8 -8
- package/src/mcp/presentation.ts +4 -4
- package/src/mcp/project-profile-instructions.ts +32 -0
- package/src/mcp/tool-groups.ts +1 -1
- package/src/mcp/tools-spawn.ts +4 -3
- package/src/mcp-agent-action.ts +1 -2
- package/src/mcp-host-tools.ts +1 -1
- package/src/mcp-issue-ref-tools.ts +12 -11
- package/src/mcp-maintenance-tools.ts +2 -2
- package/src/mcp-profile-trust-tools.ts +4 -4
- package/src/mcp-provider-catalog.ts +2 -2
- package/src/mcp-task-tools.ts +11 -5
- package/src/mcp-team-template-tools.ts +3 -3
- package/src/mcp-workflow-tools.ts +10 -3
- package/src/notification-types.ts +2 -1
- package/src/operator-alarm.ts +26 -11
- package/src/package-pack.ts +42 -39
- package/src/project-instructions.ts +4 -0
- package/src/project-profile-instruction-text.ts +6 -0
- package/src/project-profile-instructions.ts +20 -0
- package/src/provider-catalog-store.ts +3 -49
- package/src/provider-config-store.ts +12 -11
- package/src/provider-model-seed.ts +1 -18
- package/src/provider-quota-config-store.ts +5 -4
- package/src/provider-secrets.test-fixture.ts +3 -3
- package/src/recipe-runner.ts +2 -2
- package/src/repo-config-source.ts +10 -4
- package/src/routes/agent-profiles.ts +6 -6
- package/src/routes/agents-spawn.ts +1 -1
- package/src/routes/agents.ts +6 -6
- package/src/routes/commands-listing.ts +4 -0
- package/src/routes/commands.ts +2 -2
- package/src/routes/continuation-archives.ts +2 -2
- package/src/routes/host-resource-control.ts +4 -1
- package/src/routes/index.ts +10 -1
- package/src/routes/integrations.ts +24 -50
- package/src/routes/issue-refs.ts +7 -7
- package/src/routes/orchestrator-bootstrap.ts +3 -3
- package/src/routes/orchestrator-host-registration.ts +2 -2
- package/src/routes/orchestrator-host-utilization.ts +3 -1
- package/src/routes/orchestrator-proxy.ts +2 -2
- package/src/routes/orchestrator.ts +4 -4
- package/src/routes/project-profile-instructions.ts +27 -0
- package/src/routes/project-trust.ts +2 -2
- package/src/routes/provider-config.ts +9 -8
- package/src/routes/provider-quota-config.ts +2 -2
- package/src/routes/settings.ts +5 -5
- package/src/routes/stats.ts +6 -6
- package/src/routes/task-http.ts +15 -1
- package/src/routes/task-issues.ts +11 -26
- package/src/routes/tasks.ts +22 -53
- package/src/routes/team-templates.ts +5 -5
- package/src/routes/teams-config.ts +2 -2
- package/src/routes/tokens.ts +12 -5
- package/src/routes/workflows.ts +20 -5
- package/src/routing-policy-normalize.ts +1 -2
- package/src/routing-step-defaults.ts +0 -2
- package/src/runner-token-mint-limit.ts +63 -0
- package/src/runtime-grant.ts +4 -2
- package/src/runtime-tokens.ts +50 -9
- package/src/security.ts +13 -20
- package/src/server-background.ts +1 -1
- package/src/server.ts +5 -2
- package/src/services/host-utilization.ts +23 -0
- package/src/services/idle-epics.ts +5 -6
- package/src/services/issue-lifecycle.ts +57 -61
- package/src/services/issue-refs.ts +9 -13
- package/src/services/kernel-timers-cutover-chaos-worker.ts +12 -7
- package/src/services/kernel-timers-ingress.ts +1 -1
- package/src/services/kernel-timers-request-key-migration-chaos-worker.ts +4 -1
- package/src/services/landed-ancestry.ts +19 -3
- package/src/services/memory-areas.ts +15 -51
- package/src/services/plan-ci-notice.ts +57 -0
- package/src/services/plan-ci-recovery.ts +2 -55
- package/src/services/plan-wake-digest.ts +1 -0
- package/src/services/profile-trust.ts +4 -3
- package/src/services/project-config-read.ts +70 -0
- package/src/services/project-profile-instructions.ts +54 -0
- package/src/services/provisioning-harness/budget-baseline.json +4 -4
- package/src/services/restart-spawn.ts +61 -49
- package/src/services/self-care-state.ts +2 -1
- package/src/services/self-effort.ts +5 -5
- package/src/services/send-message.ts +5 -1
- package/src/services/spawn-agent.ts +13 -2
- package/src/services/spawn-chain-inspector.ts +6 -13
- package/src/services/spawn-policy.ts +68 -0
- package/src/services/spawn-tree-cleanup.ts +2 -2
- package/src/services/task-dispatch.ts +3 -0
- package/src/services/task-state-reconcile.ts +6 -14
- package/src/services/team-templates.ts +9 -8
- package/src/services/transient-agent-reaper.ts +3 -1
- package/src/services/workflow-auto-start.ts +25 -13
- package/src/services/workflow-ci-main.ts +1 -1
- package/src/services/workflow-deferred-cleanup.ts +4 -2
- package/src/services/workflow-effect-driver.ts +2 -0
- package/src/services/workflow-effects.ts +18 -11
- package/src/services/workflow-land-notice.ts +10 -0
- package/src/services/workflow-pause-text.ts +43 -0
- package/src/services/workflow-reconcile.ts +13 -11
- package/src/services/workflow-run-view.ts +2 -0
- package/src/services/workflow-runs.ts +29 -13
- package/src/services/workflow-supervisor-state.ts +5 -0
- package/src/services/workflow-supervisor.ts +7 -3
- package/src/settings/registry.ts +1 -2
- package/src/spawn-command.ts +4 -4
- package/src/spawn-models.ts +10 -12
- package/src/spawn-placement.ts +1 -1
- package/src/spawn-targets.ts +1 -1
- package/src/startup-context-injection.ts +1 -0
- package/src/state-adoption/cd-cutover.ts +53 -7
- package/src/state-adoption/fleet-cutover.ts +35 -0
- package/src/state-adoption/fleet-plan.ts +36 -0
- package/src/state-adoption/index.ts +2 -2
- package/src/state-adoption/linux-plan.ts +2 -2
- package/src/state-adoption/linux.ts +2 -2
- package/src/state-adoption/macos-plan.ts +2 -2
- package/src/state-adoption/macos.ts +1 -1
- package/src/steward.ts +7 -6
- package/src/task-dispatch-live.ts +1 -1
- package/src/task-dispatcher.ts +1 -1
- package/src/teams-config-store.ts +4 -3
- package/src/token-db-write.ts +10 -0
- package/src/workspace-command-settle.ts +2 -1
- package/src/workspace-steward-wake.ts +2 -2
- package/node_modules/@humwick/kernel/LICENSE +0 -167
- package/node_modules/@humwick/kernel/async-entity-persistence.ts +0 -344
- package/node_modules/@humwick/kernel/async-event-log.ts +0 -107
- package/node_modules/@humwick/kernel/automations.pilot.ts +0 -147
- package/node_modules/@humwick/kernel/config.ts +0 -111
- package/node_modules/@humwick/kernel/contract.ts +0 -572
- package/node_modules/@humwick/kernel/control-authz.ts +0 -281
- package/node_modules/@humwick/kernel/control.ts +0 -645
- package/node_modules/@humwick/kernel/effects.ts +0 -493
- package/node_modules/@humwick/kernel/event-log.ts +0 -319
- package/node_modules/@humwick/kernel/http-transport.ts +0 -384
- package/node_modules/@humwick/kernel/identity-grammar.ts +0 -20
- package/node_modules/@humwick/kernel/identity.ts +0 -248
- package/node_modules/@humwick/kernel/index.ts +0 -28
- package/node_modules/@humwick/kernel/kernel-persistence.ts +0 -181
- package/node_modules/@humwick/kernel/landing-incident.ts +0 -306
- package/node_modules/@humwick/kernel/landing.ts +0 -953
- package/node_modules/@humwick/kernel/manifest-validation.ts +0 -460
- package/node_modules/@humwick/kernel/mcp-transport.ts +0 -109
- package/node_modules/@humwick/kernel/message-authority-chaos-worker.ts +0 -200
- package/node_modules/@humwick/kernel/message-authority.ts +0 -281
- package/node_modules/@humwick/kernel/module-host.ts +0 -722
- package/node_modules/@humwick/kernel/openapi-init.ts +0 -14
- package/node_modules/@humwick/kernel/package.json +0 -47
- package/node_modules/@humwick/kernel/persistence-schema.ts +0 -248
- package/node_modules/@humwick/kernel/persistence.ts +0 -510
- package/node_modules/@humwick/kernel/registry.ts +0 -60
- package/node_modules/@humwick/kernel/sqlite-kernel-persistence.ts +0 -499
- package/node_modules/@humwick/kernel/sqlite-module-host.ts +0 -87
- package/node_modules/@humwick/kernel/timers-chaos-worker.ts +0 -186
- package/node_modules/@humwick/kernel/timers.pilot.ts +0 -1411
- package/node_modules/@humwick/kernel/workflow-activation.ts +0 -25
- package/node_modules/@humwick/kernel/workflow-backstop.ts +0 -40
- package/node_modules/@humwick/kernel/workflow-cancel-tree.ts +0 -20
- package/node_modules/@humwick/kernel/workflow-chaos-worker.ts +0 -213
- package/node_modules/@humwick/kernel/workflow-composition.ts +0 -231
- package/node_modules/@humwick/kernel/workflow-container.ts +0 -72
- package/node_modules/@humwick/kernel/workflow-control.test-fixture.ts +0 -42
- package/node_modules/@humwick/kernel/workflow-control.ts +0 -178
- package/node_modules/@humwick/kernel/workflow-definition.ts +0 -507
- package/node_modules/@humwick/kernel/workflow-dispatch.ts +0 -60
- package/node_modules/@humwick/kernel/workflow-foreach.ts +0 -167
- package/node_modules/@humwick/kernel/workflow-journal-codec.ts +0 -339
- package/node_modules/@humwick/kernel/workflow-journal.ts +0 -922
- package/node_modules/@humwick/kernel/workflow-lane.ts +0 -48
- package/node_modules/@humwick/kernel/workflow-lease-owner.ts +0 -60
- package/node_modules/@humwick/kernel/workflow-recovery.test-fixture.ts +0 -19
- package/node_modules/@humwick/kernel/workflow-runtime.ts +0 -938
- package/node_modules/@humwick/kernel/workflow-script.ts +0 -11
- package/node_modules/@humwick/kernel/workflow-state.ts +0 -299
- package/node_modules/@humwick/kernel/workflow-step-commit.ts +0 -195
- package/node_modules/@humwick/kernel/workflow-transitions.ts +0 -317
- package/node_modules/@humwick/kernel/workflow-values.ts +0 -153
- package/node_modules/@humwick/kernel/workflow.pilot.ts +0 -376
- package/node_modules/@humwick/offload/LICENSE +0 -167
- package/node_modules/@humwick/offload/README.md +0 -57
- package/node_modules/@humwick/offload/dist/async-runner.d.ts +0 -18
- package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/async-runner.js +0 -158
- package/node_modules/@humwick/offload/dist/async-runner.js.map +0 -1
- package/node_modules/@humwick/offload/dist/envelope.d.ts +0 -25
- package/node_modules/@humwick/offload/dist/envelope.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/envelope.js +0 -80
- package/node_modules/@humwick/offload/dist/envelope.js.map +0 -1
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +0 -10
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +0 -59
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +0 -1
- package/node_modules/@humwick/offload/dist/index.d.ts +0 -5
- package/node_modules/@humwick/offload/dist/index.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/index.js +0 -5
- package/node_modules/@humwick/offload/dist/index.js.map +0 -1
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +0 -62
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/subprocess-runner.js +0 -295
- package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +0 -1
- package/node_modules/@humwick/offload/dist/types.d.ts +0 -111
- package/node_modules/@humwick/offload/dist/types.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/types.js +0 -176
- package/node_modules/@humwick/offload/dist/types.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts +0 -23
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-pool.js +0 -435
- package/node_modules/@humwick/offload/dist/worker-pool.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +0 -2
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-runtime.js +0 -27
- package/node_modules/@humwick/offload/dist/worker-runtime.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker.d.ts +0 -5
- package/node_modules/@humwick/offload/dist/worker.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker.js +0 -1
- package/node_modules/@humwick/offload/dist/worker.js.map +0 -1
- package/node_modules/@humwick/offload/package.json +0 -46
- package/node_modules/@humwick/offload/src/async-runner.ts +0 -170
- package/node_modules/@humwick/offload/src/envelope.ts +0 -92
- package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +0 -49
- package/node_modules/@humwick/offload/src/index.ts +0 -4
- package/node_modules/@humwick/offload/src/subprocess-runner.ts +0 -334
- package/node_modules/@humwick/offload/src/types.ts +0 -235
- package/node_modules/@humwick/offload/src/worker-pool.ts +0 -463
- package/node_modules/@humwick/offload/src/worker-runtime.ts +0 -32
- package/node_modules/@humwick/offload/src/worker.ts +0 -5
- package/public/assets/agent-liveness-DyqxfB-t.js +0 -2
- package/public/assets/agent-profiles-BpI_jTxq.js +0 -2
- package/public/assets/fleet-BL4UXS_k.js +0 -2
- package/public/assets/index-B7ZerHLY.css +0 -2
- package/public/assets/index-BK0WV6hF.js +0 -15
- package/public/assets/projects-B_lmcqfO.js +0 -2
- package/public/assets/provider-display-DbLWpP3j.js +0 -2
- package/public/assets/run-card-CO_bguQf.js +0 -2
- package/public/assets/use-project-list-B-T2Bnnn.js +0 -2
- package/public/assets/versioned-prompt-editor-Diu8BIaM.js +0 -8
- package/runner/src/gate-run-lease-probe.ts +0 -38
- package/src/db/claude-catalog-reconcile.ts +0 -27
- package/src/routing-model-aliases.ts +0 -21
|
@@ -1,510 +0,0 @@
|
|
|
1
|
-
import type { Database } from "bun:sqlite";
|
|
2
|
-
import { Buffer } from "node:buffer";
|
|
3
|
-
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
4
|
-
import type {
|
|
5
|
-
Clock,
|
|
6
|
-
EntityDecl,
|
|
7
|
-
EntityListOptions,
|
|
8
|
-
EntityPage,
|
|
9
|
-
EntityStore,
|
|
10
|
-
EntityStores,
|
|
11
|
-
ModuleDefinition,
|
|
12
|
-
ModuleManifest,
|
|
13
|
-
Scope,
|
|
14
|
-
} from "./contract";
|
|
15
|
-
import { assertValidManifest } from "./manifest-validation";
|
|
16
|
-
import {
|
|
17
|
-
decodeField,
|
|
18
|
-
encodeField,
|
|
19
|
-
installModuleEntities,
|
|
20
|
-
type EntityField,
|
|
21
|
-
type EntityTable,
|
|
22
|
-
} from "./persistence-schema";
|
|
23
|
-
|
|
24
|
-
export const DEFAULT_ENTITY_PAGE_SIZE = 50;
|
|
25
|
-
export const MAX_ENTITY_PAGE_SIZE = 200;
|
|
26
|
-
|
|
27
|
-
export class EntityNotFoundError extends Error {
|
|
28
|
-
constructor(readonly moduleId: string, readonly entityName: string, readonly key: unknown) {
|
|
29
|
-
super(`entity '${moduleId}.${entityName}' with key '${String(key)}' was not found`);
|
|
30
|
-
this.name = "EntityNotFoundError";
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export class EntityConflictError extends Error {
|
|
35
|
-
constructor(readonly moduleId: string, readonly entityName: string, readonly key: unknown) {
|
|
36
|
-
super(`stale entity version for '${moduleId}.${entityName}' key '${String(key)}'`);
|
|
37
|
-
this.name = "EntityConflictError";
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export class EntitySessionClosedError extends Error {
|
|
42
|
-
constructor() {
|
|
43
|
-
super("entity session is closed");
|
|
44
|
-
this.name = "EntitySessionClosedError";
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
type EntityValue = Record<string, unknown>;
|
|
49
|
-
type StoredRow = Record<string, unknown> & { version: number };
|
|
50
|
-
type Mutation =
|
|
51
|
-
| { readonly kind: "put"; readonly table: EntityTable; readonly key: unknown; readonly expectedVersion: number | null; value: EntityValue }
|
|
52
|
-
| { readonly kind: "delete"; readonly table: EntityTable; readonly key: unknown; readonly expectedVersion: number };
|
|
53
|
-
|
|
54
|
-
interface RowState {
|
|
55
|
-
readonly table: EntityTable;
|
|
56
|
-
readonly key: unknown;
|
|
57
|
-
readonly expectedVersion: number | null;
|
|
58
|
-
value?: EntityValue;
|
|
59
|
-
deleted?: boolean;
|
|
60
|
-
mutation?: Mutation;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
interface CursorPayload {
|
|
64
|
-
readonly v: 2;
|
|
65
|
-
readonly module: string;
|
|
66
|
-
readonly entity: string;
|
|
67
|
-
readonly scope: string;
|
|
68
|
-
readonly type: string;
|
|
69
|
-
readonly key: string | number;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function signCursorBody(secret: Buffer, body: string): string {
|
|
73
|
-
return createHmac("sha256", secret).update(body).digest("base64url");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Length-prefixed (not delimited) so no field value, however constructed, can forge a collision. */
|
|
77
|
-
function keyIdentity(table: EntityTable, key: unknown): string {
|
|
78
|
-
const encoded = encodeField(table.key, key);
|
|
79
|
-
const parts = [table.name, typeof encoded, String(encoded)];
|
|
80
|
-
return parts.map((part) => `${part.length}:${part}`).join("");
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function rowValue(table: EntityTable, row: StoredRow): EntityValue {
|
|
84
|
-
const raw: EntityValue = {};
|
|
85
|
-
for (const field of table.fields) {
|
|
86
|
-
const value = decodeField(field, row[field.name]);
|
|
87
|
-
if (value !== undefined) raw[field.name] = value;
|
|
88
|
-
}
|
|
89
|
-
return table.decl.schema.parse(raw) as EntityValue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function encodeCursor(secret: Buffer, table: EntityTable, scope: Scope, key: unknown): string {
|
|
93
|
-
const encoded = encodeField(table.key, key);
|
|
94
|
-
const payload: CursorPayload = {
|
|
95
|
-
v: 2,
|
|
96
|
-
module: table.moduleId,
|
|
97
|
-
entity: table.entityName,
|
|
98
|
-
scope,
|
|
99
|
-
type: typeof encoded,
|
|
100
|
-
key: typeof encoded === "bigint" ? encoded.toString() : encoded as string | number,
|
|
101
|
-
};
|
|
102
|
-
const body = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
103
|
-
return `${body}.${signCursorBody(secret, body)}`;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function decodeCursor(secret: Buffer, table: EntityTable, scope: Scope, cursor: string): unknown {
|
|
107
|
-
try {
|
|
108
|
-
const separator = cursor.lastIndexOf(".");
|
|
109
|
-
if (separator < 0) throw new Error();
|
|
110
|
-
const body = cursor.slice(0, separator);
|
|
111
|
-
const signature = Buffer.from(cursor.slice(separator + 1));
|
|
112
|
-
const expected = Buffer.from(signCursorBody(secret, body));
|
|
113
|
-
if (signature.length !== expected.length || !timingSafeEqual(signature, expected)) throw new Error();
|
|
114
|
-
const payload = JSON.parse(Buffer.from(body, "base64url").toString("utf8")) as CursorPayload;
|
|
115
|
-
if (payload.v !== 2 || payload.module !== table.moduleId || payload.entity !== table.entityName || payload.scope !== scope) {
|
|
116
|
-
throw new Error();
|
|
117
|
-
}
|
|
118
|
-
if (payload.type === "bigint") return BigInt(payload.key);
|
|
119
|
-
if (payload.type !== typeof payload.key) throw new Error();
|
|
120
|
-
return payload.key;
|
|
121
|
-
} catch {
|
|
122
|
-
throw new TypeError(`invalid cursor for entity '${table.moduleId}.${table.entityName}'`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function compareKeys(table: EntityTable, left: unknown, right: unknown): number {
|
|
127
|
-
const a = encodeField(table.key, left);
|
|
128
|
-
const b = encodeField(table.key, right);
|
|
129
|
-
if (a === b) return 0;
|
|
130
|
-
return a! < b! ? -1 : 1;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function parsedWhere(table: EntityTable, where: Readonly<Record<string, unknown>> | undefined): ReadonlyMap<EntityField, unknown> {
|
|
134
|
-
const result = new Map<EntityField, unknown>();
|
|
135
|
-
for (const [name, value] of Object.entries(where ?? {})) {
|
|
136
|
-
const field = table.fieldByName.get(name);
|
|
137
|
-
if (!field) throw new TypeError(`unknown where field '${name}' on '${table.moduleId}.${table.entityName}'`);
|
|
138
|
-
result.set(field, field.schema.parse(value));
|
|
139
|
-
}
|
|
140
|
-
return result;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function matchesWhere(value: EntityValue, where: ReadonlyMap<EntityField, unknown>): boolean {
|
|
144
|
-
for (const [field, expected] of where) {
|
|
145
|
-
if (encodeField(field, value[field.name]) !== encodeField(field, expected)) return false;
|
|
146
|
-
}
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function pageSize(limit: number | undefined): number {
|
|
151
|
-
const resolved = limit ?? DEFAULT_ENTITY_PAGE_SIZE;
|
|
152
|
-
if (!Number.isInteger(resolved) || resolved < 1 || resolved > MAX_ENTITY_PAGE_SIZE) {
|
|
153
|
-
throw new RangeError(`entity page limit must be an integer from 1 to ${MAX_ENTITY_PAGE_SIZE}`);
|
|
154
|
-
}
|
|
155
|
-
return resolved;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export interface EntitySession<E> {
|
|
159
|
-
/** Scope-bound stores installed directly as ModuleCtx.entities. */
|
|
160
|
-
readonly entities: EntityStores<E>;
|
|
161
|
-
readonly pendingMutations: number;
|
|
162
|
-
/** Close the async attempt before entering its synchronous commit transaction. */
|
|
163
|
-
seal(): void;
|
|
164
|
-
/** Flush into a caller-owned synchronous transaction (for workflow checkpoint composition). */
|
|
165
|
-
flush(): void;
|
|
166
|
-
/** Seal and flush in a standalone synchronous transaction. */
|
|
167
|
-
commit(): void;
|
|
168
|
-
/** Close the session and forget every buffered mutation. */
|
|
169
|
-
discard(): void;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
class EntitySessionRuntime<E> implements EntitySession<E> {
|
|
173
|
-
readonly entities: EntityStores<E>;
|
|
174
|
-
readonly #states = new Map<string, RowState>();
|
|
175
|
-
#closed = false;
|
|
176
|
-
#finished = false;
|
|
177
|
-
|
|
178
|
-
constructor(
|
|
179
|
-
readonly db: Database,
|
|
180
|
-
readonly clock: Clock,
|
|
181
|
-
readonly scope: Scope,
|
|
182
|
-
readonly cursorSecret: Buffer,
|
|
183
|
-
tables: ReadonlyMap<string, EntityTable>,
|
|
184
|
-
) {
|
|
185
|
-
const stores: Record<string, EntityStore> = {};
|
|
186
|
-
for (const [name, table] of tables) stores[name] = this.#store(table);
|
|
187
|
-
this.entities = Object.freeze(stores) as EntityStores<E>;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
get pendingMutations(): number {
|
|
191
|
-
let count = 0;
|
|
192
|
-
for (const state of this.#states.values()) if (state.mutation) count++;
|
|
193
|
-
return count;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
#assertOpen(): void {
|
|
197
|
-
if (this.#closed) throw new EntitySessionClosedError();
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
#read(table: EntityTable, key: unknown): RowState {
|
|
201
|
-
const identity = keyIdentity(table, key);
|
|
202
|
-
const known = this.#states.get(identity);
|
|
203
|
-
if (known) return known;
|
|
204
|
-
const columns = [...table.fields.map((field) => field.quoted), "version"].join(", ");
|
|
205
|
-
const row = this.db.query(`SELECT ${columns} FROM ${table.quoted} WHERE scope = ? AND ${table.key.quoted} = ?`)
|
|
206
|
-
.get(this.scope, encodeField(table.key, key)) as StoredRow | null;
|
|
207
|
-
const state: RowState = {
|
|
208
|
-
table,
|
|
209
|
-
key,
|
|
210
|
-
expectedVersion: row?.version ?? null,
|
|
211
|
-
value: row ? rowValue(table, row) : undefined,
|
|
212
|
-
};
|
|
213
|
-
this.#states.set(identity, state);
|
|
214
|
-
return state;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
#remember(table: EntityTable, row: StoredRow): RowState {
|
|
218
|
-
const value = rowValue(table, row);
|
|
219
|
-
const key = value[table.key.name];
|
|
220
|
-
const identity = keyIdentity(table, key);
|
|
221
|
-
const known = this.#states.get(identity);
|
|
222
|
-
if (known) return known;
|
|
223
|
-
const state: RowState = { table, key, expectedVersion: row.version, value };
|
|
224
|
-
this.#states.set(identity, state);
|
|
225
|
-
return state;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
#store(table: EntityTable): EntityStore {
|
|
229
|
-
return {
|
|
230
|
-
get: async (key) => {
|
|
231
|
-
this.#assertOpen();
|
|
232
|
-
const state = this.#read(table, key);
|
|
233
|
-
return state.deleted ? undefined : state.value;
|
|
234
|
-
},
|
|
235
|
-
list: async (options) => this.#list(table, options as EntityListOptions<EntityValue> | undefined),
|
|
236
|
-
create: async (input) => {
|
|
237
|
-
this.#assertOpen();
|
|
238
|
-
const value = table.decl.schema.parse(input) as EntityValue;
|
|
239
|
-
const key = value[table.key.name];
|
|
240
|
-
const state = this.#read(table, key);
|
|
241
|
-
if (!state.deleted && state.value !== undefined) throw new EntityConflictError(table.moduleId, table.entityName, key);
|
|
242
|
-
state.value = value;
|
|
243
|
-
state.deleted = false;
|
|
244
|
-
state.mutation = { kind: "put", table, key, expectedVersion: state.expectedVersion, value };
|
|
245
|
-
return value;
|
|
246
|
-
},
|
|
247
|
-
update: async (key, patch) => {
|
|
248
|
-
this.#assertOpen();
|
|
249
|
-
const state = this.#read(table, key);
|
|
250
|
-
if (state.deleted || !state.value) throw new EntityNotFoundError(table.moduleId, table.entityName, key);
|
|
251
|
-
const value = table.decl.schema.parse({ ...state.value, ...patch }) as EntityValue;
|
|
252
|
-
if (keyIdentity(table, value[table.key.name]) !== keyIdentity(table, key)) {
|
|
253
|
-
throw new TypeError(`entity key '${table.key.name}' cannot be changed`);
|
|
254
|
-
}
|
|
255
|
-
state.value = value;
|
|
256
|
-
state.mutation = { kind: "put", table, key, expectedVersion: state.expectedVersion, value };
|
|
257
|
-
return value;
|
|
258
|
-
},
|
|
259
|
-
delete: async (key) => {
|
|
260
|
-
this.#assertOpen();
|
|
261
|
-
const state = this.#read(table, key);
|
|
262
|
-
if (state.deleted || !state.value) return false;
|
|
263
|
-
state.deleted = true;
|
|
264
|
-
state.value = undefined;
|
|
265
|
-
if (state.expectedVersion === null) state.mutation = undefined;
|
|
266
|
-
else state.mutation = { kind: "delete", table, key, expectedVersion: state.expectedVersion };
|
|
267
|
-
return true;
|
|
268
|
-
},
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
async #list(table: EntityTable, options: EntityListOptions<EntityValue> | undefined): Promise<EntityPage<EntityValue>> {
|
|
273
|
-
this.#assertOpen();
|
|
274
|
-
const limit = pageSize(options?.limit);
|
|
275
|
-
const where = parsedWhere(table, options?.where);
|
|
276
|
-
const cursorKey = options?.cursor ? decodeCursor(this.cursorSecret, table, this.scope, options.cursor) : undefined;
|
|
277
|
-
const clauses = ["scope = ?"];
|
|
278
|
-
const bindings: Array<string | number | bigint | null> = [this.scope];
|
|
279
|
-
for (const [field, value] of where) {
|
|
280
|
-
const encoded = encodeField(field, value);
|
|
281
|
-
clauses.push(encoded === null ? `${field.quoted} IS NULL` : `${field.quoted} = ?`);
|
|
282
|
-
if (encoded !== null) bindings.push(encoded);
|
|
283
|
-
}
|
|
284
|
-
if (cursorKey !== undefined) {
|
|
285
|
-
clauses.push(`${table.key.quoted} > ?`);
|
|
286
|
-
bindings.push(encodeField(table.key, cursorKey));
|
|
287
|
-
}
|
|
288
|
-
const stagedCount = [...this.#states.values()].filter((state) => state.table === table && state.mutation).length;
|
|
289
|
-
bindings.push(limit + stagedCount + 1);
|
|
290
|
-
const columns = [...table.fields.map((field) => field.quoted), "version"].join(", ");
|
|
291
|
-
const rows = this.db.query(`SELECT ${columns} FROM ${table.quoted}
|
|
292
|
-
WHERE ${clauses.join(" AND ")} ORDER BY ${table.key.quoted} ASC LIMIT ?`).all(...bindings) as StoredRow[];
|
|
293
|
-
|
|
294
|
-
const candidates = new Map<string, EntityValue>();
|
|
295
|
-
for (const row of rows) {
|
|
296
|
-
const state = this.#remember(table, row);
|
|
297
|
-
if (!state.deleted && state.value && matchesWhere(state.value, where)) {
|
|
298
|
-
candidates.set(keyIdentity(table, state.key), state.value);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
for (const state of this.#states.values()) {
|
|
302
|
-
if (state.table !== table || state.deleted || !state.value || !matchesWhere(state.value, where)) continue;
|
|
303
|
-
if (cursorKey !== undefined && compareKeys(table, state.key, cursorKey) <= 0) continue;
|
|
304
|
-
candidates.set(keyIdentity(table, state.key), state.value);
|
|
305
|
-
}
|
|
306
|
-
const sorted = [...candidates.values()].sort((a, b) => compareKeys(table, a[table.key.name], b[table.key.name]));
|
|
307
|
-
const hasMore = sorted.length > limit;
|
|
308
|
-
const items = sorted.slice(0, limit);
|
|
309
|
-
return {
|
|
310
|
-
items,
|
|
311
|
-
...(hasMore ? { cursor: encodeCursor(this.cursorSecret, table, this.scope, items[items.length - 1]![table.key.name]) } : {}),
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
seal(): void {
|
|
316
|
-
if (this.#finished) throw new EntitySessionClosedError();
|
|
317
|
-
this.#closed = true;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
#flushInsideTransaction(): void {
|
|
321
|
-
if (!this.#closed || this.#finished) throw new EntitySessionClosedError();
|
|
322
|
-
if (!this.db.inTransaction) throw new Error("entity mutations must flush inside a synchronous SQLite transaction");
|
|
323
|
-
try {
|
|
324
|
-
for (const state of this.#states.values()) {
|
|
325
|
-
const mutation = state.mutation;
|
|
326
|
-
if (!mutation) continue;
|
|
327
|
-
const now = this.clock.now();
|
|
328
|
-
let changes: number;
|
|
329
|
-
if (mutation.kind === "delete") {
|
|
330
|
-
changes = this.db.query(`DELETE FROM ${mutation.table.quoted}
|
|
331
|
-
WHERE scope = ? AND ${mutation.table.key.quoted} = ? AND version = ?`)
|
|
332
|
-
.run(this.scope, encodeField(mutation.table.key, mutation.key), mutation.expectedVersion).changes;
|
|
333
|
-
} else if (mutation.expectedVersion === null) {
|
|
334
|
-
const fields = mutation.table.fields;
|
|
335
|
-
const columns = ["scope", ...fields.map((field) => field.quoted), "version", "created_at", "updated_at"];
|
|
336
|
-
const placeholders = columns.map(() => "?").join(", ");
|
|
337
|
-
changes = this.db.query(`INSERT INTO ${mutation.table.quoted} (${columns.join(", ")})
|
|
338
|
-
VALUES (${placeholders}) ON CONFLICT(scope, ${mutation.table.key.quoted}) DO NOTHING`)
|
|
339
|
-
.run(this.scope, ...fields.map((field) => encodeField(field, mutation.value[field.name])), 0, now, now).changes;
|
|
340
|
-
} else {
|
|
341
|
-
const fields = mutation.table.fields.filter((field) => field !== mutation.table.key);
|
|
342
|
-
const assignments = [
|
|
343
|
-
...fields.map((field) => `${field.quoted} = ?`),
|
|
344
|
-
"version = version + 1",
|
|
345
|
-
"updated_at = ?",
|
|
346
|
-
];
|
|
347
|
-
changes = this.db.query(`UPDATE ${mutation.table.quoted}
|
|
348
|
-
SET ${assignments.join(", ")}
|
|
349
|
-
WHERE scope = ? AND ${mutation.table.key.quoted} = ? AND version = ?`)
|
|
350
|
-
.run(...fields.map((field) => encodeField(field, mutation.value[field.name])), now, this.scope,
|
|
351
|
-
encodeField(mutation.table.key, mutation.key), mutation.expectedVersion).changes;
|
|
352
|
-
}
|
|
353
|
-
if (changes !== 1) throw new EntityConflictError(mutation.table.moduleId, mutation.table.entityName, mutation.key);
|
|
354
|
-
}
|
|
355
|
-
this.#finished = true;
|
|
356
|
-
} catch (error) {
|
|
357
|
-
this.#finished = true;
|
|
358
|
-
throw error;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
flush(): void {
|
|
363
|
-
this.#flushInsideTransaction();
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
commit(): void {
|
|
367
|
-
if (!this.#closed) this.seal();
|
|
368
|
-
this.db.transaction(() => this.#flushInsideTransaction())();
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
discard(): void {
|
|
372
|
-
if (this.#finished) throw new EntitySessionClosedError();
|
|
373
|
-
this.#closed = true;
|
|
374
|
-
this.#states.clear();
|
|
375
|
-
this.#finished = true;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
export interface EntityPersistenceOptions {
|
|
380
|
-
readonly db: Database;
|
|
381
|
-
readonly clock: Clock;
|
|
382
|
-
readonly modules?: Iterable<ModuleDefinition<ModuleManifest>>;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
type RowMeta = { readonly created_at: number; readonly updated_at: number };
|
|
386
|
-
|
|
387
|
-
/** One decoded `kernel_entity__<module>__<entity>` row, module-agnostic (KAPI-1 / G-C). `fields`
|
|
388
|
-
* carries every declared column decoded through the entity's own codec (including the key) —
|
|
389
|
-
* raw, ungated: a control-plane caller applies redaction/allowlisting on top (§1.3, K-C3). */
|
|
390
|
-
export interface EntityRawRow {
|
|
391
|
-
readonly module: string;
|
|
392
|
-
readonly entity: string;
|
|
393
|
-
readonly scope: Scope;
|
|
394
|
-
readonly key: unknown;
|
|
395
|
-
readonly version: number;
|
|
396
|
-
readonly createdAt: number;
|
|
397
|
-
readonly updatedAt: number;
|
|
398
|
-
readonly fields: Readonly<Record<string, unknown>>;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
export interface EntityRawListOptions {
|
|
402
|
-
readonly where?: Readonly<Record<string, unknown>>;
|
|
403
|
-
readonly cursor?: string;
|
|
404
|
-
readonly limit?: number;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
export interface EntityRawPage {
|
|
408
|
-
readonly items: readonly EntityRawRow[];
|
|
409
|
-
readonly cursor?: string;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
function toRawRow(table: EntityTable, scope: Scope, row: StoredRow & RowMeta): EntityRawRow {
|
|
413
|
-
const value = rowValue(table, row);
|
|
414
|
-
return {
|
|
415
|
-
module: table.moduleId,
|
|
416
|
-
entity: table.entityName,
|
|
417
|
-
scope,
|
|
418
|
-
key: value[table.key.name],
|
|
419
|
-
version: row.version,
|
|
420
|
-
createdAt: row.created_at,
|
|
421
|
-
updatedAt: row.updated_at,
|
|
422
|
-
fields: value,
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
export class EntityPersistence {
|
|
427
|
-
readonly #tables = new Map<string, ReadonlyMap<string, EntityTable>>();
|
|
428
|
-
readonly #cursorSecret = randomBytes(32);
|
|
429
|
-
|
|
430
|
-
constructor(readonly options: EntityPersistenceOptions) {
|
|
431
|
-
options.db.run("PRAGMA foreign_keys = ON");
|
|
432
|
-
const { foreign_keys: enforced } = options.db.query("PRAGMA foreign_keys").get() as { foreign_keys: number };
|
|
433
|
-
if (enforced !== 1) {
|
|
434
|
-
throw new Error("entity persistence requires PRAGMA foreign_keys = ON to be honored by the injected SQLite handle");
|
|
435
|
-
}
|
|
436
|
-
for (const module of options.modules ?? []) this.register(module);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
register<M extends ModuleManifest>(module: ModuleDefinition<M>): void {
|
|
440
|
-
assertValidManifest(module);
|
|
441
|
-
if (this.#tables.has(module.id)) throw new Error(`module '${module.id}' entity persistence is already installed`);
|
|
442
|
-
this.#tables.set(module.id, installModuleEntities(this.options.db, module));
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
begin<M extends ModuleManifest>(module: ModuleDefinition<M>, scope: Scope): EntitySession<M["entities"]> {
|
|
446
|
-
const tables = this.#tables.get(module.id);
|
|
447
|
-
if (!tables) throw new Error(`module '${module.id}' entity persistence is not installed`);
|
|
448
|
-
return new EntitySessionRuntime<M["entities"]>(this.options.db, this.options.clock, scope, this.#cursorSecret, tables);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
#lookupTable(moduleId: string, entityName: string): EntityTable | undefined {
|
|
452
|
-
return this.#tables.get(moduleId)?.get(entityName);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* Generic `(module, entity, key)` point read (KAPI-1 / G-C) — the module-agnostic sibling of
|
|
457
|
-
* `EntityStore.get`, usable without a module's typed `ModuleDefinition` on hand. Returns
|
|
458
|
-
* `undefined` for an unregistered module/entity (not an error: a caller enumerating module ids
|
|
459
|
-
* dynamically should see "no such row," not a throw) or an absent key, exactly like `getInstance`
|
|
460
|
-
* on the workflow journal. No owner-scoping or redaction — that's the control module's job.
|
|
461
|
-
*/
|
|
462
|
-
readEntity(moduleId: string, entityName: string, scope: Scope, key: unknown): EntityRawRow | undefined {
|
|
463
|
-
const table = this.#lookupTable(moduleId, entityName);
|
|
464
|
-
if (!table) return undefined;
|
|
465
|
-
const columns = [...table.fields.map((field) => field.quoted), "version", "created_at", "updated_at"].join(", ");
|
|
466
|
-
const row = this.options.db.query(`SELECT ${columns} FROM ${table.quoted} WHERE scope = ? AND ${table.key.quoted} = ?`)
|
|
467
|
-
.get(scope, encodeField(table.key, key)) as (StoredRow & RowMeta) | null;
|
|
468
|
-
return row ? toRawRow(table, scope, row) : undefined;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
/**
|
|
472
|
-
* Generic `(module, entity)` cursor-paginated list (KAPI-1 / G-C), mirroring `EntityStore.list`'s
|
|
473
|
-
* cursor/where semantics but keyed by string module/entity names instead of a typed
|
|
474
|
-
* `ModuleDefinition`. Returns an empty page for an unregistered module/entity. No owner-scoping
|
|
475
|
-
* or redaction — the control module filters/gates the raw rows this returns.
|
|
476
|
-
*/
|
|
477
|
-
listEntity(moduleId: string, entityName: string, scope: Scope, options?: EntityRawListOptions): EntityRawPage {
|
|
478
|
-
const table = this.#lookupTable(moduleId, entityName);
|
|
479
|
-
if (!table) return { items: [] };
|
|
480
|
-
const limit = pageSize(options?.limit);
|
|
481
|
-
const where = parsedWhere(table, options?.where);
|
|
482
|
-
const cursorKey = options?.cursor ? decodeCursor(this.#cursorSecret, table, scope, options.cursor) : undefined;
|
|
483
|
-
const clauses = ["scope = ?"];
|
|
484
|
-
const bindings: Array<string | number | bigint | null> = [scope];
|
|
485
|
-
for (const [field, value] of where) {
|
|
486
|
-
const encoded = encodeField(field, value);
|
|
487
|
-
clauses.push(encoded === null ? `${field.quoted} IS NULL` : `${field.quoted} = ?`);
|
|
488
|
-
if (encoded !== null) bindings.push(encoded);
|
|
489
|
-
}
|
|
490
|
-
if (cursorKey !== undefined) {
|
|
491
|
-
clauses.push(`${table.key.quoted} > ?`);
|
|
492
|
-
bindings.push(encodeField(table.key, cursorKey));
|
|
493
|
-
}
|
|
494
|
-
bindings.push(limit + 1);
|
|
495
|
-
const columns = [...table.fields.map((field) => field.quoted), "version", "created_at", "updated_at"].join(", ");
|
|
496
|
-
const rows = this.options.db.query(`SELECT ${columns} FROM ${table.quoted}
|
|
497
|
-
WHERE ${clauses.join(" AND ")} ORDER BY ${table.key.quoted} ASC LIMIT ?`)
|
|
498
|
-
.all(...bindings) as Array<StoredRow & RowMeta>;
|
|
499
|
-
const hasMore = rows.length > limit;
|
|
500
|
-
const items = rows.slice(0, limit).map((row) => toRawRow(table, scope, row));
|
|
501
|
-
return {
|
|
502
|
-
items,
|
|
503
|
-
...(hasMore ? { cursor: encodeCursor(this.#cursorSecret, table, scope, items[items.length - 1]!.key) } : {}),
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
export function createEntityPersistence(options: EntityPersistenceOptions): EntityPersistence {
|
|
509
|
-
return new EntityPersistence(options);
|
|
510
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import type { ModuleDefinition, ModuleManifest } from "./contract";
|
|
2
|
-
import { assertValidManifest } from "./manifest-validation";
|
|
3
|
-
|
|
4
|
-
export class DuplicateModuleIdError extends Error {
|
|
5
|
-
constructor(readonly moduleId: string) {
|
|
6
|
-
super(`module '${moduleId}' is already registered`);
|
|
7
|
-
this.name = "DuplicateModuleIdError";
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class UnknownModuleError extends Error {
|
|
12
|
-
constructor(readonly moduleId: string) {
|
|
13
|
-
super(`module '${moduleId}' is not registered`);
|
|
14
|
-
this.name = "UnknownModuleError";
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
type AnyModuleDefinition = ModuleDefinition<ModuleManifest>;
|
|
19
|
-
|
|
20
|
-
/** In-process catalog of validated, compile-time module definitions. */
|
|
21
|
-
export class ModuleRegistry {
|
|
22
|
-
readonly #modules = new Map<string, AnyModuleDefinition>();
|
|
23
|
-
|
|
24
|
-
constructor(definitions: Iterable<AnyModuleDefinition> = []) {
|
|
25
|
-
for (const definition of definitions) this.register(definition);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
register<const M extends ModuleManifest>(definition: ModuleDefinition<M>): ModuleDefinition<M> {
|
|
29
|
-
assertValidManifest(definition);
|
|
30
|
-
if (this.#modules.has(definition.id)) throw new DuplicateModuleIdError(definition.id);
|
|
31
|
-
this.#modules.set(definition.id, definition as AnyModuleDefinition);
|
|
32
|
-
return definition;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
has(moduleId: string): boolean {
|
|
36
|
-
return this.#modules.has(moduleId);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
get(moduleId: string): AnyModuleDefinition | undefined {
|
|
40
|
-
return this.#modules.get(moduleId);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
require(moduleId: string): AnyModuleDefinition {
|
|
44
|
-
const definition = this.get(moduleId);
|
|
45
|
-
if (!definition) throw new UnknownModuleError(moduleId);
|
|
46
|
-
return definition;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
list(): readonly AnyModuleDefinition[] {
|
|
50
|
-
return [...this.#modules.values()];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
get size(): number {
|
|
54
|
-
return this.#modules.size;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function createModuleRegistry(definitions: Iterable<AnyModuleDefinition> = []): ModuleRegistry {
|
|
59
|
-
return new ModuleRegistry(definitions);
|
|
60
|
-
}
|