@gajae-code/coding-agent 0.18.5 → 0.18.7
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 +140 -0
- package/dist/types/capability/types.d.ts +4 -0
- package/dist/types/cli-commands.d.ts +10 -0
- package/dist/types/cli-ordinary.d.ts +13 -0
- package/dist/types/cli.d.ts +12 -3
- package/dist/types/commands/contribution-prep.d.ts +0 -3
- package/dist/types/commands/sdk.d.ts +3 -0
- package/dist/types/commands/update.d.ts +1 -0
- package/dist/types/config/model-profile-activation.d.ts +19 -0
- package/dist/types/config/settings.d.ts +4 -0
- package/dist/types/coordinator-mcp/session-reaper.d.ts +5 -0
- package/dist/types/discovery/helpers.d.ts +2 -2
- package/dist/types/edit/modes/replace.d.ts +3 -3
- package/dist/types/edit/streaming.d.ts +2 -2
- package/dist/types/exec/bash-shell-supervisor.d.ts +12 -0
- package/dist/types/runtime-mcp/elicitation.d.ts +12 -0
- package/dist/types/sdk/broker/discovery.d.ts +8 -0
- package/dist/types/sdk/broker/ensure.d.ts +1 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +4 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +9 -0
- package/dist/types/sdk/broker/runtime.d.ts +1 -1
- package/dist/types/sdk/broker/task-artifact-owner-validation.d.ts +55 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +4 -2
- package/dist/types/sdk/cli/diagnostics-cli.d.ts +18 -0
- package/dist/types/sdk/diagnostics/observe-broker.d.ts +185 -0
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +6 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +9 -0
- package/dist/types/sdk/session.d.ts +13 -0
- package/dist/types/session/agent-session.d.ts +5 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +11 -1
- package/dist/types/session/internal/task-artifact-owner-access.d.ts +17 -0
- package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +3 -0
- package/dist/types/session/session-retirement.d.ts +47 -0
- package/dist/types/session/session-storage.d.ts +0 -46
- package/dist/types/session/session-transcript-header.d.ts +2 -0
- package/dist/types/session/task-artifact-owner-codec.d.ts +107 -0
- package/dist/types/session/task-artifact-owner-retirement.d.ts +7 -0
- package/dist/types/session/task-artifact-owner.d.ts +10 -0
- package/dist/types/tools/ask-contract.d.ts +75 -254
- package/dist/types/tools/bash.d.ts +1 -1
- package/package.json +7 -7
- package/scripts/build-sdk-package-smoke.ts +5 -0
- package/scripts/resident-memory-bench.ts +174 -20
- package/src/capability/index.ts +1 -0
- package/src/capability/types.ts +4 -0
- package/src/cli/config-cli.ts +8 -18
- package/src/cli/public-command-entry.ts +5 -0
- package/src/cli/ssh-cli.ts +6 -3
- package/src/cli/web-search-cli.ts +9 -3
- package/src/cli-commands.ts +14 -0
- package/src/cli-main.ts +7 -8
- package/src/cli-ordinary.ts +197 -0
- package/src/cli.ts +25 -182
- package/src/commands/contribution-prep.ts +1 -3
- package/src/commands/sdk.ts +171 -21
- package/src/commands/update.ts +15 -1
- package/src/config/file-lock.ts +91 -2
- package/src/config/model-profile-activation.ts +70 -4
- package/src/config/model-profile-contract.ts +4 -1
- package/src/config/model-profiles.ts +23 -31
- package/src/config/model-resolver.ts +9 -1
- package/src/config/settings.ts +44 -4
- package/src/coordinator-mcp/event-webhook.ts +19 -14
- package/src/coordinator-mcp/policy.ts +14 -4
- package/src/coordinator-mcp/server.ts +142 -7
- package/src/coordinator-mcp/session-reaper.ts +9 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/discovery/helpers.ts +9 -3
- package/src/discovery/ssh.ts +8 -3
- package/src/edit/modes/replace.ts +6 -6
- package/src/edit/streaming.ts +15 -15
- package/src/exec/bash-shell-supervisor.ts +60 -3
- package/src/gjc-runtime/gc-runtime.ts +2 -1
- package/src/hindsight/config.ts +9 -3
- package/src/internal-urls/docs-index.generated.ts +11 -10
- package/src/memories/index.ts +8 -2
- package/src/modes/acp/acp-agent.ts +724 -370
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/prompts/tools/ask.md +2 -0
- package/src/runtime-mcp/client.ts +13 -4
- package/src/runtime-mcp/elicitation.ts +142 -0
- package/src/sdk/broker/broker.ts +54 -1
- package/src/sdk/broker/daemon-entry.ts +67 -6
- package/src/sdk/broker/discovery.ts +8 -0
- package/src/sdk/broker/ensure.ts +156 -20
- package/src/sdk/broker/lifecycle-ledger.ts +110 -12
- package/src/sdk/broker/lifecycle.ts +334 -348
- package/src/sdk/broker/runtime.ts +5 -1
- package/src/sdk/broker/task-artifact-owner-validation.ts +259 -0
- package/src/sdk/broker/transport.ts +5 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +3 -0
- package/src/sdk/bus/index.ts +3 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/bus/telegram-daemon-control.ts +15 -12
- package/src/sdk/bus/telegram-daemon.ts +38 -3
- package/src/sdk/cli/diagnostics-cli.ts +247 -0
- package/src/sdk/diagnostics/observe-broker.ts +654 -0
- package/src/sdk/host/control/dispatch.ts +52 -4
- package/src/sdk/host/control/operations.ts +1 -1
- package/src/sdk/host/host.ts +6 -0
- package/src/sdk/host/session-runtime.test.ts +83 -4
- package/src/sdk/host/session-runtime.ts +15 -7
- package/src/sdk/index.ts +1 -0
- package/src/sdk/lifecycle-session.ts +1 -0
- package/src/sdk/mcp/server.ts +15 -3
- package/src/sdk/protocol/operation-registry.ts +1 -1
- package/src/sdk/router/session-router.ts +26 -3
- package/src/sdk/session.ts +51 -8
- package/src/session/agent-session.ts +169 -43
- package/src/session/blob-store.ts +78 -33
- package/src/session/internal/managed-session-storage.ts +203 -4
- package/src/session/internal/task-artifact-owner-access.ts +229 -0
- package/src/session/internal/task-artifact-owner-transcript.ts +68 -0
- package/src/session/session-retirement.ts +155 -0
- package/src/session/session-storage.ts +1 -160
- package/src/session/session-transcript-header.ts +17 -0
- package/src/session/task-artifact-owner-codec.ts +578 -0
- package/src/session/task-artifact-owner-retirement.ts +475 -0
- package/src/session/task-artifact-owner.ts +289 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +19 -1
- package/src/setup/hermes-setup.ts +4 -1
- package/src/slash-commands/builtin-registry.ts +8 -5
- package/src/tools/ask-contract.test.ts +76 -1
- package/src/tools/ask-contract.ts +57 -6
- package/src/tools/ask.ts +16 -7
- package/src/tools/bash.ts +38 -29
- package/src/tools/descriptors.test.ts +29 -0
- package/src/tools/tool-catalog.generated.ts +7 -4
- package/src/web/search/index.ts +2 -2
|
@@ -93,6 +93,7 @@ const IDEMPOTENCY_TTL_MS = 15 * 60 * 1_000;
|
|
|
93
93
|
const MAX_IDEMPOTENCY_ENTRIES = 256;
|
|
94
94
|
|
|
95
95
|
const sessionChains = new WeakMap<ControlSurface, Promise<void>>();
|
|
96
|
+
const pendingReplacementBarriers = new WeakMap<ControlSurface, Promise<void>>();
|
|
96
97
|
interface IdempotencyEntry {
|
|
97
98
|
hash: string;
|
|
98
99
|
expiresAt: number;
|
|
@@ -189,6 +190,19 @@ function invalidInput(message: string): never {
|
|
|
189
190
|
throw new TypedControlError("invalid_input", message);
|
|
190
191
|
}
|
|
191
192
|
|
|
193
|
+
const EXPECTED_SDK_RUN_TOKEN_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}:[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/;
|
|
194
|
+
|
|
195
|
+
function enqueueReplacementBarrier(surface: ControlSurface): () => void {
|
|
196
|
+
const previous = pendingReplacementBarriers.get(surface) ?? Promise.resolve();
|
|
197
|
+
const { promise: current, resolve: resolveCurrent } = Promise.withResolvers<void>();
|
|
198
|
+
const barrier = previous.then(() => current);
|
|
199
|
+
pendingReplacementBarriers.set(surface, barrier);
|
|
200
|
+
return () => {
|
|
201
|
+
resolveCurrent();
|
|
202
|
+
if (pendingReplacementBarriers.get(surface) === barrier) pendingReplacementBarriers.delete(surface);
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
192
206
|
/**
|
|
193
207
|
* C04 `turn.abort` dispatch.
|
|
194
208
|
*
|
|
@@ -244,7 +258,11 @@ function invoke(
|
|
|
244
258
|
case "turn.prompt":
|
|
245
259
|
return surface.prompt(text(input), input.images, input.clientRef as string | undefined);
|
|
246
260
|
case "turn.steer":
|
|
247
|
-
return surface.steer(
|
|
261
|
+
return surface.steer(
|
|
262
|
+
text(input),
|
|
263
|
+
typeof input.clientRef === "string" ? input.clientRef : undefined,
|
|
264
|
+
Object.hasOwn(input, "expectedSdkRunToken") ? (input.expectedSdkRunToken as string) : undefined,
|
|
265
|
+
);
|
|
248
266
|
case "turn.follow_up":
|
|
249
267
|
return surface.followUp(text(input));
|
|
250
268
|
case "turn.abort":
|
|
@@ -486,6 +504,15 @@ export function dispatchControl(
|
|
|
486
504
|
);
|
|
487
505
|
if (!isInput(request.input))
|
|
488
506
|
return Promise.resolve(failure(request.id, "invalid_input", "Control input must be an object."));
|
|
507
|
+
if (
|
|
508
|
+
row.sdkId === "turn.steer" &&
|
|
509
|
+
Object.hasOwn(request.input, "expectedSdkRunToken") &&
|
|
510
|
+
(typeof request.input.expectedSdkRunToken !== "string" ||
|
|
511
|
+
!EXPECTED_SDK_RUN_TOKEN_PATTERN.test(request.input.expectedSdkRunToken))
|
|
512
|
+
)
|
|
513
|
+
return Promise.resolve(
|
|
514
|
+
failure(request.id, "invalid_input", "expectedSdkRunToken must be a commandId:turnId pair."),
|
|
515
|
+
);
|
|
489
516
|
if (
|
|
490
517
|
row.sdkId === "turn.abort" &&
|
|
491
518
|
((brokerAbortFieldPresent && !brokerAbortAuthorized) ||
|
|
@@ -538,10 +565,31 @@ export function dispatchControl(
|
|
|
538
565
|
const work = () => execute(surface, row, dispatchRequest);
|
|
539
566
|
if (row.sdkId === "turn.abort_and_prompt") {
|
|
540
567
|
const cancellable = surface as PreflightCancellableSurface;
|
|
541
|
-
|
|
542
|
-
|
|
568
|
+
const releaseBarrier = enqueueReplacementBarrier(surface);
|
|
569
|
+
try {
|
|
570
|
+
if (Object.hasOwn(cancellable, "cancelPendingPreflights")) cancellable.cancelPendingPreflights?.();
|
|
571
|
+
return serialize(surface, async () => {
|
|
572
|
+
try {
|
|
573
|
+
return await work();
|
|
574
|
+
} finally {
|
|
575
|
+
releaseBarrier();
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
} catch (error) {
|
|
579
|
+
releaseBarrier();
|
|
580
|
+
throw error;
|
|
581
|
+
}
|
|
543
582
|
}
|
|
544
583
|
if (row.idempotency === "idempotent" && dispatchRequest.idempotencyKey)
|
|
545
584
|
return idempotent(surface, row, dispatchRequest, work);
|
|
546
|
-
|
|
585
|
+
if (row.sdkId === "turn.steer") {
|
|
586
|
+
const replacementBarrier = pendingReplacementBarriers.get(surface);
|
|
587
|
+
return replacementBarrier ? replacementBarrier.then(work) : work();
|
|
588
|
+
}
|
|
589
|
+
// Active feedback must reach the worker even while an earlier ordered control
|
|
590
|
+
// waits for that worker. Correlated steer admission remains deduplicated by
|
|
591
|
+
// the runtime reconciliation store, independently of this dispatch chain.
|
|
592
|
+
return row.idempotency === "ordered" && row.sdkId !== "retry.now" && row.sdkId !== "turn.steer"
|
|
593
|
+
? serialize(surface, work)
|
|
594
|
+
: work();
|
|
547
595
|
}
|
|
@@ -22,7 +22,7 @@ export type ControlInput = Record<string, unknown>;
|
|
|
22
22
|
*/
|
|
23
23
|
export interface ControlSurface {
|
|
24
24
|
prompt(text: string, images?: ControlValue, clientRef?: string): Promise<ControlValue> | ControlValue;
|
|
25
|
-
steer(text: string, clientRef?: string): Promise<ControlValue> | ControlValue;
|
|
25
|
+
steer(text: string, clientRef?: string, expectedSdkRunToken?: string): Promise<ControlValue> | ControlValue;
|
|
26
26
|
followUp(text: string): Promise<ControlValue> | ControlValue;
|
|
27
27
|
abort(): Promise<ControlValue> | ControlValue;
|
|
28
28
|
/** Terminal abort: stop the current root turn (and optionally exact owned work). */
|
package/src/sdk/host/host.ts
CHANGED
|
@@ -107,6 +107,12 @@ export const TOOL_ACTIVITY_CAPABILITY = "tool_activity_v2";
|
|
|
107
107
|
export const TURN_STREAM_CAPABILITY = "turn_stream";
|
|
108
108
|
/** Capability used by notification adapters that observe a host without owning its work. */
|
|
109
109
|
export const SESSION_HOST_OBSERVER_CAPABILITY = "session_host_observer_v1";
|
|
110
|
+
/**
|
|
111
|
+
* Opts notification adapters into positioned-only delivery for matching live
|
|
112
|
+
* effects: the host excludes a negotiating connection from the raw native leg
|
|
113
|
+
* of every frame its positioned envelope already reached.
|
|
114
|
+
*/
|
|
115
|
+
export const POSITIONED_NOTIFICATION_EFFECTS_CAPABILITY = "positioned_notification_effects_v1";
|
|
110
116
|
export const CAP_GATED_FRAME_KINDS: ReadonlySet<string> = new Set(["tool_activity", "reasoning_summary"]);
|
|
111
117
|
const EMPTY_CAPABILITIES: ReadonlySet<string> = new Set();
|
|
112
118
|
|
|
@@ -3,8 +3,9 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
import { chmod, mkdir, mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import * as path from "node:path";
|
|
6
|
-
import { markNonDispatchedToolEvent } from "@gajae-code/agent-core";
|
|
6
|
+
import { markNonDispatchedToolEvent, ThinkingLevel } from "@gajae-code/agent-core";
|
|
7
7
|
import { logger } from "@gajae-code/utils";
|
|
8
|
+
import { createTestSession } from "../../../test/utilities";
|
|
8
9
|
import { AsyncJobManager } from "../../async";
|
|
9
10
|
import type { Settings } from "../../config/settings";
|
|
10
11
|
import type { ExtensionAPI, ExtensionContext, ExtensionTranscriptEntry } from "../../extensibility/extensions";
|
|
@@ -103,7 +104,46 @@ test("runtime capabilities preserve explicit primary control surface", () => {
|
|
|
103
104
|
expect(createSdkCapabilities(policy, false, "cli")).toMatchObject({ primaryControlSurface: "cli" });
|
|
104
105
|
});
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
test("thinking.set uses the control setter and reports the applied session level", async () => {
|
|
108
|
+
const sessionContext = await createTestSession({ inMemory: true });
|
|
109
|
+
const { session, tempDir: cwd, cleanup } = sessionContext;
|
|
110
|
+
const handlers = new Map<string, (event: unknown, ctx: ExtensionContext) => Promise<void> | void>();
|
|
111
|
+
const api = {
|
|
112
|
+
on(event: string, handler: (event: unknown, ctx: ExtensionContext) => Promise<void> | void) {
|
|
113
|
+
handlers.set(event, handler);
|
|
114
|
+
},
|
|
115
|
+
sendUserMessage: async () => {},
|
|
116
|
+
setThinkingLevel: () => {
|
|
117
|
+
throw new Error("thinking.set must not use the legacy setter");
|
|
118
|
+
},
|
|
119
|
+
setThinkingLevelForControl: async (level: ThinkingLevel, persist: boolean) => {
|
|
120
|
+
expect(persist).toBe(false);
|
|
121
|
+
await session.setThinkingLevelForControl(level, persist);
|
|
122
|
+
},
|
|
123
|
+
} as unknown as ExtensionAPI;
|
|
124
|
+
const response = Promise.withResolvers<SdkFrame>();
|
|
125
|
+
const transport = memoryTransport(frame => {
|
|
126
|
+
if (frame.type === "control_response" && frame.id === "thinking-set") response.resolve(frame);
|
|
127
|
+
});
|
|
128
|
+
createSdkSessionRuntimeExtension(api, { agentDir: cwd, createTransport: async () => transport });
|
|
129
|
+
const ctx = extensionContext(transport.sessionId, cwd);
|
|
130
|
+
try {
|
|
131
|
+
await handlers.get("session_start")?.({}, ctx);
|
|
132
|
+
transport.feed("client", {
|
|
133
|
+
type: "control_request",
|
|
134
|
+
id: "thinking-set",
|
|
135
|
+
operation: "thinking.set",
|
|
136
|
+
input: { level: ThinkingLevel.Medium },
|
|
137
|
+
} as SdkFrame);
|
|
138
|
+
await response.promise;
|
|
139
|
+
expect(session.thinkingLevel).toBe(ThinkingLevel.Medium);
|
|
140
|
+
} finally {
|
|
141
|
+
await handlers.get("session_shutdown")?.({}, ctx);
|
|
142
|
+
await cleanup();
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
function memoryTransport(onSend?: (frame: SdkFrame) => void): SessionSdkTransport & {
|
|
107
147
|
feed(connectionId: string, frame: SdkFrame): void;
|
|
108
148
|
readonly sent: SdkFrame[];
|
|
109
149
|
readonly broadcasts: SdkFrame[];
|
|
@@ -126,6 +166,7 @@ function memoryTransport(): SessionSdkTransport & {
|
|
|
126
166
|
},
|
|
127
167
|
sendFrame(_connectionId, frame) {
|
|
128
168
|
sent.push(frame);
|
|
169
|
+
onSend?.(frame);
|
|
129
170
|
},
|
|
130
171
|
start: async () => {
|
|
131
172
|
started = true;
|
|
@@ -290,7 +331,7 @@ test("session.last_assistant returns the latest projected readable text past non
|
|
|
290
331
|
});
|
|
291
332
|
});
|
|
292
333
|
|
|
293
|
-
test("session.last_assistant returns
|
|
334
|
+
test("session.last_assistant returns null when the projected transcript has no readable assistant text", async () => {
|
|
294
335
|
const sessionId = "last-assistant-empty";
|
|
295
336
|
const ctx = extensionContext(sessionId, "/tmp", {
|
|
296
337
|
transcript: [
|
|
@@ -311,7 +352,7 @@ test("session.last_assistant returns resource_gone when the projected transcript
|
|
|
311
352
|
],
|
|
312
353
|
});
|
|
313
354
|
|
|
314
|
-
expect(await queryLastAssistant(ctx, sessionId)).toMatchObject({ ok:
|
|
355
|
+
expect(await queryLastAssistant(ctx, sessionId)).toMatchObject({ ok: true, page: { items: [null] } });
|
|
315
356
|
});
|
|
316
357
|
|
|
317
358
|
test("native prompt reconciliation fails closed for an explicitly empty assistant result", () => {
|
|
@@ -4142,6 +4183,7 @@ describe("SessionSdkSessionRuntime", () => {
|
|
|
4142
4183
|
interface PreflightHooks {
|
|
4143
4184
|
onPreflightAccepted?: () => void;
|
|
4144
4185
|
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
4186
|
+
expectedSdkRunToken?: string;
|
|
4145
4187
|
}
|
|
4146
4188
|
|
|
4147
4189
|
interface ResponseFrame {
|
|
@@ -4482,6 +4524,43 @@ function neverSettlingPromise(): Promise<void> {
|
|
|
4482
4524
|
return Promise.withResolvers<void>().promise;
|
|
4483
4525
|
}
|
|
4484
4526
|
|
|
4527
|
+
test("SDK turn.steer preserves its expected run token and propagates a stale-run rejection", async () => {
|
|
4528
|
+
const cwd = await mkdtemp(path.join(os.tmpdir(), "gjc-turn-steer-run-token-"));
|
|
4529
|
+
const staleToken = "ended-command:ended-turn";
|
|
4530
|
+
const clientRef = "stale-steer-ref";
|
|
4531
|
+
const calls: Array<{ content: unknown; deliverAs?: string; expectedSdkRunToken?: string }> = [];
|
|
4532
|
+
let harness: InvocationHarness | undefined;
|
|
4533
|
+
try {
|
|
4534
|
+
harness = await invocationHarness("steer-run-token", cwd, {
|
|
4535
|
+
sendUserMessage: async (content, options) => {
|
|
4536
|
+
calls.push({
|
|
4537
|
+
content,
|
|
4538
|
+
deliverAs: options?.deliverAs,
|
|
4539
|
+
expectedSdkRunToken: options?.expectedSdkRunToken,
|
|
4540
|
+
});
|
|
4541
|
+
if (options?.expectedSdkRunToken === staleToken)
|
|
4542
|
+
throw Object.assign(new Error("The expected SDK run is not active."), { code: "turn_not_active" });
|
|
4543
|
+
return "completed";
|
|
4544
|
+
},
|
|
4545
|
+
});
|
|
4546
|
+
|
|
4547
|
+
const rejected = await harness.control("turn.steer", {
|
|
4548
|
+
text: "stale steer",
|
|
4549
|
+
expectedSdkRunToken: staleToken,
|
|
4550
|
+
clientRef,
|
|
4551
|
+
});
|
|
4552
|
+
expect(rejected).toMatchObject({ ok: false, error: { code: "turn_not_active" } });
|
|
4553
|
+
expect(calls).toEqual([{ content: "stale steer", deliverAs: "steer", expectedSdkRunToken: staleToken }]);
|
|
4554
|
+
expect(await harness.query("turn.steer_status", { clientRef })).toMatchObject({
|
|
4555
|
+
ok: true,
|
|
4556
|
+
result: { status: "rejected" },
|
|
4557
|
+
});
|
|
4558
|
+
} finally {
|
|
4559
|
+
if (harness) await harness.stop();
|
|
4560
|
+
await rm(cwd, { recursive: true, force: true });
|
|
4561
|
+
}
|
|
4562
|
+
});
|
|
4563
|
+
|
|
4485
4564
|
describe("post-acceptance invocation terminalization", () => {
|
|
4486
4565
|
test.each([
|
|
4487
4566
|
{ status: 402, code: "provider_http_402" },
|
|
@@ -2059,7 +2059,7 @@ function createQuerySurface(
|
|
|
2059
2059
|
: entry.content?.flatMap(block => (block.type === "text" ? [block.text] : [])).join("\n");
|
|
2060
2060
|
if (text !== undefined && text.trim().length > 0) return text;
|
|
2061
2061
|
}
|
|
2062
|
-
return
|
|
2062
|
+
return null;
|
|
2063
2063
|
};
|
|
2064
2064
|
const getProfileCredentialSessionId = () => ctx.credentialSessionId ?? id;
|
|
2065
2065
|
const profileSettings = (options.settings ?? ctx.settings) as Pick<Settings, "get"> | undefined;
|
|
@@ -2914,6 +2914,7 @@ function createControlSurface(
|
|
|
2914
2914
|
};
|
|
2915
2915
|
type InternalSendOptions = NonNullable<Parameters<ExtensionAPI["sendUserMessage"]>[1]> & {
|
|
2916
2916
|
sdkRunCapability?: SdkRunCapability;
|
|
2917
|
+
expectedSdkRunToken?: string;
|
|
2917
2918
|
};
|
|
2918
2919
|
type InternalSdkApi = Omit<ExtensionAPI, "sendUserMessage"> & {
|
|
2919
2920
|
sendUserMessage: (
|
|
@@ -4243,23 +4244,30 @@ function createControlSurface(
|
|
|
4243
4244
|
),
|
|
4244
4245
|
);
|
|
4245
4246
|
},
|
|
4246
|
-
steer: async (text, clientRef) => {
|
|
4247
|
+
steer: async (text, clientRef, expectedSdkRunToken) => {
|
|
4247
4248
|
const invalid = validateRequiredPromptText("turn.steer", { text });
|
|
4248
4249
|
if (invalid) throw Object.assign(new Error(invalid.message), { code: invalid.code });
|
|
4249
4250
|
const retainedClientRef = normalizeClientRef(clientRef);
|
|
4251
|
+
const sendSteer = () =>
|
|
4252
|
+
sendSdkUserMessage(text, {
|
|
4253
|
+
deliverAs: "steer",
|
|
4254
|
+
...(expectedSdkRunToken === undefined ? {} : { expectedSdkRunToken }),
|
|
4255
|
+
});
|
|
4250
4256
|
if (retainedClientRef === undefined) {
|
|
4251
4257
|
const correlation = newCorrelation();
|
|
4252
|
-
await
|
|
4258
|
+
await sendSteer();
|
|
4253
4259
|
return { accepted: true, ...correlation };
|
|
4254
4260
|
}
|
|
4255
4261
|
const durable = steerReconciliation;
|
|
4256
4262
|
const reservation = await durable.reserveSteer(retainedClientRef, text);
|
|
4257
4263
|
if (reservation.replay) return { accepted: reservation.result.status === "accepted", ...reservation.result };
|
|
4258
4264
|
try {
|
|
4259
|
-
await
|
|
4265
|
+
await sendSteer();
|
|
4260
4266
|
return { accepted: true, ...(await durable.settleSteer(retainedClientRef, "accepted")) };
|
|
4261
4267
|
} catch (error) {
|
|
4262
|
-
|
|
4268
|
+
const settled = await durable.settleSteer(retainedClientRef, "rejected", error);
|
|
4269
|
+
if (expectedSdkRunToken !== undefined) throw error;
|
|
4270
|
+
return { accepted: false, ...settled };
|
|
4263
4271
|
}
|
|
4264
4272
|
},
|
|
4265
4273
|
followUp: async text => {
|
|
@@ -4364,8 +4372,8 @@ function createControlSurface(
|
|
|
4364
4372
|
},
|
|
4365
4373
|
setModelProfile: id => (ctx.setModelProfile ? ctx.setModelProfile(id) : unavailable("model.profile.set")()),
|
|
4366
4374
|
cycleModel: () => (ctx.cycleModel ? ctx.cycleModel() : unavailable("model.cycle")()),
|
|
4367
|
-
setThinking: level => {
|
|
4368
|
-
api.
|
|
4375
|
+
setThinking: async level => {
|
|
4376
|
+
await api.setThinkingLevelForControl(level as ThinkingLevel, false);
|
|
4369
4377
|
return { changed: true };
|
|
4370
4378
|
},
|
|
4371
4379
|
cycleThinking: () =>
|
package/src/sdk/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export type {
|
|
|
21
21
|
TrackedSendUserMessageOptions,
|
|
22
22
|
} from "../session/agent-session";
|
|
23
23
|
export * as bus from "./bus";
|
|
24
|
+
export * from "./diagnostics/observe-broker";
|
|
24
25
|
export * as host from "./host";
|
|
25
26
|
export * as lifecycle from "./lifecycle";
|
|
26
27
|
export * as mcp from "./mcp";
|
|
@@ -123,6 +123,7 @@ export async function createLifecycleAgentSession(
|
|
|
123
123
|
// inside the broker's readiness window is what kills the child at the
|
|
124
124
|
// cutoff; the host resumes it once readiness is published.
|
|
125
125
|
deferMemoryBackendStartup: true,
|
|
126
|
+
deferOptionalModelRefresh: true,
|
|
126
127
|
[lifecycleStartupCapabilityOption]: capability,
|
|
127
128
|
...(mcpStartupTimeoutMs !== undefined ? { [lifecycleMcpStartupTimeoutOption]: mcpStartupTimeoutMs } : {}),
|
|
128
129
|
} as CreateAgentSessionOptions & {
|
package/src/sdk/mcp/server.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getAgentDir } from "@gajae-code/utils";
|
|
1
|
+
import { getAgentDir, VERSION } from "@gajae-code/utils";
|
|
2
2
|
import { ensureBroker } from "../broker/ensure";
|
|
3
3
|
import { lifecycleRequestTimeoutMs } from "../broker/startup-budget";
|
|
4
4
|
import { SdkClientError } from "../client/client";
|
|
@@ -14,6 +14,8 @@ import { type SessionAttachment, SessionRouter, SessionRouterError } from "../ro
|
|
|
14
14
|
import { SessionListTraversalError, sessionListPageFromResponse, traverseSessionList } from "../session-list";
|
|
15
15
|
|
|
16
16
|
const PROTOCOL_VERSION = "2024-11-05";
|
|
17
|
+
const SUPPORTED_PROTOCOL_VERSIONS = [PROTOCOL_VERSION, "2025-03-26", "2025-06-18"] as const;
|
|
18
|
+
const NEWEST_PROTOCOL_VERSION = SUPPORTED_PROTOCOL_VERSIONS[SUPPORTED_PROTOCOL_VERSIONS.length - 1];
|
|
17
19
|
const SERVER_NAME = "gjc-sdk-mcp";
|
|
18
20
|
const ENDPOINT_CREDENTIAL_OPERATION = "session.get_endpoint";
|
|
19
21
|
|
|
@@ -107,6 +109,16 @@ function isObject(value: unknown): value is Arguments {
|
|
|
107
109
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
function negotiatedProtocolVersion(params: unknown): string {
|
|
113
|
+
const requestedProtocolVersion = isObject(params) ? params.protocolVersion : undefined;
|
|
114
|
+
if (
|
|
115
|
+
typeof requestedProtocolVersion === "string" &&
|
|
116
|
+
(SUPPORTED_PROTOCOL_VERSIONS as readonly string[]).includes(requestedProtocolVersion)
|
|
117
|
+
)
|
|
118
|
+
return requestedProtocolVersion;
|
|
119
|
+
return NEWEST_PROTOCOL_VERSION;
|
|
120
|
+
}
|
|
121
|
+
|
|
110
122
|
function asString(args: Arguments, name: string): string | null {
|
|
111
123
|
return typeof args[name] === "string" && args[name] ? (args[name] as string) : null;
|
|
112
124
|
}
|
|
@@ -389,9 +401,9 @@ export function createSdkMcpServer(options: SdkMcpServerOptions = {}) {
|
|
|
389
401
|
jsonrpc: "2.0",
|
|
390
402
|
id,
|
|
391
403
|
result: {
|
|
392
|
-
protocolVersion:
|
|
404
|
+
protocolVersion: negotiatedProtocolVersion(request.params),
|
|
393
405
|
capabilities: { tools: {} },
|
|
394
|
-
serverInfo: { name: SERVER_NAME },
|
|
406
|
+
serverInfo: { name: SERVER_NAME, version: VERSION },
|
|
395
407
|
},
|
|
396
408
|
};
|
|
397
409
|
if (request.method === "tools/list")
|
|
@@ -267,7 +267,7 @@ function controlErrors(id: string): string[] {
|
|
|
267
267
|
// refuses a session with no selected model before admission. The code is
|
|
268
268
|
// appended: ACP machine-only mapping reads errorCodes[0].
|
|
269
269
|
C01: ["client_ref_conflict", "reconciliation_capacity", "reconciliation_persist_failed", "model_not_selected"],
|
|
270
|
-
C02: ["client_ref_conflict", "reconciliation_capacity", "reconciliation_persist_failed"],
|
|
270
|
+
C02: ["turn_not_active", "client_ref_conflict", "reconciliation_capacity", "reconciliation_persist_failed"],
|
|
271
271
|
C09: ["client_ref_conflict", "reconciliation_capacity", "reconciliation_persist_failed"],
|
|
272
272
|
C06: ["action_claimed"],
|
|
273
273
|
C07: ["action_claimed", "terminal_uncertain"],
|
|
@@ -25,7 +25,11 @@ import {
|
|
|
25
25
|
readSdkSessionEndpoint,
|
|
26
26
|
type SdkSessionEndpoint,
|
|
27
27
|
} from "../client/discovery";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
POSITIONED_NOTIFICATION_EFFECTS_CAPABILITY,
|
|
30
|
+
SESSION_HOST_OBSERVER_CAPABILITY,
|
|
31
|
+
TURN_STREAM_CAPABILITY,
|
|
32
|
+
} from "../host/host";
|
|
29
33
|
import {
|
|
30
34
|
type ActivatedPreparedSession,
|
|
31
35
|
type PreparedSessionActivationClient,
|
|
@@ -287,8 +291,17 @@ export interface SessionRouterOptions {
|
|
|
287
291
|
agentDir: string;
|
|
288
292
|
/** Limits attachment to exact ids selected by a Broker-scoped operation. */
|
|
289
293
|
sessionIds?: readonly string[];
|
|
294
|
+
/** Dynamically limits attachment to sessions currently owned by the caller. */
|
|
295
|
+
attachFilter?: (sessionId: string) => boolean;
|
|
290
296
|
/** Marks default SDK clients as notification-only observers; demanding by default. */
|
|
291
297
|
observer?: boolean;
|
|
298
|
+
/**
|
|
299
|
+
* Negotiates positioned-only notification effects on default SDK clients, so the
|
|
300
|
+
* host stops sending them the raw native copy of frames whose positioned
|
|
301
|
+
* envelope already reached them. Only notification-effect publishers opt in;
|
|
302
|
+
* ordinary SDK observers keep both public surfaces.
|
|
303
|
+
*/
|
|
304
|
+
positionedNotificationEffects?: boolean;
|
|
292
305
|
deps?: SessionRouterDeps;
|
|
293
306
|
/** Runtime-specific identity validation; Router supplies a conservative fallback. */
|
|
294
307
|
correlateFrame?: SessionRouterFrameCorrelator;
|
|
@@ -615,7 +628,9 @@ type AdoptedSession = {
|
|
|
615
628
|
export class SessionRouter {
|
|
616
629
|
readonly #agentDir: string;
|
|
617
630
|
readonly #sessionIds: ReadonlySet<string> | undefined;
|
|
631
|
+
readonly #attachFilter: ((sessionId: string) => boolean) | undefined;
|
|
618
632
|
readonly #observer: boolean;
|
|
633
|
+
readonly #positionedNotificationEffects: boolean;
|
|
619
634
|
readonly #deps: SessionRouterDeps;
|
|
620
635
|
readonly #correlateFrame: SessionRouterFrameCorrelator;
|
|
621
636
|
readonly #index: SessionIndex;
|
|
@@ -657,7 +672,9 @@ export class SessionRouter {
|
|
|
657
672
|
constructor(options: SessionRouterOptions) {
|
|
658
673
|
this.#agentDir = options.agentDir;
|
|
659
674
|
this.#sessionIds = options.sessionIds === undefined ? undefined : new Set(options.sessionIds);
|
|
675
|
+
this.#attachFilter = options.attachFilter;
|
|
660
676
|
this.#observer = options.observer === true;
|
|
677
|
+
this.#positionedNotificationEffects = options.positionedNotificationEffects === true;
|
|
661
678
|
this.#deps = options.deps ?? {};
|
|
662
679
|
this.#correlateFrame = options.correlateFrame ?? fallbackCorrelation;
|
|
663
680
|
this.#index = this.#deps.createIndex?.(options.agentDir) ?? new DefaultSessionIndex(options.agentDir);
|
|
@@ -797,6 +814,7 @@ export class SessionRouter {
|
|
|
797
814
|
if (
|
|
798
815
|
sessionId !== fallback.sessionId ||
|
|
799
816
|
(this.#sessionIds !== undefined && !this.#sessionIds.has(sessionId ?? "")) ||
|
|
817
|
+
(this.#attachFilter !== undefined && !this.#attachFilter(sessionId ?? "")) ||
|
|
800
818
|
endpointGeneration === undefined ||
|
|
801
819
|
pid === undefined ||
|
|
802
820
|
endpointMtimeMs === undefined ||
|
|
@@ -1294,7 +1312,8 @@ export class SessionRouter {
|
|
|
1294
1312
|
session.live &&
|
|
1295
1313
|
isSessionAuthorityEligible(session) &&
|
|
1296
1314
|
!session.terminalUncertain &&
|
|
1297
|
-
(this.#sessionIds === undefined || this.#sessionIds.has(session.sessionId))
|
|
1315
|
+
(this.#sessionIds === undefined || this.#sessionIds.has(session.sessionId)) &&
|
|
1316
|
+
(this.#attachFilter === undefined || this.#attachFilter(session.sessionId)),
|
|
1298
1317
|
);
|
|
1299
1318
|
const liveIds = new Set(live.map(session => session.sessionId));
|
|
1300
1319
|
const attachedIds = new Set<string>();
|
|
@@ -1524,9 +1543,13 @@ export class SessionRouter {
|
|
|
1524
1543
|
endpointMtimeMs,
|
|
1525
1544
|
});
|
|
1526
1545
|
} else {
|
|
1546
|
+
const capabilities = [
|
|
1547
|
+
...(this.#observer ? [SESSION_HOST_OBSERVER_CAPABILITY, TURN_STREAM_CAPABILITY] : []),
|
|
1548
|
+
...(this.#positionedNotificationEffects ? [POSITIONED_NOTIFICATION_EFFECTS_CAPABILITY] : []),
|
|
1549
|
+
];
|
|
1527
1550
|
const defaultClient = new SdkClient(endpoint.url, endpoint.token, {
|
|
1528
1551
|
...ACP_SESSION_RECONNECT,
|
|
1529
|
-
...(
|
|
1552
|
+
...(capabilities.length > 0 ? { capabilities } : {}),
|
|
1530
1553
|
});
|
|
1531
1554
|
transport = defaultClient;
|
|
1532
1555
|
connection = defaultClient.connect().then(() => defaultClient);
|
package/src/sdk/session.ts
CHANGED
|
@@ -59,7 +59,11 @@ import { loadCapability, reset as resetCapabilities } from "../capability";
|
|
|
59
59
|
import { type Rule, ruleCapability, setActiveRules } from "../capability/rule";
|
|
60
60
|
import type { SourceMeta } from "../capability/types";
|
|
61
61
|
import { AUTOROUTING_INACTIVE_WARNING } from "../config/autorouting-contract";
|
|
62
|
-
import {
|
|
62
|
+
import {
|
|
63
|
+
isSessionCredentialPinBlocking,
|
|
64
|
+
ModelProfileCredentialError,
|
|
65
|
+
resolveMissingSessionModelRecovery,
|
|
66
|
+
} from "../config/model-profile-activation";
|
|
63
67
|
import { resolveModelProfileName } from "../config/model-profile-contract";
|
|
64
68
|
import { resolveProfileBindings } from "../config/model-profiles";
|
|
65
69
|
import { kNoAuth, ModelRegistry } from "../config/model-registry";
|
|
@@ -157,6 +161,7 @@ import {
|
|
|
157
161
|
type MCPToolCache,
|
|
158
162
|
resolveMCPToolCache,
|
|
159
163
|
} from "../runtime-mcp";
|
|
164
|
+
import { createMCPFormInputHandler } from "../runtime-mcp/elicitation";
|
|
160
165
|
import type { MCPLoadResult } from "../runtime-mcp/manager";
|
|
161
166
|
import { MCP_STARTUP_WAIT_GRACE_MS } from "../runtime-mcp/startup-policy";
|
|
162
167
|
import type { MCPServerConfig } from "../runtime-mcp/types";
|
|
@@ -577,6 +582,12 @@ export interface CreateAgentSessionOptions {
|
|
|
577
582
|
* @internal CLI-only ordering guard; SDK callers retain immediate startup by default.
|
|
578
583
|
*/
|
|
579
584
|
deferMemoryBackendStartup?: boolean;
|
|
585
|
+
/**
|
|
586
|
+
* Skip optional model-catalog discovery during lifecycle startup. ACP hosts must
|
|
587
|
+
* publish their endpoint before unrelated provider credential refreshes begin.
|
|
588
|
+
* @internal lifecycle-only startup guard.
|
|
589
|
+
*/
|
|
590
|
+
deferOptionalModelRefresh?: boolean;
|
|
580
591
|
|
|
581
592
|
/** Enable LSP integration (tool, formatting, diagnostics, warmup). Default: true */
|
|
582
593
|
enableLsp?: boolean;
|
|
@@ -695,6 +706,22 @@ export interface CreateAgentSessionResult {
|
|
|
695
706
|
gjcRuntimeSnapshot?: GjcRuntimeSnapshotProvider;
|
|
696
707
|
}
|
|
697
708
|
|
|
709
|
+
/**
|
|
710
|
+
* Start optional provider/model discovery only when it is safe to add work to
|
|
711
|
+
* the caller's startup graph. Lifecycle hosts deliberately skip this during
|
|
712
|
+
* construction: discovery preflights every configured provider and may refresh
|
|
713
|
+
* an unrelated OAuth credential while several hosts are opening the same DB.
|
|
714
|
+
*/
|
|
715
|
+
export function startOptionalModelRefresh(
|
|
716
|
+
modelRegistry: Pick<ModelRegistry, "refreshInBackground">,
|
|
717
|
+
credentialSessionId: string | undefined,
|
|
718
|
+
deferred: boolean,
|
|
719
|
+
): boolean {
|
|
720
|
+
if (deferred) return false;
|
|
721
|
+
modelRegistry.refreshInBackground("online-if-uncached", credentialSessionId);
|
|
722
|
+
return true;
|
|
723
|
+
}
|
|
724
|
+
|
|
698
725
|
export interface DeferredMcpConfigStartupResult {
|
|
699
726
|
loadedToolCount: number;
|
|
700
727
|
hasErrors: boolean;
|
|
@@ -1970,11 +1997,15 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1970
1997
|
options.preferredCredentialSelector.selector,
|
|
1971
1998
|
);
|
|
1972
1999
|
}
|
|
2000
|
+
// A runtime key or this registry's literal models.yml apiKey outranks an
|
|
2001
|
+
// unavailable pin; without one the provider stays blocked (never retargeted).
|
|
2002
|
+
const isCredentialPinBlocking = (provider: string): boolean =>
|
|
2003
|
+
isSessionCredentialPinBlocking(modelRegistry, provider, credentialSessionId);
|
|
1973
2004
|
const modelApiKeyAvailability = new Map<string, boolean>();
|
|
1974
2005
|
const getModelAvailabilityKey = (candidate: Model): string =>
|
|
1975
2006
|
`${candidate.provider}\u0000${candidate.baseUrl ?? ""}`;
|
|
1976
2007
|
const hasModelApiKey = async (candidate: Model): Promise<boolean> => {
|
|
1977
|
-
if (
|
|
2008
|
+
if (isCredentialPinBlocking(candidate.provider)) return false;
|
|
1978
2009
|
const availabilityKey = getModelAvailabilityKey(candidate);
|
|
1979
2010
|
const cached = modelApiKeyAvailability.get(availabilityKey);
|
|
1980
2011
|
if (cached !== undefined) {
|
|
@@ -2000,6 +2031,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2000
2031
|
const key = await modelRegistry
|
|
2001
2032
|
.getApiKey(candidate, credentialSessionId, { credentialSelector })
|
|
2002
2033
|
.catch(error => {
|
|
2034
|
+
if (isCredentialPinBlocking(candidate.provider)) return undefined;
|
|
2003
2035
|
if (credentialSelector) {
|
|
2004
2036
|
logger.debug("Credential selector did not match model availability candidate", {
|
|
2005
2037
|
provider: candidate.provider,
|
|
@@ -2148,7 +2180,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2148
2180
|
settings,
|
|
2149
2181
|
);
|
|
2150
2182
|
await refreshMissingQualifiedModelProviders(startupModelSelectors, modelRegistry, credentialSessionId);
|
|
2151
|
-
modelRegistry
|
|
2183
|
+
startOptionalModelRefresh(modelRegistry, credentialSessionId, options.deferOptionalModelRefresh === true);
|
|
2152
2184
|
}
|
|
2153
2185
|
|
|
2154
2186
|
const hasExplicitModel = options.model !== undefined || options.modelPattern !== undefined;
|
|
@@ -2241,8 +2273,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2241
2273
|
{
|
|
2242
2274
|
managedFallback: defaultModelEntries.length > 1,
|
|
2243
2275
|
canonicalSessionId: providerSessionId,
|
|
2244
|
-
isCredentialUnavailable:
|
|
2245
|
-
authStorage.hasSessionCredentialUnavailable(provider, credentialSessionId),
|
|
2276
|
+
isCredentialUnavailable: isCredentialPinBlocking,
|
|
2246
2277
|
...(persistedProfileOwnsDefault ? { aliasIntent: "preset-equivalent" as const } : {}),
|
|
2247
2278
|
},
|
|
2248
2279
|
);
|
|
@@ -2297,7 +2328,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2297
2328
|
!hasExplicitModel &&
|
|
2298
2329
|
!model &&
|
|
2299
2330
|
defaultRoleSpec.model &&
|
|
2300
|
-
!
|
|
2331
|
+
!isCredentialPinBlocking(defaultRoleSpec.model.provider) &&
|
|
2301
2332
|
(!preferredCredentialProvider || defaultRoleSpec.model.provider === preferredCredentialProvider)
|
|
2302
2333
|
) {
|
|
2303
2334
|
const settingsDefaultModel = defaultRoleSpec.model;
|
|
@@ -2623,7 +2654,19 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2623
2654
|
let ownedMcpManagerTools: readonly CustomTool[] = [];
|
|
2624
2655
|
let publishOwnedMcpTools = false;
|
|
2625
2656
|
const notificationDebounceTimers = new Map<string, Timer>();
|
|
2657
|
+
const installMcpInputHandler = (manager: MCPManager): void => {
|
|
2658
|
+
manager.setInputRequestHandler(
|
|
2659
|
+
createMCPFormInputHandler({
|
|
2660
|
+
getUi: () => {
|
|
2661
|
+
const context = toolContextStore.getContext();
|
|
2662
|
+
return { ui: context.ui, hasUI: context.hasUI === true };
|
|
2663
|
+
},
|
|
2664
|
+
getAskAnswerSource: () => session.getAskAnswerSource(),
|
|
2665
|
+
}),
|
|
2666
|
+
);
|
|
2667
|
+
};
|
|
2626
2668
|
const wireMcpManagerCallbacks = (manager: MCPManager): void => {
|
|
2669
|
+
installMcpInputHandler(manager);
|
|
2627
2670
|
manager.setOnPromptsChanged(serverName => {
|
|
2628
2671
|
const promptCommands = buildMCPPromptCommands(manager);
|
|
2629
2672
|
session.setMCPPromptCommands(promptCommands);
|
|
@@ -3655,6 +3698,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3655
3698
|
: {}),
|
|
3656
3699
|
});
|
|
3657
3700
|
owned.setAuthStorage(authStorage);
|
|
3701
|
+
installMcpInputHandler(owned);
|
|
3658
3702
|
mcpManager = owned;
|
|
3659
3703
|
ownsMcpManager = true;
|
|
3660
3704
|
registerOwnedMcpManagerCleanup(owned);
|
|
@@ -4236,8 +4280,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
4236
4280
|
{
|
|
4237
4281
|
managedFallback: defaultModelEntries.length > 1,
|
|
4238
4282
|
canonicalSessionId: providerSessionId,
|
|
4239
|
-
isCredentialUnavailable:
|
|
4240
|
-
authStorage.hasSessionCredentialUnavailable(provider, credentialSessionId),
|
|
4283
|
+
isCredentialUnavailable: isCredentialPinBlocking,
|
|
4241
4284
|
...(persistedProfileOwnsDefault ? { aliasIntent: "preset-equivalent" as const } : {}),
|
|
4242
4285
|
},
|
|
4243
4286
|
);
|