@tea-agent/loop-agent 0.44.0-next.2 → 0.44.0-next.4
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/CHANGELOG.md +5 -3
- package/dist/build-stamp.json +3 -3
- package/dist/executors/dag-pi-executor.js +2 -2
- package/dist/task/config-types.js +2 -1
- package/dist/task/contract/schema.js +1 -0
- package/dist/worker/console/chat/chat-event-store.js +32 -0
- package/dist/worker/console/chat/conversation-nodes.js +383 -0
- package/dist/worker/console/chat/db-connections.js +48 -16
- package/dist/worker/console/chat/pi-runtime.js +131 -15
- package/dist/worker/console/chat/provider-error.js +0 -12
- package/dist/worker/console/chat/routes.js +136 -3
- package/dist/worker/console/chat/turn-process.js +128 -7
- package/dist/worker/console/prd-intake-bridge.js +3 -0
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-B8EdIjdl.js → abnfDiagram-N423BO3Z-B_lmJjOm.js} +1 -1
- package/dist/worker/console/static/assets/{arc-CUaduV17.js → arc-pymM4Qeh.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-bYAcxRaf.js → architectureDiagram-T3A2C74G-Bw9EPAhi.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-FfhFUEMJ.js → blockDiagram-VBNYF7ZC-C7_oXJp2.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-0Rqwn2_U.js → c4Diagram-5PPSVZJV-LnLWDbh8.js} +1 -1
- package/dist/worker/console/static/assets/channel-Ym13M3Lb.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D4FbDmsM.js → chunk-2GRJ4B5K-BBiu3sCW.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-C2nu32ml.js → chunk-2Q5K7J3B-DiXqDQml.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-G2tHSClK.js → chunk-5RXB4S5H-lmNq9nYk.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-181nyzuH.js → chunk-5VM5RSS4-boBu74dU.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-D091VbiQ.js → chunk-6Q2QTUOP-CjxqoJLQ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-BjrMCKTH.js → chunk-GF5L2VYU-DL0vQ9MG.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-T3zFHTpb.js → chunk-JWPE2WC7-DGPqE-O0.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CIB7wrYq.js → chunk-KBJHAD2P-v4BbT_r2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-HwnAsgDw.js → chunk-RYQCIY6F-Ci0ynvyL.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CQSdQAqo.js → chunk-XXDRQBXY-CGsbJoTV.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BeFR1wGo.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BeFR1wGo.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CyBVLpjP.js → cose-bilkent-JH36ORCC-CzoRDEI7.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-D2Pf3U7m.js → cynefin-VYW2F7L2-BOwFwbX4.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-CzXuQ1a2.js → cynefinDiagram-MW4NZA55-2Cfg451l.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-9cMmcOsQ.js → dagre-VZM6K2ZE-DbT0D6Y5.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DTOr8GLs.js → diagram-7IWD3JNH-Dfd_JoMs.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B8saqFHU.js → diagram-B4RE2ZJO-DaGm82kh.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-Bvygp--w.js → diagram-LBJQPF4R-DMoI8Ibc.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-CJyLwBhZ.js → diagram-Q27KOJAE-BtIFA3aY.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-CkR1FDRN.js → diagram-UB23O5K3-D_2X1DTZ.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BsbNtwvi.js → ebnfDiagram-BXEA7PRR-yFW5ZYlZ.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-EVZo9AKy.js → erDiagram-JOGREHBK-DBXh5Svj.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-C1x91tWN.js → flowDiagram-UKHOOZJN-C4ycm8KM.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CiNBUsrJ.js → ganttDiagram-PKOTCBZU-qr-_Pekr.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-CSd50p3d.js → gitGraphDiagram-DS77QQ5N-DzOSLv_A.js} +1 -1
- package/dist/worker/console/static/assets/index-PFjB6g6l.js +482 -0
- package/dist/worker/console/static/assets/index-ZtyB6Ol-.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-1JDiYCuA.js → infoDiagram-6WML65LV-C7Hl8D7l.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BZLE6zpm.js → ishikawaDiagram-WSZJBQD7-fBxVlr6t.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Cg0nDmbe.js → journeyDiagram-NVQOT4AX-CoiJEVCl.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-DcwEdKjq.js → kanban-definition-27J2QSJJ-TJXpxNuV.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CXvVge5x.js → linear-CjQWz1pG.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-5R4WTYSN.js → mermaid.core-DY9_apIE.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-By9pSeeq.js → mindmap-definition-FAOFIHXS-HB0Id8gM.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DzbVZTqW.js → pegDiagram-VL7TDLO6-0OSWgr6v.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B9nbfnAq.js → pieDiagram-7S7Q4E2Y-Cei1GGC2.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-D268jfWH.js → quadrantDiagram-CIZ2JOQS-CDy0Adfu.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-oNOXPRNn.js → railroadDiagram-AXF67PYL-BwqzUWwN.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DXdr8K-4.js → requirementDiagram-LRYGKXZP-Bif2IBRL.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Dtm4uOJ0.js → sankeyDiagram-W5VNT64P-CYIoNMLg.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CeeFiof4.js → sequenceDiagram-SI44F4Z6-BDS7Y0aQ.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CK1gARll.js → sizeCapture-X5ZJPWSS-lZic-_Bp.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-3jMoAcZw.js → stateDiagram-OKZ733FA-WCVlCqSd.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-cj5UUB6m.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-Div6kwoD.js → swimlanes-SLNWSIFB-pGccxTw_.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-C_Qp6bnh.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-B7P_6Up9.js → timeline-definition-Z64GVDOM-D29RlrQA.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-eOPaGUOX.js → vennDiagram-T6HMQDX7-01fEM1NU.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-tAcDnb31.js → wardleyDiagram-T6FBY63Y-C4GeB6dq.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-Dg_r_785.js → xychartDiagram-ELKLHX3M-BXHrXHps.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/animation-frame-batcher.js +38 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +97 -3
- package/dist/worker/console/static-src/operator-chat/process-label.js +11 -2
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +2 -1
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -0
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +9 -0
- package/dist/worker/console/workspace-context.js +1 -0
- package/dist/worker/observe/static/console-theme.css +174 -0
- package/dist/worker/observe/static/console-theme.js +36 -0
- package/dist/worker/observe/static/operator-chrome.css +85 -0
- package/dist/worker/observe/static/operator-chrome.js +51 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +3 -1
- package/dist/workflows/dag/frontend-plan-completeness.js +13 -3
- package/dist/workflows/dag/frontend-recovery-plan.js +10 -6
- package/dist/workflows/dag/frontend-typed-event-store.js +5 -0
- package/dist/workflows/dag/frontend-writer-admission.js +1 -0
- package/dist/workflows/dag/output-protocol.js +1 -0
- package/dist/workflows/dag/rerun-task.js +1 -0
- package/docs/governance/harness-methodology-verification.md +11 -0
- package/package.json +1 -1
- package/dist/worker/console/static/assets/channel-ICokO4I1.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C0c0PBcA.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C0c0PBcA.js +0 -1
- package/dist/worker/console/static/assets/index-DHBWEwz_.css +0 -1
- package/dist/worker/console/static/assets/index-RFWQLUE-.js +0 -478
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cr_kaxrX.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bl4fQmZh.js +0 -8
|
@@ -8,6 +8,7 @@ import { mergePendingSubmissionOnHydration } from "./pending-user-message.js";
|
|
|
8
8
|
import { taskContextUnchanged } from "./format.js";
|
|
9
9
|
import { upsertCompactionMessage } from "./compaction-message.js";
|
|
10
10
|
import { sameTurnGroupInputs } from "./turn-group-equality.js";
|
|
11
|
+
import { conversationNodesFromEvents, } from "../../chat/conversation-nodes.js";
|
|
11
12
|
const EMPTY_USAGE = {
|
|
12
13
|
inputTokens: 0,
|
|
13
14
|
outputTokens: 0,
|
|
@@ -141,6 +142,7 @@ export function useChatThread(params) {
|
|
|
141
142
|
const [sessionStats, setSessionStats] = useState(EMPTY_SESSION_STATS);
|
|
142
143
|
const [turnTelemetry, setTurnTelemetry] = useState([]);
|
|
143
144
|
const [operatorActions, setOperatorActions] = useState([]);
|
|
145
|
+
const [conversationNodes, setConversationNodes] = useState([]);
|
|
144
146
|
const [sddSessionIdentity, setSddSessionIdentity] = useState(null);
|
|
145
147
|
const [streaming, setStreaming] = useState(false);
|
|
146
148
|
const [turnPhase, setTurnPhase] = useState("idle");
|
|
@@ -192,6 +194,7 @@ export function useChatThread(params) {
|
|
|
192
194
|
setTodos,
|
|
193
195
|
setGoal,
|
|
194
196
|
setOperatorActions,
|
|
197
|
+
setConversationNodes,
|
|
195
198
|
setSddSessionIdentity,
|
|
196
199
|
setStreamingAssistantId,
|
|
197
200
|
setUnread,
|
|
@@ -330,6 +333,7 @@ export function useChatThread(params) {
|
|
|
330
333
|
toolCalls,
|
|
331
334
|
streaming,
|
|
332
335
|
activeAssistantId: streamingAssistantId,
|
|
336
|
+
conversationNodes,
|
|
333
337
|
});
|
|
334
338
|
// Preserve completed group objects across streaming tokens. Stable
|
|
335
339
|
// historical groups keep their references so
|
|
@@ -348,6 +352,7 @@ export function useChatThread(params) {
|
|
|
348
352
|
toolCalls,
|
|
349
353
|
streaming,
|
|
350
354
|
streamingAssistantId,
|
|
355
|
+
conversationNodes,
|
|
351
356
|
historyLimit,
|
|
352
357
|
]);
|
|
353
358
|
const loadEarlierMessages = useCallback(async (beforePrepend) => {
|
|
@@ -427,6 +432,7 @@ export function useChatThread(params) {
|
|
|
427
432
|
setSessionStats(EMPTY_SESSION_STATS);
|
|
428
433
|
setTurnTelemetry([]);
|
|
429
434
|
setOperatorActions([]);
|
|
435
|
+
setConversationNodes([]);
|
|
430
436
|
setSddSessionIdentity(null);
|
|
431
437
|
setStreaming(false);
|
|
432
438
|
setTurnPhase("idle");
|
|
@@ -476,6 +482,7 @@ export function useChatThread(params) {
|
|
|
476
482
|
}
|
|
477
483
|
: undefined));
|
|
478
484
|
setToolCalls(next.toolCalls);
|
|
485
|
+
setConversationNodes(conversationNodesFromEvents(next.conversationEvents ?? []));
|
|
479
486
|
setOperationCards([]);
|
|
480
487
|
setHumanGateCards([]);
|
|
481
488
|
setArtifactCards([]);
|
|
@@ -546,6 +553,7 @@ export function useChatThread(params) {
|
|
|
546
553
|
refs.turnAssistantIdsRef.current = new Map();
|
|
547
554
|
refs.sealedAssistantIdsRef.current = new Set();
|
|
548
555
|
const hydrated = hydrateSessionThread(body.session.messages ?? []);
|
|
556
|
+
setConversationNodes(conversationNodesFromEvents(body.conversationEvents ?? []));
|
|
549
557
|
// Recovery is addressed to targetSessionId, so an optimistic
|
|
550
558
|
// submission from another session must never be projected into
|
|
551
559
|
// this snapshot (the activation path enforces the same owner gate).
|
|
@@ -618,6 +626,7 @@ export function useChatThread(params) {
|
|
|
618
626
|
sessionStats,
|
|
619
627
|
turnTelemetry,
|
|
620
628
|
operatorActions,
|
|
629
|
+
conversationNodes,
|
|
621
630
|
sddSessionIdentity,
|
|
622
631
|
setSddSessionIdentity,
|
|
623
632
|
streaming,
|
|
@@ -319,6 +319,7 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
319
319
|
terminalRegistry,
|
|
320
320
|
scmService,
|
|
321
321
|
browserAutomation,
|
|
322
|
+
globalAppDataRoot: appData.root,
|
|
322
323
|
});
|
|
323
324
|
let chatRouteDeps;
|
|
324
325
|
const goalRoundDriver = new GoalRoundDriver((sessionId) => driveGoalRound(chatRouteDeps, sessionId));
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/* Opt-in palettes. Repeat tokens at the Inspect boundary: it owns local defaults
|
|
2
|
+
* and is loaded after the Console stylesheet. Specificity keeps load order safe. */
|
|
3
|
+
:root[data-console-theme="midnight"],
|
|
4
|
+
:root[data-console-theme="midnight"] body[data-shell="observe-console"],
|
|
5
|
+
:root[data-console-theme="midnight"] .inspect-workspace-root {
|
|
6
|
+
color-scheme: dark;
|
|
7
|
+
--canvas: #1c222a;
|
|
8
|
+
--canvas-soft: #202730;
|
|
9
|
+
--surface: #252d37;
|
|
10
|
+
--surface-muted: #2d3641;
|
|
11
|
+
--ink: #dce2e9;
|
|
12
|
+
--surface-ink: #dce2e9;
|
|
13
|
+
--body: #bcc5d0;
|
|
14
|
+
--muted: #a1acba;
|
|
15
|
+
--muted-soft: #909eaf;
|
|
16
|
+
--muted-ink: #a1acba;
|
|
17
|
+
--hairline: #353f4b;
|
|
18
|
+
--hairline-strong: #526171;
|
|
19
|
+
--orange: #a0bcd3;
|
|
20
|
+
--orange-active: #b8cfe0;
|
|
21
|
+
--blue: #a0bcd3;
|
|
22
|
+
--brand: #b2aacb;
|
|
23
|
+
--theme-action: #4d6b86;
|
|
24
|
+
--theme-action-hover: #557590;
|
|
25
|
+
--theme-on-action: #ffffff;
|
|
26
|
+
--green: #99c5b0;
|
|
27
|
+
--amber: #d3b783;
|
|
28
|
+
--warn: #d3b783;
|
|
29
|
+
--red: #dea4ac;
|
|
30
|
+
--peach: #303e4d;
|
|
31
|
+
--mint: #2d403b;
|
|
32
|
+
--lavender: #393746;
|
|
33
|
+
--gold: #c3ac80;
|
|
34
|
+
--shadow-sm: 0 1px 2px rgb(0 0 0 / 12%);
|
|
35
|
+
--shadow-panel: 0 1px 2px rgb(0 0 0 / 12%), 0 6px 18px rgb(0 0 0 / 10%);
|
|
36
|
+
--shadow-soft: 0 8px 24px rgb(0 0 0 / 18%);
|
|
37
|
+
--shadow-lg: 0 16px 40px rgb(0 0 0 / 24%);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
:root[data-console-theme="daylight"],
|
|
41
|
+
:root[data-console-theme="daylight"] body[data-shell="observe-console"],
|
|
42
|
+
:root[data-console-theme="daylight"] .inspect-workspace-root {
|
|
43
|
+
color-scheme: light;
|
|
44
|
+
--canvas: #eef0f2;
|
|
45
|
+
--canvas-soft: #f3f4f5;
|
|
46
|
+
--surface: #f9fafb;
|
|
47
|
+
--surface-muted: #e5e9ed;
|
|
48
|
+
--ink: #303b47;
|
|
49
|
+
--surface-ink: #303b47;
|
|
50
|
+
--body: #4b5867;
|
|
51
|
+
--muted: #5e6a78;
|
|
52
|
+
--muted-soft: #697583;
|
|
53
|
+
--muted-ink: #5e6a78;
|
|
54
|
+
--hairline: #dce1e6;
|
|
55
|
+
--hairline-strong: #b5bfc9;
|
|
56
|
+
--orange: #466581;
|
|
57
|
+
--orange-active: #365570;
|
|
58
|
+
--blue: #466581;
|
|
59
|
+
--brand: #706387;
|
|
60
|
+
--theme-action: #536f88;
|
|
61
|
+
--theme-action-hover: #466079;
|
|
62
|
+
--theme-on-action: #ffffff;
|
|
63
|
+
--green: #3e735d;
|
|
64
|
+
--amber: #85632d;
|
|
65
|
+
--warn: #85632d;
|
|
66
|
+
--red: #a04f5c;
|
|
67
|
+
--peach: #dfe7ee;
|
|
68
|
+
--mint: #e0eae4;
|
|
69
|
+
--lavender: #e7e3ed;
|
|
70
|
+
--gold: #927644;
|
|
71
|
+
--shadow-sm: 0 1px 2px rgb(35 45 58 / 4%);
|
|
72
|
+
--shadow-panel: 0 1px 2px rgb(35 45 58 / 3%), 0 6px 18px rgb(35 45 58 / 3%);
|
|
73
|
+
--shadow-soft: 0 8px 24px rgb(35 45 58 / 6%);
|
|
74
|
+
--shadow-lg: 0 16px 40px rgb(35 45 58 / 9%);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Only the two added themes override component literals; original warm paper
|
|
78
|
+
* remains byte-for-byte defined by its existing stylesheets. */
|
|
79
|
+
:root:is([data-console-theme="midnight"], [data-console-theme="daylight"]) {
|
|
80
|
+
--orange-soft: color-mix(in srgb, var(--orange) 10%, var(--surface));
|
|
81
|
+
background: var(--canvas);
|
|
82
|
+
|
|
83
|
+
body[data-shell="observe-console"], .inspect-workspace-root {
|
|
84
|
+
--orange-soft: color-mix(in srgb, var(--orange) 10%, var(--surface));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
::selection { background: color-mix(in srgb, var(--blue) 32%, var(--surface)); }
|
|
88
|
+
|
|
89
|
+
.oc-composer-shell, .console-update-channel, .console-update-button,
|
|
90
|
+
.panel-output { background: var(--surface); color: var(--ink); }
|
|
91
|
+
.console-update-channel, .console-update-button { border-color: var(--hairline-strong); }
|
|
92
|
+
.console-update-channel-label { color: var(--body); }
|
|
93
|
+
.console-update-error, .console-update-result-error { color: var(--red); }
|
|
94
|
+
.console-update-badge { background: var(--surface-muted); color: var(--amber); }
|
|
95
|
+
|
|
96
|
+
.oc-bubble-user { background: color-mix(in srgb, var(--blue) 9%, var(--surface)); }
|
|
97
|
+
.oc-composer-shell { box-shadow: var(--shadow-panel); }
|
|
98
|
+
.oc-bubble-assistant .oc-codeblock { background: var(--canvas-soft); }
|
|
99
|
+
.oc-session-scroll, .oc-tree-scroll { background: var(--canvas-soft); }
|
|
100
|
+
.oc-sidebar-new-session:is(:hover, :focus-visible),
|
|
101
|
+
.oc-hero-chip:is(:hover, :focus-visible, [aria-expanded="true"]) { background: var(--surface-muted); }
|
|
102
|
+
|
|
103
|
+
.night-health-pill, .models-status-pill[data-state="ready"],
|
|
104
|
+
.models-probe-item.is-ok .models-probe-badge {
|
|
105
|
+
background: color-mix(in srgb, var(--green) 14%, var(--surface));
|
|
106
|
+
}
|
|
107
|
+
.night-health-pill.is-bad, .models-status-pill[data-state="setup-required"],
|
|
108
|
+
.models-probe-item.is-bad .models-probe-badge, .dag-error-node {
|
|
109
|
+
background: color-mix(in srgb, var(--red) 12%, var(--surface));
|
|
110
|
+
}
|
|
111
|
+
.models-status-pill[data-state="degraded"], .models-degraded-note,
|
|
112
|
+
.dag-active-node { background: var(--orange-soft); }
|
|
113
|
+
.liveness-timeout-risk { background: color-mix(in srgb, var(--amber) 12%, var(--surface)); }
|
|
114
|
+
.night-strip-icon { color: var(--brand); }
|
|
115
|
+
|
|
116
|
+
/* Quiet selected surfaces keep navigation secondary to the content. */
|
|
117
|
+
.och-mode-thumb { background: var(--surface); box-shadow: var(--shadow-sm); }
|
|
118
|
+
.och-mode-btn.och-on { color: var(--ink); }
|
|
119
|
+
.dag-inspector-tab[aria-selected="true"],
|
|
120
|
+
.dag-graph-mode-tab[aria-selected="true"],
|
|
121
|
+
#dag-inspector-panel-prompt-restart button.prompt-restart-mode-option.is-selected {
|
|
122
|
+
background: var(--surface-muted);
|
|
123
|
+
color: var(--ink);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Accent text needs a different luminance from a white-labelled action. */
|
|
127
|
+
.btn-primary, button.btn-primary, .oc-send, .oc-question-button.is-primary,
|
|
128
|
+
.oc-scm-commitbtn, .skip-link, .dags-page-num.is-current,
|
|
129
|
+
.dag-action-primary, .context-export-submit,
|
|
130
|
+
#dag-inspector-panel-prompt-restart button.prompt-restart-submit:not(.is-secondary) {
|
|
131
|
+
background: var(--theme-action);
|
|
132
|
+
border-color: var(--theme-action);
|
|
133
|
+
color: var(--theme-on-action);
|
|
134
|
+
box-shadow: var(--shadow-sm);
|
|
135
|
+
}
|
|
136
|
+
.btn-primary:hover:not(:disabled), button.btn-primary:hover:not(:disabled),
|
|
137
|
+
.oc-send:hover:not(:disabled), .oc-question-button.is-primary:hover:not(:disabled),
|
|
138
|
+
.oc-scm-commitbtn:hover:not(:disabled), .dag-action-primary:hover:not(:disabled),
|
|
139
|
+
.context-export-submit:hover:not(:disabled),
|
|
140
|
+
#dag-inspector-panel-prompt-restart button.prompt-restart-submit:not(.is-secondary):hover {
|
|
141
|
+
background: var(--theme-action-hover);
|
|
142
|
+
border-color: var(--theme-action-hover);
|
|
143
|
+
}
|
|
144
|
+
.och-context-return, .process-timeline-autoscroll::after {
|
|
145
|
+
background: var(--surface-muted);
|
|
146
|
+
color: var(--ink);
|
|
147
|
+
}
|
|
148
|
+
.markdown-output pre, .output-block { background: var(--canvas-soft); color: var(--ink); }
|
|
149
|
+
.context-export-ticket { background: var(--surface-muted); color: var(--ink); }
|
|
150
|
+
.context-export-ticket-safety { color: var(--green); }
|
|
151
|
+
.context-export-ticket-safety.is-risk { color: var(--red); }
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* Syntax highlighting is independently hardcoded to a light palette. */
|
|
155
|
+
:root[data-console-theme="midnight"] {
|
|
156
|
+
/* The SVG brand uses dark strokes; provide a subtle light backing, not a filter. */
|
|
157
|
+
.och-brand-mark, .oc-hero img { background: #aebac7; border-radius: 6px; }
|
|
158
|
+
.hljs { color: var(--ink); background: var(--canvas-soft); }
|
|
159
|
+
.hljs-doctag, .hljs-keyword, .hljs-meta .hljs-keyword,
|
|
160
|
+
.hljs-template-tag, .hljs-template-variable, .hljs-type,
|
|
161
|
+
.hljs-variable.language_ { color: #c7afcf; }
|
|
162
|
+
.hljs-title, .hljs-title.class_, .hljs-title.class_.inherited__,
|
|
163
|
+
.hljs-title.function_ { color: #b9b3d1; }
|
|
164
|
+
.hljs-attr, .hljs-attribute, .hljs-literal, .hljs-meta, .hljs-number,
|
|
165
|
+
.hljs-operator, .hljs-selector-attr, .hljs-selector-class,
|
|
166
|
+
.hljs-selector-id, .hljs-variable, .hljs-section { color: var(--blue); }
|
|
167
|
+
.hljs-meta .hljs-string, .hljs-regexp, .hljs-string,
|
|
168
|
+
.hljs-name, .hljs-quote, .hljs-selector-pseudo, .hljs-selector-tag { color: var(--green); }
|
|
169
|
+
.hljs-built_in, .hljs-symbol, .hljs-bullet { color: var(--amber); }
|
|
170
|
+
.hljs-code, .hljs-comment, .hljs-formula { color: var(--muted); }
|
|
171
|
+
.hljs-subst, .hljs-emphasis, .hljs-strong { color: var(--ink); }
|
|
172
|
+
.hljs-addition { color: var(--green); background: var(--mint); }
|
|
173
|
+
.hljs-deletion { color: var(--red); background: color-mix(in srgb, var(--red) 12%, var(--surface)); }
|
|
174
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Shared Console theme identities; the default leaves the original palette intact. */
|
|
2
|
+
export const CONSOLE_THEMES = Object.freeze([
|
|
3
|
+
{ id: "default", label: "默认" },
|
|
4
|
+
{ id: "midnight", label: "深色" },
|
|
5
|
+
{ id: "daylight", label: "浅色" },
|
|
6
|
+
]);
|
|
7
|
+
|
|
8
|
+
const STORAGE_KEY = "loop-console.theme.v1";
|
|
9
|
+
|
|
10
|
+
function normalizeTheme(value) {
|
|
11
|
+
return CONSOLE_THEMES.some((theme) => theme.id === value) ? value : "default";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function applyStoredConsoleTheme() {
|
|
15
|
+
let value;
|
|
16
|
+
try {
|
|
17
|
+
value = window.localStorage.getItem(STORAGE_KEY);
|
|
18
|
+
} catch {
|
|
19
|
+
// Storage can be disabled, including the localStorage getter itself.
|
|
20
|
+
}
|
|
21
|
+
const theme = normalizeTheme(value);
|
|
22
|
+
document.documentElement.dataset.consoleTheme = theme;
|
|
23
|
+
return theme;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function setConsoleTheme(value) {
|
|
27
|
+
const theme = normalizeTheme(value);
|
|
28
|
+
// Apply before persistence: a storage failure must not disable the picker.
|
|
29
|
+
document.documentElement.dataset.consoleTheme = theme;
|
|
30
|
+
try {
|
|
31
|
+
window.localStorage.setItem(STORAGE_KEY, theme);
|
|
32
|
+
} catch {
|
|
33
|
+
// The document attribute remains the in-memory preference.
|
|
34
|
+
}
|
|
35
|
+
return theme;
|
|
36
|
+
}
|
|
@@ -8,6 +8,84 @@
|
|
|
8
8
|
* 的暖白 token 保持一致;宿主样式表已定义同名变量时以宿主为准。
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
@import "./console-theme.css";
|
|
12
|
+
|
|
13
|
+
.och-theme {
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: 6px;
|
|
17
|
+
flex: none;
|
|
18
|
+
color: var(--och-muted);
|
|
19
|
+
font-size: 12px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.och-theme-trigger {
|
|
23
|
+
display: inline-flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
width: 26px;
|
|
27
|
+
min-height: 26px;
|
|
28
|
+
padding: 0;
|
|
29
|
+
border: 1px solid transparent;
|
|
30
|
+
border-radius: 6px;
|
|
31
|
+
background: transparent;
|
|
32
|
+
color: var(--och-body);
|
|
33
|
+
font: inherit;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.och-theme-trigger:hover,
|
|
38
|
+
.och-theme-trigger[aria-expanded="true"] {
|
|
39
|
+
background: var(--och-surface-muted);
|
|
40
|
+
color: var(--och-ink);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.och-theme-trigger:focus-visible,
|
|
44
|
+
.och-theme-option:focus-visible {
|
|
45
|
+
outline: 2px solid var(--och-orange);
|
|
46
|
+
outline-offset: 2px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Native popover renders in the top layer, outside the narrow header's clip. */
|
|
50
|
+
.och-theme-menu {
|
|
51
|
+
position: fixed;
|
|
52
|
+
inset: auto;
|
|
53
|
+
box-sizing: border-box;
|
|
54
|
+
width: 112px;
|
|
55
|
+
margin: 0;
|
|
56
|
+
padding: 4px;
|
|
57
|
+
border: 1px solid var(--och-hairline);
|
|
58
|
+
border-radius: 8px;
|
|
59
|
+
background: var(--och-surface);
|
|
60
|
+
color: var(--och-ink);
|
|
61
|
+
box-shadow: var(--shadow-soft);
|
|
62
|
+
font: inherit;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.och-theme-option {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
justify-content: space-between;
|
|
69
|
+
width: 100%;
|
|
70
|
+
min-height: 28px;
|
|
71
|
+
padding: 4px 8px;
|
|
72
|
+
border: 0;
|
|
73
|
+
border-radius: 4px;
|
|
74
|
+
background: transparent;
|
|
75
|
+
color: inherit;
|
|
76
|
+
font: inherit;
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.och-theme-option:hover,
|
|
81
|
+
.och-theme-option[aria-pressed="true"] {
|
|
82
|
+
background: var(--och-surface-muted);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.och-theme-option[aria-pressed="true"]::after {
|
|
86
|
+
content: "✓";
|
|
87
|
+
}
|
|
88
|
+
|
|
11
89
|
.och {
|
|
12
90
|
--och-canvas: var(--canvas, #f3f4f6);
|
|
13
91
|
--och-surface: var(--surface, #fbfcfd);
|
|
@@ -622,7 +700,14 @@
|
|
|
622
700
|
.och-inner {
|
|
623
701
|
padding-inline: 12px;
|
|
624
702
|
}
|
|
703
|
+
.och-theme {
|
|
704
|
+
position: absolute;
|
|
705
|
+
top: 46px;
|
|
706
|
+
right: 0;
|
|
707
|
+
}
|
|
625
708
|
.och-global {
|
|
709
|
+
height: 84px;
|
|
710
|
+
padding-bottom: 40px;
|
|
626
711
|
gap: 6px;
|
|
627
712
|
overflow-x: auto;
|
|
628
713
|
scrollbar-width: none;
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { withWorkspaceQuery } from "./router.js";
|
|
15
|
+
import { CONSOLE_THEMES, applyStoredConsoleTheme, setConsoleTheme } from "./console-theme.js";
|
|
16
|
+
export { CONSOLE_THEMES } from "./console-theme.js";
|
|
15
17
|
|
|
16
18
|
export const OPERATE_TABS = Object.freeze([
|
|
17
19
|
{ id: "tasks", label: "任务", href: "/#/operate/tasks" },
|
|
@@ -296,6 +298,13 @@ export function buildOperatorChromeModel(config) {
|
|
|
296
298
|
|
|
297
299
|
const SVG_NS = "http:" + "//www.w3.org/2000/svg";
|
|
298
300
|
const ICONS = Object.freeze({
|
|
301
|
+
theme: {
|
|
302
|
+
size: 16,
|
|
303
|
+
parts: [
|
|
304
|
+
["circle", { cx: 12, cy: 12, r: 9 }],
|
|
305
|
+
["path", { d: "M12 3a9 9 0 0 1 0 18Z", fill: "currentColor", stroke: "none" }],
|
|
306
|
+
],
|
|
307
|
+
},
|
|
299
308
|
radar: {
|
|
300
309
|
size: 17,
|
|
301
310
|
parts: [
|
|
@@ -426,6 +435,7 @@ function readWorkspaceCollapsedPref() {
|
|
|
426
435
|
export function mountOperatorChrome(root, config = {}) {
|
|
427
436
|
if (!root) throw new Error("mountOperatorChrome: root element is required");
|
|
428
437
|
root.classList.add("och-host");
|
|
438
|
+
if (!document.documentElement.dataset.consoleTheme) applyStoredConsoleTheme();
|
|
429
439
|
|
|
430
440
|
let currentConfig = { ...config };
|
|
431
441
|
let popoverOpen = false;
|
|
@@ -701,6 +711,47 @@ export function mountOperatorChrome(root, config = {}) {
|
|
|
701
711
|
|
|
702
712
|
global.appendChild(el("span", "och-spacer"));
|
|
703
713
|
|
|
714
|
+
const themePicker = el("div", "och-theme");
|
|
715
|
+
const trigger = el("button", "och-theme-trigger");
|
|
716
|
+
trigger.type = "button";
|
|
717
|
+
trigger.title = "主题";
|
|
718
|
+
trigger.setAttribute("aria-label", "主题");
|
|
719
|
+
trigger.appendChild(makeIcon("theme"));
|
|
720
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
721
|
+
const menu = el("div", "och-theme-menu");
|
|
722
|
+
menu.id = `${popoverId}-theme`;
|
|
723
|
+
menu.popover = "auto";
|
|
724
|
+
menu.setAttribute("role", "group");
|
|
725
|
+
menu.setAttribute("aria-label", "选择主题");
|
|
726
|
+
trigger.setAttribute("aria-controls", menu.id);
|
|
727
|
+
trigger.popoverTargetElement = menu;
|
|
728
|
+
menu.addEventListener("beforetoggle", (event) => {
|
|
729
|
+
if (event.newState !== "open") return;
|
|
730
|
+
const rect = trigger.getBoundingClientRect();
|
|
731
|
+
menu.style.top = `${rect.bottom + 6}px`;
|
|
732
|
+
menu.style.left = `${Math.max(8, Math.min(rect.right - 112, window.innerWidth - 120))}px`;
|
|
733
|
+
});
|
|
734
|
+
menu.addEventListener("toggle", (event) => {
|
|
735
|
+
trigger.setAttribute("aria-expanded", String(event.newState === "open"));
|
|
736
|
+
});
|
|
737
|
+
for (const theme of CONSOLE_THEMES) {
|
|
738
|
+
const option = el("button", "och-theme-option", theme.label);
|
|
739
|
+
option.type = "button";
|
|
740
|
+
option.dataset.theme = theme.id;
|
|
741
|
+
option.setAttribute("aria-pressed", String(theme.id === (document.documentElement.dataset.consoleTheme ?? "default")));
|
|
742
|
+
option.addEventListener("click", () => {
|
|
743
|
+
setConsoleTheme(theme.id);
|
|
744
|
+
for (const item of menu.children) {
|
|
745
|
+
item.setAttribute("aria-pressed", String(item.dataset.theme === theme.id));
|
|
746
|
+
}
|
|
747
|
+
menu.hidePopover();
|
|
748
|
+
trigger.focus();
|
|
749
|
+
});
|
|
750
|
+
menu.appendChild(option);
|
|
751
|
+
}
|
|
752
|
+
themePicker.append(trigger, menu);
|
|
753
|
+
global.appendChild(themePicker);
|
|
754
|
+
|
|
704
755
|
if (model.statusVisible && model.freshness) {
|
|
705
756
|
const fresh = el("span", "och-fresh");
|
|
706
757
|
fresh.setAttribute("aria-label", "数据新鲜度");
|
|
@@ -619,13 +619,15 @@ export function composeDependencyPolicyWithBoundary(policy, boundary) {
|
|
|
619
619
|
return policy;
|
|
620
620
|
const allowed = (boundary.allowed ?? []).filter((item) => item.trim());
|
|
621
621
|
const forbidden = (boundary.forbidden ?? []).filter((item) => item.trim());
|
|
622
|
-
|
|
622
|
+
const requiredClauses = (boundary.requiredClauses ?? []).filter((item) => item.trim());
|
|
623
|
+
if (allowed.length === 0 && forbidden.length === 0 && requiredClauses.length === 0)
|
|
623
624
|
return policy;
|
|
624
625
|
if (policy?.includes(CAPABILITY_BOUNDARY_MARKER))
|
|
625
626
|
return policy;
|
|
626
627
|
const sentence = `${CAPABILITY_BOUNDARY_MARKER}:` +
|
|
627
628
|
(allowed.length > 0 ? `仅可使用 ${allowed.join("、")}` : "无允许清单") +
|
|
628
629
|
(forbidden.length > 0 ? `;禁止 ${forbidden.join("、")}` : "") +
|
|
630
|
+
(requiredClauses.length > 0 ? `;必须满足 ${requiredClauses.join(";")}` : "") +
|
|
629
631
|
"。";
|
|
630
632
|
return policy?.trim() ? `${policy.trim()};${sentence}` : sentence;
|
|
631
633
|
}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { collectCanonicalStateFlowNames } from "./frontend-contract-facts.js";
|
|
2
|
+
export function enrichFrontendPlanMissingFacts(missing) {
|
|
3
|
+
return missing.map((item) => ({
|
|
4
|
+
...item,
|
|
5
|
+
owner: "Plan",
|
|
6
|
+
repairScope: item.kind === "state-registry" || item.kind === "mock-api"
|
|
7
|
+
? "global-phase"
|
|
8
|
+
: item.requirementIds.length > 1 ? "requirement-slice" : "fact",
|
|
9
|
+
nextAction: "replace-fact-and-finalize",
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
2
12
|
export function committedFactFromPlanRecord(value) {
|
|
3
13
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
4
14
|
return undefined;
|
|
@@ -94,7 +104,7 @@ export function collectFrontendPlanMissingFacts(input) {
|
|
|
94
104
|
}
|
|
95
105
|
}
|
|
96
106
|
}
|
|
97
|
-
return missing;
|
|
107
|
+
return enrichFrontendPlanMissingFacts(missing);
|
|
98
108
|
}
|
|
99
109
|
/** Completeness checks for phases whose facts are committed incrementally. */
|
|
100
110
|
export function collectFrontendPlanPhaseMissingFacts(input) {
|
|
@@ -155,7 +165,7 @@ export function collectFrontendPlanPhaseMissingFacts(input) {
|
|
|
155
165
|
});
|
|
156
166
|
}
|
|
157
167
|
}
|
|
158
|
-
return missing;
|
|
168
|
+
return enrichFrontendPlanMissingFacts(missing);
|
|
159
169
|
}
|
|
160
170
|
const hasMockApi = facts.some((fact) => fact.kind === "mock-api");
|
|
161
171
|
const allInteractions = collectCanonicalStateFlowNames(facts).interactionNames;
|
|
@@ -182,5 +192,5 @@ export function collectFrontendPlanPhaseMissingFacts(input) {
|
|
|
182
192
|
reason: `interaction ${interaction} has no committed data-flow fact`,
|
|
183
193
|
});
|
|
184
194
|
}
|
|
185
|
-
return missing;
|
|
195
|
+
return enrichFrontendPlanMissingFacts(missing);
|
|
186
196
|
}
|
|
@@ -14,7 +14,8 @@ export const FRONTEND_RECOVERY_IMPORT_MANIFEST_REL_PATH = ".runtime/import-manif
|
|
|
14
14
|
/** Staging directory prefix under `active/` for in-flight child materialization. */
|
|
15
15
|
export const FRONTEND_RECOVERY_STAGING_PREFIX = ".frontend-recovery-staging-";
|
|
16
16
|
export function deriveFrontendRecoveryFailureSource(result) {
|
|
17
|
-
if (result.failureSource === "frontend-
|
|
17
|
+
if (result.failureSource === "frontend-contract-pi" ||
|
|
18
|
+
result.failureSource === "frontend-plan-pi" ||
|
|
18
19
|
result.failureSource === "frontend-design-policy-shell" ||
|
|
19
20
|
result.failureSource === "frontend-implement-pi" ||
|
|
20
21
|
result.failureSource === "frontend-verify-shell") {
|
|
@@ -61,11 +62,14 @@ function computeFrontendRecoveryPlanForSource(input) {
|
|
|
61
62
|
// verification commands / trace. Resetting only verify + its descendants
|
|
62
63
|
// would re-run the same failing checks against the same implementation, so
|
|
63
64
|
// reset from implement: the recovery re-implements and re-verifies.
|
|
64
|
-
const resetRoot = input.failureSource === "frontend-
|
|
65
|
-
? "frontend-
|
|
66
|
-
:
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
const resetRoot = input.failureSource === "frontend-contract-pi"
|
|
66
|
+
? "frontend-contract-pi"
|
|
67
|
+
:
|
|
68
|
+
input.failureSource === "frontend-verify-shell"
|
|
69
|
+
? "frontend-implement-pi"
|
|
70
|
+
: input.failureSource === "frontend-review-pi"
|
|
71
|
+
? "frontend-implement-pi"
|
|
72
|
+
: input.failureSource;
|
|
69
73
|
const resetNodeIds = computeResetClosure(input.spec, resetRoot);
|
|
70
74
|
const resetSet = new Set(resetNodeIds);
|
|
71
75
|
const importedNodeIds = input.spec.tasks
|
|
@@ -196,6 +196,11 @@ export const reviewFindingSchema = z
|
|
|
196
196
|
line: z.number().int().positive().optional(),
|
|
197
197
|
issue: z.string().min(1),
|
|
198
198
|
requiredChange: z.string().min(1).optional(),
|
|
199
|
+
repairAssertions: z.array(z.object({
|
|
200
|
+
check: z.enum(["file-absent", "state-present", "vt-changed"]),
|
|
201
|
+
token: z.string().min(1), source: z.string(),
|
|
202
|
+
expectedRequirementIds: z.array(z.string().min(1)).optional(),
|
|
203
|
+
}).strict()).optional(),
|
|
199
204
|
})
|
|
200
205
|
.strict();
|
|
201
206
|
/** `approve_review` terminal fact payload. Approval may carry informational
|
|
@@ -49,6 +49,7 @@ export const frontendAdmissionCoverageSchema = z.discriminatedUnion("coverageId"
|
|
|
49
49
|
]);
|
|
50
50
|
/** Failure-source values shared with frontend recovery planning (M8). */
|
|
51
51
|
export const frontendAdmissionFailureSourceSchema = z.enum([
|
|
52
|
+
"frontend-contract-pi",
|
|
52
53
|
"frontend-plan-pi",
|
|
53
54
|
"frontend-design-policy-shell",
|
|
54
55
|
"frontend-writer-admission-shell",
|
|
@@ -39,6 +39,7 @@ const reviewFindingSchema = z
|
|
|
39
39
|
line: z.number().int().positive().optional(),
|
|
40
40
|
issue: z.string().min(1),
|
|
41
41
|
requiredChange: z.string().min(1).optional(),
|
|
42
|
+
repairAssertions: z.array(z.record(z.unknown())).optional(),
|
|
42
43
|
})
|
|
43
44
|
.strict();
|
|
44
45
|
const reviewJsonVerdictSchema = z
|
|
@@ -447,6 +447,7 @@ async function executeFrontendRecoveryContinuation(input) {
|
|
|
447
447
|
severity: typeof finding.severity === "string" ? finding.severity : "Important",
|
|
448
448
|
issue: typeof finding.issue === "string" ? finding.issue : "",
|
|
449
449
|
requiredChange: typeof finding.requiredChange === "string" ? finding.requiredChange : "",
|
|
450
|
+
...(Array.isArray(finding.repairAssertions) ? { repairAssertions: finding.repairAssertions } : {}),
|
|
450
451
|
});
|
|
451
452
|
}
|
|
452
453
|
}
|
|
@@ -19,9 +19,20 @@
|
|
|
19
19
|
| 行为有覆盖 | `npm test` |
|
|
20
20
|
| 完整本地交付有效 | `bash scripts/ci.sh` |
|
|
21
21
|
|
|
22
|
+
## 代理与拦截层:校验状态码分布,不是请求条数
|
|
23
|
+
|
|
24
|
+
当验证经过中间层(网关 shim、mock server、反向代理、录制层)时,「请求到了中间层」不等于「中间层在起作用」:
|
|
25
|
+
|
|
26
|
+
- 只看条数会把 **404/5xx 也算成命中**。真实的失败模式是「流量根本没经过」或「经过了但立刻被拒」,两者都产出请求记录。
|
|
27
|
+
- 必须核对 **状态码分布**(如 `200×99` 而非 `200×1 + 404×42`),以及流量是否落在**目标时间窗口**内。
|
|
28
|
+
- 用一条已知成功的直接调用(curl)建立参照,并确认它的路径形态与被测链路的路径形态**一致**——baseUrl 拼接差异(如 `/v1` 重复)会造成静默 404,而外层 runner 可能仍产出看似完整的产物。
|
|
29
|
+
- 尤其警惕「未触发阈值」这类结论:它既可能是真的余量充足,也可能是**根本没走到阈值判断**。两者的证据形态完全不同,不能只凭「无拦截记录」下结论。
|
|
30
|
+
|
|
22
31
|
## Red Flags
|
|
23
32
|
|
|
24
33
|
- 凭意图声明完成
|
|
25
34
|
- 依赖陈旧命令输出
|
|
26
35
|
- 用窄检查支撑宽声明
|
|
27
36
|
- 跳过失败命令的细节
|
|
37
|
+
- 用计数(请求数/命中数)代替状态码与时间窗口证据
|
|
38
|
+
- 把「没有触发拦截」直接读成「余量充足」,未确认流量确实经过了拦截点
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ag as o,ah as n}from"./mermaid.core-5R4WTYSN.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-BjrMCKTH.js";import{_ as i}from"./mermaid.core-5R4WTYSN.js";import"./chunk-5VM5RSS4-181nyzuH.js";import"./chunk-XXDRQBXY-CQSdQAqo.js";import"./chunk-KBJHAD2P-CIB7wrYq.js";import"./chunk-2GRJ4B5K-D4FbDmsM.js";import"./index-RFWQLUE-.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-BjrMCKTH.js";import{_ as i}from"./mermaid.core-5R4WTYSN.js";import"./chunk-5VM5RSS4-181nyzuH.js";import"./chunk-XXDRQBXY-CQSdQAqo.js";import"./chunk-KBJHAD2P-CIB7wrYq.js";import"./chunk-2GRJ4B5K-D4FbDmsM.js";import"./index-RFWQLUE-.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
|