@gajae-code/coding-agent 0.17.2 → 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 +194 -0
- 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 +18 -0
- package/dist/types/config/file-lock.d.ts +9 -1
- package/dist/types/config/model-profile-activation.d.ts +45 -1
- package/dist/types/config/model-registry.d.ts +11 -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/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 +55 -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/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/broker/broker.d.ts +13 -2
- 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 +37 -0
- package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -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 +48 -4
- 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/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.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 +22 -1
- 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 +175 -117
- package/src/commands/setup.ts +2 -0
- package/src/commands/skills.ts +11 -0
- package/src/config/autorouting-tier-map.ts +14 -0
- package/src/config/file-lock.ts +147 -51
- package/src/config/model-profile-activation.ts +334 -43
- package/src/config/model-registry.ts +183 -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 +507 -81
- 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/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/session-state-lock.ts +165 -24
- package/src/gjc-runtime/ultragoal-succession.ts +31 -3
- package/src/internal-urls/docs-index.generated.ts +20 -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 +408 -35
- package/src/modes/acp/startup-provenance.ts +59 -0
- package/src/modes/components/custom-provider-wizard.ts +308 -7
- package/src/modes/components/oauth-selector.ts +9 -0
- package/src/modes/components/session-selector.ts +47 -4
- 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 +161 -15
- 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 +27 -8
- package/src/sdk/broker/broker.ts +378 -52
- 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 +690 -80
- 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 +138 -2
- package/src/sdk/host/session-runtime.ts +311 -97
- 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 +133 -1
- package/src/sdk/prompt-tool-boundary.ts +121 -0
- package/src/sdk/protocol/operation-inventory.generated.json +106 -0
- package/src/sdk/router/session-router.ts +9 -1
- package/src/sdk/session.ts +182 -7
- package/src/sdk/startup-capability.ts +1 -1
- package/src/sdk/transport/relay.ts +71 -6
- package/src/sdk/transport/serve-cli.ts +397 -42
- 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/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/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.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
|
@@ -26,7 +26,12 @@ import {
|
|
|
26
26
|
import { isAuthenticated, kNoAuth } from "../../config/model-registry";
|
|
27
27
|
import { resolveModelChainWithAuth, splitSelectorThinkingSuffix } from "../../config/model-resolver";
|
|
28
28
|
import { type ModelSelectorValue, normalizeModelSelectorValue } from "../../config/model-selector-value";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
resolveSdkPromptDeadlineMs,
|
|
31
|
+
resolveSdkPromptMaxRuntimeMs,
|
|
32
|
+
type Settings,
|
|
33
|
+
validateSettingPatch,
|
|
34
|
+
} from "../../config/settings";
|
|
30
35
|
import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "../../extensibility/extensions";
|
|
31
36
|
import type { AgentEndEvent } from "../../extensibility/shared-events";
|
|
32
37
|
import { normalizeGoal } from "../../goals/state";
|
|
@@ -59,6 +64,7 @@ import {
|
|
|
59
64
|
syntheticNamespaceCollision,
|
|
60
65
|
} from "../model-profile-model";
|
|
61
66
|
import { projectQ10Models } from "../models.js";
|
|
67
|
+
import { flushWorktreeOnPromptDeadline } from "../prompt-deadline-flush";
|
|
62
68
|
import { PromptDeadlineManager, type PromptTerminalTransitionEvidence } from "../prompt-deadline-manager";
|
|
63
69
|
import {
|
|
64
70
|
assistantFailureCode,
|
|
@@ -92,7 +98,12 @@ import {
|
|
|
92
98
|
BROKER_RUNTIME_CLOSE_CAPABILITY_FIELD,
|
|
93
99
|
hasBrokerRuntimeAbortCapability,
|
|
94
100
|
} from "./control/runtime-gate";
|
|
95
|
-
import {
|
|
101
|
+
import {
|
|
102
|
+
SESSION_HOST_OBSERVER_CAPABILITY,
|
|
103
|
+
SessionSdkHost,
|
|
104
|
+
type SessionSdkHostOptions,
|
|
105
|
+
TURN_STREAM_CAPABILITY,
|
|
106
|
+
} from "./host";
|
|
96
107
|
import { clearAutoroutingInactive, isAutoroutingInactive, markAutoroutingInactive } from "./internal-autorouting-state";
|
|
97
108
|
import { CursorRegistry, QueryHandlers, RevisionStore, type SessionSurface } from "./query";
|
|
98
109
|
import { createSdkRunCapability } from "./sdk-run-capability";
|
|
@@ -196,6 +207,13 @@ export function verifyMasterCapabilityFrame(input: {
|
|
|
196
207
|
|
|
197
208
|
/** Maximum time a replaced runtime may retain a lifecycle persistence task. */
|
|
198
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;
|
|
199
217
|
|
|
200
218
|
class DiffQueryError extends Error {
|
|
201
219
|
constructor(
|
|
@@ -357,6 +375,17 @@ export interface SdkOnlyTerminalAbortSeams {
|
|
|
357
375
|
terminal?: { scope: "turn" | "owned"; expectedEpoch?: number; steeringSnapshotToken?: number };
|
|
358
376
|
},
|
|
359
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[];
|
|
360
389
|
/** Test override for the maximum durable terminal reservation rows. */
|
|
361
390
|
maxDurableTerminalReservationsForTests?: number;
|
|
362
391
|
}
|
|
@@ -372,6 +401,9 @@ export interface SdkOnlyTerminalAbortSeams {
|
|
|
372
401
|
export class SessionSdkSessionRuntime {
|
|
373
402
|
readonly host: SessionSdkHost;
|
|
374
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;
|
|
375
407
|
readonly #connectionDisposer?: () => void;
|
|
376
408
|
readonly #malformedDisposer?: () => void;
|
|
377
409
|
readonly #capabilitiesDisposer?: () => void;
|
|
@@ -380,10 +412,11 @@ export class SessionSdkSessionRuntime {
|
|
|
380
412
|
|
|
381
413
|
constructor(options: SessionSdkRuntimeOptions) {
|
|
382
414
|
this.transport = options.transport;
|
|
383
|
-
|
|
415
|
+
this.#connectionCapabilitiesProvider =
|
|
416
|
+
options.connectionCapabilities ?? (connectionId => this.#connectionCapabilities.get(connectionId));
|
|
384
417
|
this.host = new SessionSdkHost({
|
|
385
418
|
...options,
|
|
386
|
-
connectionCapabilities:
|
|
419
|
+
connectionCapabilities: this.#connectionCapabilitiesProvider,
|
|
387
420
|
sessionId: options.transport.sessionId,
|
|
388
421
|
stateRoot: options.transport.stateRoot,
|
|
389
422
|
token: options.transport.token,
|
|
@@ -392,14 +425,20 @@ export class SessionSdkSessionRuntime {
|
|
|
392
425
|
if (result instanceof Promise) return result.then(outcome => outcome ?? "written");
|
|
393
426
|
return result ?? "written";
|
|
394
427
|
},
|
|
395
|
-
onFrame:
|
|
428
|
+
onFrame: handler =>
|
|
429
|
+
options.transport.onFrame((connectionId, frame) => {
|
|
430
|
+
this.#connectionIds.add(connectionId);
|
|
431
|
+
handler(connectionId, frame);
|
|
432
|
+
}),
|
|
396
433
|
});
|
|
397
434
|
this.#connectionDisposer = options.transport.onConnectionClose?.(connectionId => {
|
|
398
|
-
|
|
435
|
+
this.#connectionIds.delete(connectionId);
|
|
436
|
+
this.#connectionCapabilities.delete(connectionId);
|
|
399
437
|
this.host.handleDisconnect(connectionId);
|
|
400
438
|
});
|
|
401
439
|
this.#capabilitiesDisposer = options.transport.onNegotiatedCapabilities?.((connectionId, negotiated) => {
|
|
402
|
-
|
|
440
|
+
this.#connectionIds.add(connectionId);
|
|
441
|
+
this.#connectionCapabilities.set(connectionId, new Set(negotiated));
|
|
403
442
|
});
|
|
404
443
|
this.#malformedDisposer = options.transport.onMalformedFrame?.((connectionId, message) => {
|
|
405
444
|
this.host.handleMalformedFrame(connectionId, message);
|
|
@@ -418,6 +457,11 @@ export class SessionSdkSessionRuntime {
|
|
|
418
457
|
return this.host.getProviderDefinitions(capability);
|
|
419
458
|
}
|
|
420
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
|
+
|
|
421
465
|
emitEvent(frame: SdkFrame): void {
|
|
422
466
|
const eventInput =
|
|
423
467
|
typeof frame.kind === "string"
|
|
@@ -447,6 +491,28 @@ export class SessionSdkSessionRuntime {
|
|
|
447
491
|
}
|
|
448
492
|
}
|
|
449
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
|
+
|
|
450
516
|
publish(frame: SdkFrame): void {
|
|
451
517
|
this.emitEvent(frame);
|
|
452
518
|
}
|
|
@@ -500,7 +566,7 @@ export class SessionSdkSessionRuntime {
|
|
|
500
566
|
}
|
|
501
567
|
}
|
|
502
568
|
|
|
503
|
-
async stop(options: { allowLockContention?: boolean } = {}): Promise<void> {
|
|
569
|
+
async stop(options: { allowLockContention?: boolean; unregisterReason?: "detached_idle" } = {}): Promise<void> {
|
|
504
570
|
this.#connectionDisposer?.();
|
|
505
571
|
this.#capabilitiesDisposer?.();
|
|
506
572
|
this.#malformedDisposer?.();
|
|
@@ -558,6 +624,11 @@ export interface CreateSdkSessionRuntimeOptions {
|
|
|
558
624
|
terminalAbortSeams?: SdkOnlyTerminalAbortSeams;
|
|
559
625
|
/** Callback when a frame is admitted to the runtime (test harness). */
|
|
560
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;
|
|
561
632
|
/** Test-only observation of a genuinely timed-out lifecycle drain. */
|
|
562
633
|
onLifecycleDrainTimeoutForTests?: () => void;
|
|
563
634
|
/** Test-only observation of bounded failure-diagnostic deduplication state. */
|
|
@@ -1947,6 +2018,7 @@ function createQuerySurface(
|
|
|
1947
2018
|
...gate,
|
|
1948
2019
|
id: `pending:${gate.gate_id}`,
|
|
1949
2020
|
tag: "pending" as const,
|
|
2021
|
+
answer_recorded: false as const,
|
|
1950
2022
|
})) ??
|
|
1951
2023
|
[]
|
|
1952
2024
|
);
|
|
@@ -4167,6 +4239,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4167
4239
|
sdkRunToken: string;
|
|
4168
4240
|
}>;
|
|
4169
4241
|
registerBroker: () => Promise<void>;
|
|
4242
|
+
stopBrokerRecovery: () => void;
|
|
4170
4243
|
quiesceInput: () => void;
|
|
4171
4244
|
fenceGateResolutions: () => void;
|
|
4172
4245
|
waitForGateResolutionQuiescence: () => Promise<void>;
|
|
@@ -4474,8 +4547,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4474
4547
|
): void => {
|
|
4475
4548
|
try {
|
|
4476
4549
|
const payload = toAgentWireEventPayload(event);
|
|
4550
|
+
const delivered = new Set<string>();
|
|
4477
4551
|
for (const invocation of invocations) {
|
|
4478
4552
|
if (invocation.connectionId === undefined) continue;
|
|
4553
|
+
delivered.add(invocation.connectionId);
|
|
4479
4554
|
current.runtime.sendFrameTo(invocation.connectionId, {
|
|
4480
4555
|
type: "event",
|
|
4481
4556
|
kind: event.type,
|
|
@@ -4483,6 +4558,14 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
4483
4558
|
...invocation.correlation,
|
|
4484
4559
|
});
|
|
4485
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
|
+
);
|
|
4486
4569
|
} catch {
|
|
4487
4570
|
// Streamed content is best-effort; the turn producing it is authoritative.
|
|
4488
4571
|
}
|
|
@@ -5031,7 +5114,16 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5031
5114
|
};
|
|
5032
5115
|
api.on("agent_start", (event, ctx) => {
|
|
5033
5116
|
const owner = lifecycleStateForEvent(ctx, "agent_start", event.sdkRunToken);
|
|
5034
|
-
|
|
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(
|
|
5035
5127
|
async () =>
|
|
5036
5128
|
emitLifecycle(
|
|
5037
5129
|
"agent_start",
|
|
@@ -5047,12 +5139,19 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5047
5139
|
event.sdkRunTokens,
|
|
5048
5140
|
),
|
|
5049
5141
|
owner,
|
|
5050
|
-
)
|
|
5142
|
+
).catch(error => {
|
|
5143
|
+
logger.error("SDK agent_start lifecycle task failed", {
|
|
5144
|
+
error: sanitizePromptFailure(error),
|
|
5145
|
+
});
|
|
5146
|
+
});
|
|
5051
5147
|
});
|
|
5052
5148
|
api.on("agent_end", (event, ctx) => {
|
|
5053
5149
|
const tokenBinding =
|
|
5054
5150
|
typeof event.sdkRunToken === "string" ? lifecycleRunOwners.get(event.sdkRunToken) : undefined;
|
|
5055
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)}`));
|
|
5056
5155
|
// Capture the oldest unmatched batch synchronously. A successor may start
|
|
5057
5156
|
// while the failed diagnostic persists; that must not retarget the
|
|
5058
5157
|
// predecessor's reason or terminal boundary to the successor invocation.
|
|
@@ -5218,10 +5317,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5218
5317
|
* the session and see the turn settle but never receive a single word of the
|
|
5219
5318
|
* answer.
|
|
5220
5319
|
*
|
|
5221
|
-
*
|
|
5222
|
-
*
|
|
5223
|
-
*
|
|
5224
|
-
*
|
|
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.
|
|
5225
5324
|
*/
|
|
5226
5325
|
const STREAMED_TURN_EVENT_TYPES: ReadonlySet<string> = new Set([
|
|
5227
5326
|
"message_update",
|
|
@@ -5234,6 +5333,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5234
5333
|
const current = lifecycleStateForContext(ctx, "agent_start");
|
|
5235
5334
|
const activeInvocation = current?.activeInvocation;
|
|
5236
5335
|
if (!current) return;
|
|
5336
|
+
const observerConnections = current.runtime.connectionIdsWithCapabilities([
|
|
5337
|
+
TURN_STREAM_CAPABILITY,
|
|
5338
|
+
SESSION_HOST_OBSERVER_CAPABILITY,
|
|
5339
|
+
]);
|
|
5237
5340
|
const batch = activeInvocation
|
|
5238
5341
|
? current.openLifecycleBatches.find(candidate =>
|
|
5239
5342
|
candidate.invocations.some(
|
|
@@ -5248,7 +5351,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5248
5351
|
: current.lifecycleActive
|
|
5249
5352
|
? (current.attachedInvocations ?? [])
|
|
5250
5353
|
: [];
|
|
5251
|
-
if (invocations.length === 0) return;
|
|
5354
|
+
if (invocations.length === 0 && observerConnections.length === 0) return;
|
|
5252
5355
|
// Content bypasses the lifecycle replay ring and must never interrupt its producer.
|
|
5253
5356
|
if (!event || typeof event.type !== "string" || !STREAMED_TURN_EVENT_TYPES.has(event.type)) return;
|
|
5254
5357
|
// emitLifecycle("agent_start") awaits durable persistence before it
|
|
@@ -5291,6 +5394,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5291
5394
|
typeof (error as { code?: unknown }).code === "string"
|
|
5292
5395
|
? (error as { code: string }).code
|
|
5293
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
|
+
};
|
|
5294
5401
|
const startRuntime = async (ctx: ExtensionContext): Promise<void> => {
|
|
5295
5402
|
if (active) return;
|
|
5296
5403
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
@@ -5315,13 +5422,23 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5315
5422
|
await steerReconciliation.hydrateFromStore();
|
|
5316
5423
|
const deadlineManager = new PromptDeadlineManager({
|
|
5317
5424
|
reconciliation,
|
|
5318
|
-
getLeaseMs: () =>
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
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 });
|
|
5325
5442
|
},
|
|
5326
5443
|
onExpired: (correlation, deadlineOutcome) => {
|
|
5327
5444
|
const owner = lifecycleOwnerHolder.state;
|
|
@@ -6025,83 +6142,168 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6025
6142
|
};
|
|
6026
6143
|
let publishedEndpointUrl: string | undefined;
|
|
6027
6144
|
let brokerRegistered = false;
|
|
6145
|
+
let brokerRegistrationInFlight: Promise<void> | undefined;
|
|
6146
|
+
let brokerRecoveryStopped = false;
|
|
6028
6147
|
const registerBroker = async (): Promise<void> => {
|
|
6029
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;
|
|
6030
6268
|
try {
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
await runtime.registerWithBroker({
|
|
6049
|
-
register: async input => {
|
|
6050
|
-
if (publishedEndpointUrl === undefined)
|
|
6051
|
-
throw new Error("SDK transport endpoint was not published before broker registration.");
|
|
6052
|
-
const endpointPath = path.join(input.stateRoot, "sdk", `${input.sessionId}.json`);
|
|
6053
|
-
const file = await readEndpointFile(endpointPath);
|
|
6054
|
-
if (!file) throw new Error("SDK endpoint could not be read as a stable regular file.");
|
|
6055
|
-
const endpoint = JSON.parse(file.source) as Record<string, unknown>;
|
|
6056
|
-
if (
|
|
6057
|
-
endpoint.sessionId !== input.sessionId ||
|
|
6058
|
-
endpoint.pid !== process.pid ||
|
|
6059
|
-
endpoint.url !== publishedEndpointUrl ||
|
|
6060
|
-
endpoint.token !== transport.token
|
|
6061
|
-
)
|
|
6062
|
-
throw new Error("SDK endpoint did not match the published transport authority.");
|
|
6063
|
-
const endpointMtimeMs = file.mtimeMs;
|
|
6064
|
-
const endpointFileId = `${file.dev}:${file.ino}`;
|
|
6065
|
-
const masterRole = masterAttestationForEffectiveHost({
|
|
6066
|
-
masterCapability: options.masterCapability,
|
|
6067
|
-
attestationEpoch: options.masterAttestationEpoch,
|
|
6068
|
-
ownerSessionId: options.masterOwnerSessionId,
|
|
6069
|
-
sessionId: input.sessionId,
|
|
6070
|
-
pid: process.pid,
|
|
6071
|
-
processIncarnation: effectiveIncarnation,
|
|
6072
|
-
direct,
|
|
6073
|
-
});
|
|
6074
|
-
registration = await index.append({
|
|
6075
|
-
type: "host_registered",
|
|
6076
|
-
...input,
|
|
6077
|
-
locator,
|
|
6078
|
-
pid: process.pid,
|
|
6079
|
-
endpointMtimeMs,
|
|
6080
|
-
endpointFileId,
|
|
6081
|
-
...(options.lifecycleRequestId ? { lifecycleRequestId: options.lifecycleRequestId } : {}),
|
|
6082
|
-
...(masterRole ? { masterRole } : {}),
|
|
6083
|
-
});
|
|
6084
|
-
},
|
|
6085
|
-
unregister: async input => {
|
|
6086
|
-
const expected = registration;
|
|
6087
|
-
if (
|
|
6088
|
-
!expected ||
|
|
6089
|
-
expected.sessionId !== input.sessionId ||
|
|
6090
|
-
expected.endpointGeneration !== input.endpointGeneration ||
|
|
6091
|
-
path.resolve(expected.locator.stateRoot) !== path.resolve(input.stateRoot)
|
|
6092
|
-
)
|
|
6093
|
-
return;
|
|
6094
|
-
// Use the successful publication's proof, never a teardown-time file read.
|
|
6095
|
-
await index.unregisterIfCurrent(expected);
|
|
6096
|
-
if (registration === expected) registration = undefined;
|
|
6097
|
-
},
|
|
6098
|
-
});
|
|
6099
|
-
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;
|
|
6100
6286
|
} catch (error) {
|
|
6101
|
-
|
|
6102
|
-
|
|
6287
|
+
logger.warn("sdk broker recovery unavailable", { code: brokerDiagnosticCode(error) });
|
|
6288
|
+
} finally {
|
|
6289
|
+
if (brokerRecoveryInFlight === recovery) brokerRecoveryInFlight = undefined;
|
|
6103
6290
|
}
|
|
6104
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
|
+
};
|
|
6105
6307
|
const runtimeOwner: RuntimeState = {
|
|
6106
6308
|
sessionId,
|
|
6107
6309
|
sessionIdentity,
|
|
@@ -6114,6 +6316,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6114
6316
|
pending,
|
|
6115
6317
|
openLifecycleBatches,
|
|
6116
6318
|
registerBroker,
|
|
6319
|
+
stopBrokerRecovery,
|
|
6117
6320
|
quiesceInput: () => {
|
|
6118
6321
|
inputGate.quiescing = true;
|
|
6119
6322
|
lifecycleOwnerHolder.quiescing = true;
|
|
@@ -6129,13 +6332,19 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6129
6332
|
failureDiagnosticCodes: new Map(),
|
|
6130
6333
|
lifecycleTasks: new Set(),
|
|
6131
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);
|
|
6132
6339
|
lifecycleOwnerHolder.state = runtimeOwner;
|
|
6133
6340
|
active = runtimeOwner;
|
|
6134
6341
|
try {
|
|
6135
6342
|
publishedEndpointUrl = (await runtime.start()).url;
|
|
6136
6343
|
await registerBroker();
|
|
6344
|
+
if (!brokerRecoveryStopped) startBrokerRecovery();
|
|
6137
6345
|
} catch (error) {
|
|
6138
6346
|
active = undefined;
|
|
6347
|
+
stopBrokerRecovery();
|
|
6139
6348
|
disposeGate?.();
|
|
6140
6349
|
try {
|
|
6141
6350
|
await runtime.stop();
|
|
@@ -6156,6 +6365,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6156
6365
|
pending,
|
|
6157
6366
|
openLifecycleBatches,
|
|
6158
6367
|
registerBroker,
|
|
6368
|
+
stopBrokerRecovery,
|
|
6159
6369
|
quiesceInput: () => {
|
|
6160
6370
|
inputGate.quiescing = true;
|
|
6161
6371
|
lifecycleOwnerHolder.quiescing = true;
|
|
@@ -6180,7 +6390,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6180
6390
|
throw error;
|
|
6181
6391
|
}
|
|
6182
6392
|
};
|
|
6183
|
-
const stopActive = async (cancelSkillRecovery = false): Promise<void> => {
|
|
6393
|
+
const stopActive = async (cancelSkillRecovery = false, unregisterReason?: "detached_idle"): Promise<void> => {
|
|
6184
6394
|
const current = active;
|
|
6185
6395
|
if (!current) return;
|
|
6186
6396
|
if (cancelSkillRecovery) {
|
|
@@ -6189,6 +6399,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6189
6399
|
}
|
|
6190
6400
|
activePromptOwnerHolder.connectionIds = undefined;
|
|
6191
6401
|
activePromptOwnerHolder.lifecycleEpoch = undefined;
|
|
6402
|
+
current.stopBrokerRecovery();
|
|
6192
6403
|
current.quiesceInput();
|
|
6193
6404
|
current.fenceGateResolutions();
|
|
6194
6405
|
try {
|
|
@@ -6237,7 +6448,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6237
6448
|
retiredLifecycleOwnerTimers.set(current, timer);
|
|
6238
6449
|
} else current.deadlineManager.clearAll();
|
|
6239
6450
|
current.disposeGate?.();
|
|
6240
|
-
await current.runtime.stop({
|
|
6451
|
+
await current.runtime.stop({
|
|
6452
|
+
allowLockContention: cancelSkillRecovery,
|
|
6453
|
+
...(unregisterReason === undefined ? {} : { unregisterReason }),
|
|
6454
|
+
});
|
|
6241
6455
|
} catch (error) {
|
|
6242
6456
|
// Keep the immutable owner available for a retry when transport teardown
|
|
6243
6457
|
// fails after quiescing. Clearing `active` before stop prevents a second
|
|
@@ -6260,7 +6474,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6260
6474
|
await stopActive();
|
|
6261
6475
|
await startRuntime(ctx);
|
|
6262
6476
|
});
|
|
6263
|
-
api.on("session_shutdown", async
|
|
6264
|
-
await stopActive(true);
|
|
6477
|
+
api.on("session_shutdown", async event => {
|
|
6478
|
+
await stopActive(true, event.reason);
|
|
6265
6479
|
});
|
|
6266
6480
|
}
|
package/src/sdk/host/types.ts
CHANGED
|
@@ -25,5 +25,16 @@ export interface HostEndpointAdapters {
|
|
|
25
25
|
|
|
26
26
|
export interface BrokerIndexWriter {
|
|
27
27
|
register(input: { sessionId: string; stateRoot: string; endpointGeneration: number }): void | Promise<void>;
|
|
28
|
-
|
|
28
|
+
heartbeat?(input: {
|
|
29
|
+
sessionId: string;
|
|
30
|
+
stateRoot: string;
|
|
31
|
+
endpointGeneration: number;
|
|
32
|
+
activity: { state: "active" | "idle"; at: number };
|
|
33
|
+
}): void | Promise<void>;
|
|
34
|
+
unregister?(input: {
|
|
35
|
+
sessionId: string;
|
|
36
|
+
stateRoot: string;
|
|
37
|
+
endpointGeneration: number;
|
|
38
|
+
reason?: "detached_idle";
|
|
39
|
+
}): void | Promise<void>;
|
|
29
40
|
}
|