@tea-agent/loop-agent 0.37.0 → 0.37.1-beta.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/AGENTS.md +2 -6
- package/CHANGELOG.md +71 -144
- package/README.md +1 -1
- package/bin/loop-agent.js +1 -37
- package/dist/application/task-lifecycle/advance.js +0 -1
- package/dist/application/task-lifecycle/observe.js +0 -15
- package/dist/application/task-lifecycle/plan-transitions.js +0 -15
- package/dist/cli/command-definitions.js +0 -7
- package/dist/cli/program.js +3 -8
- package/dist/commands/init-upgrade.js +19 -351
- package/dist/commands/init.js +67 -14
- package/dist/commands/run-dag-progress.js +0 -14
- package/dist/commands/task-advance.js +3 -33
- package/dist/executors/dag-pi-executor.js +30 -47
- package/dist/executors/pi-sdk-executor.js +0 -34
- package/dist/executors/shell-executor.js +95 -6
- package/dist/shared/operator/capabilities.js +13 -200
- package/dist/shared/package-metadata.js +0 -42
- package/dist/task/contract/project.js +0 -4
- package/dist/task/source-prepare/completeness.js +0 -53
- package/dist/task/source-prepare/parse-intent.js +12 -82
- package/dist/task/source-prepare/prepare.js +5 -102
- package/dist/task/source-prepare/semantic-intake.js +23 -144
- package/dist/worker/console/chat/artifact-card.js +1 -8
- package/dist/worker/console/chat/chat-event-store.js +0 -152
- package/dist/worker/console/chat/human-gate-card.js +1 -9
- package/dist/worker/console/chat/operation-card.js +2 -71
- package/dist/worker/console/chat/pi-runtime.js +62 -111
- package/dist/worker/console/chat/routes.js +10 -43
- package/dist/worker/console/chat/session-store.js +1 -9
- package/dist/worker/console/chat/shortcuts.js +7 -9
- package/dist/worker/console/chat/turn-process.js +23 -204
- package/dist/worker/console/chat/workspace-landing.js +1 -2
- package/dist/worker/console/operation-runner.js +6 -155
- package/dist/worker/console/operator-actions.js +14 -391
- package/dist/worker/console/operator-user-error.js +0 -4
- package/dist/worker/console/recovery-cta.js +3 -50
- package/dist/worker/console/static/assets/index-HX1pbOyl.css +1 -0
- package/dist/worker/console/static/assets/index-M0BLEBfh.js +56 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +9 -13
- package/dist/worker/console/static-src/app/useOperatorActions.js +2 -4
- package/dist/worker/console/static-src/app/useRecoveryActions.js +56 -65
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +1 -73
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +30 -114
- package/dist/worker/console/static-src/operator-chat/refs.js +0 -3
- package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +13 -67
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -12
- package/dist/worker/console/static-src/operator-chat/useComposer.js +8 -45
- package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -7
- package/dist/worker/loop-agent/loop-agent-client.js +3 -17
- package/dist/worker/observability/read-model.js +0 -20
- package/dist/worker/observe/spec-evidence.js +8 -3
- package/dist/worker/observe/static/operator-chrome.d.ts +0 -1
- package/dist/worker/observe/static/operator-chrome.js +3 -10
- package/dist/worker/observe/static/views/dag-inspector.js +71 -6
- package/dist/worker/observe/static/views/dag.js +0 -12
- package/dist/worker/preflight.js +1 -2
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +707 -37
- package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +109 -7
- package/dist/workflows/dag/backend-test-module-stem.js +5 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +345 -24
- package/dist/workflows/dag/backend-test-scenario-param.js +890 -164
- package/dist/workflows/dag/backend-test-writer-completeness.js +47 -16
- package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
- package/dist/workflows/dag/dynamic-runtime/shared.js +1 -9
- package/dist/workflows/dag/frontend-implementation-contract.js +39 -233
- package/dist/workflows/dag/frontend-prewrite-gate.js +61 -364
- package/dist/workflows/dag/frontend-repair.js +18 -219
- package/dist/workflows/dag/frontend-verification-trace.js +32 -47
- package/dist/workflows/dag/init-hybrid.js +82 -165
- package/dist/workflows/dag/node-execution.js +0 -89
- package/dist/workflows/dag/recovery-recommendation.js +0 -58
- package/dist/workflows/dag/runner.js +15 -333
- package/dist/workflows/dag/scheduler.js +3 -257
- package/dist/workflows/dag/types.js +9 -130
- package/docs/README.md +3 -3
- package/docs/architecture/evolution.md +67 -102
- package/docs/templates/backend-test-dag.json +50 -32
- package/docs/templates/init-managed-agents.md +2 -5
- package/harness.json +2 -2
- package/package.json +3 -9
- package/skills/loop-agent/SKILL.md +0 -1
- package/skills/loop-agent/references/command-reference.md +0 -4
- package/dist/build-stamp.json +0 -6
- package/dist/commands/dag-request-interrupt.js +0 -20
- package/dist/worker/console/chat/assistant-content.js +0 -121
- package/dist/worker/console/chat/semantic-activity.js +0 -477
- package/dist/worker/console/operation-run-facts.js +0 -190
- package/dist/worker/console/operation-wait.js +0 -355
- package/dist/worker/console/recovery-error-copy.js +0 -198
- package/dist/worker/console/static/assets/abnfDiagram-N423BO3Z-DSvF6RXQ.js +0 -1
- package/dist/worker/console/static/assets/arc-DuI4ogtJ.js +0 -1
- package/dist/worker/console/static/assets/architectureDiagram-T3A2C74G-DdBbgD4y.js +0 -36
- package/dist/worker/console/static/assets/blockDiagram-VBNYF7ZC-xQC2HELm.js +0 -132
- package/dist/worker/console/static/assets/c4Diagram-5PPSVZJV-DPMSgwIZ.js +0 -10
- package/dist/worker/console/static/assets/channel-DLVz9TYW.js +0 -1
- package/dist/worker/console/static/assets/chunk-2GRJ4B5K-WTEKTxin.js +0 -1
- package/dist/worker/console/static/assets/chunk-2Q5K7J3B-B6vZ0Pv_.js +0 -1
- package/dist/worker/console/static/assets/chunk-5RXB4S5H-BrerNAix.js +0 -231
- package/dist/worker/console/static/assets/chunk-5VM5RSS4-D_ch2aaI.js +0 -15
- package/dist/worker/console/static/assets/chunk-6Q2QTUOP-VvfacNrq.js +0 -88
- package/dist/worker/console/static/assets/chunk-GF5L2VYU-B-uGFIg2.js +0 -206
- package/dist/worker/console/static/assets/chunk-JWPE2WC7-BlysCtod.js +0 -1
- package/dist/worker/console/static/assets/chunk-KBJHAD2P-BjAUAfcT.js +0 -1
- package/dist/worker/console/static/assets/chunk-RYQCIY6F-BeZnBmb-.js +0 -1
- package/dist/worker/console/static/assets/chunk-XXDRQBXY-Bbfe3UJG.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BeAuGcO9.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BeAuGcO9.js +0 -1
- package/dist/worker/console/static/assets/cose-bilkent-JH36ORCC-BKJQnYVM.js +0 -1
- package/dist/worker/console/static/assets/cynefin-VYW2F7L2-B1wtI3hU.js +0 -166
- package/dist/worker/console/static/assets/cynefinDiagram-MW4NZA55-DCF6DBSu.js +0 -62
- package/dist/worker/console/static/assets/cytoscape.esm-yzknjiTM.js +0 -321
- package/dist/worker/console/static/assets/dagre-VZM6K2ZE-BJyNGRlP.js +0 -4
- package/dist/worker/console/static/assets/defaultLocale-DX6XiGOO.js +0 -1
- package/dist/worker/console/static/assets/diagram-7IWD3JNH-B7j1-VsZ.js +0 -30
- package/dist/worker/console/static/assets/diagram-B4RE2ZJO-BREJ140a.js +0 -3
- package/dist/worker/console/static/assets/diagram-LBJQPF4R-CDPHmHGH.js +0 -24
- package/dist/worker/console/static/assets/diagram-Q27KOJAE-BtZLrez1.js +0 -24
- package/dist/worker/console/static/assets/diagram-UB23O5K3-mzybu8oU.js +0 -41
- package/dist/worker/console/static/assets/ebnfDiagram-BXEA7PRR-Dk3I2uO6.js +0 -1
- package/dist/worker/console/static/assets/erDiagram-JOGREHBK-BT97u_tQ.js +0 -85
- package/dist/worker/console/static/assets/flowDiagram-UKHOOZJN-CMOtYzui.js +0 -156
- package/dist/worker/console/static/assets/ganttDiagram-PKOTCBZU-DiNydkuY.js +0 -292
- package/dist/worker/console/static/assets/gitGraphDiagram-DS77QQ5N-Cwt6fLkl.js +0 -106
- package/dist/worker/console/static/assets/graph-DOmOIIwC.js +0 -1
- package/dist/worker/console/static/assets/index-DQxiso1w.js +0 -325
- package/dist/worker/console/static/assets/index-_j0Hzo69.css +0 -1
- package/dist/worker/console/static/assets/infoDiagram-6WML65LV-AsGIGKKB.js +0 -2
- package/dist/worker/console/static/assets/init-Gi6I4Gst.js +0 -1
- package/dist/worker/console/static/assets/ishikawaDiagram-WSZJBQD7-BL8ITZh3.js +0 -70
- package/dist/worker/console/static/assets/journeyDiagram-NVQOT4AX-VPKAUX8M.js +0 -139
- package/dist/worker/console/static/assets/kanban-definition-27J2QSJJ-BL56hfGp.js +0 -89
- package/dist/worker/console/static/assets/layout-D-LzfAck.js +0 -1
- package/dist/worker/console/static/assets/linear-BmSG3nIZ.js +0 -1
- package/dist/worker/console/static/assets/map-DxJ2ADlA.js +0 -1
- package/dist/worker/console/static/assets/mermaid.core-DWGK9kwP.js +0 -308
- package/dist/worker/console/static/assets/mindmap-definition-FAOFIHXS--vvKLjo0.js +0 -96
- package/dist/worker/console/static/assets/ordinal-Cboi1Yqb.js +0 -1
- package/dist/worker/console/static/assets/pegDiagram-VL7TDLO6-B41mxb2-.js +0 -1
- package/dist/worker/console/static/assets/pieDiagram-7S7Q4E2Y-DTlu77sw.js +0 -39
- package/dist/worker/console/static/assets/quadrantDiagram-CIZ2JOQS-Bk5A7alv.js +0 -7
- package/dist/worker/console/static/assets/railroadDiagram-AXF67PYL-Csf9jzQy.js +0 -1
- package/dist/worker/console/static/assets/requirementDiagram-LRYGKXZP-CeptoSNx.js +0 -84
- package/dist/worker/console/static/assets/sankeyDiagram-W5VNT64P-A-DLCLer.js +0 -40
- package/dist/worker/console/static/assets/sequenceDiagram-SI44F4Z6-22VbPdOI.js +0 -162
- package/dist/worker/console/static/assets/sizeCapture-X5ZJPWSS-DWvAyBC9.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-OKZ733FA-qICZ75yT.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DzKvfHTX.js +0 -1
- package/dist/worker/console/static/assets/swimlanes-SLNWSIFB-xi7lcMyp.js +0 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cxqr_qM1.js +0 -8
- package/dist/worker/console/static/assets/timeline-definition-Z64GVDOM-Bu6Nl6O2.js +0 -120
- package/dist/worker/console/static/assets/vennDiagram-T6HMQDX7-BzMz4Bn9.js +0 -34
- package/dist/worker/console/static/assets/wardleyDiagram-T6FBY63Y-CxaJnzc5.js +0 -78
- package/dist/worker/console/static/assets/xychartDiagram-ELKLHX3M-5JK-omQK.js +0 -7
- package/dist/worker/console/static/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/worker/console/static-src/operator-chat/activity-journey.js +0 -125
- package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
- package/dist/worker/console/static-src/operator-chat/activity-references.js +0 -20
- package/dist/worker/console/static-src/operator-chat/slash-palette-layout.js +0 -24
- package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +0 -141
- package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
- package/dist/worker/observability/interrupt-eligibility.js +0 -264
- package/dist/workflows/dag/contract-output-registry.js +0 -14
- package/dist/workflows/dag/contract-validator-registrations.js +0 -8
- package/dist/workflows/dag/frontend-recovery-plan.js +0 -73
- package/dist/workflows/dag/frontend-recovery-root-manifest.js +0 -123
- package/dist/workflows/dag/frontend-recovery-run.js +0 -539
- package/dist/workflows/dag/frontend-writer-recovery.js +0 -106
- package/dist/workflows/dag/frontend-writer-rollback.js +0 -821
- package/dist/workflows/dag/interrupt-request.js +0 -559
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Turn grouping for Operator Chat inline process UX (pi-web ProcessDetails mind).
|
|
3
3
|
*/
|
|
4
|
-
import { getDisplayableAssistantBlocks, joinAssistantText, parseAssistantContent, splitFinalAssistantBlocks, } from "./assistant-content.js";
|
|
5
|
-
import { semanticActivityForTool, } from "./semantic-activity.js";
|
|
6
|
-
function emptyGroup(key) {
|
|
7
|
-
return {
|
|
8
|
-
key,
|
|
9
|
-
assistants: [],
|
|
10
|
-
tools: [],
|
|
11
|
-
processItems: [],
|
|
12
|
-
live: false,
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
4
|
/**
|
|
16
5
|
* Restore chat bubbles + tool cards from durable session messages.
|
|
17
6
|
* Tool rows often appear before the final assistant `message_end` message;
|
|
@@ -47,13 +36,11 @@ export function hydrateSessionThread(raw) {
|
|
|
47
36
|
if (item.role === "assistant") {
|
|
48
37
|
if (!turnId)
|
|
49
38
|
turnId = `restored-${item.id}`;
|
|
50
|
-
const content = parseAssistantContent(item.content);
|
|
51
39
|
messages.push({
|
|
52
40
|
id: item.id,
|
|
53
41
|
role: "assistant",
|
|
54
42
|
text: item.text,
|
|
55
43
|
createdAt,
|
|
56
|
-
...(content && content.length > 0 ? { content } : {}),
|
|
57
44
|
});
|
|
58
45
|
flushPendingToAssistant(item.id);
|
|
59
46
|
continue;
|
|
@@ -70,7 +57,6 @@ export function hydrateSessionThread(raw) {
|
|
|
70
57
|
createdAt,
|
|
71
58
|
finishedAt: createdAt,
|
|
72
59
|
turnId,
|
|
73
|
-
...(item.operationId ? { operationId: item.operationId } : {}),
|
|
74
60
|
};
|
|
75
61
|
toolCalls.push(entry);
|
|
76
62
|
pendingTools.push(entry);
|
|
@@ -78,125 +64,6 @@ export function hydrateSessionThread(raw) {
|
|
|
78
64
|
}
|
|
79
65
|
return { messages, toolCalls };
|
|
80
66
|
}
|
|
81
|
-
/** Stable tool order: server eventSeq first, createdAt fallback, id tiebreak. */
|
|
82
|
-
function sortToolsStable(tools) {
|
|
83
|
-
return [...tools].sort((a, b) => {
|
|
84
|
-
if (a.eventSeq !== undefined &&
|
|
85
|
-
b.eventSeq !== undefined &&
|
|
86
|
-
a.eventSeq !== b.eventSeq) {
|
|
87
|
-
return a.eventSeq - b.eventSeq;
|
|
88
|
-
}
|
|
89
|
-
if (a.createdAt !== b.createdAt)
|
|
90
|
-
return a.createdAt - b.createdAt;
|
|
91
|
-
return a.id < b.id ? -1 : 1;
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Flush buffered consecutive tools into process items. Semantic operator
|
|
96
|
-
* tools stay as individual activity events; plain tools group into a
|
|
97
|
-
* `tool-group` ("并行调用 · N 项") when two or more land back to back.
|
|
98
|
-
* Thinking/text/activity boundaries break the buffer upstream.
|
|
99
|
-
*/
|
|
100
|
-
function flushToolBuffer(processItems, buffer) {
|
|
101
|
-
if (buffer.length === 0)
|
|
102
|
-
return;
|
|
103
|
-
const sorted = sortToolsStable(buffer);
|
|
104
|
-
let plainGroup = [];
|
|
105
|
-
const flushPlain = () => {
|
|
106
|
-
if (plainGroup.length === 1) {
|
|
107
|
-
processItems.push({ kind: "tool", tool: plainGroup[0] });
|
|
108
|
-
}
|
|
109
|
-
else if (plainGroup.length > 1) {
|
|
110
|
-
processItems.push({ kind: "tool-group", tools: plainGroup });
|
|
111
|
-
}
|
|
112
|
-
plainGroup = [];
|
|
113
|
-
};
|
|
114
|
-
for (const tool of sorted) {
|
|
115
|
-
const activity = semanticActivityForTool(tool);
|
|
116
|
-
if (activity) {
|
|
117
|
-
flushPlain();
|
|
118
|
-
processItems.push({ kind: "activity", activity });
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
plainGroup.push(tool);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
flushPlain();
|
|
125
|
-
}
|
|
126
|
-
function emitProcessBlocks(blocks, group, processItems, usedToolIds) {
|
|
127
|
-
let buffer = [];
|
|
128
|
-
for (const block of blocks) {
|
|
129
|
-
if (block.type === "thinking") {
|
|
130
|
-
flushToolBuffer(processItems, buffer);
|
|
131
|
-
buffer = [];
|
|
132
|
-
processItems.push({ kind: "thinking", thinking: block.thinking });
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
if (block.type === "text") {
|
|
136
|
-
flushToolBuffer(processItems, buffer);
|
|
137
|
-
buffer = [];
|
|
138
|
-
if (block.text.trim()) {
|
|
139
|
-
processItems.push({ kind: "text", text: block.text });
|
|
140
|
-
}
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
const tool = group.tools.find((entry) => entry.id === block.toolCallId);
|
|
144
|
-
if (tool && !usedToolIds.has(tool.id)) {
|
|
145
|
-
buffer.push(tool);
|
|
146
|
-
usedToolIds.add(tool.id);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
flushToolBuffer(processItems, buffer);
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Insert tools attributed to this assistant stage but without a matching
|
|
153
|
-
* content toolCall block (live SSE tool_call frames often arrive before the
|
|
154
|
-
* assistant content does). They follow the stage's thinking/text instead of
|
|
155
|
-
* sinking to the end of the whole turn.
|
|
156
|
-
*/
|
|
157
|
-
function appendAssistantUnmatchedTools(group, assistant, processItems, usedToolIds) {
|
|
158
|
-
const unmatched = group.tools.filter((tool) => !usedToolIds.has(tool.id) && tool.assistantMessageId === assistant.id);
|
|
159
|
-
if (unmatched.length === 0)
|
|
160
|
-
return;
|
|
161
|
-
for (const tool of unmatched)
|
|
162
|
-
usedToolIds.add(tool.id);
|
|
163
|
-
flushToolBuffer(processItems, unmatched);
|
|
164
|
-
}
|
|
165
|
-
function fillProcessDetails(group) {
|
|
166
|
-
const usedToolIds = new Set();
|
|
167
|
-
const processItems = [];
|
|
168
|
-
const prior = group.assistants.slice(0, -1);
|
|
169
|
-
const finalAssistant = group.assistants.at(-1);
|
|
170
|
-
for (const assistant of prior) {
|
|
171
|
-
emitProcessBlocks(getDisplayableAssistantBlocks(assistant.content ?? []), group, processItems, usedToolIds);
|
|
172
|
-
appendAssistantUnmatchedTools(group, assistant, processItems, usedToolIds);
|
|
173
|
-
}
|
|
174
|
-
const blocks = finalAssistant?.content ?? [];
|
|
175
|
-
const split = splitFinalAssistantBlocks(blocks, { isStreaming: group.live });
|
|
176
|
-
emitProcessBlocks(split.processBlocks, group, processItems, usedToolIds);
|
|
177
|
-
if (finalAssistant) {
|
|
178
|
-
appendAssistantUnmatchedTools(group, finalAssistant, processItems, usedToolIds);
|
|
179
|
-
}
|
|
180
|
-
// Bounded fallback for legacy restored rows with no assistant attribution:
|
|
181
|
-
// keep them at this turn's end in stable server order instead of randomly
|
|
182
|
-
// interleaving them into earlier stages.
|
|
183
|
-
const orphans = group.tools.filter((tool) => !usedToolIds.has(tool.id));
|
|
184
|
-
if (orphans.length > 0)
|
|
185
|
-
flushToolBuffer(processItems, orphans);
|
|
186
|
-
group.processItems = processItems;
|
|
187
|
-
if (!finalAssistant)
|
|
188
|
-
return;
|
|
189
|
-
if (blocks.length === 0) {
|
|
190
|
-
group.answer = finalAssistant;
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
const answerText = joinAssistantText(split.answerBlocks);
|
|
194
|
-
group.answer = {
|
|
195
|
-
...finalAssistant,
|
|
196
|
-
text: answerText,
|
|
197
|
-
content: split.answerBlocks,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
67
|
/**
|
|
201
68
|
* Build user → process → answer groups for the message timeline.
|
|
202
69
|
*/
|
|
@@ -205,7 +72,12 @@ export function buildTurnProcessGroups(input) {
|
|
|
205
72
|
const groups = [];
|
|
206
73
|
let current = null;
|
|
207
74
|
const startGroup = (key) => {
|
|
208
|
-
const group =
|
|
75
|
+
const group = {
|
|
76
|
+
key,
|
|
77
|
+
assistants: [],
|
|
78
|
+
tools: [],
|
|
79
|
+
live: false,
|
|
80
|
+
};
|
|
209
81
|
groups.push(group);
|
|
210
82
|
return group;
|
|
211
83
|
};
|
|
@@ -215,20 +87,17 @@ export function buildTurnProcessGroups(input) {
|
|
|
215
87
|
current.user = message;
|
|
216
88
|
continue;
|
|
217
89
|
}
|
|
218
|
-
if (message.kind === "compaction") {
|
|
219
|
-
// Compaction is a standalone timeline event. Starting a fresh group
|
|
220
|
-
// prevents it from replacing the previous turn's assistant answer.
|
|
221
|
-
current = startGroup(`compaction-${message.id}`);
|
|
222
|
-
current.assistants.push(message);
|
|
223
|
-
current = null;
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
90
|
if (!current)
|
|
227
91
|
current = startGroup(`assistant-${message.id}`);
|
|
228
92
|
current.assistants.push(message);
|
|
229
93
|
}
|
|
230
94
|
if (groups.length === 0 && input.toolCalls.length > 0) {
|
|
231
|
-
groups.push(
|
|
95
|
+
groups.push({
|
|
96
|
+
key: "orphan-tools",
|
|
97
|
+
assistants: [],
|
|
98
|
+
tools: [],
|
|
99
|
+
live: false,
|
|
100
|
+
});
|
|
232
101
|
}
|
|
233
102
|
const assigned = new Set();
|
|
234
103
|
const assistantToGroup = new Map();
|
|
@@ -243,6 +112,7 @@ export function buildTurnProcessGroups(input) {
|
|
|
243
112
|
if (group) {
|
|
244
113
|
group.tools.push(tool);
|
|
245
114
|
assigned.add(tool.id);
|
|
115
|
+
continue;
|
|
246
116
|
}
|
|
247
117
|
}
|
|
248
118
|
}
|
|
@@ -264,6 +134,7 @@ export function buildTurnProcessGroups(input) {
|
|
|
264
134
|
if (restored) {
|
|
265
135
|
restored.tools.push(tool);
|
|
266
136
|
assigned.add(tool.id);
|
|
137
|
+
continue;
|
|
267
138
|
}
|
|
268
139
|
}
|
|
269
140
|
}
|
|
@@ -280,80 +151,28 @@ export function buildTurnProcessGroups(input) {
|
|
|
280
151
|
windowGroup.tools.push(tool);
|
|
281
152
|
assigned.add(tool.id);
|
|
282
153
|
}
|
|
283
|
-
else {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
lastGroup.tools.push(tool);
|
|
287
|
-
assigned.add(tool.id);
|
|
288
|
-
}
|
|
154
|
+
else if (groups.length > 0) {
|
|
155
|
+
groups[groups.length - 1].tools.push(tool);
|
|
156
|
+
assigned.add(tool.id);
|
|
289
157
|
}
|
|
290
158
|
}
|
|
291
159
|
for (const group of groups) {
|
|
292
|
-
group.tools.sort((a, b) =>
|
|
293
|
-
if (a.eventSeq !== undefined && b.eventSeq !== undefined) {
|
|
294
|
-
return a.eventSeq - b.eventSeq;
|
|
295
|
-
}
|
|
296
|
-
return a.createdAt - b.createdAt;
|
|
297
|
-
});
|
|
160
|
+
group.tools.sort((a, b) => a.createdAt - b.createdAt);
|
|
298
161
|
group.live =
|
|
299
162
|
input.streaming &&
|
|
300
163
|
input.activeAssistantId !== null &&
|
|
301
164
|
group.assistants.some((a) => a.id === input.activeAssistantId);
|
|
302
|
-
fillProcessDetails(group);
|
|
303
165
|
}
|
|
304
166
|
return groups;
|
|
305
167
|
}
|
|
306
|
-
export function processGroupSummary(tools
|
|
168
|
+
export function processGroupSummary(tools) {
|
|
307
169
|
const count = tools.length;
|
|
308
|
-
const
|
|
309
|
-
const
|
|
310
|
-
const
|
|
311
|
-
const stepCount = thinkingCount + textCount + activityCount + count;
|
|
312
|
-
const errorCount = tools.filter((t) => t.status === "error").length +
|
|
313
|
-
(options?.activityErrorCount ?? 0);
|
|
314
|
-
const runningCount = tools.filter((t) => t.status === "running").length +
|
|
315
|
-
(options?.activityRunningCount ?? 0);
|
|
316
|
-
const parts = [`处理详情 · ${stepCount} 步`];
|
|
317
|
-
if (count > 0)
|
|
318
|
-
parts.push(`${count} 工具`);
|
|
319
|
-
if (activityCount > 0)
|
|
320
|
-
parts.push(`${activityCount} 进展`);
|
|
170
|
+
const errorCount = tools.filter((t) => t.status === "error").length;
|
|
171
|
+
const runningCount = tools.filter((t) => t.status === "running").length;
|
|
172
|
+
const parts = [`执行过程 · ${count} 工具`];
|
|
321
173
|
if (errorCount > 0)
|
|
322
174
|
parts.push(`${errorCount} 出错`);
|
|
323
175
|
else if (runningCount > 0)
|
|
324
176
|
parts.push("运行中");
|
|
325
|
-
return { count, errorCount, runningCount,
|
|
326
|
-
}
|
|
327
|
-
/** Summary over rendered process items (tool-group tools count individually). */
|
|
328
|
-
export function summarizeProcessItems(items) {
|
|
329
|
-
const tools = [];
|
|
330
|
-
let thinkingCount = 0;
|
|
331
|
-
let textCount = 0;
|
|
332
|
-
let activityCount = 0;
|
|
333
|
-
let activityErrorCount = 0;
|
|
334
|
-
let activityRunningCount = 0;
|
|
335
|
-
for (const item of items) {
|
|
336
|
-
if (item.kind === "thinking")
|
|
337
|
-
thinkingCount += 1;
|
|
338
|
-
else if (item.kind === "text")
|
|
339
|
-
textCount += 1;
|
|
340
|
-
else if (item.kind === "activity") {
|
|
341
|
-
activityCount += 1;
|
|
342
|
-
if (item.activity.kind === "error")
|
|
343
|
-
activityErrorCount += 1;
|
|
344
|
-
else if (item.activity.kind === "progress")
|
|
345
|
-
activityRunningCount += 1;
|
|
346
|
-
}
|
|
347
|
-
else if (item.kind === "tool")
|
|
348
|
-
tools.push(item.tool);
|
|
349
|
-
else
|
|
350
|
-
tools.push(...item.tools);
|
|
351
|
-
}
|
|
352
|
-
return processGroupSummary(tools, {
|
|
353
|
-
thinkingCount,
|
|
354
|
-
textCount,
|
|
355
|
-
activityCount,
|
|
356
|
-
activityErrorCount,
|
|
357
|
-
activityRunningCount,
|
|
358
|
-
});
|
|
177
|
+
return { count, errorCount, runningCount, label: parts.join(" · ") };
|
|
359
178
|
}
|
|
@@ -27,7 +27,6 @@ function readWorkspaceParams(query) {
|
|
|
27
27
|
...(read("dagRunId") ? { dagRunId: read("dagRunId") } : {}),
|
|
28
28
|
...(read("fromNodeId") ? { fromNodeId: read("fromNodeId") } : {}),
|
|
29
29
|
...(read("featureId") ? { featureId: read("featureId") } : {}),
|
|
30
|
-
...(read("intent") ? { intent: read("intent") } : {}),
|
|
31
30
|
};
|
|
32
31
|
}
|
|
33
32
|
/** Parse `#/tasks?taskId=…` hash routes (Observe-style virtual paths). */
|
|
@@ -79,7 +78,7 @@ export function resolveDefaultLanding(input) {
|
|
|
79
78
|
return (workspaceFromLocation(input.location) ??
|
|
80
79
|
configuredLanding(input.storage?.getItem(DEFAULT_LANDING_STORAGE_KEY)) ??
|
|
81
80
|
configuredLanding(input.env) ??
|
|
82
|
-
"
|
|
81
|
+
"tasks");
|
|
83
82
|
}
|
|
84
83
|
/** Hash path segment for Console virtual routing (no leading `#`). */
|
|
85
84
|
export function buildConsoleHashPath(workspace, params = {}) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { clipPreview, } from "./operation-store.js";
|
|
2
2
|
import { stateEvent } from "./operation-sse.js";
|
|
3
|
-
import { extractTrustedRunFacts } from "./operation-run-facts.js";
|
|
4
3
|
/**
|
|
5
4
|
* Execute a queued operation via LoopAgentClient (or inject).
|
|
6
5
|
* Timeout without proven exit → needs-reconcile (never Cancel).
|
|
@@ -75,30 +74,6 @@ export async function runOperation(operationId, deps) {
|
|
|
75
74
|
message: chunk,
|
|
76
75
|
});
|
|
77
76
|
},
|
|
78
|
-
onHeartbeat: (info) => {
|
|
79
|
-
// P1 (2026-08-13): project sibling CLI heartbeats into canonical
|
|
80
|
-
// operation events so operationWait/operationEventSummary can consume
|
|
81
|
-
// them. The projection is best-effort: a failed append must never
|
|
82
|
-
// terminate the sibling CLI execution (AC-002). The injected test
|
|
83
|
-
// runCommand path has no LoopAgentClient callback guard, so the
|
|
84
|
-
// runner protects itself here.
|
|
85
|
-
try {
|
|
86
|
-
deps.events.append(operationId, {
|
|
87
|
-
at: info.at,
|
|
88
|
-
kind: "heartbeat",
|
|
89
|
-
message: `heartbeat elapsedMs=${info.elapsedMs}`,
|
|
90
|
-
data: {
|
|
91
|
-
elapsedMs: info.elapsedMs,
|
|
92
|
-
action: op.action,
|
|
93
|
-
...(op.taskId ? { taskId: op.taskId } : {}),
|
|
94
|
-
...(op.dagRunId ? { dagRunId: op.dagRunId } : {}),
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
// Heartbeat is derived telemetry; ignore projection failures.
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
77
|
});
|
|
103
78
|
await spawnUpdate;
|
|
104
79
|
finished = await finalizeFromWorkerResult(operationId, result, deps);
|
|
@@ -122,38 +97,6 @@ export async function runOperation(operationId, deps) {
|
|
|
122
97
|
}
|
|
123
98
|
return finished;
|
|
124
99
|
}
|
|
125
|
-
function taskAdvanceBusinessBlock(operation, envelope) {
|
|
126
|
-
if (!operation || !envelope)
|
|
127
|
-
return undefined;
|
|
128
|
-
if (operation.action !== "taskAdvance" && operation.action !== "dagRunTask") {
|
|
129
|
-
return undefined;
|
|
130
|
-
}
|
|
131
|
-
const result = envelope.result && typeof envelope.result === "object"
|
|
132
|
-
? envelope.result
|
|
133
|
-
: undefined;
|
|
134
|
-
const lifecycleState = typeof result?.lifecycleState === "string"
|
|
135
|
-
? result.lifecycleState
|
|
136
|
-
: undefined;
|
|
137
|
-
const blockers = Array.isArray(result?.blockers)
|
|
138
|
-
? result.blockers
|
|
139
|
-
: [];
|
|
140
|
-
const expectedGateStop = lifecycleState === "awaiting-write-set-approval" && blockers.length === 0;
|
|
141
|
-
if (expectedGateStop)
|
|
142
|
-
return undefined;
|
|
143
|
-
if (lifecycleState !== "intake-incomplete" &&
|
|
144
|
-
blockers.length === 0 &&
|
|
145
|
-
envelope.outcome !== "blocked") {
|
|
146
|
-
return undefined;
|
|
147
|
-
}
|
|
148
|
-
const firstBlocker = blockers[0];
|
|
149
|
-
const code = typeof firstBlocker?.code === "string"
|
|
150
|
-
? firstBlocker.code
|
|
151
|
-
: "TASK_ADVANCE_BLOCKED";
|
|
152
|
-
const message = typeof firstBlocker?.message === "string"
|
|
153
|
-
? firstBlocker.message
|
|
154
|
-
: `task advance stopped before runnable DAG completion (lifecycleState=${lifecycleState ?? "unknown"})`;
|
|
155
|
-
return { code, message };
|
|
156
|
-
}
|
|
157
100
|
async function finalizeFromWorkerResult(operationId, result, deps) {
|
|
158
101
|
const previewStdout = clipPreview(result.stdout ?? "");
|
|
159
102
|
const previewStderr = clipPreview(result.stderr ?? "");
|
|
@@ -163,8 +106,6 @@ async function finalizeFromWorkerResult(operationId, result, deps) {
|
|
|
163
106
|
resultPath: result.artifacts.resultPath,
|
|
164
107
|
dir: result.artifacts.dir,
|
|
165
108
|
};
|
|
166
|
-
// Trusted structured run facts only (AC-003): never scanned from free text.
|
|
167
|
-
const runFacts = extractTrustedRunFacts(result);
|
|
168
109
|
if (result.timedOut) {
|
|
169
110
|
// Supervisory timeout without classified CLI timeout → needs-reconcile
|
|
170
111
|
// Only mark timed-out when we have a machine timeout result after exit.
|
|
@@ -180,35 +121,12 @@ async function finalizeFromWorkerResult(operationId, result, deps) {
|
|
|
180
121
|
? "CLI timed out with process exit"
|
|
181
122
|
: "timeout reached without proven process exit; needs reconcile (not cancelled)",
|
|
182
123
|
result: coerceEnvelope(result),
|
|
183
|
-
...(runFacts
|
|
184
|
-
? {
|
|
185
|
-
dagRunId: runFacts.runId,
|
|
186
|
-
...(runFacts.runSummary ? { runSummary: runFacts.runSummary } : {}),
|
|
187
|
-
}
|
|
188
|
-
: {}),
|
|
189
124
|
});
|
|
190
125
|
stateEvent(deps.events, updated, state, updated.errorMessage);
|
|
191
|
-
if (runFacts) {
|
|
192
|
-
appendSafeResultEvent(deps, operationId, state, {
|
|
193
|
-
ok: false,
|
|
194
|
-
exitCode: result.exitCode,
|
|
195
|
-
}, runFacts);
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
deps.events.append(operationId, {
|
|
199
|
-
at: new Date().toISOString(),
|
|
200
|
-
kind: "result",
|
|
201
|
-
state,
|
|
202
|
-
data: { ok: false, exitCode: result.exitCode },
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
126
|
return updated;
|
|
206
127
|
}
|
|
207
128
|
const envelope = coerceEnvelope(result);
|
|
208
|
-
const
|
|
209
|
-
const businessBlock = taskAdvanceBusinessBlock(operation, envelope);
|
|
210
|
-
const processOk = envelope?.ok === true || (result.ok && result.exitCode === 0);
|
|
211
|
-
const ok = processOk && !businessBlock;
|
|
129
|
+
const ok = envelope?.ok === true || (result.ok && result.exitCode === 0);
|
|
212
130
|
const state = ok ? "succeeded" : "failed";
|
|
213
131
|
const updated = await deps.store.update(operationId, {
|
|
214
132
|
state,
|
|
@@ -220,43 +138,21 @@ async function finalizeFromWorkerResult(operationId, result, deps) {
|
|
|
220
138
|
stdout: previewStdout,
|
|
221
139
|
stderr: previewStderr,
|
|
222
140
|
}),
|
|
223
|
-
errorCode: ok
|
|
224
|
-
? undefined
|
|
225
|
-
: (businessBlock?.code ?? envelope?.error?.code ?? "INTERNAL_ERROR"),
|
|
141
|
+
errorCode: ok ? undefined : (envelope?.error?.code ?? "INTERNAL_ERROR"),
|
|
226
142
|
errorMessage: ok
|
|
227
143
|
? undefined
|
|
228
|
-
: (
|
|
229
|
-
envelope?.error?.message ??
|
|
144
|
+
: (envelope?.error?.message ??
|
|
230
145
|
(previewStderr.trim() ||
|
|
231
146
|
`command failed with exit ${result.exitCode}`)),
|
|
232
|
-
// Atomic final patch: state/result + trusted dagRunId + safe runSummary
|
|
233
|
-
// in a single per-op write-locked update (AC-004). The runSummary is a
|
|
234
|
-
// projection and never launders the envelope/exit-decided state.
|
|
235
|
-
...(runFacts
|
|
236
|
-
? {
|
|
237
|
-
dagRunId: runFacts.runId,
|
|
238
|
-
...(runFacts.runSummary ? { runSummary: runFacts.runSummary } : {}),
|
|
239
|
-
}
|
|
240
|
-
: {}),
|
|
241
147
|
});
|
|
242
|
-
stateEvent(deps.events, updated, state
|
|
243
|
-
appendSafeResultEvent(deps, operationId, state, { ok, exitCode: result.exitCode }, runFacts);
|
|
244
|
-
if (ok && operation?.action === "dagRequestInterrupt") {
|
|
245
|
-
await projectInterruptRequestOntoTarget(operation, result.json, deps);
|
|
246
|
-
}
|
|
247
|
-
return updated;
|
|
248
|
-
}
|
|
249
|
-
/** Result event carries the same safe summary persisted on the record. */
|
|
250
|
-
function appendSafeResultEvent(deps, operationId, state, base, runFacts) {
|
|
148
|
+
stateEvent(deps.events, updated, state);
|
|
251
149
|
deps.events.append(operationId, {
|
|
252
150
|
at: new Date().toISOString(),
|
|
253
151
|
kind: "result",
|
|
254
152
|
state,
|
|
255
|
-
data: {
|
|
256
|
-
...base,
|
|
257
|
-
...(runFacts?.runSummary ? { runSummary: runFacts.runSummary } : {}),
|
|
258
|
-
},
|
|
153
|
+
data: { ok, exitCode: result.exitCode },
|
|
259
154
|
});
|
|
155
|
+
return updated;
|
|
260
156
|
}
|
|
261
157
|
function coerceEnvelope(result) {
|
|
262
158
|
const json = result.json;
|
|
@@ -306,48 +202,3 @@ export function scheduleOperation(operationId, deps) {
|
|
|
306
202
|
});
|
|
307
203
|
});
|
|
308
204
|
}
|
|
309
|
-
async function projectInterruptRequestOntoTarget(operation, json, deps) {
|
|
310
|
-
const params = operation.actionParams ?? {};
|
|
311
|
-
const targetOperationId = typeof params.targetOperationId === "string"
|
|
312
|
-
? params.targetOperationId.trim()
|
|
313
|
-
: "";
|
|
314
|
-
const runId = typeof params.runId === "string" ? params.runId.trim() : "";
|
|
315
|
-
if (!targetOperationId)
|
|
316
|
-
return;
|
|
317
|
-
const rec = json && typeof json === "object" ? json : {};
|
|
318
|
-
const request = rec.request && typeof rec.request === "object"
|
|
319
|
-
? rec.request
|
|
320
|
-
: rec;
|
|
321
|
-
const status = (typeof rec.interruptStatus === "string" && rec.interruptStatus) ||
|
|
322
|
-
(typeof request.status === "string" && request.status) ||
|
|
323
|
-
"requested";
|
|
324
|
-
if (status !== "requested" &&
|
|
325
|
-
status !== "acknowledged" &&
|
|
326
|
-
status !== "settled" &&
|
|
327
|
-
status !== "needs-reconcile") {
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
const target = await deps.store.get(targetOperationId);
|
|
331
|
-
if (!target)
|
|
332
|
-
return;
|
|
333
|
-
await deps.store.update(targetOperationId, {
|
|
334
|
-
...(runId && !target.dagRunId ? { dagRunId: runId } : {}),
|
|
335
|
-
interrupt: {
|
|
336
|
-
status,
|
|
337
|
-
...(typeof request.requestId === "string"
|
|
338
|
-
? { requestId: request.requestId }
|
|
339
|
-
: {}),
|
|
340
|
-
...(typeof request.reasonCode === "string"
|
|
341
|
-
? { reasonCode: request.reasonCode }
|
|
342
|
-
: {}),
|
|
343
|
-
...(typeof request.requestedAt === "string"
|
|
344
|
-
? { requestedAt: request.requestedAt }
|
|
345
|
-
: {}),
|
|
346
|
-
},
|
|
347
|
-
});
|
|
348
|
-
deps.events.append(targetOperationId, {
|
|
349
|
-
kind: "state",
|
|
350
|
-
message: `interrupt ${status}`,
|
|
351
|
-
data: { interruptStatus: status, ...(runId ? { runId } : {}) },
|
|
352
|
-
});
|
|
353
|
-
}
|