iterate-plugin 2.11.0 → 2.12.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.
package/lib/client.js CHANGED
@@ -264,6 +264,213 @@ function scanSessionForReport(session) {
264
264
  }
265
265
  return null;
266
266
  }
267
+ function isTranscriptManifest(obj) {
268
+ if (!obj || typeof obj !== "object") return false;
269
+ const o = (
270
+ /** @type {Record<string, unknown>} */
271
+ obj
272
+ );
273
+ return typeof safeGet(o, "version") === "number" && Array.isArray(safeGet(o, "rounds")) && Array.isArray(safeGet(o, "convergence"));
274
+ }
275
+ function attachLive(manifest, source) {
276
+ if (!manifest || typeof manifest !== "object") return manifest;
277
+ const live = source && typeof source === "object" ? safeGet(source, "live") : void 0;
278
+ if (!Array.isArray(live)) return manifest;
279
+ const m = (
280
+ /** @type {Record<string, unknown>} */
281
+ manifest
282
+ );
283
+ if (safeGet(m, "live") !== void 0) return manifest;
284
+ const copy = (
285
+ /** @type {Record<string, unknown>} */
286
+ {}
287
+ );
288
+ for (const k of safeKeys(m)) copy[k] = safeGet(m, k);
289
+ copy.live = live;
290
+ return copy;
291
+ }
292
+ function extractTranscript(obj) {
293
+ if (typeof obj === "string") {
294
+ try {
295
+ const parsed = JSON.parse(obj);
296
+ return extractTranscript(parsed);
297
+ } catch {
298
+ return null;
299
+ }
300
+ }
301
+ if (!obj || typeof obj !== "object") return null;
302
+ if (isTranscriptManifest(obj)) return (
303
+ /** @type {Record<string, unknown>} */
304
+ obj
305
+ );
306
+ const o = (
307
+ /** @type {Record<string, unknown>} */
308
+ obj
309
+ );
310
+ if (safeGet(o, "operation") === "capture") {
311
+ const t = safeGet(o, "transcript");
312
+ if (t && typeof t === "object" && isTranscriptManifest(t)) {
313
+ return attachLive(
314
+ /** @type {Record<string, unknown>} */
315
+ t,
316
+ o
317
+ );
318
+ }
319
+ }
320
+ for (const key of ["message", "result", "content"]) {
321
+ const val = safeGet(o, key);
322
+ if (val !== void 0) {
323
+ if (Array.isArray(val)) {
324
+ for (const item of val) {
325
+ const found = extractTranscript(item);
326
+ if (found) return attachLive(found, o);
327
+ }
328
+ } else {
329
+ const found = extractTranscript(val);
330
+ if (found) return attachLive(found, o);
331
+ }
332
+ }
333
+ }
334
+ return attachLive(findTranscriptInObject(o), o);
335
+ }
336
+ function findTranscriptInObject(obj, seen, maxDepth = 20) {
337
+ if (maxDepth <= 0) return null;
338
+ if (!obj || typeof obj !== "object") return null;
339
+ const s = seen || /* @__PURE__ */ new Set();
340
+ if (s.has(obj)) return null;
341
+ s.add(obj);
342
+ if (isTranscriptManifest(obj)) return (
343
+ /** @type {Record<string, unknown>} */
344
+ obj
345
+ );
346
+ if (Array.isArray(obj)) {
347
+ for (const item of obj) {
348
+ const found = findTranscriptInObject(item, s, maxDepth - 1);
349
+ if (found) return found;
350
+ }
351
+ return null;
352
+ }
353
+ const o = (
354
+ /** @type {Record<string, unknown>} */
355
+ obj
356
+ );
357
+ for (const key of safeKeys(o)) {
358
+ const val = safeGet(o, key);
359
+ if (val && typeof val === "object") {
360
+ const found = findTranscriptInObject(val, s, maxDepth - 1);
361
+ if (found) return found;
362
+ }
363
+ }
364
+ return null;
365
+ }
366
+ function scanSessionForTranscript(session) {
367
+ if (!session || typeof session !== "object") return null;
368
+ const s = (
369
+ /** @type {Record<string, unknown>} */
370
+ session
371
+ );
372
+ const toolCalls = safeGet(s, "toolCalls");
373
+ if (Array.isArray(toolCalls)) {
374
+ const calls = (
375
+ /** @type {Array<Record<string, unknown>>} */
376
+ toolCalls
377
+ );
378
+ for (let i = calls.length - 1; i >= 0; i--) {
379
+ const call = calls[i];
380
+ if (!call) continue;
381
+ const tool = String(safeGet(call, "tool") ?? "");
382
+ if (tool !== "iterate_transcript" && !tool.endsWith("iterate_transcript")) continue;
383
+ const found = extractTranscript(safeGet(call, "result")) || extractTranscript(safeGet(call, "message"));
384
+ if (found) return found;
385
+ }
386
+ }
387
+ const messages = safeGet(s, "messages");
388
+ if (Array.isArray(messages)) {
389
+ const msgs = (
390
+ /** @type {Array<Record<string, unknown>>} */
391
+ messages
392
+ );
393
+ for (let i = msgs.length - 1; i >= 0; i--) {
394
+ const msg = msgs[i];
395
+ if (!msg) continue;
396
+ const calls = safeGet(msg, "tool_calls");
397
+ if (Array.isArray(calls)) {
398
+ for (const call of calls) {
399
+ if (!call) continue;
400
+ const args = safeGet(call, "arguments");
401
+ if (typeof args === "string") {
402
+ const found2 = extractTranscript(args);
403
+ if (found2) return found2;
404
+ }
405
+ }
406
+ }
407
+ const found = extractTranscript(safeGet(msg, "content"));
408
+ if (found) return found;
409
+ }
410
+ }
411
+ return null;
412
+ }
413
+ function normalizeTranscript(manifest) {
414
+ const src = manifest && typeof manifest === "object" ? (
415
+ /** @type {Record<string, unknown>} */
416
+ manifest
417
+ ) : {};
418
+ const asNum = (v) => typeof v === "number" ? v : 0;
419
+ const asStr = (v) => typeof v === "string" ? v : "";
420
+ const asBool = (v) => v === true;
421
+ const asCount = (v) => typeof v === "number" ? v : 0;
422
+ const asArray = (v) => Array.isArray(v) ? (
423
+ /** @type {Array<Record<string, unknown>>} */
424
+ v
425
+ ) : [];
426
+ const rounds = asArray(safeGet(src, "rounds")).map((r) => ({
427
+ round: asNum(safeGet(r, "round")),
428
+ threads: asArray(safeGet(r, "threads")).map((t) => ({
429
+ dimension: asStr(safeGet(t, "dimension")),
430
+ attempt: asNum(safeGet(t, "attempt")),
431
+ messages: asArray(safeGet(t, "messages")).map((m) => typeof m === "string" ? m : ""),
432
+ readFiles: asArray(safeGet(t, "readFiles")).map((f) => typeof f === "string" ? f : ""),
433
+ findings: asArray(safeGet(t, "findings")).map((f) => ({ ...f }))
434
+ }))
435
+ }));
436
+ const cp = safeGet(src, "checkpoint");
437
+ const checkpoint = cp && typeof cp === "object" ? {
438
+ mode: asStr(safeGet(cp, "mode")),
439
+ round: asNum(safeGet(cp, "round")),
440
+ maxRounds: asNum(safeGet(cp, "maxRounds")),
441
+ fixedCount: asNum(safeGet(cp, "fixedCount")),
442
+ resumeCount: asNum(safeGet(cp, "resumeCount")),
443
+ updatedAt: asStr(safeGet(cp, "updatedAt"))
444
+ } : null;
445
+ const ng = safeGet(src, "nudge");
446
+ const nudge = ng && typeof ng === "object" ? { timestamp: asStr(safeGet(ng, "timestamp")), text: asStr(safeGet(ng, "text")) } : null;
447
+ const ap = safeGet(src, "approval");
448
+ return {
449
+ version: asNum(safeGet(src, "version")),
450
+ project: asStr(safeGet(src, "project")),
451
+ updatedAt: asStr(safeGet(src, "updatedAt")),
452
+ active: asBool(safeGet(src, "active")),
453
+ mode: asStr(safeGet(src, "mode")) || null,
454
+ goal: asStr(safeGet(src, "goal")),
455
+ phases: asArray(safeGet(src, "phases")).map((p) => typeof p === "string" ? p : ""),
456
+ round: asNum(safeGet(src, "round")),
457
+ maxRounds: asNum(safeGet(src, "maxRounds")),
458
+ rounds,
459
+ convergence: asArray(safeGet(src, "convergence")).map((n) => asCount(n)),
460
+ findings: asArray(safeGet(src, "findings")).map((f) => ({ ...f })),
461
+ fixes: asArray(safeGet(src, "fixes")).map((f) => ({ ...f })),
462
+ live: asArray(safeGet(src, "live")).map((e) => ({
463
+ ts: typeof safeGet(e, "ts") === "number" ? String(safeGet(e, "ts")) : asStr(safeGet(e, "ts")),
464
+ type: asStr(safeGet(e, "type")),
465
+ tool: asStr(safeGet(e, "tool")),
466
+ target: asStr(safeGet(e, "target"))
467
+ })),
468
+ checkpoint,
469
+ timeline: asArray(safeGet(src, "timeline")).map((t) => ({ ...t })),
470
+ nudge,
471
+ approval: ap && typeof ap === "object" ? { active: asBool(safeGet(ap, "active")), policy: asStr(safeGet(ap, "policy")) || "ask" } : { active: false, policy: "ask" }
472
+ };
473
+ }
267
474
  function isRunSummary(obj) {
268
475
  if (!obj || typeof obj !== "object") return false;
269
476
  const o = (
@@ -987,6 +1194,38 @@ var ITERATE_CSS = `
987
1194
  .iterate-guide { margin-top: 4px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 10px; overflow: hidden; background: var(--dsw-alias-bg-layer-2); }
988
1195
  .iterate-guide-bar { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 12px; background: var(--dsw-alias-bg-layer-2); border-bottom: 1px solid var(--dsw-alias-border-l1); font-size: 12px; color: var(--dsw-alias-label-secondary); }
989
1196
  .iterate-guide-body { padding: 12px 14px; font-family: var(--dsw-font-mono, ui-monospace, monospace); font-size: 11.5px; line-height: 1.75; white-space: pre-wrap; color: var(--dsw-alias-label-primary); max-height: 260px; overflow: auto; }
1197
+
1198
+ /* \u2500\u2500 Runtime observatory panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
1199
+ .iterate-obs { margin: 6px 0; border: 1px solid var(--dsw-alias-border-l1); border-radius: 12px; background: var(--dsw-alias-bg-layer-1); overflow: hidden; font-size: 12px; color: var(--dsw-alias-label-primary); }
1200
+ .iterate-obs-head { display: flex; align-items: center; gap: 10px; padding: 8px 14px; cursor: pointer; border-bottom: 1px solid var(--dsw-alias-border-l1); flex-wrap: wrap; }
1201
+ .iterate-obs-head[data-closed] { border-bottom: none; }
1202
+ .iterate-obs-title { font-weight: 650; }
1203
+ .iterate-obs-badge { display: inline-flex; align-items: center; gap: 6px; padding: 2px 10px; border-radius: 999px; background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent); color: var(--dsw-alias-brand-primary); font-size: 11px; font-weight: 600; white-space: nowrap; }
1204
+ .iterate-obs-badge[data-live] { color: var(--dsw-alias-state-success-primary); background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent); }
1205
+ .iterate-obs-head-meta { margin-left: auto; font-size: 11px; color: var(--dsw-alias-label-secondary); white-space: nowrap; }
1206
+ .iterate-obs-tabs { display: flex; gap: 4px; padding: 8px 14px 0; flex-wrap: wrap; border-bottom: 1px solid var(--dsw-alias-border-l1); }
1207
+ .iterate-obs-tab { padding: 4px 10px; border-radius: 7px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-secondary); font-size: 11px; cursor: pointer; }
1208
+ .iterate-obs-tab[data-active] { border-color: var(--dsw-alias-brand-primary); color: var(--dsw-alias-brand-primary); }
1209
+ .iterate-obs-body { padding: 12px 14px; max-height: 460px; overflow: auto; }
1210
+ .iterate-obs-block { border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; overflow: hidden; margin-bottom: 8px; }
1211
+ .iterate-obs-block:last-child { margin-bottom: 0; }
1212
+ .iterate-obs-block-head { display: flex; align-items: center; gap: 8px; padding: 7px 10px; background: var(--dsw-alias-bg-layer-2); border-bottom: 1px solid var(--dsw-alias-border-l1); font-size: 11px; color: var(--dsw-alias-label-primary); flex-wrap: wrap; }
1213
+ .iterate-obs-block-head[data-click] { cursor: pointer; }
1214
+ .iterate-obs-block-body { padding: 8px 10px; }
1215
+ .iterate-obs-chip { display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; border-radius: 6px; background: var(--dsw-alias-bg-layer-1); border: 1px solid var(--dsw-alias-border-l1); font-size: 11px; color: var(--dsw-alias-label-secondary); white-space: nowrap; }
1216
+ .iterate-obs-msg { padding: 2px 0; color: var(--dsw-alias-label-secondary); line-height: 1.5; font-size: 11px; word-break: break-word; }
1217
+ .iterate-obs-file { font-family: var(--dsw-font-mono, ui-monospace, monospace); font-size: 10.5px; color: var(--dsw-alias-label-secondary); word-break: break-all; }
1218
+ .iterate-obs-bar { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--dsw-alias-label-secondary); flex-wrap: wrap; }
1219
+ .iterate-obs-bar b { color: var(--dsw-alias-label-primary); font-weight: 600; }
1220
+ .iterate-obs-mono { font-family: var(--dsw-font-mono, ui-monospace, monospace); }
1221
+ .iterate-obs-code { padding: 6px 8px; margin-top: 4px; border-radius: 6px; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); font-family: var(--dsw-font-mono, ui-monospace, monospace); font-size: 10.5px; color: var(--dsw-alias-label-secondary); white-space: pre-wrap; word-break: break-all; }
1222
+ .iterate-obs-input { width: 100%; padding: 8px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-primary); font-size: 11px; resize: vertical; }
1223
+ .iterate-obs-input:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 2px; }
1224
+ .iterate-obs-input::placeholder { color: var(--dsw-alias-label-secondary); }
1225
+ .iterate-obs-empty { font-size: 11px; color: var(--dsw-alias-label-secondary); padding: 8px 0; }
1226
+ .iterate-obs-row { display: flex; align-items: center; gap: 8px; padding: 5px 0; border-bottom: 1px solid var(--dsw-alias-border-l1); font-size: 11px; color: var(--dsw-alias-label-secondary); flex-wrap: wrap; }
1227
+ .iterate-obs-row:last-child { border-bottom: none; }
1228
+ .iterate-obs-livebadge { display: inline-flex; align-items: center; gap: 4px; padding: 1px 7px; border-radius: 6px; border: 1px solid currentColor; background: color-mix(in srgb, currentColor 10%, transparent); font-size: 10.5px; font-weight: 600; white-space: nowrap; }
990
1229
  `;
991
1230
  function log(...args) {
992
1231
  if (typeof console !== "undefined" && typeof console.error === "function") {
@@ -1390,6 +1629,11 @@ function TriagePanel(props) {
1390
1629
  const t = ev.target;
1391
1630
  if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.tagName === "SELECT")) return;
1392
1631
  if (t && typeof t.isContentEditable === "boolean" && t.isContentEditable) return;
1632
+ const rootEl = doc.querySelector('[data-iterate="triage"]');
1633
+ if (!rootEl) return;
1634
+ const activeEl = doc.activeElement;
1635
+ if (!activeEl) return;
1636
+ if (activeEl !== rootEl && !(typeof rootEl.contains === "function" && rootEl.contains(activeEl))) return;
1393
1637
  const verdict = keyToVerdict(ev.key);
1394
1638
  if (verdict && selected !== null && indices.includes(selected)) {
1395
1639
  ev.preventDefault();
@@ -1840,6 +2084,620 @@ function SettingsPanel(_props) {
1840
2084
  statusCard
1841
2085
  );
1842
2086
  }
2087
+ var OBS_TABS = [
2088
+ { key: "live", label: "\u5B9E\u65F6\u6D3B\u52A8\u6D41" },
2089
+ { key: "f1", label: "\u5BA1\u67E5\u7EBF\u7A0B" },
2090
+ { key: "f2", label: "\u6536\u655B\u8D8B\u52BF" },
2091
+ { key: "f3", label: "\u53D1\u73B0\u5B9A\u4F4D" },
2092
+ { key: "f4", label: "\u4FEE\u590D\u4E0E\u56DE\u6EDA" },
2093
+ { key: "f5", label: "\u65AD\u70B9\u6062\u590D" },
2094
+ { key: "f6", label: "\u8FD0\u884C\u63A7\u5236\u53F0" },
2095
+ { key: "f7", label: "\u51B3\u7B56\u65F6\u95F4\u7EBF" }
2096
+ ];
2097
+ var OBS_LIVE_META = {
2098
+ read: { label: "\u9605\u8BFB", color: "var(--dsw-alias-brand-primary)" },
2099
+ fix: { label: "\u4FEE\u590D", color: "var(--dsw-alias-state-success-primary)" },
2100
+ rollback: { label: "\u56DE\u6EDA", color: "var(--dsw-alias-state-error-primary)" },
2101
+ diff: { label: "\u5DEE\u5F02", color: "var(--dsw-alias-state-warn-primary)" },
2102
+ review: { label: "\u5BA1\u67E5", color: "var(--dsw-alias-brand-primary)" },
2103
+ triage: { label: "\u5206\u6D41", color: "var(--dsw-alias-state-warn-primary)" },
2104
+ checkpoint: { label: "\u65AD\u70B9", color: "var(--dsw-alias-brand-primary)" },
2105
+ validate: { label: "\u6821\u9A8C", color: "var(--dsw-alias-state-success-primary)" },
2106
+ log: { label: "\u8BB0\u5F55", color: "var(--dsw-alias-label-secondary)" },
2107
+ prune: { label: "\u6E05\u7406", color: "var(--dsw-alias-state-warn-primary)" },
2108
+ info: { label: "\u4FE1\u606F", color: "var(--dsw-alias-label-secondary)" }
2109
+ };
2110
+ var OBS_LIVE_FALLBACK_COLOR = "var(--dsw-alias-label-secondary)";
2111
+ function formatObsLiveTime(ts) {
2112
+ if (!ts) return "";
2113
+ let date = null;
2114
+ const num = Number(ts);
2115
+ if (typeof ts === "string" && ts !== "" && !Number.isNaN(num) && /^\d+$/.test(ts)) {
2116
+ date = new Date(num);
2117
+ } else {
2118
+ const d = new Date(ts);
2119
+ date = Number.isNaN(d.getTime()) ? null : d;
2120
+ }
2121
+ if (!date) return String(ts);
2122
+ const p = (n) => String(n).padStart(2, "0");
2123
+ const h = p(date.getHours());
2124
+ const mi = p(date.getMinutes());
2125
+ const s = p(date.getSeconds());
2126
+ return `${p(date.getDate())}/${p(date.getMonth() + 1)} ${h}:${mi}:${s}`;
2127
+ }
2128
+ function latestTranscript(session) {
2129
+ if (!session) return null;
2130
+ const raw = scanSessionForTranscript(session);
2131
+ return raw ? normalizeTranscript(raw) : null;
2132
+ }
2133
+ function buildObsFixInstruction(f) {
2134
+ const payload = JSON.stringify({
2135
+ file: String(f.file || ""),
2136
+ ...typeof f.line === "number" && f.line > 0 ? { line: f.line } : {},
2137
+ dimension: String(f.dimension || ""),
2138
+ summary: String(f.summary || ""),
2139
+ ...f.suggested_fix ? { suggested_fix: String(f.suggested_fix) } : {}
2140
+ }, null, 2);
2141
+ return `\u8BF7\u8C03\u7528 \`iterate_fix\` \u4FEE\u590D\u4EE5\u4E0B finding\uFF1A
2142
+
2143
+ \`\`\`json
2144
+ ${payload}
2145
+ \`\`\``;
2146
+ }
2147
+ function ObservatoryPanel(props) {
2148
+ const session = props && props.session ? props.session : null;
2149
+ const manifest = latestTranscript(session);
2150
+ const [open, setOpen] = React.useState(false);
2151
+ const [tab, setTab] = React.useState("live");
2152
+ const [expandedThreads, setExpandedThreads] = React.useState(/* @__PURE__ */ new Set());
2153
+ const [copiedKey, setCopiedKey] = React.useState(null);
2154
+ const [copyFailText, setCopyFailText] = React.useState(null);
2155
+ const [nudgeText, setNudgeText] = React.useState("");
2156
+ const [timelineType, setTimelineType] = React.useState("");
2157
+ const [timelineSearch, setTimelineSearch] = React.useState("");
2158
+ const copyTimer = React.useRef(null);
2159
+ React.useEffect(() => () => {
2160
+ if (copyTimer.current) clearTimeout(copyTimer.current);
2161
+ }, []);
2162
+ const copyInstruction = (key, text) => {
2163
+ if (!text) return;
2164
+ copyText(text).then((ok) => {
2165
+ if (!ok) {
2166
+ setCopyFailText(text);
2167
+ return;
2168
+ }
2169
+ setCopiedKey(key);
2170
+ if (copyTimer.current) clearTimeout(copyTimer.current);
2171
+ copyTimer.current = setTimeout(() => setCopiedKey((cur) => cur === key ? null : cur), 1600);
2172
+ });
2173
+ };
2174
+ const toggleThread = (key) => {
2175
+ setExpandedThreads((prev) => {
2176
+ const next = new Set(prev);
2177
+ if (next.has(key)) next.delete(key);
2178
+ else next.add(key);
2179
+ return next;
2180
+ });
2181
+ };
2182
+ if (!manifest) {
2183
+ return React.createElement(
2184
+ "div",
2185
+ { "data-iterate-root": "", "data-iterate": "obs", className: "iterate-obs" },
2186
+ React.createElement(
2187
+ "div",
2188
+ { className: "iterate-obs-head", "data-closed": "" },
2189
+ React.createElement("span", { className: "iterate-obs-title" }, "iterate \u89C2\u6D4B\u53F0"),
2190
+ React.createElement("span", { className: "iterate-obs-head-meta" }, "\u6682\u65E0\u8FD0\u884C\u65F6\u89C2\u6D4B\u6570\u636E")
2191
+ )
2192
+ );
2193
+ }
2194
+ const live = manifest.active === true;
2195
+ const roundMeta = typeof manifest.round === "number" ? typeof manifest.maxRounds === "number" ? `Round ${manifest.round}/${manifest.maxRounds}` : `Round ${manifest.round}` : "";
2196
+ const headMeta = [manifest.mode || "", roundMeta, manifest.updatedAt || ""].filter(Boolean).join(" \xB7 ");
2197
+ const threadFindingPill = (f, keyBase) => {
2198
+ const loc = `${String(f.file || "?")}${typeof f.line === "number" && f.line > 0 ? `:${f.line}` : ""}`;
2199
+ return React.createElement(
2200
+ "div",
2201
+ { key: keyBase, className: "iterate-obs-row" },
2202
+ React.createElement("span", { className: "iterate-sev-dot", style: { background: severityColor(f.severity) } }),
2203
+ React.createElement("span", {}, severityLabel(f.severity)),
2204
+ React.createElement("span", { className: "iterate-obs-file" }, loc),
2205
+ React.createElement("span", {}, String(f.dimension || "")),
2206
+ React.createElement("span", { className: "iterate-obs-msg" }, String(f.summary || ""))
2207
+ );
2208
+ };
2209
+ const renderThreads = () => {
2210
+ const rounds = manifest.rounds || [];
2211
+ if (rounds.length === 0) {
2212
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u5BA1\u67E5\u7EBF\u7A0B");
2213
+ }
2214
+ return React.createElement("div", {}, ...rounds.map((r, ri) => {
2215
+ const threads = r.threads || [];
2216
+ const fCount = threads.reduce((sum, t) => sum + (t.findings ? t.findings.length : 0), 0);
2217
+ const threadBlocks = threads.length === 0 ? [React.createElement("div", { key: "none", className: "iterate-obs-empty" }, "\u672C\u8F6E\u65E0\u7EBF\u7A0B")] : threads.map((t, ti) => {
2218
+ const key = `${ri}-${ti}`;
2219
+ const expanded = expandedThreads.has(key);
2220
+ const dim = t.dimension || "\u672A\u547D\u540D\u7EF4\u5EA6";
2221
+ const tFindings = t.findings || [];
2222
+ const files = t.readFiles || [];
2223
+ const msgs = t.messages || [];
2224
+ return React.createElement(
2225
+ "div",
2226
+ { key, className: "iterate-obs-block" },
2227
+ React.createElement(
2228
+ "div",
2229
+ { className: "iterate-obs-block-head", "data-click": "", onClick: () => toggleThread(key), title: expanded ? "\u6536\u8D77\u7EBF\u7A0B" : "\u5C55\u5F00\u7EBF\u7A0B" },
2230
+ React.createElement("span", {}, expanded ? "\u2212" : "+"),
2231
+ React.createElement("b", {}, dim),
2232
+ React.createElement("span", { className: "iterate-obs-chip" }, `attempt ${typeof t.attempt === "number" ? t.attempt : "?"}`),
2233
+ React.createElement("span", { className: "iterate-obs-chip" }, `${tFindings.length} findings`)
2234
+ ),
2235
+ expanded ? React.createElement(
2236
+ "div",
2237
+ { className: "iterate-obs-block-body" },
2238
+ React.createElement(
2239
+ "div",
2240
+ { className: "iterate-obs-bar" },
2241
+ React.createElement("b", {}, "\u53D9\u8FF0"),
2242
+ React.createElement("span", { className: "iterate-obs-head-meta" }, `${msgs.length} \u6761`)
2243
+ ),
2244
+ ...msgs.length === 0 ? [React.createElement("div", { key: "msg-none", className: "iterate-obs-empty" }, "\u65E0\u7EBF\u7D22\u53D9\u8FF0")] : msgs.map((m, mi) => React.createElement("div", { key: mi, className: "iterate-obs-msg" }, String(m))),
2245
+ ...files.length > 0 ? [
2246
+ React.createElement(
2247
+ "div",
2248
+ { key: "files-h", className: "iterate-obs-bar", style: { marginTop: 6 } },
2249
+ React.createElement("b", {}, "\u8BFB\u53D6\u6587\u4EF6")
2250
+ ),
2251
+ ...files.map(
2252
+ (f, fi) => React.createElement("div", { key: `f${fi}`, className: "iterate-obs-file" }, String(f))
2253
+ )
2254
+ ] : [],
2255
+ React.createElement(
2256
+ "div",
2257
+ { className: "iterate-obs-bar", style: { marginTop: 6 } },
2258
+ React.createElement("b", {}, "\u53D1\u73B0")
2259
+ ),
2260
+ ...tFindings.length === 0 ? [React.createElement("div", { key: "finding-none", className: "iterate-obs-empty" }, "\u672C\u7EBF\u7A0B\u65E0\u53D1\u73B0")] : tFindings.map((f, fi) => threadFindingPill(f, `tf${fi}`))
2261
+ ) : null
2262
+ );
2263
+ });
2264
+ return React.createElement(
2265
+ "div",
2266
+ { key: ri, className: "iterate-obs-block" },
2267
+ React.createElement(
2268
+ "div",
2269
+ { className: "iterate-obs-block-head" },
2270
+ React.createElement("b", {}, `Round ${typeof r.round === "number" ? r.round : ri + 1}`),
2271
+ React.createElement("span", { className: "iterate-obs-chip" }, `${threads.length} \u7EBF\u7A0B`),
2272
+ React.createElement("span", { className: "iterate-obs-head-meta" }, `${fCount} findings`)
2273
+ ),
2274
+ React.createElement("div", { className: "iterate-obs-block-body" }, ...threadBlocks)
2275
+ );
2276
+ }));
2277
+ };
2278
+ const renderTrend = () => {
2279
+ const conv = manifest.convergence || [];
2280
+ if (conv.length === 0) {
2281
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u6536\u655B\u6570\u636E");
2282
+ }
2283
+ const points = conv.map((n, i) => ({ round: i + 1, count: n }));
2284
+ return React.createElement(
2285
+ "div",
2286
+ {},
2287
+ React.createElement(TrendChart, { points }),
2288
+ React.createElement(
2289
+ "div",
2290
+ { className: "iterate-obs-bar", style: { marginTop: 6 } },
2291
+ `\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\uFF1A${conv.join(" \u2192 ")}${typeof manifest.round === "number" ? ` \xB7 \u5F53\u524D Round ${manifest.round}` : ""}`
2292
+ )
2293
+ );
2294
+ };
2295
+ const renderFindings = () => {
2296
+ const findings = manifest.findings || [];
2297
+ if (findings.length === 0) {
2298
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u53D1\u73B0");
2299
+ }
2300
+ return React.createElement("div", {}, ...findings.map((f, i) => {
2301
+ const k = `f3-${i}`;
2302
+ const loc = `${String(f.file || "?")}${typeof f.line === "number" && f.line > 0 ? `:${f.line}` : ""}`;
2303
+ const entry = {
2304
+ file: String(f.file || ""),
2305
+ ...typeof f.line === "number" && f.line > 0 ? { line: f.line } : {},
2306
+ dimension: String(f.dimension || ""),
2307
+ reason: String(f.summary || "")
2308
+ };
2309
+ const knownInstruction = buildApplyInstruction([entry]);
2310
+ const fixInstruction = buildObsFixInstruction(f);
2311
+ return React.createElement(
2312
+ "div",
2313
+ { key: k, className: "iterate-obs-block" },
2314
+ React.createElement(
2315
+ "div",
2316
+ { className: "iterate-obs-block-head" },
2317
+ React.createElement("span", { className: "iterate-sev-dot", style: { background: severityColor(f.severity) } }),
2318
+ React.createElement("span", {}, severityLabel(f.severity)),
2319
+ React.createElement("span", { className: "iterate-obs-file" }, loc),
2320
+ React.createElement("span", {}, String(f.dimension || "")),
2321
+ f.acknowledged === true ? React.createElement("span", { className: "iterate-obs-chip" }, "\u5DF2\u786E\u8BA4") : null
2322
+ ),
2323
+ React.createElement(
2324
+ "div",
2325
+ { className: "iterate-obs-block-body" },
2326
+ React.createElement("div", { className: "iterate-obs-msg" }, String(f.summary || "")),
2327
+ React.createElement(
2328
+ "div",
2329
+ { className: "iterate-obs-bar", style: { marginTop: 8 } },
2330
+ React.createElement("button", {
2331
+ className: "iterate-btn",
2332
+ "data-copied": copiedKey === `${k}-fix` ? "" : void 0,
2333
+ onClick: () => copyInstruction(`${k}-fix`, String(f.suggested_fix || "")),
2334
+ title: "\u590D\u5236\u8BE5 finding \u7684\u5EFA\u8BAE\u4FEE\u590D\u6587\u672C"
2335
+ }, copiedKey === `${k}-fix` ? "\u5DF2\u590D\u5236" : "\u590D\u5236 suggested_fix"),
2336
+ React.createElement("button", {
2337
+ className: "iterate-btn",
2338
+ "data-copied": copiedKey === `${k}-known` ? "" : void 0,
2339
+ onClick: () => copyInstruction(`${k}-known`, knownInstruction),
2340
+ title: "\u590D\u5236 iterate_triage \u6307\u4EE4\u6587\u672C\uFF08\u6807\u8BB0\u4E3A known_intentional\uFF09"
2341
+ }, copiedKey === `${k}-known` ? "\u5DF2\u590D\u5236" : "known_intentional"),
2342
+ React.createElement("button", {
2343
+ className: "iterate-btn",
2344
+ "data-primary": "",
2345
+ "data-copied": copiedKey === `${k}-repair` ? "" : void 0,
2346
+ onClick: () => copyInstruction(`${k}-repair`, fixInstruction),
2347
+ title: "\u590D\u5236 iterate_fix \u6307\u4EE4\u6587\u672C"
2348
+ }, copiedKey === `${k}-repair` ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u4FEE\u590D\u6307\u4EE4")
2349
+ )
2350
+ )
2351
+ );
2352
+ }));
2353
+ };
2354
+ const renderFixes = () => {
2355
+ const fixes = manifest.fixes || [];
2356
+ if (fixes.length === 0) {
2357
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u4FEE\u590D\u8BB0\u5F55");
2358
+ }
2359
+ return React.createElement("div", {}, ...fixes.map((f, i) => {
2360
+ const k = `f4-${i}`;
2361
+ const id = String(f.id || `fix#${i + 1}`);
2362
+ const rollbackText = `\u8BF7\u8C03\u7528 \`iterate_rollback\` \u56DE\u6EDA\u4EE5\u4E0B\u4FEE\u590D\uFF1A
2363
+
2364
+ \`\`\`json
2365
+ ${JSON.stringify({ id }, null, 2)}
2366
+ \`\`\``;
2367
+ const added = typeof f.linesAdded === "number" ? f.linesAdded : 0;
2368
+ const removed = typeof f.linesRemoved === "number" ? f.linesRemoved : 0;
2369
+ return React.createElement(
2370
+ "div",
2371
+ { key: k, className: "iterate-obs-block" },
2372
+ React.createElement(
2373
+ "div",
2374
+ { className: "iterate-obs-block-head" },
2375
+ React.createElement("b", {}, id),
2376
+ React.createElement("span", { className: "iterate-obs-file" }, String(f.file || "?")),
2377
+ React.createElement("span", { className: "iterate-obs-chip" }, f.success === true ? "\u6210\u529F" : "\u5931\u8D25"),
2378
+ typeof f.round === "number" ? React.createElement("span", { className: "iterate-obs-head-meta" }, `Round ${f.round}`) : null
2379
+ ),
2380
+ React.createElement(
2381
+ "div",
2382
+ { className: "iterate-obs-block-body" },
2383
+ React.createElement("div", { className: "iterate-obs-msg" }, String(f.summary || "")),
2384
+ React.createElement(
2385
+ "div",
2386
+ { className: "iterate-obs-bar", style: { marginTop: 8 } },
2387
+ React.createElement("span", { className: "iterate-obs-chip" }, `+${added}`),
2388
+ React.createElement("span", { className: "iterate-obs-chip" }, `\u2212${removed}`),
2389
+ React.createElement("button", {
2390
+ className: "iterate-btn",
2391
+ "data-danger": "",
2392
+ "data-copied": copiedKey === `${k}-rb` ? "" : void 0,
2393
+ onClick: () => copyInstruction(`${k}-rb`, rollbackText),
2394
+ title: "\u590D\u5236 iterate_rollback \u6307\u4EE4\u6587\u672C"
2395
+ }, copiedKey === `${k}-rb` ? "\u5DF2\u590D\u5236" : "\u56DE\u6EDA")
2396
+ )
2397
+ )
2398
+ );
2399
+ }));
2400
+ };
2401
+ const renderCheckpoint = () => {
2402
+ const cp = manifest.checkpoint;
2403
+ if (!cp) {
2404
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u65AD\u70B9");
2405
+ }
2406
+ const resumeText = `\u8BF7\u8C03\u7528 \`iterate_checkpoint\` \u4ECE\u65AD\u70B9\u6062\u590D\u8FED\u4EE3\uFF1A
2407
+
2408
+ \`\`\`json
2409
+ ${JSON.stringify({
2410
+ operation: "resume",
2411
+ mode: String(cp.mode || "normal"),
2412
+ maxRounds: typeof cp.maxRounds === "number" ? cp.maxRounds : null
2413
+ }, null, 2)}
2414
+ \`\`\``;
2415
+ const item = (label, value) => React.createElement("span", { className: "iterate-obs-chip" }, `${label} ${String(value ?? "?")}`);
2416
+ return React.createElement(
2417
+ "div",
2418
+ { className: "iterate-obs-block" },
2419
+ React.createElement(
2420
+ "div",
2421
+ { className: "iterate-obs-block-head" },
2422
+ React.createElement("b", {}, "\u65AD\u70B9"),
2423
+ React.createElement("span", { className: "iterate-obs-head-meta" }, String(cp.updatedAt || ""))
2424
+ ),
2425
+ React.createElement(
2426
+ "div",
2427
+ { className: "iterate-obs-block-body" },
2428
+ React.createElement(
2429
+ "div",
2430
+ { className: "iterate-obs-bar", style: { marginBottom: 8 } },
2431
+ item("mode", cp.mode || "normal"),
2432
+ item("round", cp.round),
2433
+ item("maxRounds", cp.maxRounds),
2434
+ item("fixed", cp.fixedCount),
2435
+ item("resume", cp.resumeCount)
2436
+ ),
2437
+ React.createElement("button", {
2438
+ className: "iterate-btn",
2439
+ "data-primary": "",
2440
+ "data-copied": copiedKey === "cp-resume" ? "" : void 0,
2441
+ onClick: () => copyInstruction("cp-resume", resumeText),
2442
+ title: "\u590D\u5236 iterate_checkpoint \u7EFC/\u6062\u590D\u6307\u4EE4\u6587\u672C"
2443
+ }, copiedKey === "cp-resume" ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u6062\u590D\u6307\u4EE4")
2444
+ )
2445
+ );
2446
+ };
2447
+ const renderConsole = () => {
2448
+ const approval = manifest.approval || { policy: "ask" };
2449
+ const policyLabelMap = { ask: "\u8BE2\u95EE\u7528\u6237", deny: "\u62D2\u7EDD", allow: "\u76F4\u63A5\u6267\u884C" };
2450
+ const policyLabel = policyLabelMap[String(approval.policy || "ask")] || String(approval.policy || "ask");
2451
+ const nudge = manifest.nudge || null;
2452
+ const present = Boolean(nudge && nudge.text);
2453
+ const activeNudgeText = nudge && nudge.text ? nudge.text : "";
2454
+ const nudgeInstruction = `\u8BF7\u8C03\u7528 \`iterate_transcript\` \u5199\u5165 nudge \u6307\u4EE4\uFF1A
2455
+
2456
+ \`\`\`json
2457
+ ${JSON.stringify({ operation: "nudge", text: nudgeText }, null, 2)}
2458
+ \`\`\``;
2459
+ const activeClearInstruction = `\u8BF7\u8C03\u7528 \`iterate_transcript\` \u6E05\u9664\u5F53\u524D nudge\uFF1A
2460
+
2461
+ \`\`\`json
2462
+ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
2463
+ \`\`\``;
2464
+ return React.createElement(
2465
+ "div",
2466
+ { className: "iterate-obs-block" },
2467
+ React.createElement(
2468
+ "div",
2469
+ { className: "iterate-obs-block-head" },
2470
+ React.createElement("span", {}, "\u8FD0\u884C\u63A7\u5236\u53F0"),
2471
+ React.createElement("span", { className: "iterate-obs-badge", "data-live": live ? "" : void 0 }, live ? "\u8FD0\u884C\u4E2D" : "\u5DF2\u7ED3\u675F"),
2472
+ React.createElement("span", { className: "iterate-obs-head-meta" }, `\u6279\u51C6\u7B56\u7565\uFF1A${policyLabel}`)
2473
+ ),
2474
+ React.createElement(
2475
+ "div",
2476
+ { className: "iterate-obs-block-body" },
2477
+ present ? React.createElement(
2478
+ "div",
2479
+ { className: "iterate-obs-bar", style: { marginBottom: 6 } },
2480
+ React.createElement("b", {}, "\u5F53\u524D nudge"),
2481
+ React.createElement("span", { className: "iterate-obs-msg" }, activeNudgeText),
2482
+ React.createElement("button", {
2483
+ className: "iterate-btn",
2484
+ "data-copied": copiedKey === "nudge-clear" ? "" : void 0,
2485
+ onClick: () => copyInstruction("nudge-clear", activeClearInstruction),
2486
+ title: "\u590D\u5236\u6E05\u9664 nudge \u6307\u4EE4\uFF08\u5199 text:null\uFF09\uFF0C\u5728\u8FD0\u884C\u63A7\u5236\u53F0\u63D0\u793A\u6E05\u9664\u5DF2\u6301\u4E45\u5316\u7684 nudge"
2487
+ }, copiedKey === "nudge-clear" ? "\u5DF2\u590D\u5236\u6E05\u9664\u6307\u4EE4" : "\u590D\u5236\u6E05\u9664\u6307\u4EE4")
2488
+ ) : null,
2489
+ React.createElement("textarea", {
2490
+ className: "iterate-obs-input iterate-obs-mono",
2491
+ rows: 3,
2492
+ placeholder: "\u8D77\u8349\u4E00\u6761 nudge \u65B9\u5411\u6307\u4EE4\u2026",
2493
+ value: nudgeText,
2494
+ "aria-label": "nudge \u8349\u7A3F",
2495
+ onChange: (e) => setNudgeText(e.target.value)
2496
+ }),
2497
+ React.createElement(
2498
+ "div",
2499
+ { className: "iterate-obs-bar", style: { marginTop: 6 } },
2500
+ React.createElement("button", {
2501
+ className: "iterate-btn",
2502
+ "data-primary": "",
2503
+ "data-copied": copiedKey === "nudge" ? "" : void 0,
2504
+ disabled: !nudgeText,
2505
+ onClick: () => copyInstruction("nudge", nudgeInstruction),
2506
+ title: "\u590D\u5236 iterate_transcript nudge \u6307\u4EE4\u6587\u672C"
2507
+ }, copiedKey === "nudge" ? "\u5DF2\u590D\u5236" : "\u590D\u5236 nudge \u6307\u4EE4")
2508
+ )
2509
+ )
2510
+ );
2511
+ };
2512
+ const renderTimeline = () => {
2513
+ const entries = manifest.timeline || [];
2514
+ if (entries.length === 0) {
2515
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u65F6\u95F4\u7EBF\u6761\u76EE");
2516
+ }
2517
+ const types = Array.from(new Set(entries.map((t) => String(t.type || "unknown")).filter(Boolean))).sort();
2518
+ const q = timelineSearch.trim().toLowerCase();
2519
+ const filtered = entries.filter((t) => {
2520
+ if (timelineType && String(t.type || "") !== timelineType) return false;
2521
+ if (!q) return true;
2522
+ const hay = [String(t.type || ""), String(t.round ?? ""), JSON.stringify(t.data || {})].join(" ").toLowerCase();
2523
+ return hay.indexOf(q) >= 0;
2524
+ });
2525
+ const sorted = filtered.slice().sort((a, b) => String(b.timestamp || "").localeCompare(String(a.timestamp || "")));
2526
+ const rows = sorted.map((t, i) => {
2527
+ const k = `f7-${i}`;
2528
+ const dataText = t.data && typeof t.data === "object" ? JSON.stringify(t.data) : "";
2529
+ return React.createElement(
2530
+ "div",
2531
+ { key: k, className: "iterate-obs-row", style: { flexDirection: "column", alignItems: "flex-start" } },
2532
+ React.createElement(
2533
+ "div",
2534
+ { className: "iterate-obs-bar", style: { width: "100%" } },
2535
+ React.createElement("span", { className: "iterate-obs-chip" }, String(t.type || "?")),
2536
+ React.createElement(
2537
+ "span",
2538
+ { className: "iterate-obs-head-meta" },
2539
+ `${typeof t.round === "number" ? `Round ${t.round} \xB7 ` : ""}${String(t.timestamp || "")}`
2540
+ )
2541
+ ),
2542
+ dataText ? React.createElement("div", { className: "iterate-obs-code" }, dataText) : null
2543
+ );
2544
+ });
2545
+ if (rows.length === 0) {
2546
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u65E0\u5339\u914D\u7684\u65F6\u95F4\u7EBF\u6761\u76EE");
2547
+ }
2548
+ return React.createElement(
2549
+ "div",
2550
+ {},
2551
+ React.createElement(
2552
+ "div",
2553
+ { className: "iterate-obs-bar", style: { marginBottom: 8 } },
2554
+ React.createElement(
2555
+ "select",
2556
+ {
2557
+ className: "iterate-filter-select",
2558
+ value: timelineType,
2559
+ "aria-label": "\u6309\u65F6\u95F4\u7EBF\u7C7B\u578B\u7B5B\u9009",
2560
+ onChange: (e) => setTimelineType(e.target.value)
2561
+ },
2562
+ React.createElement("option", { value: "" }, "\u5168\u90E8\u7C7B\u578B"),
2563
+ ...types.map((t) => React.createElement("option", { key: t, value: t }, t))
2564
+ ),
2565
+ React.createElement("input", {
2566
+ className: "iterate-filter-search",
2567
+ type: "search",
2568
+ placeholder: "\u641C\u7D22\u65F6\u95F4\u7EBF\u2026",
2569
+ "aria-label": "\u641C\u7D22\u65F6\u95F4\u7EBF",
2570
+ value: timelineSearch,
2571
+ onChange: (e) => setTimelineSearch(e.target.value)
2572
+ }),
2573
+ React.createElement("span", { className: "iterate-filter-count" }, `${filtered.length}/${entries.length}`)
2574
+ ),
2575
+ ...rows
2576
+ );
2577
+ };
2578
+ const renderLive = () => {
2579
+ const liveEntries = manifest.live || [];
2580
+ if (liveEntries.length === 0) {
2581
+ const hasThreads = (manifest.rounds || []).length > 0;
2582
+ const hasFindings = (manifest.findings || []).length > 0;
2583
+ if (hasThreads || hasFindings) {
2584
+ return React.createElement(
2585
+ "div",
2586
+ { className: "iterate-obs-empty" },
2587
+ React.createElement("div", {}, "\u5B9E\u65F6\u6D3B\u52A8\u6D41\u4EC5\u5728\u8FD0\u884C\u671F\u95F4\u8BB0\u5F55\uFF0C\u5F53\u524D\u5DF2\u5B8C\u6210\u3002"),
2588
+ React.createElement(
2589
+ "div",
2590
+ { className: "iterate-obs-bar", style: { marginTop: 8 } },
2591
+ React.createElement("button", { className: "iterate-btn", onClick: () => setTab("f1") }, "\u67E5\u770B\u5BA1\u67E5\u7EBF\u7A0B"),
2592
+ React.createElement("button", { className: "iterate-btn", onClick: () => setTab("f3") }, "\u67E5\u770B\u53D1\u73B0")
2593
+ )
2594
+ );
2595
+ }
2596
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u5B9E\u65F6\u6D3B\u52A8");
2597
+ }
2598
+ const rows = liveEntries.map((e, i) => {
2599
+ const type = String(e.type || "unknown");
2600
+ const meta = OBS_LIVE_META[type] || { label: String(e.type || "?"), color: OBS_LIVE_FALLBACK_COLOR };
2601
+ const tool = String(e.tool || "");
2602
+ const target = String(e.target || "");
2603
+ const time = formatObsLiveTime(e.ts);
2604
+ return React.createElement(
2605
+ "div",
2606
+ { key: `live-${i}`, className: "iterate-obs-row" },
2607
+ React.createElement("span", { className: "iterate-obs-livebadge", style: { color: meta.color } }, meta.label),
2608
+ tool ? React.createElement("span", { className: "iterate-obs-chip" }, tool) : null,
2609
+ React.createElement("span", { className: "iterate-obs-msg" }, target || "\u2014"),
2610
+ React.createElement("span", { className: "iterate-obs-head-meta" }, time)
2611
+ );
2612
+ });
2613
+ return React.createElement(
2614
+ "div",
2615
+ {},
2616
+ React.createElement(
2617
+ "div",
2618
+ { className: "iterate-obs-bar", style: { marginBottom: 8 } },
2619
+ React.createElement("b", {}, "\u5B9E\u65F6\u6D3B\u52A8"),
2620
+ React.createElement("span", { className: "iterate-obs-chip" }, `${liveEntries.length} \u9879`)
2621
+ ),
2622
+ ...rows
2623
+ );
2624
+ };
2625
+ const renderBody = () => {
2626
+ switch (tab) {
2627
+ case "live":
2628
+ return renderLive();
2629
+ case "f1":
2630
+ return renderThreads();
2631
+ case "f2":
2632
+ return renderTrend();
2633
+ case "f3":
2634
+ return renderFindings();
2635
+ case "f4":
2636
+ return renderFixes();
2637
+ case "f5":
2638
+ return renderCheckpoint();
2639
+ case "f6":
2640
+ return renderConsole();
2641
+ case "f7":
2642
+ return renderTimeline();
2643
+ default:
2644
+ return null;
2645
+ }
2646
+ };
2647
+ return React.createElement(
2648
+ "div",
2649
+ { "data-iterate-root": "", "data-iterate": "obs", className: "iterate-obs" },
2650
+ React.createElement(
2651
+ "div",
2652
+ { className: "iterate-obs-head", "data-closed": open ? void 0 : "", onClick: () => setOpen((v) => !v) },
2653
+ React.createElement("span", { className: "iterate-obs-title" }, "iterate \u89C2\u6D4B\u53F0"),
2654
+ React.createElement("span", { className: "iterate-obs-badge", "data-live": live ? "" : void 0 }, live ? "\u8FD0\u884C\u4E2D" : "\u5DF2\u7ED3\u675F"),
2655
+ React.createElement("span", { className: "iterate-obs-head-meta" }, headMeta || "runtime"),
2656
+ React.createElement("button", {
2657
+ className: "iterate-btn",
2658
+ "data-ghost": "",
2659
+ onClick: (e) => {
2660
+ e.stopPropagation();
2661
+ setOpen((v) => !v);
2662
+ }
2663
+ }, open ? "\u6536\u8D77" : "\u5C55\u5F00")
2664
+ ),
2665
+ open ? React.createElement(
2666
+ "div",
2667
+ {},
2668
+ copyFailText ? React.createElement(
2669
+ "div",
2670
+ { className: "iterate-obs-block" },
2671
+ React.createElement(
2672
+ "div",
2673
+ { className: "iterate-obs-block-head" },
2674
+ React.createElement("b", {}, "\u590D\u5236\u672A\u6210\u529F"),
2675
+ React.createElement("span", { className: "iterate-obs-head-meta" }, "\u526A\u8D34\u677F\u5199\u5165\u88AB\u62D2\u7EDD\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0B\u65B9\u6307\u4EE4\u590D\u5236"),
2676
+ React.createElement("button", {
2677
+ className: "iterate-btn",
2678
+ "data-ghost": "",
2679
+ "aria-label": "\u5173\u95ED\u590D\u5236\u5931\u8D25\u63D0\u793A",
2680
+ onClick: () => setCopyFailText(null)
2681
+ }, "\u5173\u95ED")
2682
+ ),
2683
+ React.createElement("div", { className: "iterate-obs-code" }, copyFailText)
2684
+ ) : null,
2685
+ React.createElement(
2686
+ "div",
2687
+ { className: "iterate-obs-tabs" },
2688
+ ...OBS_TABS.map(
2689
+ (t) => React.createElement("button", {
2690
+ key: t.key,
2691
+ className: "iterate-obs-tab",
2692
+ "data-active": tab === t.key ? "" : void 0,
2693
+ onClick: () => setTab(t.key)
2694
+ }, t.label)
2695
+ )
2696
+ ),
2697
+ React.createElement("div", { className: "iterate-obs-body" }, renderBody())
2698
+ ) : null
2699
+ );
2700
+ }
1843
2701
  function selectTurnTail(owner) {
1844
2702
  if (!owner) return null;
1845
2703
  if (findReportInObject(owner.turn, void 0, 24) || scanSessionForReport(owner.turn)) return { matched: true };
@@ -1880,6 +2738,13 @@ function apply(ctx) {
1880
2738
  (props) => React.createElement(ConvergenceDashboard, props)
1881
2739
  )
1882
2740
  );
2741
+ slotsSvc.inject(
2742
+ "conversation.input.dock",
2743
+ () => slotsSvc?.register(
2744
+ { name: "conversation.input.dock", id: "iterate-observatory", order: 91 },
2745
+ (props) => React.createElement(ObservatoryPanel, props)
2746
+ )
2747
+ );
1883
2748
  slotsSvc.inject(
1884
2749
  "conversation.chat.turnTail",
1885
2750
  () => slotsSvc?.register(