@tea-agent/loop-agent 0.32.1 → 0.33.1

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 (40) hide show
  1. package/CHANGELOG.md +78 -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/operator-surface-health.js +1 -0
  9. package/dist/worker/console/pi-readiness.js +26 -17
  10. package/dist/worker/console/server.js +2 -0
  11. package/dist/worker/console/static/assets/index-CnUXAqxG.css +1 -0
  12. package/dist/worker/console/static/assets/index-PzYzcuFG.js +29 -0
  13. package/dist/worker/console/static/index.html +3 -2
  14. package/dist/worker/console/static-src/app/useRecoveryConsole.js +3 -2
  15. package/dist/worker/console/static-src/night/useNightBoard.js +0 -31
  16. package/dist/worker/observability/read-model.js +106 -0
  17. package/dist/worker/observe/routes.js +22 -9
  18. package/dist/worker/observe/static/api.js +42 -3
  19. package/dist/worker/observe/static/app.js +15 -0
  20. package/dist/worker/observe/static/constants.js +10 -0
  21. package/dist/worker/observe/static/custom-select.js +567 -0
  22. package/dist/worker/observe/static/index.html +56 -47
  23. package/dist/worker/observe/static/kpi.js +2 -24
  24. package/dist/worker/observe/static/operator-chrome.css +480 -0
  25. package/dist/worker/observe/static/operator-chrome.d.ts +82 -0
  26. package/dist/worker/observe/static/operator-chrome.js +554 -0
  27. package/dist/worker/observe/static/router.js +54 -1
  28. package/dist/worker/observe/static/shell-chrome.js +1 -11
  29. package/dist/worker/observe/static/state.js +20 -0
  30. package/dist/worker/observe/static/styles.css +680 -299
  31. package/dist/worker/observe/static/views/dag-inspector.js +20 -17
  32. package/dist/worker/observe/static/views/dag.js +136 -59
  33. package/dist/worker/observe/static/views/dags.js +877 -0
  34. package/dist/worker/observe/static/views/dashboard.js +67 -8
  35. package/dist/workflows/dag/init-hybrid.js +58 -29
  36. package/docs/templates/harness.schema.json +5 -0
  37. package/harness.json +4 -4
  38. package/package.json +1 -1
  39. package/dist/worker/console/static/assets/index-Bpa2qrc-.js +0 -29
  40. package/dist/worker/console/static/assets/index-BqfFDdnG.css +0 -1
@@ -1,7 +1,6 @@
1
- /** KPI cards, repo banner, projection fault, artifact/inbox links. */
1
+ /** KPI cards, projection fault, artifact/inbox links. */
2
2
  import { UI_TEXT } from "./constants.js";
3
- import { clearNode, el } from "./dom.js";
4
- import { formatRepoPathDisplay, repoBaseName } from "./format.js";
3
+ import { el } from "./dom.js";
5
4
  import { navigate } from "./router.js";
6
5
 
7
6
  export function failuresInboxLink(count) {
@@ -55,27 +54,6 @@ export function kpiCard(label, value, extraClass, helpText) {
55
54
  return card;
56
55
  }
57
56
 
58
- export function renderRepoBanner(repoEl, snapshot) {
59
- if (!repoEl) return;
60
- clearNode(repoEl);
61
- const repoRoot = snapshot.repoRoot ?? "";
62
- const iconWrap = el("span", "repo-banner-icon");
63
- const icon = el("i", "ri-git-repository-line");
64
- icon.setAttribute("aria-hidden", "true");
65
- iconWrap.appendChild(icon);
66
- repoEl.appendChild(iconWrap);
67
- const text = el("div", "repo-banner-text");
68
- text.appendChild(el("span", "repo-banner-name", repoBaseName(repoRoot)));
69
- const pathLine = el(
70
- "span",
71
- "repo-banner-path",
72
- formatRepoPathDisplay(repoRoot),
73
- );
74
- pathLine.title = repoRoot || "路径未知";
75
- text.appendChild(pathLine);
76
- repoEl.appendChild(text);
77
- }
78
-
79
57
  export function renderProjectionFault(error) {
80
58
  const banner = el("div", "projection-fault");
81
59
  banner.setAttribute("role", "alert");
@@ -0,0 +1,480 @@
1
+ /**
2
+ * Operator Chrome — 统一壳样式(Operate 与 Inspect 共用唯一来源)。
3
+ *
4
+ * 由统一 Console server 同时服务两侧:
5
+ * - Inspect(/inspect/)以相对路径引入本文件;
6
+ * - Operate(/)经 /inspect/operator-chrome.css 引入。
7
+ * 变量默认回退值与 observe/static/styles.css 及 console/static-src/styles.css
8
+ * 的暖白 token 保持一致;宿主样式表已定义同名变量时以宿主为准。
9
+ */
10
+
11
+ .och {
12
+ --och-canvas: var(--canvas, #f7f7f4);
13
+ --och-surface: var(--surface, #ffffff);
14
+ --och-surface-muted: var(--surface-muted, #f0efe9);
15
+ --och-ink: var(--ink, #26251e);
16
+ --och-body: var(--body, #5a5852);
17
+ --och-muted: var(--muted, #807d72);
18
+ --och-muted-soft: var(--muted-soft, #a09c92);
19
+ --och-hairline: var(--hairline, #e6e5e0);
20
+ --och-hairline-strong: var(--hairline-strong, #cfcdc4);
21
+ --och-orange: var(--orange, #f54e00);
22
+ --och-orange-active: var(--orange-active, #d04200);
23
+ --och-green: var(--green, #1f8a65);
24
+ --och-amber: var(--amber, #a66a00);
25
+ --och-red: var(--red, #c72d53);
26
+ --och-mint: var(--mint, #d8ead5);
27
+ --och-peach: var(--peach, #f1d8cb);
28
+ --och-mono: var(
29
+ --font-mono,
30
+ "JetBrains Mono",
31
+ ui-monospace,
32
+ SFMono-Regular,
33
+ Menlo,
34
+ Consolas,
35
+ monospace
36
+ );
37
+ --och-shell-max: var(--shell-max, 1680px);
38
+
39
+ position: sticky;
40
+ top: 0;
41
+ z-index: 50;
42
+ border-bottom: 1px solid var(--och-hairline);
43
+ background: color-mix(in srgb, var(--och-canvas) 88%, transparent);
44
+ backdrop-filter: blur(12px);
45
+ -webkit-backdrop-filter: blur(12px);
46
+ font-family: inherit;
47
+ }
48
+
49
+ .och-inner {
50
+ width: min(100% - 48px, var(--och-shell-max));
51
+ margin: 0 auto;
52
+ }
53
+
54
+ /* Band 1 — 全局带:品牌 / 仓库身份 / 模式 / 对象上下文 / 状态格 */
55
+ .och-global {
56
+ position: relative;
57
+ display: flex;
58
+ align-items: center;
59
+ gap: 12px;
60
+ height: 54px;
61
+ }
62
+
63
+ .och-brand {
64
+ display: inline-flex;
65
+ align-items: center;
66
+ gap: 8px;
67
+ font-size: 14px;
68
+ font-weight: 700;
69
+ letter-spacing: 0.01em;
70
+ color: var(--och-ink);
71
+ text-decoration: none;
72
+ white-space: nowrap;
73
+ }
74
+ .och-brand svg {
75
+ color: var(--och-orange);
76
+ flex-shrink: 0;
77
+ }
78
+ .och-brand:hover {
79
+ color: var(--och-orange-active);
80
+ }
81
+
82
+ .och-repo {
83
+ appearance: none;
84
+ display: inline-flex;
85
+ align-items: center;
86
+ gap: 7px;
87
+ min-height: 30px;
88
+ padding: 0 11px;
89
+ border: 1px solid var(--och-hairline);
90
+ border-radius: 999px;
91
+ background: var(--och-surface);
92
+ color: var(--och-body);
93
+ font-size: 12px;
94
+ font-family: inherit;
95
+ white-space: nowrap;
96
+ cursor: pointer;
97
+ transition:
98
+ border-color 0.15s ease,
99
+ color 0.15s ease;
100
+ }
101
+ .och-repo:hover {
102
+ border-color: var(--och-hairline-strong);
103
+ color: var(--och-ink);
104
+ }
105
+ .och-repo svg {
106
+ color: var(--och-muted);
107
+ }
108
+ .och-repo-fp {
109
+ font-family: var(--och-mono);
110
+ font-size: 11px;
111
+ color: var(--och-muted);
112
+ }
113
+
114
+ .och-pop {
115
+ position: absolute;
116
+ top: 50px;
117
+ left: 150px;
118
+ z-index: 60;
119
+ min-width: 300px;
120
+ padding: 14px 16px;
121
+ border: 1px solid var(--och-hairline);
122
+ border-radius: 14px;
123
+ background: var(--och-surface);
124
+ box-shadow: 0 8px 32px rgba(38, 37, 30, 0.14);
125
+ font-size: 12px;
126
+ color: var(--och-body);
127
+ }
128
+ .och-pop[hidden] {
129
+ display: none;
130
+ }
131
+ .och-pop-title {
132
+ font-size: 13px;
133
+ font-weight: 700;
134
+ color: var(--och-ink);
135
+ margin: 0 0 8px;
136
+ }
137
+ .och-pop-kv {
138
+ display: flex;
139
+ justify-content: space-between;
140
+ gap: 12px;
141
+ padding: 4px 0;
142
+ }
143
+ .och-pop-kv b {
144
+ font-weight: 500;
145
+ color: var(--och-muted);
146
+ }
147
+ .och-pop-kv span {
148
+ font-family: var(--och-mono);
149
+ font-size: 11.5px;
150
+ color: var(--och-ink);
151
+ text-align: right;
152
+ word-break: break-all;
153
+ }
154
+
155
+ /* 模式分段控件:全局唯一模式入口(重语法:ink 实底) */
156
+ .och-mode {
157
+ display: inline-flex;
158
+ padding: 3px;
159
+ border: 1px solid var(--och-hairline);
160
+ border-radius: 999px;
161
+ background: var(--och-surface-muted);
162
+ flex-shrink: 0;
163
+ }
164
+ .och-mode-btn {
165
+ display: inline-flex;
166
+ align-items: center;
167
+ justify-content: center;
168
+ gap: 6px;
169
+ min-width: 92px;
170
+ height: 30px;
171
+ padding: 0 14px;
172
+ border-radius: 999px;
173
+ font-size: 13px;
174
+ font-weight: 600;
175
+ color: var(--och-body);
176
+ text-decoration: none;
177
+ transition:
178
+ color 0.18s ease,
179
+ background-color 0.18s ease,
180
+ box-shadow 0.18s ease;
181
+ }
182
+ .och-mode-btn svg {
183
+ opacity: 0.85;
184
+ }
185
+ .och-mode-btn:hover {
186
+ color: var(--och-ink);
187
+ }
188
+ .och-mode-btn.och-on,
189
+ .och-mode-btn.och-on:hover {
190
+ background: var(--och-ink);
191
+ color: #fff;
192
+ box-shadow: 0 2px 8px rgba(38, 37, 30, 0.25);
193
+ }
194
+
195
+ /* 对象上下文 chip:跨模式选中连续性 + 显式返回路径 */
196
+ .och-context {
197
+ display: inline-flex;
198
+ align-items: center;
199
+ gap: 8px;
200
+ min-height: 30px;
201
+ padding: 0 6px 0 12px;
202
+ border: 1px solid
203
+ color-mix(in srgb, var(--och-orange) 38%, var(--och-hairline));
204
+ border-radius: 999px;
205
+ background: color-mix(in srgb, var(--och-peach) 42%, var(--och-surface));
206
+ font-size: 12px;
207
+ color: var(--och-ink);
208
+ white-space: nowrap;
209
+ }
210
+ .och-context[hidden] {
211
+ display: none;
212
+ }
213
+ .och-context-kind {
214
+ color: var(--och-orange-active);
215
+ font-weight: 700;
216
+ }
217
+ .och-context-id {
218
+ font-family: var(--och-mono);
219
+ font-size: 11px;
220
+ color: var(--och-body);
221
+ max-width: 260px;
222
+ overflow: hidden;
223
+ text-overflow: ellipsis;
224
+ }
225
+ .och-context-return {
226
+ display: inline-flex;
227
+ align-items: center;
228
+ gap: 4px;
229
+ height: 22px;
230
+ padding: 0 9px;
231
+ border-radius: 999px;
232
+ background: var(--och-ink);
233
+ color: #fff;
234
+ font-size: 11px;
235
+ font-weight: 600;
236
+ text-decoration: none;
237
+ }
238
+ .och-context-return:hover {
239
+ background: var(--och-orange-active);
240
+ }
241
+ .och-context-close {
242
+ appearance: none;
243
+ display: inline-flex;
244
+ align-items: center;
245
+ justify-content: center;
246
+ width: 20px;
247
+ height: 20px;
248
+ padding: 0;
249
+ border: none;
250
+ border-radius: 50%;
251
+ background: transparent;
252
+ color: var(--och-muted);
253
+ cursor: pointer;
254
+ }
255
+ .och-context-close:hover {
256
+ background: rgba(38, 37, 30, 0.08);
257
+ color: var(--och-ink);
258
+ }
259
+
260
+ .och-spacer {
261
+ flex: 1 1 auto;
262
+ }
263
+
264
+ /* 新鲜度格(Inspect 模式渲染;元素 id 保持 header-refresh 供轮询回写) */
265
+ .och-fresh {
266
+ display: inline-flex;
267
+ align-items: center;
268
+ gap: 7px;
269
+ min-height: 30px;
270
+ padding: 0 12px;
271
+ border: 1px solid var(--och-hairline);
272
+ border-radius: 999px;
273
+ background: var(--och-surface);
274
+ color: var(--och-muted);
275
+ font-size: 12px;
276
+ font-variant-numeric: tabular-nums;
277
+ white-space: nowrap;
278
+ }
279
+ .och-fresh-dot {
280
+ width: 7px;
281
+ height: 7px;
282
+ border-radius: 50%;
283
+ background: var(--och-green);
284
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--och-green) 18%, transparent);
285
+ flex-shrink: 0;
286
+ }
287
+
288
+ /* Pi readiness:三态语义色与现行 Console 一致 */
289
+ .och-pi {
290
+ display: inline-flex;
291
+ align-items: center;
292
+ gap: 7px;
293
+ min-height: 30px;
294
+ padding: 0 12px;
295
+ border-radius: 999px;
296
+ border: 1px solid var(--och-hairline);
297
+ background: var(--och-surface);
298
+ font-size: 12px;
299
+ color: var(--och-body);
300
+ text-decoration: none;
301
+ white-space: nowrap;
302
+ transition: border-color 0.15s ease;
303
+ }
304
+ .och-pi:hover {
305
+ border-color: var(--och-hairline-strong);
306
+ }
307
+ .och-pi-label {
308
+ font-family: var(--och-mono);
309
+ font-size: 11px;
310
+ font-weight: 700;
311
+ color: var(--och-ink);
312
+ }
313
+ .och-pi strong {
314
+ font-weight: 600;
315
+ }
316
+ .och-pi[data-state="ready"] {
317
+ border-color: color-mix(in srgb, var(--och-green) 34%, var(--och-hairline));
318
+ background: color-mix(in srgb, var(--och-mint) 46%, var(--och-surface));
319
+ }
320
+ .och-pi[data-state="ready"] strong {
321
+ color: var(--och-green);
322
+ }
323
+ .och-pi[data-state="degraded"] {
324
+ border-color: color-mix(in srgb, var(--och-amber) 38%, var(--och-hairline));
325
+ background: color-mix(in srgb, var(--och-peach) 40%, var(--och-surface));
326
+ }
327
+ .och-pi[data-state="degraded"] strong {
328
+ color: var(--och-amber);
329
+ }
330
+ .och-pi[data-state="setup-required"],
331
+ .och-pi[data-state="unknown"] {
332
+ border-color: color-mix(in srgb, var(--och-red) 30%, var(--och-hairline));
333
+ background: color-mix(in srgb, var(--och-red) 5%, var(--och-surface));
334
+ }
335
+ .och-pi[data-state="setup-required"] strong,
336
+ .och-pi[data-state="unknown"] strong {
337
+ color: var(--och-red);
338
+ }
339
+
340
+ /* Band 2 — 工作区带:下划线 tab(轻语法,从属于模式控件) */
341
+ .och-workspace {
342
+ display: flex;
343
+ align-items: center;
344
+ gap: 16px;
345
+ height: 44px;
346
+ }
347
+ .och-tabs {
348
+ display: flex;
349
+ align-items: stretch;
350
+ gap: 2px;
351
+ height: 100%;
352
+ }
353
+ .och-tab {
354
+ position: relative;
355
+ display: inline-flex;
356
+ align-items: center;
357
+ padding: 0 13px;
358
+ color: var(--och-muted);
359
+ font-size: 13.5px;
360
+ font-weight: 500;
361
+ text-decoration: none;
362
+ white-space: nowrap;
363
+ transition: color 0.15s ease;
364
+ }
365
+ .och-tab:hover {
366
+ color: var(--och-ink);
367
+ }
368
+ .och-tab.och-tab-active {
369
+ color: var(--och-ink);
370
+ font-weight: 600;
371
+ }
372
+ .och-tab.och-tab-active::after {
373
+ content: "";
374
+ position: absolute;
375
+ left: 10px;
376
+ right: 10px;
377
+ bottom: -1px;
378
+ height: 2px;
379
+ border-radius: 2px;
380
+ background: var(--och-orange);
381
+ }
382
+
383
+ .och a:focus-visible,
384
+ .och button:focus-visible,
385
+ .operator-chrome-fallback button:focus-visible {
386
+ outline: 2px solid color-mix(in srgb, var(--och-orange) 55%, transparent);
387
+ outline-offset: 2px;
388
+ }
389
+
390
+ .operator-chrome-fallback {
391
+ display: flex;
392
+ align-items: center;
393
+ gap: 10px;
394
+ min-height: 54px;
395
+ padding: 0 24px;
396
+ border-bottom: 1px solid var(--hairline, #e6e5e0);
397
+ background: var(--canvas, #f7f7f4);
398
+ }
399
+ .operator-chrome-fallback button {
400
+ appearance: none;
401
+ padding: 6px 10px;
402
+ border: 1px solid var(--hairline, #e6e5e0);
403
+ border-radius: 999px;
404
+ background: var(--surface, #fff);
405
+ color: var(--ink, #26251e);
406
+ font: inherit;
407
+ cursor: pointer;
408
+ }
409
+ .operator-chrome-fallback [role="status"] {
410
+ color: var(--red, #c72d53);
411
+ font-size: 12px;
412
+ }
413
+
414
+ @media (max-width: 1100px) {
415
+ .och-repo-fp {
416
+ display: none;
417
+ }
418
+ .och-mode-btn {
419
+ min-width: 0;
420
+ }
421
+ .och-workspace {
422
+ overflow-x: auto;
423
+ }
424
+ .och-global {
425
+ gap: 8px;
426
+ }
427
+ .och-fresh,
428
+ .och-pi {
429
+ padding-inline: 9px;
430
+ }
431
+ .och-fresh-text,
432
+ .och-pi strong {
433
+ position: absolute;
434
+ width: 1px;
435
+ height: 1px;
436
+ padding: 0;
437
+ margin: -1px;
438
+ overflow: hidden;
439
+ clip: rect(0, 0, 0, 0);
440
+ white-space: nowrap;
441
+ border: 0;
442
+ }
443
+ }
444
+
445
+ @media (max-width: 760px) {
446
+ .och-inner {
447
+ width: min(100% - 24px, var(--och-shell-max));
448
+ }
449
+ .och-global {
450
+ gap: 6px;
451
+ overflow-x: auto;
452
+ scrollbar-width: none;
453
+ }
454
+ .och-global::-webkit-scrollbar {
455
+ display: none;
456
+ }
457
+ .och-brand > span:last-child {
458
+ display: none;
459
+ }
460
+ .och-repo-name {
461
+ max-width: 120px;
462
+ overflow: hidden;
463
+ text-overflow: ellipsis;
464
+ }
465
+ .och-mode-btn {
466
+ padding-inline: 10px;
467
+ }
468
+ .och-tab {
469
+ padding-inline: 10px;
470
+ }
471
+ }
472
+
473
+ @media (prefers-reduced-motion: reduce) {
474
+ .och-mode-btn,
475
+ .och-tab,
476
+ .och-repo,
477
+ .och-pi {
478
+ transition: none;
479
+ }
480
+ }
@@ -0,0 +1,82 @@
1
+ /** 与 operator-chrome.js 对应的类型声明(供 vitest/TS 侧导入契约测试)。 */
2
+
3
+ export type OperatorChromeTab = { id: string; label: string; href: string };
4
+
5
+ export const OPERATE_TABS: readonly OperatorChromeTab[];
6
+ export const INSPECT_TABS: readonly OperatorChromeTab[];
7
+ export const READINESS_LABELS: Record<string, string>;
8
+
9
+ export type InspectObjectView = { view: string; id: string };
10
+
11
+ export type InspectContext = {
12
+ view: string;
13
+ id: string;
14
+ from: string;
15
+ kindLabel: string;
16
+ returnHref: string;
17
+ returnLabel: string;
18
+ };
19
+
20
+ export type ChromeRepoInfo = {
21
+ name: string;
22
+ fingerprint: string;
23
+ version: string;
24
+ product: string;
25
+ inspectReady?: boolean;
26
+ };
27
+
28
+ export type ChromeModel = {
29
+ mode: "operate" | "inspect";
30
+ homeHref: string;
31
+ brand: string;
32
+ repo: ChromeRepoInfo | null;
33
+ modeButtons: Array<{
34
+ id: "operate" | "inspect";
35
+ label: string;
36
+ href: string;
37
+ active: boolean;
38
+ }>;
39
+ context: InspectContext | null;
40
+ freshness: boolean;
41
+ pi: { state: string; label: string; href: string; title: string };
42
+ tabs: Array<OperatorChromeTab & { active: boolean }>;
43
+ tabsAriaLabel: string;
44
+ };
45
+
46
+ export function parseInspectObjectView(
47
+ hashValue: string,
48
+ ): InspectObjectView | null;
49
+ export function activeInspectTabHref(hashValue: string): string | null;
50
+ export function parseInspectContext(hashValue: string): InspectContext | null;
51
+ export function resolveOperateHrefFromInspectHash(hashValue: string): string;
52
+ export function buildInspectDagHref(
53
+ dagRunId: string,
54
+ options?: { from?: string },
55
+ ): string;
56
+ export function clearInspectContextSource(hashValue: string): string;
57
+ export function repoBaseName(repoRoot: string): string;
58
+ export function shortFingerprint(fingerprint: string): string;
59
+ export function chromeStateFromHealth(body: unknown): {
60
+ readiness: string;
61
+ readinessLabel: string;
62
+ repo: ChromeRepoInfo;
63
+ };
64
+ export function buildOperatorChromeModel(config: {
65
+ mode: "operate" | "inspect";
66
+ activeTabHref?: string | null;
67
+ operateHref?: string;
68
+ inspectHref?: string;
69
+ readinessState?: string;
70
+ readinessHref?: string;
71
+ readinessTitle?: string;
72
+ repo?: ChromeRepoInfo | null;
73
+ freshness?: boolean;
74
+ context?: InspectContext | null;
75
+ }): ChromeModel;
76
+ export function mountOperatorChrome(
77
+ root: HTMLElement,
78
+ config?: Record<string, unknown>,
79
+ ): {
80
+ update: (nextConfig?: Record<string, unknown>) => void;
81
+ destroy: () => void;
82
+ };