@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,94 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { processIncarnation } from "./process-incarnation";
|
|
5
|
+
import { assertSupportedStateVersion, SDK_STATE_VERSION } from "./state-version";
|
|
6
|
+
|
|
7
|
+
export const BROKER_HEARTBEAT_TTL_MS = 15_000;
|
|
8
|
+
export interface BrokerDiscovery {
|
|
9
|
+
version: typeof SDK_STATE_VERSION;
|
|
10
|
+
protocolVersion: 3;
|
|
11
|
+
packageGeneration: string;
|
|
12
|
+
ownerId: string;
|
|
13
|
+
pid: number;
|
|
14
|
+
incarnation: string;
|
|
15
|
+
host: "127.0.0.1";
|
|
16
|
+
port: number;
|
|
17
|
+
url: string;
|
|
18
|
+
token: string;
|
|
19
|
+
startedAt: number;
|
|
20
|
+
heartbeatAt: number;
|
|
21
|
+
}
|
|
22
|
+
export type RedactedBrokerDiscovery = Omit<BrokerDiscovery, "token"> & { token: "[redacted]" };
|
|
23
|
+
export type BrokerDiscoveryWrite = Omit<BrokerDiscovery, "incarnation"> & { incarnation?: string };
|
|
24
|
+
export const brokerDiscoveryPath = (agentDir: string) => path.join(agentDir, "sdk", "broker.json");
|
|
25
|
+
export const newBrokerToken = () => randomBytes(32).toString("hex");
|
|
26
|
+
export const brokerProcessIncarnation = processIncarnation;
|
|
27
|
+
export function isPidAlive(pid: number): boolean {
|
|
28
|
+
try {
|
|
29
|
+
process.kill(pid, 0);
|
|
30
|
+
return true;
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return (e as NodeJS.ErrnoException).code === "EPERM";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function syncFile(file: string): Promise<void> {
|
|
36
|
+
const handle = await fs.open(file, "r");
|
|
37
|
+
try {
|
|
38
|
+
await handle.sync();
|
|
39
|
+
} finally {
|
|
40
|
+
await handle.close();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export async function writeBrokerDiscovery(agentDir: string, discovery: BrokerDiscoveryWrite): Promise<void> {
|
|
44
|
+
const incarnation = discovery.incarnation ?? brokerProcessIncarnation(discovery.pid);
|
|
45
|
+
if (!incarnation) throw new Error(`Broker process incarnation is unavailable for pid ${discovery.pid}.`);
|
|
46
|
+
const record: BrokerDiscovery = { ...discovery, incarnation };
|
|
47
|
+
const file = brokerDiscoveryPath(agentDir);
|
|
48
|
+
const dir = path.dirname(file);
|
|
49
|
+
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
50
|
+
await fs.chmod(dir, 0o700);
|
|
51
|
+
const temp = `${file}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
52
|
+
try {
|
|
53
|
+
await fs.writeFile(temp, `${JSON.stringify(record)}\n`, { mode: 0o600 });
|
|
54
|
+
await fs.chmod(temp, 0o600);
|
|
55
|
+
await syncFile(temp);
|
|
56
|
+
await fs.rename(temp, file);
|
|
57
|
+
await syncFile(dir);
|
|
58
|
+
} finally {
|
|
59
|
+
await fs.rm(temp, { force: true });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export async function readBrokerDiscovery(
|
|
63
|
+
agentDir: string,
|
|
64
|
+
ttlMs = BROKER_HEARTBEAT_TTL_MS,
|
|
65
|
+
): Promise<BrokerDiscovery | null> {
|
|
66
|
+
try {
|
|
67
|
+
const raw: unknown = JSON.parse(await fs.readFile(brokerDiscoveryPath(agentDir), "utf8"));
|
|
68
|
+
if (!raw || typeof raw !== "object") return null;
|
|
69
|
+
assertSupportedStateVersion(brokerDiscoveryPath(agentDir), raw);
|
|
70
|
+
const d = raw as BrokerDiscovery;
|
|
71
|
+
if (
|
|
72
|
+
d.version !== SDK_STATE_VERSION ||
|
|
73
|
+
d.protocolVersion !== 3 ||
|
|
74
|
+
d.host !== "127.0.0.1" ||
|
|
75
|
+
!d.token ||
|
|
76
|
+
!Number.isSafeInteger(d.pid) ||
|
|
77
|
+
d.pid <= 0 ||
|
|
78
|
+
typeof d.incarnation !== "string" ||
|
|
79
|
+
d.incarnation.length === 0 ||
|
|
80
|
+
!Number.isFinite(d.heartbeatAt)
|
|
81
|
+
)
|
|
82
|
+
return null;
|
|
83
|
+
if (!isPidAlive(d.pid)) return null;
|
|
84
|
+
const incarnation = brokerProcessIncarnation(d.pid);
|
|
85
|
+
if (!incarnation || incarnation !== d.incarnation || Date.now() - d.heartbeatAt > ttlMs) return null;
|
|
86
|
+
return d;
|
|
87
|
+
} catch (e) {
|
|
88
|
+
if ((e as NodeJS.ErrnoException).code === "ENOENT" || e instanceof SyntaxError) return null;
|
|
89
|
+
throw e;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export function redactBrokerDiscovery(discovery: BrokerDiscovery): RedactedBrokerDiscovery {
|
|
93
|
+
return { ...discovery, token: "[redacted]" };
|
|
94
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { type ChildProcess, spawn } from "node:child_process";
|
|
2
|
+
import { type BrokerDiscovery, brokerProcessIncarnation, readBrokerDiscovery } from "./discovery";
|
|
3
|
+
import { resolveSdkInternalSpawnCommand, type SdkInternalSpawnCommand } from "./runtime";
|
|
4
|
+
export interface EnsureBrokerSettings {
|
|
5
|
+
agentDir: string;
|
|
6
|
+
heartbeatTtlMs?: number;
|
|
7
|
+
/**
|
|
8
|
+
* Environment for the spawned detached broker. Defaults to `process.env`; tests
|
|
9
|
+
* that pre-start an isolated broker pass the same sanitized child env so the
|
|
10
|
+
* broker and the child that attaches to it share one owned root.
|
|
11
|
+
*/
|
|
12
|
+
env?: NodeJS.ProcessEnv;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DISCOVERY_TIMEOUT_MS = 10_000;
|
|
16
|
+
// Bounded grace windows for reaping a spawned broker on failure, mirroring the
|
|
17
|
+
// owned-process teardown convention (SIGTERM -> grace -> SIGKILL -> hard cap).
|
|
18
|
+
const REAP_GRACEFUL_MS = 2_000;
|
|
19
|
+
const REAP_SIGKILL_CAP_MS = 2_000;
|
|
20
|
+
interface BrokerOwner {
|
|
21
|
+
stop(): Promise<void>;
|
|
22
|
+
canReuse(discovery: BrokerDiscovery | null): boolean;
|
|
23
|
+
markReady(discovery: BrokerDiscovery): boolean;
|
|
24
|
+
}
|
|
25
|
+
const owners = new Map<string, BrokerOwner>();
|
|
26
|
+
const ensureInFlight = new Map<string, Promise<BrokerDiscovery>>();
|
|
27
|
+
const reapErrorGuards = new WeakSet<ChildProcess>();
|
|
28
|
+
interface ReapTiming {
|
|
29
|
+
gracefulMs: number;
|
|
30
|
+
killVerifyMs: number;
|
|
31
|
+
}
|
|
32
|
+
const DEFAULT_REAP_TIMING: ReapTiming = {
|
|
33
|
+
gracefulMs: REAP_GRACEFUL_MS,
|
|
34
|
+
killVerifyMs: REAP_SIGKILL_CAP_MS,
|
|
35
|
+
};
|
|
36
|
+
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Terminate and reap a detached broker this process spawned, targeting the exact
|
|
40
|
+
* owned {@link ChildProcess} (never by name). SIGTERM escalates to SIGKILL after
|
|
41
|
+
* a bounded grace window; a child still alive after SIGKILL is surfaced rather
|
|
42
|
+
* than silently orphaned. Reaping is idempotent once the child has exited.
|
|
43
|
+
*
|
|
44
|
+
* Termination is proven only by an observed exit — an `exit`/`close` event or a
|
|
45
|
+
* non-null `exitCode`/`signalCode`. A still-live child can emit `error` during
|
|
46
|
+
* teardown (e.g. a transient signal-delivery failure); that is diagnostic only
|
|
47
|
+
* and never counts as exit, so the escalation cannot be skipped mid-shutdown.
|
|
48
|
+
*/
|
|
49
|
+
async function reapSpawnedBroker(child: ChildProcess, timing: ReapTiming = DEFAULT_REAP_TIMING): Promise<void> {
|
|
50
|
+
// A spawn failure (e.g. ENOENT) never created a kernel process: pid is
|
|
51
|
+
// undefined and there is nothing to signal or await. The `error` event is the
|
|
52
|
+
// only signal and is diagnostic here — termination trivially holds, so do not
|
|
53
|
+
// run out the TERM/KILL windows or report a stuck child that never existed.
|
|
54
|
+
if (child.pid === undefined) return;
|
|
55
|
+
// Reaping owns repeated teardown diagnostics too. Keep exactly one error
|
|
56
|
+
// listener for the retained child so a later signal-delivery error cannot
|
|
57
|
+
// become an unhandled EventEmitter error after the spawn listener is consumed.
|
|
58
|
+
if (!reapErrorGuards.has(child)) {
|
|
59
|
+
child.on("error", () => {});
|
|
60
|
+
reapErrorGuards.add(child);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Awaits an authoritative exit signal, never a transient `error`. Resolves on
|
|
64
|
+
// an `exit`/`close` event or when the codes are already set; the caller
|
|
65
|
+
// re-checks the codes after the race, so resolution alone is never proof.
|
|
66
|
+
const awaitVerifiedExit = (): Promise<void> => {
|
|
67
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
68
|
+
if (child.exitCode !== null || child.signalCode !== null) resolve();
|
|
69
|
+
else {
|
|
70
|
+
child.once("exit", () => resolve());
|
|
71
|
+
child.once("close", () => resolve());
|
|
72
|
+
}
|
|
73
|
+
return promise;
|
|
74
|
+
};
|
|
75
|
+
// Observed exit is authoritative: only non-null exit/signal codes prove the
|
|
76
|
+
// child is gone, regardless of which event (if any) resolved the wait.
|
|
77
|
+
const hasExited = (): boolean => child.exitCode !== null || child.signalCode !== null;
|
|
78
|
+
const signal = (sig: NodeJS.Signals): void => {
|
|
79
|
+
if (hasExited()) return;
|
|
80
|
+
try {
|
|
81
|
+
child.kill(sig);
|
|
82
|
+
} catch {
|
|
83
|
+
// already exited between the liveness check and the kill
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
if (hasExited()) return;
|
|
87
|
+
signal("SIGTERM");
|
|
88
|
+
await Promise.race([awaitVerifiedExit(), sleep(timing.gracefulMs)]);
|
|
89
|
+
if (hasExited()) return;
|
|
90
|
+
signal("SIGKILL");
|
|
91
|
+
await Promise.race([awaitVerifiedExit(), sleep(timing.killVerifyMs)]);
|
|
92
|
+
if (hasExited()) return;
|
|
93
|
+
// SIGKILL is uninterruptible; a child still alive past this bounded wait is a
|
|
94
|
+
// kernel-level stuck state. Surface it rather than silently orphaning the spawn.
|
|
95
|
+
throw new Error(`Detached SDK broker (pid ${child.pid}) did not exit after SIGKILL during reap.`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function registerBrokerOwner(
|
|
99
|
+
agentDir: string,
|
|
100
|
+
child: ChildProcess,
|
|
101
|
+
timing: ReapTiming = DEFAULT_REAP_TIMING,
|
|
102
|
+
): BrokerOwner {
|
|
103
|
+
const incarnation = child.pid === undefined ? undefined : brokerProcessIncarnation(child.pid);
|
|
104
|
+
let state: "starting" | "ready" | "cleanup-unverified" = "starting";
|
|
105
|
+
const matches = (discovery: BrokerDiscovery | null): boolean =>
|
|
106
|
+
Boolean(
|
|
107
|
+
discovery &&
|
|
108
|
+
child.pid !== undefined &&
|
|
109
|
+
incarnation &&
|
|
110
|
+
discovery.pid === child.pid &&
|
|
111
|
+
discovery.incarnation === incarnation,
|
|
112
|
+
);
|
|
113
|
+
const owner: BrokerOwner = {
|
|
114
|
+
async stop(): Promise<void> {
|
|
115
|
+
try {
|
|
116
|
+
await reapSpawnedBroker(child, timing);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
state = "cleanup-unverified";
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
if (owners.get(agentDir) === owner) owners.delete(agentDir);
|
|
122
|
+
},
|
|
123
|
+
canReuse(discovery): boolean {
|
|
124
|
+
return state === "ready" && matches(discovery);
|
|
125
|
+
},
|
|
126
|
+
markReady(discovery): boolean {
|
|
127
|
+
if (!matches(discovery)) return false;
|
|
128
|
+
state = "ready";
|
|
129
|
+
return true;
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
owners.set(agentDir, owner);
|
|
133
|
+
return owner;
|
|
134
|
+
}
|
|
135
|
+
function brokerSpawnEnvironment(command: SdkInternalSpawnCommand, override?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
136
|
+
const environment = { ...(override ?? command.env) };
|
|
137
|
+
delete environment.BUN_OPTIONS;
|
|
138
|
+
if (command.kind === "bun-source") {
|
|
139
|
+
delete environment.PI_COMPILED;
|
|
140
|
+
delete environment.GJC_COMPILED;
|
|
141
|
+
}
|
|
142
|
+
return environment;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function ensureBrokerOnce(settings: EnsureBrokerSettings): Promise<BrokerDiscovery> {
|
|
146
|
+
const priorOwner = owners.get(settings.agentDir);
|
|
147
|
+
const existing = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
|
|
148
|
+
if (priorOwner) {
|
|
149
|
+
// A retained cleanup failure fences every discovery record. Only a ready
|
|
150
|
+
// record bound to this exact child incarnation may be reused.
|
|
151
|
+
if (priorOwner.canReuse(existing)) return existing!;
|
|
152
|
+
await priorOwner.stop();
|
|
153
|
+
const discoveredAfterCleanup = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
|
|
154
|
+
if (discoveredAfterCleanup) return discoveredAfterCleanup;
|
|
155
|
+
} else if (existing) {
|
|
156
|
+
return existing;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const command = resolveSdkInternalSpawnCommand("broker-internal");
|
|
160
|
+
const child = spawn(command.file, [...command.args, "--agent-dir", settings.agentDir], {
|
|
161
|
+
detached: true,
|
|
162
|
+
stdio: "ignore",
|
|
163
|
+
env: brokerSpawnEnvironment(command, settings.env),
|
|
164
|
+
...(command.kind === "bun-source" ? { cwd: command.cwd } : {}),
|
|
165
|
+
});
|
|
166
|
+
child.unref();
|
|
167
|
+
let spawnError: Error | undefined;
|
|
168
|
+
child.once("error", error => {
|
|
169
|
+
spawnError = error;
|
|
170
|
+
});
|
|
171
|
+
const owner = registerBrokerOwner(settings.agentDir, child);
|
|
172
|
+
const deadline = Date.now() + DISCOVERY_TIMEOUT_MS;
|
|
173
|
+
let discoveryError: unknown;
|
|
174
|
+
while (Date.now() < deadline) {
|
|
175
|
+
// Fail fast: if the spawn failed or the child already exited, discovery can
|
|
176
|
+
// never succeed — stop (a no-op once dead) and surface the failure instead
|
|
177
|
+
// of running out the discovery timeout with an orphaned child.
|
|
178
|
+
if (spawnError || child.exitCode !== null || child.signalCode !== null) break;
|
|
179
|
+
try {
|
|
180
|
+
const discovered = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
|
|
181
|
+
if (discovered) {
|
|
182
|
+
if (owner.markReady(discovered)) return discovered;
|
|
183
|
+
await owner.stop();
|
|
184
|
+
return discovered;
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
187
|
+
// A transient read failure (e.g. a half-written record) must not orphan the
|
|
188
|
+
// child; remember it and keep polling. It is surfaced if discovery fails.
|
|
189
|
+
discoveryError = error;
|
|
190
|
+
}
|
|
191
|
+
await sleep(50);
|
|
192
|
+
}
|
|
193
|
+
// Discovery failed (timeout, early child exit, spawn error, or unreadable
|
|
194
|
+
// discovery): the spawned broker never became reachable. Terminate and reap it
|
|
195
|
+
// so the failure cannot leave a detached orphan behind, then surface why.
|
|
196
|
+
const exitedBeforeDiscovery = child.exitCode !== null || child.signalCode !== null;
|
|
197
|
+
const failure = spawnError
|
|
198
|
+
? new Error(`Failed to spawn detached SDK broker: ${spawnError.message}`)
|
|
199
|
+
: exitedBeforeDiscovery
|
|
200
|
+
? new Error(
|
|
201
|
+
`Detached SDK broker exited before discovery (code=${child.exitCode}, signal=${child.signalCode}).`,
|
|
202
|
+
)
|
|
203
|
+
: discoveryError
|
|
204
|
+
? discoveryError
|
|
205
|
+
: new Error("Timed out waiting for detached SDK broker discovery.");
|
|
206
|
+
try {
|
|
207
|
+
await owner.stop();
|
|
208
|
+
} catch (cleanupError) {
|
|
209
|
+
throw new AggregateError([failure, cleanupError], "SDK broker discovery and spawned broker cleanup both failed.");
|
|
210
|
+
}
|
|
211
|
+
throw failure;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Starts the detached broker entrypoint when discovery has no live owner. */
|
|
215
|
+
export function ensureBroker(settings: EnsureBrokerSettings): Promise<BrokerDiscovery> {
|
|
216
|
+
const existing = ensureInFlight.get(settings.agentDir);
|
|
217
|
+
if (existing) return existing;
|
|
218
|
+
const pending = ensureBrokerOnce(settings);
|
|
219
|
+
ensureInFlight.set(settings.agentDir, pending);
|
|
220
|
+
const clear = (): void => {
|
|
221
|
+
if (ensureInFlight.get(settings.agentDir) === pending) ensureInFlight.delete(settings.agentDir);
|
|
222
|
+
};
|
|
223
|
+
void pending.then(clear, clear);
|
|
224
|
+
return pending;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Test hook: returns a stop handle for the detached broker this process spawned. */
|
|
228
|
+
export function brokerOwnerForTest(agentDir: string): BrokerOwner | undefined {
|
|
229
|
+
return owners.get(agentDir);
|
|
230
|
+
}
|
|
231
|
+
/** Test hook: drives the detached-broker reap on a controllable child surface. */
|
|
232
|
+
export function reapSpawnedBrokerForTest(child: ChildProcess, timing: ReapTiming = DEFAULT_REAP_TIMING): Promise<void> {
|
|
233
|
+
return reapSpawnedBroker(child, timing);
|
|
234
|
+
}
|
|
235
|
+
/** Test hook: resolves the complete broker environment without spawning. */
|
|
236
|
+
export function brokerSpawnEnvironmentForTest(
|
|
237
|
+
command: SdkInternalSpawnCommand,
|
|
238
|
+
override?: NodeJS.ProcessEnv,
|
|
239
|
+
): NodeJS.ProcessEnv {
|
|
240
|
+
return brokerSpawnEnvironment(command, override);
|
|
241
|
+
}
|
|
242
|
+
/** Test hook: installs an exact controllable owner to exercise replacement fencing. */
|
|
243
|
+
export function registerBrokerOwnerForTest(
|
|
244
|
+
agentDir: string,
|
|
245
|
+
child: ChildProcess,
|
|
246
|
+
timing: ReapTiming = DEFAULT_REAP_TIMING,
|
|
247
|
+
): BrokerOwner {
|
|
248
|
+
return registerBrokerOwner(agentDir, child, timing);
|
|
249
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const MODE = 0o600;
|
|
6
|
+
|
|
7
|
+
export function brokerIdentityPath(agentDir: string): string {
|
|
8
|
+
return path.join(agentDir, "sdk", "broker.identity");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function getBrokerIdentityKey(agentDir: string): Promise<string> {
|
|
12
|
+
const file = brokerIdentityPath(agentDir);
|
|
13
|
+
await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
14
|
+
try {
|
|
15
|
+
const key = (await fs.readFile(file, "utf8")).trim();
|
|
16
|
+
if (/^[0-9a-f]{64}$/i.test(key)) return key;
|
|
17
|
+
throw new Error(`Invalid broker identity key at ${file}`);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
20
|
+
const key = randomBytes(32).toString("hex");
|
|
21
|
+
try {
|
|
22
|
+
await fs.writeFile(file, `${key}\n`, { encoding: "utf8", mode: MODE, flag: "wx" });
|
|
23
|
+
await fs.chmod(file, MODE);
|
|
24
|
+
return key;
|
|
25
|
+
} catch (writeError) {
|
|
26
|
+
if ((writeError as NodeJS.ErrnoException).code !== "EEXIST") throw writeError;
|
|
27
|
+
return getBrokerIdentityKey(agentDir);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function deriveIdempotencyIdentity(
|
|
33
|
+
agentDir: string,
|
|
34
|
+
operation: string,
|
|
35
|
+
callerKey: string,
|
|
36
|
+
canonicalTargetHash: string,
|
|
37
|
+
): Promise<string> {
|
|
38
|
+
const key = await getBrokerIdentityKey(agentDir);
|
|
39
|
+
return createHmac("sha256", Buffer.from(key, "hex"))
|
|
40
|
+
.update(`3|${operation}|${callerKey}|${canonicalTargetHash}`)
|
|
41
|
+
.digest("hex");
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gjc-sdk-broker-internal-source-marker-2178
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Product-owned empty Bun configuration for isolated SDK-internal source children.
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import type { SdkStartupFailure, SdkStartupRollbackResult } from "../startup-capability";
|
|
5
|
+
import { assertSupportedStateVersion, SDK_STATE_VERSION } from "./state-version";
|
|
6
|
+
|
|
7
|
+
export type LifecycleState =
|
|
8
|
+
| "accepted"
|
|
9
|
+
| "effect_started"
|
|
10
|
+
| "awaiting_ready"
|
|
11
|
+
| "terminal_ok"
|
|
12
|
+
| "terminal_error"
|
|
13
|
+
| "terminal_uncertain";
|
|
14
|
+
export interface LifecycleWorktreeIntent {
|
|
15
|
+
repoRoot: string;
|
|
16
|
+
worktreePath: string;
|
|
17
|
+
detached: boolean;
|
|
18
|
+
baseRef: string;
|
|
19
|
+
branchName?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface LifecycleEffectIntent {
|
|
23
|
+
sessionId: string;
|
|
24
|
+
stateRoot: string;
|
|
25
|
+
childOwnershipEstablished?: boolean;
|
|
26
|
+
worktree?: LifecycleWorktreeIntent;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Durable lifecycle effects retained for exact replay; never implies rollback authority. */
|
|
30
|
+
export interface LifecycleCleanupProof {
|
|
31
|
+
processExited: true;
|
|
32
|
+
endpointRemoved: true;
|
|
33
|
+
hostUnregistered:
|
|
34
|
+
| { state: "unregistered"; indexSeq: number; lifecycleRequestId?: string }
|
|
35
|
+
| { state: "not_registered" };
|
|
36
|
+
rollback: {
|
|
37
|
+
endpointGeneration: number | null;
|
|
38
|
+
fenced: true;
|
|
39
|
+
runtimeRemoved: true;
|
|
40
|
+
hostStopped: true;
|
|
41
|
+
brokerRegistrationReleased: true;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface LifecycleStartupFailureReceipt extends SdkStartupFailure {
|
|
46
|
+
artifactDigest: string;
|
|
47
|
+
rollback: SdkStartupRollbackResult;
|
|
48
|
+
cleanupProof?: LifecycleCleanupProof;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface LifecycleDurableEffectsReceipt {
|
|
52
|
+
worktree?: {
|
|
53
|
+
cwdDigest: string;
|
|
54
|
+
created: boolean;
|
|
55
|
+
reused: boolean;
|
|
56
|
+
branchDigest?: string;
|
|
57
|
+
};
|
|
58
|
+
transcript?: {
|
|
59
|
+
identityDigest: string;
|
|
60
|
+
contentDigest: string;
|
|
61
|
+
};
|
|
62
|
+
startup?: LifecycleStartupFailureReceipt;
|
|
63
|
+
digest?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface LifecycleLedgerEntry {
|
|
67
|
+
version: typeof SDK_STATE_VERSION;
|
|
68
|
+
identity: string;
|
|
69
|
+
requestHash: string;
|
|
70
|
+
state: LifecycleState;
|
|
71
|
+
intendedSessionId?: string;
|
|
72
|
+
resultSessionId?: string;
|
|
73
|
+
effectMarker?: string;
|
|
74
|
+
effectIntent?: LifecycleEffectIntent;
|
|
75
|
+
durableEffects?: LifecycleDurableEffectsReceipt;
|
|
76
|
+
startupFailure?: LifecycleStartupFailureReceipt;
|
|
77
|
+
|
|
78
|
+
endpointGeneration?: number;
|
|
79
|
+
responseDigest?: string;
|
|
80
|
+
response?: unknown;
|
|
81
|
+
ts: number;
|
|
82
|
+
}
|
|
83
|
+
export type BeginResult =
|
|
84
|
+
| { kind: "new"; entry: LifecycleLedgerEntry }
|
|
85
|
+
| { kind: "replay"; entry: LifecycleLedgerEntry }
|
|
86
|
+
| { kind: "idempotency_conflict" }
|
|
87
|
+
| { kind: "terminal_uncertain"; entry: LifecycleLedgerEntry }
|
|
88
|
+
| { kind: "in_progress"; entry: LifecycleLedgerEntry };
|
|
89
|
+
const terminal = (s: LifecycleState) => s === "terminal_ok" || s === "terminal_error";
|
|
90
|
+
function canonicalJson(value: unknown): string {
|
|
91
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
92
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
93
|
+
const record = value as Record<string, unknown>;
|
|
94
|
+
return `{${Object.keys(record)
|
|
95
|
+
.sort()
|
|
96
|
+
.map(key => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)
|
|
97
|
+
.join(",")}}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function hasValidTerminalDigests(entry: LifecycleLedgerEntry): boolean {
|
|
101
|
+
if (!terminal(entry.state) && entry.state !== "terminal_uncertain") return true;
|
|
102
|
+
if (
|
|
103
|
+
(entry.state !== "terminal_uncertain" || entry.response !== undefined) &&
|
|
104
|
+
(entry.response === undefined ||
|
|
105
|
+
typeof entry.responseDigest !== "string" ||
|
|
106
|
+
entry.responseDigest !== createHash("sha256").update(canonicalJson(entry.response)).digest("hex"))
|
|
107
|
+
)
|
|
108
|
+
return false;
|
|
109
|
+
if (!entry.durableEffects) return true;
|
|
110
|
+
const { digest, ...body } = entry.durableEffects;
|
|
111
|
+
return typeof digest === "string" && digest === createHash("sha256").update(canonicalJson(body)).digest("hex");
|
|
112
|
+
}
|
|
113
|
+
export class LifecycleLedger {
|
|
114
|
+
#file: string;
|
|
115
|
+
#corruptFile: string;
|
|
116
|
+
#entries: LifecycleLedgerEntry[] = [];
|
|
117
|
+
#byIdentity = new Map<string, LifecycleLedgerEntry>();
|
|
118
|
+
#warnings: string[] = [];
|
|
119
|
+
constructor(agentDir: string) {
|
|
120
|
+
this.#file = path.join(agentDir, "sdk", "lifecycle-ledger.jsonl");
|
|
121
|
+
this.#corruptFile = `${this.#file}.corrupt`;
|
|
122
|
+
}
|
|
123
|
+
async open(): Promise<this> {
|
|
124
|
+
await this.assertSupportedStateVersions();
|
|
125
|
+
await fs.mkdir(path.dirname(this.#file), { recursive: true, mode: 0o700 });
|
|
126
|
+
this.#entries = [];
|
|
127
|
+
const quarantinedTerminal = new Set<string>();
|
|
128
|
+
this.#byIdentity.clear();
|
|
129
|
+
this.#warnings = [];
|
|
130
|
+
const uncertainAfterCorruption = new Set<string>();
|
|
131
|
+
let tornTail = false;
|
|
132
|
+
try {
|
|
133
|
+
const source = await fs.readFile(this.#file, "utf8");
|
|
134
|
+
tornTail = source.length > 0 && !source.endsWith("\n");
|
|
135
|
+
const lines = source.split("\n");
|
|
136
|
+
for (const line of lines) {
|
|
137
|
+
if (!line) continue;
|
|
138
|
+
try {
|
|
139
|
+
const e = JSON.parse(line) as LifecycleLedgerEntry;
|
|
140
|
+
assertSupportedStateVersion(this.#file, e);
|
|
141
|
+
if (!e.identity || !e.requestHash || !e.state) throw new Error("invalid ledger entry");
|
|
142
|
+
if (!hasValidTerminalDigests(e)) {
|
|
143
|
+
await this.#quarantine(line);
|
|
144
|
+
const {
|
|
145
|
+
response: _response,
|
|
146
|
+
responseDigest: _responseDigest,
|
|
147
|
+
durableEffects: _durableEffects,
|
|
148
|
+
...uncertain
|
|
149
|
+
} = e;
|
|
150
|
+
const quarantined = { ...uncertain, state: "terminal_uncertain" as const, ts: Date.now() };
|
|
151
|
+
this.#entries.push(quarantined);
|
|
152
|
+
this.#byIdentity.set(quarantined.identity, quarantined);
|
|
153
|
+
quarantinedTerminal.add(quarantined.identity);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
this.#entries.push(e);
|
|
157
|
+
this.#byIdentity.set(e.identity, e);
|
|
158
|
+
uncertainAfterCorruption.delete(e.identity);
|
|
159
|
+
} catch {
|
|
160
|
+
for (const [identity, latest] of this.#byIdentity) {
|
|
161
|
+
if (!terminal(latest.state) && latest.state !== "terminal_uncertain")
|
|
162
|
+
uncertainAfterCorruption.add(identity);
|
|
163
|
+
}
|
|
164
|
+
await this.#quarantine(line);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch (e) {
|
|
168
|
+
if ((e as NodeJS.ErrnoException).code !== "ENOENT") throw e;
|
|
169
|
+
}
|
|
170
|
+
if (tornTail) await this.#sealTornTail();
|
|
171
|
+
for (const identity of quarantinedTerminal) {
|
|
172
|
+
const entry = this.#byIdentity.get(identity);
|
|
173
|
+
if (entry) await this.#append(entry);
|
|
174
|
+
}
|
|
175
|
+
for (const identity of uncertainAfterCorruption) {
|
|
176
|
+
const entry = this.#byIdentity.get(identity);
|
|
177
|
+
if (entry && !terminal(entry.state) && entry.state !== "terminal_uncertain") {
|
|
178
|
+
await this.#append({ ...entry, state: "terminal_uncertain", ts: Date.now() });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Effects may have completed after the last durable marker; do not retry them after a restart.
|
|
182
|
+
for (const entry of [...this.#byIdentity.values()]) {
|
|
183
|
+
if (entry.state === "effect_started" || entry.state === "awaiting_ready")
|
|
184
|
+
await this.#append({ ...entry, state: "terminal_uncertain", ts: Date.now() });
|
|
185
|
+
}
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
async #sealTornTail(): Promise<void> {
|
|
189
|
+
const h = await fs.open(this.#file, "a", 0o600);
|
|
190
|
+
try {
|
|
191
|
+
await h.writeFile("\n");
|
|
192
|
+
await h.sync();
|
|
193
|
+
} finally {
|
|
194
|
+
await h.close();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async #quarantine(line: string): Promise<void> {
|
|
198
|
+
const h = await fs.open(this.#corruptFile, "a", 0o600);
|
|
199
|
+
try {
|
|
200
|
+
await h.writeFile(`${line}\n`);
|
|
201
|
+
await h.sync();
|
|
202
|
+
} finally {
|
|
203
|
+
await h.close();
|
|
204
|
+
}
|
|
205
|
+
this.#warnings.push("Malformed lifecycle ledger entry quarantined");
|
|
206
|
+
}
|
|
207
|
+
get warnings(): readonly string[] {
|
|
208
|
+
return this.#warnings;
|
|
209
|
+
}
|
|
210
|
+
async #append(entry: LifecycleLedgerEntry): Promise<LifecycleLedgerEntry> {
|
|
211
|
+
const h = await fs.open(this.#file, "a", 0o600);
|
|
212
|
+
try {
|
|
213
|
+
await h.writeFile(`${JSON.stringify(entry)}\n`);
|
|
214
|
+
await h.sync();
|
|
215
|
+
} finally {
|
|
216
|
+
await h.close();
|
|
217
|
+
}
|
|
218
|
+
this.#entries.push(entry);
|
|
219
|
+
this.#byIdentity.set(entry.identity, entry);
|
|
220
|
+
return entry;
|
|
221
|
+
}
|
|
222
|
+
async begin(identity: string, requestHash: string): Promise<BeginResult> {
|
|
223
|
+
const prior = this.#byIdentity.get(identity);
|
|
224
|
+
if (!prior)
|
|
225
|
+
return {
|
|
226
|
+
kind: "new",
|
|
227
|
+
entry: await this.#append({
|
|
228
|
+
version: SDK_STATE_VERSION,
|
|
229
|
+
identity,
|
|
230
|
+
requestHash,
|
|
231
|
+
state: "accepted",
|
|
232
|
+
ts: Date.now(),
|
|
233
|
+
}),
|
|
234
|
+
};
|
|
235
|
+
if (prior.requestHash !== requestHash) return { kind: "idempotency_conflict" };
|
|
236
|
+
if (terminal(prior.state)) return { kind: "replay", entry: prior };
|
|
237
|
+
if (prior.state === "terminal_uncertain") return { kind: "terminal_uncertain", entry: prior };
|
|
238
|
+
// An accepted row has no durable side effect. Target serialization makes retrying it safe.
|
|
239
|
+
if (prior.state === "accepted") return { kind: "new", entry: prior };
|
|
240
|
+
return { kind: "in_progress", entry: prior };
|
|
241
|
+
}
|
|
242
|
+
async transition(
|
|
243
|
+
identity: string,
|
|
244
|
+
state: LifecycleState,
|
|
245
|
+
fields: Omit<Partial<LifecycleLedgerEntry>, "identity" | "requestHash" | "state" | "ts"> = {},
|
|
246
|
+
): Promise<LifecycleLedgerEntry> {
|
|
247
|
+
const previous = this.#byIdentity.get(identity);
|
|
248
|
+
if (!previous) throw new Error("Unknown lifecycle identity");
|
|
249
|
+
const next = { ...previous, ...fields, state, ts: Date.now() };
|
|
250
|
+
if (
|
|
251
|
+
(terminal(state) || state === "terminal_uncertain") &&
|
|
252
|
+
next.response !== undefined &&
|
|
253
|
+
next.responseDigest === undefined
|
|
254
|
+
)
|
|
255
|
+
next.responseDigest = createHash("sha256").update(canonicalJson(next.response)).digest("hex");
|
|
256
|
+
if (next.durableEffects && next.durableEffects.digest === undefined) {
|
|
257
|
+
const { digest: _digest, ...body } = next.durableEffects;
|
|
258
|
+
next.durableEffects = {
|
|
259
|
+
...body,
|
|
260
|
+
digest: createHash("sha256").update(canonicalJson(body)).digest("hex"),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return this.#append(next);
|
|
264
|
+
}
|
|
265
|
+
async assertSupportedStateVersions(): Promise<void> {
|
|
266
|
+
let source: string;
|
|
267
|
+
try {
|
|
268
|
+
source = await fs.readFile(this.#file, "utf8");
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return;
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
for (const line of source.split("\n")) {
|
|
274
|
+
if (!line) continue;
|
|
275
|
+
try {
|
|
276
|
+
assertSupportedStateVersion(this.#file, JSON.parse(line));
|
|
277
|
+
} catch (error) {
|
|
278
|
+
if (error instanceof Error && "code" in error && error.code === "unsupported_state_version") throw error;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
get(identity: string): LifecycleLedgerEntry | undefined {
|
|
284
|
+
return this.#byIdentity.get(identity);
|
|
285
|
+
}
|
|
286
|
+
}
|