@tea-agent/loop-agent 0.32.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/dist/executors/model-routing.js +14 -4
- package/dist/governance/manifest-types.js +34 -7
- package/dist/worker/console/chat/model-resolver.js +114 -34
- package/dist/worker/console/chat/workspace-landing.js +58 -22
- package/dist/worker/console/doctor.js +1 -0
- package/dist/worker/console/night-aux-ticker.js +5 -0
- package/dist/worker/console/pi-readiness.js +26 -17
- package/dist/worker/console/server.js +2 -0
- package/dist/worker/console/static/assets/index-3R-GT3a_.js +29 -0
- package/dist/worker/console/static/assets/index-B0EQt_yq.css +1 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +140 -0
- package/dist/worker/console/static-src/app/useConsoleShell.js +101 -0
- package/dist/worker/console/static-src/app/useOperatorActions.js +304 -0
- package/dist/worker/console/static-src/app/usePrdImport.js +171 -0
- package/dist/worker/console/static-src/app/useRecoveryActions.js +257 -0
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +334 -0
- package/dist/worker/console/static-src/app/useTaskWizard.js +229 -0
- package/dist/worker/console/static-src/chat-view-types.js +2 -0
- package/dist/worker/console/static-src/night/night-types.js +24 -0
- package/dist/worker/console/static-src/night/useNightBoard.js +94 -0
- package/dist/worker/console/static-src/night/useNightWizard.js +171 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +253 -0
- package/dist/worker/console/static-src/operator-chat/format.js +71 -0
- package/dist/worker/console/static-src/operator-chat/refs.js +47 -0
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +77 -0
- package/dist/worker/console/static-src/operator-chat/types.js +1 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +320 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +209 -0
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +218 -0
- package/dist/worker/console/static-src/operator-chat/useComposer.js +125 -0
- package/dist/worker/console/static-src/operator-chat/useInterview.js +108 -0
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +123 -0
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +207 -0
- package/dist/worker/observability/read-model.js +106 -0
- package/dist/worker/observe/routes.js +19 -1
- package/dist/worker/observe/static/api.js +42 -3
- package/dist/worker/observe/static/app.js +4 -0
- package/dist/worker/observe/static/constants.js +10 -0
- package/dist/worker/observe/static/custom-select.js +567 -0
- package/dist/worker/observe/static/index.html +47 -6
- package/dist/worker/observe/static/router.js +54 -1
- package/dist/worker/observe/static/state.js +20 -0
- package/dist/worker/observe/static/styles.css +618 -30
- package/dist/worker/observe/static/views/dag-inspector.js +20 -17
- package/dist/worker/observe/static/views/dag.js +136 -59
- package/dist/worker/observe/static/views/dags.js +877 -0
- package/dist/worker/observe/static/views/dashboard.js +15 -4
- package/dist/workflows/dag/init-hybrid.js +58 -29
- package/docs/templates/agent-worker-production-readiness-checklist.md +26 -24
- package/docs/templates/harness.schema.json +5 -0
- package/harness.json +4 -4
- package/package.json +5 -4
- package/dist/worker/console/static/assets/index-D9gnJn_l.js +0 -29
- package/dist/worker/console/static/assets/index-rajoXwkM.css +0 -1
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
formatSessionEventTime,
|
|
37
37
|
renderSessionTimeline,
|
|
38
38
|
} from "./session-timeline.js";
|
|
39
|
+
import { createCustomSelect } from "../custom-select.js";
|
|
39
40
|
|
|
40
41
|
// Current inspector selection. Tab handlers reference this mutable slot so a
|
|
41
42
|
// reused inspector shell never keeps stale node closures: every render writes
|
|
@@ -1189,24 +1190,26 @@ function buildAttemptBar(dagRunId, node) {
|
|
|
1189
1190
|
|
|
1190
1191
|
const bar = el("div", "dag-inspector-attempt-bar");
|
|
1191
1192
|
bar.appendChild(el("label", "dag-inspector-attempt-label", "执行分次"));
|
|
1192
|
-
const select =
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
}
|
|
1208
|
-
renderDagInspectorForCurrent();
|
|
1193
|
+
const select = createCustomSelect({
|
|
1194
|
+
ariaLabel: "选择执行分次",
|
|
1195
|
+
className: "dag-inspector-attempt-select",
|
|
1196
|
+
value: selectedKey,
|
|
1197
|
+
options: options.map((option) => ({
|
|
1198
|
+
value: option.key,
|
|
1199
|
+
label: option.label,
|
|
1200
|
+
})),
|
|
1201
|
+
onChange: (nextKey) => {
|
|
1202
|
+
uiState.dagInspectorAttempt = {
|
|
1203
|
+
dagRunId,
|
|
1204
|
+
nodeId: node.nodeId,
|
|
1205
|
+
key: String(nextKey),
|
|
1206
|
+
};
|
|
1207
|
+
renderDagInspectorForCurrent();
|
|
1208
|
+
},
|
|
1209
1209
|
});
|
|
1210
|
+
// Keep raw key on the root for existing integration assertions that read
|
|
1211
|
+
// `.value` after assign + change (StubNode handler contract).
|
|
1212
|
+
select.value = selectedKey;
|
|
1210
1213
|
bar.appendChild(select);
|
|
1211
1214
|
|
|
1212
1215
|
const selected =
|
|
@@ -93,6 +93,141 @@ import {
|
|
|
93
93
|
} from "./dag-inspector.js";
|
|
94
94
|
import { mergeSessionEvents } from "./session-timeline.js";
|
|
95
95
|
|
|
96
|
+
const ANSI_ESCAPE_RE =
|
|
97
|
+
// CSI + OSC sequences commonly present in shell/test error previews.
|
|
98
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: intentional ANSI strip
|
|
99
|
+
/\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)/g;
|
|
100
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: intentional control-char strip
|
|
101
|
+
const CONTROL_CHARS_RE = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g;
|
|
102
|
+
const ERROR_NAME_LINE_RE =
|
|
103
|
+
/(?:^|\b)(?:Error|AssertionError|TypeError|ReferenceError|SyntaxError|RangeError)\b/;
|
|
104
|
+
const FAIL_LINE_RE = /\bFAIL\b/;
|
|
105
|
+
const SEPARATOR_LINE_RE = /^[\s\-_=*~#.]+$/;
|
|
106
|
+
// Shell prompts and common package/test runners only — not result lines like
|
|
107
|
+
// "node exited 1" or real runner errors such as "npm ERR! code 1".
|
|
108
|
+
const COMMAND_LINE_RE =
|
|
109
|
+
/^(?:[$>#%]\s+|\s*(?:(?:npm|pnpm|yarn|npx)(?!\s+ERR\b)|vitest|tsc|eslint)\b)/i;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Extract a short first-error summary from a raw errorPreview for the DAG
|
|
113
|
+
* node table 备注 column. Never returns the full multi-line dump.
|
|
114
|
+
*/
|
|
115
|
+
export function extractFirstErrorSummary(errorPreview) {
|
|
116
|
+
if (typeof errorPreview !== "string" || !errorPreview) return "";
|
|
117
|
+
const cleaned = errorPreview
|
|
118
|
+
.replace(ANSI_ESCAPE_RE, "")
|
|
119
|
+
.replace(CONTROL_CHARS_RE, "");
|
|
120
|
+
const lines = cleaned
|
|
121
|
+
.split(/\r?\n/)
|
|
122
|
+
.map((line) => line.replace(/\s+/g, " ").trim())
|
|
123
|
+
.filter((line) => line.length > 0);
|
|
124
|
+
if (lines.length === 0) return "";
|
|
125
|
+
|
|
126
|
+
const errorLine = lines.find((line) => ERROR_NAME_LINE_RE.test(line));
|
|
127
|
+
if (errorLine) return errorLine;
|
|
128
|
+
|
|
129
|
+
const failLine = lines.find((line) => FAIL_LINE_RE.test(line));
|
|
130
|
+
if (failLine) return failLine;
|
|
131
|
+
|
|
132
|
+
const effective = lines.find(
|
|
133
|
+
(line) => !SEPARATOR_LINE_RE.test(line) && !COMMAND_LINE_RE.test(line),
|
|
134
|
+
);
|
|
135
|
+
return effective ?? "";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Resolve 备注 cell content for a DAG node (failed → summary; else label). */
|
|
139
|
+
export function resolveNodeRemark(node) {
|
|
140
|
+
const failed = isNodeFailed(node?.status);
|
|
141
|
+
if (failed) {
|
|
142
|
+
const summary = extractFirstErrorSummary(node?.errorPreview);
|
|
143
|
+
return {
|
|
144
|
+
failed: true,
|
|
145
|
+
text: summary || "—",
|
|
146
|
+
isErrorSummary: Boolean(summary),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const label = typeof node?.label === "string" ? node.label.trim() : "";
|
|
150
|
+
return {
|
|
151
|
+
failed: false,
|
|
152
|
+
text: label || "—",
|
|
153
|
+
isErrorSummary: false,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Sole production constructor for the DAG node table (sort, rows, buildTable,
|
|
159
|
+
* remark cells, title tooltips, row click → selectDagNode).
|
|
160
|
+
* Tests must call this (or renderDagDetail) instead of hand-building DOM.
|
|
161
|
+
*/
|
|
162
|
+
export function buildDagNodeTable(dagRunId, nodes) {
|
|
163
|
+
const sortedNodes = [...nodes].sort((a, b) => {
|
|
164
|
+
const ra = Number(a.rank);
|
|
165
|
+
const rb = Number(b.rank);
|
|
166
|
+
if (!Number.isNaN(ra) && !Number.isNaN(rb) && ra !== rb) return ra - rb;
|
|
167
|
+
if (a.rank !== b.rank) return (a.rank ?? "").localeCompare(b.rank ?? "");
|
|
168
|
+
return a.nodeId.localeCompare(b.nodeId);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const rows = sortedNodes.map((node, index) => {
|
|
172
|
+
const active = isNodeActive(node.status);
|
|
173
|
+
const failed = isNodeFailed(node.status);
|
|
174
|
+
let className = "";
|
|
175
|
+
if (active) className = "dag-active-node";
|
|
176
|
+
if (failed) className = "dag-error-node";
|
|
177
|
+
if (node.nodeId === uiState.selectedDagNodeId) {
|
|
178
|
+
className = className
|
|
179
|
+
? `${className} dag-node-selected`
|
|
180
|
+
: "dag-node-selected";
|
|
181
|
+
}
|
|
182
|
+
const remark = resolveNodeRemark(node);
|
|
183
|
+
const remarkCell = remark.isErrorSummary
|
|
184
|
+
? el("span", "dag-node-error-summary", remark.text)
|
|
185
|
+
: remark.text;
|
|
186
|
+
return {
|
|
187
|
+
className,
|
|
188
|
+
onClick: () => selectDagNode(dagRunId, node.nodeId),
|
|
189
|
+
cells: [
|
|
190
|
+
index + 1,
|
|
191
|
+
node.nodeId,
|
|
192
|
+
node.rank ?? "—",
|
|
193
|
+
node.executor ?? "—",
|
|
194
|
+
node.model ?? "—",
|
|
195
|
+
node.status ? badge(node.status) : "—",
|
|
196
|
+
formatMs(node.durationMs),
|
|
197
|
+
(typeof node.executionSummary?.historyLabel === "string" &&
|
|
198
|
+
node.executionSummary.historyLabel.trim()) ||
|
|
199
|
+
"—",
|
|
200
|
+
remarkCell,
|
|
201
|
+
],
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
const nodeTable = buildTable(
|
|
205
|
+
[
|
|
206
|
+
"序号",
|
|
207
|
+
"节点 ID",
|
|
208
|
+
"依赖层",
|
|
209
|
+
"执行方式",
|
|
210
|
+
"模型",
|
|
211
|
+
"状态",
|
|
212
|
+
"耗时",
|
|
213
|
+
"执行历史",
|
|
214
|
+
"备注",
|
|
215
|
+
],
|
|
216
|
+
rows,
|
|
217
|
+
);
|
|
218
|
+
nodeTable.classList.add("dag-node-table");
|
|
219
|
+
for (const row of nodeTable.tBodies[0]?.rows ?? []) {
|
|
220
|
+
// Title tooltips for long ID/model/remark text only; remark never
|
|
221
|
+
// carries full errorPreview (summary or — only).
|
|
222
|
+
for (const columnIndex of [1, 4, 8]) {
|
|
223
|
+
const cell = row.cells[columnIndex];
|
|
224
|
+
const text = cell?.textContent ?? "";
|
|
225
|
+
if (text) cell.title = text;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return nodeTable;
|
|
229
|
+
}
|
|
230
|
+
|
|
96
231
|
export async function renderDagDetail(dagRunId, initial = true) {
|
|
97
232
|
showView("dag");
|
|
98
233
|
setBreadcrumb([
|
|
@@ -299,65 +434,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
299
434
|
if (nodes.length === 0) {
|
|
300
435
|
mountViewState(nodesEl, "empty", UI_TEXT.noNodes);
|
|
301
436
|
} else {
|
|
302
|
-
|
|
303
|
-
const ra = Number(a.rank);
|
|
304
|
-
const rb = Number(b.rank);
|
|
305
|
-
if (!Number.isNaN(ra) && !Number.isNaN(rb) && ra !== rb) return ra - rb;
|
|
306
|
-
if (a.rank !== b.rank) return (a.rank ?? "").localeCompare(b.rank ?? "");
|
|
307
|
-
return a.nodeId.localeCompare(b.nodeId);
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
const rows = sortedNodes.map((node, index) => {
|
|
311
|
-
const active = isNodeActive(node.status);
|
|
312
|
-
const failed = isNodeFailed(node.status);
|
|
313
|
-
let className = "";
|
|
314
|
-
if (active) className = "dag-active-node";
|
|
315
|
-
if (failed) className = "dag-error-node";
|
|
316
|
-
if (node.nodeId === uiState.selectedDagNodeId) {
|
|
317
|
-
className = className
|
|
318
|
-
? `${className} dag-node-selected`
|
|
319
|
-
: "dag-node-selected";
|
|
320
|
-
}
|
|
321
|
-
return {
|
|
322
|
-
className,
|
|
323
|
-
onClick: () => selectDagNode(dagRunId, node.nodeId),
|
|
324
|
-
cells: [
|
|
325
|
-
index + 1,
|
|
326
|
-
node.nodeId,
|
|
327
|
-
node.rank ?? "—",
|
|
328
|
-
node.executor ?? "—",
|
|
329
|
-
node.model ?? "—",
|
|
330
|
-
node.status ? badge(node.status) : "—",
|
|
331
|
-
formatMs(node.durationMs),
|
|
332
|
-
(typeof node.executionSummary?.historyLabel === "string" &&
|
|
333
|
-
node.executionSummary.historyLabel.trim()) ||
|
|
334
|
-
"—",
|
|
335
|
-
node.errorPreview ?? node.label ?? "—",
|
|
336
|
-
],
|
|
337
|
-
};
|
|
338
|
-
});
|
|
339
|
-
const nodeTable = buildTable(
|
|
340
|
-
[
|
|
341
|
-
"序号",
|
|
342
|
-
"节点 ID",
|
|
343
|
-
"依赖层",
|
|
344
|
-
"执行方式",
|
|
345
|
-
"模型",
|
|
346
|
-
"状态",
|
|
347
|
-
"耗时",
|
|
348
|
-
"执行历史",
|
|
349
|
-
"备注",
|
|
350
|
-
],
|
|
351
|
-
rows,
|
|
352
|
-
);
|
|
353
|
-
nodeTable.classList.add("dag-node-table");
|
|
354
|
-
for (const row of nodeTable.tBodies[0]?.rows ?? []) {
|
|
355
|
-
for (const columnIndex of [1, 4, 8]) {
|
|
356
|
-
const cell = row.cells[columnIndex];
|
|
357
|
-
if (cell?.textContent) cell.title = cell.textContent;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
nodesEl.appendChild(nodeTable);
|
|
437
|
+
nodesEl.appendChild(buildDagNodeTable(dagRunId, nodes));
|
|
361
438
|
}
|
|
362
439
|
nodesEl.scrollLeft = nodesScrollLeft;
|
|
363
440
|
|