@gajae-code/coding-agent 0.13.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +215 -8
- package/README.md +2 -0
- package/bin/gajaessi.js +4 -0
- package/dist/types/async/job-manager.d.ts +55 -2
- package/dist/types/async/support.d.ts +14 -0
- package/dist/types/capability/mcp.d.ts +6 -0
- package/dist/types/capability/types.d.ts +6 -0
- package/dist/types/cli/accounts-cli.d.ts +19 -0
- package/dist/types/cli/args.d.ts +3 -0
- package/dist/types/cli/config-cli.d.ts +6 -0
- package/dist/types/cli/crash-cli.d.ts +10 -0
- package/dist/types/cli/customize-doctor.d.ts +145 -0
- package/dist/types/cli/daemon-cli.d.ts +3 -1
- package/dist/types/cli/mcp-cli.d.ts +11 -0
- package/dist/types/cli/notify-cli.d.ts +1 -1
- package/dist/types/cli/root-flags.d.ts +12 -0
- package/dist/types/cli/setup-cli.d.ts +3 -1
- package/dist/types/cli/skills-cli.d.ts +2 -1
- package/dist/types/cli/update-cli.d.ts +19 -1
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/commands/accounts.d.ts +40 -0
- package/dist/types/commands/crash.d.ts +24 -0
- package/dist/types/commands/customize.d.ts +22 -0
- package/dist/types/commands/daemon.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +6 -0
- package/dist/types/commands/setup.d.ts +6 -0
- package/dist/types/commands/skills.d.ts +6 -1
- package/dist/types/commands/update.d.ts +7 -0
- package/dist/types/config/atomic-yaml-patch-worker.d.ts +25 -0
- package/dist/types/config/atomic-yaml-patch.d.ts +62 -2
- package/dist/types/config/config-schema-version.d.ts +10 -0
- package/dist/types/config/file-lock.d.ts +18 -0
- package/dist/types/config/model-profile-activation.d.ts +3 -1
- package/dist/types/config/model-registry.d.ts +10 -1
- package/dist/types/config/models-config-schema.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +73 -9
- package/dist/types/config/settings.d.ts +63 -0
- package/dist/types/config/skill-settings-defaults.d.ts +26 -0
- package/dist/types/config/task-delegation.d.ts +39 -0
- package/dist/types/config/update-channel.d.ts +10 -0
- package/dist/types/coordinator-mcp/server.d.ts +5 -1
- package/dist/types/crash/index-store.d.ts +87 -0
- package/dist/types/crash/nudge.d.ts +50 -0
- package/dist/types/crash/record-loader.d.ts +17 -0
- package/dist/types/crash/report.d.ts +134 -0
- package/dist/types/crash/sanitize.d.ts +25 -0
- package/dist/types/cursor.d.ts +13 -2
- package/dist/types/customization/import.d.ts +37 -0
- package/dist/types/customization/index.d.ts +4 -0
- package/dist/types/customization/inventory.d.ts +25 -0
- package/dist/types/customization/mutations.d.ts +32 -0
- package/dist/types/customization/types.d.ts +115 -0
- package/dist/types/daemon/control-types.d.ts +8 -1
- package/dist/types/daemon/operator-contract.d.ts +4 -4
- package/dist/types/discovery/claude.d.ts +39 -1
- package/dist/types/discovery/codex.d.ts +39 -1
- package/dist/types/discovery/index.d.ts +2 -0
- package/dist/types/discovery/mcp-compat.d.ts +30 -0
- package/dist/types/edit/renderer.d.ts +23 -0
- package/dist/types/extensibility/extensions/runner.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +21 -4
- package/dist/types/extensibility/gjc-plugins/observability.d.ts +6 -1
- package/dist/types/extensibility/gjc-plugins/registry.d.ts +8 -1
- package/dist/types/extensibility/hooks/loader.d.ts +19 -5
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +22 -1
- package/dist/types/extensibility/skill-management.d.ts +107 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +3 -1
- package/dist/types/gjc-runtime/launch-worktree.d.ts +8 -0
- package/dist/types/gjc-runtime/linux-proc.d.ts +3 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +10 -6
- package/dist/types/gjc-runtime/session-state-lock.d.ts +104 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +120 -2
- package/dist/types/gjc-runtime/state-writer.d.ts +10 -1
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +4 -2
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -6
- package/dist/types/gjc-runtime/workflow-settings.d.ts +109 -0
- package/dist/types/hooks/events.d.ts +60 -0
- package/dist/types/hooks/normalize.d.ts +83 -0
- package/dist/types/hooks/skill-state.d.ts +9 -2
- package/dist/types/main.d.ts +2 -1
- package/dist/types/modes/acp/abort-scope.d.ts +13 -0
- package/dist/types/modes/acp/acp-agent.d.ts +7 -4
- package/dist/types/modes/components/customization/customization-dashboard.d.ts +44 -0
- package/dist/types/modes/components/customization/import-wizard.d.ts +21 -0
- package/dist/types/modes/components/customization/index.d.ts +1 -0
- package/dist/types/modes/components/dynamic-theme-text.d.ts +9 -0
- package/dist/types/modes/components/gajae-pet-widget.d.ts +6 -28
- package/dist/types/modes/components/irc-sidebar.d.ts +1 -0
- package/dist/types/modes/components/iterm-pet-transport.d.ts +122 -0
- package/dist/types/modes/components/oauth-selector.d.ts +10 -4
- package/dist/types/modes/components/pet-capability.d.ts +7 -21
- package/dist/types/modes/components/settings-selector.d.ts +5 -0
- package/dist/types/modes/components/status-line/gh.d.ts +6 -2
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +7 -8
- package/dist/types/modes/controllers/stt-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/theme/defaults/index.d.ts +118 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/injected-user-submission.d.ts +10 -0
- package/dist/types/runtime-mcp/config.d.ts +9 -0
- package/dist/types/runtime-mcp/manager.d.ts +26 -1
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +4 -0
- package/dist/types/runtime-mcp/oauth-flow.d.ts +24 -1
- package/dist/types/runtime-mcp/pool-key.d.ts +2 -0
- package/dist/types/runtime-mcp/protocol.d.ts +278 -0
- package/dist/types/runtime-mcp/scope-config.d.ts +29 -0
- package/dist/types/runtime-mcp/tool-bridge.d.ts +15 -13
- package/dist/types/runtime-mcp/transports/http.d.ts +9 -0
- package/dist/types/runtime-mcp/types.d.ts +102 -10
- package/dist/types/sdk/acp/adapter.d.ts +26 -5
- package/dist/types/sdk/broker/broker.d.ts +3 -55
- package/dist/types/sdk/broker/lifecycle.d.ts +18 -12
- package/dist/types/sdk/broker/session-index.d.ts +14 -5
- package/dist/types/sdk/broker/startup-failure.d.ts +16 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +51 -1
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -41
- package/dist/types/sdk/bus/config.d.ts +11 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +0 -1
- package/dist/types/sdk/bus/daemon-paths.d.ts +6 -0
- package/dist/types/sdk/bus/discord-conversation.d.ts +2 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +17 -12
- package/dist/types/sdk/bus/existing-thread-readiness.d.ts +4 -34
- package/dist/types/sdk/bus/inbound-reaction-ordering.d.ts +61 -0
- package/dist/types/sdk/bus/index.d.ts +85 -99
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +29 -7
- package/dist/types/sdk/bus/managed-daemon.d.ts +2 -32
- package/dist/types/sdk/bus/notification-service.d.ts +52 -0
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -1
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +16 -5
- package/dist/types/sdk/bus/reconciliation-store.d.ts +113 -5
- package/dist/types/sdk/bus/session-control.d.ts +25 -1
- package/dist/types/sdk/bus/slack-conversation.d.ts +4 -0
- package/dist/types/sdk/bus/slack-daemon.d.ts +9 -17
- package/dist/types/sdk/bus/slack-live-provider.d.ts +3 -1
- package/dist/types/sdk/bus/slack-provider.d.ts +4 -0
- package/dist/types/sdk/bus/slack-thread-binding.d.ts +6 -40
- package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +30 -104
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +10 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +262 -19
- package/dist/types/sdk/bus/telegram-daemon-orphan-reap.d.ts +125 -0
- package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +22 -0
- package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -310
- package/dist/types/sdk/bus/telegram-reference.d.ts +1 -47
- package/dist/types/sdk/bus/threaded-render.d.ts +1 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +33 -17
- package/dist/types/sdk/cli/rows.d.ts +2 -11
- package/dist/types/sdk/cli/session-cli.d.ts +15 -25
- package/dist/types/sdk/client/client.d.ts +97 -1
- package/dist/types/sdk/host/control/dispatch.d.ts +0 -7
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +18 -5
- package/dist/types/sdk/host/control/runtime-gate.d.ts +19 -0
- package/dist/types/sdk/host/host.d.ts +15 -1
- package/dist/types/sdk/host/query/handlers.d.ts +5 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
- package/dist/types/sdk/host/session-runtime.d.ts +127 -7
- package/dist/types/sdk/host/types.d.ts +1 -1
- package/dist/types/sdk/host/websocket-transport.d.ts +1 -1
- package/dist/types/sdk/index.d.ts +2 -1
- package/dist/types/sdk/lifecycle/broker-client.d.ts +8 -0
- package/dist/types/sdk/lifecycle/client.d.ts +59 -0
- package/dist/types/sdk/lifecycle/index.d.ts +3 -0
- package/dist/types/sdk/lifecycle/service.d.ts +232 -0
- package/dist/types/sdk/mcp/server.d.ts +9 -6
- package/dist/types/sdk/prompt-deadline-lease.d.ts +43 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +28 -0
- package/dist/types/sdk/prompt-status.d.ts +10 -3
- package/dist/types/sdk/protocol/operation-registry.d.ts +5 -0
- package/dist/types/sdk/receipt-state.d.ts +17 -0
- package/dist/types/sdk/reconciliation-extensions.d.ts +2 -0
- package/dist/types/sdk/router/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +152 -0
- package/dist/types/sdk/session-list.d.ts +22 -0
- package/dist/types/sdk/session-reconnect.d.ts +43 -1
- package/dist/types/sdk/session.d.ts +34 -1
- package/dist/types/sdk/transport/serve-cli.d.ts +1 -4
- package/dist/types/session/account-inventory.d.ts +86 -0
- package/dist/types/session/agent-session.d.ts +145 -6
- package/dist/types/session/agent-storage.d.ts +10 -0
- package/dist/types/session/coordinator-tool-label.d.ts +28 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +23 -4
- package/dist/types/session/messages.d.ts +1 -1
- package/dist/types/session/secure-token-file.d.ts +3 -0
- package/dist/types/session/session-manager.d.ts +47 -1
- package/dist/types/session/startup-auth-config.d.ts +44 -0
- package/dist/types/session/terminal-abort.d.ts +378 -0
- package/dist/types/session/yield-queue.d.ts +7 -0
- package/dist/types/session-import/claude.d.ts +26 -0
- package/dist/types/session-import/codex.d.ts +88 -0
- package/dist/types/session-import/command.d.ts +35 -0
- package/dist/types/session-import/detect.d.ts +22 -0
- package/dist/types/session-import/index.d.ts +7 -0
- package/dist/types/session-import/provider-codex.d.ts +22 -0
- package/dist/types/session-import/provider-service.d.ts +38 -0
- package/dist/types/session-import/redact.d.ts +23 -0
- package/dist/types/session-import/service.d.ts +47 -0
- package/dist/types/session-import/types.d.ts +120 -0
- package/dist/types/setup/paseo/check.d.ts +16 -0
- package/dist/types/setup/paseo/install-saga.d.ts +115 -0
- package/dist/types/setup/paseo/json-publisher.d.ts +76 -0
- package/dist/types/setup/paseo/orchestration-preferences.d.ts +24 -0
- package/dist/types/setup/paseo/paseo-ownership.d.ts +87 -0
- package/dist/types/setup/paseo/paseo-setup.d.ts +29 -0
- package/dist/types/setup/paseo/provider-config.d.ts +35 -0
- package/dist/types/setup/paseo/remove.d.ts +14 -0
- package/dist/types/setup/paseo/result-types.d.ts +59 -0
- package/dist/types/setup/paseo/setup-deps.d.ts +73 -0
- package/dist/types/setup/paseo/skills-bridge.d.ts +44 -0
- package/dist/types/skill-state/active-state.d.ts +5 -1
- package/dist/types/slash-commands/acp-builtins.d.ts +5 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +2 -0
- package/dist/types/slash-commands/helpers/credential-switch.d.ts +12 -0
- package/dist/types/slash-commands/helpers/usage-report.d.ts +5 -6
- package/dist/types/slash-commands/types.d.ts +7 -1
- package/dist/types/task/executor.d.ts +10 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/tools/ask-answer-registry.d.ts +6 -3
- package/dist/types/tools/bash-sleep-advisory.d.ts +14 -0
- package/dist/types/tools/bash.d.ts +2 -6
- package/dist/types/tools/browser/attach.d.ts +11 -0
- package/dist/types/tools/browser/launch.d.ts +20 -0
- package/dist/types/tools/browser/profile-discovery.d.ts +6 -0
- package/dist/types/tools/browser/render.d.ts +1 -0
- package/dist/types/tools/browser.d.ts +3 -1
- package/dist/types/tools/image-gen.d.ts +10 -2
- package/dist/types/tools/index.d.ts +8 -0
- package/dist/types/tools/monitor.d.ts +1 -1
- package/dist/types/tools/skill-discovery.d.ts +8 -1
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +2 -2
- package/dist/types/utils/herdr-pane.d.ts +76 -0
- package/package.json +19 -9
- package/scripts/build-binary.ts +2 -1
- package/scripts/build-sdk-package-smoke.ts +2 -13
- package/scripts/capture-sticky-viewport-showcase.ts +0 -1
- package/scripts/compile-args.ts +2 -0
- package/scripts/generate-sdk-adapter-parity-manifest.ts +31 -16
- package/scripts/generate-sdk-operation-inventory.ts +28 -0
- package/scripts/generate-telegram-baseline-manifest.ts +1 -0
- package/scripts/generate-tool-catalog.ts +6 -4
- package/scripts/verify-gjc-sdk-canonicalization.ts +5 -39
- package/src/async/job-manager.ts +352 -17
- package/src/async/support.ts +25 -0
- package/src/autoresearch/dashboard.ts +28 -13
- package/src/capability/index.ts +3 -1
- package/src/capability/mcp.ts +6 -0
- package/src/capability/types.ts +6 -0
- package/src/cli/accounts-cli.ts +395 -0
- package/src/cli/args.ts +23 -0
- package/src/cli/auth-broker-cli.ts +136 -66
- package/src/cli/auth-gateway-cli.ts +94 -70
- package/src/cli/config-cli.ts +27 -2
- package/src/cli/crash-cli.ts +78 -0
- package/src/cli/customize-doctor.ts +1605 -0
- package/src/cli/daemon-cli.ts +12 -3
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/mcp-cli.ts +128 -60
- package/src/cli/notify-cli.ts +8 -14
- package/src/cli/root-flags.ts +8 -0
- package/src/cli/setup-cli.ts +145 -0
- package/src/cli/skills-cli.ts +38 -2
- package/src/cli/update-cli.ts +167 -12
- package/src/cli.ts +4 -0
- package/src/commands/accounts.ts +71 -0
- package/src/commands/crash.ts +35 -0
- package/src/commands/customize.ts +84 -0
- package/src/commands/daemon.ts +2 -2
- package/src/commands/deep-interview.ts +2 -0
- package/src/commands/launch.ts +1 -0
- package/src/commands/mcp.ts +12 -7
- package/src/commands/ralplan.ts +2 -0
- package/src/commands/sdk.ts +65 -32
- package/src/commands/setup.ts +5 -0
- package/src/commands/skills.ts +14 -4
- package/src/commands/ultragoal.ts +10 -1
- package/src/commands/update.ts +26 -15
- package/src/config/atomic-yaml-patch-worker.ts +57 -0
- package/src/config/atomic-yaml-patch.ts +769 -29
- package/src/config/config-schema-version.ts +10 -0
- package/src/config/file-lock.ts +70 -1
- package/src/config/mcp-schema.json +52 -14
- package/src/config/model-profile-activation.ts +23 -2
- package/src/config/model-profiles.ts +88 -21
- package/src/config/model-registry.ts +147 -21
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +77 -7
- package/src/config/settings.ts +4038 -199
- package/src/config/skill-settings-defaults.ts +41 -3
- package/src/config/task-delegation.ts +87 -0
- package/src/config/update-channel.ts +23 -0
- package/src/coordinator-mcp/server.ts +377 -308
- package/src/crash/index-store.ts +723 -0
- package/src/crash/nudge.ts +89 -0
- package/src/crash/record-loader.ts +149 -0
- package/src/crash/report.ts +551 -0
- package/src/crash/sanitize.ts +129 -0
- package/src/cursor.ts +36 -10
- package/src/customization/import.ts +946 -0
- package/src/customization/index.ts +4 -0
- package/src/customization/inventory.ts +335 -0
- package/src/customization/mutations.ts +141 -0
- package/src/customization/types.ts +223 -0
- package/src/daemon/control-types.ts +8 -1
- package/src/daemon/operator-contract.ts +6 -6
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +31 -6
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +11 -22
- package/src/defaults/gjc/skills/ralplan/SKILL.md +36 -23
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +16 -0
- package/src/discovery/claude-hooks.ts +38 -0
- package/src/discovery/claude.ts +142 -209
- package/src/discovery/codex-hooks.ts +38 -0
- package/src/discovery/codex.ts +132 -198
- package/src/discovery/helpers.ts +8 -1
- package/src/discovery/index.ts +2 -0
- package/src/discovery/mcp-compat.ts +234 -0
- package/src/discovery/mcp-json.ts +6 -0
- package/src/edit/renderer.ts +40 -0
- package/src/edit/streaming.ts +4 -2
- package/src/exec/bash-executor.ts +24 -20
- package/src/export/html/index.ts +51 -2
- package/src/extensibility/extensions/loader.ts +9 -1
- package/src/extensibility/extensions/runner.ts +17 -4
- package/src/extensibility/extensions/types.ts +31 -6
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +33 -0
- package/src/extensibility/gjc-plugins/installer.ts +9 -5
- package/src/extensibility/gjc-plugins/observability.ts +8 -2
- package/src/extensibility/gjc-plugins/registry.ts +13 -2
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +3 -1
- package/src/extensibility/gjc-plugins/schema.ts +222 -39
- package/src/extensibility/hooks/loader.ts +154 -10
- package/src/extensibility/runtime-skill-discovery.ts +257 -60
- package/src/extensibility/skill-management.ts +368 -0
- package/src/extensibility/skills.ts +52 -16
- package/src/gjc-runtime/deep-interview-runtime.ts +45 -61
- package/src/gjc-runtime/launch-worktree.ts +56 -2
- package/src/gjc-runtime/linux-proc.ts +7 -1
- package/src/gjc-runtime/ralplan-runtime.ts +100 -164
- package/src/gjc-runtime/session-state-lock.ts +992 -0
- package/src/gjc-runtime/session-state-sidecar.ts +644 -142
- package/src/gjc-runtime/state-writer.ts +39 -14
- package/src/gjc-runtime/tmux-owner-isolation.ts +5 -4
- package/src/gjc-runtime/ultragoal-guard.ts +11 -4
- package/src/gjc-runtime/ultragoal-runtime.ts +44 -41
- package/src/gjc-runtime/workflow-settings.ts +596 -0
- package/src/goals/tools/goal-tool.ts +6 -1
- package/src/hooks/events.ts +291 -0
- package/src/hooks/native-skill-hook.ts +4 -0
- package/src/hooks/normalize.ts +369 -0
- package/src/hooks/skill-state.ts +165 -46
- package/src/internal-urls/docs-index.generated.ts +41 -32
- package/src/main.ts +36 -5
- package/src/migrate/mcp-mapper.ts +25 -2
- package/src/modes/acp/abort-scope.ts +29 -0
- package/src/modes/acp/acp-agent.ts +423 -98
- package/src/modes/components/assistant-message.ts +6 -1
- package/src/modes/components/chrome.ts +4 -3
- package/src/modes/components/customization/customization-dashboard.ts +443 -0
- package/src/modes/components/customization/import-wizard.ts +372 -0
- package/src/modes/components/customization/index.ts +1 -0
- package/src/modes/components/dynamic-theme-text.ts +22 -0
- package/src/modes/components/gajae-pet-widget.ts +607 -193
- package/src/modes/components/irc-sidebar.ts +11 -0
- package/src/modes/components/iterm-pet-transport.ts +585 -0
- package/src/modes/components/model-selector.ts +1 -6
- package/src/modes/components/oauth-selector.ts +277 -86
- package/src/modes/components/pet-capability.ts +35 -31
- package/src/modes/components/settings-selector.ts +29 -5
- package/src/modes/components/status-line/gh.ts +45 -1
- package/src/modes/components/status-line/segments.ts +19 -4
- package/src/modes/components/tool-status-header.ts +13 -3
- package/src/modes/controllers/command-controller.ts +20 -8
- package/src/modes/controllers/extension-ui-controller.ts +56 -257
- package/src/modes/controllers/input-controller.ts +10 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +16 -3
- package/src/modes/controllers/selector-controller.ts +238 -77
- package/src/modes/controllers/stt-controller.ts +3 -3
- package/src/modes/interactive-mode.ts +88 -16
- package/src/modes/print-mode.ts +35 -6
- package/src/modes/runtime-init.ts +21 -68
- package/src/modes/theme/defaults/index.ts +2 -0
- package/src/modes/theme/defaults/ouroboros.json +118 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/injected-user-submission.ts +25 -0
- package/src/modes/utils/terminal-bell.ts +15 -1
- package/src/prompts/agent-fragments/ralplan-persistence.md +1 -1
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/critic.md +1 -1
- package/src/prompts/system/rlm-research.md +1 -1
- package/src/prompts/system/system-prompt.md +0 -2
- package/src/prompts/tools/bash.md +7 -1
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/cron.md +6 -6
- package/src/prompts/tools/read.md +0 -2
- package/src/prompts/tools/skill-discovery.md +4 -2
- package/src/runtime-mcp/client.ts +427 -23
- package/src/runtime-mcp/config.ts +37 -5
- package/src/runtime-mcp/manager-pool.test.ts +56 -0
- package/src/runtime-mcp/manager.ts +52 -2
- package/src/runtime-mcp/oauth-discovery.ts +13 -0
- package/src/runtime-mcp/oauth-flow.ts +46 -1
- package/src/runtime-mcp/pool-key.ts +5 -1
- package/src/runtime-mcp/pool.test.ts +9 -0
- package/src/runtime-mcp/protocol.test.ts +353 -0
- package/src/runtime-mcp/protocol.ts +624 -0
- package/src/runtime-mcp/scope-config.ts +78 -0
- package/src/runtime-mcp/tool-bridge.ts +44 -10
- package/src/runtime-mcp/transports/http.ts +84 -13
- package/src/runtime-mcp/types.ts +123 -12
- package/src/sdk/acp/adapter.ts +328 -97
- package/src/sdk/broker/broker.ts +160 -741
- package/src/sdk/broker/ensure.ts +9 -1
- package/src/sdk/broker/lifecycle.ts +510 -116
- package/src/sdk/broker/operation-receipt-ledger.ts +11 -2
- package/src/sdk/broker/process-incarnation.ts +19 -0
- package/src/sdk/broker/session-index.ts +516 -117
- package/src/sdk/broker/startup-failure.ts +28 -5
- package/src/sdk/broker/transport.ts +1 -13
- package/src/sdk/bus/chat-daemon-cli.ts +4 -2
- package/src/sdk/bus/chat-daemon-control.ts +67 -6
- package/src/sdk/bus/chat-daemon-runtime.ts +183 -859
- package/src/sdk/bus/config.ts +13 -0
- package/src/sdk/bus/conversation-store.ts +59 -171
- package/src/sdk/bus/daemon-paths.ts +20 -0
- package/src/sdk/bus/discord-conversation.ts +2 -0
- package/src/sdk/bus/discord-daemon.ts +524 -126
- package/src/sdk/bus/discord-live-provider.ts +206 -79
- package/src/sdk/bus/existing-thread-readiness.ts +4 -97
- package/src/sdk/bus/inbound-reaction-ordering.ts +122 -0
- package/src/sdk/bus/index.ts +2480 -456
- package/src/sdk/bus/kind-aware-reconciliation.ts +204 -34
- package/src/sdk/bus/lifecycle-commands.ts +2 -2
- package/src/sdk/bus/managed-daemon.ts +2 -145
- package/src/sdk/bus/notification-service.ts +196 -0
- package/src/sdk/bus/operator-runtime.ts +1 -1
- package/src/sdk/bus/prompt-reconciliation.ts +24 -15
- package/src/sdk/bus/reconciliation-store.ts +553 -24
- package/src/sdk/bus/session-control.ts +49 -3
- package/src/sdk/bus/slack-conversation.ts +4 -0
- package/src/sdk/bus/slack-daemon.ts +733 -134
- package/src/sdk/bus/slack-live-provider.ts +65 -15
- package/src/sdk/bus/slack-provider.ts +4 -0
- package/src/sdk/bus/slack-thread-binding.ts +9 -104
- package/src/sdk/bus/telegram-adoption-intent.ts +299 -209
- package/src/sdk/bus/telegram-daemon-cli.ts +194 -7
- package/src/sdk/bus/telegram-daemon-contract.ts +265 -19
- package/src/sdk/bus/telegram-daemon-control.ts +3 -13
- package/src/sdk/bus/telegram-daemon-orphan-reap.ts +498 -0
- package/src/sdk/bus/telegram-daemon-owner-registry.ts +176 -0
- package/src/sdk/bus/telegram-daemon.ts +2552 -2280
- package/src/sdk/bus/telegram-reference.ts +1 -226
- package/src/sdk/bus/telegram-setup.ts +2 -2
- package/src/sdk/bus/threaded-render.ts +1 -0
- package/src/sdk/bus/topic-registry.ts +279 -146
- package/src/sdk/cli/rows.ts +2 -35
- package/src/sdk/cli/session-cli.ts +776 -700
- package/src/sdk/client/client.ts +892 -1
- package/src/sdk/host/control/dispatch.ts +93 -58
- package/src/sdk/host/control/index.ts +1 -1
- package/src/sdk/host/control/operations.ts +18 -10
- package/src/sdk/host/control/runtime-gate.ts +40 -0
- package/src/sdk/host/host.ts +63 -12
- package/src/sdk/host/query/handlers.ts +66 -7
- package/src/sdk/host/reverse-leases.ts +2 -2
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -1
- package/src/sdk/host/session-runtime.test.ts +2787 -34
- package/src/sdk/host/session-runtime.ts +1489 -101
- package/src/sdk/host/surface-policy.ts +12 -8
- package/src/sdk/host/types.ts +1 -1
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +31 -59
- package/src/sdk/host/websocket-transport.ts +85 -50
- package/src/sdk/index.ts +2 -1
- package/src/sdk/lifecycle/broker-client.ts +53 -0
- package/src/sdk/lifecycle/client.ts +234 -0
- package/src/sdk/lifecycle/index.ts +3 -0
- package/src/sdk/{bus/recent-activity.ts → lifecycle/recent-sessions.ts} +5 -2
- package/src/sdk/lifecycle/service.ts +720 -0
- package/src/sdk/mcp/server.ts +226 -188
- package/src/sdk/prompt-deadline-lease.ts +55 -0
- package/src/sdk/prompt-deadline-manager.ts +148 -0
- package/src/sdk/prompt-status.ts +11 -3
- package/src/sdk/protocol/operation-inventory.generated.json +272 -129
- package/src/sdk/protocol/operation-registry.ts +71 -8
- package/src/sdk/receipt-state.ts +43 -0
- package/src/sdk/reconciliation-extensions.ts +14 -0
- package/src/sdk/router/index.ts +1 -0
- package/src/sdk/router/session-router.ts +1658 -0
- package/src/sdk/session-list.ts +93 -0
- package/src/sdk/session-reconnect.ts +45 -1
- package/src/sdk/session.ts +716 -120
- package/src/sdk/transport/serve-cli.ts +18 -34
- package/src/session/account-inventory.ts +603 -0
- package/src/session/agent-session.ts +2873 -559
- package/src/session/agent-storage.ts +35 -0
- package/src/session/blob-store.ts +32 -1
- package/src/session/coordinator-tool-label.ts +40 -0
- package/src/session/internal/managed-session-scope.ts +4 -0
- package/src/session/internal/managed-session-storage.ts +199 -31
- package/src/session/messages.ts +1 -1
- package/src/session/secure-token-file.ts +80 -0
- package/src/session/session-manager.ts +757 -57
- package/src/session/session-storage.ts +25 -16
- package/src/session/startup-auth-config.ts +297 -0
- package/src/session/terminal-abort.ts +1253 -0
- package/src/session/yield-queue.ts +57 -15
- package/src/session-import/claude.ts +334 -0
- package/src/session-import/codex.ts +1033 -0
- package/src/session-import/command.ts +77 -0
- package/src/session-import/detect.ts +133 -0
- package/src/session-import/index.ts +7 -0
- package/src/session-import/provider-codex.ts +327 -0
- package/src/session-import/provider-service.ts +457 -0
- package/src/session-import/redact.ts +128 -0
- package/src/session-import/service.ts +1264 -0
- package/src/session-import/types.ts +148 -0
- package/src/setup/paseo/check.ts +213 -0
- package/src/setup/paseo/install-saga.ts +293 -0
- package/src/setup/paseo/json-publisher.ts +263 -0
- package/src/setup/paseo/orchestration-preferences.ts +86 -0
- package/src/setup/paseo/paseo-ownership.ts +224 -0
- package/src/setup/paseo/paseo-setup.ts +253 -0
- package/src/setup/paseo/provider-config.ts +139 -0
- package/src/setup/paseo/remove.ts +183 -0
- package/src/setup/paseo/result-types.ts +79 -0
- package/src/setup/paseo/setup-deps.ts +175 -0
- package/src/setup/paseo/skills-bridge.ts +260 -0
- package/src/skill-state/active-state.ts +21 -11
- package/src/slash-commands/acp-builtins.ts +9 -1
- package/src/slash-commands/builtin-registry.ts +117 -19
- package/src/slash-commands/helpers/credential-switch.ts +143 -0
- package/src/slash-commands/helpers/mcp.ts +9 -3
- package/src/slash-commands/helpers/usage-report.ts +61 -56
- package/src/slash-commands/types.ts +7 -2
- package/src/task/executor.ts +30 -11
- package/src/task/index.ts +66 -7
- package/src/task/render.ts +23 -9
- package/src/tools/ask-answer-registry.ts +23 -7
- package/src/tools/ask.ts +8 -4
- package/src/tools/bash-sleep-advisory.ts +115 -0
- package/src/tools/bash.ts +98 -18
- package/src/tools/browser/attach.ts +71 -3
- package/src/tools/browser/launch.ts +86 -13
- package/src/tools/browser/profile-discovery.ts +57 -7
- package/src/tools/browser/render.ts +8 -1
- package/src/tools/browser.ts +130 -21
- package/src/tools/computer.enforcement.test.ts +1 -1
- package/src/tools/computer.ts +3 -2
- package/src/tools/cron.ts +12 -1
- package/src/tools/image-gen.ts +349 -142
- package/src/tools/index.ts +8 -0
- package/src/tools/job.ts +33 -6
- package/src/tools/monitor.ts +41 -2
- package/src/tools/skill-discovery.ts +21 -29
- package/src/tools/subagent.ts +142 -24
- package/src/tools/todo-contract.ts +55 -3
- package/src/tools/todo-write.ts +4 -3
- package/src/tools/tool-catalog.generated.ts +6 -6
- package/src/tools/ultragoal-ask-guard.ts +10 -3
- package/src/utils/herdr-pane.ts +345 -0
- package/src/utils/title-generator.ts +2 -0
- package/src/web/scrapers/index.ts +6 -4
- package/dist/types/cli/malloc-env-guard.test.d.ts +0 -1
- package/dist/types/defaults/gjc-defaults.test.d.ts +0 -1
- package/dist/types/memory-backend/lazy-loading.test.d.ts +0 -1
- package/dist/types/runtime/boot-ordering.test.d.ts +0 -1
- package/dist/types/runtime/lazy-service.test.d.ts +0 -1
- package/dist/types/runtime/network-prewarm-service.test.d.ts +0 -1
- package/dist/types/runtime/workspace-tree-service.test.d.ts +0 -1
- package/dist/types/runtime-mcp/config.test.d.ts +0 -1
- package/dist/types/runtime-mcp/http.test.d.ts +0 -1
- package/dist/types/runtime-mcp/manager-pool.test.d.ts +0 -1
- package/dist/types/runtime-mcp/pool-key.test.d.ts +0 -1
- package/dist/types/runtime-mcp/pool.test.d.ts +0 -1
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +0 -117
- package/dist/types/sdk/bus/lifecycle-orchestrator.d.ts +0 -141
- package/dist/types/sdk/bus/telegram-cli.d.ts +0 -31
- package/dist/types/sdk/elevation/allowlist.d.ts +0 -20
- package/dist/types/sdk/elevation/audit.d.ts +0 -93
- package/dist/types/sdk/elevation/capability.d.ts +0 -3
- package/dist/types/sdk/elevation/digest.d.ts +0 -72
- package/dist/types/sdk/elevation/dispatch-receipt.d.ts +0 -85
- package/dist/types/sdk/elevation/grant-ledger.d.ts +0 -163
- package/dist/types/sdk/elevation/owner.d.ts +0 -48
- package/dist/types/sdk/host/import-graph.test.d.ts +0 -1
- package/dist/types/sdk/host/sdk-surface-parity.test.d.ts +0 -1
- package/dist/types/sdk/host/session-runtime.test.d.ts +0 -1
- package/dist/types/sdk/host/websocket-transport.lifecycle.test.d.ts +0 -1
- package/dist/types/session/auth-broker-config.d.ts +0 -13
- package/dist/types/session/heap-eviction-artifacts.test.d.ts +0 -1
- package/dist/types/skills/skills.test.d.ts +0 -1
- package/dist/types/slash-commands/notify-command.test.d.ts +0 -1
- package/dist/types/task/token-log.test.d.ts +0 -1
- package/dist/types/tools/computer.enforcement.test.d.ts +0 -1
- package/dist/types/tools/descriptors.test.d.ts +0 -1
- package/scripts/g005-daemon-smoke.ts +0 -180
- package/scripts/g011-daemon-path-smoke.ts +0 -153
- package/src/sdk/bus/lifecycle-control-runtime.ts +0 -1733
- package/src/sdk/bus/lifecycle-orchestrator.ts +0 -489
- package/src/sdk/bus/telegram-cli.ts +0 -212
- package/src/sdk/elevation/allowlist.ts +0 -54
- package/src/sdk/elevation/audit.ts +0 -208
- package/src/sdk/elevation/capability.ts +0 -41
- package/src/sdk/elevation/digest.ts +0 -129
- package/src/sdk/elevation/dispatch-receipt.ts +0 -184
- package/src/sdk/elevation/grant-ledger.ts +0 -1142
- package/src/sdk/elevation/owner.ts +0 -109
- package/src/session/auth-broker-config.ts +0 -109
- /package/dist/types/{ai-core-import-gate.test.d.ts → discovery/claude-hooks.d.ts} +0 -0
- /package/dist/types/{cli/fixture-report.test.d.ts → discovery/codex-hooks.d.ts} +0 -0
- /package/dist/types/sdk/{bus/recent-activity.d.ts → lifecycle/recent-sessions.d.ts} +0 -0
|
@@ -6,23 +6,37 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Safe session ids only: /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/
|
|
8
8
|
* Atomic write: temp + fsync + rename + 0600. Corrupt → quarantine + empty.
|
|
9
|
-
* Non-terminal
|
|
10
|
-
*
|
|
9
|
+
* Non-terminal records with a pending outcome finalize that exact claim on bootstrap.
|
|
10
|
+
* Outcome-less skills retain the existing failed/process_restart settlement.
|
|
11
11
|
*/
|
|
12
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
12
13
|
import * as fs from "node:fs/promises";
|
|
13
14
|
import * as path from "node:path";
|
|
14
15
|
import type { PromptReconciliationStatus, SdkPromptTerminalOutcome } from "../prompt-status";
|
|
16
|
+
import type { ReceiptState } from "../receipt-state";
|
|
15
17
|
import { TURN_RESULT_CONTENT_MAX_BYTES, type TurnResultContent } from "../turn-result";
|
|
16
|
-
|
|
17
18
|
import type { PromptCorrelation } from "./prompt-reconciliation";
|
|
18
19
|
|
|
19
|
-
export const RECONCILIATION_STORE_VERSION =
|
|
20
|
+
export const RECONCILIATION_STORE_VERSION = 2;
|
|
21
|
+
export const RECONCILIATION_STORE_VERSION_V1 = 1;
|
|
20
22
|
export const RECONCILIATION_SESSION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
21
23
|
export const RECONCILIATION_DIR_NAME = ".sdk-reconciliation";
|
|
24
|
+
// Replay-authority hashes are canonical SHA-256 hex digests; anything else in a
|
|
25
|
+
// durable document can never match a real retry and would make the reserved key
|
|
26
|
+
// invisible (and reusable for an unrelated later abort), so reload rejects it.
|
|
27
|
+
const SHA256_RE = /^[0-9a-f]{64}$/;
|
|
28
|
+
|
|
29
|
+
export function resolveReconciliationSessionFile(
|
|
30
|
+
sessionFile: string | undefined | null,
|
|
31
|
+
stateRoot: string,
|
|
32
|
+
sessionId: string,
|
|
33
|
+
): string {
|
|
34
|
+
return sessionFile ?? path.join(stateRoot, `${sessionId}.jsonl`);
|
|
35
|
+
}
|
|
22
36
|
|
|
23
|
-
export type ReconciliationKind = "prompt" | "skill";
|
|
37
|
+
export type ReconciliationKind = "prompt" | "skill" | "terminal";
|
|
24
38
|
|
|
25
|
-
export interface
|
|
39
|
+
export interface DurableExecutionReconciliationRecord extends PromptCorrelation {
|
|
26
40
|
kind: ReconciliationKind;
|
|
27
41
|
clientRef?: string;
|
|
28
42
|
status: PromptReconciliationStatus;
|
|
@@ -31,16 +45,105 @@ export interface DurableReconciliationRecord extends PromptCorrelation {
|
|
|
31
45
|
startedAt?: number;
|
|
32
46
|
terminalAt?: number;
|
|
33
47
|
outcome?: SdkPromptTerminalOutcome;
|
|
48
|
+
receiptState?: Exclude<ReceiptState, "absent">;
|
|
34
49
|
pendingOutcome?: SdkPromptTerminalOutcome;
|
|
50
|
+
pendingReceiptState?: Extract<ReceiptState, "present" | "missing">;
|
|
35
51
|
/** Skill-only safe token; never skill args bodies. */
|
|
36
52
|
skillName?: string;
|
|
37
53
|
content?: TurnResultContent;
|
|
38
54
|
}
|
|
39
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Durable terminal scope record (approved abort-SDK plan, v2 document).
|
|
58
|
+
* Bounded origin/fence and owned-settlement fields only; no prompt text and no
|
|
59
|
+
* suppressed/deferred receipts for left-running turn work.
|
|
60
|
+
*/
|
|
61
|
+
export interface DurableTerminalScopeRecord {
|
|
62
|
+
selection: "turn" | "owned";
|
|
63
|
+
/** SHA-256 of the bounded idempotency key; the raw key is never persisted. */
|
|
64
|
+
idempotencyKeyHash?: string;
|
|
65
|
+
/** SHA-256 of the canonicalized normalized input; raw input is never persisted. */
|
|
66
|
+
idempotencyInputHash?: string;
|
|
67
|
+
turnDisposition:
|
|
68
|
+
| "pending"
|
|
69
|
+
| "stopped"
|
|
70
|
+
| "uncertain"
|
|
71
|
+
| "no_effect"
|
|
72
|
+
| "no_effect_reserved"
|
|
73
|
+
| "no_effect_marker_failure";
|
|
74
|
+
/** Whether the correlated agent_end event was published (AC 19). */
|
|
75
|
+
terminalPublished?: boolean;
|
|
76
|
+
ownedWorkDisposition: "not_requested" | "left_running" | "stopped" | "uncertain";
|
|
77
|
+
automaticDeliveryDisposition: "enabled" | "none";
|
|
78
|
+
resumeOnOwnedCompletion: boolean;
|
|
79
|
+
turnContinuationFence: {
|
|
80
|
+
state: "retained" | "released";
|
|
81
|
+
abortedAttemptEpoch: number;
|
|
82
|
+
blockedContinuationIds: string[];
|
|
83
|
+
predecessorTombstones: string[];
|
|
84
|
+
ownedCompletionPolicy: "enabled" | "disabled";
|
|
85
|
+
};
|
|
86
|
+
ownedDeliverySettlements?: Array<{
|
|
87
|
+
keyHash: string;
|
|
88
|
+
entryIdHash: string;
|
|
89
|
+
status: "settled" | "absent" | "uncertain";
|
|
90
|
+
observedAt: number;
|
|
91
|
+
}>;
|
|
92
|
+
responseState: "pending" | "sent" | "delivered" | "failed";
|
|
93
|
+
responsePayloadHash: string;
|
|
94
|
+
/** Hash of the replay-shaped public result a same-key retry delivers, when
|
|
95
|
+
* it differs from the original response (the replay appends metadata). */
|
|
96
|
+
replayPayloadHash?: string;
|
|
97
|
+
acceptedAt: number;
|
|
98
|
+
terminalAt?: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Compact evicted-key tombstone with enough disposition metadata to
|
|
102
|
+
* reconstruct the original terminal replay result (review thread P2). */
|
|
103
|
+
export interface EvictedTerminalKeyEntry {
|
|
104
|
+
keyHash: string;
|
|
105
|
+
inputHash: string;
|
|
106
|
+
turnDisposition?: "stopped" | "uncertain" | "no_effect" | "no_effect_reserved" | "no_effect_marker_failure";
|
|
107
|
+
ownedWorkDisposition?: "not_requested" | "left_running" | "stopped" | "uncertain";
|
|
108
|
+
responseState?: "pending" | "sent" | "delivered" | "failed";
|
|
109
|
+
responsePayloadHash?: string;
|
|
110
|
+
replayPayloadHash?: string;
|
|
111
|
+
terminalPublished?: boolean;
|
|
112
|
+
}
|
|
113
|
+
export interface DurableSteerReconciliationRecord {
|
|
114
|
+
kind: "steer";
|
|
115
|
+
clientRef: string;
|
|
116
|
+
textDigest: string;
|
|
117
|
+
createdAt: number;
|
|
118
|
+
status: "dispatching" | "accepted" | "rejected" | "uncertain";
|
|
119
|
+
settledAt?: number;
|
|
120
|
+
error?: { code: string; message: string };
|
|
121
|
+
commandId: string;
|
|
122
|
+
turnId: string;
|
|
123
|
+
acceptedAt: number;
|
|
124
|
+
startedAt?: never;
|
|
125
|
+
terminalAt?: never;
|
|
126
|
+
outcome?: never;
|
|
127
|
+
receiptState?: never;
|
|
128
|
+
pendingOutcome?: never;
|
|
129
|
+
pendingReceiptState?: never;
|
|
130
|
+
skillName?: never;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export type DurableReconciliationRecord = DurableExecutionReconciliationRecord | DurableSteerReconciliationRecord;
|
|
134
|
+
|
|
40
135
|
export interface ReconciliationStoreDocument {
|
|
41
136
|
version: typeof RECONCILIATION_STORE_VERSION;
|
|
42
137
|
sessionId: string;
|
|
43
138
|
records: DurableReconciliationRecord[];
|
|
139
|
+
terminalScopes?: DurableTerminalScopeRecord[];
|
|
140
|
+
/**
|
|
141
|
+
* Compact key tombstones for completed terminal rows evicted by the
|
|
142
|
+
* retention cap: the key hash is retained durably so a same-key retry
|
|
143
|
+
* after dispatch-cache expiry/restart still replays instead of aborting an
|
|
144
|
+
* unrelated later prompt (review thread P2).
|
|
145
|
+
*/
|
|
146
|
+
evictedTerminalKeys?: EvictedTerminalKeyEntry[];
|
|
44
147
|
}
|
|
45
148
|
|
|
46
149
|
export interface ReconciliationStoreFs {
|
|
@@ -63,7 +166,7 @@ const nodeFs: ReconciliationStoreFs = {
|
|
|
63
166
|
mkdir: fs.mkdir,
|
|
64
167
|
readFile: fs.readFile,
|
|
65
168
|
writeFile: fs.writeFile,
|
|
66
|
-
rename: fs.rename,
|
|
169
|
+
rename: (from, to) => fs.rename(from, to),
|
|
67
170
|
unlink: fs.unlink,
|
|
68
171
|
open: fs.open as ReconciliationStoreFs["open"],
|
|
69
172
|
};
|
|
@@ -88,18 +191,54 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
88
191
|
/** Record-level validation: JSON-valid but malformed entries must be quarantined too. */
|
|
89
192
|
function isValidRecord(value: unknown): boolean {
|
|
90
193
|
if (!isRecord(value)) return false;
|
|
91
|
-
const { kind
|
|
194
|
+
const { kind } = value;
|
|
195
|
+
if (kind === "steer") {
|
|
196
|
+
if (typeof value.clientRef !== "string" || !value.clientRef) return false;
|
|
197
|
+
if (typeof value.textDigest !== "string" || !/^[0-9a-f]{64}$/.test(value.textDigest)) return false;
|
|
198
|
+
if (typeof value.createdAt !== "number" || !Number.isFinite(value.createdAt)) return false;
|
|
199
|
+
if (typeof value.commandId !== "string" || !value.commandId || typeof value.turnId !== "string" || !value.turnId)
|
|
200
|
+
return false;
|
|
201
|
+
if (typeof value.acceptedAt !== "number" || !Number.isFinite(value.acceptedAt)) return false;
|
|
202
|
+
if (!["dispatching", "accepted", "rejected", "uncertain"].includes(value.status as string)) return false;
|
|
203
|
+
const settled = value.status !== "dispatching";
|
|
204
|
+
if (settled !== (typeof value.settledAt === "number" && Number.isFinite(value.settledAt))) return false;
|
|
205
|
+
if ((value.status === "rejected" || value.status === "uncertain") !== (value.error !== undefined)) return false;
|
|
206
|
+
if (
|
|
207
|
+
value.error !== undefined &&
|
|
208
|
+
(!isRecord(value.error) || typeof value.error.code !== "string" || typeof value.error.message !== "string")
|
|
209
|
+
)
|
|
210
|
+
return false;
|
|
211
|
+
return value.receiptState === undefined && value.outcome === undefined;
|
|
212
|
+
}
|
|
92
213
|
if (kind !== "prompt" && kind !== "skill") return false;
|
|
214
|
+
const {
|
|
215
|
+
commandId,
|
|
216
|
+
turnId,
|
|
217
|
+
status,
|
|
218
|
+
acceptedAt,
|
|
219
|
+
terminalAt,
|
|
220
|
+
outcome,
|
|
221
|
+
pendingOutcome,
|
|
222
|
+
receiptState,
|
|
223
|
+
pendingReceiptState,
|
|
224
|
+
} = value;
|
|
93
225
|
if (typeof commandId !== "string" || !commandId || typeof turnId !== "string" || !turnId) return false;
|
|
94
226
|
if (status !== "accepted" && status !== "in_flight" && status !== "terminal_ok" && status !== "failed") return false;
|
|
95
227
|
if (typeof acceptedAt !== "number" || !Number.isFinite(acceptedAt)) return false;
|
|
96
228
|
if (terminalAt !== undefined && (typeof terminalAt !== "number" || !Number.isFinite(terminalAt))) return false;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
229
|
+
if (receiptState !== undefined && !["present", "missing", "unknown"].includes(receiptState as string)) return false;
|
|
230
|
+
if (pendingReceiptState !== undefined && pendingReceiptState !== "present" && pendingReceiptState !== "missing")
|
|
231
|
+
return false;
|
|
232
|
+
// Durable invariants: only prompts carry a pending receipt claim, a finalized
|
|
233
|
+
// record has no pending claim left, and terminal/active status must agree with
|
|
234
|
+
// `terminalAt`. Prompts and skills may both carry a pending outcome claim.
|
|
235
|
+
if (pendingReceiptState !== undefined && kind !== "prompt") return false;
|
|
236
|
+
if (pendingReceiptState !== undefined && pendingOutcome === undefined) return false;
|
|
100
237
|
if (pendingOutcome !== undefined && terminalAt !== undefined) return false;
|
|
101
238
|
const isTerminalStatus = status === "terminal_ok" || status === "failed";
|
|
102
239
|
if (isTerminalStatus !== (terminalAt !== undefined)) return false;
|
|
240
|
+
if (!isTerminalStatus && receiptState !== undefined) return false;
|
|
241
|
+
if (isTerminalStatus && pendingReceiptState !== undefined) return false;
|
|
103
242
|
if (outcome !== undefined && !isTerminalStatus) return false;
|
|
104
243
|
if (
|
|
105
244
|
outcome !== undefined &&
|
|
@@ -111,10 +250,6 @@ function isValidRecord(value: unknown): boolean {
|
|
|
111
250
|
return false;
|
|
112
251
|
if (value.clientRef !== undefined && typeof value.clientRef !== "string") return false;
|
|
113
252
|
if (value.skillName !== undefined && typeof value.skillName !== "string") return false;
|
|
114
|
-
if (value.error !== undefined) {
|
|
115
|
-
if (!isRecord(value.error)) return false;
|
|
116
|
-
if (typeof value.error.code !== "string" || typeof value.error.message !== "string") return false;
|
|
117
|
-
}
|
|
118
253
|
if (value.content !== undefined) {
|
|
119
254
|
if (
|
|
120
255
|
!isRecord(value.content) ||
|
|
@@ -128,6 +263,10 @@ function isValidRecord(value: unknown): boolean {
|
|
|
128
263
|
)
|
|
129
264
|
return false;
|
|
130
265
|
}
|
|
266
|
+
if (value.error !== undefined) {
|
|
267
|
+
if (!isRecord(value.error)) return false;
|
|
268
|
+
if (typeof value.error.code !== "string" || typeof value.error.message !== "string") return false;
|
|
269
|
+
}
|
|
131
270
|
return [outcome, pendingOutcome].every(candidate => {
|
|
132
271
|
if (candidate === undefined) return true;
|
|
133
272
|
if (!isRecord(candidate)) return false;
|
|
@@ -145,29 +284,274 @@ function isValidRecord(value: unknown): boolean {
|
|
|
145
284
|
);
|
|
146
285
|
});
|
|
147
286
|
}
|
|
287
|
+
/** Terminal scope validation: bounded origin/fence/settlement fields only. */
|
|
288
|
+
function isValidTerminalScope(value: unknown): boolean {
|
|
289
|
+
if (!isRecord(value)) return false;
|
|
290
|
+
const {
|
|
291
|
+
selection,
|
|
292
|
+
turnDisposition,
|
|
293
|
+
ownedWorkDisposition,
|
|
294
|
+
automaticDeliveryDisposition,
|
|
295
|
+
resumeOnOwnedCompletion,
|
|
296
|
+
turnContinuationFence,
|
|
297
|
+
ownedDeliverySettlements,
|
|
298
|
+
responseState,
|
|
299
|
+
responsePayloadHash,
|
|
300
|
+
replayPayloadHash,
|
|
301
|
+
acceptedAt,
|
|
302
|
+
terminalAt,
|
|
303
|
+
idempotencyKeyHash,
|
|
304
|
+
idempotencyInputHash,
|
|
305
|
+
terminalPublished,
|
|
306
|
+
} = value;
|
|
307
|
+
if (selection !== "turn" && selection !== "owned") return false;
|
|
308
|
+
if (
|
|
309
|
+
turnDisposition !== "pending" &&
|
|
310
|
+
turnDisposition !== "stopped" &&
|
|
311
|
+
turnDisposition !== "uncertain" &&
|
|
312
|
+
turnDisposition !== "no_effect" &&
|
|
313
|
+
turnDisposition !== "no_effect_reserved" &&
|
|
314
|
+
turnDisposition !== "no_effect_marker_failure"
|
|
315
|
+
)
|
|
316
|
+
return false;
|
|
317
|
+
if (
|
|
318
|
+
ownedWorkDisposition !== "not_requested" &&
|
|
319
|
+
ownedWorkDisposition !== "left_running" &&
|
|
320
|
+
ownedWorkDisposition !== "stopped" &&
|
|
321
|
+
ownedWorkDisposition !== "uncertain"
|
|
322
|
+
)
|
|
323
|
+
return false;
|
|
324
|
+
if (automaticDeliveryDisposition !== "enabled" && automaticDeliveryDisposition !== "none") return false;
|
|
325
|
+
if (typeof resumeOnOwnedCompletion !== "boolean") return false;
|
|
326
|
+
if (!isRecord(turnContinuationFence)) return false;
|
|
327
|
+
const { state, abortedAttemptEpoch, blockedContinuationIds, predecessorTombstones, ownedCompletionPolicy } =
|
|
328
|
+
turnContinuationFence;
|
|
329
|
+
if (state !== "retained" && state !== "released") return false;
|
|
330
|
+
if (typeof abortedAttemptEpoch !== "number" || !Number.isFinite(abortedAttemptEpoch)) return false;
|
|
331
|
+
if (!Array.isArray(blockedContinuationIds) || !blockedContinuationIds.every(id => typeof id === "string"))
|
|
332
|
+
return false;
|
|
333
|
+
if (!Array.isArray(predecessorTombstones) || !predecessorTombstones.every(id => typeof id === "string"))
|
|
334
|
+
return false;
|
|
335
|
+
if (ownedCompletionPolicy !== "enabled" && ownedCompletionPolicy !== "disabled") return false;
|
|
336
|
+
if (ownedDeliverySettlements !== undefined) {
|
|
337
|
+
if (!Array.isArray(ownedDeliverySettlements) || ownedDeliverySettlements.length > 256) return false;
|
|
338
|
+
for (const settlement of ownedDeliverySettlements) {
|
|
339
|
+
if (!isRecord(settlement)) return false;
|
|
340
|
+
if (typeof settlement.keyHash !== "string" || !settlement.keyHash) return false;
|
|
341
|
+
if (typeof settlement.entryIdHash !== "string" || !settlement.entryIdHash) return false;
|
|
342
|
+
if (settlement.status !== "settled" && settlement.status !== "absent" && settlement.status !== "uncertain")
|
|
343
|
+
return false;
|
|
344
|
+
if (typeof settlement.observedAt !== "number" || !Number.isFinite(settlement.observedAt)) return false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (
|
|
348
|
+
responseState !== "pending" &&
|
|
349
|
+
responseState !== "sent" &&
|
|
350
|
+
responseState !== "delivered" &&
|
|
351
|
+
responseState !== "failed"
|
|
352
|
+
)
|
|
353
|
+
return false;
|
|
354
|
+
if (typeof responsePayloadHash !== "string" || !responsePayloadHash) return false;
|
|
355
|
+
if (replayPayloadHash !== undefined && (typeof replayPayloadHash !== "string" || !replayPayloadHash)) return false;
|
|
356
|
+
if (typeof idempotencyKeyHash !== "string" || !SHA256_RE.test(idempotencyKeyHash)) return false;
|
|
357
|
+
if (typeof idempotencyInputHash !== "string" || !SHA256_RE.test(idempotencyInputHash)) return false;
|
|
358
|
+
if (terminalPublished !== undefined && typeof terminalPublished !== "boolean") return false;
|
|
359
|
+
if (typeof acceptedAt !== "number" || !Number.isFinite(acceptedAt)) return false;
|
|
360
|
+
if (terminalAt !== undefined && (typeof terminalAt !== "number" || !Number.isFinite(terminalAt))) return false;
|
|
361
|
+
// An incomplete (pending) scope cannot already be terminal.
|
|
362
|
+
if (turnDisposition === "pending" && terminalAt !== undefined) return false;
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** Evicted tombstones are replay authority, so every optional replay field
|
|
367
|
+
* is validated before a durable document is trusted. */
|
|
368
|
+
function isValidEvictedTerminalKeyEntry(value: unknown): value is EvictedTerminalKeyEntry {
|
|
369
|
+
if (!isRecord(value)) return false;
|
|
370
|
+
if (typeof value.keyHash !== "string" || !SHA256_RE.test(value.keyHash)) return false;
|
|
371
|
+
if (typeof value.inputHash !== "string" || !SHA256_RE.test(value.inputHash)) return false;
|
|
372
|
+
if (
|
|
373
|
+
value.turnDisposition !== undefined &&
|
|
374
|
+
value.turnDisposition !== "stopped" &&
|
|
375
|
+
value.turnDisposition !== "uncertain" &&
|
|
376
|
+
value.turnDisposition !== "no_effect" &&
|
|
377
|
+
value.turnDisposition !== "no_effect_reserved" &&
|
|
378
|
+
value.turnDisposition !== "no_effect_marker_failure"
|
|
379
|
+
)
|
|
380
|
+
return false;
|
|
381
|
+
if (
|
|
382
|
+
value.ownedWorkDisposition !== undefined &&
|
|
383
|
+
value.ownedWorkDisposition !== "not_requested" &&
|
|
384
|
+
value.ownedWorkDisposition !== "left_running" &&
|
|
385
|
+
value.ownedWorkDisposition !== "stopped" &&
|
|
386
|
+
value.ownedWorkDisposition !== "uncertain"
|
|
387
|
+
)
|
|
388
|
+
return false;
|
|
389
|
+
if (
|
|
390
|
+
value.responseState !== undefined &&
|
|
391
|
+
value.responseState !== "pending" &&
|
|
392
|
+
value.responseState !== "sent" &&
|
|
393
|
+
value.responseState !== "delivered" &&
|
|
394
|
+
value.responseState !== "failed"
|
|
395
|
+
)
|
|
396
|
+
return false;
|
|
397
|
+
if (
|
|
398
|
+
value.responsePayloadHash !== undefined &&
|
|
399
|
+
(typeof value.responsePayloadHash !== "string" || !value.responsePayloadHash)
|
|
400
|
+
)
|
|
401
|
+
return false;
|
|
402
|
+
if (
|
|
403
|
+
value.replayPayloadHash !== undefined &&
|
|
404
|
+
(typeof value.replayPayloadHash !== "string" || !value.replayPayloadHash)
|
|
405
|
+
)
|
|
406
|
+
return false;
|
|
407
|
+
if (value.terminalPublished !== undefined && typeof value.terminalPublished !== "boolean") return false;
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
148
410
|
|
|
149
411
|
function parseDocument(raw: string, expectedSessionId: string): ReconciliationStoreDocument {
|
|
150
412
|
const value = JSON.parse(raw) as unknown;
|
|
151
|
-
if (
|
|
413
|
+
if (
|
|
414
|
+
!isRecord(value) ||
|
|
415
|
+
(value.version !== RECONCILIATION_STORE_VERSION && value.version !== RECONCILIATION_STORE_VERSION_V1)
|
|
416
|
+
)
|
|
152
417
|
throw new Error("invalid reconciliation store version");
|
|
153
418
|
if (value.sessionId !== expectedSessionId) throw new Error("session id mismatch");
|
|
154
419
|
if (!Array.isArray(value.records)) throw new Error("invalid records");
|
|
155
420
|
if (!value.records.every(isValidRecord)) throw new Error("invalid reconciliation record");
|
|
156
|
-
|
|
421
|
+
// v1 documents migrate to v2 (records only; terminalScopes added later).
|
|
422
|
+
if (value.version === RECONCILIATION_STORE_VERSION_V1)
|
|
423
|
+
return {
|
|
424
|
+
version: RECONCILIATION_STORE_VERSION,
|
|
425
|
+
sessionId: expectedSessionId,
|
|
426
|
+
records: value.records as DurableReconciliationRecord[],
|
|
427
|
+
};
|
|
428
|
+
const terminalScopes = value.terminalScopes;
|
|
429
|
+
if (terminalScopes !== undefined) {
|
|
430
|
+
if (!Array.isArray(terminalScopes)) throw new Error("invalid terminal scopes");
|
|
431
|
+
if (!terminalScopes.every(isValidTerminalScope)) throw new Error("invalid terminal scope");
|
|
432
|
+
}
|
|
433
|
+
const evictedTerminalKeys = value.evictedTerminalKeys;
|
|
434
|
+
if (
|
|
435
|
+
evictedTerminalKeys !== undefined &&
|
|
436
|
+
(!Array.isArray(evictedTerminalKeys) || !evictedTerminalKeys.every(isValidEvictedTerminalKeyEntry))
|
|
437
|
+
) {
|
|
438
|
+
throw new Error("invalid evicted terminal keys");
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
version: RECONCILIATION_STORE_VERSION,
|
|
442
|
+
sessionId: expectedSessionId,
|
|
443
|
+
records: value.records as DurableReconciliationRecord[],
|
|
444
|
+
...(terminalScopes !== undefined ? { terminalScopes: terminalScopes as DurableTerminalScopeRecord[] } : {}),
|
|
445
|
+
...(evictedTerminalKeys !== undefined
|
|
446
|
+
? { evictedTerminalKeys: evictedTerminalKeys as EvictedTerminalKeyEntry[] }
|
|
447
|
+
: {}),
|
|
448
|
+
};
|
|
157
449
|
}
|
|
158
450
|
|
|
159
451
|
/**
|
|
160
452
|
* Settle non-terminal durable records after process death.
|
|
161
|
-
*
|
|
162
|
-
* reconciliation-incomplete result.
|
|
453
|
+
* Any record with a durable pending outcome preserves that exact terminal claim;
|
|
454
|
+
* outcome-less skills retain the existing reconciliation-incomplete result.
|
|
163
455
|
*/
|
|
456
|
+
/**
|
|
457
|
+
* Settle incomplete terminal scopes (turnDisposition "pending") to safe
|
|
458
|
+
* uncertainty after process death. A terminal scope that never finalized its
|
|
459
|
+
* semantic CAS replays as uncertainty, never as success.
|
|
460
|
+
*/
|
|
461
|
+
export function settleTerminalScopeRestart(
|
|
462
|
+
scopes: DurableTerminalScopeRecord[],
|
|
463
|
+
now: number,
|
|
464
|
+
): DurableTerminalScopeRecord[] {
|
|
465
|
+
return scopes.map(scope => {
|
|
466
|
+
// An abandoned no_effect_reserved reservation (the process exited or the
|
|
467
|
+
// finalize failed after the reservation was written) becomes a completed
|
|
468
|
+
// no_effect so normal retention can evict it: reserved rows are otherwise
|
|
469
|
+
// permanently non-evictable and same-key replay never finalizes them,
|
|
470
|
+
// so repeated crashes with unique idle-abort keys grow the reconciliation
|
|
471
|
+
// document without bound (review thread P2).
|
|
472
|
+
if (scope.turnDisposition === "no_effect_reserved") {
|
|
473
|
+
// The reservation's only deliverable after restart is the
|
|
474
|
+
// metadata-bearing no_active_turn replay; store the replay-shaped
|
|
475
|
+
// payload hash so a written replay can advance the row instead of
|
|
476
|
+
// staying durably pending (review thread P2). The stored
|
|
477
|
+
// responsePayloadHash stays the ORIGINAL placeholder: the delivered
|
|
478
|
+
// replay envelope embeds it, so the replay hash computed from that
|
|
479
|
+
// envelope is the hash the delivery observer will actually compare —
|
|
480
|
+
// replacing the field with the replay hash would make the delivered
|
|
481
|
+
// envelope embed a DIFFERENT value and never advance the row
|
|
482
|
+
// (review thread P2).
|
|
483
|
+
const replayResult = {
|
|
484
|
+
ok: true,
|
|
485
|
+
selection: scope.selection,
|
|
486
|
+
turn: "no_active_turn",
|
|
487
|
+
terminal: "terminal_no_effect",
|
|
488
|
+
replay: {
|
|
489
|
+
responseState: "pending",
|
|
490
|
+
responsePayloadHash: scope.responsePayloadHash,
|
|
491
|
+
terminalPublished: scope.terminalPublished === true,
|
|
492
|
+
},
|
|
493
|
+
};
|
|
494
|
+
const replayPayloadHash = createHash("sha256").update(JSON.stringify(replayResult)).digest("hex");
|
|
495
|
+
return {
|
|
496
|
+
...scope,
|
|
497
|
+
turnDisposition: "no_effect" as const,
|
|
498
|
+
replayPayloadHash,
|
|
499
|
+
terminalAt: scope.terminalAt ?? now,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
if (scope.turnDisposition !== "pending" || scope.terminalAt !== undefined) return scope;
|
|
503
|
+
// A restart-replay is the ONLY response a settled pending row can ever
|
|
504
|
+
// deliver (the process died before the original response was written), so
|
|
505
|
+
// the stored payload hash must describe the replay-shaped public result —
|
|
506
|
+
// the replay appends metadata (reason + replay envelope) that the delivery
|
|
507
|
+
// hash check would otherwise reject forever, leaving the row durably
|
|
508
|
+
// pending (review thread P2).
|
|
509
|
+
const replayResult = {
|
|
510
|
+
ok: true,
|
|
511
|
+
selection: scope.selection,
|
|
512
|
+
turn: "uncertain",
|
|
513
|
+
ownedWork: scope.selection === "turn" ? "left_running" : "uncertain",
|
|
514
|
+
automaticDelivery: scope.selection === "turn" ? "enabled" : "none",
|
|
515
|
+
resumeOnOwnedCompletion: scope.selection === "turn",
|
|
516
|
+
reason: "replay_uncertain",
|
|
517
|
+
replay: {
|
|
518
|
+
responseState: "pending",
|
|
519
|
+
responsePayloadHash: scope.responsePayloadHash,
|
|
520
|
+
terminalPublished: scope.terminalPublished === true,
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
const replayPayloadHash = createHash("sha256").update(JSON.stringify(replayResult)).digest("hex");
|
|
524
|
+
return {
|
|
525
|
+
...scope,
|
|
526
|
+
turnDisposition: "uncertain",
|
|
527
|
+
ownedWorkDisposition: scope.ownedWorkDisposition === "not_requested" ? "not_requested" : "uncertain",
|
|
528
|
+
// responsePayloadHash stays the ORIGINAL hash: the delivered replay
|
|
529
|
+
// envelope embeds it, so replayPayloadHash (computed from that
|
|
530
|
+
// envelope) is the hash the delivery observer compares — replacing
|
|
531
|
+
// the field with the replay hash would embed a different value and
|
|
532
|
+
// keep the row durably pending (review thread P2).
|
|
533
|
+
responsePayloadHash: scope.responsePayloadHash,
|
|
534
|
+
replayPayloadHash,
|
|
535
|
+
terminalAt: now,
|
|
536
|
+
};
|
|
537
|
+
});
|
|
538
|
+
}
|
|
164
539
|
export function settleProcessRestart(
|
|
165
540
|
records: DurableReconciliationRecord[],
|
|
166
541
|
now: number,
|
|
167
542
|
): DurableReconciliationRecord[] {
|
|
168
543
|
return records.map(record => {
|
|
544
|
+
if (record.kind === "steer") {
|
|
545
|
+
if (record.status !== "dispatching") return record;
|
|
546
|
+
return {
|
|
547
|
+
...record,
|
|
548
|
+
status: "uncertain",
|
|
549
|
+
settledAt: now,
|
|
550
|
+
error: { code: "process_restart_uncertain", message: "Steer delivery is uncertain after process restart." },
|
|
551
|
+
};
|
|
552
|
+
}
|
|
169
553
|
if (record.terminalAt !== undefined) return record;
|
|
170
|
-
if (record.kind === "prompt") {
|
|
554
|
+
if (record.pendingOutcome !== undefined || record.kind === "prompt") {
|
|
171
555
|
const outcome: SdkPromptTerminalOutcome = record.pendingOutcome ?? {
|
|
172
556
|
kind: "failed",
|
|
173
557
|
code: "prompt_failed",
|
|
@@ -179,7 +563,9 @@ export function settleProcessRestart(
|
|
|
179
563
|
status: outcome.kind === "stopped" ? "terminal_ok" : "failed",
|
|
180
564
|
terminalAt: now,
|
|
181
565
|
outcome,
|
|
566
|
+
receiptState: record.pendingReceiptState ?? (outcome.kind === "stopped" ? "missing" : "unknown"),
|
|
182
567
|
pendingOutcome: undefined,
|
|
568
|
+
pendingReceiptState: undefined,
|
|
183
569
|
...(outcome.kind === "failed" ? { error: { code: outcome.code, message: outcome.message } } : {}),
|
|
184
570
|
};
|
|
185
571
|
}
|
|
@@ -188,6 +574,7 @@ export function settleProcessRestart(
|
|
|
188
574
|
status: "failed",
|
|
189
575
|
terminalAt: now,
|
|
190
576
|
error: { code: "process_restart", message: "Reconciliation incomplete after process restart." },
|
|
577
|
+
receiptState: "unknown",
|
|
191
578
|
};
|
|
192
579
|
});
|
|
193
580
|
}
|
|
@@ -200,6 +587,23 @@ export interface ReconciliationStore {
|
|
|
200
587
|
load(): Promise<DurableReconciliationRecord[]>;
|
|
201
588
|
/** Snapshot currently held in memory after last load/transact. */
|
|
202
589
|
snapshot(): DurableReconciliationRecord[];
|
|
590
|
+
/** Terminal-scope mutations through the same serialized full-document owner. */
|
|
591
|
+
transactTerminalScopes(
|
|
592
|
+
mutator: (scopes: DurableTerminalScopeRecord[]) => DurableTerminalScopeRecord[],
|
|
593
|
+
): Promise<void>;
|
|
594
|
+
transactTerminalState(
|
|
595
|
+
mutator: (state: { scopes: DurableTerminalScopeRecord[]; keys: EvictedTerminalKeyEntry[] }) => {
|
|
596
|
+
scopes: DurableTerminalScopeRecord[];
|
|
597
|
+
keys: EvictedTerminalKeyEntry[];
|
|
598
|
+
},
|
|
599
|
+
): Promise<void>;
|
|
600
|
+
/** Wait until every previously admitted reconciliation transaction settles. */
|
|
601
|
+
drain?(): Promise<void>;
|
|
602
|
+
transactTerminalKeys(mutator: (keys: EvictedTerminalKeyEntry[]) => EvictedTerminalKeyEntry[]): Promise<void>;
|
|
603
|
+
snapshotTerminalKeys(): EvictedTerminalKeyEntry[];
|
|
604
|
+
loadTerminalScopes(): Promise<DurableTerminalScopeRecord[]>;
|
|
605
|
+
/** Snapshot of terminal scopes currently held in memory. */
|
|
606
|
+
snapshotTerminalScopes(): DurableTerminalScopeRecord[];
|
|
203
607
|
delete(): Promise<void>;
|
|
204
608
|
}
|
|
205
609
|
|
|
@@ -218,13 +622,15 @@ export function createReconciliationStore(options: {
|
|
|
218
622
|
: null;
|
|
219
623
|
|
|
220
624
|
let memory: DurableReconciliationRecord[] = [];
|
|
625
|
+
let terminalMemory: DurableTerminalScopeRecord[] = [];
|
|
626
|
+
let terminalKeyMemory: EvictedTerminalKeyEntry[] = [];
|
|
221
627
|
let chain: Promise<void> = Promise.resolve();
|
|
222
628
|
|
|
223
629
|
const writeAtomic = async (document: ReconciliationStoreDocument): Promise<void> => {
|
|
224
630
|
if (!filePath) return;
|
|
225
631
|
const directory = path.dirname(filePath);
|
|
226
632
|
await fileFs.mkdir(directory, { recursive: true, mode: 0o700 });
|
|
227
|
-
const temporary = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
633
|
+
const temporary = `${filePath}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
|
|
228
634
|
try {
|
|
229
635
|
await fileFs.writeFile(temporary, `${JSON.stringify(document)}\n`, { mode: 0o600 });
|
|
230
636
|
try {
|
|
@@ -249,6 +655,11 @@ export function createReconciliationStore(options: {
|
|
|
249
655
|
const load = async (): Promise<DurableReconciliationRecord[]> => {
|
|
250
656
|
if (!filePath) {
|
|
251
657
|
memory = [];
|
|
658
|
+
terminalMemory = [];
|
|
659
|
+
// No durable store means no evicted-key tombstones either: a store
|
|
660
|
+
// instance that already loaded tombstones must not keep replaying or
|
|
661
|
+
// conflicting on keys that no longer exist on disk (review thread P2).
|
|
662
|
+
terminalKeyMemory = [];
|
|
252
663
|
return memory;
|
|
253
664
|
}
|
|
254
665
|
let raw: string;
|
|
@@ -259,6 +670,8 @@ export function createReconciliationStore(options: {
|
|
|
259
670
|
// so the endpoint never becomes ready as if no prompt had been accepted.
|
|
260
671
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
261
672
|
memory = [];
|
|
673
|
+
terminalMemory = [];
|
|
674
|
+
terminalKeyMemory = [];
|
|
262
675
|
return memory;
|
|
263
676
|
}
|
|
264
677
|
let document: ReconciliationStoreDocument;
|
|
@@ -272,15 +685,30 @@ export function createReconciliationStore(options: {
|
|
|
272
685
|
// ignore
|
|
273
686
|
}
|
|
274
687
|
memory = [];
|
|
688
|
+
terminalMemory = [];
|
|
689
|
+
terminalKeyMemory = [];
|
|
275
690
|
return memory;
|
|
276
691
|
}
|
|
277
692
|
const settled = settleProcessRestart(document.records, now());
|
|
693
|
+
const settledTerminal = settleTerminalScopeRestart(document.terminalScopes ?? [], now());
|
|
278
694
|
// Restart settlement must be durable before it is observable: a failed rewrite
|
|
279
695
|
// propagates so the endpoint stays unready instead of serving empty state as if
|
|
280
696
|
// no prompt had ever been accepted.
|
|
281
|
-
|
|
282
|
-
|
|
697
|
+
const recordsChanged = settled.some((record, index) => record !== document.records[index]);
|
|
698
|
+
const terminalChanged = settledTerminal.some((scope, index) => scope !== (document.terminalScopes ?? [])[index]);
|
|
699
|
+
if (recordsChanged || terminalChanged)
|
|
700
|
+
await writeAtomic({
|
|
701
|
+
version: RECONCILIATION_STORE_VERSION,
|
|
702
|
+
sessionId,
|
|
703
|
+
records: settled,
|
|
704
|
+
...(document.terminalScopes !== undefined || terminalChanged ? { terminalScopes: settledTerminal } : {}),
|
|
705
|
+
...(document.evictedTerminalKeys !== undefined
|
|
706
|
+
? { evictedTerminalKeys: document.evictedTerminalKeys }
|
|
707
|
+
: {}),
|
|
708
|
+
});
|
|
283
709
|
memory = settled;
|
|
710
|
+
terminalMemory = settledTerminal;
|
|
711
|
+
terminalKeyMemory = document.evictedTerminalKeys ?? [];
|
|
284
712
|
return memory;
|
|
285
713
|
};
|
|
286
714
|
|
|
@@ -289,7 +717,13 @@ export function createReconciliationStore(options: {
|
|
|
289
717
|
): Promise<void> => {
|
|
290
718
|
const run = async () => {
|
|
291
719
|
const next = mutator(memory.map(r => ({ ...r })));
|
|
292
|
-
await writeAtomic({
|
|
720
|
+
await writeAtomic({
|
|
721
|
+
version: RECONCILIATION_STORE_VERSION,
|
|
722
|
+
sessionId,
|
|
723
|
+
records: next,
|
|
724
|
+
...(terminalMemory.length > 0 ? { terminalScopes: terminalMemory } : {}),
|
|
725
|
+
...(terminalKeyMemory.length > 0 ? { evictedTerminalKeys: terminalKeyMemory } : {}),
|
|
726
|
+
});
|
|
293
727
|
memory = next;
|
|
294
728
|
};
|
|
295
729
|
const pending = chain.then(run, run);
|
|
@@ -300,8 +734,83 @@ export function createReconciliationStore(options: {
|
|
|
300
734
|
await pending;
|
|
301
735
|
};
|
|
302
736
|
|
|
737
|
+
const transactTerminalScopes = async (
|
|
738
|
+
mutator: (scopes: DurableTerminalScopeRecord[]) => DurableTerminalScopeRecord[],
|
|
739
|
+
): Promise<void> => {
|
|
740
|
+
const run = async () => {
|
|
741
|
+
const next = mutator(terminalMemory.map(s => ({ ...s })));
|
|
742
|
+
await writeAtomic({
|
|
743
|
+
version: RECONCILIATION_STORE_VERSION,
|
|
744
|
+
sessionId,
|
|
745
|
+
records: memory,
|
|
746
|
+
...(next.length > 0 ? { terminalScopes: next } : {}),
|
|
747
|
+
...(terminalKeyMemory.length > 0 ? { evictedTerminalKeys: terminalKeyMemory } : {}),
|
|
748
|
+
});
|
|
749
|
+
terminalMemory = next;
|
|
750
|
+
};
|
|
751
|
+
const pending = chain.then(run, run);
|
|
752
|
+
chain = pending.then(
|
|
753
|
+
() => undefined,
|
|
754
|
+
() => undefined,
|
|
755
|
+
);
|
|
756
|
+
await pending;
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
const transactTerminalState = async (
|
|
760
|
+
mutator: (state: { scopes: DurableTerminalScopeRecord[]; keys: EvictedTerminalKeyEntry[] }) => {
|
|
761
|
+
scopes: DurableTerminalScopeRecord[];
|
|
762
|
+
keys: EvictedTerminalKeyEntry[];
|
|
763
|
+
},
|
|
764
|
+
): Promise<void> => {
|
|
765
|
+
const run = async () => {
|
|
766
|
+
const next = mutator({
|
|
767
|
+
scopes: terminalMemory.map(s => ({ ...s })),
|
|
768
|
+
keys: terminalKeyMemory.map(k => ({ ...k })),
|
|
769
|
+
});
|
|
770
|
+
await writeAtomic({
|
|
771
|
+
version: RECONCILIATION_STORE_VERSION,
|
|
772
|
+
sessionId,
|
|
773
|
+
records: memory,
|
|
774
|
+
...(next.scopes.length > 0 ? { terminalScopes: next.scopes } : {}),
|
|
775
|
+
...(next.keys.length > 0 ? { evictedTerminalKeys: next.keys } : {}),
|
|
776
|
+
});
|
|
777
|
+
terminalMemory = next.scopes;
|
|
778
|
+
terminalKeyMemory = next.keys;
|
|
779
|
+
};
|
|
780
|
+
const pending = chain.then(run, run);
|
|
781
|
+
chain = pending.then(
|
|
782
|
+
() => undefined,
|
|
783
|
+
() => undefined,
|
|
784
|
+
);
|
|
785
|
+
await pending;
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
const transactTerminalKeys = async (
|
|
789
|
+
mutator: (keys: EvictedTerminalKeyEntry[]) => EvictedTerminalKeyEntry[],
|
|
790
|
+
): Promise<void> => {
|
|
791
|
+
const run = async () => {
|
|
792
|
+
const next = mutator(terminalKeyMemory.map(k => ({ ...k })));
|
|
793
|
+
await writeAtomic({
|
|
794
|
+
version: RECONCILIATION_STORE_VERSION,
|
|
795
|
+
sessionId,
|
|
796
|
+
records: memory,
|
|
797
|
+
...(terminalMemory.length > 0 ? { terminalScopes: terminalMemory } : {}),
|
|
798
|
+
...(next.length > 0 ? { evictedTerminalKeys: next } : {}),
|
|
799
|
+
});
|
|
800
|
+
terminalKeyMemory = next;
|
|
801
|
+
};
|
|
802
|
+
const pending = chain.then(run, run);
|
|
803
|
+
chain = pending.then(
|
|
804
|
+
() => undefined,
|
|
805
|
+
() => undefined,
|
|
806
|
+
);
|
|
807
|
+
await pending;
|
|
808
|
+
};
|
|
809
|
+
|
|
303
810
|
const deleteStore = async (): Promise<void> => {
|
|
304
811
|
memory = [];
|
|
812
|
+
terminalMemory = [];
|
|
813
|
+
terminalKeyMemory = [];
|
|
305
814
|
if (!filePath) return;
|
|
306
815
|
await fileFs.unlink(filePath).catch(error => {
|
|
307
816
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
@@ -314,6 +823,26 @@ export function createReconciliationStore(options: {
|
|
|
314
823
|
transact,
|
|
315
824
|
load,
|
|
316
825
|
snapshot: () => memory.map(r => ({ ...r })),
|
|
826
|
+
transactTerminalScopes,
|
|
827
|
+
transactTerminalState,
|
|
828
|
+
transactTerminalKeys,
|
|
829
|
+
drain: async () => {
|
|
830
|
+
while (true) {
|
|
831
|
+
const observed = chain;
|
|
832
|
+
await observed;
|
|
833
|
+
// Fire-and-forget frame handlers can enqueue their first transaction in
|
|
834
|
+
// a later microtask. Yield once and require the queue tail to remain
|
|
835
|
+
// stable before reporting durable quiescence.
|
|
836
|
+
await Bun.sleep(0);
|
|
837
|
+
if (chain === observed) return;
|
|
838
|
+
}
|
|
839
|
+
},
|
|
840
|
+
loadTerminalScopes: async () => {
|
|
841
|
+
await load();
|
|
842
|
+
return terminalMemory.map(s => ({ ...s }));
|
|
843
|
+
},
|
|
844
|
+
snapshotTerminalScopes: () => terminalMemory.map(s => ({ ...s })),
|
|
845
|
+
snapshotTerminalKeys: () => terminalKeyMemory.map(k => ({ ...k })),
|
|
317
846
|
delete: deleteStore,
|
|
318
847
|
};
|
|
319
848
|
}
|