@gajae-code/coding-agent 0.17.1 → 0.17.4
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 +266 -0
- package/dist/types/async/job-manager.d.ts +7 -6
- package/dist/types/cli/daemon-cli.d.ts +3 -1
- package/dist/types/cli/github-release.d.ts +17 -0
- package/dist/types/cli/public-command-entry.d.ts +56 -0
- package/dist/types/cli/public-command-errors.d.ts +126 -0
- package/dist/types/cli/public-command-evidence.d.ts +99 -0
- package/dist/types/cli/public-command-help.d.ts +65 -0
- package/dist/types/cli/public-command-registry.d.ts +69 -0
- package/dist/types/cli/session-picker.d.ts +1 -1
- package/dist/types/cli/setup-cli.d.ts +1 -0
- package/dist/types/cli/skills-cli.d.ts +5 -0
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/commands/daemon.d.ts +4 -50
- package/dist/types/commands/sdk.d.ts +20 -37
- package/dist/types/commands/setup.d.ts +3 -0
- package/dist/types/commands/skills.d.ts +9 -0
- package/dist/types/config/autorouting-tier-map.d.ts +24 -0
- package/dist/types/config/file-lock.d.ts +10 -1
- package/dist/types/config/model-profile-activation.d.ts +63 -4
- package/dist/types/config/model-registry.d.ts +20 -2
- package/dist/types/config/settings-schema.d.ts +11 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +6 -1
- package/dist/types/coordinator-mcp/server.d.ts +3 -0
- package/dist/types/daemon/operator-contract.d.ts +5 -1
- package/dist/types/eval/js/worker-protocol.d.ts +2 -0
- package/dist/types/extensibility/extensions/types.d.ts +12 -0
- package/dist/types/extensibility/gjc-plugins/registry.d.ts +4 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +18 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -1
- package/dist/types/gjc-runtime/session-state-lock.d.ts +61 -0
- package/dist/types/internal-urls/embedded-protocol.d.ts +9 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/parse.d.ts +9 -1
- package/dist/types/main.d.ts +22 -0
- package/dist/types/modes/acp/acp-agent.d.ts +94 -1
- package/dist/types/modes/acp/startup-provenance.d.ts +16 -0
- package/dist/types/modes/components/custom-provider-wizard.d.ts +19 -1
- package/dist/types/modes/components/session-selector.d.ts +5 -2
- package/dist/types/modes/components/thinking-selector.d.ts +2 -1
- package/dist/types/modes/components/user-message-selector.d.ts +5 -2
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +11 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +14 -1
- package/dist/types/modes/types.d.ts +2 -2
- package/dist/types/runtime/process-lifecycle.d.ts +2 -0
- package/dist/types/runtime-mcp/manager.d.ts +56 -0
- package/dist/types/runtime-mcp/pool.d.ts +17 -0
- package/dist/types/runtime-mcp/redaction.d.ts +20 -0
- package/dist/types/runtime-mcp/startup-policy.d.ts +6 -0
- package/dist/types/runtime-mcp/tool-bridge.d.ts +9 -0
- package/dist/types/runtime-mcp/transports/http.d.ts +11 -0
- package/dist/types/sdk/acp/adapter.d.ts +27 -0
- package/dist/types/sdk/acp/final-text.d.ts +8 -0
- package/dist/types/sdk/broker/broker.d.ts +13 -2
- package/dist/types/sdk/broker/ensure.d.ts +46 -1
- package/dist/types/sdk/broker/identity.d.ts +3 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +23 -3
- package/dist/types/sdk/broker/lifecycle.d.ts +15 -8
- package/dist/types/sdk/broker/runtime.d.ts +1 -1
- package/dist/types/sdk/broker/session-index.d.ts +22 -3
- package/dist/types/sdk/broker/spawn-authority.d.ts +4 -0
- package/dist/types/sdk/broker/stderr-drainer.d.ts +13 -0
- package/dist/types/sdk/bus/index.d.ts +19 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +5 -0
- package/dist/types/sdk/cli/master-cli.d.ts +6 -10
- package/dist/types/sdk/cli/rows.d.ts +19 -1
- package/dist/types/sdk/cli/session-cli.d.ts +22 -5
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/guides/cli.d.ts +3 -4
- package/dist/types/sdk/guides/verify.d.ts +2 -0
- package/dist/types/sdk/host/host.d.ts +9 -0
- package/dist/types/sdk/host/session-runtime.d.ts +31 -0
- package/dist/types/sdk/host/types.d.ts +10 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/prompt-deadline-flush.d.ts +83 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +38 -1
- package/dist/types/sdk/prompt-failure.d.ts +30 -1
- package/dist/types/sdk/prompt-status.d.ts +8 -0
- package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -0
- package/dist/types/sdk/router/replay-retention-gap-cache.d.ts +13 -0
- package/dist/types/sdk/router/session-router.d.ts +2 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +73 -2
- package/dist/types/sdk/transport/stdio.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +168 -21
- package/dist/types/session/fallback-chain-controller.d.ts +6 -6
- package/dist/types/session/fold-coordinator.d.ts +17 -3
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -1
- package/dist/types/session/session-manager.d.ts +64 -1
- package/dist/types/session/session-work-lease.d.ts +14 -0
- package/dist/types/setup/model-onboarding-guidance.d.ts +1 -1
- package/dist/types/setup/provider-onboarding.d.ts +81 -1
- package/dist/types/thinking.d.ts +3 -1
- package/dist/types/tools/bash.d.ts +37 -11
- package/dist/types/tools/job.d.ts +1 -1
- package/dist/types/tools/steer-fold.d.ts +31 -0
- package/dist/types/utils/git.d.ts +28 -2
- package/dist/types/utils/mupdf-embedded.d.ts +2 -0
- package/dist/types/utils/mupdf-wasm-embedded.d.ts +2 -0
- package/dist/types/utils/mupdf.d.ts +4 -0
- package/dist/types/utils/session-title-generation.d.ts +6 -0
- package/dist/types/utils/title-generator.d.ts +11 -2
- package/dist/types/web/scrapers/types.d.ts +3 -0
- package/package.json +18 -10
- package/scripts/build-binary.ts +9 -3
- package/scripts/compile-args.ts +0 -9
- package/scripts/embed-mupdf.ts +52 -0
- package/scripts/generate-sdk-operation-inventory.ts +31 -1
- package/scripts/vendor-markit.ts +196 -0
- package/src/async/job-manager.ts +60 -9
- package/src/cli/daemon-cli.ts +53 -18
- package/src/cli/github-release.ts +87 -5
- package/src/cli/mcp-cli.ts +38 -4
- package/src/cli/public-command-entry.ts +450 -0
- package/src/cli/public-command-errors.ts +581 -0
- package/src/cli/public-command-evidence.ts +717 -0
- package/src/cli/public-command-help.ts +360 -0
- package/src/cli/public-command-registry.ts +535 -0
- package/src/cli/session-picker.ts +2 -1
- package/src/cli/setup-cli.ts +9 -1
- package/src/cli/skills-cli.ts +46 -2
- package/src/cli.ts +61 -1
- package/src/commands/daemon.ts +65 -69
- package/src/commands/sdk.ts +232 -100
- package/src/commands/setup.ts +2 -0
- package/src/commands/skills.ts +11 -0
- package/src/config/autorouting-tier-map.ts +16 -0
- package/src/config/file-lock.ts +199 -68
- package/src/config/model-profile-activation.ts +406 -53
- package/src/config/model-registry.ts +204 -25
- package/src/config/settings-schema.ts +26 -2
- package/src/coordinator-mcp/policy.ts +10 -5
- package/src/coordinator-mcp/question-gate-codec.ts +17 -0
- package/src/coordinator-mcp/question-state.ts +175 -8
- package/src/coordinator-mcp/server.ts +528 -87
- package/src/coordinator-mcp/session-reaper.ts +6 -4
- package/src/crash/record-loader.ts +6 -2
- package/src/daemon/operator-contract.ts +26 -12
- package/src/debug/report-bundle.ts +114 -24
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
- package/src/eval/js/context-manager.ts +8 -8
- package/src/eval/js/worker-core.ts +9 -4
- package/src/eval/js/worker-protocol.ts +2 -0
- package/src/extensibility/extensions/runner.ts +52 -4
- package/src/extensibility/extensions/types.ts +10 -0
- package/src/extensibility/gjc-plugins/registry.ts +196 -37
- package/src/extensibility/runtime-skill-discovery.ts +61 -3
- package/src/extensibility/shared-events.ts +3 -1
- package/src/gjc-runtime/managed-owner-admission.ts +67 -50
- package/src/gjc-runtime/session-state-lock.ts +165 -24
- package/src/gjc-runtime/ultragoal-succession.ts +31 -3
- package/src/internal-urls/docs-index.generated.ts +21 -19
- package/src/internal-urls/embedded-protocol.ts +83 -0
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/parse.ts +21 -1
- package/src/internal-urls/router.ts +4 -2
- package/src/main.ts +123 -55
- package/src/modes/acp/acp-agent.ts +593 -47
- package/src/modes/acp/acp-event-mapper.ts +9 -0
- package/src/modes/acp/startup-provenance.ts +59 -0
- package/src/modes/components/custom-provider-wizard.ts +308 -7
- package/src/modes/components/model-selector.ts +31 -15
- package/src/modes/components/oauth-selector.ts +9 -0
- package/src/modes/components/session-selector.ts +47 -4
- package/src/modes/components/status-line/segments.ts +6 -1
- package/src/modes/components/thinking-selector.ts +7 -3
- package/src/modes/components/user-message-selector.ts +57 -15
- package/src/modes/controllers/command-controller.ts +58 -32
- package/src/modes/controllers/extension-ui-controller.ts +33 -7
- package/src/modes/controllers/input-controller.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +177 -18
- package/src/modes/interactive-mode.ts +21 -7
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +124 -7
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +16 -2
- package/src/modes/types.ts +2 -2
- package/src/prompts/agent-fragments/browser-aside.md +1 -0
- package/src/prompts/system/title-system.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/job.md +1 -1
- package/src/prompts/tools/read.md +2 -0
- package/src/runtime/network-prewarm-service.test.ts +36 -1
- package/src/runtime/network-prewarm-service.ts +28 -3
- package/src/runtime/process-lifecycle.ts +22 -11
- package/src/runtime-mcp/config.ts +16 -1
- package/src/runtime-mcp/manager.ts +689 -55
- package/src/runtime-mcp/pool.test.ts +31 -0
- package/src/runtime-mcp/pool.ts +304 -28
- package/src/runtime-mcp/redaction.ts +42 -0
- package/src/runtime-mcp/startup-policy.ts +7 -0
- package/src/runtime-mcp/tool-bridge.ts +71 -0
- package/src/runtime-mcp/transports/http.ts +108 -25
- package/src/sdk/acp/adapter.ts +90 -9
- package/src/sdk/acp/final-text.ts +19 -2
- package/src/sdk/broker/broker.ts +378 -52
- package/src/sdk/broker/ensure.ts +97 -19
- package/src/sdk/broker/identity.ts +9 -1
- package/src/sdk/broker/lifecycle-ledger.ts +95 -31
- package/src/sdk/broker/lifecycle.ts +690 -141
- package/src/sdk/broker/runtime.ts +1 -1
- package/src/sdk/broker/session-index.ts +97 -4
- package/src/sdk/broker/spawn-authority.ts +35 -7
- package/src/sdk/broker/stderr-drainer.ts +59 -0
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/index.ts +721 -84
- package/src/sdk/bus/telegram-daemon-cli.ts +31 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -3
- package/src/sdk/cli/master-cli.ts +47 -36
- package/src/sdk/cli/rows.ts +30 -7
- package/src/sdk/cli/session-cli.ts +687 -184
- package/src/sdk/client/client.ts +18 -0
- package/src/sdk/guides/catalog.ts +2 -2
- package/src/sdk/guides/cli.ts +20 -62
- package/src/sdk/guides/verify.ts +14 -0
- package/src/sdk/host/host.ts +49 -5
- package/src/sdk/host/session-runtime.test.ts +636 -9
- package/src/sdk/host/session-runtime.ts +499 -118
- package/src/sdk/host/types.ts +12 -1
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +84 -0
- package/src/sdk/host/websocket-transport.ts +40 -6
- package/src/sdk/index.ts +11 -0
- package/src/sdk/prompt-deadline-flush.ts +334 -0
- package/src/sdk/prompt-deadline-manager.ts +136 -4
- package/src/sdk/prompt-failure.ts +43 -1
- package/src/sdk/prompt-status.ts +8 -0
- package/src/sdk/prompt-tool-boundary.ts +121 -0
- package/src/sdk/protocol/operation-inventory.generated.json +106 -0
- package/src/sdk/router/replay-retention-gap-cache.ts +27 -0
- package/src/sdk/router/session-router.ts +31 -4
- package/src/sdk/session.ts +194 -16
- package/src/sdk/startup-capability.ts +1 -1
- package/src/sdk/transport/relay.ts +71 -6
- package/src/sdk/transport/serve-cli.ts +494 -49
- package/src/sdk/transport/socket.ts +12 -11
- package/src/sdk/transport/stdio.ts +7 -3
- package/src/session/agent-session.ts +2437 -400
- package/src/session/fallback-chain-controller.ts +6 -7
- package/src/session/fold-coordinator.ts +24 -5
- package/src/session/internal/managed-session-scope.ts +58 -18
- package/src/session/internal/managed-session-storage.ts +304 -11
- package/src/session/session-manager.ts +382 -44
- package/src/session/session-work-lease.ts +34 -0
- package/src/session-import/codex.ts +12 -3
- package/src/setup/model-onboarding-guidance.ts +1 -1
- package/src/setup/provider-onboarding.ts +397 -24
- package/src/slash-commands/builtin-registry.ts +258 -14
- package/src/task/index.ts +14 -2
- package/src/thinking.ts +21 -2
- package/src/tools/bash.ts +98 -78
- package/src/tools/fetch.ts +119 -20
- package/src/tools/job.ts +151 -25
- package/src/tools/output-meta.ts +15 -2
- package/src/tools/path-utils.ts +27 -3
- package/src/tools/read.ts +3 -3
- package/src/tools/steer-fold.ts +107 -0
- package/src/tools/tool-catalog.generated.ts +29 -2
- package/src/tools/tool-errors.ts +4 -0
- package/src/utils/git.ts +53 -3
- package/src/utils/markit.ts +55 -41
- package/src/utils/mupdf-assets.d.ts +4 -0
- package/src/utils/mupdf-embedded.ts +4 -0
- package/src/utils/mupdf-wasm-embedded.ts +3 -0
- package/src/utils/mupdf-wasm.ts +24 -2
- package/src/utils/mupdf.ts +129 -0
- package/src/utils/session-title-generation.ts +29 -0
- package/src/utils/title-generator.ts +46 -2
- package/src/web/scrapers/types.ts +24 -3
- package/vendor/markit-ai/LICENSE +21 -0
- package/vendor/markit-ai/MANIFEST.json +412 -0
- package/vendor/markit-ai/dist/commands/config.d.ts +4 -0
- package/vendor/markit-ai/dist/commands/config.js +139 -0
- package/vendor/markit-ai/dist/commands/convert.d.ts +6 -0
- package/vendor/markit-ai/dist/commands/convert.js +113 -0
- package/vendor/markit-ai/dist/commands/formats.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/formats.js +92 -0
- package/vendor/markit-ai/dist/commands/init.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/init.js +29 -0
- package/vendor/markit-ai/dist/commands/onboard.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/onboard.js +61 -0
- package/vendor/markit-ai/dist/commands/plugin.d.ts +4 -0
- package/vendor/markit-ai/dist/commands/plugin.js +58 -0
- package/vendor/markit-ai/dist/config.d.ts +26 -0
- package/vendor/markit-ai/dist/config.js +42 -0
- package/vendor/markit-ai/dist/converters/audio.d.ts +7 -0
- package/vendor/markit-ai/dist/converters/audio.js +104 -0
- package/vendor/markit-ai/dist/converters/csv.d.ts +7 -0
- package/vendor/markit-ai/dist/converters/csv.js +83 -0
- package/vendor/markit-ai/dist/converters/docx.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/docx.js +57 -0
- package/vendor/markit-ai/dist/converters/epub.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/epub.js +128 -0
- package/vendor/markit-ai/dist/converters/github.d.ts +18 -0
- package/vendor/markit-ai/dist/converters/github.js +148 -0
- package/vendor/markit-ai/dist/converters/html.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/html.js +30 -0
- package/vendor/markit-ai/dist/converters/image.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/image.js +124 -0
- package/vendor/markit-ai/dist/converters/ipynb.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/ipynb.js +75 -0
- package/vendor/markit-ai/dist/converters/iwork.d.ts +20 -0
- package/vendor/markit-ai/dist/converters/iwork.js +391 -0
- package/vendor/markit-ai/dist/converters/json.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/json.js +21 -0
- package/vendor/markit-ai/dist/converters/pdf/columns.d.ts +35 -0
- package/vendor/markit-ai/dist/converters/pdf/columns.js +93 -0
- package/vendor/markit-ai/dist/converters/pdf/extract.d.ts +19 -0
- package/vendor/markit-ai/dist/converters/pdf/extract.js +514 -0
- package/vendor/markit-ai/dist/converters/pdf/grid.d.ts +25 -0
- package/vendor/markit-ai/dist/converters/pdf/grid.js +795 -0
- package/vendor/markit-ai/dist/converters/pdf/headers.d.ts +24 -0
- package/vendor/markit-ai/dist/converters/pdf/headers.js +108 -0
- package/vendor/markit-ai/dist/converters/pdf/index.d.ts +19 -0
- package/vendor/markit-ai/dist/converters/pdf/index.js +136 -0
- package/vendor/markit-ai/dist/converters/pdf/mupdf-loader.js +9 -0
- package/vendor/markit-ai/dist/converters/pdf/render.d.ts +24 -0
- package/vendor/markit-ai/dist/converters/pdf/render.js +513 -0
- package/vendor/markit-ai/dist/converters/pdf/types.d.ts +75 -0
- package/vendor/markit-ai/dist/converters/pdf/types.js +1 -0
- package/vendor/markit-ai/dist/converters/plain-text.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/plain-text.js +87 -0
- package/vendor/markit-ai/dist/converters/pptx.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/pptx.js +289 -0
- package/vendor/markit-ai/dist/converters/rss.d.ts +11 -0
- package/vendor/markit-ai/dist/converters/rss.js +138 -0
- package/vendor/markit-ai/dist/converters/wikipedia.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/wikipedia.js +33 -0
- package/vendor/markit-ai/dist/converters/xlsx.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/xlsx.js +147 -0
- package/vendor/markit-ai/dist/converters/xml.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/xml.js +18 -0
- package/vendor/markit-ai/dist/converters/yaml.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/yaml.js +17 -0
- package/vendor/markit-ai/dist/converters/zip.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/zip.js +57 -0
- package/vendor/markit-ai/dist/index.d.ts +30 -0
- package/vendor/markit-ai/dist/index.js +26 -0
- package/vendor/markit-ai/dist/main.d.ts +2 -0
- package/vendor/markit-ai/dist/main.js +191 -0
- package/vendor/markit-ai/dist/markit.d.ts +45 -0
- package/vendor/markit-ai/dist/markit.js +267 -0
- package/vendor/markit-ai/dist/plugins/api.d.ts +7 -0
- package/vendor/markit-ai/dist/plugins/api.js +42 -0
- package/vendor/markit-ai/dist/plugins/index.d.ts +4 -0
- package/vendor/markit-ai/dist/plugins/index.js +3 -0
- package/vendor/markit-ai/dist/plugins/installer.d.ts +25 -0
- package/vendor/markit-ai/dist/plugins/installer.js +176 -0
- package/vendor/markit-ai/dist/plugins/loader.d.ts +6 -0
- package/vendor/markit-ai/dist/plugins/loader.js +61 -0
- package/vendor/markit-ai/dist/plugins/types.d.ts +25 -0
- package/vendor/markit-ai/dist/plugins/types.js +1 -0
- package/vendor/markit-ai/dist/providers/anthropic.d.ts +2 -0
- package/vendor/markit-ai/dist/providers/anthropic.js +47 -0
- package/vendor/markit-ai/dist/providers/index.d.ts +21 -0
- package/vendor/markit-ai/dist/providers/index.js +56 -0
- package/vendor/markit-ai/dist/providers/openai.d.ts +2 -0
- package/vendor/markit-ai/dist/providers/openai.js +76 -0
- package/vendor/markit-ai/dist/providers/types.d.ts +26 -0
- package/vendor/markit-ai/dist/providers/types.js +1 -0
- package/vendor/markit-ai/dist/types.d.ts +36 -0
- package/vendor/markit-ai/dist/types.js +1 -0
- package/vendor/markit-ai/dist/utils/exit-codes.d.ts +4 -0
- package/vendor/markit-ai/dist/utils/exit-codes.js +4 -0
- package/vendor/markit-ai/dist/utils/output.d.ts +22 -0
- package/vendor/markit-ai/dist/utils/output.js +31 -0
- package/vendor/markit-ai/dist/utils/turndown.d.ts +8 -0
- package/vendor/markit-ai/dist/utils/turndown.js +70 -0
- package/vendor/markit-ai/package.json +74 -0
- package/vendor/markit-ai.patch +49 -0
|
@@ -4,13 +4,14 @@ import * as crypto from "node:crypto";
|
|
|
4
4
|
import * as fs from "node:fs/promises";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
|
-
import { isContinuingMidRunMaintenanceOutcome, ThinkingLevel } from "@gajae-code/agent-core";
|
|
7
|
+
import { isContinuingMidRunMaintenanceOutcome, isNonDispatchedToolEvent, ThinkingLevel } from "@gajae-code/agent-core";
|
|
8
8
|
import type { Api, ImageContent, Model } from "@gajae-code/ai/core";
|
|
9
9
|
import { logger } from "@gajae-code/utils";
|
|
10
10
|
import { AsyncJobManager } from "../../async";
|
|
11
11
|
import {
|
|
12
12
|
getProxyRoutableProviders,
|
|
13
13
|
inspectProxyProviderId,
|
|
14
|
+
isModelProfileProxyConfigured,
|
|
14
15
|
requiresQualifiedModelProfileRoleResolution,
|
|
15
16
|
resolveProxyMode,
|
|
16
17
|
rewriteSelectorForProxy,
|
|
@@ -25,7 +26,12 @@ import {
|
|
|
25
26
|
import { isAuthenticated, kNoAuth } from "../../config/model-registry";
|
|
26
27
|
import { resolveModelChainWithAuth, splitSelectorThinkingSuffix } from "../../config/model-resolver";
|
|
27
28
|
import { type ModelSelectorValue, normalizeModelSelectorValue } from "../../config/model-selector-value";
|
|
28
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
resolveSdkPromptDeadlineMs,
|
|
31
|
+
resolveSdkPromptMaxRuntimeMs,
|
|
32
|
+
type Settings,
|
|
33
|
+
validateSettingPatch,
|
|
34
|
+
} from "../../config/settings";
|
|
29
35
|
import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "../../extensibility/extensions";
|
|
30
36
|
import type { AgentEndEvent } from "../../extensibility/shared-events";
|
|
31
37
|
import { normalizeGoal } from "../../goals/state";
|
|
@@ -58,6 +64,7 @@ import {
|
|
|
58
64
|
syntheticNamespaceCollision,
|
|
59
65
|
} from "../model-profile-model";
|
|
60
66
|
import { projectQ10Models } from "../models.js";
|
|
67
|
+
import { flushWorktreeOnPromptDeadline } from "../prompt-deadline-flush";
|
|
61
68
|
import { PromptDeadlineManager, type PromptTerminalTransitionEvidence } from "../prompt-deadline-manager";
|
|
62
69
|
import {
|
|
63
70
|
assistantFailureCode,
|
|
@@ -91,7 +98,12 @@ import {
|
|
|
91
98
|
BROKER_RUNTIME_CLOSE_CAPABILITY_FIELD,
|
|
92
99
|
hasBrokerRuntimeAbortCapability,
|
|
93
100
|
} from "./control/runtime-gate";
|
|
94
|
-
import {
|
|
101
|
+
import {
|
|
102
|
+
SESSION_HOST_OBSERVER_CAPABILITY,
|
|
103
|
+
SessionSdkHost,
|
|
104
|
+
type SessionSdkHostOptions,
|
|
105
|
+
TURN_STREAM_CAPABILITY,
|
|
106
|
+
} from "./host";
|
|
95
107
|
import { clearAutoroutingInactive, isAutoroutingInactive, markAutoroutingInactive } from "./internal-autorouting-state";
|
|
96
108
|
import { CursorRegistry, QueryHandlers, RevisionStore, type SessionSurface } from "./query";
|
|
97
109
|
import { createSdkRunCapability } from "./sdk-run-capability";
|
|
@@ -195,6 +207,13 @@ export function verifyMasterCapabilityFrame(input: {
|
|
|
195
207
|
|
|
196
208
|
/** Maximum time a replaced runtime may retain a lifecycle persistence task. */
|
|
197
209
|
const LIFECYCLE_QUIESCENCE_MS = 1_000;
|
|
210
|
+
/**
|
|
211
|
+
* Keep a live session's broker discovery recoverable well inside the session
|
|
212
|
+
* index's two-minute heartbeat freshness window. `ensureBroker` has its own
|
|
213
|
+
* bounded startup/recovery budget; the in-flight guard below prevents those
|
|
214
|
+
* budgets from stacking when one attempt outlives this cadence.
|
|
215
|
+
*/
|
|
216
|
+
export const SESSION_BROKER_RECOVERY_INTERVAL_MS = 30_000;
|
|
198
217
|
|
|
199
218
|
class DiffQueryError extends Error {
|
|
200
219
|
constructor(
|
|
@@ -356,6 +375,17 @@ export interface SdkOnlyTerminalAbortSeams {
|
|
|
356
375
|
terminal?: { scope: "turn" | "owned"; expectedEpoch?: number; steeringSnapshotToken?: number };
|
|
357
376
|
},
|
|
358
377
|
) => Promise<{ status: string; terminalScope?: unknown }>;
|
|
378
|
+
/**
|
|
379
|
+
* Live, SYNCHRONOUS view of the dispatched tool calls the run resource
|
|
380
|
+
* ledger holds for an execution handle (#5637). Strictly read-only: it
|
|
381
|
+
* never claims, reserves, seals or quarantines ledger state. Declared here
|
|
382
|
+
* so both hosts expose ONE seam contract — the bus route consumes it for its
|
|
383
|
+
* deadline tool-boundary wait, and this route threads it so a future boundary
|
|
384
|
+
* wait or deadline abort reads the ledger rather than silently falling back
|
|
385
|
+
* to the event-derived set. Optional so an older host that does not thread it
|
|
386
|
+
* degrades to that fallback instead of failing to construct.
|
|
387
|
+
*/
|
|
388
|
+
pendingToolExecutions?: (handle: string) => readonly string[];
|
|
359
389
|
/** Test override for the maximum durable terminal reservation rows. */
|
|
360
390
|
maxDurableTerminalReservationsForTests?: number;
|
|
361
391
|
}
|
|
@@ -371,6 +401,9 @@ export interface SdkOnlyTerminalAbortSeams {
|
|
|
371
401
|
export class SessionSdkSessionRuntime {
|
|
372
402
|
readonly host: SessionSdkHost;
|
|
373
403
|
readonly transport: SessionSdkTransport;
|
|
404
|
+
readonly #connectionCapabilities = new Map<string, ReadonlySet<string>>();
|
|
405
|
+
readonly #connectionIds = new Set<string>();
|
|
406
|
+
readonly #connectionCapabilitiesProvider: (connectionId: string) => ReadonlySet<string> | undefined;
|
|
374
407
|
readonly #connectionDisposer?: () => void;
|
|
375
408
|
readonly #malformedDisposer?: () => void;
|
|
376
409
|
readonly #capabilitiesDisposer?: () => void;
|
|
@@ -379,10 +412,11 @@ export class SessionSdkSessionRuntime {
|
|
|
379
412
|
|
|
380
413
|
constructor(options: SessionSdkRuntimeOptions) {
|
|
381
414
|
this.transport = options.transport;
|
|
382
|
-
|
|
415
|
+
this.#connectionCapabilitiesProvider =
|
|
416
|
+
options.connectionCapabilities ?? (connectionId => this.#connectionCapabilities.get(connectionId));
|
|
383
417
|
this.host = new SessionSdkHost({
|
|
384
418
|
...options,
|
|
385
|
-
connectionCapabilities:
|
|
419
|
+
connectionCapabilities: this.#connectionCapabilitiesProvider,
|
|
386
420
|
sessionId: options.transport.sessionId,
|
|
387
421
|
stateRoot: options.transport.stateRoot,
|
|
388
422
|
token: options.transport.token,
|
|
@@ -391,14 +425,20 @@ export class SessionSdkSessionRuntime {
|
|
|
391
425
|
if (result instanceof Promise) return result.then(outcome => outcome ?? "written");
|
|
392
426
|
return result ?? "written";
|
|
393
427
|
},
|
|
394
|
-
onFrame:
|
|
428
|
+
onFrame: handler =>
|
|
429
|
+
options.transport.onFrame((connectionId, frame) => {
|
|
430
|
+
this.#connectionIds.add(connectionId);
|
|
431
|
+
handler(connectionId, frame);
|
|
432
|
+
}),
|
|
395
433
|
});
|
|
396
434
|
this.#connectionDisposer = options.transport.onConnectionClose?.(connectionId => {
|
|
397
|
-
|
|
435
|
+
this.#connectionIds.delete(connectionId);
|
|
436
|
+
this.#connectionCapabilities.delete(connectionId);
|
|
398
437
|
this.host.handleDisconnect(connectionId);
|
|
399
438
|
});
|
|
400
439
|
this.#capabilitiesDisposer = options.transport.onNegotiatedCapabilities?.((connectionId, negotiated) => {
|
|
401
|
-
|
|
440
|
+
this.#connectionIds.add(connectionId);
|
|
441
|
+
this.#connectionCapabilities.set(connectionId, new Set(negotiated));
|
|
402
442
|
});
|
|
403
443
|
this.#malformedDisposer = options.transport.onMalformedFrame?.((connectionId, message) => {
|
|
404
444
|
this.host.handleMalformedFrame(connectionId, message);
|
|
@@ -417,6 +457,11 @@ export class SessionSdkSessionRuntime {
|
|
|
417
457
|
return this.host.getProviderDefinitions(capability);
|
|
418
458
|
}
|
|
419
459
|
|
|
460
|
+
/** Persist the host's current observable activity for broker/session-list consumers. */
|
|
461
|
+
async reportActivity(state: "active" | "idle", at = Date.now()): Promise<void> {
|
|
462
|
+
await this.host.reportActivity(state, at);
|
|
463
|
+
}
|
|
464
|
+
|
|
420
465
|
emitEvent(frame: SdkFrame): void {
|
|
421
466
|
const eventInput =
|
|
422
467
|
typeof frame.kind === "string"
|
|
@@ -446,6 +491,28 @@ export class SessionSdkSessionRuntime {
|
|
|
446
491
|
}
|
|
447
492
|
}
|
|
448
493
|
|
|
494
|
+
/** Snapshot connections that negotiated every capability in the requirement set. */
|
|
495
|
+
connectionIdsWithCapabilities(required: readonly string[]): string[] {
|
|
496
|
+
return [...this.#connectionIds].flatMap(connectionId => {
|
|
497
|
+
const capabilities = this.#connectionCapabilitiesProvider(connectionId);
|
|
498
|
+
return capabilities !== undefined && required.every(capability => capabilities.has(capability))
|
|
499
|
+
? [connectionId]
|
|
500
|
+
: [];
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/** Deliver a non-replayable frame to connections with an explicit capability intersection. */
|
|
505
|
+
sendFrameToCapabilities(
|
|
506
|
+
required: readonly string[],
|
|
507
|
+
frame: SdkFrame,
|
|
508
|
+
excluding: ReadonlySet<string> = new Set(),
|
|
509
|
+
): void {
|
|
510
|
+
for (const connectionId of this.connectionIdsWithCapabilities(required)) {
|
|
511
|
+
if (excluding.has(connectionId)) continue;
|
|
512
|
+
this.sendFrameTo(connectionId, frame);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
449
516
|
publish(frame: SdkFrame): void {
|
|
450
517
|
this.emitEvent(frame);
|
|
451
518
|
}
|
|
@@ -499,7 +566,7 @@ export class SessionSdkSessionRuntime {
|
|
|
499
566
|
}
|
|
500
567
|
}
|
|
501
568
|
|
|
502
|
-
async stop(options: { allowLockContention?: boolean } = {}): Promise<void> {
|
|
569
|
+
async stop(options: { allowLockContention?: boolean; unregisterReason?: "detached_idle" } = {}): Promise<void> {
|
|
503
570
|
this.#connectionDisposer?.();
|
|
504
571
|
this.#capabilitiesDisposer?.();
|
|
505
572
|
this.#malformedDisposer?.();
|
|
@@ -557,6 +624,11 @@ export interface CreateSdkSessionRuntimeOptions {
|
|
|
557
624
|
terminalAbortSeams?: SdkOnlyTerminalAbortSeams;
|
|
558
625
|
/** Callback when a frame is admitted to the runtime (test harness). */
|
|
559
626
|
onFrameAdmitted?: () => void;
|
|
627
|
+
/** Timer seams for deterministic broker-recovery lifecycle tests. */
|
|
628
|
+
setIntervalImpl?: typeof setInterval;
|
|
629
|
+
clearIntervalImpl?: typeof clearInterval;
|
|
630
|
+
/** Test-only broker ensure seam; production always uses the owner-fenced helper. */
|
|
631
|
+
ensureBrokerImpl?: typeof ensureBroker;
|
|
560
632
|
/** Test-only observation of a genuinely timed-out lifecycle drain. */
|
|
561
633
|
onLifecycleDrainTimeoutForTests?: () => void;
|
|
562
634
|
/** Test-only observation of bounded failure-diagnostic deduplication state. */
|
|
@@ -1645,18 +1717,18 @@ function createQuerySurface(
|
|
|
1645
1717
|
};
|
|
1646
1718
|
try {
|
|
1647
1719
|
const proxyMode = profile.source === "user" ? "fallback" : resolveProxyMode(profileSettings);
|
|
1648
|
-
if (
|
|
1649
|
-
const configuredProviders = ctx.modelRegistry.getConfiguredProviderIds?.()
|
|
1650
|
-
|
|
1720
|
+
if (proxyProvider !== undefined) {
|
|
1721
|
+
const configuredProviders = ctx.modelRegistry.getConfiguredProviderIds?.();
|
|
1722
|
+
const credentialless =
|
|
1723
|
+
proxyProvider === "opencodex" &&
|
|
1724
|
+
!configuredProviders?.includes(proxyProvider) &&
|
|
1725
|
+
(await ctx.modelRegistry.getApiKeyForProvider(proxyProvider, getProfileCredentialSessionId())) ===
|
|
1726
|
+
kNoAuth;
|
|
1727
|
+
if (!isModelProfileProxyConfigured(proxyProvider, configuredProviders, credentialless))
|
|
1651
1728
|
return { available: false };
|
|
1652
1729
|
}
|
|
1653
1730
|
if (profile.source !== "user" && proxyMode === "always") {
|
|
1654
|
-
if (
|
|
1655
|
-
proxyProvider === undefined ||
|
|
1656
|
-
!proxyAuthenticated ||
|
|
1657
|
-
!(ctx.modelRegistry.getConfiguredProviderIds?.() ?? []).includes(proxyProvider)
|
|
1658
|
-
)
|
|
1659
|
-
return { available: false };
|
|
1731
|
+
if (proxyProvider === undefined || !proxyAuthenticated) return { available: false };
|
|
1660
1732
|
}
|
|
1661
1733
|
const bindings = resolveProfileBindings(profile);
|
|
1662
1734
|
const assignments: Array<{ value: ModelSelectorValue; isDefault: boolean }> = [];
|
|
@@ -1946,6 +2018,7 @@ function createQuerySurface(
|
|
|
1946
2018
|
...gate,
|
|
1947
2019
|
id: `pending:${gate.gate_id}`,
|
|
1948
2020
|
tag: "pending" as const,
|
|
2021
|
+
answer_recorded: false as const,
|
|
1949
2022
|
})) ??
|
|
1950
2023
|
[]
|
|
1951
2024
|
);
|
|
@@ -4166,6 +4239,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4166
4239
|
sdkRunToken: string;
|
|
4167
4240
|
}>;
|
|
4168
4241
|
registerBroker: () => Promise<void>;
|
|
4242
|
+
stopBrokerRecovery: () => void;
|
|
4169
4243
|
quiesceInput: () => void;
|
|
4170
4244
|
fenceGateResolutions: () => void;
|
|
4171
4245
|
waitForGateResolutionQuiescence: () => Promise<void>;
|
|
@@ -4378,6 +4452,78 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4378
4452
|
const activePromptOwnerHolder: { connectionIds?: Set<string>; lifecycleEpoch?: number } = {};
|
|
4379
4453
|
let nextLifecycleEpoch = 0;
|
|
4380
4454
|
const skillTerminalRecoveryKeys = new Set<string>();
|
|
4455
|
+
/**
|
|
4456
|
+
* Exactly one correlated terminal BOUNDARY (agent_end) may reach the wire per
|
|
4457
|
+
* correlation. The provider agent_end handler and the prompt-deadline expiry can both
|
|
4458
|
+
* reach their emission after the other has already published: the handler captures its
|
|
4459
|
+
* transitions synchronously and then awaits durable writes, while the deadline callback
|
|
4460
|
+
* fires only after its own claim/finalize awaits have settled. Neither can be stopped by
|
|
4461
|
+
* removing lifecycle references, and noteTransition is a no-op once the record is
|
|
4462
|
+
* terminal, so durable state would record one outcome while clients received two. This
|
|
4463
|
+
* claim is the shared arbiter, taken synchronously -- no await between the check and the
|
|
4464
|
+
* set -- by whoever is about to publish the boundary.
|
|
4465
|
+
*
|
|
4466
|
+
* The key is never removed at cleanup: dropping it when the correlation retires would
|
|
4467
|
+
* re-open the window, and would stop a late real boundary from being idempotent for an
|
|
4468
|
+
* already-retired correlation. Growth is bounded by FIFO eviction instead (a Set
|
|
4469
|
+
* preserves insertion order). A correlation displaced by 1024 later boundaries is long
|
|
4470
|
+
* retired, and its durable record stays authoritative for anything a consumer missed.
|
|
4471
|
+
*
|
|
4472
|
+
* Eviction must never reach a correlation that can still be published (review P1). A
|
|
4473
|
+
* handler captures its transitions, awaits durable writes, and claims only immediately
|
|
4474
|
+
* before its emit; parked on those awaits, 1024 later boundaries would otherwise evict
|
|
4475
|
+
* its key and let its delayed agent_end publish a SECOND boundary for a correlation the
|
|
4476
|
+
* deadline already terminalized. Every would-be publisher therefore RETAINS its
|
|
4477
|
+
* correlations for as long as it can still reach a claim, and a retained key is skipped
|
|
4478
|
+
* as an eviction victim.
|
|
4479
|
+
*/
|
|
4480
|
+
const MAX_PUBLISHED_TERMINAL_BOUNDARIES = 1024;
|
|
4481
|
+
const publishedTerminalBoundaries = new Set<string>();
|
|
4482
|
+
/** Correlations with at least one publisher still able to reach a claim. */
|
|
4483
|
+
const retainedTerminalBoundaries = new Map<string, number>();
|
|
4484
|
+
/**
|
|
4485
|
+
* Protect every correlation this publisher may still claim, and return its release.
|
|
4486
|
+
* Callers MUST release on every exit path -- published, claim lost, or threw -- so a
|
|
4487
|
+
* failure cannot leak a permanent protection.
|
|
4488
|
+
*/
|
|
4489
|
+
const retainTerminalBoundaries = (
|
|
4490
|
+
invocations: ReadonlyArray<{ correlation: InvocationCorrelation }>,
|
|
4491
|
+
): (() => void) => {
|
|
4492
|
+
const keys = invocations.map(({ correlation }) => lifecycleCorrelationKey(correlation));
|
|
4493
|
+
for (const key of keys) retainedTerminalBoundaries.set(key, (retainedTerminalBoundaries.get(key) ?? 0) + 1);
|
|
4494
|
+
let released = false;
|
|
4495
|
+
return () => {
|
|
4496
|
+
if (released) return;
|
|
4497
|
+
released = true;
|
|
4498
|
+
for (const key of keys) {
|
|
4499
|
+
const retained = retainedTerminalBoundaries.get(key);
|
|
4500
|
+
if (retained === undefined) continue;
|
|
4501
|
+
if (retained > 1) retainedTerminalBoundaries.set(key, retained - 1);
|
|
4502
|
+
else retainedTerminalBoundaries.delete(key);
|
|
4503
|
+
}
|
|
4504
|
+
};
|
|
4505
|
+
};
|
|
4506
|
+
const claimTerminalBoundary = (correlation: InvocationCorrelation): boolean => {
|
|
4507
|
+
const key = lifecycleCorrelationKey(correlation);
|
|
4508
|
+
if (publishedTerminalBoundaries.has(key)) return false;
|
|
4509
|
+
publishedTerminalBoundaries.add(key);
|
|
4510
|
+
while (publishedTerminalBoundaries.size > MAX_PUBLISHED_TERMINAL_BOUNDARIES) {
|
|
4511
|
+
let victim: string | undefined;
|
|
4512
|
+
for (const candidate of publishedTerminalBoundaries) {
|
|
4513
|
+
if (retainedTerminalBoundaries.has(candidate)) continue;
|
|
4514
|
+
victim = candidate;
|
|
4515
|
+
break;
|
|
4516
|
+
}
|
|
4517
|
+
// Every key is still publishable: exceed the bound rather than evict a live
|
|
4518
|
+
// one. Correctness beats the bound, and the excess is bounded by the live
|
|
4519
|
+
// lifecycles the session already bounds.
|
|
4520
|
+
if (victim === undefined) break;
|
|
4521
|
+
publishedTerminalBoundaries.delete(victim);
|
|
4522
|
+
}
|
|
4523
|
+
return true;
|
|
4524
|
+
};
|
|
4525
|
+
const hasClaimedTerminalBoundary = (correlation: InvocationCorrelation): boolean =>
|
|
4526
|
+
publishedTerminalBoundaries.has(lifecycleCorrelationKey(correlation));
|
|
4381
4527
|
const trackLifecycle = (handler: () => Promise<void>, owner: RuntimeState | undefined): Promise<void> => {
|
|
4382
4528
|
if (!owner) return Promise.resolve();
|
|
4383
4529
|
let task: Promise<void>;
|
|
@@ -4401,8 +4547,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4401
4547
|
): void => {
|
|
4402
4548
|
try {
|
|
4403
4549
|
const payload = toAgentWireEventPayload(event);
|
|
4550
|
+
const delivered = new Set<string>();
|
|
4404
4551
|
for (const invocation of invocations) {
|
|
4405
4552
|
if (invocation.connectionId === undefined) continue;
|
|
4553
|
+
delivered.add(invocation.connectionId);
|
|
4406
4554
|
current.runtime.sendFrameTo(invocation.connectionId, {
|
|
4407
4555
|
type: "event",
|
|
4408
4556
|
kind: event.type,
|
|
@@ -4410,6 +4558,14 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4410
4558
|
...invocation.correlation,
|
|
4411
4559
|
});
|
|
4412
4560
|
}
|
|
4561
|
+
// A relay or dashboard can observe a turn submitted by another client. It
|
|
4562
|
+
// explicitly negotiates both the stream and observer capabilities, so give it
|
|
4563
|
+
// the same live content frames without inventing a submitter correlation.
|
|
4564
|
+
current.runtime.sendFrameToCapabilities(
|
|
4565
|
+
[TURN_STREAM_CAPABILITY, SESSION_HOST_OBSERVER_CAPABILITY],
|
|
4566
|
+
{ type: "event", kind: event.type, payload },
|
|
4567
|
+
delivered,
|
|
4568
|
+
);
|
|
4413
4569
|
} catch {
|
|
4414
4570
|
// Streamed content is best-effort; the turn producing it is authoritative.
|
|
4415
4571
|
}
|
|
@@ -4728,6 +4884,11 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4728
4884
|
// recorded as observed=false, never rethrown into the api handler.
|
|
4729
4885
|
let observed = true;
|
|
4730
4886
|
const failedTransitions: Array<{ kind: InvocationKind; correlation: InvocationCorrelation }> = [];
|
|
4887
|
+
// Retained BEFORE the first durable await: from here this publisher can still
|
|
4888
|
+
// reach the claim below, so no later boundary may evict its correlations while
|
|
4889
|
+
// it is parked. A start retains too -- its keys are not published yet, so the
|
|
4890
|
+
// protection is inert, and one unconditional release path cannot leak.
|
|
4891
|
+
const releaseTerminalRetention = retainTerminalBoundaries(transitions);
|
|
4731
4892
|
try {
|
|
4732
4893
|
for (const invocation of transitions) {
|
|
4733
4894
|
try {
|
|
@@ -4809,6 +4970,15 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4809
4970
|
failureCause ?? Object.assign(new Error("agent run failed"), { code: "agent_failed" }),
|
|
4810
4971
|
);
|
|
4811
4972
|
for (const invocation of transitions) {
|
|
4973
|
+
// A correlation whose terminal boundary is already published was
|
|
4974
|
+
// terminalized by the deadline, which emitted its own diagnostic together
|
|
4975
|
+
// with the pair; a second correlated agent_failed would duplicate it. The
|
|
4976
|
+
// check is READ-ONLY: the claim is taken by would-be agent_end publishers
|
|
4977
|
+
// alone, because the real path emits its agent_failed and its agent_end
|
|
4978
|
+
// from two SEPARATE emitLifecycle invocations, so claiming here would make
|
|
4979
|
+
// the publisher block its own boundary. In the ordinary flow this changes
|
|
4980
|
+
// nothing -- agent_failed precedes agent_end and the claim is untaken.
|
|
4981
|
+
if (hasClaimedTerminalBoundary(invocation.correlation)) continue;
|
|
4812
4982
|
try {
|
|
4813
4983
|
current.runtime.emitEvent({
|
|
4814
4984
|
type,
|
|
@@ -4856,6 +5026,15 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4856
5026
|
// above with the one uncorrelated frame, so it cannot settle a live prompt.
|
|
4857
5027
|
const outcome = terminalOutcome ?? terminalStoppedOutcome(stopReason, maintenanceOutcome);
|
|
4858
5028
|
for (const invocation of transitions) {
|
|
5029
|
+
// Claim synchronously, immediately before the emit: the durable awaits
|
|
5030
|
+
// above give the deadline expiry room to publish this correlation's
|
|
5031
|
+
// boundary first. On a lost claim skip ONLY this frame -- every durable
|
|
5032
|
+
// write, diagnostic cleanup, batch retirement and waiter resolution below
|
|
5033
|
+
// still runs. `observed` deliberately stays true: it means "the correlated
|
|
5034
|
+
// publication reached the wire", and it did, the deadline put it there.
|
|
5035
|
+
// Flipping it would make a terminal abort's durable row refuse to claim
|
|
5036
|
+
// terminalPublished for a boundary that IS on the wire.
|
|
5037
|
+
if (!claimTerminalBoundary(invocation.correlation)) continue;
|
|
4859
5038
|
try {
|
|
4860
5039
|
current.runtime.emitEvent({ type, sessionId, ...invocation.correlation, outcome });
|
|
4861
5040
|
} catch {
|
|
@@ -4872,6 +5051,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4872
5051
|
}
|
|
4873
5052
|
} catch {
|
|
4874
5053
|
observed = false;
|
|
5054
|
+
} finally {
|
|
5055
|
+
// Every exit path -- published, claim lost, or threw -- releases: this
|
|
5056
|
+
// publisher can no longer reach a claim.
|
|
5057
|
+
releaseTerminalRetention();
|
|
4875
5058
|
}
|
|
4876
5059
|
if (type === "agent_end" && isContinuingMidRunMaintenanceOutcome(maintenanceOutcome)) return;
|
|
4877
5060
|
if (type === "agent_end") {
|
|
@@ -4931,7 +5114,16 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4931
5114
|
};
|
|
4932
5115
|
api.on("agent_start", (event, ctx) => {
|
|
4933
5116
|
const owner = lifecycleStateForEvent(ctx, "agent_start", event.sdkRunToken);
|
|
4934
|
-
|
|
5117
|
+
// The activity checkpoint is already fire-and-forget and does not depend on
|
|
5118
|
+
// the handler awaiting trackLifecycle, so it composes with #5683 unchanged.
|
|
5119
|
+
void (owner?.runtime ?? lifecycleStateForContext(ctx, "agent_start")?.runtime)
|
|
5120
|
+
?.reportActivity("active")
|
|
5121
|
+
.catch(error => logger.warn(`sdk: active activity checkpoint failed: ${String(error)}`));
|
|
5122
|
+
// Interactive/skill turns must not wait on durable start persist. Keep
|
|
5123
|
+
// trackLifecycle so drain, persist-then-publish, content-hold release, and
|
|
5124
|
+
// shutdown still run; do not return that promise to the extension runner
|
|
5125
|
+
// (EXTENSION_HANDLER_TIMEOUT_MS would otherwise stall the prompt).
|
|
5126
|
+
void trackLifecycle(
|
|
4935
5127
|
async () =>
|
|
4936
5128
|
emitLifecycle(
|
|
4937
5129
|
"agent_start",
|
|
@@ -4947,12 +5139,19 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4947
5139
|
event.sdkRunTokens,
|
|
4948
5140
|
),
|
|
4949
5141
|
owner,
|
|
4950
|
-
)
|
|
5142
|
+
).catch(error => {
|
|
5143
|
+
logger.error("SDK agent_start lifecycle task failed", {
|
|
5144
|
+
error: sanitizePromptFailure(error),
|
|
5145
|
+
});
|
|
5146
|
+
});
|
|
4951
5147
|
});
|
|
4952
5148
|
api.on("agent_end", (event, ctx) => {
|
|
4953
5149
|
const tokenBinding =
|
|
4954
5150
|
typeof event.sdkRunToken === "string" ? lifecycleRunOwners.get(event.sdkRunToken) : undefined;
|
|
4955
5151
|
const owner = tokenBinding?.state ?? lifecycleStateForEvent(ctx, "agent_end", event.sdkRunToken);
|
|
5152
|
+
void (owner?.runtime ?? lifecycleStateForContext(ctx, "agent_end")?.runtime)
|
|
5153
|
+
?.reportActivity("idle")
|
|
5154
|
+
.catch(error => logger.warn(`sdk: idle activity checkpoint failed: ${String(error)}`));
|
|
4956
5155
|
// Capture the oldest unmatched batch synchronously. A successor may start
|
|
4957
5156
|
// while the failed diagnostic persists; that must not retarget the
|
|
4958
5157
|
// predecessor's reason or terminal boundary to the successor invocation.
|
|
@@ -5055,7 +5254,16 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5055
5254
|
// the active run — the root invocation plus any in-run consumed follow-ups
|
|
5056
5255
|
// sharing it — not only the head, or an attached correlation would
|
|
5057
5256
|
// false-fire prompt_deadline_exceeded during a long shared run.
|
|
5058
|
-
const renewAttributableProgress = (
|
|
5257
|
+
const renewAttributableProgress = (
|
|
5258
|
+
eventType: "tool_execution_start" | "tool_execution_update" | "tool_execution_end",
|
|
5259
|
+
event: AgentSessionEvent,
|
|
5260
|
+
ctx: ExtensionContext,
|
|
5261
|
+
): void => {
|
|
5262
|
+
// Pairing-only tool boundaries synthesized while an abort unwinds never
|
|
5263
|
+
// entered a tool. Treating them as progress lets a deadline renew its own
|
|
5264
|
+
// lease after claiming the terminal outcome, leaving that claim pending
|
|
5265
|
+
// instead of durably finalizing it.
|
|
5266
|
+
if (isNonDispatchedToolEvent(event)) return;
|
|
5059
5267
|
// Tool events do not carry an SDK run token. Prefer the lifecycle-active
|
|
5060
5268
|
// runtime for the current session so a retained predecessor cannot make a
|
|
5061
5269
|
// live replacement look ambiguous and suppress its lease renewal.
|
|
@@ -5109,10 +5317,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5109
5317
|
* the session and see the turn settle but never receive a single word of the
|
|
5110
5318
|
* answer.
|
|
5111
5319
|
*
|
|
5112
|
-
*
|
|
5113
|
-
*
|
|
5114
|
-
*
|
|
5115
|
-
*
|
|
5320
|
+
* Correlated content is scoped to the owning invocations of the batch that is
|
|
5321
|
+
* actually running. Explicit observer connections additionally receive the
|
|
5322
|
+
* uncorrelated copy, including for agent-owned runs with no SDK submitter.
|
|
5323
|
+
* A session with no owner or eligible observer pays one capability-map lookup.
|
|
5116
5324
|
*/
|
|
5117
5325
|
const STREAMED_TURN_EVENT_TYPES: ReadonlySet<string> = new Set([
|
|
5118
5326
|
"message_update",
|
|
@@ -5125,6 +5333,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5125
5333
|
const current = lifecycleStateForContext(ctx, "agent_start");
|
|
5126
5334
|
const activeInvocation = current?.activeInvocation;
|
|
5127
5335
|
if (!current) return;
|
|
5336
|
+
const observerConnections = current.runtime.connectionIdsWithCapabilities([
|
|
5337
|
+
TURN_STREAM_CAPABILITY,
|
|
5338
|
+
SESSION_HOST_OBSERVER_CAPABILITY,
|
|
5339
|
+
]);
|
|
5128
5340
|
const batch = activeInvocation
|
|
5129
5341
|
? current.openLifecycleBatches.find(candidate =>
|
|
5130
5342
|
candidate.invocations.some(
|
|
@@ -5139,7 +5351,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5139
5351
|
: current.lifecycleActive
|
|
5140
5352
|
? (current.attachedInvocations ?? [])
|
|
5141
5353
|
: [];
|
|
5142
|
-
if (invocations.length === 0) return;
|
|
5354
|
+
if (invocations.length === 0 && observerConnections.length === 0) return;
|
|
5143
5355
|
// Content bypasses the lifecycle replay ring and must never interrupt its producer.
|
|
5144
5356
|
if (!event || typeof event.type !== "string" || !STREAMED_TURN_EVENT_TYPES.has(event.type)) return;
|
|
5145
5357
|
// emitLifecycle("agent_start") awaits durable persistence before it
|
|
@@ -5166,14 +5378,14 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5166
5378
|
}
|
|
5167
5379
|
publishContentFrames(current, event as AgentSessionEvent, invocations);
|
|
5168
5380
|
};
|
|
5169
|
-
api.on("tool_execution_start", async (
|
|
5170
|
-
renewAttributableProgress("tool_execution_start", ctx);
|
|
5381
|
+
api.on("tool_execution_start", async (event, ctx) => {
|
|
5382
|
+
renewAttributableProgress("tool_execution_start", event as AgentSessionEvent, ctx);
|
|
5171
5383
|
});
|
|
5172
|
-
api.on("tool_execution_update", async (
|
|
5173
|
-
renewAttributableProgress("tool_execution_update", ctx);
|
|
5384
|
+
api.on("tool_execution_update", async (event, ctx) => {
|
|
5385
|
+
renewAttributableProgress("tool_execution_update", event as AgentSessionEvent, ctx);
|
|
5174
5386
|
});
|
|
5175
|
-
api.on("tool_execution_end", async (
|
|
5176
|
-
renewAttributableProgress("tool_execution_end", ctx);
|
|
5387
|
+
api.on("tool_execution_end", async (event, ctx) => {
|
|
5388
|
+
renewAttributableProgress("tool_execution_end", event as AgentSessionEvent, ctx);
|
|
5177
5389
|
});
|
|
5178
5390
|
const errorCode = (error: unknown): string | undefined =>
|
|
5179
5391
|
typeof error === "object" &&
|
|
@@ -5182,6 +5394,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5182
5394
|
typeof (error as { code?: unknown }).code === "string"
|
|
5183
5395
|
? (error as { code: string }).code
|
|
5184
5396
|
: undefined;
|
|
5397
|
+
const brokerDiagnosticCode = (error: unknown): string => {
|
|
5398
|
+
const code = errorCode(error);
|
|
5399
|
+
return code !== undefined && /^[A-Za-z0-9_.-]{1,64}$/u.test(code) ? code : "unavailable";
|
|
5400
|
+
};
|
|
5185
5401
|
const startRuntime = async (ctx: ExtensionContext): Promise<void> => {
|
|
5186
5402
|
if (active) return;
|
|
5187
5403
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
@@ -5206,19 +5422,87 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5206
5422
|
await steerReconciliation.hydrateFromStore();
|
|
5207
5423
|
const deadlineManager = new PromptDeadlineManager({
|
|
5208
5424
|
reconciliation,
|
|
5209
|
-
getLeaseMs: () =>
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5425
|
+
getLeaseMs: () => resolveSdkPromptDeadlineMs(options.settings?.get("sdk.promptDeadlineMs" as never)),
|
|
5426
|
+
getMaxMs: () => resolveSdkPromptMaxRuntimeMs(options.settings?.get("sdk.promptMaxRuntimeMs" as never)),
|
|
5427
|
+
// Persist the agent's uncommitted work before the retirement below tears
|
|
5428
|
+
// the session down (#5583). Best effort by contract: failures are logged
|
|
5429
|
+
// inside the flush and the deadline outcome is unaffected.
|
|
5430
|
+
onDeadlineExceeded: async (_correlation, signal, isCurrent) => {
|
|
5431
|
+
if (options.settings?.get("sdk.flushWorktreeOnDeadline" as never) === false) return;
|
|
5432
|
+
// `has` is true only for a value the user actually wrote, so this
|
|
5433
|
+
// separates an explicit opt-in from the schema default. The flush only
|
|
5434
|
+
// honours the default inside a linked worktree the session owns.
|
|
5435
|
+
const configured = options.settings?.get("sdk.flushWorktreeOnDeadline" as never);
|
|
5436
|
+
const hasExplicitSetting =
|
|
5437
|
+
typeof options.settings?.has === "function"
|
|
5438
|
+
? options.settings.has("sdk.flushWorktreeOnDeadline" as never)
|
|
5439
|
+
: configured === true;
|
|
5440
|
+
const explicitOptIn = hasExplicitSetting === true && configured === true;
|
|
5441
|
+
await flushWorktreeOnPromptDeadline(ctx.cwd, { explicitOptIn, isCurrent, signal });
|
|
5216
5442
|
},
|
|
5217
|
-
onExpired: correlation => {
|
|
5443
|
+
onExpired: (correlation, deadlineOutcome) => {
|
|
5218
5444
|
const owner = lifecycleOwnerHolder.state;
|
|
5219
|
-
if (
|
|
5220
|
-
|
|
5221
|
-
|
|
5445
|
+
if (deadlineOutcome === undefined) {
|
|
5446
|
+
if (!owner) return;
|
|
5447
|
+
removeLifecycleReferences(owner, correlation);
|
|
5448
|
+
maybeRetireLifecycleOwner(owner);
|
|
5449
|
+
return;
|
|
5450
|
+
}
|
|
5451
|
+
const failure = sanitizePromptFailure(
|
|
5452
|
+
Object.assign(new Error("Prompt deadline exceeded."), { code: deadlineOutcome.code }),
|
|
5453
|
+
);
|
|
5454
|
+
// The deadline publishes a failed/end PAIR and owns it atomically, so the
|
|
5455
|
+
// boundary claim is taken once, here, covering both frames. Losing it means
|
|
5456
|
+
// a real agent_end already reached the wire for this correlation: emit
|
|
5457
|
+
// NEITHER frame, but still run the cleanup below, and still let #expire
|
|
5458
|
+
// finish its durable reconciliation. Losing the boundary must not skip
|
|
5459
|
+
// cleanup.
|
|
5460
|
+
//
|
|
5461
|
+
// Retained for the whole publication: this path owns the correlation from
|
|
5462
|
+
// here, so a concurrent boundary must not evict its key mid-flight.
|
|
5463
|
+
const releaseTerminalRetention = retainTerminalBoundaries([{ correlation }]);
|
|
5464
|
+
try {
|
|
5465
|
+
if (claimTerminalBoundary(correlation)) {
|
|
5466
|
+
// Each required frame is emitted INDEPENDENTLY (review P2): a throwing
|
|
5467
|
+
// diagnostic must never suppress the boundary, which is the frame this
|
|
5468
|
+
// path exists to deliver. Both failures are reported, never rethrown --
|
|
5469
|
+
// the cleanup below still has to run.
|
|
5470
|
+
try {
|
|
5471
|
+
runtime.emitEvent({
|
|
5472
|
+
type: "agent_failed",
|
|
5473
|
+
sessionId,
|
|
5474
|
+
...correlation,
|
|
5475
|
+
error: failure,
|
|
5476
|
+
});
|
|
5477
|
+
} catch (error) {
|
|
5478
|
+
logger.warn("sdk: deadline correlated diagnostic publication failed", {
|
|
5479
|
+
commandId: correlation.commandId,
|
|
5480
|
+
turnId: correlation.turnId,
|
|
5481
|
+
error: sanitizePromptFailure(error),
|
|
5482
|
+
});
|
|
5483
|
+
}
|
|
5484
|
+
try {
|
|
5485
|
+
runtime.emitEvent({
|
|
5486
|
+
type: "agent_end",
|
|
5487
|
+
sessionId,
|
|
5488
|
+
...correlation,
|
|
5489
|
+
outcome: canonicalFailedOutcome(failure, "deadline"),
|
|
5490
|
+
});
|
|
5491
|
+
} catch (error) {
|
|
5492
|
+
logger.error("sdk: deadline correlated boundary publication failed", {
|
|
5493
|
+
commandId: correlation.commandId,
|
|
5494
|
+
turnId: correlation.turnId,
|
|
5495
|
+
error: sanitizePromptFailure(error),
|
|
5496
|
+
});
|
|
5497
|
+
}
|
|
5498
|
+
}
|
|
5499
|
+
} finally {
|
|
5500
|
+
releaseTerminalRetention();
|
|
5501
|
+
}
|
|
5502
|
+
if (owner) {
|
|
5503
|
+
removeLifecycleReferences(owner, correlation);
|
|
5504
|
+
maybeRetireLifecycleOwner(owner);
|
|
5505
|
+
}
|
|
5222
5506
|
},
|
|
5223
5507
|
});
|
|
5224
5508
|
const pending: Array<{
|
|
@@ -5858,83 +6142,168 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5858
6142
|
};
|
|
5859
6143
|
let publishedEndpointUrl: string | undefined;
|
|
5860
6144
|
let brokerRegistered = false;
|
|
6145
|
+
let brokerRegistrationInFlight: Promise<void> | undefined;
|
|
6146
|
+
let brokerRecoveryStopped = false;
|
|
5861
6147
|
const registerBroker = async (): Promise<void> => {
|
|
5862
6148
|
if (brokerRegistered) return;
|
|
6149
|
+
if (brokerRegistrationInFlight !== undefined) {
|
|
6150
|
+
await brokerRegistrationInFlight;
|
|
6151
|
+
return;
|
|
6152
|
+
}
|
|
6153
|
+
const registration = (async (): Promise<void> => {
|
|
6154
|
+
try {
|
|
6155
|
+
if (options.brokerRegistrationRequired && !options.lifecycleRequestId)
|
|
6156
|
+
throw new Error("Lifecycle broker registration requires a request identity.");
|
|
6157
|
+
await (options.ensureBrokerImpl ?? ensureBroker)({ agentDir: options.agentDir });
|
|
6158
|
+
if (brokerRecoveryStopped) return;
|
|
6159
|
+
const index = await new SessionIndex(options.agentDir).open();
|
|
6160
|
+
if (brokerRecoveryStopped) return;
|
|
6161
|
+
const locator = await resolveSessionLocator(ctx.cwd, stateRoot);
|
|
6162
|
+
const effectiveIncarnation = processIncarnation(process.pid);
|
|
6163
|
+
const direct = await reattestMasterSessionIdentity({
|
|
6164
|
+
index,
|
|
6165
|
+
locator,
|
|
6166
|
+
masterCapability: options.masterCapability,
|
|
6167
|
+
attestationEpoch: options.masterAttestationEpoch,
|
|
6168
|
+
ownerSessionId: options.masterOwnerSessionId,
|
|
6169
|
+
sessionId,
|
|
6170
|
+
pid: process.pid,
|
|
6171
|
+
processIncarnation: effectiveIncarnation,
|
|
6172
|
+
});
|
|
6173
|
+
if (brokerRecoveryStopped) return;
|
|
6174
|
+
let brokerRegistrationEvent: SessionIndexEvent | undefined;
|
|
6175
|
+
await runtime.registerWithBroker({
|
|
6176
|
+
register: async input => {
|
|
6177
|
+
if (publishedEndpointUrl === undefined)
|
|
6178
|
+
throw new Error("SDK transport endpoint was not published before broker registration.");
|
|
6179
|
+
const endpointPath = path.join(input.stateRoot, "sdk", `${input.sessionId}.json`);
|
|
6180
|
+
const file = await readEndpointFile(endpointPath);
|
|
6181
|
+
if (!file) throw new Error("SDK endpoint could not be read as a stable regular file.");
|
|
6182
|
+
const endpoint = JSON.parse(file.source) as Record<string, unknown>;
|
|
6183
|
+
if (
|
|
6184
|
+
endpoint.sessionId !== input.sessionId ||
|
|
6185
|
+
endpoint.pid !== process.pid ||
|
|
6186
|
+
endpoint.url !== publishedEndpointUrl ||
|
|
6187
|
+
endpoint.token !== transport.token
|
|
6188
|
+
)
|
|
6189
|
+
throw new Error("SDK endpoint did not match the published transport authority.");
|
|
6190
|
+
const endpointMtimeMs = file.mtimeMs;
|
|
6191
|
+
const endpointFileId = `${file.dev}:${file.ino}`;
|
|
6192
|
+
const masterRole = masterAttestationForEffectiveHost({
|
|
6193
|
+
masterCapability: options.masterCapability,
|
|
6194
|
+
attestationEpoch: options.masterAttestationEpoch,
|
|
6195
|
+
ownerSessionId: options.masterOwnerSessionId,
|
|
6196
|
+
sessionId: input.sessionId,
|
|
6197
|
+
pid: process.pid,
|
|
6198
|
+
processIncarnation: effectiveIncarnation,
|
|
6199
|
+
direct,
|
|
6200
|
+
});
|
|
6201
|
+
const published = await index.append({
|
|
6202
|
+
type: "host_registered",
|
|
6203
|
+
...input,
|
|
6204
|
+
locator,
|
|
6205
|
+
pid: process.pid,
|
|
6206
|
+
endpointMtimeMs,
|
|
6207
|
+
endpointFileId,
|
|
6208
|
+
...(options.lifecycleRequestId ? { lifecycleRequestId: options.lifecycleRequestId } : {}),
|
|
6209
|
+
...(masterRole ? { masterRole } : {}),
|
|
6210
|
+
});
|
|
6211
|
+
brokerRegistrationEvent = published;
|
|
6212
|
+
if (brokerRecoveryStopped) {
|
|
6213
|
+
// Teardown may have run before append returned its ownership proof.
|
|
6214
|
+
await index.unregisterIfCurrent(published);
|
|
6215
|
+
if (brokerRegistrationEvent === published) brokerRegistrationEvent = undefined;
|
|
6216
|
+
}
|
|
6217
|
+
},
|
|
6218
|
+
heartbeat: async input => {
|
|
6219
|
+
// Heartbeat only ever speaks for the publication this host proved it
|
|
6220
|
+
// owns. A stale or foreign generation must not renew liveness.
|
|
6221
|
+
const expected = brokerRegistrationEvent;
|
|
6222
|
+
if (
|
|
6223
|
+
!expected ||
|
|
6224
|
+
expected.sessionId !== input.sessionId ||
|
|
6225
|
+
expected.endpointGeneration !== input.endpointGeneration ||
|
|
6226
|
+
path.resolve(expected.locator.stateRoot) !== path.resolve(input.stateRoot)
|
|
6227
|
+
)
|
|
6228
|
+
return;
|
|
6229
|
+
await index.append({
|
|
6230
|
+
type: "host_heartbeat",
|
|
6231
|
+
sessionId: expected.sessionId,
|
|
6232
|
+
locator: expected.locator,
|
|
6233
|
+
endpointGeneration: expected.endpointGeneration,
|
|
6234
|
+
pid: expected.pid,
|
|
6235
|
+
...(expected.processIncarnation === undefined
|
|
6236
|
+
? {}
|
|
6237
|
+
: { processIncarnation: expected.processIncarnation }),
|
|
6238
|
+
...(expected.hostIncarnation === undefined
|
|
6239
|
+
? {}
|
|
6240
|
+
: { hostIncarnation: expected.hostIncarnation }),
|
|
6241
|
+
...(expected.masterRole === undefined ? {} : { masterRole: expected.masterRole }),
|
|
6242
|
+
activity: input.activity,
|
|
6243
|
+
ts: input.activity.at,
|
|
6244
|
+
});
|
|
6245
|
+
},
|
|
6246
|
+
unregister: async input => {
|
|
6247
|
+
const expected = brokerRegistrationEvent;
|
|
6248
|
+
if (
|
|
6249
|
+
!expected ||
|
|
6250
|
+
expected.sessionId !== input.sessionId ||
|
|
6251
|
+
expected.endpointGeneration !== input.endpointGeneration ||
|
|
6252
|
+
path.resolve(expected.locator.stateRoot) !== path.resolve(input.stateRoot)
|
|
6253
|
+
)
|
|
6254
|
+
return;
|
|
6255
|
+
// Use the successful publication's proof, never a teardown-time file read.
|
|
6256
|
+
await index.unregisterIfCurrent(expected, input.reason);
|
|
6257
|
+
if (brokerRegistrationEvent === expected) brokerRegistrationEvent = undefined;
|
|
6258
|
+
},
|
|
6259
|
+
});
|
|
6260
|
+
if (brokerRecoveryStopped) return;
|
|
6261
|
+
brokerRegistered = true;
|
|
6262
|
+
} catch (error) {
|
|
6263
|
+
if (options.brokerRegistrationRequired) throw error;
|
|
6264
|
+
logger.warn("sdk broker registration unavailable", { code: brokerDiagnosticCode(error) });
|
|
6265
|
+
}
|
|
6266
|
+
})();
|
|
6267
|
+
brokerRegistrationInFlight = registration;
|
|
5863
6268
|
try {
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
await runtime.registerWithBroker({
|
|
5882
|
-
register: async input => {
|
|
5883
|
-
if (publishedEndpointUrl === undefined)
|
|
5884
|
-
throw new Error("SDK transport endpoint was not published before broker registration.");
|
|
5885
|
-
const endpointPath = path.join(input.stateRoot, "sdk", `${input.sessionId}.json`);
|
|
5886
|
-
const file = await readEndpointFile(endpointPath);
|
|
5887
|
-
if (!file) throw new Error("SDK endpoint could not be read as a stable regular file.");
|
|
5888
|
-
const endpoint = JSON.parse(file.source) as Record<string, unknown>;
|
|
5889
|
-
if (
|
|
5890
|
-
endpoint.sessionId !== input.sessionId ||
|
|
5891
|
-
endpoint.pid !== process.pid ||
|
|
5892
|
-
endpoint.url !== publishedEndpointUrl ||
|
|
5893
|
-
endpoint.token !== transport.token
|
|
5894
|
-
)
|
|
5895
|
-
throw new Error("SDK endpoint did not match the published transport authority.");
|
|
5896
|
-
const endpointMtimeMs = file.mtimeMs;
|
|
5897
|
-
const endpointFileId = `${file.dev}:${file.ino}`;
|
|
5898
|
-
const masterRole = masterAttestationForEffectiveHost({
|
|
5899
|
-
masterCapability: options.masterCapability,
|
|
5900
|
-
attestationEpoch: options.masterAttestationEpoch,
|
|
5901
|
-
ownerSessionId: options.masterOwnerSessionId,
|
|
5902
|
-
sessionId: input.sessionId,
|
|
5903
|
-
pid: process.pid,
|
|
5904
|
-
processIncarnation: effectiveIncarnation,
|
|
5905
|
-
direct,
|
|
5906
|
-
});
|
|
5907
|
-
registration = await index.append({
|
|
5908
|
-
type: "host_registered",
|
|
5909
|
-
...input,
|
|
5910
|
-
locator,
|
|
5911
|
-
pid: process.pid,
|
|
5912
|
-
endpointMtimeMs,
|
|
5913
|
-
endpointFileId,
|
|
5914
|
-
...(options.lifecycleRequestId ? { lifecycleRequestId: options.lifecycleRequestId } : {}),
|
|
5915
|
-
...(masterRole ? { masterRole } : {}),
|
|
5916
|
-
});
|
|
5917
|
-
},
|
|
5918
|
-
unregister: async input => {
|
|
5919
|
-
const expected = registration;
|
|
5920
|
-
if (
|
|
5921
|
-
!expected ||
|
|
5922
|
-
expected.sessionId !== input.sessionId ||
|
|
5923
|
-
expected.endpointGeneration !== input.endpointGeneration ||
|
|
5924
|
-
path.resolve(expected.locator.stateRoot) !== path.resolve(input.stateRoot)
|
|
5925
|
-
)
|
|
5926
|
-
return;
|
|
5927
|
-
// Use the successful publication's proof, never a teardown-time file read.
|
|
5928
|
-
await index.unregisterIfCurrent(expected);
|
|
5929
|
-
if (registration === expected) registration = undefined;
|
|
5930
|
-
},
|
|
5931
|
-
});
|
|
5932
|
-
brokerRegistered = true;
|
|
6269
|
+
await registration;
|
|
6270
|
+
} finally {
|
|
6271
|
+
if (brokerRegistrationInFlight === registration) brokerRegistrationInFlight = undefined;
|
|
6272
|
+
}
|
|
6273
|
+
};
|
|
6274
|
+
let brokerRecoveryTimer: NodeJS.Timeout | undefined;
|
|
6275
|
+
let brokerRecoveryInFlight: Promise<void> | undefined;
|
|
6276
|
+
const runBrokerRecovery = async (): Promise<void> => {
|
|
6277
|
+
if (brokerRecoveryStopped || brokerRecoveryInFlight !== undefined) return;
|
|
6278
|
+
const recovery = (async (): Promise<void> => {
|
|
6279
|
+
if (brokerRecoveryStopped) return;
|
|
6280
|
+
if (brokerRegistered) await (options.ensureBrokerImpl ?? ensureBroker)({ agentDir: options.agentDir });
|
|
6281
|
+
else await registerBroker();
|
|
6282
|
+
})();
|
|
6283
|
+
brokerRecoveryInFlight = recovery;
|
|
6284
|
+
try {
|
|
6285
|
+
await recovery;
|
|
5933
6286
|
} catch (error) {
|
|
5934
|
-
|
|
5935
|
-
|
|
6287
|
+
logger.warn("sdk broker recovery unavailable", { code: brokerDiagnosticCode(error) });
|
|
6288
|
+
} finally {
|
|
6289
|
+
if (brokerRecoveryInFlight === recovery) brokerRecoveryInFlight = undefined;
|
|
5936
6290
|
}
|
|
5937
6291
|
};
|
|
6292
|
+
const startBrokerRecovery = (): void => {
|
|
6293
|
+
if (brokerRecoveryStopped || brokerRecoveryTimer !== undefined) return;
|
|
6294
|
+
const timer = (options.setIntervalImpl ?? setInterval)(
|
|
6295
|
+
() => void runBrokerRecovery(),
|
|
6296
|
+
SESSION_BROKER_RECOVERY_INTERVAL_MS,
|
|
6297
|
+
) as NodeJS.Timeout;
|
|
6298
|
+
brokerRecoveryTimer = timer;
|
|
6299
|
+
timer.unref?.();
|
|
6300
|
+
};
|
|
6301
|
+
const stopBrokerRecovery = (): void => {
|
|
6302
|
+
brokerRecoveryStopped = true;
|
|
6303
|
+
if (brokerRecoveryTimer === undefined) return;
|
|
6304
|
+
(options.clearIntervalImpl ?? clearInterval)(brokerRecoveryTimer);
|
|
6305
|
+
brokerRecoveryTimer = undefined;
|
|
6306
|
+
};
|
|
5938
6307
|
const runtimeOwner: RuntimeState = {
|
|
5939
6308
|
sessionId,
|
|
5940
6309
|
sessionIdentity,
|
|
@@ -5947,6 +6316,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5947
6316
|
pending,
|
|
5948
6317
|
openLifecycleBatches,
|
|
5949
6318
|
registerBroker,
|
|
6319
|
+
stopBrokerRecovery,
|
|
5950
6320
|
quiesceInput: () => {
|
|
5951
6321
|
inputGate.quiescing = true;
|
|
5952
6322
|
lifecycleOwnerHolder.quiescing = true;
|
|
@@ -5962,13 +6332,19 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5962
6332
|
failureDiagnosticCodes: new Map(),
|
|
5963
6333
|
lifecycleTasks: new Set(),
|
|
5964
6334
|
};
|
|
6335
|
+
// Capture the SDK turn that owns a headless workflow gate before it is
|
|
6336
|
+
// persisted. Q12 can then explain both sides of a stalled turn: the gate
|
|
6337
|
+
// identity and whether an answer was recorded for that originating turn.
|
|
6338
|
+
ctx.workflowGate?.setRuntimeTurnProvider?.(() => active?.activeInvocation?.correlation.turnId);
|
|
5965
6339
|
lifecycleOwnerHolder.state = runtimeOwner;
|
|
5966
6340
|
active = runtimeOwner;
|
|
5967
6341
|
try {
|
|
5968
6342
|
publishedEndpointUrl = (await runtime.start()).url;
|
|
5969
6343
|
await registerBroker();
|
|
6344
|
+
if (!brokerRecoveryStopped) startBrokerRecovery();
|
|
5970
6345
|
} catch (error) {
|
|
5971
6346
|
active = undefined;
|
|
6347
|
+
stopBrokerRecovery();
|
|
5972
6348
|
disposeGate?.();
|
|
5973
6349
|
try {
|
|
5974
6350
|
await runtime.stop();
|
|
@@ -5989,6 +6365,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5989
6365
|
pending,
|
|
5990
6366
|
openLifecycleBatches,
|
|
5991
6367
|
registerBroker,
|
|
6368
|
+
stopBrokerRecovery,
|
|
5992
6369
|
quiesceInput: () => {
|
|
5993
6370
|
inputGate.quiescing = true;
|
|
5994
6371
|
lifecycleOwnerHolder.quiescing = true;
|
|
@@ -6013,7 +6390,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6013
6390
|
throw error;
|
|
6014
6391
|
}
|
|
6015
6392
|
};
|
|
6016
|
-
const stopActive = async (cancelSkillRecovery = false): Promise<void> => {
|
|
6393
|
+
const stopActive = async (cancelSkillRecovery = false, unregisterReason?: "detached_idle"): Promise<void> => {
|
|
6017
6394
|
const current = active;
|
|
6018
6395
|
if (!current) return;
|
|
6019
6396
|
if (cancelSkillRecovery) {
|
|
@@ -6022,6 +6399,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6022
6399
|
}
|
|
6023
6400
|
activePromptOwnerHolder.connectionIds = undefined;
|
|
6024
6401
|
activePromptOwnerHolder.lifecycleEpoch = undefined;
|
|
6402
|
+
current.stopBrokerRecovery();
|
|
6025
6403
|
current.quiesceInput();
|
|
6026
6404
|
current.fenceGateResolutions();
|
|
6027
6405
|
try {
|
|
@@ -6070,7 +6448,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6070
6448
|
retiredLifecycleOwnerTimers.set(current, timer);
|
|
6071
6449
|
} else current.deadlineManager.clearAll();
|
|
6072
6450
|
current.disposeGate?.();
|
|
6073
|
-
await current.runtime.stop({
|
|
6451
|
+
await current.runtime.stop({
|
|
6452
|
+
allowLockContention: cancelSkillRecovery,
|
|
6453
|
+
...(unregisterReason === undefined ? {} : { unregisterReason }),
|
|
6454
|
+
});
|
|
6074
6455
|
} catch (error) {
|
|
6075
6456
|
// Keep the immutable owner available for a retry when transport teardown
|
|
6076
6457
|
// fails after quiescing. Clearing `active` before stop prevents a second
|
|
@@ -6093,7 +6474,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6093
6474
|
await stopActive();
|
|
6094
6475
|
await startRuntime(ctx);
|
|
6095
6476
|
});
|
|
6096
|
-
api.on("session_shutdown", async
|
|
6097
|
-
await stopActive(true);
|
|
6477
|
+
api.on("session_shutdown", async event => {
|
|
6478
|
+
await stopActive(true, event.reason);
|
|
6098
6479
|
});
|
|
6099
6480
|
}
|