@tea-agent/loop-agent 0.44.0-next.5 → 0.44.0-next.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 +299 -445
- package/dist/build-stamp.json +3 -3
- package/dist/executors/pi-sdk-executor.js +7 -7
- package/dist/infrastructure/console/operation-store.js +46 -14
- package/dist/shared/git-workspace-status.js +2 -0
- package/dist/shared/wizard-local-telemetry.js +2 -1
- package/dist/worker/console/chat/assistant-content.js +5 -34
- package/dist/worker/console/chat/chat-event-store.js +27 -10
- package/dist/worker/console/chat/db-connections.js +72 -8
- package/dist/worker/console/chat/pi-runtime/custom-tools/sql-tools.js +52 -24
- package/dist/worker/console/chat/pi-runtime.js +251 -18
- package/dist/worker/console/chat/repo-browser.js +49 -0
- package/dist/worker/console/chat/routes.js +215 -66
- package/dist/worker/console/chat/session-store.js +147 -19
- package/dist/worker/console/chat/turn-process.js +69 -23
- package/dist/worker/console/chat/user-turn-outline.js +19 -0
- package/dist/worker/console/console-update-runtime.js +2 -2
- package/dist/worker/console/operator-user-error.js +28 -1
- package/dist/worker/console/pi-readiness.js +4 -4
- package/dist/worker/console/recovery-cta.js +14 -14
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BHuhGM1e.js → abnfDiagram-N423BO3Z-DTuku_-7.js} +1 -1
- package/dist/worker/console/static/assets/{arc-CKh5E_sr.js → arc-CQbCsSqb.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DizhiBEv.js → architectureDiagram-T3A2C74G-C-xUntTR.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-3Neea-Z-.js → blockDiagram-VBNYF7ZC-DhNlwGAl.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BCP_Qrgi.js → c4Diagram-5PPSVZJV-B7j3nx8o.js} +1 -1
- package/dist/worker/console/static/assets/channel-5_os7t_x.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-C_wydftK.js → chunk-2GRJ4B5K-BCrcxEh2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-jRPXZbzx.js → chunk-2Q5K7J3B-CsaePDb3.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BLQf2OYO.js → chunk-5RXB4S5H--SRQc1rN.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-0qRq64rI.js → chunk-5VM5RSS4-C5BFY2Po.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-ClMg8xmV.js → chunk-6Q2QTUOP-Caso6uN7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Coop2FlL.js → chunk-GF5L2VYU-AY2ZSAx2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CDdO-jPJ.js → chunk-JWPE2WC7-DiMqZQ_8.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-D8Gkppjl.js → chunk-KBJHAD2P-gpvvriOp.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DA9HIySf.js → chunk-RYQCIY6F-CUdyoJhI.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DZNDRX1k.js → chunk-XXDRQBXY-Bp1xW3T4.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-loz0n18W.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-loz0n18W.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CZ1zGyjq.js → cose-bilkent-JH36ORCC-D-JIcOIT.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-B8u1_FtM.js → cynefin-VYW2F7L2-C-NKFflx.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-CPBB6lfo.js → cynefinDiagram-MW4NZA55-CSOcvOAA.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CG3Eov3E.js → dagre-VZM6K2ZE-BQSBltMy.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-ycI-dOjh.js → diagram-7IWD3JNH-B0LVC45G.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Bx_GyQ7Y.js → diagram-B4RE2ZJO-Cypg3nqy.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-m0G6mP4d.js → diagram-LBJQPF4R-BBDU88XJ.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DTDJkqcJ.js → diagram-Q27KOJAE-BZusLqSb.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BCfFrG-p.js → diagram-UB23O5K3-Cfolotcr.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BIkTNPpp.js → ebnfDiagram-BXEA7PRR-CjfyUy0m.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DTcMWrd2.js → erDiagram-JOGREHBK-DJdz7txE.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BqzDaNQ7.js → flowDiagram-UKHOOZJN-CQqgPO6y.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BSl_vGFe.js → ganttDiagram-PKOTCBZU-BOpGfS2m.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DCA5HXM6.js → gitGraphDiagram-DS77QQ5N-C6UCu0qx.js} +1 -1
- package/dist/worker/console/static/assets/index-CUT440A8.css +1 -0
- package/dist/worker/console/static/assets/index-DWKejNVJ.js +486 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DvqT18ge.js → infoDiagram-6WML65LV-BWeaVEUB.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-B6zGKrQ3.js → ishikawaDiagram-WSZJBQD7-C8gmlBbk.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BpLQPI9C.js → journeyDiagram-NVQOT4AX-DF3bm8Bp.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CzIz_-Lt.js → kanban-definition-27J2QSJJ-D2fG2Vxi.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CMQ8Jpp-.js → linear-BGKgMc2T.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BdAMrTf7.js → mermaid.core-DF6mMPJ9.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-hSwF6sOq.js → mindmap-definition-FAOFIHXS-AMhePheC.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DZdv509B.js → pegDiagram-VL7TDLO6-DN1Cl0W0.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CmD4-b0K.js → pieDiagram-7S7Q4E2Y-Cx9ngUeN.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DtKxNxlS.js → quadrantDiagram-CIZ2JOQS-BcYPTQJb.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-D48iIoiS.js → railroadDiagram-AXF67PYL-BqHUZWnH.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C3N0K-2p.js → requirementDiagram-LRYGKXZP-CmqWZ0-B.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CACWR-Xs.js → sankeyDiagram-W5VNT64P--4YetH1y.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-g_QgugvZ.js → sequenceDiagram-SI44F4Z6-DGD4VMfD.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DqLoRPGK.js → sizeCapture-X5ZJPWSS-QVw9q7rh.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-ES59VgMG.js → stateDiagram-OKZ733FA-CuemV9pe.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BdlGdIJw.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-fjyLVNWo.js → swimlanes-SLNWSIFB-C7oKzZKs.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DjjbHbde.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cd5-biKv.js → timeline-definition-Z64GVDOM-C2Of60nT.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CZTu6mPf.js → vennDiagram-T6HMQDX7-DHtpQHyL.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DR01jjbe.js → wardleyDiagram-T6FBY63Y-COi9OhSy.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-iZcuGVGa.js → xychartDiagram-ELKLHX3M-DPHQfRrt.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +9 -1
- package/dist/worker/console/static-src/operator-chat/chat-link.js +20 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +58 -3
- package/dist/worker/console/static-src/operator-chat/details-rail-surfaces.js +98 -0
- package/dist/worker/console/static-src/operator-chat/file-palette-nav.js +81 -0
- package/dist/worker/console/static-src/operator-chat/process-label.js +19 -6
- package/dist/worker/console/static-src/operator-chat/transcript-mode.js +21 -0
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +2 -0
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +13 -5
- package/dist/worker/console/static-src/operator-chat/turn-state-projection.js +166 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +56 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +202 -41
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +78 -23
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +55 -18
- package/dist/worker/console/static-src/operator-chat/useComposer.js +80 -6
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +13 -3
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +36 -17
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +18 -0
- package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
- package/dist/worker/console/static-src/shell/console-update-reload.js +1 -1
- package/dist/worker/console/workspace-initialization.js +3 -3
- package/dist/worker/observability/read-model.js +23 -2
- package/dist/worker/scheduler/scheduled-goal-dispatch.js +3 -2
- package/dist/worker/scheduler/scheduled-goal-evidence.js +6 -1
- package/docs/templates/backend-test-dag.json +1 -0
- package/package.json +4 -4
- package/dist/worker/console/static/assets/channel-DxgzA1GF.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BZcm7fs6.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BZcm7fs6.js +0 -1
- package/dist/worker/console/static/assets/index-BAEDjTbD.css +0 -1
- package/dist/worker/console/static/assets/index-wKEJUaP5.js +0 -482
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DyCIq6jx.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CsKtO34q.js +0 -8
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* operator action call. Every invocation is re-authorized here.
|
|
12
12
|
*/
|
|
13
13
|
import path from "node:path";
|
|
14
|
-
import {
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
15
|
+
import { copyFile, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
15
16
|
import { readJsonIfExists, writeSecureJson, } from "../../../infrastructure/console/app-data.js";
|
|
16
17
|
import { dispatchOperatorAction } from "../operator-actions.js";
|
|
17
18
|
import { authorizeOperatorChatTool } from "./tools.js";
|
|
@@ -19,6 +20,18 @@ import { authorizeChatSessionTool, DEFAULT_CONSOLE_CHAT_SESSION_MODE, readStored
|
|
|
19
20
|
import { projectOperationForChat } from "./chat-event-store.js";
|
|
20
21
|
import { scrubSecrets } from "./explore-tools.js";
|
|
21
22
|
import { hashDeferredTurnPayload, parseDeferredTurns, } from "./deferred-turn.js";
|
|
23
|
+
const CHAT_IMAGE_MIME_TYPES = new Set([
|
|
24
|
+
"image/png",
|
|
25
|
+
"image/jpeg",
|
|
26
|
+
"image/gif",
|
|
27
|
+
"image/webp",
|
|
28
|
+
]);
|
|
29
|
+
function isChatImageMimeType(value) {
|
|
30
|
+
return typeof value === "string" && CHAT_IMAGE_MIME_TYPES.has(value);
|
|
31
|
+
}
|
|
32
|
+
function isChatAttachmentId(value) {
|
|
33
|
+
return typeof value === "string" && /^[0-9a-f-]{36}$/i.test(value);
|
|
34
|
+
}
|
|
22
35
|
/** True only for a durable session that has never received user work. */
|
|
23
36
|
export function isBlankChatSession(record) {
|
|
24
37
|
return (record.state === "active" &&
|
|
@@ -48,6 +61,11 @@ export function cleanChatTitle(value) {
|
|
|
48
61
|
function recordPath(appData, sessionId) {
|
|
49
62
|
return path.join(appData.chats, `${sessionId}.json`);
|
|
50
63
|
}
|
|
64
|
+
function attachmentDir(appData, sessionId) {
|
|
65
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(sessionId))
|
|
66
|
+
throw new Error("invalid chat session id");
|
|
67
|
+
return path.join(appData.chats, "attachments", sessionId);
|
|
68
|
+
}
|
|
51
69
|
/** True when `sessionFile` lives inside this store's dedicated Pi JSONL dir. */
|
|
52
70
|
export function isConsoleOwnedSessionFile(appData, sessionFile) {
|
|
53
71
|
const value = sessionFile?.trim();
|
|
@@ -196,6 +214,12 @@ export class ChatSessionStore {
|
|
|
196
214
|
const source = await this.get(input.sourceSessionId);
|
|
197
215
|
if (!source)
|
|
198
216
|
throw new Error(`chat session not found: ${input.sourceSessionId}`);
|
|
217
|
+
const throughIndex = input.throughMessageId
|
|
218
|
+
? source.messages.findIndex((message) => message.id === input.throughMessageId && message.role === "user")
|
|
219
|
+
: -1;
|
|
220
|
+
if (input.throughMessageId && throughIndex < 0) {
|
|
221
|
+
throw new Error("FORK_MESSAGE_NOT_FOUND");
|
|
222
|
+
}
|
|
199
223
|
return this.serialize(input.targetSessionId, async () => {
|
|
200
224
|
const now = new Date().toISOString();
|
|
201
225
|
const record = {
|
|
@@ -208,11 +232,30 @@ export class ChatSessionStore {
|
|
|
208
232
|
updatedAt: now,
|
|
209
233
|
deferredTurns: [],
|
|
210
234
|
deferredQueueRevision: 0,
|
|
211
|
-
messages: structuredClone(
|
|
212
|
-
|
|
235
|
+
messages: structuredClone(throughIndex >= 0
|
|
236
|
+
? source.messages.slice(0, throughIndex + 1)
|
|
237
|
+
: source.messages),
|
|
238
|
+
toolInvocations: throughIndex >= 0 ? [] : structuredClone(source.toolInvocations),
|
|
213
239
|
};
|
|
214
|
-
|
|
215
|
-
|
|
240
|
+
const attachmentIds = record.messages.flatMap((message) => Array.isArray(message.attachments)
|
|
241
|
+
? message.attachments.map((attachment) => attachment.id)
|
|
242
|
+
: []);
|
|
243
|
+
if (!attachmentIds.every(isChatAttachmentId)) {
|
|
244
|
+
throw new Error("INVALID_CHAT_ATTACHMENT_REFERENCE");
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
if (attachmentIds.length > 0) {
|
|
248
|
+
const targetDir = attachmentDir(this.appData, input.targetSessionId);
|
|
249
|
+
await mkdir(targetDir, { recursive: true });
|
|
250
|
+
await Promise.all(attachmentIds.map((attachmentId) => copyFile(path.join(attachmentDir(this.appData, input.sourceSessionId), attachmentId), path.join(targetDir, attachmentId))));
|
|
251
|
+
}
|
|
252
|
+
await this.persist(record);
|
|
253
|
+
return record;
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
await this.removeAttachmentFiles(input.targetSessionId).catch(() => undefined);
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
216
259
|
});
|
|
217
260
|
}
|
|
218
261
|
/** Atomically backfill the per-conversation simulated identity on first use. */
|
|
@@ -286,17 +329,84 @@ export class ChatSessionStore {
|
|
|
286
329
|
const record = await this.get(sessionId);
|
|
287
330
|
if (!record)
|
|
288
331
|
return false;
|
|
289
|
-
|
|
290
|
-
await rm(recordPath(this.appData, sessionId), { force: true });
|
|
291
|
-
this.options.onRecordChange?.(sessionId, undefined);
|
|
292
|
-
// Record first, then best-effort JSONL unlink. Final consistency is
|
|
293
|
-
// "both gone" — a leftover JSONL is harmless, a leftover record is not.
|
|
294
|
-
if (isConsoleOwnedSessionFile(this.appData, sessionFile) && sessionFile) {
|
|
295
|
-
await rm(sessionFile, { force: true }).catch(() => undefined);
|
|
296
|
-
}
|
|
332
|
+
await this.removeSessionArtifacts(sessionId, record.sessionFile);
|
|
297
333
|
return true;
|
|
298
334
|
});
|
|
299
335
|
}
|
|
336
|
+
/** Roll back a runtime-created session that never acquired a durable owner. */
|
|
337
|
+
async discardUnownedSession(sessionId, sessionFile) {
|
|
338
|
+
await this.serialize(sessionId, () => this.removeSessionArtifacts(sessionId, sessionFile));
|
|
339
|
+
}
|
|
340
|
+
async removeSessionArtifacts(sessionId, sessionFile) {
|
|
341
|
+
await rm(recordPath(this.appData, sessionId), { force: true });
|
|
342
|
+
await this.removeAttachmentFiles(sessionId).catch(() => undefined);
|
|
343
|
+
this.options.onRecordChange?.(sessionId, undefined);
|
|
344
|
+
// Record first, then best-effort JSONL unlink. Final consistency is
|
|
345
|
+
// "both gone" — a leftover JSONL is harmless, a leftover record is not.
|
|
346
|
+
if (isConsoleOwnedSessionFile(this.appData, sessionFile) && sessionFile) {
|
|
347
|
+
await rm(sessionFile, { force: true }).catch(() => undefined);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async removeAttachmentFiles(sessionId, attachmentIds) {
|
|
351
|
+
const dir = attachmentDir(this.appData, sessionId);
|
|
352
|
+
if (!attachmentIds) {
|
|
353
|
+
await rm(dir, { recursive: true, force: true });
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
await Promise.all(attachmentIds.map((attachmentId) => rm(path.join(dir, attachmentId), { force: true })));
|
|
357
|
+
}
|
|
358
|
+
async saveAttachments(sessionId, images) {
|
|
359
|
+
if (images.length === 0)
|
|
360
|
+
return [];
|
|
361
|
+
if (!(await this.get(sessionId)))
|
|
362
|
+
throw new Error(`chat session not found: ${sessionId}`);
|
|
363
|
+
const dir = attachmentDir(this.appData, sessionId);
|
|
364
|
+
await mkdir(dir, { recursive: true });
|
|
365
|
+
const stored = [];
|
|
366
|
+
try {
|
|
367
|
+
for (const [index, image] of images.entries()) {
|
|
368
|
+
if (!isChatImageMimeType(image.mimeType)) {
|
|
369
|
+
throw new Error("unsupported chat attachment mime type");
|
|
370
|
+
}
|
|
371
|
+
const id = randomUUID();
|
|
372
|
+
const bytes = Buffer.from(image.data, "base64");
|
|
373
|
+
await writeFile(path.join(dir, id), bytes, { flag: "wx", mode: 0o600 });
|
|
374
|
+
stored.push({
|
|
375
|
+
id,
|
|
376
|
+
name: `图片 ${index + 1}`,
|
|
377
|
+
mimeType: image.mimeType,
|
|
378
|
+
byteLength: bytes.length,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
await this.removeAttachmentFiles(sessionId, stored.map((attachment) => attachment.id)).catch(() => undefined);
|
|
384
|
+
throw error;
|
|
385
|
+
}
|
|
386
|
+
return stored;
|
|
387
|
+
}
|
|
388
|
+
async readAttachment(sessionId, attachmentId) {
|
|
389
|
+
if (!isChatAttachmentId(attachmentId))
|
|
390
|
+
return undefined;
|
|
391
|
+
const record = await this.get(sessionId);
|
|
392
|
+
const metadata = record?.messages
|
|
393
|
+
.flatMap((message) => Array.isArray(message.attachments) ? message.attachments : [])
|
|
394
|
+
.find((attachment) => attachment.id === attachmentId);
|
|
395
|
+
if (!metadata || !isChatImageMimeType(metadata.mimeType))
|
|
396
|
+
return undefined;
|
|
397
|
+
try {
|
|
398
|
+
return {
|
|
399
|
+
metadata,
|
|
400
|
+
bytes: await readFile(path.join(attachmentDir(this.appData, sessionId), attachmentId)),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return undefined;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
async removeAttachments(sessionId, attachments) {
|
|
408
|
+
await this.removeAttachmentFiles(sessionId, attachments.map((attachment) => attachment.id));
|
|
409
|
+
}
|
|
300
410
|
async putDeferredTurn(sessionId, input) {
|
|
301
411
|
return this.serialize(sessionId, async () => {
|
|
302
412
|
const record = await this.get(sessionId);
|
|
@@ -454,11 +564,13 @@ export class ChatSessionStore {
|
|
|
454
564
|
const deferredTurn = index >= 0 ? queue[index] : undefined;
|
|
455
565
|
if (!deferredTurn || deferredTurn.state !== "dispatching")
|
|
456
566
|
return undefined;
|
|
567
|
+
const attachments = await this.saveAttachments(sessionId, deferredTurn.images);
|
|
457
568
|
const message = {
|
|
458
569
|
id: `m_${record.messages.length + 1}_${Math.random().toString(36).slice(2, 8)}`,
|
|
459
570
|
role: "user",
|
|
460
571
|
text,
|
|
461
572
|
at: new Date().toISOString(),
|
|
573
|
+
...(attachments.length > 0 ? { attachments } : {}),
|
|
462
574
|
};
|
|
463
575
|
queue.splice(index, 1);
|
|
464
576
|
record.messages.push(message);
|
|
@@ -468,7 +580,13 @@ export class ChatSessionStore {
|
|
|
468
580
|
if (!record.displayTitle)
|
|
469
581
|
record.displayTitle = cleanChatTitle(message.text);
|
|
470
582
|
record.updatedAt = message.at;
|
|
471
|
-
|
|
583
|
+
try {
|
|
584
|
+
await this.persist(record);
|
|
585
|
+
}
|
|
586
|
+
catch (error) {
|
|
587
|
+
await this.removeAttachments(sessionId, attachments).catch(() => undefined);
|
|
588
|
+
throw error;
|
|
589
|
+
}
|
|
472
590
|
return {
|
|
473
591
|
record,
|
|
474
592
|
message,
|
|
@@ -623,11 +741,20 @@ export async function runChatTurn(options) {
|
|
|
623
741
|
};
|
|
624
742
|
}
|
|
625
743
|
if (options.persistUserMessage !== false) {
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
744
|
+
const attachments = await store.saveAttachments(sessionId, options.images ?? []);
|
|
745
|
+
let userMessage;
|
|
746
|
+
try {
|
|
747
|
+
userMessage = await store.appendMessage(sessionId, {
|
|
748
|
+
role: "user",
|
|
749
|
+
text,
|
|
750
|
+
...(options.clientRequestId ? { clientRequestId: options.clientRequestId } : {}),
|
|
751
|
+
...(attachments.length > 0 ? { attachments } : {}),
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
catch (error) {
|
|
755
|
+
await store.removeAttachments(sessionId, attachments).catch(() => undefined);
|
|
756
|
+
throw error;
|
|
757
|
+
}
|
|
631
758
|
const afterUserMessage = await store.get(sessionId);
|
|
632
759
|
if (afterUserMessage)
|
|
633
760
|
await options.onUserMessagePersisted?.(afterUserMessage, userMessage);
|
|
@@ -687,6 +814,7 @@ export async function runChatTurn(options) {
|
|
|
687
814
|
// persisted + framed above. Nothing further to persist here.
|
|
688
815
|
break;
|
|
689
816
|
case "tool_call":
|
|
817
|
+
case "tool_update":
|
|
690
818
|
// Forwarded to the caller (SSE) for UI feedback. The actual tool
|
|
691
819
|
// execution happens in the SDK's customTools[].execute(), which
|
|
692
820
|
// routes to dispatchChatToolCall (Gate 3). Do NOT dispatch here —
|
|
@@ -51,7 +51,7 @@ export function projectTurnFlow(group) {
|
|
|
51
51
|
(item.kind === "step" && item.node.status === "running"))
|
|
52
52
|
.sort((a, b) => compareChatOrder(conversationNodeOrder(a.node), conversationNodeOrder(b.node)));
|
|
53
53
|
const postAnswer = conversationItems
|
|
54
|
-
.filter((item) => item.kind === "max-tokens")
|
|
54
|
+
.filter((item) => item.kind === "max-tokens" || item.kind === "turn-error")
|
|
55
55
|
.sort((a, b) => compareChatOrder(conversationNodeOrder(a.node), conversationNodeOrder(b.node)));
|
|
56
56
|
const preludeKeys = new Set(prelude.map((item) => item.node.key));
|
|
57
57
|
const postAnswerKeys = new Set(postAnswer.map((item) => item.node.key));
|
|
@@ -86,6 +86,19 @@ export function projectTurnFlow(group) {
|
|
|
86
86
|
}
|
|
87
87
|
return { prelude, process, postAnswer };
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Main-chat projection: request provenance and model-step bookkeeping remain
|
|
91
|
+
* available to Trajectory, but do not occupy the user-facing message stream.
|
|
92
|
+
*/
|
|
93
|
+
export function projectTimelineTurnFlow(group) {
|
|
94
|
+
const flow = projectTurnFlow(group);
|
|
95
|
+
return {
|
|
96
|
+
prelude: [],
|
|
97
|
+
process: flow.process.filter((entry) => entry.source === "process" ||
|
|
98
|
+
(entry.item.kind !== "request" && entry.item.kind !== "step")),
|
|
99
|
+
postAnswer: flow.postAnswer,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
89
102
|
function emptyGroup(key, turnId) {
|
|
90
103
|
return {
|
|
91
104
|
key,
|
|
@@ -94,6 +107,7 @@ function emptyGroup(key, turnId) {
|
|
|
94
107
|
tools: [],
|
|
95
108
|
processItems: [],
|
|
96
109
|
conversationItems: [],
|
|
110
|
+
turnClosed: false,
|
|
97
111
|
live: false,
|
|
98
112
|
};
|
|
99
113
|
}
|
|
@@ -129,6 +143,7 @@ export function hydrateSessionThread(raw) {
|
|
|
129
143
|
...(item.clientRequestId ? { clientRequestId: item.clientRequestId } : {}),
|
|
130
144
|
...(item.eventSeq === undefined ? {} : { eventSeq: item.eventSeq }),
|
|
131
145
|
...(item.turnId ? { turnId: item.turnId } : {}),
|
|
146
|
+
...(item.attachments?.length ? { attachments: item.attachments } : {}),
|
|
132
147
|
});
|
|
133
148
|
continue;
|
|
134
149
|
}
|
|
@@ -225,12 +240,17 @@ function flushToolBuffer(processItems, buffer) {
|
|
|
225
240
|
};
|
|
226
241
|
const dagProgress = dagProgressActivityForTool(tool);
|
|
227
242
|
if (dagProgress) {
|
|
228
|
-
processItems.push({
|
|
243
|
+
processItems.push({
|
|
244
|
+
kind: "dag-progress",
|
|
245
|
+
progress: dagProgress,
|
|
246
|
+
toolCallCount: 1,
|
|
247
|
+
...order,
|
|
248
|
+
});
|
|
229
249
|
continue;
|
|
230
250
|
}
|
|
231
251
|
const activity = semanticActivityForTool(tool);
|
|
232
252
|
if (activity) {
|
|
233
|
-
processItems.push({ kind: "activity", activity, ...order });
|
|
253
|
+
processItems.push({ kind: "activity", activity, toolCallCount: 1, ...order });
|
|
234
254
|
}
|
|
235
255
|
else {
|
|
236
256
|
processItems.push({ kind: "tool", tool, ...order });
|
|
@@ -238,15 +258,28 @@ function flushToolBuffer(processItems, buffer) {
|
|
|
238
258
|
}
|
|
239
259
|
}
|
|
240
260
|
function collapseDagProgressItems(items) {
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
261
|
+
const collapsed = [];
|
|
262
|
+
const progressIndexByRef = new Map();
|
|
263
|
+
for (const item of items) {
|
|
264
|
+
if (item.kind !== "dag-progress") {
|
|
265
|
+
collapsed.push(item);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const existingIndex = progressIndexByRef.get(item.progress.activityRef);
|
|
269
|
+
if (existingIndex === undefined) {
|
|
270
|
+
progressIndexByRef.set(item.progress.activityRef, collapsed.length);
|
|
271
|
+
collapsed.push(item);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const existing = collapsed[existingIndex];
|
|
275
|
+
if (existing?.kind === "dag-progress") {
|
|
276
|
+
collapsed[existingIndex] = {
|
|
277
|
+
...existing,
|
|
278
|
+
toolCallCount: (existing.toolCallCount ?? 1) + (item.toolCallCount ?? 1),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return collapsed;
|
|
250
283
|
}
|
|
251
284
|
function fillProcessDetails(group) {
|
|
252
285
|
const usedToolIds = new Set();
|
|
@@ -289,6 +322,7 @@ function fillProcessDetails(group) {
|
|
|
289
322
|
items.push({
|
|
290
323
|
kind: "text",
|
|
291
324
|
text: block.text,
|
|
325
|
+
assistantMessageId: assistant.id,
|
|
292
326
|
...(anchorSeq === undefined ? {} : { eventSeq: anchorSeq }),
|
|
293
327
|
createdAt: anchorAt,
|
|
294
328
|
});
|
|
@@ -353,8 +387,16 @@ function fillProcessDetails(group) {
|
|
|
353
387
|
}
|
|
354
388
|
addAssistantNode(assistant, getDisplayableAssistantBlocks(assistant.content ?? []));
|
|
355
389
|
}
|
|
356
|
-
const blocks = finalAssistant?.content ??
|
|
357
|
-
|
|
390
|
+
const blocks = finalAssistant?.content ??
|
|
391
|
+
(finalAssistant?.text.trim()
|
|
392
|
+
? [{ type: "text", text: finalAssistant.text }]
|
|
393
|
+
: []);
|
|
394
|
+
// Without durable step-completion metadata, only a normally settled Turn can
|
|
395
|
+
// prove that its latest assistant generation is a final answer. Keep partial
|
|
396
|
+
// text in process for running/aborted/failed and legacy unknown states.
|
|
397
|
+
const split = splitFinalAssistantBlocks(blocks, {
|
|
398
|
+
isStreaming: group.live || group.turnState !== "settled",
|
|
399
|
+
});
|
|
358
400
|
if (finalAssistant) {
|
|
359
401
|
addAssistantNode(finalAssistant, split.processBlocks);
|
|
360
402
|
}
|
|
@@ -391,10 +433,6 @@ function fillProcessDetails(group) {
|
|
|
391
433
|
group.processItems = collapseDagProgressItems(processItems);
|
|
392
434
|
if (!finalAssistant)
|
|
393
435
|
return;
|
|
394
|
-
if (blocks.length === 0) {
|
|
395
|
-
group.answer = finalAssistant;
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
436
|
if (split.answerBlocks.length === 0)
|
|
399
437
|
return;
|
|
400
438
|
const answerText = joinAssistantText(split.answerBlocks);
|
|
@@ -404,9 +442,6 @@ function fillProcessDetails(group) {
|
|
|
404
442
|
content: split.answerBlocks,
|
|
405
443
|
};
|
|
406
444
|
}
|
|
407
|
-
/**
|
|
408
|
-
* Build user → process → answer groups for the message timeline.
|
|
409
|
-
*/
|
|
410
445
|
export function buildTurnProcessGroups(input) {
|
|
411
446
|
const visible = ensureLinkedSubmissionOrder(input.messages
|
|
412
447
|
.filter((m) => m.role === "user" || m.role === "assistant")
|
|
@@ -523,10 +558,21 @@ export function buildTurnProcessGroups(input) {
|
|
|
523
558
|
}
|
|
524
559
|
return a.createdAt - b.createdAt;
|
|
525
560
|
});
|
|
561
|
+
const turnState = group.turnId
|
|
562
|
+
? input.turnStatesById?.[group.turnId]
|
|
563
|
+
: undefined;
|
|
564
|
+
if (turnState !== undefined)
|
|
565
|
+
group.turnState = turnState;
|
|
566
|
+
group.turnClosed =
|
|
567
|
+
turnState === "settled" ||
|
|
568
|
+
turnState === "aborted" ||
|
|
569
|
+
turnState === "failed";
|
|
570
|
+
const targetsActiveAssistant = input.activeAssistantId !== null &&
|
|
571
|
+
group.assistants.some((assistant) => assistant.id === input.activeAssistantId);
|
|
526
572
|
group.live =
|
|
527
573
|
input.streaming &&
|
|
528
|
-
|
|
529
|
-
|
|
574
|
+
!group.turnClosed &&
|
|
575
|
+
(targetsActiveAssistant || turnState === "running");
|
|
530
576
|
fillProcessDetails(group);
|
|
531
577
|
group.conversationItems = (input.conversationNodes ?? [])
|
|
532
578
|
.filter((node) => node.location.kind !== "session" && node.location.turnId === group.turnId)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const PREVIEW_LIMIT = 160;
|
|
2
|
+
/** Project the complete durable user-message index without shipping full history. */
|
|
3
|
+
export function projectUserTurnOutline(messages) {
|
|
4
|
+
let index = 0;
|
|
5
|
+
return messages.flatMap((message) => {
|
|
6
|
+
if (message.role !== "user")
|
|
7
|
+
return [];
|
|
8
|
+
index += 1;
|
|
9
|
+
const flat = message.text.trim().replace(/\s+/g, " ");
|
|
10
|
+
return [{
|
|
11
|
+
messageId: message.id,
|
|
12
|
+
index,
|
|
13
|
+
textPreview: flat.length > PREVIEW_LIMIT
|
|
14
|
+
? `${flat.slice(0, PREVIEW_LIMIT).trimEnd()}…`
|
|
15
|
+
: flat,
|
|
16
|
+
createdAt: Date.parse(message.at) || 0,
|
|
17
|
+
}];
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -4,7 +4,7 @@ import { createNpmUpdateClient } from "../../shared/update/npm-client.js";
|
|
|
4
4
|
import { acquireUpdateLock, updateLockPath } from "../../shared/update/state.js";
|
|
5
5
|
import { readInitRuntimeActivity } from "../observability/init-runtime-activity.js";
|
|
6
6
|
import { performConsoleUpdateAndInit, } from "./console-update-and-init.js";
|
|
7
|
-
import {
|
|
7
|
+
import { isOperationOccupied, } from "../../infrastructure/console/operation-store.js";
|
|
8
8
|
export function createConsoleUpdateExecutor(input) {
|
|
9
9
|
return async (channel) => {
|
|
10
10
|
const currentVersion = readPackageVersion(input.packageRoot);
|
|
@@ -49,7 +49,7 @@ export function createConsoleUpdateExecutor(input) {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
const liveOtherOperation = operations.some((operation) => operation.action !== "consoleUpdateAndInit" &&
|
|
52
|
-
|
|
52
|
+
isOperationOccupied(operation.state));
|
|
53
53
|
return liveOtherOperation
|
|
54
54
|
? {
|
|
55
55
|
ok: false,
|
|
@@ -114,7 +114,7 @@ const CONTEXT_TRANSFER_CODE_COPY = [
|
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
116
|
pattern: /\bOPERATION_NEEDS_RECONCILE\b/,
|
|
117
|
-
copy: "
|
|
117
|
+
copy: "当前操作的执行结果尚未确认,需要先核对(OPERATION_NEEDS_RECONCILE)。请查看操作记录和相关任务状态,核对实际执行结果后再决定后续操作;确认前请勿重复提交。",
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
pattern: /\bCONTEXT_EXPORT_FAILED\b/,
|
|
@@ -169,6 +169,33 @@ const CONTEXT_TRANSFER_CODE_COPY = [
|
|
|
169
169
|
pattern: /\bINVALID_VERSION\b/,
|
|
170
170
|
copy: "安装版本或渠道版本号无法解析(INVALID_VERSION),请重试;若持续出现请反馈。",
|
|
171
171
|
},
|
|
172
|
+
// 源绑定漂移(dag-confirmation.ts / dag-execution-receipt.ts 的英文原文)
|
|
173
|
+
// 与 frontend split CAS 冲突(operator-actions.ts)原本会原样透传到操作者
|
|
174
|
+
// 横幅,这里给出可执行的中文说明。
|
|
175
|
+
{
|
|
176
|
+
pattern: /\bBINDING_DRIFT\b|\bbinding drift\b|\bbinding drifted\b/i,
|
|
177
|
+
copy: "这次确认所依据的内容已经变了(任务契约、源文件、程序版本或校验结果在确认之后被修改),因此旧确认不能再使用。这是保护措施,不是故障:请回到任务页重新生成确认,然后重新开始运行。",
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
pattern: /frontend split CAS conflict/i,
|
|
181
|
+
copy: "前端拆分任务的状态在你操作期间被其他操作改过了(通常是另一个标签页或并发操作),为避免覆盖对方的结果,本次修改没有生效。请刷新页面,确认当前状态后重试。",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
pattern: /\bCONFIRMATION_EXPIRED\b/,
|
|
185
|
+
copy: "这次确认已经超时失效。请回到任务页重新生成确认后再继续。",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
pattern: /\bCONFIRMATION_STALE\b/,
|
|
189
|
+
copy: "这次确认对应的是改动之前的内容,已经过期作废。请回到任务页重新生成确认后再继续。",
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
pattern: /\bCONFIRMATION_CONSUMED\b/,
|
|
193
|
+
copy: "这次确认已经被使用过了,不能重复提交。如需再次执行,请重新生成确认。",
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
pattern: /\bHUMAN_CONFIRMATION_REQUIRED\b/,
|
|
197
|
+
copy: "这一步需要你先在页面上确认一次才能继续。请按提示完成确认后再重试。",
|
|
198
|
+
},
|
|
172
199
|
];
|
|
173
200
|
/** Map internal spawn/controller errors to actionable Chinese copy. */
|
|
174
201
|
export function formatOperatorUserError(message) {
|
|
@@ -85,7 +85,7 @@ function hasProviderConfig(snapshot) {
|
|
|
85
85
|
/**
|
|
86
86
|
* Discover live Pi SDK / auth / model readiness via ModelRuntime.
|
|
87
87
|
* Kept inside worker/console (dynamic import) so Worker does not depend on
|
|
88
|
-
* executors; contract matches pi-sdk-executor 0.
|
|
88
|
+
* executors; contract matches pi-sdk-executor 0.84.4.
|
|
89
89
|
*/
|
|
90
90
|
export async function discoverPiReadinessProbe(options) {
|
|
91
91
|
const repoRoot = options?.repoRoot ?? process.cwd();
|
|
@@ -118,7 +118,7 @@ export async function discoverPiReadinessProbe(options) {
|
|
|
118
118
|
modelsAvailable: false,
|
|
119
119
|
interviewSessionOk: false,
|
|
120
120
|
details: {
|
|
121
|
-
sdkMessage: "Pi SDK incompatible: requires createAgentSession, getAgentDir, and ModelRuntime.create (0.
|
|
121
|
+
sdkMessage: "Pi SDK incompatible: requires createAgentSession, getAgentDir, and ModelRuntime.create (0.84.4)",
|
|
122
122
|
authMessage: "skipped (sdk incompatible)",
|
|
123
123
|
modelsMessage: "skipped (sdk incompatible)",
|
|
124
124
|
sessionMessage: "skipped (sdk incompatible)",
|
|
@@ -159,7 +159,7 @@ export async function discoverPiReadinessProbe(options) {
|
|
|
159
159
|
modelsAvailable: false,
|
|
160
160
|
interviewSessionOk: false,
|
|
161
161
|
details: {
|
|
162
|
-
sdkMessage: "Pi SDK 0.
|
|
162
|
+
sdkMessage: "Pi SDK 0.84.4 contract available",
|
|
163
163
|
authMessage: `ModelRuntime.create failed: ${message}`,
|
|
164
164
|
modelsMessage: "skipped (runtime init failed)",
|
|
165
165
|
sessionMessage: "skipped (runtime init failed)",
|
|
@@ -225,7 +225,7 @@ export async function discoverPiReadinessProbe(options) {
|
|
|
225
225
|
interviewSessionOk,
|
|
226
226
|
piAvailableModels: availableModels,
|
|
227
227
|
details: {
|
|
228
|
-
sdkMessage: `Pi SDK 0.
|
|
228
|
+
sdkMessage: `Pi SDK 0.84.4 contract available (agentDir=${agentDir})`,
|
|
229
229
|
authMessage: authReady
|
|
230
230
|
? `credentials/providers ready (available=${availableCount}, providerConfig=${providerConfigured})`
|
|
231
231
|
: "no available models and no stored/configured provider credentials (set Pi auth or provider API keys)",
|
|
@@ -13,40 +13,40 @@ const CTA = {
|
|
|
13
13
|
},
|
|
14
14
|
doctor: {
|
|
15
15
|
id: "doctor",
|
|
16
|
-
label: "
|
|
16
|
+
label: "体检诊断",
|
|
17
17
|
action: "doctor",
|
|
18
18
|
lane: "dag",
|
|
19
|
-
guide: "
|
|
19
|
+
guide: "只做检查、不改动任何东西:看看 Console、模型服务和仓库是否正常。重跑之前先跑它,能省很多试错。",
|
|
20
20
|
commandHint: "agent-worker console doctor --repo .",
|
|
21
21
|
},
|
|
22
22
|
decision: {
|
|
23
23
|
id: "decision",
|
|
24
24
|
label: "人工决策",
|
|
25
25
|
lane: "dag",
|
|
26
|
-
guide: "
|
|
26
|
+
guide: "运行停下来等你拍板时用:先点「体检诊断」看清现状,再决定继续还是调整(需要走命令行)。",
|
|
27
27
|
commandHint: "loop-agent dag doctor --run-id <run-id> --markdown",
|
|
28
28
|
},
|
|
29
29
|
resume: {
|
|
30
30
|
id: "resume",
|
|
31
|
-
label: "
|
|
31
|
+
label: "继续运行",
|
|
32
32
|
lane: "dag",
|
|
33
|
-
guide: "
|
|
33
|
+
guide: "只在确认这次运行还能安全接着跑时使用;不确定就先点「体检诊断」(需要走命令行)。",
|
|
34
34
|
commandHint: "loop-agent task advance <task-id> --json",
|
|
35
35
|
},
|
|
36
36
|
reconcile: {
|
|
37
37
|
id: "reconcile",
|
|
38
|
-
label: "
|
|
38
|
+
label: "核对并归档中断的运行",
|
|
39
39
|
requiresReason: true,
|
|
40
40
|
action: "dagReconcileRun",
|
|
41
41
|
lane: "dag",
|
|
42
|
-
guide: "
|
|
42
|
+
guide: "运行被中断或找不到归属时,用它把这次运行标记为「已归档」,之后才能重新发起。如果运行其实还在跑,请不要用这个动作,否则会打断它。",
|
|
43
43
|
commandHint: "loop-agent dag reconcile-run --run-id <run-id> --action supersede --reason <structured-reason>",
|
|
44
44
|
},
|
|
45
45
|
regenerate: {
|
|
46
46
|
id: "regenerate",
|
|
47
47
|
label: "重新生成契约/计划",
|
|
48
48
|
lane: "dag",
|
|
49
|
-
guide: "
|
|
49
|
+
guide: "任务契约或计划已经过期时用:回任务页重新生成,或走命令行重跑一次 task advance。本页只是提示,按钮在任务页。",
|
|
50
50
|
commandHint: "loop-agent task advance <task-id> --json",
|
|
51
51
|
},
|
|
52
52
|
observeLink: {
|
|
@@ -54,16 +54,16 @@ const CTA = {
|
|
|
54
54
|
label: "全屏观测",
|
|
55
55
|
action: "observeLink",
|
|
56
56
|
lane: "dag",
|
|
57
|
-
guide: "
|
|
57
|
+
guide: "打开完整的过程视图;顶栏可以回到操作台。",
|
|
58
58
|
commandHint: "agent-worker console --repo . --port 8790",
|
|
59
59
|
},
|
|
60
60
|
dagRerun: {
|
|
61
61
|
id: "dagRerun",
|
|
62
|
-
label: "
|
|
62
|
+
label: "从失败节点继续",
|
|
63
63
|
requiresReason: true,
|
|
64
64
|
action: "dagRerun",
|
|
65
65
|
lane: "dag",
|
|
66
|
-
guide: "
|
|
66
|
+
guide: "失败原因跟代码无关时(模型服务报错、网络、限流、超时)首选这个:系统会先判断哪些步骤可以安全跳过,再从出错前的那一步接着跑,不用从头再来,也不需要人工审批。以下几种情况不能这样跑:这次运行是 Worker 队列发起的、仓库或程序版本在这次运行后被改过、或者后续步骤里有会执行命令的验证环节——遇到这些请改用「带反馈重新执行任务」。",
|
|
67
67
|
commandHint: "loop-agent dag rerun --run-id <run-id> --from-node <node-id> --plan",
|
|
68
68
|
},
|
|
69
69
|
standaloneTaskRerun: {
|
|
@@ -72,7 +72,7 @@ const CTA = {
|
|
|
72
72
|
requiresReason: true,
|
|
73
73
|
action: "standaloneTaskRerun",
|
|
74
74
|
lane: "dag",
|
|
75
|
-
guide: "
|
|
75
|
+
guide: "不能从中间某一步接着跑时用这个:会把整个任务从头重新执行一遍,并自动带上上一轮还没解决的验证/评审意见,不用人工审批。如果没有可以继承的意见,会明确告诉你。注意:Worker 队列发起的任务请用下方「重新排队 Worker Task」。",
|
|
76
76
|
commandHint: "loop-agent dag rerun-task --run-id <run-id> --reason <text> --json",
|
|
77
77
|
},
|
|
78
78
|
workerTaskRetry: {
|
|
@@ -81,7 +81,7 @@ const CTA = {
|
|
|
81
81
|
requiresReason: true,
|
|
82
82
|
action: "workerTaskRetry",
|
|
83
83
|
lane: "worker",
|
|
84
|
-
guide: "
|
|
84
|
+
guide: "只用于 Worker 队列里失败的任务:服务端会先确认它确实处于失败状态再重新入队,不需要人工审批。不要和上面的「带反馈重新执行任务」混用。",
|
|
85
85
|
commandHint: "agent-worker task retry <task-id> --feature-id <feature-id> --repo . --reason <reason>",
|
|
86
86
|
},
|
|
87
87
|
interrupt: {
|
|
@@ -90,7 +90,7 @@ const CTA = {
|
|
|
90
90
|
requiresReason: true,
|
|
91
91
|
action: "dagRequestInterrupt",
|
|
92
92
|
lane: "dag",
|
|
93
|
-
guide: "
|
|
93
|
+
guide: "只对这台 Console 正在跑的运行有效:提交后需要你在浏览器里确认一次,然后等当前步骤安全跑完,不会立刻杀掉进程。",
|
|
94
94
|
commandHint: "loop-agent dag request-interrupt --run-id <run-id> --reason-code operator-request --reason <structured-reason>",
|
|
95
95
|
},
|
|
96
96
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-Caso6uN7.js";import{p as f}from"./chunk-JWPE2WC7-DiMqZQ_8.js";import{_ as n,l as o}from"./mermaid.core-DF6mMPJ9.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-C-NKFflx.js";import"./index-DWKejNVJ.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
|