neoctl 0.2.49 → 0.2.51
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/context/compaction.d.ts +1 -0
- package/dist/context/compaction.js +9 -2
- package/dist/context/compaction.js.map +1 -1
- package/dist/core/compaction-progress.d.ts +1 -1
- package/dist/core/compaction-progress.js +9 -0
- package/dist/core/compaction-progress.js.map +1 -1
- package/dist/core/query-engine.d.ts +2 -0
- package/dist/core/query-engine.js +7 -7
- package/dist/core/query-engine.js.map +1 -1
- package/dist/core/query.d.ts +1 -1
- package/dist/core/query.js +7 -6
- package/dist/core/query.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/session/session-pagination.d.ts +20 -0
- package/dist/session/session-pagination.js +27 -0
- package/dist/session/session-pagination.js.map +1 -0
- package/dist/session/session-store.js +27 -24
- package/dist/session/session-store.js.map +1 -1
- package/dist/session/session-summary.d.ts +10 -0
- package/dist/session/session-summary.js +101 -0
- package/dist/session/session-summary.js.map +1 -0
- package/dist/types/events.d.ts +3 -0
- package/dist/web/index.d.ts +28 -1
- package/dist/web/index.js +50 -10
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
package/dist/types/events.d.ts
CHANGED
package/dist/web/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { type SessionPageOptions } from "../session/session-pagination.js";
|
|
2
3
|
import { type ToolInputChange } from "../model/tool-input-stream.js";
|
|
3
4
|
import { type ToolActivity } from "./tool-activity.js";
|
|
4
5
|
import type { TimingRecord } from "../core/query-timing.js";
|
|
@@ -210,6 +211,7 @@ type WebActionResult<T extends object = object> = ({
|
|
|
210
211
|
ok: true;
|
|
211
212
|
} & T) | WebActionFailure;
|
|
212
213
|
interface UiStatus {
|
|
214
|
+
compactionText?: string;
|
|
213
215
|
modelOutput?: {
|
|
214
216
|
kind: "text";
|
|
215
217
|
} | {
|
|
@@ -372,6 +374,7 @@ export declare class WebRuntimeRouter {
|
|
|
372
374
|
constructor(options?: WebRuntimeRouterOptions);
|
|
373
375
|
get(scope?: WebRuntimeScope): Promise<WebRepl>;
|
|
374
376
|
snapshot(scope?: WebRuntimeScope, includeCatalog?: boolean): Promise<ReturnType<WebRepl["snapshot"]>>;
|
|
377
|
+
runningSessionIds(): Promise<string[]>;
|
|
375
378
|
reloadModelConfig(): Promise<void>;
|
|
376
379
|
reloadGlobalTools(overrides: Record<string, boolean>): Promise<void>;
|
|
377
380
|
activeScopes(): string[];
|
|
@@ -467,6 +470,11 @@ export declare class WebRepl {
|
|
|
467
470
|
sessionId: string;
|
|
468
471
|
runId: string;
|
|
469
472
|
} | undefined;
|
|
473
|
+
runningSessionIds(): string[];
|
|
474
|
+
queuedImage(imageId: string): {
|
|
475
|
+
body: Buffer;
|
|
476
|
+
mimeType: string;
|
|
477
|
+
} | undefined;
|
|
470
478
|
snapshot(includeCatalog?: boolean): {
|
|
471
479
|
lines: UiLine[];
|
|
472
480
|
status: UiStatus;
|
|
@@ -483,6 +491,17 @@ export declare class WebRepl {
|
|
|
483
491
|
};
|
|
484
492
|
demoMode: boolean;
|
|
485
493
|
queuedInput: string | undefined;
|
|
494
|
+
queuedAttachments: ({
|
|
495
|
+
kind: "image";
|
|
496
|
+
imageId: string;
|
|
497
|
+
previewUrl: string;
|
|
498
|
+
name?: undefined;
|
|
499
|
+
} | {
|
|
500
|
+
kind: "file";
|
|
501
|
+
name: string;
|
|
502
|
+
imageId?: undefined;
|
|
503
|
+
previewUrl?: undefined;
|
|
504
|
+
})[];
|
|
486
505
|
backgroundTaskCount: number;
|
|
487
506
|
backgroundTasks: ({
|
|
488
507
|
kind: "agent";
|
|
@@ -800,9 +819,17 @@ export declare class WebRepl {
|
|
|
800
819
|
}>;
|
|
801
820
|
private enqueueInput;
|
|
802
821
|
private startRun;
|
|
803
|
-
listSessions(): Promise<{
|
|
822
|
+
listSessions(options?: SessionPageOptions): Promise<{
|
|
823
|
+
runningSessionIds: string[];
|
|
804
824
|
sessions: import("../index.js").SessionSummary[];
|
|
825
|
+
total: number;
|
|
826
|
+
totalCount: number;
|
|
827
|
+
page: number;
|
|
828
|
+
pageSize: number;
|
|
829
|
+
totalPages: number;
|
|
830
|
+
} | {
|
|
805
831
|
runningSessionIds: string[];
|
|
832
|
+
sessions: import("../index.js").SessionSummary[];
|
|
806
833
|
}>;
|
|
807
834
|
resumeSession(sessionId: string): Promise<WebActionResult>;
|
|
808
835
|
newSession(): Promise<WebActionResult>;
|
package/dist/web/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { paginateSessions, sessionPageOptions } from "../session/session-pagination.js";
|
|
2
3
|
import { applyToolInputChanges } from "../model/tool-input-stream.js";
|
|
3
4
|
import { toolActivity } from "./tool-activity.js";
|
|
4
5
|
import { toolObservationResult } from "./tool-observation-result.js";
|
|
@@ -498,6 +499,11 @@ export class WebRuntimeRouter {
|
|
|
498
499
|
async snapshot(scope = {}, includeCatalog = true) {
|
|
499
500
|
return (await this.get(scope)).snapshot(includeCatalog);
|
|
500
501
|
}
|
|
502
|
+
async runningSessionIds() {
|
|
503
|
+
// Inspect only existing runtime instances: never restore sessions or read transcripts.
|
|
504
|
+
const entries = await Promise.allSettled([...this.repls.values()]);
|
|
505
|
+
return [...new Set(entries.flatMap(entry => entry.status === "fulfilled" ? entry.value.runningSessionIds() : []))];
|
|
506
|
+
}
|
|
501
507
|
async reloadModelConfig() {
|
|
502
508
|
const repls = await Promise.all([...this.repls.values()]);
|
|
503
509
|
await Promise.all(repls.map(repl => repl.reloadModelConfig()));
|
|
@@ -749,6 +755,14 @@ export class WebRepl {
|
|
|
749
755
|
// Cursor addresses stored bytes, not the possibly additionally redacted response length.
|
|
750
756
|
return page.ok ? this.runtime.engine.redactDisplayValue({ sessionId, runId, ...page.value }) : undefined;
|
|
751
757
|
}
|
|
758
|
+
runningSessionIds() {
|
|
759
|
+
const id = this.runtime.engine.snapshot().session?.sessionId;
|
|
760
|
+
return [...new Set([...this.backgroundSessionRuns.keys(), ...(this.busy && !this.runControl().canResume && id ? [id] : [])])];
|
|
761
|
+
}
|
|
762
|
+
queuedImage(imageId) {
|
|
763
|
+
const attachment = this.queuedAttachments?.find(item => item.kind === "image" && item.imageId === imageId);
|
|
764
|
+
return attachment?.kind === "image" ? imagePayloadForBlock({ type: "image", ...attachment }) : undefined;
|
|
765
|
+
}
|
|
752
766
|
snapshot(includeCatalog = false) {
|
|
753
767
|
const backgroundTasks = this.backgroundTasks();
|
|
754
768
|
const engineSnapshot = this.runtime.engine.snapshot();
|
|
@@ -765,12 +779,16 @@ export class WebRepl {
|
|
|
765
779
|
runControl: this.runControl(),
|
|
766
780
|
demoMode: this.isDemo(),
|
|
767
781
|
queuedInput: this.queuedInput,
|
|
782
|
+
// Preview metadata only. Base64 image contents must not inflate every status snapshot.
|
|
783
|
+
queuedAttachments: (this.queuedAttachments ?? []).map(item => item.kind === "image"
|
|
784
|
+
? { kind: "image", imageId: item.imageId, previewUrl: `/api/queue/images/${encodeURIComponent(item.imageId)}` }
|
|
785
|
+
: { kind: "file", name: item.name }),
|
|
768
786
|
backgroundTaskCount: backgroundTasks.length,
|
|
769
787
|
backgroundTasks,
|
|
770
788
|
terminalTaskHistory: this.terminalTaskHistory(),
|
|
771
789
|
agentTaskHistory: sessionAgentTasks(this.runtime).filter((task) => this.runtime.taskStore.isTerminal(task)).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)).map((task) => this.agentTaskSnapshot(task)),
|
|
772
790
|
backgroundSessionRunCount: this.backgroundSessionRuns.size,
|
|
773
|
-
runningSessionIds:
|
|
791
|
+
runningSessionIds: this.runningSessionIds(),
|
|
774
792
|
session: engineSnapshot.session,
|
|
775
793
|
modelSettings: { model: engineSnapshot.model, reasoning: engineSnapshot.reasoning },
|
|
776
794
|
pendingSessionSettings,
|
|
@@ -1097,10 +1115,10 @@ export class WebRepl {
|
|
|
1097
1115
|
this.foregroundRun = undefined;
|
|
1098
1116
|
}).catch(() => undefined);
|
|
1099
1117
|
}
|
|
1100
|
-
async listSessions() {
|
|
1118
|
+
async listSessions(options) {
|
|
1101
1119
|
const sessions = await this.runtime.engine.listSessions(Number.POSITIVE_INFINITY);
|
|
1102
|
-
const runningSessionIds =
|
|
1103
|
-
return { sessions, runningSessionIds };
|
|
1120
|
+
const runningSessionIds = this.runningSessionIds();
|
|
1121
|
+
return { ...(options ? paginateSessions(sessions, options) : { sessions }), runningSessionIds };
|
|
1104
1122
|
}
|
|
1105
1123
|
async resumeSession(sessionId) {
|
|
1106
1124
|
if (!sessionId)
|
|
@@ -1347,11 +1365,15 @@ export class WebRepl {
|
|
|
1347
1365
|
if (showProgress) {
|
|
1348
1366
|
this.compactingSession = engine;
|
|
1349
1367
|
this.busy = true;
|
|
1350
|
-
this.setStatus({ ...this.status, phase: "compacting", detail: "manualcompact: generating context summary", activityTick: this.status.activityTick + 1 });
|
|
1368
|
+
this.setStatus({ ...this.status, phase: "compacting", compactionText: "", detail: "manualcompact: generating context summary", activityTick: this.status.activityTick + 1 });
|
|
1351
1369
|
}
|
|
1352
1370
|
try {
|
|
1353
1371
|
// An idle click executes now through the settings barrier, not the chat/input queue.
|
|
1354
|
-
const result = await engine.updateSessionSettings({ compact: true }, { abortSignal: controller?.signal
|
|
1372
|
+
const result = await engine.updateSessionSettings({ compact: true }, { abortSignal: controller?.signal, onCompactionDelta: text => {
|
|
1373
|
+
if (showProgress && this.runtime.engine === engine && this.foregroundRunToken === runToken && !controller?.signal.aborted) {
|
|
1374
|
+
this.handleEvent({ type: "context.compaction.delta", text });
|
|
1375
|
+
}
|
|
1376
|
+
} });
|
|
1355
1377
|
if (this.runtime.engine === engine && this.foregroundRunToken === runToken) {
|
|
1356
1378
|
if (result.compaction?.report)
|
|
1357
1379
|
this.appendCompaction(result.compaction.report);
|
|
@@ -1545,6 +1567,8 @@ export class WebRepl {
|
|
|
1545
1567
|
this.broadcastSync();
|
|
1546
1568
|
}
|
|
1547
1569
|
setStatus(next) {
|
|
1570
|
+
if (next.phase !== "compacting")
|
|
1571
|
+
next = { ...next, compactionText: undefined };
|
|
1548
1572
|
const waiting = next.phase === "calling_model" && !next.modelOutput;
|
|
1549
1573
|
const wasWaiting = this.status.phase === "calling_model" && !this.status.modelOutput;
|
|
1550
1574
|
next = { ...next, responseWaitStartedAt: waiting
|
|
@@ -2002,6 +2026,10 @@ export class WebRepl {
|
|
|
2002
2026
|
this.liveToolLineIds.set(event.toolUse.id, this.append(patch));
|
|
2003
2027
|
return;
|
|
2004
2028
|
}
|
|
2029
|
+
if (event.type === "context.compaction.delta") {
|
|
2030
|
+
this.queueDeltaStatus();
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2005
2033
|
if (event.type === "tool_call.delta") {
|
|
2006
2034
|
this.queueDeltaStatus();
|
|
2007
2035
|
return;
|
|
@@ -2336,10 +2364,13 @@ export class WebRepl {
|
|
|
2336
2364
|
this.activeAbortController = abortController;
|
|
2337
2365
|
this.interruptArmed = false;
|
|
2338
2366
|
this.setBusy(true);
|
|
2339
|
-
this.setStatus({ ...this.status, phase: "compacting", detail: type === "compact" ? "manual compact" : "pure compact", activityTick: this.status.activityTick + 1 });
|
|
2367
|
+
this.setStatus({ ...this.status, phase: "compacting", compactionText: "", detail: type === "compact" ? "manual compact" : "pure compact", activityTick: this.status.activityTick + 1 });
|
|
2340
2368
|
try {
|
|
2341
2369
|
const result = type === "compact"
|
|
2342
|
-
? await this.runtime.engine.compact({ abortSignal: abortController.signal
|
|
2370
|
+
? await this.runtime.engine.compact({ abortSignal: abortController.signal, onCompactionDelta: text => {
|
|
2371
|
+
if (this.foregroundRunToken === runToken && !abortController.signal.aborted)
|
|
2372
|
+
this.handleEvent({ type: "context.compaction.delta", text });
|
|
2373
|
+
} })
|
|
2343
2374
|
: await this.runtime.engine.pureCompact({ abortSignal: abortController.signal });
|
|
2344
2375
|
if (this.foregroundRunToken !== runToken)
|
|
2345
2376
|
return;
|
|
@@ -2556,6 +2587,8 @@ export async function handleWebRequest(req, res, router) {
|
|
|
2556
2587
|
if (["/api/tool-call-detail", "/api/terminal-output", "/api/agent-content"].includes(url.pathname) && !allowToolDetailRequest(req.headers.origin, req.headers.host, req.headers["sec-fetch-site"])) {
|
|
2557
2588
|
return sendJson(res, { error: "详情访问被拒绝" }, 403);
|
|
2558
2589
|
}
|
|
2590
|
+
if (req.method === "GET" && url.pathname === "/api/sessions/running")
|
|
2591
|
+
return sendJson(res, { runningSessionIds: await router.runningSessionIds() });
|
|
2559
2592
|
const scope = webRuntimeScopeFromUrl(url);
|
|
2560
2593
|
const repl = await router.get(scope);
|
|
2561
2594
|
if (req.method === "GET" && url.pathname === "/events")
|
|
@@ -2585,6 +2618,11 @@ export async function handleWebRequest(req, res, router) {
|
|
|
2585
2618
|
const page = repl.terminalOutput(url.searchParams.get("sessionId") ?? "", url.searchParams.get("runId") ?? "", url.searchParams.get("stream") ?? "stdout", Number(url.searchParams.get("offset") ?? 0), Number(url.searchParams.get("limitBytes") ?? 65536), url.searchParams.get("tail") === "true");
|
|
2586
2619
|
return page ? sendJson(res, page) : sendJson(res, { error: "终端输出不可获取或无权访问" }, 404);
|
|
2587
2620
|
}
|
|
2621
|
+
const queuedImageMatch = /^\/api\/queue\/images\/(image_[0-9a-f-]{36})$/iu.exec(url.pathname);
|
|
2622
|
+
if (req.method === "GET" && queuedImageMatch) {
|
|
2623
|
+
const image = repl.queuedImage(queuedImageMatch[1]);
|
|
2624
|
+
return image ? sendImage(res, image, req) : sendJson(res, { error: "图片不可用" }, 404);
|
|
2625
|
+
}
|
|
2588
2626
|
if (req.method === "GET" && url.pathname === "/api/state")
|
|
2589
2627
|
return sendJson(res, repl.snapshot(true));
|
|
2590
2628
|
if (req.method === "GET" && url.pathname === "/api/runtime-context")
|
|
@@ -2755,7 +2793,7 @@ export async function handleWebRequest(req, res, router) {
|
|
|
2755
2793
|
if (req.method === "POST" && url.pathname === "/api/queue/send-now")
|
|
2756
2794
|
return sendJson(res, await repl.sendQueuedNow());
|
|
2757
2795
|
if (req.method === "GET" && url.pathname === "/api/sessions")
|
|
2758
|
-
return sendJson(res, await repl.listSessions());
|
|
2796
|
+
return sendJson(res, await repl.listSessions(sessionPageOptions(url.searchParams)));
|
|
2759
2797
|
if (req.method === "POST" && url.pathname === "/api/sessions/resume") {
|
|
2760
2798
|
const body = await readJsonBody(req);
|
|
2761
2799
|
return sendJson(res, await repl.resumeSession(String(body.sessionId ?? "")));
|
|
@@ -3115,8 +3153,10 @@ function toolStreamStepStatus(phase) {
|
|
|
3115
3153
|
return "running";
|
|
3116
3154
|
}
|
|
3117
3155
|
function reduceStatus(status, event) {
|
|
3156
|
+
if (event.type === "context.compaction.delta")
|
|
3157
|
+
return { ...status, phase: "compacting", compactionText: (status.compactionText || "") + event.text, activityTick: status.activityTick + 1 };
|
|
3118
3158
|
if (event.type === "state")
|
|
3119
|
-
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 };
|
|
3159
|
+
return { ...status, phase: event.phase, detail: event.detail, compactionText: event.phase === "compacting" && status.phase === "compacting" ? status.compactionText : undefined, 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 };
|
|
3120
3160
|
if (event.type === "context.compacted")
|
|
3121
3161
|
return { ...status, phase: "compacting", detail: formatCompactionReportSummary(event.compaction), activityTick: status.activityTick + 1 };
|
|
3122
3162
|
if (event.type === "context.metrics")
|