@gajae-code/coding-agent 0.15.5 → 0.16.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 +85 -1
- package/dist/types/async/job-manager.d.ts +83 -0
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/auth-gateway-cli.d.ts +17 -0
- package/dist/types/cli/model-presets-cli.d.ts +9 -0
- package/dist/types/cli/root-flags.d.ts +14 -0
- package/dist/types/cli/update-cli.d.ts +15 -0
- package/dist/types/cli.d.ts +7 -0
- package/dist/types/commands/auth-gateway.d.ts +3 -0
- package/dist/types/commands/gc.d.ts +13 -0
- package/dist/types/commands/launch.d.ts +10 -0
- package/dist/types/commands/model-presets.d.ts +21 -0
- package/dist/types/config/autorouting-tier-map.d.ts +120 -0
- package/dist/types/config/file-lock.d.ts +54 -2
- package/dist/types/config/internal/model-preset-registry-test-state.d.ts +4 -0
- package/dist/types/config/internal/model-preset-registry-test-support.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +3 -2
- package/dist/types/config/model-preset-registry.d.ts +368 -0
- package/dist/types/config/model-profile-activation.d.ts +39 -0
- package/dist/types/config/model-profiles.d.ts +5 -3
- package/dist/types/config/model-registry.d.ts +17 -5
- package/dist/types/config/settings-schema.d.ts +19 -1
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -5
- package/dist/types/coordinator-mcp/projection-scan.d.ts +54 -0
- package/dist/types/crash/record-loader.d.ts +15 -0
- package/dist/types/extensibility/extensions/types.d.ts +2 -5
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +58 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +35 -6
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +72 -1
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/main.d.ts +3 -1
- package/dist/types/master-mode/context.d.ts +21 -0
- package/dist/types/master-mode/first-request.d.ts +34 -0
- package/dist/types/master-mode/snapshot.d.ts +32 -0
- package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
- package/dist/types/modes/components/jobs-overlay-model.d.ts +4 -2
- package/dist/types/modes/components/oauth-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
- package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
- package/dist/types/modes/components/status-line/segments.d.ts +10 -0
- package/dist/types/modes/jobs-observer.d.ts +36 -0
- package/dist/types/modes/print-mode.d.ts +17 -0
- package/dist/types/modes/ui-language.d.ts +7 -1
- package/dist/types/sdk/broker/broker.d.ts +80 -1
- package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
- package/dist/types/sdk/broker/index.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +26 -0
- package/dist/types/sdk/broker/master-capability.d.ts +10 -0
- package/dist/types/sdk/broker/session-index.d.ts +40 -11
- package/dist/types/sdk/broker/session-scope.d.ts +72 -0
- package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
- package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
- package/dist/types/sdk/broker/state-version.d.ts +4 -1
- package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
- package/dist/types/sdk/bus/index.d.ts +6 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +3 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +4 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/master-cli.d.ts +63 -0
- package/dist/types/sdk/cli/rows.d.ts +4 -3
- package/dist/types/sdk/cli/session-cli.d.ts +18 -2
- package/dist/types/sdk/client/client.d.ts +3 -2
- package/dist/types/sdk/client/discovery.d.ts +1 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +2 -0
- package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
- package/dist/types/sdk/host/host.d.ts +10 -0
- package/dist/types/sdk/host/model-pin.d.ts +7 -2
- package/dist/types/sdk/host/sdk-run-capability.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +67 -1
- package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
- package/dist/types/sdk/lifecycle/service.d.ts +15 -3
- package/dist/types/sdk/lifecycle-session.d.ts +2 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +11 -1
- package/dist/types/sdk/protocol/adapter-validation.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +16 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -1
- package/dist/types/session/account-inventory.d.ts +1 -0
- package/dist/types/session/agent-session.d.ts +24 -7
- package/dist/types/session/credential-store-errors.d.ts +1 -0
- package/dist/types/session/fold-coordinator.d.ts +201 -0
- package/dist/types/session/sdk-run-capability-internal.d.ts +7 -0
- package/dist/types/session/sdk-run-capability.d.ts +2 -0
- package/dist/types/session/session-manager.d.ts +65 -0
- package/dist/types/session/yield-queue.d.ts +5 -4
- package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
- package/dist/types/telemetry/control.d.ts +11 -0
- package/dist/types/telemetry/events.d.ts +20 -0
- package/dist/types/telemetry/index.d.ts +3 -0
- package/dist/types/telemetry/transport.d.ts +22 -0
- package/dist/types/tools/bash-interactive.d.ts +23 -0
- package/dist/types/tools/bash.d.ts +4 -0
- package/dist/types/tools/gh.d.ts +8 -0
- package/dist/types/tools/index.d.ts +10 -5
- package/dist/types/web/search/providers/base.d.ts +9 -4
- package/dist/types/web/search/providers/xai.d.ts +2 -1
- package/dist/types/web/search/types.d.ts +14 -0
- package/package.json +10 -7
- package/scripts/generate-hotkeys-docs.ts +4 -0
- package/scripts/generate-sdk-operation-inventory.ts +9 -3
- package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
- package/src/async/job-manager.ts +671 -39
- package/src/cli/accounts-cli.ts +9 -5
- package/src/cli/args.ts +17 -0
- package/src/cli/auth-broker-cli.ts +40 -12
- package/src/cli/auth-gateway-cli.ts +273 -97
- package/src/cli/config-cli.ts +2 -2
- package/src/cli/fast-help.ts +4 -0
- package/src/cli/model-presets-cli.ts +100 -0
- package/src/cli/root-flags.ts +5 -0
- package/src/cli/update-cli.ts +117 -16
- package/src/cli.ts +6 -2
- package/src/commands/auth-gateway.ts +12 -8
- package/src/commands/gc.ts +19 -0
- package/src/commands/launch.ts +86 -18
- package/src/commands/model-presets.ts +24 -0
- package/src/commands/sdk.ts +113 -21
- package/src/config/autorouting-tier-map.ts +44 -0
- package/src/config/file-lock-gc.ts +9 -4
- package/src/config/file-lock.ts +890 -99
- package/src/config/internal/model-preset-registry-test-state.ts +28 -0
- package/src/config/internal/model-preset-registry-test-support.ts +11 -0
- package/src/config/keybindings.ts +6 -2
- package/src/config/model-preset-registry.ts +2795 -0
- package/src/config/model-profile-activation.ts +115 -28
- package/src/config/model-profile-contract.ts +4 -1
- package/src/config/model-profiles.ts +56 -14
- package/src/config/model-registry.ts +1044 -122
- package/src/config/model-resolver.ts +5 -0
- package/src/config/settings-schema.ts +19 -1
- package/src/config/settings.ts +11 -1
- package/src/coordinator-mcp/model-preset.ts +32 -17
- package/src/coordinator-mcp/projection-scan.ts +592 -0
- package/src/coordinator-mcp/question-state.ts +9 -11
- package/src/coordinator-mcp/server.ts +111 -42
- package/src/coordinator-mcp/session-reaper.ts +9 -3
- package/src/crash/index-store.ts +2 -0
- package/src/crash/record-loader.ts +10 -0
- package/src/extensibility/extensions/loader.ts +0 -1
- package/src/extensibility/extensions/types.ts +2 -3
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +1 -0
- package/src/gjc-runtime/empty-delete-gc.ts +301 -0
- package/src/gjc-runtime/gc-render.ts +29 -0
- package/src/gjc-runtime/gc-runtime.ts +174 -11
- package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
- package/src/gjc-runtime/session-state-lock.ts +1321 -147
- package/src/gjc-runtime/session-state-sidecar.ts +1004 -25
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -2
- package/src/gjc-runtime/tmux-sessions.ts +252 -13
- package/src/hooks/skill-state.ts +6 -0
- package/src/index.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/internal-urls/issue-pr-protocol.ts +4 -2
- package/src/lsp/config.ts +4 -3
- package/src/lsp/path-trust.ts +12 -6
- package/src/main.ts +66 -4
- package/src/master-mode/context.ts +50 -0
- package/src/master-mode/first-request.ts +65 -0
- package/src/master-mode/snapshot.ts +205 -0
- package/src/modes/acp/acp-agent.ts +616 -161
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
- package/src/modes/components/jobs-overlay-model.ts +99 -16
- package/src/modes/components/model-selector.ts +158 -18
- package/src/modes/components/oauth-selector.ts +6 -1
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/model-name.ts +53 -0
- package/src/modes/components/status-line/priority-row.ts +107 -0
- package/src/modes/components/status-line/segments.ts +29 -10
- package/src/modes/components/tool-status-header.ts +151 -15
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +51 -6
- package/src/modes/controllers/selector-controller.ts +191 -96
- package/src/modes/interactive-mode.ts +14 -1
- package/src/modes/jobs-observer.ts +149 -16
- package/src/modes/print-mode.ts +64 -0
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/ui-language.ts +49 -2
- package/src/modes/utils/injected-user-submission.ts +15 -1
- package/src/prompts/system/master-mode.md +28 -0
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/fold-receipt.md +3 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +17 -2
- package/src/sdk/broker/broker.ts +1671 -34
- package/src/sdk/broker/endpoint-authority.ts +86 -0
- package/src/sdk/broker/ensure.ts +5 -0
- package/src/sdk/broker/index.ts +3 -0
- package/src/sdk/broker/lifecycle.ts +217 -46
- package/src/sdk/broker/master-capability.ts +232 -0
- package/src/sdk/broker/session-index.ts +121 -17
- package/src/sdk/broker/session-scope.ts +272 -0
- package/src/sdk/broker/spawn-authority.ts +1031 -0
- package/src/sdk/broker/spawn-substrate.ts +548 -0
- package/src/sdk/broker/state-version.ts +26 -14
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/broker/worktree-occupancy.ts +90 -0
- package/src/sdk/bus/chat-daemon-control.ts +5 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +46 -5
- package/src/sdk/bus/index.ts +51 -4
- package/src/sdk/bus/kind-aware-reconciliation.ts +36 -6
- package/src/sdk/bus/prompt-reconciliation.ts +28 -4
- package/src/sdk/bus/slack-conversation.ts +5 -1
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/master-cli.ts +259 -0
- package/src/sdk/cli/rows.ts +11 -8
- package/src/sdk/cli/session-cli.ts +276 -6
- package/src/sdk/client/client.ts +14 -8
- package/src/sdk/client/discovery.ts +3 -1
- package/src/sdk/host/control/dispatch.ts +101 -37
- package/src/sdk/host/control/index.ts +2 -0
- package/src/sdk/host/control/operations.ts +2 -0
- package/src/sdk/host/control/runtime-gate.ts +203 -11
- package/src/sdk/host/host.ts +27 -2
- package/src/sdk/host/model-pin.ts +185 -25
- package/src/sdk/host/sdk-run-capability.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +581 -43
- package/src/sdk/host/session-runtime.ts +655 -101
- package/src/sdk/lifecycle/broker-client.ts +28 -0
- package/src/sdk/lifecycle/service.ts +279 -20
- package/src/sdk/lifecycle-session.ts +10 -2
- package/src/sdk/prompt-deadline-manager.ts +16 -1
- package/src/sdk/protocol/adapter-validation.ts +27 -0
- package/src/sdk/protocol/operation-inventory.generated.json +53 -2
- package/src/sdk/protocol/operation-registry.ts +17 -4
- package/src/sdk/router/session-router.ts +294 -154
- package/src/sdk/session.ts +440 -167
- package/src/sdk/startup-capability.ts +1 -0
- package/src/session/account-inventory.ts +27 -11
- package/src/session/agent-session.ts +1292 -180
- package/src/session/blob-store.ts +15 -5
- package/src/session/credential-store-errors.ts +2 -0
- package/src/session/fold-coordinator.ts +433 -0
- package/src/session/internal/managed-session-storage.ts +153 -78
- package/src/session/sdk-run-capability-internal.ts +15 -0
- package/src/session/sdk-run-capability.ts +2 -0
- package/src/session/session-manager.ts +342 -28
- package/src/session/yield-queue.ts +24 -10
- package/src/setup/frictionless-onboarding.ts +2 -0
- package/src/setup/provider-presets.json +1 -1
- package/src/slash-commands/helpers/credential-switch.ts +6 -1
- package/src/task/executor.ts +19 -3
- package/src/telemetry/control.ts +26 -0
- package/src/telemetry/events.ts +1268 -0
- package/src/telemetry/index.ts +3 -0
- package/src/telemetry/transport.ts +87 -0
- package/src/tools/bash-interactive.ts +182 -85
- package/src/tools/bash.ts +1028 -109
- package/src/tools/gh.ts +104 -48
- package/src/tools/github-cache.ts +2 -2
- package/src/tools/index.ts +11 -5
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/web/search/provider.ts +120 -11
- package/src/web/search/providers/anthropic.ts +89 -17
- package/src/web/search/providers/base.ts +9 -4
- package/src/web/search/providers/gemini.ts +87 -15
- package/src/web/search/providers/openai-compatible.ts +23 -8
- package/src/web/search/providers/xai.ts +98 -12
- package/src/web/search/types.ts +16 -0
|
@@ -51,3 +51,31 @@ export class AgentDirSessionLifecycleClient implements SessionLifecycleClient {
|
|
|
51
51
|
export function createBrokerSessionLifecycleService(agentDir: string): SessionLifecycleService {
|
|
52
52
|
return new SessionLifecycleService(new AgentDirSessionLifecycleClient(agentDir));
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Dedicated dispatch for the local master `session.spawn` surface. It shares
|
|
57
|
+
* the lifecycle client's credential boundary but bypasses the generic mutation
|
|
58
|
+
* outcome types: spawn responses are validated by the caller's allowlist.
|
|
59
|
+
*/
|
|
60
|
+
export async function dispatchSpawnGlobal(
|
|
61
|
+
agentDir: string,
|
|
62
|
+
input: Record<string, unknown>,
|
|
63
|
+
idempotencyKey: string,
|
|
64
|
+
timeoutMs: number,
|
|
65
|
+
): Promise<unknown> {
|
|
66
|
+
await ensureBroker({ agentDir });
|
|
67
|
+
const discovery = await readSdkBrokerDiscovery(agentDir);
|
|
68
|
+
if (!discovery) throw new Error("SDK broker discovery is unavailable.");
|
|
69
|
+
const client = await SdkClient.connect(discovery.url, discovery.token, { timeoutMs });
|
|
70
|
+
try {
|
|
71
|
+
return await client.global("session.spawn", input, { idempotencyKey, timeoutMs });
|
|
72
|
+
} finally {
|
|
73
|
+
try {
|
|
74
|
+
await client.close();
|
|
75
|
+
} catch (error) {
|
|
76
|
+
logger.warn("SDK spawn client cleanup failed", {
|
|
77
|
+
error: error instanceof Error ? error.message : String(error),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
resolvedScopeV1,
|
|
5
|
+
resolveScopeRequest,
|
|
6
|
+
type ScopeRequestV1,
|
|
7
|
+
type SdkSearchResultV1,
|
|
8
|
+
type SdkSearchRowV1,
|
|
9
|
+
scopeRequestV1,
|
|
10
|
+
searchRowV1,
|
|
11
|
+
} from "../broker/session-scope";
|
|
3
12
|
import {
|
|
4
13
|
SessionListTraversalError,
|
|
5
14
|
type SessionListTraversalPage,
|
|
@@ -141,6 +150,9 @@ export interface SessionDeleteTarget {
|
|
|
141
150
|
export interface SessionListTarget {
|
|
142
151
|
readonly cwd?: string;
|
|
143
152
|
readonly resolveSessionId?: string;
|
|
153
|
+
readonly scope?: ScopeRequestV1;
|
|
154
|
+
readonly limit?: number;
|
|
155
|
+
readonly cursor?: string;
|
|
144
156
|
}
|
|
145
157
|
|
|
146
158
|
interface SessionLifecycleMutationRequestBase<
|
|
@@ -197,7 +209,8 @@ export interface SessionLifecycleListEntry {
|
|
|
197
209
|
readonly live?: boolean;
|
|
198
210
|
readonly endpointGeneration?: number;
|
|
199
211
|
readonly terminalUncertain?: boolean;
|
|
200
|
-
readonly
|
|
212
|
+
readonly cwd?: string;
|
|
213
|
+
readonly locator?: { readonly cwd: string; readonly worktreeRoot: string | null; readonly stateRoot: string };
|
|
201
214
|
}
|
|
202
215
|
|
|
203
216
|
export interface SessionLifecycleListResult {
|
|
@@ -207,6 +220,8 @@ export interface SessionLifecycleListResult {
|
|
|
207
220
|
readonly savedSession?: SessionLifecycleSavedSession;
|
|
208
221
|
}
|
|
209
222
|
|
|
223
|
+
export type SessionScopedListResult = SdkSearchResultV1;
|
|
224
|
+
|
|
210
225
|
export interface SessionCreateResult {
|
|
211
226
|
readonly ok: true;
|
|
212
227
|
readonly operation: "session.create";
|
|
@@ -240,7 +255,7 @@ export interface SessionReconcileUncertainResult {
|
|
|
240
255
|
export interface SessionListSuccessResult {
|
|
241
256
|
readonly ok: true;
|
|
242
257
|
readonly operation: "session.list";
|
|
243
|
-
readonly result: SessionLifecycleListResult;
|
|
258
|
+
readonly result: SessionLifecycleListResult | SessionScopedListResult;
|
|
244
259
|
}
|
|
245
260
|
|
|
246
261
|
export interface SessionLifecycleError {
|
|
@@ -289,6 +304,7 @@ export type SessionListFailure = {
|
|
|
289
304
|
readonly operation: "session.list";
|
|
290
305
|
readonly certainty: SessionLifecycleCertainty;
|
|
291
306
|
readonly error: SessionLifecycleError;
|
|
307
|
+
readonly result?: SessionScopedListResult;
|
|
292
308
|
};
|
|
293
309
|
|
|
294
310
|
export type SessionCreateOutcome = SessionCreateResult | SessionCreateFailure;
|
|
@@ -582,12 +598,24 @@ function listResult(value: unknown): SessionLifecycleListResult | undefined {
|
|
|
582
598
|
live?: boolean;
|
|
583
599
|
endpointGeneration?: number;
|
|
584
600
|
terminalUncertain?: boolean;
|
|
585
|
-
|
|
601
|
+
cwd?: string;
|
|
602
|
+
locator?: { cwd: string; worktreeRoot: string | null; stateRoot: string };
|
|
586
603
|
} = { sessionId: entry.sessionId };
|
|
587
604
|
if (typeof entry.live === "boolean") item.live = entry.live;
|
|
588
605
|
if (typeof entry.endpointGeneration === "number") item.endpointGeneration = entry.endpointGeneration;
|
|
589
606
|
if (typeof entry.terminalUncertain === "boolean") item.terminalUncertain = entry.terminalUncertain;
|
|
590
|
-
if (isRecord(entry.locator) && typeof entry.locator.
|
|
607
|
+
if (isRecord(entry.locator) && typeof entry.locator.cwd === "string") item.cwd = entry.locator.cwd;
|
|
608
|
+
if (
|
|
609
|
+
isRecord(entry.locator) &&
|
|
610
|
+
typeof entry.locator.cwd === "string" &&
|
|
611
|
+
(entry.locator.worktreeRoot === null || typeof entry.locator.worktreeRoot === "string") &&
|
|
612
|
+
typeof entry.locator.stateRoot === "string"
|
|
613
|
+
)
|
|
614
|
+
item.locator = {
|
|
615
|
+
cwd: entry.locator.cwd,
|
|
616
|
+
worktreeRoot: entry.locator.worktreeRoot,
|
|
617
|
+
stateRoot: entry.locator.stateRoot,
|
|
618
|
+
};
|
|
591
619
|
sessions.push(item);
|
|
592
620
|
}
|
|
593
621
|
const warnings: string[] = [];
|
|
@@ -600,10 +628,40 @@ function listResult(value: unknown): SessionLifecycleListResult | undefined {
|
|
|
600
628
|
if (savedSessionPresent && !savedSession) return undefined;
|
|
601
629
|
return { indexSeq, sessions, warnings, ...(savedSession ? { savedSession } : {}) };
|
|
602
630
|
}
|
|
631
|
+
|
|
632
|
+
function scopedPage(value: unknown): { scope: SdkSearchResultV1["scope"]; observedAt: string } | undefined {
|
|
633
|
+
if (!isRecord(value)) return undefined;
|
|
634
|
+
const scope = resolvedScopeV1(value.scope);
|
|
635
|
+
return scope && typeof value.observedAt === "string" ? { scope, observedAt: value.observedAt } : undefined;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function scopedSearchRows(sessions: readonly SessionLifecycleListEntry[]): SdkSearchRowV1[] | undefined {
|
|
639
|
+
const rows: SdkSearchRowV1[] = [];
|
|
640
|
+
for (const session of sessions) {
|
|
641
|
+
const locator = session.locator;
|
|
642
|
+
if (
|
|
643
|
+
locator === undefined ||
|
|
644
|
+
typeof locator.cwd !== "string" ||
|
|
645
|
+
(locator.worktreeRoot !== null && typeof locator.worktreeRoot !== "string") ||
|
|
646
|
+
typeof locator.stateRoot !== "string"
|
|
647
|
+
)
|
|
648
|
+
return undefined;
|
|
649
|
+
rows.push(
|
|
650
|
+
searchRowV1({
|
|
651
|
+
sessionId: session.sessionId,
|
|
652
|
+
locator: { cwd: locator.cwd, worktreeRoot: locator.worktreeRoot, stateRoot: locator.stateRoot },
|
|
653
|
+
live: session.live === true,
|
|
654
|
+
}),
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
return rows;
|
|
658
|
+
}
|
|
603
659
|
type LifecycleListPage = {
|
|
604
660
|
readonly result: SessionLifecycleListResult;
|
|
605
661
|
readonly sessions: readonly SessionLifecycleListEntry[];
|
|
606
662
|
readonly continuationCursor?: unknown;
|
|
663
|
+
readonly scope?: SdkSearchResultV1["scope"];
|
|
664
|
+
readonly observedAt?: string;
|
|
607
665
|
};
|
|
608
666
|
|
|
609
667
|
function brokerError(value: unknown): { code: string; message: string } | undefined {
|
|
@@ -647,6 +705,24 @@ function brokerErrorFromThrown(value: unknown): { code: string; message: string;
|
|
|
647
705
|
return { code, message, ...(requestSent === undefined ? {} : { requestSent }) };
|
|
648
706
|
}
|
|
649
707
|
|
|
708
|
+
function unavailableScopedList(scope: SdkSearchResultV1["scope"]): SessionListFailure {
|
|
709
|
+
return {
|
|
710
|
+
ok: false,
|
|
711
|
+
operation: "session.list",
|
|
712
|
+
certainty: "retryable",
|
|
713
|
+
error: { code: "unavailable", message: "broker search is unavailable" },
|
|
714
|
+
result: {
|
|
715
|
+
version: 1,
|
|
716
|
+
scope,
|
|
717
|
+
status: "unavailable",
|
|
718
|
+
observedAt: new Date().toISOString(),
|
|
719
|
+
rows: [],
|
|
720
|
+
warnings: [],
|
|
721
|
+
error: { code: "unavailable", message: "broker search is unavailable" },
|
|
722
|
+
},
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
650
726
|
const TRANSPORT_ERROR_CODES = new Set([
|
|
651
727
|
"timeout",
|
|
652
728
|
"connection_closed",
|
|
@@ -678,7 +754,110 @@ export class SessionLifecycleService {
|
|
|
678
754
|
this.#client = client;
|
|
679
755
|
}
|
|
680
756
|
|
|
681
|
-
async
|
|
757
|
+
async scopedList(scope: ScopeRequestV1, limit?: number, cursor?: string): Promise<SessionListOutcome> {
|
|
758
|
+
return await this.#list(
|
|
759
|
+
{
|
|
760
|
+
actor: { id: "gjc-sdk-search-cli", namespace: "sdk:search-cli" },
|
|
761
|
+
capability: "session.list",
|
|
762
|
+
target: {
|
|
763
|
+
scope,
|
|
764
|
+
...(limit === undefined ? {} : { limit }),
|
|
765
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
true,
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
async #singleScopedList(
|
|
773
|
+
request: Omit<SessionListRequest, "operation">,
|
|
774
|
+
target: Readonly<Record<string, unknown>>,
|
|
775
|
+
locallyResolvedScope: SdkSearchResultV1["scope"],
|
|
776
|
+
): Promise<SessionListOutcome> {
|
|
777
|
+
let response: unknown;
|
|
778
|
+
try {
|
|
779
|
+
response = await this.#client.global(
|
|
780
|
+
"session.list",
|
|
781
|
+
{ ...target },
|
|
782
|
+
{
|
|
783
|
+
...(request.timeoutMs === undefined ? {} : { timeoutMs: request.timeoutMs }),
|
|
784
|
+
},
|
|
785
|
+
);
|
|
786
|
+
} catch (thrown) {
|
|
787
|
+
const error = brokerError(thrown) ?? brokerErrorFromThrown(thrown);
|
|
788
|
+
if (!error || error.code === "unavailable") return unavailableScopedList(locallyResolvedScope);
|
|
789
|
+
return failure("session.list", certaintyForThrownError(error), error.code, error.message);
|
|
790
|
+
}
|
|
791
|
+
const error = brokerError(response);
|
|
792
|
+
if (error) {
|
|
793
|
+
if (error.code === "unavailable") return unavailableScopedList(locallyResolvedScope);
|
|
794
|
+
return failure("session.list", certaintyForBrokerCode(error.code), error.code, error.message);
|
|
795
|
+
}
|
|
796
|
+
if (!isRecord(response) || response.ok !== true)
|
|
797
|
+
return failure(
|
|
798
|
+
"session.list",
|
|
799
|
+
"uncertain",
|
|
800
|
+
"malformed_response",
|
|
801
|
+
"lifecycle broker returned a malformed scoped list result",
|
|
802
|
+
);
|
|
803
|
+
const page = sessionListPageFromResponse(response);
|
|
804
|
+
const result = page === undefined ? undefined : listResult(page);
|
|
805
|
+
const pageScope = page === undefined ? undefined : scopedPage(page);
|
|
806
|
+
if (page === undefined || !result || !pageScope)
|
|
807
|
+
return failure(
|
|
808
|
+
"session.list",
|
|
809
|
+
"uncertain",
|
|
810
|
+
"malformed_response",
|
|
811
|
+
"lifecycle broker returned a malformed scoped list result",
|
|
812
|
+
);
|
|
813
|
+
if (JSON.stringify(pageScope.scope) !== JSON.stringify(locallyResolvedScope))
|
|
814
|
+
return failure(
|
|
815
|
+
"session.list",
|
|
816
|
+
"uncertain",
|
|
817
|
+
"scope_observation_drift",
|
|
818
|
+
"lifecycle broker scope does not match the locally resolved request scope",
|
|
819
|
+
);
|
|
820
|
+
const continuationCursor = page.continuationCursor;
|
|
821
|
+
if (
|
|
822
|
+
continuationCursor !== undefined &&
|
|
823
|
+
(typeof continuationCursor !== "string" || continuationCursor.length === 0)
|
|
824
|
+
)
|
|
825
|
+
return failure(
|
|
826
|
+
"session.list",
|
|
827
|
+
"uncertain",
|
|
828
|
+
"malformed_response",
|
|
829
|
+
"lifecycle broker returned a malformed scoped list result",
|
|
830
|
+
);
|
|
831
|
+
const rows = scopedSearchRows(result.sessions);
|
|
832
|
+
if (!rows)
|
|
833
|
+
return failure(
|
|
834
|
+
"session.list",
|
|
835
|
+
"uncertain",
|
|
836
|
+
"malformed_response",
|
|
837
|
+
"lifecycle broker returned a malformed scoped list result",
|
|
838
|
+
);
|
|
839
|
+
return {
|
|
840
|
+
ok: true,
|
|
841
|
+
operation: "session.list",
|
|
842
|
+
result: {
|
|
843
|
+
version: 1,
|
|
844
|
+
scope: pageScope.scope,
|
|
845
|
+
status:
|
|
846
|
+
pageScope.scope.resolution === "not-in-git-worktree"
|
|
847
|
+
? "not-in-git-worktree"
|
|
848
|
+
: rows.length === 0
|
|
849
|
+
? "empty"
|
|
850
|
+
: "populated",
|
|
851
|
+
observedAt: pageScope.observedAt,
|
|
852
|
+
indexSeq: result.indexSeq,
|
|
853
|
+
rows,
|
|
854
|
+
...(continuationCursor === undefined ? {} : { cursor: continuationCursor }),
|
|
855
|
+
warnings: result.warnings,
|
|
856
|
+
},
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
async execute(
|
|
682
861
|
request: SessionLifecycleMutationRequest,
|
|
683
862
|
idempotencyKeyOverride?: string,
|
|
684
863
|
): Promise<
|
|
@@ -759,19 +938,6 @@ export class SessionLifecycleService {
|
|
|
759
938
|
| SessionReconcileUncertainOutcome;
|
|
760
939
|
}
|
|
761
940
|
|
|
762
|
-
async execute(
|
|
763
|
-
request: SessionLifecycleMutationRequest,
|
|
764
|
-
): Promise<
|
|
765
|
-
| SessionCreateOutcome
|
|
766
|
-
| SessionForkOutcome
|
|
767
|
-
| SessionResumeOutcome
|
|
768
|
-
| SessionCloseOutcome
|
|
769
|
-
| SessionDeleteOutcome
|
|
770
|
-
| SessionReconcileUncertainOutcome
|
|
771
|
-
> {
|
|
772
|
-
return this.#execute(request);
|
|
773
|
-
}
|
|
774
|
-
|
|
775
941
|
async executeWithIdempotencyKey(
|
|
776
942
|
request: SessionLifecycleMutationRequest,
|
|
777
943
|
idempotencyKey: string,
|
|
@@ -783,7 +949,7 @@ export class SessionLifecycleService {
|
|
|
783
949
|
| SessionDeleteOutcome
|
|
784
950
|
| SessionReconcileUncertainOutcome
|
|
785
951
|
> {
|
|
786
|
-
return this
|
|
952
|
+
return this.execute(request, idempotencyKey);
|
|
787
953
|
}
|
|
788
954
|
|
|
789
955
|
async create(request: Omit<SessionCreateRequest, "operation">): Promise<SessionCreateOutcome> {
|
|
@@ -816,6 +982,9 @@ export class SessionLifecycleService {
|
|
|
816
982
|
}
|
|
817
983
|
|
|
818
984
|
async list(request: Omit<SessionListRequest, "operation">): Promise<SessionListOutcome> {
|
|
985
|
+
return await this.#list(request, false);
|
|
986
|
+
}
|
|
987
|
+
async #list(request: Omit<SessionListRequest, "operation">, singleScopedPage: boolean): Promise<SessionListOutcome> {
|
|
819
988
|
if (!validActor((request as { actor?: unknown }).actor))
|
|
820
989
|
return failure("session.list", "terminal", "unauthorized", "authenticated actor is required");
|
|
821
990
|
if ((request as { capability?: unknown }).capability !== "session.list")
|
|
@@ -823,6 +992,24 @@ export class SessionLifecycleService {
|
|
|
823
992
|
const target = request.target ?? {};
|
|
824
993
|
if (!validTarget(target))
|
|
825
994
|
return failure("session.list", "terminal", "invalid_request", "target must be an object");
|
|
995
|
+
const scopeRequest = target.scope === undefined ? undefined : scopeRequestV1(target.scope);
|
|
996
|
+
if (target.scope !== undefined && !scopeRequest)
|
|
997
|
+
return failure("session.list", "terminal", "invalid_request", "scope must be a valid ScopeRequestV1");
|
|
998
|
+
let locallyResolvedScope: SdkSearchResultV1["scope"] | undefined;
|
|
999
|
+
if (scopeRequest !== undefined) {
|
|
1000
|
+
try {
|
|
1001
|
+
locallyResolvedScope = await resolveScopeRequest(scopeRequest);
|
|
1002
|
+
} catch (cause) {
|
|
1003
|
+
return failure(
|
|
1004
|
+
"session.list",
|
|
1005
|
+
"terminal",
|
|
1006
|
+
"invalid_request",
|
|
1007
|
+
cause instanceof Error ? cause.message : "scope could not be resolved",
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (singleScopedPage && locallyResolvedScope !== undefined)
|
|
1012
|
+
return await this.#singleScopedList(request, target, locallyResolvedScope);
|
|
826
1013
|
let pages: readonly SessionListTraversalPage<unknown, LifecycleListPage>[];
|
|
827
1014
|
try {
|
|
828
1015
|
pages = await traverseSessionList<Record<string, unknown>, unknown, LifecycleListPage>(
|
|
@@ -838,14 +1025,22 @@ export class SessionLifecycleService {
|
|
|
838
1025
|
const page = sessionListPageFromResponse(response);
|
|
839
1026
|
if (!page) return undefined;
|
|
840
1027
|
const result = listResult(page);
|
|
1028
|
+
const scoped = scopedPage(page);
|
|
841
1029
|
return result
|
|
842
|
-
? {
|
|
1030
|
+
? {
|
|
1031
|
+
result,
|
|
1032
|
+
sessions: result.sessions,
|
|
1033
|
+
continuationCursor: page.continuationCursor,
|
|
1034
|
+
...(scoped === undefined ? {} : scoped),
|
|
1035
|
+
}
|
|
843
1036
|
: undefined;
|
|
844
1037
|
},
|
|
845
1038
|
);
|
|
846
1039
|
} catch (thrown) {
|
|
847
1040
|
if (thrown instanceof BrokerSessionListResponseError) {
|
|
848
1041
|
const error = thrown.brokerError;
|
|
1042
|
+
if (error.code === "unavailable" && locallyResolvedScope)
|
|
1043
|
+
return unavailableScopedList(locallyResolvedScope);
|
|
849
1044
|
return failure("session.list", certaintyForBrokerCode(error.code), error.code, error.message);
|
|
850
1045
|
}
|
|
851
1046
|
if (thrown instanceof SessionListTraversalError)
|
|
@@ -860,6 +1055,8 @@ export class SessionLifecycleService {
|
|
|
860
1055
|
: "session.list exceeded the page budget",
|
|
861
1056
|
);
|
|
862
1057
|
const error = brokerError(thrown) ?? brokerErrorFromThrown(thrown);
|
|
1058
|
+
if (locallyResolvedScope && (!error || error.code === "unavailable"))
|
|
1059
|
+
return unavailableScopedList(locallyResolvedScope);
|
|
863
1060
|
return error
|
|
864
1061
|
? failure("session.list", certaintyForThrownError(error), error.code, error.message)
|
|
865
1062
|
: failure("session.list", "retryable", "unavailable", "lifecycle broker request was unavailable");
|
|
@@ -873,6 +1070,68 @@ export class SessionLifecycleService {
|
|
|
873
1070
|
"lifecycle broker returned a malformed list result",
|
|
874
1071
|
);
|
|
875
1072
|
const { result } = firstPage.page;
|
|
1073
|
+
if (request.target?.scope !== undefined) {
|
|
1074
|
+
const scope = firstPage.page.scope;
|
|
1075
|
+
const observedAt = firstPage.page.observedAt;
|
|
1076
|
+
if (!scope || !observedAt)
|
|
1077
|
+
return failure(
|
|
1078
|
+
"session.list",
|
|
1079
|
+
"uncertain",
|
|
1080
|
+
"malformed_response",
|
|
1081
|
+
"lifecycle broker returned a malformed scoped list result",
|
|
1082
|
+
);
|
|
1083
|
+
const frozenScope = JSON.stringify(scope);
|
|
1084
|
+
const frozenIndexSeq = result.indexSeq;
|
|
1085
|
+
for (const page of pages) {
|
|
1086
|
+
const pageScope = page.page.scope;
|
|
1087
|
+
const pageObservedAt = page.page.observedAt;
|
|
1088
|
+
if (
|
|
1089
|
+
pageScope === undefined ||
|
|
1090
|
+
pageObservedAt !== observedAt ||
|
|
1091
|
+
page.page.result.indexSeq !== frozenIndexSeq ||
|
|
1092
|
+
JSON.stringify(pageScope) !== frozenScope
|
|
1093
|
+
)
|
|
1094
|
+
return failure(
|
|
1095
|
+
"session.list",
|
|
1096
|
+
"uncertain",
|
|
1097
|
+
"scope_observation_drift",
|
|
1098
|
+
"lifecycle broker changed the frozen scope observation across list pages",
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
if (locallyResolvedScope && JSON.stringify(locallyResolvedScope) !== frozenScope)
|
|
1102
|
+
return failure(
|
|
1103
|
+
"session.list",
|
|
1104
|
+
"uncertain",
|
|
1105
|
+
"scope_observation_drift",
|
|
1106
|
+
"lifecycle broker scope does not match the locally resolved request scope",
|
|
1107
|
+
);
|
|
1108
|
+
const rows = scopedSearchRows(pages.flatMap(page => page.page.result.sessions));
|
|
1109
|
+
if (!rows)
|
|
1110
|
+
return failure(
|
|
1111
|
+
"session.list",
|
|
1112
|
+
"uncertain",
|
|
1113
|
+
"malformed_response",
|
|
1114
|
+
"lifecycle broker returned a malformed scoped list result",
|
|
1115
|
+
);
|
|
1116
|
+
return {
|
|
1117
|
+
ok: true,
|
|
1118
|
+
operation: "session.list",
|
|
1119
|
+
result: {
|
|
1120
|
+
version: 1,
|
|
1121
|
+
scope,
|
|
1122
|
+
status:
|
|
1123
|
+
scope.resolution === "not-in-git-worktree"
|
|
1124
|
+
? "not-in-git-worktree"
|
|
1125
|
+
: rows.length === 0
|
|
1126
|
+
? "empty"
|
|
1127
|
+
: "populated",
|
|
1128
|
+
observedAt,
|
|
1129
|
+
indexSeq: result.indexSeq,
|
|
1130
|
+
rows,
|
|
1131
|
+
warnings: result.warnings,
|
|
1132
|
+
},
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
876
1135
|
return {
|
|
877
1136
|
ok: true,
|
|
878
1137
|
operation: "session.list",
|
|
@@ -55,6 +55,8 @@ export type CreateLifecycleAgentSessionOptions = Omit<CreateAgentSessionOptions,
|
|
|
55
55
|
* a prepared signal instead of readiness until it is explicitly activated.
|
|
56
56
|
*/
|
|
57
57
|
readiness?: "immediate" | "deferred";
|
|
58
|
+
/** Broker-issued lifecycle request identity published with host registration. */
|
|
59
|
+
lifecycleRequestId?: string;
|
|
58
60
|
};
|
|
59
61
|
|
|
60
62
|
/** Internal lifecycle-only session construction with an owner-bound SDK startup result. */
|
|
@@ -62,9 +64,15 @@ export async function createLifecycleAgentSession(
|
|
|
62
64
|
options: CreateLifecycleAgentSessionOptions = {},
|
|
63
65
|
): Promise<CreateLifecycleAgentSessionResult> {
|
|
64
66
|
const rollback = new SdkStartupRollbackTracker();
|
|
65
|
-
const capability = new SdkStartupCapability(rollback, options.readiness ?? "immediate");
|
|
67
|
+
const capability = new SdkStartupCapability(rollback, options.readiness ?? "immediate", options.lifecycleRequestId);
|
|
66
68
|
try {
|
|
67
|
-
const {
|
|
69
|
+
const {
|
|
70
|
+
modelId,
|
|
71
|
+
mcpStartupTimeoutMs,
|
|
72
|
+
readiness: _readiness,
|
|
73
|
+
lifecycleRequestId: _lifecycleRequestId,
|
|
74
|
+
...sessionOptions
|
|
75
|
+
} = options;
|
|
68
76
|
const internalOptions = {
|
|
69
77
|
...sessionOptions,
|
|
70
78
|
// Explicit model pin (#4707): resolve through the staged selector
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
promptDeadlineAt,
|
|
8
8
|
recordAttributableProgress,
|
|
9
9
|
} from "./prompt-deadline-lease";
|
|
10
|
+
import type { TurnResultContent } from "./turn-result";
|
|
10
11
|
|
|
11
12
|
const MAX_EXPIRY_RETRIES = 5;
|
|
12
13
|
const MAX_UNCERTAINTY_RETRIES = 3;
|
|
@@ -22,6 +23,12 @@ export type PromptDeadlineOutcome = {
|
|
|
22
23
|
provenance: "deadline";
|
|
23
24
|
};
|
|
24
25
|
|
|
26
|
+
export interface PromptTerminalTransitionEvidence {
|
|
27
|
+
content?: TurnResultContent;
|
|
28
|
+
hasActivity?: boolean;
|
|
29
|
+
outcome?: { kind: "stopped"; reason: "cancelled"; provenance: "client_cancel" };
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
function leaseKey(correlation: InvocationCorrelation): string {
|
|
26
33
|
return `${correlation.commandId}:${correlation.turnId}`;
|
|
27
34
|
}
|
|
@@ -37,6 +44,7 @@ export class PromptDeadlineManager {
|
|
|
37
44
|
readonly #expiring = new Set<string>();
|
|
38
45
|
readonly #pendingTerminalTransitions = new Set<string>();
|
|
39
46
|
readonly #pendingTerminalFailureReasons = new Map<string, { code: string; message: string }>();
|
|
47
|
+
readonly #pendingTerminalEvidence = new Map<string, PromptTerminalTransitionEvidence>();
|
|
40
48
|
readonly #getLeaseMs: () => number;
|
|
41
49
|
readonly #getMaxMs: () => number;
|
|
42
50
|
readonly #now: () => number;
|
|
@@ -102,7 +110,10 @@ export class PromptDeadlineManager {
|
|
|
102
110
|
error: Object.assign(new Error(failureReason.message), { code: failureReason.code }),
|
|
103
111
|
} as never);
|
|
104
112
|
}
|
|
105
|
-
await this.#reconciliation.noteTransition("prompt", correlation, {
|
|
113
|
+
await this.#reconciliation.noteTransition("prompt", correlation, {
|
|
114
|
+
type: "agent_end",
|
|
115
|
+
...this.#pendingTerminalEvidence.get(key),
|
|
116
|
+
});
|
|
106
117
|
// Supersession check before retiring ownership (exact-head review P1):
|
|
107
118
|
// attributable progress during the awaited writes renews the lease
|
|
108
119
|
// past its deadline, so this replay instance must back off and
|
|
@@ -367,6 +378,7 @@ export class PromptDeadlineManager {
|
|
|
367
378
|
noteTerminalTransition(
|
|
368
379
|
correlation: InvocationCorrelation,
|
|
369
380
|
pendingFailure?: { code: string; message: string },
|
|
381
|
+
evidence?: PromptTerminalTransitionEvidence,
|
|
370
382
|
): void {
|
|
371
383
|
const key = leaseKey(correlation);
|
|
372
384
|
if (!this.#leases.has(key)) {
|
|
@@ -377,6 +389,7 @@ export class PromptDeadlineManager {
|
|
|
377
389
|
this.onAccepted(correlation);
|
|
378
390
|
}
|
|
379
391
|
this.#pendingTerminalTransitions.add(key);
|
|
392
|
+
if (evidence !== undefined) this.#pendingTerminalEvidence.set(key, evidence);
|
|
380
393
|
// Compound failure-plus-terminal recovery intent (exact-head review HIGH):
|
|
381
394
|
// when expiry replays this real terminal transition after a failed write, it
|
|
382
395
|
// must re-record the failure reason BEFORE agent_end, or the abandoned or
|
|
@@ -395,6 +408,7 @@ export class PromptDeadlineManager {
|
|
|
395
408
|
this.#expiring.delete(key);
|
|
396
409
|
this.#pendingTerminalTransitions.delete(key);
|
|
397
410
|
this.#pendingTerminalFailureReasons.delete(key);
|
|
411
|
+
this.#pendingTerminalEvidence.delete(key);
|
|
398
412
|
}
|
|
399
413
|
|
|
400
414
|
clearAll(): void {
|
|
@@ -407,6 +421,7 @@ export class PromptDeadlineManager {
|
|
|
407
421
|
this.#expiring.clear();
|
|
408
422
|
this.#pendingTerminalTransitions.clear();
|
|
409
423
|
this.#pendingTerminalFailureReasons.clear();
|
|
424
|
+
this.#pendingTerminalEvidence.clear();
|
|
410
425
|
}
|
|
411
426
|
|
|
412
427
|
/** For tests: current deadline or undefined if no lease. */
|
|
@@ -28,8 +28,35 @@ export type AdapterValidationError = {
|
|
|
28
28
|
message: string;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
const PROMPT_OPERATIONS = new Set(["turn.prompt", "turn.steer", "turn.follow_up", "turn.abort_and_prompt"]);
|
|
32
|
+
|
|
33
|
+
function hasUsablePromptImage(value: unknown): boolean {
|
|
34
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
35
|
+
const image = value as Record<string, unknown>;
|
|
36
|
+
return (
|
|
37
|
+
typeof image.data === "string" &&
|
|
38
|
+
image.data.trim().length > 0 &&
|
|
39
|
+
(image.mimeType === undefined || (typeof image.mimeType === "string" && image.mimeType.length > 0))
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Rejects instruction-bearing controls before correlation or provider work. */
|
|
44
|
+
export function validateRequiredPromptText(operation: string, input: Input): AdapterValidationError | undefined {
|
|
45
|
+
const hasUsableImage =
|
|
46
|
+
operation === "turn.prompt" && Array.isArray(input.images) && input.images.some(hasUsablePromptImage);
|
|
47
|
+
if (
|
|
48
|
+
PROMPT_OPERATIONS.has(operation) &&
|
|
49
|
+
(typeof input.text !== "string" || (input.text.trim().length === 0 && !hasUsableImage))
|
|
50
|
+
) {
|
|
51
|
+
return { code: "invalid_input", message: "Prompt must not be empty." };
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
31
56
|
/** Validates controls that adapters must reject before forwarding them to a host. */
|
|
32
57
|
export function validateAdapterControl(operation: string, input: Input): AdapterValidationError | undefined {
|
|
58
|
+
const promptError = validateRequiredPromptText(operation, input);
|
|
59
|
+
if (promptError) return promptError;
|
|
33
60
|
if (operation === "runtime.reload") {
|
|
34
61
|
if (
|
|
35
62
|
!Array.isArray(input.components) ||
|
|
@@ -1097,6 +1097,24 @@
|
|
|
1097
1097
|
"packages/coding-agent/test/sdk-operation-inventory.test.ts"
|
|
1098
1098
|
]
|
|
1099
1099
|
},
|
|
1100
|
+
{
|
|
1101
|
+
"sourceId": "registry:G09",
|
|
1102
|
+
"sourceFile": "packages/coding-agent/src/sdk/protocol/operation-registry.ts",
|
|
1103
|
+
"sourceKind": "registry",
|
|
1104
|
+
"decision": "include",
|
|
1105
|
+
"sdkId": "session.spawn",
|
|
1106
|
+
"adapterMappings": {
|
|
1107
|
+
"telegram": "prohibited",
|
|
1108
|
+
"discord": "prohibited",
|
|
1109
|
+
"slack": "prohibited",
|
|
1110
|
+
"mcp": "prohibited",
|
|
1111
|
+
"acp": "prohibited",
|
|
1112
|
+
"daemonCli": "prohibited"
|
|
1113
|
+
},
|
|
1114
|
+
"testIds": [
|
|
1115
|
+
"packages/coding-agent/test/sdk-operation-inventory.test.ts"
|
|
1116
|
+
]
|
|
1117
|
+
},
|
|
1100
1118
|
{
|
|
1101
1119
|
"sourceId": "registry:Q01",
|
|
1102
1120
|
"sourceFile": "packages/coding-agent/src/sdk/protocol/operation-registry.ts",
|
|
@@ -2845,6 +2863,39 @@
|
|
|
2845
2863
|
"testIds": "not_applicable"
|
|
2846
2864
|
}
|
|
2847
2865
|
},
|
|
2866
|
+
{
|
|
2867
|
+
"sourceId": "agent_session:queueCoordinatorRuntimeStatePersistForTests",
|
|
2868
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2869
|
+
"sourceKind": "agent_session",
|
|
2870
|
+
"decision": "exclude",
|
|
2871
|
+
"rationale": "test-only coordinator persistence ordering seam, not a user-facing SDK control seam",
|
|
2872
|
+
"exclusionMetadata": {
|
|
2873
|
+
"adapterMappings": "not_applicable",
|
|
2874
|
+
"testIds": "not_applicable"
|
|
2875
|
+
}
|
|
2876
|
+
},
|
|
2877
|
+
{
|
|
2878
|
+
"sourceId": "agent_session:parkAgentEndForCoordinatorPersistForTests",
|
|
2879
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2880
|
+
"sourceKind": "agent_session",
|
|
2881
|
+
"decision": "exclude",
|
|
2882
|
+
"rationale": "test-only parked terminal persistence seam, not a user-facing SDK control seam",
|
|
2883
|
+
"exclusionMetadata": {
|
|
2884
|
+
"adapterMappings": "not_applicable",
|
|
2885
|
+
"testIds": "not_applicable"
|
|
2886
|
+
}
|
|
2887
|
+
},
|
|
2888
|
+
{
|
|
2889
|
+
"sourceId": "agent_session:flushParkedAgentEndForCoordinatorPersistForTests",
|
|
2890
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2891
|
+
"sourceKind": "agent_session",
|
|
2892
|
+
"decision": "exclude",
|
|
2893
|
+
"rationale": "test-only parked terminal flush seam, not a user-facing SDK control seam",
|
|
2894
|
+
"exclusionMetadata": {
|
|
2895
|
+
"adapterMappings": "not_applicable",
|
|
2896
|
+
"testIds": "not_applicable"
|
|
2897
|
+
}
|
|
2898
|
+
},
|
|
2848
2899
|
{
|
|
2849
2900
|
"sourceId": "agent_session:drainAsyncJobDeliveriesForAcp",
|
|
2850
2901
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
@@ -2937,7 +2988,7 @@
|
|
|
2937
2988
|
}
|
|
2938
2989
|
},
|
|
2939
2990
|
{
|
|
2940
|
-
"sourceId": "agent_session:
|
|
2991
|
+
"sourceId": "agent_session:registerForegroundFoldParticipant",
|
|
2941
2992
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2942
2993
|
"sourceKind": "agent_session",
|
|
2943
2994
|
"decision": "exclude",
|
|
@@ -2992,7 +3043,7 @@
|
|
|
2992
3043
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2993
3044
|
"sourceKind": "agent_session",
|
|
2994
3045
|
"decision": "exclude",
|
|
2995
|
-
"rationale": "internal accessor/plumbing, not a user-facing control seam",
|
|
3046
|
+
"rationale": "internal accessor/plumbing, not a user-facing SDK control seam",
|
|
2996
3047
|
"exclusionMetadata": {
|
|
2997
3048
|
"adapterMappings": "not_applicable",
|
|
2998
3049
|
"testIds": "not_applicable"
|