@gajae-code/coding-agent 0.16.1 → 0.16.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/dist/types/cli/args.d.ts +2 -2
- package/dist/types/cli/root-flags.d.ts +4 -2
- package/dist/types/cli.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +1 -1
- package/dist/types/config/autorouting-tier-map.d.ts +3 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +9 -0
- package/dist/types/config/settings-schema.d.ts +29 -1
- package/dist/types/discovery/claude-plugins.d.ts +3 -1
- package/dist/types/discovery/helpers.d.ts +2 -0
- package/dist/types/extensibility/gjc-plugins/mcp-policy.d.ts +16 -0
- package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +1 -1
- package/dist/types/extensibility/shared-events.d.ts +1 -1
- package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -2
- package/dist/types/modes/components/settings-selector.d.ts +6 -1
- package/dist/types/modes/components/status-line/command.d.ts +35 -0
- package/dist/types/modes/components/status-line/index.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +5 -0
- package/dist/types/modes/components/tool-status-header.d.ts +14 -0
- package/dist/types/modes/controllers/event-controller.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/runtime-mcp/client.d.ts +9 -1
- package/dist/types/runtime-mcp/manager.d.ts +7 -1
- package/dist/types/runtime-mcp/pool.d.ts +22 -3
- package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
- package/dist/types/runtime-mcp/types.d.ts +22 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +12 -0
- package/dist/types/sdk/bus/slack-live-provider.d.ts +6 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +12 -0
- package/dist/types/sdk/cli/rows.d.ts +7 -0
- package/dist/types/sdk/host/events.d.ts +1 -0
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/session-runtime.d.ts +23 -0
- package/dist/types/sdk/session-api-key.d.ts +26 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +9 -0
- package/dist/types/setup/paseo/announce.d.ts +111 -0
- package/dist/types/setup/paseo/setup-deps.d.ts +2 -1
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +2 -1
- package/dist/types/tools/fetch.d.ts +7 -0
- package/dist/types/tools/index.d.ts +8 -2
- package/dist/types/tools/path-utils.d.ts +2 -0
- package/dist/types/tools/search.d.ts +6 -0
- package/dist/types/utils/external-editor.d.ts +5 -0
- package/package.json +7 -7
- package/scripts/capture-status-line-custom-editor-showcase.ts +151 -0
- package/scripts/generate-sdk-operation-inventory.ts +9 -0
- package/scripts/verify-status-line-custom-editor-showcase.ts +309 -0
- package/src/cli/args.ts +20 -9
- package/src/cli/root-flags.ts +5 -3
- package/src/config/autorouting-tier-map.ts +1 -0
- package/src/config/file-lock.ts +181 -17
- package/src/config/model-preset-registry.ts +5 -3
- package/src/config/model-profiles.ts +40 -0
- package/src/config/model-registry.ts +57 -11
- package/src/config/model-resolver.ts +48 -0
- package/src/config/settings-schema.ts +33 -1
- package/src/config/settings.ts +1 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +11 -11
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +13 -2
- package/src/discovery/builtin.ts +13 -4
- package/src/discovery/claude-plugins.ts +12 -4
- package/src/discovery/helpers.ts +10 -2
- package/src/extensibility/gjc-plugins/compiler.ts +208 -58
- package/src/extensibility/gjc-plugins/installer.ts +13 -12
- package/src/extensibility/gjc-plugins/mcp-policy.ts +81 -52
- package/src/extensibility/gjc-plugins/paths.ts +13 -0
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +758 -16
- package/src/extensibility/runtime-skill-discovery.ts +77 -151
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/skills.ts +44 -16
- package/src/gjc-runtime/repository-binding.ts +1 -1
- package/src/gjc-runtime/session-state-lock.ts +94 -6
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/main.ts +72 -34
- package/src/modes/DESIGN.md +54 -0
- package/src/modes/acp/acp-agent.ts +29 -14
- package/src/modes/components/assistant-message.ts +22 -9
- package/src/modes/components/model-selector.ts +166 -33
- package/src/modes/components/settings-selector.ts +1075 -235
- package/src/modes/components/status-line/command.ts +190 -0
- package/src/modes/components/status-line/index.ts +1 -0
- package/src/modes/components/status-line/segments.ts +32 -5
- package/src/modes/components/status-line/types.ts +5 -0
- package/src/modes/components/tool-status-header.ts +393 -21
- package/src/modes/controllers/command-controller.ts +2 -0
- package/src/modes/controllers/event-controller.ts +148 -5
- package/src/modes/controllers/selector-controller.ts +21 -0
- package/src/modes/interactive-mode.ts +47 -1
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/system/system-prompt.md +8 -0
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/job.md +1 -1
- package/src/prompts/tools/search.md +2 -0
- package/src/runtime-mcp/client.ts +60 -17
- package/src/runtime-mcp/manager.ts +171 -14
- package/src/runtime-mcp/pool.test.ts +209 -0
- package/src/runtime-mcp/pool.ts +224 -23
- package/src/runtime-mcp/transports/stdio.ts +158 -24
- package/src/runtime-mcp/types.ts +24 -0
- package/src/sdk/acp/adapter.ts +1 -1
- package/src/sdk/broker/broker.ts +5 -4
- package/src/sdk/bus/chat-daemon-control.ts +1 -1
- package/src/sdk/bus/chat-daemon-runtime.ts +44 -1
- package/src/sdk/bus/slack-daemon.ts +92 -2
- package/src/sdk/bus/slack-live-provider.ts +12 -0
- package/src/sdk/bus/slack-provider.ts +5 -0
- package/src/sdk/cli/rows.ts +47 -2
- package/src/sdk/cli/session-cli.ts +47 -23
- package/src/sdk/host/events.ts +25 -2
- package/src/sdk/host/host.ts +4 -1
- package/src/sdk/host/session-runtime.test.ts +36 -25
- package/src/sdk/host/session-runtime.ts +279 -43
- package/src/sdk/protocol/operation-inventory.generated.json +66 -0
- package/src/sdk/router/session-router.ts +60 -37
- package/src/sdk/session-api-key.ts +94 -0
- package/src/sdk/session.ts +73 -30
- package/src/sdk/transport/serve-cli.ts +3 -3
- package/src/session/agent-session.ts +123 -38
- package/src/session/contribution-prep.ts +214 -21
- package/src/setup/paseo/announce.ts +450 -0
- package/src/setup/paseo/setup-deps.ts +15 -3
- package/src/slash-commands/builtin-registry.ts +1 -0
- package/src/system-prompt.ts +4 -0
- package/src/task/executor.ts +2 -1
- package/src/task/index.ts +17 -4
- package/src/tools/bash.ts +50 -21
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +29 -1
- package/src/tools/index.ts +8 -2
- package/src/tools/job.ts +8 -2
- package/src/tools/monitor.ts +8 -2
- package/src/tools/path-utils.ts +7 -0
- package/src/tools/search.ts +116 -58
- package/src/tools/skill.ts +22 -2
- package/src/tools/subagent.ts +3 -1
- package/src/tools/tool-catalog.generated.ts +9 -2
- package/src/utils/external-editor.ts +16 -2
- package/src/web/insane/bridge.ts +113 -23
- package/src/web/search/providers/perplexity.ts +102 -41
|
@@ -2841,6 +2841,50 @@
|
|
|
2841
2841
|
"testIds": "not_applicable"
|
|
2842
2842
|
}
|
|
2843
2843
|
},
|
|
2844
|
+
{
|
|
2845
|
+
"sourceId": "agent_session:awaitDisposeCompletion",
|
|
2846
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2847
|
+
"sourceKind": "agent_session",
|
|
2848
|
+
"decision": "exclude",
|
|
2849
|
+
"rationale": "test-only disposal completion seam, not a user-facing SDK control seam",
|
|
2850
|
+
"exclusionMetadata": {
|
|
2851
|
+
"adapterMappings": "not_applicable",
|
|
2852
|
+
"testIds": "not_applicable"
|
|
2853
|
+
}
|
|
2854
|
+
},
|
|
2855
|
+
{
|
|
2856
|
+
"sourceId": "agent_session:setDisposeTimeoutForTests",
|
|
2857
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2858
|
+
"sourceKind": "agent_session",
|
|
2859
|
+
"decision": "exclude",
|
|
2860
|
+
"rationale": "test-only disposal timeout seam, not a user-facing SDK control seam",
|
|
2861
|
+
"exclusionMetadata": {
|
|
2862
|
+
"adapterMappings": "not_applicable",
|
|
2863
|
+
"testIds": "not_applicable"
|
|
2864
|
+
}
|
|
2865
|
+
},
|
|
2866
|
+
{
|
|
2867
|
+
"sourceId": "agent_session:trackPostPromptTaskForTests",
|
|
2868
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2869
|
+
"sourceKind": "agent_session",
|
|
2870
|
+
"decision": "exclude",
|
|
2871
|
+
"rationale": "test-only post-prompt task 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:requestWorkerIntegrationForTests",
|
|
2879
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2880
|
+
"sourceKind": "agent_session",
|
|
2881
|
+
"decision": "exclude",
|
|
2882
|
+
"rationale": "test-only worker integration seam, not a user-facing SDK control seam",
|
|
2883
|
+
"exclusionMetadata": {
|
|
2884
|
+
"adapterMappings": "not_applicable",
|
|
2885
|
+
"testIds": "not_applicable"
|
|
2886
|
+
}
|
|
2887
|
+
},
|
|
2844
2888
|
{
|
|
2845
2889
|
"sourceId": "agent_session:closeWriterStrict",
|
|
2846
2890
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
@@ -2885,6 +2929,17 @@
|
|
|
2885
2929
|
"testIds": "not_applicable"
|
|
2886
2930
|
}
|
|
2887
2931
|
},
|
|
2932
|
+
{
|
|
2933
|
+
"sourceId": "agent_session:runWithPromptAdmissionForTests",
|
|
2934
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2935
|
+
"sourceKind": "agent_session",
|
|
2936
|
+
"decision": "exclude",
|
|
2937
|
+
"rationale": "test-only prompt admission seam, not a user-facing SDK control seam",
|
|
2938
|
+
"exclusionMetadata": {
|
|
2939
|
+
"adapterMappings": "not_applicable",
|
|
2940
|
+
"testIds": "not_applicable"
|
|
2941
|
+
}
|
|
2942
|
+
},
|
|
2888
2943
|
{
|
|
2889
2944
|
"sourceId": "agent_session:queueCoordinatorRuntimeStatePersistForTests",
|
|
2890
2945
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
@@ -3912,6 +3967,17 @@
|
|
|
3912
3967
|
"testIds": "not_applicable"
|
|
3913
3968
|
}
|
|
3914
3969
|
},
|
|
3970
|
+
{
|
|
3971
|
+
"sourceId": "agent_session:reloadSkills",
|
|
3972
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
3973
|
+
"sourceKind": "agent_session",
|
|
3974
|
+
"decision": "exclude",
|
|
3975
|
+
"rationale": "internal marketplace lifecycle refresh for the session skill catalog; not an independent SDK ingress operation",
|
|
3976
|
+
"exclusionMetadata": {
|
|
3977
|
+
"adapterMappings": "not_applicable",
|
|
3978
|
+
"testIds": "not_applicable"
|
|
3979
|
+
}
|
|
3980
|
+
},
|
|
3915
3981
|
{
|
|
3916
3982
|
"sourceId": "agent_session:retireWorkspaceTreeForRescope",
|
|
3917
3983
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
@@ -338,6 +338,8 @@ type AttachedSession = {
|
|
|
338
338
|
replayPending: boolean;
|
|
339
339
|
replayPendingSince?: number;
|
|
340
340
|
replayPendingToken?: object;
|
|
341
|
+
drainingHeldSeq?: number;
|
|
342
|
+
drainingHeldBatch?: HeldFrame[];
|
|
341
343
|
readyTail: Promise<void>;
|
|
342
344
|
readonly publication: { promise: Promise<void>; resolve: () => void; reject: (reason?: unknown) => void };
|
|
343
345
|
dispose: () => void;
|
|
@@ -1899,44 +1901,53 @@ export class SessionRouter {
|
|
|
1899
1901
|
}
|
|
1900
1902
|
|
|
1901
1903
|
async #reinitializeAttachment(attached: AttachedSession): Promise<void> {
|
|
1902
|
-
|
|
1904
|
+
// A prior ready tail may be waiting for publication work carried by the dead
|
|
1905
|
+
// socket. Chaining the reconnect behind it prevents the replacement from even
|
|
1906
|
+
// issuing replay. Replace its hold-array identity to fence that tail, but carry
|
|
1907
|
+
// forward frames it had not started draining; the active draining sequence is
|
|
1908
|
+
// already represented by the independent frame tail.
|
|
1909
|
+
if (attached.barrier.held) {
|
|
1910
|
+
const carried = [...(attached.drainingHeldBatch ?? []), ...attached.barrier.held];
|
|
1911
|
+
attached.drainingHeldBatch = undefined;
|
|
1912
|
+
attached.barrier.held = carried
|
|
1913
|
+
.filter(entry => entry.seq !== attached.drainingHeldSeq)
|
|
1914
|
+
.filter((entry, index, entries) => entries.findIndex(candidate => candidate.seq === entry.seq) === index);
|
|
1915
|
+
}
|
|
1903
1916
|
const replayToken = {};
|
|
1904
1917
|
attached.replayPending = true;
|
|
1905
1918
|
attached.replayPendingSince = Date.now();
|
|
1906
1919
|
attached.replayPendingToken = replayToken;
|
|
1907
|
-
const current =
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
.
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
if (this.#attachmentLive(attached)) await this.#replayAttachment(attached, attached.cursor.seq);
|
|
1939
|
-
});
|
|
1920
|
+
const current = Promise.resolve().then(async () => {
|
|
1921
|
+
if (!this.#attachmentLive(attached)) return;
|
|
1922
|
+
const proven = await this.#readProvenEndpoint(attached.indexed);
|
|
1923
|
+
if (
|
|
1924
|
+
!proven ||
|
|
1925
|
+
proven.endpoint.url !== attached.endpoint.url ||
|
|
1926
|
+
proven.endpoint.token !== attached.endpoint.token ||
|
|
1927
|
+
proven.endpoint.pid !== attached.pid ||
|
|
1928
|
+
!sameEndpointIdentity(attached.endpointIdentity, proven.identity)
|
|
1929
|
+
) {
|
|
1930
|
+
await this.#retireAttachment(attached, proven ? "replaced_same_generation" : undefined);
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
attached.initializingPublication = true;
|
|
1934
|
+
try {
|
|
1935
|
+
void Promise.resolve()
|
|
1936
|
+
.then(() => this.#deps.onNotificationSubscriptionReady?.(attached.notificationSubscription))
|
|
1937
|
+
.catch(error => {
|
|
1938
|
+
this.#detachNotification(attached, "cancelled");
|
|
1939
|
+
logger.warn(`SDK notification subscription reconnect hook failed locally: ${String(error)}`);
|
|
1940
|
+
});
|
|
1941
|
+
await this.#deps.onAttachmentReady?.(attached.capability);
|
|
1942
|
+
await this.#deps.onReplayReady?.();
|
|
1943
|
+
} catch {
|
|
1944
|
+
await this.#retireAttachment(attached);
|
|
1945
|
+
return;
|
|
1946
|
+
} finally {
|
|
1947
|
+
attached.initializingPublication = false;
|
|
1948
|
+
}
|
|
1949
|
+
if (this.#attachmentLive(attached)) await this.#replayAttachment(attached, attached.cursor.seq);
|
|
1950
|
+
});
|
|
1940
1951
|
attached.readyTail = current.finally(() => {
|
|
1941
1952
|
if (attached.replayPendingToken === replayToken) {
|
|
1942
1953
|
attached.replayPending = false;
|
|
@@ -2294,7 +2305,19 @@ export class SessionRouter {
|
|
|
2294
2305
|
return;
|
|
2295
2306
|
}
|
|
2296
2307
|
const batch = held.splice(0, held.length).sort((left, right) => left.seq - right.seq);
|
|
2297
|
-
|
|
2308
|
+
attached.drainingHeldBatch = batch;
|
|
2309
|
+
for (;;) {
|
|
2310
|
+
const entry = batch.shift();
|
|
2311
|
+
if (!entry) break;
|
|
2312
|
+
attached.drainingHeldSeq = entry.seq;
|
|
2313
|
+
try {
|
|
2314
|
+
await this.#enqueueFrame(attached, entry.frame, "ordered");
|
|
2315
|
+
} finally {
|
|
2316
|
+
if (attached.drainingHeldSeq === entry.seq) attached.drainingHeldSeq = undefined;
|
|
2317
|
+
}
|
|
2318
|
+
if (attached.barrier.held !== held) return;
|
|
2319
|
+
}
|
|
2320
|
+
if (attached.drainingHeldBatch === batch) attached.drainingHeldBatch = undefined;
|
|
2298
2321
|
}
|
|
2299
2322
|
}
|
|
2300
2323
|
|
|
@@ -2374,7 +2397,7 @@ export class SessionRouter {
|
|
|
2374
2397
|
this.#failBarrier(attached, "replay response contained a malformed event");
|
|
2375
2398
|
return;
|
|
2376
2399
|
}
|
|
2377
|
-
let previousSeq =
|
|
2400
|
+
let previousSeq = -1;
|
|
2378
2401
|
for (const event of events) {
|
|
2379
2402
|
if (event.type !== "event") {
|
|
2380
2403
|
this.#failBarrier(attached, "replay response contained a non-event frame");
|
|
@@ -2411,7 +2434,7 @@ export class SessionRouter {
|
|
|
2411
2434
|
}
|
|
2412
2435
|
const retained = events
|
|
2413
2436
|
.map(event => readSequence(event.seq))
|
|
2414
|
-
.find(seq => seq !== undefined && seq <= gap.toSeq);
|
|
2437
|
+
.find(seq => seq !== undefined && seq >= gap.fromSeq && seq <= gap.toSeq);
|
|
2415
2438
|
if (retained !== undefined) {
|
|
2416
2439
|
this.#failBarrier(
|
|
2417
2440
|
attached,
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentLoop calls `getApiKey(provider)` before `streamFn`.
|
|
3
|
+
* Resolve/stream already use `getApiKey(model)`. If those two lookups diverge,
|
|
4
|
+
* the child dies locally (~8ms, 0 tokens, sanitized "Agent run failed.")
|
|
5
|
+
* and never reaches the working model-scoped path (#5081).
|
|
6
|
+
*/
|
|
7
|
+
export type SessionApiKeyModel = {
|
|
8
|
+
provider: string;
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type SessionApiKeyRegistry = {
|
|
13
|
+
getApiKey(
|
|
14
|
+
model: SessionApiKeyModel,
|
|
15
|
+
sessionId?: string,
|
|
16
|
+
options?: { signal?: AbortSignal },
|
|
17
|
+
): Promise<string | undefined>;
|
|
18
|
+
getApiKeyForProvider(
|
|
19
|
+
provider: string,
|
|
20
|
+
sessionId?: string,
|
|
21
|
+
baseUrl?: string,
|
|
22
|
+
options?: { signal?: AbortSignal },
|
|
23
|
+
): Promise<string | undefined>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type SessionApiKeyResolution = {
|
|
27
|
+
apiKey: string;
|
|
28
|
+
credentialSessionId: string | undefined;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type SessionApiKeyCredentialType = "api_key" | "oauth" | undefined;
|
|
32
|
+
|
|
33
|
+
export function isStableSessionApiKeyCredentialType(
|
|
34
|
+
previous: SessionApiKeyCredentialType,
|
|
35
|
+
replacement: SessionApiKeyCredentialType,
|
|
36
|
+
): boolean {
|
|
37
|
+
return previous === replacement;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function resolveLiveSessionApiKeyModel(
|
|
41
|
+
live: SessionApiKeyModel | undefined,
|
|
42
|
+
captured: SessionApiKeyModel | undefined,
|
|
43
|
+
provider: string,
|
|
44
|
+
): SessionApiKeyModel | undefined {
|
|
45
|
+
if (live?.provider === provider) return live;
|
|
46
|
+
if (captured?.provider === provider) return captured;
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function lookupOnce(
|
|
51
|
+
registry: SessionApiKeyRegistry,
|
|
52
|
+
provider: string,
|
|
53
|
+
sessionId: string | undefined,
|
|
54
|
+
model: SessionApiKeyModel | undefined,
|
|
55
|
+
signal: AbortSignal | undefined,
|
|
56
|
+
): Promise<string | undefined> {
|
|
57
|
+
signal?.throwIfAborted();
|
|
58
|
+
const matchingModel = model?.provider === provider ? model : undefined;
|
|
59
|
+
const options = signal ? { signal } : undefined;
|
|
60
|
+
let key: string | undefined;
|
|
61
|
+
if (matchingModel) {
|
|
62
|
+
key = await registry.getApiKey(matchingModel, sessionId, options);
|
|
63
|
+
signal?.throwIfAborted();
|
|
64
|
+
}
|
|
65
|
+
if (!key) {
|
|
66
|
+
key = await registry.getApiKeyForProvider(provider, sessionId, matchingModel?.baseUrl, options);
|
|
67
|
+
signal?.throwIfAborted();
|
|
68
|
+
}
|
|
69
|
+
return key;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function lookupSessionApiKey(
|
|
73
|
+
registry: SessionApiKeyRegistry,
|
|
74
|
+
provider: string,
|
|
75
|
+
sessionId: string | undefined,
|
|
76
|
+
model: SessionApiKeyModel | undefined,
|
|
77
|
+
allowUnscopedRetry = true,
|
|
78
|
+
signal?: AbortSignal,
|
|
79
|
+
): Promise<SessionApiKeyResolution> {
|
|
80
|
+
let key = await lookupOnce(registry, provider, sessionId, model, signal);
|
|
81
|
+
// Architect children inherit the parent SID. Direct `-p --no-session` does
|
|
82
|
+
// not. Scoped miss then global/broker hit is the remaining 0-token death
|
|
83
|
+
// after #5105 (jsonl: Agent run failed / output 0).
|
|
84
|
+
if (!key && sessionId && allowUnscopedRetry) {
|
|
85
|
+
key = await lookupOnce(registry, provider, undefined, model, signal);
|
|
86
|
+
if (key) return { apiKey: key, credentialSessionId: undefined };
|
|
87
|
+
}
|
|
88
|
+
if (!key) {
|
|
89
|
+
throw Object.assign(new Error(`No API key found for provider "${provider}"`), {
|
|
90
|
+
code: "provider_unavailable",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return { apiKey: key, credentialSessionId: sessionId };
|
|
94
|
+
}
|
package/src/sdk/session.ts
CHANGED
|
@@ -197,6 +197,11 @@ import { EventBus } from "../utils/event-bus";
|
|
|
197
197
|
import { buildNamedToolChoice, buildNamedToolChoiceResult } from "../utils/tool-choice";
|
|
198
198
|
import type { WorkspaceTree } from "../workspace-tree";
|
|
199
199
|
import { createSessionLifecycleService } from "./lifecycle/client";
|
|
200
|
+
import {
|
|
201
|
+
isStableSessionApiKeyCredentialType,
|
|
202
|
+
lookupSessionApiKey,
|
|
203
|
+
resolveLiveSessionApiKeyModel,
|
|
204
|
+
} from "./session-api-key";
|
|
200
205
|
import {
|
|
201
206
|
attachLifecycleStartupCapability,
|
|
202
207
|
lifecycleMcpStartupTimeoutOption,
|
|
@@ -2338,15 +2343,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2338
2343
|
}
|
|
2339
2344
|
if (options.skills === undefined && settings.get("skills.enabled")) {
|
|
2340
2345
|
try {
|
|
2341
|
-
|
|
2342
|
-
...settings.getGroup("skills"),
|
|
2343
|
-
agentDir,
|
|
2344
|
-
cwd: to,
|
|
2345
|
-
disabledExtensions: settings.get("disabledExtensions"),
|
|
2346
|
-
});
|
|
2347
|
-
skills = withEmbeddedDefaultGjcSkills(reloaded.skills);
|
|
2348
|
-
if (!options.parentTaskPrefix) setActiveSkills(skills);
|
|
2349
|
-
await session?.replaceSkills(skills);
|
|
2346
|
+
await session?.reloadSkills(to);
|
|
2350
2347
|
} catch (error) {
|
|
2351
2348
|
logger.warn("Failed to reload skills after session rescope", { error: safeErrorForLog(error) });
|
|
2352
2349
|
}
|
|
@@ -2708,19 +2705,19 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2708
2705
|
else signal.addEventListener("abort", () => resolve(), { once: true });
|
|
2709
2706
|
return promise;
|
|
2710
2707
|
},
|
|
2708
|
+
getSessionId: () => sessionManager.getSessionId(),
|
|
2711
2709
|
// Subagents inherit the parent's manager; its registered endpoint is
|
|
2712
2710
|
// authoritative for owned-registration keying and endpoint-first
|
|
2713
2711
|
// manager lookup (the child's own id is never registered), so tools
|
|
2714
2712
|
// pass the same endpoint the manager's completion callback resolves
|
|
2715
2713
|
// (review thread P1). For a top-level session this equals the
|
|
2716
2714
|
// session id.
|
|
2717
|
-
|
|
2715
|
+
getAsyncEndpointId: () => AsyncJobManager.endpointIdOf(asyncJobManager) ?? asyncJobEndpointId,
|
|
2718
2716
|
registerForegroundFoldParticipant: adapter =>
|
|
2719
2717
|
session?.registerForegroundFoldParticipant(adapter) ?? (() => {}),
|
|
2720
2718
|
hasForegroundBashBackgroundRequestHandler: () => session?.hasForegroundBashBackgroundRequestHandler() ?? false,
|
|
2721
2719
|
requestForegroundBashBackground: (reason, adapter) =>
|
|
2722
2720
|
Promise.resolve(session?.requestForegroundBashBackground(reason, adapter) ?? false),
|
|
2723
|
-
getToolInterruptPolicy: () => agent?.getToolInterruptPolicy() ?? "abort_tools",
|
|
2724
2721
|
|
|
2725
2722
|
getCredentialSessionId: () => session?.credentialSessionId ?? credentialSessionId,
|
|
2726
2723
|
getMcpManager: () => mcpManager ?? options.inheritedMcpManager,
|
|
@@ -3925,6 +3922,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3925
3922
|
appendSystemPrompt: appendPrompt,
|
|
3926
3923
|
pluginAppendices: pluginSystemAppendices,
|
|
3927
3924
|
repeatToolDescriptions,
|
|
3925
|
+
reasoningLanguage: settings.get("reasoningLanguage"),
|
|
3928
3926
|
intentField,
|
|
3929
3927
|
toolDiscoveryActive: effectiveDiscoveryMode === "all" || mcpDiscoveryEnabled,
|
|
3930
3928
|
eagerTasks: resolveEagerTasks(),
|
|
@@ -4258,6 +4256,32 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
4258
4256
|
).seedNormalizedMessages(options.forkContextSeed.messages);
|
|
4259
4257
|
}
|
|
4260
4258
|
|
|
4259
|
+
const resolvedCredentialScopes = new Map<string, string | undefined>();
|
|
4260
|
+
const resolveAgentApiKey = async (provider: string, signal?: AbortSignal): Promise<string> => {
|
|
4261
|
+
const liveModel = resolveLiveSessionApiKeyModel(sessionAgent?.state.model, model, provider);
|
|
4262
|
+
const hasExplicitScopePolicy =
|
|
4263
|
+
modelRegistry.authStorage.hasSessionCredentialSelector(provider, credentialSessionId) ||
|
|
4264
|
+
modelRegistry.authStorage.hasSessionCredentialAuto(provider, credentialSessionId);
|
|
4265
|
+
const resolution = await lookupSessionApiKey(
|
|
4266
|
+
modelRegistry,
|
|
4267
|
+
provider,
|
|
4268
|
+
credentialSessionId,
|
|
4269
|
+
liveModel,
|
|
4270
|
+
!hasExplicitScopePolicy,
|
|
4271
|
+
signal,
|
|
4272
|
+
);
|
|
4273
|
+
resolvedCredentialScopes.set(provider, resolution.credentialSessionId);
|
|
4274
|
+
return resolution.apiKey;
|
|
4275
|
+
};
|
|
4276
|
+
const resolvedCredentialType = (provider: string): "api_key" | "oauth" | undefined => {
|
|
4277
|
+
const scope = resolvedCredentialScopes.has(provider)
|
|
4278
|
+
? resolvedCredentialScopes.get(provider)
|
|
4279
|
+
: credentialSessionId;
|
|
4280
|
+
return modelRegistry.authStorage.getEffectiveCredentialType(provider, scope, {
|
|
4281
|
+
owner: modelRegistry.getAuthStorageOwner(),
|
|
4282
|
+
});
|
|
4283
|
+
};
|
|
4284
|
+
|
|
4261
4285
|
agent = new Agent({
|
|
4262
4286
|
initialState: {
|
|
4263
4287
|
systemPrompt,
|
|
@@ -4296,44 +4320,42 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
4296
4320
|
shouldPause: options.shouldPause,
|
|
4297
4321
|
preferWebsockets: preferOpenAICodexWebsockets,
|
|
4298
4322
|
getToolContext: tc => toolContextStore.getContext(tc),
|
|
4299
|
-
getApiKey:
|
|
4323
|
+
getApiKey: provider => {
|
|
4300
4324
|
// AgentLoop asks by provider, but the active model carries the
|
|
4301
4325
|
// model-scoped credential selector. Read it at call time so model
|
|
4302
4326
|
// changes are honored after /new, fork, resume, or branch switches.
|
|
4303
|
-
|
|
4304
|
-
const key =
|
|
4305
|
-
liveModel?.provider === provider
|
|
4306
|
-
? await modelRegistry.getApiKey(liveModel, credentialSessionId)
|
|
4307
|
-
: undefined;
|
|
4308
|
-
const providerKey = key ?? (await modelRegistry.getApiKeyForProvider(provider, credentialSessionId));
|
|
4309
|
-
if (!providerKey) {
|
|
4310
|
-
throw new Error(`No API key found for provider "${provider}"`);
|
|
4311
|
-
}
|
|
4312
|
-
return providerKey;
|
|
4327
|
+
return resolveAgentApiKey(provider);
|
|
4313
4328
|
},
|
|
4314
|
-
getAuthCredentialType:
|
|
4329
|
+
getAuthCredentialType: resolvedCredentialType,
|
|
4315
4330
|
streamFn: async (streamModel, context, streamOptions) => {
|
|
4316
4331
|
const requestStartedAt = performance.now();
|
|
4317
4332
|
let stream: Awaited<ReturnType<typeof streamSimple>>;
|
|
4318
4333
|
try {
|
|
4319
|
-
const effectiveApiKey = await modelRegistry.getApiKey(streamModel, credentialSessionId);
|
|
4320
4334
|
stream = await streamSimple(streamModel, context, {
|
|
4321
4335
|
...streamOptions,
|
|
4322
|
-
apiKey: effectiveApiKey,
|
|
4323
|
-
authCredentialType: modelRegistry.getSessionCredentialType(streamModel.provider, credentialSessionId),
|
|
4324
4336
|
onAuthError: async (provider, oldKey, error) => {
|
|
4337
|
+
const resolvedScope = resolvedCredentialScopes.has(provider)
|
|
4338
|
+
? resolvedCredentialScopes.get(provider)
|
|
4339
|
+
: credentialSessionId;
|
|
4325
4340
|
await modelRegistry.authStorage.invalidateCredentialMatching(provider, oldKey, {
|
|
4326
4341
|
signal: streamOptions?.signal,
|
|
4327
|
-
sessionId:
|
|
4342
|
+
sessionId: resolvedScope,
|
|
4328
4343
|
owner: modelRegistry.getAuthStorageOwner(),
|
|
4329
4344
|
});
|
|
4330
4345
|
logger.debug("Retrying provider request after credential invalidation", {
|
|
4331
4346
|
provider,
|
|
4332
4347
|
error: error instanceof Error ? error.message : String(error),
|
|
4333
4348
|
});
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4349
|
+
const replacementApiKey = await resolveAgentApiKey(provider, streamOptions?.signal);
|
|
4350
|
+
if (
|
|
4351
|
+
!isStableSessionApiKeyCredentialType(
|
|
4352
|
+
streamOptions?.authCredentialType,
|
|
4353
|
+
resolvedCredentialType(provider),
|
|
4354
|
+
)
|
|
4355
|
+
) {
|
|
4356
|
+
return undefined;
|
|
4357
|
+
}
|
|
4358
|
+
return replacementApiKey;
|
|
4337
4359
|
},
|
|
4338
4360
|
});
|
|
4339
4361
|
} catch (error) {
|
|
@@ -4460,6 +4482,27 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
4460
4482
|
customCommands: customCommandsResult.commands,
|
|
4461
4483
|
skills,
|
|
4462
4484
|
skillWarnings,
|
|
4485
|
+
reloadSkills:
|
|
4486
|
+
options.skills === undefined
|
|
4487
|
+
? async reloadCwd => {
|
|
4488
|
+
if (!settings.get("skills.enabled")) {
|
|
4489
|
+
return { skills: getEmbeddedDefaultGjcSkills(), warnings: [] };
|
|
4490
|
+
} else {
|
|
4491
|
+
const reloaded = await loadSkills({
|
|
4492
|
+
...settings.getGroup("skills"),
|
|
4493
|
+
agentDir,
|
|
4494
|
+
cwd: reloadCwd,
|
|
4495
|
+
disabledExtensions: settings.get("disabledExtensions"),
|
|
4496
|
+
});
|
|
4497
|
+
return { skills: withEmbeddedDefaultGjcSkills(reloaded.skills), warnings: reloaded.warnings };
|
|
4498
|
+
}
|
|
4499
|
+
}
|
|
4500
|
+
: undefined,
|
|
4501
|
+
onSkillsReloaded: (reloadedSkills, reloadedWarnings) => {
|
|
4502
|
+
skills = reloadedSkills;
|
|
4503
|
+
skillWarnings = reloadedWarnings;
|
|
4504
|
+
if (!options.parentTaskPrefix) setActiveSkills(reloadedSkills);
|
|
4505
|
+
},
|
|
4463
4506
|
skillsSettings: settings.getGroup("skills"),
|
|
4464
4507
|
modelRegistry,
|
|
4465
4508
|
taskDepth,
|
|
@@ -92,10 +92,10 @@ function brokerSessionRows(sessions: readonly unknown[]): BrokerSessionRow[] {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/** Exhausts strict broker `session.list` pages into one full session snapshot. */
|
|
95
|
-
export async function listBrokerSessions(broker: SdkClient): Promise<BrokerSessionRow[]> {
|
|
95
|
+
export async function listBrokerSessions(broker: SdkClient, explicitSessionId?: string): Promise<BrokerSessionRow[]> {
|
|
96
96
|
try {
|
|
97
97
|
const pages = await traverseSessionList(
|
|
98
|
-
{},
|
|
98
|
+
{ ...(explicitSessionId ? { resolveSessionId: explicitSessionId } : {}) },
|
|
99
99
|
async input => await broker.global("session.list", input),
|
|
100
100
|
response => {
|
|
101
101
|
brokerResult(response);
|
|
@@ -147,7 +147,7 @@ export async function runSdkServe(argv: string[]): Promise<void> {
|
|
|
147
147
|
throw new Error("broker_unavailable: SDK broker is not reachable");
|
|
148
148
|
}
|
|
149
149
|
try {
|
|
150
|
-
const sessionId = selectBrokerSession(await listBrokerSessions(broker), parsed.sessionId);
|
|
150
|
+
const sessionId = selectBrokerSession(await listBrokerSessions(broker, parsed.sessionId), parsed.sessionId);
|
|
151
151
|
const endpoint = brokerResult(await broker.global("session.get_endpoint", { sessionId }));
|
|
152
152
|
const url = typeof endpoint.url === "string" && endpoint.url ? endpoint.url : undefined;
|
|
153
153
|
const token = typeof endpoint.token === "string" ? endpoint.token : "";
|