@tea-agent/loop-agent 0.43.0-next.18 → 0.43.0-next.19
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/AGENTS.md +1 -1
- package/CHANGELOG.md +60 -2308
- package/README.md +7 -0
- package/dist/application/task-lifecycle/advance.js +38 -7
- package/dist/build-stamp.json +3 -3
- package/dist/commands/init.js +13 -9
- package/dist/commands/task-advance.js +32 -0
- package/dist/executors/dag-pi-executor.js +1615 -188
- package/dist/executors/pi-executor.js +85 -3
- package/dist/executors/pi-sdk-executor.js +18 -0
- package/dist/executors/shell-executor.js +92 -7
- package/dist/shared/backend-dogfood-preflight.js +47 -0
- package/dist/shared/dag-failure-category.js +3 -0
- package/dist/shared/operator/capabilities.js +180 -0
- package/dist/task/config-types.js +29 -0
- package/dist/task/contract/project.js +9 -0
- package/dist/task/contract/schema.js +17 -0
- package/dist/task/source-prepare/build-draft.js +60 -0
- package/dist/worker/console/chat/pi-mode-loop-isolation.js +155 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +24 -16
- package/dist/worker/console/chat/pi-runtime/custom-tools/scheduled-goal-tools.js +22 -0
- package/dist/worker/console/chat/pi-runtime/progressive-tools.js +195 -0
- package/dist/worker/console/chat/pi-runtime.js +86 -27
- package/dist/worker/console/chat/routes.js +19 -0
- package/dist/worker/console/chat/scheduled-goal-booking.js +59 -0
- package/dist/worker/console/chat/scheduled-goal-delivery.js +27 -0
- package/dist/worker/console/chat/scheduled-goal-request.js +190 -0
- package/dist/worker/console/chat/session-mode.js +7 -3
- package/dist/worker/console/chat/session-store.js +42 -9
- package/dist/worker/console/chat/tool-preview.js +115 -0
- package/dist/worker/console/chat/turn-order.js +13 -0
- package/dist/worker/console/chat/turn-process.js +32 -30
- package/dist/worker/console/operator-actions.js +50 -0
- package/dist/worker/console/prd-intake-bridge.js +54 -1
- package/dist/worker/console/scheduled-goal-host.js +98 -0
- package/dist/worker/console/scheduled-goal-operation-adapter.js +127 -0
- package/dist/worker/console/server.js +15 -0
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-B_UDknhR.js → abnfDiagram-N423BO3Z-8-j6y-sd.js} +1 -1
- package/dist/worker/console/static/assets/{arc-D6hU0drN.js → arc-eoQiMvuk.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DmVikdRK.js → architectureDiagram-T3A2C74G-C4A3uMcI.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-DydABCRg.js → blockDiagram-VBNYF7ZC-D4zD2F-Q.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DrXKC2Jl.js → c4Diagram-5PPSVZJV-j1RkJziL.js} +1 -1
- package/dist/worker/console/static/assets/channel-ChE7y-cx.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D2-qFxr7.js → chunk-2GRJ4B5K-YBHmhik1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-ChfeF41P.js → chunk-2Q5K7J3B-COfWyo9P.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Drwrz55x.js → chunk-5RXB4S5H-I99OUkHY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CAQGErL7.js → chunk-5VM5RSS4-XKxoJNJ4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DHO38w5U.js → chunk-6Q2QTUOP-DLT_cYx1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-EYIN0EOI.js → chunk-GF5L2VYU-CxcKZ9mV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CrFLKqkT.js → chunk-JWPE2WC7-V1EqXdjY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DMchffoo.js → chunk-KBJHAD2P-DebTtdFp.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DaSvtKQa.js → chunk-RYQCIY6F-B-ivNRDf.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DcW8W9Qj.js → chunk-XXDRQBXY-DC_Ds11b.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BUirrMEi.js → cose-bilkent-JH36ORCC-sWEqKwIb.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BXzO8iXR.js → cynefin-VYW2F7L2-BXa_dcu4.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DAtgaxIn.js → cynefinDiagram-MW4NZA55-C-Mle84F.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-C1re9Noh.js → dagre-VZM6K2ZE-CXPDBITe.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-N8Kj08J9.js → diagram-7IWD3JNH-CC-WJQfa.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-D6BpSLs3.js → diagram-B4RE2ZJO-CDAV5Vs4.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-C2Vlgnl4.js → diagram-LBJQPF4R-CmFiAcNz.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-CMbqtVLS.js → diagram-Q27KOJAE-DPBZHuyn.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-A770Eb-0.js → diagram-UB23O5K3-jUlm_Ds3.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-LV2w_2pT.js → ebnfDiagram-BXEA7PRR-DWhQ3mfY.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-bwqf56ah.js → erDiagram-JOGREHBK-Tr2gMqet.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BnVtHhZh.js → flowDiagram-UKHOOZJN-DJjVQHPA.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BGVToEqc.js → ganttDiagram-PKOTCBZU-D74dQ4u0.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DL2l7Vne.js → gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js} +1 -1
- package/dist/worker/console/static/assets/index-BWkIfcrK.css +1 -0
- package/dist/worker/console/static/assets/{index-B_V4wvXs.js → index-Cdkvw_H6.js} +97 -97
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BWIpubOx.js → infoDiagram-6WML65LV-ILCbxJyb.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DjYBD8vv.js → ishikawaDiagram-WSZJBQD7-DeuWBQ89.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-C0_mBaHb.js → journeyDiagram-NVQOT4AX-CF5ih8Fk.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Cy71zbX6.js → kanban-definition-27J2QSJJ-C7yOSuRO.js} +1 -1
- package/dist/worker/console/static/assets/{linear-Dt3_w3Vn.js → linear-BDZ9riWi.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-DnNOlfEP.js → mermaid.core-7pKqYtpZ.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DAbTspwq.js → mindmap-definition-FAOFIHXS-LeJDybSU.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-NNiM141p.js → pegDiagram-VL7TDLO6-BJvT3pMD.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CN80Z1Do.js → pieDiagram-7S7Q4E2Y-_rGqpMan.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Bxyfh0JU.js → quadrantDiagram-CIZ2JOQS-DPcSv5aA.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BlWnC79M.js → railroadDiagram-AXF67PYL-Drxx4hkJ.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BrEu4P_e.js → requirementDiagram-LRYGKXZP-BCTUdU4z.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Bt70igBr.js → sankeyDiagram-W5VNT64P-B6wzbZmB.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-C8DQR-r9.js → sequenceDiagram-SI44F4Z6-BGt8d3QQ.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DEMinJAT.js → sizeCapture-X5ZJPWSS-7nlhJKo7.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-DDdDBzQc.js → stateDiagram-OKZ733FA-Cv2stqMA.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-G2wcBs3T.js → swimlanes-SLNWSIFB-qDAo4Yc1.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Dzn4g2gJ.js → timeline-definition-Z64GVDOM-CKbDNKTr.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-5v2rP9oO.js → vennDiagram-T6HMQDX7-DI-9EHic.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-B4EOx7ew.js → wardleyDiagram-T6FBY63Y-BrzzRDpX.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CQ1v8eYP.js → xychartDiagram-ELKLHX3M-BclH5hGh.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +41 -11
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +3 -17
- package/dist/worker/console/static-src/operator-chat/timeline-merge.js +29 -0
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +2 -5
- package/dist/worker/console/workspace-context.js +11 -0
- package/dist/worker/observe/static/operator-chrome.js +3 -1
- package/dist/worker/scheduler/scheduled-goal-dispatch.js +117 -0
- package/dist/worker/scheduler/scheduled-goal-evidence.js +167 -0
- package/dist/worker/scheduler/scheduled-goal-recovery.js +62 -0
- package/dist/worker/scheduler/scheduled-goal-store.js +699 -0
- package/dist/worker/scheduler/scheduled-goal-supervisor.js +132 -0
- package/dist/worker/scheduler/scheduled-goal-time.js +102 -0
- package/dist/worker/scheduler/scheduled-goal-types.js +95 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +6 -24
- package/dist/workflows/dag/backend-test-plan-protocol.js +82 -5
- package/dist/workflows/dag/dag-retry-schema.js +11 -0
- package/dist/workflows/dag/frontend-closeout.js +4 -2
- package/dist/workflows/dag/frontend-committed-facts.js +461 -0
- package/dist/workflows/dag/frontend-durable-tools.js +15 -3
- package/dist/workflows/dag/frontend-implementation-contract.js +365 -8
- package/dist/workflows/dag/frontend-plan-canary.js +53 -0
- package/dist/workflows/dag/frontend-plan-decision-contract.js +803 -0
- package/dist/workflows/dag/frontend-plan-render.js +0 -2
- package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +8 -61
- package/dist/workflows/dag/frontend-recovery-run.js +57 -0
- package/dist/workflows/dag/frontend-review-context.js +43 -70
- package/dist/workflows/dag/frontend-risk.js +92 -10
- package/dist/workflows/dag/frontend-session-budget.js +117 -3
- package/dist/workflows/dag/frontend-shape.js +11 -55
- package/dist/workflows/dag/frontend-test-execution-evidence.js +35 -10
- package/dist/workflows/dag/frontend-typed-event-store.js +32 -25
- package/dist/workflows/dag/frontend-verification-trace.js +11 -2
- package/dist/workflows/dag/frontend-writer-admission.js +3 -47
- package/dist/workflows/dag/frontend-writer-status.js +0 -23
- package/dist/workflows/dag/init-hybrid.js +182 -185
- package/dist/workflows/dag/lifecycle.js +7 -2
- package/dist/workflows/dag/node-execution.js +48 -13
- package/dist/workflows/dag/rerun-plan.js +10 -0
- package/dist/workflows/dag/rerun-task.js +77 -1
- package/dist/workflows/dag/retry-policy.js +18 -0
- package/dist/workflows/dag/scheduler.js +2 -4
- package/dist/workflows/dag/types.js +23 -0
- package/docs/README.md +1 -0
- package/docs/init-surface.manifest.json +1 -0
- package/docs/operations/README.md +2 -0
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +2 -2
- package/docs/templates/backend-test-dag.json +14 -10
- package/docs/templates/frontend-implementation-contract.schema.json +0 -7
- package/docs/templates/frontend-implementation-dag.json +8 -9
- package/package.json +6 -3
- package/skills/frontend-bounded-implement/SKILL.md +3 -4
- 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 +8 -18
- package/skills/frontend-plan/references/decision-contract.md +19 -26
- package/skills/frontend-review/SKILL.md +21 -25
- package/skills/frontend-review/references/review-findings.md +48 -24
- package/dist/worker/console/static/assets/channel-Drecd94a.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-8Udu0t8-.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-8Udu0t8-.js +0 -1
- package/dist/worker/console/static/assets/index-DcudonhZ.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CyCiS0Lt.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-U0lhi6N0.js +0 -8
- package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -975
|
@@ -1023,7 +1023,74 @@ export function shouldRetryWithFallback(stderr, stdout) {
|
|
|
1023
1023
|
stdout,
|
|
1024
1024
|
timedOut: false,
|
|
1025
1025
|
});
|
|
1026
|
-
return [
|
|
1026
|
+
return [
|
|
1027
|
+
"quota",
|
|
1028
|
+
"rate-limit",
|
|
1029
|
+
"unavailable",
|
|
1030
|
+
"upstream-unavailable",
|
|
1031
|
+
"network",
|
|
1032
|
+
].includes(category);
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Provider/transport failure text, with model-authored content excluded.
|
|
1036
|
+
*
|
|
1037
|
+
* The captured stdout is a JSONL session transcript: it carries assistant text,
|
|
1038
|
+
* tool arguments and saved findings verbatim. Matching provider vocabulary
|
|
1039
|
+
* against that whole stream let model content drive the verdict (smoke r28: the
|
|
1040
|
+
* reviewer's own finding id `FDR-R28-UNAUTHORIZED-APP-CSS` was read as an auth
|
|
1041
|
+
* failure, which is not retryable, so a completed review was reported as a node
|
|
1042
|
+
* error). Only error-shaped channels may classify a transport failure:
|
|
1043
|
+
* stderr, error events, and the error fields of a message record.
|
|
1044
|
+
*/
|
|
1045
|
+
export function extractProviderFailureText(stdout) {
|
|
1046
|
+
const parts = [];
|
|
1047
|
+
for (const rawLine of stdout.split(/\r?\n/)) {
|
|
1048
|
+
const line = rawLine.trim();
|
|
1049
|
+
if (!line)
|
|
1050
|
+
continue;
|
|
1051
|
+
if (!line.startsWith("{")) {
|
|
1052
|
+
// Non-JSON child output (warnings, CLI errors) is not model-authored.
|
|
1053
|
+
parts.push(line);
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
let record;
|
|
1057
|
+
try {
|
|
1058
|
+
const parsed = JSON.parse(line);
|
|
1059
|
+
if (!isRecord(parsed))
|
|
1060
|
+
continue;
|
|
1061
|
+
record = parsed;
|
|
1062
|
+
}
|
|
1063
|
+
catch {
|
|
1064
|
+
// An unparseable line may be truncated model content: skip it.
|
|
1065
|
+
continue;
|
|
1066
|
+
}
|
|
1067
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
1068
|
+
const isErrorEvent = type === "error" || type === "agent_error" || type.endsWith("_error");
|
|
1069
|
+
const errorMessage = typeof record.errorMessage === "string" ? record.errorMessage : "";
|
|
1070
|
+
const errorField = record.error;
|
|
1071
|
+
const message = isRecord(record.message) ? record.message : undefined;
|
|
1072
|
+
const messageErrorMessage = typeof message?.errorMessage === "string" ? message.errorMessage : "";
|
|
1073
|
+
const messageErrored = message?.stopReason === "error";
|
|
1074
|
+
if (!isErrorEvent &&
|
|
1075
|
+
!errorMessage &&
|
|
1076
|
+
errorField === undefined &&
|
|
1077
|
+
!messageErrorMessage) {
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
parts.push([type, errorMessage, messageErrorMessage, messageErrored ? "stopReason error" : ""]
|
|
1081
|
+
.filter(Boolean)
|
|
1082
|
+
.join(" "));
|
|
1083
|
+
if (typeof errorField === "string") {
|
|
1084
|
+
parts.push(errorField);
|
|
1085
|
+
}
|
|
1086
|
+
else if (isRecord(errorField)) {
|
|
1087
|
+
parts.push(["message", "errorMessage", "reason", "code", "status", "type"]
|
|
1088
|
+
.map((key) => (errorField[key] === undefined ? "" : String(errorField[key])))
|
|
1089
|
+
.filter(Boolean)
|
|
1090
|
+
.join(" "));
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
return parts.join("\n");
|
|
1027
1094
|
}
|
|
1028
1095
|
export function classifyPiFailure(input) {
|
|
1029
1096
|
if (input.exitCode === 0 && input.assistantText.trim())
|
|
@@ -1034,7 +1101,9 @@ export function classifyPiFailure(input) {
|
|
|
1034
1101
|
return "timeout";
|
|
1035
1102
|
if (input.outputTooLarge)
|
|
1036
1103
|
return "output-too-large";
|
|
1037
|
-
|
|
1104
|
+
// Provider/transport verdicts read error-shaped channels only; the raw stdout
|
|
1105
|
+
// stream embeds assistant text, tool arguments and findings verbatim.
|
|
1106
|
+
const combined = `${input.stderr}\n${extractProviderFailureText(input.stdout)}`.toLowerCase();
|
|
1038
1107
|
// Context overflow (400 request-too-large / context window exceeded).
|
|
1039
1108
|
// Must be detected BEFORE the generic rate-limit/quota phrases: an overflow
|
|
1040
1109
|
// error often contains "too many tokens" / "maximum context" which would
|
|
@@ -1052,8 +1121,21 @@ export function classifyPiFailure(input) {
|
|
|
1052
1121
|
return "rate-limit";
|
|
1053
1122
|
if (/quota|usage limit|reached[^\n.]{0,60}limit|5\s*小时|5小时|insufficient_quota|billing/.test(combined))
|
|
1054
1123
|
return "quota";
|
|
1055
|
-
|
|
1124
|
+
// Error words must stand alone: the captured stream also contains
|
|
1125
|
+
// model-authored identifiers, and smoke r28 lost a whole run because the
|
|
1126
|
+
// reviewer's own finding id `FDR-R28-UNAUTHORIZED-APP-CSS` matched a bare
|
|
1127
|
+
// `\bunauthorized\b`, turning a retryable empty-output into a terminal
|
|
1128
|
+
// `auth`. Identifier delimiters (`-`/`_`/alphanumerics) disqualify a match.
|
|
1129
|
+
if (/(?<![\w-])unauthorized(?![\w-])|\bhttp\s*40[13]\b|invalid api key|(?<![\w-])authentication (?:failed|required|error)(?![\w-])|(?<![\w-])auth(?:entication)? failed(?![\w-])/.test(combined))
|
|
1056
1130
|
return "auth";
|
|
1131
|
+
// Gateway reachability first: a 502/503 body means the provider was
|
|
1132
|
+
// unreachable, not that the request lacked a capability. Reporting the
|
|
1133
|
+
// generic category here previously let an upstream outage surface as the
|
|
1134
|
+
// plan ladder's `unsupported-provider-capability`, which reads as a model
|
|
1135
|
+
// capability mismatch and sent the incident diagnosis in the wrong
|
|
1136
|
+
// direction (2026-09-11).
|
|
1137
|
+
if (/\bhttp\s*50[23]\b|(?:^|\D)50[23](?:\D|$)|service temporarily unavailable|bad gateway|upstream (?:is )?(?:unavailable|down|unreachable)|网关(?:暂时)?不可用/.test(combined))
|
|
1138
|
+
return "upstream-unavailable";
|
|
1057
1139
|
if (/unknown provider|unknown model|(?:provider|model|service|gateway)\s+(?:is\s+)?unavailable|overloaded|at capacity|temporarily unavailable/.test(combined))
|
|
1058
1140
|
return "unavailable";
|
|
1059
1141
|
if (/network\s*(?:error|failure|issue|timeout|unreachable|reset|abort)|networkerror|connection (?:reset|error|refused|closed|timed\s*out)|request timed out|socket hang up|econn\w*|etimedout|fetch failed|dns (?:error|failure|lookup failed)/.test(combined))
|
|
@@ -322,6 +322,21 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
322
322
|
extensionToolExtras.push(...computeExtensionToolAllowlistExtras([{ name: SIFT_RETRIEVE_TOOL_NAME }], input.toolNames));
|
|
323
323
|
}
|
|
324
324
|
const sessionToolNames = [...input.toolNames, ...extensionToolExtras];
|
|
325
|
+
// Pre-session protocol self-check: createAgentSession silently drops a
|
|
326
|
+
// custom tool whose name is absent from this allowlist (the post-create
|
|
327
|
+
// mismatch check cannot see the omission either — expected and actual are
|
|
328
|
+
// both false). The model would then be prompted to call tools that do not
|
|
329
|
+
// exist and burn attempts discovering it (smoke r10/r14 class of failure),
|
|
330
|
+
// so fail before any model call, naming the dropped tools.
|
|
331
|
+
if (input.requireAllowlistedCustomTools && Array.isArray(input.customTools) && input.customTools.length > 0) {
|
|
332
|
+
const allowlist = new Set(sessionToolNames);
|
|
333
|
+
const dropped = input.customTools
|
|
334
|
+
.map((tool) => (isRecord(tool) && typeof tool.name === "string" ? tool.name : undefined))
|
|
335
|
+
.filter((name) => typeof name === "string" && !allowlist.has(name));
|
|
336
|
+
if (dropped.length > 0) {
|
|
337
|
+
throw new Error(`pi session custom tools would be silently dropped (absent from the activation allowlist): ${dropped.join(", ")}; allowlist: [${sessionToolNames.join(", ")}]`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
325
340
|
if (input.requireWriterCustomTools) {
|
|
326
341
|
if (!Array.isArray(input.customTools) || input.customTools.length === 0) {
|
|
327
342
|
throw new Error("pi writer tool policy missing customTools; refusing to create uncontrolled writer session");
|
|
@@ -707,6 +722,9 @@ async function executeSingleSdkAttemptInternal(options) {
|
|
|
707
722
|
? { customTools: writerCustomTools }
|
|
708
723
|
: {}),
|
|
709
724
|
...(requireWriterCustomTools ? { requireWriterCustomTools: true } : {}),
|
|
725
|
+
...(options.writerToolPolicy?.requireAllowlistedCustomTools
|
|
726
|
+
? { requireAllowlistedCustomTools: true }
|
|
727
|
+
: {}),
|
|
710
728
|
...(options.piExtensionPaths && options.piExtensionPaths.length > 0
|
|
711
729
|
? { additionalExtensionPaths: options.piExtensionPaths }
|
|
712
730
|
: {}),
|
|
@@ -470,6 +470,54 @@ function parseCheckRepoSubcheckDurations(result) {
|
|
|
470
470
|
}
|
|
471
471
|
return durations;
|
|
472
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* Frozen-command evidence for the review node: which commandId/label/lane each
|
|
475
|
+
* executed command belongs to, its exit status, and the lint lane's status
|
|
476
|
+
* (explicitly `not-configured` when the task declares no lint commands).
|
|
477
|
+
*
|
|
478
|
+
* The review protocol requires exit evidence ("typecheck, build, and test are
|
|
479
|
+
* required successful final exits") while the verification trace only proves
|
|
480
|
+
* command/file/target binding, so this artifact is what makes that requirement
|
|
481
|
+
* satisfiable at all (smoke r31: the reviewer had to request changes for a
|
|
482
|
+
* check no context could support).
|
|
483
|
+
*/
|
|
484
|
+
export function buildFrontendVerificationEvidence(input) {
|
|
485
|
+
const directory = buildFrontendVerifyCommandDirectory({
|
|
486
|
+
staticLabels: input.bundle.staticEvidence?.commandLabels ?? [],
|
|
487
|
+
staticCommandTexts: input.bundle.staticEvidence?.commandTexts ?? [],
|
|
488
|
+
behaviorLabels: input.bundle.behaviorEvidence?.commandLabels ?? [],
|
|
489
|
+
behaviorCommandTexts: input.bundle.behaviorEvidence?.commandTexts ?? [],
|
|
490
|
+
mockLabels: input.bundle.mockEvidence?.commandLabels ?? [],
|
|
491
|
+
mockCommandTexts: input.bundle.mockEvidence?.commandTexts ?? [],
|
|
492
|
+
lintLabels: input.bundle.lintEvidence?.commandLabels ?? [],
|
|
493
|
+
lintCommandTexts: input.bundle.lintEvidence?.commandTexts ?? [],
|
|
494
|
+
});
|
|
495
|
+
const entryByCommand = new Map(directory.map((entry) => [entry.command.trim(), entry]));
|
|
496
|
+
return {
|
|
497
|
+
schemaVersion: 1,
|
|
498
|
+
schemaId: "frontend-verification-evidence-v1",
|
|
499
|
+
nodeId: input.nodeId,
|
|
500
|
+
runId: input.runId,
|
|
501
|
+
capturedAt: new Date().toISOString(),
|
|
502
|
+
allPassed: input.results.every((result) => result.ok),
|
|
503
|
+
lintStatus: input.lintStatus,
|
|
504
|
+
lintConfigured: input.lintConfigured,
|
|
505
|
+
commands: input.results.map((result, index) => {
|
|
506
|
+
const entry = entryByCommand.get(result.command.trim());
|
|
507
|
+
return {
|
|
508
|
+
index: index + 1,
|
|
509
|
+
commandId: entry?.commandId ?? null,
|
|
510
|
+
label: entry?.label ?? null,
|
|
511
|
+
lane: entry?.lane ?? null,
|
|
512
|
+
command: result.command,
|
|
513
|
+
ok: result.ok,
|
|
514
|
+
exitCode: result.exitCode,
|
|
515
|
+
failureCategory: result.failureCategory,
|
|
516
|
+
reused: result.reused === true,
|
|
517
|
+
};
|
|
518
|
+
}),
|
|
519
|
+
};
|
|
520
|
+
}
|
|
473
521
|
export function buildShellResultSummaryMarkdown(input) {
|
|
474
522
|
const lines = [
|
|
475
523
|
"# Shell execution summary",
|
|
@@ -1915,7 +1963,7 @@ async function executePipelineCommands(input, meta, overrideCommands) {
|
|
|
1915
1963
|
* of inventing refs. */
|
|
1916
1964
|
async function deriveUnresolvedVerificationEntrypointRefs(runDir, frozenCommandIds) {
|
|
1917
1965
|
try {
|
|
1918
|
-
const { restorePlanPatchFromCommittedFacts } = await import("../workflows/dag/frontend-
|
|
1966
|
+
const { restorePlanPatchFromCommittedFacts } = await import("../workflows/dag/frontend-implementation-contract.js");
|
|
1919
1967
|
const records = await readTypedEventStoreFromJsonl(path.join(runDir, "frontend-plan-pi", "plan-typed-facts.jsonl"));
|
|
1920
1968
|
const patch = restorePlanPatchFromCommittedFacts(records);
|
|
1921
1969
|
const targets = patch
|
|
@@ -2127,6 +2175,27 @@ async function executeFrontendVerificationBundle(input, meta) {
|
|
|
2127
2175
|
command: result.command,
|
|
2128
2176
|
reused: result.reused === true,
|
|
2129
2177
|
}));
|
|
2178
|
+
// Auditable per-command evidence for the review node (smoke r31): the review
|
|
2179
|
+
// protocol treats shell exit status as authoritative, but the review context
|
|
2180
|
+
// only carried the verification trace's command/file/target *binding*
|
|
2181
|
+
// status, so a reviewer could not confirm the frozen commands actually
|
|
2182
|
+
// exited 0 and had to request changes for missing evidence. Written before
|
|
2183
|
+
// either the failure or the success path returns.
|
|
2184
|
+
const lintConfigured = (bundle.lintCommands?.length ?? 0) > 0;
|
|
2185
|
+
await writeDagRunJsonArtifact(meta.runDir, "contracts/frontend-verification-evidence.json", buildFrontendVerificationEvidence({
|
|
2186
|
+
nodeId: input.task.id,
|
|
2187
|
+
runId: meta.runId,
|
|
2188
|
+
bundle,
|
|
2189
|
+
results,
|
|
2190
|
+
// The review protocol's lint vocabulary is
|
|
2191
|
+
// passed | baseline-debt | failed | unavailable; a task that declares
|
|
2192
|
+
// no lint commands reports `unavailable` plus lintConfigured:false
|
|
2193
|
+
// rather than inventing a value outside that catalog (smoke r33).
|
|
2194
|
+
lintStatus: lintConfigured
|
|
2195
|
+
? (lintAssessment?.status ?? "unavailable")
|
|
2196
|
+
: "unavailable",
|
|
2197
|
+
lintConfigured,
|
|
2198
|
+
}));
|
|
2130
2199
|
// Lint baseline-debt is tolerated, so a lint-only failure must not become the
|
|
2131
2200
|
// verification failure. A shared lint/static or lint/behavior command is
|
|
2132
2201
|
// different: once reused by a non-lint lane, its failure belongs to that lane
|
|
@@ -2320,16 +2389,22 @@ async function readRunDirJson(runDir, relativePath) {
|
|
|
2320
2389
|
}
|
|
2321
2390
|
/** Committed typed review terminal kinds from the review node's typed event
|
|
2322
2391
|
* store (AC-003: typed terminal tools are the only authoritative verdict). */
|
|
2323
|
-
async function collectReviewTerminalKinds(runDir, reviewNodeId) {
|
|
2392
|
+
export async function collectReviewTerminalKinds(runDir, reviewNodeId) {
|
|
2324
2393
|
try {
|
|
2325
2394
|
const records = await readTypedEventStoreFromJsonl(path.join(runDir, reviewNodeId, "review-typed-facts.jsonl"));
|
|
2395
|
+
// The store also holds findings and scope checkpoints; only the verdict
|
|
2396
|
+
// kinds decide "missing or ambiguous" (fail closed on 0 or 2+).
|
|
2397
|
+
const terminalKinds = new Set([
|
|
2398
|
+
"approve_review",
|
|
2399
|
+
"request_review_changes",
|
|
2400
|
+
]);
|
|
2326
2401
|
return records
|
|
2327
2402
|
.filter((record) => record.phase === "committed")
|
|
2328
2403
|
.map((record) => {
|
|
2329
2404
|
const kind = record.fact.kind;
|
|
2330
2405
|
return typeof kind === "string" ? kind : "";
|
|
2331
2406
|
})
|
|
2332
|
-
.filter((kind) => kind
|
|
2407
|
+
.filter((kind) => terminalKinds.has(kind));
|
|
2333
2408
|
}
|
|
2334
2409
|
catch {
|
|
2335
2410
|
return [];
|
|
@@ -2339,16 +2414,24 @@ async function collectReviewTerminalKinds(runDir, reviewNodeId) {
|
|
|
2339
2414
|
* event store (M8: the committed approve_design / request_design_changes fact
|
|
2340
2415
|
* is the only authoritative verdict; the legacy first-line VERDICT text is
|
|
2341
2416
|
* never read for writer admission). Fail-closed on a missing/unreadable store. */
|
|
2342
|
-
async function collectDesignTerminalKinds(runDir, designNodeId) {
|
|
2417
|
+
export async function collectDesignTerminalKinds(runDir, designNodeId) {
|
|
2343
2418
|
try {
|
|
2344
2419
|
const records = await readTypedEventStoreFromJsonl(path.join(runDir, designNodeId, "design-typed-facts.jsonl"));
|
|
2420
|
+
// The store also holds findings and scope checkpoints; only the verdict
|
|
2421
|
+
// kinds decide "missing or ambiguous" (fail closed on 0 or 2+). Without
|
|
2422
|
+
// this filter any recorded finding makes admission read the committed
|
|
2423
|
+
// verdict as ambiguous and blocks the writer (r11 decision-path smoke).
|
|
2424
|
+
const terminalKinds = new Set([
|
|
2425
|
+
"approve_design",
|
|
2426
|
+
"request_design_changes",
|
|
2427
|
+
]);
|
|
2345
2428
|
return records
|
|
2346
2429
|
.filter((record) => record.phase === "committed")
|
|
2347
2430
|
.map((record) => {
|
|
2348
2431
|
const kind = record.fact.kind;
|
|
2349
2432
|
return typeof kind === "string" ? kind : "";
|
|
2350
2433
|
})
|
|
2351
|
-
.filter((kind) => kind
|
|
2434
|
+
.filter((kind) => terminalKinds.has(kind));
|
|
2352
2435
|
}
|
|
2353
2436
|
catch {
|
|
2354
2437
|
return [];
|
|
@@ -2511,7 +2594,7 @@ async function executeFrontendDesignPolicy(input, meta) {
|
|
|
2511
2594
|
// M9a the plan node's assistantText is a pure Markdown narrative; the
|
|
2512
2595
|
// compile authority is the committed plan ledger ⊕ runtime skeleton.
|
|
2513
2596
|
// Fail closed on missing committed facts — never fall back to text.
|
|
2514
|
-
const { readCommittedOriginFacts, checkCommittedOriginFacts } = await import("../workflows/dag/frontend-
|
|
2597
|
+
const { readCommittedOriginFacts, checkCommittedOriginFacts } = await import("../workflows/dag/frontend-committed-facts.js");
|
|
2515
2598
|
// A+B (AC-001/003): fail-closed committed contract/scout typed facts.
|
|
2516
2599
|
// Missing/uncommitted facts block here; never fall back to text parsing.
|
|
2517
2600
|
const committedContractFacts = await readCommittedOriginFacts(meta.runDir, "frontend-contract-pi", "contract-typed-facts.jsonl");
|
|
@@ -2630,7 +2713,7 @@ async function executeFrontendDesignPolicy(input, meta) {
|
|
|
2630
2713
|
});
|
|
2631
2714
|
}
|
|
2632
2715
|
// Byte freshness is necessary but not sufficient: require the same ledger
|
|
2633
|
-
// semantic checks as the
|
|
2716
|
+
// semantic checks as the canonical admission policy before production writer
|
|
2634
2717
|
// admission can be materialized.
|
|
2635
2718
|
if (meta.spec.sourceBinding) {
|
|
2636
2719
|
const ledgerCheck = await checkFrontendSourceFidelityLedger({
|
|
@@ -3016,6 +3099,8 @@ async function executeFrontendCloseout(input, meta) {
|
|
|
3016
3099
|
coverageMatrix: verificationTargets.map((target, index) => ({
|
|
3017
3100
|
verificationTargetId: trace?.targets?.[index]?.id ?? `vt-${index}`,
|
|
3018
3101
|
status: target.status === "ok" ? "passed" : "failed",
|
|
3102
|
+
evidence: trace?.targets?.[index]?.evidence ?? "unavailable",
|
|
3103
|
+
behaviorCoverage: "unconfirmed",
|
|
3019
3104
|
})),
|
|
3020
3105
|
lintStatus: "unavailable",
|
|
3021
3106
|
integrationFacts,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { buildControllerIdentityFromEntry, readBuildStamp } from "./package-metadata.js";
|
|
5
|
+
export function sha256File(file) {
|
|
6
|
+
return `sha256:${createHash("sha256").update(readFileSync(file)).digest("hex")}`;
|
|
7
|
+
}
|
|
8
|
+
export function classifyBackendDogfoodProvenance(input) {
|
|
9
|
+
if (input.tarballPath)
|
|
10
|
+
return { kind: "local-tarball", version: input.identity.packageVersion, tarballSha256: sha256File(input.tarballPath) };
|
|
11
|
+
if (input.distTag)
|
|
12
|
+
return { kind: "published-npm", version: input.identity.packageVersion, distTag: input.distTag };
|
|
13
|
+
return { kind: "local-candidate", version: input.identity.packageVersion };
|
|
14
|
+
}
|
|
15
|
+
export function buildBackendDogfoodIdentityEvidence(input) {
|
|
16
|
+
const controller = buildControllerIdentityFromEntry({ requested: input.controllerEntry, entry: path.resolve(input.controllerEntry) });
|
|
17
|
+
if (!controller)
|
|
18
|
+
throw new Error("BACKEND_DOGFOOD_IDENTITY_UNRESOLVED");
|
|
19
|
+
const fingerprint = controller.packageFingerprint.value;
|
|
20
|
+
if (fingerprint !== input.expectedFingerprint)
|
|
21
|
+
throw new Error(`BACKEND_DOGFOOD_CONTROLLER_MISMATCH: expected ${input.expectedFingerprint}, got ${fingerprint}`);
|
|
22
|
+
const consoleIdentity = input.consoleHost
|
|
23
|
+
? undefined
|
|
24
|
+
: buildControllerIdentityFromEntry({
|
|
25
|
+
requested: input.consoleControllerEntry ?? path.join(controller.packageRoot, "bin", "agent-worker.js"),
|
|
26
|
+
entry: input.consoleControllerEntry ?? path.join(controller.packageRoot, "bin", "agent-worker.js"),
|
|
27
|
+
});
|
|
28
|
+
if (!input.consoleHost && !consoleIdentity)
|
|
29
|
+
throw new Error("BACKEND_DOGFOOD_CONSOLE_IDENTITY_UNRESOLVED");
|
|
30
|
+
const consoleHost = {
|
|
31
|
+
packageName: String(input.consoleHost?.packageName ?? consoleIdentity?.packageName),
|
|
32
|
+
packageVersion: String(input.consoleHost?.packageVersion ?? consoleIdentity?.packageVersion),
|
|
33
|
+
packageFingerprint: String(input.consoleHost?.packageFingerprint ?? consoleIdentity?.packageFingerprint.value),
|
|
34
|
+
};
|
|
35
|
+
const expected = `${controller.packageName}@${controller.packageVersion}@${fingerprint}`;
|
|
36
|
+
const actual = `${consoleHost.packageName}@${consoleHost.packageVersion}@${consoleHost.packageFingerprint}`;
|
|
37
|
+
if (actual !== expected)
|
|
38
|
+
throw new Error(`BACKEND_DOGFOOD_CONSOLE_IDENTITY_MISMATCH: expected ${expected}, got ${actual}`);
|
|
39
|
+
return {
|
|
40
|
+
schemaVersion: 1,
|
|
41
|
+
controller,
|
|
42
|
+
provenance: classifyBackendDogfoodProvenance({ identity: controller, distTag: input.distTag, tarballPath: input.tarballPath }),
|
|
43
|
+
buildCommitSha: readBuildStamp(controller.packageRoot)?.gitSha ?? null,
|
|
44
|
+
consoleHost,
|
|
45
|
+
dagController: { packageName: controller.packageName, packageVersion: controller.packageVersion, packageFingerprint: fingerprint },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -59,6 +59,9 @@ const RAW_TO_NORMALIZED = {
|
|
|
59
59
|
quota: "executor",
|
|
60
60
|
"rate-limit": "executor",
|
|
61
61
|
unavailable: "executor",
|
|
62
|
+
// Gateway reachability outage: still an executor-class failure, but named so
|
|
63
|
+
// reports do not read as a capability mismatch.
|
|
64
|
+
"upstream-unavailable": "executor",
|
|
62
65
|
// Context window overflow (400 request-too-large): the session needs
|
|
63
66
|
// compaction / fresh context before retrying — an executor-side
|
|
64
67
|
// environment condition, not a plan-quality defect.
|
|
@@ -392,6 +392,147 @@ const COMMON_MUTATION_ERRORS = [
|
|
|
392
392
|
];
|
|
393
393
|
export function buildOperatorCapabilitiesDocument() {
|
|
394
394
|
const actions = [
|
|
395
|
+
{
|
|
396
|
+
action: "prepareScheduledTask", cli: "console scheduled task preparation", kind: "mutation",
|
|
397
|
+
inputSchemaVersion: 1, resultSchemaVersion: 1, envelopeSchemaVersion: 1, requiredErrorCodes: ["INVALID_INPUT"],
|
|
398
|
+
description: "Preferred entry for natural-language one-time scheduled tasks, including read-only inspection reports. Call this directly after understanding the goal, output scope, executable acceptance and time; the server uses the CURRENT project directory and creates the immutable PRD, managed Task and strict writeSet gate, binds THIS Chat automatically and returns a REAL planned goal/confirmation card. No pre-existing task, worktree or session ID is needed: never ask the user for these internal fields and do not manually create/edit them with bash/write/newTask/importPrd. Keep preparation concise: preserve the user goal without inventing extra requirements. Omit budget for ordinary tasks. Use minimal independent acceptance commands (AC-1, AC-2), preferably existing test/build scripts or test -s scheduled-report.md for a report; no compound shell/pipelines/substitution, language-specific grep checks or redundant scope assertions (the host enforces scope). Infer routine defaults and verification commands from the repository; ask only about genuinely ambiguous goal/scope/time. For read-only requests allow just an ordinary report file in the current project directory, e.g. scheduled-report.md, not .harness/. Use schedule.delayMinutes for relative time (counted after preparation finishes), or localAt + IANA timezone for an exact date/time. The current workspace must match committed HEAD; never auto-commit/stash/reset existing changes. The card exposes the exact time, bounded defaults and committed base before one human confirmation. Same request in this Chat is idempotent. Only a returned goalId with state=planned is a prepared reservation; textual drafts, Tasks or failed operations are NOT bookings. Never replace a requested schedule with immediate execution or sleep, never call runDag or self-confirm. On failure explain the returned stage, correct business inputs and retry this tool; do not mutate imported PRD/runtime artifacts. For a new distinct appointment, make its goal/time distinct.",
|
|
399
|
+
inputParams: [{ name: "request", type: "object", required: true, description: "Business goal, workflow, allowedPaths, acceptance and schedule; optional forbiddenPaths/budget. Runtime identity is server-owned." }],
|
|
400
|
+
modelCallable: "always", humanConfirmation: "none",
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
"action": "prepareScheduledGoal",
|
|
404
|
+
"cli": "console scheduled goal service",
|
|
405
|
+
"kind": "mutation",
|
|
406
|
+
"inputSchemaVersion": 1,
|
|
407
|
+
"resultSchemaVersion": 1,
|
|
408
|
+
"envelopeSchemaVersion": 1,
|
|
409
|
+
"requiredErrorCodes": [
|
|
410
|
+
"INVALID_INPUT"
|
|
411
|
+
],
|
|
412
|
+
"description": "Advanced entry ONLY for an already prepared Task. For a new natural-language reservation use prepareScheduledTask instead. Requires the current repository or its linked worktree at frozenBase and a managed Task already strict-prepared at its writeSet gate. booking must include managedTaskId, originalGoal, chatSessionRef, workflow, worktreeRef, frozenBase, allowedPaths, forbiddenPaths, acceptance {policy, criteria [{acId,description,command}]}, budget {maxTurns,maxDagRuns,maxSupervisorCalls}, time {executeAtUtc,latestStartUtc,stopAtUtc,expiresAt,timezone,allowedHours,misfirePolicy}. Resolve natural-language dates with the user timezone; ask about ambiguous time/AC/scope. No execution or authorization occurs. Show the complete scope/time/budget for one browser confirmation. Never call runDag for this reservation.",
|
|
413
|
+
"inputParams": [
|
|
414
|
+
{
|
|
415
|
+
"name": "booking",
|
|
416
|
+
"type": "object",
|
|
417
|
+
"required": true,
|
|
418
|
+
"description": "Complete frozen booking inputs; never supply prepared, controllerRef, approval or owner tokens."
|
|
419
|
+
}
|
|
420
|
+
],
|
|
421
|
+
"modelCallable": "always",
|
|
422
|
+
"humanConfirmation": "none"
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
"action": "confirmScheduledGoal",
|
|
426
|
+
"cli": "console scheduled goal service",
|
|
427
|
+
"kind": "mutation",
|
|
428
|
+
"inputSchemaVersion": 1,
|
|
429
|
+
"resultSchemaVersion": 1,
|
|
430
|
+
"envelopeSchemaVersion": 1,
|
|
431
|
+
"requiredErrorCodes": [
|
|
432
|
+
"INVALID_INPUT"
|
|
433
|
+
],
|
|
434
|
+
"description": "Human browser confirmation of the exact displayed ScheduledGoal envelope.",
|
|
435
|
+
"inputParams": [
|
|
436
|
+
{
|
|
437
|
+
"name": "goalId",
|
|
438
|
+
"type": "string",
|
|
439
|
+
"required": true,
|
|
440
|
+
"description": "Prepared goal id"
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
"name": "stateVersion",
|
|
444
|
+
"type": "number",
|
|
445
|
+
"required": true,
|
|
446
|
+
"description": "Displayed state version"
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
"name": "envelopeHash",
|
|
450
|
+
"type": "string",
|
|
451
|
+
"required": true,
|
|
452
|
+
"description": "Displayed envelope hash"
|
|
453
|
+
}
|
|
454
|
+
],
|
|
455
|
+
"modelCallable": "never",
|
|
456
|
+
"humanConfirmation": "required"
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
"action": "cancelScheduledGoal",
|
|
460
|
+
"cli": "console scheduled goal service",
|
|
461
|
+
"kind": "mutation",
|
|
462
|
+
"inputSchemaVersion": 1,
|
|
463
|
+
"resultSchemaVersion": 1,
|
|
464
|
+
"envelopeSchemaVersion": 1,
|
|
465
|
+
"requiredErrorCodes": [
|
|
466
|
+
"INVALID_INPUT"
|
|
467
|
+
],
|
|
468
|
+
"description": "Cancel future ScheduledGoal admission; in-flight/unknown ownership remains until reconciled.",
|
|
469
|
+
"inputParams": [
|
|
470
|
+
{
|
|
471
|
+
"name": "goalId",
|
|
472
|
+
"type": "string",
|
|
473
|
+
"required": true,
|
|
474
|
+
"description": "Goal id"
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
"name": "stateVersion",
|
|
478
|
+
"type": "number",
|
|
479
|
+
"required": true,
|
|
480
|
+
"description": "Current state version"
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
"name": "reason",
|
|
484
|
+
"type": "string",
|
|
485
|
+
"required": true,
|
|
486
|
+
"description": "Human cancellation reason"
|
|
487
|
+
}
|
|
488
|
+
],
|
|
489
|
+
"modelCallable": "never",
|
|
490
|
+
"humanConfirmation": "required"
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
"action": "scheduledGoalGet",
|
|
494
|
+
"cli": "console scheduled goal service",
|
|
495
|
+
"kind": "read",
|
|
496
|
+
"inputSchemaVersion": 1,
|
|
497
|
+
"resultSchemaVersion": 1,
|
|
498
|
+
"envelopeSchemaVersion": 1,
|
|
499
|
+
"requiredErrorCodes": [
|
|
500
|
+
"INVALID_INPUT"
|
|
501
|
+
],
|
|
502
|
+
"description": "Read a durable ScheduledGoal booking and separate execution, acceptance, delivery and reconciliation results. Never implies authorization.",
|
|
503
|
+
"inputParams": [
|
|
504
|
+
{
|
|
505
|
+
"name": "goalId",
|
|
506
|
+
"type": "string",
|
|
507
|
+
"required": true,
|
|
508
|
+
"description": "Goal id"
|
|
509
|
+
}
|
|
510
|
+
],
|
|
511
|
+
"modelCallable": "always",
|
|
512
|
+
"humanConfirmation": "none"
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
"action": "scheduledGoalList",
|
|
516
|
+
"cli": "console scheduled goal service",
|
|
517
|
+
"kind": "read",
|
|
518
|
+
"inputSchemaVersion": 1,
|
|
519
|
+
"resultSchemaVersion": 1,
|
|
520
|
+
"envelopeSchemaVersion": 1,
|
|
521
|
+
"requiredErrorCodes": [
|
|
522
|
+
"INVALID_INPUT"
|
|
523
|
+
],
|
|
524
|
+
"description": "List durable ScheduledGoals, including those whose original Chat was archived or deleted.",
|
|
525
|
+
"inputParams": [
|
|
526
|
+
{
|
|
527
|
+
"name": "chatSessionRef",
|
|
528
|
+
"type": "string",
|
|
529
|
+
"required": false,
|
|
530
|
+
"description": "Optional original Chat session id"
|
|
531
|
+
}
|
|
532
|
+
],
|
|
533
|
+
"modelCallable": "always",
|
|
534
|
+
"humanConfirmation": "none"
|
|
535
|
+
},
|
|
395
536
|
{
|
|
396
537
|
action: "operatorCapabilities",
|
|
397
538
|
cli: "loop-agent operator capabilities --json",
|
|
@@ -714,6 +855,24 @@ export function buildOperatorCapabilitiesDocument() {
|
|
|
714
855
|
required: false,
|
|
715
856
|
description: "optional verify commands (label:command or shell)",
|
|
716
857
|
},
|
|
858
|
+
{
|
|
859
|
+
name: "requirementOwnership",
|
|
860
|
+
type: "array",
|
|
861
|
+
required: false,
|
|
862
|
+
description: "optional frozen requirement→file ownership for frontend-implementation DAGs: [{requirementIds, implementationFiles?, verificationFiles?}] — machine-enforced at plan finalize",
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
name: "capabilityBoundary",
|
|
866
|
+
type: "array",
|
|
867
|
+
required: false,
|
|
868
|
+
description: "optional frozen test capability boundary {allowed?, forbidden?} composed into dependencyPolicy by the runtime",
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
name: "interactionIds",
|
|
872
|
+
type: "array",
|
|
873
|
+
required: false,
|
|
874
|
+
description: "optional frozen interaction id vocabulary — contract interaction names are machine-checked against it at plan finalize",
|
|
875
|
+
},
|
|
717
876
|
],
|
|
718
877
|
modelCallable: "always",
|
|
719
878
|
humanConfirmation: "none",
|
|
@@ -1069,6 +1228,27 @@ export function buildOperatorCapabilitiesDocument() {
|
|
|
1069
1228
|
required: false,
|
|
1070
1229
|
description: "verification entries as label:command strings",
|
|
1071
1230
|
},
|
|
1231
|
+
{
|
|
1232
|
+
name: "requirementOwnership",
|
|
1233
|
+
type: "array",
|
|
1234
|
+
itemsType: "string",
|
|
1235
|
+
required: false,
|
|
1236
|
+
description: "frozen requirement→file ownership: [{requirementIds, implementationFiles?, verificationFiles?}] — machine-enforced at plan finalize",
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
name: "capabilityBoundary",
|
|
1240
|
+
type: "array",
|
|
1241
|
+
itemsType: "string",
|
|
1242
|
+
required: false,
|
|
1243
|
+
description: "frozen test capability boundary {allowed?, forbidden?} composed into dependencyPolicy by the runtime",
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
name: "interactionIds",
|
|
1247
|
+
type: "array",
|
|
1248
|
+
itemsType: "string",
|
|
1249
|
+
required: false,
|
|
1250
|
+
description: "frozen interaction id vocabulary — contract interaction names are machine-checked against it at plan finalize",
|
|
1251
|
+
},
|
|
1072
1252
|
{
|
|
1073
1253
|
name: "approveGate",
|
|
1074
1254
|
type: "string",
|
|
@@ -286,6 +286,35 @@ const taskConfigObjectSchema = z.object({
|
|
|
286
286
|
verifyPreset: verifyPresetSchema.optional().default("auto"),
|
|
287
287
|
/** Task-contract commands that must be included in final DAG shell verification. */
|
|
288
288
|
verifyCommands: z.array(taskVerifyCommandSchema).optional().default([]),
|
|
289
|
+
/** Generation-frozen requirement→file ownership for frontend-implementation
|
|
290
|
+
* DAGs. Machine-checked at plan finalize: a requirement's implementation
|
|
291
|
+
* targets (and its verification targets' files) must stay inside the
|
|
292
|
+
* declared sets, so ownership never depends on model compliance with prose.
|
|
293
|
+
*/
|
|
294
|
+
requirementOwnership: z
|
|
295
|
+
.array(z
|
|
296
|
+
.object({
|
|
297
|
+
requirementIds: z.array(z.string().min(1)).min(1),
|
|
298
|
+
implementationFiles: z.array(z.string().min(1)).optional(),
|
|
299
|
+
verificationFiles: z.array(z.string().min(1)).optional(),
|
|
300
|
+
})
|
|
301
|
+
.strict()
|
|
302
|
+
.refine((entry) => (entry.implementationFiles?.length ?? 0) > 0 ||
|
|
303
|
+
(entry.verificationFiles?.length ?? 0) > 0, { message: "requirementOwnership entry needs implementationFiles or verificationFiles" }))
|
|
304
|
+
.optional(),
|
|
305
|
+
/** Generation-frozen test capability boundary composed into the canonical
|
|
306
|
+
* dependencyPolicy by the runtime (planner never maintains it). */
|
|
307
|
+
capabilityBoundary: z
|
|
308
|
+
.object({
|
|
309
|
+
allowed: z.array(z.string().min(1)).optional(),
|
|
310
|
+
forbidden: z.array(z.string().min(1)).optional(),
|
|
311
|
+
})
|
|
312
|
+
.strict()
|
|
313
|
+
.refine((entry) => (entry.allowed?.length ?? 0) > 0 || (entry.forbidden?.length ?? 0) > 0, { message: "capabilityBoundary needs allowed or forbidden entries" })
|
|
314
|
+
.optional(),
|
|
315
|
+
/** Generation-frozen interaction id vocabulary. Contract interaction names
|
|
316
|
+
* must come from this set (plan finalize deterministic check). */
|
|
317
|
+
interactionIds: z.array(z.string().min(1)).optional(),
|
|
289
318
|
/** Verification selection policy. Intermediate loops may use quota; final gates still run full required verification. */
|
|
290
319
|
verifyQuota: verifyQuotaSchema.optional().default("full"),
|
|
291
320
|
/** DAG supervised verify strategy. Final quota is fixed to full; intermediate quota may reduce cost. */
|
|
@@ -187,6 +187,15 @@ export function mergeManagedTaskConfigFields(existing, draft) {
|
|
|
187
187
|
...(draft.frontendOpenspec !== undefined
|
|
188
188
|
? { frontendOpenspec: draft.frontendOpenspec }
|
|
189
189
|
: {}),
|
|
190
|
+
...(draft.requirementOwnership !== undefined
|
|
191
|
+
? { requirementOwnership: draft.requirementOwnership }
|
|
192
|
+
: {}),
|
|
193
|
+
...(draft.capabilityBoundary !== undefined
|
|
194
|
+
? { capabilityBoundary: draft.capabilityBoundary }
|
|
195
|
+
: {}),
|
|
196
|
+
...(draft.interactionIds !== undefined
|
|
197
|
+
? { interactionIds: draft.interactionIds }
|
|
198
|
+
: {}),
|
|
190
199
|
};
|
|
191
200
|
if (draft.featureId !== undefined) {
|
|
192
201
|
next.featureId = draft.featureId;
|
|
@@ -75,6 +75,23 @@ export const taskContractDraftSchema = z
|
|
|
75
75
|
assumptions: z.array(z.string()).optional(),
|
|
76
76
|
hardConstraints: z.array(z.string()).optional(),
|
|
77
77
|
frontendOpenspec: frontendOpenspecConfigSchema.optional(),
|
|
78
|
+
requirementOwnership: z
|
|
79
|
+
.array(z
|
|
80
|
+
.object({
|
|
81
|
+
requirementIds: z.array(z.string().min(1)).min(1),
|
|
82
|
+
implementationFiles: z.array(z.string().min(1)).optional(),
|
|
83
|
+
verificationFiles: z.array(z.string().min(1)).optional(),
|
|
84
|
+
})
|
|
85
|
+
.strict())
|
|
86
|
+
.optional(),
|
|
87
|
+
capabilityBoundary: z
|
|
88
|
+
.object({
|
|
89
|
+
allowed: z.array(z.string().min(1)).optional(),
|
|
90
|
+
forbidden: z.array(z.string().min(1)).optional(),
|
|
91
|
+
})
|
|
92
|
+
.strict()
|
|
93
|
+
.optional(),
|
|
94
|
+
interactionIds: z.array(z.string().min(1)).optional(),
|
|
78
95
|
interviewAssessmentRef: z
|
|
79
96
|
.object({
|
|
80
97
|
draftSha256: z.string().min(1),
|