@tea-agent/loop-agent 0.38.3 → 0.38.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -1
- package/CHANGELOG.md +153 -96
- package/README.md +9 -5
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +428 -27
- package/dist/commands/inspect-next.js +8 -0
- package/dist/executors/dag-pi-executor.js +18 -9
- package/dist/executors/pi-playwright-cli-tool.js +51 -12
- package/dist/executors/shell-executor.js +209 -13
- package/dist/shared/dag-prompt-override.js +28 -0
- package/dist/shared/operator/capabilities.js +32 -0
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +41 -8
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/worker/cli.js +31 -1
- package/dist/worker/console/chat/routes.js +1 -0
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/operator-actions.js +116 -0
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/routes.js +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +36 -5
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4kVYweA.js → abnfDiagram-N423BO3Z-gauhD9kD.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-pm3c2Rsl.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-D_JAg3wH.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-Dk7-ex52.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-Uval9oRI.js} +1 -1
- package/dist/worker/console/static/assets/channel-CVrnUdZl.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-BSYjHs-v.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-KGUuBXI8.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-JqAtxD9N.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-D6EJOVQK.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-B1JriSjm.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-D62b96Jd.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-BsNKEC_r.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-B56v6-ZP.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-D2KPnd8K.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-kzKSCaaL.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-aX91WTya.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-aX91WTya.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-C6N6KNJd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-DiyymT2D.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-BeKAXvf3.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-TkIdXfPF.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-BFmSG3ks.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-DqpJT2Fs.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-r5ATN43t.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-DEcSCMuR.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-VOvYRLVc.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-D7f4Cb1u.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-ziUf3U0G.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-BoRdQ-1y.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-B1-GncNm.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-CTW-ph8W.js} +1 -1
- package/dist/worker/console/static/assets/{index-Ya5FE7cD.css → index-C8rOIAJr.css} +1 -1
- package/dist/worker/console/static/assets/{index-DTZOKgAn.js → index-DEbyrEfW.js} +71 -71
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-BFA337tZ.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-BtEujgRR.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-C8ZwD2Jp.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-Ck-z8B11.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-CAyXjQjN.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-CcWKn6tS.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-C2NuDO7E.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-BFub3PDz.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-BJx5THZ5.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-DhalkDWV.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-CVcBM36o.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-D_vy4n0w.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-DGYdJL1D.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-C1xJFg3k.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-D_Fc43df.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-BhGbIjbu.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DUt1lyFL.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-DVkNcO-V.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BM6SGr7j.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-BQU9ufV0.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-BCQhRmYB.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-DWerOyHO.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-DxLeebQU.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/observe/node-input.js +2 -0
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +36 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +66 -0
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/format.js +23 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/state.js +11 -0
- package/dist/worker/observe/static/styles.css +73 -0
- package/dist/worker/observe/static/views/dag-inspector.js +561 -8
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/session-timeline.js +60 -4
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +20 -2
- package/dist/workflows/dag/failure-category.js +8 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +5 -1
- package/dist/workflows/dag/frontend-repair.js +14 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-test-case-checklist.js +26 -2
- package/dist/workflows/dag/frontend-test-case-manifest.js +5 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +151 -11
- package/dist/workflows/dag/frontend-verification-trace.js +29 -4
- package/dist/workflows/dag/init-hybrid.js +125 -24
- package/dist/workflows/dag/node-execution.js +20 -2
- package/dist/workflows/dag/rerun-plan.js +112 -0
- package/dist/workflows/dag/rerun-run.js +58 -6
- package/dist/workflows/dag/retry-policy.js +91 -0
- package/dist/workflows/dag/types.js +9 -1
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +2 -2
- package/package.json +5 -1
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/playwright-cli/SKILL.md +22 -0
- package/skills/playwright-cli-case-generator/SKILL.md +20 -1
- package/dist/worker/console/static/assets/channel-CPF4N7pf.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +0 -8
|
@@ -6,7 +6,7 @@ import os from "node:os";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { resolveAdapter } from "../adapters/index.js";
|
|
9
|
-
import { createNpmUpdateClient } from "../
|
|
9
|
+
import { createNpmUpdateClient, } from "../shared/update/npm-client.js";
|
|
10
10
|
import { auditDocs } from "../governance/checks.js";
|
|
11
11
|
import { harnessManifestSchema } from "../governance/manifest-types.js";
|
|
12
12
|
import { isInitRuntimeActive, } from "../shared/runtime-activity.js";
|
|
@@ -126,6 +126,7 @@ function controllerStateProjection(state) {
|
|
|
126
126
|
phase: state.phase,
|
|
127
127
|
status: state.status,
|
|
128
128
|
mergeTasks: cloneJson(state.mergeTasks),
|
|
129
|
+
queuedMergeTasks: cloneJson(state.queuedMergeTasks),
|
|
129
130
|
...(state.mergeGuard ? { mergeGuard: cloneJson(state.mergeGuard) } : {}),
|
|
130
131
|
mergeReceipts: cloneJson(state.mergeReceipts),
|
|
131
132
|
humanDecisions: cloneJson(state.humanDecisions),
|
|
@@ -202,10 +203,16 @@ function assertStateShape(state, repoRoot, runId) {
|
|
|
202
203
|
!isControllerIdentity(candidate.controllerIdentity) ||
|
|
203
204
|
!isRecord(candidate.versionGate) ||
|
|
204
205
|
!Array.isArray(candidate.mergeTasks) ||
|
|
206
|
+
!Array.isArray(candidate.queuedMergeTasks) ||
|
|
205
207
|
!Array.isArray(candidate.mergeReceipts) ||
|
|
206
208
|
!Array.isArray(candidate.humanDecisions) ||
|
|
207
209
|
!Array.isArray(candidate.actionReceipts) ||
|
|
208
|
-
!isHomeFingerprint(candidate.homeFingerprint)
|
|
210
|
+
!isHomeFingerprint(candidate.homeFingerprint) ||
|
|
211
|
+
(candidate.modelCatalog !== undefined &&
|
|
212
|
+
(!isRecord(candidate.modelCatalog) ||
|
|
213
|
+
candidate.modelCatalog.schemaVersion !== 1 ||
|
|
214
|
+
!Array.isArray(candidate.modelCatalog.models) ||
|
|
215
|
+
!Array.isArray(candidate.modelCatalog.eligibleReferences)))) {
|
|
209
216
|
throw new Error("invalid init upgrade state or repoRoot authority");
|
|
210
217
|
}
|
|
211
218
|
}
|
|
@@ -432,6 +439,7 @@ async function writeControllerAnchor(input) {
|
|
|
432
439
|
}
|
|
433
440
|
let authoritativeHomeFingerprint;
|
|
434
441
|
let authoritativeGitignoreMigrationSha256;
|
|
442
|
+
let authoritativeModelCatalogSha256;
|
|
435
443
|
if (input.initialize) {
|
|
436
444
|
if (await exists(location.file)) {
|
|
437
445
|
throw new Error("init upgrade controller authority anchor already exists");
|
|
@@ -440,6 +448,9 @@ async function writeControllerAnchor(input) {
|
|
|
440
448
|
throw new Error("init upgrade controller home fingerprint is invalid");
|
|
441
449
|
}
|
|
442
450
|
authoritativeHomeFingerprint = cloneJson(input.state.homeFingerprint);
|
|
451
|
+
authoritativeModelCatalogSha256 = input.state.modelCatalog
|
|
452
|
+
? canonicalSha256(input.state.modelCatalog)
|
|
453
|
+
: undefined;
|
|
443
454
|
}
|
|
444
455
|
else {
|
|
445
456
|
const current = await readControllerAnchor(location.repoRoot, input.state);
|
|
@@ -452,6 +463,7 @@ async function writeControllerAnchor(input) {
|
|
|
452
463
|
throw new Error("init upgrade controller authority gitignore migration digest binding is invalid");
|
|
453
464
|
}
|
|
454
465
|
authoritativeGitignoreMigrationSha256 = digest;
|
|
466
|
+
authoritativeModelCatalogSha256 = current.modelCatalogSha256;
|
|
455
467
|
}
|
|
456
468
|
await writeJsonAtomic(location.file, {
|
|
457
469
|
schemaVersion: 1,
|
|
@@ -459,6 +471,9 @@ async function writeControllerAnchor(input) {
|
|
|
459
471
|
runId: input.state.runId,
|
|
460
472
|
controllerIdentity: input.state.controllerIdentity,
|
|
461
473
|
homeFingerprint: authoritativeHomeFingerprint,
|
|
474
|
+
...(authoritativeModelCatalogSha256 !== undefined
|
|
475
|
+
? { modelCatalogSha256: authoritativeModelCatalogSha256 }
|
|
476
|
+
: {}),
|
|
462
477
|
...(authoritativeGitignoreMigrationSha256 !== undefined
|
|
463
478
|
? { gitignoreMigrationSha256: authoritativeGitignoreMigrationSha256 }
|
|
464
479
|
: {}),
|
|
@@ -483,6 +498,12 @@ async function readControllerAnchor(repoRoot, state) {
|
|
|
483
498
|
!sameControllerIdentity(parsed.controllerIdentity, state.controllerIdentity)) {
|
|
484
499
|
throw new Error("init upgrade controller authority anchor binding is invalid");
|
|
485
500
|
}
|
|
501
|
+
const expectedModelCatalogSha256 = state.modelCatalog
|
|
502
|
+
? canonicalSha256(state.modelCatalog)
|
|
503
|
+
: undefined;
|
|
504
|
+
if (parsed.modelCatalogSha256 !== expectedModelCatalogSha256) {
|
|
505
|
+
throw new Error("init upgrade model catalog differs from controller authority");
|
|
506
|
+
}
|
|
486
507
|
if (!isHomeFingerprint(parsed.homeFingerprint) ||
|
|
487
508
|
!sameJsonValue(parsed.homeFingerprint, state.homeFingerprint)) {
|
|
488
509
|
throw new Error("init upgrade controller authority home fingerprint binding is invalid");
|
|
@@ -660,6 +681,7 @@ function toResult(state, verification, gitignoreMigration) {
|
|
|
660
681
|
status: state.status,
|
|
661
682
|
phase: state.phase,
|
|
662
683
|
controllerIdentity: state.controllerIdentity,
|
|
684
|
+
versionGate: state.versionGate,
|
|
663
685
|
deterministicActions: state.actionReceipts,
|
|
664
686
|
modelMergeTasks: state.mergeTasks,
|
|
665
687
|
humanDecisions: state.humanDecisions,
|
|
@@ -677,12 +699,18 @@ function renderReport(state, verification, gitignoreMigration) {
|
|
|
677
699
|
`- phase: \`${state.phase}\``,
|
|
678
700
|
`- controller: \`${state.controllerIdentity.portableIdentity}\``,
|
|
679
701
|
`- version gate: \`${state.versionGate.outcome}\``,
|
|
702
|
+
...(state.versionGate.failureReason
|
|
703
|
+
? [`- version check failure: \`${state.versionGate.failureReason}\``]
|
|
704
|
+
: []),
|
|
705
|
+
...(state.versionGate.diagnostic
|
|
706
|
+
? [`- version check diagnostic: ${state.versionGate.diagnostic}`]
|
|
707
|
+
: []),
|
|
680
708
|
`- version choice: \`${state.versionGate.choice ?? "pending"}\``,
|
|
681
709
|
`- deterministic actions applied: ${state.actionReceipts.length}`,
|
|
682
710
|
`- semantic merges accepted: ${state.mergeReceipts.length}`,
|
|
683
711
|
`- preserved user modifications: ${state.mergeReceipts.length}`,
|
|
684
712
|
`- retired path actions: ${state.actionReceipts.filter((action) => action.type === "migrate-owned-file" || action.type === "remove-owned-file" || action.type === "remove-empty-directory").length}`,
|
|
685
|
-
`- remaining model merge tasks: ${state.mergeTasks.length}`,
|
|
713
|
+
`- remaining model merge tasks: ${state.mergeTasks.length + state.queuedMergeTasks.length}`,
|
|
686
714
|
`- remaining human decisions: ${state.humanDecisions.length}`,
|
|
687
715
|
`- gitignore migration assessment: \`.harness/init-upgrades/${state.runId}/gitignore-migration.json\` (index-only untrack guidance; the controller never modifies the Git index)`,
|
|
688
716
|
...(gitignoreMigration
|
|
@@ -836,22 +864,45 @@ async function acquireGlobalLock(input) {
|
|
|
836
864
|
}
|
|
837
865
|
async function currentVersionGate(input, identity) {
|
|
838
866
|
try {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
867
|
+
let latest;
|
|
868
|
+
if (input.latestVersion) {
|
|
869
|
+
latest = await input.latestVersion();
|
|
870
|
+
if (!latest) {
|
|
871
|
+
return registryUnavailable("invalid-response");
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
const checked = await createNpmUpdateClient({
|
|
842
876
|
currentVersion: identity.version,
|
|
843
877
|
packageRoot: identity.packageRoot,
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
878
|
+
cwd: path.resolve(input.repoRoot),
|
|
879
|
+
env: process.env,
|
|
880
|
+
}).latestVersionCheck();
|
|
881
|
+
if (!checked.ok)
|
|
882
|
+
return registryUnavailable(checked.reason);
|
|
883
|
+
latest = checked.version;
|
|
884
|
+
}
|
|
847
885
|
return versionsNewer(latest, identity.version)
|
|
848
886
|
? { outcome: "newer-available", latestVersion: latest }
|
|
849
887
|
: { outcome: "latest", latestVersion: latest };
|
|
850
888
|
}
|
|
851
889
|
catch {
|
|
852
|
-
return
|
|
890
|
+
return registryUnavailable("command-failed");
|
|
853
891
|
}
|
|
854
892
|
}
|
|
893
|
+
function registryUnavailable(reason) {
|
|
894
|
+
const diagnostics = {
|
|
895
|
+
timeout: "npm version check timed out while using the configured npm settings. Verify the registry/proxy and retry.",
|
|
896
|
+
"spawn-failure": "npm could not be started. Verify that npm is installed and available on PATH.",
|
|
897
|
+
"command-failed": "npm version check failed using the configured registry. Run `npm config get registry` and `npm view @tea-agent/loop-agent dist-tags.latest` to diagnose.",
|
|
898
|
+
"invalid-response": "npm returned no valid stable latest version. Verify that the configured registry mirrors @tea-agent/loop-agent and its dist-tags.",
|
|
899
|
+
};
|
|
900
|
+
return {
|
|
901
|
+
outcome: "registry-unavailable",
|
|
902
|
+
failureReason: reason,
|
|
903
|
+
diagnostic: diagnostics[reason],
|
|
904
|
+
};
|
|
905
|
+
}
|
|
855
906
|
function toRepoPath(repoRoot, absolutePath) {
|
|
856
907
|
return path.relative(repoRoot, absolutePath).split(path.sep).join("/");
|
|
857
908
|
}
|
|
@@ -1729,7 +1780,8 @@ async function validateContinueMerge(directory, state, anchor) {
|
|
|
1729
1780
|
state.mergeTasks = [];
|
|
1730
1781
|
state.mergeGuard = undefined;
|
|
1731
1782
|
state.phase = "PLAN";
|
|
1732
|
-
state.nextAction =
|
|
1783
|
+
state.nextAction =
|
|
1784
|
+
"Accepted the bounded semantic merge; continue with the next queued merge or run final verification.";
|
|
1733
1785
|
await saveState(directory, state);
|
|
1734
1786
|
await rm(path.join(directory, "merge-acceptance-pending.json"), {
|
|
1735
1787
|
force: true,
|
|
@@ -2397,10 +2449,13 @@ async function verifyRecoveryFiles(repoRoot) {
|
|
|
2397
2449
|
}
|
|
2398
2450
|
return verification;
|
|
2399
2451
|
}
|
|
2400
|
-
async function runFinalVerification(state, authoritativeHomeFingerprint, surface) {
|
|
2452
|
+
async function runFinalVerification(state, authoritativeHomeFingerprint, surface, modelCatalog) {
|
|
2401
2453
|
await assertFrozenVerificationSurface(state.repoRoot, surface);
|
|
2402
|
-
const finalCheck = await checkInitUpdate({
|
|
2403
|
-
|
|
2454
|
+
const finalCheck = await checkInitUpdate({
|
|
2455
|
+
repoRoot: state.repoRoot,
|
|
2456
|
+
modelCatalog,
|
|
2457
|
+
});
|
|
2458
|
+
const doctor = await runInitDoctor({ repoRoot: state.repoRoot, modelCatalog });
|
|
2404
2459
|
let inspect;
|
|
2405
2460
|
try {
|
|
2406
2461
|
const adapter = await resolveAdapter(state.repoRoot);
|
|
@@ -2474,6 +2529,11 @@ async function runFinalVerification(state, authoritativeHomeFingerprint, surface
|
|
|
2474
2529
|
mergeReceiptCount: state.mergeReceipts.length,
|
|
2475
2530
|
},
|
|
2476
2531
|
initSurfaceClean: finalCheck.ok,
|
|
2532
|
+
initSurface: {
|
|
2533
|
+
deterministicActions: finalCheck.deterministicActions,
|
|
2534
|
+
modelMergeTasks: finalCheck.modelMergeTasks,
|
|
2535
|
+
humanDecisions: finalCheck.humanDecisions,
|
|
2536
|
+
},
|
|
2477
2537
|
doctor: { ok: doctor.ok, checks: doctor.checks },
|
|
2478
2538
|
inspect,
|
|
2479
2539
|
docsAudit,
|
|
@@ -2488,7 +2548,7 @@ async function runFinalVerification(state, authoritativeHomeFingerprint, surface
|
|
|
2488
2548
|
projectPi.reason === "enabled-false",
|
|
2489
2549
|
},
|
|
2490
2550
|
userHomeUnchanged: sameJsonValue(homeAfter, authoritativeHomeFingerprint),
|
|
2491
|
-
mergeBoundaryClean: state.mergeTasks.length === 0,
|
|
2551
|
+
mergeBoundaryClean: state.mergeTasks.length === 0 && state.queuedMergeTasks.length === 0,
|
|
2492
2552
|
humanDecisionClean: state.humanDecisions.length === 0,
|
|
2493
2553
|
};
|
|
2494
2554
|
}
|
|
@@ -2589,6 +2649,7 @@ function assertAnchorCompletion(anchor, state) {
|
|
|
2589
2649
|
!isSha256(completion.actionReceiptsSha256) ||
|
|
2590
2650
|
!isSha256(completion.mergeReceiptsSha256) ||
|
|
2591
2651
|
!isSha256(completion.stateProjectionSha256) ||
|
|
2652
|
+
!isSha256(completion.verificationSha256) ||
|
|
2592
2653
|
!isSha256(completion.finalReportSha256) ||
|
|
2593
2654
|
!isFrozenVerificationSurface(completion.verificationSurface) ||
|
|
2594
2655
|
completion.actionReceiptsSha256 !== sha256(serializeJson(state.actionReceipts)) ||
|
|
@@ -2609,7 +2670,19 @@ async function readFreshCompletedVerification(repoRoot, state) {
|
|
|
2609
2670
|
if (!anchor.completion?.verificationSurface) {
|
|
2610
2671
|
throw new Error("completed verification surface authority is missing");
|
|
2611
2672
|
}
|
|
2612
|
-
const
|
|
2673
|
+
const verificationBytes = await readFile(path.join(runDirectory(repoRoot, state.runId), "verification.json")).catch(() => {
|
|
2674
|
+
throw new Error("completed init upgrade verification evidence is missing or unreadable");
|
|
2675
|
+
});
|
|
2676
|
+
if (sha256(verificationBytes) !== anchor.completion.verificationSha256) {
|
|
2677
|
+
throw new Error("completed init upgrade verification evidence is not authorized by the controller anchor");
|
|
2678
|
+
}
|
|
2679
|
+
let verification;
|
|
2680
|
+
try {
|
|
2681
|
+
verification = JSON.parse(verificationBytes.toString("utf-8"));
|
|
2682
|
+
}
|
|
2683
|
+
catch {
|
|
2684
|
+
throw new Error("completed init upgrade verification evidence is invalid JSON");
|
|
2685
|
+
}
|
|
2613
2686
|
assertCompletedVerification(state, verification);
|
|
2614
2687
|
return { verification, reportMarkdown: report.toString("utf-8") };
|
|
2615
2688
|
}
|
|
@@ -2683,6 +2756,119 @@ async function readStatusResult(repoRoot, runId) {
|
|
|
2683
2756
|
const authenticated = await readFreshCompletedVerification(repoRoot, state);
|
|
2684
2757
|
return toResult(state, authenticated.verification, await readGitignoreMigrationProjection(directory, state, anchor.gitignoreMigrationSha256));
|
|
2685
2758
|
}
|
|
2759
|
+
async function activateModelMergeTask(input) {
|
|
2760
|
+
const { directory, state, task, gitignoreMigration } = input;
|
|
2761
|
+
const verificationSurface = await freezeVerificationSurface(state.repoRoot);
|
|
2762
|
+
assertTaskOutsideVerificationSurface(task, verificationSurface);
|
|
2763
|
+
if (verificationSurface.files.some((entry) => entry.path === normalizeAllowedPath(task.path))) {
|
|
2764
|
+
await validateControllerAuthorizedVerificationMerge({
|
|
2765
|
+
repoRoot: state.repoRoot,
|
|
2766
|
+
task,
|
|
2767
|
+
surface: verificationSurface,
|
|
2768
|
+
});
|
|
2769
|
+
}
|
|
2770
|
+
state.mergeTasks = [task];
|
|
2771
|
+
state.mergeGuard = await createMergeGuard(state.repoRoot, task);
|
|
2772
|
+
state.phase = "MODEL_MERGE";
|
|
2773
|
+
state.status = "in-progress";
|
|
2774
|
+
await writeControllerAnchor({
|
|
2775
|
+
state,
|
|
2776
|
+
mergeAuthority: {
|
|
2777
|
+
...state.mergeGuard,
|
|
2778
|
+
priorMergeReceiptCount: state.mergeReceipts.length,
|
|
2779
|
+
priorMergeReceiptsSha256: sha256(serializeJson(state.mergeReceipts)),
|
|
2780
|
+
stateProjection: controllerStateProjection(state),
|
|
2781
|
+
stateProjectionSha256: canonicalSha256(controllerStateProjection(state)),
|
|
2782
|
+
verificationSurface,
|
|
2783
|
+
},
|
|
2784
|
+
});
|
|
2785
|
+
state.nextAction = `Complete only task ${task.taskId} within allowedPaths=[${task.allowedPaths.join(", ")}], then call init upgrade --continue. The controller will reject every other workspace change and all symlink/realpath escapes. ${state.queuedMergeTasks.length} semantic merge task(s) remain queued without replanning.`;
|
|
2786
|
+
await writeJsonAtomic(path.join(directory, "merge-tasks.json"), [state.mergeTasks[0], ...state.queuedMergeTasks]);
|
|
2787
|
+
await saveState(directory, state);
|
|
2788
|
+
await writeReport(directory, state, undefined, gitignoreMigration);
|
|
2789
|
+
return toResult(state, undefined, gitignoreMigration);
|
|
2790
|
+
}
|
|
2791
|
+
async function verifyAndCompleteUpgrade(input) {
|
|
2792
|
+
const { directory, state, gitignoreMigration, modelCatalog } = input;
|
|
2793
|
+
state.mergeTasks = [];
|
|
2794
|
+
state.queuedMergeTasks = [];
|
|
2795
|
+
state.mergeGuard = undefined;
|
|
2796
|
+
state.humanDecisions = [];
|
|
2797
|
+
if (state.mergeReceipts.length > 0) {
|
|
2798
|
+
let postMerge = await checkInitUpdate({
|
|
2799
|
+
repoRoot: state.repoRoot,
|
|
2800
|
+
modelCatalog,
|
|
2801
|
+
});
|
|
2802
|
+
const seen = new Set();
|
|
2803
|
+
for (let pass = 0; postMerge.deterministicActions.length > 0 && pass < 2; pass += 1) {
|
|
2804
|
+
const digest = canonicalSha256(postMerge.deterministicActions);
|
|
2805
|
+
if (seen.has(digest)) {
|
|
2806
|
+
throw new Error(`post-merge deterministic init update did not converge: ${postMerge.deterministicActions.map((action) => `${action.type}:${action.path}`).join(", ")}`);
|
|
2807
|
+
}
|
|
2808
|
+
seen.add(digest);
|
|
2809
|
+
const converged = await applyInitUpdate({
|
|
2810
|
+
repoRoot: state.repoRoot,
|
|
2811
|
+
applySafe: true,
|
|
2812
|
+
clientRecovery: "project",
|
|
2813
|
+
modelCatalog,
|
|
2814
|
+
});
|
|
2815
|
+
state.actionReceipts.push(...converged.applied);
|
|
2816
|
+
await writeJsonAtomic(path.join(directory, "change-manifest.json"), state.actionReceipts);
|
|
2817
|
+
if (converged.applied.length === 0)
|
|
2818
|
+
break;
|
|
2819
|
+
postMerge = await checkInitUpdate({
|
|
2820
|
+
repoRoot: state.repoRoot,
|
|
2821
|
+
modelCatalog,
|
|
2822
|
+
});
|
|
2823
|
+
}
|
|
2824
|
+
if (postMerge.deterministicActions.length > 0) {
|
|
2825
|
+
throw new Error(`post-merge deterministic init update exceeded the 2-pass convergence limit: ${postMerge.deterministicActions.map((action) => `${action.type}:${action.path}`).join(", ")}`);
|
|
2826
|
+
}
|
|
2827
|
+
if (postMerge.modelMergeTasks.length > 0 ||
|
|
2828
|
+
postMerge.humanDecisions.length > 0) {
|
|
2829
|
+
throw new Error("accepted semantic merges introduced new non-deterministic init decisions; start a new bounded upgrade run");
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
state.phase = "VERIFY";
|
|
2833
|
+
const anchor = await readControllerAnchor(state.repoRoot, state);
|
|
2834
|
+
const verificationSurface = await freezeVerificationSurface(state.repoRoot);
|
|
2835
|
+
const verification = await runFinalVerification(state, anchor.homeFingerprint, verificationSurface, modelCatalog);
|
|
2836
|
+
await writeJsonAtomic(path.join(directory, "verification.json"), verification);
|
|
2837
|
+
const verificationSha256 = sha256(await readFile(path.join(directory, "verification.json")));
|
|
2838
|
+
if (verificationPassed(verification)) {
|
|
2839
|
+
state.status = "completed";
|
|
2840
|
+
state.phase = "COMPLETED";
|
|
2841
|
+
state.nextAction =
|
|
2842
|
+
"Init upgrade completed; reload the host session so updated AGENTS, skills, and recovery extensions are discovered.";
|
|
2843
|
+
await writeReport(directory, state, verification, gitignoreMigration);
|
|
2844
|
+
const finalReportSha256 = sha256(await readFile(path.join(directory, "final-report.md")));
|
|
2845
|
+
await writeControllerAnchor({
|
|
2846
|
+
state,
|
|
2847
|
+
completion: {
|
|
2848
|
+
status: "completed",
|
|
2849
|
+
phase: "COMPLETED",
|
|
2850
|
+
actionReceiptCount: state.actionReceipts.length,
|
|
2851
|
+
mergeReceiptCount: state.mergeReceipts.length,
|
|
2852
|
+
actionReceiptsSha256: sha256(serializeJson(state.actionReceipts)),
|
|
2853
|
+
mergeReceiptsSha256: sha256(serializeJson(state.mergeReceipts)),
|
|
2854
|
+
stateProjectionSha256: canonicalSha256(controllerStateProjection(state)),
|
|
2855
|
+
verificationSurface,
|
|
2856
|
+
verificationSha256,
|
|
2857
|
+
finalReportSha256,
|
|
2858
|
+
},
|
|
2859
|
+
});
|
|
2860
|
+
}
|
|
2861
|
+
else {
|
|
2862
|
+
state.status = "failed";
|
|
2863
|
+
const authorityReason = authoritativeVerificationFailureReason(verification);
|
|
2864
|
+
state.nextAction = authorityReason
|
|
2865
|
+
? `Verification failed: ${authorityReason}. Inspect verification.json, repair only the controller-returned concrete merge path, then use --continue.`
|
|
2866
|
+
: "Verification failed. Inspect verification.json; repair only the controller-returned concrete merge path or address the reported external blocker, then use --continue.";
|
|
2867
|
+
}
|
|
2868
|
+
await saveState(directory, state);
|
|
2869
|
+
await writeReport(directory, state, verification, gitignoreMigration);
|
|
2870
|
+
return toResult(state, verification, gitignoreMigration);
|
|
2871
|
+
}
|
|
2686
2872
|
async function executeUpgrade(directory, state, input) {
|
|
2687
2873
|
const identity = await readIdentity();
|
|
2688
2874
|
if (!sameControllerIdentity(identity, state.controllerIdentity)) {
|
|
@@ -2711,8 +2897,7 @@ async function executeUpgrade(directory, state, input) {
|
|
|
2711
2897
|
}
|
|
2712
2898
|
if (state.versionGate.outcome === "registry-unavailable" && !choice) {
|
|
2713
2899
|
state.status = "needs-human-decision";
|
|
2714
|
-
state.nextAction =
|
|
2715
|
-
"npm registry is unavailable. Choose --version-choice current, retry, or cancel.";
|
|
2900
|
+
state.nextAction = `${state.versionGate.diagnostic ?? "npm registry is unavailable."} Choose --version-choice current, retry, or cancel.`;
|
|
2716
2901
|
await saveState(directory, state);
|
|
2717
2902
|
await writeReport(directory, state);
|
|
2718
2903
|
return toResult(state);
|
|
@@ -2742,15 +2927,17 @@ async function executeUpgrade(directory, state, input) {
|
|
|
2742
2927
|
if (choice === "retry" &&
|
|
2743
2928
|
state.versionGate.outcome === "registry-unavailable") {
|
|
2744
2929
|
state.status = "needs-human-decision";
|
|
2745
|
-
state.nextAction =
|
|
2746
|
-
"npm registry remains unavailable after retry. Choose --version-choice current, retry, or cancel.";
|
|
2930
|
+
state.nextAction = `${state.versionGate.diagnostic ?? "npm registry remains unavailable after retry."} Choose --version-choice current, retry, or cancel.`;
|
|
2747
2931
|
await saveState(directory, state);
|
|
2748
2932
|
await writeReport(directory, state);
|
|
2749
2933
|
return toResult(state);
|
|
2750
2934
|
}
|
|
2751
2935
|
state.status = "in-progress";
|
|
2752
2936
|
state.phase = "PLAN";
|
|
2753
|
-
const pre = await checkInitUpdate({
|
|
2937
|
+
const pre = await checkInitUpdate({
|
|
2938
|
+
repoRoot: state.repoRoot,
|
|
2939
|
+
modelCatalog: state.modelCatalog,
|
|
2940
|
+
});
|
|
2754
2941
|
await writeJsonAtomic(path.join(directory, "pre-update-report.json"), pre);
|
|
2755
2942
|
await writeJsonAtomic(path.join(directory, "upgrade-plan.json"), {
|
|
2756
2943
|
deterministicActions: pre.deterministicActions,
|
|
@@ -2791,29 +2978,42 @@ async function executeUpgrade(directory, state, input) {
|
|
|
2791
2978
|
repoRoot: state.repoRoot,
|
|
2792
2979
|
applySafe: true,
|
|
2793
2980
|
clientRecovery: "project",
|
|
2981
|
+
modelCatalog: state.modelCatalog,
|
|
2794
2982
|
});
|
|
2795
2983
|
state.actionReceipts.push(...safe.applied);
|
|
2796
2984
|
await writeJsonAtomic(path.join(directory, "change-manifest.json"), state.actionReceipts);
|
|
2797
|
-
let post = await checkInitUpdate({
|
|
2985
|
+
let post = await checkInitUpdate({
|
|
2986
|
+
repoRoot: state.repoRoot,
|
|
2987
|
+
modelCatalog: state.modelCatalog,
|
|
2988
|
+
});
|
|
2798
2989
|
// Converge remaining safe deterministic init actions before the final check.
|
|
2799
2990
|
// Project/governance-derived drift (e.g. a personalized project rename after
|
|
2800
2991
|
// an accepted merge) settles here instead of failing finalCheck. Model merge
|
|
2801
2992
|
// tasks and human decisions are never auto-applied.
|
|
2802
|
-
|
|
2993
|
+
const deterministicPassDigests = new Set();
|
|
2994
|
+
for (let pass = 0; post.deterministicActions.length > 0 && pass < 2; pass += 1) {
|
|
2995
|
+
const actionDigest = canonicalSha256(post.deterministicActions);
|
|
2996
|
+
if (deterministicPassDigests.has(actionDigest)) {
|
|
2997
|
+
throw new Error(`deterministic init update did not converge; repeated actions: ${post.deterministicActions.map((action) => `${action.type}:${action.path}`).join(", ")}`);
|
|
2998
|
+
}
|
|
2999
|
+
deterministicPassDigests.add(actionDigest);
|
|
2803
3000
|
const converged = await applyInitUpdate({
|
|
2804
3001
|
repoRoot: state.repoRoot,
|
|
2805
3002
|
applySafe: true,
|
|
2806
3003
|
clientRecovery: "project",
|
|
3004
|
+
modelCatalog: state.modelCatalog,
|
|
2807
3005
|
});
|
|
2808
3006
|
state.actionReceipts.push(...converged.applied);
|
|
2809
3007
|
await writeJsonAtomic(path.join(directory, "change-manifest.json"), state.actionReceipts);
|
|
2810
3008
|
if (converged.applied.length === 0)
|
|
2811
3009
|
break;
|
|
2812
|
-
post = await checkInitUpdate({
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
3010
|
+
post = await checkInitUpdate({
|
|
3011
|
+
repoRoot: state.repoRoot,
|
|
3012
|
+
modelCatalog: state.modelCatalog,
|
|
3013
|
+
});
|
|
3014
|
+
}
|
|
3015
|
+
if (post.deterministicActions.length > 0) {
|
|
3016
|
+
throw new Error(`deterministic init update exceeded the 2-pass convergence limit: ${post.deterministicActions.map((action) => `${action.type}:${action.path}`).join(", ")}`);
|
|
2817
3017
|
}
|
|
2818
3018
|
state.humanDecisions = post.humanDecisions;
|
|
2819
3019
|
// Gitignore migration assessment: after the managed block has converged,
|
|
@@ -2851,6 +3051,7 @@ async function executeUpgrade(directory, state, input) {
|
|
|
2851
3051
|
state.humanDecisions = post.humanDecisions;
|
|
2852
3052
|
if (post.humanDecisions.length > 0) {
|
|
2853
3053
|
state.mergeTasks = [];
|
|
3054
|
+
state.queuedMergeTasks = [];
|
|
2854
3055
|
state.mergeGuard = undefined;
|
|
2855
3056
|
state.status = "needs-human-decision";
|
|
2856
3057
|
state.phase = "MODEL_MERGE";
|
|
@@ -2863,78 +3064,21 @@ async function executeUpgrade(directory, state, input) {
|
|
|
2863
3064
|
}
|
|
2864
3065
|
if (post.modelMergeTasks.length > 0) {
|
|
2865
3066
|
const activeTask = post.modelMergeTasks[0];
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
await validateControllerAuthorizedVerificationMerge({
|
|
2870
|
-
repoRoot: state.repoRoot,
|
|
2871
|
-
task: activeTask,
|
|
2872
|
-
surface: verificationSurface,
|
|
2873
|
-
});
|
|
2874
|
-
}
|
|
2875
|
-
state.mergeTasks = [activeTask];
|
|
2876
|
-
state.mergeGuard = await createMergeGuard(state.repoRoot, activeTask);
|
|
2877
|
-
// Bind the Git-dir anchor to the state actually handed to the model.
|
|
2878
|
-
state.phase = "MODEL_MERGE";
|
|
2879
|
-
state.status = "in-progress";
|
|
2880
|
-
await writeControllerAnchor({
|
|
2881
|
-
state,
|
|
2882
|
-
mergeAuthority: {
|
|
2883
|
-
...state.mergeGuard,
|
|
2884
|
-
priorMergeReceiptCount: state.mergeReceipts.length,
|
|
2885
|
-
priorMergeReceiptsSha256: sha256(serializeJson(state.mergeReceipts)),
|
|
2886
|
-
stateProjection: controllerStateProjection(state),
|
|
2887
|
-
stateProjectionSha256: canonicalSha256(controllerStateProjection(state)),
|
|
2888
|
-
verificationSurface,
|
|
2889
|
-
},
|
|
2890
|
-
});
|
|
2891
|
-
state.nextAction = `Complete only task ${activeTask.taskId} within allowedPaths=[${activeTask.allowedPaths.join(", ")}], then call init upgrade --continue. The controller will reject every other workspace change and all symlink/realpath escapes.`;
|
|
2892
|
-
await writeJsonAtomic(path.join(directory, "merge-tasks.json"), state.mergeTasks);
|
|
2893
|
-
await saveState(directory, state);
|
|
2894
|
-
await writeReport(directory, state, undefined, gitignoreMigration);
|
|
2895
|
-
return toResult(state, undefined, gitignoreMigration);
|
|
2896
|
-
}
|
|
2897
|
-
state.mergeTasks = [];
|
|
2898
|
-
state.mergeGuard = undefined;
|
|
2899
|
-
state.humanDecisions = [];
|
|
2900
|
-
state.phase = "VERIFY";
|
|
2901
|
-
const anchor = await readControllerAnchor(state.repoRoot, state);
|
|
2902
|
-
const verificationSurface = await freezeVerificationSurface(state.repoRoot);
|
|
2903
|
-
const verification = await runFinalVerification(state, anchor.homeFingerprint, verificationSurface);
|
|
2904
|
-
await writeJsonAtomic(path.join(directory, "verification.json"), verification);
|
|
2905
|
-
if (verificationPassed(verification)) {
|
|
2906
|
-
state.status = "completed";
|
|
2907
|
-
state.phase = "COMPLETED";
|
|
2908
|
-
state.nextAction =
|
|
2909
|
-
"Init upgrade completed; reload the host session so updated AGENTS, skills, and recovery extensions are discovered.";
|
|
2910
|
-
// The report is controller-produced before its exact bytes are anchored.
|
|
2911
|
-
await writeReport(directory, state, verification, gitignoreMigration);
|
|
2912
|
-
const finalReportSha256 = sha256(await readFile(path.join(directory, "final-report.md")));
|
|
2913
|
-
await writeControllerAnchor({
|
|
3067
|
+
state.queuedMergeTasks = post.modelMergeTasks.slice(1);
|
|
3068
|
+
return await activateModelMergeTask({
|
|
3069
|
+
directory,
|
|
2914
3070
|
state,
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
phase: "COMPLETED",
|
|
2918
|
-
actionReceiptCount: state.actionReceipts.length,
|
|
2919
|
-
mergeReceiptCount: state.mergeReceipts.length,
|
|
2920
|
-
actionReceiptsSha256: sha256(serializeJson(state.actionReceipts)),
|
|
2921
|
-
mergeReceiptsSha256: sha256(serializeJson(state.mergeReceipts)),
|
|
2922
|
-
stateProjectionSha256: canonicalSha256(controllerStateProjection(state)),
|
|
2923
|
-
verificationSurface,
|
|
2924
|
-
finalReportSha256,
|
|
2925
|
-
},
|
|
3071
|
+
task: activeTask,
|
|
3072
|
+
gitignoreMigration,
|
|
2926
3073
|
});
|
|
2927
3074
|
}
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
state
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
}
|
|
2935
|
-
await saveState(directory, state);
|
|
2936
|
-
await writeReport(directory, state, verification, gitignoreMigration);
|
|
2937
|
-
return toResult(state, verification, gitignoreMigration);
|
|
3075
|
+
state.queuedMergeTasks = [];
|
|
3076
|
+
return await verifyAndCompleteUpgrade({
|
|
3077
|
+
directory,
|
|
3078
|
+
state,
|
|
3079
|
+
gitignoreMigration,
|
|
3080
|
+
modelCatalog: state.modelCatalog,
|
|
3081
|
+
});
|
|
2938
3082
|
}
|
|
2939
3083
|
export async function runInitUpgrade(input) {
|
|
2940
3084
|
const repoRoot = path.resolve(input.repoRoot);
|
|
@@ -3031,6 +3175,28 @@ export async function runInitUpgrade(input) {
|
|
|
3031
3175
|
force: true,
|
|
3032
3176
|
});
|
|
3033
3177
|
}
|
|
3178
|
+
if (mergeOutcome === "accepted") {
|
|
3179
|
+
const anchor = await readControllerAnchor(repoRoot, state);
|
|
3180
|
+
const gitignoreMigration = await readGitignoreMigrationProjection(directory, state, anchor.gitignoreMigrationSha256);
|
|
3181
|
+
if (!gitignoreMigration) {
|
|
3182
|
+
throw new Error("accepted init upgrade merge lacks its migration assessment");
|
|
3183
|
+
}
|
|
3184
|
+
const nextTask = state.queuedMergeTasks.shift();
|
|
3185
|
+
if (nextTask) {
|
|
3186
|
+
return await activateModelMergeTask({
|
|
3187
|
+
directory,
|
|
3188
|
+
state,
|
|
3189
|
+
task: nextTask,
|
|
3190
|
+
gitignoreMigration,
|
|
3191
|
+
});
|
|
3192
|
+
}
|
|
3193
|
+
return await verifyAndCompleteUpgrade({
|
|
3194
|
+
directory,
|
|
3195
|
+
state,
|
|
3196
|
+
gitignoreMigration,
|
|
3197
|
+
modelCatalog: state.modelCatalog,
|
|
3198
|
+
});
|
|
3199
|
+
}
|
|
3034
3200
|
}
|
|
3035
3201
|
else {
|
|
3036
3202
|
directory = runDirectory(repoRoot, runId);
|
|
@@ -3049,10 +3215,12 @@ export async function runInitUpgrade(input) {
|
|
|
3049
3215
|
versionGate: { outcome: "latest" },
|
|
3050
3216
|
nextAction: "Evaluate controller version and init surface.",
|
|
3051
3217
|
mergeTasks: [],
|
|
3218
|
+
queuedMergeTasks: [],
|
|
3052
3219
|
mergeReceipts: [],
|
|
3053
3220
|
humanDecisions: [],
|
|
3054
3221
|
actionReceipts: [],
|
|
3055
3222
|
homeFingerprint: await homeFingerprint(),
|
|
3223
|
+
modelCatalog: input.modelCatalog,
|
|
3056
3224
|
};
|
|
3057
3225
|
await writeControllerAnchor({ state, initialize: true });
|
|
3058
3226
|
await writeJsonAtomic(path.join(directory, "controller-identity.json"), {
|