iterate-plugin 2.11.0 → 2.12.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/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") {
@@ -1840,6 +2079,574 @@ function SettingsPanel(_props) {
1840
2079
  statusCard
1841
2080
  );
1842
2081
  }
2082
+ var OBS_TABS = [
2083
+ { key: "live", label: "\u5B9E\u65F6\u6D3B\u52A8\u6D41" },
2084
+ { key: "f1", label: "\u5BA1\u67E5\u7EBF\u7A0B" },
2085
+ { key: "f2", label: "\u6536\u655B\u8D8B\u52BF" },
2086
+ { key: "f3", label: "\u53D1\u73B0\u5B9A\u4F4D" },
2087
+ { key: "f4", label: "\u4FEE\u590D\u4E0E\u56DE\u6EDA" },
2088
+ { key: "f5", label: "\u65AD\u70B9\u6062\u590D" },
2089
+ { key: "f6", label: "\u8FD0\u884C\u63A7\u5236\u53F0" },
2090
+ { key: "f7", label: "\u51B3\u7B56\u65F6\u95F4\u7EBF" }
2091
+ ];
2092
+ var OBS_LIVE_META = {
2093
+ read: { label: "\u9605\u8BFB", color: "var(--dsw-alias-brand-primary)" },
2094
+ fix: { label: "\u4FEE\u590D", color: "var(--dsw-alias-state-success-primary)" },
2095
+ rollback: { label: "\u56DE\u6EDA", color: "var(--dsw-alias-state-error-primary)" },
2096
+ diff: { label: "\u5DEE\u5F02", color: "var(--dsw-alias-state-warn-primary)" },
2097
+ review: { label: "\u5BA1\u67E5", color: "var(--dsw-alias-brand-primary)" },
2098
+ triage: { label: "\u5206\u6D41", color: "var(--dsw-alias-state-warn-primary)" },
2099
+ checkpoint: { label: "\u65AD\u70B9", color: "var(--dsw-alias-brand-primary)" },
2100
+ validate: { label: "\u6821\u9A8C", color: "var(--dsw-alias-state-success-primary)" },
2101
+ log: { label: "\u8BB0\u5F55", color: "var(--dsw-alias-label-secondary)" },
2102
+ prune: { label: "\u6E05\u7406", color: "var(--dsw-alias-state-warn-primary)" },
2103
+ info: { label: "\u4FE1\u606F", color: "var(--dsw-alias-label-secondary)" }
2104
+ };
2105
+ var OBS_LIVE_FALLBACK_COLOR = "var(--dsw-alias-label-secondary)";
2106
+ function formatObsLiveTime(ts) {
2107
+ if (!ts) return "";
2108
+ let date = null;
2109
+ const num = Number(ts);
2110
+ if (typeof ts === "string" && ts !== "" && !Number.isNaN(num) && /^\d+$/.test(ts)) {
2111
+ date = new Date(num);
2112
+ } else {
2113
+ const d = new Date(ts);
2114
+ date = Number.isNaN(d.getTime()) ? null : d;
2115
+ }
2116
+ if (!date) return String(ts);
2117
+ const p = (n) => String(n).padStart(2, "0");
2118
+ const h = p(date.getHours());
2119
+ const mi = p(date.getMinutes());
2120
+ const s = p(date.getSeconds());
2121
+ return `${p(date.getDate())}/${p(date.getMonth() + 1)} ${h}:${mi}:${s}`;
2122
+ }
2123
+ function latestTranscript(session) {
2124
+ if (!session) return null;
2125
+ const raw = scanSessionForTranscript(session);
2126
+ return raw ? normalizeTranscript(raw) : null;
2127
+ }
2128
+ function buildObsFixInstruction(f) {
2129
+ const payload = JSON.stringify({
2130
+ file: String(f.file || ""),
2131
+ ...typeof f.line === "number" && f.line > 0 ? { line: f.line } : {},
2132
+ dimension: String(f.dimension || ""),
2133
+ summary: String(f.summary || ""),
2134
+ ...f.suggested_fix ? { suggested_fix: String(f.suggested_fix) } : {}
2135
+ }, null, 2);
2136
+ return `\u8BF7\u8C03\u7528 \`iterate_fix\` \u4FEE\u590D\u4EE5\u4E0B finding\uFF1A
2137
+
2138
+ \`\`\`json
2139
+ ${payload}
2140
+ \`\`\``;
2141
+ }
2142
+ function ObservatoryPanel(props) {
2143
+ const session = props && props.session ? props.session : null;
2144
+ const manifest = latestTranscript(session);
2145
+ const [open, setOpen] = React.useState(false);
2146
+ const [tab, setTab] = React.useState("live");
2147
+ const [expandedThreads, setExpandedThreads] = React.useState(/* @__PURE__ */ new Set());
2148
+ const [copiedKey, setCopiedKey] = React.useState(null);
2149
+ const [nudgeText, setNudgeText] = React.useState("");
2150
+ const [timelineType, setTimelineType] = React.useState("");
2151
+ const [timelineSearch, setTimelineSearch] = React.useState("");
2152
+ const copyTimer = React.useRef(null);
2153
+ React.useEffect(() => () => {
2154
+ if (copyTimer.current) clearTimeout(copyTimer.current);
2155
+ }, []);
2156
+ const copyInstruction = (key, text) => {
2157
+ if (!text) return;
2158
+ copyText(text).then((ok) => {
2159
+ if (!ok) return;
2160
+ setCopiedKey(key);
2161
+ if (copyTimer.current) clearTimeout(copyTimer.current);
2162
+ copyTimer.current = setTimeout(() => setCopiedKey((cur) => cur === key ? null : cur), 1600);
2163
+ });
2164
+ };
2165
+ const toggleThread = (key) => {
2166
+ setExpandedThreads((prev) => {
2167
+ const next = new Set(prev);
2168
+ if (next.has(key)) next.delete(key);
2169
+ else next.add(key);
2170
+ return next;
2171
+ });
2172
+ };
2173
+ if (!manifest) {
2174
+ return React.createElement(
2175
+ "div",
2176
+ { "data-iterate-root": "", "data-iterate": "obs", className: "iterate-obs" },
2177
+ React.createElement(
2178
+ "div",
2179
+ { className: "iterate-obs-head", "data-closed": "" },
2180
+ React.createElement("span", { className: "iterate-obs-title" }, "iterate \u89C2\u6D4B\u53F0"),
2181
+ React.createElement("span", { className: "iterate-obs-head-meta" }, "\u6682\u65E0\u8FD0\u884C\u65F6\u89C2\u6D4B\u6570\u636E")
2182
+ )
2183
+ );
2184
+ }
2185
+ const live = manifest.active === true;
2186
+ const roundMeta = typeof manifest.round === "number" ? typeof manifest.maxRounds === "number" ? `Round ${manifest.round}/${manifest.maxRounds}` : `Round ${manifest.round}` : "";
2187
+ const headMeta = [manifest.mode || "", roundMeta, manifest.updatedAt || ""].filter(Boolean).join(" \xB7 ");
2188
+ const threadFindingPill = (f, keyBase) => {
2189
+ const loc = `${String(f.file || "?")}${typeof f.line === "number" && f.line > 0 ? `:${f.line}` : ""}`;
2190
+ return React.createElement(
2191
+ "div",
2192
+ { key: keyBase, className: "iterate-obs-row" },
2193
+ React.createElement("span", { className: "iterate-sev-dot", style: { background: severityColor(f.severity) } }),
2194
+ React.createElement("span", {}, severityLabel(f.severity)),
2195
+ React.createElement("span", { className: "iterate-obs-file" }, loc),
2196
+ React.createElement("span", {}, String(f.dimension || "")),
2197
+ React.createElement("span", { className: "iterate-obs-msg" }, String(f.summary || ""))
2198
+ );
2199
+ };
2200
+ const renderThreads = () => {
2201
+ const rounds = manifest.rounds || [];
2202
+ if (rounds.length === 0) {
2203
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u5BA1\u67E5\u7EBF\u7A0B");
2204
+ }
2205
+ return React.createElement("div", {}, ...rounds.map((r, ri) => {
2206
+ const threads = r.threads || [];
2207
+ const fCount = threads.reduce((sum, t) => sum + (t.findings ? t.findings.length : 0), 0);
2208
+ const threadBlocks = threads.length === 0 ? [React.createElement("div", { key: "none", className: "iterate-obs-empty" }, "\u672C\u8F6E\u65E0\u7EBF\u7A0B")] : threads.map((t, ti) => {
2209
+ const key = `${ri}-${ti}`;
2210
+ const expanded = expandedThreads.has(key);
2211
+ const dim = t.dimension || "\u672A\u547D\u540D\u7EF4\u5EA6";
2212
+ const tFindings = t.findings || [];
2213
+ const files = t.readFiles || [];
2214
+ const msgs = t.messages || [];
2215
+ return React.createElement(
2216
+ "div",
2217
+ { key, className: "iterate-obs-block" },
2218
+ React.createElement(
2219
+ "div",
2220
+ { className: "iterate-obs-block-head", "data-click": "", onClick: () => toggleThread(key), title: expanded ? "\u6536\u8D77\u7EBF\u7A0B" : "\u5C55\u5F00\u7EBF\u7A0B" },
2221
+ React.createElement("span", {}, expanded ? "\u2212" : "+"),
2222
+ React.createElement("b", {}, dim),
2223
+ React.createElement("span", { className: "iterate-obs-chip" }, `attempt ${typeof t.attempt === "number" ? t.attempt : "?"}`),
2224
+ React.createElement("span", { className: "iterate-obs-chip" }, `${tFindings.length} findings`)
2225
+ ),
2226
+ expanded ? React.createElement(
2227
+ "div",
2228
+ { className: "iterate-obs-block-body" },
2229
+ React.createElement(
2230
+ "div",
2231
+ { className: "iterate-obs-bar" },
2232
+ React.createElement("b", {}, "\u53D9\u8FF0"),
2233
+ React.createElement("span", { className: "iterate-obs-head-meta" }, `${msgs.length} \u6761`)
2234
+ ),
2235
+ ...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))),
2236
+ ...files.length > 0 ? [
2237
+ React.createElement(
2238
+ "div",
2239
+ { key: "files-h", className: "iterate-obs-bar", style: { marginTop: 6 } },
2240
+ React.createElement("b", {}, "\u8BFB\u53D6\u6587\u4EF6")
2241
+ ),
2242
+ ...files.map(
2243
+ (f, fi) => React.createElement("div", { key: `f${fi}`, className: "iterate-obs-file" }, String(f))
2244
+ )
2245
+ ] : [],
2246
+ React.createElement(
2247
+ "div",
2248
+ { className: "iterate-obs-bar", style: { marginTop: 6 } },
2249
+ React.createElement("b", {}, "\u53D1\u73B0")
2250
+ ),
2251
+ ...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}`))
2252
+ ) : null
2253
+ );
2254
+ });
2255
+ return React.createElement(
2256
+ "div",
2257
+ { key: ri, className: "iterate-obs-block" },
2258
+ React.createElement(
2259
+ "div",
2260
+ { className: "iterate-obs-block-head" },
2261
+ React.createElement("b", {}, `Round ${typeof r.round === "number" ? r.round : ri + 1}`),
2262
+ React.createElement("span", { className: "iterate-obs-chip" }, `${threads.length} \u7EBF\u7A0B`),
2263
+ React.createElement("span", { className: "iterate-obs-head-meta" }, `${fCount} findings`)
2264
+ ),
2265
+ React.createElement("div", { className: "iterate-obs-block-body" }, ...threadBlocks)
2266
+ );
2267
+ }));
2268
+ };
2269
+ const renderTrend = () => {
2270
+ const conv = manifest.convergence || [];
2271
+ if (conv.length === 0) {
2272
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u6536\u655B\u6570\u636E");
2273
+ }
2274
+ const points = conv.map((n, i) => ({ round: i + 1, count: n }));
2275
+ return React.createElement(
2276
+ "div",
2277
+ {},
2278
+ React.createElement(TrendChart, { points }),
2279
+ React.createElement(
2280
+ "div",
2281
+ { className: "iterate-obs-bar", style: { marginTop: 6 } },
2282
+ `\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\uFF1A${conv.join(" \u2192 ")}${typeof manifest.round === "number" ? ` \xB7 \u5F53\u524D Round ${manifest.round}` : ""}`
2283
+ )
2284
+ );
2285
+ };
2286
+ const renderFindings = () => {
2287
+ const findings = manifest.findings || [];
2288
+ if (findings.length === 0) {
2289
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u53D1\u73B0");
2290
+ }
2291
+ return React.createElement("div", {}, ...findings.map((f, i) => {
2292
+ const k = `f3-${i}`;
2293
+ const loc = `${String(f.file || "?")}${typeof f.line === "number" && f.line > 0 ? `:${f.line}` : ""}`;
2294
+ const entry = {
2295
+ file: String(f.file || ""),
2296
+ ...typeof f.line === "number" && f.line > 0 ? { line: f.line } : {},
2297
+ dimension: String(f.dimension || ""),
2298
+ reason: String(f.summary || "")
2299
+ };
2300
+ const knownInstruction = buildApplyInstruction([entry]);
2301
+ const fixInstruction = buildObsFixInstruction(f);
2302
+ return React.createElement(
2303
+ "div",
2304
+ { key: k, className: "iterate-obs-block" },
2305
+ React.createElement(
2306
+ "div",
2307
+ { className: "iterate-obs-block-head" },
2308
+ React.createElement("span", { className: "iterate-sev-dot", style: { background: severityColor(f.severity) } }),
2309
+ React.createElement("span", {}, severityLabel(f.severity)),
2310
+ React.createElement("span", { className: "iterate-obs-file" }, loc),
2311
+ React.createElement("span", {}, String(f.dimension || "")),
2312
+ f.acknowledged === true ? React.createElement("span", { className: "iterate-obs-chip" }, "\u5DF2\u786E\u8BA4") : null
2313
+ ),
2314
+ React.createElement(
2315
+ "div",
2316
+ { className: "iterate-obs-block-body" },
2317
+ React.createElement("div", { className: "iterate-obs-msg" }, String(f.summary || "")),
2318
+ React.createElement(
2319
+ "div",
2320
+ { className: "iterate-obs-bar", style: { marginTop: 8 } },
2321
+ React.createElement("button", {
2322
+ className: "iterate-btn",
2323
+ "data-copied": copiedKey === `${k}-fix` ? "" : void 0,
2324
+ onClick: () => copyInstruction(`${k}-fix`, String(f.suggested_fix || "")),
2325
+ title: "\u590D\u5236\u8BE5 finding \u7684\u5EFA\u8BAE\u4FEE\u590D\u6587\u672C"
2326
+ }, copiedKey === `${k}-fix` ? "\u5DF2\u590D\u5236" : "\u590D\u5236 suggested_fix"),
2327
+ React.createElement("button", {
2328
+ className: "iterate-btn",
2329
+ "data-copied": copiedKey === `${k}-known` ? "" : void 0,
2330
+ onClick: () => copyInstruction(`${k}-known`, knownInstruction),
2331
+ title: "\u590D\u5236 iterate_triage \u6307\u4EE4\u6587\u672C\uFF08\u6807\u8BB0\u4E3A known_intentional\uFF09"
2332
+ }, copiedKey === `${k}-known` ? "\u5DF2\u590D\u5236" : "known_intentional"),
2333
+ React.createElement("button", {
2334
+ className: "iterate-btn",
2335
+ "data-primary": "",
2336
+ "data-copied": copiedKey === `${k}-repair` ? "" : void 0,
2337
+ onClick: () => copyInstruction(`${k}-repair`, fixInstruction),
2338
+ title: "\u590D\u5236 iterate_fix \u6307\u4EE4\u6587\u672C"
2339
+ }, copiedKey === `${k}-repair` ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u4FEE\u590D\u6307\u4EE4")
2340
+ )
2341
+ )
2342
+ );
2343
+ }));
2344
+ };
2345
+ const renderFixes = () => {
2346
+ const fixes = manifest.fixes || [];
2347
+ if (fixes.length === 0) {
2348
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u4FEE\u590D\u8BB0\u5F55");
2349
+ }
2350
+ return React.createElement("div", {}, ...fixes.map((f, i) => {
2351
+ const k = `f4-${i}`;
2352
+ const id = String(f.id || `fix#${i + 1}`);
2353
+ const rollbackText = `\u8BF7\u8C03\u7528 \`iterate_rollback\` \u56DE\u6EDA\u4EE5\u4E0B\u4FEE\u590D\uFF1A
2354
+
2355
+ \`\`\`json
2356
+ ${JSON.stringify({ id }, null, 2)}
2357
+ \`\`\``;
2358
+ const added = typeof f.linesAdded === "number" ? f.linesAdded : 0;
2359
+ const removed = typeof f.linesRemoved === "number" ? f.linesRemoved : 0;
2360
+ return React.createElement(
2361
+ "div",
2362
+ { key: k, className: "iterate-obs-block" },
2363
+ React.createElement(
2364
+ "div",
2365
+ { className: "iterate-obs-block-head" },
2366
+ React.createElement("b", {}, id),
2367
+ React.createElement("span", { className: "iterate-obs-file" }, String(f.file || "?")),
2368
+ React.createElement("span", { className: "iterate-obs-chip" }, f.success === true ? "\u6210\u529F" : "\u5931\u8D25"),
2369
+ typeof f.round === "number" ? React.createElement("span", { className: "iterate-obs-head-meta" }, `Round ${f.round}`) : null
2370
+ ),
2371
+ React.createElement(
2372
+ "div",
2373
+ { className: "iterate-obs-block-body" },
2374
+ React.createElement("div", { className: "iterate-obs-msg" }, String(f.summary || "")),
2375
+ React.createElement(
2376
+ "div",
2377
+ { className: "iterate-obs-bar", style: { marginTop: 8 } },
2378
+ React.createElement("span", { className: "iterate-obs-chip" }, `+${added}`),
2379
+ React.createElement("span", { className: "iterate-obs-chip" }, `\u2212${removed}`),
2380
+ React.createElement("button", {
2381
+ className: "iterate-btn",
2382
+ "data-danger": "",
2383
+ "data-copied": copiedKey === `${k}-rb` ? "" : void 0,
2384
+ onClick: () => copyInstruction(`${k}-rb`, rollbackText),
2385
+ title: "\u590D\u5236 iterate_rollback \u6307\u4EE4\u6587\u672C"
2386
+ }, copiedKey === `${k}-rb` ? "\u5DF2\u590D\u5236" : "\u56DE\u6EDA")
2387
+ )
2388
+ )
2389
+ );
2390
+ }));
2391
+ };
2392
+ const renderCheckpoint = () => {
2393
+ const cp = manifest.checkpoint;
2394
+ if (!cp) {
2395
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u65AD\u70B9");
2396
+ }
2397
+ const resumeText = `\u8BF7\u8C03\u7528 \`iterate_checkpoint\` \u4ECE\u65AD\u70B9\u6062\u590D\u8FED\u4EE3\uFF1A
2398
+
2399
+ \`\`\`json
2400
+ ${JSON.stringify({
2401
+ operation: "resume",
2402
+ mode: String(cp.mode || "normal"),
2403
+ maxRounds: typeof cp.maxRounds === "number" ? cp.maxRounds : null
2404
+ }, null, 2)}
2405
+ \`\`\``;
2406
+ const item = (label, value) => React.createElement("span", { className: "iterate-obs-chip" }, `${label} ${String(value ?? "?")}`);
2407
+ return React.createElement(
2408
+ "div",
2409
+ { className: "iterate-obs-block" },
2410
+ React.createElement(
2411
+ "div",
2412
+ { className: "iterate-obs-block-head" },
2413
+ React.createElement("b", {}, "\u65AD\u70B9"),
2414
+ React.createElement("span", { className: "iterate-obs-head-meta" }, String(cp.updatedAt || ""))
2415
+ ),
2416
+ React.createElement(
2417
+ "div",
2418
+ { className: "iterate-obs-block-body" },
2419
+ React.createElement(
2420
+ "div",
2421
+ { className: "iterate-obs-bar", style: { marginBottom: 8 } },
2422
+ item("mode", cp.mode || "normal"),
2423
+ item("round", cp.round),
2424
+ item("maxRounds", cp.maxRounds),
2425
+ item("fixed", cp.fixedCount),
2426
+ item("resume", cp.resumeCount)
2427
+ ),
2428
+ React.createElement("button", {
2429
+ className: "iterate-btn",
2430
+ "data-primary": "",
2431
+ "data-copied": copiedKey === "cp-resume" ? "" : void 0,
2432
+ onClick: () => copyInstruction("cp-resume", resumeText),
2433
+ title: "\u590D\u5236 iterate_checkpoint \u7EFC/\u6062\u590D\u6307\u4EE4\u6587\u672C"
2434
+ }, copiedKey === "cp-resume" ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u6062\u590D\u6307\u4EE4")
2435
+ )
2436
+ );
2437
+ };
2438
+ const renderConsole = () => {
2439
+ const approval = manifest.approval || { policy: "ask" };
2440
+ const policyLabelMap = { ask: "\u8BE2\u95EE\u7528\u6237", deny: "\u62D2\u7EDD", allow: "\u76F4\u63A5\u6267\u884C" };
2441
+ const policyLabel = policyLabelMap[String(approval.policy || "ask")] || String(approval.policy || "ask");
2442
+ const nudge = manifest.nudge || null;
2443
+ const present = Boolean(nudge && nudge.text);
2444
+ const activeNudgeText = nudge && nudge.text ? nudge.text : "";
2445
+ const nudgeInstruction = `\u8BF7\u8C03\u7528 \`iterate_transcript\` \u5199\u5165 nudge \u6307\u4EE4\uFF1A
2446
+
2447
+ \`\`\`json
2448
+ ${JSON.stringify({ operation: "nudge", text: nudgeText }, null, 2)}
2449
+ \`\`\``;
2450
+ return React.createElement(
2451
+ "div",
2452
+ { className: "iterate-obs-block" },
2453
+ React.createElement(
2454
+ "div",
2455
+ { className: "iterate-obs-block-head" },
2456
+ React.createElement("span", {}, "\u8FD0\u884C\u63A7\u5236\u53F0"),
2457
+ React.createElement("span", { className: "iterate-obs-badge", "data-live": live ? "" : void 0 }, live ? "\u8FD0\u884C\u4E2D" : "\u5DF2\u7ED3\u675F"),
2458
+ React.createElement("span", { className: "iterate-obs-head-meta" }, `\u6279\u51C6\u7B56\u7565\uFF1A${policyLabel}`)
2459
+ ),
2460
+ React.createElement(
2461
+ "div",
2462
+ { className: "iterate-obs-block-body" },
2463
+ present ? React.createElement(
2464
+ "div",
2465
+ { className: "iterate-obs-bar", style: { marginBottom: 6 } },
2466
+ React.createElement("b", {}, "\u5F53\u524D nudge"),
2467
+ React.createElement("span", { className: "iterate-obs-msg" }, activeNudgeText),
2468
+ React.createElement("button", { className: "iterate-btn", onClick: () => setNudgeText("") }, "\u6E05\u9664")
2469
+ ) : null,
2470
+ React.createElement("textarea", {
2471
+ className: "iterate-obs-input iterate-obs-mono",
2472
+ rows: 3,
2473
+ placeholder: "\u8D77\u8349\u4E00\u6761 nudge \u65B9\u5411\u6307\u4EE4\u2026",
2474
+ value: nudgeText,
2475
+ "aria-label": "nudge \u8349\u7A3F",
2476
+ onChange: (e) => setNudgeText(e.target.value)
2477
+ }),
2478
+ React.createElement(
2479
+ "div",
2480
+ { className: "iterate-obs-bar", style: { marginTop: 6 } },
2481
+ React.createElement("button", {
2482
+ className: "iterate-btn",
2483
+ "data-primary": "",
2484
+ "data-copied": copiedKey === "nudge" ? "" : void 0,
2485
+ disabled: !nudgeText,
2486
+ onClick: () => copyInstruction("nudge", nudgeInstruction),
2487
+ title: "\u590D\u5236 iterate_transcript nudge \u6307\u4EE4\u6587\u672C"
2488
+ }, copiedKey === "nudge" ? "\u5DF2\u590D\u5236" : "\u590D\u5236 nudge \u6307\u4EE4")
2489
+ )
2490
+ )
2491
+ );
2492
+ };
2493
+ const renderTimeline = () => {
2494
+ const entries = manifest.timeline || [];
2495
+ if (entries.length === 0) {
2496
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u65F6\u95F4\u7EBF\u6761\u76EE");
2497
+ }
2498
+ const types = Array.from(new Set(entries.map((t) => String(t.type || "unknown")).filter(Boolean))).sort();
2499
+ const q = timelineSearch.trim().toLowerCase();
2500
+ const filtered = entries.filter((t) => {
2501
+ if (timelineType && String(t.type || "") !== timelineType) return false;
2502
+ if (!q) return true;
2503
+ const hay = [String(t.type || ""), String(t.round ?? ""), JSON.stringify(t.data || {})].join(" ").toLowerCase();
2504
+ return hay.indexOf(q) >= 0;
2505
+ });
2506
+ const sorted = filtered.slice().sort((a, b) => String(b.timestamp || "").localeCompare(String(a.timestamp || "")));
2507
+ const rows = sorted.map((t, i) => {
2508
+ const k = `f7-${i}`;
2509
+ const dataText = t.data && typeof t.data === "object" ? JSON.stringify(t.data) : "";
2510
+ return React.createElement(
2511
+ "div",
2512
+ { key: k, className: "iterate-obs-row", style: { flexDirection: "column", alignItems: "flex-start" } },
2513
+ React.createElement(
2514
+ "div",
2515
+ { className: "iterate-obs-bar", style: { width: "100%" } },
2516
+ React.createElement("span", { className: "iterate-obs-chip" }, String(t.type || "?")),
2517
+ React.createElement(
2518
+ "span",
2519
+ { className: "iterate-obs-head-meta" },
2520
+ `${typeof t.round === "number" ? `Round ${t.round} \xB7 ` : ""}${String(t.timestamp || "")}`
2521
+ )
2522
+ ),
2523
+ dataText ? React.createElement("div", { className: "iterate-obs-code" }, dataText) : null
2524
+ );
2525
+ });
2526
+ if (rows.length === 0) {
2527
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u65E0\u5339\u914D\u7684\u65F6\u95F4\u7EBF\u6761\u76EE");
2528
+ }
2529
+ return React.createElement(
2530
+ "div",
2531
+ {},
2532
+ React.createElement(
2533
+ "div",
2534
+ { className: "iterate-obs-bar", style: { marginBottom: 8 } },
2535
+ React.createElement(
2536
+ "select",
2537
+ {
2538
+ className: "iterate-filter-select",
2539
+ value: timelineType,
2540
+ "aria-label": "\u6309\u65F6\u95F4\u7EBF\u7C7B\u578B\u7B5B\u9009",
2541
+ onChange: (e) => setTimelineType(e.target.value)
2542
+ },
2543
+ React.createElement("option", { value: "" }, "\u5168\u90E8\u7C7B\u578B"),
2544
+ ...types.map((t) => React.createElement("option", { key: t, value: t }, t))
2545
+ ),
2546
+ React.createElement("input", {
2547
+ className: "iterate-filter-search",
2548
+ type: "search",
2549
+ placeholder: "\u641C\u7D22\u65F6\u95F4\u7EBF\u2026",
2550
+ "aria-label": "\u641C\u7D22\u65F6\u95F4\u7EBF",
2551
+ value: timelineSearch,
2552
+ onChange: (e) => setTimelineSearch(e.target.value)
2553
+ }),
2554
+ React.createElement("span", { className: "iterate-filter-count" }, `${filtered.length}/${entries.length}`)
2555
+ ),
2556
+ ...rows
2557
+ );
2558
+ };
2559
+ const renderLive = () => {
2560
+ const liveEntries = manifest.live || [];
2561
+ if (liveEntries.length === 0) {
2562
+ return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u5B9E\u65F6\u6D3B\u52A8");
2563
+ }
2564
+ const rows = liveEntries.map((e, i) => {
2565
+ const type = String(e.type || "unknown");
2566
+ const meta = OBS_LIVE_META[type] || { label: String(e.type || "?"), color: OBS_LIVE_FALLBACK_COLOR };
2567
+ const tool = String(e.tool || "");
2568
+ const target = String(e.target || "");
2569
+ const time = formatObsLiveTime(e.ts);
2570
+ return React.createElement(
2571
+ "div",
2572
+ { key: `live-${i}`, className: "iterate-obs-row" },
2573
+ React.createElement("span", { className: "iterate-obs-livebadge", style: { color: meta.color } }, meta.label),
2574
+ tool ? React.createElement("span", { className: "iterate-obs-chip" }, tool) : null,
2575
+ React.createElement("span", { className: "iterate-obs-msg" }, target || "\u2014"),
2576
+ React.createElement("span", { className: "iterate-obs-head-meta" }, time)
2577
+ );
2578
+ });
2579
+ return React.createElement(
2580
+ "div",
2581
+ {},
2582
+ React.createElement(
2583
+ "div",
2584
+ { className: "iterate-obs-bar", style: { marginBottom: 8 } },
2585
+ React.createElement("b", {}, "\u5B9E\u65F6\u6D3B\u52A8"),
2586
+ React.createElement("span", { className: "iterate-obs-chip" }, `${liveEntries.length} \u9879`)
2587
+ ),
2588
+ ...rows
2589
+ );
2590
+ };
2591
+ const renderBody = () => {
2592
+ switch (tab) {
2593
+ case "live":
2594
+ return renderLive();
2595
+ case "f1":
2596
+ return renderThreads();
2597
+ case "f2":
2598
+ return renderTrend();
2599
+ case "f3":
2600
+ return renderFindings();
2601
+ case "f4":
2602
+ return renderFixes();
2603
+ case "f5":
2604
+ return renderCheckpoint();
2605
+ case "f6":
2606
+ return renderConsole();
2607
+ case "f7":
2608
+ return renderTimeline();
2609
+ default:
2610
+ return null;
2611
+ }
2612
+ };
2613
+ return React.createElement(
2614
+ "div",
2615
+ { "data-iterate-root": "", "data-iterate": "obs", className: "iterate-obs" },
2616
+ React.createElement(
2617
+ "div",
2618
+ { className: "iterate-obs-head", "data-closed": open ? void 0 : "", onClick: () => setOpen((v) => !v) },
2619
+ React.createElement("span", { className: "iterate-obs-title" }, "iterate \u89C2\u6D4B\u53F0"),
2620
+ React.createElement("span", { className: "iterate-obs-badge", "data-live": live ? "" : void 0 }, live ? "\u8FD0\u884C\u4E2D" : "\u5DF2\u7ED3\u675F"),
2621
+ React.createElement("span", { className: "iterate-obs-head-meta" }, headMeta || "runtime"),
2622
+ React.createElement("button", {
2623
+ className: "iterate-btn",
2624
+ "data-ghost": "",
2625
+ onClick: (e) => {
2626
+ e.stopPropagation();
2627
+ setOpen((v) => !v);
2628
+ }
2629
+ }, open ? "\u6536\u8D77" : "\u5C55\u5F00")
2630
+ ),
2631
+ open ? React.createElement(
2632
+ "div",
2633
+ {},
2634
+ React.createElement(
2635
+ "div",
2636
+ { className: "iterate-obs-tabs" },
2637
+ ...OBS_TABS.map(
2638
+ (t) => React.createElement("button", {
2639
+ key: t.key,
2640
+ className: "iterate-obs-tab",
2641
+ "data-active": tab === t.key ? "" : void 0,
2642
+ onClick: () => setTab(t.key)
2643
+ }, t.label)
2644
+ )
2645
+ ),
2646
+ React.createElement("div", { className: "iterate-obs-body" }, renderBody())
2647
+ ) : null
2648
+ );
2649
+ }
1843
2650
  function selectTurnTail(owner) {
1844
2651
  if (!owner) return null;
1845
2652
  if (findReportInObject(owner.turn, void 0, 24) || scanSessionForReport(owner.turn)) return { matched: true };
@@ -1880,6 +2687,13 @@ function apply(ctx) {
1880
2687
  (props) => React.createElement(ConvergenceDashboard, props)
1881
2688
  )
1882
2689
  );
2690
+ slotsSvc.inject(
2691
+ "conversation.input.dock",
2692
+ () => slotsSvc?.register(
2693
+ { name: "conversation.input.dock", id: "iterate-observatory", order: 91 },
2694
+ (props) => React.createElement(ObservatoryPanel, props)
2695
+ )
2696
+ );
1883
2697
  slotsSvc.inject(
1884
2698
  "conversation.chat.turnTail",
1885
2699
  () => slotsSvc?.register(