@tea-agent/loop-agent 0.40.0-next.9 → 0.41.0-beta.1
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 +130 -3
- package/README.md +2 -2
- package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
- package/dist/adapters/context-transfer/pi-session.js +61 -0
- package/dist/application/dag/generate-task-dag.js +6 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +21 -5
- package/dist/cli/program.js +61 -4
- package/dist/commands/client-recovery.js +8 -36
- package/dist/commands/dag-artifact.js +284 -0
- package/dist/commands/dag-context.js +184 -0
- package/dist/commands/dag-rerun.js +206 -1
- package/dist/commands/init-model-catalog.js +22 -0
- package/dist/commands/task-advance.js +23 -1
- package/dist/commands/task-source-prepare.js +5 -0
- package/dist/commands/task-status.js +1 -0
- package/dist/executors/dag-pi-executor.js +39 -12
- package/dist/executors/pi-sdk-executor.js +36 -5
- package/dist/executors/shell-executor.js +64 -17
- package/dist/governance/checks.js +1 -0
- package/dist/{worker → infrastructure}/console/app-data.js +4 -0
- package/dist/infrastructure/console/artifact-revision-store.js +430 -0
- package/dist/infrastructure/console/context-export-store.js +160 -0
- package/dist/infrastructure/console/dir-lock.js +132 -0
- package/dist/{worker → infrastructure}/console/operation-store.js +28 -0
- package/dist/infrastructure/harness/artifact-store.js +10 -1
- package/dist/infrastructure/harness/atomic-write.js +12 -2
- package/dist/shared/context-transfer/artifact-revision.js +172 -0
- package/dist/shared/context-transfer.js +418 -0
- package/dist/shared/operator/capabilities.js +23 -1
- package/dist/shared/operator/safe-run-summary.js +1 -0
- package/dist/shared/path-safety.js +93 -0
- package/dist/shared/pi-context-pressure/checkpoint.js +116 -0
- package/dist/shared/pi-context-pressure/compaction-policy.js +151 -0
- package/dist/shared/pi-context-pressure/env.js +58 -0
- package/dist/shared/pi-context-pressure/extension.js +100 -0
- package/dist/shared/pi-context-pressure/index.js +7 -0
- package/dist/shared/pi-context-pressure/overflow.js +252 -0
- package/dist/shared/pi-context-pressure/sift-bridge.js +386 -0
- package/dist/shared/pi-context-pressure/telemetry.js +51 -0
- package/dist/shared/preview.js +28 -4
- package/dist/task/config-types.js +2 -1
- package/dist/task/task-demand-routing.js +3 -0
- package/dist/worker/console/chat/assistant-content.js +7 -0
- package/dist/worker/console/chat/chat-event-store.js +84 -13
- package/dist/worker/console/chat/goal-round-driver.js +72 -0
- package/dist/worker/console/chat/goals.js +300 -0
- package/dist/worker/console/chat/pi-runtime.js +285 -8
- package/dist/worker/console/chat/provider-error.js +2 -1
- package/dist/worker/console/chat/repo-browser.js +94 -12
- package/dist/worker/console/chat/resource-preferences-store.js +1 -1
- package/dist/worker/console/chat/routes.js +487 -30
- package/dist/worker/console/chat/session-catalog.js +48 -0
- package/dist/worker/console/chat/session-stats.js +96 -0
- package/dist/worker/console/chat/session-store.js +28 -19
- package/dist/worker/console/chat/shortcuts.js +7 -0
- package/dist/worker/console/chat/sift-bridge.js +1 -0
- package/dist/worker/console/chat/turn-process.js +32 -3
- package/dist/worker/console/chat/user-questions.js +1 -1
- package/dist/worker/console/console-update-runtime.js +1 -1
- package/dist/worker/console/context-transfer-diagnostics.js +198 -0
- package/dist/worker/console/dag-confirmation.js +1 -1
- package/dist/worker/console/dag-execution-receipt.js +21 -3
- package/dist/worker/console/doctor.js +1 -1
- package/dist/worker/console/draft-store.js +1 -1
- package/dist/worker/console/human-gate-token.js +1 -1
- package/dist/worker/console/index.js +3 -3
- package/dist/worker/console/interview/assessment.js +1 -1
- package/dist/worker/console/interview/session.js +1 -1
- package/dist/worker/console/operation-runner.js +45 -4
- package/dist/worker/console/operation-sse.js +1 -1
- package/dist/worker/console/operation-wait.js +1 -1
- package/dist/worker/console/operator-actions.js +124 -8
- package/dist/worker/console/operator-selection.js +6 -2
- package/dist/worker/console/operator-surface-health.js +1 -1
- package/dist/worker/console/operator-user-error.js +169 -0
- package/dist/worker/console/pi-plugins.catalog.json +73658 -0
- package/dist/worker/console/pi-plugins.js +179 -0
- package/dist/worker/console/routes.js +864 -13
- package/dist/worker/console/security.js +35 -0
- package/dist/worker/console/server.js +177 -44
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bs-CDAXM.js → abnfDiagram-N423BO3Z-DSVcg1YO.js} +1 -1
- package/dist/worker/console/static/assets/{arc-CAkA3We3.js → arc-BtXhIqui.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DEmI_zqr.js → architectureDiagram-T3A2C74G-B-nh-ilJ.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-uDHANebE.js → blockDiagram-VBNYF7ZC-BIUSVdZI.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Bv97b4_B.js → c4Diagram-5PPSVZJV-Cmw3L4qM.js} +1 -1
- package/dist/worker/console/static/assets/channel-BhZFNefx.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-shmQKsFz.js → chunk-2GRJ4B5K-CGpMMjGm.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-BAraNvcN.js → chunk-2Q5K7J3B-BNBkEpEJ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BirhxUop.js → chunk-5RXB4S5H-ubln3eaU.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C9LgUJpR.js → chunk-5VM5RSS4-_XcBTt1i.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DrU-s-t7.js → chunk-6Q2QTUOP-Ln30Pgrh.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Tx-H_FA2.js → chunk-GF5L2VYU-ESH3lhSx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BjLtNFrR.js → chunk-JWPE2WC7-DAJt-qXB.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-UNLVwSsy.js → chunk-KBJHAD2P-Cx10LIcH.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CBEUX8XD.js → chunk-RYQCIY6F-CENeOxoJ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-WTL7IZOq.js → chunk-XXDRQBXY-DXqK-D38.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Sbv-NJni.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Sbv-NJni.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CkHG6W3s.js → cose-bilkent-JH36ORCC-CE4dmvGY.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BRzFqTaL.js → cynefin-VYW2F7L2-CRacN1zK.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-2_CFp_i7.js → cynefinDiagram-MW4NZA55-Cneukjrt.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BJtsmdkE.js → dagre-VZM6K2ZE-DPFboRoL.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DNEFFo6w.js → diagram-7IWD3JNH-DIAQgcOP.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B_IWV7eo.js → diagram-B4RE2ZJO-VrYMKxuy.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DvvRx2dz.js → diagram-LBJQPF4R-CLHvBghn.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-CsibsWLg.js → diagram-Q27KOJAE-DVCWBif8.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-les_djBQ.js → diagram-UB23O5K3-BTMmbBzJ.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BEm2n7qJ.js → ebnfDiagram-BXEA7PRR-BUZsKJ56.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BI7vHHVm.js → erDiagram-JOGREHBK-BsBZ5NXC.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CUFCERrB.js → flowDiagram-UKHOOZJN-D7fH_kDa.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-wxyLiGWt.js → ganttDiagram-PKOTCBZU-Bza5OohY.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bt897K8Z.js → gitGraphDiagram-DS77QQ5N-DpkoGR_Y.js} +1 -1
- package/dist/worker/console/static/assets/index-Ius3vxhf.css +1 -0
- package/dist/worker/console/static/assets/index-yASzcbBr.js +451 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DYNx_IPv.js → infoDiagram-6WML65LV-BYm3g6LT.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CzpLNEu6.js → ishikawaDiagram-WSZJBQD7-C2RS_H4v.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-97f9owNU.js → journeyDiagram-NVQOT4AX-B4slKpCo.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BxM9hyBn.js → kanban-definition-27J2QSJJ-B7orRNpI.js} +1 -1
- package/dist/worker/console/static/assets/{linear-ByuxcHvp.js → linear-D1BeBxLQ.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-DwPGvpCv.js → mermaid.core-0TtMP8k4.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DCoqG9RT.js → mindmap-definition-FAOFIHXS-XL5EbCtT.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BLSBmBMx.js → pegDiagram-VL7TDLO6-CwF7uas5.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B0t_0oJW.js → pieDiagram-7S7Q4E2Y-A_Jo-72f.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-sTzDk7Nt.js → quadrantDiagram-CIZ2JOQS-CxOa4gKL.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-B3jRdATC.js → railroadDiagram-AXF67PYL-DoAoswIH.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D-cTtXEN.js → requirementDiagram-LRYGKXZP-C1Lc1MlD.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B25igJRE.js → sankeyDiagram-W5VNT64P-DFXOkq0U.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-jW_lM1_D.js → sequenceDiagram-SI44F4Z6-B0ufUCuW.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CrzTgsHE.js → sizeCapture-X5ZJPWSS-iPbW55g3.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-B01MvlqE.js → stateDiagram-OKZ733FA-CHiXzEKP.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BtMgzo_H.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-YcF12FiU.js → swimlanes-SLNWSIFB-LyFs7TpX.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cb-OVd9Y.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ClnRcg2s.js → timeline-definition-Z64GVDOM-tNkJnb6z.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-B6gr_z-q.js → vennDiagram-T6HMQDX7-DFm7Oo8o.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CvSgguGH.js → wardleyDiagram-T6FBY63Y-tpblTp9i.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-23OtSZCb.js → xychartDiagram-ELKLHX3M-BCDLWR_D.js} +1 -1
- package/dist/worker/console/static/index.html +7 -2
- package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +95 -25
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +35 -1
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +3 -1
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +26 -19
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +0 -11
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +14 -1
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +34 -4
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
- package/dist/worker/console/static-src/operator-chat/workspace-reorder.js +18 -0
- package/dist/worker/console/static-src/shell/console-update-reload.js +25 -0
- package/dist/worker/console/static-src/shell/useWorkspaces.js +150 -34
- package/dist/worker/console/static-src/shell/workspace-route.js +3 -1
- package/dist/worker/console/workspace-context.js +56 -5
- package/dist/worker/console/workspace-registry.js +335 -62
- package/dist/worker/continuation/worker-continuation.js +278 -0
- package/dist/worker/observe/health.js +1 -1
- package/dist/worker/observe/node-transparency.js +572 -0
- package/dist/worker/observe/routes.js +51 -1
- package/dist/worker/observe/static/api.js +69 -5
- package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
- package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
- package/dist/worker/observe/static/inspect-bootstrap.d.ts +8 -0
- package/dist/worker/observe/static/inspect-bootstrap.js +57 -0
- package/dist/worker/observe/static/inspect-workspace.js +54 -10
- package/dist/worker/observe/static/operator-chrome.css +23 -4
- package/dist/worker/observe/static/operator-chrome.js +7 -2
- package/dist/worker/observe/static/state.js +3 -1
- package/dist/worker/observe/static/styles.css +1108 -135
- package/dist/worker/observe/static/views/dag-inspector.js +1257 -59
- package/dist/worker/observe/static/views/dags.js +7 -5
- package/dist/worker/observe/static/views/session-timeline.js +210 -10
- package/dist/worker/outcomes/adapters.js +19 -0
- package/dist/worker/outcomes/types.js +1 -0
- package/dist/worker/pool/attempt-lease.js +97 -66
- package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
- package/dist/worker/pool/reconcile.js +46 -1
- package/dist/worker/pool/run-store.js +2 -0
- package/dist/worker/pool/state-projection.js +2 -0
- package/dist/worker/task-spec/workflow-routing.js +8 -3
- package/dist/workflows/dag/artifact-bindings.js +149 -0
- package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +162 -8
- package/dist/workflows/dag/backend-test-pytest-collection.js +70 -2
- package/dist/workflows/dag/backend-test-result-contract.js +4 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +339 -53
- package/dist/workflows/dag/backend-test-writer-completeness.js +11 -0
- package/dist/workflows/dag/budget-enforcement.js +10 -0
- package/dist/workflows/dag/context-receipt.js +305 -0
- package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
- package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
- package/dist/workflows/dag/context-transfer/renderers.js +93 -0
- package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
- package/dist/workflows/dag/init-hybrid.js +151 -4
- package/dist/workflows/dag/node-execution.js +112 -27
- package/dist/workflows/dag/path-safety.js +1 -0
- package/dist/workflows/dag/prompt.js +29 -4
- package/dist/workflows/dag/rerun-plan.js +555 -1
- package/dist/workflows/dag/rerun-run.js +601 -15
- package/dist/workflows/dag/runner.js +20 -1
- package/dist/workflows/dag/skill-snapshot.js +17 -0
- package/dist/workflows/dag/types.js +183 -7
- package/docs/architecture/runtime-boundaries.md +8 -7
- package/docs/architecture/worker-and-feature.md +1 -1
- package/docs/templates/agent-dag.schema.json +32 -0
- package/docs/templates/backend-test-dag.json +6 -5
- package/docs/templates/init-managed-agents.md +1 -1
- package/docs/templates/product-line/task.yaml +1 -1
- package/package.json +5 -2
- package/skills/loop-agent/references/command-reference.md +21 -1
- package/dist/worker/console/static/assets/channel-amxUpk7o.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-bejwBVIz.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-bejwBVIz.js +0 -1
- package/dist/worker/console/static/assets/index-BHhUOTri.css +0 -1
- package/dist/worker/console/static/assets/index-DqJbO3-p.js +0 -407
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BvFKEtm2.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DIB2eVg-.js +0 -8
- /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
1
2
|
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { executeArtifactRevisionContinuation, planArtifactRevisionContinuation, } from "../workflows/dag/context-transfer/operator-actions.js";
|
|
2
4
|
import { isDagRerunPromptOverrideEnabled } from "../shared/dag-prompt-override.js";
|
|
3
|
-
import {
|
|
5
|
+
import { workerContinuationAuthorityV1Schema, } from "../shared/context-transfer.js";
|
|
6
|
+
import { openConsoleAppData } from "../infrastructure/console/app-data.js";
|
|
7
|
+
import { buildArtifactOverrideSet, consumeArtifactRevision, readArtifactRevision, readArtifactRevisionCandidate, acquireArtifactRevisionLease, releaseArtifactRevisionLease, EXECUTE_REVISION_LEASE_TTL_MS, } from "../infrastructure/console/artifact-revision-store.js";
|
|
8
|
+
import { buildArtifactBindingGraph } from "../workflows/dag/artifact-bindings.js";
|
|
9
|
+
import { locateDagRun, readDagRunSpec } from "../workflows/dag/lifecycle.js";
|
|
10
|
+
import { executeDagRerun, planDagRerun, readOperatorRequestReplay, } from "../workflows/dag/rerun-run.js";
|
|
4
11
|
export function parseDagRerunArgs(args) {
|
|
5
12
|
if (args.length === 0) {
|
|
6
13
|
throw new Error("usage: dag rerun --run-id <id> --from-node <node-id> --plan [--json] | dag rerun --run-id <id> --from-node <node-id> --plan-hash <hash> --request-id <key> --reason <text> [--json]");
|
|
@@ -111,6 +118,10 @@ async function loadPromptOverrides(specs, mode) {
|
|
|
111
118
|
return overrides;
|
|
112
119
|
}
|
|
113
120
|
export async function runDagRerun(repoRoot, rawArgs) {
|
|
121
|
+
if (rawArgs.some((arg) => arg === "--artifact-revision" || arg.startsWith("--artifact-revision="))) {
|
|
122
|
+
await runArtifactRevisionRerun(repoRoot, rawArgs);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
114
125
|
const parsed = parseDagRerunArgs(rawArgs);
|
|
115
126
|
let promptOverrides;
|
|
116
127
|
const hasOverrides = parsed.promptOverrideSpecs.length > 0 ||
|
|
@@ -163,3 +174,197 @@ export async function runDagRerun(repoRoot, rawArgs) {
|
|
|
163
174
|
}
|
|
164
175
|
}
|
|
165
176
|
}
|
|
177
|
+
function readFlag(args, name) {
|
|
178
|
+
const exact = args.indexOf(name);
|
|
179
|
+
if (exact >= 0)
|
|
180
|
+
return args[exact + 1];
|
|
181
|
+
const prefix = `${name}=`;
|
|
182
|
+
return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
|
|
183
|
+
}
|
|
184
|
+
function parseWorkerContinuationAuthority(raw) {
|
|
185
|
+
if (raw === undefined)
|
|
186
|
+
return undefined;
|
|
187
|
+
let parsed;
|
|
188
|
+
try {
|
|
189
|
+
parsed = JSON.parse(raw);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
throw new Error("dag rerun --worker-continuation must be valid JSON");
|
|
193
|
+
}
|
|
194
|
+
const result = workerContinuationAuthorityV1Schema.safeParse(parsed);
|
|
195
|
+
if (!result.success) {
|
|
196
|
+
throw new Error(`dag rerun --worker-continuation is invalid: ${result.error.issues[0]?.message ?? "schema mismatch"}`);
|
|
197
|
+
}
|
|
198
|
+
return result.data;
|
|
199
|
+
}
|
|
200
|
+
function executeLeaseHolder(requestId) {
|
|
201
|
+
// The request id identifies the idempotency envelope, not a live process.
|
|
202
|
+
// A unique invocation holder prevents two concurrent direct CLI invocations
|
|
203
|
+
// with the same request id from treating each other as a lease renewal.
|
|
204
|
+
return `cli-execute:${requestId}:${randomBytes(12).toString("hex")}`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Hold the staging lease across candidate read + continuation execution so a
|
|
208
|
+
* concurrent discard/second execute fails with LEASE_HELD and a crashed
|
|
209
|
+
* process leaves a stale, TTL-takeover-able lease instead of silent races.
|
|
210
|
+
* The acquire/renew here also refreshes the sliding window before the long
|
|
211
|
+
* child run starts.
|
|
212
|
+
*/
|
|
213
|
+
async function withExecuteRevisionLease(appData, requestId, revisionId, operation) {
|
|
214
|
+
const holder = executeLeaseHolder(requestId);
|
|
215
|
+
await acquireArtifactRevisionLease({
|
|
216
|
+
appData,
|
|
217
|
+
revisionId,
|
|
218
|
+
holder,
|
|
219
|
+
ttlMs: EXECUTE_REVISION_LEASE_TTL_MS,
|
|
220
|
+
});
|
|
221
|
+
try {
|
|
222
|
+
return await operation(holder);
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
await releaseArtifactRevisionLease({
|
|
226
|
+
appData,
|
|
227
|
+
revisionId,
|
|
228
|
+
holder,
|
|
229
|
+
}).catch(() => { });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
export async function runArtifactRevisionRerunCore(repoRoot, rawArgs, cliOptions) {
|
|
233
|
+
const allowed = new Set([
|
|
234
|
+
"--run-id",
|
|
235
|
+
"--artifact-revision",
|
|
236
|
+
"--plan-hash",
|
|
237
|
+
"--request-id",
|
|
238
|
+
"--reason",
|
|
239
|
+
"--worker-continuation",
|
|
240
|
+
]);
|
|
241
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
242
|
+
const arg = rawArgs[index];
|
|
243
|
+
if (arg === "--plan" || arg === "--json")
|
|
244
|
+
continue;
|
|
245
|
+
const name = arg.split("=", 1)[0];
|
|
246
|
+
if (!allowed.has(name))
|
|
247
|
+
throw new Error(`unknown artifact revision rerun flag: ${arg}`);
|
|
248
|
+
if (!arg.includes("="))
|
|
249
|
+
index += 1;
|
|
250
|
+
}
|
|
251
|
+
const parentRunId = readFlag(rawArgs, "--run-id")?.trim();
|
|
252
|
+
const revisionId = readFlag(rawArgs, "--artifact-revision")?.trim();
|
|
253
|
+
const planOnly = rawArgs.includes("--plan");
|
|
254
|
+
if (!parentRunId || !revisionId) {
|
|
255
|
+
throw new Error("dag rerun artifact revision requires --run-id and --artifact-revision");
|
|
256
|
+
}
|
|
257
|
+
const appData = openConsoleAppData({
|
|
258
|
+
repoRoot,
|
|
259
|
+
...(cliOptions?.appDataRoot ? { appDataRoot: cliOptions.appDataRoot } : {}),
|
|
260
|
+
});
|
|
261
|
+
const planHash = readFlag(rawArgs, "--plan-hash")?.trim();
|
|
262
|
+
const requestId = readFlag(rawArgs, "--request-id")?.trim();
|
|
263
|
+
const reason = readFlag(rawArgs, "--reason")?.trim();
|
|
264
|
+
const workerContinuation = parseWorkerContinuationAuthority(readFlag(rawArgs, "--worker-continuation"));
|
|
265
|
+
if (!planOnly && (!planHash || !requestId || !reason)) {
|
|
266
|
+
throw new Error("dag rerun artifact revision execute requires --plan-hash, --request-id, and --reason");
|
|
267
|
+
}
|
|
268
|
+
if (!planOnly) {
|
|
269
|
+
// Replay-first (B4): a prior execute may have consumed the revision and
|
|
270
|
+
// the staging record may already have been reconciled away. The persisted
|
|
271
|
+
// operator request is the authoritative idempotent result and must be
|
|
272
|
+
// honored before any dependency on the revision metadata. A replay that
|
|
273
|
+
// does not match the requested parent/planHash is a reuse conflict, not a
|
|
274
|
+
// valid idempotent answer (SF2).
|
|
275
|
+
const replay = await readOperatorRequestReplay(repoRoot, requestId, {
|
|
276
|
+
parentRunId,
|
|
277
|
+
planHash,
|
|
278
|
+
});
|
|
279
|
+
if (replay) {
|
|
280
|
+
return {
|
|
281
|
+
payload: replay,
|
|
282
|
+
exitCode: replay.ok
|
|
283
|
+
? 0
|
|
284
|
+
: replay.errorCode === "RERUN_PLAN_DRIFT"
|
|
285
|
+
? 3
|
|
286
|
+
: replay.errorCode === "INELIGIBLE"
|
|
287
|
+
? 2
|
|
288
|
+
: 1,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const revision = await readArtifactRevision(appData, revisionId);
|
|
293
|
+
if (!revision || revision.parent.runId !== parentRunId) {
|
|
294
|
+
throw new Error("ARTIFACT_REVISION_EXPIRED");
|
|
295
|
+
}
|
|
296
|
+
const located = await locateDagRun(repoRoot, parentRunId);
|
|
297
|
+
if (!located)
|
|
298
|
+
throw new Error(`dag run not found: ${parentRunId}`);
|
|
299
|
+
const declaration = buildArtifactBindingGraph(await readDagRunSpec(located.runDir)).artifacts.find((item) => item.producerNodeId === revision.parent.nodeId &&
|
|
300
|
+
item.path === revision.parent.artifactPath);
|
|
301
|
+
if (!declaration)
|
|
302
|
+
throw new Error("ARTIFACT_BINDING_UNAVAILABLE");
|
|
303
|
+
const artifactOverrides = buildArtifactOverrideSet(revision);
|
|
304
|
+
if (planOnly) {
|
|
305
|
+
if (planHash || requestId || reason) {
|
|
306
|
+
throw new Error("dag rerun --plan cannot be combined with execute flags");
|
|
307
|
+
}
|
|
308
|
+
const plan = await planArtifactRevisionContinuation({
|
|
309
|
+
cwd: repoRoot,
|
|
310
|
+
parentRunId,
|
|
311
|
+
artifactId: declaration.artifactId,
|
|
312
|
+
artifactOverrides,
|
|
313
|
+
stagingRevisionSha256: revision.candidate.sha256,
|
|
314
|
+
...(workerContinuation
|
|
315
|
+
? { workerContinuationAuthority: workerContinuation }
|
|
316
|
+
: {}),
|
|
317
|
+
});
|
|
318
|
+
return { payload: { ok: plan.eligible, plan }, exitCode: plan.eligible ? 0 : 2 };
|
|
319
|
+
}
|
|
320
|
+
if (revision.state === "consumed") {
|
|
321
|
+
// Replay was not found above, so this is a crashed consume without a
|
|
322
|
+
// persisted result — refuse rather than re-execute a consumed revision.
|
|
323
|
+
throw new Error("ARTIFACT_REVISION_EXPIRED: revision already consumed and no operator replay exists for this request");
|
|
324
|
+
}
|
|
325
|
+
const result = await withExecuteRevisionLease(appData, requestId, revision.revisionId, async (leaseHolder) => {
|
|
326
|
+
const candidateBytes = await readArtifactRevisionCandidate({ appData, revision });
|
|
327
|
+
return executeArtifactRevisionContinuation({
|
|
328
|
+
cwd: repoRoot,
|
|
329
|
+
parentRunId,
|
|
330
|
+
artifactId: declaration.artifactId,
|
|
331
|
+
artifactOverrides,
|
|
332
|
+
stagingRevisionSha256: revision.candidate.sha256,
|
|
333
|
+
candidateBytes,
|
|
334
|
+
planHash: planHash,
|
|
335
|
+
requestId: requestId,
|
|
336
|
+
reason: reason,
|
|
337
|
+
...(workerContinuation
|
|
338
|
+
? { workerContinuationAuthority: workerContinuation }
|
|
339
|
+
: {}),
|
|
340
|
+
consumeRevision: async ({ revisionId: consumedId, expectedSha256 }) => {
|
|
341
|
+
await consumeArtifactRevision({
|
|
342
|
+
appData,
|
|
343
|
+
revisionId: consumedId,
|
|
344
|
+
expectedSha256,
|
|
345
|
+
holder: leaseHolder,
|
|
346
|
+
});
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
return {
|
|
351
|
+
payload: result,
|
|
352
|
+
exitCode: result.ok
|
|
353
|
+
? 0
|
|
354
|
+
: result.errorCode === "RERUN_PLAN_DRIFT"
|
|
355
|
+
? 3
|
|
356
|
+
: result.errorCode === "INELIGIBLE"
|
|
357
|
+
? 2
|
|
358
|
+
: 1,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
async function runArtifactRevisionRerun(repoRoot, rawArgs) {
|
|
362
|
+
const { payload, exitCode } = await runArtifactRevisionRerunCore(repoRoot, rawArgs);
|
|
363
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
364
|
+
if (exitCode !== 0) {
|
|
365
|
+
const failure = payload;
|
|
366
|
+
const message = failure.message ?? failure.errorCode ?? `artifact rerun failed with exit ${exitCode}`;
|
|
367
|
+
process.stderr.write(`${message}\n`);
|
|
368
|
+
process.exitCode = exitCode;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
@@ -31,8 +31,28 @@ function sanitizeInputKinds(value) {
|
|
|
31
31
|
.sort();
|
|
32
32
|
return entries.length > 0 ? entries : undefined;
|
|
33
33
|
}
|
|
34
|
+
const AICODER_CAPABILITY_RANK = {
|
|
35
|
+
"aicoder-default": 1,
|
|
36
|
+
"aicoder-plus": 2,
|
|
37
|
+
"aicoder-pro": 3,
|
|
38
|
+
"aicoder-max": 4,
|
|
39
|
+
};
|
|
40
|
+
function relativeCapabilityForModelId(provider, modelId) {
|
|
41
|
+
if (provider !== "wizard-local")
|
|
42
|
+
return undefined;
|
|
43
|
+
const rank = AICODER_CAPABILITY_RANK[modelId];
|
|
44
|
+
if (!rank)
|
|
45
|
+
return undefined;
|
|
46
|
+
return {
|
|
47
|
+
family: "aicoder",
|
|
48
|
+
rank,
|
|
49
|
+
scale: 4,
|
|
50
|
+
order: "weakest-to-strongest",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
34
53
|
function createCatalogEntry(input) {
|
|
35
54
|
const piCompatible = input.source === "pi";
|
|
55
|
+
const relativeCapability = relativeCapabilityForModelId(input.provider, input.modelId);
|
|
36
56
|
return {
|
|
37
57
|
reference: `${input.provider}/${input.modelId}`,
|
|
38
58
|
provider: input.provider,
|
|
@@ -42,6 +62,7 @@ function createCatalogEntry(input) {
|
|
|
42
62
|
credentialReady: input.credentialReady,
|
|
43
63
|
piCompatible,
|
|
44
64
|
eligibleForHarness: piCompatible && input.credentialReady === true && input.tools !== false,
|
|
65
|
+
...(relativeCapability ? { relativeCapability } : {}),
|
|
45
66
|
...(compactRecord({
|
|
46
67
|
reasoning: input.reasoning,
|
|
47
68
|
tools: input.tools,
|
|
@@ -448,6 +469,7 @@ export async function discoverInitModelCatalog(options) {
|
|
|
448
469
|
rules: [
|
|
449
470
|
"Choose only models with eligibleForHarness=true; OpenCode-only entries are context, not proof of Pi executor compatibility.",
|
|
450
471
|
"Use target-project evidence plus model capabilities, cost, context window, and credential readiness; tiers may intentionally share a model.",
|
|
472
|
+
"When relativeCapability is present, compare only entries from the same family and treat larger rank values as stronger; it is a relative capability hint, not a mandatory LOW/MED/HIGH assignment.",
|
|
451
473
|
"Preserve an existing complete usable matrix unless it is unavailable or the user asked to reselect it.",
|
|
452
474
|
"Ask the user only for a material unresolved credential, cost, compliance, or availability decision.",
|
|
453
475
|
],
|
|
@@ -13,7 +13,7 @@ const USAGE = `usage:
|
|
|
13
13
|
[--feature-id <id>]
|
|
14
14
|
[--allowed-path <glob>]...
|
|
15
15
|
[--forbidden-path <glob>]...
|
|
16
|
-
[--verify <label:command>]...
|
|
16
|
+
[--verify <label:command>]... # e.g. --verify "typecheck:npx tsc --noEmit" (label BEFORE the colon)
|
|
17
17
|
[--verify-timeout <label:timeoutMs>]...
|
|
18
18
|
[--backend-test <json>]
|
|
19
19
|
[--accept-recommendations <digest>]
|
|
@@ -31,6 +31,7 @@ const USAGE = `usage:
|
|
|
31
31
|
[--progress-interval-ms <ms>]
|
|
32
32
|
[--dry-run]
|
|
33
33
|
[--json]`;
|
|
34
|
+
export { USAGE as TASK_ADVANCE_USAGE };
|
|
34
35
|
function pushList(target, value) {
|
|
35
36
|
const list = target ?? [];
|
|
36
37
|
list.push(value);
|
|
@@ -232,6 +233,26 @@ export function parseTaskAdvanceArgs(args) {
|
|
|
232
233
|
}
|
|
233
234
|
return { taskId, options };
|
|
234
235
|
}
|
|
236
|
+
const FOREGROUND_DAG_WARNING = [
|
|
237
|
+
"[task advance] --approve-gate will execute the whole DAG in the FOREGROUND:",
|
|
238
|
+
" this terminal must stay open until every node finishes (writer nodes can run 10-60+ min).",
|
|
239
|
+
" Killing it mid-run (timeout / Ctrl-C / closed terminal) leaves an orphaned run that",
|
|
240
|
+
" only `dag reconcile-run --action supersede|abandon` can close.",
|
|
241
|
+
" Prefer background holders instead:",
|
|
242
|
+
" - Console: prepareDagExecution -> runDag (operation-held)",
|
|
243
|
+
" - CLI: nohup loop-agent task advance <task-id> --approve-gate <token> ... &",
|
|
244
|
+
].join("\n");
|
|
245
|
+
/**
|
|
246
|
+
* Operator guardrail (dogfood 2026-08-29): warn a human sitting on a TTY that
|
|
247
|
+
* `--approve-gate` runs the full DAG in the foreground before the process
|
|
248
|
+
* parks on node execution. Piped/non-TTY invocations (Console operations,
|
|
249
|
+
* scripts, --json pipelines) stay silent so machine paths are unaffected.
|
|
250
|
+
*/
|
|
251
|
+
export function warnForegroundDagExecution(options, stream = process.stderr) {
|
|
252
|
+
if (options.quiet || stream.isTTY !== true)
|
|
253
|
+
return;
|
|
254
|
+
stream.write(`${FOREGROUND_DAG_WARNING}\n`);
|
|
255
|
+
}
|
|
235
256
|
function mapOutcome(result) {
|
|
236
257
|
if (result.blockers.length > 0) {
|
|
237
258
|
const code = result.blockers[0]?.code ?? "";
|
|
@@ -351,6 +372,7 @@ export async function runTaskAdvance(repoRoot, args) {
|
|
|
351
372
|
// ONLY for the approve-gate execution path and disposed on every exit;
|
|
352
373
|
// its timer starts only after onRunStart (double guard, no stray timer).
|
|
353
374
|
if (options.approveGate && !options.dryRun && !options.quiet) {
|
|
375
|
+
warnForegroundDagExecution(options);
|
|
354
376
|
progress = createRunDagProgressObserver({
|
|
355
377
|
intervalMs: options.progressIntervalMs,
|
|
356
378
|
});
|
|
@@ -32,6 +32,7 @@ const USAGE = `usage:
|
|
|
32
32
|
[--dry-run | --apply]
|
|
33
33
|
[--force-overwrite-source]
|
|
34
34
|
[--json]`;
|
|
35
|
+
export { USAGE as TASK_SOURCE_PREPARE_USAGE };
|
|
35
36
|
function pushList(target, value) {
|
|
36
37
|
const list = target ?? [];
|
|
37
38
|
list.push(value);
|
|
@@ -386,6 +387,10 @@ function printHuman(result) {
|
|
|
386
387
|
console.log(lines.join("\n"));
|
|
387
388
|
}
|
|
388
389
|
export async function runTaskSourcePrepare(repoRoot, args) {
|
|
390
|
+
// CLI chain routing (task -> source -> prepare) folds the leaf name into
|
|
391
|
+
// rest[0]; strip it so `loop-agent task source prepare <id> ...` works.
|
|
392
|
+
if (args[0] === "prepare")
|
|
393
|
+
args = args.slice(1);
|
|
389
394
|
if (args.includes("--help") || args.includes("-h")) {
|
|
390
395
|
console.log(USAGE);
|
|
391
396
|
process.exitCode = 0;
|
|
@@ -3,6 +3,7 @@ import { buildOperatorResult, operatorFailed, processExitCodeForOutcome, writeOp
|
|
|
3
3
|
const COMMAND = "task status";
|
|
4
4
|
const USAGE = `usage:
|
|
5
5
|
task status <task-id> [--json|--markdown] [--details]`;
|
|
6
|
+
export { USAGE as TASK_STATUS_USAGE };
|
|
6
7
|
export function parseTaskStatusArgs(args) {
|
|
7
8
|
const options = {};
|
|
8
9
|
const positional = [];
|
|
@@ -13,8 +13,9 @@ import { captureWorkspaceWriteSnapshot, diffWorkspaceWriteSnapshots, } from "./w
|
|
|
13
13
|
import { isTargetTemplateTransientRetryNode, isWriterEmptyDiffRetryCandidate, isWriterTransportRetryCandidate, INCOMPLETE_WRITE_SET_RETRY_CATEGORY, WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY, WRITER_EMPTY_DIFF_RETRY_CATEGORY, } from "../workflows/dag/retry-policy.js";
|
|
14
14
|
import { assessBackendTestMdPlanCompleteness, assessBackendTestMdWriterCompleteness, assessBackendTestPytestPlanCompleteness, assessBackendTestPytestWriterCompleteness, assessBackendTestShardChildCompleteness, backendTestWriterProgressRoleForTask, classifyBackendTestWriterCompletenessFailure, isBackendTestCompletenessRetryCandidate, isBackendTestMdPlanTask, isBackendTestPytestPlanTask, isBackendTestShardChildTask, writeBackendTestWriterProgressArtifacts, } from "../workflows/dag/backend-test-writer-completeness.js";
|
|
15
15
|
import { resolveBackendTestLayout } from "../workflows/dag/backend-test-layout.js";
|
|
16
|
-
import { frontendTestLayoutFromSpec
|
|
16
|
+
import { frontendTestLayoutFromSpec } from "../workflows/dag/frontend-test-layout.js";
|
|
17
17
|
import { redactSecrets, truncateUtf8Preview } from "../shared/preview.js";
|
|
18
|
+
import { writeEffectiveContextReceipt } from "../workflows/dag/context-receipt.js";
|
|
18
19
|
/**
|
|
19
20
|
* Writer classification for a length-stopped thinking-only attempt: the model
|
|
20
21
|
* exhausted its output budget thinking but never issued a write/edit tool call
|
|
@@ -516,7 +517,11 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
|
|
|
516
517
|
let playwrightToolContext;
|
|
517
518
|
if (allowsPlaywrightCli && !isWriteTask) {
|
|
518
519
|
return {
|
|
519
|
-
ok: false,
|
|
520
|
+
ok: false,
|
|
521
|
+
stdout: "",
|
|
522
|
+
stderr: "pi command capability requires an explicit bounded write tool profile",
|
|
523
|
+
failureCategory: "tool-policy",
|
|
524
|
+
durationMs: Date.now() - started,
|
|
520
525
|
};
|
|
521
526
|
}
|
|
522
527
|
const strictReadSet = (input.task.readSet?.length ?? 0) > 0;
|
|
@@ -642,14 +647,34 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
|
|
|
642
647
|
const piExtensionPaths = piExtensionsResolution && resolvePiBackend() !== "cli-only"
|
|
643
648
|
? piExtensionsResolution.resolved.flatMap((entry) => entry.entryPaths)
|
|
644
649
|
: undefined;
|
|
650
|
+
const modelConfig = resolveDagPiModelConfig(input.model, input.thinking ? { thinking: input.thinking } : undefined);
|
|
651
|
+
const toolNames = resolveDagPiToolNames(input.task);
|
|
652
|
+
const userMessage = buildDagPiUserMessage(input.task, persona, step);
|
|
653
|
+
await writeEffectiveContextReceipt({
|
|
654
|
+
runDir: meta.runDir,
|
|
655
|
+
runId: meta.runId,
|
|
656
|
+
nodeId: input.task.id,
|
|
657
|
+
attempt,
|
|
658
|
+
task: input.task,
|
|
659
|
+
spec: meta.spec,
|
|
660
|
+
repoRoot: input.cwd,
|
|
661
|
+
systemPrompt: input.prompt,
|
|
662
|
+
userMessage,
|
|
663
|
+
resolvedModel: `${modelConfig.provider}/${modelConfig.model}`,
|
|
664
|
+
resolvedSkills: input.resolvedSkills ?? [],
|
|
665
|
+
resolvedExtensions: piExtensionsResolution && resolvePiBackend() !== "cli-only"
|
|
666
|
+
? piExtensionsResolution.resolved.map((entry) => entry.id)
|
|
667
|
+
: [],
|
|
668
|
+
activeTools: toolNames,
|
|
669
|
+
});
|
|
645
670
|
result = await piStepFn({
|
|
646
671
|
attachedFiles: [],
|
|
647
|
-
modelConfig
|
|
672
|
+
modelConfig,
|
|
648
673
|
prompt: input.prompt,
|
|
649
674
|
repoRoot: input.cwd,
|
|
650
675
|
step,
|
|
651
|
-
toolNames
|
|
652
|
-
userMessage
|
|
676
|
+
toolNames,
|
|
677
|
+
userMessage,
|
|
653
678
|
sessionEventsPath: path.join(meta.runDir, input.task.id, "session-events.jsonl"),
|
|
654
679
|
// Absolute max wall clock from adaptive liveness policy (defaults to 4h).
|
|
655
680
|
timeoutMs: input.timeoutMs,
|
|
@@ -927,8 +952,7 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
|
|
|
927
952
|
});
|
|
928
953
|
}
|
|
929
954
|
let completenessFailure;
|
|
930
|
-
if (writeGuardOk &&
|
|
931
|
-
isBackendTestCompletenessRetryCandidate(input.task)) {
|
|
955
|
+
if (writeGuardOk && isBackendTestCompletenessRetryCandidate(input.task)) {
|
|
932
956
|
try {
|
|
933
957
|
const writerRole = backendTestWriterProgressRoleForTask(input.task.id);
|
|
934
958
|
const backendLayout = resolveBackendTestLayout(meta.spec.backendTestLayout
|
|
@@ -941,7 +965,9 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
|
|
|
941
965
|
const progress = isBackendTestShardChildTask(input.task)
|
|
942
966
|
? await assessBackendTestShardChildCompleteness({
|
|
943
967
|
workspaceRoot: input.cwd,
|
|
944
|
-
role: writerRole === "pytest-generate"
|
|
968
|
+
role: writerRole === "pytest-generate"
|
|
969
|
+
? "pytest-generate"
|
|
970
|
+
: "md-generate",
|
|
945
971
|
writeSet: input.task.writeSet ?? [],
|
|
946
972
|
})
|
|
947
973
|
: isBackendTestMdPlanTask(input.task)
|
|
@@ -987,7 +1013,10 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep, wr
|
|
|
987
1013
|
};
|
|
988
1014
|
}
|
|
989
1015
|
}
|
|
990
|
-
if (mapped.ok &&
|
|
1016
|
+
if (mapped.ok &&
|
|
1017
|
+
writeGuardOk &&
|
|
1018
|
+
!writerOutcomeViolation &&
|
|
1019
|
+
!completenessFailure) {
|
|
991
1020
|
return mapped;
|
|
992
1021
|
}
|
|
993
1022
|
const writerThinkingExhausted = isWriterThinkingExhausted(result, mapped, changeManifestChangedFiles);
|
|
@@ -1393,9 +1422,7 @@ async function persistWriterChangeManifest(input) {
|
|
|
1393
1422
|
...(input.approvalSourceNodeId
|
|
1394
1423
|
? { approvalSourceNodeId: input.approvalSourceNodeId }
|
|
1395
1424
|
: {}),
|
|
1396
|
-
...(input.approvalDigest
|
|
1397
|
-
? { approvalDigest: input.approvalDigest }
|
|
1398
|
-
: {}),
|
|
1425
|
+
...(input.approvalDigest ? { approvalDigest: input.approvalDigest } : {}),
|
|
1399
1426
|
changedFiles: input.changedFiles,
|
|
1400
1427
|
beforeStatusSha256: createHash("sha256")
|
|
1401
1428
|
.update(input.beforeStatus)
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { appendFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
3
5
|
import { computeExtensionToolAllowlistExtras } from "./pi-extension-resolver.js";
|
|
4
6
|
import { BoundedTextPreview, classifyPiFailure, createPiJsonlStreamCollector, DEFAULT_ABORT_GRACE_MS, DEFAULT_STALL_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, extractPiEventStopReason, extractAssistantTextFromPiJson, } from "./pi-executor.js";
|
|
5
7
|
import { serializeSessionEvent } from "./pi-event-serializer.js";
|
|
6
8
|
import { PI_RECOMMENDED_COMPACTION, PI_RECOMMENDED_RETRY, } from "../shared/pi-retry-settings.js";
|
|
9
|
+
import { createCompactionController, createContextPressureExtension, createSiftBridgeState, inMemoryCompactionSettings, isDagSiftEnabled, parseCompactionPolicy, parseSiftBridgeConfig, SIFT_RETRIEVE_TOOL_NAME, } from "../shared/pi-context-pressure/index.js";
|
|
7
10
|
/** Same-session recovery after a transport stall. Exactly one continue per SDK attempt. */
|
|
8
11
|
export const PI_SDK_STALL_CONTINUE_MAX = 1;
|
|
9
12
|
/**
|
|
@@ -212,18 +215,43 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
212
215
|
const model = input.provider && input.model
|
|
213
216
|
? await resolveModel(modelRuntime, input.provider, input.model)
|
|
214
217
|
: undefined;
|
|
218
|
+
if (input.provider && input.model && !model) {
|
|
219
|
+
throw new Error(`pi SDK model unavailable: ${input.provider}/${input.model}; refusing to fall back to the session default`);
|
|
220
|
+
}
|
|
215
221
|
// Inject rate-limit-tolerant auto-retry settings for the session lifecycle.
|
|
216
222
|
// Prefer SettingsManager.inMemory so harness runs do not depend on ~/.pi or
|
|
217
223
|
// project .pi/settings.json being present/trusted.
|
|
224
|
+
const policy = parseCompactionPolicy();
|
|
225
|
+
const compaction = {
|
|
226
|
+
...PI_RECOMMENDED_COMPACTION,
|
|
227
|
+
...inMemoryCompactionSettings(policy),
|
|
228
|
+
};
|
|
218
229
|
const settingsManager = typeof SettingsManager?.inMemory === "function"
|
|
219
230
|
? SettingsManager.inMemory({
|
|
220
231
|
retry: {
|
|
221
232
|
...PI_RECOMMENDED_RETRY,
|
|
222
233
|
provider: { ...PI_RECOMMENDED_RETRY.provider },
|
|
223
234
|
},
|
|
224
|
-
compaction
|
|
235
|
+
compaction,
|
|
225
236
|
}, { projectTrusted: true })
|
|
226
237
|
: undefined;
|
|
238
|
+
const nodeSessionId = `dag-${randomUUID()}`;
|
|
239
|
+
const controller = createCompactionController();
|
|
240
|
+
const siftEnabled = isDagSiftEnabled();
|
|
241
|
+
const sift = siftEnabled.enabled
|
|
242
|
+
? createSiftBridgeState(parseSiftBridgeConfig({
|
|
243
|
+
sessionId: nodeSessionId,
|
|
244
|
+
defaultStashRoot: path.join(os.tmpdir(), "loop-agent-dag-sift"),
|
|
245
|
+
}))
|
|
246
|
+
: undefined;
|
|
247
|
+
if (sift)
|
|
248
|
+
sift.counters = controller.counters;
|
|
249
|
+
const pressureExtension = createContextPressureExtension({
|
|
250
|
+
name: "dag-pi-context-pressure",
|
|
251
|
+
policy,
|
|
252
|
+
controller,
|
|
253
|
+
sift,
|
|
254
|
+
});
|
|
227
255
|
const loader = new DefaultResourceLoader({
|
|
228
256
|
cwd: input.cwd,
|
|
229
257
|
agentDir,
|
|
@@ -233,6 +261,7 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
233
261
|
...(input.additionalExtensionPaths && input.additionalExtensionPaths.length > 0
|
|
234
262
|
? { additionalExtensionPaths: input.additionalExtensionPaths }
|
|
235
263
|
: {}),
|
|
264
|
+
...(pressureExtension ? { extensionFactories: [pressureExtension] } : {}),
|
|
236
265
|
...(settingsManager ? { settingsManager } : {}),
|
|
237
266
|
appendSystemPromptOverride: (base) => [
|
|
238
267
|
...base,
|
|
@@ -247,10 +276,12 @@ async function createSdkSession(sdk, input, shared) {
|
|
|
247
276
|
// extras into the create-time allowlist; protected builtins, the deny
|
|
248
277
|
// surface, and write-capable extension tools never join.
|
|
249
278
|
const extensionToolExtras = [];
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
extensionToolExtras.
|
|
279
|
+
const registered = collectLoaderExtensionToolNames(loader);
|
|
280
|
+
extensionToolExtras.push(...computeExtensionToolAllowlistExtras(registered.map((name) => ({ name })), input.toolNames));
|
|
281
|
+
if (siftEnabled.enabled &&
|
|
282
|
+
!extensionToolExtras.includes(SIFT_RETRIEVE_TOOL_NAME) &&
|
|
283
|
+
!input.toolNames.includes(SIFT_RETRIEVE_TOOL_NAME)) {
|
|
284
|
+
extensionToolExtras.push(...computeExtensionToolAllowlistExtras([{ name: SIFT_RETRIEVE_TOOL_NAME }], input.toolNames));
|
|
254
285
|
}
|
|
255
286
|
const sessionToolNames = [...input.toolNames, ...extensionToolExtras];
|
|
256
287
|
if (input.requireWriterCustomTools) {
|