@gajae-code/coding-agent 0.18.12 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +467 -0
- package/dist/types/capability/rule.d.ts +7 -2
- package/dist/types/cli/append-system-prompt.d.ts +6 -0
- package/dist/types/cli/args.d.ts +7 -2
- package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
- package/dist/types/cli/customize-doctor.d.ts +4 -3
- package/dist/types/cli/public-command-errors.d.ts +1 -0
- package/dist/types/cli/root-flags.d.ts +0 -12
- package/dist/types/cli/update-cli.d.ts +7 -2
- package/dist/types/cli-main.d.ts +0 -6
- package/dist/types/commands/launch.d.ts +0 -6
- package/dist/types/commands/migrate.d.ts +1 -1
- package/dist/types/commands/sdk.d.ts +8 -0
- package/dist/types/commit/map-reduce/index.d.ts +4 -4
- package/dist/types/config/autorouting-tier-map.d.ts +23 -1
- package/dist/types/config/config-hot-reload.d.ts +5 -0
- package/dist/types/config/file-lock.d.ts +44 -2
- package/dist/types/config/model-profile-activation.d.ts +19 -5
- package/dist/types/config/model-registry.d.ts +16 -3
- package/dist/types/config/model-resolver.d.ts +5 -4
- package/dist/types/config/settings-schema.d.ts +11 -1
- package/dist/types/coordinator-mcp/server.d.ts +4 -0
- package/dist/types/customization/import.d.ts +2 -0
- package/dist/types/customization/types.d.ts +1 -1
- package/dist/types/dap/session.d.ts +11 -2
- package/dist/types/discovery/helpers.d.ts +7 -2
- package/dist/types/edit/index.d.ts +5 -0
- package/dist/types/edit/modes/patch.d.ts +9 -0
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/export/ttsr.d.ts +19 -0
- package/dist/types/extensibility/extensions/types.d.ts +5 -1
- package/dist/types/extensibility/slash-commands.d.ts +1 -0
- package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
- package/dist/types/hindsight/content.d.ts +5 -6
- package/dist/types/internal-urls/types.d.ts +2 -0
- package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
- package/dist/types/main.d.ts +23 -18
- package/dist/types/migrate/action-planner.d.ts +10 -1
- package/dist/types/migrate/adapters/codex.d.ts +1 -1
- package/dist/types/migrate/adapters/index.d.ts +35 -3
- package/dist/types/migrate/hook-module.d.ts +23 -0
- package/dist/types/migrate/report.d.ts +2 -1
- package/dist/types/migrate/types.d.ts +69 -2
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +43 -0
- package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
- package/dist/types/modes/utils/host-status.d.ts +15 -11
- package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
- package/dist/types/sdk/broker/broker-exit.d.ts +1 -1
- package/dist/types/sdk/broker/broker.d.ts +22 -1
- package/dist/types/sdk/broker/ensure.d.ts +5 -2
- package/dist/types/sdk/broker/hop.d.ts +9 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
- package/dist/types/sdk/broker/session-index.d.ts +2 -0
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/index.d.ts +21 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
- package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
- package/dist/types/sdk/host/control/operations.d.ts +5 -1
- package/dist/types/sdk/host/events.d.ts +4 -0
- package/dist/types/sdk/host/host.d.ts +2 -1
- package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
- package/dist/types/sdk/host/session-runtime.d.ts +8 -8
- package/dist/types/sdk/host/surface-policy.d.ts +2 -1
- package/dist/types/sdk/lifecycle/service.d.ts +12 -0
- package/dist/types/sdk/prompt-status.d.ts +11 -4
- package/dist/types/session/agent-session.d.ts +50 -8
- package/dist/types/session/session-manager.d.ts +16 -0
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +13 -1
- package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
- package/dist/types/ssh/ssh-executor.d.ts +8 -1
- package/dist/types/system-prompt.d.ts +3 -2
- package/dist/types/task/role-body.d.ts +11 -0
- package/dist/types/tools/awaiting-input.d.ts +28 -0
- package/dist/types/tools/bash-allowed-prefixes.d.ts +6 -0
- package/dist/types/tools/bash-skill-urls.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +7 -0
- package/dist/types/tools/descriptors.d.ts +13 -2
- package/dist/types/tools/fetch.d.ts +6 -0
- package/dist/types/tools/irc.d.ts +5 -3
- package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/utils/file-mentions.d.ts +5 -0
- package/dist/types/utils/image-limits.d.ts +7 -0
- package/dist/types/utils/image-loading.d.ts +0 -1
- package/dist/types/utils/open.d.ts +0 -1
- package/dist/types/utils/pasted-image-loading.d.ts +1 -5
- package/dist/types/utils/prompt-suggestion.d.ts +2 -2
- package/dist/types/utils/shell-snapshot.d.ts +2 -0
- package/dist/types/utils/title-generator.d.ts +13 -3
- package/dist/types/web/search/providers/codex.d.ts +6 -7
- package/package.json +9 -8
- package/scripts/acp-conformance-agent.ts +12 -2
- package/scripts/generate-sdk-operation-inventory.ts +16 -0
- package/src/capability/fs.ts +31 -3
- package/src/capability/rule.ts +11 -2
- package/src/cli/append-system-prompt.ts +12 -0
- package/src/cli/args.ts +11 -7
- package/src/cli/customize-doctor-precedence.ts +31 -0
- package/src/cli/customize-doctor.ts +49 -39
- package/src/cli/fast-help.ts +0 -2
- package/src/cli/migrate-cli.ts +14 -3
- package/src/cli/public-command-errors.ts +2 -0
- package/src/cli/root-flags.ts +0 -2
- package/src/cli/update-cli.ts +247 -6
- package/src/commands/harness.ts +25 -15
- package/src/commands/migrate.ts +3 -3
- package/src/commands/sdk.ts +81 -11
- package/src/commit/agentic/agent.ts +6 -6
- package/src/commit/analysis/conventional.ts +14 -5
- package/src/commit/map-reduce/index.ts +7 -7
- package/src/commit/pipeline.ts +12 -12
- package/src/config/autorouting-tier-map.ts +20 -1
- package/src/config/config-file.ts +106 -3
- package/src/config/config-hot-reload.ts +8 -0
- package/src/config/file-lock.ts +1431 -304
- package/src/config/keybindings.ts +154 -7
- package/src/config/model-profile-activation.ts +177 -39
- package/src/config/model-registry.ts +380 -55
- package/src/config/model-resolver.ts +49 -21
- package/src/config/settings-schema.ts +13 -1
- package/src/config/settings.ts +2 -0
- package/src/coordinator-mcp/server.ts +37 -2
- package/src/crash/sanitize.ts +3 -1
- package/src/customization/import.ts +276 -7
- package/src/customization/inventory.ts +24 -8
- package/src/customization/types.ts +1 -0
- package/src/dap/client.ts +3 -2
- package/src/dap/session.ts +161 -36
- package/src/debug/crash-diagnostics.ts +87 -5
- package/src/debug/report-bundle.ts +71 -2
- package/src/discovery/builtin.ts +4 -3
- package/src/discovery/helpers.ts +24 -14
- package/src/edit/index.ts +191 -5
- package/src/edit/modes/patch.ts +27 -0
- package/src/eval/py/runtime.ts +44 -4
- package/src/exec/non-interactive-env.ts +0 -1
- package/src/export/html/index.ts +60 -20
- package/src/export/ttsr.ts +176 -1
- package/src/extensibility/extensions/loader.ts +2 -0
- package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
- package/src/extensibility/extensions/types.ts +5 -1
- package/src/extensibility/hooks/loader.ts +10 -2
- package/src/extensibility/plugins/marketplace/manager.ts +36 -10
- package/src/extensibility/slash-commands.ts +5 -0
- package/src/gjc-runtime/gc-runtime.ts +4 -1
- package/src/gjc-runtime/launch-tmux.ts +23 -13
- package/src/gjc-runtime/ralplan-current-final.ts +294 -0
- package/src/gjc-runtime/ralplan-runtime.ts +95 -1
- package/src/gjc-runtime/state-writer.ts +109 -5
- package/src/gjc-runtime/tmux-sessions.ts +93 -13
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
- package/src/gjc-runtime/ultragoal-guard.ts +9 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
- package/src/harness-control-plane/finalize.ts +11 -0
- package/src/harness-control-plane/receipt-spool.ts +105 -28
- package/src/harness-control-plane/storage.ts +124 -6
- package/src/hindsight/content.ts +116 -16
- package/src/hindsight/mental-models.ts +22 -10
- package/src/hindsight/state.ts +7 -4
- package/src/hooks/native-skill-hook.ts +3 -1
- package/src/internal-urls/artifact-protocol.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +14 -14
- package/src/internal-urls/local-protocol.ts +28 -3
- package/src/internal-urls/types.ts +2 -0
- package/src/lsp/client.ts +2 -1
- package/src/lsp/clients/biome-client.ts +2 -0
- package/src/lsp/clients/swiftlint-client.ts +2 -0
- package/src/main.ts +330 -92
- package/src/memories/index.ts +25 -2
- package/src/migrate/action-planner.ts +517 -2
- package/src/migrate/adapters/claude-code.ts +141 -3
- package/src/migrate/adapters/codex.ts +57 -5
- package/src/migrate/adapters/index.ts +240 -5
- package/src/migrate/adapters/opencode.ts +4 -0
- package/src/migrate/executor.ts +141 -3
- package/src/migrate/hook-module.ts +193 -0
- package/src/migrate/report.ts +45 -1
- package/src/migrate/types.ts +73 -3
- package/src/modes/acp/acp-agent.ts +10 -1
- package/src/modes/components/agent-dashboard.ts +20 -4
- package/src/modes/components/customization/customization-dashboard.ts +7 -0
- package/src/modes/components/customization/import-wizard.ts +29 -3
- package/src/modes/components/model-selector.ts +157 -103
- package/src/modes/components/welcome.ts +3 -1
- package/src/modes/controllers/command-controller.ts +271 -16
- package/src/modes/controllers/event-controller.ts +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +31 -1
- package/src/modes/controllers/goal-mode-controller.ts +79 -10
- package/src/modes/controllers/input-controller.ts +2 -0
- package/src/modes/controllers/plan-mode-controller.ts +49 -4
- package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
- package/src/modes/controllers/selector-controller.ts +14 -1
- package/src/modes/interactive-mode.ts +55 -4
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +5 -1
- package/src/modes/utils/host-status.ts +21 -10
- package/src/prompts/agent-fragments/browser-aside.md +2 -2
- package/src/prompts/system/irc-incoming.md +3 -1
- package/src/prompts/tools/irc.md +1 -1
- package/src/runtime-mcp/manager.ts +8 -3
- package/src/runtime-mcp/project-host-values.ts +12 -8
- package/src/runtime-mcp/tool-bridge.ts +68 -4
- package/src/runtime-mcp/transports/stdio.ts +2 -1
- package/src/sdk/broker/broker-exit.ts +2 -0
- package/src/sdk/broker/broker.ts +143 -4
- package/src/sdk/broker/ensure.ts +74 -6
- package/src/sdk/broker/hop.ts +13 -1
- package/src/sdk/broker/lifecycle-ledger.ts +3 -0
- package/src/sdk/broker/lifecycle.ts +102 -6
- package/src/sdk/broker/session-index.ts +29 -4
- package/src/sdk/broker/transport.ts +17 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +408 -14
- package/src/sdk/bus/session-control.ts +2 -0
- package/src/sdk/bus/slack-daemon.ts +105 -9
- package/src/sdk/bus/telegram-daemon-contract.ts +7 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -2
- package/src/sdk/bus/threaded-render.ts +20 -1
- package/src/sdk/cli/session-cli.ts +2 -0
- package/src/sdk/host/control/dispatch.ts +13 -1
- package/src/sdk/host/control/operations.ts +10 -1
- package/src/sdk/host/events.ts +67 -4
- package/src/sdk/host/host.ts +8 -2
- package/src/sdk/host/prompt-image-upload.ts +438 -0
- package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
- package/src/sdk/host/session-runtime.test.ts +1537 -24
- package/src/sdk/host/session-runtime.ts +332 -36
- package/src/sdk/host/surface-policy.ts +4 -0
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
- package/src/sdk/host/websocket-transport.ts +19 -7
- package/src/sdk/lifecycle/service.ts +14 -1
- package/src/sdk/prompt-status.ts +6 -4
- package/src/sdk/protocol/adapter-validation.ts +28 -1
- package/src/sdk/protocol/operation-inventory.generated.json +160 -0
- package/src/sdk/protocol/operation-registry.ts +33 -3
- package/src/sdk/session.ts +123 -36
- package/src/session/agent-session.ts +1614 -329
- package/src/session/history-storage.ts +182 -12
- package/src/session/internal/managed-session-scope.ts +47 -18
- package/src/session/internal/managed-session-storage.ts +71 -1
- package/src/session/session-manager.ts +90 -5
- package/src/session-import/redact.ts +53 -2
- package/src/skill-state/workflow-mutation-guard.ts +1498 -18
- package/src/slash-commands/builtin-registry.ts +6 -3
- package/src/slash-commands/helpers/mcp.ts +11 -3
- package/src/ssh/ssh-executor.ts +9 -2
- package/src/stt/recorder.ts +26 -9
- package/src/system-prompt.ts +61 -16
- package/src/task/executor.ts +12 -8
- package/src/task/role-body.ts +84 -0
- package/src/task/token-log-mode.test.ts +162 -0
- package/src/task/token-log.ts +91 -2
- package/src/tools/archive-reader.ts +650 -37
- package/src/tools/ask.ts +1 -0
- package/src/tools/ast-grep.ts +7 -2
- package/src/tools/auto-generated-guard.ts +12 -3
- package/src/tools/awaiting-input.ts +114 -0
- package/src/tools/bash-allowed-prefixes.ts +61 -1
- package/src/tools/bash-skill-urls.ts +58 -5
- package/src/tools/bash.ts +99 -26
- package/src/tools/browser/tab-worker.ts +39 -13
- package/src/tools/browser.ts +17 -7
- package/src/tools/cron.ts +10 -1
- package/src/tools/debug.ts +9 -2
- package/src/tools/descriptors.test.ts +4 -4
- package/src/tools/descriptors.ts +16 -4
- package/src/tools/eval.ts +15 -0
- package/src/tools/fetch.ts +90 -2
- package/src/tools/gh.ts +27 -12
- package/src/tools/image-gen.ts +20 -6
- package/src/tools/irc.ts +5 -3
- package/src/tools/monitor.ts +9 -1
- package/src/tools/output-meta.ts +51 -3
- package/src/tools/read.ts +9 -2
- package/src/tools/recipe/runner.ts +23 -12
- package/src/tools/render-utils.ts +2 -2
- package/src/tools/search.ts +3 -2
- package/src/tools/ssh.ts +274 -8
- package/src/tools/subagent.ts +10 -4
- package/src/tools/tmux-self-injection-guard.ts +3301 -122
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/tools/ultragoal-ask-guard.ts +13 -1
- package/src/tools/write.ts +7 -0
- package/src/tui/tree-list.ts +15 -1
- package/src/utils/commit-message-generator.ts +3 -2
- package/src/utils/external-editor.ts +21 -7
- package/src/utils/file-mentions.ts +46 -6
- package/src/utils/git.ts +38 -7
- package/src/utils/image-limits.ts +8 -0
- package/src/utils/image-loading.ts +1 -1
- package/src/utils/open.ts +73 -4
- package/src/utils/pasted-image-loading.ts +9 -8
- package/src/utils/prompt-suggestion.ts +5 -4
- package/src/utils/shell-snapshot.ts +9 -1
- package/src/utils/title-generator.ts +65 -20
- package/src/web/search/index.ts +2 -1
- package/src/web/search/providers/codex.ts +29 -32
|
@@ -4,7 +4,12 @@ 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 {
|
|
7
|
+
import {
|
|
8
|
+
isContinuingMidRunMaintenanceOutcome,
|
|
9
|
+
isNonDispatchedToolEvent,
|
|
10
|
+
type RunSettlementProof,
|
|
11
|
+
ThinkingLevel,
|
|
12
|
+
} from "@gajae-code/agent-core";
|
|
8
13
|
import type { AttemptScope } from "@gajae-code/agent-core/attempt-scope";
|
|
9
14
|
import type { Api, ImageContent, Model, ProviderDiagnostic } from "@gajae-code/ai/core";
|
|
10
15
|
|
|
@@ -39,7 +44,7 @@ import { tagSdkLifecycleObserver } from "../../extensibility/extensions/function
|
|
|
39
44
|
import type { AgentEndEvent } from "../../extensibility/shared-events";
|
|
40
45
|
import { normalizeGoal } from "../../goals/state";
|
|
41
46
|
import { toAgentWireEventPayload } from "../../modes/shared/agent-wire/event-envelope";
|
|
42
|
-
import type { AgentSessionEvent } from "../../session/agent-session";
|
|
47
|
+
import type { AgentSessionEvent, QueuedRunStartFailure } from "../../session/agent-session";
|
|
43
48
|
import type { SdkRunCapability } from "../../session/sdk-run-capability";
|
|
44
49
|
import {
|
|
45
50
|
boundTerminalRetentionState,
|
|
@@ -125,6 +130,7 @@ import {
|
|
|
125
130
|
TURN_STREAM_CAPABILITY,
|
|
126
131
|
} from "./host";
|
|
127
132
|
import { clearAutoroutingInactive, isAutoroutingInactive, markAutoroutingInactive } from "./internal-autorouting-state";
|
|
133
|
+
import { PromptImageUploadStore, retirePromptImageReservation } from "./prompt-image-upload";
|
|
128
134
|
import { CursorRegistry, QueryHandlers, RevisionStore, type SessionSurface } from "./query";
|
|
129
135
|
import { createSdkRunCapability } from "./sdk-run-capability";
|
|
130
136
|
import {
|
|
@@ -350,6 +356,8 @@ export interface SessionSdkTransport {
|
|
|
350
356
|
stop(): Promise<void>;
|
|
351
357
|
broadcastFrame?(frame: SdkFrame): void;
|
|
352
358
|
onConnectionClose?(handler: (connectionId: string) => void): undefined | (() => void);
|
|
359
|
+
/** Authoritative live socket membership, not a cache of previously observed frames. */
|
|
360
|
+
isConnectionOpen?(connectionId: string): boolean;
|
|
353
361
|
onNegotiatedCapabilities?(
|
|
354
362
|
handler: (connectionId: string, capabilities: readonly string[]) => void,
|
|
355
363
|
): undefined | (() => void);
|
|
@@ -357,6 +365,7 @@ export interface SessionSdkTransport {
|
|
|
357
365
|
|
|
358
366
|
export interface SessionSdkRuntimeOptions
|
|
359
367
|
extends Omit<SessionSdkHostOptions, "sessionId" | "stateRoot" | "token" | "sendFrame" | "onFrame"> {
|
|
368
|
+
onDisconnected?: (connectionId: string) => void;
|
|
360
369
|
transport: SessionSdkTransport;
|
|
361
370
|
/** Session settings; enables `config.patch` application on this runtime. */
|
|
362
371
|
settings?: Settings;
|
|
@@ -443,6 +452,8 @@ export interface SdkOnlyReconciliationStore {
|
|
|
443
452
|
}
|
|
444
453
|
|
|
445
454
|
export interface SdkOnlyTerminalAbortSeams {
|
|
455
|
+
/** Definitive event-driven observation: never abort, seal or quarantine a run to reclaim images. */
|
|
456
|
+
waitForRunSettlement?: (handle: string) => Promise<RunSettlementProof>;
|
|
446
457
|
getReconciliationStore?: () => SdkOnlyReconciliationStore | undefined;
|
|
447
458
|
getTerminalTurnEpoch: () => number | undefined;
|
|
448
459
|
getActivePromptHandle: () => string | undefined;
|
|
@@ -521,6 +532,10 @@ export interface SdkOnlyDeadlineRecoveryCheckpoint {
|
|
|
521
532
|
* dispatch, replay/event publication, and reverse-provider lifecycle without
|
|
522
533
|
* importing any notification adapter or native notification class.
|
|
523
534
|
*/
|
|
535
|
+
function isStringArray(value: unknown): value is string[] {
|
|
536
|
+
return Array.isArray(value) && value.every((item: unknown) => typeof item === "string");
|
|
537
|
+
}
|
|
538
|
+
|
|
524
539
|
export class SessionSdkSessionRuntime {
|
|
525
540
|
readonly host: SessionSdkHost;
|
|
526
541
|
readonly transport: SessionSdkTransport;
|
|
@@ -551,18 +566,32 @@ export class SessionSdkSessionRuntime {
|
|
|
551
566
|
onFrame: handler =>
|
|
552
567
|
options.transport.onFrame((connectionId, frame) => {
|
|
553
568
|
this.#connectionIds.add(connectionId);
|
|
569
|
+
if (
|
|
570
|
+
!options.connectionCapabilities &&
|
|
571
|
+
frame.type === "event_replay" &&
|
|
572
|
+
isStringArray(frame.capabilities)
|
|
573
|
+
) {
|
|
574
|
+
this.#connectionCapabilities.set(
|
|
575
|
+
connectionId,
|
|
576
|
+
new Set([...(this.#connectionCapabilities.get(connectionId) ?? []), ...frame.capabilities]),
|
|
577
|
+
);
|
|
578
|
+
}
|
|
554
579
|
handler(connectionId, frame);
|
|
555
580
|
}),
|
|
556
581
|
});
|
|
557
582
|
this.#connectionDisposer = options.transport.onConnectionClose?.(connectionId => {
|
|
558
583
|
this.#connectionIds.delete(connectionId);
|
|
559
584
|
this.#connectionCapabilities.delete(connectionId);
|
|
585
|
+
options.onDisconnected?.(connectionId);
|
|
560
586
|
this.host.handleDisconnect(connectionId);
|
|
561
587
|
});
|
|
562
588
|
this.#capabilitiesDisposer = options.transport.onNegotiatedCapabilities?.((connectionId, negotiated) => {
|
|
563
589
|
if (options.connectionCapabilities && this.#connectionCapabilitiesProvider(connectionId) === undefined) return;
|
|
564
590
|
this.#connectionIds.add(connectionId);
|
|
565
|
-
this.#connectionCapabilities.set(
|
|
591
|
+
this.#connectionCapabilities.set(
|
|
592
|
+
connectionId,
|
|
593
|
+
new Set([...(this.#connectionCapabilities.get(connectionId) ?? []), ...negotiated]),
|
|
594
|
+
);
|
|
566
595
|
});
|
|
567
596
|
this.#malformedDisposer = options.transport.onMalformedFrame?.((connectionId, message) => {
|
|
568
597
|
this.host.handleMalformedFrame(connectionId, message);
|
|
@@ -2215,6 +2244,8 @@ export interface SdkSurfaceFactoryOptions {
|
|
|
2215
2244
|
api: ExtensionAPI;
|
|
2216
2245
|
/** Startup surface that owns host-level permission and lifecycle authority. */
|
|
2217
2246
|
primaryControlSurface?: "cli" | "sdk";
|
|
2247
|
+
/** Private staging controls are advertised only with authoritative transport liveness. */
|
|
2248
|
+
imageUploadsAvailable?: boolean;
|
|
2218
2249
|
policy?: SdkSurfacePolicy;
|
|
2219
2250
|
getInstalledDefinitions?: (capability: string) => unknown | undefined;
|
|
2220
2251
|
getLiveState?: () => { isStreaming: boolean; steeringQueueDepth: number; followupQueueDepth: number };
|
|
@@ -2298,7 +2329,8 @@ function createQuerySurface(
|
|
|
2298
2329
|
};
|
|
2299
2330
|
const getProfileCredentialSessionId = () => ctx.credentialSessionId ?? id;
|
|
2300
2331
|
const profileSettings = (options.settings ?? ctx.settings) as Pick<Settings, "get"> | undefined;
|
|
2301
|
-
const getProfileAvailableModels = (): Model<Api>[] =>
|
|
2332
|
+
const getProfileAvailableModels = (): Model<Api>[] =>
|
|
2333
|
+
ctx.modelRegistry.getAvailable(getProfileCredentialSessionId());
|
|
2302
2334
|
const resolveProfileAvailability = async (
|
|
2303
2335
|
profile: ModelProfileDefinition,
|
|
2304
2336
|
authenticatedProviders: ReadonlySet<string>,
|
|
@@ -2519,7 +2551,7 @@ function createQuerySurface(
|
|
|
2519
2551
|
getDiff,
|
|
2520
2552
|
getUsage: () => ctx.sessionManager.getUsageStatistics(),
|
|
2521
2553
|
getModels: async () => {
|
|
2522
|
-
const models = ctx.modelRegistry.getAll();
|
|
2554
|
+
const models = ctx.modelRegistry.getAll(getProfileCredentialSessionId());
|
|
2523
2555
|
const currentModel = ctx.model;
|
|
2524
2556
|
const currentThinkingLevel = api.getThinkingLevel();
|
|
2525
2557
|
const activeProfile =
|
|
@@ -2642,7 +2674,9 @@ function createQuerySurface(
|
|
|
2642
2674
|
getBranchCandidates: () => ctx.getBranchCandidates(),
|
|
2643
2675
|
getLastAssistant: lastAssistant,
|
|
2644
2676
|
getCapabilities: () => createSdkCapabilities(policy, hasHostTools(), options.primaryControlSurface),
|
|
2645
|
-
getAuthProviders: () => [
|
|
2677
|
+
getAuthProviders: () => [
|
|
2678
|
+
...new Set(ctx.modelRegistry.getAll(getProfileCredentialSessionId()).map(model => model.provider)),
|
|
2679
|
+
],
|
|
2646
2680
|
getActiveProviders: () => ctx.modelRegistry.getActiveProviders(),
|
|
2647
2681
|
getTools: () => {
|
|
2648
2682
|
const tools = typeof (ctx as Partial<ExtensionContext>).getAllTools === "function" ? ctx.getAllTools() : [];
|
|
@@ -2720,7 +2754,11 @@ export function createSdkSurfaceFactory(
|
|
|
2720
2754
|
): SdkSurfaceFactory {
|
|
2721
2755
|
const policy =
|
|
2722
2756
|
options.policy ??
|
|
2723
|
-
createSdkSurfacePolicyForContext(
|
|
2757
|
+
createSdkSurfacePolicyForContext(
|
|
2758
|
+
options.ctx,
|
|
2759
|
+
hasSdkWorkflowGateCapability(options.ctx.workflowGate),
|
|
2760
|
+
options.imageUploadsAvailable,
|
|
2761
|
+
);
|
|
2724
2762
|
const reconciliation =
|
|
2725
2763
|
options.reconciliation ??
|
|
2726
2764
|
createInvocationReconciliation({
|
|
@@ -3106,6 +3144,7 @@ function createControlSurface(
|
|
|
3106
3144
|
) => void,
|
|
3107
3145
|
armPromptDeadline: (correlation: InvocationCorrelation) => void,
|
|
3108
3146
|
steerReconciliation: KindAwareReconciliation,
|
|
3147
|
+
imageUploads: PromptImageUploadStore | undefined,
|
|
3109
3148
|
onPromotedTurn?: (
|
|
3110
3149
|
kind: InvocationKind,
|
|
3111
3150
|
correlation: InvocationCorrelation,
|
|
@@ -3133,7 +3172,15 @@ function createControlSurface(
|
|
|
3133
3172
|
trackGateResolution: <T>(resolution: Promise<T>) => Promise<T> = async resolution => await resolution,
|
|
3134
3173
|
onInvocationCompletionReconciledForTests?: (kind: InvocationKind, correlation: InvocationCorrelation) => void,
|
|
3135
3174
|
publishLifecycleFrame?: (frame: SdkFrame) => void,
|
|
3175
|
+
retainAcceptedImage?: (correlation: InvocationCorrelation, release: () => void) => void,
|
|
3176
|
+
settleAcceptedImageSubmission?: (correlation: InvocationCorrelation) => void,
|
|
3177
|
+
releaseAcceptedImage?: (correlation: InvocationCorrelation) => void,
|
|
3136
3178
|
acceptedQueueCancellations: Map<string, AcceptedQueueCancellation> = new Map(),
|
|
3179
|
+
onQueuedRunStartFailed?: (
|
|
3180
|
+
kind: InvocationKind,
|
|
3181
|
+
correlation: InvocationCorrelation,
|
|
3182
|
+
failure: QueuedRunStartFailure,
|
|
3183
|
+
) => boolean,
|
|
3137
3184
|
): ControlSurface {
|
|
3138
3185
|
const normalizePromptImages = (value: unknown): ImageContent[] => {
|
|
3139
3186
|
if (!Array.isArray(value)) return [];
|
|
@@ -3154,6 +3201,7 @@ function createControlSurface(
|
|
|
3154
3201
|
type InternalSendOptions = NonNullable<Parameters<ExtensionAPI["sendUserMessage"]>[1]> & {
|
|
3155
3202
|
sdkRunCapability?: SdkRunCapability;
|
|
3156
3203
|
expectedSdkRunToken?: string;
|
|
3204
|
+
onQueuedRunStartFailed?: (failure: QueuedRunStartFailure) => void;
|
|
3157
3205
|
};
|
|
3158
3206
|
type InternalSdkApi = Omit<ExtensionAPI, "sendUserMessage"> & {
|
|
3159
3207
|
sendUserMessage: (
|
|
@@ -3164,7 +3212,7 @@ function createControlSurface(
|
|
|
3164
3212
|
const internalApi = api as InternalSdkApi;
|
|
3165
3213
|
const sendSdkUserMessage = (
|
|
3166
3214
|
content: Parameters<ExtensionAPI["sendUserMessage"]>[0],
|
|
3167
|
-
options?:
|
|
3215
|
+
options?: InternalSendOptions,
|
|
3168
3216
|
): Promise<void> => internalApi.sendUserMessage(content, options);
|
|
3169
3217
|
const surfacePolicy =
|
|
3170
3218
|
policy ?? createSdkSurfacePolicyForContext(ctx, hasSdkWorkflowGateCapability(ctx.workflowGate));
|
|
@@ -3174,8 +3222,14 @@ function createControlSurface(
|
|
|
3174
3222
|
const [provider, ...modelId] = id.split("/");
|
|
3175
3223
|
const model =
|
|
3176
3224
|
modelId.length > 0
|
|
3177
|
-
? ctx.modelRegistry.find(
|
|
3178
|
-
|
|
3225
|
+
? ctx.modelRegistry.find(
|
|
3226
|
+
provider,
|
|
3227
|
+
modelId.join("/"),
|
|
3228
|
+
ctx.credentialSessionId ?? ctx.sessionManager.getSessionId(),
|
|
3229
|
+
)
|
|
3230
|
+
: ctx.modelRegistry
|
|
3231
|
+
.getAll(ctx.credentialSessionId ?? ctx.sessionManager.getSessionId())
|
|
3232
|
+
.find(candidate => candidate.id === id);
|
|
3179
3233
|
if (!model) throw Object.assign(new Error(`Model ${id} was not found.`), { code: "invalid_input" });
|
|
3180
3234
|
return model;
|
|
3181
3235
|
};
|
|
@@ -3301,11 +3355,13 @@ function createControlSurface(
|
|
|
3301
3355
|
onDispatchDisposition: (promotion: { startsOwnRun: boolean }) => void;
|
|
3302
3356
|
/** Fired when a queued submission (steering or follow-up) is promoted to its own run (SDK ownership correlation). */
|
|
3303
3357
|
onQueuedPromoted: (promotion?: { startsOwnRun?: boolean; removed?: boolean }) => void;
|
|
3358
|
+
onQueuedRunStartFailed: (failure: QueuedRunStartFailure) => void;
|
|
3304
3359
|
queuedAtDispatch: boolean;
|
|
3305
3360
|
}) => Promise<unknown>,
|
|
3306
3361
|
acceptedFields?: () => Record<string, unknown>,
|
|
3307
3362
|
allowCompletionFallback = false,
|
|
3308
3363
|
alwaysQueued = false,
|
|
3364
|
+
onCreated?: (correlation: InvocationCorrelation) => void,
|
|
3309
3365
|
): Promise<unknown> => {
|
|
3310
3366
|
// Capture the REQUESTING connection at admission: a terminal abort from
|
|
3311
3367
|
// another SDK connection must never stop the prompt this one accepts
|
|
@@ -3314,9 +3370,11 @@ function createControlSurface(
|
|
|
3314
3370
|
const retainedClientRef = normalizeClientRef(clientRef);
|
|
3315
3371
|
reconciliation.admit(kind, retainedClientRef);
|
|
3316
3372
|
const correlation = newCorrelation();
|
|
3373
|
+
onCreated?.(correlation);
|
|
3317
3374
|
const sdkRunToken = `${correlation.commandId}:${correlation.turnId}`;
|
|
3318
3375
|
const sdkRunCapability = createSdkRunCapability(sdkRunToken);
|
|
3319
3376
|
const publishTerminal = (outcome: InvocationOutcome): void => {
|
|
3377
|
+
releaseAcceptedImage?.(correlation);
|
|
3320
3378
|
retireAcceptedQueueCancellation(acceptedQueueCancellations, correlation);
|
|
3321
3379
|
publishLifecycleFrame?.({
|
|
3322
3380
|
type: "agent_end",
|
|
@@ -3350,6 +3408,7 @@ function createControlSurface(
|
|
|
3350
3408
|
};
|
|
3351
3409
|
let accepted = false;
|
|
3352
3410
|
let settled = false;
|
|
3411
|
+
let submission: Promise<unknown> | undefined;
|
|
3353
3412
|
const cancelPreflight = () => {
|
|
3354
3413
|
preflightController.abort();
|
|
3355
3414
|
if (settled) return;
|
|
@@ -3409,14 +3468,16 @@ function createControlSurface(
|
|
|
3409
3468
|
if (kind === "prompt")
|
|
3410
3469
|
acceptedQueueCancellations.set(`${correlation.commandId}:${correlation.turnId}`, queueCancellation);
|
|
3411
3470
|
let promotionStartsOwnRun: boolean | undefined;
|
|
3471
|
+
let queuedRunStartRefused = false;
|
|
3412
3472
|
try {
|
|
3413
|
-
|
|
3473
|
+
submission = Promise.resolve(
|
|
3414
3474
|
run({
|
|
3415
3475
|
onPreflightAccepted: () => void accept().catch(() => undefined),
|
|
3416
3476
|
preflightSignal: preflightController.signal,
|
|
3417
3477
|
sdkRunCapability,
|
|
3418
3478
|
onPreflightAcceptCommit: accept,
|
|
3419
3479
|
onDispatchDisposition: promotion => {
|
|
3480
|
+
if (queuedRunStartRefused) return;
|
|
3420
3481
|
promotionStartsOwnRun = promotion.startsOwnRun;
|
|
3421
3482
|
if (promotion.startsOwnRun === false) {
|
|
3422
3483
|
queueCancellation.queueCandidate = true;
|
|
@@ -3436,6 +3497,7 @@ function createControlSurface(
|
|
|
3436
3497
|
// created at promotion so the submitting connection can
|
|
3437
3498
|
// terminal-abort that turn (review threads P1/P2).
|
|
3438
3499
|
onQueuedPromoted: (promotion?: { startsOwnRun?: boolean; removed?: boolean }) => {
|
|
3500
|
+
if (queuedRunStartRefused) return;
|
|
3439
3501
|
promotionStartsOwnRun = promotion?.startsOwnRun;
|
|
3440
3502
|
if (promotion?.removed) {
|
|
3441
3503
|
queueCancellation.disposition = "removed";
|
|
@@ -3469,11 +3531,29 @@ function createControlSurface(
|
|
|
3469
3531
|
}
|
|
3470
3532
|
void onPromotedTurn?.(kind, correlation, requesterConnectionId, sdkRunToken, promotion);
|
|
3471
3533
|
},
|
|
3534
|
+
onQueuedRunStartFailed: failure => {
|
|
3535
|
+
if (
|
|
3536
|
+
queuedRunStartRefused ||
|
|
3537
|
+
!accepted ||
|
|
3538
|
+
!queueCancellation.queueCandidate ||
|
|
3539
|
+
failure.disposition !== "not-started"
|
|
3540
|
+
)
|
|
3541
|
+
return;
|
|
3542
|
+
// Only the private Core decision for this selected queue entry is proof.
|
|
3543
|
+
// The lifecycle owner rejects contradictory decisions after adoption.
|
|
3544
|
+
queuedRunStartRefused = onQueuedRunStartFailed?.(kind, correlation, failure) === true;
|
|
3545
|
+
if (queuedRunStartRefused) {
|
|
3546
|
+
queueCancellation.disposition = "consumed";
|
|
3547
|
+
queueCancellation.resolveDisposition("consumed");
|
|
3548
|
+
}
|
|
3549
|
+
},
|
|
3472
3550
|
queuedAtDispatch,
|
|
3473
3551
|
}),
|
|
3474
3552
|
);
|
|
3475
3553
|
void submission.then(
|
|
3476
3554
|
result => {
|
|
3555
|
+
settleAcceptedImageSubmission?.(correlation);
|
|
3556
|
+
if (queuedRunStartRefused) return;
|
|
3477
3557
|
if (settled) {
|
|
3478
3558
|
// A resolved submission after preflight acceptance means the work is over
|
|
3479
3559
|
// for every kind. `noteTransition` ignores an already-terminal record, so
|
|
@@ -3484,7 +3564,9 @@ function createControlSurface(
|
|
|
3484
3564
|
// a delayed preflight diverted to steering fires onQueuedPromoted
|
|
3485
3565
|
// with startsOwnRun:false and is now attached to the in-flight run.
|
|
3486
3566
|
// Do not terminalize from the stale snapshot — the run will.
|
|
3487
|
-
|
|
3567
|
+
// Remember actual diversion even after own-run promotion changes
|
|
3568
|
+
// promotionStartsOwnRun: enqueue completion is still not run completion.
|
|
3569
|
+
if (!queueCancellation.queueCandidate && promotionStartsOwnRun !== false) {
|
|
3488
3570
|
// The accepted work settled without its own run still pending:
|
|
3489
3571
|
// retire the pending ownership entry (and with it the
|
|
3490
3572
|
// acceptance-anchored deadline lease, #4668 review) BEFORE
|
|
@@ -3553,7 +3635,10 @@ function createControlSurface(
|
|
|
3553
3635
|
);
|
|
3554
3636
|
},
|
|
3555
3637
|
error => {
|
|
3638
|
+
settleAcceptedImageSubmission?.(correlation);
|
|
3639
|
+
if (queuedRunStartRefused) return;
|
|
3556
3640
|
if (settled) {
|
|
3641
|
+
releaseAcceptedImage?.(correlation);
|
|
3557
3642
|
retireAcceptedQueueCancellation(acceptedQueueCancellations, correlation);
|
|
3558
3643
|
// The submission promise rejects after preflight acceptance only when the
|
|
3559
3644
|
// work itself is over (provider stream interrupt, abort, queue failure).
|
|
@@ -3675,6 +3760,9 @@ function createControlSurface(
|
|
|
3675
3760
|
...(acceptedFields?.() ?? {}),
|
|
3676
3761
|
};
|
|
3677
3762
|
} catch (error) {
|
|
3763
|
+
if (!accepted) preflightController.abort();
|
|
3764
|
+
if (!submission) settleAcceptedImageSubmission?.(correlation);
|
|
3765
|
+
releaseAcceptedImage?.(correlation);
|
|
3678
3766
|
retireAcceptedQueueCancellation(acceptedQueueCancellations, correlation);
|
|
3679
3767
|
if (!accepted) reconciliation.release(kind, retainedClientRef);
|
|
3680
3768
|
throw error;
|
|
@@ -4593,28 +4681,57 @@ function createControlSurface(
|
|
|
4593
4681
|
}
|
|
4594
4682
|
};
|
|
4595
4683
|
return {
|
|
4596
|
-
prompt: async (text, images, clientRef) => {
|
|
4597
|
-
const invalid = validateRequiredPromptText("turn.prompt", { text, images });
|
|
4684
|
+
prompt: async (text, images, clientRef, stagedImages) => {
|
|
4685
|
+
const invalid = validateRequiredPromptText("turn.prompt", { text, images, stagedImages });
|
|
4598
4686
|
if (invalid) throw Object.assign(new Error(invalid.message), { code: invalid.code });
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
//
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4687
|
+
if (stagedImages !== undefined && images !== undefined)
|
|
4688
|
+
throw Object.assign(new Error("Direct and staged images cannot be mixed."), { code: "invalid_input" });
|
|
4689
|
+
if (stagedImages !== undefined && !imageUploads)
|
|
4690
|
+
throw Object.assign(new Error("Image uploads are unavailable."), { code: "operation_prohibited" });
|
|
4691
|
+
let reservation: { images: ImageContent[]; release: () => void } | undefined;
|
|
4692
|
+
let imageCorrelation: InvocationCorrelation | undefined;
|
|
4693
|
+
try {
|
|
4694
|
+
return await submit(
|
|
4695
|
+
"prompt",
|
|
4696
|
+
clientRef,
|
|
4697
|
+
({ queuedAtDispatch, sdkRunCapability, ...options }) => {
|
|
4698
|
+
// submit has reserved reconciliation/clientRef before consuming staged IDs.
|
|
4699
|
+
if (stagedImages !== undefined) {
|
|
4700
|
+
reservation = imageUploads!.redeem(sdkControlRequesterContext.getStore(), stagedImages);
|
|
4701
|
+
if (imageCorrelation && retainAcceptedImage)
|
|
4702
|
+
retainAcceptedImage(imageCorrelation, reservation.release);
|
|
4703
|
+
}
|
|
4704
|
+
return sendSdkUserMessage(
|
|
4705
|
+
typeof images === "undefined" && !reservation
|
|
4706
|
+
? text
|
|
4707
|
+
: ([{ type: "text", text }, ...(reservation?.images ?? normalizePromptImages(images))] as [
|
|
4708
|
+
{ type: "text"; text: string },
|
|
4709
|
+
...ImageContent[],
|
|
4710
|
+
]),
|
|
4711
|
+
{ ...options, sdkRunCapability, ...(queuedAtDispatch ? { queuedAtDispatch: true } : {}) },
|
|
4712
|
+
);
|
|
4614
4713
|
},
|
|
4615
|
-
|
|
4616
|
-
|
|
4714
|
+
undefined,
|
|
4715
|
+
false,
|
|
4716
|
+
false,
|
|
4717
|
+
correlation => {
|
|
4718
|
+
if (retainAcceptedImage) imageCorrelation = correlation;
|
|
4719
|
+
},
|
|
4720
|
+
);
|
|
4721
|
+
} catch (error) {
|
|
4722
|
+
if (imageCorrelation) releaseAcceptedImage?.(imageCorrelation);
|
|
4723
|
+
else reservation?.release();
|
|
4724
|
+
throw error;
|
|
4725
|
+
}
|
|
4617
4726
|
},
|
|
4727
|
+
...(imageUploads
|
|
4728
|
+
? {
|
|
4729
|
+
imageBegin: (input: unknown) => imageUploads.begin(sdkControlRequesterContext.getStore(), input),
|
|
4730
|
+
imageAppend: (input: unknown) => imageUploads.append(sdkControlRequesterContext.getStore(), input),
|
|
4731
|
+
imageFinish: (input: unknown) => imageUploads.finish(sdkControlRequesterContext.getStore(), input),
|
|
4732
|
+
imageDiscard: (input: unknown) => imageUploads.discard(sdkControlRequesterContext.getStore(), input),
|
|
4733
|
+
}
|
|
4734
|
+
: {}),
|
|
4618
4735
|
steer: async (text, clientRef, expectedSdkRunToken) => {
|
|
4619
4736
|
const invalid = validateRequiredPromptText("turn.steer", { text });
|
|
4620
4737
|
if (invalid) throw Object.assign(new Error(invalid.message), { code: invalid.code });
|
|
@@ -4869,7 +4986,9 @@ function createControlSurface(
|
|
|
4869
4986
|
retryLast: () => typed("retry.last"),
|
|
4870
4987
|
retryNow: () => typed("retry.now"),
|
|
4871
4988
|
backgroundBash: () => typed("bash.background"),
|
|
4872
|
-
installedOperations:
|
|
4989
|
+
installedOperations: imageUploads
|
|
4990
|
+
? surfacePolicy.installedControls
|
|
4991
|
+
: new Set([...surfacePolicy.installedControls].filter(operation => !operation.startsWith("turn.image."))),
|
|
4873
4992
|
revisionProvider: resource => (resource === "config" ? String(configRevision.current) : undefined),
|
|
4874
4993
|
};
|
|
4875
4994
|
}
|
|
@@ -5019,6 +5138,89 @@ function quiescingFrame(frame: Record<string, unknown>): Record<string, unknown>
|
|
|
5019
5138
|
|
|
5020
5139
|
/** Install a complete SDK host for a session when notifications are inactive. */
|
|
5021
5140
|
export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: CreateSdkSessionRuntimeOptions): void {
|
|
5141
|
+
const acceptedImages = new Map<
|
|
5142
|
+
string,
|
|
5143
|
+
{
|
|
5144
|
+
store: PromptImageUploadStore;
|
|
5145
|
+
release: () => void;
|
|
5146
|
+
handle?: string;
|
|
5147
|
+
executionStarted?: boolean;
|
|
5148
|
+
submissionPending?: boolean;
|
|
5149
|
+
awaitingAdoption?: boolean;
|
|
5150
|
+
releaseRequested?: boolean;
|
|
5151
|
+
retiring?: boolean;
|
|
5152
|
+
}
|
|
5153
|
+
>();
|
|
5154
|
+
const imageKey = (correlation: InvocationCorrelation): string => `${correlation.commandId}:${correlation.turnId}`;
|
|
5155
|
+
const retireAcceptedImage = (key: string): void => {
|
|
5156
|
+
const reservation = acceptedImages.get(key);
|
|
5157
|
+
if (!reservation) return;
|
|
5158
|
+
if ((reservation.submissionPending || reservation.awaitingAdoption) && !reservation.executionStarted) {
|
|
5159
|
+
reservation.releaseRequested = true;
|
|
5160
|
+
return;
|
|
5161
|
+
}
|
|
5162
|
+
if (reservation.retiring) return;
|
|
5163
|
+
const release = (): void => {
|
|
5164
|
+
if (acceptedImages.get(key) !== reservation) return;
|
|
5165
|
+
acceptedImages.delete(key);
|
|
5166
|
+
reservation.release();
|
|
5167
|
+
};
|
|
5168
|
+
if (!reservation.handle) {
|
|
5169
|
+
if (reservation.executionStarted) {
|
|
5170
|
+
reservation.retiring = true;
|
|
5171
|
+
logger.warn("SDK image capacity retained for execution without an exact resource handle");
|
|
5172
|
+
return;
|
|
5173
|
+
}
|
|
5174
|
+
release();
|
|
5175
|
+
return;
|
|
5176
|
+
}
|
|
5177
|
+
const waitForSettlement = options.terminalAbortSeams?.waitForRunSettlement;
|
|
5178
|
+
if (!waitForSettlement) return;
|
|
5179
|
+
reservation.retiring = true;
|
|
5180
|
+
retirePromptImageReservation(
|
|
5181
|
+
reservation.handle,
|
|
5182
|
+
waitForSettlement,
|
|
5183
|
+
() => acceptedImages.get(key) === reservation,
|
|
5184
|
+
release,
|
|
5185
|
+
);
|
|
5186
|
+
};
|
|
5187
|
+
const releaseAcceptedImage = (correlation: InvocationCorrelation): void =>
|
|
5188
|
+
retireAcceptedImage(imageKey(correlation));
|
|
5189
|
+
const settleAcceptedImageSubmission = (correlation: InvocationCorrelation): void => {
|
|
5190
|
+
const key = imageKey(correlation);
|
|
5191
|
+
const reservation = acceptedImages.get(key);
|
|
5192
|
+
if (!reservation) return;
|
|
5193
|
+
reservation.submissionPending = false;
|
|
5194
|
+
if (reservation.releaseRequested) {
|
|
5195
|
+
reservation.releaseRequested = false;
|
|
5196
|
+
retireAcceptedImage(key);
|
|
5197
|
+
}
|
|
5198
|
+
};
|
|
5199
|
+
const bindAcceptedImage = (correlation: InvocationCorrelation, handle: string | undefined): void => {
|
|
5200
|
+
const reservation = acceptedImages.get(imageKey(correlation));
|
|
5201
|
+
if (!reservation) return;
|
|
5202
|
+
reservation.awaitingAdoption = false;
|
|
5203
|
+
reservation.executionStarted = true;
|
|
5204
|
+
if (handle && reservation.handle === undefined) reservation.handle = handle;
|
|
5205
|
+
if (reservation.releaseRequested) retireAcceptedImage(imageKey(correlation));
|
|
5206
|
+
};
|
|
5207
|
+
// Promotion commits a queued image to its next own-run before agent_start supplies the exact resource handle.
|
|
5208
|
+
const awaitAcceptedImageAdoption = (correlation: InvocationCorrelation): void => {
|
|
5209
|
+
const reservation = acceptedImages.get(imageKey(correlation));
|
|
5210
|
+
if (reservation) reservation.awaitingAdoption = true;
|
|
5211
|
+
};
|
|
5212
|
+
const releaseImagesForStore = (store: PromptImageUploadStore | undefined): void => {
|
|
5213
|
+
for (const [key, reservation] of acceptedImages) {
|
|
5214
|
+
if (reservation.store !== store) continue;
|
|
5215
|
+
retireAcceptedImage(key);
|
|
5216
|
+
}
|
|
5217
|
+
};
|
|
5218
|
+
const pendingImageCleanups = new Set<PromptImageUploadStore>();
|
|
5219
|
+
const completeImageCleanups = (): void => {
|
|
5220
|
+
for (const store of pendingImageCleanups) releaseImagesForStore(store);
|
|
5221
|
+
pendingImageCleanups.clear();
|
|
5222
|
+
};
|
|
5223
|
+
let currentImageUploads: PromptImageUploadStore | undefined;
|
|
5022
5224
|
const acceptedQueueCancellations = new Map<string, AcceptedQueueCancellation>();
|
|
5023
5225
|
const disposeAcceptedQueueCancellations = async (): Promise<void> => {
|
|
5024
5226
|
const cancellations = [...acceptedQueueCancellations.values()];
|
|
@@ -5549,6 +5751,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5549
5751
|
): Promise<void> => {
|
|
5550
5752
|
const current = lifecycleOwner?.state ?? active;
|
|
5551
5753
|
if (!current) return;
|
|
5754
|
+
const imageRunHandle =
|
|
5755
|
+
type === "agent_start" && acceptedImages.size > 0
|
|
5756
|
+
? options.terminalAbortSeams?.getActivePromptHandle()
|
|
5757
|
+
: undefined;
|
|
5552
5758
|
const failureBatch = lifecycleOwner?.batch;
|
|
5553
5759
|
const adoptLifecycleBatch = (batch: LifecycleBatch | undefined): void => {
|
|
5554
5760
|
current.activeInvocation = batch?.invocations[0];
|
|
@@ -5666,6 +5872,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
5666
5872
|
adoptLifecycleBatch(batch);
|
|
5667
5873
|
for (const entry of drained) {
|
|
5668
5874
|
if (entry.kind !== "prompt") continue;
|
|
5875
|
+
bindAcceptedImage(entry.correlation, imageRunHandle);
|
|
5669
5876
|
if (current.deadlineManager.isExpiring(entry.correlation))
|
|
5670
5877
|
current.deadlineManager.captureExpiringRun(entry.correlation);
|
|
5671
5878
|
else current.deadlineManager.onRunStarted(entry.correlation);
|
|
@@ -6175,8 +6382,13 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6175
6382
|
}
|
|
6176
6383
|
if (type === "agent_end" && isContinuingMidRunMaintenanceOutcome(maintenanceOutcome)) return;
|
|
6177
6384
|
if (type === "agent_end") {
|
|
6178
|
-
for (const invocation of transitions)
|
|
6385
|
+
for (const invocation of transitions) {
|
|
6386
|
+
// A deadline can capture this event while the exact run still owns
|
|
6387
|
+
// pending tools. Its settlement owner, not event capture, releases quota.
|
|
6388
|
+
if (!current.deadlineManager.shouldDeferTerminalTransition(invocation.correlation))
|
|
6389
|
+
releaseAcceptedImage(invocation.correlation);
|
|
6179
6390
|
retireAcceptedQueueCancellation(acceptedQueueCancellations, invocation.correlation);
|
|
6391
|
+
}
|
|
6180
6392
|
if (current.lifecycleEpoch !== eventLifecycleEpoch) {
|
|
6181
6393
|
// A successor agent_start won the lifecycle race while this event's
|
|
6182
6394
|
// durable transitions were awaiting persistence. Retire the ended
|
|
@@ -6596,6 +6808,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6596
6808
|
const startRuntime = async (ctx: ExtensionContext): Promise<void> => {
|
|
6597
6809
|
if (active) return;
|
|
6598
6810
|
if (acceptedQueueCancellations.size > 0) await disposeAcceptedQueueCancellations();
|
|
6811
|
+
completeImageCleanups();
|
|
6599
6812
|
if (active) return;
|
|
6600
6813
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
6601
6814
|
const stateRoot = path.join(ctx.cwd, ".gjc", "state");
|
|
@@ -6947,6 +7160,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
6947
7160
|
await autosaveWorktreeOnPromptDeadline(ctx.cwd, options.settings, { isCurrent, signal });
|
|
6948
7161
|
},
|
|
6949
7162
|
onExpired: correlation => {
|
|
7163
|
+
// The manager invokes retirement only after confirmed settlement and
|
|
7164
|
+
// durable terminalization (including recovered queue removals), never
|
|
7165
|
+
// merely because an inactivity timer fired or execution is uncertain.
|
|
7166
|
+
releaseAcceptedImage(correlation);
|
|
6950
7167
|
retireAcceptedQueueCancellation(acceptedQueueCancellations, correlation, true);
|
|
6951
7168
|
const owner = lifecycleOwnerHolder.state;
|
|
6952
7169
|
if (owner) {
|
|
@@ -7011,6 +7228,9 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7011
7228
|
ctx,
|
|
7012
7229
|
id: sessionId,
|
|
7013
7230
|
api,
|
|
7231
|
+
imageUploadsAvailable:
|
|
7232
|
+
typeof transport.isConnectionOpen === "function" &&
|
|
7233
|
+
typeof options.terminalAbortSeams?.waitForRunSettlement === "function",
|
|
7014
7234
|
reconciliation,
|
|
7015
7235
|
turnResultLookup: selector => reconciliation.lookupResult(selector.kind, selector),
|
|
7016
7236
|
steerStatusLookup: selector => steerReconciliation.lookupSteer(selector),
|
|
@@ -7141,7 +7361,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7141
7361
|
});
|
|
7142
7362
|
// 3. Release recovery ownership ONLY after durable terminalization.
|
|
7143
7363
|
deadlineManager.clear(correlation);
|
|
7144
|
-
if (kind === "prompt")
|
|
7364
|
+
if (kind === "prompt") {
|
|
7365
|
+
releaseAcceptedImage(correlation);
|
|
7366
|
+
retireAcceptedQueueCancellation(acceptedQueueCancellations, correlation);
|
|
7367
|
+
}
|
|
7145
7368
|
return true;
|
|
7146
7369
|
} catch (transitionError) {
|
|
7147
7370
|
// Keep prompt recovery leased; skill recovery has no prompt lease.
|
|
@@ -7159,6 +7382,14 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7159
7382
|
return attempt(3);
|
|
7160
7383
|
};
|
|
7161
7384
|
|
|
7385
|
+
// This is a private SDK transport facility. A frame-observer cache cannot
|
|
7386
|
+
// establish that the authenticated connection still exists at redemption.
|
|
7387
|
+
const imageUploads =
|
|
7388
|
+
typeof transport.isConnectionOpen === "function" &&
|
|
7389
|
+
typeof options.terminalAbortSeams?.waitForRunSettlement === "function"
|
|
7390
|
+
? new PromptImageUploadStore(connectionId => transport.isConnectionOpen?.(connectionId) === true)
|
|
7391
|
+
: undefined;
|
|
7392
|
+
currentImageUploads = imageUploads;
|
|
7162
7393
|
const controlSurface = createControlSurface(
|
|
7163
7394
|
ctx,
|
|
7164
7395
|
api,
|
|
@@ -7195,7 +7426,13 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7195
7426
|
},
|
|
7196
7427
|
correlation => deadlineManager.onAccepted(correlation),
|
|
7197
7428
|
steerReconciliation,
|
|
7429
|
+
imageUploads,
|
|
7198
7430
|
(kind, correlation, connectionId, sdkRunToken, promotion) => {
|
|
7431
|
+
if (!promotion?.removed) {
|
|
7432
|
+
if (promotion?.startsOwnRun === false)
|
|
7433
|
+
bindAcceptedImage(correlation, options.terminalAbortSeams?.getActivePromptHandle());
|
|
7434
|
+
else awaitAcceptedImageAdoption(correlation);
|
|
7435
|
+
}
|
|
7199
7436
|
const bindPromotedToken = (batch?: LifecycleBatch): void => {
|
|
7200
7437
|
const owner = lifecycleOwnerHolder.state;
|
|
7201
7438
|
if (sdkRunToken && owner)
|
|
@@ -7251,6 +7488,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7251
7488
|
// with it.
|
|
7252
7489
|
const current = active;
|
|
7253
7490
|
if (current?.lifecycleActive) {
|
|
7491
|
+
bindAcceptedImage(correlation, options.terminalAbortSeams?.getActivePromptHandle());
|
|
7254
7492
|
// If this correlation was already admitted as own-run via the
|
|
7255
7493
|
// idle snapshot (onAccepted -> pending) but later diverted to
|
|
7256
7494
|
// steering and consumed in-run, move it from pending to the
|
|
@@ -7411,7 +7649,47 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7411
7649
|
throw error;
|
|
7412
7650
|
}
|
|
7413
7651
|
},
|
|
7652
|
+
(correlation, release) => {
|
|
7653
|
+
if (imageUploads)
|
|
7654
|
+
acceptedImages.set(imageKey(correlation), {
|
|
7655
|
+
store: imageUploads,
|
|
7656
|
+
release,
|
|
7657
|
+
submissionPending: true,
|
|
7658
|
+
});
|
|
7659
|
+
},
|
|
7660
|
+
settleAcceptedImageSubmission,
|
|
7661
|
+
releaseAcceptedImage,
|
|
7414
7662
|
acceptedQueueCancellations,
|
|
7663
|
+
(kind, correlation, failure) => {
|
|
7664
|
+
const record = reconciliation.lookup(kind, correlation) as { status?: string };
|
|
7665
|
+
const reservation = acceptedImages.get(imageKey(correlation));
|
|
7666
|
+
const terminal = record.status === "terminal_ok" || record.status === "failed";
|
|
7667
|
+
if (record.status !== "accepted" && !(terminal && reservation)) return false;
|
|
7668
|
+
// A known execution/handle retains exact-ledger authority even if a
|
|
7669
|
+
// contradictory callback arrives later. Never downgrade adoption.
|
|
7670
|
+
if (reservation?.executionStarted || reservation?.handle) return false;
|
|
7671
|
+
const adopted = active?.openLifecycleBatches.some(batch =>
|
|
7672
|
+
[...batch.invocations, ...batch.attachedInvocations].some(
|
|
7673
|
+
entry => entry.kind === kind && imageKey(entry.correlation) === imageKey(correlation),
|
|
7674
|
+
),
|
|
7675
|
+
);
|
|
7676
|
+
if (adopted) return false;
|
|
7677
|
+
const index = pending.findIndex(entry => entry.kind === kind && entry.correlation === correlation);
|
|
7678
|
+
if (index >= 0) {
|
|
7679
|
+
const [removed] = pending.splice(index, 1);
|
|
7680
|
+
if (removed) lifecycleRunOwners.delete(removed.sdkRunToken);
|
|
7681
|
+
}
|
|
7682
|
+
if (reservation) {
|
|
7683
|
+
reservation.awaitingAdoption = false;
|
|
7684
|
+
// Ordinary retirement still waits for an outstanding enqueue producer.
|
|
7685
|
+
retireAcceptedImage(imageKey(correlation));
|
|
7686
|
+
}
|
|
7687
|
+
// Keep the existing durable-first failure/recovery owner; this is not
|
|
7688
|
+
// queue removal, and cannot publish cancellation or synthetic success.
|
|
7689
|
+
if (!terminal)
|
|
7690
|
+
void terminalizeAbandonedSubmission(kind, correlation, rejectionRecoveryIntent(failure.error));
|
|
7691
|
+
return true;
|
|
7692
|
+
},
|
|
7415
7693
|
);
|
|
7416
7694
|
const installProviderDefinitions = (capability: string, definitions: unknown): void => {
|
|
7417
7695
|
if (capability === "permission") {
|
|
@@ -7479,6 +7757,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7479
7757
|
};
|
|
7480
7758
|
runtime = new SessionSdkSessionRuntime({
|
|
7481
7759
|
transport,
|
|
7760
|
+
onDisconnected: connectionId => imageUploads?.disconnect(connectionId),
|
|
7482
7761
|
eventRevision: () =>
|
|
7483
7762
|
typeof (ctx as Partial<ExtensionContext>).getTranscript === "function"
|
|
7484
7763
|
? ctx.getTranscript().length
|
|
@@ -7929,6 +8208,7 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7929
8208
|
} catch (error) {
|
|
7930
8209
|
runtimeOwner.quiesceInput();
|
|
7931
8210
|
runtimeOwner.fenceGateResolutions();
|
|
8211
|
+
imageUploads?.close();
|
|
7932
8212
|
active = undefined;
|
|
7933
8213
|
let queueCancellationFailure: unknown;
|
|
7934
8214
|
try {
|
|
@@ -7987,7 +8267,8 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7987
8267
|
}
|
|
7988
8268
|
cursors.close();
|
|
7989
8269
|
await revisions.close().catch(() => undefined);
|
|
7990
|
-
if (queueCancellationFailure !== undefined)
|
|
8270
|
+
if (queueCancellationFailure !== undefined) {
|
|
8271
|
+
if (imageUploads) pendingImageCleanups.add(imageUploads);
|
|
7991
8272
|
throw Object.assign(
|
|
7992
8273
|
new AggregateError(
|
|
7993
8274
|
[error, queueCancellationFailure],
|
|
@@ -7995,14 +8276,25 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
7995
8276
|
),
|
|
7996
8277
|
{ code: "sdk_reconciliation_teardown_failed" },
|
|
7997
8278
|
);
|
|
8279
|
+
}
|
|
8280
|
+
if (currentImageUploads === imageUploads) currentImageUploads = undefined;
|
|
8281
|
+
releaseImagesForStore(imageUploads);
|
|
7998
8282
|
throw error;
|
|
7999
8283
|
}
|
|
8000
8284
|
};
|
|
8001
8285
|
const stopActive = async (cancelSkillRecovery = false, unregisterReason?: "detached_idle"): Promise<void> => {
|
|
8002
8286
|
const current = active;
|
|
8003
|
-
if (!current)
|
|
8287
|
+
if (!current) {
|
|
8288
|
+
if (pendingImageCleanups.size > 0) {
|
|
8289
|
+
await disposeAcceptedQueueCancellations();
|
|
8290
|
+
completeImageCleanups();
|
|
8291
|
+
}
|
|
8292
|
+
return;
|
|
8293
|
+
}
|
|
8004
8294
|
current.quiesceInput();
|
|
8005
8295
|
current.fenceGateResolutions();
|
|
8296
|
+
const imageUploads = currentImageUploads;
|
|
8297
|
+
imageUploads?.close();
|
|
8006
8298
|
let queueCancellationFailure: unknown;
|
|
8007
8299
|
try {
|
|
8008
8300
|
await disposeAcceptedQueueCancellations();
|
|
@@ -8068,6 +8360,10 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
|
|
|
8068
8360
|
allowLockContention: cancelSkillRecovery,
|
|
8069
8361
|
...(unregisterReason === undefined ? {} : { unregisterReason }),
|
|
8070
8362
|
});
|
|
8363
|
+
if (queueCancellationFailure === undefined) {
|
|
8364
|
+
if (currentImageUploads === imageUploads) currentImageUploads = undefined;
|
|
8365
|
+
releaseImagesForStore(imageUploads);
|
|
8366
|
+
} else if (imageUploads) pendingImageCleanups.add(imageUploads);
|
|
8071
8367
|
} catch (error) {
|
|
8072
8368
|
// Keep the immutable owner available for a retry when transport teardown
|
|
8073
8369
|
// fails after quiescing. Clearing `active` before stop prevents a second
|