@oh-my-pi/pi-coding-agent 17.1.8 → 17.2.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 +92 -0
- package/dist/{CHANGELOG-vt8ene9g.md → CHANGELOG-be1f2t8h.md} +92 -0
- package/dist/cli.js +9986 -9776
- package/dist/{template-dys3vk5b.js → template-8vdv6xb4.js} +1 -1
- package/dist/types/advisor/runtime.d.ts +8 -2
- package/dist/types/advisor/transcript-recorder.d.ts +15 -0
- package/dist/types/auto-thinking/classifier.d.ts +9 -2
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/session-picker.d.ts +14 -7
- package/dist/types/collab/guest.d.ts +21 -2
- package/dist/types/commands/launch.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +63 -8
- package/dist/types/cursor-bridge-tools.d.ts +54 -0
- package/dist/types/cursor.d.ts +193 -8
- package/dist/types/edit/edit-clipboard.d.ts +19 -0
- package/dist/types/edit/hashline/diff.d.ts +9 -1
- package/dist/types/edit/index.d.ts +8 -1
- package/dist/types/edit/streaming.d.ts +8 -1
- package/dist/types/extensibility/extensions/runner.d.ts +19 -2
- package/dist/types/extensibility/extensions/types.d.ts +25 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +2 -0
- package/dist/types/internal-urls/local-protocol.d.ts +3 -2
- package/dist/types/launch/terminal-output-worker-client.d.ts +5 -0
- package/dist/types/launch/terminal-output-worker-protocol.d.ts +14 -0
- package/dist/types/launch/terminal-output-worker.d.ts +1 -0
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/main.d.ts +2 -0
- package/dist/types/mcp/manager.d.ts +33 -4
- package/dist/types/modes/components/codex-reset-fireworks.d.ts +51 -0
- package/dist/types/modes/components/read-tool-group.d.ts +12 -0
- package/dist/types/modes/components/session-selector.d.ts +6 -0
- package/dist/types/modes/components/status-line/component.d.ts +23 -0
- package/dist/types/modes/components/tool-execution.d.ts +16 -7
- package/dist/types/modes/components/usage-row.d.ts +2 -0
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +26 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
- package/dist/types/modes/interactive-mode.d.ts +4 -2
- package/dist/types/modes/rpc/rpc-client.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +16 -0
- package/dist/types/modes/theme/theme.d.ts +12 -1
- package/dist/types/modes/types.d.ts +2 -1
- package/dist/types/sdk.d.ts +1 -0
- package/dist/types/session/agent-session-events.d.ts +2 -0
- package/dist/types/session/agent-session-types.d.ts +36 -1
- package/dist/types/session/agent-session.d.ts +4 -2
- package/dist/types/session/claude-session-store.d.ts +13 -0
- package/dist/types/session/codex-session-store.d.ts +14 -0
- package/dist/types/session/credential-pin.d.ts +58 -0
- package/dist/types/session/foreign-session-import.d.ts +15 -0
- package/dist/types/session/foreign-session-jsonl.d.ts +9 -0
- package/dist/types/session/foreign-session-store.d.ts +23 -0
- package/dist/types/session/session-advisors.d.ts +47 -2
- package/dist/types/session/session-entries.d.ts +19 -1
- package/dist/types/session/session-handoff.d.ts +4 -1
- package/dist/types/session/session-loader.d.ts +2 -0
- package/dist/types/session/session-maintenance.d.ts +1 -1
- package/dist/types/session/session-manager.d.ts +21 -0
- package/dist/types/session/session-tools.d.ts +25 -1
- package/dist/types/session/turn-recovery.d.ts +10 -4
- package/dist/types/system-prompt.d.ts +2 -1
- package/dist/types/task/executor.d.ts +11 -2
- package/dist/types/task/render.d.ts +7 -0
- package/dist/types/thinking.d.ts +18 -6
- package/dist/types/tools/acp-bridge.d.ts +28 -5
- package/dist/types/tools/grep.d.ts +17 -1
- package/dist/types/tools/hub/launch.d.ts +5 -1
- package/dist/types/tools/index.d.ts +4 -1
- package/dist/types/tools/path-utils.d.ts +19 -0
- package/dist/types/tools/read.d.ts +2 -0
- package/dist/types/tools/xdev.d.ts +2 -1
- package/dist/types/utils/changelog.d.ts +19 -0
- package/dist/types/utils/git.d.ts +10 -0
- package/dist/types/utils/jj.d.ts +11 -4
- package/dist/types/web/search/types.d.ts +2 -2
- package/package.json +12 -12
- package/src/advisor/runtime.ts +138 -12
- package/src/advisor/transcript-recorder.ts +56 -0
- package/src/auto-thinking/classifier.ts +48 -10
- package/src/cli/args.ts +6 -0
- package/src/cli/flag-tables.ts +2 -0
- package/src/cli/gallery-fixtures/fs.ts +25 -0
- package/src/cli/session-picker.ts +34 -17
- package/src/cli/ttsr-cli.ts +19 -1
- package/src/cli.ts +9 -0
- package/src/collab/guest.ts +37 -4
- package/src/collab/host.ts +1 -0
- package/src/commands/launch.ts +6 -0
- package/src/config/model-registry.ts +103 -25
- package/src/config/settings-schema.ts +65 -6
- package/src/config/settings.ts +25 -0
- package/src/cursor-bridge-tools.ts +81 -0
- package/src/cursor.ts +463 -12
- package/src/edit/edit-clipboard.ts +23 -0
- package/src/edit/hashline/diff.ts +49 -10
- package/src/edit/hashline/execute.ts +38 -6
- package/src/edit/hashline/filesystem.ts +27 -3
- package/src/edit/index.ts +12 -2
- package/src/edit/renderer.ts +4 -4
- package/src/edit/streaming.ts +17 -2
- package/src/export/html/template.js +1 -1
- package/src/extensibility/extensions/runner.ts +70 -0
- package/src/extensibility/extensions/types.ts +30 -0
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +12 -13
- package/src/internal-urls/local-protocol.ts +3 -2
- package/src/internal-urls/mcp-protocol.ts +4 -1
- package/src/launch/terminal-output-worker-client.ts +53 -0
- package/src/launch/terminal-output-worker-protocol.ts +11 -0
- package/src/launch/terminal-output-worker.ts +23 -0
- package/src/lsp/client.ts +18 -3
- package/src/lsp/index.ts +4 -0
- package/src/lsp/render.ts +1 -1
- package/src/main.ts +127 -56
- package/src/mcp/manager.ts +120 -18
- package/src/modes/acp/acp-agent.ts +15 -9
- package/src/modes/components/chat-transcript-builder.ts +30 -11
- package/src/modes/components/codex-reset-fireworks.ts +369 -0
- package/src/modes/components/read-tool-group.ts +159 -17
- package/src/modes/components/session-selector.ts +19 -3
- package/src/modes/components/status-line/component.ts +381 -54
- package/src/modes/components/tool-execution.ts +78 -31
- package/src/modes/components/usage-row.ts +10 -5
- package/src/modes/controllers/event-controller.ts +26 -4
- package/src/modes/controllers/mcp-command-controller.ts +68 -3
- package/src/modes/controllers/selector-controller.ts +94 -36
- package/src/modes/controllers/tan-command-controller.ts +14 -0
- package/src/modes/interactive-mode.ts +31 -17
- package/src/modes/rpc/rpc-client.ts +18 -0
- package/src/modes/rpc/rpc-mode.ts +16 -1
- package/src/modes/rpc/rpc-types.ts +11 -0
- package/src/modes/theme/theme.ts +151 -6
- package/src/modes/types.ts +2 -1
- package/src/modes/utils/ui-helpers.ts +38 -21
- package/src/prompts/system/auto-thinking-difficulty.md +4 -2
- package/src/prompts/system/plan-mode-active.md +2 -2
- package/src/prompts/system/system-prompt.md +3 -0
- package/src/prompts/system/xdev-mount-notice.md +1 -1
- package/src/prompts/tools/bash.md +15 -17
- package/src/prompts/tools/checkpoint.md +1 -1
- package/src/prompts/tools/glob.md +7 -6
- package/src/prompts/tools/grep.md +7 -6
- package/src/sdk.ts +178 -15
- package/src/session/agent-session-events.ts +1 -0
- package/src/session/agent-session-types.ts +40 -1
- package/src/session/agent-session.ts +302 -175
- package/src/session/claude-session-store.ts +426 -0
- package/src/session/codex-session-store.ts +673 -0
- package/src/session/credential-pin.ts +93 -0
- package/src/session/foreign-session-import.ts +52 -0
- package/src/session/foreign-session-jsonl.ts +29 -0
- package/src/session/foreign-session-store.ts +26 -0
- package/src/session/model-controls.ts +11 -3
- package/src/session/session-advisors.ts +140 -16
- package/src/session/session-entries.ts +21 -1
- package/src/session/session-handoff.ts +20 -3
- package/src/session/session-loader.ts +25 -9
- package/src/session/session-maintenance.ts +59 -11
- package/src/session/session-manager.ts +61 -0
- package/src/session/session-tools.ts +107 -5
- package/src/session/settings-stream-fn.ts +1 -0
- package/src/session/turn-recovery.ts +97 -51
- package/src/slash-commands/builtin-registry.ts +131 -3
- package/src/system-prompt.ts +3 -2
- package/src/task/executor.ts +17 -4
- package/src/task/render.ts +20 -4
- package/src/thinking.ts +29 -10
- package/src/tools/acp-bridge.ts +52 -8
- package/src/tools/browser.ts +5 -0
- package/src/tools/checkpoint.ts +0 -13
- package/src/tools/grep.ts +64 -8
- package/src/tools/hub/launch.ts +10 -15
- package/src/tools/index.ts +28 -4
- package/src/tools/path-utils.ts +88 -0
- package/src/tools/read.ts +3 -0
- package/src/tools/write.ts +9 -3
- package/src/tools/xdev.ts +39 -14
- package/src/utils/changelog.ts +144 -4
- package/src/utils/git.ts +61 -14
- package/src/utils/jj.ts +19 -13
- package/src/web/search/providers/codex.ts +34 -0
- package/src/web/search/types.ts +1 -1
package/src/mcp/manager.ts
CHANGED
|
@@ -94,6 +94,14 @@ const STARTUP_TIMEOUT_MS = 250;
|
|
|
94
94
|
const RECONNECT_BURST_WINDOW_MS = 30_000;
|
|
95
95
|
const RECONNECT_BURST_LIMIT = 5;
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Bounded buffer for notifications received before any listener attaches.
|
|
99
|
+
* Mirrors {@link IrcBus}'s `MAILBOX_CAP` — drop-oldest on overflow. Drained
|
|
100
|
+
* into the first {@link MCPManager.addNotificationListener} subscriber, then
|
|
101
|
+
* cleared; subsequent frames deliver directly to attached listeners.
|
|
102
|
+
*/
|
|
103
|
+
const NOTIFICATION_BUFFER_CAP = 100;
|
|
104
|
+
|
|
97
105
|
function trackPromise<T>(promise: Promise<T>): TrackedPromise<T> {
|
|
98
106
|
const tracked: TrackedPromise<T> = { promise, status: "pending" };
|
|
99
107
|
promise.then(
|
|
@@ -194,8 +202,14 @@ export class MCPManager {
|
|
|
194
202
|
#sources = new Map<string, SourceMeta>();
|
|
195
203
|
#authStorage: AuthStorage | null = null;
|
|
196
204
|
#authHandler?: MCPAuthHandler;
|
|
197
|
-
#
|
|
198
|
-
|
|
205
|
+
#notificationListeners = new Set<(serverName: string, method: string, params: unknown) => void>();
|
|
206
|
+
/**
|
|
207
|
+
* Notifications received before any listener attached, to be drained on
|
|
208
|
+
* the first {@link addNotificationListener} call. Bounded by
|
|
209
|
+
* {@link NOTIFICATION_BUFFER_CAP}, drop-oldest on overflow.
|
|
210
|
+
*/
|
|
211
|
+
#pendingNotifications: Array<{ server: string; method: string; params: unknown }> = [];
|
|
212
|
+
#onToolsChanged?: (tools: CustomTool<TSchema, MCPToolDetails>[]) => void | Promise<void>;
|
|
199
213
|
#onResourcesChanged?: (serverName: string, uri: string) => void;
|
|
200
214
|
#onPromptsChanged?: (serverName: string) => void;
|
|
201
215
|
#notificationsEnabled = false;
|
|
@@ -219,16 +233,66 @@ export class MCPManager {
|
|
|
219
233
|
) {}
|
|
220
234
|
|
|
221
235
|
/**
|
|
222
|
-
*
|
|
236
|
+
* Register a listener for server-initiated MCP notifications.
|
|
237
|
+
*
|
|
238
|
+
* The listener is called for every JSON-RPC notification received from any
|
|
239
|
+
* connected server, AFTER the manager's own handling of known methods
|
|
240
|
+
* (`notifications/tools/list_changed`, `notifications/resources/list_changed`,
|
|
241
|
+
* `notifications/resources/updated`, `notifications/prompts/list_changed`).
|
|
242
|
+
* For list-change methods the internal refresh promise is awaited before
|
|
243
|
+
* fanout, so listeners observe up-to-date manager and tool state. Unknown
|
|
244
|
+
* or server-custom methods are also delivered, letting consumers bridge
|
|
245
|
+
* server-initiated events into session-level behavior (e.g. an extension
|
|
246
|
+
* injecting a steer via `pi.sendMessage`).
|
|
247
|
+
*
|
|
248
|
+
* Notifications received before any listener attached are buffered
|
|
249
|
+
* (bounded FIFO, cap {@link NOTIFICATION_BUFFER_CAP}, drop-oldest) and
|
|
250
|
+
* drained into the first subscriber — matches {@link setOnPromptsChanged}'s
|
|
251
|
+
* replay-on-attach and {@link IrcBus}'s mailbox semantics.
|
|
252
|
+
*
|
|
253
|
+
* Returns an unsubscribe function; call it to remove the listener.
|
|
254
|
+
*
|
|
255
|
+
* Multiple listeners are allowed; each is invoked with independent error
|
|
256
|
+
* isolation — a listener that throws does not prevent other listeners from
|
|
257
|
+
* firing.
|
|
223
258
|
*/
|
|
224
|
-
|
|
225
|
-
this.#
|
|
259
|
+
addNotificationListener(listener: (serverName: string, method: string, params: unknown) => void): () => void {
|
|
260
|
+
const wasEmpty = this.#notificationListeners.size === 0;
|
|
261
|
+
this.#notificationListeners.add(listener);
|
|
262
|
+
|
|
263
|
+
// Drain startup-buffered notifications into the first attaching listener.
|
|
264
|
+
if (wasEmpty && this.#pendingNotifications.length > 0) {
|
|
265
|
+
const pending = this.#pendingNotifications.splice(0);
|
|
266
|
+
for (const frame of pending) {
|
|
267
|
+
try {
|
|
268
|
+
listener(frame.server, frame.method, frame.params);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
logger.debug("MCP notification listener threw during buffered drain", {
|
|
271
|
+
path: `mcp:${frame.server}`,
|
|
272
|
+
method: frame.method,
|
|
273
|
+
error,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return () => {
|
|
280
|
+
this.#notificationListeners.delete(listener);
|
|
281
|
+
};
|
|
226
282
|
}
|
|
227
283
|
|
|
228
284
|
/**
|
|
229
285
|
* Set a callback to fire when any server's tools change.
|
|
286
|
+
*
|
|
287
|
+
* May return a Promise; if so, {@link refreshServerTools} awaits it so that
|
|
288
|
+
* downstream consumers (e.g. `mcp_notification` listeners for
|
|
289
|
+
* `notifications/tools/list_changed`) observe not just the manager's
|
|
290
|
+
* refreshed tool set but also any session-level rebind driven by the
|
|
291
|
+
* handler (`session.refreshMCPTools`). Other callsites (initial connect,
|
|
292
|
+
* disconnect, reconnect) invoke the handler synchronously — their downstream
|
|
293
|
+
* chains don't need to serialize on the rebind.
|
|
230
294
|
*/
|
|
231
|
-
setOnToolsChanged(handler: (tools: CustomTool<TSchema, MCPToolDetails>[]) => void): void {
|
|
295
|
+
setOnToolsChanged(handler: (tools: CustomTool<TSchema, MCPToolDetails>[]) => void | Promise<void>): void {
|
|
232
296
|
this.#onToolsChanged = handler;
|
|
233
297
|
}
|
|
234
298
|
|
|
@@ -488,7 +552,7 @@ export class MCPManager {
|
|
|
488
552
|
this.reconnectServer(name, options);
|
|
489
553
|
const customTools = MCPTool.fromTools(connection, serverTools, reconnect);
|
|
490
554
|
this.#replaceServerTools(name, customTools);
|
|
491
|
-
this.#onToolsChanged?.(this.#tools);
|
|
555
|
+
void this.#onToolsChanged?.(this.#tools);
|
|
492
556
|
void this.toolCache?.set(name, config, serverTools);
|
|
493
557
|
|
|
494
558
|
onStatus?.({ type: "connected", serverName: name });
|
|
@@ -597,7 +661,7 @@ export class MCPManager {
|
|
|
597
661
|
sortMCPToolsByName(this.#tools);
|
|
598
662
|
}
|
|
599
663
|
|
|
600
|
-
#triggerNotificationRefresh(serverName: string, kind: "tools" | "resources" | "prompts"): void {
|
|
664
|
+
#triggerNotificationRefresh(serverName: string, kind: "tools" | "resources" | "prompts"): Promise<void> {
|
|
601
665
|
const refresh = (() => {
|
|
602
666
|
switch (kind) {
|
|
603
667
|
case "tools":
|
|
@@ -608,22 +672,33 @@ export class MCPManager {
|
|
|
608
672
|
return this.refreshServerPrompts(serverName);
|
|
609
673
|
}
|
|
610
674
|
})();
|
|
611
|
-
|
|
675
|
+
return refresh.catch(error => {
|
|
612
676
|
logger.debug("Failed MCP notification refresh", { path: `mcp:${serverName}`, kind, error });
|
|
613
677
|
});
|
|
614
678
|
}
|
|
615
|
-
#handleServerNotification(serverName: string, method: string, params: unknown): void {
|
|
679
|
+
async #handleServerNotification(serverName: string, method: string, params: unknown): Promise<void> {
|
|
616
680
|
logger.debug("MCP notification received", { path: `mcp:${serverName}`, method });
|
|
617
681
|
|
|
682
|
+
// Only trigger refresh if the connection is already stored — during the
|
|
683
|
+
// initial connect handshake, notifications may arrive before
|
|
684
|
+
// `#connections.set()` completes, and `refreshServer*` would no-op
|
|
685
|
+
// anyway. Skipping the await in that case preserves arrival order
|
|
686
|
+
// across concurrently-dispatched notifications (an awaited refresh,
|
|
687
|
+
// even a no-op, yields a microtask that lets later frames overtake).
|
|
688
|
+
const connectionKnown = this.#connections.has(serverName);
|
|
689
|
+
let refreshPromise: Promise<void> | undefined;
|
|
618
690
|
switch (method) {
|
|
619
691
|
case MCPNotificationMethods.TOOLS_LIST_CHANGED:
|
|
620
|
-
this.#triggerNotificationRefresh(serverName, "tools");
|
|
692
|
+
if (connectionKnown) refreshPromise = this.#triggerNotificationRefresh(serverName, "tools");
|
|
621
693
|
break;
|
|
622
694
|
case MCPNotificationMethods.RESOURCES_LIST_CHANGED:
|
|
623
|
-
this.#triggerNotificationRefresh(serverName, "resources");
|
|
695
|
+
if (connectionKnown) refreshPromise = this.#triggerNotificationRefresh(serverName, "resources");
|
|
624
696
|
break;
|
|
625
697
|
case MCPNotificationMethods.RESOURCES_UPDATED: {
|
|
626
|
-
const uri =
|
|
698
|
+
const uri =
|
|
699
|
+
params && typeof params === "object" && "uri" in params && typeof params.uri === "string"
|
|
700
|
+
? params.uri
|
|
701
|
+
: undefined;
|
|
627
702
|
const subscribed = this.#subscribedResources.get(serverName);
|
|
628
703
|
if (uri && subscribed?.has(uri)) {
|
|
629
704
|
this.#onResourcesChanged?.(serverName, uri);
|
|
@@ -631,13 +706,40 @@ export class MCPManager {
|
|
|
631
706
|
break;
|
|
632
707
|
}
|
|
633
708
|
case MCPNotificationMethods.PROMPTS_LIST_CHANGED:
|
|
634
|
-
this.#triggerNotificationRefresh(serverName, "prompts");
|
|
709
|
+
if (connectionKnown) refreshPromise = this.#triggerNotificationRefresh(serverName, "prompts");
|
|
635
710
|
break;
|
|
636
711
|
default:
|
|
637
712
|
break;
|
|
638
713
|
}
|
|
639
714
|
|
|
640
|
-
|
|
715
|
+
// Await internal refresh so listeners see the manager's post-refresh
|
|
716
|
+
// state (satisfies the documented "AFTER the manager's own handling"
|
|
717
|
+
// contract on `addNotificationListener` — otherwise an extension acting
|
|
718
|
+
// on `tools/list_changed` could hit stale `getTools()`).
|
|
719
|
+
if (refreshPromise) {
|
|
720
|
+
await refreshPromise;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Buffer for late-attaching subscribers when no listener exists yet.
|
|
724
|
+
if (this.#notificationListeners.size === 0) {
|
|
725
|
+
this.#pendingNotifications.push({ server: serverName, method, params });
|
|
726
|
+
if (this.#pendingNotifications.length > NOTIFICATION_BUFFER_CAP) {
|
|
727
|
+
this.#pendingNotifications.shift();
|
|
728
|
+
}
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
for (const listener of this.#notificationListeners) {
|
|
733
|
+
try {
|
|
734
|
+
listener(serverName, method, params);
|
|
735
|
+
} catch (error) {
|
|
736
|
+
logger.debug("MCP notification listener threw", {
|
|
737
|
+
path: `mcp:${serverName}`,
|
|
738
|
+
method,
|
|
739
|
+
error,
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
}
|
|
641
743
|
}
|
|
642
744
|
|
|
643
745
|
/** Handle server-to-client JSON-RPC requests (e.g. ping, roots/list). */
|
|
@@ -781,7 +883,7 @@ export class MCPManager {
|
|
|
781
883
|
// Remove tools from this server and notify consumers
|
|
782
884
|
const hadTools = this.#tools.some(t => t.mcpServerName === name);
|
|
783
885
|
this.#tools = this.#tools.filter(t => t.mcpServerName !== name);
|
|
784
|
-
if (hadTools) this.#onToolsChanged?.(this.#tools);
|
|
886
|
+
if (hadTools) void this.#onToolsChanged?.(this.#tools);
|
|
785
887
|
|
|
786
888
|
// Notify prompt consumers so stale commands are cleared
|
|
787
889
|
if (connection?.prompts?.length) this.#onPromptsChanged?.(name);
|
|
@@ -1022,7 +1124,7 @@ export class MCPManager {
|
|
|
1022
1124
|
const customTools = MCPTool.fromTools(connection, serverTools, reconnect);
|
|
1023
1125
|
void this.toolCache?.set(name, config, serverTools);
|
|
1024
1126
|
this.#replaceServerTools(name, customTools);
|
|
1025
|
-
this.#onToolsChanged?.(this.#tools);
|
|
1127
|
+
void this.#onToolsChanged?.(this.#tools);
|
|
1026
1128
|
void this.#loadServerResourcesAndPrompts(name, connection);
|
|
1027
1129
|
return connection;
|
|
1028
1130
|
} catch (error) {
|
|
@@ -1075,7 +1177,7 @@ export class MCPManager {
|
|
|
1075
1177
|
|
|
1076
1178
|
// Replace tools from this server
|
|
1077
1179
|
this.#replaceServerTools(name, customTools);
|
|
1078
|
-
this.#onToolsChanged?.(this.#tools);
|
|
1180
|
+
await this.#onToolsChanged?.(this.#tools);
|
|
1079
1181
|
}
|
|
1080
1182
|
|
|
1081
1183
|
/**
|
|
@@ -651,12 +651,17 @@ export class AcpAgent implements Agent {
|
|
|
651
651
|
});
|
|
652
652
|
}
|
|
653
653
|
|
|
654
|
-
// For `thinking`
|
|
655
|
-
//
|
|
656
|
-
//
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
654
|
+
// For `model`/`thinking`, `#setModelById`/`#setThinkingLevelById` change
|
|
655
|
+
// the session model/thinking level through AgentSession, which now emits
|
|
656
|
+
// a lifetime event (`model_changed`/`thinking_level_changed`) that
|
|
657
|
+
// `#handleLifetimeEvent` turns into a push once the subscription is
|
|
658
|
+
// installed. Only push here when that subscription is not yet
|
|
659
|
+
// installed, so pre-bootstrap callers still see the change without a
|
|
660
|
+
// post-bootstrap duplicate.
|
|
661
|
+
const handledBySubscription =
|
|
662
|
+
(params.configId === THINKING_CONFIG_ID || params.configId === MODEL_CONFIG_ID) &&
|
|
663
|
+
record.lifetimeUnsubscribe !== undefined;
|
|
664
|
+
if (!handledBySubscription) {
|
|
660
665
|
await this.#pushConfigOptionUpdate(record);
|
|
661
666
|
}
|
|
662
667
|
return { configOptions: this.#buildConfigOptions(record.session) };
|
|
@@ -1150,14 +1155,15 @@ export class AcpAgent implements Agent {
|
|
|
1150
1155
|
}
|
|
1151
1156
|
|
|
1152
1157
|
async #handleLifetimeEvent(record: ManagedSessionRecord, event: AgentSessionEvent): Promise<void> {
|
|
1153
|
-
if (event.type !== "thinking_level_changed") {
|
|
1158
|
+
if (event.type !== "thinking_level_changed" && event.type !== "model_changed") {
|
|
1154
1159
|
return;
|
|
1155
1160
|
}
|
|
1156
1161
|
try {
|
|
1157
1162
|
await this.#pushConfigOptionUpdate(record);
|
|
1158
1163
|
} catch (error) {
|
|
1159
|
-
logger.warn("Failed to push
|
|
1164
|
+
logger.warn("Failed to push config_option_update after a lifetime event", {
|
|
1160
1165
|
sessionId: record.session.sessionId,
|
|
1166
|
+
eventType: event.type,
|
|
1161
1167
|
error,
|
|
1162
1168
|
});
|
|
1163
1169
|
}
|
|
@@ -1575,7 +1581,7 @@ export class AcpAgent implements Agent {
|
|
|
1575
1581
|
#buildThinkingOptions(session: AgentSession): Array<{ value: string; name: string; description?: string }> {
|
|
1576
1582
|
return [
|
|
1577
1583
|
{ value: THINKING_OFF, name: "Off" },
|
|
1578
|
-
{ value: AUTO_THINKING, name: "Auto", description: "Auto-detect per prompt
|
|
1584
|
+
{ value: AUTO_THINKING, name: "Auto", description: "Auto-detect per prompt" },
|
|
1579
1585
|
...session.getAvailableThinkingLevels().map(level => ({
|
|
1580
1586
|
value: level,
|
|
1581
1587
|
name: level,
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
import { CustomMessageComponent } from "./custom-message";
|
|
52
52
|
import { EvalExecutionComponent } from "./eval-execution";
|
|
53
53
|
import { type LateDiagnosticsFile, LateDiagnosticsMessageComponent } from "./late-diagnostics-message";
|
|
54
|
-
import { ReadToolGroupComponent, readArgsCollapseIntoGroup } from "./read-tool-group";
|
|
54
|
+
import { groupedReadUsageCallIds, ReadToolGroupComponent, readArgsCollapseIntoGroup } from "./read-tool-group";
|
|
55
55
|
import { SkillMessageComponent } from "./skill-message";
|
|
56
56
|
import { ToolExecutionComponent } from "./tool-execution";
|
|
57
57
|
import { TranscriptContainer } from "./transcript-container";
|
|
@@ -86,6 +86,7 @@ export class ChatTranscriptBuilder {
|
|
|
86
86
|
#pendingUsageDuration: number | undefined;
|
|
87
87
|
#pendingUsageTtft: number | undefined;
|
|
88
88
|
#pendingUsageTimestamp: number | undefined;
|
|
89
|
+
#pendingReadUsageCallIds: string[] | undefined;
|
|
89
90
|
#lastAssistantUsage: Usage | undefined;
|
|
90
91
|
#waitingPoll: ToolExecutionComponent | null = null;
|
|
91
92
|
#todoSnapshot: ToolExecutionComponent | null = null;
|
|
@@ -135,6 +136,7 @@ export class ChatTranscriptBuilder {
|
|
|
135
136
|
this.#pendingUsageDuration = undefined;
|
|
136
137
|
this.#pendingUsageTtft = undefined;
|
|
137
138
|
this.#pendingUsageTimestamp = undefined;
|
|
139
|
+
this.#pendingReadUsageCallIds = undefined;
|
|
138
140
|
this.#lastAssistantUsage = undefined;
|
|
139
141
|
this.#waitingPoll = null;
|
|
140
142
|
this.#todoSnapshot = null;
|
|
@@ -194,30 +196,46 @@ export class ChatTranscriptBuilder {
|
|
|
194
196
|
return this.#readGroup;
|
|
195
197
|
}
|
|
196
198
|
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
// are placed, sealing the read run so the row sits under it.
|
|
199
|
+
// Defer per-turn metrics until the turn's tool results have materialized.
|
|
200
|
+
// Read-only invisible turns attach the metrics to their shared compact
|
|
201
|
+
// group; every other turn keeps the standalone row below its tool blocks.
|
|
201
202
|
#flushPendingUsage(): void {
|
|
202
203
|
if (!this.#pendingUsage) return;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
const usageAttached =
|
|
205
|
+
this.#pendingReadUsageCallIds !== undefined &&
|
|
206
|
+
(this.#readGroup?.attachUsage(
|
|
207
|
+
this.#pendingReadUsageCallIds,
|
|
207
208
|
this.#pendingUsage,
|
|
208
209
|
this.#pendingUsageDuration,
|
|
209
210
|
this.#pendingUsageTtft,
|
|
210
211
|
this.#pendingUsageTimestamp,
|
|
211
|
-
)
|
|
212
|
-
|
|
212
|
+
) ??
|
|
213
|
+
false);
|
|
214
|
+
if (!usageAttached) {
|
|
215
|
+
this.#readGroup?.seal();
|
|
216
|
+
this.#readGroup = null;
|
|
217
|
+
this.container.addChild(
|
|
218
|
+
createUsageRowBlock(
|
|
219
|
+
this.#pendingUsage,
|
|
220
|
+
this.#pendingUsageDuration,
|
|
221
|
+
this.#pendingUsageTtft,
|
|
222
|
+
this.#pendingUsageTimestamp,
|
|
223
|
+
),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
213
226
|
this.#pendingUsage = undefined;
|
|
214
227
|
this.#pendingUsageDuration = undefined;
|
|
215
228
|
this.#pendingUsageTtft = undefined;
|
|
216
229
|
this.#pendingUsageTimestamp = undefined;
|
|
230
|
+
this.#pendingReadUsageCallIds = undefined;
|
|
217
231
|
}
|
|
218
232
|
|
|
219
233
|
#appendChatMessage(message: AgentMessage): void {
|
|
220
234
|
if (message.role !== "toolResult") this.#flushPendingUsage();
|
|
235
|
+
if (message.role !== "assistant" && message.role !== "toolResult") {
|
|
236
|
+
this.#readGroup?.seal();
|
|
237
|
+
this.#readGroup = null;
|
|
238
|
+
}
|
|
221
239
|
switch (message.role) {
|
|
222
240
|
case "assistant":
|
|
223
241
|
this.#appendAssistantMessage(message);
|
|
@@ -403,6 +421,7 @@ export class ChatTranscriptBuilder {
|
|
|
403
421
|
this.#pendingUsageDuration = message.duration;
|
|
404
422
|
this.#pendingUsageTtft = message.ttft;
|
|
405
423
|
this.#pendingUsageTimestamp = message.timestamp;
|
|
424
|
+
this.#pendingReadUsageCallIds = this.#pendingUsage ? groupedReadUsageCallIds(message) : undefined;
|
|
406
425
|
}
|
|
407
426
|
|
|
408
427
|
#appendToolResult(message: Extract<AgentMessage, { role: "toolResult" }>): void {
|