@gajae-code/coding-agent 0.10.2 → 0.11.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 +50 -2
- package/dist/types/append-only-mode.d.ts +1 -1
- package/dist/types/cli/args.d.ts +4 -5
- package/dist/types/cli/daemon-cli.d.ts +12 -1
- package/dist/types/cli/notify-cli.d.ts +21 -1
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/commands/daemon.d.ts +39 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/commands/notify.d.ts +28 -1
- package/dist/types/commands/sdk.d.ts +33 -0
- package/dist/types/config/atomic-yaml-patch.d.ts +85 -0
- package/dist/types/config/model-profile-activation.d.ts +35 -15
- package/dist/types/config/model-profiles.d.ts +11 -5
- package/dist/types/config/model-registry.d.ts +3 -3
- package/dist/types/config/model-resolver.d.ts +39 -6
- package/dist/types/config/model-selector-value.d.ts +17 -0
- package/dist/types/config/models-config-schema.d.ts +10 -6
- package/dist/types/config/settings-schema.d.ts +133 -6
- package/dist/types/config/settings.d.ts +63 -35
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -6
- package/dist/types/coordinator-mcp/server.d.ts +6 -49
- package/dist/types/daemon/builtin.d.ts +6 -5
- package/dist/types/daemon/control-types.d.ts +1 -1
- package/dist/types/discovery/mcp-json.d.ts +12 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
- package/dist/types/extensibility/extensions/compact-handler.d.ts +4 -2
- package/dist/types/extensibility/extensions/get-commands-handler.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +10 -1
- package/dist/types/extensibility/extensions/runner.d.ts +3 -3
- package/dist/types/extensibility/extensions/types.d.ts +141 -16
- package/dist/types/extensibility/hooks/types.d.ts +1 -1
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -2
- package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +3 -4
- package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +13 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +4 -1
- package/dist/types/gjc-runtime/windows-powershell-command.d.ts +9 -0
- package/dist/types/harness-control-plane/operate.d.ts +5 -5
- package/dist/types/harness-control-plane/owner.d.ts +18 -4
- package/dist/types/harness-control-plane/receipts.d.ts +0 -1
- package/dist/types/harness-control-plane/sdk-transport.d.ts +43 -0
- package/dist/types/harness-control-plane/session-transport.d.ts +48 -0
- package/dist/types/harness-control-plane/types.d.ts +3 -4
- package/dist/types/lsp/index.d.ts +1 -1
- package/dist/types/main.d.ts +32 -38
- package/dist/types/modes/acp/acp-agent.d.ts +224 -53
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -0
- package/dist/types/modes/acp/acp-mode.d.ts +6 -15
- package/dist/types/modes/acp/permission-mode.d.ts +4 -0
- package/dist/types/modes/acp/startup-options.d.ts +6 -0
- package/dist/types/modes/components/agent-dashboard.d.ts +3 -0
- package/dist/types/modes/components/model-selector.d.ts +1 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +135 -0
- package/dist/types/modes/components/settings-selector.d.ts +6 -4
- package/dist/types/modes/components/welcome.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +35 -1
- package/dist/types/modes/index.d.ts +0 -4
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/runtime-init.d.ts +2 -2
- package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +6 -9
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -6
- package/dist/types/modes/shared/agent-wire/event-contract.d.ts +6 -6
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +4 -9
- package/dist/types/modes/shared/agent-wire/event-observation.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +169 -30
- package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +7 -7
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +105 -0
- package/dist/types/modes/types.d.ts +5 -0
- package/dist/types/runtime-mcp/client.d.ts +2 -0
- package/dist/types/runtime-mcp/config.d.ts +5 -1
- package/dist/types/runtime-mcp/manager.d.ts +8 -1
- package/dist/types/runtime-mcp/types.d.ts +4 -0
- package/dist/types/sdk/acp/adapter.d.ts +67 -0
- package/dist/types/sdk/acp/index.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +53 -0
- package/dist/types/sdk/broker/discovery.d.ts +30 -0
- package/dist/types/sdk/broker/ensure.d.ts +33 -0
- package/dist/types/sdk/broker/identity.d.ts +3 -0
- package/dist/types/sdk/broker/index.d.ts +8 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +95 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +85 -0
- package/dist/types/sdk/broker/process-incarnation.d.ts +14 -0
- package/dist/types/sdk/broker/runtime.d.ts +33 -0
- package/dist/types/sdk/broker/session-index.d.ts +57 -0
- package/dist/types/sdk/broker/state-version.d.ts +8 -0
- package/dist/types/sdk/broker/transport.d.ts +9 -0
- package/dist/types/sdk/bus/chat-command-policy.d.ts +49 -0
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +21 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +111 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +77 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +84 -0
- package/dist/types/{notifications → sdk/bus}/config-commands.d.ts +7 -2
- package/dist/types/sdk/bus/config.d.ts +274 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +72 -0
- package/dist/types/sdk/bus/discord-conversation.d.ts +58 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +45 -0
- package/dist/types/sdk/bus/discord-live-provider.d.ts +70 -0
- package/dist/types/sdk/bus/discord-provider.d.ts +85 -0
- package/dist/types/{notifications → sdk/bus}/html-format.d.ts +2 -2
- package/dist/types/{notifications → sdk/bus}/index.d.ts +113 -13
- package/dist/types/{notifications → sdk/bus}/lifecycle-control-runtime.d.ts +4 -2
- package/dist/types/{notifications → sdk/bus}/lifecycle-orchestrator.d.ts +18 -21
- package/dist/types/{notifications → sdk/bus}/managed-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/native-runtime-compatibility.d.ts +13 -0
- package/dist/types/sdk/bus/notification-orchestration.d.ts +170 -0
- package/dist/types/{notifications → sdk/bus}/notification-service.d.ts +7 -1
- package/dist/types/sdk/bus/session-control.d.ts +83 -0
- package/dist/types/sdk/bus/slack-conversation.d.ts +56 -0
- package/dist/types/sdk/bus/slack-daemon.d.ts +60 -0
- package/dist/types/sdk/bus/slack-live-provider.d.ts +59 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +55 -0
- package/dist/types/{notifications → sdk/bus}/telegram-cli.d.ts +4 -4
- package/dist/types/{notifications → sdk/bus}/telegram-daemon-cli.d.ts +14 -5
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -0
- package/dist/types/{notifications → sdk/bus}/telegram-daemon-control.d.ts +2 -2
- package/dist/types/{notifications → sdk/bus}/telegram-daemon.d.ts +55 -17
- package/dist/types/{notifications → sdk/bus}/telegram-reference.d.ts +2 -2
- package/dist/types/sdk/bus/telegram-setup.d.ts +128 -0
- package/dist/types/{notifications → sdk/bus}/topic-registry.d.ts +11 -0
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/session-cli.d.ts +19 -0
- package/dist/types/sdk/client/client.d.ts +1 -0
- package/dist/types/sdk/client/discovery.d.ts +27 -0
- package/dist/types/sdk/client/index.d.ts +2 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +34 -0
- package/dist/types/sdk/host/control/index.d.ts +2 -0
- package/dist/types/sdk/host/control/operations.d.ts +65 -0
- package/dist/types/sdk/host/events.d.ts +35 -0
- package/dist/types/sdk/host/host.d.ts +35 -0
- package/dist/types/sdk/host/index.d.ts +4 -0
- package/dist/types/sdk/host/query/cursor.d.ts +62 -0
- package/dist/types/sdk/host/query/handlers.d.ts +70 -0
- package/dist/types/sdk/host/query/index.d.ts +3 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +60 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +45 -0
- package/dist/types/sdk/host/types.d.ts +32 -0
- package/dist/types/sdk/index.d.ts +6 -0
- package/dist/types/sdk/lifecycle-session.d.ts +14 -0
- package/dist/types/sdk/mcp/index.d.ts +1 -0
- package/dist/types/sdk/mcp/server.d.ts +37 -0
- package/dist/types/sdk/models.d.ts +44 -0
- package/dist/types/sdk/protocol/adapter-validation.d.ts +14 -0
- package/dist/types/sdk/protocol/operation-registry.d.ts +24 -0
- package/dist/types/{sdk.d.ts → sdk/session.d.ts} +44 -38
- package/dist/types/sdk/startup-capability.d.ts +50 -0
- package/dist/types/session/agent-session.d.ts +114 -21
- package/dist/types/session/default-model-selection.d.ts +25 -0
- package/dist/types/session/fallback-chain-controller.d.ts +52 -0
- package/dist/types/session/session-manager.d.ts +58 -1
- package/dist/types/setup/credential-auto-import.d.ts +36 -6
- package/dist/types/tools/ask-answer-registry.d.ts +1 -1
- package/dist/types/tools/browser/benchmark/detector-report.d.ts +63 -0
- package/dist/types/tools/browser/benchmark/run-suite.d.ts +18 -0
- package/dist/types/tools/browser/launch.d.ts +22 -0
- package/dist/types/tools/browser/profile-discovery.d.ts +31 -0
- package/dist/types/tools/browser/profile-posture.d.ts +37 -0
- package/dist/types/tools/browser/profile-reuse.d.ts +39 -0
- package/dist/types/tools/browser/profile-warmup.d.ts +39 -0
- package/dist/types/tools/browser/registry.d.ts +19 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
- package/dist/types/tools/index.d.ts +8 -8
- package/package.json +34 -13
- package/scripts/build-sdk-package-smoke.ts +94 -0
- package/scripts/capture-notifications-settings-showcase.ts +435 -0
- package/scripts/compile-args.ts +4 -2
- package/scripts/g005-daemon-smoke.ts +4 -5
- package/scripts/g011-daemon-path-smoke.ts +5 -5
- package/scripts/generate-sdk-adapter-parity-manifest.ts +163 -0
- package/scripts/generate-sdk-operation-inventory.ts +867 -0
- package/scripts/generate-telegram-baseline-manifest.ts +128 -0
- package/scripts/manifest-schema.ts +124 -0
- package/scripts/run-test-manifest.ts +153 -0
- package/scripts/run-threat-model-check.ts +157 -0
- package/scripts/verify-gjc-sdk-canonicalization.ts +3467 -0
- package/scripts/verify-gjc-sdk-rename.ts +90 -0
- package/src/append-only-mode.ts +1 -1
- package/src/cli/args.ts +43 -12
- package/src/cli/auth-gateway-cli.ts +2 -2
- package/src/cli/config-cli.ts +2 -1
- package/src/cli/daemon-cli.ts +63 -14
- package/src/cli/list-models.ts +1 -1
- package/src/cli/notify-cli.ts +288 -286
- package/src/cli.ts +21 -2
- package/src/commands/daemon.ts +50 -6
- package/src/commands/harness.ts +93 -36
- package/src/commands/launch.ts +5 -2
- package/src/commands/mcp-serve.ts +19 -12
- package/src/commands/notify.ts +36 -3
- package/src/commands/sdk.ts +426 -0
- package/src/commit/agentic/agent.ts +1 -0
- package/src/config/atomic-yaml-patch.ts +423 -0
- package/src/config/model-profile-activation.ts +240 -89
- package/src/config/model-profiles.ts +18 -15
- package/src/config/model-registry.ts +51 -26
- package/src/config/model-resolver.ts +138 -51
- package/src/config/model-selector-value.ts +50 -0
- package/src/config/models-config-schema.ts +14 -21
- package/src/config/settings-schema.ts +128 -10
- package/src/config/settings.ts +1120 -158
- package/src/coordinator-mcp/model-preset.ts +74 -20
- package/src/coordinator-mcp/server.ts +1597 -3052
- package/src/daemon/builtin.ts +8 -5
- package/src/daemon/control-types.ts +1 -1
- package/src/defaults/gjc/skills/team/SKILL.md +4 -4
- package/src/discovery/mcp-json.ts +327 -20
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/compact-handler.ts +3 -3
- package/src/extensibility/extensions/get-commands-handler.ts +2 -2
- package/src/extensibility/extensions/loader.ts +69 -5
- package/src/extensibility/extensions/runner.ts +112 -4
- package/src/extensibility/extensions/types.ts +176 -17
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/gjc-runtime/launch-tmux.ts +170 -86
- package/src/gjc-runtime/psmux-detect.ts +82 -4
- package/src/gjc-runtime/session-state-sidecar.ts +41 -31
- package/src/gjc-runtime/team-runtime.ts +54 -35
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +53 -17
- package/src/gjc-runtime/tmux-owner-isolation.ts +1 -1
- package/src/gjc-runtime/tmux-sessions.ts +19 -13
- package/src/gjc-runtime/ultragoal-guard.ts +53 -6
- package/src/gjc-runtime/ultragoal-runtime.ts +155 -3
- package/src/gjc-runtime/windows-powershell-command.ts +58 -0
- package/src/harness-control-plane/operate.ts +9 -9
- package/src/harness-control-plane/owner.ts +283 -39
- package/src/harness-control-plane/receipts.ts +0 -1
- package/src/harness-control-plane/sdk-transport.ts +395 -0
- package/src/harness-control-plane/session-transport.ts +94 -0
- package/src/harness-control-plane/types.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +34 -35
- package/src/lsp/client.ts +42 -6
- package/src/lsp/index.ts +2 -2
- package/src/main.ts +299 -204
- package/src/modes/DESIGN.md +258 -0
- package/src/modes/acp/acp-agent.ts +1228 -2612
- package/src/modes/acp/acp-client-bridge.ts +2 -20
- package/src/modes/acp/acp-event-mapper.ts +18 -2
- package/src/modes/acp/acp-mode.ts +8 -36
- package/src/modes/acp/permission-mode.ts +25 -0
- package/src/modes/acp/startup-options.ts +6 -0
- package/src/modes/components/agent-dashboard.ts +37 -17
- package/src/modes/components/model-selector.ts +93 -35
- package/src/modes/components/notifications-settings-editor.ts +1379 -0
- package/src/modes/components/settings-selector.ts +60 -11
- package/src/modes/components/welcome.ts +4 -4
- package/src/modes/controllers/command-controller.ts +1 -2
- package/src/modes/controllers/event-controller.ts +11 -16
- package/src/modes/controllers/extension-ui-controller.ts +348 -4
- package/src/modes/controllers/selector-controller.ts +674 -36
- package/src/modes/index.ts +0 -22
- package/src/modes/interactive-mode.ts +59 -20
- package/src/modes/runtime-init.ts +298 -6
- package/src/modes/shared/agent-wire/approval-gate.ts +9 -11
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +10 -11
- package/src/modes/shared/agent-wire/event-contract.ts +7 -8
- package/src/modes/shared/agent-wire/event-envelope.ts +9 -17
- package/src/modes/shared/agent-wire/event-observation.ts +16 -7
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +1254 -87
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +15 -15
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +113 -0
- package/src/modes/types.ts +5 -0
- package/src/runtime-mcp/client.ts +94 -19
- package/src/runtime-mcp/config.ts +27 -15
- package/src/runtime-mcp/manager.ts +343 -105
- package/src/runtime-mcp/oauth-flow.ts +42 -12
- package/src/runtime-mcp/transports/http.ts +49 -19
- package/src/runtime-mcp/transports/stdio.ts +47 -28
- package/src/runtime-mcp/types.ts +10 -0
- package/src/sdk/acp/adapter.ts +418 -0
- package/src/sdk/acp/index.ts +2 -0
- package/src/sdk/broker/broker.ts +642 -0
- package/src/sdk/broker/discovery.ts +94 -0
- package/src/sdk/broker/ensure.ts +249 -0
- package/src/sdk/broker/identity.ts +42 -0
- package/src/sdk/broker/index.ts +8 -0
- package/src/sdk/broker/internal-source-marker-2178.txt +1 -0
- package/src/sdk/broker/internal-source.bunfig.toml +1 -0
- package/src/sdk/broker/lifecycle-ledger.ts +286 -0
- package/src/sdk/broker/lifecycle.ts +2157 -0
- package/src/sdk/broker/process-incarnation.ts +159 -0
- package/src/sdk/broker/runtime.ts +164 -0
- package/src/sdk/broker/session-index.ts +318 -0
- package/src/sdk/broker/state-version.ts +23 -0
- package/src/sdk/broker/transport.ts +125 -0
- package/src/sdk/bus/chat-command-policy.ts +107 -0
- package/src/sdk/bus/chat-daemon-cli.ts +214 -0
- package/src/sdk/bus/chat-daemon-control.ts +713 -0
- package/src/sdk/bus/chat-daemon-runtime.ts +526 -0
- package/src/sdk/bus/chat-effect-journal.ts +353 -0
- package/src/{notifications → sdk/bus}/config-commands.ts +39 -10
- package/src/sdk/bus/config.ts +433 -0
- package/src/sdk/bus/conversation-store.ts +391 -0
- package/src/sdk/bus/discord-conversation.ts +85 -0
- package/src/sdk/bus/discord-daemon.ts +2275 -0
- package/src/sdk/bus/discord-live-provider.ts +521 -0
- package/src/sdk/bus/discord-provider.ts +56 -0
- package/src/{notifications → sdk/bus}/html-format.ts +2 -2
- package/src/sdk/bus/index.ts +3943 -0
- package/src/{notifications → sdk/bus}/lifecycle-commands.ts +2 -0
- package/src/{notifications → sdk/bus}/lifecycle-control-runtime.ts +225 -196
- package/src/{notifications → sdk/bus}/lifecycle-orchestrator.ts +161 -74
- package/src/{notifications → sdk/bus}/managed-daemon.ts +2 -2
- package/src/sdk/bus/native-runtime-compatibility.ts +42 -0
- package/src/sdk/bus/natives-augment.d.ts +38 -0
- package/src/sdk/bus/notification-orchestration.ts +535 -0
- package/src/{notifications → sdk/bus}/notification-service.ts +106 -12
- package/src/sdk/bus/session-control.ts +333 -0
- package/src/sdk/bus/slack-conversation.ts +74 -0
- package/src/sdk/bus/slack-daemon.ts +2144 -0
- package/src/sdk/bus/slack-live-provider.ts +380 -0
- package/src/sdk/bus/slack-provider.ts +76 -0
- package/src/{notifications → sdk/bus}/telegram-cli.ts +8 -8
- package/src/sdk/bus/telegram-daemon-cli.ts +330 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +14 -0
- package/src/{notifications → sdk/bus}/telegram-daemon-control.ts +4 -4
- package/src/{notifications → sdk/bus}/telegram-daemon.ts +502 -54
- package/src/{notifications → sdk/bus}/telegram-reference.ts +2 -2
- package/src/sdk/bus/telegram-setup.ts +663 -0
- package/src/{notifications → sdk/bus}/topic-registry.ts +32 -0
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/session-cli.ts +275 -0
- package/src/sdk/client/client.ts +1 -0
- package/src/sdk/client/discovery.ts +108 -0
- package/src/sdk/client/index.ts +2 -0
- package/src/sdk/host/control/dispatch.ts +367 -0
- package/src/sdk/host/control/index.ts +10 -0
- package/src/sdk/host/control/operations.ts +63 -0
- package/src/sdk/host/events.ts +91 -0
- package/src/sdk/host/host.ts +296 -0
- package/src/sdk/host/index.ts +4 -0
- package/src/sdk/host/query/cursor.ts +189 -0
- package/src/sdk/host/query/handlers.ts +533 -0
- package/src/sdk/host/query/index.ts +3 -0
- package/src/sdk/host/query/revision-store.ts +1126 -0
- package/src/sdk/host/reverse-leases.ts +325 -0
- package/src/sdk/host/types.ts +29 -0
- package/src/sdk/index.ts +13 -0
- package/src/sdk/lifecycle-session.ts +39 -0
- package/src/sdk/mcp/index.ts +1 -0
- package/src/sdk/mcp/server.ts +332 -0
- package/src/sdk/models.ts +168 -0
- package/src/sdk/protocol/adapter-validation.ts +48 -0
- package/src/sdk/protocol/operation-inventory.generated.json +5654 -0
- package/src/sdk/protocol/operation-registry.ts +312 -0
- package/src/{sdk.ts → sdk/session.ts} +339 -212
- package/src/sdk/startup-capability.ts +189 -0
- package/src/session/agent-session.ts +2453 -894
- package/src/session/default-model-selection.ts +93 -0
- package/src/session/fallback-chain-controller.ts +136 -0
- package/src/session/session-manager.ts +438 -11
- package/src/session/session-storage.ts +8 -1
- package/src/setup/credential-auto-import.ts +290 -35
- package/src/setup/hermes/templates/operator-instructions.v1.md +3 -5
- package/src/slash-commands/builtin-registry.ts +2 -1
- package/src/task/executor.ts +62 -13
- package/src/tools/ask-answer-registry.ts +1 -1
- package/src/tools/ask.ts +13 -8
- package/src/tools/browser/STEALTH-THIRD-PARTY-NOTICES.md +26 -0
- package/src/tools/browser/benchmark/detector-report.ts +157 -0
- package/src/tools/browser/benchmark/run-suite.ts +39 -0
- package/src/tools/browser/launch.ts +124 -19
- package/src/tools/browser/profile-discovery.ts +63 -0
- package/src/tools/browser/profile-posture.ts +59 -0
- package/src/tools/browser/profile-reuse.ts +102 -0
- package/src/tools/browser/profile-warmup.ts +109 -0
- package/src/tools/browser/registry.ts +87 -11
- package/src/tools/browser/tab-protocol.ts +1 -0
- package/src/tools/browser/tab-supervisor.ts +1 -0
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/browser.ts +6 -0
- package/src/tools/index.ts +8 -8
- package/src/tools/puppeteer/14_stealth_permissions.txt +57 -0
- package/src/tools/puppeteer/15_stealth_webrtc.txt +60 -0
- package/src/tools/telegram-send.ts +2 -2
- package/vendor/insane-search/MANIFEST.json +2 -1
- package/vendor/insane-search/engine/bias_check.py +4 -1
- package/vendor/insane-search/engine/phase0.py +26 -1
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +0 -69
- package/dist/types/modes/bridge/auth.d.ts +0 -12
- package/dist/types/modes/bridge/bridge-client-bridge.d.ts +0 -9
- package/dist/types/modes/bridge/bridge-mode.d.ts +0 -47
- package/dist/types/modes/bridge/bridge-ui-context.d.ts +0 -88
- package/dist/types/modes/bridge/event-stream.d.ts +0 -8
- package/dist/types/modes/rpc/host-tools.d.ts +0 -1
- package/dist/types/modes/rpc/host-uris.d.ts +0 -1
- package/dist/types/modes/rpc/rpc-client.d.ts +0 -299
- package/dist/types/modes/rpc/rpc-mode.d.ts +0 -86
- package/dist/types/modes/rpc/rpc-socket-security.d.ts +0 -10
- package/dist/types/modes/rpc/rpc-types.d.ts +0 -810
- package/dist/types/modes/shared/agent-wire/command-contract.d.ts +0 -18
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +0 -37
- package/dist/types/modes/shared/agent-wire/command-validation.d.ts +0 -2
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +0 -56
- package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +0 -16
- package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +0 -17
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +0 -25
- package/dist/types/modes/shared/agent-wire/responses.d.ts +0 -4
- package/dist/types/modes/shared/agent-wire/scopes.d.ts +0 -18
- package/dist/types/modes/shared/agent-wire/session-registry.d.ts +0 -25
- package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +0 -42
- package/dist/types/modes/shared/agent-wire/ui-result.d.ts +0 -27
- package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +0 -29
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +0 -68
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +0 -166
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +0 -127
- package/dist/types/notifications/config.d.ts +0 -132
- package/src/harness-control-plane/rpc-adapter.ts +0 -290
- package/src/modes/bridge/auth.ts +0 -41
- package/src/modes/bridge/bridge-client-bridge.ts +0 -47
- package/src/modes/bridge/bridge-mode.ts +0 -675
- package/src/modes/bridge/bridge-ui-context.ts +0 -200
- package/src/modes/bridge/event-stream.ts +0 -70
- package/src/modes/rpc/host-tools.ts +0 -1
- package/src/modes/rpc/host-uris.ts +0 -1
- package/src/modes/rpc/rpc-client.ts +0 -1166
- package/src/modes/rpc/rpc-mode.ts +0 -1029
- package/src/modes/rpc/rpc-socket-security.ts +0 -115
- package/src/modes/rpc/rpc-types.ts +0 -637
- package/src/modes/shared/agent-wire/command-contract.ts +0 -18
- package/src/modes/shared/agent-wire/command-dispatch.ts +0 -498
- package/src/modes/shared/agent-wire/command-validation.ts +0 -181
- package/src/modes/shared/agent-wire/handshake.ts +0 -157
- package/src/modes/shared/agent-wire/host-tool-bridge.ts +0 -197
- package/src/modes/shared/agent-wire/host-uri-bridge.ts +0 -236
- package/src/modes/shared/agent-wire/protocol.ts +0 -46
- package/src/modes/shared/agent-wire/responses.ts +0 -17
- package/src/modes/shared/agent-wire/scopes.ts +0 -94
- package/src/modes/shared/agent-wire/session-registry.ts +0 -109
- package/src/modes/shared/agent-wire/ui-request-broker.ts +0 -150
- package/src/modes/shared/agent-wire/ui-result.ts +0 -48
- package/src/modes/shared/agent-wire/unattended-action-policy.ts +0 -365
- package/src/modes/shared/agent-wire/unattended-audit.ts +0 -176
- package/src/modes/shared/agent-wire/unattended-run-controller.ts +0 -426
- package/src/modes/shared/agent-wire/unattended-session.ts +0 -528
- package/src/notifications/config.ts +0 -219
- package/src/notifications/index.ts +0 -1835
- package/src/notifications/telegram-daemon-cli.ts +0 -243
- package/src/tools/puppeteer/09_stealth_locale.txt +0 -46
- /package/dist/types/{notifications → sdk/bus}/attachment-registry.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/chat-adapters.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/daemon-paths.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/engine.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/helpers.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/lifecycle-commands.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/operator-runtime.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rate-limit-pool.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/recent-activity.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/reply-sent-store.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rich-draft.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/rich-render.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/threaded-inbound.d.ts +0 -0
- /package/dist/types/{notifications → sdk/bus}/threaded-render.d.ts +0 -0
- /package/src/{notifications → sdk/bus}/attachment-registry.ts +0 -0
- /package/src/{notifications → sdk/bus}/chat-adapters.ts +0 -0
- /package/src/{notifications → sdk/bus}/daemon-paths.ts +0 -0
- /package/src/{notifications → sdk/bus}/engine.ts +0 -0
- /package/src/{notifications → sdk/bus}/helpers.ts +0 -0
- /package/src/{notifications → sdk/bus}/operator-runtime.ts +0 -0
- /package/src/{notifications → sdk/bus}/rate-limit-pool.ts +0 -0
- /package/src/{notifications → sdk/bus}/recent-activity.ts +0 -0
- /package/src/{notifications → sdk/bus}/reply-sent-store.ts +0 -0
- /package/src/{notifications → sdk/bus}/rich-draft.ts +0 -0
- /package/src/{notifications → sdk/bus}/rich-render.ts +0 -0
- /package/src/{notifications → sdk/bus}/threaded-inbound.ts +0 -0
- /package/src/{notifications → sdk/bus}/threaded-render.ts +0 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import * as fs from "node:fs/promises";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
|
|
7
|
+
export const CONVERSATION_STORE_VERSION = 1;
|
|
8
|
+
export const MAX_DEDUPE_IDS = 128;
|
|
9
|
+
|
|
10
|
+
export interface ConversationRecord {
|
|
11
|
+
generation: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ConversationStoreDocument<T extends ConversationRecord> {
|
|
15
|
+
version: typeof CONVERSATION_STORE_VERSION;
|
|
16
|
+
conversations: Record<string, T>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ConversationStoreFileHandle {
|
|
20
|
+
sync(): Promise<void>;
|
|
21
|
+
close(): Promise<void>;
|
|
22
|
+
writeFile(data: string, encoding: "utf8"): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Minimal persistence seam; callers can inject it to make durability failures deterministic. */
|
|
26
|
+
export interface ConversationStoreFs {
|
|
27
|
+
mkdir(directory: string, options: { recursive: true; mode: number }): Promise<unknown>;
|
|
28
|
+
chmod(target: string, mode: number): Promise<void>;
|
|
29
|
+
readFile(file: string, encoding: "utf8"): Promise<string>;
|
|
30
|
+
writeFile(file: string, data: string, options: { mode: number }): Promise<void>;
|
|
31
|
+
rename(from: string, to: string): Promise<void>;
|
|
32
|
+
unlink(file: string): Promise<void>;
|
|
33
|
+
open(file: string, flags: string): Promise<ConversationStoreFileHandle>;
|
|
34
|
+
stat?(file: string): Promise<{ mtimeMs: number }>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class ConversationLockTimeoutError extends Error {
|
|
38
|
+
constructor(
|
|
39
|
+
readonly lockFile: string,
|
|
40
|
+
readonly timeoutMs: number,
|
|
41
|
+
) {
|
|
42
|
+
super(`Timed out waiting ${timeoutMs}ms for conversation store lock: ${lockFile}`);
|
|
43
|
+
this.name = "ConversationLockTimeoutError";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface ConversationStoreLock {
|
|
48
|
+
pid: number;
|
|
49
|
+
incarnation: string;
|
|
50
|
+
timestamp: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const nodeFs: ConversationStoreFs = fs;
|
|
54
|
+
const UNPUBLISHED_LOCK_STALE_MS = 30_000;
|
|
55
|
+
|
|
56
|
+
export function conversationStorePath(agentDir: string, kind: string, fileName = "conversations.json"): string {
|
|
57
|
+
return path.join(agentDir, "sdk", "daemons", kind, fileName);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function boundedDedupe(ids: readonly string[], limit = MAX_DEDUPE_IDS): string[] {
|
|
61
|
+
const unique: string[] = [];
|
|
62
|
+
const seen = new Set<string>();
|
|
63
|
+
for (const id of ids) {
|
|
64
|
+
if (!id || seen.has(id)) continue;
|
|
65
|
+
seen.add(id);
|
|
66
|
+
unique.push(id);
|
|
67
|
+
}
|
|
68
|
+
return unique.length <= limit ? unique : unique.slice(unique.length - limit);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function emptyDocument<T extends ConversationRecord>(): ConversationStoreDocument<T> {
|
|
72
|
+
return { version: CONVERSATION_STORE_VERSION, conversations: {} };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
76
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Durable, per-transport mapping store. Every mutation takes an exclusive
|
|
81
|
+
* lock file shared across processes and reloads the on-disk document before
|
|
82
|
+
* applying its monotonic-generation compare-and-swap, preventing either stale
|
|
83
|
+
* writers or unrelated-key updates from replacing newer mappings.
|
|
84
|
+
*/
|
|
85
|
+
export class ConversationStore<T extends ConversationRecord> {
|
|
86
|
+
readonly #directory: string;
|
|
87
|
+
readonly #file: string;
|
|
88
|
+
readonly #fs: ConversationStoreFs;
|
|
89
|
+
readonly #clock: () => number;
|
|
90
|
+
readonly #locks = new Map<string, Promise<void>>();
|
|
91
|
+
readonly #pid: number;
|
|
92
|
+
readonly #pidIncarnation: (pid: number) => string | undefined;
|
|
93
|
+
readonly #pidAlive: (pid: number) => boolean;
|
|
94
|
+
readonly #sleep: (ms: number) => Promise<void>;
|
|
95
|
+
readonly #lockTimeoutMs: number;
|
|
96
|
+
|
|
97
|
+
constructor(input: {
|
|
98
|
+
agentDir: string;
|
|
99
|
+
kind: string;
|
|
100
|
+
fileName?: string;
|
|
101
|
+
fs?: ConversationStoreFs;
|
|
102
|
+
now?: () => number;
|
|
103
|
+
pid?: number;
|
|
104
|
+
pidIncarnation?: (pid: number) => string | undefined;
|
|
105
|
+
pidAlive?: (pid: number) => boolean;
|
|
106
|
+
sleep?: (ms: number) => Promise<void>;
|
|
107
|
+
lockTimeoutMs?: number;
|
|
108
|
+
}) {
|
|
109
|
+
this.#file = conversationStorePath(input.agentDir, input.kind, input.fileName);
|
|
110
|
+
this.#directory = path.dirname(this.#file);
|
|
111
|
+
this.#fs = input.fs ?? nodeFs;
|
|
112
|
+
this.#clock = input.now ?? Date.now;
|
|
113
|
+
this.#pid = input.pid ?? process.pid;
|
|
114
|
+
this.#pidIncarnation = input.pidIncarnation ?? defaultPidIncarnation;
|
|
115
|
+
this.#pidAlive = input.pidAlive ?? defaultPidAlive;
|
|
116
|
+
this.#sleep = input.sleep ?? (async ms => await Bun.sleep(ms));
|
|
117
|
+
this.#lockTimeoutMs = input.lockTimeoutMs ?? 1_000;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
get filePath(): string {
|
|
121
|
+
return this.#file;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async load(): Promise<ConversationStoreDocument<T>> {
|
|
125
|
+
return this.#readDocument();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async read(key: string): Promise<T | undefined> {
|
|
129
|
+
return (await this.#readDocument()).conversations[key];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Atomically write one mapping when its observed generation still matches.
|
|
134
|
+
* `undefined` only creates an absent mapping; successful writes must advance
|
|
135
|
+
* the generation by exactly one.
|
|
136
|
+
*/
|
|
137
|
+
async write(key: string, expectedGeneration: number | undefined, record: T): Promise<boolean> {
|
|
138
|
+
return this.#withLock(async () => {
|
|
139
|
+
const document = await this.#readDocument();
|
|
140
|
+
const current = document.conversations[key];
|
|
141
|
+
if (current?.generation !== expectedGeneration) return false;
|
|
142
|
+
if (!current && expectedGeneration !== undefined) return false;
|
|
143
|
+
const nextGeneration = (expectedGeneration ?? 0) + 1;
|
|
144
|
+
if (record.generation !== nextGeneration) {
|
|
145
|
+
throw new Error(`Conversation generation must advance to ${nextGeneration}`);
|
|
146
|
+
}
|
|
147
|
+
document.conversations[key] = record;
|
|
148
|
+
await this.#persist(document);
|
|
149
|
+
return true;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async delete(key: string, expectedGeneration: number): Promise<boolean> {
|
|
153
|
+
return this.#withLock(async () => {
|
|
154
|
+
const document = await this.#readDocument();
|
|
155
|
+
const current = document.conversations[key];
|
|
156
|
+
if (!current || current.generation !== expectedGeneration) return false;
|
|
157
|
+
delete document.conversations[key];
|
|
158
|
+
await this.#persist(document);
|
|
159
|
+
return true;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Apply a synchronous update under the mapping lock, retrying no stale state. */
|
|
164
|
+
async transact(key: string, update: (current: T | undefined) => T | undefined): Promise<T | undefined> {
|
|
165
|
+
return this.#withLock(async () => {
|
|
166
|
+
const document = await this.#readDocument();
|
|
167
|
+
const current = document.conversations[key];
|
|
168
|
+
const next = update(current);
|
|
169
|
+
if (!next || next === current) return current;
|
|
170
|
+
const expectedGeneration = current?.generation;
|
|
171
|
+
const expectedNext = (expectedGeneration ?? 0) + 1;
|
|
172
|
+
if (next.generation !== expectedNext) {
|
|
173
|
+
throw new Error(`Conversation generation must advance to ${expectedNext}`);
|
|
174
|
+
}
|
|
175
|
+
document.conversations[key] = next;
|
|
176
|
+
await this.#persist(document);
|
|
177
|
+
return next;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async #withLock<R>(operation: () => Promise<R>): Promise<R> {
|
|
182
|
+
const previous = this.#locks.get(this.#file) ?? Promise.resolve();
|
|
183
|
+
const gate = Promise.withResolvers<void>();
|
|
184
|
+
const tail = previous.then(() => gate.promise);
|
|
185
|
+
this.#locks.set(this.#file, tail);
|
|
186
|
+
await previous;
|
|
187
|
+
let fileLock: ConversationStoreFileHandle | undefined;
|
|
188
|
+
try {
|
|
189
|
+
fileLock = await this.#acquireFileLock();
|
|
190
|
+
return await operation();
|
|
191
|
+
} finally {
|
|
192
|
+
try {
|
|
193
|
+
await fileLock?.close();
|
|
194
|
+
if (fileLock)
|
|
195
|
+
await this.#fs.unlink(`${this.#file}.lock`).catch(error => {
|
|
196
|
+
if (!isMissing(error)) throw error;
|
|
197
|
+
});
|
|
198
|
+
} finally {
|
|
199
|
+
gate.resolve();
|
|
200
|
+
if (this.#locks.get(this.#file) === tail) this.#locks.delete(this.#file);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async #acquireFileLock(): Promise<ConversationStoreFileHandle> {
|
|
206
|
+
const lockFile = `${this.#file}.lock`;
|
|
207
|
+
const deadline = Date.now() + this.#lockTimeoutMs;
|
|
208
|
+
await this.#fs.mkdir(this.#directory, { recursive: true, mode: 0o700 });
|
|
209
|
+
for (;;) {
|
|
210
|
+
try {
|
|
211
|
+
const handle = await this.#fs.open(lockFile, "wx");
|
|
212
|
+
try {
|
|
213
|
+
const lock: ConversationStoreLock = {
|
|
214
|
+
pid: this.#pid,
|
|
215
|
+
incarnation: this.#pidIncarnation(this.#pid) ?? "unavailable",
|
|
216
|
+
timestamp: this.#clock(),
|
|
217
|
+
};
|
|
218
|
+
await handle.writeFile(`${JSON.stringify(lock)}\n`, "utf8");
|
|
219
|
+
await handle.sync();
|
|
220
|
+
return handle;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
await handle.close().catch(() => undefined);
|
|
223
|
+
await this.#fs.unlink(lockFile).catch(() => undefined);
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (!isAlreadyExists(error)) throw error;
|
|
228
|
+
if (await this.#reclaimStaleLock(lockFile)) continue;
|
|
229
|
+
if (Date.now() >= deadline) throw new ConversationLockTimeoutError(lockFile, this.#lockTimeoutMs);
|
|
230
|
+
await this.#sleep(Math.min(10, Math.max(1, deadline - Date.now())));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async #reclaimStaleLock(lockFile: string): Promise<boolean> {
|
|
236
|
+
if (!(await this.#isStaleLock(lockFile))) return false;
|
|
237
|
+
const reclaimFile = `${lockFile}.reclaim`;
|
|
238
|
+
const reclaimLock = await this.#acquireReclaimLock(reclaimFile);
|
|
239
|
+
if (!reclaimLock) return false;
|
|
240
|
+
try {
|
|
241
|
+
if (!(await this.#isStaleLock(lockFile))) return false;
|
|
242
|
+
await this.#fs.unlink(lockFile).catch(() => undefined);
|
|
243
|
+
return true;
|
|
244
|
+
} finally {
|
|
245
|
+
await reclaimLock.close().catch(() => undefined);
|
|
246
|
+
await this.#fs.unlink(reclaimFile).catch(() => undefined);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async #acquireReclaimLock(reclaimFile: string): Promise<ConversationStoreFileHandle | undefined> {
|
|
250
|
+
try {
|
|
251
|
+
return await this.#createLockFile(reclaimFile);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (!isAlreadyExists(error) || !(await this.#isStaleLock(reclaimFile))) return undefined;
|
|
254
|
+
await this.#fs.unlink(reclaimFile).catch(() => undefined);
|
|
255
|
+
try {
|
|
256
|
+
return await this.#createLockFile(reclaimFile);
|
|
257
|
+
} catch (retryError) {
|
|
258
|
+
if (isAlreadyExists(retryError)) return undefined;
|
|
259
|
+
throw retryError;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async #createLockFile(lockFile: string): Promise<ConversationStoreFileHandle> {
|
|
264
|
+
const handle = await this.#fs.open(lockFile, "wx");
|
|
265
|
+
try {
|
|
266
|
+
const lock: ConversationStoreLock = {
|
|
267
|
+
pid: this.#pid,
|
|
268
|
+
incarnation: this.#pidIncarnation(this.#pid) ?? "unavailable",
|
|
269
|
+
timestamp: this.#clock(),
|
|
270
|
+
};
|
|
271
|
+
await handle.writeFile(`${JSON.stringify(lock)}\n`, "utf8");
|
|
272
|
+
await handle.sync();
|
|
273
|
+
return handle;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
await handle.close().catch(() => undefined);
|
|
276
|
+
await this.#fs.unlink(lockFile).catch(() => undefined);
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async #isStaleLock(lockFile: string): Promise<boolean> {
|
|
281
|
+
let parsed: unknown;
|
|
282
|
+
try {
|
|
283
|
+
parsed = JSON.parse(await this.#fs.readFile(lockFile, "utf8"));
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (isMissing(error)) return true;
|
|
286
|
+
return await this.#isExpiredUnpublishedLock(lockFile);
|
|
287
|
+
}
|
|
288
|
+
if (!isConversationStoreLock(parsed)) return await this.#isExpiredUnpublishedLock(lockFile);
|
|
289
|
+
const currentIncarnation = this.#pidIncarnation(parsed.pid);
|
|
290
|
+
return (
|
|
291
|
+
!this.#pidAlive(parsed.pid) ||
|
|
292
|
+
(currentIncarnation !== undefined &&
|
|
293
|
+
parsed.incarnation !== "unavailable" &&
|
|
294
|
+
currentIncarnation !== parsed.incarnation)
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
async #isExpiredUnpublishedLock(lockFile: string): Promise<boolean> {
|
|
298
|
+
const stat = this.#fs.stat ? await this.#fs.stat(lockFile).catch(() => undefined) : undefined;
|
|
299
|
+
return Boolean(stat && this.#clock() - stat.mtimeMs >= UNPUBLISHED_LOCK_STALE_MS);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async #readDocument(): Promise<ConversationStoreDocument<T>> {
|
|
303
|
+
try {
|
|
304
|
+
const parsed: unknown = JSON.parse(await this.#fs.readFile(this.#file, "utf8"));
|
|
305
|
+
if (!isRecord(parsed) || parsed.version !== CONVERSATION_STORE_VERSION || !isRecord(parsed.conversations)) {
|
|
306
|
+
throw new Error("Invalid conversation store document");
|
|
307
|
+
}
|
|
308
|
+
return { version: CONVERSATION_STORE_VERSION, conversations: parsed.conversations as Record<string, T> };
|
|
309
|
+
} catch (error) {
|
|
310
|
+
if (isMissing(error)) return emptyDocument<T>();
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async #persist(document: ConversationStoreDocument<T>): Promise<void> {
|
|
316
|
+
await this.#fs.mkdir(this.#directory, { recursive: true, mode: 0o700 });
|
|
317
|
+
await this.#fs.chmod(this.#directory, 0o700);
|
|
318
|
+
const temporary = `${this.#file}.${process.pid}.${randomUUID()}.tmp`;
|
|
319
|
+
try {
|
|
320
|
+
await this.#fs.writeFile(temporary, `${JSON.stringify(document)}\n`, { mode: 0o600 });
|
|
321
|
+
await this.#fs.chmod(temporary, 0o600);
|
|
322
|
+
const handle = await this.#fs.open(temporary, "r");
|
|
323
|
+
try {
|
|
324
|
+
await handle.sync();
|
|
325
|
+
} finally {
|
|
326
|
+
await handle.close();
|
|
327
|
+
}
|
|
328
|
+
await this.#fs.rename(temporary, this.#file);
|
|
329
|
+
const directory = await this.#fs.open(this.#directory, "r");
|
|
330
|
+
try {
|
|
331
|
+
await directory.sync();
|
|
332
|
+
} finally {
|
|
333
|
+
await directory.close();
|
|
334
|
+
}
|
|
335
|
+
} catch (error) {
|
|
336
|
+
await this.#fs.unlink(temporary).catch(() => undefined);
|
|
337
|
+
throw error;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function isMissing(error: unknown): error is NodeJS.ErrnoException {
|
|
343
|
+
return isRecord(error) && error.code === "ENOENT";
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function isAlreadyExists(error: unknown): error is NodeJS.ErrnoException {
|
|
347
|
+
return isRecord(error) && error.code === "EEXIST";
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function defaultPidAlive(pid: number): boolean {
|
|
351
|
+
try {
|
|
352
|
+
process.kill(pid, 0);
|
|
353
|
+
return true;
|
|
354
|
+
} catch {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function defaultPidIncarnation(pid: number): string | undefined {
|
|
360
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return undefined;
|
|
361
|
+
if (process.platform === "linux") {
|
|
362
|
+
try {
|
|
363
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
364
|
+
return `linux:${
|
|
365
|
+
stat
|
|
366
|
+
.slice(stat.lastIndexOf(")") + 2)
|
|
367
|
+
.trim()
|
|
368
|
+
.split(/\s+/)[19]
|
|
369
|
+
}`;
|
|
370
|
+
} catch {
|
|
371
|
+
return undefined;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (process.platform === "darwin") {
|
|
375
|
+
const result = spawnSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8" });
|
|
376
|
+
const startedAt = result.status === 0 ? result.stdout.trim() : "";
|
|
377
|
+
return startedAt ? `darwin:${startedAt}` : undefined;
|
|
378
|
+
}
|
|
379
|
+
return undefined;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function isConversationStoreLock(value: unknown): value is ConversationStoreLock {
|
|
383
|
+
return (
|
|
384
|
+
isRecord(value) &&
|
|
385
|
+
typeof value.pid === "number" &&
|
|
386
|
+
Number.isSafeInteger(value.pid) &&
|
|
387
|
+
value.pid > 0 &&
|
|
388
|
+
typeof value.incarnation === "string" &&
|
|
389
|
+
typeof value.timestamp === "number"
|
|
390
|
+
);
|
|
391
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { boundedDedupe, type ConversationRecord } from "./conversation-store";
|
|
2
|
+
|
|
3
|
+
/** Identifier-only reference to an inbound effect whose payload and lifecycle live in ChatEffectJournal. */
|
|
4
|
+
export interface DiscordInboundDispatchReceipt {
|
|
5
|
+
key: string;
|
|
6
|
+
eventId: string;
|
|
7
|
+
interactionId?: string;
|
|
8
|
+
kind: "action" | "command";
|
|
9
|
+
actionId?: string;
|
|
10
|
+
actionNonce?: string;
|
|
11
|
+
endpointGeneration: number;
|
|
12
|
+
effectId: string;
|
|
13
|
+
idempotencyKey: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type DiscordConversationState = "absent" | "creating" | "active" | "archived" | "resuming" | "closed" | "error";
|
|
17
|
+
|
|
18
|
+
/** Durable Discord mapping. It deliberately contains identifiers only: never endpoint tokens or message bodies. */
|
|
19
|
+
export interface DiscordConversation extends ConversationRecord {
|
|
20
|
+
state: DiscordConversationState;
|
|
21
|
+
appId: string;
|
|
22
|
+
guildId: string;
|
|
23
|
+
parentChannelId: string;
|
|
24
|
+
threadId?: string;
|
|
25
|
+
sessionId?: string;
|
|
26
|
+
createNonce?: string;
|
|
27
|
+
/** A lease makes thread creation exclusive across daemon processes. */
|
|
28
|
+
createOwner?: string;
|
|
29
|
+
createLeaseExpiresAt?: number;
|
|
30
|
+
endpointGeneration?: number;
|
|
31
|
+
/** Immutable lineage for archive and resume provider effects. */
|
|
32
|
+
effectIncarnationId?: string;
|
|
33
|
+
/** Current archive operation authority. */
|
|
34
|
+
archiveOccurrenceId?: string;
|
|
35
|
+
archiveEffectId?: string;
|
|
36
|
+
/** Current resume operation authority. */
|
|
37
|
+
resumeOccurrenceId?: string;
|
|
38
|
+
resumeEffectId?: string;
|
|
39
|
+
/** Action authority only; action bodies and endpoint credentials are never persisted. */
|
|
40
|
+
pendingActionId?: string;
|
|
41
|
+
/** Immutable per-publication action authority, bound to component routes and inbound effects. */
|
|
42
|
+
pendingActionNonce?: string;
|
|
43
|
+
/** Durable journal identity for the immutable outbound action publication intent. */
|
|
44
|
+
pendingActionEffectId?: string;
|
|
45
|
+
/** Replayable durable receipts for acknowledged inbound effects. */
|
|
46
|
+
inboundDispatches?: DiscordInboundDispatchReceipt[];
|
|
47
|
+
updatedAt: number;
|
|
48
|
+
archivedAt?: number;
|
|
49
|
+
closedAt?: number;
|
|
50
|
+
supersededByThreadId?: string;
|
|
51
|
+
seenEventIds: string[];
|
|
52
|
+
seenInteractionIds: string[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function discordConversationKey(input: {
|
|
56
|
+
appId: string;
|
|
57
|
+
guildId: string;
|
|
58
|
+
parentChannelId: string;
|
|
59
|
+
threadId: string;
|
|
60
|
+
}): string {
|
|
61
|
+
return `${input.appId}:${input.guildId}:${input.parentChannelId}:${input.threadId}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function normalizeDiscordConversation(record: DiscordConversation): DiscordConversation {
|
|
65
|
+
const dispatches = record.inboundDispatches ?? [];
|
|
66
|
+
return {
|
|
67
|
+
...record,
|
|
68
|
+
seenEventIds: boundedDedupe(record.seenEventIds),
|
|
69
|
+
seenInteractionIds: boundedDedupe(record.seenInteractionIds),
|
|
70
|
+
...(dispatches.length === 0 ? {} : { inboundDispatches: dispatches }),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function acceptsDiscordInbound(
|
|
75
|
+
record: DiscordConversation,
|
|
76
|
+
threadId: string,
|
|
77
|
+
endpointGeneration: number,
|
|
78
|
+
): boolean {
|
|
79
|
+
return (
|
|
80
|
+
record.state === "active" &&
|
|
81
|
+
record.threadId === threadId &&
|
|
82
|
+
record.supersededByThreadId === undefined &&
|
|
83
|
+
record.endpointGeneration === endpointGeneration
|
|
84
|
+
);
|
|
85
|
+
}
|