@gajae-code/coding-agent 0.12.21 → 0.13.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 +67 -2
- package/dist/types/cli/args.d.ts +5 -2
- package/dist/types/cli/root-flags.d.ts +246 -0
- package/dist/types/cli.d.ts +11 -19
- package/dist/types/commands/daemon.d.ts +0 -30
- package/dist/types/commands/launch.d.ts +11 -19
- package/dist/types/commands/quick-lane.d.ts +32 -0
- package/dist/types/commands/sdk.d.ts +21 -15
- package/dist/types/config/file-lock-gc.d.ts +2 -0
- package/dist/types/config/model-equivalence.d.ts +20 -0
- package/dist/types/config/model-profile-activation.d.ts +62 -17
- package/dist/types/config/model-profiles.d.ts +10 -0
- package/dist/types/config/model-registry.d.ts +68 -1
- package/dist/types/config/model-resolver.d.ts +15 -4
- package/dist/types/config/models-config-schema.d.ts +7 -2
- package/dist/types/config/provider-selection-policy.d.ts +62 -0
- package/dist/types/config/settings-schema.d.ts +36 -78
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/edit/renderer.d.ts +5 -2
- package/dist/types/edit/streaming.d.ts +13 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -0
- package/dist/types/extensibility/extensions/compact-handler.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +19 -5
- package/dist/types/internal-urls/local-protocol.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +0 -2
- package/dist/types/modes/components/provider-order-context.d.ts +45 -0
- package/dist/types/modes/components/provider-order-editor.d.ts +24 -0
- package/dist/types/modes/components/settings-defs.d.ts +5 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/quick-lane/classify.d.ts +40 -0
- package/dist/types/runtime/stt-settings-setup.d.ts +18 -0
- package/dist/types/sdk/broker/broker.d.ts +56 -2
- package/dist/types/sdk/broker/operation-receipt-ledger.d.ts +25 -0
- package/dist/types/sdk/broker/session-index.d.ts +83 -2
- package/dist/types/sdk/broker/state-version.d.ts +1 -1
- package/dist/types/sdk/bus/notification-service.d.ts +6 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +8 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +26 -0
- package/dist/types/sdk/cli/rows.d.ts +98 -0
- package/dist/types/sdk/cli/session-cli.d.ts +35 -3
- package/dist/types/sdk/elevation/allowlist.d.ts +20 -0
- package/dist/types/sdk/elevation/audit.d.ts +93 -0
- package/dist/types/sdk/elevation/capability.d.ts +3 -0
- package/dist/types/sdk/elevation/digest.d.ts +72 -0
- package/dist/types/sdk/elevation/dispatch-receipt.d.ts +85 -0
- package/dist/types/sdk/elevation/grant-ledger.d.ts +163 -0
- package/dist/types/sdk/elevation/owner.d.ts +48 -0
- package/dist/types/sdk/guides/cache.d.ts +98 -0
- package/dist/types/sdk/guides/catalog.d.ts +156 -0
- package/dist/types/sdk/guides/cli.d.ts +39 -0
- package/dist/types/sdk/guides/manifest.d.ts +66 -0
- package/dist/types/sdk/guides/verify.d.ts +66 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +8 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/query/cursor.d.ts +10 -0
- package/dist/types/sdk/host/query/handlers.d.ts +21 -0
- package/dist/types/sdk/host/session-runtime.d.ts +9 -0
- package/dist/types/sdk/mcp/server.d.ts +2 -2
- package/dist/types/sdk/prompt-status.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +4 -0
- package/dist/types/sdk/transport/index.d.ts +1 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +20 -1
- package/dist/types/session/agent-session.d.ts +32 -18
- package/dist/types/session/blob-store.d.ts +13 -7
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +25 -1
- package/dist/types/session/internal/session-memory-sidecar.d.ts +5 -5
- package/dist/types/session/session-manager.d.ts +139 -10
- package/dist/types/session/session-storage.d.ts +48 -0
- package/dist/types/setup/provider-onboarding.d.ts +7 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +14 -0
- package/dist/types/stt/setup.d.ts +2 -0
- package/dist/types/task/executor.d.ts +10 -2
- package/dist/types/tools/browser/runtime-diagnostics.d.ts +65 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -0
- package/dist/types/tools/browser.d.ts +2 -0
- package/dist/types/tools/image-gen.d.ts +14 -18
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/resource-gc.d.ts +2 -0
- package/dist/types/utils/pasted-image-path.d.ts +6 -0
- package/package.json +9 -9
- package/scripts/generate-sdk-operation-inventory.ts +8 -0
- package/scripts/run-test-manifest.ts +61 -21
- package/scripts/verify-gjc-sdk-canonicalization.ts +9 -4
- package/src/cli/args.ts +62 -9
- package/src/cli/completion-cli.ts +1 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/root-flags.ts +100 -0
- package/src/cli/setup-cli.ts +6 -2
- package/src/cli.ts +42 -65
- package/src/commands/acp.ts +1 -1
- package/src/commands/daemon.ts +2 -31
- package/src/commands/gc.ts +1 -1
- package/src/commands/launch.ts +8 -129
- package/src/commands/quick-lane.ts +51 -0
- package/src/commands/sdk.ts +200 -26
- package/src/commands/setup.ts +4 -2
- package/src/config/file-lock-gc.ts +4 -1
- package/src/config/model-equivalence.ts +76 -7
- package/src/config/model-profile-activation.ts +1115 -265
- package/src/config/model-profiles.ts +134 -2
- package/src/config/model-registry.ts +352 -50
- package/src/config/model-resolver.ts +289 -38
- package/src/config/models-config-schema.ts +11 -4
- package/src/config/provider-selection-policy.ts +146 -0
- package/src/config/settings-schema.ts +50 -77
- package/src/config/settings.ts +40 -0
- package/src/coordinator-mcp/server.ts +32 -2
- package/src/edit/renderer.ts +175 -223
- package/src/edit/streaming.ts +108 -1
- package/src/export/html/index.ts +57 -7
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/extensions/compact-handler.ts +3 -2
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/gjc-runtime/gc-runtime.ts +379 -20
- package/src/gjc-runtime/launch-worktree.ts +5 -1
- package/src/gjc-runtime/team-runtime.ts +59 -2
- package/src/gjc-runtime/tmux-owner-isolation.ts +19 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +16 -1
- package/src/internal-urls/docs-index.generated.ts +17 -13
- package/src/internal-urls/local-protocol.ts +1 -0
- package/src/main.ts +40 -3
- package/src/memories/index.ts +4 -2
- package/src/modes/acp/acp-agent.ts +235 -91
- package/src/modes/components/model-selector.ts +206 -42
- package/src/modes/components/provider-order-context.ts +197 -0
- package/src/modes/components/provider-order-editor.ts +441 -0
- package/src/modes/components/settings-defs.ts +21 -1
- package/src/modes/components/settings-selector.ts +45 -2
- package/src/modes/components/tool-execution.ts +47 -15
- package/src/modes/controllers/command-controller.ts +6 -3
- package/src/modes/controllers/extension-ui-controller.ts +2 -0
- package/src/modes/controllers/input-controller.ts +73 -2
- package/src/modes/controllers/selector-controller.ts +260 -153
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +18 -0
- package/src/prompts/tools/browser.md +1 -0
- package/src/quick-lane/classify.ts +257 -0
- package/src/rlm/index.ts +1 -1
- package/src/runtime/stt-settings-setup.ts +57 -0
- package/src/sdk/acp/adapter.ts +2 -1
- package/src/sdk/broker/broker.ts +780 -12
- package/src/sdk/broker/lifecycle.ts +70 -29
- package/src/sdk/broker/operation-receipt-ledger.ts +144 -0
- package/src/sdk/broker/session-index.ts +463 -48
- package/src/sdk/broker/state-version.ts +13 -9
- package/src/sdk/broker/transport.ts +14 -1
- package/src/sdk/bus/index.ts +34 -13
- package/src/sdk/bus/kind-aware-reconciliation.ts +21 -2
- package/src/sdk/bus/notification-service.ts +47 -1
- package/src/sdk/bus/prompt-reconciliation.ts +22 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +4 -1
- package/src/sdk/bus/telegram-daemon.ts +54 -3
- package/src/sdk/cli/rows.ts +247 -0
- package/src/sdk/cli/session-cli.ts +930 -79
- package/src/sdk/elevation/allowlist.ts +54 -0
- package/src/sdk/elevation/audit.ts +208 -0
- package/src/sdk/elevation/capability.ts +41 -0
- package/src/sdk/elevation/digest.ts +129 -0
- package/src/sdk/elevation/dispatch-receipt.ts +184 -0
- package/src/sdk/elevation/grant-ledger.ts +1142 -0
- package/src/sdk/elevation/owner.ts +109 -0
- package/src/sdk/guides/cache.ts +363 -0
- package/src/sdk/guides/catalog.ts +554 -0
- package/src/sdk/guides/cli.ts +235 -0
- package/src/sdk/guides/manifest.ts +152 -0
- package/src/sdk/guides/verify.ts +178 -0
- package/src/sdk/host/control/dispatch.ts +49 -1
- package/src/sdk/host/control/index.ts +1 -0
- package/src/sdk/host/control/operations.ts +8 -1
- package/src/sdk/host/query/cursor.ts +47 -3
- package/src/sdk/host/query/handlers.ts +155 -2
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +391 -1
- package/src/sdk/host/session-runtime.ts +308 -73
- package/src/sdk/host/surface-policy.ts +3 -0
- package/src/sdk/host/websocket-transport.ts +7 -1
- package/src/sdk/mcp/server.ts +88 -15
- package/src/sdk/prompt-status.ts +8 -0
- package/src/sdk/protocol/operation-inventory.generated.json +62 -0
- package/src/sdk/protocol/operation-registry.ts +3 -2
- package/src/sdk/session.ts +87 -39
- package/src/sdk/transport/index.ts +1 -0
- package/src/sdk/transport/relay.ts +3 -0
- package/src/sdk/transport/serve-cli.ts +111 -24
- package/src/secrets/obfuscator.ts +92 -12
- package/src/session/agent-session.ts +322 -93
- package/src/session/blob-store.ts +60 -8
- package/src/session/fallback-chain-controller.ts +35 -0
- package/src/session/internal/managed-session-scope.ts +25 -3
- package/src/session/internal/managed-session-storage.ts +574 -73
- package/src/session/internal/session-memory-sidecar.ts +19 -29
- package/src/session/session-manager.ts +2166 -743
- package/src/session/session-storage.ts +715 -48
- package/src/setup/provider-onboarding.ts +19 -3
- package/src/setup/provider-presets.json +3 -1
- package/src/slash-commands/builtin-registry.ts +85 -3
- package/src/stt/downloader.ts +2 -3
- package/src/stt/setup.ts +29 -4
- package/src/task/executor.ts +22 -18
- package/src/task/index.ts +24 -1
- package/src/tools/archive-reader.ts +11 -2
- package/src/tools/browser/runtime-diagnostics.ts +249 -0
- package/src/tools/browser/tab-protocol.ts +4 -0
- package/src/tools/browser/tab-supervisor.ts +5 -0
- package/src/tools/browser/tab-worker.ts +36 -1
- package/src/tools/browser.ts +7 -0
- package/src/tools/image-gen.ts +110 -166
- package/src/tools/index.ts +2 -0
- package/src/tools/read.ts +1 -1
- package/src/tools/resource-gc.ts +6 -1
- package/src/tools/tool-catalog.generated.ts +5 -1
- package/src/utils/pasted-image-path.ts +93 -0
- package/src/web/search/index.ts +3 -3
package/src/sdk/mcp/server.ts
CHANGED
|
@@ -3,12 +3,7 @@ import { getAgentDir } from "@gajae-code/utils";
|
|
|
3
3
|
import { ensureBroker } from "../broker/ensure";
|
|
4
4
|
import { lifecycleRequestTimeoutMs } from "../broker/startup-budget";
|
|
5
5
|
import { SdkClient, SdkClientError } from "../client/client";
|
|
6
|
-
import {
|
|
7
|
-
listSdkSessionEndpoints,
|
|
8
|
-
readSdkBrokerDiscovery,
|
|
9
|
-
readSdkSessionEndpoint,
|
|
10
|
-
SdkDiscoveryError,
|
|
11
|
-
} from "../client/discovery";
|
|
6
|
+
import { readSdkBrokerDiscovery, SdkDiscoveryError } from "../client/discovery";
|
|
12
7
|
import { validateAdapterControl, validateAdapterSecretFields } from "../protocol/adapter-validation";
|
|
13
8
|
import { adapterDispositionError, findOperation } from "../protocol/operation-registry";
|
|
14
9
|
|
|
@@ -30,6 +25,7 @@ export interface SdkMcpServerOptions {
|
|
|
30
25
|
agentDir?: string;
|
|
31
26
|
connect?: (url: string, token: string) => Promise<SdkClient>;
|
|
32
27
|
}
|
|
28
|
+
const MAX_SESSION_LIST_PAGES = 10_000;
|
|
33
29
|
|
|
34
30
|
export const SDK_MCP_TOOL_NAMES = [
|
|
35
31
|
"gjc_session_control",
|
|
@@ -169,34 +165,111 @@ function textResult(
|
|
|
169
165
|
return { content: [{ type: "text", text: JSON.stringify(payload) }], isError };
|
|
170
166
|
}
|
|
171
167
|
|
|
172
|
-
/** Creates the model-facing MCP adapter
|
|
168
|
+
/** Creates the model-facing MCP adapter; session targeting is broker-bound (C10). */
|
|
173
169
|
export function createSdkMcpServer(options: SdkMcpServerOptions = {}) {
|
|
174
|
-
const repo = options.repo ?? process.cwd();
|
|
175
170
|
const agentDir = options.agentDir ?? getAgentDir();
|
|
176
171
|
const connect = options.connect ?? ((url, token) => SdkClient.connect(url, token));
|
|
177
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Connects to the agent broker, starting one when absent (mirrors the
|
|
175
|
+
* lifecycle bootstrap path). The broker is the sole session authority; the
|
|
176
|
+
* MCP server never reads endpoint discovery files itself.
|
|
177
|
+
*/
|
|
178
|
+
async function brokerClient(): Promise<SdkClient> {
|
|
179
|
+
await ensureBroker({ agentDir });
|
|
180
|
+
const broker = await readSdkBrokerDiscovery(agentDir);
|
|
181
|
+
if (!broker) throw new SdkClientError("not_found", "SDK broker not found");
|
|
182
|
+
return await connect(broker.url, broker.token);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Extracts a broker `result` envelope, converting an explicit error frame into a typed throw. */
|
|
186
|
+
function brokerResult(value: unknown): Record<string, unknown> {
|
|
187
|
+
if (isObject(value) && value.ok === false) {
|
|
188
|
+
const error = isObject(value.error) ? value.error : {};
|
|
189
|
+
const code = typeof error.code === "string" ? error.code : "unavailable";
|
|
190
|
+
const message = typeof error.message === "string" ? error.message : "SDK broker request failed";
|
|
191
|
+
throw new SdkClientError(code, message, value.error);
|
|
192
|
+
}
|
|
193
|
+
return isObject(value) && isObject(value.result) ? value.result : {};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function arrayOf(value: unknown): unknown[] {
|
|
197
|
+
return Array.isArray(value) ? value : [];
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Exhausts broker `session.list` cursor pagination (default page limit 100)
|
|
201
|
+
* to one full snapshot. The broker remains the sole session authority; this
|
|
202
|
+
* only aggregates its indexed truth across pages.
|
|
203
|
+
*/
|
|
204
|
+
async function paginatedBrokerSessionList(
|
|
205
|
+
client: SdkClient,
|
|
206
|
+
): Promise<{ sessions: Record<string, unknown>[]; warnings: unknown[] }> {
|
|
207
|
+
const sessions: Record<string, unknown>[] = [];
|
|
208
|
+
let warnings: unknown[] = [];
|
|
209
|
+
let cursor: string | undefined;
|
|
210
|
+
for (let pageCount = 0; pageCount < MAX_SESSION_LIST_PAGES; pageCount++) {
|
|
211
|
+
const listed = brokerResult(await client.global("session.list", cursor === undefined ? {} : { cursor }));
|
|
212
|
+
sessions.push(...arrayOf(listed.sessions).filter(isObject));
|
|
213
|
+
if (Array.isArray(listed.warnings)) warnings = listed.warnings;
|
|
214
|
+
const nextCursor =
|
|
215
|
+
typeof listed.continuationCursor === "string" && listed.continuationCursor.length > 0
|
|
216
|
+
? listed.continuationCursor
|
|
217
|
+
: undefined;
|
|
218
|
+
if (!nextCursor) return { sessions, warnings };
|
|
219
|
+
cursor = nextCursor;
|
|
220
|
+
}
|
|
221
|
+
throw new SdkClientError("protocol_error", "session.list exceeded the page budget.");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Resolves one session through broker `session.list` + `session.get_endpoint`
|
|
226
|
+
* (C10): the broker's liveness and ambiguity truth decides reachability, and
|
|
227
|
+
* only the broker mints the endpoint credential.
|
|
228
|
+
*/
|
|
178
229
|
async function withSession(sessionId: string, action: (client: SdkClient) => Promise<unknown>): Promise<unknown> {
|
|
230
|
+
let broker: SdkClient | undefined;
|
|
179
231
|
let client: SdkClient | undefined;
|
|
180
232
|
try {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
233
|
+
broker = await brokerClient();
|
|
234
|
+
const { sessions } = await paginatedBrokerSessionList(broker);
|
|
235
|
+
const row = sessions.find(item => item.sessionId === sessionId) as Record<string, unknown> | undefined;
|
|
236
|
+
if (!row) return { ok: false, error: { code: "not_found", message: `SDK session not found: ${sessionId}` } };
|
|
237
|
+
if (row.ambiguous === true)
|
|
238
|
+
return {
|
|
239
|
+
ok: false,
|
|
240
|
+
error: { code: "ambiguous_session", message: "session id maps to more than one state root" },
|
|
241
|
+
};
|
|
242
|
+
if (row.live !== true)
|
|
243
|
+
return { ok: false, error: { code: "endpoint_stale", message: "session endpoint is not live" } };
|
|
244
|
+
const endpoint = brokerResult(await broker.global("session.get_endpoint", { sessionId }));
|
|
245
|
+
const url = typeof endpoint.url === "string" && endpoint.url ? endpoint.url : undefined;
|
|
246
|
+
const token = typeof endpoint.token === "string" ? endpoint.token : "";
|
|
247
|
+
if (!url)
|
|
248
|
+
return { ok: false, error: { code: "unavailable", message: "SDK broker returned an invalid endpoint" } };
|
|
249
|
+
client = await connect(url, token);
|
|
185
250
|
return await action(client);
|
|
186
251
|
} catch (error) {
|
|
187
252
|
return resultError(error);
|
|
188
253
|
} finally {
|
|
189
254
|
await client?.close();
|
|
255
|
+
await broker?.close();
|
|
190
256
|
}
|
|
191
257
|
}
|
|
192
258
|
|
|
193
259
|
async function callTool(name: string, args: Arguments = {}): Promise<unknown> {
|
|
194
260
|
if (name === "gjc_session_list") {
|
|
261
|
+
let broker: SdkClient | undefined;
|
|
195
262
|
try {
|
|
196
|
-
|
|
197
|
-
|
|
263
|
+
broker = await brokerClient();
|
|
264
|
+
const { sessions, warnings } = await paginatedBrokerSessionList(broker);
|
|
265
|
+
const listed = sessions.flatMap(item =>
|
|
266
|
+
typeof item.sessionId === "string" && item.sessionId ? [{ sessionId: item.sessionId }] : [],
|
|
267
|
+
);
|
|
268
|
+
return { ok: true, sessions: listed, warnings };
|
|
198
269
|
} catch (error) {
|
|
199
270
|
return resultError(error);
|
|
271
|
+
} finally {
|
|
272
|
+
await broker?.close();
|
|
200
273
|
}
|
|
201
274
|
}
|
|
202
275
|
if (name === "gjc_session_control") {
|
|
@@ -301,7 +374,7 @@ export function createSdkMcpServer(options: SdkMcpServerOptions = {}) {
|
|
|
301
374
|
/**
|
|
302
375
|
* Runs the SDK MCP server over stdio (newline-delimited JSON-RPC), the shipped
|
|
303
376
|
* `gjc mcp-serve sdk` entrypoint. Pure SDK client: session control/query flows
|
|
304
|
-
* through
|
|
377
|
+
* through the broker and v3 WebSockets only.
|
|
305
378
|
*/
|
|
306
379
|
export async function runSdkMcpStdio(options: SdkMcpServerOptions = {}): Promise<void> {
|
|
307
380
|
const server = createSdkMcpServer(options);
|
package/src/sdk/prompt-status.ts
CHANGED
|
@@ -24,6 +24,12 @@
|
|
|
24
24
|
* - Q26 tracks prompts accepted through the SDK control surface (which always
|
|
25
25
|
* carries a requesting connection); submissions without a delivery owner are
|
|
26
26
|
* outside the reconciliation contract and hold no reservation.
|
|
27
|
+
* - A terminal outcome settles once: the first terminal transition wins. A late
|
|
28
|
+
* `agent_failed` (arriving on a different delivery path than the one that
|
|
29
|
+
* claimed the terminal) may still attach its bounded, sanitized reason to an
|
|
30
|
+
* already-terminal record that has none, surfaced as `error` on `terminal_ok`.
|
|
31
|
+
* It never changes status, `terminalAt`, retention order, or the clientRef
|
|
32
|
+
* index, and the first recorded reason always wins over a later frame.
|
|
27
33
|
*/
|
|
28
34
|
|
|
29
35
|
export const PROMPT_CLIENT_REF_MAX_LENGTH = 128;
|
|
@@ -79,6 +85,8 @@ export interface TurnPromptReconciliationTerminalOk extends TurnPromptReconcilia
|
|
|
79
85
|
/** Epoch milliseconds of the terminal transition. */
|
|
80
86
|
terminalAt: number;
|
|
81
87
|
outcome?: SdkPromptTerminalOutcome;
|
|
88
|
+
/** Present when a late `agent_failed` supplied the only failure reason. */
|
|
89
|
+
error?: { code: string; message: string };
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
export interface TurnPromptReconciliationFailed extends TurnPromptReconciliationIdentity {
|
|
@@ -1601,6 +1601,24 @@
|
|
|
1601
1601
|
"packages/coding-agent/test/sdk-operation-inventory.test.ts"
|
|
1602
1602
|
]
|
|
1603
1603
|
},
|
|
1604
|
+
{
|
|
1605
|
+
"sourceId": "registry:Q30",
|
|
1606
|
+
"sourceFile": "packages/coding-agent/src/sdk/protocol/operation-registry.ts",
|
|
1607
|
+
"sourceKind": "registry",
|
|
1608
|
+
"decision": "include",
|
|
1609
|
+
"sdkId": "session.checkpoint",
|
|
1610
|
+
"adapterMappings": {
|
|
1611
|
+
"telegram": "prohibited",
|
|
1612
|
+
"discord": "prohibited",
|
|
1613
|
+
"slack": "prohibited",
|
|
1614
|
+
"mcp": "generic_safe",
|
|
1615
|
+
"acp": "generic_safe",
|
|
1616
|
+
"daemonCli": "generic_safe"
|
|
1617
|
+
},
|
|
1618
|
+
"testIds": [
|
|
1619
|
+
"packages/coding-agent/test/sdk-operation-inventory.test.ts"
|
|
1620
|
+
]
|
|
1621
|
+
},
|
|
1604
1622
|
{
|
|
1605
1623
|
"sourceId": "registry:R01",
|
|
1606
1624
|
"sourceFile": "packages/coding-agent/src/sdk/protocol/operation-registry.ts",
|
|
@@ -3947,6 +3965,17 @@
|
|
|
3947
3965
|
"testIds": "not_applicable"
|
|
3948
3966
|
}
|
|
3949
3967
|
},
|
|
3968
|
+
{
|
|
3969
|
+
"sourceId": "agent_session:getConfiguredModelChainState",
|
|
3970
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
3971
|
+
"sourceKind": "agent_session",
|
|
3972
|
+
"decision": "exclude",
|
|
3973
|
+
"rationale": "internal model-profile transaction snapshot, not a user-facing SDK control seam",
|
|
3974
|
+
"exclusionMetadata": {
|
|
3975
|
+
"adapterMappings": "not_applicable",
|
|
3976
|
+
"testIds": "not_applicable"
|
|
3977
|
+
}
|
|
3978
|
+
},
|
|
3950
3979
|
{
|
|
3951
3980
|
"sourceId": "agent_session:setConfiguredModelChain",
|
|
3952
3981
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
@@ -3980,6 +4009,28 @@
|
|
|
3980
4009
|
"testIds": "not_applicable"
|
|
3981
4010
|
}
|
|
3982
4011
|
},
|
|
4012
|
+
{
|
|
4013
|
+
"sourceId": "agent_session:getDefaultFallbackRuntimeState",
|
|
4014
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
4015
|
+
"sourceKind": "agent_session",
|
|
4016
|
+
"decision": "exclude",
|
|
4017
|
+
"rationale": "internal model-profile transaction snapshot, not a user-facing SDK control seam",
|
|
4018
|
+
"exclusionMetadata": {
|
|
4019
|
+
"adapterMappings": "not_applicable",
|
|
4020
|
+
"testIds": "not_applicable"
|
|
4021
|
+
}
|
|
4022
|
+
},
|
|
4023
|
+
{
|
|
4024
|
+
"sourceId": "agent_session:restoreDefaultFallbackRuntimeState",
|
|
4025
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
4026
|
+
"sourceKind": "agent_session",
|
|
4027
|
+
"decision": "exclude",
|
|
4028
|
+
"rationale": "internal rollback compensation, not a user-facing SDK control seam",
|
|
4029
|
+
"exclusionMetadata": {
|
|
4030
|
+
"adapterMappings": "not_applicable",
|
|
4031
|
+
"testIds": "not_applicable"
|
|
4032
|
+
}
|
|
4033
|
+
},
|
|
3983
4034
|
{
|
|
3984
4035
|
"sourceId": "agent_session:getSessionDefaultModelSelector",
|
|
3985
4036
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
@@ -4024,6 +4075,17 @@
|
|
|
4024
4075
|
"testIds": "not_applicable"
|
|
4025
4076
|
}
|
|
4026
4077
|
},
|
|
4078
|
+
{
|
|
4079
|
+
"sourceId": "agent_session:restoreModelSelectionForRollback",
|
|
4080
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
4081
|
+
"sourceKind": "agent_session",
|
|
4082
|
+
"decision": "exclude",
|
|
4083
|
+
"rationale": "internal rollback compensation, not a user-facing SDK control seam",
|
|
4084
|
+
"exclusionMetadata": {
|
|
4085
|
+
"adapterMappings": "not_applicable",
|
|
4086
|
+
"testIds": "not_applicable"
|
|
4087
|
+
}
|
|
4088
|
+
},
|
|
4027
4089
|
{
|
|
4028
4090
|
"sourceId": "agent_session:setModelTemporaryForControl",
|
|
4029
4091
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
@@ -165,6 +165,7 @@ const queries = [
|
|
|
165
165
|
"Read the authoritative reconciliation status of a skill.invoke by command/turn IDs or clientRef.",
|
|
166
166
|
],
|
|
167
167
|
["providers.list/active", "List active providers."],
|
|
168
|
+
["session.checkpoint", "Resolve a durable transcript checkpoint into a connection-owned replay cursor."],
|
|
168
169
|
] as const;
|
|
169
170
|
|
|
170
171
|
const reverse = [
|
|
@@ -237,14 +238,14 @@ function revision(id: string): string | undefined {
|
|
|
237
238
|
}
|
|
238
239
|
function queryContinuityClass(id: string): QueryContinuityClass {
|
|
239
240
|
if (["Q01", "Q02"].includes(id)) return "stable_prefix";
|
|
240
|
-
if (["Q04", "Q05", "Q06", "Q07", "Q08", "Q11", "Q12", "Q13", "Q20", "Q21", "Q22", "Q23", "Q27"].includes(id))
|
|
241
|
+
if (["Q04", "Q05", "Q06", "Q07", "Q08", "Q11", "Q12", "Q13", "Q20", "Q21", "Q22", "Q23", "Q27", "Q30"].includes(id))
|
|
241
242
|
return "retained_revision";
|
|
242
243
|
if (id === "Q24") return "content_addressed";
|
|
243
244
|
return "scalar_snapshot";
|
|
244
245
|
}
|
|
245
246
|
|
|
246
247
|
function queryDisposition(id: string): Record<Adapter, AdapterDisposition> {
|
|
247
|
-
if (["Q23", "Q24", "Q25", "Q26", "Q27", "Q28", "Q29"].includes(id))
|
|
248
|
+
if (["Q23", "Q24", "Q25", "Q26", "Q27", "Q28", "Q29", "Q30"].includes(id))
|
|
248
249
|
return dispositions({ telegram: "prohibited", discord: "prohibited", slack: "prohibited" });
|
|
249
250
|
return dispositions();
|
|
250
251
|
}
|
package/src/sdk/session.ts
CHANGED
|
@@ -41,6 +41,8 @@ import {
|
|
|
41
41
|
import { type AsyncJob, AsyncJobManager, isBackgroundJobSupportEnabled, jobElapsedMs } from "../async";
|
|
42
42
|
import { loadCapability } from "../capability";
|
|
43
43
|
import { type Rule, ruleCapability, setActiveRules } from "../capability/rule";
|
|
44
|
+
import { resolveModelProfileName } from "../config/model-profile-contract";
|
|
45
|
+
import { resolveProfileBindings } from "../config/model-profiles";
|
|
44
46
|
import { kNoAuth, ModelRegistry } from "../config/model-registry";
|
|
45
47
|
import {
|
|
46
48
|
formatModelString,
|
|
@@ -115,6 +117,7 @@ import {
|
|
|
115
117
|
import { NotificationSessionController } from "../sdk/bus/session-control";
|
|
116
118
|
import { shouldHostSdk } from "../sdk/host";
|
|
117
119
|
import { createSdkSessionRuntimeExtension, registerSdkOnlyNotificationCommand } from "../sdk/host/session-runtime";
|
|
120
|
+
import { createSdkWebSocketTransport } from "../sdk/host/websocket-transport";
|
|
118
121
|
import type { SecretObfuscator } from "../secrets";
|
|
119
122
|
import { AgentSession, type ForkContextSeed } from "../session/agent-session";
|
|
120
123
|
import { resolveAuthBrokerConfig } from "../session/auth-broker-config";
|
|
@@ -325,6 +328,8 @@ export interface CreateAgentSessionOptions {
|
|
|
325
328
|
/** Raw model pattern string (e.g. from --model CLI flag) to resolve after extensions load.
|
|
326
329
|
* Used when model lookup is deferred because extension-provided models aren't registered yet. */
|
|
327
330
|
modelPattern?: string;
|
|
331
|
+
/** Active profile inherited by a nested SDK/subagent session. */
|
|
332
|
+
activeModelProfile?: string;
|
|
328
333
|
/** Thinking selector. Default: from settings, else unset */
|
|
329
334
|
thinkingLevel?: ThinkingLevel;
|
|
330
335
|
/** Runtime substitution metadata for the initial model_change session event. */
|
|
@@ -337,6 +342,8 @@ export interface CreateAgentSessionOptions {
|
|
|
337
342
|
/** Optional provider-facing session identifier for prompt caches and sticky auth selection.
|
|
338
343
|
* Keeps persisted session files isolated while reusing provider-side caches. */
|
|
339
344
|
providerSessionId?: string;
|
|
345
|
+
/** Optional credential-selection session identity, distinct from provider transport/cache identity. */
|
|
346
|
+
credentialSessionId?: string;
|
|
340
347
|
/** Runtime credential selector for multi-account auth pools. */
|
|
341
348
|
credentialSelector?: { provider?: string; selector: AuthCredentialSelector; raw: string };
|
|
342
349
|
|
|
@@ -1287,31 +1294,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1287
1294
|
}
|
|
1288
1295
|
applyConfiguredSearchTimeout(settings);
|
|
1289
1296
|
|
|
1290
|
-
const imageProvider = settings.get("providers.image");
|
|
1291
|
-
const imageModel = settings.get("providers.imageModel");
|
|
1292
|
-
const imageCustomUrl = settings.get("providers.imageCustomUrl");
|
|
1293
|
-
const imageCustomKey = settings.get("providers.imageCustomKey");
|
|
1294
|
-
const imageCustomKeyEnv = settings.get("providers.imageCustomKeyEnv");
|
|
1295
|
-
if (
|
|
1296
|
-
imageProvider === "auto" ||
|
|
1297
|
-
imageProvider === "openai" ||
|
|
1298
|
-
imageProvider === "gemini" ||
|
|
1299
|
-
imageProvider === "openrouter" ||
|
|
1300
|
-
imageProvider === "antigravity" ||
|
|
1301
|
-
imageProvider === "alibaba" ||
|
|
1302
|
-
imageProvider === "custom"
|
|
1303
|
-
) {
|
|
1304
|
-
const { setConfiguredImageModel, setPreferredImageProvider } = await import("../tools/image-gen");
|
|
1305
|
-
setPreferredImageProvider(imageProvider === "custom" ? "auto" : imageProvider);
|
|
1306
|
-
setConfiguredImageModel({
|
|
1307
|
-
provider: imageProvider,
|
|
1308
|
-
model: imageModel ?? null,
|
|
1309
|
-
customUrl: imageCustomUrl,
|
|
1310
|
-
customKey: imageCustomKey,
|
|
1311
|
-
customKeyEnv: imageCustomKeyEnv,
|
|
1312
|
-
});
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
1297
|
const sessionManager =
|
|
1316
1298
|
options.sessionManager ??
|
|
1317
1299
|
(await logger.time("sessionManager", async () => {
|
|
@@ -1323,6 +1305,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1323
1305
|
// session_id upstream, where session-owning transports reject the extra
|
|
1324
1306
|
// downstreams (owner_busy) and degrade those turns to uncached HTTP.
|
|
1325
1307
|
const providerSessionId = options.providerSessionId ?? logicalSessionId;
|
|
1308
|
+
const credentialSessionId = options.credentialSessionId ?? providerSessionId;
|
|
1326
1309
|
const modelApiKeyAvailability = new Map<string, boolean>();
|
|
1327
1310
|
const getModelAvailabilityKey = (candidate: Model): string =>
|
|
1328
1311
|
`${candidate.provider}\u0000${candidate.baseUrl ?? ""}`;
|
|
@@ -1345,7 +1328,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1345
1328
|
return false;
|
|
1346
1329
|
}
|
|
1347
1330
|
const key = await modelRegistry
|
|
1348
|
-
.getApiKey(candidate,
|
|
1331
|
+
.getApiKey(candidate, credentialSessionId, { credentialSelector })
|
|
1349
1332
|
.catch(error => {
|
|
1350
1333
|
if (credentialSelector) {
|
|
1351
1334
|
logger.debug("Credential selector did not match model availability candidate", {
|
|
@@ -1395,6 +1378,20 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1395
1378
|
const modelMatchPreferences = {
|
|
1396
1379
|
usageOrder: settings.getStorage()?.getModelUsageOrder(),
|
|
1397
1380
|
};
|
|
1381
|
+
const persistedProfiles = modelRegistry.getModelProfiles();
|
|
1382
|
+
const resolvedInheritedProfileName = options.activeModelProfile
|
|
1383
|
+
? resolveModelProfileName(options.activeModelProfile, persistedProfiles)
|
|
1384
|
+
: undefined;
|
|
1385
|
+
const acceptedInheritedProfileName =
|
|
1386
|
+
resolvedInheritedProfileName && persistedProfiles.has(resolvedInheritedProfileName)
|
|
1387
|
+
? resolvedInheritedProfileName
|
|
1388
|
+
: undefined;
|
|
1389
|
+
const inheritedProfileOwnsDefault = acceptedInheritedProfileName
|
|
1390
|
+
? resolveProfileBindings(persistedProfiles.get(acceptedInheritedProfileName)!).defaultSelector !== undefined
|
|
1391
|
+
: false;
|
|
1392
|
+
const settingsProfileAliasIntent: { aliasIntent: "preset-equivalent" } | undefined = inheritedProfileOwnsDefault
|
|
1393
|
+
? { aliasIntent: "preset-equivalent" }
|
|
1394
|
+
: undefined;
|
|
1398
1395
|
const allowedModels = await logger.time("resolveAllowedModels", () =>
|
|
1399
1396
|
resolveAllowedModels(modelRegistry, settings, modelMatchPreferences),
|
|
1400
1397
|
);
|
|
@@ -1403,20 +1400,41 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1403
1400
|
settings,
|
|
1404
1401
|
matchPreferences: modelMatchPreferences,
|
|
1405
1402
|
modelRegistry,
|
|
1403
|
+
...(settingsProfileAliasIntent ?? {}),
|
|
1404
|
+
sessionId: providerSessionId,
|
|
1405
|
+
credentialSessionId,
|
|
1406
1406
|
}),
|
|
1407
1407
|
);
|
|
1408
1408
|
let model = options.model;
|
|
1409
1409
|
let modelFallbackMessage: string | undefined;
|
|
1410
1410
|
const resumeModelBehavior = settings.get("session.resumeModelBehavior");
|
|
1411
|
-
const persistedDefaultChain = existingSession.configuredModelChains.default
|
|
1411
|
+
const persistedDefaultChain = existingSession.configuredModelChains.default;
|
|
1412
1412
|
const defaultModelEntries =
|
|
1413
1413
|
resumeModelBehavior === "useCurrentDefault"
|
|
1414
1414
|
? []
|
|
1415
|
-
: persistedDefaultChain && persistedDefaultChain.length > 0
|
|
1416
|
-
? persistedDefaultChain
|
|
1415
|
+
: persistedDefaultChain?.entries && persistedDefaultChain.entries.length > 0
|
|
1416
|
+
? persistedDefaultChain.entries
|
|
1417
1417
|
: existingSession.models.default
|
|
1418
1418
|
? [existingSession.models.default]
|
|
1419
1419
|
: [];
|
|
1420
|
+
const persistedProfileName =
|
|
1421
|
+
persistedDefaultChain?.origin === "profile-activation" ? persistedDefaultChain.identity : undefined;
|
|
1422
|
+
const resolvedPersistedProfileName = persistedProfileName
|
|
1423
|
+
? resolveModelProfileName(persistedProfileName, persistedProfiles)
|
|
1424
|
+
: undefined;
|
|
1425
|
+
const acceptedPersistedProfileName =
|
|
1426
|
+
resolvedPersistedProfileName &&
|
|
1427
|
+
persistedProfiles.has(resolvedPersistedProfileName) &&
|
|
1428
|
+
(!acceptedInheritedProfileName || acceptedInheritedProfileName === resolvedPersistedProfileName) &&
|
|
1429
|
+
resumeModelBehavior !== "useCurrentDefault"
|
|
1430
|
+
? resolvedPersistedProfileName
|
|
1431
|
+
: undefined;
|
|
1432
|
+
const persistedProfileOwnsDefault = acceptedPersistedProfileName
|
|
1433
|
+
? resolveProfileBindings(persistedProfiles.get(acceptedPersistedProfileName)!).defaultSelector !== undefined
|
|
1434
|
+
: false;
|
|
1435
|
+
const startupActiveModelProfile =
|
|
1436
|
+
acceptedInheritedProfileName ??
|
|
1437
|
+
(!hasExplicitModel && acceptedPersistedProfileName ? acceptedPersistedProfileName : undefined);
|
|
1420
1438
|
// If session has data, restore its configured default chain rather than the
|
|
1421
1439
|
// scalar runtime model, which may be a stale fallback from the prior run.
|
|
1422
1440
|
if (!hasExplicitModel && !model && hasExistingSession && defaultModelEntries.length > 0) {
|
|
@@ -1425,8 +1443,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1425
1443
|
defaultModelEntries,
|
|
1426
1444
|
modelRegistry,
|
|
1427
1445
|
settings,
|
|
1428
|
-
|
|
1429
|
-
{
|
|
1446
|
+
credentialSessionId,
|
|
1447
|
+
{
|
|
1448
|
+
managedFallback: defaultModelEntries.length > 1,
|
|
1449
|
+
canonicalSessionId: providerSessionId,
|
|
1450
|
+
...(persistedProfileOwnsDefault ? { aliasIntent: "preset-equivalent" as const } : {}),
|
|
1451
|
+
},
|
|
1430
1452
|
);
|
|
1431
1453
|
model = restoredDefaultResolution.model;
|
|
1432
1454
|
if (!model) modelFallbackMessage = `Could not restore model ${defaultModelEntries.join(" -> ")}`;
|
|
@@ -1671,6 +1693,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1671
1693
|
trackEvalExecution: (execution, abortController) =>
|
|
1672
1694
|
session ? session.trackEvalExecution(execution, abortController) : execution,
|
|
1673
1695
|
getSessionId: () => sessionManager.getSessionId?.() ?? null,
|
|
1696
|
+
getCredentialSessionId: () => session?.credentialSessionId ?? credentialSessionId,
|
|
1674
1697
|
getMcpManager: () => mcpManager ?? options.inheritedMcpManager,
|
|
1675
1698
|
isManagedSessionDestination: () => sessionManager.isManagedDestination(),
|
|
1676
1699
|
getActiveSkillState: () => session?.getActiveSkillState(),
|
|
@@ -1771,6 +1794,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1771
1794
|
isManagedDestination: () => sessionManager.isManagedDestination(),
|
|
1772
1795
|
getManagedLegacyLocalMigrationSource: () => sessionManager.getManagedLegacyLocalMigrationSource(),
|
|
1773
1796
|
getSessionId: () => sessionManager.getSessionId(),
|
|
1797
|
+
getCredentialSessionId: () => credentialSessionId,
|
|
1774
1798
|
};
|
|
1775
1799
|
if (!options.parentTaskPrefix) {
|
|
1776
1800
|
setActiveSkills(skills);
|
|
@@ -1782,6 +1806,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1782
1806
|
disposeLocalProtocolOverride = LocalProtocolHandler.installOverride(options.localProtocolOptions);
|
|
1783
1807
|
}
|
|
1784
1808
|
toolSession.getArtifactsDir = getArtifactsDir;
|
|
1809
|
+
// Live parent profile accessor for task/subagent dispatch: profile-owned
|
|
1810
|
+
// persisted model overrides may resolve through preset-equivalent aliases
|
|
1811
|
+
// only while an active profile claims them (see task/executor.ts).
|
|
1812
|
+
(toolSession as ToolSession & { getActiveModelProfile?: () => string | undefined }).getActiveModelProfile = () =>
|
|
1813
|
+
session?.getActiveModelProfile();
|
|
1785
1814
|
toolSession.getAuthorizedArtifactsDirs = () => {
|
|
1786
1815
|
const manager = sessionManager.getArtifactManager();
|
|
1787
1816
|
return manager ? [manager.dir] : [];
|
|
@@ -1806,9 +1835,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1806
1835
|
const pluginMcpToolNames: string[] = [];
|
|
1807
1836
|
let deferredExactMcpConfig: { manager: MCPManager; configPath: string } | undefined;
|
|
1808
1837
|
|
|
1809
|
-
// Add image tools when
|
|
1838
|
+
// Add image tools when an image role model is configured.
|
|
1810
1839
|
const { getImageGenTools } = await import("../tools/image-gen");
|
|
1811
|
-
const imageGenTools = await logger.time("getImageGenTools", () => getImageGenTools(modelRegistry,
|
|
1840
|
+
const imageGenTools = await logger.time("getImageGenTools", () => getImageGenTools(modelRegistry, settings));
|
|
1812
1841
|
if (imageGenTools.length > 0) {
|
|
1813
1842
|
customTools.push(...(imageGenTools as unknown as CustomTool[]));
|
|
1814
1843
|
}
|
|
@@ -2121,8 +2150,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2121
2150
|
});
|
|
2122
2151
|
} else if (sdkHostEligible) {
|
|
2123
2152
|
registerSdkOnlyNotificationCommand(api);
|
|
2124
|
-
const { createSdkWebSocketTransport } = await import("../sdk/host/websocket-transport");
|
|
2125
2153
|
createSdkSessionRuntimeExtension(api, {
|
|
2154
|
+
agentDir,
|
|
2155
|
+
brokerRegistrationRequired: lifecycleStartupCapability !== undefined,
|
|
2126
2156
|
createTransport: input => createSdkWebSocketTransport(input),
|
|
2127
2157
|
settings,
|
|
2128
2158
|
configOverrides: new Map(),
|
|
@@ -2196,6 +2226,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2196
2226
|
};
|
|
2197
2227
|
const { model: resolved } = parseModelPattern(options.modelPattern, availableModels, matchPreferences, {
|
|
2198
2228
|
modelRegistry,
|
|
2229
|
+
sessionId: logicalSessionId,
|
|
2230
|
+
credentialSessionId,
|
|
2199
2231
|
});
|
|
2200
2232
|
if (resolved) {
|
|
2201
2233
|
model = resolved;
|
|
@@ -2283,6 +2315,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2283
2315
|
const getSessionContext = () => ({
|
|
2284
2316
|
sessionManager: createReadonlySessionManager(sessionManager),
|
|
2285
2317
|
modelRegistry,
|
|
2318
|
+
get credentialSessionId() {
|
|
2319
|
+
return session.credentialSessionId;
|
|
2320
|
+
},
|
|
2286
2321
|
model: agent.state.model,
|
|
2287
2322
|
isIdle: () => !session.isStreaming,
|
|
2288
2323
|
hasQueuedMessages: () => session.queuedMessageCount > 0,
|
|
@@ -2312,6 +2347,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2312
2347
|
const customToolContext = (): CustomToolContext => ({
|
|
2313
2348
|
sessionManager: createReadonlySessionManager(sessionManager),
|
|
2314
2349
|
modelRegistry,
|
|
2350
|
+
get credentialSessionId() {
|
|
2351
|
+
return session?.credentialSessionId ?? credentialSessionId;
|
|
2352
|
+
},
|
|
2315
2353
|
model: agent?.state.model,
|
|
2316
2354
|
isIdle: () => !session?.isStreaming,
|
|
2317
2355
|
hasQueuedMessages: () => (session?.queuedMessageCount ?? 0) > 0,
|
|
@@ -2843,14 +2881,20 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2843
2881
|
getApiKey: async provider => {
|
|
2844
2882
|
// Read agent.sessionId at call time so credential selection stays aligned
|
|
2845
2883
|
// with metadataResolver after /new, fork, resume, or branch switches.
|
|
2846
|
-
const key = await modelRegistry.getApiKeyForProvider(
|
|
2884
|
+
const key = await modelRegistry.getApiKeyForProvider(
|
|
2885
|
+
provider,
|
|
2886
|
+
options.credentialSessionId ?? agent.providerSessionId ?? agent.sessionId,
|
|
2887
|
+
);
|
|
2847
2888
|
if (!key) {
|
|
2848
2889
|
throw new Error(`No API key found for provider "${provider}"`);
|
|
2849
2890
|
}
|
|
2850
2891
|
return key;
|
|
2851
2892
|
},
|
|
2852
2893
|
getAuthCredentialType: provider =>
|
|
2853
|
-
modelRegistry.getSessionCredentialType(
|
|
2894
|
+
modelRegistry.getSessionCredentialType(
|
|
2895
|
+
provider,
|
|
2896
|
+
options.credentialSessionId ?? agent.providerSessionId ?? agent.sessionId,
|
|
2897
|
+
),
|
|
2854
2898
|
streamFn: async (streamModel, context, streamOptions) => {
|
|
2855
2899
|
const requestStartedAt = performance.now();
|
|
2856
2900
|
let stream: Awaited<ReturnType<typeof streamSimple>>;
|
|
@@ -2858,15 +2902,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2858
2902
|
stream = await streamSimple(streamModel, context, {
|
|
2859
2903
|
...streamOptions,
|
|
2860
2904
|
onAuthError: async (provider, oldKey, error) => {
|
|
2905
|
+
const credentialSessionId =
|
|
2906
|
+
options.credentialSessionId ?? agent.providerSessionId ?? agent.sessionId;
|
|
2861
2907
|
await modelRegistry.authStorage.invalidateCredentialMatching(provider, oldKey, {
|
|
2862
2908
|
signal: streamOptions?.signal,
|
|
2863
|
-
sessionId:
|
|
2909
|
+
sessionId: credentialSessionId,
|
|
2864
2910
|
});
|
|
2865
2911
|
logger.debug("Retrying provider request after credential invalidation", {
|
|
2866
2912
|
provider,
|
|
2867
2913
|
error: error instanceof Error ? error.message : String(error),
|
|
2868
2914
|
});
|
|
2869
|
-
return modelRegistry.getApiKeyForProvider(provider,
|
|
2915
|
+
return modelRegistry.getApiKeyForProvider(provider, credentialSessionId);
|
|
2870
2916
|
},
|
|
2871
2917
|
});
|
|
2872
2918
|
} catch (error) {
|
|
@@ -3028,10 +3074,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3028
3074
|
agentId: resolvedAgentId,
|
|
3029
3075
|
agentRegistry,
|
|
3030
3076
|
providerSessionId: options.providerSessionId,
|
|
3077
|
+
credentialSessionId: options.credentialSessionId,
|
|
3031
3078
|
providerCacheSessionId: providerSessionId,
|
|
3032
3079
|
forkContextSeed: options.forkContextSeed,
|
|
3033
3080
|
providerSessionState: options.providerSessionState,
|
|
3034
3081
|
});
|
|
3082
|
+
session.setActiveModelProfile(startupActiveModelProfile);
|
|
3035
3083
|
session.configWarnings.push(...contextFileWarnings);
|
|
3036
3084
|
hasSession = true;
|
|
3037
3085
|
if (asyncJobManager) {
|
|
@@ -3081,7 +3129,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3081
3129
|
if (codexTransport.websocketPreferred) {
|
|
3082
3130
|
void (async () => {
|
|
3083
3131
|
try {
|
|
3084
|
-
const codexPrewarmApiKey = await modelRegistry.getApiKey(codexModel,
|
|
3132
|
+
const codexPrewarmApiKey = await modelRegistry.getApiKey(codexModel, credentialSessionId);
|
|
3085
3133
|
if (!codexPrewarmApiKey) return;
|
|
3086
3134
|
await logger.time("prewarmOpenAICodexResponses", prewarmOpenAICodexResponses, codexModel, {
|
|
3087
3135
|
apiKey: codexPrewarmApiKey,
|
|
@@ -44,6 +44,7 @@ export type RelayOptions = {
|
|
|
44
44
|
onTransportError: (error: TransportError) => void;
|
|
45
45
|
webSocketFactory?: (url: string) => RelayWebSocket;
|
|
46
46
|
signal?: AbortSignal;
|
|
47
|
+
validateDownstreamFrame?: (frame: string) => boolean;
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
type QueuedFrame = { bytes: Buffer; accounted: boolean };
|
|
@@ -193,6 +194,8 @@ export async function startRelayPair(options: RelayOptions): Promise<RelayPair>
|
|
|
193
194
|
return fail({ type: "transport_error", code: "protocol_error", direction: "downstream->ws" });
|
|
194
195
|
if (line.length > REQUEST_FRAME_BYTES)
|
|
195
196
|
return fail({ type: "transport_error", code: "frame_oversize", direction: "downstream->ws" });
|
|
197
|
+
if (options.validateDownstreamFrame && !options.validateDownstreamFrame(line.toString("utf8")))
|
|
198
|
+
return fail({ type: "transport_error", code: "protocol_error", direction: "downstream->ws" });
|
|
196
199
|
enqueue(toWs, "downstream->ws", line);
|
|
197
200
|
if (closed) return;
|
|
198
201
|
newline = downstreamBuffer.indexOf(0x0a);
|