@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.
Files changed (56) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/executors/model-routing.js +14 -4
  3. package/dist/governance/manifest-types.js +34 -7
  4. package/dist/worker/console/chat/model-resolver.js +114 -34
  5. package/dist/worker/console/chat/workspace-landing.js +58 -22
  6. package/dist/worker/console/doctor.js +1 -0
  7. package/dist/worker/console/night-aux-ticker.js +5 -0
  8. package/dist/worker/console/pi-readiness.js +26 -17
  9. package/dist/worker/console/server.js +2 -0
  10. package/dist/worker/console/static/assets/index-3R-GT3a_.js +29 -0
  11. package/dist/worker/console/static/assets/index-B0EQt_yq.css +1 -0
  12. package/dist/worker/console/static/index.html +2 -2
  13. package/dist/worker/console/static-src/app/console-types.js +140 -0
  14. package/dist/worker/console/static-src/app/useConsoleShell.js +101 -0
  15. package/dist/worker/console/static-src/app/useOperatorActions.js +304 -0
  16. package/dist/worker/console/static-src/app/usePrdImport.js +171 -0
  17. package/dist/worker/console/static-src/app/useRecoveryActions.js +257 -0
  18. package/dist/worker/console/static-src/app/useRecoveryConsole.js +334 -0
  19. package/dist/worker/console/static-src/app/useTaskWizard.js +229 -0
  20. package/dist/worker/console/static-src/chat-view-types.js +2 -0
  21. package/dist/worker/console/static-src/night/night-types.js +24 -0
  22. package/dist/worker/console/static-src/night/useNightBoard.js +94 -0
  23. package/dist/worker/console/static-src/night/useNightWizard.js +171 -0
  24. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +253 -0
  25. package/dist/worker/console/static-src/operator-chat/format.js +71 -0
  26. package/dist/worker/console/static-src/operator-chat/refs.js +47 -0
  27. package/dist/worker/console/static-src/operator-chat/tools-catalog.js +77 -0
  28. package/dist/worker/console/static-src/operator-chat/types.js +1 -0
  29. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +320 -0
  30. package/dist/worker/console/static-src/operator-chat/useChatStream.js +209 -0
  31. package/dist/worker/console/static-src/operator-chat/useChatThread.js +218 -0
  32. package/dist/worker/console/static-src/operator-chat/useComposer.js +125 -0
  33. package/dist/worker/console/static-src/operator-chat/useInterview.js +108 -0
  34. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +123 -0
  35. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +207 -0
  36. package/dist/worker/observability/read-model.js +106 -0
  37. package/dist/worker/observe/routes.js +19 -1
  38. package/dist/worker/observe/static/api.js +42 -3
  39. package/dist/worker/observe/static/app.js +4 -0
  40. package/dist/worker/observe/static/constants.js +10 -0
  41. package/dist/worker/observe/static/custom-select.js +567 -0
  42. package/dist/worker/observe/static/index.html +47 -6
  43. package/dist/worker/observe/static/router.js +54 -1
  44. package/dist/worker/observe/static/state.js +20 -0
  45. package/dist/worker/observe/static/styles.css +618 -30
  46. package/dist/worker/observe/static/views/dag-inspector.js +20 -17
  47. package/dist/worker/observe/static/views/dag.js +136 -59
  48. package/dist/worker/observe/static/views/dags.js +877 -0
  49. package/dist/worker/observe/static/views/dashboard.js +15 -4
  50. package/dist/workflows/dag/init-hybrid.js +58 -29
  51. package/docs/templates/agent-worker-production-readiness-checklist.md +26 -24
  52. package/docs/templates/harness.schema.json +5 -0
  53. package/harness.json +4 -4
  54. package/package.json +5 -4
  55. package/dist/worker/console/static/assets/index-D9gnJn_l.js +0 -29
  56. 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 = document.createElement("select");
1193
- select.className = "dag-inspector-attempt-select";
1194
- select.setAttribute("aria-label", "选择执行分次");
1195
- for (const option of options) {
1196
- const optionEl = document.createElement("option");
1197
- optionEl.value = option.key;
1198
- optionEl.textContent = option.label;
1199
- select.appendChild(optionEl);
1200
- }
1201
- select.value = selectedKey;
1202
- select.addEventListener("change", () => {
1203
- uiState.dagInspectorAttempt = {
1204
- dagRunId,
1205
- nodeId: node.nodeId,
1206
- key: select.value,
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
- const sortedNodes = [...nodes].sort((a, b) => {
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