iterate-plugin 3.5.7 → 3.7.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 +97 -22
- package/README.zh-CN.md +79 -19
- package/dist/approval-gate.js +143 -23
- package/dist/atomic-fs.js +148 -17
- package/dist/config-loader.js +516 -35
- package/dist/config-write.js +83 -22
- package/dist/evidence.js +141 -87
- package/dist/file-lock.js +193 -0
- package/dist/index.js +61 -28
- package/dist/jobs.js +56 -11
- package/dist/live.js +54 -14
- package/dist/meta-review.js +132 -28
- package/dist/method-scope.js +31 -8
- package/dist/review-scope.js +32 -9
- package/dist/review.js +96 -17
- package/dist/session-hooks.js +61 -14
- package/dist/skill-prompt.js +396 -66
- package/dist/tools/checkpoint.js +85 -19
- package/dist/tools/config.js +44 -2
- package/dist/tools/context.js +22 -5
- package/dist/tools/decision-log.js +106 -28
- package/dist/tools/defense-events.js +90 -31
- package/dist/tools/defense-store.js +28 -3
- package/dist/tools/experience-bank.js +108 -43
- package/dist/tools/experience-store.js +54 -4
- package/dist/tools/fix.js +347 -119
- package/dist/tools/history.js +68 -14
- package/dist/tools/present.js +80 -0
- package/dist/tools/prune.js +150 -53
- package/dist/tools/quality-gate.js +52 -2
- package/dist/tools/quality-store.js +17 -1
- package/dist/tools/review.js +158 -7
- package/dist/tools/transcript.js +173 -36
- package/dist/tools/triage.js +67 -12
- package/dist/tools/validate.js +115 -24
- package/dist/transcript.js +267 -4
- package/dist/working-directory.js +114 -0
- package/lib/client.js +1912 -512
- package/lib/parse.js +1001 -103
- package/package.json +16 -9
- package/src/approval-gate.ts +160 -23
- package/src/atomic-fs.ts +152 -17
- package/src/client/i18n.ts +517 -0
- package/src/client/index.ts +1031 -369
- package/src/client/session-cache.ts +54 -0
- package/src/config-loader.ts +549 -36
- package/src/config-write.ts +108 -26
- package/src/evidence.ts +194 -92
- package/src/file-lock.ts +211 -0
- package/src/index.ts +60 -29
- package/src/jobs.ts +63 -12
- package/src/live.ts +50 -12
- package/src/meta-review.ts +151 -34
- package/src/method-scope.ts +31 -8
- package/src/review-scope.ts +33 -10
- package/src/review.ts +96 -17
- package/src/session-hooks.ts +60 -13
- package/src/skill-prompt.ts +396 -66
- package/src/tools/checkpoint.ts +81 -19
- package/src/tools/config.ts +44 -2
- package/src/tools/context.ts +25 -5
- package/src/tools/decision-log.ts +114 -23
- package/src/tools/defense-events.ts +87 -31
- package/src/tools/defense-store.ts +30 -3
- package/src/tools/experience-bank.ts +112 -44
- package/src/tools/experience-store.ts +54 -5
- package/src/tools/fix.ts +258 -20
- package/src/tools/history.ts +74 -15
- package/src/tools/present.ts +89 -0
- package/src/tools/prune.ts +154 -51
- package/src/tools/quality-gate.ts +57 -2
- package/src/tools/quality-store.ts +18 -1
- package/src/tools/review.ts +155 -6
- package/src/tools/transcript.ts +178 -36
- package/src/tools/triage.ts +70 -13
- package/src/tools/validate.ts +138 -32
- package/src/transcript.ts +279 -4
- package/src/types.ts +29 -1
- package/src/working-directory.ts +126 -0
package/lib/client.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* iterate-plugin client source-digest: 7da1ef5af84458a763f4b0ad8cd75ba2de3fb16b9831e00c0a9767bc16fece65 */
|
|
1
2
|
window.__ModuleLoader__.load({ id: "iterate-plugin", factory: (require) => {
|
|
2
3
|
var module = { exports: {} };
|
|
3
4
|
var exports = module.exports;
|
|
@@ -60,6 +61,16 @@ function safeKeys(o) {
|
|
|
60
61
|
return [];
|
|
61
62
|
}
|
|
62
63
|
}
|
|
64
|
+
function coerceJsonNode(v) {
|
|
65
|
+
if (typeof v !== "string") return v;
|
|
66
|
+
const t2 = v.trim();
|
|
67
|
+
if (!t2.startsWith("{") && !t2.startsWith("[")) return v;
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(t2);
|
|
70
|
+
} catch {
|
|
71
|
+
return v;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
63
74
|
function scanSessionForResume(obj, seen, maxDepth = 20) {
|
|
64
75
|
if (maxDepth <= 0) return 0;
|
|
65
76
|
if (!obj || typeof obj !== "object") return 0;
|
|
@@ -165,7 +176,8 @@ function isReviewReport(obj) {
|
|
|
165
176
|
obj
|
|
166
177
|
);
|
|
167
178
|
const convergence = safeGet(o, "convergence");
|
|
168
|
-
|
|
179
|
+
if (!convergence || typeof convergence !== "object" || Array.isArray(convergence)) return false;
|
|
180
|
+
return Array.isArray(safeGet(o, "findings")) && Array.isArray(safeGet(o, "rounds"));
|
|
169
181
|
}
|
|
170
182
|
function findReportInObject(obj, seen, maxDepth = 20) {
|
|
171
183
|
if (maxDepth <= 0) return null;
|
|
@@ -213,19 +225,16 @@ function scanSessionForReport(session) {
|
|
|
213
225
|
const call = calls[i];
|
|
214
226
|
if (!call) continue;
|
|
215
227
|
if (safeGet(call, "tool") === "iterate_review" || String(safeGet(call, "tool") ?? "").endsWith("iterate_review")) {
|
|
216
|
-
const result = safeGet(call, "result");
|
|
228
|
+
const result = coerceJsonNode(safeGet(call, "result"));
|
|
217
229
|
if (result && typeof result === "object") {
|
|
218
230
|
const r = (
|
|
219
231
|
/** @type {Record<string, unknown>} */
|
|
220
232
|
result
|
|
221
233
|
);
|
|
222
234
|
const report = safeGet(r, "report");
|
|
223
|
-
if (report
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
report
|
|
227
|
-
);
|
|
228
|
-
}
|
|
235
|
+
if (isReviewReport(report)) return report;
|
|
236
|
+
const found = findReportInObject(result);
|
|
237
|
+
if (found) return found;
|
|
229
238
|
}
|
|
230
239
|
}
|
|
231
240
|
}
|
|
@@ -244,8 +253,24 @@ function scanSessionForReport(session) {
|
|
|
244
253
|
/** @type {Array<Record<string, unknown>>} */
|
|
245
254
|
msgCalls
|
|
246
255
|
);
|
|
247
|
-
for (
|
|
256
|
+
for (let j = calls.length - 1; j >= 0; j--) {
|
|
257
|
+
const call = calls[j];
|
|
248
258
|
if (!call) continue;
|
|
259
|
+
const callName = String(safeGet(call, "name") ?? safeGet(call, "tool") ?? "");
|
|
260
|
+
if (callName === "iterate_review" || callName.endsWith("iterate_review")) {
|
|
261
|
+
for (const key of ["result", "response", "message"]) {
|
|
262
|
+
const node = coerceJsonNode(safeGet(call, key));
|
|
263
|
+
if (!node || typeof node !== "object") continue;
|
|
264
|
+
const r = (
|
|
265
|
+
/** @type {Record<string, unknown>} */
|
|
266
|
+
node
|
|
267
|
+
);
|
|
268
|
+
const report = safeGet(r, "report");
|
|
269
|
+
if (isReviewReport(report)) return report;
|
|
270
|
+
const found = findReportInObject(node);
|
|
271
|
+
if (found) return found;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
249
274
|
const fn = safeGet(call, "function");
|
|
250
275
|
if (fn && typeof fn === "object") {
|
|
251
276
|
const f = (
|
|
@@ -315,11 +340,11 @@ function extractTranscript(obj, seen, depth) {
|
|
|
315
340
|
obj
|
|
316
341
|
);
|
|
317
342
|
if (safeGet(o, "operation") === "capture") {
|
|
318
|
-
const
|
|
319
|
-
if (
|
|
343
|
+
const t2 = safeGet(o, "transcript");
|
|
344
|
+
if (t2 && typeof t2 === "object" && isTranscriptManifest(t2)) {
|
|
320
345
|
return attachLive(
|
|
321
346
|
/** @type {Record<string, unknown>} */
|
|
322
|
-
|
|
347
|
+
t2,
|
|
323
348
|
o
|
|
324
349
|
);
|
|
325
350
|
}
|
|
@@ -402,10 +427,29 @@ function scanSessionForTranscript(session) {
|
|
|
402
427
|
if (!msg) continue;
|
|
403
428
|
const calls = safeGet(msg, "tool_calls");
|
|
404
429
|
if (Array.isArray(calls)) {
|
|
405
|
-
|
|
430
|
+
const callList = (
|
|
431
|
+
/** @type {Array<Record<string, unknown>>} */
|
|
432
|
+
calls
|
|
433
|
+
);
|
|
434
|
+
for (let j = callList.length - 1; j >= 0; j--) {
|
|
435
|
+
const call = callList[j];
|
|
406
436
|
if (!call) continue;
|
|
407
|
-
const
|
|
408
|
-
|
|
437
|
+
const fn = safeGet(call, "function");
|
|
438
|
+
const candidates = [
|
|
439
|
+
// Result surface first: `{ name|tool, result }` message tool calls
|
|
440
|
+
// (the same shape scanSessionForQualityGate already accepts).
|
|
441
|
+
safeGet(call, "result"),
|
|
442
|
+
safeGet(call, "response"),
|
|
443
|
+
safeGet(call, "message"),
|
|
444
|
+
safeGet(call, "arguments"),
|
|
445
|
+
fn && typeof fn === "object" ? safeGet(
|
|
446
|
+
/** @type {Record<string, unknown>} */
|
|
447
|
+
fn,
|
|
448
|
+
"arguments"
|
|
449
|
+
) : void 0
|
|
450
|
+
];
|
|
451
|
+
for (const args of candidates) {
|
|
452
|
+
if (args === void 0 || args === null) continue;
|
|
409
453
|
const found2 = extractTranscript(args);
|
|
410
454
|
if (found2) return found2;
|
|
411
455
|
}
|
|
@@ -415,7 +459,39 @@ function scanSessionForTranscript(session) {
|
|
|
415
459
|
if (found) return found;
|
|
416
460
|
}
|
|
417
461
|
}
|
|
418
|
-
|
|
462
|
+
const loose = findFirstInObject(session, isTranscriptManifest);
|
|
463
|
+
return loose ? (
|
|
464
|
+
/** @type {Record<string, unknown>} */
|
|
465
|
+
loose
|
|
466
|
+
) : null;
|
|
467
|
+
}
|
|
468
|
+
function normalizeValidations(raw) {
|
|
469
|
+
if (!Array.isArray(raw)) return [];
|
|
470
|
+
const out = [];
|
|
471
|
+
for (
|
|
472
|
+
const v of
|
|
473
|
+
/** @type {unknown[]} */
|
|
474
|
+
raw
|
|
475
|
+
) {
|
|
476
|
+
if (!v || typeof v !== "object") continue;
|
|
477
|
+
const rec = (
|
|
478
|
+
/** @type {Record<string, unknown>} */
|
|
479
|
+
v
|
|
480
|
+
);
|
|
481
|
+
const roundRaw = Number(rec.round);
|
|
482
|
+
const exitRaw = rec.exitCode;
|
|
483
|
+
const command = typeof rec.command === "string" ? rec.command : String(rec.command ?? "");
|
|
484
|
+
if (!command) continue;
|
|
485
|
+
const rejectReason = typeof rec.rejectReason === "string" ? rec.rejectReason : "";
|
|
486
|
+
out.push({
|
|
487
|
+
round: Number.isFinite(roundRaw) && roundRaw > 0 ? Math.floor(roundRaw) : 0,
|
|
488
|
+
command,
|
|
489
|
+
exitCode: typeof exitRaw === "number" && Number.isFinite(exitRaw) ? exitRaw : null,
|
|
490
|
+
allowed: rec.allowed === true,
|
|
491
|
+
...rejectReason ? { rejectReason } : {}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
return out;
|
|
419
495
|
}
|
|
420
496
|
function normalizeTranscript(manifest) {
|
|
421
497
|
const src = manifest && typeof manifest === "object" ? (
|
|
@@ -432,12 +508,12 @@ function normalizeTranscript(manifest) {
|
|
|
432
508
|
) : [];
|
|
433
509
|
const rounds = asArray(safeGet(src, "rounds")).map((r) => ({
|
|
434
510
|
round: asNum(safeGet(r, "round")),
|
|
435
|
-
threads: asArray(safeGet(r, "threads")).map((
|
|
436
|
-
dimension: asStr(safeGet(
|
|
437
|
-
attempt: asNum(safeGet(
|
|
438
|
-
messages: asArray(safeGet(
|
|
439
|
-
readFiles: asArray(safeGet(
|
|
440
|
-
findings: asArray(safeGet(
|
|
511
|
+
threads: asArray(safeGet(r, "threads")).map((t2) => ({
|
|
512
|
+
dimension: asStr(safeGet(t2, "dimension")),
|
|
513
|
+
attempt: asNum(safeGet(t2, "attempt")),
|
|
514
|
+
messages: asArray(safeGet(t2, "messages")).map((m) => typeof m === "string" ? m : ""),
|
|
515
|
+
readFiles: asArray(safeGet(t2, "readFiles")).map((f) => typeof f === "string" ? f : ""),
|
|
516
|
+
findings: asArray(safeGet(t2, "findings")).map((f) => ({ ...f }))
|
|
441
517
|
}))
|
|
442
518
|
}));
|
|
443
519
|
const cp = safeGet(src, "checkpoint");
|
|
@@ -480,7 +556,10 @@ function normalizeTranscript(manifest) {
|
|
|
480
556
|
target: asStr(safeGet(e, "target"))
|
|
481
557
|
})),
|
|
482
558
|
checkpoint,
|
|
483
|
-
timeline: asArray(safeGet(src, "timeline")).map((
|
|
559
|
+
timeline: asArray(safeGet(src, "timeline")).map((t2) => ({ ...t2 })),
|
|
560
|
+
// #6: per-round validation rows (command / exitCode / allowed), captured
|
|
561
|
+
// by the workflow and persisted through `iterate_transcript capture`.
|
|
562
|
+
validations: normalizeValidations(safeGet(src, "validations")),
|
|
484
563
|
nudge,
|
|
485
564
|
approval: ap && typeof ap === "object" ? { active: asBool(safeGet(ap, "active")), policy: asStr(safeGet(ap, "policy")) || "ask" } : { active: false, policy: "ask" }
|
|
486
565
|
};
|
|
@@ -515,6 +594,11 @@ function isDefenseEventsResult(obj) {
|
|
|
515
594
|
}
|
|
516
595
|
function findFirstInObject(obj, predicate, seen, maxDepth = 20) {
|
|
517
596
|
if (maxDepth <= 0) return null;
|
|
597
|
+
if (typeof obj === "string") {
|
|
598
|
+
const parsed = coerceJsonNode(obj);
|
|
599
|
+
if (parsed === obj) return null;
|
|
600
|
+
return findFirstInObject(parsed, predicate, seen, maxDepth - 1);
|
|
601
|
+
}
|
|
518
602
|
if (!obj || typeof obj !== "object") return null;
|
|
519
603
|
const s = seen || /* @__PURE__ */ new Set();
|
|
520
604
|
if (s.has(obj)) return null;
|
|
@@ -551,7 +635,7 @@ function findExperienceResultInObject(obj) {
|
|
|
551
635
|
function findDefenseResultInObject(obj) {
|
|
552
636
|
return findFirstInObject(obj, (o) => isDefenseEventsResult(o));
|
|
553
637
|
}
|
|
554
|
-
function latestToolResultNode(session, toolName) {
|
|
638
|
+
function latestToolResultNode(session, toolName, find) {
|
|
555
639
|
if (!session || typeof session !== "object") return null;
|
|
556
640
|
const s = (
|
|
557
641
|
/** @type {Record<string, unknown>} */
|
|
@@ -569,9 +653,9 @@ function latestToolResultNode(session, toolName) {
|
|
|
569
653
|
const tool = String(safeGet(call, "tool") ?? "");
|
|
570
654
|
if (tool !== toolName && !tool.endsWith(toolName)) continue;
|
|
571
655
|
const result = safeGet(call, "result");
|
|
572
|
-
if (result !== void 0 && result !== null) return result;
|
|
656
|
+
if (result !== void 0 && result !== null && find(result)) return result;
|
|
573
657
|
const message = safeGet(call, "message");
|
|
574
|
-
if (message !== void 0 && message !== null) return message;
|
|
658
|
+
if (message !== void 0 && message !== null && find(message)) return message;
|
|
575
659
|
}
|
|
576
660
|
}
|
|
577
661
|
const messages = safeGet(s, "messages");
|
|
@@ -595,14 +679,15 @@ function latestToolResultNode(session, toolName) {
|
|
|
595
679
|
const name2 = String(safeGet(call, "name") ?? safeGet(call, "tool") ?? "");
|
|
596
680
|
if (name2 !== toolName && !name2.endsWith(toolName)) continue;
|
|
597
681
|
const result = safeGet(call, "result") ?? safeGet(call, "response") ?? safeGet(call, "message");
|
|
598
|
-
if (result !== void 0 && result !== null) return result;
|
|
682
|
+
if (result !== void 0 && result !== null && find(result)) return result;
|
|
599
683
|
}
|
|
600
684
|
}
|
|
601
685
|
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
602
686
|
const msg = msgs[i];
|
|
603
687
|
if (!msg) continue;
|
|
604
688
|
const content = safeGet(msg, "content");
|
|
605
|
-
if (content
|
|
689
|
+
if (content === void 0 || content === null) continue;
|
|
690
|
+
if (find(content)) return content;
|
|
606
691
|
}
|
|
607
692
|
}
|
|
608
693
|
return null;
|
|
@@ -651,11 +736,13 @@ function normalizeQualityGateSnapshot(raw) {
|
|
|
651
736
|
};
|
|
652
737
|
}
|
|
653
738
|
function scanSessionForQualityGate(session) {
|
|
654
|
-
const node = latestToolResultNode(session, "iterate_quality_gate");
|
|
655
|
-
if (node
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
739
|
+
const node = latestToolResultNode(session, "iterate_quality_gate", findQualityGateInObject);
|
|
740
|
+
if (node !== null) {
|
|
741
|
+
const found = findQualityGateInObject(node);
|
|
742
|
+
if (found) return normalizeQualityGateSnapshot(found);
|
|
743
|
+
}
|
|
744
|
+
const loose = findFirstInObject(session, isQualityGateSnapshot);
|
|
745
|
+
return loose ? normalizeQualityGateSnapshot(loose) : null;
|
|
659
746
|
}
|
|
660
747
|
function normalizeExperienceBankResult(raw) {
|
|
661
748
|
const src = raw && typeof raw === "object" ? (
|
|
@@ -695,17 +782,19 @@ function normalizeExperienceBankResult(raw) {
|
|
|
695
782
|
hitCount: asNum(safeGet(rec, "hitCount")),
|
|
696
783
|
lastHitAt: asStr(safeGet(rec, "lastHitAt")) || null,
|
|
697
784
|
files: asArr(safeGet(rec, "files")).map((f) => typeof f === "string" ? f : ""),
|
|
698
|
-
tags: asArr(safeGet(rec, "tags")).map((
|
|
785
|
+
tags: asArr(safeGet(rec, "tags")).map((t2) => typeof t2 === "string" ? t2 : "")
|
|
699
786
|
};
|
|
700
787
|
})
|
|
701
788
|
};
|
|
702
789
|
}
|
|
703
790
|
function scanSessionForExperienceBank(session) {
|
|
704
|
-
const node = latestToolResultNode(session, "iterate_experience");
|
|
705
|
-
if (node
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
791
|
+
const node = latestToolResultNode(session, "iterate_experience", findExperienceResultInObject);
|
|
792
|
+
if (node !== null) {
|
|
793
|
+
const found = findExperienceResultInObject(node);
|
|
794
|
+
if (found) return normalizeExperienceBankResult(found);
|
|
795
|
+
}
|
|
796
|
+
const loose = findFirstInObject(session, isExperienceBankResult);
|
|
797
|
+
return loose ? normalizeExperienceBankResult(loose) : null;
|
|
709
798
|
}
|
|
710
799
|
function normalizeDefenseEventsResult(raw) {
|
|
711
800
|
const src = raw && typeof raw === "object" ? (
|
|
@@ -757,11 +846,13 @@ function normalizeDefenseEventsResult(raw) {
|
|
|
757
846
|
};
|
|
758
847
|
}
|
|
759
848
|
function scanSessionForDefenseEvents(session) {
|
|
760
|
-
const node = latestToolResultNode(session, "iterate_defense_events");
|
|
761
|
-
if (node
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
849
|
+
const node = latestToolResultNode(session, "iterate_defense_events", findDefenseResultInObject);
|
|
850
|
+
if (node !== null) {
|
|
851
|
+
const found = findDefenseResultInObject(node);
|
|
852
|
+
if (found) return normalizeDefenseEventsResult(found);
|
|
853
|
+
}
|
|
854
|
+
const loose = findFirstInObject(session, isDefenseEventsResult);
|
|
855
|
+
return loose ? normalizeDefenseEventsResult(loose) : null;
|
|
765
856
|
}
|
|
766
857
|
function isRunSummary(obj) {
|
|
767
858
|
if (!obj || typeof obj !== "object") return false;
|
|
@@ -871,13 +962,14 @@ function normalizeReport(report) {
|
|
|
871
962
|
);
|
|
872
963
|
const rounds = (
|
|
873
964
|
/** @type {Array<unknown>} */
|
|
874
|
-
report.rounds
|
|
965
|
+
Array.isArray(report.rounds) ? report.rounds : []
|
|
875
966
|
);
|
|
876
967
|
const findings = (
|
|
877
968
|
/** @type {Array<Record<string, unknown>>} */
|
|
878
|
-
report.findings
|
|
969
|
+
Array.isArray(report.findings) ? report.findings : []
|
|
879
970
|
);
|
|
880
971
|
const totalRounds = typeof convergence.totalRounds === "number" ? convergence.totalRounds : rounds.length;
|
|
972
|
+
const explicitReason = typeof convergence.stoppedReason === "string" && convergence.stoppedReason.trim() ? convergence.stoppedReason : null;
|
|
881
973
|
const normalizedConvergence = {
|
|
882
974
|
totalRounds,
|
|
883
975
|
findingsByRound: Array.isArray(convergence.findingsByRound) ? convergence.findingsByRound : rounds.map((r) => {
|
|
@@ -888,7 +980,7 @@ function normalizeReport(report) {
|
|
|
888
980
|
return Array.isArray(rr?.findings) ? rr.findings.length : 0;
|
|
889
981
|
}),
|
|
890
982
|
converged: convergence.converged === true,
|
|
891
|
-
stoppedReason:
|
|
983
|
+
stoppedReason: explicitReason ?? (convergence.converged === true ? "converged" : rounds.length >= totalRounds ? "max_rounds_reached" : null)
|
|
892
984
|
};
|
|
893
985
|
let summary = report.summary;
|
|
894
986
|
if (!summary || typeof summary !== "object") {
|
|
@@ -921,17 +1013,18 @@ function normalizeReport(report) {
|
|
|
921
1013
|
};
|
|
922
1014
|
}
|
|
923
1015
|
function computeSummaryFromFindings(findings) {
|
|
1016
|
+
const list = Array.isArray(findings) ? findings : [];
|
|
924
1017
|
const counts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
925
|
-
const byDimension =
|
|
926
|
-
for (const f of
|
|
1018
|
+
const byDimension = /* @__PURE__ */ Object.create(null);
|
|
1019
|
+
for (const f of list) {
|
|
927
1020
|
if (!f || typeof f !== "object") continue;
|
|
928
1021
|
const sev = String(f.severity ?? "low");
|
|
929
|
-
if (sev
|
|
1022
|
+
if (Object.prototype.hasOwnProperty.call(counts, sev)) counts[sev]++;
|
|
930
1023
|
const dim = String(f.dimension ?? "unknown");
|
|
931
1024
|
byDimension[dim] = (byDimension[dim] ?? 0) + 1;
|
|
932
1025
|
}
|
|
933
1026
|
return {
|
|
934
|
-
totalFindings:
|
|
1027
|
+
totalFindings: list.length,
|
|
935
1028
|
critical: counts.critical,
|
|
936
1029
|
high: counts.high,
|
|
937
1030
|
medium: counts.medium,
|
|
@@ -952,7 +1045,7 @@ function computeConvergenceProgress(report) {
|
|
|
952
1045
|
function currentRoundNumber(report) {
|
|
953
1046
|
const rounds = (
|
|
954
1047
|
/** @type {Array<Record<string, unknown>>} */
|
|
955
|
-
report.rounds
|
|
1048
|
+
Array.isArray(report.rounds) ? report.rounds : []
|
|
956
1049
|
);
|
|
957
1050
|
let max = 0;
|
|
958
1051
|
for (const r of rounds) {
|
|
@@ -975,34 +1068,38 @@ function getTotalRounds(report) {
|
|
|
975
1068
|
function severityStats(report) {
|
|
976
1069
|
const findings = (
|
|
977
1070
|
/** @type {Array<Record<string, unknown>>} */
|
|
978
|
-
report.findings
|
|
1071
|
+
Array.isArray(report.findings) ? report.findings : []
|
|
979
1072
|
);
|
|
980
1073
|
const counts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
981
1074
|
for (const f of findings) {
|
|
982
1075
|
if (!f || typeof f !== "object") continue;
|
|
983
1076
|
const sev = String(f.severity ?? "low");
|
|
984
|
-
if (sev
|
|
1077
|
+
if (Object.prototype.hasOwnProperty.call(counts, sev)) counts[sev]++;
|
|
985
1078
|
}
|
|
986
1079
|
return counts;
|
|
987
1080
|
}
|
|
988
1081
|
function groupByDimension(report) {
|
|
989
1082
|
const findings = (
|
|
990
1083
|
/** @type {Array<Record<string, unknown>>} */
|
|
991
|
-
report.findings
|
|
1084
|
+
Array.isArray(report.findings) ? report.findings : []
|
|
992
1085
|
);
|
|
993
|
-
const groups =
|
|
1086
|
+
const groups = /* @__PURE__ */ Object.create(null);
|
|
994
1087
|
for (const f of findings) {
|
|
995
1088
|
if (!f || typeof f !== "object") continue;
|
|
996
1089
|
const dim = String(f.dimension ?? "unknown");
|
|
997
|
-
if (!groups
|
|
1090
|
+
if (!Object.prototype.hasOwnProperty.call(groups, dim)) groups[dim] = [];
|
|
998
1091
|
groups[dim].push(f);
|
|
999
1092
|
}
|
|
1000
1093
|
return groups;
|
|
1001
1094
|
}
|
|
1095
|
+
var TRIAGE_VERDICTS = (
|
|
1096
|
+
/** @type {const} */
|
|
1097
|
+
["keep", "skip", "ignore"]
|
|
1098
|
+
);
|
|
1002
1099
|
function buildTriageState(report) {
|
|
1003
1100
|
const findings = (
|
|
1004
1101
|
/** @type {Array<unknown>} */
|
|
1005
|
-
report.findings
|
|
1102
|
+
Array.isArray(report.findings) ? report.findings : []
|
|
1006
1103
|
);
|
|
1007
1104
|
const state = {};
|
|
1008
1105
|
for (let i = 0; i < findings.length; i++) {
|
|
@@ -1013,7 +1110,7 @@ function buildTriageState(report) {
|
|
|
1013
1110
|
function hashReport(report) {
|
|
1014
1111
|
const findings = (
|
|
1015
1112
|
/** @type {Array<Record<string, unknown>>} */
|
|
1016
|
-
report.findings
|
|
1113
|
+
Array.isArray(report.findings) ? report.findings : []
|
|
1017
1114
|
);
|
|
1018
1115
|
let h = 2166136261;
|
|
1019
1116
|
const mix = (s) => {
|
|
@@ -1058,17 +1155,27 @@ function buildApplyInstruction(entries) {
|
|
|
1058
1155
|
null,
|
|
1059
1156
|
2
|
|
1060
1157
|
);
|
|
1061
|
-
return
|
|
1158
|
+
return `\u8BF7\u8C03\u7528 \`iterate_triage\` \u5E94\u7528\u4E0B\u9762\u7684 known_intentional \u6761\u76EE\uFF08\u7C98\u8D34\u540E\u7531\u6A21\u578B\u8C03\u7528 \`iterate_triage\` \u7684 \`operation: "apply"\` \u5199\u5165\u914D\u7F6E\uFF1A\u81EA\u52A8\u53BB\u91CD\u3001\u5199\u5165\u524D\u5907\u4EFD\u3001\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\uFF09\uFF1A
|
|
1062
1159
|
|
|
1063
1160
|
\`\`\`json
|
|
1064
1161
|
${payload}
|
|
1162
|
+
\`\`\`
|
|
1163
|
+
|
|
1164
|
+
\u5199\u5165\u5B8C\u6210\u540E\u8BF7\u590D\u5236\u9762\u677F\u7684\u300C\u56DE\u8BFB\u5DF2\u5199\u5165\u6761\u76EE\u300D\u6307\u4EE4\uFF08\`iterate_triage\` \`operation: "list"\`\uFF09\u6838\u5BF9\u7ED3\u679C\u3002`;
|
|
1165
|
+
}
|
|
1166
|
+
function buildTriageReadbackInstruction() {
|
|
1167
|
+
return `\u8BF7\u8C03\u7528 \`iterate_triage\` \u56DE\u8BFB\u5F53\u524D\u5DF2\u5199\u5165\u914D\u7F6E\u7684 known_intentional \u6761\u76EE\uFF0C\u5E76\u628A\u6BCF\u6761\u7684 file / line / dimension / reason \u539F\u6837\u5217\u51FA\u6765\u4F9B\u6211\u6838\u5BF9\uFF1A
|
|
1168
|
+
|
|
1169
|
+
\`\`\`json
|
|
1170
|
+
${JSON.stringify({ operation: "list" }, null, 2)}
|
|
1065
1171
|
\`\`\``;
|
|
1066
1172
|
}
|
|
1067
1173
|
function collectIgnoredEntries(triageState, findings) {
|
|
1174
|
+
const list = Array.isArray(findings) ? findings : [];
|
|
1068
1175
|
const entries = [];
|
|
1069
1176
|
for (const [idx, verdict] of Object.entries(triageState)) {
|
|
1070
1177
|
if (verdict !== "ignore") continue;
|
|
1071
|
-
const finding =
|
|
1178
|
+
const finding = list[Number(idx)];
|
|
1072
1179
|
if (!finding) continue;
|
|
1073
1180
|
entries.push({
|
|
1074
1181
|
file: String(finding.file ?? ""),
|
|
@@ -1079,6 +1186,207 @@ function collectIgnoredEntries(triageState, findings) {
|
|
|
1079
1186
|
}
|
|
1080
1187
|
return entries;
|
|
1081
1188
|
}
|
|
1189
|
+
var CONTENT_PLACEHOLDER = "<\u7531\u6A21\u578B\u586B\u5199\uFF1A\u4FEE\u590D\u540E\u7684\u5B8C\u6574\u6587\u4EF6\u5185\u5BB9>";
|
|
1190
|
+
var ROUND_PLACEHOLDER = "<\u7531\u6A21\u578B\u586B\u5199\uFF1A\u5F53\u524D\u8FED\u4EE3\u8F6E\u6B21\uFF08\u22651 \u7684\u6574\u6570\uFF09>";
|
|
1191
|
+
var START_INSTRUCTION_FULL = '\u5F00\u59CB\u4E00\u6B21\u5B8C\u6574\u8FED\u4EE3\uFF08\u5BA1\u67E5 \u2192 \u4FEE\u590D \u2192 \u9A8C\u8BC1 \u2192 \u590D\u76D8\uFF09\uFF1A\u8BF7\u8C03\u7528 `workflow` \u5DE5\u5177\u8FD0\u884C iterate \u5DE5\u4F5C\u6D41\uFF0Cmode: "normal"\uFF0C\u6309\u7CFB\u7EDF\u63D0\u793A\u7684 Iterate Workflow \u6267\u884C plan \u2192 \u591A\u7EBF\u7A0B\u5E76\u884C\u8BC4\u5BA1 \u2192 \u5206\u8BCA \u2192 \u539F\u5B50\u4FEE\u590D \u2192 \u6BCF\u8F6E\u9A8C\u8BC1 \u2192 \u5FAA\u73AF\u76F4\u81F3\u6536\u655B\u6216\u8FBE\u5230 max_rounds \u2192 \u7EC8\u62A5\u5E76 `iterate_transcript` capture\u3002\u5F00\u8DD1\u524D\u5148 `iterate_config({operation:"read"})` \u68C0\u67E5 validation.commands\uFF1A\u82E5\u4E3A\u7A7A\uFF0C\u5148\u505C\u4E0B\u6765\u63D0\u793A\u6211\u7528 `iterate_config` \u5199\u5165\u9A8C\u8BC1\u547D\u4EE4\uFF0C\u5426\u5219\u672C\u8F6E\u8FED\u4EE3\u4E0D\u53D7\u4EFB\u4F55\u6D4B\u8BD5\u4FDD\u62A4\u3002';
|
|
1192
|
+
var START_INSTRUCTION_REVIEW_ONLY = '\u5F00\u59CB\u4E00\u6B21\u4EC5\u8BC4\u5BA1\uFF08dry-run\uFF0C\u4E0D\u4FEE\u6539\u4EFB\u4F55\u6587\u4EF6\uFF09\uFF1A\u8BF7\u8C03\u7528 `workflow` \u5DE5\u5177\u8FD0\u884C iterate \u5DE5\u4F5C\u6D41\uFF0Cmode: "dry-run"\uFF0C\u6309\u7CFB\u7EDF\u63D0\u793A\u7684 Iterate Workflow \u6267\u884C plan \u2192 \u591A\u7EBF\u7A0B\u5E76\u884C\u8BC4\u5BA1 \u2192 \u5206\u8BCA \u2192 `iterate_review aggregate` / `meta-review` \u7EC8\u62A5\u5E76 `iterate_transcript` capture\uFF1B\u5168\u7A0B\u4E0D\u8981\u8C03\u7528 `iterate_fix` / `iterate_rollback`\u3002';
|
|
1193
|
+
var START_INSTRUCTIONS = {
|
|
1194
|
+
full: START_INSTRUCTION_FULL,
|
|
1195
|
+
reviewOnly: START_INSTRUCTION_REVIEW_ONLY
|
|
1196
|
+
};
|
|
1197
|
+
function fixFindingPayload(finding) {
|
|
1198
|
+
const f = finding && typeof finding === "object" ? finding : {};
|
|
1199
|
+
const file = String(f.file ?? "");
|
|
1200
|
+
return {
|
|
1201
|
+
file,
|
|
1202
|
+
...typeof f.line === "number" && f.line > 0 ? { line: f.line } : {},
|
|
1203
|
+
dimension: String(f.dimension ?? ""),
|
|
1204
|
+
severity: String(f.severity ?? ""),
|
|
1205
|
+
summary: String(f.summary ?? ""),
|
|
1206
|
+
...f.failure_scenario ? { failure_scenario: String(f.failure_scenario) } : {},
|
|
1207
|
+
...f.suggested_fix ? { suggested_fix: String(f.suggested_fix) } : {}
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
function buildFixInstruction(finding, opts) {
|
|
1211
|
+
const payload = fixFindingPayload(finding);
|
|
1212
|
+
if (!payload.file) return "";
|
|
1213
|
+
const round = opts && Number.isInteger(opts.round) ? opts.round : null;
|
|
1214
|
+
const call = {
|
|
1215
|
+
file: payload.file,
|
|
1216
|
+
content: CONTENT_PLACEHOLDER,
|
|
1217
|
+
finding: payload,
|
|
1218
|
+
round: round !== null ? round : ROUND_PLACEHOLDER
|
|
1219
|
+
};
|
|
1220
|
+
return [
|
|
1221
|
+
"\u8BF7\u4FEE\u590D\u4E0B\u9762\u8FD9\u4E00\u4E2A finding\u3002`iterate_fix` \u4E00\u6B21\u53EA\u63A5\u53D7\u4E00\u4E2A finding\uFF0C\u4E14 file / content / finding / round \u56DB\u4E2A\u53C2\u6570\u5168\u90E8\u5FC5\u586B\uFF1A",
|
|
1222
|
+
`\u6B65\u9AA4 1\uFF1A\u8BFB\u53D6 \`${payload.file}\`\uFF0C\u6309 finding \u751F\u6210\u4FEE\u590D\u540E\u7684\u3010\u5B8C\u6574\u6587\u4EF6\u5185\u5BB9\u3011\uFF08content \u5360\u4F4D\u5FC5\u987B\u6362\u6210\u771F\u5B9E\u6587\u4EF6\u5185\u5BB9\uFF09\u3002`,
|
|
1223
|
+
`\u6B65\u9AA4 2\uFF1A\u8C03\u7528 \`iterate_fix\`\uFF0C\u628A content \u4E0E round \u586B\u6210\u771F\u5B9E\u503C${round !== null ? `\uFF08\u672C\u8F6E round = ${round}\uFF09` : ""}\uFF1A`,
|
|
1224
|
+
"```json",
|
|
1225
|
+
JSON.stringify(call, null, 2),
|
|
1226
|
+
"```",
|
|
1227
|
+
'\u9700\u8981\u8D85\u8FC7 atomic \u884C\u6570\u4E0A\u9650\u65F6\uFF0C\u53E6\u52A0 "force": true\u3002'
|
|
1228
|
+
].join("\n");
|
|
1229
|
+
}
|
|
1230
|
+
function buildAssignFixesInstruction(findings) {
|
|
1231
|
+
const list = (Array.isArray(findings) ? findings : []).filter((f) => f && typeof f === "object");
|
|
1232
|
+
if (list.length === 0) return "";
|
|
1233
|
+
const payload = list.map((f) => fixFindingPayload(f)).filter((p) => p.file);
|
|
1234
|
+
if (payload.length === 0) return "";
|
|
1235
|
+
return [
|
|
1236
|
+
`\u8BF7\u4FEE\u590D\u4E0B\u9762\u8FD9 ${payload.length} \u4E2A findings\u3002\u6CE8\u610F\uFF1A\`iterate_fix\` \u6BCF\u6B21\u53EA\u5904\u7406\u3010\u4E00\u4E2A\u3011finding\uFF08\u6CA1\u6709\u6570\u7EC4\u5165\u53C2\uFF09\uFF0C\u8BF7\u9010\u4E2A\u8C03\u7528 iterate_fix\u2014\u2014\u4E00\u6B21\u4E00\u4E2A finding\uFF0C\u5904\u7406\u5B8C\u4E00\u4E2A\u518D\u53D1\u8D77\u4E0B\u4E00\u4E2A\uFF0C\u7EDD\u4E0D\u8981\u628A\u6574\u6279\u6570\u7EC4\u5F53\u4F5C\u53C2\u6570\u4F20\u5165\u3002`,
|
|
1237
|
+
"\u6BCF\u4E2A finding \u90FD\u8D70\u540C\u6837\u7684\u4E24\u6B65\uFF1A\u2460 \u8BFB\u53D6\u6587\u4EF6\u751F\u6210\u4FEE\u590D\u540E\u7684\u5B8C\u6574 content\uFF1B\u2461 \u8C03\u7528 iterate_fix\uFF08file / content / finding / round \u5FC5\u586B\uFF0Ccontent \u4E0E round \u7531\u4F60\u586B\u5199\uFF09\u3002",
|
|
1238
|
+
"\u5F85\u4FEE\u590D\u6E05\u5355\uFF1A",
|
|
1239
|
+
"```json",
|
|
1240
|
+
JSON.stringify(payload, null, 2),
|
|
1241
|
+
"```"
|
|
1242
|
+
].join("\n");
|
|
1243
|
+
}
|
|
1244
|
+
function buildArchitecturalFixInstruction(findings) {
|
|
1245
|
+
const list = (Array.isArray(findings) ? findings : []).filter((f) => f && typeof f === "object");
|
|
1246
|
+
const lines = [
|
|
1247
|
+
'\u8BF7\u6279\u51C6\u5E76\u6267\u884C\u67B6\u6784\u578B\u4FEE\u590D\uFF08\u5141\u8BB8\u8D85\u8FC7 atomic \u884C\u6570\u4E0A\u9650\uFF09\uFF1A\u5BF9\u6BCF\u4E2A\u67B6\u6784\u578B finding \u9010\u4E2A\u8C03\u7528 `iterate_fix`\uFF0C\u4E00\u6B21\u4E00\u4E2A finding\uFF0C\u5E76\u663E\u5F0F\u8BBE\u7F6E `"force": true`\uFF1B',
|
|
1248
|
+
"file / content / finding / round \u56DB\u4E2A\u5FC5\u586B\u53C2\u6570\u4E00\u4E2A\u90FD\u4E0D\u80FD\u5C11\uFF08content = \u4FEE\u590D\u540E\u7684\u5B8C\u6574\u6587\u4EF6\u5185\u5BB9\uFF0Cround = \u5F53\u524D\u8FED\u4EE3\u8F6E\u6B21\uFF0C\u4E24\u8005\u7531\u4F60\u586B\u5199\uFF09\u3002"
|
|
1249
|
+
];
|
|
1250
|
+
if (list.length > 0) {
|
|
1251
|
+
lines.push("\u672C\u9879\u76EE\u5269\u4F59\u67B6\u6784\u578B finding\uFF1A", "```json", JSON.stringify(list.map((f) => fixFindingPayload(f)), null, 2), "```");
|
|
1252
|
+
}
|
|
1253
|
+
return lines.join("\n");
|
|
1254
|
+
}
|
|
1255
|
+
function buildCheckpointResumeInstruction() {
|
|
1256
|
+
return `\u8BF7\u8C03\u7528 \`iterate_checkpoint\` \u4ECE\u65AD\u70B9\u6062\u590D\u8FED\u4EE3\uFF08\u52A0\u8F7D\u65AD\u70B9\u5E76\u628A resumeCount +1\uFF09\uFF1A
|
|
1257
|
+
|
|
1258
|
+
\`\`\`json
|
|
1259
|
+
${JSON.stringify({ operation: "resume" }, null, 2)}
|
|
1260
|
+
\`\`\``;
|
|
1261
|
+
}
|
|
1262
|
+
function buildCheckpointClearInstruction() {
|
|
1263
|
+
return `\u8BF7\u8C03\u7528 \`iterate_checkpoint\` \u6E05\u9664\u5F53\u524D\u65AD\u70B9\uFF1A
|
|
1264
|
+
|
|
1265
|
+
\`\`\`json
|
|
1266
|
+
${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
1267
|
+
\`\`\``;
|
|
1268
|
+
}
|
|
1269
|
+
function buildQualityGateQueryInstruction() {
|
|
1270
|
+
return `\u8BF7\u8C03\u7528 \`iterate_quality_gate\` \u67E5\u8BE2\u5F53\u524D\u8D28\u91CF\u95E8\u7981\u72B6\u6001\uFF08\u8BFB\u53D6\u78C1\u76D8\u4E0A\u6301\u4E45\u5316\u7684\u8BC1\u4E66\uFF09\uFF1A
|
|
1271
|
+
|
|
1272
|
+
\`\`\`json
|
|
1273
|
+
${JSON.stringify({ operation: "read" }, null, 2)}
|
|
1274
|
+
\`\`\``;
|
|
1275
|
+
}
|
|
1276
|
+
function buildQualityGateClearInstruction() {
|
|
1277
|
+
return `\u8BF7\u8C03\u7528 \`iterate_quality_gate\` \u6E05\u9664\u5F53\u524D\u8D28\u91CF\u95E8\u7981\u8BC1\u4E66\uFF1A
|
|
1278
|
+
|
|
1279
|
+
\`\`\`json
|
|
1280
|
+
${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
1281
|
+
\`\`\``;
|
|
1282
|
+
}
|
|
1283
|
+
function buildRollbackInstruction(id) {
|
|
1284
|
+
return `\u8BF7\u8C03\u7528 \`iterate_rollback\` \u56DE\u6EDA\u4EE5\u4E0B\u4FEE\u590D\uFF1A
|
|
1285
|
+
|
|
1286
|
+
\`\`\`json
|
|
1287
|
+
${JSON.stringify({ id: String(id || "") }, null, 2)}
|
|
1288
|
+
\`\`\``;
|
|
1289
|
+
}
|
|
1290
|
+
function buildExperienceListInstruction() {
|
|
1291
|
+
return `\u8BF7\u8C03\u7528 \`iterate_experience\` \u5217\u51FA\u7ECF\u9A8C\u94F6\u884C\u4E2D\u7684\u6240\u6709\u7ECF\u9A8C\uFF0C\u5E76\u628A\u5B8C\u6574\u8FD4\u56DE\u7ED3\u679C\u56DE\u663E\u5230\u4F1A\u8BDD\u91CC\uFF1A
|
|
1292
|
+
|
|
1293
|
+
\`\`\`json
|
|
1294
|
+
${JSON.stringify({ operation: "list" }, null, 2)}
|
|
1295
|
+
\`\`\``;
|
|
1296
|
+
}
|
|
1297
|
+
function buildDefenseEventsListInstruction() {
|
|
1298
|
+
return `\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u5217\u51FA\u5168\u90E8\u9632\u5FA1\u4E8B\u4EF6\uFF0C\u5E76\u628A\u5B8C\u6574\u8FD4\u56DE\u7ED3\u679C\u56DE\u663E\u5230\u4F1A\u8BDD\u91CC\uFF1A
|
|
1299
|
+
|
|
1300
|
+
\`\`\`json
|
|
1301
|
+
${JSON.stringify({ operation: "list" }, null, 2)}
|
|
1302
|
+
\`\`\``;
|
|
1303
|
+
}
|
|
1304
|
+
function buildDiskSnapshotInstruction() {
|
|
1305
|
+
const lines = [
|
|
1306
|
+
"\u8BF7\u4ECE\u78C1\u76D8\u62C9\u53D6 iterate \u5FEB\u7167\uFF1A\u9010\u4E2A\u8C03\u7528\u4E0B\u9762\u7684\u53EA\u8BFB\u5DE5\u5177\uFF0C\u5E76\u628A\u6BCF\u4E2A\u5DE5\u5177\u7684\u3010\u5B8C\u6574\u8FD4\u56DE\u7ED3\u679C\u3011\u539F\u6837\u56DE\u663E\u5230\u4F1A\u8BDD\u91CC\uFF08\u4E0D\u8981\u53EA\u7ED9\u6458\u8981\u2014\u2014\u63D2\u4EF6\u9762\u677F\u9760\u8FD9\u4E9B\u56DE\u663E\u586B\u5145\uFF09\uFF1A"
|
|
1307
|
+
];
|
|
1308
|
+
DISK_SNAPSHOT_SOURCES.forEach((s, i) => {
|
|
1309
|
+
lines.push(`${i + 1}. \`${s.tool}\`\uFF08${s.label}\uFF09\uFF1A${JSON.stringify(s.args)}`);
|
|
1310
|
+
});
|
|
1311
|
+
lines.push("\u56DE\u663E\u540E\uFF0C\u89C2\u6D4B\u53F0\u5B9E\u65F6\u6D41 / F2 \u8DE8\u8F6E\u5BF9\u6BD4 / F5 \u65AD\u70B9 / F8 \u8D28\u91CF\u95E8\u7981 / F9 \u7ECF\u9A8C\u94F6\u884C / F10 \u9632\u5FA1\u4E8B\u4EF6\u4F1A\u81EA\u52A8\u8BFB\u53D6\u8FD9\u4E9B\u7ED3\u679C\u3002");
|
|
1312
|
+
return lines.join("\n");
|
|
1313
|
+
}
|
|
1314
|
+
var DISK_SNAPSHOT_SOURCES = [
|
|
1315
|
+
{ tool: "iterate_status", args: {}, label: "\u8FD0\u884C\u72B6\u6001\u6C47\u603B" },
|
|
1316
|
+
{ tool: "iterate_transcript", args: { operation: "read" }, label: "transcript \u89C2\u6D4B\u6E05\u5355" },
|
|
1317
|
+
{ tool: "iterate_quality_gate", args: { operation: "read" }, label: "\u8D28\u91CF\u95E8\u7981\u8BC1\u4E66" },
|
|
1318
|
+
{ tool: "iterate_experience", args: { operation: "list" }, label: "\u7ECF\u9A8C\u94F6\u884C" },
|
|
1319
|
+
{ tool: "iterate_defense_events", args: { operation: "list" }, label: "\u9632\u5FA1\u4E8B\u4EF6\u6D41" },
|
|
1320
|
+
{ tool: "iterate_history", args: {}, label: "\u51B3\u7B56\u65E5\u5FD7\u4E0E\u4FEE\u590D\u6CE8\u518C\u8868\uFF08\u8DE8\u8F6E/\u8DE8\u4F1A\u8BDD\u5BF9\u6BD4\uFF09" }
|
|
1321
|
+
];
|
|
1322
|
+
function toolCalledInSession(session, toolName) {
|
|
1323
|
+
if (!session || typeof session !== "object" || typeof toolName !== "string" || !toolName) return false;
|
|
1324
|
+
const s = (
|
|
1325
|
+
/** @type {Record<string, unknown>} */
|
|
1326
|
+
session
|
|
1327
|
+
);
|
|
1328
|
+
const toolCalls = safeGet(s, "toolCalls");
|
|
1329
|
+
if (Array.isArray(toolCalls)) {
|
|
1330
|
+
for (
|
|
1331
|
+
const call of
|
|
1332
|
+
/** @type {unknown[]} */
|
|
1333
|
+
toolCalls
|
|
1334
|
+
) {
|
|
1335
|
+
if (!call || typeof call !== "object") continue;
|
|
1336
|
+
const t2 = String(safeGet(
|
|
1337
|
+
/** @type {Record<string, unknown>} */
|
|
1338
|
+
call,
|
|
1339
|
+
"tool"
|
|
1340
|
+
) ?? "");
|
|
1341
|
+
if (t2 === toolName || t2.endsWith(toolName)) return true;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
const messages = safeGet(s, "messages");
|
|
1345
|
+
if (Array.isArray(messages)) {
|
|
1346
|
+
for (
|
|
1347
|
+
const msg of
|
|
1348
|
+
/** @type {unknown[]} */
|
|
1349
|
+
messages
|
|
1350
|
+
) {
|
|
1351
|
+
if (!msg || typeof msg !== "object") continue;
|
|
1352
|
+
const calls = safeGet(
|
|
1353
|
+
/** @type {Record<string, unknown>} */
|
|
1354
|
+
msg,
|
|
1355
|
+
"tool_calls"
|
|
1356
|
+
);
|
|
1357
|
+
if (!Array.isArray(calls)) continue;
|
|
1358
|
+
for (
|
|
1359
|
+
const call of
|
|
1360
|
+
/** @type {unknown[]} */
|
|
1361
|
+
calls
|
|
1362
|
+
) {
|
|
1363
|
+
if (!call || typeof call !== "object") continue;
|
|
1364
|
+
const c = (
|
|
1365
|
+
/** @type {Record<string, unknown>} */
|
|
1366
|
+
call
|
|
1367
|
+
);
|
|
1368
|
+
const name2 = String(safeGet(c, "name") ?? safeGet(c, "tool") ?? "");
|
|
1369
|
+
if (name2 === toolName || name2.endsWith(toolName)) return true;
|
|
1370
|
+
const fn = safeGet(c, "function");
|
|
1371
|
+
if (fn && typeof fn === "object" && String(safeGet(
|
|
1372
|
+
/** @type {Record<string, unknown>} */
|
|
1373
|
+
fn,
|
|
1374
|
+
"name"
|
|
1375
|
+
) ?? "") === toolName) {
|
|
1376
|
+
return true;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
return false;
|
|
1382
|
+
}
|
|
1383
|
+
function diskEmptyStateText(pulled, topic, lang = "zh") {
|
|
1384
|
+
const subject = topic || (lang === "en" ? "data" : "\u6570\u636E");
|
|
1385
|
+
if (lang === "en") {
|
|
1386
|
+
return pulled ? `Pulled from disk: ${subject} has no records yet (the disk is empty).` : `Not pulled from disk yet: ${subject} lives under the project's .iterate/ \u2014 copy the "Pull disk snapshot" instruction back into the session and it will fill in.`;
|
|
1387
|
+
}
|
|
1388
|
+
return pulled ? `\u5DF2\u4ECE\u78C1\u76D8\u62C9\u53D6\uFF1A${subject}\u6682\u65E0\u8BB0\u5F55\uFF08\u78C1\u76D8\u65E0\u6570\u636E\uFF09\u3002` : `\u5C1A\u672A\u4ECE\u78C1\u76D8\u62C9\u53D6${subject}\uFF1A\u6570\u636E\u5B58\u653E\u5728\u9879\u76EE .iterate/ \u4E0B\uFF0C\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u590D\u5236\u6307\u4EE4\u53D1\u56DE\u4F1A\u8BDD\u5373\u53EF\u56DE\u586B\u3002`;
|
|
1389
|
+
}
|
|
1082
1390
|
function normalizeFindingFilter(filter) {
|
|
1083
1391
|
const f = filter && typeof filter === "object" ? filter : {};
|
|
1084
1392
|
const severities = Array.isArray(f.severities) ? f.severities.filter((s) => SEVERITY_ORDER.includes(String(s))) : [];
|
|
@@ -1122,6 +1430,7 @@ function buildFilterOptions(findings) {
|
|
|
1122
1430
|
const severities = SEVERITY_ORDER.map((value) => ({ value, count: 0 }));
|
|
1123
1431
|
const dimCounts = {};
|
|
1124
1432
|
for (const f of list) {
|
|
1433
|
+
if (!f || typeof f !== "object") continue;
|
|
1125
1434
|
const sev = String(f.severity ?? "low");
|
|
1126
1435
|
const sv = severities.find((s) => s.value === sev);
|
|
1127
1436
|
if (sv) sv.count++;
|
|
@@ -1141,7 +1450,7 @@ function countVerdicts(triageState) {
|
|
|
1141
1450
|
return counts;
|
|
1142
1451
|
}
|
|
1143
1452
|
function batchSetVerdict(triageState, indices, verdict) {
|
|
1144
|
-
if (verdict
|
|
1453
|
+
if (!TRIAGE_VERDICTS.includes(verdict)) return triageState;
|
|
1145
1454
|
if (!Array.isArray(indices) || indices.length === 0) return triageState;
|
|
1146
1455
|
const next = { ...triageState };
|
|
1147
1456
|
for (const idx of indices) {
|
|
@@ -1211,27 +1520,29 @@ function trendMax(points) {
|
|
|
1211
1520
|
}
|
|
1212
1521
|
return max;
|
|
1213
1522
|
}
|
|
1214
|
-
function stoppedReasonLabel(reason) {
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1523
|
+
function stoppedReasonLabel(reason, lang = "zh") {
|
|
1524
|
+
const zh = {
|
|
1525
|
+
converged: "\u5DF2\u7ED3\u675F \xB7 \u5DF2\u6536\u655B\uFF08\u65E0\u65B0\u53D1\u73B0\uFF09",
|
|
1526
|
+
max_rounds_reached: "\u5DF2\u7ED3\u675F \xB7 \u8FBE\u5230\u8F6E\u6570\u4E0A\u9650",
|
|
1527
|
+
aborted_by_validation: "\u5DF2\u7ED3\u675F \xB7 \u9A8C\u8BC1\u5931\u8D25\u540E\u56DE\u6EDA\u505C\u6B62",
|
|
1528
|
+
aborted_by_config: "\u5DF2\u7ED3\u675F \xB7 \u9A8C\u8BC1\u547D\u4EE4\u4E0D\u5728\u767D\u540D\u5355\uFF08\u914D\u7F6E\u9700\u4FEE\u590D\uFF09",
|
|
1529
|
+
inconclusive: "\u5DF2\u7ED3\u675F \xB7 \u8F6E\u6B21\u7ED3\u8BBA\u4E0D\u660E\uFF08\u5BA1\u67E5\u8F93\u51FA\u65E0\u6548\uFF09",
|
|
1530
|
+
schema_invalid: "\u5DF2\u7ED3\u675F \xB7 \u5BA1\u67E5\u8F93\u51FA\u8FDE\u7EED schema \u6821\u9A8C\u5931\u8D25",
|
|
1531
|
+
no_usable_reviewer_output: "\u5DF2\u7ED3\u675F \xB7 \u65E0\u53EF\u7528\u5BA1\u67E5\u8F93\u51FA"
|
|
1532
|
+
};
|
|
1533
|
+
const en = {
|
|
1534
|
+
converged: "Done \xB7 converged (no new findings)",
|
|
1535
|
+
max_rounds_reached: "Done \xB7 reached the round limit",
|
|
1536
|
+
aborted_by_validation: "Done \xB7 rolled back after a validation failure",
|
|
1537
|
+
aborted_by_config: "Done \xB7 validation command not allow-listed (config needs fixing)",
|
|
1538
|
+
inconclusive: "Done \xB7 inconclusive rounds (invalid review output)",
|
|
1539
|
+
schema_invalid: "Done \xB7 repeated schema validation failures in review output",
|
|
1540
|
+
no_usable_reviewer_output: "Done \xB7 no usable review output"
|
|
1541
|
+
};
|
|
1542
|
+
if (lang === "en") return en[reason] ?? (reason ? `Done \xB7 ${reason}` : "Done");
|
|
1543
|
+
return zh[reason] ?? (reason ? `\u5DF2\u7ED3\u675F \xB7 ${reason}` : "\u5DF2\u7ED3\u675F");
|
|
1544
|
+
}
|
|
1545
|
+
function buildCompletionSummary(report, lang = "zh") {
|
|
1235
1546
|
const conv = (
|
|
1236
1547
|
/** @type {Record<string, unknown>} */
|
|
1237
1548
|
report.convergence ?? {}
|
|
@@ -1240,18 +1551,74 @@ function buildCompletionSummary(report) {
|
|
|
1240
1551
|
const total = getTotalRounds(report);
|
|
1241
1552
|
const stats = severityStats(report);
|
|
1242
1553
|
const converged = conv.converged === true;
|
|
1243
|
-
const reason = converged ? "\u5DF2\u6536\u655B" : `\u5DF2\u8FBE\u6700\u5927\u8F6E\u6570 ${total}`;
|
|
1244
1554
|
const totalFindings = stats.critical + stats.high + stats.medium + stats.low;
|
|
1555
|
+
if (lang === "en") {
|
|
1556
|
+
const reason2 = converged ? "converged" : `reached the round limit ${total}`;
|
|
1557
|
+
return `iterate review complete \xB7 ${rounds}/${total} rounds \xB7 ${totalFindings} findings \xB7 ${reason2}`;
|
|
1558
|
+
}
|
|
1559
|
+
const reason = converged ? "\u5DF2\u6536\u655B" : `\u5DF2\u8FBE\u6700\u5927\u8F6E\u6570 ${total}`;
|
|
1245
1560
|
return `iterate \u8BC4\u5BA1\u5B8C\u6210 \xB7 ${rounds}/${total} \u8F6E \xB7 ${totalFindings} \u9879\u53D1\u73B0 \xB7 ${reason}`;
|
|
1246
1561
|
}
|
|
1247
1562
|
var CONFIG_EDIT_FIELDS = [
|
|
1248
|
-
{
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
{
|
|
1254
|
-
|
|
1563
|
+
{
|
|
1564
|
+
key: "goal",
|
|
1565
|
+
label: { zh: "\u76EE\u6807", en: "Goal" },
|
|
1566
|
+
hint: { zh: "\u4E00\u53E5\u8BDD\u63CF\u8FF0\u672C\u6B21\u8FED\u4EE3\u76EE\u6807\uFF08\u5B57\u7B26\u4E32\uFF09", en: "One-sentence description of this iteration's goal (a string)" }
|
|
1567
|
+
},
|
|
1568
|
+
{
|
|
1569
|
+
key: "dimensions",
|
|
1570
|
+
label: { zh: "\u5BA1\u67E5\u7EF4\u5EA6", en: "Review dimensions" },
|
|
1571
|
+
hint: { zh: '\u6570\u7EC4\uFF0C\u5982 ["correctness","security"]', en: 'Array, e.g. ["correctness","security"]' }
|
|
1572
|
+
},
|
|
1573
|
+
{
|
|
1574
|
+
key: "max_rounds",
|
|
1575
|
+
label: { zh: "\u6700\u5927\u8F6E\u6570", en: "Max rounds" },
|
|
1576
|
+
hint: { zh: "\u6B63\u6574\u6570", en: "Positive integer" }
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
key: "review.scope",
|
|
1580
|
+
label: { zh: "\u5BA1\u67E5\u8303\u56F4", en: "Review scope" },
|
|
1581
|
+
hint: { zh: '"full" \u6216 "changed-only"', en: '"full" or "changed-only"' }
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
key: "reasoning_effort",
|
|
1585
|
+
label: { zh: "\u5BA1\u67E5\u63A8\u7406\u5F3A\u5EA6", en: "Review reasoning effort" },
|
|
1586
|
+
hint: { zh: '"low" / "medium" / "high"\uFF0C\u7F3A\u7701\u8DDF\u968F\u6A21\u578B\u9ED8\u8BA4', en: '"low" / "medium" / "high"; omitted = follow the model default' }
|
|
1587
|
+
},
|
|
1588
|
+
{
|
|
1589
|
+
key: "validation.commands",
|
|
1590
|
+
label: { zh: "\u9A8C\u8BC1\u547D\u4EE4", en: "Validation commands" },
|
|
1591
|
+
hint: {
|
|
1592
|
+
zh: '\u6570\u7EC4\uFF0C\u6BCF\u9879\u662F\u4E00\u6761\u767D\u540D\u5355\u547D\u4EE4\u5B57\u7B26\u4E32\uFF0C\u5982 ["npm test","npm run lint"]\uFF1B\u6BCF\u8F6E\u9A8C\u8BC1\u9010\u6761\u6267\u884C\u3002\u7559\u7A7A\u6570\u7EC4 = \u4E0D\u9A8C\u8BC1\uFF08\u672C\u8F6E\u8FED\u4EE3\u4E0D\u53D7\u4EFB\u4F55\u6D4B\u8BD5\u4FDD\u62A4\uFF09',
|
|
1593
|
+
en: 'Array of allow-listed command strings, e.g. ["npm test","npm run lint"]; run one by one every round. An empty array = no verification (this iteration runs with no test protection)'
|
|
1594
|
+
}
|
|
1595
|
+
},
|
|
1596
|
+
{
|
|
1597
|
+
key: "language",
|
|
1598
|
+
label: { zh: "\u754C\u9762\u8BED\u8A00", en: "Interface language" },
|
|
1599
|
+
hint: {
|
|
1600
|
+
zh: '"zh" \u6216 "en"\uFF08\u9ED8\u8BA4 "en"\uFF09\uFF1A\u63A7\u5236\u5BA1\u6279\u7406\u7531\u3001\u9632\u5FA1\u4E8B\u4EF6\u6807\u7B7E\u7B49\u9762\u5411\u4EBA\u6587\u6848\u7684\u8BED\u8A00',
|
|
1601
|
+
en: '"zh" or "en" (default "en"): language of human-facing copy such as approval rationales and defense-event labels'
|
|
1602
|
+
}
|
|
1603
|
+
},
|
|
1604
|
+
{
|
|
1605
|
+
key: "personalization.known_intentional",
|
|
1606
|
+
label: { zh: "\u5DF2\u77E5\u6709\u610F\u95EE\u9898", en: "Known intentional issues" },
|
|
1607
|
+
hint: {
|
|
1608
|
+
zh: "\u6570\u7EC4\uFF0C\u6BCF\u9879 {file, line?, dimension, reason}\uFF1B\u547D\u4E2D\u6761\u76EE\u5728\u8BC4\u5BA1\u4E2D\u88AB\u8DF3\u8FC7\u3002\u53EF\u7528\u5206\u8BCA\u9762\u677F\u7684\u300C\u56DE\u8BFB\u5DF2\u5199\u5165\u6761\u76EE\u300D\u6838\u5BF9",
|
|
1609
|
+
en: `Array of {file, line?, dimension, reason}; findings hitting an entry are skipped in review. Verify via the triage panel's "read back written entries"`
|
|
1610
|
+
}
|
|
1611
|
+
},
|
|
1612
|
+
{
|
|
1613
|
+
key: "atomic.max_lines",
|
|
1614
|
+
label: { zh: "\u539F\u5B50\u4FEE\u590D\u4E0A\u9650\u884C\u6570", en: "Atomic fix line cap" },
|
|
1615
|
+
hint: { zh: "\u6B63\u6574\u6570", en: "Positive integer" }
|
|
1616
|
+
},
|
|
1617
|
+
{
|
|
1618
|
+
key: "git.push_per_round",
|
|
1619
|
+
label: { zh: "\u6BCF\u8F6E\u63A8\u9001", en: "Push per round" },
|
|
1620
|
+
hint: { zh: "true / false", en: "true / false" }
|
|
1621
|
+
}
|
|
1255
1622
|
];
|
|
1256
1623
|
function buildConfigEditGuide() {
|
|
1257
1624
|
const lines = [
|
|
@@ -1259,16 +1626,115 @@ function buildConfigEditGuide() {
|
|
|
1259
1626
|
"---------------------",
|
|
1260
1627
|
"\u914D\u7F6E\u6587\u4EF6\uFF1A\u9879\u76EE\u6839\u76EE\u5F55 iterate.config.yaml\u3002",
|
|
1261
1628
|
"",
|
|
1629
|
+
"\u26A0 \u672A\u914D\u7F6E validation.commands \u65F6\u8FED\u4EE3\u4E0D\u88AB\u4FDD\u62A4\uFF1A\u6BCF\u8F6E validate \u62FF\u5230\u7A7A\u7ED3\u679C\u4F1A\u88AB\u5F53\u4F5C",
|
|
1630
|
+
" \u201C\u9A8C\u8BC1\u901A\u8FC7\u201D\uFF0C\u7B49\u4E8E\u5728\u6CA1\u6709\u4EFB\u4F55\u6D4B\u8BD5\u4FDD\u62A4\u7684\u60C5\u51B5\u4E0B\u7EE7\u7EED\u6536\u655B\u3002\u7B2C\u4E00\u6B21\u542F\u52A8\u8FED\u4EE3\u524D\uFF0C",
|
|
1631
|
+
" \u8BF7\u5148\u7ED9 validation.commands \u914D\u4E0A\u81F3\u5C11\u4E00\u6761\u771F\u5B9E\u547D\u4EE4\uFF08\u5982 npm test\uFF09\u3002",
|
|
1632
|
+
"",
|
|
1262
1633
|
"\u53EF\u7F16\u8F91\u5B57\u6BB5\uFF1A",
|
|
1263
|
-
...CONFIG_EDIT_FIELDS.map((f) => `- ${f.key}\uFF08${f.label}\uFF09\uFF1A${f.hint}`),
|
|
1634
|
+
...CONFIG_EDIT_FIELDS.map((f) => `- ${f.key}\uFF08${f.label.zh}\uFF09\uFF1A${f.hint.zh}`),
|
|
1264
1635
|
"",
|
|
1265
1636
|
"\u8BA9\u6A21\u578B\u5E2E\u4F60\u6539\uFF1A",
|
|
1266
1637
|
'1. \u8C03\u7528 iterate_config({ operation: "read" }) \u67E5\u770B\u5F53\u524D\u914D\u7F6E\uFF1B',
|
|
1267
|
-
"2. \u8BF4\u660E\u60F3\u6539\u7684\u5B57\u6BB5\uFF0C\u4F8B\u5982\u300C\u628A max_rounds \u6539\u6210 5\
|
|
1268
|
-
'3. \u6A21\u578B\u4F1A\u8C03\u7528 iterate_config({ operation: "write", updates: {...} }) \u5199\u5165\uFF0C\u5199\u5165\u524D\u81EA\u52A8\u5907\u4EFD\uFF0C\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\u3002'
|
|
1638
|
+
"2. \u8BF4\u660E\u60F3\u6539\u7684\u5B57\u6BB5\uFF0C\u4F8B\u5982\u300C\u628A max_rounds \u6539\u6210 5\uFF0Cvalidation.commands \u52A0\u4E0A npm test\u300D\uFF1B",
|
|
1639
|
+
'3. \u6A21\u578B\u4F1A\u8C03\u7528 iterate_config({ operation: "write", updates: {...} }) \u5199\u5165\uFF0C\u5199\u5165\u524D\u81EA\u52A8\u5907\u4EFD\uFF0C\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\u3002',
|
|
1640
|
+
"",
|
|
1641
|
+
"\u4E5F\u53EF\u4EE5\u7528\u8BBE\u7F6E\u9875\u7684\u201C\u9009\u5B57\u6BB5 \u2192 \u751F\u6210\u5199\u5165\u6307\u4EE4\u201D\u76F4\u63A5\u590D\u5236\u7B2C 3 \u6B65\u7684\u6307\u4EE4\u7C98\u8D34\u7ED9\u6A21\u578B\u3002"
|
|
1269
1642
|
];
|
|
1270
1643
|
return lines.join("\n");
|
|
1271
1644
|
}
|
|
1645
|
+
function buildConfigEditInstruction(desiredChanges) {
|
|
1646
|
+
const payload = JSON.stringify({ operation: "write", updates: desiredChanges }, null, 2);
|
|
1647
|
+
return `\u8BF7\u8C03\u7528 \`iterate_config\` \u5199\u5165\u4EE5\u4E0B\u914D\u7F6E\u66F4\u65B0\uFF1A
|
|
1648
|
+
|
|
1649
|
+
\`\`\`json
|
|
1650
|
+
${payload}
|
|
1651
|
+
\`\`\``;
|
|
1652
|
+
}
|
|
1653
|
+
var CONFIG_VALUE_PLACEHOLDER = "<\u7531\u6A21\u578B\u586B\u5199\uFF1A\u8BE5\u5B57\u6BB5\u7684\u65B0\u503C>";
|
|
1654
|
+
function configFieldByKey(key) {
|
|
1655
|
+
if (typeof key !== "string" || !key) return null;
|
|
1656
|
+
return CONFIG_EDIT_FIELDS.find((f) => f.key === key) ?? null;
|
|
1657
|
+
}
|
|
1658
|
+
function buildConfigFieldInstruction(key, value) {
|
|
1659
|
+
const field = configFieldByKey(key);
|
|
1660
|
+
const label = field ? field.label.zh : key;
|
|
1661
|
+
const hint = field ? field.hint.zh : "\u89C1 iterate.config.yaml";
|
|
1662
|
+
const updates = { [key]: value === void 0 ? CONFIG_VALUE_PLACEHOLDER : value };
|
|
1663
|
+
const payload = buildConfigEditInstruction(updates);
|
|
1664
|
+
return [
|
|
1665
|
+
`\u914D\u7F6E\u5B57\u6BB5 \xB7 ${label}\uFF08${key}\uFF09`,
|
|
1666
|
+
`\u5B57\u6BB5\u8BF4\u660E\uFF1A${hint}`,
|
|
1667
|
+
"",
|
|
1668
|
+
payload,
|
|
1669
|
+
"",
|
|
1670
|
+
'\u5199\u5165\u524D\u8BF7\u5148 `operation: "read"` \u5C55\u793A\u5F53\u524D\u503C\uFF1B\u5199\u5165\u7531\u5DE5\u5177\u81EA\u52A8\u5907\u4EFD\uFF0C\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\u3002'
|
|
1671
|
+
].join("\n");
|
|
1672
|
+
}
|
|
1673
|
+
function buildConfigReadInstruction() {
|
|
1674
|
+
return `\u8BF7\u8C03\u7528 \`iterate_config\` \u56DE\u8BFB\u5F53\u524D\u751F\u6548\u914D\u7F6E\uFF0C\u5E76\u628A\u5B8C\u6574\u7ED3\u679C\uFF08\u542B config \u5B57\u6BB5\uFF09\u539F\u6837\u56DE\u663E\uFF0C\u4F9B\u6211\u6838\u5BF9\u754C\u9762\u4E0A\u5C55\u793A\u7684\u5F53\u524D\u503C\uFF1A
|
|
1675
|
+
|
|
1676
|
+
\`\`\`json
|
|
1677
|
+
${JSON.stringify({ operation: "read" }, null, 2)}
|
|
1678
|
+
\`\`\``;
|
|
1679
|
+
}
|
|
1680
|
+
var CONFIG_TOP_LEVEL_KEYS = [...new Set(CONFIG_EDIT_FIELDS.map((f) => f.key.split(".")[0]))];
|
|
1681
|
+
function isEffectiveConfig(v) {
|
|
1682
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return false;
|
|
1683
|
+
const rec = (
|
|
1684
|
+
/** @type {Record<string, unknown>} */
|
|
1685
|
+
v
|
|
1686
|
+
);
|
|
1687
|
+
let hits = 0;
|
|
1688
|
+
for (const key of CONFIG_TOP_LEVEL_KEYS) {
|
|
1689
|
+
if (Object.hasOwn(rec, key)) hits += 1;
|
|
1690
|
+
if (hits >= 3) return true;
|
|
1691
|
+
}
|
|
1692
|
+
return false;
|
|
1693
|
+
}
|
|
1694
|
+
function findConfigInResult(v) {
|
|
1695
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return null;
|
|
1696
|
+
const rec = (
|
|
1697
|
+
/** @type {Record<string, unknown>} */
|
|
1698
|
+
v
|
|
1699
|
+
);
|
|
1700
|
+
const cfg = safeGet(rec, "config");
|
|
1701
|
+
if (isEffectiveConfig(cfg)) return (
|
|
1702
|
+
/** @type {Record<string, unknown>} */
|
|
1703
|
+
cfg
|
|
1704
|
+
);
|
|
1705
|
+
return isEffectiveConfig(v) ? rec : null;
|
|
1706
|
+
}
|
|
1707
|
+
function scanSessionForConfigRead(session) {
|
|
1708
|
+
const node = latestToolResultNode(session, "iterate_config", findConfigInResult);
|
|
1709
|
+
if (node !== null) {
|
|
1710
|
+
const cfg = findConfigInResult(node);
|
|
1711
|
+
if (cfg) return cfg;
|
|
1712
|
+
}
|
|
1713
|
+
const loose = findFirstInObject(session, isEffectiveConfig);
|
|
1714
|
+
return loose ? (
|
|
1715
|
+
/** @type {Record<string, unknown>} */
|
|
1716
|
+
loose
|
|
1717
|
+
) : null;
|
|
1718
|
+
}
|
|
1719
|
+
function configValueByPath(config, key) {
|
|
1720
|
+
if (!config || typeof config !== "object" || typeof key !== "string" || !key) {
|
|
1721
|
+
return { resolved: false, value: void 0 };
|
|
1722
|
+
}
|
|
1723
|
+
let node = (
|
|
1724
|
+
/** @type {unknown} */
|
|
1725
|
+
config
|
|
1726
|
+
);
|
|
1727
|
+
for (const part of key.split(".")) {
|
|
1728
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return { resolved: false, value: void 0 };
|
|
1729
|
+
const rec = (
|
|
1730
|
+
/** @type {Record<string, unknown>} */
|
|
1731
|
+
node
|
|
1732
|
+
);
|
|
1733
|
+
if (!Object.hasOwn(rec, part)) return { resolved: false, value: void 0 };
|
|
1734
|
+
node = rec[part];
|
|
1735
|
+
}
|
|
1736
|
+
return { resolved: true, value: node };
|
|
1737
|
+
}
|
|
1272
1738
|
var VERDICT_SHORTCUTS = {
|
|
1273
1739
|
y: "keep",
|
|
1274
1740
|
Y: "keep",
|
|
@@ -1278,7 +1744,8 @@ var VERDICT_SHORTCUTS = {
|
|
|
1278
1744
|
A: "ignore"
|
|
1279
1745
|
};
|
|
1280
1746
|
function keyToVerdict(key) {
|
|
1281
|
-
|
|
1747
|
+
const verdict = VERDICT_SHORTCUTS[key];
|
|
1748
|
+
return verdict !== void 0 && TRIAGE_VERDICTS.includes(verdict) ? verdict : null;
|
|
1282
1749
|
}
|
|
1283
1750
|
function allVerdictKeys(triageState) {
|
|
1284
1751
|
const state = triageState && typeof triageState === "object" ? triageState : {};
|
|
@@ -1324,9 +1791,9 @@ function buildRuntimeStatusGuide() {
|
|
|
1324
1791
|
}
|
|
1325
1792
|
function filterLiveEntries(entries, type) {
|
|
1326
1793
|
const list = Array.isArray(entries) ? entries : [];
|
|
1327
|
-
const
|
|
1328
|
-
if (!
|
|
1329
|
-
return list.filter((e) => e && typeof e === "object" && String(e.type ?? "") ===
|
|
1794
|
+
const t2 = typeof type === "string" ? type.trim() : "";
|
|
1795
|
+
if (!t2) return list.slice();
|
|
1796
|
+
return list.filter((e) => e && typeof e === "object" && String(e.type ?? "") === t2);
|
|
1330
1797
|
}
|
|
1331
1798
|
function filterTimelineEntries(entries, opts) {
|
|
1332
1799
|
const list = Array.isArray(entries) ? entries : [];
|
|
@@ -1334,29 +1801,30 @@ function filterTimelineEntries(entries, opts) {
|
|
|
1334
1801
|
const type = typeof o.type === "string" ? o.type : "";
|
|
1335
1802
|
const round = typeof o.round === "string" ? o.round : "";
|
|
1336
1803
|
const q = typeof o.search === "string" ? o.search.trim().toLowerCase() : "";
|
|
1337
|
-
const filtered = list.filter((
|
|
1338
|
-
if (!
|
|
1339
|
-
if (type && String(
|
|
1340
|
-
if (round && String(
|
|
1804
|
+
const filtered = list.filter((t2) => {
|
|
1805
|
+
if (!t2 || typeof t2 !== "object") return false;
|
|
1806
|
+
if (type && String(t2.type ?? "") !== type) return false;
|
|
1807
|
+
if (round && String(t2.round ?? "") !== round) return false;
|
|
1341
1808
|
if (q) {
|
|
1342
1809
|
let dataText = "";
|
|
1343
1810
|
try {
|
|
1344
|
-
dataText = JSON.stringify(
|
|
1811
|
+
dataText = JSON.stringify(t2.data ?? {});
|
|
1345
1812
|
} catch {
|
|
1346
|
-
dataText =
|
|
1813
|
+
dataText = t2.data === void 0 ? "{}" : String(t2.data);
|
|
1347
1814
|
}
|
|
1348
|
-
const hay = [String(
|
|
1815
|
+
const hay = [String(t2.type ?? ""), String(t2.round ?? ""), dataText].join(" ").toLowerCase();
|
|
1349
1816
|
if (hay.indexOf(q) < 0) return false;
|
|
1350
1817
|
}
|
|
1351
1818
|
return true;
|
|
1352
1819
|
});
|
|
1353
1820
|
return filtered.slice().sort((a, b) => String(b.timestamp ?? "").localeCompare(String(a.timestamp ?? "")));
|
|
1354
1821
|
}
|
|
1355
|
-
function serializeObservatoryExport(manifest, live) {
|
|
1822
|
+
function serializeObservatoryExport(manifest, live, extra) {
|
|
1356
1823
|
const payload = {
|
|
1357
1824
|
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1358
1825
|
manifest: manifest && typeof manifest === "object" ? manifest : null,
|
|
1359
|
-
live: Array.isArray(live) ? live : []
|
|
1826
|
+
live: Array.isArray(live) ? live : [],
|
|
1827
|
+
...pickExportExtras(extra)
|
|
1360
1828
|
};
|
|
1361
1829
|
try {
|
|
1362
1830
|
return JSON.stringify(payload, null, 2);
|
|
@@ -1364,6 +1832,62 @@ function serializeObservatoryExport(manifest, live) {
|
|
|
1364
1832
|
return JSON.stringify({ exportedAt: payload.exportedAt, manifest: null, live: [] }, null, 2);
|
|
1365
1833
|
}
|
|
1366
1834
|
}
|
|
1835
|
+
var EXPORT_EXTRA_KEYS = ["qualityGate", "experienceBank", "defenseEvents", "report"];
|
|
1836
|
+
function pickExportExtras(extra) {
|
|
1837
|
+
const out = {};
|
|
1838
|
+
if (!extra || typeof extra !== "object") return out;
|
|
1839
|
+
for (const key of EXPORT_EXTRA_KEYS) {
|
|
1840
|
+
if (!Object.prototype.hasOwnProperty.call(extra, key)) continue;
|
|
1841
|
+
const value = extra[key];
|
|
1842
|
+
if (value === void 0) continue;
|
|
1843
|
+
out[key] = value;
|
|
1844
|
+
}
|
|
1845
|
+
return out;
|
|
1846
|
+
}
|
|
1847
|
+
function buildRoundComparison(report, validations) {
|
|
1848
|
+
const rounds = /* @__PURE__ */ new Map();
|
|
1849
|
+
const rowsFor = (round) => {
|
|
1850
|
+
const n = Number(round);
|
|
1851
|
+
const key = Number.isFinite(n) && n > 0 ? Math.floor(n) : 0;
|
|
1852
|
+
if (!rounds.has(key)) {
|
|
1853
|
+
rounds.set(key, { round: key, findings: 0, fixed: 0, severities: /* @__PURE__ */ Object.create(null), validations: [] });
|
|
1854
|
+
}
|
|
1855
|
+
return rounds.get(key);
|
|
1856
|
+
};
|
|
1857
|
+
const list = report && Array.isArray(report.findings) ? report.findings : [];
|
|
1858
|
+
for (const f of list) {
|
|
1859
|
+
if (!f || typeof f !== "object") continue;
|
|
1860
|
+
const row = rowsFor(f.round);
|
|
1861
|
+
row.findings += 1;
|
|
1862
|
+
const sev = typeof f.severity === "string" && f.severity ? f.severity : "unknown";
|
|
1863
|
+
row.severities[sev] = (row.severities[sev] || 0) + 1;
|
|
1864
|
+
if (f.status === "fixed") row.fixed += 1;
|
|
1865
|
+
}
|
|
1866
|
+
const vals = Array.isArray(validations) ? validations : [];
|
|
1867
|
+
for (const v of vals) {
|
|
1868
|
+
if (!v || typeof v !== "object") continue;
|
|
1869
|
+
const row = rowsFor(v.round);
|
|
1870
|
+
row.validations.push({
|
|
1871
|
+
command: typeof v.command === "string" ? v.command : "",
|
|
1872
|
+
exitCode: typeof v.exitCode === "number" ? v.exitCode : null,
|
|
1873
|
+
allowed: v.allowed === true
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1876
|
+
return [...rounds.values()].filter((r) => r.round > 0).sort((a, b) => a.round - b.round).map((r) => ({ ...r, severities: { ...r.severities } }));
|
|
1877
|
+
}
|
|
1878
|
+
function dashboardRunState(transcript) {
|
|
1879
|
+
const t2 = transcript && typeof transcript === "object" ? transcript : null;
|
|
1880
|
+
if (!t2) return { state: "empty", round: 0, phase: "", stoppedReason: "" };
|
|
1881
|
+
const rawRound = Number(t2.round);
|
|
1882
|
+
const round = Number.isFinite(rawRound) && rawRound > 0 ? Math.floor(rawRound) : 1;
|
|
1883
|
+
const stoppedReason = typeof t2.stoppedReason === "string" ? t2.stoppedReason.trim() : "";
|
|
1884
|
+
return {
|
|
1885
|
+
state: t2.active === true ? "running" : "done",
|
|
1886
|
+
round,
|
|
1887
|
+
phase: latestPhase(t2.phases),
|
|
1888
|
+
stoppedReason
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1367
1891
|
function latestPhase(phases) {
|
|
1368
1892
|
const list = Array.isArray(phases) ? phases : [];
|
|
1369
1893
|
let latest = "";
|
|
@@ -1373,6 +1897,387 @@ function latestPhase(phases) {
|
|
|
1373
1897
|
return latest;
|
|
1374
1898
|
}
|
|
1375
1899
|
|
|
1900
|
+
// src/client/i18n.ts
|
|
1901
|
+
var LANG_STORAGE_KEY = "iterate-ui-lang";
|
|
1902
|
+
var LANG_NAMES = {
|
|
1903
|
+
zh: "\u7B80\u4F53\u4E2D\u6587",
|
|
1904
|
+
en: "English"
|
|
1905
|
+
};
|
|
1906
|
+
var EN = {
|
|
1907
|
+
// ── run state / dashboard ────────────────────────────────────────────────
|
|
1908
|
+
\u8FD0\u884C\u4E2D: "Running",
|
|
1909
|
+
\u5DF2\u7ED3\u675F: "Finished",
|
|
1910
|
+
"Round {a0}\uFF1A{a1} \u9879": "Round {a0}: {a1} findings",
|
|
1911
|
+
"\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\u8D8B\u52BF\uFF1A{a0}": "Findings per round: {a0}",
|
|
1912
|
+
\u5B8C\u6574\u8FED\u4EE3: "Full iteration",
|
|
1913
|
+
"\u590D\u5236\u542F\u52A8\u6307\u4EE4\uFF1A\u5B8C\u6574\u300C\u5BA1\u67E5 \u2192 \u4FEE\u590D \u2192 \u9A8C\u8BC1 \u2192 \u590D\u76D8\u300D\u95ED\u73AF\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u5373\u53EF\uFF09": 'Copy the start instruction: the full "review \u2192 fix \u2192 validate \u2192 debrief" loop (paste it to the model)',
|
|
1914
|
+
\u4EC5\u8BC4\u5BA1: "Review only",
|
|
1915
|
+
"\u590D\u5236\u542F\u52A8\u6307\u4EE4\uFF1A\u53EA\u5BA1\u67E5\u4E0D\u4FEE\u6539\uFF08dry-run\uFF09\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u5373\u53EF\uFF09": "Copy the start instruction: review without changing anything (dry-run) \u2014 paste it to the model",
|
|
1916
|
+
"\u7B2C {a0} \u8F6E\u8FDB\u884C\u4E2D{a1}\u2014\u2014\u9996\u8F6E\u6C47\u603B\u62A5\u544A\u843D\u5730\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u6536\u655B\u8FDB\u5EA6\u4E0E\u53D1\u73B0\u7EDF\u8BA1\u3002": "Round {a0} in progress{a1} \u2014 convergence progress and finding stats appear here once the first aggregated report lands.",
|
|
1917
|
+
" \xB7 \u9636\u6BB5\u300C{a0}\u300D": ' \xB7 phase "{a0}"',
|
|
1918
|
+
"\u4E0A\u6B21\u8FED\u4EE3\u5728\u7B2C {a0} \u8F6E\u540E\u505C\u6B62{a1}\uFF0C\u4F46\u6CA1\u6709\u53EF\u5C55\u793A\u7684\u6C47\u603B\u62A5\u544A\u3002": "The last iteration stopped at round {a0}{a1}, but there is no report to show.",
|
|
1919
|
+
"\u8FD0\u884C\u4E00\u6B21\u8BC4\u5BA1\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u6536\u655B\u8FDB\u5EA6\u4E0E\u53D1\u73B0\u7EDF\u8BA1\u3002": "Run a review and convergence progress and finding stats will show up here.",
|
|
1920
|
+
\u672C\u6B21\u8FED\u4EE3\u4ECE\u4E0A\u4E00\u6B21\u4E2D\u65AD\u7684\u65AD\u70B9\u7EE7\u7EED\u6267\u884C: "This run resumed from the previous interrupted checkpoint",
|
|
1921
|
+
"\u5DF2\u4E2D\u65AD\u6062\u590D \xD7{a0}": "Resumed \xD7{a0}",
|
|
1922
|
+
"\u4F1A\u8BDD\u4E2D\u68C0\u6D4B\u5230\u7528\u6237\u9644\u5E26\u7684\u56FE\u7247\uFF0C\u8BC4\u5BA1\u5C06\u4F5C\u4E3A\u89C6\u89C9\u8BC1\u636E\u53C2\u8003": "Images attached by the user were detected in this session; the review treats them as visual evidence",
|
|
1923
|
+
"\u9644\u4EF6\u56FE\u7247 {a0}": "Attached images {a0}",
|
|
1924
|
+
"\u5F53\u524D\u5904\u4E8E\u300C{a0}\u300D\u9636\u6BB5\uFF08\u8FD0\u884C\u4E2D\uFF09": 'Currently in the "{a0}" phase (running)',
|
|
1925
|
+
"\u6700\u8FD1\u4E00\u6B21\u6267\u884C\u505C\u7559\u5728\u300C{a0}\u300D\u9636\u6BB5\uFF08\u5DF2\u7ED3\u675F\uFF09": 'The last run stopped in the "{a0}" phase (finished)',
|
|
1926
|
+
"\u4EE3\u7801\u6A21\u5F0F\uFF1A\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6": "Code mode: edits files directly",
|
|
1927
|
+
"\u8FED\u4EE3\u6A21\u5F0F\uFF1A\u5BA1\u67E5-\u4FEE\u590D\u5FAA\u73AF": "Iterate mode: review \u2192 fix loop",
|
|
1928
|
+
"+{a0} \u66F4\u591A": "+{a0} more",
|
|
1929
|
+
"\u672C\u8F6E\u5DF2\u5E94\u7528\u7684\u539F\u5B50\u4FEE\u590D\u6570\uFF08\u6B63\u5E38\u6A21\u5F0F\uFF09": "Atomic fixes applied this round (normal mode)",
|
|
1930
|
+
"\u5BA1\u67E5\u5DF2\u6536\u655B\uFF1A\u6700\u540E\u4E00\u8F6E\u672A\u53D1\u73B0\u65B0\u95EE\u9898": "Review converged: the final round found nothing new",
|
|
1931
|
+
"\u2713 \u5DF2\u6536\u655B": "\u2713 Converged",
|
|
1932
|
+
"\u9996\u8F6E {a0} \u2192 \u672B\u8F6E {a1} \u9879\uFF0C\u964D\u5E45 {a2}%{a3}": "Round {a0} \u2192 round {a1} findings, down {a2}%{a3}",
|
|
1933
|
+
"\uFF0C\u5DF2\u6536\u655B": ", converged",
|
|
1934
|
+
"Iterate \xB7 \u6536\u655B\u7EDF\u8BA1": "Iterate \xB7 Convergence stats",
|
|
1935
|
+
\u6536\u8D77\u5386\u53F2: "Collapse history",
|
|
1936
|
+
"\u5386\u53F2 / \u8D8B\u52BF": "History / trend",
|
|
1937
|
+
\u8F6E\u6B21: "Round",
|
|
1938
|
+
\u53D1\u73B0: "Findings",
|
|
1939
|
+
\u8BF7\u7EE7\u7EED\u6267\u884C\u4E0B\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5: "Please continue with the next review round",
|
|
1940
|
+
"\u8BF7\u56DE\u6EDA\u4E0A\u4E00\u8F6E\u8FED\u4EE3\u7684\u4FEE\u590D\uFF1A\u8C03\u7528 `iterate_history` \u67E5\u770B\u672C\u8F6E fix id\uFF0C\u518D\u7528 `iterate_rollback` \u9010\u4E2A\u64A4\u9500\u8FD9\u4E9B\u4FEE\u590D": "Please roll back the fixes from the last round: call `iterate_history` for this round\u2019s fix ids, then `iterate_rollback` to undo them one by one",
|
|
1941
|
+
"y \u4FEE\u590D": "y fix",
|
|
1942
|
+
"\u4FDD\u7559\u8BE5 finding\uFF0C\u8FDB\u5165\u4FEE\u590D": "Keep this finding and move it to fixing",
|
|
1943
|
+
"n \u8DF3\u8FC7": "n skip",
|
|
1944
|
+
"\u8DF3\u8FC7\u8BE5 finding": "Skip this finding",
|
|
1945
|
+
"a \u5DF2\u77E5\u6709\u610F": "a known-intentional",
|
|
1946
|
+
"\u6807\u8BB0\u4E3A\u5DF2\u77E5\u6709\u610F\u5E76\u5199\u5165 known_intentional": "Mark as known-intentional and write it into known_intentional",
|
|
1947
|
+
// ── triage panel ─────────────────────────────────────────────────────────
|
|
1948
|
+
"Iterate \xB7 Findings \u5206\u8BCA ({a0}/{a1})": "Iterate \xB7 Findings triage ({a0}/{a1})",
|
|
1949
|
+
"y=\u4FEE\u590D \xB7 n=\u8DF3\u8FC7 \xB7 a=\u5DF2\u77E5\u6709\u610F \xB7 \u2191/\u2193 \u9009\u62E9": "y=fix \xB7 n=skip \xB7 a=known-intentional \xB7 \u2191/\u2193 to move",
|
|
1950
|
+
\u6309\u4E25\u91CD\u5EA6\u7B5B\u9009: "Filter by severity",
|
|
1951
|
+
\u5168\u90E8\u4E25\u91CD\u5EA6: "All severities",
|
|
1952
|
+
\u6309\u7EF4\u5EA6\u7B5B\u9009: "Filter by dimension",
|
|
1953
|
+
\u5168\u90E8\u7EF4\u5EA6: "All dimensions",
|
|
1954
|
+
"\u641C\u7D22\u6587\u4EF6 / \u6458\u8981\u2026": "Search file / summary\u2026",
|
|
1955
|
+
\u641C\u7D22\u6587\u4EF6\u6216\u6458\u8981: "Search file or summary",
|
|
1956
|
+
"\u6E05\u9664\u7B5B\u9009\uFF08\u663E\u793A {a0}\uFF09": "Clear filters (showing {a0})",
|
|
1957
|
+
"\u5171 {a0} \u9879": "{a0} in total",
|
|
1958
|
+
"\u6279\u91CF\u4F5C\u7528\u4E8E\uFF1A": "Batch applies to:",
|
|
1959
|
+
"\u6279\u91CF\u6309\u94AE\u4EC5\u4F5C\u7528\u4E8E\u5F53\u524D\u7B5B\u9009\u53EF\u89C1\u7684 findings": "Batch buttons only apply to the findings visible under the current filter",
|
|
1960
|
+
"\u53EF\u89C1 {a0}": "Visible {a0}",
|
|
1961
|
+
"\u6279\u91CF\u6309\u94AE\u4F5C\u7528\u4E8E\u5168\u90E8 findings": "Batch buttons apply to every finding",
|
|
1962
|
+
"\u5168\u90E8 {a0}": "All {a0}",
|
|
1963
|
+
"\u628A\u6240\u6709\u5224\u5B9A\u6062\u590D\u4E3A\u9ED8\u8BA4 y\uFF08\u4FEE\u590D\uFF09": "Reset every verdict to the default y (fix)",
|
|
1964
|
+
\u91CD\u7F6E: "Reset",
|
|
1965
|
+
"y {a0} \xB7 n {a1} \xB7 a {a2} \xB7 \u5F85\u5199\u56DE known_intentional\uFF1A{a3} \u6761": "y {a0} \xB7 n {a1} \xB7 a {a2} \xB7 pending known_intentional write-back: {a3}",
|
|
1966
|
+
"\u5F53\u524D\u6CA1\u6709\u6807\u8BB0\u4E3A\u300C\u5DF2\u77E5\u6709\u610F\u300D\u7684 finding": "No finding is marked known-intentional yet",
|
|
1967
|
+
"\u590D\u5236\u5E94\u7528\u6307\u4EE4\uFF1A\u7C98\u8D34\u540E\u6A21\u578B\u4F1A\u8C03\u7528 iterate_triage apply\uFF08\u6821\u9A8C\u3001\u53BB\u91CD\u3001\u5199\u5165\u524D\u5907\u4EFD\u3001\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\uFF09": "Copy the apply instruction: once pasted, the model calls iterate_triage apply (validates, dedupes, backs up before writing, rolls back on failure)",
|
|
1968
|
+
"\u751F\u6210\u5E94\u7528\u6307\u4EE4{a0}": "Generate apply instruction{a0}",
|
|
1969
|
+
"\u53EA\u590D\u5236 known_intentional YAML \u7247\u6BB5\uFF08\u9700\u81EA\u884C\u7C98\u5230 iterate.config.yaml \u7684 personalization.known_intentional \u4E0B\uFF09": "Copy only the known_intentional YAML fragment (you must paste it under personalization.known_intentional in iterate.config.yaml yourself)",
|
|
1970
|
+
"\u4EC5\u590D\u5236 YAML \u7247\u6BB5": "Copy YAML fragment only",
|
|
1971
|
+
"\u590D\u5236 iterate_triage list \u6307\u4EE4\uFF0C\u6838\u5BF9\u5DF2\u7ECF\u5199\u5165\u7684 known_intentional \u6761\u76EE\uFF08\u5199\u56DE\u95ED\u73AF\u786E\u8BA4\uFF09": "Copy the iterate_triage list instruction to check which known_intentional entries actually landed (write-back \u2192 readback)",
|
|
1972
|
+
\u56DE\u8BFB\u5DF2\u5199\u5165\u6761\u76EE: "Read back written entries",
|
|
1973
|
+
"\u5DF2\u590D\u5236\u5E94\u7528\u6307\u4EE4\u2014\u2014\u7C98\u8D34\u56DE\u4F1A\u8BDD\uFF0C\u6A21\u578B\u5C06\u8C03\u7528 iterate_triage apply \u5E76\u56DE\u62A5\u5199\u5165\u6761\u6570\u3002": "Apply instruction copied \u2014 paste it back into the chat and the model will call iterate_triage apply and report how many entries were written.",
|
|
1974
|
+
"\u5DF2\u590D\u5236\u56DE\u8BFB\u6307\u4EE4\u2014\u2014\u7C98\u8D34\u56DE\u4F1A\u8BDD\u53EF\u5217\u51FA\u5F53\u524D\u5DF2\u5199\u5165\u7684 known_intentional\u3002": "Readback instruction copied \u2014 paste it back to list the known_intentional entries currently written.",
|
|
1975
|
+
// ── command actions ──────────────────────────────────────────────────────
|
|
1976
|
+
\u6307\u6325\u64CD\u4F5C: "Command actions",
|
|
1977
|
+
"\u6279\u51C6\u67B6\u6784\u4FEE\u590D\uFF08\u590D\u5236\u6307\u4EE4\u6587\u672C\uFF1Aiterate_fix force:true\uFF0C\u542B\u5168\u90E8\u5FC5\u586B\u53C2\u6570\uFF09": "Approve the architectural fix (copies the instruction text: iterate_fix with force:true and every required argument)",
|
|
1978
|
+
\u6279\u51C6\u67B6\u6784\u4FEE\u590D: "Approve architectural fix",
|
|
1979
|
+
\u89E6\u53D1\u65B0\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5: "Trigger the next review round",
|
|
1980
|
+
\u89E6\u53D1\u65B0\u4E00\u8F6E: "New round",
|
|
1981
|
+
"\u6307\u6D3E\u5168\u90E8 {a0} \u4E2A findings \u4FEE\u590D\uFF08\u9010\u4E2A iterate_fix\uFF09": "Assign all {a0} findings for fixing (one iterate_fix each)",
|
|
1982
|
+
"\u6307\u6D3E\u5F53\u524D\u53EF\u89C1 {a0} \u4E2A findings \u4FEE\u590D\uFF08\u9010\u4E2A iterate_fix\uFF09": "Assign the {a0} visible findings for fixing (one iterate_fix each)",
|
|
1983
|
+
\u6307\u6D3E\u4FEE\u590D: "Assign fixes",
|
|
1984
|
+
\u56DE\u6EDA\u5230\u4E0A\u4E00\u4E2A\u68C0\u67E5\u70B9: "Roll back to the previous checkpoint",
|
|
1985
|
+
\u56DE\u6EDA\u68C0\u67E5\u70B9: "Rollback to checkpoint",
|
|
1986
|
+
// ── verdict banner / stats card ──────────────────────────────────────────
|
|
1987
|
+
\u62A5\u544A\u5DF2\u6279\u51C6: "Report approved",
|
|
1988
|
+
\u62A5\u544A\u9700\u4FEE\u8BA2: "Report needs revision",
|
|
1989
|
+
\u9879\u53D1\u73B0: " findings",
|
|
1990
|
+
\u8F6E: " rounds",
|
|
1991
|
+
\u9879\u5BA1\u67E5: " reviews",
|
|
1992
|
+
\u62A5\u544A\u901A\u8FC7\u5168\u90E8\u4E00\u81F4\u6027\u68C0\u67E5: "The report passed every consistency check",
|
|
1993
|
+
\u5904\u62A5\u544A\u7F3A\u9677: " report defects found",
|
|
1994
|
+
\u5DF2\u6536\u655B: "Converged",
|
|
1995
|
+
\u672A\u6536\u655B: "Not converged",
|
|
1996
|
+
"Round {a0} \u5B8C\u6210 \xB7 \u5DF2\u6536\u655B": "Round {a0} complete \xB7 converged",
|
|
1997
|
+
"Round {a0} \u5B8C\u6210": "Round {a0} complete",
|
|
1998
|
+
// ── settings ─────────────────────────────────────────────────────────────
|
|
1999
|
+
"\u5DF2\u6E05\u9664 {a0} \u6761": "Cleared {a0}",
|
|
2000
|
+
\u518D\u6B21\u70B9\u51FB\u4EE5\u786E\u8BA4\u6E05\u9664\u5168\u90E8\u5224\u5B9A: "Click again to confirm clearing every verdict",
|
|
2001
|
+
"\u6E05\u9664\u6240\u6709\u5206\u8BCA\u5224\u5B9A\u8BB0\u5F55\uFF08\u9700\u8981\u4E8C\u6B21\u786E\u8BA4\uFF09": "Clear every triage verdict (requires a second click)",
|
|
2002
|
+
"\u786E\u8BA4\u6E05\u9664\uFF1F": "Confirm clear?",
|
|
2003
|
+
\u6E05\u9664\u5206\u8BCA: "Clear triage",
|
|
2004
|
+
\u8FED\u4EE3\u4E3B\u9898: "Iterate theme",
|
|
2005
|
+
"\u542F\u7528\u6696\u7425\u73C0\u914D\u8272\u7684 iterate \u4E13\u5C5E\u76AE\u80A4\uFF0C\u8986\u76D6 dsh \u9ED8\u8BA4\u4E3B\u9898\u4EE4\u724C\u3002": "Enables the warm-amber iterate skin, overriding the default dsh theme tokens.",
|
|
2006
|
+
\u5DF2\u542F\u7528: "Enabled",
|
|
2007
|
+
\u5DF2\u5173\u95ED: "Off",
|
|
2008
|
+
"\u5F00\u5173 iterate \u4E3B\u9898": "Toggle the iterate theme",
|
|
2009
|
+
\u5206\u8BCA\u6301\u4E45\u5316: "Triage persistence",
|
|
2010
|
+
"y / n / a \u5224\u5B9A\u4FDD\u5B58\u5728\u672C\u5730\u6D4F\u89C8\u5668\uFF08localStorage\uFF09\uFF0C\u5237\u65B0\u4F1A\u8BDD\u540E\u4ECD\u4FDD\u7559\u3002": "y / n / a verdicts are stored in your browser (localStorage) and survive a session refresh.",
|
|
2011
|
+
\u672C\u5730\u4FDD\u5B58: "Stored locally",
|
|
2012
|
+
\u914D\u7F6E\u7BA1\u7406: "Configuration",
|
|
2013
|
+
"\u76EE\u6807 / \u7EF4\u5EA6 / \u6700\u5927\u8F6E\u6570 / validation.commands \u5199\u5728\u9879\u76EE\u7684 iterate.config.yaml\uFF0C\u590D\u5236\u6307\u5F15\u53EF\u8BA9\u6A21\u578B\u6309\u9700\u8C03\u6574\u3002": "Goal / dimensions / max rounds / validation.commands live in the project\u2019s iterate.config.yaml; copy the guide to let the model adjust them on demand.",
|
|
2014
|
+
\u590D\u5236\u6307\u5F15: "Copy guide",
|
|
2015
|
+
\u6536\u8D77: "Collapse",
|
|
2016
|
+
\u5C55\u5F00: "Expand",
|
|
2017
|
+
\u9009\u62E9\u8981\u4FEE\u6539\u7684\u914D\u7F6E\u5B57\u6BB5: "Pick the config field to edit",
|
|
2018
|
+
"\u751F\u6210\u5E76\u590D\u5236\u8BE5\u5B57\u6BB5\u7684 iterate_config \u5199\u5165\u6307\u4EE4\uFF08\u5199\u5165\u524D\u81EA\u52A8\u5907\u4EFD\u3001\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\uFF09": "Build and copy the iterate_config write instruction for this field (backs up first, rolls back on failure)",
|
|
2019
|
+
\u751F\u6210\u5199\u5165\u6307\u4EE4: "Build write instruction",
|
|
2020
|
+
"\u526A\u8D34\u677F\u88AB\u62D2\u7EDD\u2014\u2014\u6307\u4EE4\u5DF2\u5C55\u793A\u5728\u4E0B\u65B9\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u3002": "The clipboard write was refused \u2014 the instruction is shown below, copy it manually.",
|
|
2021
|
+
"\u5199\u5165\u6307\u4EE4\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\uFF09": "Write instruction (paste to the model)",
|
|
2022
|
+
"\u5C1A\u672A\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E\u2014\u2014\u70B9\u300C\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E\u300D\u590D\u5236\u6307\u4EE4\u53D1\u7ED9\u6A21\u578B\uFF0C\u56DE\u663E\u540E\u5373\u53EF\u5728\u6B64\u770B\u5230\u5F53\u524D\u503C\u3002": "No config read back yet \u2014 press \u201CRead back current config\u201D, send the instruction to the model, and the current value shows up here.",
|
|
2023
|
+
"\u5F53\u524D\u503C {a0}\uFF1A{a1}": "Current value for {a0}: {a1}",
|
|
2024
|
+
"\u56DE\u8BFB\u7ED3\u679C\u4E2D\u6CA1\u6709 {a0}\uFF08\u8BE5\u5B57\u6BB5\u53EF\u80FD\u5C1A\u672A\u5199\u5165\u914D\u7F6E\u6587\u4EF6\uFF09\u3002": "The read-back does not contain {a0} (it may not have been written to the config file yet).",
|
|
2025
|
+
'\u590D\u5236\u56DE\u8BFB\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u8C03\u7528 iterate_config(operation:"read") \u5E76\u56DE\u663E\u7ED3\u679C': 'Copy the read-back instruction: have the model call iterate_config(operation:"read") and echo the result',
|
|
2026
|
+
\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E: "Read back current config",
|
|
2027
|
+
"\u56DE\u8BFB\u6307\u4EE4\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\uFF09": "Read-back instruction (paste to the model)",
|
|
2028
|
+
\u590D\u5236: "Copy",
|
|
2029
|
+
"iterate.config.yaml \u53EF\u7F16\u8F91\u5B57\u6BB5": "Editable iterate.config.yaml fields",
|
|
2030
|
+
"\u4E2D\u6587\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u7684\u5951\u7EA6\u6587\u6848\uFF09": "Chinese (contract text to paste to the model)",
|
|
2031
|
+
\u72B6\u6001\u6982\u89C8: "Runtime status",
|
|
2032
|
+
"\u67E5\u770B\u8FD0\u884C\u65F6\u4EA7\u7269\u5E03\u5C40\u4E0E\u6E05\u7406\u6307\u5F15\u3002iterate_status / iterate_history / iterate_prune \u5DE5\u5177\u7528\u4E8E\u67E5\u770B\u548C\u7BA1\u7406\u3002": "The runtime artifact layout and cleanup guide. Use iterate_status / iterate_history / iterate_prune to inspect and manage them.",
|
|
2033
|
+
\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167: "Pull disk snapshot",
|
|
2034
|
+
\u8FD0\u884C\u65F6\u5E03\u5C40\u4E0E\u6E05\u7406: "Runtime layout & cleanup",
|
|
2035
|
+
"\u4E3A\u6BCF\u6B21\u4EE3\u7801\u8BC4\u5BA1\u751F\u6210 Review \u62A5\u544A\u4E0E\u4EA4\u4E92\u5F0F\u5206\u8BCA\u89C6\u56FE\uFF0C\u4E13\u6CE8 AI \u81EA\u6211\u5BA1\u67E5\u4E0E\u4FEE\u6B63\u3002": "Generates a review report and an interactive triage view for every code review, focused on AI self-review and correction.",
|
|
2036
|
+
\u5C31\u7EEA: "Ready",
|
|
2037
|
+
"iterate \u8BBE\u7F6E": "iterate settings",
|
|
2038
|
+
"\u526A\u8D34\u677F\u5199\u5165\u88AB\u62D2\u7EDD\uFF0C\u590D\u5236\u672A\u751F\u6548\u2014\u2014\u8BF7\u5C55\u5F00\u5BF9\u5E94\u533A\u5757\u624B\u52A8\u9009\u4E2D\u6587\u672C\u590D\u5236\u3002": "The clipboard write was refused, so nothing was copied \u2014 expand the section and copy the text manually.",
|
|
2039
|
+
"\u754C\u9762\u8BED\u8A00": "Interface language",
|
|
2040
|
+
"\u9762\u677F\u6587\u6848\u7684\u663E\u793A\u8BED\u8A00\uFF1B\u590D\u5236\u7ED9\u6A21\u578B\u7684\u6307\u4EE4\u4FDD\u6301\u4E2D\u6587\uFF0C\u4E0E SKILL.md \u7684\u5DE5\u4F5C\u6D41\u5951\u7EA6\u4E00\u81F4\u3002": "The language of the panel copy. Instructions copied for the model stay Chinese \u2014 they mirror the SKILL.md workflow contract.",
|
|
2041
|
+
"\u5207\u6362\u754C\u9762\u8BED\u8A00": "Switch the interface language",
|
|
2042
|
+
// ── observatory shell / live feed ────────────────────────────────────────
|
|
2043
|
+
"{a0} \u9875 \xB7 \u6309 1-9/0 \u5207 Fn\u3001- \u56DE\u5B9E\u65F6\u6D41": "{a0} tabs \xB7 keys 1-9/0 switch Fn, - returns to the live feed",
|
|
2044
|
+
"\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u9010\u4E2A\u8C03\u7528 {a0} \u5E76\u56DE\u663E\u5B8C\u6574\u7ED3\u679C": "Copy the disk-snapshot instruction: have the model call {a0} and echo the full results",
|
|
2045
|
+
"iterate \u89C2\u6D4B\u53F0": "iterate observatory",
|
|
2046
|
+
"\u6682\u65E0\u8FD0\u884C\u65F6\u89C2\u6D4B\u6570\u636E \xB7 {a0}": "No runtime observations yet \xB7 {a0}",
|
|
2047
|
+
"\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u78C1\u76D8\u4E0A\u53EF\u80FD\u5DF2\u6709\u5386\u53F2\u4EA7\u7269\uFF0C\u56DE\u663E\u8FDB\u4F1A\u8BDD\u540E\u89C2\u6D4B\u53F0\u5373\u53EF\u586B\u5145": "Copy the disk-snapshot instruction: older artifacts may already be on disk; once echoed back into the chat the observatory fills up",
|
|
2048
|
+
"\u5B9E\u65F6\u6D3B\u52A8\u6D41\u4EC5\u5728\u8FD0\u884C\u671F\u95F4\u8BB0\u5F55\uFF0C\u5F53\u524D\u5DF2\u5B8C\u6210\u3002": "The live activity stream is only recorded while a run is active; this run has finished.",
|
|
2049
|
+
\u67E5\u770B\u5BA1\u67E5\u7EBF\u7A0B: "View review threads",
|
|
2050
|
+
\u67E5\u770B\u53D1\u73B0: "View findings",
|
|
2051
|
+
\u6682\u65E0\u5B9E\u65F6\u6D3B\u52A8: "No live activity yet",
|
|
2052
|
+
\u5B9E\u65F6\u6D3B\u52A8: "Live activity",
|
|
2053
|
+
\u6309\u6D3B\u52A8\u7C7B\u578B\u7B5B\u9009: "Filter by activity type",
|
|
2054
|
+
\u5168\u90E8\u6D3B\u52A8: "All activity",
|
|
2055
|
+
\u65E0\u5339\u914D\u7684\u5B9E\u65F6\u6D3B\u52A8: "No matching live activity",
|
|
2056
|
+
\u590D\u5236\u672A\u6210\u529F: "Copy failed",
|
|
2057
|
+
"\u526A\u8D34\u677F\u5199\u5165\u88AB\u62D2\u7EDD\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0B\u65B9\u6307\u4EE4\u590D\u5236": "The clipboard write was refused \u2014 select the instruction below and copy it manually",
|
|
2058
|
+
\u5173\u95ED\u590D\u5236\u5931\u8D25\u63D0\u793A: "Dismiss the copy-failure notice",
|
|
2059
|
+
\u5173\u95ED: "Dismiss",
|
|
2060
|
+
"\u5C06\u89C2\u6D4B\u53F0\u5168\u90E8\u6570\u636E\u5BFC\u51FA\u4E3A JSON\uFF08\u4F18\u5148\u4E0B\u8F7D\uFF0C\u5931\u8D25\u5219\u590D\u5236\uFF09": "Export every observatory panel as JSON (downloads first, falls back to copying)",
|
|
2061
|
+
\u5DF2\u5BFC\u51FA: "Exported",
|
|
2062
|
+
"\u5BFC\u51FA JSON": "Export JSON",
|
|
2063
|
+
"\u89C2\u6D4B\u53F0\u9875\u7B7E\uFF08\u5FEB\u6377\u952E 1-9 / 0 / -\uFF09": "Observatory tabs (keys 1-9 / 0 / -)",
|
|
2064
|
+
"{a0}\uFF08\u6309 {a1} \u5207\u6362\uFF09": "{a0} (press {a1})",
|
|
2065
|
+
"{a0}\uFF08\u6309 - \u5207\u6362\uFF09": "{a0} (press -)",
|
|
2066
|
+
// ── F1 review threads ────────────────────────────────────────────────────
|
|
2067
|
+
\u6682\u65E0\u5BA1\u67E5\u7EBF\u7A0B: "No review threads yet",
|
|
2068
|
+
\u5BA1\u67E5\u7EBF\u7A0B: "Review threads",
|
|
2069
|
+
\u5C55\u5F00\u5168\u90E8\u7EBF\u7A0B: "Expand all threads",
|
|
2070
|
+
\u5168\u90E8\u5C55\u5F00: "Expand all",
|
|
2071
|
+
\u6536\u8D77\u5168\u90E8\u7EBF\u7A0B: "Collapse all threads",
|
|
2072
|
+
\u5168\u90E8\u6536\u8D77: "Collapse all",
|
|
2073
|
+
"{a0} \xB7 {a1} \u8F6E": "{a0} \xB7 {a1} rounds",
|
|
2074
|
+
\u81EA\u7531\u5207\u6362: "Mixed",
|
|
2075
|
+
\u672C\u8F6E\u65E0\u7EBF\u7A0B: "No threads in this round",
|
|
2076
|
+
\u672A\u547D\u540D\u7EF4\u5EA6: "Unnamed dimension",
|
|
2077
|
+
\u6536\u8D77\u7EBF\u7A0B: "Collapse thread",
|
|
2078
|
+
\u5C55\u5F00\u7EBF\u7A0B: "Expand thread",
|
|
2079
|
+
\u53D9\u8FF0: "Narration",
|
|
2080
|
+
"{a0} \u6761": "{a0}",
|
|
2081
|
+
\u65E0\u7EBF\u7D22\u53D9\u8FF0: "No narration recorded",
|
|
2082
|
+
\u8BFB\u53D6\u6587\u4EF6: "Files read",
|
|
2083
|
+
\u672C\u7EBF\u7A0B\u65E0\u53D1\u73B0: "This thread found nothing",
|
|
2084
|
+
"{a0} \u7EBF\u7A0B": "{a0} threads",
|
|
2085
|
+
// ── F2 convergence trend ─────────────────────────────────────────────────
|
|
2086
|
+
\u6682\u65E0\u6536\u655B\u6570\u636E: "No convergence data yet",
|
|
2087
|
+
\u5DF2\u4FEE\u590D: "Fixed",
|
|
2088
|
+
\u4E25\u91CD\u5EA6\u5206\u5E03: "Severity mix",
|
|
2089
|
+
\u9A8C\u8BC1: "Validation",
|
|
2090
|
+
"{a0}/{a1} \u901A\u8FC7{a2}": "{a0}/{a1} passed{a2}",
|
|
2091
|
+
"\uFF08\u542B\u5931\u8D25\uFF09": " (failures included)",
|
|
2092
|
+
"\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\uFF1A{a0}{a1}": "Findings per round: {a0}{a1}",
|
|
2093
|
+
" \xB7 \u5F53\u524D Round {a0}": " \xB7 current round {a0}",
|
|
2094
|
+
"\u8DE8\u8F6E\u5BF9\u6BD4\uFF08\u672C\u4F1A\u8BDD\uFF09": "Cross-round comparison (this session)",
|
|
2095
|
+
"\u8DE8\u4F1A\u8BDD\u5BF9\u6BD4\uFF1A\u672C\u8868\u53EA\u8986\u76D6\u5F53\u524D\u4F1A\u8BDD\uFF1B\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u8BA9\u6A21\u578B\u56DE\u663E iterate_history\uFF0C\u518D\u7528\u300C\u5BFC\u51FA JSON\u300D\u5E76\u6392\u6BD4\u8F83\u4E24\u6B21\u8FD0\u884C": 'Cross-session comparison: this table only covers the current session \u2014 click "Pull disk snapshot" to have the model echo iterate_history, then use "Export JSON" to compare two runs side by side',
|
|
2096
|
+
// ── F3 finding locations ─────────────────────────────────────────────────
|
|
2097
|
+
\u6682\u65E0\u53D1\u73B0: "No findings",
|
|
2098
|
+
"{a0}\uFF08{a1} \u9879\uFF09": "{a0} ({a1} findings)",
|
|
2099
|
+
"\u641C\u7D22\u53D1\u73B0\u2026": "Search findings\u2026",
|
|
2100
|
+
\u641C\u7D22\u53D1\u73B0: "Search findings",
|
|
2101
|
+
\u6E05\u9664\u5168\u90E8\u7B5B\u9009: "Clear all filters",
|
|
2102
|
+
\u6E05\u9664\u7B5B\u9009: "Clear filters",
|
|
2103
|
+
\u5DF2\u786E\u8BA4: "Acknowledged",
|
|
2104
|
+
"\u590D\u5236\u8BE5 finding \u7684\u5EFA\u8BAE\u4FEE\u590D\u6587\u672C": "Copy this finding\u2019s suggested fix text",
|
|
2105
|
+
"\u590D\u5236 suggested_fix": "Copy suggested_fix",
|
|
2106
|
+
"\u590D\u5236 iterate_triage \u6307\u4EE4\u6587\u672C\uFF08\u6807\u8BB0\u4E3A known_intentional\uFF09": "Copy the iterate_triage instruction text (mark as known_intentional)",
|
|
2107
|
+
"\u590D\u5236 iterate_fix \u6307\u4EE4\u6587\u672C": "Copy the iterate_fix instruction text",
|
|
2108
|
+
\u590D\u5236\u4FEE\u590D\u6307\u4EE4: "Copy fix instruction",
|
|
2109
|
+
\u65E0\u5339\u914D\u7684\u53D1\u73B0: "No matching findings",
|
|
2110
|
+
// ── F4 fixes & rollback ──────────────────────────────────────────────────
|
|
2111
|
+
\u6682\u65E0\u4FEE\u590D\u8BB0\u5F55: "No fixes recorded yet",
|
|
2112
|
+
\u6210\u529F: "Success",
|
|
2113
|
+
\u5931\u8D25: "Failed",
|
|
2114
|
+
"\u590D\u5236 iterate_rollback \u6307\u4EE4\u6587\u672C": "Copy the iterate_rollback instruction text",
|
|
2115
|
+
\u56DE\u6EDA: "Rollback",
|
|
2116
|
+
// ── F5 checkpoint ────────────────────────────────────────────────────────
|
|
2117
|
+
\u65AD\u70B9: "Checkpoint",
|
|
2118
|
+
"\u590D\u5236 iterate_checkpoint resume \u6307\u4EE4\u6587\u672C\uFF08\u52A0\u8F7D\u65AD\u70B9\u5E76\u8BA1\u6570\u4E00\u6B21\u6062\u590D\uFF09": "Copy the iterate_checkpoint resume instruction (loads the checkpoint and counts one resume)",
|
|
2119
|
+
\u590D\u5236\u6062\u590D\u6307\u4EE4: "Copy resume instruction",
|
|
2120
|
+
"\u590D\u5236 iterate_checkpoint clear \u6307\u4EE4\u6587\u672C\uFF08\u6E05\u9664\u5F53\u524D\u65AD\u70B9\uFF0C\u4F9B\u91CD\u65B0\u5F00\u59CB\uFF09": "Copy the iterate_checkpoint clear instruction (drops the current checkpoint so you can start over)",
|
|
2121
|
+
\u6E05\u9664\u65AD\u70B9: "Clear checkpoint",
|
|
2122
|
+
// ── F6 run console ───────────────────────────────────────────────────────
|
|
2123
|
+
\u8BE2\u95EE\u7528\u6237: "Ask the user",
|
|
2124
|
+
\u62D2\u7EDD: "Deny",
|
|
2125
|
+
\u76F4\u63A5\u6267\u884C: "Run directly",
|
|
2126
|
+
"\u8BF7\u8C03\u7528 `iterate_transcript` \u5199\u5165 nudge \u6307\u4EE4\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_transcript` to write this nudge instruction:\n\n```json\n{a0}\n```",
|
|
2127
|
+
"\u8BF7\u8C03\u7528 `iterate_transcript` \u6E05\u9664\u5F53\u524D nudge\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_transcript` to clear the current nudge:\n\n```json\n{a0}\n```",
|
|
2128
|
+
\u901A\u8FC7: "Passed",
|
|
2129
|
+
\u5BFC\u81F4\u4E2D\u6B62: " aborted the run",
|
|
2130
|
+
\u9A8C\u8BC1\u7ED3\u679C: "Validation results",
|
|
2131
|
+
\u672C\u8F6E\u5C1A\u65E0\u9A8C\u8BC1\u8BB0\u5F55: "No validation was recorded in this round",
|
|
2132
|
+
"{a0} \u6761\u547D\u4EE4": "{a0} commands",
|
|
2133
|
+
\u56E0\u9A8C\u8BC1\u5931\u8D25\u4E2D\u6B62: "Stopped because validation failed",
|
|
2134
|
+
"\u5C1A\u672A\u62C9\u53D6\u9A8C\u8BC1\u7ED3\u679C\uFF1A\u6570\u636E\u5728\u78C1\u76D8 transcript \u91CC\uFF0C\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u590D\u5236\u6307\u4EE4\u53D1\u56DE\u4F1A\u8BDD\u5373\u53EF\u56DE\u586B\u3002": 'Validation results have not been pulled yet: they live in the transcript on disk \u2014 click "Pull disk snapshot", paste the instruction back into the chat, and this panel fills.',
|
|
2135
|
+
\u8FD0\u884C\u63A7\u5236\u53F0: "Run console",
|
|
2136
|
+
"\u6279\u51C6\u7B56\u7565\uFF1A{a0}": "Approval policy: {a0}",
|
|
2137
|
+
"\u5F53\u524D nudge": "Current nudge",
|
|
2138
|
+
"\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": "Copy the clear-nudge instruction (writes text:null) to ask the model to drop the persisted nudge",
|
|
2139
|
+
\u5DF2\u590D\u5236\u6E05\u9664\u6307\u4EE4: "Clear instruction copied",
|
|
2140
|
+
\u590D\u5236\u6E05\u9664\u6307\u4EE4: "Copy clear instruction",
|
|
2141
|
+
"\u8D77\u8349\u4E00\u6761 nudge \u65B9\u5411\u6307\u4EE4\u2026": "Draft a nudge to steer the run\u2026",
|
|
2142
|
+
"nudge \u8349\u7A3F": "Nudge draft",
|
|
2143
|
+
"\u590D\u5236 iterate_transcript nudge \u6307\u4EE4\u6587\u672C": "Copy the iterate_transcript nudge instruction text",
|
|
2144
|
+
"\u590D\u5236 nudge \u6307\u4EE4": "Copy nudge instruction",
|
|
2145
|
+
// ── F7 decision timeline ─────────────────────────────────────────────────
|
|
2146
|
+
\u6682\u65E0\u65F6\u95F4\u7EBF\u6761\u76EE: "No timeline entries yet",
|
|
2147
|
+
\u65F6\u95F4\u7EBF: "Timeline",
|
|
2148
|
+
\u6309\u65F6\u95F4\u7EBF\u7C7B\u578B\u7B5B\u9009: "Filter the timeline by type",
|
|
2149
|
+
\u5168\u90E8\u7C7B\u578B: "All types",
|
|
2150
|
+
\u6309\u8F6E\u6B21\u7B5B\u9009: "Filter by round",
|
|
2151
|
+
\u5168\u90E8\u8F6E\u6B21: "All rounds",
|
|
2152
|
+
"\u641C\u7D22\u65F6\u95F4\u7EBF\u2026": "Search timeline\u2026",
|
|
2153
|
+
\u641C\u7D22\u65F6\u95F4\u7EBF: "Search timeline",
|
|
2154
|
+
\u6E05\u9664: "Clear",
|
|
2155
|
+
\u65E0\u5339\u914D\u7684\u65F6\u95F4\u7EBF\u6761\u76EE: "No matching timeline entries",
|
|
2156
|
+
// ── F8 quality gate ──────────────────────────────────────────────────────
|
|
2157
|
+
\u8D28\u91CF\u95E8\u7981: "Quality gate",
|
|
2158
|
+
"\u590D\u5236 iterate_quality_gate \u67E5\u8BE2\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09": "Copy the iterate_quality_gate query instruction (JSON arguments included)",
|
|
2159
|
+
\u67E5\u8BE2\u95E8\u7981: "Query gate",
|
|
2160
|
+
"\u590D\u5236 iterate_quality_gate clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u7684 FAIL \u8BC1\u4E66\uFF09": "Copy the iterate_quality_gate clear instruction (resets a stale FAIL certificate)",
|
|
2161
|
+
\u6E05\u9664\u95E8\u7981: "Clear gate",
|
|
2162
|
+
"\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u56DE\u663E .iterate/ \u4E0B\u7684\u95E8\u7981\u3001\u7ECF\u9A8C\u3001\u9632\u5FA1\u4E8B\u4EF6\u4E0E transcript": "Copy the disk-snapshot instruction: have the model echo the gate, experience, defense events and transcript artifacts under .iterate/",
|
|
2163
|
+
\u8D28\u91CF\u95E8\u7981\u89C6\u56FE: "Quality gate view",
|
|
2164
|
+
\u8D28\u91CF\u95E8\u7981\u8BC1\u4E66: "Quality gate certificate",
|
|
2165
|
+
"\u8D28\u91CF\u95E8\u7981\u663E\u793A\u5404\u7EF4\u5EA6\u6536\u655B\u5EA6\u3001\u9A8C\u8BC1\u901A\u8FC7\u7387\u548C\u6574\u4F53 PASS/FAIL \u72B6\u6001\u3002": "The quality gate shows per-dimension convergence, the validation pass rate and the overall PASS/FAIL state.",
|
|
2166
|
+
\u590D\u5236\u67E5\u8BE2\u6307\u4EE4: "Copy query instruction",
|
|
2167
|
+
\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4: "Copy disk-snapshot instruction",
|
|
2168
|
+
"\u6574\u4F53\u8BC4\u5206 {a0} \xB7 \u9A8C\u8BC1 {a1}/{a2} \xB7 \u901A\u8FC7\u7387 {a3}%": "Overall score {a0} \xB7 validation {a1}/{a2} \xB7 pass rate {a3}%",
|
|
2169
|
+
"{a0} \xB7 \u6536\u655B {a1}% \xB7 \u8BC4\u5206 {a2}": "{a0} \xB7 convergence {a1}% \xB7 score {a2}",
|
|
2170
|
+
"\u8BC4\u5206 {a0} \xB7 \u53D1\u73B0 {a1} \xB7 \u5DF2\u4FEE\u590D {a2}": "Score {a0} \xB7 findings {a1} \xB7 fixed {a2}",
|
|
2171
|
+
"\u5931\u8D25\u539F\u56E0\uFF1A{a0}": "Failure reason: {a0}",
|
|
2172
|
+
// ── F9 experience bank ───────────────────────────────────────────────────
|
|
2173
|
+
"\u8BF7\u8C03\u7528 `iterate_experience` \u641C\u7D22\u7ECF\u9A8C\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_experience` to search the experience bank:\n\n```json\n{a0}\n```",
|
|
2174
|
+
"\u8BF7\u8C03\u7528 `iterate_experience` \u91C7\u7EB3\u7ECF\u9A8C\u5E76\u5E94\u7528\u5DF2\u9A8C\u8BC1\u4FEE\u6CD5\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_experience` to adopt this entry and apply its verified fix:\n\n```json\n{a0}\n```",
|
|
2175
|
+
"\u547D\u4E2D {a0} \u6B21": "Hit {a0} times",
|
|
2176
|
+
"\u590D\u5236\u91C7\u7EB3\u6307\u4EE4\uFF08\u67E5\u8BE2\u8BE5\u7ECF\u9A8C\u5E76\u5E94\u7528\u5230\u5F53\u524D\u4E0A\u4E0B\u6587\uFF09": "Copy the adopt instruction (queries this entry and applies it to the current context)",
|
|
2177
|
+
\u91C7\u7EB3: "Adopt",
|
|
2178
|
+
\u7ECF\u9A8C\u94F6\u884C: "Experience bank",
|
|
2179
|
+
"\u4F1A\u8BDD\u5185 {a0} \u6761": "{a0} in this session",
|
|
2180
|
+
"\u590D\u5236\u5217\u51FA\u7ECF\u9A8C\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09": "Copy the list-experience instruction (JSON arguments included)",
|
|
2181
|
+
\u5217\u51FA\u7ECF\u9A8C: "List experiences",
|
|
2182
|
+
"\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u628A .iterate/ \u4E0B\u7684\u7ECF\u9A8C\u94F6\u884C\u7B49\u4EA7\u7269\u56DE\u663E\u8FDB\u4F1A\u8BDD": "Copy the disk-snapshot instruction: echo the experience bank and other .iterate/ artifacts back into the chat",
|
|
2183
|
+
"\u641C\u7D22\u7ECF\u9A8C\u6A21\u5F0F\u2026": "Search experience patterns\u2026",
|
|
2184
|
+
\u641C\u7D22\u7ECF\u9A8C: "Search experiences",
|
|
2185
|
+
\u590D\u5236\u641C\u7D22\u6307\u4EE4: "Copy search instruction",
|
|
2186
|
+
\u641C\u7D22: "Search",
|
|
2187
|
+
"\u6CA1\u6709\u5339\u914D\u300C": "No experience matches \u201C",
|
|
2188
|
+
"\u300D\u7684\u7ECF\u9A8C": "\u201D",
|
|
2189
|
+
"\u4E5F\u53EF\u70B9\u300C\u5217\u51FA\u7ECF\u9A8C\u300D\u67E5\u8BE2\u78C1\u76D8\u4E0A\u7684\u5168\u90E8\u5386\u53F2\u7ECF\u9A8C\u3002": 'You can also click "List experiences" to query every historical entry on disk.',
|
|
2190
|
+
// ── F10 defense events ───────────────────────────────────────────────────
|
|
2191
|
+
"\u8BF7\u8C03\u7528 `iterate_defense_events` \u67E5\u8BE2\u9632\u5FA1\u4E8B\u4EF6\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_defense_events` to query defense events:\n\n```json\n{a0}\n```",
|
|
2192
|
+
\u524D\u7F6E\u6821\u9A8C\u5931\u8D25: "Precondition failed",
|
|
2193
|
+
\u4E0D\u53D8\u91CF\u8FDD\u53CD: "Invariant violated",
|
|
2194
|
+
\u5047\u8BBE\u88AB\u8BC1\u4F2A: "Assumption falsified",
|
|
2195
|
+
\u9632\u5FA1\u4E8B\u4EF6: "Defense events",
|
|
2196
|
+
"\u4F1A\u8BDD\u5185 {a0} \u6761 / \u7D2F\u8BA1 {a1}": "{a0} in this session / {a1} total",
|
|
2197
|
+
"\u590D\u5236\u5217\u51FA\u9632\u5FA1\u4E8B\u4EF6\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09": "Copy the list-defense-events instruction (JSON arguments included)",
|
|
2198
|
+
\u5217\u51FA\u4E8B\u4EF6: "List events",
|
|
2199
|
+
"\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u628A .iterate/ \u4E0B\u7684\u9632\u5FA1\u4E8B\u4EF6\u6D41\u56DE\u663E\u8FDB\u4F1A\u8BDD": "Copy the disk-snapshot instruction: echo the defense event stream under .iterate/ back into the chat",
|
|
2200
|
+
"\u8BF7\u8C03\u7528 `iterate_defense_events` \u67E5\u8BE2\u4E8B\u4EF6\u7EDF\u8BA1\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_defense_events` for the event counts:\n\n```json\n{a0}\n```",
|
|
2201
|
+
"\u590D\u5236\u7EDF\u8BA1\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09": "Copy the counts instruction (JSON arguments included)",
|
|
2202
|
+
\u7EDF\u8BA1: "Counts",
|
|
2203
|
+
"\u8BF7\u8C03\u7528 `iterate_defense_events` \u6E05\u9664\u9632\u5FA1\u4E8B\u4EF6\u6D41\uFF1A\n\n```json\n{a0}\n```": "Please call `iterate_defense_events` to clear the defense event stream:\n\n```json\n{a0}\n```",
|
|
2204
|
+
"\u590D\u5236 iterate_defense_events clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u4E8B\u4EF6\u6D41\uFF09": "Copy the iterate_defense_events clear instruction (resets a stale event stream)",
|
|
2205
|
+
\u6E05\u9664\u4E8B\u4EF6: "Clear events",
|
|
2206
|
+
\u9632\u5FA1\u4E8B\u4EF6\u6D41: "Defense event stream",
|
|
2207
|
+
"\u9632\u5FA1\u4E8B\u4EF6\u5305\u62EC\uFF1A\u524D\u7F6E\u6821\u9A8C\u5931\u8D25\u3001\u56DE\u6EDA\u3001\u4E0D\u53D8\u91CF\u8FDD\u53CD\u3001\u5047\u8BBE\u88AB\u8BC1\u4F2A\u3002": "Defense events cover failed preconditions, rollbacks, violated invariants and falsified assumptions.",
|
|
2208
|
+
\u6309\u4E8B\u4EF6\u7C7B\u578B\u7B5B\u9009: "Filter by event type",
|
|
2209
|
+
\u6E05\u9664\u7C7B\u578B\u7B5B\u9009: "Clear type filter",
|
|
2210
|
+
"\u9632\u7EBF\uFF1A{a0}": "Defense: {a0}",
|
|
2211
|
+
\u6CA1\u6709\u5339\u914D\u8BE5\u7C7B\u578B\u7684\u9632\u5FA1\u4E8B\u4EF6: "No defense events of this type",
|
|
2212
|
+
// ── copy feedback ────────────────────────────────────────────────────────
|
|
2213
|
+
\u5DF2\u590D\u5236: "Copied",
|
|
2214
|
+
\u590D\u5236\u5931\u8D25: "Copy failed"
|
|
2215
|
+
};
|
|
2216
|
+
var current = "zh";
|
|
2217
|
+
var storage = null;
|
|
2218
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
2219
|
+
function getLang() {
|
|
2220
|
+
return current;
|
|
2221
|
+
}
|
|
2222
|
+
function setLang(next, persist = true) {
|
|
2223
|
+
if (next !== current) {
|
|
2224
|
+
current = next;
|
|
2225
|
+
if (persist) {
|
|
2226
|
+
try {
|
|
2227
|
+
storage?.set(LANG_STORAGE_KEY, next);
|
|
2228
|
+
} catch {
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
for (const listener of [...listeners]) {
|
|
2232
|
+
try {
|
|
2233
|
+
listener(next);
|
|
2234
|
+
} catch {
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
function subscribeLang(listener) {
|
|
2240
|
+
listeners.add(listener);
|
|
2241
|
+
return () => {
|
|
2242
|
+
listeners.delete(listener);
|
|
2243
|
+
};
|
|
2244
|
+
}
|
|
2245
|
+
function configureLangStorage(next) {
|
|
2246
|
+
storage = next;
|
|
2247
|
+
}
|
|
2248
|
+
function detectLang(stored, navigatorLanguage) {
|
|
2249
|
+
if (stored === "en" || stored === "zh") return stored;
|
|
2250
|
+
return /^zh\b/i.test(navigatorLanguage ?? "") ? "zh" : "en";
|
|
2251
|
+
}
|
|
2252
|
+
function t(key) {
|
|
2253
|
+
return current === "en" ? EN[key] ?? key : key;
|
|
2254
|
+
}
|
|
2255
|
+
function tf(template, params = {}) {
|
|
2256
|
+
const translated = current === "en" ? EN[template] ?? template : template;
|
|
2257
|
+
return translated.replace(
|
|
2258
|
+
/\{([A-Za-z0-9_]+)\}/g,
|
|
2259
|
+
(whole, name2) => Object.prototype.hasOwnProperty.call(params, name2) ? String(params[name2]) : whole
|
|
2260
|
+
);
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
// src/client/session-cache.ts
|
|
2264
|
+
var current2 = null;
|
|
2265
|
+
var listeners2 = /* @__PURE__ */ new Set();
|
|
2266
|
+
function noteSession(session) {
|
|
2267
|
+
if (Object.is(session, current2)) return;
|
|
2268
|
+
current2 = session;
|
|
2269
|
+
for (const listener of [...listeners2]) listener();
|
|
2270
|
+
}
|
|
2271
|
+
function readSession() {
|
|
2272
|
+
return current2;
|
|
2273
|
+
}
|
|
2274
|
+
function subscribeSession(listener) {
|
|
2275
|
+
listeners2.add(listener);
|
|
2276
|
+
return () => {
|
|
2277
|
+
listeners2.delete(listener);
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
|
|
1376
2281
|
// src/client/index.ts
|
|
1377
2282
|
var React = require("react");
|
|
1378
2283
|
var name = "iterate-plugin";
|
|
@@ -1453,6 +2358,7 @@ var ITERATE_CSS = `
|
|
|
1453
2358
|
}
|
|
1454
2359
|
.iterate-btn[data-primary] { border-color: var(--dsw-alias-brand-primary); color: var(--dsw-alias-brand-primary); }
|
|
1455
2360
|
.iterate-btn[data-copied] { border-color: var(--dsw-alias-state-success-primary); color: var(--dsw-alias-state-success-primary); }
|
|
2361
|
+
.iterate-btn[data-failed], .iterate-cmd[data-failed] { border-color: var(--dsw-alias-state-error-primary); color: var(--dsw-alias-state-error-primary); }
|
|
1456
2362
|
.iterate-payload { width: 100%; margin-top: 8px; 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-family: var(--dsw-font-mono, ui-monospace, monospace); font-size: 11px; white-space: pre-wrap; }
|
|
1457
2363
|
|
|
1458
2364
|
.iterate-stats { margin: 10px 0; border: 1px solid var(--dsw-alias-border-l1); border-radius: 12px; background: var(--dsw-alias-bg-layer-1); padding: 12px 14px; }
|
|
@@ -1637,6 +2543,16 @@ var ITERATE_CSS = `
|
|
|
1637
2543
|
.iterate-obs-block-head[data-click] { cursor: pointer; }
|
|
1638
2544
|
.iterate-obs-block-body { padding: 8px 10px; }
|
|
1639
2545
|
.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; }
|
|
2546
|
+
/* Gap #6: validation / gate status colors (pass=green, fail=red). */
|
|
2547
|
+
.iterate-obs-chip[data-status="pass"], .iterate-obs-badge[data-status="pass"] { color: var(--dsw-alias-state-success-primary); background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent); border-color: color-mix(in srgb, var(--dsw-alias-state-success-primary) 28%, transparent); }
|
|
2548
|
+
.iterate-obs-chip[data-status="fail"], .iterate-obs-badge[data-status="fail"] { color: var(--dsw-alias-state-error-primary); background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent); border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 28%, transparent); }
|
|
2549
|
+
.iterate-obs-row[data-status="fail"] { color: var(--dsw-alias-state-error-primary); }
|
|
2550
|
+
.iterate-obs-row[data-highlight] { background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent); border-radius: 6px; padding-left: 6px; padding-right: 6px; border-bottom-color: transparent; font-weight: 600; }
|
|
2551
|
+
/* Gap #13: cross-round comparison table. */
|
|
2552
|
+
.iterate-obs-table { width: 100%; border-collapse: collapse; font-size: 11px; }
|
|
2553
|
+
.iterate-obs-table th, .iterate-obs-table td { text-align: left; padding: 5px 8px; border-bottom: 1px solid var(--dsw-alias-border-l1); white-space: nowrap; }
|
|
2554
|
+
.iterate-obs-table th { color: var(--dsw-alias-label-secondary); font-weight: 600; }
|
|
2555
|
+
.iterate-obs-table td { color: var(--dsw-alias-label-primary); }
|
|
1640
2556
|
.iterate-obs-msg { padding: 2px 0; color: var(--dsw-alias-label-secondary); line-height: 1.5; font-size: 11px; word-break: break-word; }
|
|
1641
2557
|
.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; }
|
|
1642
2558
|
.iterate-obs-bar { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--dsw-alias-label-secondary); flex-wrap: wrap; }
|
|
@@ -1694,12 +2610,12 @@ function createStorage() {
|
|
|
1694
2610
|
}
|
|
1695
2611
|
}
|
|
1696
2612
|
function removeStorageByPrefix(prefix) {
|
|
1697
|
-
if (!
|
|
2613
|
+
if (!storage2) return 0;
|
|
1698
2614
|
let removed = 0;
|
|
1699
2615
|
try {
|
|
1700
|
-
for (const key of
|
|
2616
|
+
for (const key of storage2.keys()) {
|
|
1701
2617
|
if (key.startsWith(prefix)) {
|
|
1702
|
-
|
|
2618
|
+
storage2.remove(key);
|
|
1703
2619
|
removed++;
|
|
1704
2620
|
}
|
|
1705
2621
|
}
|
|
@@ -1708,6 +2624,20 @@ function removeStorageByPrefix(prefix) {
|
|
|
1708
2624
|
}
|
|
1709
2625
|
return removed;
|
|
1710
2626
|
}
|
|
2627
|
+
function useLang() {
|
|
2628
|
+
const [lang, set] = React.useState(getLang());
|
|
2629
|
+
React.useEffect(() => subscribeLang(set), []);
|
|
2630
|
+
return lang;
|
|
2631
|
+
}
|
|
2632
|
+
function useSessionSnapshot() {
|
|
2633
|
+
return React.useSyncExternalStore(subscribeSession, readSession, readSession);
|
|
2634
|
+
}
|
|
2635
|
+
function withLang(Component) {
|
|
2636
|
+
return function LangScoped(props) {
|
|
2637
|
+
useLang();
|
|
2638
|
+
return React.createElement(Component, props);
|
|
2639
|
+
};
|
|
2640
|
+
}
|
|
1711
2641
|
function copyText(text) {
|
|
1712
2642
|
if (typeof navigator !== "undefined" && navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
|
|
1713
2643
|
return navigator.clipboard.writeText(text).then(
|
|
@@ -1745,10 +2675,23 @@ function severityColor(severity) {
|
|
|
1745
2675
|
function severityLabel(severity) {
|
|
1746
2676
|
return SEVERITY_LABEL[coerceSeverity(severity)];
|
|
1747
2677
|
}
|
|
2678
|
+
function formatConfigValue(value) {
|
|
2679
|
+
if (typeof value === "string") return value;
|
|
2680
|
+
if (value === void 0) return "\u2014";
|
|
2681
|
+
if (value === null) return "null";
|
|
2682
|
+
try {
|
|
2683
|
+
return JSON.stringify(value) ?? String(value);
|
|
2684
|
+
} catch {
|
|
2685
|
+
return String(value);
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
function parenthesize(label) {
|
|
2689
|
+
return getLang() === "en" ? ` (${label})` : `\uFF08${label}\uFF09`;
|
|
2690
|
+
}
|
|
1748
2691
|
function runStatusText(live, manifest) {
|
|
1749
|
-
if (live) return "\u8FD0\u884C\u4E2D";
|
|
2692
|
+
if (live) return t("\u8FD0\u884C\u4E2D");
|
|
1750
2693
|
const reason = manifest && typeof manifest.stoppedReason === "string" ? manifest.stoppedReason : "";
|
|
1751
|
-
return reason ? stoppedReasonLabel(reason) : "\u5DF2\u7ED3\u675F";
|
|
2694
|
+
return reason ? stoppedReasonLabel(reason, getLang()) : t("\u5DF2\u7ED3\u675F");
|
|
1752
2695
|
}
|
|
1753
2696
|
function readSlots(ctx) {
|
|
1754
2697
|
if (ctx && ctx.slots && typeof ctx.slots.inject === "function" && typeof ctx.slots.register === "function") {
|
|
@@ -1772,7 +2715,7 @@ function readTheme(ctx) {
|
|
|
1772
2715
|
}
|
|
1773
2716
|
var slotsSvc = void 0;
|
|
1774
2717
|
var themeSvc = void 0;
|
|
1775
|
-
var
|
|
2718
|
+
var storage2 = null;
|
|
1776
2719
|
var themeDisposer = null;
|
|
1777
2720
|
var themeListener = null;
|
|
1778
2721
|
var themeEnabled = true;
|
|
@@ -1823,7 +2766,7 @@ function registerThemeListener(ctx) {
|
|
|
1823
2766
|
}
|
|
1824
2767
|
function setThemeEnabled(enabled) {
|
|
1825
2768
|
themeEnabled = enabled;
|
|
1826
|
-
if (
|
|
2769
|
+
if (storage2) storage2.set(THEME_STORAGE_KEY, enabled ? "1" : "0");
|
|
1827
2770
|
if (enabled) applyThemeSkin();
|
|
1828
2771
|
else clearThemeSkin();
|
|
1829
2772
|
}
|
|
@@ -1840,7 +2783,7 @@ function TrendChart({ points }) {
|
|
|
1840
2783
|
key: p.round,
|
|
1841
2784
|
className: "iterate-trend-bar",
|
|
1842
2785
|
"data-hot": p.count > 0 ? "" : void 0,
|
|
1843
|
-
title: `Round
|
|
2786
|
+
title: tf(`Round {a0}\uFF1A{a1} \u9879`, { a0: p.round, a1: p.count }),
|
|
1844
2787
|
style: { height: `${Math.max(4, Math.round(p.count / max * 24))}px` }
|
|
1845
2788
|
})
|
|
1846
2789
|
);
|
|
@@ -1848,16 +2791,30 @@ function TrendChart({ points }) {
|
|
|
1848
2791
|
return React.createElement("div", {
|
|
1849
2792
|
className: "iterate-trend",
|
|
1850
2793
|
role: "img",
|
|
1851
|
-
"aria-label": `\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\u8D8B\u52BF\uFF1A
|
|
2794
|
+
"aria-label": tf(`\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\u8D8B\u52BF\uFF1A{a0}`, { a0: summary })
|
|
1852
2795
|
}, ...bars);
|
|
1853
2796
|
}
|
|
1854
2797
|
function StartIterationButton() {
|
|
1855
2798
|
const [copied, setCopied] = React.useState(null);
|
|
2799
|
+
const [failed, setFailed] = React.useState(null);
|
|
2800
|
+
const timers = React.useRef([]);
|
|
2801
|
+
React.useEffect(
|
|
2802
|
+
() => () => {
|
|
2803
|
+
for (const t2 of timers.current) clearTimeout(t2);
|
|
2804
|
+
timers.current = [];
|
|
2805
|
+
},
|
|
2806
|
+
[]
|
|
2807
|
+
);
|
|
1856
2808
|
const copy = (key, text) => {
|
|
1857
2809
|
copyText(text).then((ok) => {
|
|
1858
2810
|
if (ok) {
|
|
2811
|
+
setFailed(null);
|
|
1859
2812
|
setCopied(key);
|
|
1860
|
-
setTimeout(() => setCopied((cur) => cur === key ? null : cur), 1600);
|
|
2813
|
+
const t2 = window.setTimeout(() => setCopied((cur) => cur === key ? null : cur), 1600);
|
|
2814
|
+
timers.current.push(t2);
|
|
2815
|
+
} else {
|
|
2816
|
+
setCopied(null);
|
|
2817
|
+
setFailed(key);
|
|
1861
2818
|
}
|
|
1862
2819
|
});
|
|
1863
2820
|
};
|
|
@@ -1866,19 +2823,23 @@ function StartIterationButton() {
|
|
|
1866
2823
|
className: "iterate-cmd",
|
|
1867
2824
|
"data-primary": primary ? "" : void 0,
|
|
1868
2825
|
"data-copied": copied === key ? "" : void 0,
|
|
2826
|
+
"data-failed": failed === key ? "" : void 0,
|
|
1869
2827
|
onClick: () => copy(key, command),
|
|
1870
2828
|
title
|
|
1871
|
-
}, copied === key ? "\u5DF2\u590D\u5236" : label);
|
|
2829
|
+
}, copied === key ? t("\u5DF2\u590D\u5236") : failed === key ? t("\u590D\u5236\u5931\u8D25") : label);
|
|
1872
2830
|
return React.createElement(
|
|
1873
2831
|
"span",
|
|
1874
2832
|
{ className: "iterate-dashboard-launch" },
|
|
1875
|
-
btn("start-full", "\u5B8C\u6574\u8FED\u4EE3",
|
|
1876
|
-
btn("start-review", "\u4EC5\u8BC4\u5BA1",
|
|
2833
|
+
btn("start-full", t("\u5B8C\u6574\u8FED\u4EE3"), START_INSTRUCTIONS.full, true, t("\u590D\u5236\u542F\u52A8\u6307\u4EE4\uFF1A\u5B8C\u6574\u300C\u5BA1\u67E5 \u2192 \u4FEE\u590D \u2192 \u9A8C\u8BC1 \u2192 \u590D\u76D8\u300D\u95ED\u73AF\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u5373\u53EF\uFF09")),
|
|
2834
|
+
btn("start-review", t("\u4EC5\u8BC4\u5BA1"), START_INSTRUCTIONS.reviewOnly, false, t("\u590D\u5236\u542F\u52A8\u6307\u4EE4\uFF1A\u53EA\u5BA1\u67E5\u4E0D\u4FEE\u6539\uFF08dry-run\uFF09\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u5373\u53EF\uFF09"))
|
|
1877
2835
|
);
|
|
1878
2836
|
}
|
|
1879
2837
|
function ConvergenceDashboard(props) {
|
|
1880
2838
|
const [pulseKey, setPulseKey] = React.useState(0);
|
|
1881
2839
|
const session = props && props.session ? props.session : null;
|
|
2840
|
+
React.useEffect(() => {
|
|
2841
|
+
noteSession(session);
|
|
2842
|
+
}, [session]);
|
|
1882
2843
|
const report = latestReport(session);
|
|
1883
2844
|
React.useEffect(() => {
|
|
1884
2845
|
if (!report) return;
|
|
@@ -1888,11 +2849,38 @@ function ConvergenceDashboard(props) {
|
|
|
1888
2849
|
setPulseKey((k) => k + 1);
|
|
1889
2850
|
}, [report && hashReport(report) + ":" + getCurrentRound(report)]);
|
|
1890
2851
|
if (!report) {
|
|
2852
|
+
const runState = dashboardRunState(latestTranscript(session));
|
|
2853
|
+
if (runState.state === "running") {
|
|
2854
|
+
return React.createElement(
|
|
2855
|
+
"div",
|
|
2856
|
+
{ "data-iterate-root": "", "data-iterate": "dashboard", "data-run-state": "running", className: "iterate-dashboard iterate-dashboard-empty" },
|
|
2857
|
+
React.createElement("span", { className: "iterate-round-badge", "data-live": "" }, t("\u8FD0\u884C\u4E2D")),
|
|
2858
|
+
React.createElement(
|
|
2859
|
+
"span",
|
|
2860
|
+
{ className: "iterate-empty-hint" },
|
|
2861
|
+
tf(`\u7B2C {a0} \u8F6E\u8FDB\u884C\u4E2D{a1}\u2014\u2014\u9996\u8F6E\u6C47\u603B\u62A5\u544A\u843D\u5730\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u6536\u655B\u8FDB\u5EA6\u4E0E\u53D1\u73B0\u7EDF\u8BA1\u3002`, { a0: String(runState.round), a1: runState.phase ? tf(` \xB7 \u9636\u6BB5\u300C{a0}\u300D`, { a0: runState.phase }) : "" })
|
|
2862
|
+
)
|
|
2863
|
+
// No start button while a run is active.
|
|
2864
|
+
);
|
|
2865
|
+
}
|
|
2866
|
+
if (runState.state === "done") {
|
|
2867
|
+
return React.createElement(
|
|
2868
|
+
"div",
|
|
2869
|
+
{ "data-iterate-root": "", "data-iterate": "dashboard", "data-run-state": "done", className: "iterate-dashboard iterate-dashboard-empty" },
|
|
2870
|
+
React.createElement("span", { className: "iterate-round-badge" }, t("\u5DF2\u7ED3\u675F")),
|
|
2871
|
+
React.createElement(
|
|
2872
|
+
"span",
|
|
2873
|
+
{ className: "iterate-empty-hint" },
|
|
2874
|
+
tf(`\u4E0A\u6B21\u8FED\u4EE3\u5728\u7B2C {a0} \u8F6E\u540E\u505C\u6B62{a1}\uFF0C\u4F46\u6CA1\u6709\u53EF\u5C55\u793A\u7684\u6C47\u603B\u62A5\u544A\u3002`, { a0: String(runState.round), a1: runState.stoppedReason ? parenthesize(stoppedReasonLabel(runState.stoppedReason, getLang())) : "" })
|
|
2875
|
+
),
|
|
2876
|
+
React.createElement(StartIterationButton, null)
|
|
2877
|
+
);
|
|
2878
|
+
}
|
|
1891
2879
|
return React.createElement(
|
|
1892
2880
|
"div",
|
|
1893
|
-
{ "data-iterate-root": "", "data-iterate": "dashboard", className: "iterate-dashboard iterate-dashboard-empty" },
|
|
2881
|
+
{ "data-iterate-root": "", "data-iterate": "dashboard", "data-run-state": "empty", className: "iterate-dashboard iterate-dashboard-empty" },
|
|
1894
2882
|
React.createElement("span", { className: "iterate-round-badge" }, "iterate"),
|
|
1895
|
-
React.createElement("span", { className: "iterate-empty-hint" }, "\u8FD0\u884C\u4E00\u6B21\u8BC4\u5BA1\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u6536\u655B\u8FDB\u5EA6\u4E0E\u53D1\u73B0\u7EDF\u8BA1\u3002"),
|
|
2883
|
+
React.createElement("span", { className: "iterate-empty-hint" }, t("\u8FD0\u884C\u4E00\u6B21\u8BC4\u5BA1\u540E\uFF0C\u8FD9\u91CC\u4F1A\u663E\u793A\u6536\u655B\u8FDB\u5EA6\u4E0E\u53D1\u73B0\u7EDF\u8BA1\u3002")),
|
|
1896
2884
|
React.createElement(StartIterationButton, null)
|
|
1897
2885
|
);
|
|
1898
2886
|
}
|
|
@@ -1907,20 +2895,20 @@ function ConvergenceDashboard(props) {
|
|
|
1907
2895
|
const resumeChip = resumeCount > 0 ? React.createElement("span", {
|
|
1908
2896
|
className: "iterate-chip-resume",
|
|
1909
2897
|
key: "resume",
|
|
1910
|
-
title: "\u672C\u6B21\u8FED\u4EE3\u4ECE\u4E0A\u4E00\u6B21\u4E2D\u65AD\u7684\u65AD\u70B9\u7EE7\u7EED\u6267\u884C"
|
|
1911
|
-
}, `\u5DF2\u4E2D\u65AD\u6062\u590D \xD7
|
|
2898
|
+
title: t("\u672C\u6B21\u8FED\u4EE3\u4ECE\u4E0A\u4E00\u6B21\u4E2D\u65AD\u7684\u65AD\u70B9\u7EE7\u7EED\u6267\u884C")
|
|
2899
|
+
}, tf(`\u5DF2\u4E2D\u65AD\u6062\u590D \xD7{a0}`, { a0: String(resumeCount) })) : null;
|
|
1912
2900
|
const imageChip = imageCount > 0 ? React.createElement("span", {
|
|
1913
2901
|
className: "iterate-chip-images",
|
|
1914
2902
|
key: "images",
|
|
1915
|
-
title: "\u4F1A\u8BDD\u4E2D\u68C0\u6D4B\u5230\u7528\u6237\u9644\u5E26\u7684\u56FE\u7247\uFF0C\u8BC4\u5BA1\u5C06\u4F5C\u4E3A\u89C6\u89C9\u8BC1\u636E\u53C2\u8003"
|
|
1916
|
-
}, `\u9644\u4EF6\u56FE\u7247
|
|
2903
|
+
title: t("\u4F1A\u8BDD\u4E2D\u68C0\u6D4B\u5230\u7528\u6237\u9644\u5E26\u7684\u56FE\u7247\uFF0C\u8BC4\u5BA1\u5C06\u4F5C\u4E3A\u89C6\u89C9\u8BC1\u636E\u53C2\u8003")
|
|
2904
|
+
}, tf(`\u9644\u4EF6\u56FE\u7247 {a0}`, { a0: String(imageCount) })) : null;
|
|
1917
2905
|
const transcript = latestTranscript(session);
|
|
1918
2906
|
const phase = transcript ? latestPhase(transcript.phases) : "";
|
|
1919
2907
|
const liveChip = transcript && phase ? React.createElement("span", {
|
|
1920
2908
|
className: "iterate-chip-phase",
|
|
1921
2909
|
key: "phase",
|
|
1922
2910
|
"data-live": transcript.active === true ? "" : void 0,
|
|
1923
|
-
title: transcript.active === true ? `\u5F53\u524D\u5904\u4E8E\u300C
|
|
2911
|
+
title: transcript.active === true ? tf(`\u5F53\u524D\u5904\u4E8E\u300C{a0}\u300D\u9636\u6BB5\uFF08\u8FD0\u884C\u4E2D\uFF09`, { a0: phase }) : tf(`\u6700\u8FD1\u4E00\u6B21\u6267\u884C\u505C\u7559\u5728\u300C{a0}\u300D\u9636\u6BB5\uFF08\u5DF2\u7ED3\u675F\uFF09`, { a0: phase })
|
|
1924
2912
|
}, `${phase} \xB7 ${runStatusText(transcript.active === true, transcript)}`) : null;
|
|
1925
2913
|
const taskModeTranscript = latestTranscript(session);
|
|
1926
2914
|
const taskMode = taskModeTranscript?.taskMode;
|
|
@@ -1928,7 +2916,7 @@ function ConvergenceDashboard(props) {
|
|
|
1928
2916
|
className: "iterate-chip-taskmode",
|
|
1929
2917
|
key: "taskmode",
|
|
1930
2918
|
"data-mode": taskMode,
|
|
1931
|
-
title: taskMode === "code" ? "\u4EE3\u7801\u6A21\u5F0F\uFF1A\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6" : "\u8FED\u4EE3\u6A21\u5F0F\uFF1A\u5BA1\u67E5-\u4FEE\u590D\u5FAA\u73AF"
|
|
2919
|
+
title: taskMode === "code" ? t("\u4EE3\u7801\u6A21\u5F0F\uFF1A\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6") : t("\u8FED\u4EE3\u6A21\u5F0F\uFF1A\u5BA1\u67E5-\u4FEE\u590D\u5FAA\u73AF")
|
|
1932
2920
|
}, taskMode === "code" ? "\u25CF code" : "\u25CF iterate") : null;
|
|
1933
2921
|
const dimNames = Object.keys(dims);
|
|
1934
2922
|
const dimBadges = dimNames.slice(0, 6).map(
|
|
@@ -1944,7 +2932,7 @@ function ConvergenceDashboard(props) {
|
|
|
1944
2932
|
React.createElement(
|
|
1945
2933
|
"span",
|
|
1946
2934
|
{ key: "+more", className: "iterate-dim-badge iterate-dim-more", title: dimNames.slice(6).join(", ") },
|
|
1947
|
-
|
|
2935
|
+
tf(`+{a0} \u66F4\u591A`, { a0: overflow })
|
|
1948
2936
|
)
|
|
1949
2937
|
);
|
|
1950
2938
|
}
|
|
@@ -1955,14 +2943,14 @@ function ConvergenceDashboard(props) {
|
|
|
1955
2943
|
const fixBadge = fixCount !== null ? React.createElement("span", {
|
|
1956
2944
|
className: "iterate-metric",
|
|
1957
2945
|
key: "fixes",
|
|
1958
|
-
title: "\u672C\u8F6E\u5DF2\u5E94\u7528\u7684\u539F\u5B50\u4FEE\u590D\u6570\uFF08\u6B63\u5E38\u6A21\u5F0F\uFF09"
|
|
2946
|
+
title: t("\u672C\u8F6E\u5DF2\u5E94\u7528\u7684\u539F\u5B50\u4FEE\u590D\u6570\uFF08\u6B63\u5E38\u6A21\u5F0F\uFF09")
|
|
1959
2947
|
}, `${String(fixCount)} fixes`) : null;
|
|
1960
2948
|
const converged = report.convergence && report.convergence.converged === true;
|
|
1961
2949
|
const convChip = converged ? React.createElement("span", {
|
|
1962
2950
|
className: "iterate-chip-resume",
|
|
1963
2951
|
key: "converged",
|
|
1964
|
-
title: "\u5BA1\u67E5\u5DF2\u6536\u655B\uFF1A\u6700\u540E\u4E00\u8F6E\u672A\u53D1\u73B0\u65B0\u95EE\u9898"
|
|
1965
|
-
}, "\u2713 \u5DF2\u6536\u655B") : null;
|
|
2952
|
+
title: t("\u5BA1\u67E5\u5DF2\u6536\u655B\uFF1A\u6700\u540E\u4E00\u8F6E\u672A\u53D1\u73B0\u65B0\u95EE\u9898")
|
|
2953
|
+
}, t("\u2713 \u5DF2\u6536\u655B")) : null;
|
|
1966
2954
|
const sevMetric = (key, label) => React.createElement(
|
|
1967
2955
|
"span",
|
|
1968
2956
|
{ className: "iterate-metric", key, title: label },
|
|
@@ -2031,19 +3019,19 @@ function StatsCard(props) {
|
|
|
2031
3019
|
React.createElement("td", { style: { color: SEVERITY_COLOR.low } }, String(h.low))
|
|
2032
3020
|
)
|
|
2033
3021
|
);
|
|
2034
|
-
const trendLine = `\u9996\u8F6E
|
|
2035
|
-
const completion = buildCompletionSummary(report);
|
|
3022
|
+
const trendLine = tf(`\u9996\u8F6E {a0} \u2192 \u672B\u8F6E {a1} \u9879\uFF0C\u964D\u5E45 {a2}%{a3}`, { a0: trend.firstRound, a1: trend.lastRound, a2: trend.reductionPercent, a3: trend.converged ? t("\uFF0C\u5DF2\u6536\u655B") : "" });
|
|
3023
|
+
const completion = buildCompletionSummary(report, getLang());
|
|
2036
3024
|
return React.createElement(
|
|
2037
3025
|
"div",
|
|
2038
3026
|
{ "data-iterate-root": "", "data-iterate": "stats", className: "iterate-stats" },
|
|
2039
3027
|
React.createElement(
|
|
2040
3028
|
"div",
|
|
2041
3029
|
{ className: "iterate-triage-head" },
|
|
2042
|
-
React.createElement("span", {}, "Iterate \xB7 \u6536\u655B\u7EDF\u8BA1"),
|
|
3030
|
+
React.createElement("span", {}, t("Iterate \xB7 \u6536\u655B\u7EDF\u8BA1")),
|
|
2043
3031
|
React.createElement(
|
|
2044
3032
|
"span",
|
|
2045
3033
|
{ className: "iterate-triage-hint" },
|
|
2046
|
-
`Round ${getCurrentRound(report)}/${getTotalRounds(report)} \xB7 ${total} findings \xB7 ${report.convergence && report.convergence.converged ? "\u5DF2\u6536\u655B" : "\u672A\u6536\u655B"}`
|
|
3034
|
+
`Round ${getCurrentRound(report)}/${getTotalRounds(report)} \xB7 ${total} findings \xB7 ${report.convergence && report.convergence.converged ? t("\u5DF2\u6536\u655B") : t("\u672A\u6536\u655B")}`
|
|
2047
3035
|
)
|
|
2048
3036
|
),
|
|
2049
3037
|
React.createElement("div", { className: "iterate-stats-grid" }, ...rows),
|
|
@@ -2056,7 +3044,7 @@ function StatsCard(props) {
|
|
|
2056
3044
|
className: "iterate-btn",
|
|
2057
3045
|
"data-primary": showHistory ? "" : void 0,
|
|
2058
3046
|
onClick: () => setShowHistory((v) => !v)
|
|
2059
|
-
}, showHistory ? "\u6536\u8D77\u5386\u53F2" : "\u5386\u53F2 / \u8D8B\u52BF")
|
|
3047
|
+
}, showHistory ? t("\u6536\u8D77\u5386\u53F2") : t("\u5386\u53F2 / \u8D8B\u52BF"))
|
|
2060
3048
|
),
|
|
2061
3049
|
showHistory ? React.createElement(
|
|
2062
3050
|
"div",
|
|
@@ -2070,8 +3058,8 @@ function StatsCard(props) {
|
|
|
2070
3058
|
React.createElement(
|
|
2071
3059
|
"tr",
|
|
2072
3060
|
{},
|
|
2073
|
-
React.createElement("th", {}, "\u8F6E\u6B21"),
|
|
2074
|
-
React.createElement("th", {}, "\u53D1\u73B0"),
|
|
3061
|
+
React.createElement("th", {}, t("\u8F6E\u6B21")),
|
|
3062
|
+
React.createElement("th", {}, t("\u53D1\u73B0")),
|
|
2075
3063
|
React.createElement("th", { style: { color: SEVERITY_COLOR.critical } }, "CRIT"),
|
|
2076
3064
|
React.createElement("th", { style: { color: SEVERITY_COLOR.high } }, "HIGH"),
|
|
2077
3065
|
React.createElement("th", { style: { color: SEVERITY_COLOR.medium } }, "MED"),
|
|
@@ -2094,7 +3082,7 @@ function TriagePanel(props) {
|
|
|
2094
3082
|
const storageKey = TRIAGE_STORAGE_PREFIX + hashReport(report);
|
|
2095
3083
|
const [verdicts, setVerdicts] = React.useState(() => {
|
|
2096
3084
|
const initial = buildTriageState(report);
|
|
2097
|
-
const saved =
|
|
3085
|
+
const saved = storage2 ? storage2.get(storageKey) : null;
|
|
2098
3086
|
if (saved) {
|
|
2099
3087
|
try {
|
|
2100
3088
|
const parsed = JSON.parse(saved);
|
|
@@ -2116,8 +3104,27 @@ function TriagePanel(props) {
|
|
|
2116
3104
|
const [selected, setSelected] = React.useState(null);
|
|
2117
3105
|
const [selectAll, setSelectAll] = React.useState(false);
|
|
2118
3106
|
const [cmdCopied, setCmdCopied] = React.useState(null);
|
|
3107
|
+
const [cmdFailed, setCmdFailed] = React.useState(null);
|
|
3108
|
+
const cmdTimers = React.useRef([]);
|
|
3109
|
+
React.useEffect(() => () => {
|
|
3110
|
+
for (const t2 of cmdTimers.current) clearTimeout(t2);
|
|
3111
|
+
cmdTimers.current = [];
|
|
3112
|
+
}, []);
|
|
3113
|
+
const copyWithFeedback = (text, key) => {
|
|
3114
|
+
copyText(text).then((ok) => {
|
|
3115
|
+
if (ok) {
|
|
3116
|
+
setCmdFailed(null);
|
|
3117
|
+
setCmdCopied(key);
|
|
3118
|
+
cmdTimers.current.push(window.setTimeout(() => setCmdCopied((cur) => cur === key ? null : cur), 1600));
|
|
3119
|
+
} else {
|
|
3120
|
+
setCmdCopied(null);
|
|
3121
|
+
setCmdFailed(key);
|
|
3122
|
+
setPayload(text);
|
|
3123
|
+
}
|
|
3124
|
+
});
|
|
3125
|
+
};
|
|
2119
3126
|
React.useEffect(() => {
|
|
2120
|
-
if (
|
|
3127
|
+
if (storage2) storage2.set(storageKey, JSON.stringify(verdicts));
|
|
2121
3128
|
}, [storageKey, verdicts]);
|
|
2122
3129
|
const setVerdict = (index, verdict) => {
|
|
2123
3130
|
setVerdicts((prev) => ({ ...prev, [String(index)]: verdict }));
|
|
@@ -2144,9 +3151,9 @@ function TriagePanel(props) {
|
|
|
2144
3151
|
if (!doc) return;
|
|
2145
3152
|
const onKeyDown = (ev) => {
|
|
2146
3153
|
if (ev.metaKey || ev.ctrlKey || ev.altKey) return;
|
|
2147
|
-
const
|
|
2148
|
-
if (
|
|
2149
|
-
if (
|
|
3154
|
+
const t2 = ev.target;
|
|
3155
|
+
if (t2 && (t2.tagName === "INPUT" || t2.tagName === "TEXTAREA" || t2.tagName === "SELECT")) return;
|
|
3156
|
+
if (t2 && typeof t2.isContentEditable === "boolean" && t2.isContentEditable) return;
|
|
2150
3157
|
const rootEl = doc.querySelector('[data-iterate="triage"]');
|
|
2151
3158
|
if (!rootEl) return;
|
|
2152
3159
|
const activeEl = doc.activeElement;
|
|
@@ -2180,55 +3187,26 @@ function TriagePanel(props) {
|
|
|
2180
3187
|
const ignoredCount = ignored.length;
|
|
2181
3188
|
const counts = countVerdicts(verdicts);
|
|
2182
3189
|
const doApproveArchitecturalFix = () => {
|
|
2183
|
-
const cmd =
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
setCmdCopied("approve-arch");
|
|
2187
|
-
setTimeout(() => setCmdCopied(null), 1600);
|
|
2188
|
-
}
|
|
2189
|
-
});
|
|
3190
|
+
const cmd = buildArchitecturalFixInstruction(findings);
|
|
3191
|
+
if (!cmd) return;
|
|
3192
|
+
copyWithFeedback(cmd, "approve-arch");
|
|
2190
3193
|
};
|
|
2191
3194
|
const doTriggerNewRound = () => {
|
|
2192
|
-
const cmd = "\u8BF7\u7EE7\u7EED\u6267\u884C\u4E0B\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5";
|
|
2193
|
-
|
|
2194
|
-
if (ok) {
|
|
2195
|
-
setCmdCopied("new-round");
|
|
2196
|
-
setTimeout(() => setCmdCopied(null), 1600);
|
|
2197
|
-
}
|
|
2198
|
-
});
|
|
3195
|
+
const cmd = t("\u8BF7\u7EE7\u7EED\u6267\u884C\u4E0B\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5");
|
|
3196
|
+
copyWithFeedback(cmd, "new-round");
|
|
2199
3197
|
};
|
|
2200
3198
|
const doRollbackToCheckpoint = () => {
|
|
2201
|
-
const cmd = "\u8BF7\u56DE\u6EDA\u4E0A\u4E00\u8F6E\u8FED\u4EE3\u7684\u4FEE\u590D\uFF1A\u8C03\u7528 `iterate_history` \u67E5\u770B\u672C\u8F6E fix id\uFF0C\u518D\u7528 `iterate_rollback` \u9010\u4E2A\u64A4\u9500\u8FD9\u4E9B\u4FEE\u590D";
|
|
2202
|
-
|
|
2203
|
-
if (ok) {
|
|
2204
|
-
setCmdCopied("rollback");
|
|
2205
|
-
setTimeout(() => setCmdCopied(null), 1600);
|
|
2206
|
-
}
|
|
2207
|
-
});
|
|
3199
|
+
const cmd = t("\u8BF7\u56DE\u6EDA\u4E0A\u4E00\u8F6E\u8FED\u4EE3\u7684\u4FEE\u590D\uFF1A\u8C03\u7528 `iterate_history` \u67E5\u770B\u672C\u8F6E fix id\uFF0C\u518D\u7528 `iterate_rollback` \u9010\u4E2A\u64A4\u9500\u8FD9\u4E9B\u4FEE\u590D");
|
|
3200
|
+
copyWithFeedback(cmd, "rollback");
|
|
2208
3201
|
};
|
|
2209
3202
|
const doAssignFindings = () => {
|
|
2210
3203
|
const targetAll = selectAll;
|
|
2211
3204
|
const scopeIndices = targetAll ? allIndices : indices;
|
|
2212
|
-
const scopeFindings = findings.filter((f, i) => scopeIndices.includes(i))
|
|
2213
|
-
file: String(f.file || ""),
|
|
2214
|
-
...typeof f.line === "number" && f.line > 0 ? { line: f.line } : {},
|
|
2215
|
-
dimension: String(f.dimension || ""),
|
|
2216
|
-
severity: String(f.severity || ""),
|
|
2217
|
-
summary: String(f.summary || ""),
|
|
2218
|
-
...f.suggested_fix ? { suggested_fix: String(f.suggested_fix) } : {}
|
|
2219
|
-
}));
|
|
3205
|
+
const scopeFindings = findings.filter((f, i) => scopeIndices.includes(i));
|
|
2220
3206
|
if (scopeFindings.length === 0) return;
|
|
2221
|
-
const cmd =
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
${JSON.stringify(scopeFindings, null, 2)}
|
|
2225
|
-
\`\`\``;
|
|
2226
|
-
copyText(cmd).then((ok) => {
|
|
2227
|
-
if (ok) {
|
|
2228
|
-
setCmdCopied("assign");
|
|
2229
|
-
setTimeout(() => setCmdCopied(null), 1600);
|
|
2230
|
-
}
|
|
2231
|
-
});
|
|
3207
|
+
const cmd = buildAssignFixesInstruction(scopeFindings);
|
|
3208
|
+
if (!cmd) return;
|
|
3209
|
+
copyWithFeedback(cmd, "assign");
|
|
2232
3210
|
};
|
|
2233
3211
|
const doCopyYaml = () => {
|
|
2234
3212
|
const yaml = toKnownIntentionalYaml(ignored);
|
|
@@ -2236,19 +3214,31 @@ ${JSON.stringify(scopeFindings, null, 2)}
|
|
|
2236
3214
|
copyText(yaml).then((ok) => {
|
|
2237
3215
|
if (ok) {
|
|
2238
3216
|
setCopied(true);
|
|
2239
|
-
setTimeout(() => setCopied(false), 1600);
|
|
3217
|
+
cmdTimers.current.push(window.setTimeout(() => setCopied(false), 1600));
|
|
2240
3218
|
} else {
|
|
2241
3219
|
setPayload(yaml);
|
|
3220
|
+
setCmdFailed("yaml");
|
|
2242
3221
|
}
|
|
2243
3222
|
});
|
|
2244
3223
|
};
|
|
2245
3224
|
const doBuildInstruction = () => {
|
|
2246
3225
|
const text = buildApplyInstruction(ignored);
|
|
3226
|
+
if (!text) return;
|
|
2247
3227
|
setPayload(text);
|
|
2248
|
-
|
|
3228
|
+
copyWithFeedback(text, "apply");
|
|
2249
3229
|
};
|
|
2250
|
-
const
|
|
2251
|
-
const
|
|
3230
|
+
const doReadback = () => {
|
|
3231
|
+
const text = buildTriageReadbackInstruction();
|
|
3232
|
+
setPayload(text);
|
|
3233
|
+
copyWithFeedback(text, "readback");
|
|
3234
|
+
};
|
|
3235
|
+
const rowPairs = [];
|
|
3236
|
+
for (let i = 0; i < filtered.length; i++) {
|
|
3237
|
+
const idx = indices[i];
|
|
3238
|
+
const f = filtered[i];
|
|
3239
|
+
if (typeof idx === "number" && f) rowPairs.push({ index: idx, finding: f });
|
|
3240
|
+
}
|
|
3241
|
+
const rows = rowPairs.map(({ index, finding }) => {
|
|
2252
3242
|
const severity = finding.severity || "low";
|
|
2253
3243
|
const verdict = verdicts[String(index)] || "keep";
|
|
2254
3244
|
const isSelected = selected === index;
|
|
@@ -2294,9 +3284,9 @@ ${JSON.stringify(scopeFindings, null, 2)}
|
|
|
2294
3284
|
React.createElement(
|
|
2295
3285
|
"div",
|
|
2296
3286
|
{ className: "iterate-finding-actions" },
|
|
2297
|
-
btn("y \u4FEE\u590D", "keep", "\u4FDD\u7559\u8BE5 finding\uFF0C\u8FDB\u5165\u4FEE\u590D"),
|
|
2298
|
-
btn("n \u8DF3\u8FC7", "skip", "\u8DF3\u8FC7\u8BE5 finding"),
|
|
2299
|
-
btn("a \u5DF2\u77E5\u6709\u610F", "ignore", "\u6807\u8BB0\u4E3A\u5DF2\u77E5\u6709\u610F\u5E76\u5199\u5165 known_intentional")
|
|
3287
|
+
btn(t("y \u4FEE\u590D"), "keep", t("\u4FDD\u7559\u8BE5 finding\uFF0C\u8FDB\u5165\u4FEE\u590D")),
|
|
3288
|
+
btn(t("n \u8DF3\u8FC7"), "skip", t("\u8DF3\u8FC7\u8BE5 finding")),
|
|
3289
|
+
btn(t("a \u5DF2\u77E5\u6709\u610F"), "ignore", t("\u6807\u8BB0\u4E3A\u5DF2\u77E5\u6709\u610F\u5E76\u5199\u5165 known_intentional"))
|
|
2300
3290
|
)
|
|
2301
3291
|
);
|
|
2302
3292
|
});
|
|
@@ -2306,24 +3296,24 @@ ${JSON.stringify(scopeFindings, null, 2)}
|
|
|
2306
3296
|
React.createElement(
|
|
2307
3297
|
"div",
|
|
2308
3298
|
{ className: "iterate-triage-head" },
|
|
2309
|
-
React.createElement("span", { role: "heading", "aria-level": 3 }, `Iterate \xB7 Findings \u5206\u8BCA (
|
|
2310
|
-
React.createElement("span", { className: "iterate-triage-hint" }, "y=\u4FEE\u590D \xB7 n=\u8DF3\u8FC7 \xB7 a=\u5DF2\u77E5\u6709\u610F \xB7 \u2191/\u2193 \u9009\u62E9")
|
|
3299
|
+
React.createElement("span", { role: "heading", "aria-level": 3 }, tf(`Iterate \xB7 Findings \u5206\u8BCA ({a0}/{a1})`, { a0: filtered.length, a1: findings.length })),
|
|
3300
|
+
React.createElement("span", { className: "iterate-triage-hint" }, t("y=\u4FEE\u590D \xB7 n=\u8DF3\u8FC7 \xB7 a=\u5DF2\u77E5\u6709\u610F \xB7 \u2191/\u2193 \u9009\u62E9"))
|
|
2311
3301
|
),
|
|
2312
3302
|
React.createElement(
|
|
2313
3303
|
"div",
|
|
2314
3304
|
{ className: "iterate-filter" },
|
|
2315
3305
|
React.createElement(
|
|
2316
3306
|
"select",
|
|
2317
|
-
{ className: "iterate-filter-select", value: filter.severities[0] || "", onChange: (e) => setSeverityFilter(e.target.value), "aria-label": "\u6309\u4E25\u91CD\u5EA6\u7B5B\u9009" },
|
|
2318
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u4E25\u91CD\u5EA6"),
|
|
3307
|
+
{ className: "iterate-filter-select", value: filter.severities[0] || "", onChange: (e) => setSeverityFilter(e.target.value), "aria-label": t("\u6309\u4E25\u91CD\u5EA6\u7B5B\u9009") },
|
|
3308
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u4E25\u91CD\u5EA6")),
|
|
2319
3309
|
...options.severities.map(
|
|
2320
3310
|
(s) => React.createElement("option", { key: s.value, value: s.value }, `${severityLabel(s.value)} (${s.count})`)
|
|
2321
3311
|
)
|
|
2322
3312
|
),
|
|
2323
3313
|
React.createElement(
|
|
2324
3314
|
"select",
|
|
2325
|
-
{ className: "iterate-filter-select", value: filter.dimensions[0] || "", onChange: (e) => setDimensionFilter(e.target.value), "aria-label": "\u6309\u7EF4\u5EA6\u7B5B\u9009" },
|
|
2326
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u7EF4\u5EA6"),
|
|
3315
|
+
{ className: "iterate-filter-select", value: filter.dimensions[0] || "", onChange: (e) => setDimensionFilter(e.target.value), "aria-label": t("\u6309\u7EF4\u5EA6\u7B5B\u9009") },
|
|
3316
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u7EF4\u5EA6")),
|
|
2327
3317
|
...options.dimensions.map(
|
|
2328
3318
|
(d) => React.createElement("option", { key: d.value, value: d.value }, `${d.value} (${d.count})`)
|
|
2329
3319
|
)
|
|
@@ -2331,96 +3321,113 @@ ${JSON.stringify(scopeFindings, null, 2)}
|
|
|
2331
3321
|
React.createElement("input", {
|
|
2332
3322
|
className: "iterate-filter-search",
|
|
2333
3323
|
type: "search",
|
|
2334
|
-
placeholder: "\u641C\u7D22\u6587\u4EF6 / \u6458\u8981\u2026",
|
|
2335
|
-
"aria-label": "\u641C\u7D22\u6587\u4EF6\u6216\u6458\u8981",
|
|
3324
|
+
placeholder: t("\u641C\u7D22\u6587\u4EF6 / \u6458\u8981\u2026"),
|
|
3325
|
+
"aria-label": t("\u641C\u7D22\u6587\u4EF6\u6216\u6458\u8981"),
|
|
2336
3326
|
value: filter.search,
|
|
2337
3327
|
onChange: (e) => setSearchFilter(e.target.value)
|
|
2338
3328
|
}),
|
|
2339
3329
|
React.createElement(
|
|
2340
3330
|
"span",
|
|
2341
3331
|
{ className: "iterate-filter-count" },
|
|
2342
|
-
isFilterActive ? React.createElement("button", { className: "iterate-batch-btn", onClick: clearFilter }, `\u6E05\u9664\u7B5B\u9009\uFF08\u663E\u793A
|
|
3332
|
+
isFilterActive ? React.createElement("button", { className: "iterate-batch-btn", onClick: clearFilter }, tf(`\u6E05\u9664\u7B5B\u9009\uFF08\u663E\u793A {a0}\uFF09`, { a0: filtered.length })) : tf(`\u5171 {a0} \u9879`, { a0: findings.length })
|
|
2343
3333
|
)
|
|
2344
3334
|
),
|
|
2345
3335
|
React.createElement(
|
|
2346
3336
|
"div",
|
|
2347
3337
|
{ className: "iterate-batch" },
|
|
2348
|
-
React.createElement("span", { className: "iterate-batch-label" }, "\u6279\u91CF\u4F5C\u7528\u4E8E\uFF1A"),
|
|
3338
|
+
React.createElement("span", { className: "iterate-batch-label" }, t("\u6279\u91CF\u4F5C\u7528\u4E8E\uFF1A")),
|
|
2349
3339
|
React.createElement("button", {
|
|
2350
3340
|
className: selectAll ? "iterate-batch-btn iterate-batch-scope" : "iterate-batch-btn iterate-batch-scope iterate-batch-scope-on",
|
|
2351
3341
|
onClick: () => setSelectAll(false),
|
|
2352
|
-
title: "\u6279\u91CF\u6309\u94AE\u4EC5\u4F5C\u7528\u4E8E\u5F53\u524D\u7B5B\u9009\u53EF\u89C1\u7684 findings"
|
|
2353
|
-
}, `\u53EF\u89C1
|
|
3342
|
+
title: t("\u6279\u91CF\u6309\u94AE\u4EC5\u4F5C\u7528\u4E8E\u5F53\u524D\u7B5B\u9009\u53EF\u89C1\u7684 findings")
|
|
3343
|
+
}, tf(`\u53EF\u89C1 {a0}`, { a0: indices.length })),
|
|
2354
3344
|
React.createElement("button", {
|
|
2355
3345
|
className: selectAll ? "iterate-batch-btn iterate-batch-scope iterate-batch-scope-on" : "iterate-batch-btn iterate-batch-scope",
|
|
2356
3346
|
onClick: () => setSelectAll(true),
|
|
2357
|
-
title: "\u6279\u91CF\u6309\u94AE\u4F5C\u7528\u4E8E\u5168\u90E8 findings"
|
|
2358
|
-
}, `\u5168\u90E8
|
|
3347
|
+
title: t("\u6279\u91CF\u6309\u94AE\u4F5C\u7528\u4E8E\u5168\u90E8 findings")
|
|
3348
|
+
}, tf(`\u5168\u90E8 {a0}`, { a0: allIndices.length })),
|
|
2359
3349
|
React.createElement("button", { className: "iterate-batch-btn", onClick: () => applyBatch("keep") }, "y"),
|
|
2360
3350
|
React.createElement("button", { className: "iterate-batch-btn", onClick: () => applyBatch("skip") }, "n"),
|
|
2361
3351
|
React.createElement("button", { className: "iterate-batch-btn", onClick: () => applyBatch("ignore") }, "a"),
|
|
2362
|
-
React.createElement("button", { className: "iterate-batch-btn", onClick: doResetVerdicts, title: "\u628A\u6240\u6709\u5224\u5B9A\u6062\u590D\u4E3A\u9ED8\u8BA4 y\uFF08\u4FEE\u590D\uFF09" }, "\u91CD\u7F6E")
|
|
3352
|
+
React.createElement("button", { className: "iterate-batch-btn", onClick: doResetVerdicts, title: t("\u628A\u6240\u6709\u5224\u5B9A\u6062\u590D\u4E3A\u9ED8\u8BA4 y\uFF08\u4FEE\u590D\uFF09") }, t("\u91CD\u7F6E"))
|
|
2363
3353
|
),
|
|
2364
3354
|
...rows,
|
|
2365
3355
|
React.createElement(
|
|
2366
3356
|
"div",
|
|
2367
3357
|
{ className: "iterate-triage-foot" },
|
|
2368
|
-
React.createElement("span", {}, `y
|
|
3358
|
+
React.createElement("span", {}, tf(`y {a0} \xB7 n {a1} \xB7 a {a2} \xB7 \u5F85\u5199\u56DE known_intentional\uFF1A{a3} \u6761`, { a0: counts.keep, a1: counts.skip, a2: counts.ignore, a3: ignoredCount })),
|
|
2369
3359
|
React.createElement(
|
|
2370
3360
|
"span",
|
|
2371
|
-
{ style: { display: "flex", gap: 6 } },
|
|
3361
|
+
{ style: { display: "flex", gap: 6, flexWrap: "wrap" } },
|
|
3362
|
+
// Gap #7: the apply instruction (safe path) is PRIMARY; the raw YAML
|
|
3363
|
+
// is demoted to an explicit "just the snippet" escape hatch.
|
|
2372
3364
|
React.createElement("button", {
|
|
2373
3365
|
className: "iterate-btn",
|
|
2374
3366
|
"data-primary": "",
|
|
3367
|
+
"data-copied": cmdCopied === "apply" ? "" : void 0,
|
|
3368
|
+
"data-failed": cmdFailed === "apply" ? "" : void 0,
|
|
3369
|
+
onClick: doBuildInstruction,
|
|
3370
|
+
disabled: ignoredCount === 0,
|
|
3371
|
+
title: ignoredCount === 0 ? t("\u5F53\u524D\u6CA1\u6709\u6807\u8BB0\u4E3A\u300C\u5DF2\u77E5\u6709\u610F\u300D\u7684 finding") : t("\u590D\u5236\u5E94\u7528\u6307\u4EE4\uFF1A\u7C98\u8D34\u540E\u6A21\u578B\u4F1A\u8C03\u7528 iterate_triage apply\uFF08\u6821\u9A8C\u3001\u53BB\u91CD\u3001\u5199\u5165\u524D\u5907\u4EFD\u3001\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\uFF09")
|
|
3372
|
+
}, cmdCopied === "apply" ? t("\u5DF2\u590D\u5236") : cmdFailed === "apply" ? t("\u590D\u5236\u5931\u8D25") : tf(`\u751F\u6210\u5E94\u7528\u6307\u4EE4{a0}`, { a0: ignoredCount > 0 ? `\uFF08${ignoredCount}\uFF09` : "" })),
|
|
3373
|
+
React.createElement("button", {
|
|
3374
|
+
className: "iterate-btn",
|
|
2375
3375
|
"data-copied": copied ? "" : void 0,
|
|
3376
|
+
"data-failed": cmdFailed === "yaml" ? "" : void 0,
|
|
2376
3377
|
onClick: doCopyYaml,
|
|
2377
3378
|
disabled: ignoredCount === 0,
|
|
2378
|
-
title: ignoredCount === 0 ? "\u5F53\u524D\u6CA1\u6709\u6807\u8BB0\u4E3A\u300C\u5DF2\u77E5\u6709\u610F\u300D\u7684 finding" : "\u590D\u5236 known_intentional YAML"
|
|
2379
|
-
}, copied ? "\u5DF2\u590D\u5236" :
|
|
3379
|
+
title: ignoredCount === 0 ? t("\u5F53\u524D\u6CA1\u6709\u6807\u8BB0\u4E3A\u300C\u5DF2\u77E5\u6709\u610F\u300D\u7684 finding") : t("\u53EA\u590D\u5236 known_intentional YAML \u7247\u6BB5\uFF08\u9700\u81EA\u884C\u7C98\u5230 iterate.config.yaml \u7684 personalization.known_intentional \u4E0B\uFF09")
|
|
3380
|
+
}, copied ? t("\u5DF2\u590D\u5236") : cmdFailed === "yaml" ? t("\u590D\u5236\u5931\u8D25") : t("\u4EC5\u590D\u5236 YAML \u7247\u6BB5")),
|
|
2380
3381
|
React.createElement("button", {
|
|
2381
3382
|
className: "iterate-btn",
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
3383
|
+
"data-copied": cmdCopied === "readback" ? "" : void 0,
|
|
3384
|
+
"data-failed": cmdFailed === "readback" ? "" : void 0,
|
|
3385
|
+
onClick: doReadback,
|
|
3386
|
+
title: t("\u590D\u5236 iterate_triage list \u6307\u4EE4\uFF0C\u6838\u5BF9\u5DF2\u7ECF\u5199\u5165\u7684 known_intentional \u6761\u76EE\uFF08\u5199\u56DE\u95ED\u73AF\u786E\u8BA4\uFF09")
|
|
3387
|
+
}, cmdCopied === "readback" ? t("\u5DF2\u590D\u5236") : cmdFailed === "readback" ? t("\u590D\u5236\u5931\u8D25") : t("\u56DE\u8BFB\u5DF2\u5199\u5165\u6761\u76EE"))
|
|
3388
|
+
),
|
|
3389
|
+
cmdCopied === "apply" ? React.createElement("span", { className: "iterate-triage-hint", role: "status" }, t("\u5DF2\u590D\u5236\u5E94\u7528\u6307\u4EE4\u2014\u2014\u7C98\u8D34\u56DE\u4F1A\u8BDD\uFF0C\u6A21\u578B\u5C06\u8C03\u7528 iterate_triage apply \u5E76\u56DE\u62A5\u5199\u5165\u6761\u6570\u3002")) : cmdCopied === "readback" ? React.createElement("span", { className: "iterate-triage-hint", role: "status" }, t("\u5DF2\u590D\u5236\u56DE\u8BFB\u6307\u4EE4\u2014\u2014\u7C98\u8D34\u56DE\u4F1A\u8BDD\u53EF\u5217\u51FA\u5F53\u524D\u5DF2\u5199\u5165\u7684 known_intentional\u3002")) : null
|
|
2387
3390
|
),
|
|
2388
3391
|
// v3.0: Command buttons for native actions (§8)
|
|
2389
3392
|
React.createElement(
|
|
2390
3393
|
"div",
|
|
2391
3394
|
{ className: "iterate-triage-foot", style: { borderTop: "1px solid var(--dsw-alias-border-l1)" } },
|
|
2392
|
-
React.createElement("span", { style: { fontSize: 11, fontWeight: 600, color: "var(--dsw-alias-label-secondary)" } }, "\u6307\u6325\u64CD\u4F5C"),
|
|
3395
|
+
React.createElement("span", { style: { fontSize: 11, fontWeight: 600, color: "var(--dsw-alias-label-secondary)" } }, t("\u6307\u6325\u64CD\u4F5C")),
|
|
2393
3396
|
React.createElement(
|
|
2394
3397
|
"span",
|
|
2395
|
-
{ style: { display: "flex", gap: 6 } },
|
|
3398
|
+
{ style: { display: "flex", gap: 6, flexWrap: "wrap" } },
|
|
2396
3399
|
React.createElement("button", {
|
|
2397
3400
|
className: "iterate-cmd",
|
|
2398
3401
|
"data-primary": "",
|
|
2399
3402
|
"data-copied": cmdCopied === "approve-arch" ? "" : void 0,
|
|
3403
|
+
"data-failed": cmdFailed === "approve-arch" ? "" : void 0,
|
|
2400
3404
|
onClick: doApproveArchitecturalFix,
|
|
2401
|
-
title: "\u6279\u51C6\u67B6\u6784\u4FEE\u590D\uFF08\u590D\u5236\u6307\u4EE4\u6587\u672C\uFF09"
|
|
2402
|
-
}, cmdCopied === "approve-arch" ? "\u5DF2\u590D\u5236" : "\u6279\u51C6\u67B6\u6784\u4FEE\u590D"),
|
|
3405
|
+
title: t("\u6279\u51C6\u67B6\u6784\u4FEE\u590D\uFF08\u590D\u5236\u6307\u4EE4\u6587\u672C\uFF1Aiterate_fix force:true\uFF0C\u542B\u5168\u90E8\u5FC5\u586B\u53C2\u6570\uFF09")
|
|
3406
|
+
}, cmdCopied === "approve-arch" ? t("\u5DF2\u590D\u5236") : cmdFailed === "approve-arch" ? t("\u590D\u5236\u5931\u8D25") : t("\u6279\u51C6\u67B6\u6784\u4FEE\u590D")),
|
|
2403
3407
|
React.createElement("button", {
|
|
2404
3408
|
className: "iterate-cmd",
|
|
2405
3409
|
"data-copied": cmdCopied === "new-round" ? "" : void 0,
|
|
3410
|
+
"data-failed": cmdFailed === "new-round" ? "" : void 0,
|
|
2406
3411
|
onClick: doTriggerNewRound,
|
|
2407
|
-
title: "\u89E6\u53D1\u65B0\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5"
|
|
2408
|
-
}, cmdCopied === "new-round" ? "\u5DF2\u590D\u5236" : "\u89E6\u53D1\u65B0\u4E00\u8F6E"),
|
|
3412
|
+
title: t("\u89E6\u53D1\u65B0\u4E00\u8F6E\u8FED\u4EE3\u5BA1\u67E5")
|
|
3413
|
+
}, cmdCopied === "new-round" ? t("\u5DF2\u590D\u5236") : cmdFailed === "new-round" ? t("\u590D\u5236\u5931\u8D25") : t("\u89E6\u53D1\u65B0\u4E00\u8F6E")),
|
|
2409
3414
|
React.createElement("button", {
|
|
2410
3415
|
className: "iterate-cmd",
|
|
2411
3416
|
"data-primary": "",
|
|
2412
3417
|
"data-copied": cmdCopied === "assign" ? "" : void 0,
|
|
3418
|
+
"data-failed": cmdFailed === "assign" ? "" : void 0,
|
|
2413
3419
|
onClick: doAssignFindings,
|
|
2414
3420
|
disabled: (selectAll ? allIndices : indices).length === 0,
|
|
2415
|
-
title: selectAll ? `\u6307\u6D3E\u5168\u90E8
|
|
2416
|
-
}, cmdCopied === "assign" ? "\u5DF2\u590D\u5236" : "\u6307\u6D3E\u4FEE\u590D"),
|
|
3421
|
+
title: selectAll ? tf(`\u6307\u6D3E\u5168\u90E8 {a0} \u4E2A findings \u4FEE\u590D\uFF08\u9010\u4E2A iterate_fix\uFF09`, { a0: allIndices.length }) : tf(`\u6307\u6D3E\u5F53\u524D\u53EF\u89C1 {a0} \u4E2A findings \u4FEE\u590D\uFF08\u9010\u4E2A iterate_fix\uFF09`, { a0: indices.length })
|
|
3422
|
+
}, cmdCopied === "assign" ? t("\u5DF2\u590D\u5236") : cmdFailed === "assign" ? t("\u590D\u5236\u5931\u8D25") : t("\u6307\u6D3E\u4FEE\u590D")),
|
|
2417
3423
|
React.createElement("button", {
|
|
2418
3424
|
className: "iterate-cmd",
|
|
2419
3425
|
"data-danger": "",
|
|
2420
3426
|
"data-copied": cmdCopied === "rollback" ? "" : void 0,
|
|
3427
|
+
"data-failed": cmdFailed === "rollback" ? "" : void 0,
|
|
2421
3428
|
onClick: doRollbackToCheckpoint,
|
|
2422
|
-
title: "\u56DE\u6EDA\u5230\u4E0A\u4E00\u4E2A\u68C0\u67E5\u70B9"
|
|
2423
|
-
}, cmdCopied === "rollback" ? "\u5DF2\u590D\u5236" : "\u56DE\u6EDA\u68C0\u67E5\u70B9")
|
|
3429
|
+
title: t("\u56DE\u6EDA\u5230\u4E0A\u4E00\u4E2A\u68C0\u67E5\u70B9")
|
|
3430
|
+
}, cmdCopied === "rollback" ? t("\u5DF2\u590D\u5236") : cmdFailed === "rollback" ? t("\u590D\u5236\u5931\u8D25") : t("\u56DE\u6EDA\u68C0\u67E5\u70B9"))
|
|
2424
3431
|
)
|
|
2425
3432
|
),
|
|
2426
3433
|
payload ? React.createElement("div", { className: "iterate-payload" }, payload) : null
|
|
@@ -2443,16 +3450,16 @@ function VerdictBanner(props) {
|
|
|
2443
3450
|
React.createElement(
|
|
2444
3451
|
"span",
|
|
2445
3452
|
{ className: "iterate-verdict-tag", "data-ok": ok ? "" : void 0, "data-warn": ok ? void 0 : "" },
|
|
2446
|
-
ok ? "\u62A5\u544A\u5DF2\u6279\u51C6" : "\u62A5\u544A\u9700\u4FEE\u8BA2"
|
|
3453
|
+
ok ? t("\u62A5\u544A\u5DF2\u6279\u51C6") : t("\u62A5\u544A\u9700\u4FEE\u8BA2")
|
|
2447
3454
|
),
|
|
2448
3455
|
React.createElement(
|
|
2449
3456
|
"span",
|
|
2450
3457
|
{ className: "iterate-verdict-detail" },
|
|
2451
|
-
item(verdict.totalFindings, "\u9879\u53D1\u73B0"),
|
|
2452
|
-
item(verdict.totalRounds, "\u8F6E"),
|
|
2453
|
-
item(verdict.checksRun, "\u9879\u5BA1\u67E5"),
|
|
2454
|
-
ok ? phrase("\u62A5\u544A\u901A\u8FC7\u5168\u90E8\u4E00\u81F4\u6027\u68C0\u67E5") : item(verdict.reportIssues, "\u5904\u62A5\u544A\u7F3A\u9677"),
|
|
2455
|
-
phrase(verdict.converged ? "\u5DF2\u6536\u655B" : "\u672A\u6536\u655B")
|
|
3458
|
+
item(verdict.totalFindings, t("\u9879\u53D1\u73B0")),
|
|
3459
|
+
item(verdict.totalRounds, t("\u8F6E")),
|
|
3460
|
+
item(verdict.checksRun, t("\u9879\u5BA1\u67E5")),
|
|
3461
|
+
ok ? phrase(t("\u62A5\u544A\u901A\u8FC7\u5168\u90E8\u4E00\u81F4\u6027\u68C0\u67E5")) : item(verdict.reportIssues, t("\u5904\u62A5\u544A\u7F3A\u9677")),
|
|
3462
|
+
phrase(verdict.converged ? t("\u5DF2\u6536\u655B") : t("\u672A\u6536\u655B"))
|
|
2456
3463
|
)
|
|
2457
3464
|
);
|
|
2458
3465
|
}
|
|
@@ -2494,7 +3501,7 @@ function ProgressCapsule() {
|
|
|
2494
3501
|
const listener = (payload) => {
|
|
2495
3502
|
const converged = payload && payload.converged === true;
|
|
2496
3503
|
const round = payload && typeof payload.round === "number" ? payload.round : "?";
|
|
2497
|
-
setInfo({ text: converged ? `Round
|
|
3504
|
+
setInfo({ text: converged ? tf(`Round {a0} \u5B8C\u6210 \xB7 \u5DF2\u6536\u655B`, { a0: round }) : tf(`Round {a0} \u5B8C\u6210`, { a0: round }), ok: converged });
|
|
2498
3505
|
if (timer) clearTimeout(timer);
|
|
2499
3506
|
timer = setTimeout(() => setInfo(null), converged ? 3600 : 2400);
|
|
2500
3507
|
};
|
|
@@ -2514,7 +3521,10 @@ function ProgressCapsule() {
|
|
|
2514
3521
|
"aria-live": "polite"
|
|
2515
3522
|
}, info.text);
|
|
2516
3523
|
}
|
|
2517
|
-
function SettingsPanel(
|
|
3524
|
+
function SettingsPanel(props) {
|
|
3525
|
+
const lang = useLang();
|
|
3526
|
+
const cachedSession = useSessionSnapshot();
|
|
3527
|
+
const session = props && props.session ? props.session : cachedSession;
|
|
2518
3528
|
const [enabled, setEnabled] = React.useState(themeEnabled);
|
|
2519
3529
|
const [guideCopied, setGuideCopied] = React.useState(false);
|
|
2520
3530
|
const [statusCopied, setStatusCopied] = React.useState(false);
|
|
@@ -2522,41 +3532,82 @@ function SettingsPanel(_props) {
|
|
|
2522
3532
|
const [showStatus, setShowStatus] = React.useState(false);
|
|
2523
3533
|
const [confirming, setConfirming] = React.useState(false);
|
|
2524
3534
|
const [clearedInfo, setClearedInfo] = React.useState(null);
|
|
3535
|
+
const [cfgField, setCfgField] = React.useState(
|
|
3536
|
+
() => CONFIG_EDIT_FIELDS.some((f) => f.key === "validation.commands") ? "validation.commands" : CONFIG_EDIT_FIELDS[0]?.key ?? "goal"
|
|
3537
|
+
);
|
|
3538
|
+
const [cfgCopied, setCfgCopied] = React.useState(false);
|
|
3539
|
+
const [cfgFailed, setCfgFailed] = React.useState(false);
|
|
3540
|
+
const [cfgText, setCfgText] = React.useState(null);
|
|
3541
|
+
const [cfgReadCopied, setCfgReadCopied] = React.useState(false);
|
|
3542
|
+
const [cfgReadText, setCfgReadText] = React.useState(null);
|
|
3543
|
+
const [pullCopied, setPullCopied] = React.useState(false);
|
|
3544
|
+
const [copyError, setCopyError] = React.useState(null);
|
|
2525
3545
|
const guide = buildConfigEditGuide();
|
|
2526
3546
|
const statusGuide = buildRuntimeStatusGuide();
|
|
3547
|
+
const cfgMeta = configFieldByKey(cfgField);
|
|
3548
|
+
const cfgRead = scanSessionForConfigRead(session);
|
|
3549
|
+
const cfgCurrent = cfgRead ? configValueByPath(cfgRead, cfgField) : null;
|
|
3550
|
+
const cfgFieldLabel = cfgMeta ? cfgMeta.label[getLang()] : cfgField;
|
|
3551
|
+
const cfgTimers = React.useRef([]);
|
|
3552
|
+
React.useEffect(() => () => {
|
|
3553
|
+
for (const t2 of cfgTimers.current) clearTimeout(t2);
|
|
3554
|
+
cfgTimers.current = [];
|
|
3555
|
+
}, []);
|
|
2527
3556
|
const toggleTheme = () => {
|
|
2528
3557
|
const next = !enabled;
|
|
2529
3558
|
setEnabled(next);
|
|
2530
3559
|
setThemeEnabled(next);
|
|
2531
3560
|
};
|
|
2532
3561
|
const flashCopied = (slot) => {
|
|
2533
|
-
const setter = slot === "guide" ? setGuideCopied : setStatusCopied;
|
|
3562
|
+
const setter = slot === "guide" ? setGuideCopied : slot === "status" ? setStatusCopied : slot === "pull" ? setPullCopied : setCfgReadCopied;
|
|
2534
3563
|
setter(true);
|
|
2535
|
-
setTimeout(() => setter(false), 1600);
|
|
3564
|
+
cfgTimers.current.push(window.setTimeout(() => setter(false), 1600));
|
|
2536
3565
|
};
|
|
2537
3566
|
const doCopy = (text, slot) => {
|
|
2538
3567
|
copyText(text).then((ok) => {
|
|
3568
|
+
setCopyError(ok ? null : slot);
|
|
2539
3569
|
if (ok) flashCopied(slot);
|
|
2540
3570
|
});
|
|
2541
3571
|
};
|
|
3572
|
+
const doBuildConfigInstruction = () => {
|
|
3573
|
+
const text = buildConfigFieldInstruction(cfgField);
|
|
3574
|
+
setCfgText(text);
|
|
3575
|
+
copyText(text).then((ok) => {
|
|
3576
|
+
setCfgFailed(!ok);
|
|
3577
|
+
setCfgCopied(ok);
|
|
3578
|
+
cfgTimers.current.push(window.setTimeout(() => setCfgCopied(false), 1600));
|
|
3579
|
+
if (!ok) return;
|
|
3580
|
+
});
|
|
3581
|
+
};
|
|
3582
|
+
const doCopyConfigRead = () => {
|
|
3583
|
+
const text = buildConfigReadInstruction();
|
|
3584
|
+
copyText(text).then((ok) => {
|
|
3585
|
+
setCopyError(ok ? null : "cfgRead");
|
|
3586
|
+
if (ok) {
|
|
3587
|
+
flashCopied("cfgRead");
|
|
3588
|
+
return;
|
|
3589
|
+
}
|
|
3590
|
+
setCfgReadText(text);
|
|
3591
|
+
});
|
|
3592
|
+
};
|
|
2542
3593
|
const requestClear = () => {
|
|
2543
3594
|
if (confirming) {
|
|
2544
3595
|
const count = removeStorageByPrefix(TRIAGE_STORAGE_PREFIX);
|
|
2545
3596
|
setClearedInfo(count);
|
|
2546
3597
|
setConfirming(false);
|
|
2547
|
-
setTimeout(() => setClearedInfo(null), 3e3);
|
|
3598
|
+
cfgTimers.current.push(window.setTimeout(() => setClearedInfo(null), 3e3));
|
|
2548
3599
|
return;
|
|
2549
3600
|
}
|
|
2550
3601
|
setConfirming(true);
|
|
2551
|
-
setTimeout(() => setConfirming(false), 3e3);
|
|
3602
|
+
cfgTimers.current.push(window.setTimeout(() => setConfirming(false), 3e3));
|
|
2552
3603
|
};
|
|
2553
|
-
const clearButton = clearedInfo !== null ? React.createElement("button", { className: "iterate-btn", "data-copied": "", disabled: true }, `\u5DF2\u6E05\u9664
|
|
3604
|
+
const clearButton = clearedInfo !== null ? React.createElement("button", { className: "iterate-btn", "data-copied": "", disabled: true }, tf(`\u5DF2\u6E05\u9664 {a0} \u6761`, { a0: clearedInfo })) : React.createElement("button", {
|
|
2554
3605
|
className: "iterate-btn",
|
|
2555
3606
|
"data-danger": confirming ? void 0 : "",
|
|
2556
3607
|
"data-confirm": confirming ? "" : void 0,
|
|
2557
3608
|
onClick: requestClear,
|
|
2558
|
-
title: confirming ? "\u518D\u6B21\u70B9\u51FB\u4EE5\u786E\u8BA4\u6E05\u9664\u5168\u90E8\u5224\u5B9A" : "\u6E05\u9664\u6240\u6709\u5206\u8BCA\u5224\u5B9A\u8BB0\u5F55\uFF08\u9700\u8981\u4E8C\u6B21\u786E\u8BA4\uFF09"
|
|
2559
|
-
}, confirming ? "\u786E\u8BA4\u6E05\u9664\uFF1F" : "\u6E05\u9664\u5206\u8BCA");
|
|
3609
|
+
title: confirming ? t("\u518D\u6B21\u70B9\u51FB\u4EE5\u786E\u8BA4\u6E05\u9664\u5168\u90E8\u5224\u5B9A") : t("\u6E05\u9664\u6240\u6709\u5206\u8BCA\u5224\u5B9A\u8BB0\u5F55\uFF08\u9700\u8981\u4E8C\u6B21\u786E\u8BA4\uFF09")
|
|
3610
|
+
}, confirming ? t("\u786E\u8BA4\u6E05\u9664\uFF1F") : t("\u6E05\u9664\u5206\u8BCA"));
|
|
2560
3611
|
const themeCard = React.createElement(
|
|
2561
3612
|
"div",
|
|
2562
3613
|
{ key: "theme", className: "iterate-scard" },
|
|
@@ -2566,17 +3617,17 @@ function SettingsPanel(_props) {
|
|
|
2566
3617
|
React.createElement(
|
|
2567
3618
|
"div",
|
|
2568
3619
|
{},
|
|
2569
|
-
React.createElement("div", { className: "iterate-settings-title" }, "\u8FED\u4EE3\u4E3B\u9898"),
|
|
2570
|
-
React.createElement("div", { className: "iterate-settings-desc" }, "\u542F\u7528\u6696\u7425\u73C0\u914D\u8272\u7684 iterate \u4E13\u5C5E\u76AE\u80A4\uFF0C\u8986\u76D6 dsh \u9ED8\u8BA4\u4E3B\u9898\u4EE4\u724C\u3002")
|
|
3620
|
+
React.createElement("div", { className: "iterate-settings-title" }, t("\u8FED\u4EE3\u4E3B\u9898")),
|
|
3621
|
+
React.createElement("div", { className: "iterate-settings-desc" }, t("\u542F\u7528\u6696\u7425\u73C0\u914D\u8272\u7684 iterate \u4E13\u5C5E\u76AE\u80A4\uFF0C\u8986\u76D6 dsh \u9ED8\u8BA4\u4E3B\u9898\u4EE4\u724C\u3002"))
|
|
2571
3622
|
),
|
|
2572
3623
|
React.createElement(
|
|
2573
3624
|
"div",
|
|
2574
3625
|
{ className: "iterate-scard-actions" },
|
|
2575
|
-
React.createElement("span", { className: "iterate-chip", "data-ok": enabled ? "" : void 0 }, enabled ? "\u5DF2\u542F\u7528" : "\u5DF2\u5173\u95ED"),
|
|
3626
|
+
React.createElement("span", { className: "iterate-chip", "data-ok": enabled ? "" : void 0 }, enabled ? t("\u5DF2\u542F\u7528") : t("\u5DF2\u5173\u95ED")),
|
|
2576
3627
|
React.createElement("button", {
|
|
2577
3628
|
role: "switch",
|
|
2578
3629
|
"aria-checked": enabled,
|
|
2579
|
-
"aria-label": "\u5F00\u5173 iterate \u4E3B\u9898",
|
|
3630
|
+
"aria-label": t("\u5F00\u5173 iterate \u4E3B\u9898"),
|
|
2580
3631
|
className: "iterate-switch",
|
|
2581
3632
|
"data-on": enabled ? "" : void 0,
|
|
2582
3633
|
onClick: toggleTheme
|
|
@@ -2584,6 +3635,37 @@ function SettingsPanel(_props) {
|
|
|
2584
3635
|
)
|
|
2585
3636
|
)
|
|
2586
3637
|
);
|
|
3638
|
+
const langCard = React.createElement(
|
|
3639
|
+
"div",
|
|
3640
|
+
{ key: "lang", className: "iterate-scard" },
|
|
3641
|
+
React.createElement(
|
|
3642
|
+
"div",
|
|
3643
|
+
{ className: "iterate-scard-head" },
|
|
3644
|
+
React.createElement(
|
|
3645
|
+
"div",
|
|
3646
|
+
{},
|
|
3647
|
+
React.createElement("div", { className: "iterate-settings-title" }, t("\u754C\u9762\u8BED\u8A00")),
|
|
3648
|
+
React.createElement(
|
|
3649
|
+
"div",
|
|
3650
|
+
{ className: "iterate-settings-desc" },
|
|
3651
|
+
t("\u9762\u677F\u6587\u6848\u7684\u663E\u793A\u8BED\u8A00\uFF1B\u590D\u5236\u7ED9\u6A21\u578B\u7684\u6307\u4EE4\u4FDD\u6301\u4E2D\u6587\uFF0C\u4E0E SKILL.md \u7684\u5DE5\u4F5C\u6D41\u5951\u7EA6\u4E00\u81F4\u3002")
|
|
3652
|
+
)
|
|
3653
|
+
),
|
|
3654
|
+
React.createElement(
|
|
3655
|
+
"div",
|
|
3656
|
+
{ className: "iterate-scard-actions" },
|
|
3657
|
+
React.createElement("span", { className: "iterate-chip", "data-ok": "" }, LANG_NAMES[lang]),
|
|
3658
|
+
React.createElement("button", {
|
|
3659
|
+
role: "switch",
|
|
3660
|
+
"aria-checked": lang === "en",
|
|
3661
|
+
"aria-label": t("\u5207\u6362\u754C\u9762\u8BED\u8A00"),
|
|
3662
|
+
className: "iterate-switch",
|
|
3663
|
+
"data-on": lang === "en" ? "" : void 0,
|
|
3664
|
+
onClick: () => setLang(lang === "en" ? "zh" : "en")
|
|
3665
|
+
}, React.createElement("span", { className: "iterate-switch-knob" }))
|
|
3666
|
+
)
|
|
3667
|
+
)
|
|
3668
|
+
);
|
|
2587
3669
|
const dataCard = React.createElement(
|
|
2588
3670
|
"div",
|
|
2589
3671
|
{ key: "data", className: "iterate-scard" },
|
|
@@ -2593,13 +3675,13 @@ function SettingsPanel(_props) {
|
|
|
2593
3675
|
React.createElement(
|
|
2594
3676
|
"div",
|
|
2595
3677
|
{},
|
|
2596
|
-
React.createElement("div", { className: "iterate-settings-title" }, "\u5206\u8BCA\u6301\u4E45\u5316"),
|
|
2597
|
-
React.createElement("div", { className: "iterate-settings-desc" }, "y / n / a \u5224\u5B9A\u4FDD\u5B58\u5728\u672C\u5730\u6D4F\u89C8\u5668\uFF08localStorage\uFF09\uFF0C\u5237\u65B0\u4F1A\u8BDD\u540E\u4ECD\u4FDD\u7559\u3002")
|
|
3678
|
+
React.createElement("div", { className: "iterate-settings-title" }, t("\u5206\u8BCA\u6301\u4E45\u5316")),
|
|
3679
|
+
React.createElement("div", { className: "iterate-settings-desc" }, t("y / n / a \u5224\u5B9A\u4FDD\u5B58\u5728\u672C\u5730\u6D4F\u89C8\u5668\uFF08localStorage\uFF09\uFF0C\u5237\u65B0\u4F1A\u8BDD\u540E\u4ECD\u4FDD\u7559\u3002"))
|
|
2598
3680
|
),
|
|
2599
3681
|
React.createElement(
|
|
2600
3682
|
"div",
|
|
2601
3683
|
{ className: "iterate-scard-actions" },
|
|
2602
|
-
React.createElement("span", { className: "iterate-chip" }, "\u672C\u5730\u4FDD\u5B58"),
|
|
3684
|
+
React.createElement("span", { className: "iterate-chip" }, t("\u672C\u5730\u4FDD\u5B58")),
|
|
2603
3685
|
clearButton
|
|
2604
3686
|
)
|
|
2605
3687
|
)
|
|
@@ -2613,24 +3695,99 @@ function SettingsPanel(_props) {
|
|
|
2613
3695
|
React.createElement(
|
|
2614
3696
|
"div",
|
|
2615
3697
|
{},
|
|
2616
|
-
React.createElement("div", { className: "iterate-settings-title" }, "\u914D\u7F6E\u7BA1\u7406"),
|
|
2617
|
-
React.createElement("div", { className: "iterate-settings-desc" }, "\u76EE\u6807 / \u7EF4\u5EA6 / \u6700\u5927\u8F6E\u6570\u5199\u5728\u9879\u76EE\u7684 iterate.config.yaml\uFF0C\u590D\u5236\u6307\u5F15\u53EF\u8BA9\u6A21\u578B\u6309\u9700\u8C03\u6574\u3002")
|
|
3698
|
+
React.createElement("div", { className: "iterate-settings-title" }, t("\u914D\u7F6E\u7BA1\u7406")),
|
|
3699
|
+
React.createElement("div", { className: "iterate-settings-desc" }, t("\u76EE\u6807 / \u7EF4\u5EA6 / \u6700\u5927\u8F6E\u6570 / validation.commands \u5199\u5728\u9879\u76EE\u7684 iterate.config.yaml\uFF0C\u590D\u5236\u6307\u5F15\u53EF\u8BA9\u6A21\u578B\u6309\u9700\u8C03\u6574\u3002"))
|
|
2618
3700
|
),
|
|
2619
3701
|
React.createElement(
|
|
2620
3702
|
"div",
|
|
2621
3703
|
{ className: "iterate-scard-actions" },
|
|
2622
|
-
React.createElement("button", { className: "iterate-btn", "data-primary": "", "data-copied": guideCopied ? "" : void 0, onClick: () => doCopy(guide, "guide") }, guideCopied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u6307\u5F15"),
|
|
2623
|
-
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => setShowGuide((v) => !v) }, showGuide ? "\u6536\u8D77" : "\u5C55\u5F00")
|
|
3704
|
+
React.createElement("button", { className: "iterate-btn", "data-primary": "", "data-copied": guideCopied ? "" : void 0, onClick: () => doCopy(guide, "guide") }, guideCopied ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u6307\u5F15")),
|
|
3705
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => setShowGuide((v) => !v) }, showGuide ? t("\u6536\u8D77") : t("\u5C55\u5F00"))
|
|
2624
3706
|
)
|
|
2625
3707
|
),
|
|
3708
|
+
// Gap #8: wire up the previously-dead `buildConfigFieldInstruction` — pick
|
|
3709
|
+
// a field, generate the `iterate_config` write payload for it, paste it to
|
|
3710
|
+
// the model. `validation.commands` is the default selection.
|
|
3711
|
+
React.createElement(
|
|
3712
|
+
"div",
|
|
3713
|
+
{ className: "iterate-guide-bar", style: { marginTop: 8, flexWrap: "wrap" } },
|
|
3714
|
+
React.createElement(
|
|
3715
|
+
"select",
|
|
3716
|
+
{
|
|
3717
|
+
className: "iterate-filter-select",
|
|
3718
|
+
value: cfgField,
|
|
3719
|
+
"aria-label": t("\u9009\u62E9\u8981\u4FEE\u6539\u7684\u914D\u7F6E\u5B57\u6BB5"),
|
|
3720
|
+
onChange: (e) => setCfgField(e.target.value)
|
|
3721
|
+
},
|
|
3722
|
+
...CONFIG_EDIT_FIELDS.map(
|
|
3723
|
+
(f) => React.createElement("option", { key: f.key, value: f.key }, getLang() === "en" ? `${f.label.en} (${f.key})` : `${f.label.zh}\uFF08${f.key}\uFF09`)
|
|
3724
|
+
)
|
|
3725
|
+
),
|
|
3726
|
+
React.createElement(
|
|
3727
|
+
"span",
|
|
3728
|
+
{ className: "iterate-settings-desc" },
|
|
3729
|
+
cfgMeta?.hint[getLang()] ?? ""
|
|
3730
|
+
),
|
|
3731
|
+
React.createElement("button", {
|
|
3732
|
+
className: "iterate-btn",
|
|
3733
|
+
"data-primary": "",
|
|
3734
|
+
"data-copied": cfgCopied ? "" : void 0,
|
|
3735
|
+
"data-failed": cfgFailed ? "" : void 0,
|
|
3736
|
+
onClick: doBuildConfigInstruction,
|
|
3737
|
+
title: t("\u751F\u6210\u5E76\u590D\u5236\u8BE5\u5B57\u6BB5\u7684 iterate_config \u5199\u5165\u6307\u4EE4\uFF08\u5199\u5165\u524D\u81EA\u52A8\u5907\u4EFD\u3001\u5931\u8D25\u81EA\u52A8\u56DE\u6EDA\uFF09")
|
|
3738
|
+
}, cfgCopied ? t("\u5DF2\u590D\u5236") : cfgFailed ? t("\u590D\u5236\u5931\u8D25") : t("\u751F\u6210\u5199\u5165\u6307\u4EE4")),
|
|
3739
|
+
cfgFailed ? React.createElement("span", { className: "iterate-settings-desc" }, t("\u526A\u8D34\u677F\u88AB\u62D2\u7EDD\u2014\u2014\u6307\u4EE4\u5DF2\u5C55\u793A\u5728\u4E0B\u65B9\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u3002")) : null
|
|
3740
|
+
),
|
|
3741
|
+
// Gap #1 (3.7.0): close the write -> read-back loop. Three honest states:
|
|
3742
|
+
// nothing echoed yet, the value the model read back, or a read-back that
|
|
3743
|
+
// never contained this field (defaults are in play — say so, don't blank).
|
|
3744
|
+
React.createElement(
|
|
3745
|
+
"div",
|
|
3746
|
+
{ className: "iterate-guide-bar", style: { marginTop: 8, flexWrap: "wrap" } },
|
|
3747
|
+
React.createElement(
|
|
3748
|
+
"span",
|
|
3749
|
+
{ className: "iterate-settings-desc" },
|
|
3750
|
+
!cfgRead ? t("\u5C1A\u672A\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E\u2014\u2014\u70B9\u300C\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E\u300D\u590D\u5236\u6307\u4EE4\u53D1\u7ED9\u6A21\u578B\uFF0C\u56DE\u663E\u540E\u5373\u53EF\u5728\u6B64\u770B\u5230\u5F53\u524D\u503C\u3002") : cfgCurrent && cfgCurrent.resolved ? tf("\u5F53\u524D\u503C {a0}\uFF1A{a1}", { a0: cfgFieldLabel, a1: formatConfigValue(cfgCurrent.value) }) : tf("\u56DE\u8BFB\u7ED3\u679C\u4E2D\u6CA1\u6709 {a0}\uFF08\u8BE5\u5B57\u6BB5\u53EF\u80FD\u5C1A\u672A\u5199\u5165\u914D\u7F6E\u6587\u4EF6\uFF09\u3002", { a0: cfgFieldLabel })
|
|
3751
|
+
),
|
|
3752
|
+
React.createElement("button", {
|
|
3753
|
+
className: "iterate-btn",
|
|
3754
|
+
"data-primary": "",
|
|
3755
|
+
"data-copied": cfgReadCopied ? "" : void 0,
|
|
3756
|
+
onClick: doCopyConfigRead,
|
|
3757
|
+
title: t('\u590D\u5236\u56DE\u8BFB\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u8C03\u7528 iterate_config(operation:"read") \u5E76\u56DE\u663E\u7ED3\u679C')
|
|
3758
|
+
}, cfgReadCopied ? t("\u5DF2\u590D\u5236") : t("\u56DE\u8BFB\u5F53\u524D\u914D\u7F6E"))
|
|
3759
|
+
),
|
|
3760
|
+
cfgReadText ? React.createElement(
|
|
3761
|
+
"div",
|
|
3762
|
+
{ className: "iterate-guide" },
|
|
3763
|
+
React.createElement(
|
|
3764
|
+
"div",
|
|
3765
|
+
{ className: "iterate-guide-bar" },
|
|
3766
|
+
React.createElement("span", {}, t("\u56DE\u8BFB\u6307\u4EE4\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\uFF09")),
|
|
3767
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => doCopy(cfgReadText, "cfgRead") }, t("\u590D\u5236"))
|
|
3768
|
+
),
|
|
3769
|
+
React.createElement("div", { className: "iterate-guide-body" }, cfgReadText)
|
|
3770
|
+
) : null,
|
|
3771
|
+
cfgText ? React.createElement(
|
|
3772
|
+
"div",
|
|
3773
|
+
{ className: "iterate-guide" },
|
|
3774
|
+
React.createElement(
|
|
3775
|
+
"div",
|
|
3776
|
+
{ className: "iterate-guide-bar" },
|
|
3777
|
+
React.createElement("span", {}, t("\u5199\u5165\u6307\u4EE4\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\uFF09")),
|
|
3778
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => doCopy(cfgText, "guide") }, t("\u590D\u5236"))
|
|
3779
|
+
),
|
|
3780
|
+
React.createElement("div", { className: "iterate-guide-body" }, cfgText)
|
|
3781
|
+
) : null,
|
|
2626
3782
|
showGuide ? React.createElement(
|
|
2627
3783
|
"div",
|
|
2628
3784
|
{ className: "iterate-guide" },
|
|
2629
3785
|
React.createElement(
|
|
2630
3786
|
"div",
|
|
2631
3787
|
{ className: "iterate-guide-bar" },
|
|
2632
|
-
React.createElement("span", {}, "iterate.config.yaml \u53EF\u7F16\u8F91\u5B57\u6BB5"),
|
|
2633
|
-
React.createElement("
|
|
3788
|
+
React.createElement("span", {}, t("iterate.config.yaml \u53EF\u7F16\u8F91\u5B57\u6BB5")),
|
|
3789
|
+
React.createElement("span", { className: "iterate-chip" }, t("\u4E2D\u6587\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u7684\u5951\u7EA6\u6587\u6848\uFF09")),
|
|
3790
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => doCopy(guide, "guide") }, t("\u590D\u5236"))
|
|
2634
3791
|
),
|
|
2635
3792
|
React.createElement("div", { className: "iterate-guide-body" }, guide)
|
|
2636
3793
|
) : null
|
|
@@ -2644,14 +3801,22 @@ function SettingsPanel(_props) {
|
|
|
2644
3801
|
React.createElement(
|
|
2645
3802
|
"div",
|
|
2646
3803
|
{},
|
|
2647
|
-
React.createElement("div", { className: "iterate-settings-title" }, "\u72B6\u6001\u6982\u89C8"),
|
|
2648
|
-
React.createElement("div", { className: "iterate-settings-desc" }, "\u67E5\u770B\u8FD0\u884C\u65F6\u4EA7\u7269\u5E03\u5C40\u4E0E\u6E05\u7406\u6307\u5F15\u3002iterate_status / iterate_history / iterate_prune \u5DE5\u5177\u7528\u4E8E\u67E5\u770B\u548C\u7BA1\u7406\u3002")
|
|
3804
|
+
React.createElement("div", { className: "iterate-settings-title" }, t("\u72B6\u6001\u6982\u89C8")),
|
|
3805
|
+
React.createElement("div", { className: "iterate-settings-desc" }, t("\u67E5\u770B\u8FD0\u884C\u65F6\u4EA7\u7269\u5E03\u5C40\u4E0E\u6E05\u7406\u6307\u5F15\u3002iterate_status / iterate_history / iterate_prune \u5DE5\u5177\u7528\u4E8E\u67E5\u770B\u548C\u7BA1\u7406\u3002"))
|
|
2649
3806
|
),
|
|
2650
3807
|
React.createElement(
|
|
2651
3808
|
"div",
|
|
2652
3809
|
{ className: "iterate-scard-actions" },
|
|
2653
|
-
React.createElement("button", { className: "iterate-btn", "data-primary": "", "data-copied": statusCopied ? "" : void 0, onClick: () => doCopy(statusGuide, "status") }, statusCopied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u6307\u5F15"),
|
|
2654
|
-
|
|
3810
|
+
React.createElement("button", { className: "iterate-btn", "data-primary": "", "data-copied": statusCopied ? "" : void 0, onClick: () => doCopy(statusGuide, "status") }, statusCopied ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u6307\u5F15")),
|
|
3811
|
+
// Gap #4: RUNTIME_ARTIFACTS stops being documentation-only — this
|
|
3812
|
+
// copies the instruction that makes the model echo those artifacts
|
|
3813
|
+
// into the session so the observatory panels can read them.
|
|
3814
|
+
React.createElement("button", {
|
|
3815
|
+
className: "iterate-btn",
|
|
3816
|
+
"data-copied": pullCopied ? "" : void 0,
|
|
3817
|
+
onClick: () => doCopy(buildDiskSnapshotInstruction(), "pull")
|
|
3818
|
+
}, pullCopied ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167")),
|
|
3819
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => setShowStatus((v) => !v) }, showStatus ? t("\u6536\u8D77") : t("\u5C55\u5F00"))
|
|
2655
3820
|
)
|
|
2656
3821
|
),
|
|
2657
3822
|
showStatus ? React.createElement(
|
|
@@ -2660,8 +3825,9 @@ function SettingsPanel(_props) {
|
|
|
2660
3825
|
React.createElement(
|
|
2661
3826
|
"div",
|
|
2662
3827
|
{ className: "iterate-guide-bar" },
|
|
2663
|
-
React.createElement("span", {}, "\u8FD0\u884C\u65F6\u5E03\u5C40\u4E0E\u6E05\u7406"),
|
|
2664
|
-
React.createElement("
|
|
3828
|
+
React.createElement("span", {}, t("\u8FD0\u884C\u65F6\u5E03\u5C40\u4E0E\u6E05\u7406")),
|
|
3829
|
+
React.createElement("span", { className: "iterate-chip" }, t("\u4E2D\u6587\uFF08\u7C98\u8D34\u7ED9\u6A21\u578B\u7684\u5951\u7EA6\u6587\u6848\uFF09")),
|
|
3830
|
+
React.createElement("button", { className: "iterate-btn", "data-ghost": "", onClick: () => doCopy(statusGuide, "status") }, t("\u590D\u5236"))
|
|
2665
3831
|
),
|
|
2666
3832
|
React.createElement("div", { className: "iterate-guide-body" }, statusGuide)
|
|
2667
3833
|
) : null
|
|
@@ -2676,21 +3842,31 @@ function SettingsPanel(_props) {
|
|
|
2676
3842
|
"div",
|
|
2677
3843
|
{},
|
|
2678
3844
|
React.createElement("div", { className: "iterate-settings-title" }, "iterate"),
|
|
2679
|
-
React.createElement("div", { className: "iterate-settings-desc" }, "\u4E3A\u6BCF\u6B21\u4EE3\u7801\u8BC4\u5BA1\u751F\u6210 Review \u62A5\u544A\u4E0E\u4EA4\u4E92\u5F0F\u5206\u8BCA\u89C6\u56FE\uFF0C\u4E13\u6CE8 AI \u81EA\u6211\u5BA1\u67E5\u4E0E\u4FEE\u6B63\u3002")
|
|
3845
|
+
React.createElement("div", { className: "iterate-settings-desc" }, t("\u4E3A\u6BCF\u6B21\u4EE3\u7801\u8BC4\u5BA1\u751F\u6210 Review \u62A5\u544A\u4E0E\u4EA4\u4E92\u5F0F\u5206\u8BCA\u89C6\u56FE\uFF0C\u4E13\u6CE8 AI \u81EA\u6211\u5BA1\u67E5\u4E0E\u4FEE\u6B63\u3002"))
|
|
2680
3846
|
),
|
|
2681
3847
|
React.createElement(
|
|
2682
3848
|
"span",
|
|
2683
3849
|
{ className: "iterate-pill" },
|
|
2684
3850
|
React.createElement("span", { className: "iterate-pill-dot" }),
|
|
2685
|
-
"\u5C31\u7EEA"
|
|
3851
|
+
t("\u5C31\u7EEA")
|
|
2686
3852
|
)
|
|
2687
3853
|
)
|
|
2688
3854
|
);
|
|
2689
3855
|
return React.createElement(
|
|
2690
3856
|
"div",
|
|
2691
3857
|
{ "data-iterate-root": "", "data-iterate": "settings", className: "iterate-settings" },
|
|
2692
|
-
React.createElement("div", { className: "iterate-settings-title", style: { fontSize: 15, fontWeight: 700 } }, "iterate \u8BBE\u7F6E"),
|
|
3858
|
+
React.createElement("div", { className: "iterate-settings-title", style: { fontSize: 15, fontWeight: 700 } }, t("iterate \u8BBE\u7F6E")),
|
|
2693
3859
|
banner,
|
|
3860
|
+
copyError ? React.createElement(
|
|
3861
|
+
"div",
|
|
3862
|
+
{ key: "copy-err", className: "iterate-scard", role: "alert" },
|
|
3863
|
+
React.createElement(
|
|
3864
|
+
"div",
|
|
3865
|
+
{ className: "iterate-settings-desc" },
|
|
3866
|
+
t("\u526A\u8D34\u677F\u5199\u5165\u88AB\u62D2\u7EDD\uFF0C\u590D\u5236\u672A\u751F\u6548\u2014\u2014\u8BF7\u5C55\u5F00\u5BF9\u5E94\u533A\u5757\u624B\u52A8\u9009\u4E2D\u6587\u672C\u590D\u5236\u3002")
|
|
3867
|
+
)
|
|
3868
|
+
) : null,
|
|
3869
|
+
langCard,
|
|
2694
3870
|
themeCard,
|
|
2695
3871
|
dataCard,
|
|
2696
3872
|
guideCard,
|
|
@@ -2698,31 +3874,52 @@ function SettingsPanel(_props) {
|
|
|
2698
3874
|
);
|
|
2699
3875
|
}
|
|
2700
3876
|
var OBS_TABS = [
|
|
2701
|
-
{ key: "live", label: "\u5B9E\u65F6\u6D3B\u52A8\u6D41" },
|
|
2702
|
-
{ key: "f1", label: "\u5BA1\u67E5\u7EBF\u7A0B" },
|
|
2703
|
-
{ key: "f2", label: "\u6536\u655B\u8D8B\u52BF" },
|
|
2704
|
-
{ key: "f3", label: "\u53D1\u73B0\u5B9A\u4F4D" },
|
|
2705
|
-
{ key: "f4", label: "\u4FEE\u590D\u4E0E\u56DE\u6EDA" },
|
|
2706
|
-
{ key: "f5", label: "\u65AD\u70B9\u6062\u590D" },
|
|
2707
|
-
{ key: "f6", label: "\u8FD0\u884C\u63A7\u5236\u53F0" },
|
|
2708
|
-
{ key: "f7", label: "\u51B3\u7B56\u65F6\u95F4\u7EBF" },
|
|
3877
|
+
{ key: "live", label: { zh: "\u5B9E\u65F6\u6D3B\u52A8\u6D41", en: "Live activity" }, hotkey: "" },
|
|
3878
|
+
{ key: "f1", label: { zh: "\u5BA1\u67E5\u7EBF\u7A0B", en: "Review threads" }, hotkey: "1" },
|
|
3879
|
+
{ key: "f2", label: { zh: "\u6536\u655B\u8D8B\u52BF", en: "Convergence trend" }, hotkey: "2" },
|
|
3880
|
+
{ key: "f3", label: { zh: "\u53D1\u73B0\u5B9A\u4F4D", en: "Finding locations" }, hotkey: "3" },
|
|
3881
|
+
{ key: "f4", label: { zh: "\u4FEE\u590D\u4E0E\u56DE\u6EDA", en: "Fixes & rollback" }, hotkey: "4" },
|
|
3882
|
+
{ key: "f5", label: { zh: "\u65AD\u70B9\u6062\u590D", en: "Checkpoint resume" }, hotkey: "5" },
|
|
3883
|
+
{ key: "f6", label: { zh: "\u8FD0\u884C\u63A7\u5236\u53F0", en: "Run console" }, hotkey: "6" },
|
|
3884
|
+
{ key: "f7", label: { zh: "\u51B3\u7B56\u65F6\u95F4\u7EBF", en: "Decision timeline" }, hotkey: "7" },
|
|
2709
3885
|
// v3.0: Quality Command Center tabs
|
|
2710
|
-
{ key: "f8", label: "\u8D28\u91CF\u95E8\u7981" },
|
|
2711
|
-
{ key: "f9", label: "\u7ECF\u9A8C\u94F6\u884C" },
|
|
2712
|
-
{ key: "f10", label: "\u9632\u5FA1\u4E8B\u4EF6" }
|
|
3886
|
+
{ key: "f8", label: { zh: "\u8D28\u91CF\u95E8\u7981", en: "Quality gate" }, hotkey: "8" },
|
|
3887
|
+
{ key: "f9", label: { zh: "\u7ECF\u9A8C\u94F6\u884C", en: "Experience bank" }, hotkey: "9" },
|
|
3888
|
+
{ key: "f10", label: { zh: "\u9632\u5FA1\u4E8B\u4EF6", en: "Defense events" }, hotkey: "0" }
|
|
2713
3889
|
];
|
|
3890
|
+
var OBS_HOTKEY_TABS = {
|
|
3891
|
+
1: "f1",
|
|
3892
|
+
2: "f2",
|
|
3893
|
+
3: "f3",
|
|
3894
|
+
4: "f4",
|
|
3895
|
+
5: "f5",
|
|
3896
|
+
6: "f6",
|
|
3897
|
+
7: "f7",
|
|
3898
|
+
8: "f8",
|
|
3899
|
+
9: "f9",
|
|
3900
|
+
0: "f10",
|
|
3901
|
+
"-": "live"
|
|
3902
|
+
};
|
|
3903
|
+
function obsTabLabel(t2) {
|
|
3904
|
+
const label = t2.label[getLang()];
|
|
3905
|
+
if (t2.key === "live") return label;
|
|
3906
|
+
return `${t2.key.toUpperCase()} ${label}`;
|
|
3907
|
+
}
|
|
3908
|
+
function obsHotkeyHint() {
|
|
3909
|
+
return getLang() === "en" ? `${String(OBS_TABS.length)} tabs \xB7 keys 1-9/0 switch Fn, - returns to the live feed` : tf(`{a0} \u9875 \xB7 \u6309 1-9/0 \u5207 Fn\u3001- \u56DE\u5B9E\u65F6\u6D41`, { a0: String(OBS_TABS.length) });
|
|
3910
|
+
}
|
|
2714
3911
|
var OBS_LIVE_META = {
|
|
2715
|
-
read: { label: "\u9605\u8BFB", color: "var(--dsw-alias-brand-primary)" },
|
|
2716
|
-
fix: { label: "\u4FEE\u590D", color: "var(--dsw-alias-state-success-primary)" },
|
|
2717
|
-
rollback: { label: "\u56DE\u6EDA", color: "var(--dsw-alias-state-error-primary)" },
|
|
2718
|
-
diff: { label: "\u5DEE\u5F02", color: "var(--dsw-alias-state-warn-primary)" },
|
|
2719
|
-
review: { label: "\u5BA1\u67E5", color: "var(--dsw-alias-brand-primary)" },
|
|
2720
|
-
triage: { label: "\u5206\u6D41", color: "var(--dsw-alias-state-warn-primary)" },
|
|
2721
|
-
checkpoint: { label: "\u65AD\u70B9", color: "var(--dsw-alias-brand-primary)" },
|
|
2722
|
-
validate: { label: "\u6821\u9A8C", color: "var(--dsw-alias-state-success-primary)" },
|
|
2723
|
-
log: { label: "\u8BB0\u5F55", color: "var(--dsw-alias-label-secondary)" },
|
|
2724
|
-
prune: { label: "\u6E05\u7406", color: "var(--dsw-alias-state-warn-primary)" },
|
|
2725
|
-
info: { label: "\u4FE1\u606F", color: "var(--dsw-alias-label-secondary)" }
|
|
3912
|
+
read: { label: { zh: "\u9605\u8BFB", en: "Read" }, color: "var(--dsw-alias-brand-primary)" },
|
|
3913
|
+
fix: { label: { zh: "\u4FEE\u590D", en: "Fix" }, color: "var(--dsw-alias-state-success-primary)" },
|
|
3914
|
+
rollback: { label: { zh: "\u56DE\u6EDA", en: "Rollback" }, color: "var(--dsw-alias-state-error-primary)" },
|
|
3915
|
+
diff: { label: { zh: "\u5DEE\u5F02", en: "Diff" }, color: "var(--dsw-alias-state-warn-primary)" },
|
|
3916
|
+
review: { label: { zh: "\u5BA1\u67E5", en: "Review" }, color: "var(--dsw-alias-brand-primary)" },
|
|
3917
|
+
triage: { label: { zh: "\u5206\u6D41", en: "Triage" }, color: "var(--dsw-alias-state-warn-primary)" },
|
|
3918
|
+
checkpoint: { label: { zh: "\u65AD\u70B9", en: "Checkpoint" }, color: "var(--dsw-alias-brand-primary)" },
|
|
3919
|
+
validate: { label: { zh: "\u6821\u9A8C", en: "Validate" }, color: "var(--dsw-alias-state-success-primary)" },
|
|
3920
|
+
log: { label: { zh: "\u8BB0\u5F55", en: "Log" }, color: "var(--dsw-alias-label-secondary)" },
|
|
3921
|
+
prune: { label: { zh: "\u6E05\u7406", en: "Prune" }, color: "var(--dsw-alias-state-warn-primary)" },
|
|
3922
|
+
info: { label: { zh: "\u4FE1\u606F", en: "Info" }, color: "var(--dsw-alias-label-secondary)" }
|
|
2726
3923
|
};
|
|
2727
3924
|
var OBS_LIVE_FALLBACK_COLOR = "var(--dsw-alias-label-secondary)";
|
|
2728
3925
|
function formatObsLiveTime(ts) {
|
|
@@ -2760,21 +3957,13 @@ function latestDefenseEvents(session) {
|
|
|
2760
3957
|
return scanSessionForDefenseEvents(session);
|
|
2761
3958
|
}
|
|
2762
3959
|
function buildObsFixInstruction(f) {
|
|
2763
|
-
|
|
2764
|
-
file: String(f.file || ""),
|
|
2765
|
-
...typeof f.line === "number" && f.line > 0 ? { line: f.line } : {},
|
|
2766
|
-
dimension: String(f.dimension || ""),
|
|
2767
|
-
summary: String(f.summary || ""),
|
|
2768
|
-
...f.suggested_fix ? { suggested_fix: String(f.suggested_fix) } : {}
|
|
2769
|
-
}, null, 2);
|
|
2770
|
-
return `\u8BF7\u8C03\u7528 \`iterate_fix\` \u4FEE\u590D\u4EE5\u4E0B finding\uFF1A
|
|
2771
|
-
|
|
2772
|
-
\`\`\`json
|
|
2773
|
-
${payload}
|
|
2774
|
-
\`\`\``;
|
|
3960
|
+
return buildFixInstruction(f);
|
|
2775
3961
|
}
|
|
2776
3962
|
function ObservatoryPanel(props) {
|
|
2777
3963
|
const session = props && props.session ? props.session : null;
|
|
3964
|
+
React.useEffect(() => {
|
|
3965
|
+
noteSession(session);
|
|
3966
|
+
}, [session]);
|
|
2778
3967
|
const manifest = latestTranscript(session);
|
|
2779
3968
|
const qualityGate = latestQualityGate(session);
|
|
2780
3969
|
const experienceBank = latestExperienceBank(session);
|
|
@@ -2799,6 +3988,28 @@ function ObservatoryPanel(props) {
|
|
|
2799
3988
|
React.useEffect(() => () => {
|
|
2800
3989
|
if (copyTimer.current) clearTimeout(copyTimer.current);
|
|
2801
3990
|
}, []);
|
|
3991
|
+
React.useEffect(() => {
|
|
3992
|
+
const doc = typeof document !== "undefined" ? document : null;
|
|
3993
|
+
if (!doc || !open) return;
|
|
3994
|
+
const onKeyDown = (ev) => {
|
|
3995
|
+
if (ev.metaKey || ev.ctrlKey || ev.altKey || ev.shiftKey) return;
|
|
3996
|
+
const t2 = ev.target;
|
|
3997
|
+
if (t2 && (t2.tagName === "INPUT" || t2.tagName === "TEXTAREA" || t2.tagName === "SELECT")) return;
|
|
3998
|
+
if (t2 && typeof t2.isContentEditable === "boolean" && t2.isContentEditable) return;
|
|
3999
|
+
const rootEl = doc.querySelector('[data-iterate="obs"]');
|
|
4000
|
+
if (!rootEl) return;
|
|
4001
|
+
const activeEl = doc.activeElement;
|
|
4002
|
+
const inPanel = activeEl === rootEl || activeEl !== null && typeof rootEl.contains === "function" && rootEl.contains(activeEl);
|
|
4003
|
+
const idle = activeEl === null || activeEl === doc.body;
|
|
4004
|
+
if (!inPanel && !idle) return;
|
|
4005
|
+
const next = OBS_HOTKEY_TABS[ev.key];
|
|
4006
|
+
if (!next) return;
|
|
4007
|
+
ev.preventDefault();
|
|
4008
|
+
setTab(next);
|
|
4009
|
+
};
|
|
4010
|
+
doc.addEventListener("keydown", onKeyDown);
|
|
4011
|
+
return () => doc.removeEventListener("keydown", onKeyDown);
|
|
4012
|
+
}, [open]);
|
|
2802
4013
|
const copyInstruction = (key, text) => {
|
|
2803
4014
|
if (!text) return;
|
|
2804
4015
|
copyText(text).then((ok) => {
|
|
@@ -2811,6 +4022,8 @@ function ObservatoryPanel(props) {
|
|
|
2811
4022
|
copyTimer.current = setTimeout(() => setCopiedKey((cur) => cur === key ? null : cur), 1600);
|
|
2812
4023
|
});
|
|
2813
4024
|
};
|
|
4025
|
+
const pullInstruction = buildDiskSnapshotInstruction();
|
|
4026
|
+
const pullTitle = tf(`\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u9010\u4E2A\u8C03\u7528 {a0} \u5E76\u56DE\u663E\u5B8C\u6574\u7ED3\u679C`, { a0: DISK_SNAPSHOT_SOURCES.map((s) => s.tool).join(" / ") });
|
|
2814
4027
|
const toggleThread = (key) => {
|
|
2815
4028
|
setThreadMode("auto");
|
|
2816
4029
|
setExpandedThreads((prev) => {
|
|
@@ -2822,7 +4035,12 @@ function ObservatoryPanel(props) {
|
|
|
2822
4035
|
};
|
|
2823
4036
|
const exportObservatory = () => {
|
|
2824
4037
|
if (!manifest) return;
|
|
2825
|
-
const json = serializeObservatoryExport(manifest, manifest.live || []
|
|
4038
|
+
const json = serializeObservatoryExport(manifest, manifest.live || [], {
|
|
4039
|
+
qualityGate,
|
|
4040
|
+
experienceBank,
|
|
4041
|
+
defenseEvents,
|
|
4042
|
+
report: latestReport(session)
|
|
4043
|
+
});
|
|
2826
4044
|
const filename = `iterate-observatory-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`;
|
|
2827
4045
|
const flash = () => {
|
|
2828
4046
|
setCopiedKey("export");
|
|
@@ -2857,8 +4075,17 @@ function ObservatoryPanel(props) {
|
|
|
2857
4075
|
React.createElement(
|
|
2858
4076
|
"div",
|
|
2859
4077
|
{ className: "iterate-obs-head", "data-closed": "" },
|
|
2860
|
-
React.createElement("span", { className: "iterate-obs-title" }, "iterate \u89C2\u6D4B\u53F0"),
|
|
2861
|
-
React.createElement("span", { className: "iterate-obs-head-meta" },
|
|
4078
|
+
React.createElement("span", { className: "iterate-obs-title" }, t("iterate \u89C2\u6D4B\u53F0")),
|
|
4079
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`\u6682\u65E0\u8FD0\u884C\u65F6\u89C2\u6D4B\u6570\u636E \xB7 {a0}`, { a0: obsHotkeyHint() })),
|
|
4080
|
+
React.createElement("button", {
|
|
4081
|
+
className: "iterate-btn",
|
|
4082
|
+
"data-copied": copiedKey === "pull-snapshot" ? "" : void 0,
|
|
4083
|
+
onClick: (e) => {
|
|
4084
|
+
e.stopPropagation();
|
|
4085
|
+
copyInstruction("pull-snapshot", pullInstruction);
|
|
4086
|
+
},
|
|
4087
|
+
title: t("\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u78C1\u76D8\u4E0A\u53EF\u80FD\u5DF2\u6709\u5386\u53F2\u4EA7\u7269\uFF0C\u56DE\u663E\u8FDB\u4F1A\u8BDD\u540E\u89C2\u6D4B\u53F0\u5373\u53EF\u586B\u5145")
|
|
4088
|
+
}, copiedKey === "pull-snapshot" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"))
|
|
2862
4089
|
)
|
|
2863
4090
|
);
|
|
2864
4091
|
}
|
|
@@ -2880,7 +4107,7 @@ function ObservatoryPanel(props) {
|
|
|
2880
4107
|
const renderThreads = () => {
|
|
2881
4108
|
const rounds = manifest.rounds || [];
|
|
2882
4109
|
if (rounds.length === 0) {
|
|
2883
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u5BA1\u67E5\u7EBF\u7A0B");
|
|
4110
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u5BA1\u67E5\u7EBF\u7A0B"));
|
|
2884
4111
|
}
|
|
2885
4112
|
return React.createElement(
|
|
2886
4113
|
"div",
|
|
@@ -2888,34 +4115,34 @@ function ObservatoryPanel(props) {
|
|
|
2888
4115
|
React.createElement(
|
|
2889
4116
|
"div",
|
|
2890
4117
|
{ className: "iterate-obs-bar", style: { marginBottom: 8, flexWrap: "wrap" } },
|
|
2891
|
-
React.createElement("b", {}, "\u5BA1\u67E5\u7EBF\u7A0B"),
|
|
2892
|
-
React.createElement("button", { className: "iterate-btn", onClick: () => setAllThreads("all"), title: "\u5C55\u5F00\u5168\u90E8\u7EBF\u7A0B" }, "\u5168\u90E8\u5C55\u5F00"),
|
|
2893
|
-
React.createElement("button", { className: "iterate-btn", onClick: () => setAllThreads("none"), title: "\u6536\u8D77\u5168\u90E8\u7EBF\u7A0B" }, "\u5168\u90E8\u6536\u8D77"),
|
|
4118
|
+
React.createElement("b", {}, t("\u5BA1\u67E5\u7EBF\u7A0B")),
|
|
4119
|
+
React.createElement("button", { className: "iterate-btn", onClick: () => setAllThreads("all"), title: t("\u5C55\u5F00\u5168\u90E8\u7EBF\u7A0B") }, t("\u5168\u90E8\u5C55\u5F00")),
|
|
4120
|
+
React.createElement("button", { className: "iterate-btn", onClick: () => setAllThreads("none"), title: t("\u6536\u8D77\u5168\u90E8\u7EBF\u7A0B") }, t("\u5168\u90E8\u6536\u8D77")),
|
|
2894
4121
|
React.createElement(
|
|
2895
4122
|
"span",
|
|
2896
4123
|
{ className: "iterate-filter-count" },
|
|
2897
|
-
|
|
4124
|
+
tf(`{a0} \xB7 {a1} \u8F6E`, { a0: threadMode === "all" ? t("\u5168\u90E8\u5C55\u5F00") : threadMode === "none" ? t("\u5168\u90E8\u6536\u8D77") : t("\u81EA\u7531\u5207\u6362"), a1: rounds.length })
|
|
2898
4125
|
)
|
|
2899
4126
|
),
|
|
2900
4127
|
...rounds.map((r, ri) => {
|
|
2901
4128
|
const threads = r.threads || [];
|
|
2902
|
-
const fCount = threads.reduce((sum,
|
|
2903
|
-
const threadBlocks = threads.length === 0 ? [React.createElement("div", { key: "none", className: "iterate-obs-empty" }, "\u672C\u8F6E\u65E0\u7EBF\u7A0B")] : threads.map((
|
|
4129
|
+
const fCount = threads.reduce((sum, t2) => sum + (t2.findings ? t2.findings.length : 0), 0);
|
|
4130
|
+
const threadBlocks = threads.length === 0 ? [React.createElement("div", { key: "none", className: "iterate-obs-empty" }, t("\u672C\u8F6E\u65E0\u7EBF\u7A0B"))] : threads.map((th, ti) => {
|
|
2904
4131
|
const key = `${ri}-${ti}`;
|
|
2905
4132
|
const expanded = isThreadExpanded(key);
|
|
2906
|
-
const dim =
|
|
2907
|
-
const tFindings =
|
|
2908
|
-
const files =
|
|
2909
|
-
const msgs =
|
|
4133
|
+
const dim = th.dimension || t("\u672A\u547D\u540D\u7EF4\u5EA6");
|
|
4134
|
+
const tFindings = th.findings || [];
|
|
4135
|
+
const files = th.readFiles || [];
|
|
4136
|
+
const msgs = th.messages || [];
|
|
2910
4137
|
return React.createElement(
|
|
2911
4138
|
"div",
|
|
2912
4139
|
{ key, className: "iterate-obs-block" },
|
|
2913
4140
|
React.createElement(
|
|
2914
4141
|
"div",
|
|
2915
|
-
{ className: "iterate-obs-block-head", "data-click": "", onClick: () => toggleThread(key), title: expanded ? "\u6536\u8D77\u7EBF\u7A0B" : "\u5C55\u5F00\u7EBF\u7A0B" },
|
|
4142
|
+
{ className: "iterate-obs-block-head", "data-click": "", onClick: () => toggleThread(key), title: expanded ? t("\u6536\u8D77\u7EBF\u7A0B") : t("\u5C55\u5F00\u7EBF\u7A0B") },
|
|
2916
4143
|
React.createElement("span", {}, expanded ? "\u2212" : "+"),
|
|
2917
4144
|
React.createElement("b", {}, dim),
|
|
2918
|
-
React.createElement("span", { className: "iterate-obs-chip" }, `attempt ${typeof
|
|
4145
|
+
React.createElement("span", { className: "iterate-obs-chip" }, `attempt ${typeof th.attempt === "number" ? th.attempt : "?"}`),
|
|
2919
4146
|
React.createElement("span", { className: "iterate-obs-chip" }, `${tFindings.length} findings`)
|
|
2920
4147
|
),
|
|
2921
4148
|
expanded ? React.createElement(
|
|
@@ -2924,15 +4151,15 @@ function ObservatoryPanel(props) {
|
|
|
2924
4151
|
React.createElement(
|
|
2925
4152
|
"div",
|
|
2926
4153
|
{ className: "iterate-obs-bar" },
|
|
2927
|
-
React.createElement("b", {}, "\u53D9\u8FF0"),
|
|
2928
|
-
React.createElement("span", { className: "iterate-obs-head-meta" },
|
|
4154
|
+
React.createElement("b", {}, t("\u53D9\u8FF0")),
|
|
4155
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`{a0} \u6761`, { a0: msgs.length }))
|
|
2929
4156
|
),
|
|
2930
|
-
...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))),
|
|
4157
|
+
...msgs.length === 0 ? [React.createElement("div", { key: "msg-none", className: "iterate-obs-empty" }, t("\u65E0\u7EBF\u7D22\u53D9\u8FF0"))] : msgs.map((m, mi) => React.createElement("div", { key: mi, className: "iterate-obs-msg" }, String(m))),
|
|
2931
4158
|
...files.length > 0 ? [
|
|
2932
4159
|
React.createElement(
|
|
2933
4160
|
"div",
|
|
2934
4161
|
{ key: "files-h", className: "iterate-obs-bar", style: { marginTop: 6 } },
|
|
2935
|
-
React.createElement("b", {}, "\u8BFB\u53D6\u6587\u4EF6")
|
|
4162
|
+
React.createElement("b", {}, t("\u8BFB\u53D6\u6587\u4EF6"))
|
|
2936
4163
|
),
|
|
2937
4164
|
...files.map(
|
|
2938
4165
|
(f, fi) => React.createElement("div", { key: `f${fi}`, className: "iterate-obs-file" }, String(f))
|
|
@@ -2941,9 +4168,9 @@ function ObservatoryPanel(props) {
|
|
|
2941
4168
|
React.createElement(
|
|
2942
4169
|
"div",
|
|
2943
4170
|
{ className: "iterate-obs-bar", style: { marginTop: 6 } },
|
|
2944
|
-
React.createElement("b", {}, "\u53D1\u73B0")
|
|
4171
|
+
React.createElement("b", {}, t("\u53D1\u73B0"))
|
|
2945
4172
|
),
|
|
2946
|
-
...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}`))
|
|
4173
|
+
...tFindings.length === 0 ? [React.createElement("div", { key: "finding-none", className: "iterate-obs-empty" }, t("\u672C\u7EBF\u7A0B\u65E0\u53D1\u73B0"))] : tFindings.map((f, fi) => threadFindingPill(f, `tf${fi}`))
|
|
2947
4174
|
) : null
|
|
2948
4175
|
);
|
|
2949
4176
|
});
|
|
@@ -2954,7 +4181,7 @@ function ObservatoryPanel(props) {
|
|
|
2954
4181
|
"div",
|
|
2955
4182
|
{ className: "iterate-obs-block-head" },
|
|
2956
4183
|
React.createElement("b", {}, `Round ${typeof r.round === "number" ? r.round : ri + 1}`),
|
|
2957
|
-
React.createElement("span", { className: "iterate-obs-chip" },
|
|
4184
|
+
React.createElement("span", { className: "iterate-obs-chip" }, tf(`{a0} \u7EBF\u7A0B`, { a0: threads.length })),
|
|
2958
4185
|
React.createElement("span", { className: "iterate-obs-head-meta" }, `${fCount} findings`)
|
|
2959
4186
|
),
|
|
2960
4187
|
React.createElement("div", { className: "iterate-obs-block-body" }, ...threadBlocks)
|
|
@@ -2965,9 +4192,48 @@ function ObservatoryPanel(props) {
|
|
|
2965
4192
|
const renderTrend = () => {
|
|
2966
4193
|
const conv = manifest.convergence || [];
|
|
2967
4194
|
if (conv.length === 0) {
|
|
2968
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u6536\u655B\u6570\u636E");
|
|
4195
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u6536\u655B\u6570\u636E"));
|
|
2969
4196
|
}
|
|
2970
4197
|
const points = conv.map((n, i) => ({ round: i + 1, count: n }));
|
|
4198
|
+
const report = latestReport(session);
|
|
4199
|
+
const comparison = buildRoundComparison(report, manifest.validations || []);
|
|
4200
|
+
const comparisonTable = comparison.length === 0 ? null : React.createElement(
|
|
4201
|
+
"table",
|
|
4202
|
+
{ className: "iterate-obs-table" },
|
|
4203
|
+
React.createElement(
|
|
4204
|
+
"thead",
|
|
4205
|
+
{},
|
|
4206
|
+
React.createElement(
|
|
4207
|
+
"tr",
|
|
4208
|
+
{},
|
|
4209
|
+
React.createElement("th", {}, t("\u8F6E\u6B21")),
|
|
4210
|
+
React.createElement("th", {}, t("\u53D1\u73B0")),
|
|
4211
|
+
React.createElement("th", {}, t("\u5DF2\u4FEE\u590D")),
|
|
4212
|
+
React.createElement("th", {}, t("\u4E25\u91CD\u5EA6\u5206\u5E03")),
|
|
4213
|
+
React.createElement("th", {}, t("\u9A8C\u8BC1"))
|
|
4214
|
+
)
|
|
4215
|
+
),
|
|
4216
|
+
React.createElement(
|
|
4217
|
+
"tbody",
|
|
4218
|
+
{},
|
|
4219
|
+
...comparison.map((row) => {
|
|
4220
|
+
const sevText = Object.entries(row.severities).map(([k, v]) => `${k}:${v}`).join(" \xB7 ");
|
|
4221
|
+
const anyFailed = row.validations.some((v) => v.allowed !== true);
|
|
4222
|
+
const valText = row.validations.length === 0 ? "\u2014" : tf(`{a0}/{a1} \u901A\u8FC7{a2}`, { a0: row.validations.filter((v) => v.allowed === true).length, a1: row.validations.length, a2: anyFailed ? t("\uFF08\u542B\u5931\u8D25\uFF09") : "" });
|
|
4223
|
+
return React.createElement(
|
|
4224
|
+
"tr",
|
|
4225
|
+
{ key: `cmp-${row.round}` },
|
|
4226
|
+
React.createElement("td", {}, `Round ${row.round}`),
|
|
4227
|
+
React.createElement("td", {}, String(row.findings)),
|
|
4228
|
+
React.createElement("td", {}, String(row.fixed)),
|
|
4229
|
+
React.createElement("td", {}, sevText || "\u2014"),
|
|
4230
|
+
React.createElement("td", {
|
|
4231
|
+
style: anyFailed ? { color: "var(--dsw-alias-state-error-primary)", fontWeight: 600 } : row.validations.length > 0 ? { color: "var(--dsw-alias-state-success-primary)" } : void 0
|
|
4232
|
+
}, valText)
|
|
4233
|
+
);
|
|
4234
|
+
})
|
|
4235
|
+
)
|
|
4236
|
+
);
|
|
2971
4237
|
return React.createElement(
|
|
2972
4238
|
"div",
|
|
2973
4239
|
{},
|
|
@@ -2975,14 +4241,29 @@ function ObservatoryPanel(props) {
|
|
|
2975
4241
|
React.createElement(
|
|
2976
4242
|
"div",
|
|
2977
4243
|
{ className: "iterate-obs-bar", style: { marginTop: 6 } },
|
|
2978
|
-
`\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\uFF1A
|
|
2979
|
-
)
|
|
4244
|
+
tf(`\u5404\u8F6E\u53D1\u73B0\u6570\u91CF\uFF1A{a0}{a1}`, { a0: conv.join(" \u2192 "), a1: typeof manifest.round === "number" ? tf(` \xB7 \u5F53\u524D Round {a0}`, { a0: manifest.round }) : "" })
|
|
4245
|
+
),
|
|
4246
|
+
comparisonTable ? React.createElement(
|
|
4247
|
+
"div",
|
|
4248
|
+
{ className: "iterate-obs-block", style: { marginTop: 8 } },
|
|
4249
|
+
React.createElement(
|
|
4250
|
+
"div",
|
|
4251
|
+
{ className: "iterate-obs-block-head" },
|
|
4252
|
+
React.createElement("span", {}, t("\u8DE8\u8F6E\u5BF9\u6BD4\uFF08\u672C\u4F1A\u8BDD\uFF09")),
|
|
4253
|
+
React.createElement(
|
|
4254
|
+
"span",
|
|
4255
|
+
{ className: "iterate-obs-head-meta" },
|
|
4256
|
+
t("\u8DE8\u4F1A\u8BDD\u5BF9\u6BD4\uFF1A\u672C\u8868\u53EA\u8986\u76D6\u5F53\u524D\u4F1A\u8BDD\uFF1B\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u8BA9\u6A21\u578B\u56DE\u663E iterate_history\uFF0C\u518D\u7528\u300C\u5BFC\u51FA JSON\u300D\u5E76\u6392\u6BD4\u8F83\u4E24\u6B21\u8FD0\u884C")
|
|
4257
|
+
)
|
|
4258
|
+
),
|
|
4259
|
+
React.createElement("div", { className: "iterate-obs-block-body" }, comparisonTable)
|
|
4260
|
+
) : null
|
|
2980
4261
|
);
|
|
2981
4262
|
};
|
|
2982
4263
|
const renderFindings = () => {
|
|
2983
4264
|
const findings = manifest.findings || [];
|
|
2984
4265
|
if (findings.length === 0) {
|
|
2985
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u53D1\u73B0");
|
|
4266
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u53D1\u73B0"));
|
|
2986
4267
|
}
|
|
2987
4268
|
const opts = buildFilterOptions(findings);
|
|
2988
4269
|
const { filtered } = filterFindingsWithIndices(findings, f3Filter);
|
|
@@ -2996,13 +4277,13 @@ function ObservatoryPanel(props) {
|
|
|
2996
4277
|
const filterBar = React.createElement(
|
|
2997
4278
|
"div",
|
|
2998
4279
|
{ className: "iterate-obs-bar", style: { marginBottom: 8, flexWrap: "wrap" } },
|
|
2999
|
-
React.createElement("b", {}, "\u53D1\u73B0"),
|
|
4280
|
+
React.createElement("b", {}, t("\u53D1\u73B0")),
|
|
3000
4281
|
...opts.severities.map(
|
|
3001
4282
|
(s) => React.createElement("button", {
|
|
3002
4283
|
key: `sev-${s.value}`,
|
|
3003
4284
|
className: "iterate-filter-chip",
|
|
3004
4285
|
"data-active": f3Filter.severities.includes(s.value) ? "" : void 0,
|
|
3005
|
-
title:
|
|
4286
|
+
title: tf(`{a0}\uFF08{a1} \u9879\uFF09`, { a0: s.value, a1: s.count }),
|
|
3006
4287
|
onClick: () => toggleSeverity(s.value)
|
|
3007
4288
|
}, `${s.value} ${s.count}`)
|
|
3008
4289
|
),
|
|
@@ -3011,13 +4292,13 @@ function ObservatoryPanel(props) {
|
|
|
3011
4292
|
{
|
|
3012
4293
|
className: "iterate-filter-select",
|
|
3013
4294
|
value: f3Filter.dimensions[0] || "",
|
|
3014
|
-
"aria-label": "\u6309\u7EF4\u5EA6\u7B5B\u9009",
|
|
4295
|
+
"aria-label": t("\u6309\u7EF4\u5EA6\u7B5B\u9009"),
|
|
3015
4296
|
onChange: (e) => {
|
|
3016
4297
|
const v = e.target.value;
|
|
3017
4298
|
setF3Filter((prev) => ({ ...prev, dimensions: v ? [v] : [] }));
|
|
3018
4299
|
}
|
|
3019
4300
|
},
|
|
3020
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u7EF4\u5EA6"),
|
|
4301
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u7EF4\u5EA6")),
|
|
3021
4302
|
...opts.dimensions.map(
|
|
3022
4303
|
(d) => React.createElement("option", { key: d.value, value: d.value }, `${d.value} (${d.count})`)
|
|
3023
4304
|
)
|
|
@@ -3025,16 +4306,16 @@ function ObservatoryPanel(props) {
|
|
|
3025
4306
|
React.createElement("input", {
|
|
3026
4307
|
className: "iterate-filter-search",
|
|
3027
4308
|
type: "search",
|
|
3028
|
-
placeholder: "\u641C\u7D22\u53D1\u73B0\u2026",
|
|
3029
|
-
"aria-label": "\u641C\u7D22\u53D1\u73B0",
|
|
4309
|
+
placeholder: t("\u641C\u7D22\u53D1\u73B0\u2026"),
|
|
4310
|
+
"aria-label": t("\u641C\u7D22\u53D1\u73B0"),
|
|
3030
4311
|
value: f3Filter.search,
|
|
3031
4312
|
onChange: (e) => setF3Filter((prev) => ({ ...prev, search: e.target.value }))
|
|
3032
4313
|
}),
|
|
3033
4314
|
filterActive ? React.createElement("button", {
|
|
3034
4315
|
className: "iterate-filter-clear",
|
|
3035
4316
|
onClick: () => setF3Filter({ severities: [], dimensions: [], search: "" }),
|
|
3036
|
-
title: "\u6E05\u9664\u5168\u90E8\u7B5B\u9009"
|
|
3037
|
-
}, "\u6E05\u9664\u7B5B\u9009") : null,
|
|
4317
|
+
title: t("\u6E05\u9664\u5168\u90E8\u7B5B\u9009")
|
|
4318
|
+
}, t("\u6E05\u9664\u7B5B\u9009")) : null,
|
|
3038
4319
|
React.createElement("span", { className: "iterate-filter-count" }, `${filtered.length}/${findings.length}`)
|
|
3039
4320
|
);
|
|
3040
4321
|
const findingBlocks = filtered.map((f, i) => {
|
|
@@ -3059,7 +4340,7 @@ function ObservatoryPanel(props) {
|
|
|
3059
4340
|
React.createElement("span", {}, severityLabel(finding.severity)),
|
|
3060
4341
|
React.createElement("span", { className: "iterate-obs-file" }, loc),
|
|
3061
4342
|
React.createElement("span", {}, String(finding.dimension || "")),
|
|
3062
|
-
finding.acknowledged === true ? React.createElement("span", { className: "iterate-obs-chip" }, "\u5DF2\u786E\u8BA4") : null
|
|
4343
|
+
finding.acknowledged === true ? React.createElement("span", { className: "iterate-obs-chip" }, t("\u5DF2\u786E\u8BA4")) : null
|
|
3063
4344
|
),
|
|
3064
4345
|
React.createElement(
|
|
3065
4346
|
"div",
|
|
@@ -3072,21 +4353,21 @@ function ObservatoryPanel(props) {
|
|
|
3072
4353
|
className: "iterate-btn",
|
|
3073
4354
|
"data-copied": copiedKey === `${k}-fix` ? "" : void 0,
|
|
3074
4355
|
onClick: () => copyInstruction(`${k}-fix`, String(finding.suggested_fix || "")),
|
|
3075
|
-
title: "\u590D\u5236\u8BE5 finding \u7684\u5EFA\u8BAE\u4FEE\u590D\u6587\u672C"
|
|
3076
|
-
}, copiedKey === `${k}-fix` ? "\u5DF2\u590D\u5236" : "\u590D\u5236 suggested_fix"),
|
|
4356
|
+
title: t("\u590D\u5236\u8BE5 finding \u7684\u5EFA\u8BAE\u4FEE\u590D\u6587\u672C")
|
|
4357
|
+
}, copiedKey === `${k}-fix` ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236 suggested_fix")),
|
|
3077
4358
|
React.createElement("button", {
|
|
3078
4359
|
className: "iterate-btn",
|
|
3079
4360
|
"data-copied": copiedKey === `${k}-known` ? "" : void 0,
|
|
3080
4361
|
onClick: () => copyInstruction(`${k}-known`, knownInstruction),
|
|
3081
|
-
title: "\u590D\u5236 iterate_triage \u6307\u4EE4\u6587\u672C\uFF08\u6807\u8BB0\u4E3A known_intentional\uFF09"
|
|
3082
|
-
}, copiedKey === `${k}-known` ? "\u5DF2\u590D\u5236" : "known_intentional"),
|
|
4362
|
+
title: t("\u590D\u5236 iterate_triage \u6307\u4EE4\u6587\u672C\uFF08\u6807\u8BB0\u4E3A known_intentional\uFF09")
|
|
4363
|
+
}, copiedKey === `${k}-known` ? t("\u5DF2\u590D\u5236") : "known_intentional"),
|
|
3083
4364
|
React.createElement("button", {
|
|
3084
4365
|
className: "iterate-btn",
|
|
3085
4366
|
"data-primary": "",
|
|
3086
4367
|
"data-copied": copiedKey === `${k}-repair` ? "" : void 0,
|
|
3087
4368
|
onClick: () => copyInstruction(`${k}-repair`, fixInstruction),
|
|
3088
|
-
title: "\u590D\u5236 iterate_fix \u6307\u4EE4\u6587\u672C"
|
|
3089
|
-
}, copiedKey === `${k}-repair` ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u4FEE\u590D\u6307\u4EE4")
|
|
4369
|
+
title: t("\u590D\u5236 iterate_fix \u6307\u4EE4\u6587\u672C")
|
|
4370
|
+
}, copiedKey === `${k}-repair` ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u4FEE\u590D\u6307\u4EE4"))
|
|
3090
4371
|
)
|
|
3091
4372
|
)
|
|
3092
4373
|
);
|
|
@@ -3096,7 +4377,7 @@ function ObservatoryPanel(props) {
|
|
|
3096
4377
|
"div",
|
|
3097
4378
|
{},
|
|
3098
4379
|
filterBar,
|
|
3099
|
-
React.createElement("div", { className: "iterate-obs-empty" }, "\u65E0\u5339\u914D\u7684\u53D1\u73B0")
|
|
4380
|
+
React.createElement("div", { className: "iterate-obs-empty" }, t("\u65E0\u5339\u914D\u7684\u53D1\u73B0"))
|
|
3100
4381
|
);
|
|
3101
4382
|
}
|
|
3102
4383
|
return React.createElement("div", {}, filterBar, ...findingBlocks);
|
|
@@ -3104,16 +4385,12 @@ function ObservatoryPanel(props) {
|
|
|
3104
4385
|
const renderFixes = () => {
|
|
3105
4386
|
const fixes = manifest.fixes || [];
|
|
3106
4387
|
if (fixes.length === 0) {
|
|
3107
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u4FEE\u590D\u8BB0\u5F55");
|
|
4388
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u4FEE\u590D\u8BB0\u5F55"));
|
|
3108
4389
|
}
|
|
3109
4390
|
return React.createElement("div", {}, ...fixes.map((f, i) => {
|
|
3110
4391
|
const k = `f4-${i}`;
|
|
3111
4392
|
const id = String(f.id || `fix#${i + 1}`);
|
|
3112
|
-
const rollbackText =
|
|
3113
|
-
|
|
3114
|
-
\`\`\`json
|
|
3115
|
-
${JSON.stringify({ id }, null, 2)}
|
|
3116
|
-
\`\`\``;
|
|
4393
|
+
const rollbackText = buildRollbackInstruction(id);
|
|
3117
4394
|
const added = typeof f.linesAdded === "number" ? f.linesAdded : 0;
|
|
3118
4395
|
const removed = typeof f.linesRemoved === "number" ? f.linesRemoved : 0;
|
|
3119
4396
|
return React.createElement(
|
|
@@ -3124,7 +4401,7 @@ ${JSON.stringify({ id }, null, 2)}
|
|
|
3124
4401
|
{ className: "iterate-obs-block-head" },
|
|
3125
4402
|
React.createElement("b", {}, id),
|
|
3126
4403
|
React.createElement("span", { className: "iterate-obs-file" }, String(f.file || "?")),
|
|
3127
|
-
React.createElement("span", { className: "iterate-obs-chip" }, f.success === true ? "\u6210\u529F" : "\u5931\u8D25"),
|
|
4404
|
+
React.createElement("span", { className: "iterate-obs-chip" }, f.success === true ? t("\u6210\u529F") : t("\u5931\u8D25")),
|
|
3128
4405
|
typeof f.round === "number" ? React.createElement("span", { className: "iterate-obs-head-meta" }, `Round ${f.round}`) : null
|
|
3129
4406
|
),
|
|
3130
4407
|
React.createElement(
|
|
@@ -3141,8 +4418,8 @@ ${JSON.stringify({ id }, null, 2)}
|
|
|
3141
4418
|
"data-danger": "",
|
|
3142
4419
|
"data-copied": copiedKey === `${k}-rb` ? "" : void 0,
|
|
3143
4420
|
onClick: () => copyInstruction(`${k}-rb`, rollbackText),
|
|
3144
|
-
title: "\u590D\u5236 iterate_rollback \u6307\u4EE4\u6587\u672C"
|
|
3145
|
-
}, copiedKey === `${k}-rb` ? "\u5DF2\u590D\u5236" : "\u56DE\u6EDA")
|
|
4421
|
+
title: t("\u590D\u5236 iterate_rollback \u6307\u4EE4\u6587\u672C")
|
|
4422
|
+
}, copiedKey === `${k}-rb` ? t("\u5DF2\u590D\u5236") : t("\u56DE\u6EDA"))
|
|
3146
4423
|
)
|
|
3147
4424
|
)
|
|
3148
4425
|
);
|
|
@@ -3151,22 +4428,14 @@ ${JSON.stringify({ id }, null, 2)}
|
|
|
3151
4428
|
const renderCheckpoint = () => {
|
|
3152
4429
|
const cp = manifest.checkpoint;
|
|
3153
4430
|
if (!cp) {
|
|
3154
|
-
return React.createElement(
|
|
4431
|
+
return React.createElement(
|
|
4432
|
+
"div",
|
|
4433
|
+
{ className: "iterate-obs-empty" },
|
|
4434
|
+
diskEmptyStateText(toolCalledInSession(session, "iterate_transcript"), t("\u65AD\u70B9"), getLang())
|
|
4435
|
+
);
|
|
3155
4436
|
}
|
|
3156
|
-
const resumeText =
|
|
3157
|
-
|
|
3158
|
-
\`\`\`json
|
|
3159
|
-
${JSON.stringify({
|
|
3160
|
-
operation: "resume",
|
|
3161
|
-
mode: String(cp.mode || "normal"),
|
|
3162
|
-
maxRounds: typeof cp.maxRounds === "number" ? cp.maxRounds : null
|
|
3163
|
-
}, null, 2)}
|
|
3164
|
-
\`\`\``;
|
|
3165
|
-
const clearText = `\u8BF7\u8C03\u7528 \`iterate_checkpoint\` \u6E05\u9664\u5F53\u524D\u65AD\u70B9\uFF1A
|
|
3166
|
-
|
|
3167
|
-
\`\`\`json
|
|
3168
|
-
${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
3169
|
-
\`\`\``;
|
|
4437
|
+
const resumeText = buildCheckpointResumeInstruction();
|
|
4438
|
+
const clearText = buildCheckpointClearInstruction();
|
|
3170
4439
|
const item = (label, value) => React.createElement("span", { className: "iterate-obs-chip" }, `${label} ${String(value ?? "?")}`);
|
|
3171
4440
|
return React.createElement(
|
|
3172
4441
|
"div",
|
|
@@ -3174,7 +4443,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3174
4443
|
React.createElement(
|
|
3175
4444
|
"div",
|
|
3176
4445
|
{ className: "iterate-obs-block-head" },
|
|
3177
|
-
React.createElement("b", {}, "\u65AD\u70B9"),
|
|
4446
|
+
React.createElement("b", {}, t("\u65AD\u70B9")),
|
|
3178
4447
|
React.createElement("span", { className: "iterate-obs-head-meta" }, String(cp.updatedAt || ""))
|
|
3179
4448
|
),
|
|
3180
4449
|
React.createElement(
|
|
@@ -3197,45 +4466,92 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3197
4466
|
"data-primary": "",
|
|
3198
4467
|
"data-copied": copiedKey === "cp-resume" ? "" : void 0,
|
|
3199
4468
|
onClick: () => copyInstruction("cp-resume", resumeText),
|
|
3200
|
-
title: "\u590D\u5236 iterate_checkpoint resume \u6307\u4EE4\u6587\u672C\uFF08\u52A0\u8F7D\u65AD\u70B9\u5E76\u8BA1\u6570\u4E00\u6B21\u6062\u590D\uFF09"
|
|
3201
|
-
}, copiedKey === "cp-resume" ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u6062\u590D\u6307\u4EE4"),
|
|
4469
|
+
title: t("\u590D\u5236 iterate_checkpoint resume \u6307\u4EE4\u6587\u672C\uFF08\u52A0\u8F7D\u65AD\u70B9\u5E76\u8BA1\u6570\u4E00\u6B21\u6062\u590D\uFF09")
|
|
4470
|
+
}, copiedKey === "cp-resume" ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u6062\u590D\u6307\u4EE4")),
|
|
3202
4471
|
React.createElement("button", {
|
|
3203
4472
|
className: "iterate-btn",
|
|
3204
4473
|
"data-danger": "",
|
|
3205
4474
|
"data-copied": copiedKey === "cp-clear" ? "" : void 0,
|
|
3206
4475
|
onClick: () => copyInstruction("cp-clear", clearText),
|
|
3207
|
-
title: "\u590D\u5236 iterate_checkpoint clear \u6307\u4EE4\u6587\u672C\uFF08\u6E05\u9664\u5F53\u524D\u65AD\u70B9\uFF0C\u4F9B\u91CD\u65B0\u5F00\u59CB\uFF09"
|
|
3208
|
-
}, copiedKey === "cp-clear" ? "\u5DF2\u590D\u5236" : "\u6E05\u9664\u65AD\u70B9")
|
|
4476
|
+
title: t("\u590D\u5236 iterate_checkpoint clear \u6307\u4EE4\u6587\u672C\uFF08\u6E05\u9664\u5F53\u524D\u65AD\u70B9\uFF0C\u4F9B\u91CD\u65B0\u5F00\u59CB\uFF09")
|
|
4477
|
+
}, copiedKey === "cp-clear" ? t("\u5DF2\u590D\u5236") : t("\u6E05\u9664\u65AD\u70B9"))
|
|
3209
4478
|
)
|
|
3210
4479
|
)
|
|
3211
4480
|
);
|
|
3212
4481
|
};
|
|
3213
4482
|
const renderConsole = () => {
|
|
3214
4483
|
const approval = manifest.approval || { policy: "ask" };
|
|
3215
|
-
const policyLabelMap = { ask: "\u8BE2\u95EE\u7528\u6237", deny: "\u62D2\u7EDD", allow: "\u76F4\u63A5\u6267\u884C" };
|
|
4484
|
+
const policyLabelMap = { ask: t("\u8BE2\u95EE\u7528\u6237"), deny: t("\u62D2\u7EDD"), allow: t("\u76F4\u63A5\u6267\u884C") };
|
|
3216
4485
|
const policyLabel = policyLabelMap[String(approval.policy || "ask")] || String(approval.policy || "ask");
|
|
3217
4486
|
const nudge = manifest.nudge || null;
|
|
3218
4487
|
const present = Boolean(nudge && nudge.text);
|
|
3219
4488
|
const activeNudgeText = nudge && nudge.text ? nudge.text : "";
|
|
3220
|
-
const nudgeInstruction = `\u8BF7\u8C03\u7528 \`iterate_transcript\` \u5199\u5165 nudge \u6307\u4EE4\uFF1A
|
|
4489
|
+
const nudgeInstruction = tf(`\u8BF7\u8C03\u7528 \`iterate_transcript\` \u5199\u5165 nudge \u6307\u4EE4\uFF1A
|
|
3221
4490
|
|
|
3222
4491
|
\`\`\`json
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
const activeClearInstruction = `\u8BF7\u8C03\u7528 \`iterate_transcript\` \u6E05\u9664\u5F53\u524D nudge\uFF1A
|
|
4492
|
+
{a0}
|
|
4493
|
+
\`\`\``, { a0: JSON.stringify({ operation: "nudge", text: nudgeText }, null, 2) });
|
|
4494
|
+
const activeClearInstruction = tf(`\u8BF7\u8C03\u7528 \`iterate_transcript\` \u6E05\u9664\u5F53\u524D nudge\uFF1A
|
|
3226
4495
|
|
|
3227
4496
|
\`\`\`json
|
|
3228
|
-
|
|
3229
|
-
|
|
4497
|
+
{a0}
|
|
4498
|
+
\`\`\``, { a0: JSON.stringify({ operation: "nudge", text: null }, null, 2) });
|
|
4499
|
+
const validations = Array.isArray(manifest.validations) ? manifest.validations : [];
|
|
4500
|
+
const abortedByValidation = String(manifest.stoppedReason || "") === "aborted_by_validation";
|
|
4501
|
+
const validationRows = validations.map((v, i) => {
|
|
4502
|
+
const allowed = v.allowed === true;
|
|
4503
|
+
const failed = !allowed;
|
|
4504
|
+
const highlight = failed && abortedByValidation;
|
|
4505
|
+
const exit = typeof v.exitCode === "number" ? `exit ${v.exitCode}` : "exit \u2014";
|
|
4506
|
+
return React.createElement(
|
|
4507
|
+
"div",
|
|
4508
|
+
{
|
|
4509
|
+
key: `val-${i}`,
|
|
4510
|
+
className: "iterate-obs-row",
|
|
4511
|
+
"data-status": allowed ? "pass" : "fail",
|
|
4512
|
+
"data-highlight": highlight ? "" : void 0,
|
|
4513
|
+
title: v.rejectReason ? String(v.rejectReason) : void 0
|
|
4514
|
+
},
|
|
4515
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, `Round ${typeof v.round === "number" ? v.round : "?"}`),
|
|
4516
|
+
React.createElement("code", { className: "iterate-obs-mono" }, String(v.command || "")),
|
|
4517
|
+
React.createElement(
|
|
4518
|
+
"span",
|
|
4519
|
+
{ className: "iterate-obs-chip", "data-status": allowed ? "pass" : "fail" },
|
|
4520
|
+
allowed ? t("\u901A\u8FC7") : t("\u5931\u8D25")
|
|
4521
|
+
),
|
|
4522
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, exit),
|
|
4523
|
+
highlight ? React.createElement("span", { className: "iterate-obs-chip", "data-status": "fail" }, t("\u5BFC\u81F4\u4E2D\u6B62")) : null
|
|
4524
|
+
);
|
|
4525
|
+
});
|
|
4526
|
+
const validationsBlock = React.createElement(
|
|
4527
|
+
"div",
|
|
4528
|
+
{ className: "iterate-obs-block", style: { marginTop: 8 } },
|
|
4529
|
+
React.createElement(
|
|
4530
|
+
"div",
|
|
4531
|
+
{ className: "iterate-obs-block-head" },
|
|
4532
|
+
React.createElement("span", {}, t("\u9A8C\u8BC1\u7ED3\u679C")),
|
|
4533
|
+
validations.length === 0 ? React.createElement("span", { className: "iterate-obs-head-meta" }, t("\u672C\u8F6E\u5C1A\u65E0\u9A8C\u8BC1\u8BB0\u5F55")) : React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`{a0} \u6761\u547D\u4EE4`, { a0: validations.length })),
|
|
4534
|
+
abortedByValidation ? React.createElement("span", { className: "iterate-obs-badge", "data-status": "fail" }, t("\u56E0\u9A8C\u8BC1\u5931\u8D25\u4E2D\u6B62")) : null
|
|
4535
|
+
),
|
|
4536
|
+
React.createElement(
|
|
4537
|
+
"div",
|
|
4538
|
+
{ className: "iterate-obs-block-body" },
|
|
4539
|
+
validations.length === 0 ? React.createElement(
|
|
4540
|
+
"div",
|
|
4541
|
+
{ className: "iterate-obs-empty" },
|
|
4542
|
+
t("\u5C1A\u672A\u62C9\u53D6\u9A8C\u8BC1\u7ED3\u679C\uFF1A\u6570\u636E\u5728\u78C1\u76D8 transcript \u91CC\uFF0C\u70B9\u300C\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u300D\u590D\u5236\u6307\u4EE4\u53D1\u56DE\u4F1A\u8BDD\u5373\u53EF\u56DE\u586B\u3002")
|
|
4543
|
+
) : React.createElement(React.Fragment, null, ...validationRows)
|
|
4544
|
+
)
|
|
4545
|
+
);
|
|
3230
4546
|
return React.createElement(
|
|
3231
4547
|
"div",
|
|
3232
4548
|
{ className: "iterate-obs-block" },
|
|
3233
4549
|
React.createElement(
|
|
3234
4550
|
"div",
|
|
3235
4551
|
{ className: "iterate-obs-block-head" },
|
|
3236
|
-
React.createElement("span", {}, "\u8FD0\u884C\u63A7\u5236\u53F0"),
|
|
4552
|
+
React.createElement("span", {}, t("\u8FD0\u884C\u63A7\u5236\u53F0")),
|
|
3237
4553
|
React.createElement("span", { className: "iterate-obs-badge", "data-live": live ? "" : void 0 }, runStatusText(live, manifest)),
|
|
3238
|
-
React.createElement("span", { className: "iterate-obs-head-meta" }, `\u6279\u51C6\u7B56\u7565\uFF1A
|
|
4554
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`\u6279\u51C6\u7B56\u7565\uFF1A{a0}`, { a0: policyLabel }))
|
|
3239
4555
|
),
|
|
3240
4556
|
React.createElement(
|
|
3241
4557
|
"div",
|
|
@@ -3243,21 +4559,21 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3243
4559
|
present ? React.createElement(
|
|
3244
4560
|
"div",
|
|
3245
4561
|
{ className: "iterate-obs-bar", style: { marginBottom: 6 } },
|
|
3246
|
-
React.createElement("b", {}, "\u5F53\u524D nudge"),
|
|
4562
|
+
React.createElement("b", {}, t("\u5F53\u524D nudge")),
|
|
3247
4563
|
React.createElement("span", { className: "iterate-obs-msg" }, activeNudgeText),
|
|
3248
4564
|
React.createElement("button", {
|
|
3249
4565
|
className: "iterate-btn",
|
|
3250
4566
|
"data-copied": copiedKey === "nudge-clear" ? "" : void 0,
|
|
3251
4567
|
onClick: () => copyInstruction("nudge-clear", activeClearInstruction),
|
|
3252
|
-
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"
|
|
3253
|
-
}, copiedKey === "nudge-clear" ? "\u5DF2\u590D\u5236\u6E05\u9664\u6307\u4EE4" : "\u590D\u5236\u6E05\u9664\u6307\u4EE4")
|
|
4568
|
+
title: t("\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")
|
|
4569
|
+
}, copiedKey === "nudge-clear" ? t("\u5DF2\u590D\u5236\u6E05\u9664\u6307\u4EE4") : t("\u590D\u5236\u6E05\u9664\u6307\u4EE4"))
|
|
3254
4570
|
) : null,
|
|
3255
4571
|
React.createElement("textarea", {
|
|
3256
4572
|
className: "iterate-obs-input iterate-obs-mono",
|
|
3257
4573
|
rows: 3,
|
|
3258
|
-
placeholder: "\u8D77\u8349\u4E00\u6761 nudge \u65B9\u5411\u6307\u4EE4\u2026",
|
|
4574
|
+
placeholder: t("\u8D77\u8349\u4E00\u6761 nudge \u65B9\u5411\u6307\u4EE4\u2026"),
|
|
3259
4575
|
value: nudgeText,
|
|
3260
|
-
"aria-label": "nudge \u8349\u7A3F",
|
|
4576
|
+
"aria-label": t("nudge \u8349\u7A3F"),
|
|
3261
4577
|
onChange: (e) => setNudgeText(e.target.value)
|
|
3262
4578
|
}),
|
|
3263
4579
|
React.createElement(
|
|
@@ -3269,20 +4585,21 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3269
4585
|
"data-copied": copiedKey === "nudge" ? "" : void 0,
|
|
3270
4586
|
disabled: !nudgeText,
|
|
3271
4587
|
onClick: () => copyInstruction("nudge", nudgeInstruction),
|
|
3272
|
-
title: "\u590D\u5236 iterate_transcript nudge \u6307\u4EE4\u6587\u672C"
|
|
3273
|
-
}, copiedKey === "nudge" ? "\u5DF2\u590D\u5236" : "\u590D\u5236 nudge \u6307\u4EE4")
|
|
3274
|
-
)
|
|
4588
|
+
title: t("\u590D\u5236 iterate_transcript nudge \u6307\u4EE4\u6587\u672C")
|
|
4589
|
+
}, copiedKey === "nudge" ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236 nudge \u6307\u4EE4"))
|
|
4590
|
+
),
|
|
4591
|
+
validationsBlock
|
|
3275
4592
|
)
|
|
3276
4593
|
);
|
|
3277
4594
|
};
|
|
3278
4595
|
const renderTimeline = () => {
|
|
3279
4596
|
const entries = manifest.timeline || [];
|
|
3280
4597
|
if (entries.length === 0) {
|
|
3281
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u65F6\u95F4\u7EBF\u6761\u76EE");
|
|
4598
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u65F6\u95F4\u7EBF\u6761\u76EE"));
|
|
3282
4599
|
}
|
|
3283
|
-
const types = Array.from(new Set(entries.map((
|
|
4600
|
+
const types = Array.from(new Set(entries.map((t2) => String(t2.type || "unknown")).filter(Boolean))).sort();
|
|
3284
4601
|
const rounds = Array.from(
|
|
3285
|
-
new Set(entries.map((
|
|
4602
|
+
new Set(entries.map((t2) => typeof t2.round === "number" ? String(t2.round) : "").filter(Boolean))
|
|
3286
4603
|
).sort((a, b) => Number(a) - Number(b));
|
|
3287
4604
|
const filtered = filterTimelineEntries(entries, {
|
|
3288
4605
|
type: timelineType,
|
|
@@ -3296,14 +4613,14 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3296
4613
|
setTimelineSearch("");
|
|
3297
4614
|
};
|
|
3298
4615
|
const sorted = filtered.slice();
|
|
3299
|
-
const rows = sorted.map((
|
|
4616
|
+
const rows = sorted.map((t2, i) => {
|
|
3300
4617
|
const k = `f7-${i}`;
|
|
3301
4618
|
let dataText = "";
|
|
3302
|
-
if (
|
|
4619
|
+
if (t2.data && typeof t2.data === "object") {
|
|
3303
4620
|
try {
|
|
3304
|
-
dataText = JSON.stringify(
|
|
4621
|
+
dataText = JSON.stringify(t2.data);
|
|
3305
4622
|
} catch {
|
|
3306
|
-
dataText = String(
|
|
4623
|
+
dataText = String(t2.data);
|
|
3307
4624
|
}
|
|
3308
4625
|
}
|
|
3309
4626
|
return React.createElement(
|
|
@@ -3312,11 +4629,11 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3312
4629
|
React.createElement(
|
|
3313
4630
|
"div",
|
|
3314
4631
|
{ className: "iterate-obs-bar", style: { width: "100%" } },
|
|
3315
|
-
React.createElement("span", { className: "iterate-obs-chip" }, String(
|
|
4632
|
+
React.createElement("span", { className: "iterate-obs-chip" }, String(t2.type || "?")),
|
|
3316
4633
|
React.createElement(
|
|
3317
4634
|
"span",
|
|
3318
4635
|
{ className: "iterate-obs-head-meta" },
|
|
3319
|
-
`${typeof
|
|
4636
|
+
`${typeof t2.round === "number" ? `Round ${t2.round} \xB7 ` : ""}${String(t2.timestamp || "")}`
|
|
3320
4637
|
)
|
|
3321
4638
|
),
|
|
3322
4639
|
dataText ? React.createElement("div", { className: "iterate-obs-code" }, dataText) : null
|
|
@@ -3325,42 +4642,42 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3325
4642
|
const filterBar = React.createElement(
|
|
3326
4643
|
"div",
|
|
3327
4644
|
{ className: "iterate-obs-bar", style: { marginBottom: 8 } },
|
|
3328
|
-
React.createElement("b", {}, "\u65F6\u95F4\u7EBF"),
|
|
4645
|
+
React.createElement("b", {}, t("\u65F6\u95F4\u7EBF")),
|
|
3329
4646
|
React.createElement(
|
|
3330
4647
|
"select",
|
|
3331
4648
|
{
|
|
3332
4649
|
className: "iterate-filter-select",
|
|
3333
4650
|
value: timelineType,
|
|
3334
|
-
"aria-label": "\u6309\u65F6\u95F4\u7EBF\u7C7B\u578B\u7B5B\u9009",
|
|
4651
|
+
"aria-label": t("\u6309\u65F6\u95F4\u7EBF\u7C7B\u578B\u7B5B\u9009"),
|
|
3335
4652
|
onChange: (e) => setTimelineType(e.target.value)
|
|
3336
4653
|
},
|
|
3337
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u7C7B\u578B"),
|
|
3338
|
-
...types.map((
|
|
4654
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u7C7B\u578B")),
|
|
4655
|
+
...types.map((t2) => React.createElement("option", { key: t2, value: t2 }, t2))
|
|
3339
4656
|
),
|
|
3340
4657
|
React.createElement(
|
|
3341
4658
|
"select",
|
|
3342
4659
|
{
|
|
3343
4660
|
className: "iterate-filter-select",
|
|
3344
4661
|
value: timelineRound,
|
|
3345
|
-
"aria-label": "\u6309\u8F6E\u6B21\u7B5B\u9009",
|
|
4662
|
+
"aria-label": t("\u6309\u8F6E\u6B21\u7B5B\u9009"),
|
|
3346
4663
|
onChange: (e) => setTimelineRound(e.target.value)
|
|
3347
4664
|
},
|
|
3348
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u8F6E\u6B21"),
|
|
4665
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u8F6E\u6B21")),
|
|
3349
4666
|
...rounds.map((r) => React.createElement("option", { key: r, value: r }, `Round ${r}`))
|
|
3350
4667
|
),
|
|
3351
4668
|
React.createElement("input", {
|
|
3352
4669
|
className: "iterate-filter-search",
|
|
3353
4670
|
type: "search",
|
|
3354
|
-
placeholder: "\u641C\u7D22\u65F6\u95F4\u7EBF\u2026",
|
|
3355
|
-
"aria-label": "\u641C\u7D22\u65F6\u95F4\u7EBF",
|
|
4671
|
+
placeholder: t("\u641C\u7D22\u65F6\u95F4\u7EBF\u2026"),
|
|
4672
|
+
"aria-label": t("\u641C\u7D22\u65F6\u95F4\u7EBF"),
|
|
3356
4673
|
value: timelineSearch,
|
|
3357
4674
|
onChange: (e) => setTimelineSearch(e.target.value)
|
|
3358
4675
|
}),
|
|
3359
4676
|
filterActive ? React.createElement("button", {
|
|
3360
4677
|
className: "iterate-filter-clear",
|
|
3361
4678
|
onClick: clearTimelineFilter,
|
|
3362
|
-
title: "\u6E05\u9664\u5168\u90E8\u7B5B\u9009"
|
|
3363
|
-
}, "\u6E05\u9664") : null,
|
|
4679
|
+
title: t("\u6E05\u9664\u5168\u90E8\u7B5B\u9009")
|
|
4680
|
+
}, t("\u6E05\u9664")) : null,
|
|
3364
4681
|
React.createElement("span", { className: "iterate-filter-count" }, `${filtered.length}/${entries.length}`)
|
|
3365
4682
|
);
|
|
3366
4683
|
if (rows.length === 0) {
|
|
@@ -3368,42 +4685,47 @@ ${JSON.stringify({ operation: "nudge", text: null }, null, 2)}
|
|
|
3368
4685
|
"div",
|
|
3369
4686
|
{},
|
|
3370
4687
|
filterBar,
|
|
3371
|
-
React.createElement("div", { className: "iterate-obs-empty" }, "\u65E0\u5339\u914D\u7684\u65F6\u95F4\u7EBF\u6761\u76EE")
|
|
4688
|
+
React.createElement("div", { className: "iterate-obs-empty" }, t("\u65E0\u5339\u914D\u7684\u65F6\u95F4\u7EBF\u6761\u76EE"))
|
|
3372
4689
|
);
|
|
3373
4690
|
}
|
|
3374
4691
|
return React.createElement("div", {}, filterBar, ...rows);
|
|
3375
4692
|
};
|
|
3376
4693
|
const renderQualityGate = () => {
|
|
3377
|
-
const gateInstruction =
|
|
3378
|
-
const gateClearInstruction =
|
|
3379
|
-
|
|
3380
|
-
\`\`\`json
|
|
3381
|
-
${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
3382
|
-
\`\`\``;
|
|
4694
|
+
const gateInstruction = buildQualityGateQueryInstruction();
|
|
4695
|
+
const gateClearInstruction = buildQualityGateClearInstruction();
|
|
3383
4696
|
const gate = qualityGate;
|
|
3384
4697
|
const dims = gate && gate.dimensions ? gate.dimensions : [];
|
|
3385
4698
|
const hasGate = gate !== null && Boolean(gate.overallStatus || gate.overallScore != null || dims.length > 0);
|
|
3386
4699
|
const status = gate && gate.overallStatus ? String(gate.overallStatus) : "pending";
|
|
3387
4700
|
const statusLabel = status === "pass" ? "PASS" : status === "fail" ? "FAIL" : "PENDING";
|
|
3388
4701
|
const num = (v) => Number(v != null ? v : 0);
|
|
4702
|
+
const pulled = toolCalledInSession(session, "iterate_quality_gate");
|
|
3389
4703
|
const headerBar = React.createElement(
|
|
3390
4704
|
"div",
|
|
3391
4705
|
{ className: "iterate-obs-bar", style: { marginBottom: 8 } },
|
|
3392
|
-
React.createElement("b", {}, "\u8D28\u91CF\u95E8\u7981"),
|
|
4706
|
+
React.createElement("b", {}, t("\u8D28\u91CF\u95E8\u7981")),
|
|
3393
4707
|
React.createElement("button", {
|
|
3394
4708
|
className: "iterate-btn",
|
|
3395
4709
|
"data-primary": "",
|
|
3396
4710
|
"data-copied": copiedKey === "qgate" ? "" : void 0,
|
|
3397
4711
|
onClick: () => copyInstruction("qgate", gateInstruction),
|
|
3398
|
-
title: "\u590D\u5236 iterate_quality_gate \u67E5\u8BE2\u6307\u4EE4"
|
|
3399
|
-
}, copiedKey === "qgate" ? "\u5DF2\u590D\u5236" : "\u67E5\u8BE2\u95E8\u7981"),
|
|
4712
|
+
title: t("\u590D\u5236 iterate_quality_gate \u67E5\u8BE2\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09")
|
|
4713
|
+
}, copiedKey === "qgate" ? t("\u5DF2\u590D\u5236") : t("\u67E5\u8BE2\u95E8\u7981")),
|
|
3400
4714
|
React.createElement("button", {
|
|
3401
4715
|
className: "iterate-btn",
|
|
3402
4716
|
"data-danger": "",
|
|
3403
4717
|
"data-copied": copiedKey === "qgate-clear" ? "" : void 0,
|
|
3404
4718
|
onClick: () => copyInstruction("qgate-clear", gateClearInstruction),
|
|
3405
|
-
title: "\u590D\u5236 iterate_quality_gate clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u7684 FAIL \u8BC1\u4E66\uFF09"
|
|
3406
|
-
}, copiedKey === "qgate-clear" ? "\u5DF2\u590D\u5236" : "\u6E05\u9664\u95E8\u7981")
|
|
4719
|
+
title: t("\u590D\u5236 iterate_quality_gate clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u7684 FAIL \u8BC1\u4E66\uFF09")
|
|
4720
|
+
}, copiedKey === "qgate-clear" ? t("\u5DF2\u590D\u5236") : t("\u6E05\u9664\u95E8\u7981")),
|
|
4721
|
+
// Gap #4: the browser cannot read `.iterate/` directly — one click
|
|
4722
|
+
// copies the pull instruction that makes the model echo it into the stream.
|
|
4723
|
+
React.createElement("button", {
|
|
4724
|
+
className: "iterate-btn",
|
|
4725
|
+
"data-copied": copiedKey === "disk-snapshot" ? "" : void 0,
|
|
4726
|
+
onClick: () => copyInstruction("disk-snapshot", pullInstruction),
|
|
4727
|
+
title: t("\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u8BA9\u6A21\u578B\u56DE\u663E .iterate/ \u4E0B\u7684\u95E8\u7981\u3001\u7ECF\u9A8C\u3001\u9632\u5FA1\u4E8B\u4EF6\u4E0E transcript")
|
|
4728
|
+
}, copiedKey === "disk-snapshot" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"))
|
|
3407
4729
|
);
|
|
3408
4730
|
if (!hasGate) {
|
|
3409
4731
|
return React.createElement(
|
|
@@ -3416,13 +4738,24 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3416
4738
|
React.createElement(
|
|
3417
4739
|
"div",
|
|
3418
4740
|
{ className: "iterate-obs-block-head" },
|
|
3419
|
-
React.createElement("span", {}, "\u8D28\u91CF\u95E8\u7981\u89C6\u56FE"),
|
|
4741
|
+
React.createElement("span", {}, t("\u8D28\u91CF\u95E8\u7981\u89C6\u56FE")),
|
|
3420
4742
|
React.createElement("span", { className: "iterate-obs-badge" }, "v3.0")
|
|
3421
4743
|
),
|
|
3422
4744
|
React.createElement(
|
|
3423
4745
|
"div",
|
|
3424
4746
|
{ className: "iterate-obs-block-body" },
|
|
3425
|
-
React.createElement(
|
|
4747
|
+
React.createElement(
|
|
4748
|
+
"div",
|
|
4749
|
+
{ className: "iterate-obs-msg" },
|
|
4750
|
+
// Gap #4: distinguish "we pulled it and it is empty" from "we
|
|
4751
|
+
// never asked the model to read the disk".
|
|
4752
|
+
diskEmptyStateText(pulled, t("\u8D28\u91CF\u95E8\u7981\u8BC1\u4E66"), getLang())
|
|
4753
|
+
),
|
|
4754
|
+
React.createElement(
|
|
4755
|
+
"div",
|
|
4756
|
+
{ className: "iterate-obs-msg", style: { marginTop: 6 } },
|
|
4757
|
+
t("\u8D28\u91CF\u95E8\u7981\u663E\u793A\u5404\u7EF4\u5EA6\u6536\u655B\u5EA6\u3001\u9A8C\u8BC1\u901A\u8FC7\u7387\u548C\u6574\u4F53 PASS/FAIL \u72B6\u6001\u3002")
|
|
4758
|
+
),
|
|
3426
4759
|
React.createElement(
|
|
3427
4760
|
"div",
|
|
3428
4761
|
{ className: "iterate-obs-bar", style: { marginTop: 8 } },
|
|
@@ -3430,8 +4763,14 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3430
4763
|
className: "iterate-btn",
|
|
3431
4764
|
"data-copied": copiedKey === "qgate-instr" ? "" : void 0,
|
|
3432
4765
|
onClick: () => copyInstruction("qgate-instr", gateInstruction),
|
|
3433
|
-
title: "\u590D\u5236\u67E5\u8BE2\u6307\u4EE4"
|
|
3434
|
-
}, copiedKey === "qgate-instr" ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u67E5\u8BE2\u6307\u4EE4")
|
|
4766
|
+
title: t("\u590D\u5236\u67E5\u8BE2\u6307\u4EE4")
|
|
4767
|
+
}, copiedKey === "qgate-instr" ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u67E5\u8BE2\u6307\u4EE4")),
|
|
4768
|
+
React.createElement("button", {
|
|
4769
|
+
className: "iterate-btn",
|
|
4770
|
+
"data-copied": copiedKey === "disk-snapshot-2" ? "" : void 0,
|
|
4771
|
+
onClick: () => copyInstruction("disk-snapshot-2", pullInstruction),
|
|
4772
|
+
title: t("\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4")
|
|
4773
|
+
}, copiedKey === "disk-snapshot-2" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"))
|
|
3435
4774
|
)
|
|
3436
4775
|
)
|
|
3437
4776
|
)
|
|
@@ -3451,7 +4790,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3451
4790
|
React.createElement(
|
|
3452
4791
|
"span",
|
|
3453
4792
|
{ className: "iterate-obs-head-meta" },
|
|
3454
|
-
`\u6574\u4F53\u8BC4\u5206
|
|
4793
|
+
tf(`\u6574\u4F53\u8BC4\u5206 {a0} \xB7 \u9A8C\u8BC1 {a1}/{a2} \xB7 \u901A\u8FC7\u7387 {a3}%`, { a0: num(gate && gate.overallScore), a1: num(gate && gate.passedChecks), a2: num(gate && gate.totalChecks), a3: num(gate && gate.verificationPassRate) })
|
|
3455
4794
|
)
|
|
3456
4795
|
),
|
|
3457
4796
|
React.createElement(
|
|
@@ -3464,7 +4803,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3464
4803
|
(d, i) => React.createElement(
|
|
3465
4804
|
"span",
|
|
3466
4805
|
{ key: `gdim-${i}`, className: "iterate-obs-chip" },
|
|
3467
|
-
|
|
4806
|
+
tf(`{a0} \xB7 \u6536\u655B {a1}% \xB7 \u8BC4\u5206 {a2}`, { a0: String(d.dimension || "?"), a1: num(d.convergenceRate), a2: num(d.score) })
|
|
3468
4807
|
)
|
|
3469
4808
|
)
|
|
3470
4809
|
) : null,
|
|
@@ -3486,32 +4825,33 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3486
4825
|
style: { width: `${fill}%` }
|
|
3487
4826
|
})
|
|
3488
4827
|
),
|
|
3489
|
-
React.createElement("span", {}, `\u8BC4\u5206
|
|
4828
|
+
React.createElement("span", {}, tf(`\u8BC4\u5206 {a0} \xB7 \u53D1\u73B0 {a1} \xB7 \u5DF2\u4FEE\u590D {a2}`, { a0: num(d.score), a1: num(d.findingsCount), a2: num(d.fixedCount) }))
|
|
3490
4829
|
);
|
|
3491
4830
|
})
|
|
3492
4831
|
),
|
|
3493
|
-
gate && gate.failReason ? React.createElement("div", { className: "iterate-obs-msg", style: { marginTop: 6 } }, `\u5931\u8D25\u539F\u56E0\uFF1A
|
|
4832
|
+
gate && gate.failReason ? React.createElement("div", { className: "iterate-obs-msg", style: { marginTop: 6 } }, tf(`\u5931\u8D25\u539F\u56E0\uFF1A{a0}`, { a0: String(gate.failReason) })) : null
|
|
3494
4833
|
)
|
|
3495
4834
|
)
|
|
3496
4835
|
);
|
|
3497
4836
|
};
|
|
3498
4837
|
const renderExperienceBank = () => {
|
|
3499
|
-
const expInstruction = expSearch ? `\u8BF7\u8C03\u7528 \`iterate_experience\` \u641C\u7D22\u7ECF\u9A8C\uFF1A
|
|
4838
|
+
const expInstruction = expSearch ? tf(`\u8BF7\u8C03\u7528 \`iterate_experience\` \u641C\u7D22\u7ECF\u9A8C\uFF1A
|
|
3500
4839
|
|
|
3501
4840
|
\`\`\`json
|
|
3502
|
-
|
|
3503
|
-
|
|
4841
|
+
{a0}
|
|
4842
|
+
\`\`\``, { a0: JSON.stringify({ operation: "search", query: expSearch }, null, 2) }) : buildExperienceListInstruction();
|
|
3504
4843
|
const expEntries = experienceBank && experienceBank.entries ? experienceBank.entries : [];
|
|
3505
4844
|
const query = expSearch.trim().toLowerCase();
|
|
3506
4845
|
const visible = query ? expEntries.filter(
|
|
3507
|
-
(e) => [e.pattern, e.description, e.id, e.dimension, e.severity, ...e.tags || []].filter(Boolean).some((
|
|
4846
|
+
(e) => [e.pattern, e.description, e.id, e.dimension, e.severity, ...e.tags || []].filter(Boolean).some((t2) => String(t2).toLowerCase().includes(query))
|
|
3508
4847
|
) : expEntries;
|
|
4848
|
+
const pulled = toolCalledInSession(session, "iterate_experience");
|
|
3509
4849
|
const resultRows = visible.map((e, i) => {
|
|
3510
|
-
const adoptInstruction = `\u8BF7\u8C03\u7528 \`iterate_experience\` \u91C7\u7EB3\u7ECF\u9A8C\u5E76\u5E94\u7528\u5DF2\u9A8C\u8BC1\u4FEE\u6CD5\uFF1A
|
|
4850
|
+
const adoptInstruction = tf(`\u8BF7\u8C03\u7528 \`iterate_experience\` \u91C7\u7EB3\u7ECF\u9A8C\u5E76\u5E94\u7528\u5DF2\u9A8C\u8BC1\u4FEE\u6CD5\uFF1A
|
|
3511
4851
|
|
|
3512
4852
|
\`\`\`json
|
|
3513
|
-
|
|
3514
|
-
|
|
4853
|
+
{a0}
|
|
4854
|
+
\`\`\``, { a0: JSON.stringify({ operation: "get", id: e.id }, null, 2) });
|
|
3515
4855
|
return React.createElement(
|
|
3516
4856
|
"div",
|
|
3517
4857
|
{ key: `exp-${i}`, className: "iterate-obs-block" },
|
|
@@ -3520,7 +4860,7 @@ ${JSON.stringify({ operation: "get", id: e.id }, null, 2)}
|
|
|
3520
4860
|
{ className: "iterate-obs-block-head" },
|
|
3521
4861
|
React.createElement("span", { className: "iterate-obs-chip" }, String(e.dimension || "")),
|
|
3522
4862
|
React.createElement("span", {}, String(e.pattern || "?")),
|
|
3523
|
-
React.createElement("span", { className: "iterate-exp-hit" }, `\u547D\u4E2D
|
|
4863
|
+
React.createElement("span", { className: "iterate-exp-hit" }, tf(`\u547D\u4E2D {a0} \u6B21`, { a0: e.hitCount ?? 0 }))
|
|
3524
4864
|
),
|
|
3525
4865
|
React.createElement(
|
|
3526
4866
|
"div",
|
|
@@ -3534,8 +4874,8 @@ ${JSON.stringify({ operation: "get", id: e.id }, null, 2)}
|
|
|
3534
4874
|
"data-primary": "",
|
|
3535
4875
|
"data-copied": copiedKey === `exp-adopt-${i}` ? "" : void 0,
|
|
3536
4876
|
onClick: () => copyInstruction(`exp-adopt-${i}`, adoptInstruction),
|
|
3537
|
-
title: "\u590D\u5236\u91C7\u7EB3\u6307\u4EE4\uFF08\u67E5\u8BE2\u8BE5\u7ECF\u9A8C\u5E76\u5E94\u7528\u5230\u5F53\u524D\u4E0A\u4E0B\u6587\uFF09"
|
|
3538
|
-
}, copiedKey === `exp-adopt-${i}` ? "\u5DF2\u590D\u5236" : "\u91C7\u7EB3") : null
|
|
4877
|
+
title: t("\u590D\u5236\u91C7\u7EB3\u6307\u4EE4\uFF08\u67E5\u8BE2\u8BE5\u7ECF\u9A8C\u5E76\u5E94\u7528\u5230\u5F53\u524D\u4E0A\u4E0B\u6587\uFF09")
|
|
4878
|
+
}, copiedKey === `exp-adopt-${i}` ? t("\u5DF2\u590D\u5236") : t("\u91C7\u7EB3")) : null
|
|
3539
4879
|
)
|
|
3540
4880
|
)
|
|
3541
4881
|
);
|
|
@@ -3546,15 +4886,21 @@ ${JSON.stringify({ operation: "get", id: e.id }, null, 2)}
|
|
|
3546
4886
|
React.createElement(
|
|
3547
4887
|
"div",
|
|
3548
4888
|
{ className: "iterate-obs-bar", style: { marginBottom: 8 } },
|
|
3549
|
-
React.createElement("b", {}, "\u7ECF\u9A8C\u94F6\u884C"),
|
|
3550
|
-
React.createElement("span", { className: "iterate-obs-head-meta" }, `\u4F1A\u8BDD\u5185
|
|
4889
|
+
React.createElement("b", {}, t("\u7ECF\u9A8C\u94F6\u884C")),
|
|
4890
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`\u4F1A\u8BDD\u5185 {a0} \u6761`, { a0: expEntries.length })),
|
|
3551
4891
|
React.createElement("button", {
|
|
3552
4892
|
className: "iterate-btn",
|
|
3553
4893
|
"data-primary": "",
|
|
3554
4894
|
"data-copied": copiedKey === "exp-list" ? "" : void 0,
|
|
3555
|
-
onClick: () => copyInstruction("exp-list",
|
|
3556
|
-
title: "\u590D\u5236\u5217\u51FA\u7ECF\u9A8C\u6307\u4EE4"
|
|
3557
|
-
}, copiedKey === "exp-list" ? "\u5DF2\u590D\u5236" : "\u5217\u51FA\u7ECF\u9A8C")
|
|
4895
|
+
onClick: () => copyInstruction("exp-list", buildExperienceListInstruction()),
|
|
4896
|
+
title: t("\u590D\u5236\u5217\u51FA\u7ECF\u9A8C\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09")
|
|
4897
|
+
}, copiedKey === "exp-list" ? t("\u5DF2\u590D\u5236") : t("\u5217\u51FA\u7ECF\u9A8C")),
|
|
4898
|
+
React.createElement("button", {
|
|
4899
|
+
className: "iterate-btn",
|
|
4900
|
+
"data-copied": copiedKey === "exp-snapshot" ? "" : void 0,
|
|
4901
|
+
onClick: () => copyInstruction("exp-snapshot", pullInstruction),
|
|
4902
|
+
title: t("\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u628A .iterate/ \u4E0B\u7684\u7ECF\u9A8C\u94F6\u884C\u7B49\u4EA7\u7269\u56DE\u663E\u8FDB\u4F1A\u8BDD")
|
|
4903
|
+
}, copiedKey === "exp-snapshot" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"))
|
|
3558
4904
|
),
|
|
3559
4905
|
React.createElement(
|
|
3560
4906
|
"div",
|
|
@@ -3562,8 +4908,8 @@ ${JSON.stringify({ operation: "get", id: e.id }, null, 2)}
|
|
|
3562
4908
|
React.createElement("input", {
|
|
3563
4909
|
className: "iterate-filter-search",
|
|
3564
4910
|
type: "search",
|
|
3565
|
-
placeholder: "\u641C\u7D22\u7ECF\u9A8C\u6A21\u5F0F\u2026",
|
|
3566
|
-
"aria-label": "\u641C\u7D22\u7ECF\u9A8C",
|
|
4911
|
+
placeholder: t("\u641C\u7D22\u7ECF\u9A8C\u6A21\u5F0F\u2026"),
|
|
4912
|
+
"aria-label": t("\u641C\u7D22\u7ECF\u9A8C"),
|
|
3567
4913
|
value: expSearch,
|
|
3568
4914
|
onChange: (e) => setExpSearch(e.target.value)
|
|
3569
4915
|
}),
|
|
@@ -3572,30 +4918,36 @@ ${JSON.stringify({ operation: "get", id: e.id }, null, 2)}
|
|
|
3572
4918
|
"data-copied": copiedKey === "exp-search" ? "" : void 0,
|
|
3573
4919
|
disabled: !expSearch,
|
|
3574
4920
|
onClick: () => copyInstruction("exp-search", expInstruction),
|
|
3575
|
-
title: "\u590D\u5236\u641C\u7D22\u6307\u4EE4"
|
|
3576
|
-
}, copiedKey === "exp-search" ? "\u5DF2\u590D\u5236" : "\u641C\u7D22")
|
|
4921
|
+
title: t("\u590D\u5236\u641C\u7D22\u6307\u4EE4")
|
|
4922
|
+
}, copiedKey === "exp-search" ? t("\u5DF2\u590D\u5236") : t("\u641C\u7D22"))
|
|
3577
4923
|
),
|
|
3578
|
-
resultRows.length > 0 ? React.createElement("div", {}, ...resultRows) : expEntries.length > 0 ? React.createElement("div", { className: "iterate-obs-empty" }, "\u6CA1\u6709\u5339\u914D\u300C" + expSearch + "\u300D\u7684\u7ECF\u9A8C") : React.createElement(
|
|
4924
|
+
resultRows.length > 0 ? React.createElement("div", {}, ...resultRows) : expEntries.length > 0 ? React.createElement("div", { className: "iterate-obs-empty" }, t("\u6CA1\u6709\u5339\u914D\u300C") + expSearch + t("\u300D\u7684\u7ECF\u9A8C")) : React.createElement(
|
|
4925
|
+
"div",
|
|
4926
|
+
{},
|
|
4927
|
+
// Gap #4: separate "pulled and empty" from "never pulled".
|
|
4928
|
+
React.createElement("div", { className: "iterate-obs-empty" }, diskEmptyStateText(pulled, t("\u7ECF\u9A8C\u94F6\u884C"), getLang())),
|
|
4929
|
+
React.createElement("div", { className: "iterate-obs-empty" }, t("\u4E5F\u53EF\u70B9\u300C\u5217\u51FA\u7ECF\u9A8C\u300D\u67E5\u8BE2\u78C1\u76D8\u4E0A\u7684\u5168\u90E8\u5386\u53F2\u7ECF\u9A8C\u3002"))
|
|
4930
|
+
)
|
|
3579
4931
|
);
|
|
3580
4932
|
};
|
|
3581
4933
|
const renderDefenseEvents = () => {
|
|
3582
|
-
const defenseInstruction = defenseFilter ? `\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u67E5\u8BE2\u9632\u5FA1\u4E8B\u4EF6\uFF1A
|
|
4934
|
+
const defenseInstruction = defenseFilter ? tf(`\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u67E5\u8BE2\u9632\u5FA1\u4E8B\u4EF6\uFF1A
|
|
3583
4935
|
|
|
3584
4936
|
\`\`\`json
|
|
3585
|
-
|
|
3586
|
-
|
|
4937
|
+
{a0}
|
|
4938
|
+
\`\`\``, { a0: JSON.stringify({ operation: "list", type: defenseFilter || void 0 }, null, 2) }) : buildDefenseEventsListInstruction();
|
|
3587
4939
|
const typeOptions = [
|
|
3588
|
-
{ value: "", label: "\u5168\u90E8\u7C7B\u578B" },
|
|
3589
|
-
{ value: "precondition_failed", label: "\u524D\u7F6E\u6821\u9A8C\u5931\u8D25" },
|
|
3590
|
-
{ value: "rollback", label: "\u56DE\u6EDA" },
|
|
3591
|
-
{ value: "invariant_violated", label: "\u4E0D\u53D8\u91CF\u8FDD\u53CD" },
|
|
3592
|
-
{ value: "assumption_falsified", label: "\u5047\u8BBE\u88AB\u8BC1\u4F2A" }
|
|
4940
|
+
{ value: "", label: t("\u5168\u90E8\u7C7B\u578B") },
|
|
4941
|
+
{ value: "precondition_failed", label: t("\u524D\u7F6E\u6821\u9A8C\u5931\u8D25") },
|
|
4942
|
+
{ value: "rollback", label: t("\u56DE\u6EDA") },
|
|
4943
|
+
{ value: "invariant_violated", label: t("\u4E0D\u53D8\u91CF\u8FDD\u53CD") },
|
|
4944
|
+
{ value: "assumption_falsified", label: t("\u5047\u8BBE\u88AB\u8BC1\u4F2A") }
|
|
3593
4945
|
];
|
|
3594
4946
|
const TYPE_LABEL = {
|
|
3595
|
-
precondition_failed: "\u524D\u7F6E\u6821\u9A8C\u5931\u8D25",
|
|
3596
|
-
rollback: "\u56DE\u6EDA",
|
|
3597
|
-
invariant_violated: "\u4E0D\u53D8\u91CF\u8FDD\u53CD",
|
|
3598
|
-
assumption_falsified: "\u5047\u8BBE\u88AB\u8BC1\u4F2A"
|
|
4947
|
+
precondition_failed: t("\u524D\u7F6E\u6821\u9A8C\u5931\u8D25"),
|
|
4948
|
+
rollback: t("\u56DE\u6EDA"),
|
|
4949
|
+
invariant_violated: t("\u4E0D\u53D8\u91CF\u8FDD\u53CD"),
|
|
4950
|
+
assumption_falsified: t("\u5047\u8BBE\u88AB\u8BC1\u4F2A")
|
|
3599
4951
|
};
|
|
3600
4952
|
const counts = defenseEvents && defenseEvents.counts || {};
|
|
3601
4953
|
const allEvents = defenseEvents && defenseEvents.events ? defenseEvents.events : [];
|
|
@@ -3604,32 +4956,42 @@ ${JSON.stringify({ operation: "list", type: defenseFilter || void 0 }, null, 2)}
|
|
|
3604
4956
|
const headerBar = React.createElement(
|
|
3605
4957
|
"div",
|
|
3606
4958
|
{ className: "iterate-obs-bar", style: { marginBottom: 8 } },
|
|
3607
|
-
React.createElement("b", {}, "\u9632\u5FA1\u4E8B\u4EF6"),
|
|
3608
|
-
React.createElement("span", { className: "iterate-obs-head-meta" }, `\u4F1A\u8BDD\u5185
|
|
4959
|
+
React.createElement("b", {}, t("\u9632\u5FA1\u4E8B\u4EF6")),
|
|
4960
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, tf(`\u4F1A\u8BDD\u5185 {a0} \u6761 / \u7D2F\u8BA1 {a1}`, { a0: allEvents.length, a1: totalCount })),
|
|
3609
4961
|
React.createElement("button", {
|
|
3610
4962
|
className: "iterate-btn",
|
|
3611
4963
|
"data-primary": "",
|
|
3612
4964
|
"data-copied": copiedKey === "defense-list" ? "" : void 0,
|
|
3613
|
-
onClick: () => copyInstruction("defense-list",
|
|
3614
|
-
title: "\u590D\u5236\u5217\u51FA\u9632\u5FA1\u4E8B\u4EF6\u6307\u4EE4"
|
|
3615
|
-
}, copiedKey === "defense-list" ? "\u5DF2\u590D\u5236" : "\u5217\u51FA\u4E8B\u4EF6"),
|
|
4965
|
+
onClick: () => copyInstruction("defense-list", buildDefenseEventsListInstruction()),
|
|
4966
|
+
title: t("\u590D\u5236\u5217\u51FA\u9632\u5FA1\u4E8B\u4EF6\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09")
|
|
4967
|
+
}, copiedKey === "defense-list" ? t("\u5DF2\u590D\u5236") : t("\u5217\u51FA\u4E8B\u4EF6")),
|
|
4968
|
+
React.createElement("button", {
|
|
4969
|
+
className: "iterate-btn",
|
|
4970
|
+
"data-copied": copiedKey === "defense-snapshot" ? "" : void 0,
|
|
4971
|
+
onClick: () => copyInstruction("defense-snapshot", pullInstruction),
|
|
4972
|
+
title: t("\u590D\u5236\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167\u6307\u4EE4\uFF1A\u628A .iterate/ \u4E0B\u7684\u9632\u5FA1\u4E8B\u4EF6\u6D41\u56DE\u663E\u8FDB\u4F1A\u8BDD")
|
|
4973
|
+
}, copiedKey === "defense-snapshot" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167")),
|
|
3616
4974
|
React.createElement("button", {
|
|
3617
4975
|
className: "iterate-btn",
|
|
3618
4976
|
"data-copied": copiedKey === "defense-counts" ? "" : void 0,
|
|
3619
|
-
onClick: () => copyInstruction("defense-counts",
|
|
3620
|
-
|
|
3621
|
-
|
|
4977
|
+
onClick: () => copyInstruction("defense-counts", tf(`\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u67E5\u8BE2\u4E8B\u4EF6\u7EDF\u8BA1\uFF1A
|
|
4978
|
+
|
|
4979
|
+
\`\`\`json
|
|
4980
|
+
{a0}
|
|
4981
|
+
\`\`\``, { a0: JSON.stringify({ operation: "counts" }, null, 2) })),
|
|
4982
|
+
title: t("\u590D\u5236\u7EDF\u8BA1\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09")
|
|
4983
|
+
}, copiedKey === "defense-counts" ? t("\u5DF2\u590D\u5236") : t("\u7EDF\u8BA1")),
|
|
3622
4984
|
React.createElement("button", {
|
|
3623
4985
|
className: "iterate-btn",
|
|
3624
4986
|
"data-danger": "",
|
|
3625
4987
|
"data-copied": copiedKey === "defense-clear" ? "" : void 0,
|
|
3626
|
-
onClick: () => copyInstruction("defense-clear", `\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u6E05\u9664\u9632\u5FA1\u4E8B\u4EF6\u6D41\uFF1A
|
|
4988
|
+
onClick: () => copyInstruction("defense-clear", tf(`\u8BF7\u8C03\u7528 \`iterate_defense_events\` \u6E05\u9664\u9632\u5FA1\u4E8B\u4EF6\u6D41\uFF1A
|
|
3627
4989
|
|
|
3628
4990
|
\`\`\`json
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
title: "\u590D\u5236 iterate_defense_events clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u4E8B\u4EF6\u6D41\uFF09"
|
|
3632
|
-
}, copiedKey === "defense-clear" ? "\u5DF2\u590D\u5236" : "\u6E05\u9664\u4E8B\u4EF6")
|
|
4991
|
+
{a0}
|
|
4992
|
+
\`\`\``, { a0: JSON.stringify({ operation: "clear" }, null, 2) })),
|
|
4993
|
+
title: t("\u590D\u5236 iterate_defense_events clear \u6307\u4EE4\u6587\u672C\uFF08\u91CD\u7F6E\u9648\u65E7\u4E8B\u4EF6\u6D41\uFF09")
|
|
4994
|
+
}, copiedKey === "defense-clear" ? t("\u5DF2\u590D\u5236") : t("\u6E05\u9664\u4E8B\u4EF6"))
|
|
3633
4995
|
);
|
|
3634
4996
|
if (allEvents.length === 0 && totalCount === 0) {
|
|
3635
4997
|
return React.createElement(
|
|
@@ -3642,13 +5004,22 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3642
5004
|
React.createElement(
|
|
3643
5005
|
"div",
|
|
3644
5006
|
{ className: "iterate-obs-block-head" },
|
|
3645
|
-
React.createElement("span", {}, "\u9632\u5FA1\u4E8B\u4EF6\u6D41"),
|
|
5007
|
+
React.createElement("span", {}, t("\u9632\u5FA1\u4E8B\u4EF6\u6D41")),
|
|
3646
5008
|
React.createElement("span", { className: "iterate-obs-badge" }, "v3.0")
|
|
3647
5009
|
),
|
|
3648
5010
|
React.createElement(
|
|
3649
5011
|
"div",
|
|
3650
5012
|
{ className: "iterate-obs-block-body" },
|
|
3651
|
-
React.createElement(
|
|
5013
|
+
React.createElement(
|
|
5014
|
+
"div",
|
|
5015
|
+
{ className: "iterate-obs-msg" },
|
|
5016
|
+
diskEmptyStateText(toolCalledInSession(session, "iterate_defense_events"), t("\u9632\u5FA1\u4E8B\u4EF6\u6D41"), getLang())
|
|
5017
|
+
),
|
|
5018
|
+
React.createElement(
|
|
5019
|
+
"div",
|
|
5020
|
+
{ className: "iterate-obs-msg", style: { marginTop: 6 } },
|
|
5021
|
+
t("\u9632\u5FA1\u4E8B\u4EF6\u5305\u62EC\uFF1A\u524D\u7F6E\u6821\u9A8C\u5931\u8D25\u3001\u56DE\u6EDA\u3001\u4E0D\u53D8\u91CF\u8FDD\u53CD\u3001\u5047\u8BBE\u88AB\u8BC1\u4F2A\u3002")
|
|
5022
|
+
),
|
|
3652
5023
|
React.createElement(
|
|
3653
5024
|
"div",
|
|
3654
5025
|
{ className: "iterate-obs-bar", style: { marginTop: 8 } },
|
|
@@ -3656,8 +5027,8 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3656
5027
|
className: "iterate-btn",
|
|
3657
5028
|
"data-copied": copiedKey === "defense-instr" ? "" : void 0,
|
|
3658
5029
|
onClick: () => copyInstruction("defense-instr", defenseInstruction),
|
|
3659
|
-
title: "\u590D\u5236\u67E5\u8BE2\u6307\u4EE4"
|
|
3660
|
-
}, copiedKey === "defense-instr" ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u67E5\u8BE2\u6307\u4EE4")
|
|
5030
|
+
title: t("\u590D\u5236\u67E5\u8BE2\u6307\u4EE4")
|
|
5031
|
+
}, copiedKey === "defense-instr" ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u67E5\u8BE2\u6307\u4EE4"))
|
|
3661
5032
|
)
|
|
3662
5033
|
)
|
|
3663
5034
|
)
|
|
@@ -3671,7 +5042,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3671
5042
|
{
|
|
3672
5043
|
className: "iterate-filter-select",
|
|
3673
5044
|
value: defenseFilter,
|
|
3674
|
-
"aria-label": "\u6309\u4E8B\u4EF6\u7C7B\u578B\u7B5B\u9009",
|
|
5045
|
+
"aria-label": t("\u6309\u4E8B\u4EF6\u7C7B\u578B\u7B5B\u9009"),
|
|
3675
5046
|
onChange: (e) => setDefenseFilter(e.target.value)
|
|
3676
5047
|
},
|
|
3677
5048
|
...typeOptions.map((opt) => React.createElement("option", { key: opt.value, value: opt.value }, opt.label))
|
|
@@ -3679,8 +5050,8 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3679
5050
|
defenseFilter ? React.createElement("button", {
|
|
3680
5051
|
className: "iterate-filter-clear",
|
|
3681
5052
|
onClick: () => setDefenseFilter(""),
|
|
3682
|
-
title: "\u6E05\u9664\u7C7B\u578B\u7B5B\u9009"
|
|
3683
|
-
}, "\u6E05\u9664") : null
|
|
5053
|
+
title: t("\u6E05\u9664\u7C7B\u578B\u7B5B\u9009")
|
|
5054
|
+
}, t("\u6E05\u9664")) : null
|
|
3684
5055
|
);
|
|
3685
5056
|
let eventRows = [];
|
|
3686
5057
|
if (visibleEvents.length > 0) {
|
|
@@ -3701,7 +5072,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3701
5072
|
"div",
|
|
3702
5073
|
{ className: "iterate-obs-block-body" },
|
|
3703
5074
|
React.createElement("div", { className: "iterate-obs-msg" }, String(e.description || "")),
|
|
3704
|
-
e.defense ? React.createElement("div", { className: "iterate-obs-msg", style: { marginTop: 4 } }, `\u9632\u7EBF\uFF1A
|
|
5075
|
+
e.defense ? React.createElement("div", { className: "iterate-obs-msg", style: { marginTop: 4 } }, tf(`\u9632\u7EBF\uFF1A{a0}`, { a0: String(e.defense) })) : null
|
|
3705
5076
|
)
|
|
3706
5077
|
);
|
|
3707
5078
|
});
|
|
@@ -3722,7 +5093,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3722
5093
|
)
|
|
3723
5094
|
)
|
|
3724
5095
|
),
|
|
3725
|
-
eventRows.length > 0 ? React.createElement("div", {}, ...eventRows) : React.createElement("div", { className: "iterate-obs-empty" }, "\u6CA1\u6709\u5339\u914D\u8BE5\u7C7B\u578B\u7684\u9632\u5FA1\u4E8B\u4EF6")
|
|
5096
|
+
eventRows.length > 0 ? React.createElement("div", {}, ...eventRows) : React.createElement("div", { className: "iterate-obs-empty" }, t("\u6CA1\u6709\u5339\u914D\u8BE5\u7C7B\u578B\u7684\u9632\u5FA1\u4E8B\u4EF6"))
|
|
3726
5097
|
);
|
|
3727
5098
|
};
|
|
3728
5099
|
const renderLive = () => {
|
|
@@ -3734,29 +5105,31 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3734
5105
|
return React.createElement(
|
|
3735
5106
|
"div",
|
|
3736
5107
|
{ className: "iterate-obs-empty" },
|
|
3737
|
-
React.createElement("div", {}, "\u5B9E\u65F6\u6D3B\u52A8\u6D41\u4EC5\u5728\u8FD0\u884C\u671F\u95F4\u8BB0\u5F55\uFF0C\u5F53\u524D\u5DF2\u5B8C\u6210\u3002"),
|
|
5108
|
+
React.createElement("div", {}, t("\u5B9E\u65F6\u6D3B\u52A8\u6D41\u4EC5\u5728\u8FD0\u884C\u671F\u95F4\u8BB0\u5F55\uFF0C\u5F53\u524D\u5DF2\u5B8C\u6210\u3002")),
|
|
3738
5109
|
React.createElement(
|
|
3739
5110
|
"div",
|
|
3740
5111
|
{ className: "iterate-obs-bar", style: { marginTop: 8 } },
|
|
3741
|
-
React.createElement("button", { className: "iterate-btn", onClick: () => setTab("f1") }, "\u67E5\u770B\u5BA1\u67E5\u7EBF\u7A0B"),
|
|
3742
|
-
React.createElement("button", { className: "iterate-btn", onClick: () => setTab("f3") }, "\u67E5\u770B\u53D1\u73B0")
|
|
5112
|
+
React.createElement("button", { className: "iterate-btn", onClick: () => setTab("f1") }, t("\u67E5\u770B\u5BA1\u67E5\u7EBF\u7A0B")),
|
|
5113
|
+
React.createElement("button", { className: "iterate-btn", onClick: () => setTab("f3") }, t("\u67E5\u770B\u53D1\u73B0"))
|
|
3743
5114
|
)
|
|
3744
5115
|
);
|
|
3745
5116
|
}
|
|
3746
|
-
return React.createElement("div", { className: "iterate-obs-empty" }, "\u6682\u65E0\u5B9E\u65F6\u6D3B\u52A8");
|
|
5117
|
+
return React.createElement("div", { className: "iterate-obs-empty" }, t("\u6682\u65E0\u5B9E\u65F6\u6D3B\u52A8"));
|
|
3747
5118
|
}
|
|
3748
5119
|
const types = Array.from(new Set(liveEntries.map((e) => String(e.type || "unknown")).filter(Boolean))).sort();
|
|
3749
5120
|
const filtered = filterLiveEntries(liveEntries, liveType);
|
|
3750
5121
|
const rows = filtered.map((e, i) => {
|
|
3751
5122
|
const type = String(e.type || "unknown");
|
|
3752
|
-
const meta = OBS_LIVE_META[type]
|
|
5123
|
+
const meta = OBS_LIVE_META[type];
|
|
5124
|
+
const typeLabel = meta ? meta.label[getLang()] : String(e.type || "?");
|
|
5125
|
+
const typeColor = meta ? meta.color : OBS_LIVE_FALLBACK_COLOR;
|
|
3753
5126
|
const tool = String(e.tool || "");
|
|
3754
5127
|
const target = String(e.target || "");
|
|
3755
5128
|
const time = formatObsLiveTime(String(e.ts || ""));
|
|
3756
5129
|
return React.createElement(
|
|
3757
5130
|
"div",
|
|
3758
5131
|
{ key: `live-${i}`, className: "iterate-obs-row" },
|
|
3759
|
-
React.createElement("span", { className: "iterate-obs-livebadge", style: { color:
|
|
5132
|
+
React.createElement("span", { className: "iterate-obs-livebadge", style: { color: typeColor } }, typeLabel),
|
|
3760
5133
|
tool ? React.createElement("span", { className: "iterate-obs-chip" }, tool) : null,
|
|
3761
5134
|
React.createElement("span", { className: "iterate-obs-msg" }, target || "\u2014"),
|
|
3762
5135
|
React.createElement("span", { className: "iterate-obs-head-meta" }, time)
|
|
@@ -3765,23 +5138,23 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3765
5138
|
const filterBar = React.createElement(
|
|
3766
5139
|
"div",
|
|
3767
5140
|
{ className: "iterate-obs-bar", style: { marginBottom: 8 } },
|
|
3768
|
-
React.createElement("b", {}, "\u5B9E\u65F6\u6D3B\u52A8"),
|
|
5141
|
+
React.createElement("b", {}, t("\u5B9E\u65F6\u6D3B\u52A8")),
|
|
3769
5142
|
React.createElement(
|
|
3770
5143
|
"select",
|
|
3771
5144
|
{
|
|
3772
5145
|
className: "iterate-filter-select",
|
|
3773
5146
|
value: liveType,
|
|
3774
|
-
"aria-label": "\u6309\u6D3B\u52A8\u7C7B\u578B\u7B5B\u9009",
|
|
5147
|
+
"aria-label": t("\u6309\u6D3B\u52A8\u7C7B\u578B\u7B5B\u9009"),
|
|
3775
5148
|
onChange: (e) => setLiveType(e.target.value)
|
|
3776
5149
|
},
|
|
3777
|
-
React.createElement("option", { value: "" }, "\u5168\u90E8\u6D3B\u52A8"),
|
|
3778
|
-
...types.map((
|
|
5150
|
+
React.createElement("option", { value: "" }, t("\u5168\u90E8\u6D3B\u52A8")),
|
|
5151
|
+
...types.map((t2) => React.createElement("option", { key: t2, value: t2 }, t2))
|
|
3779
5152
|
),
|
|
3780
5153
|
liveType ? React.createElement("button", {
|
|
3781
5154
|
className: "iterate-filter-clear",
|
|
3782
5155
|
onClick: () => setLiveType(""),
|
|
3783
|
-
title: "\u6E05\u9664\u7C7B\u578B\u7B5B\u9009"
|
|
3784
|
-
}, "\u6E05\u9664") : null,
|
|
5156
|
+
title: t("\u6E05\u9664\u7C7B\u578B\u7B5B\u9009")
|
|
5157
|
+
}, t("\u6E05\u9664")) : null,
|
|
3785
5158
|
React.createElement("span", { className: "iterate-filter-count" }, `${filtered.length}/${liveEntries.length}`)
|
|
3786
5159
|
);
|
|
3787
5160
|
if (rows.length === 0) {
|
|
@@ -3789,7 +5162,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3789
5162
|
"div",
|
|
3790
5163
|
{},
|
|
3791
5164
|
filterBar,
|
|
3792
|
-
React.createElement("div", { className: "iterate-obs-empty" }, "\u65E0\u5339\u914D\u7684\u5B9E\u65F6\u6D3B\u52A8")
|
|
5165
|
+
React.createElement("div", { className: "iterate-obs-empty" }, t("\u65E0\u5339\u914D\u7684\u5B9E\u65F6\u6D3B\u52A8"))
|
|
3793
5166
|
);
|
|
3794
5167
|
}
|
|
3795
5168
|
return React.createElement("div", {}, filterBar, ...rows);
|
|
@@ -3829,9 +5202,14 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3829
5202
|
React.createElement(
|
|
3830
5203
|
"div",
|
|
3831
5204
|
{ className: "iterate-obs-head", "data-closed": open ? void 0 : "", onClick: () => setOpen((v) => !v) },
|
|
3832
|
-
React.createElement("span", { className: "iterate-obs-title" }, "iterate \u89C2\u6D4B\u53F0"),
|
|
5205
|
+
React.createElement("span", { className: "iterate-obs-title" }, t("iterate \u89C2\u6D4B\u53F0")),
|
|
3833
5206
|
React.createElement("span", { className: "iterate-obs-badge", "data-live": live ? "" : void 0 }, runStatusText(live, manifest)),
|
|
3834
5207
|
React.createElement("span", { className: "iterate-obs-head-meta" }, headMeta || "runtime"),
|
|
5208
|
+
// Gap #14: make the tab system discoverable without expanding it.
|
|
5209
|
+
React.createElement("span", {
|
|
5210
|
+
className: "iterate-obs-head-meta",
|
|
5211
|
+
title: obsHotkeyHint()
|
|
5212
|
+
}, obsHotkeyHint()),
|
|
3835
5213
|
React.createElement("button", {
|
|
3836
5214
|
className: "iterate-btn",
|
|
3837
5215
|
"data-ghost": "",
|
|
@@ -3839,7 +5217,7 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3839
5217
|
e.stopPropagation();
|
|
3840
5218
|
setOpen((v) => !v);
|
|
3841
5219
|
}
|
|
3842
|
-
}, open ? "\u6536\u8D77" : "\u5C55\u5F00")
|
|
5220
|
+
}, open ? t("\u6536\u8D77") : t("\u5C55\u5F00"))
|
|
3843
5221
|
),
|
|
3844
5222
|
open ? React.createElement(
|
|
3845
5223
|
"div",
|
|
@@ -3850,14 +5228,14 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3850
5228
|
React.createElement(
|
|
3851
5229
|
"div",
|
|
3852
5230
|
{ className: "iterate-obs-block-head" },
|
|
3853
|
-
React.createElement("b", {}, "\u590D\u5236\u672A\u6210\u529F"),
|
|
3854
|
-
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"),
|
|
5231
|
+
React.createElement("b", {}, t("\u590D\u5236\u672A\u6210\u529F")),
|
|
5232
|
+
React.createElement("span", { className: "iterate-obs-head-meta" }, t("\u526A\u8D34\u677F\u5199\u5165\u88AB\u62D2\u7EDD\uFF0C\u8BF7\u624B\u52A8\u9009\u4E2D\u4E0B\u65B9\u6307\u4EE4\u590D\u5236")),
|
|
3855
5233
|
React.createElement("button", {
|
|
3856
5234
|
className: "iterate-btn",
|
|
3857
5235
|
"data-ghost": "",
|
|
3858
|
-
"aria-label": "\u5173\u95ED\u590D\u5236\u5931\u8D25\u63D0\u793A",
|
|
5236
|
+
"aria-label": t("\u5173\u95ED\u590D\u5236\u5931\u8D25\u63D0\u793A"),
|
|
3859
5237
|
onClick: () => setCopyFailText(null)
|
|
3860
|
-
}, "\u5173\u95ED")
|
|
5238
|
+
}, t("\u5173\u95ED"))
|
|
3861
5239
|
),
|
|
3862
5240
|
React.createElement("div", { className: "iterate-obs-code" }, copyFailText)
|
|
3863
5241
|
) : null,
|
|
@@ -3874,19 +5252,31 @@ ${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
|
3874
5252
|
"data-primary": "",
|
|
3875
5253
|
"data-copied": copiedKey === "export" ? "" : void 0,
|
|
3876
5254
|
onClick: exportObservatory,
|
|
3877
|
-
title: "\u5C06\u89C2\u6D4B\u53F0\u5168\u90E8\u6570\u636E\u5BFC\u51FA\u4E3A JSON\uFF08\u4F18\u5148\u4E0B\u8F7D\uFF0C\u5931\u8D25\u5219\u590D\u5236\uFF09"
|
|
3878
|
-
}, copiedKey === "export" ? "\u5DF2\u5BFC\u51FA" : "\u5BFC\u51FA JSON")
|
|
5255
|
+
title: t("\u5C06\u89C2\u6D4B\u53F0\u5168\u90E8\u6570\u636E\u5BFC\u51FA\u4E3A JSON\uFF08\u4F18\u5148\u4E0B\u8F7D\uFF0C\u5931\u8D25\u5219\u590D\u5236\uFF09")
|
|
5256
|
+
}, copiedKey === "export" ? t("\u5DF2\u5BFC\u51FA") : t("\u5BFC\u51FA JSON")),
|
|
5257
|
+
// Gap #4: `.iterate/` artifacts are unreachable from the browser;
|
|
5258
|
+
// this copies the one instruction that makes the model echo them
|
|
5259
|
+
// into the session stream (which is what every panel scans).
|
|
5260
|
+
React.createElement("button", {
|
|
5261
|
+
className: "iterate-btn",
|
|
5262
|
+
"data-copied": copiedKey === "pull-snapshot" ? "" : void 0,
|
|
5263
|
+
onClick: () => copyInstruction("pull-snapshot", pullInstruction),
|
|
5264
|
+
title: pullTitle
|
|
5265
|
+
}, copiedKey === "pull-snapshot" ? t("\u5DF2\u590D\u5236") : t("\u62C9\u53D6\u78C1\u76D8\u5FEB\u7167"))
|
|
3879
5266
|
),
|
|
3880
5267
|
React.createElement(
|
|
3881
5268
|
"div",
|
|
3882
|
-
{ className: "iterate-obs-tabs" },
|
|
5269
|
+
{ className: "iterate-obs-tabs", role: "tablist", "aria-label": t("\u89C2\u6D4B\u53F0\u9875\u7B7E\uFF08\u5FEB\u6377\u952E 1-9 / 0 / -\uFF09") },
|
|
3883
5270
|
...OBS_TABS.map(
|
|
3884
|
-
(
|
|
3885
|
-
key:
|
|
5271
|
+
(t2) => React.createElement("button", {
|
|
5272
|
+
key: t2.key,
|
|
3886
5273
|
className: "iterate-obs-tab",
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
5274
|
+
role: "tab",
|
|
5275
|
+
"aria-selected": tab === t2.key,
|
|
5276
|
+
"data-active": tab === t2.key ? "" : void 0,
|
|
5277
|
+
onClick: () => setTab(t2.key),
|
|
5278
|
+
title: t2.hotkey ? tf(`{a0}\uFF08\u6309 {a1} \u5207\u6362\uFF09`, { a0: obsTabLabel(t2), a1: t2.hotkey }) : tf(`{a0}\uFF08\u6309 - \u5207\u6362\uFF09`, { a0: obsTabLabel(t2) })
|
|
5279
|
+
}, obsTabLabel(t2))
|
|
3890
5280
|
)
|
|
3891
5281
|
),
|
|
3892
5282
|
React.createElement("div", { className: "iterate-obs-body" }, renderBody())
|
|
@@ -3899,11 +5289,21 @@ function selectTurnTail(owner) {
|
|
|
3899
5289
|
if (scanSessionForRunSummary(owner.turn)) return { matched: true };
|
|
3900
5290
|
return null;
|
|
3901
5291
|
}
|
|
5292
|
+
var LangDashboard = withLang(ConvergenceDashboard);
|
|
5293
|
+
var LangObservatory = withLang(ObservatoryPanel);
|
|
5294
|
+
var LangTurnTail = withLang(TurnTailEntry);
|
|
5295
|
+
var LangProgress = withLang(ProgressCapsule);
|
|
5296
|
+
var LangSettings = withLang(SettingsPanel);
|
|
3902
5297
|
function apply(ctx) {
|
|
3903
5298
|
slotsSvc = readSlots(ctx);
|
|
3904
5299
|
themeSvc = readTheme(ctx);
|
|
3905
|
-
|
|
3906
|
-
|
|
5300
|
+
storage2 = createStorage();
|
|
5301
|
+
configureLangStorage(storage2);
|
|
5302
|
+
setLang(
|
|
5303
|
+
detectLang(storage2.get(LANG_STORAGE_KEY), typeof navigator !== "undefined" ? navigator.language : ""),
|
|
5304
|
+
false
|
|
5305
|
+
);
|
|
5306
|
+
const savedTheme = storage2.get(THEME_STORAGE_KEY);
|
|
3907
5307
|
themeEnabled = savedTheme === null ? true : savedTheme === "1";
|
|
3908
5308
|
if (themeEnabled) applyThemeSkin();
|
|
3909
5309
|
if (typeof document !== "undefined" && document.createElement && document.head) {
|
|
@@ -3933,35 +5333,35 @@ function apply(ctx) {
|
|
|
3933
5333
|
"conversation.input.dock",
|
|
3934
5334
|
() => slotsSvc?.register(
|
|
3935
5335
|
{ name: "conversation.input.dock", id: "iterate-dashboard", order: 90 },
|
|
3936
|
-
(props) => React.createElement(
|
|
5336
|
+
(props) => React.createElement(LangDashboard, props)
|
|
3937
5337
|
)
|
|
3938
5338
|
);
|
|
3939
5339
|
slotsSvc.inject(
|
|
3940
5340
|
"conversation.input.dock",
|
|
3941
5341
|
() => slotsSvc?.register(
|
|
3942
5342
|
{ name: "conversation.input.dock", id: "iterate-observatory", order: 91 },
|
|
3943
|
-
(props) => React.createElement(
|
|
5343
|
+
(props) => React.createElement(LangObservatory, props)
|
|
3944
5344
|
)
|
|
3945
5345
|
);
|
|
3946
5346
|
slotsSvc.inject(
|
|
3947
5347
|
"conversation.chat.turnTail",
|
|
3948
5348
|
() => slotsSvc?.register(
|
|
3949
5349
|
{ name: "conversation.chat.turnTail", id: "iterate-turn-tail", select: selectTurnTail },
|
|
3950
|
-
(props) => React.createElement(
|
|
5350
|
+
(props) => React.createElement(LangTurnTail, props)
|
|
3951
5351
|
)
|
|
3952
5352
|
);
|
|
3953
5353
|
slotsSvc.inject(
|
|
3954
5354
|
"shell.overlay",
|
|
3955
5355
|
() => slotsSvc?.register(
|
|
3956
5356
|
{ name: "shell.overlay", id: "iterate-progress", order: 0 },
|
|
3957
|
-
(props) => React.createElement(
|
|
5357
|
+
(props) => React.createElement(LangProgress, props)
|
|
3958
5358
|
)
|
|
3959
5359
|
);
|
|
3960
5360
|
slotsSvc.inject(
|
|
3961
5361
|
"settings.section",
|
|
3962
5362
|
() => slotsSvc?.register(
|
|
3963
5363
|
{ name: "settings.section", id: "iterate-settings", order: 30, label: () => "iterate" },
|
|
3964
|
-
(props) => React.createElement(
|
|
5364
|
+
(props) => React.createElement(LangSettings, props)
|
|
3965
5365
|
)
|
|
3966
5366
|
);
|
|
3967
5367
|
}
|