@gajae-code/coding-agent 0.10.1 → 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 +70 -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/gajae-pet-widget.d.ts +1 -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/pet-capability.d.ts +37 -0
- package/dist/types/modes/components/pet-selector.d.ts +1 -1
- package/dist/types/modes/components/settings-selector.d.ts +15 -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 +5 -1
- 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 +16 -1
- 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 +37 -34
- 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/extensibility/slash-commands.ts +3 -3
- 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 +5 -2
- 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 +36 -37
- 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/custom-editor.ts +1 -0
- package/src/modes/components/gajae-pet-widget.ts +169 -20
- package/src/modes/components/model-selector.ts +93 -35
- package/src/modes/components/notifications-settings-editor.ts +1379 -0
- package/src/modes/components/pet-capability.ts +115 -0
- package/src/modes/components/pet-selector.ts +4 -6
- package/src/modes/components/settings-selector.ts +86 -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 +357 -12
- package/src/modes/controllers/input-controller.ts +43 -13
- package/src/modes/controllers/selector-controller.ts +671 -44
- package/src/modes/index.ts +0 -22
- package/src/modes/interactive-mode.ts +108 -24
- 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 +16 -1
- 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 +2470 -866
- package/src/session/default-model-selection.ts +93 -0
- package/src/session/fallback-chain-controller.ts +136 -0
- package/src/session/session-manager.ts +447 -13
- 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 +33 -30
- 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,713 @@
|
|
|
1
|
+
import { spawn as childProcessSpawn, spawnSync } from "node:child_process";
|
|
2
|
+
import * as crypto from "node:crypto";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import type { Settings } from "../../config/settings";
|
|
6
|
+
import type {
|
|
7
|
+
BuiltInDaemonController,
|
|
8
|
+
DaemonHealth,
|
|
9
|
+
DaemonOperationOptions,
|
|
10
|
+
DaemonOperationResult,
|
|
11
|
+
DaemonRuntimeInfo,
|
|
12
|
+
DaemonStatus,
|
|
13
|
+
} from "../../daemon/control-types";
|
|
14
|
+
import { resolveGjcRuntimeSpawnInfo } from "../../daemon/runtime";
|
|
15
|
+
import { getNotificationConfig, isDiscordConfigured, isSlackConfigured } from "./config";
|
|
16
|
+
|
|
17
|
+
export type ChatDaemonKind = "discord" | "slack";
|
|
18
|
+
export type ChatDaemonAction = "stop" | "reload";
|
|
19
|
+
|
|
20
|
+
export interface ChatDaemonState {
|
|
21
|
+
version: 1;
|
|
22
|
+
kind: ChatDaemonKind;
|
|
23
|
+
pid: number;
|
|
24
|
+
ownerId: string;
|
|
25
|
+
identity: string;
|
|
26
|
+
incarnation: string;
|
|
27
|
+
startedAt: number;
|
|
28
|
+
heartbeatAt: number;
|
|
29
|
+
transportHealthy: boolean;
|
|
30
|
+
stoppedAt?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ChatDaemonControlRequest {
|
|
34
|
+
version: 1;
|
|
35
|
+
requestId: string;
|
|
36
|
+
action: ChatDaemonAction;
|
|
37
|
+
ownerId: string;
|
|
38
|
+
pid: number;
|
|
39
|
+
createdAt: number;
|
|
40
|
+
incarnation: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ChatDaemonControlDeps {
|
|
44
|
+
pidAlive?: (pid: number) => boolean;
|
|
45
|
+
sendSignal?: (pid: number, signal: NodeJS.Signals) => void;
|
|
46
|
+
spawn?: (command: string, args: string[], opts: { detached: boolean; stdio: "ignore" }) => { unref?: () => void };
|
|
47
|
+
execPath?: string;
|
|
48
|
+
ownerPid?: number;
|
|
49
|
+
randomId?: () => string;
|
|
50
|
+
pidIncarnation?: (pid: number) => string | undefined;
|
|
51
|
+
sleep?: (ms: number) => Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const HEARTBEAT_TTL_MS = 20_000;
|
|
55
|
+
const DEFAULT_GRACEFUL_TIMEOUT_MS = 8_000;
|
|
56
|
+
const DEFAULT_KILL_TIMEOUT_MS = 3_000;
|
|
57
|
+
const UNPUBLISHED_OWNER_LOCK_STALE_MS = HEARTBEAT_TTL_MS;
|
|
58
|
+
|
|
59
|
+
interface ChatDaemonOwnerLock {
|
|
60
|
+
pid: number;
|
|
61
|
+
incarnation: string;
|
|
62
|
+
createdAt: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface ChatDaemonOwnershipProbe {
|
|
66
|
+
pidAlive(pid: number): boolean;
|
|
67
|
+
pidIncarnation(pid: number): string | undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function chatDaemonPaths(
|
|
71
|
+
agentDir: string,
|
|
72
|
+
kind: ChatDaemonKind,
|
|
73
|
+
): { dir: string; lock: string; state: string; control: string } {
|
|
74
|
+
const dir = path.join(agentDir, "sdk", "daemons", kind);
|
|
75
|
+
return {
|
|
76
|
+
dir,
|
|
77
|
+
lock: path.join(dir, "owner.lock"),
|
|
78
|
+
state: path.join(dir, "state.json"),
|
|
79
|
+
control: path.join(dir, "control.json"),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function identityFor(settings: Settings, kind: ChatDaemonKind): string | undefined {
|
|
84
|
+
const cfg = getNotificationConfig(settings);
|
|
85
|
+
if (kind === "discord") {
|
|
86
|
+
if (!isDiscordConfigured(cfg)) return undefined;
|
|
87
|
+
return fingerprint([
|
|
88
|
+
cfg.discord.botToken,
|
|
89
|
+
cfg.discord.applicationId,
|
|
90
|
+
cfg.discord.guildId,
|
|
91
|
+
cfg.discord.parentChannelId,
|
|
92
|
+
String(cfg.redact),
|
|
93
|
+
cfg.verbosity,
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
if (!isSlackConfigured(cfg)) return undefined;
|
|
97
|
+
return fingerprint([
|
|
98
|
+
cfg.slack.botToken,
|
|
99
|
+
cfg.slack.appToken,
|
|
100
|
+
cfg.slack.workspaceId,
|
|
101
|
+
cfg.slack.channelId,
|
|
102
|
+
cfg.slack.authorizedUserId ?? "",
|
|
103
|
+
String(cfg.redact),
|
|
104
|
+
cfg.verbosity,
|
|
105
|
+
]);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function fingerprint(values: string[]): string {
|
|
109
|
+
return crypto.createHash("sha256").update(values.join("\0")).digest("hex").slice(0, 16);
|
|
110
|
+
}
|
|
111
|
+
function defaultPidAlive(pid: number): boolean {
|
|
112
|
+
try {
|
|
113
|
+
process.kill(pid, 0);
|
|
114
|
+
return true;
|
|
115
|
+
} catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function defaultSignal(pid: number, signal: NodeJS.Signals): void {
|
|
120
|
+
try {
|
|
121
|
+
process.kill(pid, signal);
|
|
122
|
+
} catch {}
|
|
123
|
+
}
|
|
124
|
+
function defaultPidIncarnation(pid: number): string | undefined {
|
|
125
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return undefined;
|
|
126
|
+
if (process.platform === "linux") {
|
|
127
|
+
try {
|
|
128
|
+
const stat = fs.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
129
|
+
return `linux:${
|
|
130
|
+
stat
|
|
131
|
+
.slice(stat.lastIndexOf(")") + 2)
|
|
132
|
+
.trim()
|
|
133
|
+
.split(/\s+/)[19]
|
|
134
|
+
}`;
|
|
135
|
+
} catch {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (process.platform === "darwin") {
|
|
140
|
+
const result = spawnSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8" });
|
|
141
|
+
const startedAt = result.status === 0 ? result.stdout.trim() : "";
|
|
142
|
+
return startedAt ? `darwin:${startedAt}` : undefined;
|
|
143
|
+
}
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
function runtimeInfo(execPath?: string): DaemonRuntimeInfo {
|
|
147
|
+
const rt = resolveGjcRuntimeSpawnInfo(execPath ?? process.execPath);
|
|
148
|
+
return {
|
|
149
|
+
mode: rt.mode,
|
|
150
|
+
execPath: rt.execPath,
|
|
151
|
+
reloadPicksUpSourceEdits: rt.reloadPicksUpSourceEdits,
|
|
152
|
+
warning: rt.warning,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const stateWriteTails = new Map<string, Promise<void>>();
|
|
157
|
+
|
|
158
|
+
async function withStateWriteLock<T>(file: string, operation: () => Promise<T>): Promise<T> {
|
|
159
|
+
const previous = stateWriteTails.get(file) ?? Promise.resolve();
|
|
160
|
+
const gate = Promise.withResolvers<void>();
|
|
161
|
+
const tail = previous.then(() => gate.promise);
|
|
162
|
+
stateWriteTails.set(file, tail);
|
|
163
|
+
await previous;
|
|
164
|
+
try {
|
|
165
|
+
return await operation();
|
|
166
|
+
} finally {
|
|
167
|
+
gate.resolve();
|
|
168
|
+
if (stateWriteTails.get(file) === tail) stateWriteTails.delete(file);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function readJson<T>(file: string): Promise<T | undefined> {
|
|
173
|
+
try {
|
|
174
|
+
return JSON.parse(await fs.promises.readFile(file, "utf8")) as T;
|
|
175
|
+
} catch {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function writeJson(file: string, value: unknown): Promise<void> {
|
|
180
|
+
await fs.promises.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
181
|
+
const tmp = `${file}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
182
|
+
try {
|
|
183
|
+
await fs.promises.writeFile(tmp, `${JSON.stringify(value)}\n`, { mode: 0o600 });
|
|
184
|
+
await fs.promises.rename(tmp, file);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
await fs.promises.unlink(tmp).catch(() => undefined);
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export async function readChatDaemonState(
|
|
192
|
+
agentDir: string,
|
|
193
|
+
kind: ChatDaemonKind,
|
|
194
|
+
): Promise<ChatDaemonState | undefined> {
|
|
195
|
+
return await readJson(chatDaemonPaths(agentDir, kind).state);
|
|
196
|
+
}
|
|
197
|
+
export async function readChatDaemonControlRequest(
|
|
198
|
+
agentDir: string,
|
|
199
|
+
kind: ChatDaemonKind,
|
|
200
|
+
): Promise<ChatDaemonControlRequest | undefined> {
|
|
201
|
+
return await readJson(chatDaemonPaths(agentDir, kind).control);
|
|
202
|
+
}
|
|
203
|
+
export async function writeChatDaemonControlRequest(
|
|
204
|
+
agentDir: string,
|
|
205
|
+
kind: ChatDaemonKind,
|
|
206
|
+
request: ChatDaemonControlRequest,
|
|
207
|
+
): Promise<void> {
|
|
208
|
+
await writeJson(chatDaemonPaths(agentDir, kind).control, request);
|
|
209
|
+
}
|
|
210
|
+
export async function clearChatDaemonControlRequest(
|
|
211
|
+
agentDir: string,
|
|
212
|
+
kind: ChatDaemonKind,
|
|
213
|
+
requestId?: string,
|
|
214
|
+
): Promise<void> {
|
|
215
|
+
const paths = chatDaemonPaths(agentDir, kind);
|
|
216
|
+
if (requestId && (await readChatDaemonControlRequest(agentDir, kind))?.requestId !== requestId) return;
|
|
217
|
+
await fs.promises.unlink(paths.control).catch(() => undefined);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function buildChatDaemonSpawnArgs(input: {
|
|
221
|
+
kind: ChatDaemonKind;
|
|
222
|
+
ownerId: string;
|
|
223
|
+
agentDir: string;
|
|
224
|
+
execPath?: string;
|
|
225
|
+
}): { command: string; args: string[]; runtime: DaemonRuntimeInfo } {
|
|
226
|
+
const rt = resolveGjcRuntimeSpawnInfo(input.execPath ?? process.execPath);
|
|
227
|
+
return {
|
|
228
|
+
command: rt.execPath,
|
|
229
|
+
args: [
|
|
230
|
+
...rt.argsPrefix,
|
|
231
|
+
"daemon",
|
|
232
|
+
`${input.kind}-internal`,
|
|
233
|
+
"--owner-id",
|
|
234
|
+
input.ownerId,
|
|
235
|
+
"--agent-dir",
|
|
236
|
+
input.agentDir,
|
|
237
|
+
],
|
|
238
|
+
runtime: runtimeInfo(input.execPath),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export class ChatDaemonController implements BuiltInDaemonController {
|
|
243
|
+
readonly kind: ChatDaemonKind;
|
|
244
|
+
constructor(
|
|
245
|
+
private readonly settings: Settings,
|
|
246
|
+
kind: ChatDaemonKind,
|
|
247
|
+
private readonly deps: ChatDaemonControlDeps = {},
|
|
248
|
+
) {
|
|
249
|
+
this.kind = kind;
|
|
250
|
+
}
|
|
251
|
+
private identity(): string | undefined {
|
|
252
|
+
return identityFor(this.settings, this.kind);
|
|
253
|
+
}
|
|
254
|
+
private alive(pid: number): boolean {
|
|
255
|
+
return (this.deps.pidAlive ?? defaultPidAlive)(pid);
|
|
256
|
+
}
|
|
257
|
+
async status(): Promise<DaemonStatus> {
|
|
258
|
+
const runtime = runtimeInfo(this.deps.execPath);
|
|
259
|
+
const identity = this.identity();
|
|
260
|
+
if (!identity) return { kind: this.kind, configured: false, health: "not_configured", runtime };
|
|
261
|
+
const state = await readChatDaemonState(this.settings.getAgentDir(), this.kind);
|
|
262
|
+
const live = Boolean(
|
|
263
|
+
state &&
|
|
264
|
+
state.version === 1 &&
|
|
265
|
+
state.kind === this.kind &&
|
|
266
|
+
state.identity === identity &&
|
|
267
|
+
!state.stoppedAt &&
|
|
268
|
+
state.transportHealthy &&
|
|
269
|
+
Date.now() - state.heartbeatAt <= HEARTBEAT_TTL_MS &&
|
|
270
|
+
this.alive(state.pid) &&
|
|
271
|
+
this.incarnation(state.pid) === state.incarnation,
|
|
272
|
+
);
|
|
273
|
+
const health: DaemonHealth = live ? "running" : state && !state.stoppedAt ? "stale" : "stopped";
|
|
274
|
+
return {
|
|
275
|
+
kind: this.kind,
|
|
276
|
+
configured: true,
|
|
277
|
+
health,
|
|
278
|
+
pid: state?.pid,
|
|
279
|
+
ownerId: state?.ownerId,
|
|
280
|
+
startedAt: state?.startedAt,
|
|
281
|
+
heartbeatAt: state?.heartbeatAt,
|
|
282
|
+
runtime,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
async stop(opts: DaemonOperationOptions = {}): Promise<DaemonOperationResult> {
|
|
286
|
+
return await this.operate("stop", opts);
|
|
287
|
+
}
|
|
288
|
+
async reload(opts: DaemonOperationOptions = {}): Promise<DaemonOperationResult> {
|
|
289
|
+
return await this.operate("reload", opts);
|
|
290
|
+
}
|
|
291
|
+
async ensure(): Promise<EnsureChatDaemonResult> {
|
|
292
|
+
const identity = this.identity();
|
|
293
|
+
if (!identity) return "disabled";
|
|
294
|
+
const existing = await readChatDaemonState(this.settings.getAgentDir(), this.kind);
|
|
295
|
+
if (existing && this.isLiveState(existing)) {
|
|
296
|
+
if (existing.identity === identity) return "attached";
|
|
297
|
+
await this.stopForReplacement(existing);
|
|
298
|
+
}
|
|
299
|
+
const spawned = await this.spawn();
|
|
300
|
+
if (spawned) return "owner_spawned";
|
|
301
|
+
const replacement = await readChatDaemonState(this.settings.getAgentDir(), this.kind);
|
|
302
|
+
if (replacement && replacement.identity === identity && this.isLiveState(replacement)) return "attached";
|
|
303
|
+
throw new Error(`Unable to attach or spawn ${this.kind} daemon owner`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private async operate(action: ChatDaemonAction, opts: DaemonOperationOptions): Promise<DaemonOperationResult> {
|
|
307
|
+
const before = await this.status();
|
|
308
|
+
const warnings = before.runtime.warning ? [before.runtime.warning] : [];
|
|
309
|
+
if (!before.configured)
|
|
310
|
+
return this.result(action, false, `${this.kind} notifications are not configured`, before, before, warnings);
|
|
311
|
+
if (before.health !== "running") {
|
|
312
|
+
if (action === "stop")
|
|
313
|
+
return this.result(action, true, `no running ${this.kind} daemon`, before, before, warnings);
|
|
314
|
+
if (opts.spawnIfStopped === false)
|
|
315
|
+
return this.result(action, true, `no running ${this.kind} daemon to reload`, before, before, warnings);
|
|
316
|
+
const spawned = await this.spawn();
|
|
317
|
+
const after = await this.status();
|
|
318
|
+
return this.result(
|
|
319
|
+
action,
|
|
320
|
+
spawned,
|
|
321
|
+
spawned ? `spawned fresh ${this.kind} daemon` : `a live ${this.kind} owner already exists`,
|
|
322
|
+
before,
|
|
323
|
+
after,
|
|
324
|
+
warnings,
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
const state = await readChatDaemonState(this.settings.getAgentDir(), this.kind);
|
|
328
|
+
if (!state || !this.ownsCapturedState(state, before))
|
|
329
|
+
return this.result(
|
|
330
|
+
action,
|
|
331
|
+
false,
|
|
332
|
+
`${this.kind} daemon ownership changed; refusing to signal`,
|
|
333
|
+
before,
|
|
334
|
+
await this.status(),
|
|
335
|
+
warnings,
|
|
336
|
+
);
|
|
337
|
+
const requestId = this.deps.randomId?.() ?? crypto.randomUUID();
|
|
338
|
+
await writeChatDaemonControlRequest(this.settings.getAgentDir(), this.kind, {
|
|
339
|
+
version: 1,
|
|
340
|
+
requestId,
|
|
341
|
+
action,
|
|
342
|
+
ownerId: state.ownerId,
|
|
343
|
+
pid: state.pid,
|
|
344
|
+
incarnation: state.incarnation,
|
|
345
|
+
createdAt: Date.now(),
|
|
346
|
+
});
|
|
347
|
+
if (!(await this.signalIfOwner(state, "SIGTERM"))) return this.ownerChanged(action, requestId, before, warnings);
|
|
348
|
+
let dead = await this.waitForDeath(state.pid, opts.gracefulTimeoutMs ?? DEFAULT_GRACEFUL_TIMEOUT_MS);
|
|
349
|
+
if (!dead && opts.force) {
|
|
350
|
+
if (!(await this.signalIfOwner(state, "SIGKILL")))
|
|
351
|
+
return this.ownerChanged(action, requestId, before, warnings);
|
|
352
|
+
dead = await this.waitForDeath(state.pid, opts.killTimeoutMs ?? DEFAULT_KILL_TIMEOUT_MS);
|
|
353
|
+
}
|
|
354
|
+
if (!dead) {
|
|
355
|
+
await clearChatDaemonControlRequest(this.settings.getAgentDir(), this.kind, requestId);
|
|
356
|
+
const after = await this.status();
|
|
357
|
+
return this.result(
|
|
358
|
+
action,
|
|
359
|
+
false,
|
|
360
|
+
opts.force ? "old daemon did not exit after SIGKILL" : "old daemon did not exit; rerun with --force",
|
|
361
|
+
before,
|
|
362
|
+
after,
|
|
363
|
+
warnings,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
await clearChatDaemonControlRequest(this.settings.getAgentDir(), this.kind, requestId);
|
|
367
|
+
if (action === "stop")
|
|
368
|
+
return this.result(action, true, `stopped ${this.kind} daemon`, before, await this.status(), warnings);
|
|
369
|
+
const spawned = await this.spawn();
|
|
370
|
+
return this.result(
|
|
371
|
+
action,
|
|
372
|
+
spawned,
|
|
373
|
+
spawned ? `reloaded ${this.kind} daemon` : `a live ${this.kind} owner already exists`,
|
|
374
|
+
before,
|
|
375
|
+
await this.status(),
|
|
376
|
+
warnings,
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
private incarnation(pid: number): string | undefined {
|
|
380
|
+
return (this.deps.pidIncarnation ?? defaultPidIncarnation)(pid);
|
|
381
|
+
}
|
|
382
|
+
private isLiveState(state: ChatDaemonState): boolean {
|
|
383
|
+
return (
|
|
384
|
+
state.version === 1 &&
|
|
385
|
+
state.kind === this.kind &&
|
|
386
|
+
!state.stoppedAt &&
|
|
387
|
+
state.transportHealthy &&
|
|
388
|
+
Date.now() - state.heartbeatAt <= HEARTBEAT_TTL_MS &&
|
|
389
|
+
this.alive(state.pid) &&
|
|
390
|
+
Boolean(state.incarnation) &&
|
|
391
|
+
this.incarnation(state.pid) === state.incarnation
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
private async stopForReplacement(state: ChatDaemonState): Promise<void> {
|
|
395
|
+
if (!this.isLiveState(state)) return;
|
|
396
|
+
const requestId = this.deps.randomId?.() ?? crypto.randomUUID();
|
|
397
|
+
await writeChatDaemonControlRequest(this.settings.getAgentDir(), this.kind, {
|
|
398
|
+
version: 1,
|
|
399
|
+
requestId,
|
|
400
|
+
action: "reload",
|
|
401
|
+
ownerId: state.ownerId,
|
|
402
|
+
pid: state.pid,
|
|
403
|
+
incarnation: state.incarnation,
|
|
404
|
+
createdAt: Date.now(),
|
|
405
|
+
});
|
|
406
|
+
try {
|
|
407
|
+
if (!(await this.signalIfOwner(state, "SIGTERM")))
|
|
408
|
+
throw new Error(`${this.kind} daemon ownership changed; refusing replacement`);
|
|
409
|
+
let dead = await this.waitForDeath(state.pid, DEFAULT_GRACEFUL_TIMEOUT_MS);
|
|
410
|
+
if (!dead) {
|
|
411
|
+
if (!(await this.signalIfOwner(state, "SIGKILL")))
|
|
412
|
+
throw new Error(`${this.kind} daemon ownership changed; refusing replacement`);
|
|
413
|
+
dead = await this.waitForDeath(state.pid, DEFAULT_KILL_TIMEOUT_MS);
|
|
414
|
+
}
|
|
415
|
+
if (!dead) throw new Error(`Old ${this.kind} daemon did not exit before replacement`);
|
|
416
|
+
} finally {
|
|
417
|
+
await clearChatDaemonControlRequest(this.settings.getAgentDir(), this.kind, requestId);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private ownsCapturedState(state: ChatDaemonState, before: DaemonStatus): boolean {
|
|
422
|
+
return (
|
|
423
|
+
state.ownerId === before.ownerId &&
|
|
424
|
+
state.pid === before.pid &&
|
|
425
|
+
Boolean(state.incarnation) &&
|
|
426
|
+
this.incarnation(state.pid) === state.incarnation
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
private async signalIfOwner(state: ChatDaemonState, signal: NodeJS.Signals): Promise<boolean> {
|
|
430
|
+
const current = await readChatDaemonState(this.settings.getAgentDir(), this.kind);
|
|
431
|
+
if (
|
|
432
|
+
!current ||
|
|
433
|
+
current.ownerId !== state.ownerId ||
|
|
434
|
+
current.pid !== state.pid ||
|
|
435
|
+
current.incarnation !== state.incarnation ||
|
|
436
|
+
this.incarnation(state.pid) !== state.incarnation
|
|
437
|
+
)
|
|
438
|
+
return false;
|
|
439
|
+
(this.deps.sendSignal ?? defaultSignal)(state.pid, signal);
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
private async ownerChanged(
|
|
443
|
+
action: ChatDaemonAction,
|
|
444
|
+
requestId: string,
|
|
445
|
+
before: DaemonStatus,
|
|
446
|
+
warnings: string[],
|
|
447
|
+
): Promise<DaemonOperationResult> {
|
|
448
|
+
await clearChatDaemonControlRequest(this.settings.getAgentDir(), this.kind, requestId);
|
|
449
|
+
return this.result(
|
|
450
|
+
action,
|
|
451
|
+
false,
|
|
452
|
+
`${this.kind} daemon ownership changed; refusing to signal`,
|
|
453
|
+
before,
|
|
454
|
+
await this.status(),
|
|
455
|
+
warnings,
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
private result(
|
|
459
|
+
action: ChatDaemonAction,
|
|
460
|
+
ok: boolean,
|
|
461
|
+
message: string,
|
|
462
|
+
before: DaemonStatus,
|
|
463
|
+
after: DaemonStatus,
|
|
464
|
+
warnings: string[],
|
|
465
|
+
): DaemonOperationResult {
|
|
466
|
+
return { kind: this.kind, action, ok, message, before, after, warnings };
|
|
467
|
+
}
|
|
468
|
+
private async waitForDeath(pid: number, timeout: number): Promise<boolean> {
|
|
469
|
+
const until = Date.now() + timeout;
|
|
470
|
+
while (this.alive(pid) && Date.now() < until) await this.sleep(25);
|
|
471
|
+
return !this.alive(pid);
|
|
472
|
+
}
|
|
473
|
+
private sleep(ms: number): Promise<void> {
|
|
474
|
+
return this.deps.sleep ? this.deps.sleep(ms) : new Promise(resolve => setTimeout(resolve, ms));
|
|
475
|
+
}
|
|
476
|
+
private async spawn(): Promise<boolean> {
|
|
477
|
+
const identity = this.identity();
|
|
478
|
+
if (!identity) return false;
|
|
479
|
+
const paths = chatDaemonPaths(this.settings.getAgentDir(), this.kind);
|
|
480
|
+
await fs.promises.mkdir(paths.dir, { recursive: true, mode: 0o700 });
|
|
481
|
+
const existing = await readChatDaemonState(this.settings.getAgentDir(), this.kind);
|
|
482
|
+
if (
|
|
483
|
+
existing &&
|
|
484
|
+
!existing.stoppedAt &&
|
|
485
|
+
existing.identity === identity &&
|
|
486
|
+
Date.now() - existing.heartbeatAt <= HEARTBEAT_TTL_MS &&
|
|
487
|
+
this.alive(existing.pid) &&
|
|
488
|
+
this.incarnation(existing.pid) === existing.incarnation
|
|
489
|
+
)
|
|
490
|
+
return false;
|
|
491
|
+
const ownerId = `${this.deps.ownerPid ?? process.ppid}-${this.deps.randomId?.() ?? crypto.randomUUID()}`;
|
|
492
|
+
const { command, args } = buildChatDaemonSpawnArgs({
|
|
493
|
+
kind: this.kind,
|
|
494
|
+
ownerId,
|
|
495
|
+
agentDir: this.settings.getAgentDir(),
|
|
496
|
+
execPath: this.deps.execPath,
|
|
497
|
+
});
|
|
498
|
+
(this.deps.spawn ?? ((command, args, opts) => childProcessSpawn(command, args, opts)))(command, args, {
|
|
499
|
+
detached: true,
|
|
500
|
+
stdio: "ignore",
|
|
501
|
+
}).unref?.();
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
export type EnsureChatDaemonResult = "disabled" | "owner_spawned" | "attached";
|
|
507
|
+
|
|
508
|
+
async function ensureChatDaemon(
|
|
509
|
+
kind: ChatDaemonKind,
|
|
510
|
+
settings: Settings,
|
|
511
|
+
deps: ChatDaemonControlDeps = {},
|
|
512
|
+
): Promise<EnsureChatDaemonResult> {
|
|
513
|
+
return await new ChatDaemonController(settings, kind, deps).ensure();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
export async function ensureDiscordDaemon(
|
|
517
|
+
settings: Settings,
|
|
518
|
+
deps: ChatDaemonControlDeps = {},
|
|
519
|
+
): Promise<EnsureChatDaemonResult> {
|
|
520
|
+
return await ensureChatDaemon("discord", settings, deps);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
export async function ensureSlackDaemon(
|
|
524
|
+
settings: Settings,
|
|
525
|
+
deps: ChatDaemonControlDeps = {},
|
|
526
|
+
): Promise<EnsureChatDaemonResult> {
|
|
527
|
+
return await ensureChatDaemon("slack", settings, deps);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export async function acquireChatDaemonOwnership(input: {
|
|
531
|
+
agentDir: string;
|
|
532
|
+
kind: ChatDaemonKind;
|
|
533
|
+
ownerId: string;
|
|
534
|
+
pid?: number;
|
|
535
|
+
identity: string;
|
|
536
|
+
incarnation?: string;
|
|
537
|
+
pidAlive?: (pid: number) => boolean;
|
|
538
|
+
pidIncarnation?: (pid: number) => string | undefined;
|
|
539
|
+
}): Promise<boolean> {
|
|
540
|
+
const paths = chatDaemonPaths(input.agentDir, input.kind);
|
|
541
|
+
const pid = input.pid ?? process.pid;
|
|
542
|
+
const probe: ChatDaemonOwnershipProbe = {
|
|
543
|
+
pidAlive: input.pidAlive ?? defaultPidAlive,
|
|
544
|
+
pidIncarnation: input.pidIncarnation ?? defaultPidIncarnation,
|
|
545
|
+
};
|
|
546
|
+
const incarnation = input.incarnation ?? probe.pidIncarnation(pid) ?? "unavailable";
|
|
547
|
+
await fs.promises.mkdir(paths.dir, { recursive: true, mode: 0o700 });
|
|
548
|
+
if (!(await createChatDaemonOwnerLock(paths.lock, { pid, incarnation, createdAt: Date.now() }))) {
|
|
549
|
+
if (!(await reclaimChatDaemonOwnerLock(paths.lock, paths.state, probe))) return false;
|
|
550
|
+
if (!(await createChatDaemonOwnerLock(paths.lock, { pid, incarnation, createdAt: Date.now() }))) return false;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
await withStateWriteLock(
|
|
554
|
+
paths.state,
|
|
555
|
+
async () =>
|
|
556
|
+
await writeJson(paths.state, {
|
|
557
|
+
version: 1,
|
|
558
|
+
kind: input.kind,
|
|
559
|
+
pid,
|
|
560
|
+
ownerId: input.ownerId,
|
|
561
|
+
identity: input.identity,
|
|
562
|
+
incarnation,
|
|
563
|
+
startedAt: Date.now(),
|
|
564
|
+
heartbeatAt: Date.now(),
|
|
565
|
+
transportHealthy: false,
|
|
566
|
+
} satisfies ChatDaemonState),
|
|
567
|
+
);
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
async function createChatDaemonOwnerLock(lock: string, owner: ChatDaemonOwnerLock): Promise<boolean> {
|
|
572
|
+
try {
|
|
573
|
+
const handle = await fs.promises.open(lock, "wx", 0o600);
|
|
574
|
+
try {
|
|
575
|
+
await handle.writeFile(`${JSON.stringify(owner)}\n`, "utf8");
|
|
576
|
+
await handle.sync();
|
|
577
|
+
} finally {
|
|
578
|
+
await handle.close();
|
|
579
|
+
}
|
|
580
|
+
return true;
|
|
581
|
+
} catch (error) {
|
|
582
|
+
if (isAlreadyExists(error)) return false;
|
|
583
|
+
throw error;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
async function reclaimChatDaemonOwnerLock(
|
|
588
|
+
lock: string,
|
|
589
|
+
stateFile: string,
|
|
590
|
+
probe: ChatDaemonOwnershipProbe,
|
|
591
|
+
): Promise<boolean> {
|
|
592
|
+
if (!(await canReclaimChatDaemonOwnerLock(lock, stateFile, probe))) return false;
|
|
593
|
+
|
|
594
|
+
const reclaimFile = `${lock}.reclaim`;
|
|
595
|
+
const reclaimLock = await acquireChatDaemonReclaimLock(reclaimFile, probe);
|
|
596
|
+
|
|
597
|
+
if (!reclaimLock) return false;
|
|
598
|
+
try {
|
|
599
|
+
if (!(await canReclaimChatDaemonOwnerLock(lock, stateFile, probe))) return false;
|
|
600
|
+
|
|
601
|
+
await fs.promises.unlink(lock).catch(() => undefined);
|
|
602
|
+
return true;
|
|
603
|
+
} finally {
|
|
604
|
+
await reclaimLock.close().catch(() => undefined);
|
|
605
|
+
await fs.promises.unlink(reclaimFile).catch(() => undefined);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
async function acquireChatDaemonReclaimLock(
|
|
610
|
+
reclaimFile: string,
|
|
611
|
+
probe: ChatDaemonOwnershipProbe,
|
|
612
|
+
): Promise<{ close(): Promise<void> } | undefined> {
|
|
613
|
+
const owner: ChatDaemonOwnerLock = {
|
|
614
|
+
pid: process.pid,
|
|
615
|
+
incarnation: probe.pidIncarnation(process.pid) ?? "unavailable",
|
|
616
|
+
createdAt: Date.now(),
|
|
617
|
+
};
|
|
618
|
+
if (await createChatDaemonOwnerLock(reclaimFile, owner)) return await fs.promises.open(reclaimFile, "r+");
|
|
619
|
+
if (!(await isStaleChatDaemonLock(reclaimFile, probe))) return undefined;
|
|
620
|
+
await fs.promises.unlink(reclaimFile).catch(() => undefined);
|
|
621
|
+
if (!(await createChatDaemonOwnerLock(reclaimFile, owner))) return undefined;
|
|
622
|
+
return await fs.promises.open(reclaimFile, "r+");
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
async function isStaleChatDaemonLock(lock: string, probe: ChatDaemonOwnershipProbe): Promise<boolean> {
|
|
626
|
+
let owner: unknown;
|
|
627
|
+
try {
|
|
628
|
+
owner = JSON.parse(await fs.promises.readFile(lock, "utf8"));
|
|
629
|
+
} catch {}
|
|
630
|
+
if (isChatDaemonOwnerLock(owner)) {
|
|
631
|
+
const currentIncarnation = probe.pidIncarnation(owner.pid);
|
|
632
|
+
return (
|
|
633
|
+
!probe.pidAlive(owner.pid) ||
|
|
634
|
+
(currentIncarnation !== undefined &&
|
|
635
|
+
owner.incarnation !== "unavailable" &&
|
|
636
|
+
currentIncarnation !== owner.incarnation)
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
const stat = await fs.promises.stat(lock).catch(() => undefined);
|
|
640
|
+
return Boolean(stat && Date.now() - stat.mtimeMs >= UNPUBLISHED_OWNER_LOCK_STALE_MS);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
async function canReclaimChatDaemonOwnerLock(
|
|
644
|
+
lock: string,
|
|
645
|
+
stateFile: string,
|
|
646
|
+
probe: ChatDaemonOwnershipProbe,
|
|
647
|
+
): Promise<boolean> {
|
|
648
|
+
const state = await readJson<ChatDaemonState>(stateFile);
|
|
649
|
+
if (state && probe.pidAlive(state.pid) && probe.pidIncarnation(state.pid) === state.incarnation) return false;
|
|
650
|
+
return await isStaleChatDaemonLock(lock, probe);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function isChatDaemonOwnerLock(value: unknown): value is ChatDaemonOwnerLock {
|
|
654
|
+
return (
|
|
655
|
+
typeof value === "object" &&
|
|
656
|
+
value !== null &&
|
|
657
|
+
typeof (value as ChatDaemonOwnerLock).pid === "number" &&
|
|
658
|
+
Number.isSafeInteger((value as ChatDaemonOwnerLock).pid) &&
|
|
659
|
+
(value as ChatDaemonOwnerLock).pid > 0 &&
|
|
660
|
+
typeof (value as ChatDaemonOwnerLock).incarnation === "string" &&
|
|
661
|
+
typeof (value as ChatDaemonOwnerLock).createdAt === "number"
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function isAlreadyExists(error: unknown): error is NodeJS.ErrnoException {
|
|
666
|
+
return typeof error === "object" && error !== null && (error as NodeJS.ErrnoException).code === "EEXIST";
|
|
667
|
+
}
|
|
668
|
+
export async function renewChatDaemonHeartbeat(input: {
|
|
669
|
+
agentDir: string;
|
|
670
|
+
kind: ChatDaemonKind;
|
|
671
|
+
ownerId: string;
|
|
672
|
+
pid?: number;
|
|
673
|
+
incarnation?: string;
|
|
674
|
+
transportHealthy: boolean;
|
|
675
|
+
}): Promise<boolean> {
|
|
676
|
+
const paths = chatDaemonPaths(input.agentDir, input.kind);
|
|
677
|
+
return await withStateWriteLock(paths.state, async () => {
|
|
678
|
+
const state = await readJson<ChatDaemonState>(paths.state);
|
|
679
|
+
const pid = input.pid ?? state?.pid;
|
|
680
|
+
if (
|
|
681
|
+
!state ||
|
|
682
|
+
state.ownerId !== input.ownerId ||
|
|
683
|
+
pid !== state.pid ||
|
|
684
|
+
!input.incarnation ||
|
|
685
|
+
state.incarnation !== input.incarnation
|
|
686
|
+
)
|
|
687
|
+
return false;
|
|
688
|
+
await writeJson(paths.state, { ...state, heartbeatAt: Date.now(), transportHealthy: input.transportHealthy });
|
|
689
|
+
return true;
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
export async function releaseChatDaemonOwnership(input: {
|
|
693
|
+
agentDir: string;
|
|
694
|
+
kind: ChatDaemonKind;
|
|
695
|
+
ownerId: string;
|
|
696
|
+
pid?: number;
|
|
697
|
+
incarnation?: string;
|
|
698
|
+
}): Promise<void> {
|
|
699
|
+
const paths = chatDaemonPaths(input.agentDir, input.kind);
|
|
700
|
+
await withStateWriteLock(paths.state, async () => {
|
|
701
|
+
const state = await readJson<ChatDaemonState>(paths.state);
|
|
702
|
+
if (
|
|
703
|
+
state?.ownerId !== input.ownerId ||
|
|
704
|
+
(input.pid !== undefined && state.pid !== input.pid) ||
|
|
705
|
+
(input.incarnation !== undefined && state.incarnation !== input.incarnation)
|
|
706
|
+
)
|
|
707
|
+
return;
|
|
708
|
+
await writeJson(paths.state, { ...state, stoppedAt: Date.now(), transportHealthy: false });
|
|
709
|
+
const lock = await readJson<ChatDaemonOwnerLock>(paths.lock);
|
|
710
|
+
if (lock?.pid === state.pid && lock.incarnation === state.incarnation)
|
|
711
|
+
await fs.promises.unlink(paths.lock).catch(() => undefined);
|
|
712
|
+
});
|
|
713
|
+
}
|