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/README.md +41 -1
- package/README.zh-CN.md +39 -1
- package/dist/approval-gate.js +92 -0
- package/dist/config-loader.js +4 -0
- package/dist/index.js +15 -5
- package/dist/live.js +155 -0
- package/dist/paths.js +4 -0
- package/dist/session-hooks.js +89 -0
- package/dist/skill-prompt.js +56 -4
- package/dist/tools/transcript.js +324 -0
- package/dist/transcript.js +421 -0
- package/lib/client.js +814 -0
- package/lib/parse.js +276 -0
- package/package.json +1 -1
- package/src/approval-gate.ts +119 -0
- package/src/client/index.ts +663 -6
- package/src/config-loader.ts +4 -0
- package/src/index.ts +17 -6
- package/src/live.ts +185 -0
- package/src/paths.ts +5 -0
- package/src/session-hooks.ts +90 -0
- package/src/skill-prompt.ts +56 -4
- package/src/tools/transcript.ts +334 -0
- package/src/transcript.ts +475 -0
- package/src/types.ts +117 -0
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(
|