neoctl 0.2.42 → 0.2.44
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/dist/agents/agent-tool.d.ts +1 -0
- package/dist/agents/agent-tool.js +4 -3
- package/dist/agents/agent-tool.js.map +1 -1
- package/dist/context/prompts.js +2 -2
- package/dist/context/prompts.js.map +1 -1
- package/dist/core/image-registry.js +1 -1
- package/dist/core/image-registry.js.map +1 -1
- package/dist/core/message-pipeline.js +2 -1
- package/dist/core/message-pipeline.js.map +1 -1
- package/dist/core/query-engine.d.ts +8 -2
- package/dist/core/query-engine.js +28 -19
- package/dist/core/query-engine.js.map +1 -1
- package/dist/core/query-timing.d.ts +43 -0
- package/dist/core/query-timing.js +124 -0
- package/dist/core/query-timing.js.map +1 -0
- package/dist/core/query.d.ts +13 -0
- package/dist/core/query.js +150 -90
- package/dist/core/query.js.map +1 -1
- package/dist/core/run-agent.d.ts +3 -0
- package/dist/core/run-agent.js +12 -0
- package/dist/core/run-agent.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/model/model-gateway.d.ts +6 -0
- package/dist/model/model-gateway.js.map +1 -1
- package/dist/model/openai-chat-mapper.js +6 -0
- package/dist/model/openai-chat-mapper.js.map +1 -1
- package/dist/model/openai-mappers.js +2 -2
- package/dist/model/openai-mappers.js.map +1 -1
- package/dist/model/openai-responses-mapper.js +6 -0
- package/dist/model/openai-responses-mapper.js.map +1 -1
- package/dist/plugins/plugin-system.d.ts +6 -0
- package/dist/plugins/plugin-system.js +82 -52
- package/dist/plugins/plugin-system.js.map +1 -1
- package/dist/session/session-store.d.ts +11 -0
- package/dist/session/session-store.js +24 -0
- package/dist/session/session-store.js.map +1 -1
- package/dist/tools/builtins/image-capabilities.d.ts +1 -0
- package/dist/tools/builtins/image-capabilities.js +2 -1
- package/dist/tools/builtins/image-capabilities.js.map +1 -1
- package/dist/tools/builtins/image-generation-tool.js +44 -17
- package/dist/tools/builtins/image-generation-tool.js.map +1 -1
- package/dist/tools/registry.d.ts +2 -0
- package/dist/tools/registry.js +11 -0
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/tool-orchestration.d.ts +5 -0
- package/dist/tools/tool-orchestration.js +1 -0
- package/dist/tools/tool-orchestration.js.map +1 -1
- package/dist/types/events.d.ts +10 -0
- package/dist/web/agent-content-detail.d.ts +2 -2
- package/dist/web/agent-content-detail.js +1 -1
- package/dist/web/index.d.ts +31 -2
- package/dist/web/index.js +159 -33
- package/dist/web/index.js.map +1 -1
- package/dist/web/tool-call-detail.js +1 -1
- package/package.json +1 -1
|
@@ -141,7 +141,7 @@ function part(owner, v) {
|
|
|
141
141
|
if (v === undefined)
|
|
142
142
|
return absent();
|
|
143
143
|
const isTruncated = truncated(v);
|
|
144
|
-
return { state: isTruncated ? 'truncated' : 'complete', text: text(sanitize(owner, v)), reason: isTruncated ? '保存的源数据已截断,不是全文' : '
|
|
144
|
+
return { state: isTruncated ? 'truncated' : 'complete', text: text(sanitize(owner, v)), reason: isTruncated ? '保存的源数据已截断,不是全文' : '已保存内容的原始全文' };
|
|
145
145
|
}
|
|
146
146
|
function preview(p, max = 2048) {
|
|
147
147
|
return p.text.length <= max ? p : { state: 'truncated', text: p.text.slice(0, max), reason: '摘要明确截断;不作为全文' };
|
package/dist/web/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import type { TimingRecord } from "../core/query-timing.js";
|
|
2
3
|
import { type ImageResultMetadata } from "./image-result-metadata.js";
|
|
3
4
|
import { type IncomingMessage, type ServerResponse } from "node:http";
|
|
4
5
|
import { QueryEngine } from "../core/query-engine.js";
|
|
@@ -41,7 +42,14 @@ export interface WebRuntimePluginDefinition extends Pick<NeoPluginResource, "id"
|
|
|
41
42
|
promptSections?: readonly PromptSection[];
|
|
42
43
|
presentToolResult?: NeoPluginResource["presentToolResult"];
|
|
43
44
|
}
|
|
45
|
+
export interface WebPluginSnapshot {
|
|
46
|
+
plugins: readonly WebRuntimePluginDefinition[];
|
|
47
|
+
release: () => void;
|
|
48
|
+
}
|
|
44
49
|
interface WebRuntimePluginSupport {
|
|
50
|
+
resolveCatalog?: () => readonly WebRuntimePluginDefinition[];
|
|
51
|
+
acquire?: (overrides: Readonly<Record<string, boolean>>) => WebPluginSnapshot;
|
|
52
|
+
pendingOverrides?: Record<string, boolean>;
|
|
45
53
|
catalog: WebRuntimePluginDefinition[];
|
|
46
54
|
overrides: Record<string, boolean>;
|
|
47
55
|
basePromptSections: PromptSection[];
|
|
@@ -161,6 +169,7 @@ interface UiLine {
|
|
|
161
169
|
toolPresentation?: ToolPresentation;
|
|
162
170
|
parentToolName?: string;
|
|
163
171
|
toolUseId?: string;
|
|
172
|
+
timing?: TimingRecord;
|
|
164
173
|
parentToolUseId?: string;
|
|
165
174
|
title?: string;
|
|
166
175
|
bodyTitle?: string;
|
|
@@ -188,6 +197,15 @@ type WebActionResult<T extends object = object> = ({
|
|
|
188
197
|
ok: true;
|
|
189
198
|
} & T) | WebActionFailure;
|
|
190
199
|
interface UiStatus {
|
|
200
|
+
modelOutput?: {
|
|
201
|
+
kind: "text";
|
|
202
|
+
} | {
|
|
203
|
+
kind: "tool_call";
|
|
204
|
+
callId: string;
|
|
205
|
+
name?: string;
|
|
206
|
+
};
|
|
207
|
+
queryTiming?: TimingRecord;
|
|
208
|
+
toolTimings?: TimingRecord[];
|
|
191
209
|
phase: string;
|
|
192
210
|
detail?: string;
|
|
193
211
|
currentTool?: {
|
|
@@ -234,6 +252,9 @@ export interface CreateWebRuntimeOptions {
|
|
|
234
252
|
plugins?: readonly string[];
|
|
235
253
|
/** Switchable plugins supplied by the embedding application. */
|
|
236
254
|
externalPlugins?: readonly WebRuntimePluginDefinition[];
|
|
255
|
+
resolveExternalPlugins?: WebRuntimePluginSupport["resolveCatalog"];
|
|
256
|
+
acquirePluginSnapshot?: WebRuntimePluginSupport["acquire"];
|
|
257
|
+
reservePluginToolNames?: (names: readonly string[]) => void;
|
|
237
258
|
/** Per-session plugin overrides. Missing ids inherit the global setting. */
|
|
238
259
|
sessionPluginOverrides?: Readonly<Record<string, boolean>>;
|
|
239
260
|
persistSessionPluginOverrides?: WebRuntimePluginSupport["persist"];
|
|
@@ -312,6 +333,7 @@ type WebLineOperation = {
|
|
|
312
333
|
};
|
|
313
334
|
export declare function runWebServer(argv?: string[], runtimeOptions?: RunWebServerOptions): Promise<void>;
|
|
314
335
|
export declare function createWebRuntime(options?: CreateWebRuntimeOptions): Promise<WebRuntime>;
|
|
336
|
+
export declare function refreshRuntimePlugins(runtime: WebRuntime, engine?: QueryEngine): void;
|
|
315
337
|
/** Called only at a safe turn boundary or while idle, never during tool execution. */
|
|
316
338
|
export declare function refreshGlobalToolOverrides(runtime: Pick<WebRuntime, "tools" | "toolSupport">): void;
|
|
317
339
|
/** Bind only the foreground view; never stop work owned by another session. */
|
|
@@ -438,7 +460,7 @@ export declare class WebRepl {
|
|
|
438
460
|
steps: {
|
|
439
461
|
id: string;
|
|
440
462
|
title: string;
|
|
441
|
-
status: "
|
|
463
|
+
status: "running" | "completed" | "failed";
|
|
442
464
|
}[] | undefined;
|
|
443
465
|
};
|
|
444
466
|
pendingMessageCount: number;
|
|
@@ -566,7 +588,7 @@ export declare class WebRepl {
|
|
|
566
588
|
steps: {
|
|
567
589
|
id: string;
|
|
568
590
|
title: string;
|
|
569
|
-
status: "
|
|
591
|
+
status: "running" | "completed" | "failed";
|
|
570
592
|
}[] | undefined;
|
|
571
593
|
};
|
|
572
594
|
pendingMessageCount: number;
|
|
@@ -651,6 +673,12 @@ export declare class WebRepl {
|
|
|
651
673
|
sessionPlugins(): {
|
|
652
674
|
sessionId: string | undefined;
|
|
653
675
|
busy: boolean;
|
|
676
|
+
items: never[];
|
|
677
|
+
pending?: undefined;
|
|
678
|
+
} | {
|
|
679
|
+
sessionId: string | undefined;
|
|
680
|
+
busy: boolean;
|
|
681
|
+
pending: boolean;
|
|
654
682
|
items: {
|
|
655
683
|
id: string;
|
|
656
684
|
name: string;
|
|
@@ -791,6 +819,7 @@ export declare class WebRepl {
|
|
|
791
819
|
private backgroundTasks;
|
|
792
820
|
private detachRunningForeground;
|
|
793
821
|
private reattachRunningSession;
|
|
822
|
+
private timedStatus;
|
|
794
823
|
private reduce;
|
|
795
824
|
private finalizeLiveLine;
|
|
796
825
|
private finalizeThinkingLine;
|
package/dist/web/index.js
CHANGED
|
@@ -183,6 +183,9 @@ export async function createWebRuntime(options = {}) {
|
|
|
183
183
|
};
|
|
184
184
|
for (const tool of createSubagentTools(taskStore, resumeHandler))
|
|
185
185
|
registerTool(tool, { source: "builtin" });
|
|
186
|
+
const pluginToolNames = new Set(pluginCatalog.flatMap(plugin => plugin.tools.map(tool => tool.name)));
|
|
187
|
+
options.reservePluginToolNames?.(tools.list(undefined, { includeDeferred: true }).filter(tool => !pluginToolNames.has(tool.name)).flatMap(tool => [tool.name, ...(tool.aliases ?? [])]).concat(toolCatalog.filter(tool => tool.source !== "plugin").map(tool => tool.name)));
|
|
188
|
+
agentRuntime.acquireTurnResources = (sessionId) => acquireWebPluginTurn(runtime, false, runtime.engine, sessionId);
|
|
186
189
|
const globalToolOverrides = normalizeToolOverrides(options.globalToolOverrides, toolCatalog);
|
|
187
190
|
const sessionToolOverrides = normalizeToolOverrides(options.sessionToolOverrides, toolCatalog);
|
|
188
191
|
applyToolOverrides(tools, toolCatalog, globalToolOverrides, sessionToolOverrides);
|
|
@@ -196,7 +199,10 @@ export async function createWebRuntime(options = {}) {
|
|
|
196
199
|
modelGateway,
|
|
197
200
|
tools,
|
|
198
201
|
contextManagerFactory: (cwd) => new DefaultContextManager({ cwd }),
|
|
199
|
-
|
|
202
|
+
acquireTurnResources: (engine) => acquireWebPluginTurn(runtime, true, engine),
|
|
203
|
+
refreshTools: (engine) => {
|
|
204
|
+
if (runtime)
|
|
205
|
+
refreshRuntimePlugins(runtime, engine);
|
|
200
206
|
if (!options.resolveGlobalToolOverrides)
|
|
201
207
|
return;
|
|
202
208
|
if (runtime)
|
|
@@ -239,14 +245,16 @@ export async function createWebRuntime(options = {}) {
|
|
|
239
245
|
defaultReasoning: modelConfig?.defaultReasoning,
|
|
240
246
|
envPath: process.env.NEO_ENV_FILE?.trim() ? path.resolve(process.env.NEO_ENV_FILE.trim()) : envLoad.userDotEnvPath,
|
|
241
247
|
envNotice: envLoad.createdUserDotEnv ? formatCreatedEnvNotice(envLoad.userDotEnvPath) : undefined,
|
|
242
|
-
pluginSupport:
|
|
248
|
+
pluginSupport: {
|
|
249
|
+
resolveCatalog: options.resolveExternalPlugins,
|
|
250
|
+
acquire: options.acquirePluginSnapshot,
|
|
243
251
|
catalog: pluginCatalog,
|
|
244
252
|
overrides: pluginOverrides,
|
|
245
253
|
basePromptSections: [...(options.externalPromptSections ?? [])],
|
|
246
254
|
basePluginIds: [...(options.plugins ?? [])],
|
|
247
255
|
persist: options.persistSessionPluginOverrides,
|
|
248
256
|
resolve: options.resolveSessionPluginOverrides,
|
|
249
|
-
}
|
|
257
|
+
},
|
|
250
258
|
toolSupport: {
|
|
251
259
|
catalog: toolCatalog,
|
|
252
260
|
globalOverrides: globalToolOverrides,
|
|
@@ -259,6 +267,92 @@ export async function createWebRuntime(options = {}) {
|
|
|
259
267
|
};
|
|
260
268
|
return runtime;
|
|
261
269
|
}
|
|
270
|
+
/** Build first, publish only after all registrations validate. Never called by a child on the parent registry. */
|
|
271
|
+
function buildPluginRegistry(runtime, catalog, overrides) {
|
|
272
|
+
const registry = runtime.tools.clone();
|
|
273
|
+
for (const plugin of runtime.pluginSupport?.catalog ?? [])
|
|
274
|
+
for (const tool of plugin.tools)
|
|
275
|
+
registry.unregister(tool.name);
|
|
276
|
+
const names = new Set(registry.names({ includeDisabled: true }));
|
|
277
|
+
for (const plugin of catalog)
|
|
278
|
+
for (const tool of plugin.tools) {
|
|
279
|
+
if (names.has(tool.name))
|
|
280
|
+
throw new Error(`duplicate plugin/host tool: ${tool.name}`);
|
|
281
|
+
names.add(tool.name);
|
|
282
|
+
}
|
|
283
|
+
for (const plugin of activeWebRuntimePlugins(catalog, overrides))
|
|
284
|
+
for (const tool of plugin.tools)
|
|
285
|
+
registry.register(tool);
|
|
286
|
+
const support = runtime.toolSupport;
|
|
287
|
+
if (support) {
|
|
288
|
+
const global = support.resolveGlobal?.() ?? support.globalOverrides;
|
|
289
|
+
const all = [...support.catalog.filter(tool => tool.source !== "plugin"), ...catalog.flatMap(plugin => plugin.tools.map(tool => ({ name: tool.name })))];
|
|
290
|
+
for (const tool of all)
|
|
291
|
+
registry.setEnabled(tool.name, support.sessionOverrides[tool.name] ?? global[tool.name] ?? isToolEnabledByDefault(tool.name));
|
|
292
|
+
}
|
|
293
|
+
return registry;
|
|
294
|
+
}
|
|
295
|
+
export function refreshRuntimePlugins(runtime, engine = runtime.engine) {
|
|
296
|
+
const support = runtime.pluginSupport;
|
|
297
|
+
if (!support)
|
|
298
|
+
return;
|
|
299
|
+
const catalog = normalizeWebRuntimePlugins(support.resolveCatalog?.() ?? support.catalog);
|
|
300
|
+
const overrides = normalizePluginOverrides(resolveRuntimePluginOverrides(runtime, engine), catalog);
|
|
301
|
+
const registry = buildPluginRegistry(runtime, catalog, overrides);
|
|
302
|
+
// Only plugin registrations change. Built-in objects (including task managers) retain identity.
|
|
303
|
+
for (const plugin of support.catalog)
|
|
304
|
+
for (const tool of plugin.tools)
|
|
305
|
+
runtime.tools.unregister(tool.name);
|
|
306
|
+
const active = activeWebRuntimePlugins(catalog, overrides);
|
|
307
|
+
for (const plugin of active)
|
|
308
|
+
for (const tool of plugin.tools) {
|
|
309
|
+
runtime.tools.register(tool);
|
|
310
|
+
runtime.tools.setEnabled(tool.name, registry.isEnabled(tool.name) === true);
|
|
311
|
+
}
|
|
312
|
+
support.catalog = catalog;
|
|
313
|
+
support.overrides = overrides;
|
|
314
|
+
support.pendingOverrides = undefined;
|
|
315
|
+
if (runtime.toolSupport)
|
|
316
|
+
runtime.toolSupport.catalog = [
|
|
317
|
+
...runtime.toolSupport.catalog.filter(tool => tool.source !== "plugin"),
|
|
318
|
+
...catalog.flatMap(plugin => plugin.tools.map(tool => ({ name: tool.name, source: "plugin", pluginId: plugin.id, pluginName: plugin.name, presentation: toolPresentationForSource(tool.name, "plugin") }))),
|
|
319
|
+
];
|
|
320
|
+
engine.setRuntimePlugins([...support.basePluginIds, ...active.map(plugin => plugin.id)], [...support.basePromptSections, ...active.flatMap(plugin => plugin.promptSections ?? [])]);
|
|
321
|
+
}
|
|
322
|
+
function resolveRuntimePluginOverrides(runtime, engine, sessionId = engine.snapshot().session?.sessionId) {
|
|
323
|
+
const support = runtime.pluginSupport;
|
|
324
|
+
if (!support)
|
|
325
|
+
return {};
|
|
326
|
+
if (engine === runtime.engine && sessionId === engine.snapshot().session?.sessionId && support.pendingOverrides)
|
|
327
|
+
return support.pendingOverrides;
|
|
328
|
+
const resolved = sessionId ? support.resolve?.(sessionId) : undefined;
|
|
329
|
+
// Web hosts publish in-memory settings synchronously. Legacy async embedders retain loaded overrides.
|
|
330
|
+
if (resolved && !(resolved instanceof Promise))
|
|
331
|
+
return resolved;
|
|
332
|
+
if (resolved instanceof Promise)
|
|
333
|
+
void resolved.catch(() => { });
|
|
334
|
+
return support.overrides;
|
|
335
|
+
}
|
|
336
|
+
/** Children acquire independent tools/prompts; they never refresh the parent's in-flight context. */
|
|
337
|
+
function acquireWebPluginTurn(runtime, main, engine = runtime.engine, sessionId) {
|
|
338
|
+
if (main) {
|
|
339
|
+
refreshRuntimePlugins(runtime, engine);
|
|
340
|
+
refreshGlobalToolOverrides(runtime);
|
|
341
|
+
}
|
|
342
|
+
const support = runtime.pluginSupport;
|
|
343
|
+
const overrides = resolveRuntimePluginOverrides(runtime, engine, sessionId);
|
|
344
|
+
const snapshot = support?.acquire?.(overrides);
|
|
345
|
+
try {
|
|
346
|
+
const catalog = normalizeWebRuntimePlugins(snapshot?.plugins ?? support?.resolveCatalog?.() ?? support?.catalog);
|
|
347
|
+
const tools = buildPluginRegistry(runtime, catalog, overrides);
|
|
348
|
+
return { tools, release: snapshot?.release ?? (() => { }),
|
|
349
|
+
...(!main ? { promptSections: [...(support?.basePromptSections ?? []), ...activeWebRuntimePlugins(catalog, overrides).flatMap(plugin => plugin.promptSections ?? [])] } : {}) };
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
snapshot?.release();
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
262
356
|
function normalizeWebRuntimePlugins(value) {
|
|
263
357
|
if (!Array.isArray(value))
|
|
264
358
|
return [];
|
|
@@ -545,7 +639,7 @@ export class WebRepl {
|
|
|
545
639
|
}
|
|
546
640
|
return {
|
|
547
641
|
lines: this.lines.map(line => line.toolError ? { ...line, toolError: this.runtime.engine.redactDisplayValue?.(line.toolError) ?? line.toolError } : line),
|
|
548
|
-
status: this.
|
|
642
|
+
status: this.timedStatus(),
|
|
549
643
|
busy: this.busy,
|
|
550
644
|
queuedInput: this.queuedInput,
|
|
551
645
|
backgroundTaskCount: backgroundTasks.length,
|
|
@@ -567,6 +661,7 @@ export class WebRepl {
|
|
|
567
661
|
return this.buildRuntimeContext(this.runtimeContextRevision);
|
|
568
662
|
}
|
|
569
663
|
sessionPlugins() {
|
|
664
|
+
this.runtime.engine.refreshToolsIfIdle();
|
|
570
665
|
const support = this.runtime.pluginSupport;
|
|
571
666
|
const sessionId = this.runtime.engine.snapshot().session?.sessionId;
|
|
572
667
|
if (!support)
|
|
@@ -574,8 +669,9 @@ export class WebRepl {
|
|
|
574
669
|
return {
|
|
575
670
|
sessionId,
|
|
576
671
|
busy: this.busy,
|
|
577
|
-
|
|
578
|
-
|
|
672
|
+
pending: Boolean(support.pendingOverrides),
|
|
673
|
+
items: (support.resolveCatalog?.() ?? support.catalog).map((plugin) => {
|
|
674
|
+
const override = (support.pendingOverrides ?? support.overrides)[plugin.id];
|
|
579
675
|
return {
|
|
580
676
|
id: plugin.id,
|
|
581
677
|
name: plugin.name,
|
|
@@ -684,14 +780,12 @@ export class WebRepl {
|
|
|
684
780
|
await this.applySessionToolOverrides(await support.resolveSession(sessionId), false);
|
|
685
781
|
}
|
|
686
782
|
async setSessionPlugins(value) {
|
|
687
|
-
if (this.busy)
|
|
688
|
-
return actionFailure("PLUGIN_UPDATE_BLOCKED", "cannot change session plugins while a response is running");
|
|
689
783
|
if (!isRecord(value))
|
|
690
784
|
return actionFailure("INVALID_REQUEST", "plugin overrides must be an object");
|
|
691
785
|
const support = this.runtime.pluginSupport;
|
|
692
786
|
if (!support)
|
|
693
787
|
return actionFailure("PLUGIN_NOT_CONFIGURED", "web plugins are not configured");
|
|
694
|
-
const available = new Set(support.catalog.map((plugin) => plugin.id));
|
|
788
|
+
const available = new Set((support.resolveCatalog?.() ?? support.catalog).map((plugin) => plugin.id));
|
|
695
789
|
const overrides = {};
|
|
696
790
|
for (const [id, mode] of Object.entries(value)) {
|
|
697
791
|
if (!available.has(id))
|
|
@@ -781,22 +875,18 @@ export class WebRepl {
|
|
|
781
875
|
const support = this.runtime.pluginSupport;
|
|
782
876
|
if (!support)
|
|
783
877
|
return;
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
this.runtime.tools.unregister(tool.name);
|
|
788
|
-
const activePlugins = activeWebRuntimePlugins(support.catalog, normalized);
|
|
789
|
-
for (const plugin of activePlugins)
|
|
790
|
-
for (const tool of plugin.tools)
|
|
791
|
-
this.runtime.tools.register(tool);
|
|
792
|
-
support.overrides = normalized;
|
|
793
|
-
const toolSupport = this.runtime.toolSupport;
|
|
794
|
-
if (toolSupport)
|
|
795
|
-
applyToolOverrides(this.runtime.tools, toolSupport.catalog, toolSupport.globalOverrides, toolSupport.sessionOverrides);
|
|
796
|
-
this.runtime.engine.setRuntimePlugins([...support.basePluginIds, ...activePlugins.map((plugin) => plugin.id)], [...support.basePromptSections, ...activePlugins.flatMap((plugin) => plugin.promptSections ?? [])]);
|
|
878
|
+
const catalog = normalizeWebRuntimePlugins(support.resolveCatalog?.() ?? support.catalog);
|
|
879
|
+
const normalized = normalizePluginOverrides(overrides, catalog);
|
|
880
|
+
buildPluginRegistry(this.runtime, catalog, normalized);
|
|
797
881
|
const sessionId = this.runtime.engine.snapshot().session?.sessionId;
|
|
798
882
|
if (persist && sessionId && support.persist)
|
|
799
883
|
await support.persist(sessionId, normalized);
|
|
884
|
+
support.pendingOverrides = normalized;
|
|
885
|
+
if (this.busy) {
|
|
886
|
+
this.publishRuntimeContext();
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
refreshRuntimePlugins(this.runtime);
|
|
800
890
|
const metrics = await this.runtime.engine.contextMetrics();
|
|
801
891
|
this.runtime.initialMetrics = metrics;
|
|
802
892
|
this.setStatus({ ...this.status, metrics, activityTick: this.status.activityTick + 1 });
|
|
@@ -1406,6 +1496,14 @@ export class WebRepl {
|
|
|
1406
1496
|
this.setBusy(true);
|
|
1407
1497
|
this.setStatus({ ...this.status, phase: "running", detail: "working" });
|
|
1408
1498
|
}
|
|
1499
|
+
timedStatus() {
|
|
1500
|
+
const records = this.runtime.engine.getTimingRecords?.() ?? [];
|
|
1501
|
+
if (records.length === 0)
|
|
1502
|
+
return this.status;
|
|
1503
|
+
const queryTiming = records.filter(record => record.kind === "query").at(-1);
|
|
1504
|
+
return { ...this.status, queryTiming,
|
|
1505
|
+
toolTimings: records.filter(record => record.kind === "tool" && record.runId === queryTiming?.runId) };
|
|
1506
|
+
}
|
|
1409
1507
|
reduce(event) {
|
|
1410
1508
|
this.status = reduceStatus(this.status, event);
|
|
1411
1509
|
if (event.type === "usage")
|
|
@@ -1566,6 +1664,18 @@ export class WebRepl {
|
|
|
1566
1664
|
}
|
|
1567
1665
|
handleEvent(event) {
|
|
1568
1666
|
this.reduce(event);
|
|
1667
|
+
if (event.type === "timing.updated") {
|
|
1668
|
+
if (event.timing.kind === "tool") {
|
|
1669
|
+
const line = this.lines.find(item => item.timing?.id === event.timing.id)
|
|
1670
|
+
?? this.lines.find(item => item.id === this.liveToolLineIds.get(event.timing.toolUseId));
|
|
1671
|
+
if (line) {
|
|
1672
|
+
this.lines = this.lines.map(item => item.id === line.id ? { ...item, timing: event.timing } : item);
|
|
1673
|
+
this.queueDeltaOperation({ type: "line.patch", id: line.id, patch: { timing: event.timing } });
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
this.queueDeltaStatus();
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1569
1679
|
if (event.type === "message" && this.matchesPendingUserImageEcho(event.message)) {
|
|
1570
1680
|
const messageId = this.pendingUserImageEchoMessageId;
|
|
1571
1681
|
this.pendingUserImageEchoIds = undefined;
|
|
@@ -1574,7 +1684,7 @@ export class WebRepl {
|
|
|
1574
1684
|
this.broadcastSync();
|
|
1575
1685
|
return;
|
|
1576
1686
|
}
|
|
1577
|
-
if (event.type === "tool_call.delta") {
|
|
1687
|
+
if (event.type === "tool_call.started" || event.type === "tool_call.delta") {
|
|
1578
1688
|
this.queueDeltaStatus();
|
|
1579
1689
|
return;
|
|
1580
1690
|
}
|
|
@@ -1819,7 +1929,7 @@ export class WebRepl {
|
|
|
1819
1929
|
this.activeAbortController = abortController;
|
|
1820
1930
|
this.interruptArmed = false;
|
|
1821
1931
|
this.setBusy(true);
|
|
1822
|
-
this.setStatus({ ...this.status, phase: "running", detail: "working", usage: undefined, streamedOutputTokens: 0, inputTokenUpdatedAt: undefined, outputTokenUpdatedAt: undefined, retryCooldownUntil: undefined });
|
|
1932
|
+
this.setStatus({ ...this.status, phase: "running", detail: "working", modelOutput: undefined, usage: undefined, streamedOutputTokens: 0, inputTokenUpdatedAt: undefined, outputTokenUpdatedAt: undefined, retryCooldownUntil: undefined });
|
|
1823
1933
|
const engine = this.runtime.engine;
|
|
1824
1934
|
try {
|
|
1825
1935
|
for await (const event of engine.sendUserText(promptPayload.text, {
|
|
@@ -1833,7 +1943,7 @@ export class WebRepl {
|
|
|
1833
1943
|
if (this.runtime.engine !== engine)
|
|
1834
1944
|
continue;
|
|
1835
1945
|
if (this.suppressReattachedStreaming.has(engine)) {
|
|
1836
|
-
if (event.type === "message" || event.type === "terminal" || event.type === "error" || event.type === "context.metrics" || event.type === "usage") {
|
|
1946
|
+
if (event.type === "message" || event.type === "terminal" || event.type === "error" || event.type === "context.metrics" || event.type === "usage" || event.type === "timing.updated") {
|
|
1837
1947
|
if (event.type === "message" || event.type === "terminal" || event.type === "error")
|
|
1838
1948
|
this.suppressReattachedStreaming.delete(engine);
|
|
1839
1949
|
this.handleEvent(event);
|
|
@@ -2003,7 +2113,7 @@ export class WebRepl {
|
|
|
2003
2113
|
protocolVersion: 2,
|
|
2004
2114
|
sessionId: this.runtime.engine.snapshot().session?.sessionId,
|
|
2005
2115
|
operations,
|
|
2006
|
-
status: this.
|
|
2116
|
+
status: this.timedStatus(),
|
|
2007
2117
|
};
|
|
2008
2118
|
for (const subscriber of this.subscribers)
|
|
2009
2119
|
this.send(subscriber, "delta", payload);
|
|
@@ -2377,6 +2487,14 @@ function restoredHistoryLines(runtime) {
|
|
|
2377
2487
|
historyToolLineIndexes.set(line.toolUseId, lines.length - 1);
|
|
2378
2488
|
return lines.length;
|
|
2379
2489
|
};
|
|
2490
|
+
const toolTimings = new Map();
|
|
2491
|
+
for (const record of runtime.engine.getTimingRecords?.() ?? []) {
|
|
2492
|
+
if (record.kind !== "tool" || !record.toolUseId)
|
|
2493
|
+
continue;
|
|
2494
|
+
const matches = toolTimings.get(record.toolUseId) ?? [];
|
|
2495
|
+
matches.push(record);
|
|
2496
|
+
toolTimings.set(record.toolUseId, matches);
|
|
2497
|
+
}
|
|
2380
2498
|
const entries = runtime.engine.getDisplayEntries();
|
|
2381
2499
|
let lastCompactionIndex = -1;
|
|
2382
2500
|
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
@@ -2397,6 +2515,12 @@ function restoredHistoryLines(runtime) {
|
|
|
2397
2515
|
const report = entry.report ?? legacyCompactionReport(entry.reason);
|
|
2398
2516
|
append(compactionLine(report, entry.createdAt, entryIndex === lastCompactionIndex));
|
|
2399
2517
|
}
|
|
2518
|
+
// Legacy histories can reuse call IDs. Ambiguous associations stay unknown.
|
|
2519
|
+
for (const line of lines) {
|
|
2520
|
+
const matches = line.toolUseId ? toolTimings.get(line.toolUseId) : undefined;
|
|
2521
|
+
if (matches?.length === 1)
|
|
2522
|
+
line.timing = matches[0];
|
|
2523
|
+
}
|
|
2400
2524
|
return lines;
|
|
2401
2525
|
}
|
|
2402
2526
|
function initialStatus(runtime, metrics = runtime.initialMetrics) {
|
|
@@ -2544,7 +2668,7 @@ function toolStreamStepStatus(phase) {
|
|
|
2544
2668
|
}
|
|
2545
2669
|
function reduceStatus(status, event) {
|
|
2546
2670
|
if (event.type === "state")
|
|
2547
|
-
return { ...status, phase: event.phase, detail: event.detail, currentTool: event.phase === "running_tools" ? status.currentTool : undefined, usage: event.phase === "preparing" ? undefined : status.usage, streamedOutputTokens: event.phase === "preparing" ? 0 : status.streamedOutputTokens, inputTokenUpdatedAt: event.phase === "preparing" ? undefined : status.inputTokenUpdatedAt, outputTokenUpdatedAt: event.phase === "preparing" ? undefined : status.outputTokenUpdatedAt, retryCooldownUntil: event.phase === "preparing" ? undefined : status.retryCooldownUntil, activityTick: status.activityTick + 1 };
|
|
2671
|
+
return { ...status, phase: event.phase, detail: event.detail, modelOutput: undefined, currentTool: event.phase === "running_tools" ? status.currentTool : undefined, usage: event.phase === "preparing" ? undefined : status.usage, streamedOutputTokens: event.phase === "preparing" ? 0 : status.streamedOutputTokens, inputTokenUpdatedAt: event.phase === "preparing" ? undefined : status.inputTokenUpdatedAt, outputTokenUpdatedAt: event.phase === "preparing" ? undefined : status.outputTokenUpdatedAt, retryCooldownUntil: event.phase === "preparing" ? undefined : status.retryCooldownUntil, activityTick: status.activityTick + 1 };
|
|
2548
2672
|
if (event.type === "context.compacted")
|
|
2549
2673
|
return { ...status, phase: "compacting", detail: formatCompactionReportSummary(event.compaction), activityTick: status.activityTick + 1 };
|
|
2550
2674
|
if (event.type === "context.metrics")
|
|
@@ -2552,21 +2676,23 @@ function reduceStatus(status, event) {
|
|
|
2552
2676
|
if (event.type === "usage")
|
|
2553
2677
|
return { ...status, usage: event.usage, inputTokenUpdatedAt: event.usage.inputTokens !== undefined ? Date.now() : status.inputTokenUpdatedAt, outputTokenUpdatedAt: event.usage.outputTokens !== undefined ? Date.now() : status.outputTokenUpdatedAt, activityTick: status.activityTick + 1 };
|
|
2554
2678
|
if (event.type === "assistant.delta")
|
|
2555
|
-
return { ...status, phase: "calling_model", streamedOutputTokens: status.streamedOutputTokens + estimateTokens(event.text), outputTokenUpdatedAt: Date.now(), activityTick: status.activityTick + 1 };
|
|
2679
|
+
return { ...status, phase: "calling_model", modelOutput: { kind: "text" }, streamedOutputTokens: status.streamedOutputTokens + estimateTokens(event.text), outputTokenUpdatedAt: Date.now(), activityTick: status.activityTick + 1 };
|
|
2556
2680
|
if (event.type === "thinking.delta")
|
|
2557
|
-
return { ...status, phase: "thinking", streamedOutputTokens: status.streamedOutputTokens + estimateTokens(event.text), outputTokenUpdatedAt: Date.now(), activityTick: status.activityTick + 1 };
|
|
2681
|
+
return { ...status, phase: "thinking", modelOutput: undefined, streamedOutputTokens: status.streamedOutputTokens + estimateTokens(event.text), outputTokenUpdatedAt: Date.now(), activityTick: status.activityTick + 1 };
|
|
2682
|
+
if (event.type === "tool_call.started")
|
|
2683
|
+
return { ...status, phase: "calling_model", modelOutput: { kind: "tool_call", callId: event.callId, name: event.name }, activityTick: status.activityTick + 1 };
|
|
2558
2684
|
if (event.type === "tool_call.delta")
|
|
2559
|
-
return { ...status, phase: "calling_model", streamedOutputTokens: status.streamedOutputTokens + estimateTokens(event.argumentsDelta), outputTokenUpdatedAt: Date.now(), activityTick: status.activityTick + 1 };
|
|
2685
|
+
return { ...status, phase: "calling_model", modelOutput: { kind: "tool_call", callId: event.callId, name: event.name ?? (status.modelOutput?.kind === "tool_call" && status.modelOutput.callId === event.callId ? status.modelOutput.name : undefined) }, streamedOutputTokens: status.streamedOutputTokens + estimateTokens(event.argumentsDelta), outputTokenUpdatedAt: Date.now(), activityTick: status.activityTick + 1 };
|
|
2560
2686
|
if (event.type === "retrying")
|
|
2561
|
-
return { ...status, phase: "calling_model", detail: `retrying in ${(event.delayMs / 1000).toFixed(1)}s`, retryCooldownUntil: Date.now() + event.delayMs, activityTick: status.activityTick + 1 };
|
|
2687
|
+
return { ...status, phase: "calling_model", modelOutput: undefined, detail: `retrying in ${(event.delayMs / 1000).toFixed(1)}s`, retryCooldownUntil: Date.now() + event.delayMs, activityTick: status.activityTick + 1 };
|
|
2562
2688
|
if (event.type === "tool.started")
|
|
2563
|
-
return { ...status, phase: "running_tools", currentTool: { id: event.toolUse.id, name: event.toolUse.name, input: event.toolUse.input, startedAt: Date.now() }, activityTick: status.activityTick + 1 };
|
|
2689
|
+
return { ...status, phase: "running_tools", modelOutput: undefined, currentTool: { id: event.toolUse.id, name: event.toolUse.name, input: event.toolUse.input, startedAt: Date.now() }, activityTick: status.activityTick + 1 };
|
|
2564
2690
|
if (event.type === "tool.progress")
|
|
2565
2691
|
return { ...status, phase: "running_tools", detail: event.progress.message || status.detail, currentTool: { id: event.toolUse.id, name: event.toolUse.name, input: event.toolUse.input, startedAt: status.currentTool?.id === event.toolUse.id ? status.currentTool.startedAt : Date.now() }, activityTick: status.activityTick + 1 };
|
|
2566
2692
|
if (event.type === "tool.result.available" || event.type === "tool.finished" || event.type === "tool.batch.completed")
|
|
2567
2693
|
return { ...status, currentTool: "toolUse" in event && status.currentTool?.id === event.toolUse.id ? undefined : status.currentTool, activityTick: status.activityTick + 1 };
|
|
2568
2694
|
if (event.type === "terminal")
|
|
2569
|
-
return { ...status, phase: "stopped", detail: event.reason, currentTool: undefined, inputTokenUpdatedAt: undefined, outputTokenUpdatedAt: undefined, retryCooldownUntil: undefined, activityTick: status.activityTick + 1 };
|
|
2695
|
+
return { ...status, phase: "stopped", modelOutput: undefined, detail: event.reason, currentTool: undefined, inputTokenUpdatedAt: undefined, outputTokenUpdatedAt: undefined, retryCooldownUntil: undefined, activityTick: status.activityTick + 1 };
|
|
2570
2696
|
if (event.type === "message" || event.type === "error")
|
|
2571
2697
|
return { ...status, activityTick: status.activityTick + 1 };
|
|
2572
2698
|
return status;
|