@tea-agent/loop-agent 0.43.0-next.17 → 0.43.0-next.3
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 +9 -140
- package/dist/application/task-lifecycle/advance.js +4 -17
- package/dist/application/task-lifecycle/observe.js +29 -43
- package/dist/application/task-lifecycle/plan-transitions.js +4 -5
- package/dist/build-stamp.json +3 -3
- package/dist/commands/client-recovery.js +55 -20
- package/dist/commands/new-task.js +5 -0
- package/dist/commands/status.js +57 -0
- package/dist/commands/task-contract.js +269 -0
- package/dist/executors/dag-pi-executor.js +230 -269
- package/dist/executors/pi-executor.js +0 -2
- package/dist/executors/pi-sdk-executor.js +39 -77
- package/dist/executors/shell-executor.js +2 -3
- package/dist/executors/shell-write-guard.js +32 -27
- package/dist/shared/pi-context-pressure/extension.js +16 -26
- package/dist/shared/pi-retry-settings.js +0 -40
- package/dist/shared/prompts.js +26 -0
- package/dist/task/config.js +2 -0
- package/dist/task/contract/validate-draft.js +106 -0
- package/dist/task/goal.js +3 -0
- package/dist/task/index.js +12 -0
- package/dist/task/lifecycle.js +1 -0
- package/dist/task/operator/capabilities.js +6 -0
- package/dist/task/operator/envelope.js +2 -0
- package/dist/task/operator/index.js +5 -0
- package/dist/task/operator/registry.js +2 -0
- package/dist/task/operator/types.js +1 -0
- package/dist/task/paths.js +1 -0
- package/dist/task/source-prepare/source-fidelity-pi.js +2 -2
- package/dist/task/source-references.js +23 -48
- package/dist/task/source-state.js +1 -0
- package/dist/task/subagent-guidance.js +1 -0
- package/dist/worker/console/chat/assistant-content.js +2 -23
- package/dist/worker/console/chat/explore-tools.js +0 -13
- package/dist/worker/console/chat/pi-runtime.js +1182 -221
- package/dist/worker/console/chat/routes.js +71 -213
- package/dist/worker/console/chat/sdd-data-alignment.js +9 -65
- package/dist/worker/console/chat/session-catalog.js +0 -32
- package/dist/worker/console/chat/session-store.js +6 -22
- package/dist/worker/console/chat/shortcuts.js +0 -6
- package/dist/worker/console/chat/sift-bridge.js +1 -0
- package/dist/worker/console/chat/tool-preview.js +0 -11
- package/dist/worker/console/chat/tools.js +0 -6
- package/dist/worker/console/console-update-and-init.js +19 -1
- package/dist/worker/console/console-update-runtime.js +20 -5
- package/dist/worker/console/index.js +20 -0
- package/dist/worker/console/operator-actions.js +1 -13
- package/dist/worker/console/routes.js +0 -8
- package/dist/worker/console/server.js +29 -11
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4NXyedy.js → abnfDiagram-N423BO3Z-hUmM_GRf.js} +1 -1
- package/dist/worker/console/static/assets/{arc-NZ3qXvBI.js → arc-Bt-xuieW.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-COZSRvml.js → architectureDiagram-T3A2C74G-DTqkL4DJ.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Ck0Gxi9C.js → blockDiagram-VBNYF7ZC-Cy0MNnzV.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BX5HQ4Ox.js → c4Diagram-5PPSVZJV-DDdlT0Pd.js} +1 -1
- package/dist/worker/console/static/assets/channel-BpzRnZmU.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-CoviemSL.js → chunk-2GRJ4B5K-3JawwNZ6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DbOdOkbZ.js → chunk-2Q5K7J3B-DoTp7Cri.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BsG2pmdQ.js → chunk-5RXB4S5H-CcirhZaS.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DGdvrNm8.js → chunk-5VM5RSS4-VrG21gnP.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BAy7Yd6y.js → chunk-6Q2QTUOP-DDOgIS70.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D9UA7hUb.js → chunk-GF5L2VYU-CBjmgvJ_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-DPz7SZ8V.js → chunk-JWPE2WC7-CCQSht8M.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BmuEOIh7.js → chunk-KBJHAD2P-BpGMkUUV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CIhBWnd8.js → chunk-RYQCIY6F-DNDT41QL.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CScR6mDD.js → chunk-XXDRQBXY-euXnDFbn.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BhlMVqCc.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BhlMVqCc.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BjpfWaUr.js → cose-bilkent-JH36ORCC-BJ6NAeql.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BFQohJgT.js → cynefin-VYW2F7L2-F5JKIvIq.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D3gzRJnS.js → cynefinDiagram-MW4NZA55-Cwmkl_Gi.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-5eojkhoA.js → dagre-VZM6K2ZE-BmCZ8815.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CpD8LWu_.js → diagram-7IWD3JNH-zWA3hUnX.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B8_4qJ2a.js → diagram-B4RE2ZJO-BzA-888F.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-HOsXf0eu.js → diagram-LBJQPF4R-tPNheoch.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BqwRwiWP.js → diagram-Q27KOJAE-Cdi7Fyqp.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BRVCNZe1.js → diagram-UB23O5K3-DFK9VBf1.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Cvb1TgGh.js → ebnfDiagram-BXEA7PRR-xCUr1kpg.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DS0O0qG9.js → erDiagram-JOGREHBK-BANQHY_A.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BHIRvRr3.js → flowDiagram-UKHOOZJN-VHJYEMkP.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DcCJ1JWx.js → ganttDiagram-PKOTCBZU-DiyZQ0C6.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-B9ia5ony.js → gitGraphDiagram-DS77QQ5N-CZtIeBBa.js} +1 -1
- package/dist/worker/console/static/assets/index-BlN6ytLr.css +1 -0
- package/dist/worker/console/static/assets/index-CA-jfWlE.js +449 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-3ogpgegn.js → infoDiagram-6WML65LV-BuSspC0X.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-4tgpQ5SL.js → ishikawaDiagram-WSZJBQD7-DRJZyPFy.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DNfgHqFx.js → journeyDiagram-NVQOT4AX-DIfXIVuE.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Dz7ngC8z.js → kanban-definition-27J2QSJJ-CT10_7Lt.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DI7Jv62E.js → linear-ufemj74H.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-CQA4Nzmo.js → mermaid.core-DEQe8-_m.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CtsMc8Mf.js → mindmap-definition-FAOFIHXS-BRLUrbrC.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-ByppE9yH.js → pegDiagram-VL7TDLO6-BgibKtiF.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B5PtgFgt.js → pieDiagram-7S7Q4E2Y-D3eyfdV9.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Cq4p0k2K.js → quadrantDiagram-CIZ2JOQS-DXeTk5Px.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DjfkyljN.js → railroadDiagram-AXF67PYL-BgXdFEe7.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DzynNRSl.js → requirementDiagram-LRYGKXZP-CseC2A7R.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cj7TCpTJ.js → sankeyDiagram-W5VNT64P-D80PFzfJ.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-C73C6ykA.js → sequenceDiagram-SI44F4Z6-BWzHW7LS.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-BiNLgN2x.js → sizeCapture-X5ZJPWSS-xWQYTLAn.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-UwvjS64-.js → stateDiagram-OKZ733FA-CLxtpZeL.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CiVoq4G-.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CC7DH98r.js → swimlanes-SLNWSIFB-DwugtmWV.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BkdgdpfR.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-LSBXTR70.js → timeline-definition-Z64GVDOM-DHEOwHIe.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BAuxWwGP.js → vennDiagram-T6HMQDX7-pmbZKlBK.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y--E5_gjZx.js → wardleyDiagram-T6FBY63Y-CivHoM5q.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-dJWNmyzF.js → xychartDiagram-ELKLHX3M-BfHIE7o6.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +10 -122
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +0 -7
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +15 -35
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +62 -136
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +68 -159
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +56 -75
- package/dist/worker/console/static-src/operator-chat/useComposer.js +78 -206
- package/dist/worker/console/workspace-context.js +1 -21
- package/dist/worker/observe/static/router.js +1 -1
- package/dist/worker/observe/static/styles.css +4 -7
- package/dist/worker/observe/static/views/dag-inspector.js +19 -12
- package/dist/worker/observe/static/views/dag.js +2 -2
- package/dist/worker/scheduler/index.js +25 -0
- package/dist/workflows/dag/facts.js +4 -0
- package/dist/workflows/dag/frontend-closeout.js +1 -1
- package/dist/workflows/dag/frontend-design-policy.js +1 -2
- package/dist/workflows/dag/frontend-implementation-contract.js +4 -8
- package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
- package/dist/workflows/dag/frontend-repair.js +76 -55
- package/dist/workflows/dag/frontend-review-context.js +1 -2
- package/dist/workflows/dag/frontend-review-scopes.js +12 -34
- package/dist/workflows/dag/frontend-shape-facts.js +2 -12
- package/dist/workflows/dag/frontend-verification-trace.js +42 -8
- package/dist/workflows/dag/index.js +6 -0
- package/dist/workflows/dag/init-hybrid.js +160 -97
- package/dist/workflows/dynamic/index.js +7 -0
- package/docs/architecture/runtime-boundaries.md +3 -3
- package/docs/templates/frontend-implementation-dag.json +8 -9
- package/package.json +3 -7
- package/skills/frontend-bounded-implement/SKILL.md +3 -4
- package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
- package/skills/frontend-contract/references/contract-protocol.md +1 -3
- package/skills/frontend-design-review/SKILL.md +7 -12
- package/skills/frontend-design-review/references/review-checklist.md +7 -7
- package/skills/frontend-plan/SKILL.md +0 -19
- package/skills/frontend-plan/references/decision-contract.md +19 -26
- package/skills/frontend-review/SKILL.md +18 -12
- package/skills/frontend-review/references/review-findings.md +43 -24
- package/dist/shared/pi-provider-recovery.js +0 -182
- package/dist/worker/console/chat/browser-automation.js +0 -363
- package/dist/worker/console/chat/browser-policy.js +0 -113
- package/dist/worker/console/chat/browser-routes.js +0 -162
- package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +0 -44
- package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +0 -89
- package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +0 -202
- package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +0 -79
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +0 -86
- package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +0 -38
- package/dist/worker/console/chat/pi-runtime/inspection-types.js +0 -28
- package/dist/worker/console/chat/pi-runtime/inspection.js +0 -439
- package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +0 -153
- package/dist/worker/console/chat/pi-runtime/sdk-session.js +0 -5
- package/dist/worker/console/chat/scm-routes.js +0 -217
- package/dist/worker/console/chat/scm-service.js +0 -283
- package/dist/worker/console/chat/scm-tools.js +0 -111
- package/dist/worker/console/chat/session-mode-view.js +0 -49
- package/dist/worker/console/chat/session-mode.js +0 -220
- package/dist/worker/console/chat/terminal-routes.js +0 -216
- package/dist/worker/console/chat/terminal-sessions.js +0 -348
- package/dist/worker/console/chat/terminal-tools.js +0 -211
- package/dist/worker/console/static/assets/channel-BxdTlb_5.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-_ByA1Nf8.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-_ByA1Nf8.js +0 -1
- package/dist/worker/console/static/assets/index-B9goN3S5.css +0 -1
- package/dist/worker/console/static/assets/index-wXlQohNs.js +0 -469
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-nLgMZ0D3.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BcYqTady.js +0 -8
- package/dist/worker/console/static-src/operator-chat/active-turn-tail.js +0 -10
- package/dist/worker/console/static-src/operator-chat/chat-link.js +0 -94
- package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +0 -24
- package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +0 -192
- package/dist/worker/console/static-src/operator-chat/process-label.js +0 -41
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +0 -13
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +0 -10
- package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +0 -51
- package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +0 -21
- package/dist/worker/console/workspace-initialization.js +0 -209
- package/dist/workflows/dag/frontend-capacity.js +0 -9
- package/dist/workflows/dag/frontend-plan-completeness.js +0 -186
- package/dist/workflows/dag/frontend-plan-recovery-policy.js +0 -18
|
@@ -37,9 +37,6 @@ import { UserQuestionRegistry } from "./chat/user-questions.js";
|
|
|
37
37
|
import { TodoListRegistry } from "./chat/todos.js";
|
|
38
38
|
import { GoalRegistry } from "./chat/goals.js";
|
|
39
39
|
import { GoalRoundDriver } from "./chat/goal-round-driver.js";
|
|
40
|
-
import { TerminalSessionRegistry } from "./chat/terminal-sessions.js";
|
|
41
|
-
import { ScmService } from "./chat/scm-service.js";
|
|
42
|
-
import { BrowserAutomationService } from "./chat/browser-automation.js";
|
|
43
40
|
import { driveGoalRound, recoverDeferredTurns } from "./chat/routes.js";
|
|
44
41
|
import { createOperationEventStore, } from "./operation-sse.js";
|
|
45
42
|
import { OperationStore } from "../../infrastructure/console/operation-store.js";
|
|
@@ -53,7 +50,6 @@ import { probePiReadiness, } from "./pi-readiness.js";
|
|
|
53
50
|
import { resolveSiblingLoopAgentBin } from "./sibling-controller.js";
|
|
54
51
|
import { NightAuxTicker } from "./night-aux-ticker.js";
|
|
55
52
|
import { createObserveRouteContext, } from "../observe/routes.js";
|
|
56
|
-
import { WorkspaceInitializationService } from "./workspace-initialization.js";
|
|
57
53
|
import { WorkspaceRegistry } from "./workspace-registry.js";
|
|
58
54
|
import { resolveConsoleDefaultTimeoutMs } from "./server.js";
|
|
59
55
|
/**
|
|
@@ -244,7 +240,6 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
244
240
|
readiness: readinessCache,
|
|
245
241
|
getReadiness,
|
|
246
242
|
refreshReadiness,
|
|
247
|
-
workspaceInitialization: new WorkspaceInitializationService({ workspaceId: record.workspaceId, name: record.name, repoRoot, client, operations, events, activeChatTurnCount: () => chatTurnExecutions.size() }),
|
|
248
243
|
observeBaseUrl: deps.observeBaseUrl,
|
|
249
244
|
fetchImpl: deps.fetchImpl,
|
|
250
245
|
getConsoleOrigin: deps.getConsoleOrigin,
|
|
@@ -259,9 +254,7 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
259
254
|
}
|
|
260
255
|
},
|
|
261
256
|
});
|
|
262
|
-
const
|
|
263
|
-
await aimaxAuth.getToken();
|
|
264
|
-
const sddDataAlignment = new SddDataAlignmentService(chatStore, aimaxAuth, deps.fetchImpl);
|
|
257
|
+
const sddDataAlignment = new SddDataAlignmentService(chatStore, new AimaxAuthReader(), deps.fetchImpl);
|
|
265
258
|
const chatEventEpoch = randomBytes(12).toString("hex");
|
|
266
259
|
const chatEvents = createChatEventStore({
|
|
267
260
|
appData,
|
|
@@ -292,13 +285,6 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
292
285
|
operations,
|
|
293
286
|
operationEvents: events,
|
|
294
287
|
});
|
|
295
|
-
// Persistent PTY terminal sessions shared by the model tool (terminal) and
|
|
296
|
-
// the Console UI terminal panel (HTTP API), keyed per chat session owner.
|
|
297
|
-
const terminalRegistry = new TerminalSessionRegistry();
|
|
298
|
-
// Workspace-scoped git source control shared by the model tool (scm) and
|
|
299
|
-
// the Console UI SCM panel (HTTP API).
|
|
300
|
-
const scmService = new ScmService(repoRoot);
|
|
301
|
-
const browserAutomation = new BrowserAutomationService();
|
|
302
288
|
const chatRuntime = new ConsolePiRuntime({
|
|
303
289
|
cwd: repoRoot,
|
|
304
290
|
sessionDir: appData.chatSessions,
|
|
@@ -308,9 +294,6 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
308
294
|
todoRegistry,
|
|
309
295
|
goalRegistry,
|
|
310
296
|
sddDataAlignment,
|
|
311
|
-
terminalRegistry,
|
|
312
|
-
scmService,
|
|
313
|
-
browserAutomation,
|
|
314
297
|
});
|
|
315
298
|
let chatRouteDeps;
|
|
316
299
|
const goalRoundDriver = new GoalRoundDriver((sessionId) => driveGoalRound(chatRouteDeps, sessionId));
|
|
@@ -325,9 +308,6 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
325
308
|
sddDataAlignment,
|
|
326
309
|
goalRoundDriver,
|
|
327
310
|
operationLinker: chatOperationLinker,
|
|
328
|
-
terminals: terminalRegistry,
|
|
329
|
-
scm: scmService,
|
|
330
|
-
browserAutomation,
|
|
331
311
|
appData,
|
|
332
312
|
bootToken: deps.bootToken,
|
|
333
313
|
getConsoleOrigin: deps.getConsoleOrigin,
|
|
@@ -204,7 +204,7 @@ export function parseHashRouteWithFilters(hashValue) {
|
|
|
204
204
|
if (base.view === "dag") {
|
|
205
205
|
const params = new URLSearchParams(raw.includes("?") ? raw.slice(raw.indexOf("?") + 1) : "");
|
|
206
206
|
const nodeId = params.get("node");
|
|
207
|
-
if (nodeId) return { ...base, inspector: { nodeId, tab: params.get("tab") ||
|
|
207
|
+
if (nodeId) return { ...base, inspector: { nodeId, tab: params.get("tab") || "output", executionKey: /^(attempt|pass)-[1-9]\d*$/.test(params.get("attempt") || "") ? params.get("attempt") : null } };
|
|
208
208
|
}
|
|
209
209
|
if (base.view === "dags") {
|
|
210
210
|
const history = parseDagHistoryQuery(raw);
|
|
@@ -1281,9 +1281,6 @@ body.is-resizing-dag-graph {
|
|
|
1281
1281
|
user-select: none;
|
|
1282
1282
|
}
|
|
1283
1283
|
.dag-inspector-header {
|
|
1284
|
-
/* Keep the attempt menu above the content's sticky timeline toolbar. */
|
|
1285
|
-
position: relative;
|
|
1286
|
-
z-index: 6;
|
|
1287
1284
|
display: grid;
|
|
1288
1285
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
1289
1286
|
gap: var(--space-3);
|
|
@@ -5404,10 +5401,10 @@ button.copy-command:focus-visible {
|
|
|
5404
5401
|
.risk-dag-items { display: grid; gap: 6px; padding-top: 8px; }
|
|
5405
5402
|
/* The hosted Console also has .panel styles; keep the reader shell flush. */
|
|
5406
5403
|
#dag-inspector { padding: 0; gap: 0; }
|
|
5407
|
-
.dag-inspector-header .dag-inspector-attempt-bar { flex-wrap:
|
|
5408
|
-
.dag-inspector-header .dag-inspector-attempt-select { min-width:
|
|
5409
|
-
.dag-inspector-header .dag-inspector-attempt-preview { flex: 1 1
|
|
5410
|
-
.dag-inspector-header .dag-inspector-attempt-category
|
|
5404
|
+
.dag-inspector-header .dag-inspector-attempt-bar { flex-wrap: nowrap; background: transparent; }
|
|
5405
|
+
.dag-inspector-header .dag-inspector-attempt-select { min-width: 0; flex: 0 1 126px; }
|
|
5406
|
+
.dag-inspector-header .dag-inspector-attempt-preview { flex: 1 1 auto; flex-wrap: nowrap; padding: 0; }
|
|
5407
|
+
.dag-inspector-header .dag-inspector-attempt-category,
|
|
5411
5408
|
.dag-inspector-header .dag-inspector-attempt-duration { display: none; }
|
|
5412
5409
|
@container (max-width: 560px) {
|
|
5413
5410
|
.process-timeline-toolbar { display: grid; grid-template-columns: minmax(80px, 1fr) 32px 32px 32px; gap: 4px; }
|
|
@@ -518,9 +518,6 @@ function renderDagInspectorForCurrent() {
|
|
|
518
518
|
|
|
519
519
|
function resetInspectorNodeSelection(dagRunId, nodeId) {
|
|
520
520
|
uiState.selectedDagNodeId = nodeId;
|
|
521
|
-
// Resolve the default after loading this node, rather than inheriting
|
|
522
|
-
// another node's tab. Explicit links and later tab clicks still win.
|
|
523
|
-
uiState.dagInspectorTab = null;
|
|
524
521
|
uiState.dagInspectorOpen = true;
|
|
525
522
|
// Each node opens in live-follow mode by default. The explicit floating
|
|
526
523
|
// control can pause it without losing the reader's current position.
|
|
@@ -3340,7 +3337,6 @@ function inspectorTabDefinitions(node = inspectorSelection.node) {
|
|
|
3340
3337
|
const options = node ? attemptSelectorOptions(node) : [];
|
|
3341
3338
|
const hasPassOnlyHistory = options[0]?.key.startsWith("pass-");
|
|
3342
3339
|
const tabDefs = DAG_INSPECTOR_READ_ONLY_TABS.filter(([tab]) => {
|
|
3343
|
-
if (tab === "lineage") return Boolean(inspectorSelection.run?.continuation?.parentRunId);
|
|
3344
3340
|
if (tab === "context") return !["shell", "static"].includes(node?.executor) && !hasPassOnlyHistory;
|
|
3345
3341
|
if (tab === "timeline") return node?.executor !== "static";
|
|
3346
3342
|
return true;
|
|
@@ -3357,9 +3353,6 @@ function activeInspectorTab() {
|
|
|
3357
3353
|
const available = inspectorTabDefinitions().map(([tab]) => tab);
|
|
3358
3354
|
const requested = uiState.dagInspectorTab;
|
|
3359
3355
|
if (available.includes(requested)) return requested;
|
|
3360
|
-
if (requested == null && ["running", "started"].includes(String(inspectorSelection.node?.status ?? "").toLowerCase())) {
|
|
3361
|
-
return available.includes("timeline") ? "timeline" : "overview";
|
|
3362
|
-
}
|
|
3363
3356
|
if (requested === "compare" && available.includes("timeline")) return "timeline";
|
|
3364
3357
|
return ["compare", "context", "timeline"].includes(requested) ? "overview" : "output";
|
|
3365
3358
|
}
|
|
@@ -3590,13 +3583,21 @@ function buildAttemptBar(dagRunId, node) {
|
|
|
3590
3583
|
|
|
3591
3584
|
const bar = el("div", "dag-inspector-attempt-bar");
|
|
3592
3585
|
bar.appendChild(el("label", "dag-inspector-attempt-label", "执行分次"));
|
|
3593
|
-
const choose = (nextKey) => {
|
|
3586
|
+
const choose = (nextKey, source = "select") => {
|
|
3594
3587
|
uiState.dagInspectorAttempt = { dagRunId, nodeId: node.nodeId, key: String(nextKey) };
|
|
3595
3588
|
inspectorNodeSelections.set(`${dagRunId}:${node.nodeId}`, uiState.dagInspectorAttempt);
|
|
3596
3589
|
renderDagInspectorForCurrent();
|
|
3597
3590
|
const nextBar = document.querySelector(".dag-inspector-header .dag-inspector-attempt-bar");
|
|
3598
|
-
nextBar?.querySelector(
|
|
3591
|
+
const control = nextBar?.querySelector(`[data-attempt-action="${source}"]`);
|
|
3592
|
+
(control && !control.disabled ? control : nextBar?.querySelector(".custom-select-trigger"))?.focus({ preventScroll: true });
|
|
3599
3593
|
};
|
|
3594
|
+
const selectedIndex = options.findIndex((option) => option.key === selectedKey);
|
|
3595
|
+
const previous = iconButton("上一次尝试", "ri-arrow-left-s-line");
|
|
3596
|
+
previous.type = "button";
|
|
3597
|
+
previous.dataset.attemptAction = "previous";
|
|
3598
|
+
previous.disabled = selectedIndex <= 0;
|
|
3599
|
+
previous.addEventListener("click", () => choose(options[selectedIndex - 1].key, "previous"));
|
|
3600
|
+
bar.appendChild(previous);
|
|
3600
3601
|
const select = createCustomSelect({
|
|
3601
3602
|
ariaLabel: "选择执行分次",
|
|
3602
3603
|
className: "dag-inspector-attempt-select",
|
|
@@ -3611,6 +3612,12 @@ function buildAttemptBar(dagRunId, node) {
|
|
|
3611
3612
|
// `.value` after assign + change (StubNode handler contract).
|
|
3612
3613
|
select.value = selectedKey;
|
|
3613
3614
|
bar.appendChild(select);
|
|
3615
|
+
const next = iconButton("下一次尝试", "ri-arrow-right-s-line");
|
|
3616
|
+
next.type = "button";
|
|
3617
|
+
next.dataset.attemptAction = "next";
|
|
3618
|
+
next.disabled = selectedIndex >= options.length - 1;
|
|
3619
|
+
next.addEventListener("click", () => choose(options[selectedIndex + 1].key, "next"));
|
|
3620
|
+
bar.appendChild(next);
|
|
3614
3621
|
|
|
3615
3622
|
const selected =
|
|
3616
3623
|
options.find((option) => option.key === selectedKey) ??
|
|
@@ -3622,16 +3629,16 @@ function buildAttemptBar(dagRunId, node) {
|
|
|
3622
3629
|
if (status) {
|
|
3623
3630
|
preview.appendChild(el("span", `badge badge-${status.cls}`, status.label));
|
|
3624
3631
|
}
|
|
3625
|
-
if (status?.cls === "failed") {
|
|
3632
|
+
if (status?.cls === "failed" && data.failureCategory) {
|
|
3626
3633
|
// 仅失败尝试显示原因;部分 runtime 会在成功尝试保留
|
|
3627
3634
|
// failureCategory="success",不能将其误映射为「其他失败原因」。
|
|
3628
3635
|
// 未知失败分类仍 fail-closed,raw token 仅保留在 title 诊断中。
|
|
3629
3636
|
const reason = el(
|
|
3630
3637
|
"span",
|
|
3631
3638
|
"dag-inspector-attempt-category",
|
|
3632
|
-
|
|
3639
|
+
`原因 ${attemptFailureLabel(data.failureCategory)}`,
|
|
3633
3640
|
);
|
|
3634
|
-
|
|
3641
|
+
reason.setAttribute("title", `failureCategory ${data.failureCategory}`);
|
|
3635
3642
|
preview.appendChild(reason);
|
|
3636
3643
|
}
|
|
3637
3644
|
if (data.durationMs !== undefined) {
|
|
@@ -263,7 +263,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
263
263
|
uiState.sessionEvents = [];
|
|
264
264
|
uiState.sessionEventIdentity = null;
|
|
265
265
|
uiState.dagInspectorOpen = false;
|
|
266
|
-
uiState.dagInspectorTab =
|
|
266
|
+
uiState.dagInspectorTab = "output";
|
|
267
267
|
uiState.dagInspectorAttempt = null;
|
|
268
268
|
uiState.dagGraphViewportState = null;
|
|
269
269
|
uiState.dagTimelineViewportState = null;
|
|
@@ -343,7 +343,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
343
343
|
if (routeState?.nodeId && routeState.open !== false && nodes.some((node) => node.nodeId === routeState.nodeId)) {
|
|
344
344
|
uiState.selectedDagNodeId = routeState.nodeId;
|
|
345
345
|
uiState.dagInspectorOpen = true;
|
|
346
|
-
uiState.dagInspectorTab = ["overview", "input", "context", "timeline", "compare", "output", "lineage", "spec-evidence", "prompt-restart"].includes(routeState.tab) ? routeState.tab :
|
|
346
|
+
uiState.dagInspectorTab = ["overview", "input", "context", "timeline", "compare", "output", "lineage", "spec-evidence", "prompt-restart"].includes(routeState.tab) ? routeState.tab : "output";
|
|
347
347
|
uiState.dagInspectorAttempt = { dagRunId, nodeId: routeState.nodeId, key: routeState.executionKey };
|
|
348
348
|
uiState.sessionEventIdentity = makeSessionEventIdentity(dagRunId, routeState.nodeId);
|
|
349
349
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export * from "./paths.js";
|
|
3
|
+
export * from "./lock.js";
|
|
4
|
+
export * from "./trigger.js";
|
|
5
|
+
export * from "./store.js";
|
|
6
|
+
export * from "./lifecycle.js";
|
|
7
|
+
export * from "./doctor.js";
|
|
8
|
+
export * from "./traceability.js";
|
|
9
|
+
export * from "./reservation.js";
|
|
10
|
+
export * from "./workspace-adapter.js";
|
|
11
|
+
export * from "./admission.js";
|
|
12
|
+
export * from "./lease.js";
|
|
13
|
+
export * from "./evidence.js";
|
|
14
|
+
export * from "./night-git-finalizer.js";
|
|
15
|
+
export * from "./dispatcher.js";
|
|
16
|
+
export * from "./scheduler-loop.js";
|
|
17
|
+
export * from "./night-harvest.js";
|
|
18
|
+
export * from "./morning-window.js";
|
|
19
|
+
export * from "./recovery.js";
|
|
20
|
+
export * from "./retry.js";
|
|
21
|
+
export * from "./prepared-attempt-recovery.js";
|
|
22
|
+
export * from "./auto-followup.js";
|
|
23
|
+
export * from "./clock.js";
|
|
24
|
+
export * from "./clock-install.js";
|
|
25
|
+
export { registerSchedulerCommands } from "./cli.js";
|
|
@@ -114,7 +114,7 @@ export function buildFrontendTestHandoff(input) {
|
|
|
114
114
|
* required verification evidence. Anything else downgrades to pending.
|
|
115
115
|
*/
|
|
116
116
|
export function deriveFrontendIntegrationFacts(input) {
|
|
117
|
-
const requiredEvidenceSatisfied = input.verificationTargets.
|
|
117
|
+
const requiredEvidenceSatisfied = input.verificationTargets.length > 0 &&
|
|
118
118
|
input.verificationTargets.every((target) => target.status === "ok");
|
|
119
119
|
if (input.realApiEvidence.present &&
|
|
120
120
|
input.realApiEvidence.passed &&
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { deterministicSha256, } from "./frontend-implementation-contract.js";
|
|
2
2
|
import { pathMatchesPattern } from "../../shared/git-progress.js";
|
|
3
|
-
import { FRONTEND_MAX_IMPLEMENTATION_FILES } from "./frontend-capacity.js";
|
|
4
3
|
const SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
5
4
|
const NPM_PACKAGE_NAME = /^(?:@[a-zA-Z0-9_.-]+\/)?[a-zA-Z0-9][a-zA-Z0-9_.-]*$/;
|
|
6
5
|
/** Max concrete implementation files a single writeSet may carry before the
|
|
7
6
|
* plan is asked to split. Kept at 15: beyond this, a single implement node's
|
|
8
7
|
* read/edit loop inflates provider context toward overflow. */
|
|
9
|
-
const MAX_WRITE_SET_FILES =
|
|
8
|
+
const MAX_WRITE_SET_FILES = 15;
|
|
10
9
|
function normalizeRelativePath(value) {
|
|
11
10
|
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
12
11
|
}
|
|
@@ -345,11 +345,11 @@ export async function deriveFrontendVerifyCommandDirectoryFromRun(runDir) {
|
|
|
345
345
|
}
|
|
346
346
|
/**
|
|
347
347
|
* A behavior verification target must bind a test file: the trace gate
|
|
348
|
-
*
|
|
349
|
-
*
|
|
348
|
+
* proves behavior targets by finding the target id in a real
|
|
349
|
+
* describe/it/test title inside `file`. Enforced at the plan boundary and
|
|
350
350
|
* at materialization (path shape only — the writer may create the file).
|
|
351
351
|
*
|
|
352
|
-
* Only code files count: fixtures and
|
|
352
|
+
* Only code files count (the gate reads titles from source): fixtures and
|
|
353
353
|
* data files such as `test/fixtures/data.json` are rejected even under a
|
|
354
354
|
* test directory, as are helper/support/setup files (`cypress/fixtures/*`,
|
|
355
355
|
* `tests/support/*`, `setup.*`) that never carry describe/it/test titles.
|
|
@@ -2377,7 +2377,7 @@ export async function analyzeFrontendImplementationContract(input) {
|
|
|
2377
2377
|
record.commandLabel = entry.label;
|
|
2378
2378
|
const file = asString(record.file);
|
|
2379
2379
|
if (entry.mode === "behavior" && file && !isFrontendTestFilePath(file)) {
|
|
2380
|
-
fail("retryable-invalid", `invalid-output: verification-target-phase-mismatch: verification target "${vtId}" binds behavior command "${entry.label}" (${entry.commandId}) to non-test file "${file}"; behavior targets must bind a test file (__tests__/, tests?/, e2e/, cypress/, *.test.*, *.spec.*, *.cy.*) so
|
|
2380
|
+
fail("retryable-invalid", `invalid-output: verification-target-phase-mismatch: verification target "${vtId}" binds behavior command "${entry.label}" (${entry.commandId}) to non-test file "${file}"; behavior targets must bind a test file (__tests__/, tests?/, e2e/, cypress/, *.test.*, *.spec.*, *.cy.*) so the trace gate can match the target id in a real describe/it/test title`, candidateJsonSha256);
|
|
2381
2381
|
}
|
|
2382
2382
|
}
|
|
2383
2383
|
pushAction("resolve-verification-command-ids");
|
|
@@ -2509,10 +2509,6 @@ function splitFrontendContractReviewGroup(canonical, fields) {
|
|
|
2509
2509
|
if (!Object.hasOwn(canonical, field))
|
|
2510
2510
|
continue;
|
|
2511
2511
|
const fieldValue = canonical[field];
|
|
2512
|
-
// JSON omits undefined object properties; the index must describe the
|
|
2513
|
-
// persisted contract, not phantom keys retained by normalization.
|
|
2514
|
-
if (fieldValue === undefined)
|
|
2515
|
-
continue;
|
|
2516
2512
|
if (!Array.isArray(fieldValue)) {
|
|
2517
2513
|
const candidate = { ...data, [field]: fieldValue };
|
|
2518
2514
|
if (Object.keys(data).length > 0 &&
|
|
@@ -702,10 +702,10 @@ export async function runFrontendPrewriteGate(input) {
|
|
|
702
702
|
failureCode: "mock-strategy-outside-allowed",
|
|
703
703
|
});
|
|
704
704
|
}
|
|
705
|
-
//
|
|
706
|
-
//
|
|
705
|
+
// Mock command binding (main): a non-not-needed strategy can only be proven
|
|
706
|
+
// by the DAG's frozen Mock verification commands. Block before any write is
|
|
707
|
+
// authorized when none were materialized at generation time.
|
|
707
708
|
if (mockStrategy !== "not-needed" &&
|
|
708
|
-
analysis.canonical.verificationTargets.some((target) => target.mode === "behavior") &&
|
|
709
709
|
(input.config.mockCommandLabels?.length ?? 0) === 0) {
|
|
710
710
|
return finalizePrewrite(input, {
|
|
711
711
|
...basePending,
|
|
@@ -24,50 +24,6 @@ export const frontendCommandCategorySchema = z.enum([
|
|
|
24
24
|
"unit-test",
|
|
25
25
|
"trace",
|
|
26
26
|
]);
|
|
27
|
-
/**
|
|
28
|
-
* Ordered string-blob rules. Order is the contract: the first matching rule wins,
|
|
29
|
-
* so rules that share keywords keep their original precedence. `nodeIdIncludes`
|
|
30
|
-
* and `blobIncludes` are alternatives within one rule (logical OR).
|
|
31
|
-
*/
|
|
32
|
-
const BLOB_RULES = [
|
|
33
|
-
{
|
|
34
|
-
result: "contract",
|
|
35
|
-
nodeIdIncludes: ["contract"],
|
|
36
|
-
blobIncludes: ["contract mismatch", "source binding"],
|
|
37
|
-
},
|
|
38
|
-
{ result: "trace", nodeIdIncludes: ["trace"], blobIncludes: ["trace:"] },
|
|
39
|
-
{ result: "review", nodeIdIncludes: ["review"] },
|
|
40
|
-
{
|
|
41
|
-
result: "unit-test",
|
|
42
|
-
nodeIdIncludes: ["behavior"],
|
|
43
|
-
blobIncludes: ["vitest", "jest", "npm test"],
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
result: "typecheck",
|
|
47
|
-
blobIncludes: ["typecheck", "tsc"],
|
|
48
|
-
blobPatterns: [/error ts\d+/i],
|
|
49
|
-
},
|
|
50
|
-
{ result: "lint", blobIncludes: ["eslint", "lint"] },
|
|
51
|
-
// "vite build"/"next build" are subsumed by "build".
|
|
52
|
-
{ result: "build", blobIncludes: ["build"] },
|
|
53
|
-
{
|
|
54
|
-
result: "component-test",
|
|
55
|
-
blobIncludes: ["testing-library", "component", "render("],
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
result: "dependency",
|
|
59
|
-
blobIncludes: ["package.json", "unapproved dependency", "npm install"],
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
result: "credential",
|
|
63
|
-
blobIncludes: ["credential", "api key", "secret"],
|
|
64
|
-
},
|
|
65
|
-
{ result: "deploy", blobIncludes: ["deploy", "production release"] },
|
|
66
|
-
{
|
|
67
|
-
result: "spec-unclear",
|
|
68
|
-
blobIncludes: ["spec unclear", "requirement ambiguous", "needs human"],
|
|
69
|
-
},
|
|
70
|
-
];
|
|
71
27
|
export function classifyFrontendFailure(input) {
|
|
72
28
|
// AC-2: structured evidence takes priority over string-blob heuristics.
|
|
73
29
|
if (input.failureCategory === "write-guard") {
|
|
@@ -83,21 +39,86 @@ export function classifyFrontendFailure(input) {
|
|
|
83
39
|
return "trace";
|
|
84
40
|
}
|
|
85
41
|
if (input.commandCategory) {
|
|
86
|
-
|
|
87
|
-
|
|
42
|
+
switch (input.commandCategory) {
|
|
43
|
+
case "typecheck":
|
|
44
|
+
return "typecheck";
|
|
45
|
+
case "build":
|
|
46
|
+
return "build";
|
|
47
|
+
case "lint":
|
|
48
|
+
return "lint";
|
|
49
|
+
case "component-test":
|
|
50
|
+
return "component-test";
|
|
51
|
+
case "unit-test":
|
|
52
|
+
return "unit-test";
|
|
53
|
+
}
|
|
88
54
|
}
|
|
89
|
-
// Fallback:
|
|
90
|
-
//
|
|
91
|
-
// overrides the structured checks above.
|
|
55
|
+
// Fallback: string-blob heuristic, preserved in its original branch order so
|
|
56
|
+
// legacy assertions (error TS2304 → typecheck, write-guard → path, etc.) stay green.
|
|
92
57
|
const blob = `${input.nodeId}\n${input.failureCategory ?? ""}\n${input.stdout ?? ""}\n${input.stderr ?? ""}`.toLowerCase();
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
58
|
+
if (input.nodeId.includes("contract") ||
|
|
59
|
+
blob.includes("contract mismatch") ||
|
|
60
|
+
blob.includes("source binding")) {
|
|
61
|
+
return "contract";
|
|
62
|
+
}
|
|
63
|
+
// Shell verification may contain test-runner logs from unrelated tests. Only
|
|
64
|
+
// the shell executor's structured category proves a write-boundary failure.
|
|
65
|
+
if (input.failureCategory === "write-guard") {
|
|
66
|
+
return "path";
|
|
67
|
+
}
|
|
68
|
+
if (input.nodeId.includes("trace") || blob.includes("trace:")) {
|
|
69
|
+
return "trace";
|
|
70
|
+
}
|
|
71
|
+
if (input.nodeId.includes("review")) {
|
|
72
|
+
return "review";
|
|
73
|
+
}
|
|
74
|
+
// Classify the failed verification command before inspecting its output.
|
|
75
|
+
// Full test output includes unrelated passing-test titles such as
|
|
76
|
+
// "secret-shaped" and "write-guard failure".
|
|
77
|
+
if (blob.includes("vitest") ||
|
|
78
|
+
blob.includes("jest") ||
|
|
79
|
+
blob.includes("npm test") ||
|
|
80
|
+
input.nodeId.includes("behavior")) {
|
|
81
|
+
return "unit-test";
|
|
82
|
+
}
|
|
83
|
+
if (blob.includes("typecheck") ||
|
|
84
|
+
blob.includes("tsc") ||
|
|
85
|
+
/error ts\d+/i.test(blob)) {
|
|
86
|
+
return "typecheck";
|
|
87
|
+
}
|
|
88
|
+
if (blob.includes("eslint") || blob.includes("lint")) {
|
|
89
|
+
return "lint";
|
|
90
|
+
}
|
|
91
|
+
if (blob.includes("build") ||
|
|
92
|
+
blob.includes("vite build") ||
|
|
93
|
+
blob.includes("next build")) {
|
|
94
|
+
return "build";
|
|
95
|
+
}
|
|
96
|
+
if (blob.includes("testing-library") ||
|
|
97
|
+
blob.includes("component") ||
|
|
98
|
+
blob.includes("render(")) {
|
|
99
|
+
return "component-test";
|
|
100
|
+
}
|
|
101
|
+
if (blob.includes("package.json") ||
|
|
102
|
+
blob.includes("unapproved dependency") ||
|
|
103
|
+
blob.includes("npm install")) {
|
|
104
|
+
return "dependency";
|
|
105
|
+
}
|
|
106
|
+
if (blob.includes("credential") ||
|
|
107
|
+
blob.includes("api key") ||
|
|
108
|
+
blob.includes("secret")) {
|
|
109
|
+
return "credential";
|
|
110
|
+
}
|
|
111
|
+
if (blob.includes("deploy") || blob.includes("production release")) {
|
|
112
|
+
return "deploy";
|
|
113
|
+
}
|
|
114
|
+
if (blob.includes("spec unclear") ||
|
|
115
|
+
blob.includes("requirement ambiguous") ||
|
|
116
|
+
blob.includes("needs human")) {
|
|
117
|
+
return "spec-unclear";
|
|
99
118
|
}
|
|
100
119
|
if (input.failureCategory === "invalid-output")
|
|
101
120
|
return "contract";
|
|
121
|
+
if (input.failureCategory === "write-guard")
|
|
122
|
+
return "path";
|
|
102
123
|
return "unknown";
|
|
103
124
|
}
|
|
@@ -246,8 +246,7 @@ function assertReviewEvidence(input) {
|
|
|
246
246
|
const trace = input.verificationTrace;
|
|
247
247
|
const expectedTargets = new Map(contract.data.verificationTargets.map((target) => [target.id, target]));
|
|
248
248
|
const actualTargets = Array.isArray(trace?.targets) ? trace.targets : [];
|
|
249
|
-
const mockEvidenceBound = contract.data.
|
|
250
|
-
contract.data.mockApi.strategy === "not-needed" ||
|
|
249
|
+
const mockEvidenceBound = contract.data.mockApi.strategy === "not-needed" ||
|
|
251
250
|
(typeof trace?.mockCommandNodeId === "string" && trace.mockCommandNodeId.length > 0 &&
|
|
252
251
|
Array.isArray(trace?.mockCommandLabels) && trace.mockCommandLabels.length > 0 &&
|
|
253
252
|
Array.isArray(trace?.mockCommandTexts) && trace.mockCommandTexts.length === trace.mockCommandLabels.length &&
|
|
@@ -4,11 +4,6 @@ import path from "node:path";
|
|
|
4
4
|
import { sha256OfCanonicalJson } from "../../task/contract/hash.js";
|
|
5
5
|
import { readCommittedEvents } from "./frontend-typed-event-store.js";
|
|
6
6
|
import { stageTypedEventFact, adoptTypedEventFact } from "./frontend-typed-event-transaction.js";
|
|
7
|
-
const invalidScope = (message) => Object.assign(new Error(`FRONTEND_REVIEW_SCOPE_INVALID: ${message}`), { code: "FRONTEND_REVIEW_SCOPE_INVALID" });
|
|
8
|
-
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
9
|
-
const isArtifactReference = (value) => isRecord(value)
|
|
10
|
-
&& typeof value.path === "string" && value.path.length > 0
|
|
11
|
-
&& typeof value.sha256 === "string" && /^[a-f0-9]{64}$/.test(value.sha256);
|
|
12
7
|
const hash = (text) => createHash("sha256").update(text).digest("hex");
|
|
13
8
|
/** The section index is navigation; reconstruct coverage against the canonical source before using it. */
|
|
14
9
|
export async function loadFrontendReviewScopes(runDir, phase, targetBytes) {
|
|
@@ -17,7 +12,7 @@ export async function loadFrontendReviewScopes(runDir, phase, targetBytes) {
|
|
|
17
12
|
async function read(relative, expected) {
|
|
18
13
|
const absolute = path.resolve(root, relative);
|
|
19
14
|
if (!absolute.startsWith(root + path.sep) || (await lstat(absolute)).isSymbolicLink() || !(await realpath(absolute)).startsWith(root + path.sep))
|
|
20
|
-
throw
|
|
15
|
+
throw Error("FRONTEND_REVIEW_SCOPE_INVALID: artifact escaped run");
|
|
21
16
|
const text = await readFile(absolute, "utf8");
|
|
22
17
|
const sha256 = hash(text);
|
|
23
18
|
if (expected && sha256 !== expected)
|
|
@@ -35,62 +30,45 @@ export async function loadFrontendReviewScopes(runDir, phase, targetBytes) {
|
|
|
35
30
|
throw error;
|
|
36
31
|
}
|
|
37
32
|
const index = JSON.parse(indexArtifact.text);
|
|
38
|
-
if (!isRecord(index) || !isArtifactReference(index.contract) || !Array.isArray(index.sections))
|
|
39
|
-
throw invalidScope("malformed contract index");
|
|
40
33
|
const canonicalArtifact = await read(index.contract.path, index.contract.sha256);
|
|
41
34
|
const canonical = JSON.parse(canonicalArtifact.text);
|
|
42
|
-
if (!isRecord(canonical))
|
|
43
|
-
throw invalidScope("canonical contract must be an object");
|
|
44
35
|
const covered = new Map();
|
|
45
36
|
const sections = [];
|
|
46
37
|
const ids = new Set();
|
|
47
38
|
for (const entry of index.sections) {
|
|
48
|
-
if (!isRecord(entry) || !isArtifactReference(entry) || !Array.isArray(entry.slices))
|
|
49
|
-
throw invalidScope("malformed section index entry");
|
|
50
39
|
if (ids.has(entry.path))
|
|
51
|
-
throw
|
|
40
|
+
throw Error("FRONTEND_REVIEW_SCOPE_INVALID: duplicate section identity");
|
|
52
41
|
ids.add(entry.path);
|
|
53
42
|
const section = await read(entry.path, entry.sha256);
|
|
54
43
|
const payload = JSON.parse(section.text);
|
|
55
|
-
if (!isRecord(payload) || !Array.isArray(payload.slices) || !isRecord(payload.data))
|
|
56
|
-
throw invalidScope(`malformed section: ${entry.path}`);
|
|
57
44
|
if (payload.contractSha256 !== canonicalArtifact.sha256 || sha256OfCanonicalJson(payload.slices) !== sha256OfCanonicalJson(entry.slices))
|
|
58
|
-
throw
|
|
45
|
+
throw Error("FRONTEND_REVIEW_SCOPE_INVALID: unbound section");
|
|
59
46
|
for (const slice of payload.slices) {
|
|
60
|
-
if (!isRecord(slice) || typeof slice.field !== "string" || !Object.hasOwn(canonical, slice.field) || !Object.hasOwn(payload.data, slice.field))
|
|
61
|
-
throw invalidScope(`missing or invalid field reference in ${entry.path}: ${String(slice?.field)}`);
|
|
62
47
|
const value = canonical[slice.field];
|
|
48
|
+
const range = Array.isArray(value) ? value.slice(slice.start, slice.end) : value;
|
|
49
|
+
if (sha256OfCanonicalJson(range) !== sha256OfCanonicalJson(payload.data[slice.field]))
|
|
50
|
+
throw Error("FRONTEND_REVIEW_SCOPE_INVALID: section differs from canonical obligation");
|
|
63
51
|
const positions = covered.get(slice.field) ?? new Set();
|
|
64
52
|
if (Array.isArray(value)) {
|
|
65
53
|
if (!Number.isInteger(slice.start) || !Number.isInteger(slice.end) || slice.start < 0 || slice.end < slice.start || slice.end > value.length || slice.total !== value.length)
|
|
66
|
-
throw
|
|
67
|
-
if ((value.length === 0 && covered.has(slice.field)) || (value.length > 0 && slice.start === slice.end))
|
|
68
|
-
throw invalidScope(`empty or duplicate array coverage: ${slice.field}`);
|
|
54
|
+
throw Error("FRONTEND_REVIEW_SCOPE_INVALID: invalid array coverage");
|
|
69
55
|
for (let i = slice.start; i < slice.end; i++) {
|
|
70
56
|
if (positions.has(i))
|
|
71
|
-
throw
|
|
57
|
+
throw Error("FRONTEND_REVIEW_SCOPE_INVALID: overlapping coverage");
|
|
72
58
|
positions.add(i);
|
|
73
59
|
}
|
|
74
60
|
}
|
|
75
61
|
else {
|
|
76
|
-
if (["start", "end", "total"].some(key => Object.hasOwn(slice, key)))
|
|
77
|
-
throw invalidScope(`scalar field has array range: ${slice.field}`);
|
|
78
62
|
if (positions.size)
|
|
79
|
-
throw
|
|
63
|
+
throw Error("FRONTEND_REVIEW_SCOPE_INVALID: duplicate field");
|
|
80
64
|
positions.add(0);
|
|
81
65
|
}
|
|
82
|
-
const range = Array.isArray(value) ? value.slice(slice.start, slice.end) : value;
|
|
83
|
-
if (sha256OfCanonicalJson(range) !== sha256OfCanonicalJson(payload.data[slice.field]))
|
|
84
|
-
throw invalidScope(`section differs from canonical obligation: ${slice.field}`);
|
|
85
66
|
covered.set(slice.field, positions);
|
|
86
67
|
}
|
|
87
|
-
const sectionFields = new Set(payload.slices.map(slice => slice.field));
|
|
88
|
-
if (Object.keys(payload.data).some(field => !sectionFields.has(field)))
|
|
89
|
-
throw invalidScope(`unindexed section data: ${entry.path}`);
|
|
90
68
|
sections.push(section);
|
|
91
69
|
}
|
|
92
70
|
if (Object.keys(canonical).some(field => !covered.has(field) || covered.get(field).size !== (Array.isArray(canonical[field]) ? canonical[field].length : 1)))
|
|
93
|
-
throw
|
|
71
|
+
throw Error("FRONTEND_REVIEW_SCOPE_INVALID: canonical field coverage incomplete");
|
|
94
72
|
const scopes = Buffer.byteLength(canonicalArtifact.text) <= targetBytes ? [canonicalArtifact] : sections;
|
|
95
73
|
if (phase === "review") {
|
|
96
74
|
const context = await read("contracts/frontend-review-context.json");
|
|
@@ -121,11 +99,11 @@ export function createFrontendReviewScopeProtocol(input) {
|
|
|
121
99
|
committedFacts: () => readCommittedEvents(input.getStore(), input.attemptId),
|
|
122
100
|
assertComplete() { const missing = input.inventory?.scopes.filter(s => !completedScopeIds().has(s.id)) ?? []; if (missing.length)
|
|
123
101
|
throw Object.assign(Error(`FRONTEND_REVIEW_SCOPE_INCOMPLETE: ${missing.map(s => s.id).join(", ")}`), { code: "FRONTEND_REVIEW_SCOPE_INCOMPLETE" }); },
|
|
124
|
-
customTools: !input.inventory ? [] : [{ name: "complete_review_scope", label: "complete_review_scope", description: "After reviewing the supplied complete scope and saving its findings, checkpoint its exact runtime id. This does not approve the implementation or design.", parameters: { type: "object", properties: { id: { type: "string", minLength: 1
|
|
102
|
+
customTools: !input.inventory ? [] : [{ name: "complete_review_scope", label: "complete_review_scope", description: "After reviewing the supplied complete scope and saving its findings, checkpoint its exact runtime id. This does not approve the implementation or design.", parameters: { type: "object", properties: { id: { type: "string", minLength: 1 } }, required: ["id"], additionalProperties: false },
|
|
125
103
|
async execute(callId, params) {
|
|
126
104
|
const scope = input.inventory.scopes.find(s => s.id === params.id);
|
|
127
105
|
if (!scope || !active.has(params.id))
|
|
128
|
-
return receipt({ ok: false, code: "FRONTEND_INPUT_SCOPE_VIOLATION", error: "Only the supplied active review scope may be completed
|
|
106
|
+
return receipt({ ok: false, code: "FRONTEND_INPUT_SCOPE_VIOLATION", error: "Only the supplied active review scope may be completed" });
|
|
129
107
|
await input.inventory.validate();
|
|
130
108
|
const store = input.getStore();
|
|
131
109
|
const requestId = `${input.attemptId}:${callId}:${randomUUID()}`;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { sha256OfCanonicalJson } from "../../task/contract/hash.js";
|
|
2
|
-
import { FRONTEND_MAX_IMPLEMENTATION_FILES, frontendConcreteDeliveryFiles } from "./frontend-capacity.js";
|
|
3
2
|
import { buildFrontendShapeTransitionCapsule, deriveFrontendRuntimeShapeFacts, evaluateFrontendShapeEscalation, parseFrontendShapeTransitionCapsule, } from "./frontend-shape.js";
|
|
4
3
|
import { createTypedEventStore, readCommittedEvents, } from "./frontend-typed-event-store.js";
|
|
5
4
|
import { adoptTypedEventFact, stageTypedEventFact, } from "./frontend-typed-event-transaction.js";
|
|
@@ -223,13 +222,7 @@ function extractRuntimeShapeEvidence(input) {
|
|
|
223
222
|
!sourceText.includes(path) &&
|
|
224
223
|
/(?:\.[A-Za-z0-9]+|\*\*)$/.test(path));
|
|
225
224
|
const routes = canonicalStrings(text.match(/\/(?:[A-Za-z0-9_:[\]-]+\/?)+/g) ?? []);
|
|
226
|
-
|
|
227
|
-
// coverage/UX sessions building a contract the same capacity gate will reject.
|
|
228
|
-
// allowedPaths and text mentions are permissions/evidence, never this count.
|
|
229
|
-
const skeleton = input.tasksById.get("frontend-plan-pi")?.structuredContractOutput?.skeleton;
|
|
230
|
-
const deliveryFiles = frontendConcreteDeliveryFiles(skeleton?.targets?.files);
|
|
231
|
-
const capacitySplit = deliveryFiles.length > FRONTEND_MAX_IMPLEMENTATION_FILES;
|
|
232
|
-
const splitRequired = capacitySplit || /["']splitRequired["']\s*:\s*true|\bsplit-required\s+shape\b/i.test(text);
|
|
225
|
+
const splitRequired = /["']splitRequired["']\s*:\s*true|\bsplit-required\s+shape\b/i.test(text);
|
|
233
226
|
return {
|
|
234
227
|
sourceNodeId: input.sourceNodeId,
|
|
235
228
|
sourceNodeDigest: computeFrontendShapeNodeDigest(node),
|
|
@@ -239,10 +232,7 @@ function extractRuntimeShapeEvidence(input) {
|
|
|
239
232
|
targetPaths,
|
|
240
233
|
routes,
|
|
241
234
|
splitRequired,
|
|
242
|
-
evidenceRefs: [
|
|
243
|
-
...(capacitySplit ? ["dag:frontend-plan-pi:structuredContractOutput.skeleton.targets.files"] : []),
|
|
244
|
-
`node:${input.sourceNodeId}:stdout`,
|
|
245
|
-
],
|
|
235
|
+
evidenceRefs: [`node:${input.sourceNodeId}:stdout`],
|
|
246
236
|
};
|
|
247
237
|
}
|
|
248
238
|
/** Commit a typed, structured runtime fact immediately after a canonical source
|