@tea-agent/loop-agent 0.41.0-next.3 → 0.41.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +264 -32
- package/dist/application/dag/generate-task-dag.js +75 -19
- package/dist/application/task-lifecycle/advance.js +24 -5
- package/dist/application/task-lifecycle/observe.js +171 -17
- package/dist/application/task-lifecycle/plan-transitions.js +42 -7
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +7 -0
- package/dist/cli/program.js +5 -0
- package/dist/commands/client-recovery.js +3 -0
- package/dist/commands/dag-follow-up.js +138 -0
- package/dist/commands/init.js +27 -1
- package/dist/commands/task-advance.js +19 -0
- package/dist/executors/dag-pi-executor.js +3784 -231
- package/dist/executors/pi-executor.js +15 -4
- package/dist/executors/pi-read-budget-policy.js +239 -0
- package/dist/executors/shell-executor.js +945 -217
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/shared/dag-failure-category.js +12 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +7 -0
- package/dist/task/config-types.js +34 -7
- package/dist/task/contract/adopt.js +4 -0
- package/dist/task/contract/import-revision.js +4 -0
- package/dist/task/contract/project.js +3 -0
- package/dist/task/contract/schema.js +2 -1
- package/dist/task/frontend-project-capability.js +203 -20
- package/dist/task/runtime.js +5 -2
- package/dist/task/source-prepare/build-draft.js +3 -3
- package/dist/task/source-prepare/fragment-inventory.js +15 -9
- package/dist/task/source-prepare/prepare.js +89 -1
- package/dist/task/source-prepare/semantic-intake.js +6 -2
- package/dist/task/source-references.js +22 -1
- package/dist/worker/console/chat/pi-runtime.js +2 -3
- package/dist/worker/console/chat/routes.js +3 -12
- package/dist/worker/console/chat/semantic-activity.js +11 -12
- package/dist/worker/console/chat/session-stats.js +0 -96
- package/dist/worker/console/chat/turn-process.js +4 -28
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/index.js +3 -0
- package/dist/worker/console/operation-store.js +226 -6
- package/dist/worker/console/operator-actions.js +169 -7
- package/dist/worker/console/pi-plugins.js +1 -3
- package/dist/worker/console/server.js +3 -0
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-DwVJxUxz.js → abnfDiagram-N423BO3Z-Bho4we4c.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DqfCcXdu.js → arc-TBDkTeK0.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-qMVtggRv.js → architectureDiagram-T3A2C74G-BF_fZrn2.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-ECrV38dh.js → blockDiagram-VBNYF7ZC-CCnPgUe2.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DBaaLqQv.js → c4Diagram-5PPSVZJV-BCYZJLAT.js} +1 -1
- package/dist/worker/console/static/assets/channel-bnXW0U3C.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-BvfNHkAz.js → chunk-2GRJ4B5K-D8K6adhU.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-C5StiIbc.js → chunk-2Q5K7J3B-DkT1Q78b.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-HabGiJkS.js → chunk-5RXB4S5H-DuhvpIr7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BiTg127i.js → chunk-5VM5RSS4-BnK6dC49.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-CZsXBPuR.js → chunk-6Q2QTUOP-DPz__z7r.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D-r1MxLK.js → chunk-GF5L2VYU-0kkppM2j.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-DQxb9plr.js → chunk-JWPE2WC7-BB41iK5t.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DMndGvCT.js → chunk-KBJHAD2P-CiVrxKHU.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-ByL4BgOJ.js → chunk-RYQCIY6F-C2h9Xje1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BVe1b1gL.js → chunk-XXDRQBXY-3iOSHdbM.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-rZpq_twR.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-rZpq_twR.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BAyGzN8j.js → cose-bilkent-JH36ORCC-BN3A7tkk.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-C5yVYU7E.js → cynefin-VYW2F7L2-CTuBy0Or.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-x-MwsEm9.js → cynefinDiagram-MW4NZA55-wb6tUlMN.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CT5Tle5F.js → dagre-VZM6K2ZE-GP1Ivx8B.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BARERhlS.js → diagram-7IWD3JNH-ejrcK6L0.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BwfUz0VM.js → diagram-B4RE2ZJO-YFkCCvUU.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-Mk4r2WvJ.js → diagram-LBJQPF4R-B4rrvIaL.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-KUrxuBs0.js → diagram-Q27KOJAE-C-YIZcUf.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-CjZOceZn.js → diagram-UB23O5K3-BPF5wcL1.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-rnwUCiEI.js → ebnfDiagram-BXEA7PRR-CU5SzXjO.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DM4j23FM.js → erDiagram-JOGREHBK-CpL414DE.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DD5HdDEs.js → flowDiagram-UKHOOZJN-MrhLzNau.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CIAGSF47.js → ganttDiagram-PKOTCBZU-Bg4qFuUo.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DbwFbVEL.js → gitGraphDiagram-DS77QQ5N-ColxSXLR.js} +1 -1
- package/dist/worker/console/static/assets/index-D3CC4eUz.css +1 -0
- package/dist/worker/console/static/assets/index-DLt4ZFvD.js +437 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-B-Pb0T66.js → infoDiagram-6WML65LV-Czvtm6gc.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-B9Unk-YX.js → ishikawaDiagram-WSZJBQD7-B7Xp8HyJ.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-C2F3JkPt.js → journeyDiagram-NVQOT4AX-DWYZcvph.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Bl8-Xcoz.js → kanban-definition-27J2QSJJ-KTLZRUjx.js} +1 -1
- package/dist/worker/console/static/assets/{linear-yK4uZmC5.js → linear-_Exn7bfl.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-Bpa6PmuE.js → mermaid.core-Cxg8kX8V.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BfGRoCSe.js → mindmap-definition-FAOFIHXS-oKrlo2Bt.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Bz6_HtST.js → pegDiagram-VL7TDLO6-XAeteiNd.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-i0Umbsby.js → pieDiagram-7S7Q4E2Y-BTf1K-lp.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DaVQBobe.js → quadrantDiagram-CIZ2JOQS-C0IlY0Jf.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DMTGnSq7.js → railroadDiagram-AXF67PYL-BVbKtZhW.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-2ao_DaSq.js → requirementDiagram-LRYGKXZP-C5e4JFcr.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-2SG1lTB9.js → sankeyDiagram-W5VNT64P-Cz30hc29.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BQYOLybR.js → sequenceDiagram-SI44F4Z6-DS33n6d0.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-C8g3yjuI.js → sizeCapture-X5ZJPWSS-46w7v-9T.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-q_F8nAQG.js → stateDiagram-OKZ733FA-pbcD5SsZ.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-zBMYiUyi.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-8LrWpH2s.js → swimlanes-SLNWSIFB-DuivYBSj.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-d_rms-5i.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-BQoY3qzl.js → timeline-definition-Z64GVDOM-Cb9VZVIl.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BDu1UjSp.js → vennDiagram-T6HMQDX7-D2-mfOgR.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-C48M7Y0a.js → wardleyDiagram-T6FBY63Y-DeS2aC76.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-YU6FTl_F.js → xychartDiagram-ELKLHX3M-WGsrFjxH.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/app/useRunProgress.js +2 -9
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +0 -2
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +0 -6
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +3 -28
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +0 -106
- package/dist/worker/console/workspace-context.js +111 -1
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/observability/read-model.js +0 -4
- package/dist/worker/observe/static/dag-node-purpose.js +5 -0
- package/dist/worker/observe/static/views/dag-inspector.js +7 -2
- package/dist/worker/observe/static/views/session-timeline.js +15 -46
- package/dist/workflows/dag/backend-test-result-contract.js +1 -1
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/dag-retry-schema.js +138 -0
- package/dist/workflows/dag/frontend-closeout.js +221 -0
- package/dist/workflows/dag/frontend-design-policy.js +400 -0
- package/dist/workflows/dag/frontend-human-decision.js +182 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +932 -162
- package/dist/workflows/dag/frontend-plan-render.js +2 -1
- package/dist/workflows/dag/frontend-prewrite-gate.js +255 -349
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
- package/dist/workflows/dag/frontend-read-budgets.js +12 -0
- package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
- package/dist/workflows/dag/frontend-recovery-controller.js +202 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +17 -10
- package/dist/workflows/dag/frontend-recovery-run.js +33 -20
- package/dist/workflows/dag/frontend-repair.js +1 -432
- package/dist/workflows/dag/frontend-review-context.js +261 -15
- package/dist/workflows/dag/frontend-review-findings.js +270 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +914 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +409 -0
- package/dist/workflows/dag/frontend-shape.js +427 -0
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
- package/dist/workflows/dag/frontend-split-application-service.js +203 -0
- package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
- package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
- package/dist/workflows/dag/frontend-verification-trace.js +249 -24
- package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
- package/dist/workflows/dag/frontend-writer-admission.js +285 -0
- package/dist/workflows/dag/frontend-writer-status.js +256 -0
- package/dist/workflows/dag/init-hybrid.js +685 -494
- package/dist/workflows/dag/interrupt-request.js +7 -0
- package/dist/workflows/dag/node-execution.js +732 -4
- package/dist/workflows/dag/prompt.js +34 -1
- package/dist/workflows/dag/recovery-lease.js +80 -0
- package/dist/workflows/dag/report.js +37 -1
- package/dist/workflows/dag/rerun-feedback.js +256 -1
- package/dist/workflows/dag/rerun-plan.js +10 -2
- package/dist/workflows/dag/rerun-run.js +23 -0
- package/dist/workflows/dag/rerun-task.js +29 -0
- package/dist/workflows/dag/retry-policy.js +214 -104
- package/dist/workflows/dag/runner.js +429 -122
- package/dist/workflows/dag/scheduler.js +114 -16
- package/dist/workflows/dag/types.js +239 -12
- package/dist/workflows/dag/validate.js +28 -12
- package/docs/init-surface.manifest.json +30 -12
- package/docs/skills/vetted-skill-registry.md +4 -2
- package/docs/templates/README.md +2 -0
- package/docs/templates/agent-dag-report.schema.json +8 -2
- package/docs/templates/agent-dag.schema.json +1 -1
- package/docs/templates/frontend-implementation-contract.schema.json +4 -1
- package/docs/templates/frontend-implementation-dag.json +89 -0
- package/docs/templates/spec-registry.schema.json +45 -0
- package/harness.json +3 -3
- package/package.json +1 -1
- package/skills/frontend-bounded-implement/SKILL.md +15 -14
- package/skills/frontend-bounded-implement/references/code-standards.md +19 -0
- package/skills/frontend-contract/SKILL.md +23 -0
- package/skills/frontend-contract/references/contract-protocol.md +34 -0
- package/skills/frontend-design-review/SKILL.md +22 -41
- package/skills/frontend-plan/SKILL.md +26 -0
- package/skills/frontend-plan/references/decision-contract.md +37 -0
- package/skills/frontend-plan/references/design-decisions.md +17 -0
- package/skills/frontend-review/SKILL.md +20 -15
- package/skills/frontend-review/references/review-findings.md +6 -7
- package/skills/frontend-scout/SKILL.md +25 -0
- package/skills/frontend-scout/references/design-evidence.md +16 -0
- package/skills/frontend-scout/references/scout-evidence.md +23 -0
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +1 -0
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/worker/console/static/assets/channel-CL1NRAmS.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-PBGzzjZA.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-PBGzzjZA.js +0 -1
- package/dist/worker/console/static/assets/index-BfleX-qj.css +0 -1
- package/dist/worker/console/static/assets/index-Bih9w9h9.js +0 -451
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DrxGBDJu.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BhpDxzAf.js +0 -8
- package/dist/worker/console/static-src/operator-chat/dag-progress-link.js +0 -22
- package/skills/frontend-implementation/SKILL.md +0 -52
- package/skills/frontend-implementation/references/code-standards.md +0 -33
- package/skills/frontend-implementation/references/design-spec.md +0 -56
- package/skills/frontend-implementation/references/node-contracts.md +0 -31
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { assertFrontendSplitProjectionSuccessor, frontendSplitProjectionSchema, } from "../../workflows/dag/frontend-split-application-service.js";
|
|
4
|
+
import { buildFrontendHumanDecisionProjection, consumeFrontendHumanDecision, materializeFrontendHumanDecision, parseFrontendHumanDecisionProjection, parseFrontendHumanDecisionReceipt, } from "../../workflows/dag/frontend-human-decision.js";
|
|
5
|
+
import { buildFrontendSplitAggregateCloseout, computeFrontendSplitDigest, finalizeFrontendSplitAggregate, frontendCommittedFactRefSchema, frontendSplitChildCloseoutFactsSchema, initializeFrontendSplitParent, } from "../../workflows/dag/frontend-split-orchestrator.js";
|
|
6
|
+
import { frontendCloseoutFactsSchema, frontendTestHandoffSchema, } from "../../workflows/dag/frontend-closeout.js";
|
|
3
7
|
import { listJsonFiles, readJsonIfExists, sha256Json, writeSecureJson, } from "./app-data.js";
|
|
4
8
|
export const TERMINAL_OPERATION_STATES = new Set(["succeeded", "failed", "timed-out", "restarting"]);
|
|
5
9
|
function operationFile(appData, operationId) {
|
|
@@ -15,16 +19,21 @@ export function computeActionPayloadHash(action, actionParams) {
|
|
|
15
19
|
export function newOperationId() {
|
|
16
20
|
return `op_${randomBytes(12).toString("hex")}`;
|
|
17
21
|
}
|
|
22
|
+
const sharedWriteChains = new Map();
|
|
18
23
|
export class OperationStore {
|
|
19
24
|
appData;
|
|
20
|
-
/**
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
* (Windows EPERM) or last-write-wins lost patches.
|
|
24
|
-
*/
|
|
25
|
-
writeChains = new Map();
|
|
25
|
+
/** Shared per-store-root, per-key write lock. Multiple OperationStore instances
|
|
26
|
+
* in one worker process must observe the same CAS critical section. */
|
|
27
|
+
writeChains;
|
|
26
28
|
constructor(appData) {
|
|
27
29
|
this.appData = appData;
|
|
30
|
+
const root = path.resolve(appData.operations);
|
|
31
|
+
let chains = sharedWriteChains.get(root);
|
|
32
|
+
if (!chains) {
|
|
33
|
+
chains = new Map();
|
|
34
|
+
sharedWriteChains.set(root, chains);
|
|
35
|
+
}
|
|
36
|
+
this.writeChains = chains;
|
|
28
37
|
}
|
|
29
38
|
serialize(key, op) {
|
|
30
39
|
const prev = this.writeChains.get(key) ?? Promise.resolve();
|
|
@@ -66,6 +75,7 @@ export class OperationStore {
|
|
|
66
75
|
payloadHash,
|
|
67
76
|
state: "queued",
|
|
68
77
|
createdAt: now,
|
|
78
|
+
revision: 1,
|
|
69
79
|
taskId: input.taskId,
|
|
70
80
|
cliArgs: input.cliArgs,
|
|
71
81
|
...(input.externalCommand
|
|
@@ -77,11 +87,61 @@ export class OperationStore {
|
|
|
77
87
|
return { kind: "created", operation };
|
|
78
88
|
});
|
|
79
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Idempotently materialize a split child/integration operation with a
|
|
92
|
+
* caller-specified deterministic operationId (from the split manifest).
|
|
93
|
+
* Unlike create() (random op_<hex> id) and save/update (no create), this is
|
|
94
|
+
* the durable cross-restart idempotency boundary for the production
|
|
95
|
+
* FrontendSplitTaskMaterializer backend: an existing operation file with the
|
|
96
|
+
* same taskId + payloadHash is returned as {created:false}; any drift fails
|
|
97
|
+
* closed instead of overwriting or silently diverging.
|
|
98
|
+
*/
|
|
99
|
+
async ensureSplitMaterializedOperation(input) {
|
|
100
|
+
return this.serialize(input.operationId, async () => {
|
|
101
|
+
const existing = await this.get(input.operationId);
|
|
102
|
+
if (existing) {
|
|
103
|
+
if (existing.taskId !== input.taskId ||
|
|
104
|
+
existing.payloadHash !== input.payloadHash) {
|
|
105
|
+
throw new Error("frontend split operation materialization payload drift");
|
|
106
|
+
}
|
|
107
|
+
return { operation: existing, created: false };
|
|
108
|
+
}
|
|
109
|
+
const now = new Date().toISOString();
|
|
110
|
+
const operation = {
|
|
111
|
+
schemaVersion: 1,
|
|
112
|
+
operationId: input.operationId,
|
|
113
|
+
clientRequestId: `split:${input.operationId}`,
|
|
114
|
+
action: input.action,
|
|
115
|
+
actionParams: input.actionParams,
|
|
116
|
+
payloadHash: input.payloadHash,
|
|
117
|
+
state: "queued",
|
|
118
|
+
createdAt: now,
|
|
119
|
+
revision: 1,
|
|
120
|
+
taskId: input.taskId,
|
|
121
|
+
...(input.cliArgs ? { cliArgs: input.cliArgs } : {}),
|
|
122
|
+
};
|
|
123
|
+
await writeSecureJson(operationFile(this.appData, input.operationId), operation);
|
|
124
|
+
return { operation, created: true };
|
|
125
|
+
});
|
|
126
|
+
}
|
|
80
127
|
async get(operationId) {
|
|
81
128
|
return readJsonIfExists(operationFile(this.appData, operationId));
|
|
82
129
|
}
|
|
83
130
|
async save(operation) {
|
|
84
131
|
return this.serialize(operation.operationId, async () => {
|
|
132
|
+
const current = await this.get(operation.operationId);
|
|
133
|
+
if (current?.revision !== undefined && operation.revision !== current.revision)
|
|
134
|
+
throw new Error("operation revision may not be changed by save");
|
|
135
|
+
if (operation.frontendHumanDecision !== undefined || current?.frontendHumanDecision !== undefined) {
|
|
136
|
+
if (!current || JSON.stringify(operation) !== JSON.stringify(current))
|
|
137
|
+
throw new Error("human-decision projection may only be mutated by canonical CAS");
|
|
138
|
+
return current;
|
|
139
|
+
}
|
|
140
|
+
if (operation.frontendSplit !== undefined || current?.frontendSplit !== undefined) {
|
|
141
|
+
if (!current || JSON.stringify(operation) !== JSON.stringify(current))
|
|
142
|
+
throw new Error("split projection may only be mutated by compareAndSwapFrontendSplit");
|
|
143
|
+
return current;
|
|
144
|
+
}
|
|
85
145
|
await writeSecureJson(operationFile(this.appData, operation.operationId), operation);
|
|
86
146
|
return operation;
|
|
87
147
|
});
|
|
@@ -92,6 +152,10 @@ export class OperationStore {
|
|
|
92
152
|
if (!current) {
|
|
93
153
|
throw new Error(`operation not found: ${operationId}`);
|
|
94
154
|
}
|
|
155
|
+
if ("frontendHumanDecision" in patch || (current.frontendHumanDecision !== undefined && ("state" in patch || "frontendExecution" in patch)))
|
|
156
|
+
throw new Error("human-decision projection may only be mutated by canonical CAS");
|
|
157
|
+
if ("frontendSplit" in patch || "revision" in patch || (current.frontendSplit !== undefined && ("state" in patch || "frontendExecution" in patch)))
|
|
158
|
+
throw new Error("split projection may only be mutated by compareAndSwapFrontendSplit");
|
|
95
159
|
const next = {
|
|
96
160
|
...current,
|
|
97
161
|
...patch,
|
|
@@ -106,6 +170,162 @@ export class OperationStore {
|
|
|
106
170
|
return next;
|
|
107
171
|
});
|
|
108
172
|
}
|
|
173
|
+
async materializeFrontendHumanDecision(input) {
|
|
174
|
+
return this.serialize(input.operationId, async () => {
|
|
175
|
+
const current = await this.get(input.operationId);
|
|
176
|
+
if (!current || current.revision === undefined || current.revision !== input.expectedRecordRevision) {
|
|
177
|
+
const fallback = current?.frontendHumanDecision ?? buildFrontendHumanDecisionProjection({ status: "active", operationId: input.operationId, runId: input.envelope.runId, revision: Math.max(1, input.expectedRecordRevision) });
|
|
178
|
+
return { outcome: "rejected", reason: "record revision conflict or legacy record", projection: fallback };
|
|
179
|
+
}
|
|
180
|
+
const projection = current.frontendHumanDecision ?? buildFrontendHumanDecisionProjection({ status: "active", operationId: current.operationId, runId: input.envelope.runId, revision: current.revision });
|
|
181
|
+
const result = materializeFrontendHumanDecision({ projection, envelope: input.envelope, expectedRecordRevision: input.expectedRecordRevision, expectedProjectionDigest: input.expectedProjectionDigest });
|
|
182
|
+
if (result.outcome !== "applied")
|
|
183
|
+
return result;
|
|
184
|
+
const record = { ...current, revision: result.projection.revision, frontendHumanDecision: result.projection, state: "running" };
|
|
185
|
+
await writeSecureJson(operationFile(this.appData, current.operationId), record);
|
|
186
|
+
return result;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
async consumeFrontendHumanDecision(input) {
|
|
190
|
+
return this.serialize(input.operationId, async () => {
|
|
191
|
+
const current = await this.get(input.operationId);
|
|
192
|
+
if (!current || current.revision === undefined || !current.frontendHumanDecision) {
|
|
193
|
+
const fallback = buildFrontendHumanDecisionProjection({ status: "active", operationId: input.operationId, runId: input.receipt.runId, revision: Math.max(1, input.expectedRecordRevision) });
|
|
194
|
+
return { outcome: "rejected", reason: "record has no mutable human-decision projection", projection: fallback };
|
|
195
|
+
}
|
|
196
|
+
const projection = parseFrontendHumanDecisionProjection(current.frontendHumanDecision);
|
|
197
|
+
if (current.revision !== input.expectedRecordRevision)
|
|
198
|
+
return { outcome: "rejected", reason: "record revision conflict", projection };
|
|
199
|
+
const result = consumeFrontendHumanDecision({ projection, receipt: input.receipt, expectedRecordRevision: input.expectedRecordRevision, expectedProjectionDigest: input.expectedProjectionDigest, currentTargetDigest: input.currentTargetDigest, now: input.now });
|
|
200
|
+
if (result.outcome !== "applied")
|
|
201
|
+
return result;
|
|
202
|
+
const record = { ...current, revision: result.projection.revision, frontendHumanDecision: result.projection };
|
|
203
|
+
await writeSecureJson(operationFile(this.appData, current.operationId), record);
|
|
204
|
+
return result;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
async initializeFrontendHumanApprovedSplit(input) {
|
|
208
|
+
return this.serialize(input.operationId, async () => {
|
|
209
|
+
const current = await this.get(input.operationId);
|
|
210
|
+
const fallback = current?.frontendHumanDecision ?? buildFrontendHumanDecisionProjection({ status: "active", operationId: input.operationId, runId: input.receipt.runId, revision: Math.max(1, input.expectedRecordRevision) });
|
|
211
|
+
if (!current || current.revision === undefined || current.revision !== input.expectedRecordRevision || !current.frontendHumanDecision || !current.frontendExecution || current.frontendSplit)
|
|
212
|
+
return { outcome: "rejected", reason: "record is not an authoritative pending single-task split", projection: fallback };
|
|
213
|
+
const humanProjection = parseFrontendHumanDecisionProjection(current.frontendHumanDecision);
|
|
214
|
+
if (humanProjection.status !== "blocked-human" || humanProjection.envelope.kind !== "split-materialization-approval" || humanProjection.digest !== input.expectedProjectionDigest)
|
|
215
|
+
return { outcome: "rejected", reason: "pending split envelope conflict", projection: humanProjection };
|
|
216
|
+
const receipt = parseFrontendHumanDecisionReceipt(input.receipt);
|
|
217
|
+
if (receipt.kind !== "split-materialization-approval")
|
|
218
|
+
return { outcome: "rejected", reason: "receipt is not a split approval", projection: humanProjection };
|
|
219
|
+
if (JSON.stringify(current.frontendExecution) !== JSON.stringify(input.execution) || input.execution.kind !== "single-task")
|
|
220
|
+
return { outcome: "rejected", reason: "single-task execution conflict", projection: humanProjection };
|
|
221
|
+
const consumed = consumeFrontendHumanDecision({ projection: humanProjection, receipt, expectedRecordRevision: input.expectedRecordRevision, expectedProjectionDigest: input.expectedProjectionDigest, currentTargetDigest: input.currentTargetDigest, now: input.now });
|
|
222
|
+
if (consumed.outcome !== "applied")
|
|
223
|
+
return consumed;
|
|
224
|
+
const initialized = initializeFrontendSplitParent({
|
|
225
|
+
execution: input.execution,
|
|
226
|
+
manifest: input.manifest,
|
|
227
|
+
expectedOperationRevision: input.execution.operationRevision,
|
|
228
|
+
successorOperationRevision: consumed.projection.revision,
|
|
229
|
+
envelopeTargetDigest: humanProjection.envelope.targetDigest,
|
|
230
|
+
activeWriterOperationId: input.activeWriterOperationId,
|
|
231
|
+
activeProviderOperationId: input.activeProviderOperationId,
|
|
232
|
+
authorization: { mode: "human", envelope: humanProjection.envelope, receipt },
|
|
233
|
+
});
|
|
234
|
+
if (initialized.outcome !== "applied")
|
|
235
|
+
return { outcome: "rejected", reason: initialized.reason, projection: humanProjection };
|
|
236
|
+
const splitProjection = frontendSplitProjectionSchema.parse({ revision: consumed.projection.revision, execution: initialized.execution, manifest: input.manifest, parent: initialized.parent });
|
|
237
|
+
const { frontendExecution: _singleTask, ...withoutSingleTask } = current;
|
|
238
|
+
const record = { ...withoutSingleTask, revision: consumed.projection.revision, frontendHumanDecision: consumed.projection, frontendSplit: splitProjection, state: "running" };
|
|
239
|
+
await writeSecureJson(operationFile(this.appData, current.operationId), record);
|
|
240
|
+
return { outcome: "applied", projection: splitProjection, humanDecision: consumed.projection };
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
async compareAndSwapFrontendSplit(input) {
|
|
244
|
+
return this.serialize(input.operationId, async () => {
|
|
245
|
+
const current = await this.get(input.operationId);
|
|
246
|
+
if (!current || current.revision === undefined || current.revision !== input.expectedRevision)
|
|
247
|
+
return { outcome: "conflict" };
|
|
248
|
+
const currentProjection = current.frontendSplit;
|
|
249
|
+
if (currentProjection) {
|
|
250
|
+
if (currentProjection.execution.digest !== input.expectedExecutionDigest || currentProjection.parent.digest !== input.expectedParentDigest)
|
|
251
|
+
return { outcome: "conflict" };
|
|
252
|
+
}
|
|
253
|
+
else if (input.expectedExecutionDigest || input.expectedParentDigest)
|
|
254
|
+
return { outcome: "conflict" };
|
|
255
|
+
const next = currentProjection
|
|
256
|
+
? assertFrontendSplitProjectionSuccessor(currentProjection, input.next)
|
|
257
|
+
: frontendSplitProjectionSchema.parse(input.next);
|
|
258
|
+
if (next.revision !== current.revision + 1 || next.parent.operationId !== input.operationId)
|
|
259
|
+
throw new Error("split CAS next revision or operation binding mismatch");
|
|
260
|
+
const state = next.parent.operationState === "success" ? "succeeded" : next.parent.operationTerminal !== undefined ? "failed" : current.state;
|
|
261
|
+
const record = { ...current, revision: next.revision, frontendSplit: next, state };
|
|
262
|
+
await writeSecureJson(operationFile(this.appData, input.operationId), record);
|
|
263
|
+
return { outcome: "applied", projection: next };
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
async loadChildCloseoutFacts(input) {
|
|
267
|
+
const factsDir = input.closeoutRef.ref;
|
|
268
|
+
const facts = await readJsonIfExists(path.join(factsDir, "frontend-closeout-facts.json"));
|
|
269
|
+
if (facts === undefined)
|
|
270
|
+
return frontendSplitChildCloseoutFactsSchema.parse({ childId: input.childId, residualRisks: [], frontendTestHandoffRef: undefined });
|
|
271
|
+
const parsedFacts = frontendCloseoutFactsSchema.parse(facts);
|
|
272
|
+
if (computeFrontendSplitDigest(parsedFacts) !== input.closeoutRef.digest)
|
|
273
|
+
throw new Error(`child closeout facts digest mismatch: ${input.childId}`);
|
|
274
|
+
const handoff = await readJsonIfExists(path.join(factsDir, "frontend-test-handoff.json"));
|
|
275
|
+
const frontendTestHandoffRef = handoff === undefined ? undefined : computeFrontendSplitDigest(frontendTestHandoffSchema.parse(handoff));
|
|
276
|
+
return frontendSplitChildCloseoutFactsSchema.parse({ childId: input.childId, residualRisks: parsedFacts.residualRisks ?? [], frontendTestHandoffRef });
|
|
277
|
+
}
|
|
278
|
+
async settleFrontendSplitAggregate(input) {
|
|
279
|
+
return this.serialize(input.operationId, async () => {
|
|
280
|
+
const record = await this.get(input.operationId);
|
|
281
|
+
if (!record || record.revision === undefined || record.revision !== input.expectedRevision || !record.frontendSplit)
|
|
282
|
+
return { outcome: "conflict" };
|
|
283
|
+
const authoritative = frontendSplitProjectionSchema.parse(record.frontendSplit);
|
|
284
|
+
if (authoritative.parent.operationId !== input.operationId
|
|
285
|
+
|| authoritative.revision !== input.expectedRevision
|
|
286
|
+
|| authoritative.execution.digest !== input.expectedExecutionDigest
|
|
287
|
+
|| authoritative.parent.digest !== input.expectedParentDigest
|
|
288
|
+
|| authoritative.manifest.digest !== input.expectedManifestDigest
|
|
289
|
+
|| authoritative.candidateBinding?.digest !== input.expectedCandidateBindingDigest
|
|
290
|
+
|| authoritative.integrationBinding?.digest !== input.expectedIntegrationBindingDigest)
|
|
291
|
+
return { outcome: "conflict" };
|
|
292
|
+
if (!authoritative.candidateBinding)
|
|
293
|
+
return { outcome: "conflict" };
|
|
294
|
+
const expectedAggregate = buildFrontendSplitAggregateCloseout(authoritative.parent, authoritative.manifest, authoritative.candidateBinding, authoritative.integrationBinding, input.childCloseoutFacts);
|
|
295
|
+
if (JSON.stringify(expectedAggregate) !== JSON.stringify(input.aggregate))
|
|
296
|
+
return { outcome: "conflict" };
|
|
297
|
+
const transaction = await input.prepareAggregate({
|
|
298
|
+
operationId: authoritative.parent.operationId,
|
|
299
|
+
aggregate: expectedAggregate,
|
|
300
|
+
});
|
|
301
|
+
try {
|
|
302
|
+
const committedRef = frontendCommittedFactRefSchema.parse(transaction.committedRef);
|
|
303
|
+
const finalized = finalizeFrontendSplitAggregate(authoritative.parent, expectedAggregate, committedRef);
|
|
304
|
+
const next = assertFrontendSplitProjectionSuccessor(authoritative, { ...authoritative, revision: authoritative.revision + 1, parent: finalized.parent, execution: finalized.execution, aggregateCloseout: { value: expectedAggregate, committedRef } });
|
|
305
|
+
const nextRecord = { ...record, revision: next.revision, frontendSplit: next, state: "succeeded" };
|
|
306
|
+
await transaction.commit({
|
|
307
|
+
parentRecordPath: operationFile(this.appData, record.operationId),
|
|
308
|
+
parentRecord: nextRecord,
|
|
309
|
+
});
|
|
310
|
+
return { outcome: "applied", projection: next };
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
await transaction.rollback();
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
async initializeFrontendSplit(expectedRevision, projectionValue) {
|
|
319
|
+
const projection = frontendSplitProjectionSchema.parse(projectionValue);
|
|
320
|
+
return this.serialize(projection.parent.operationId, async () => {
|
|
321
|
+
const current = await this.get(projection.parent.operationId);
|
|
322
|
+
if (!current || current.revision === undefined || current.revision !== expectedRevision || current.frontendSplit !== undefined || projection.revision !== expectedRevision + 1)
|
|
323
|
+
return { outcome: "conflict" };
|
|
324
|
+
const record = { ...current, revision: projection.revision, frontendSplit: projection };
|
|
325
|
+
await writeSecureJson(operationFile(this.appData, current.operationId), record);
|
|
326
|
+
return { outcome: "applied", projection };
|
|
327
|
+
});
|
|
328
|
+
}
|
|
109
329
|
async list() {
|
|
110
330
|
const files = await listJsonFiles(this.appData.operations);
|
|
111
331
|
const ops = [];
|
|
@@ -64,6 +64,10 @@ function paramsOf(req) {
|
|
|
64
64
|
function str(v) {
|
|
65
65
|
return typeof v === "string" && v.trim() ? v.trim() : undefined;
|
|
66
66
|
}
|
|
67
|
+
function appendExplicitTaskKindArg(args, taskKind, taskKindExplicit) {
|
|
68
|
+
if (taskKindExplicit)
|
|
69
|
+
args.push("--task-kind", taskKind);
|
|
70
|
+
}
|
|
67
71
|
const TASK_ID_PLACEHOLDER_RE = /[<>]|^(?:id|task-?id|your-task-id|todo|tbd)$/i;
|
|
68
72
|
/** Mirror of runtime GENERIC_TASK_SLUGS (not exported there); keep in sync. */
|
|
69
73
|
const GENERIC_SLUG_SET = new Set(["test", "fix", "tmp", "new-task"]);
|
|
@@ -536,6 +540,84 @@ async function acceptOperation(ctx, input) {
|
|
|
536
540
|
},
|
|
537
541
|
};
|
|
538
542
|
}
|
|
543
|
+
function splitRuntimeMissing(action) {
|
|
544
|
+
return {
|
|
545
|
+
kind: "error",
|
|
546
|
+
status: 503,
|
|
547
|
+
body: operatorFailed({
|
|
548
|
+
command: action,
|
|
549
|
+
outcome: "blocked",
|
|
550
|
+
code: "INTERNAL_ERROR",
|
|
551
|
+
message: `${action} is unavailable: frontend split runtime is not wired`,
|
|
552
|
+
}),
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
function numOf(v) {
|
|
556
|
+
return typeof v === "number" ? v : Number(v);
|
|
557
|
+
}
|
|
558
|
+
function splitCommandBase(p) {
|
|
559
|
+
return {
|
|
560
|
+
operationId: str(p.operationId) ?? "",
|
|
561
|
+
expectedRevision: numOf(p.expectedRevision),
|
|
562
|
+
expectedParentDigest: str(p.expectedParentDigest) ?? "",
|
|
563
|
+
expectedManifestDigest: str(p.expectedManifestDigest) ?? "",
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
function splitCommandDispatch(action, result) {
|
|
567
|
+
if (result.outcome === "conflict") {
|
|
568
|
+
return {
|
|
569
|
+
kind: "error",
|
|
570
|
+
status: 409,
|
|
571
|
+
body: operatorFailed({
|
|
572
|
+
command: action,
|
|
573
|
+
outcome: "conflict",
|
|
574
|
+
code: "CONFLICT",
|
|
575
|
+
message: "frontend split CAS conflict (stale revision or binding)",
|
|
576
|
+
}),
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
return { kind: "sync", status: 200, body: operatorSucceeded(action, result) };
|
|
580
|
+
}
|
|
581
|
+
async function runSplitAction(action, run) {
|
|
582
|
+
try {
|
|
583
|
+
const result = await run();
|
|
584
|
+
if (result && typeof result === "object" && "outcome" in result) {
|
|
585
|
+
return splitCommandDispatch(action, result);
|
|
586
|
+
}
|
|
587
|
+
return {
|
|
588
|
+
kind: "sync",
|
|
589
|
+
status: 200,
|
|
590
|
+
body: operatorSucceeded(action, result),
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
catch (error) {
|
|
594
|
+
return invalid(action, error instanceof Error ? error.message : String(error));
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function humanDecisionDispatch(action, result) {
|
|
598
|
+
if (result.outcome === "rejected") {
|
|
599
|
+
return {
|
|
600
|
+
kind: "error",
|
|
601
|
+
status: 409,
|
|
602
|
+
body: operatorFailed({
|
|
603
|
+
command: action,
|
|
604
|
+
outcome: "conflict",
|
|
605
|
+
code: "CONFLICT",
|
|
606
|
+
message: result.reason,
|
|
607
|
+
details: { projection: result.projection },
|
|
608
|
+
}),
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
return { kind: "sync", status: 200, body: operatorSucceeded(action, result) };
|
|
612
|
+
}
|
|
613
|
+
async function runHumanDecisionAction(action, run) {
|
|
614
|
+
try {
|
|
615
|
+
return humanDecisionDispatch(action, await run());
|
|
616
|
+
}
|
|
617
|
+
catch (error) {
|
|
618
|
+
return invalid(action, error instanceof Error ? error.message : String(error));
|
|
619
|
+
}
|
|
620
|
+
}
|
|
539
621
|
async function acceptConsoleUpdateAndInit(ctx, clientRequestId, channel) {
|
|
540
622
|
if (!ctx.consoleUpdateExecutor) {
|
|
541
623
|
return {
|
|
@@ -1877,7 +1959,7 @@ export async function dispatchOperatorAction(ctx, req) {
|
|
|
1877
1959
|
const staged = await stageUtf8Text(ctx.appData, content, {
|
|
1878
1960
|
extension: ".md",
|
|
1879
1961
|
});
|
|
1880
|
-
const
|
|
1962
|
+
const importArgs = [
|
|
1881
1963
|
"task",
|
|
1882
1964
|
"advance",
|
|
1883
1965
|
taskId,
|
|
@@ -1886,8 +1968,10 @@ export async function dispatchOperatorAction(ctx, req) {
|
|
|
1886
1968
|
staged.path,
|
|
1887
1969
|
"--role",
|
|
1888
1970
|
"requirement",
|
|
1889
|
-
|
|
1890
|
-
|
|
1971
|
+
];
|
|
1972
|
+
appendExplicitTaskKindArg(importArgs, taskKind, taskKindExplicit);
|
|
1973
|
+
importArgs.push("--json");
|
|
1974
|
+
const imported = await run(importArgs, {
|
|
1891
1975
|
cwd: ctx.repoRoot,
|
|
1892
1976
|
artifactName: "import-prd-task-advance",
|
|
1893
1977
|
expectJson: true,
|
|
@@ -2862,6 +2946,75 @@ export async function dispatchOperatorAction(ctx, req) {
|
|
|
2862
2946
|
}),
|
|
2863
2947
|
};
|
|
2864
2948
|
}
|
|
2949
|
+
case "frontendSplitWakeup": {
|
|
2950
|
+
if (!ctx.splitOrchestrator)
|
|
2951
|
+
return splitRuntimeMissing(action);
|
|
2952
|
+
return runSplitAction(action, () => ctx.splitOrchestrator.wakeupChildren(splitCommandBase(p)));
|
|
2953
|
+
}
|
|
2954
|
+
case "frontendSplitChildCallback": {
|
|
2955
|
+
if (!ctx.splitOrchestrator)
|
|
2956
|
+
return splitRuntimeMissing(action);
|
|
2957
|
+
return runSplitAction(action, () => ctx.splitOrchestrator.childCallback({
|
|
2958
|
+
...splitCommandBase(p),
|
|
2959
|
+
observation: p.observation,
|
|
2960
|
+
}));
|
|
2961
|
+
}
|
|
2962
|
+
case "frontendSplitWakeupIntegration": {
|
|
2963
|
+
if (!ctx.splitOrchestrator)
|
|
2964
|
+
return splitRuntimeMissing(action);
|
|
2965
|
+
return runSplitAction(action, () => ctx.splitOrchestrator.wakeupIntegration(splitCommandBase(p)));
|
|
2966
|
+
}
|
|
2967
|
+
case "frontendSplitIntegrationCallback": {
|
|
2968
|
+
if (!ctx.splitOrchestrator)
|
|
2969
|
+
return splitRuntimeMissing(action);
|
|
2970
|
+
return runSplitAction(action, () => ctx.splitOrchestrator.integrationCallback({
|
|
2971
|
+
...splitCommandBase(p),
|
|
2972
|
+
completion: p.completion,
|
|
2973
|
+
}));
|
|
2974
|
+
}
|
|
2975
|
+
case "frontendSplitFinalize": {
|
|
2976
|
+
if (!ctx.splitOrchestrator)
|
|
2977
|
+
return splitRuntimeMissing(action);
|
|
2978
|
+
return runSplitAction(action, () => ctx.splitOrchestrator.finalize(splitCommandBase(p)));
|
|
2979
|
+
}
|
|
2980
|
+
case "frontendHumanDecisionMaterialize": {
|
|
2981
|
+
if (!ctx.humanDecisionAdapter)
|
|
2982
|
+
return splitRuntimeMissing(action);
|
|
2983
|
+
return runHumanDecisionAction(action, () => ctx.humanDecisionAdapter.materialize({
|
|
2984
|
+
operationId: str(p.operationId) ?? "",
|
|
2985
|
+
expectedRecordRevision: numOf(p.expectedRecordRevision),
|
|
2986
|
+
expectedProjectionDigest: str(p.expectedProjectionDigest) ?? "",
|
|
2987
|
+
envelope: p.envelope,
|
|
2988
|
+
}));
|
|
2989
|
+
}
|
|
2990
|
+
case "frontendHumanDecisionApprove": {
|
|
2991
|
+
if (!ctx.humanDecisionAdapter)
|
|
2992
|
+
return splitRuntimeMissing(action);
|
|
2993
|
+
return runHumanDecisionAction(action, () => ctx.humanDecisionAdapter.consume({
|
|
2994
|
+
operationId: str(p.operationId) ?? "",
|
|
2995
|
+
expectedRecordRevision: numOf(p.expectedRecordRevision),
|
|
2996
|
+
expectedProjectionDigest: str(p.expectedProjectionDigest) ?? "",
|
|
2997
|
+
receipt: p.receipt,
|
|
2998
|
+
currentTargetDigest: str(p.currentTargetDigest) ?? "",
|
|
2999
|
+
now: str(p.now) ?? new Date().toISOString(),
|
|
3000
|
+
}));
|
|
3001
|
+
}
|
|
3002
|
+
case "frontendSplitInitialize": {
|
|
3003
|
+
if (!ctx.humanDecisionAdapter)
|
|
3004
|
+
return splitRuntimeMissing(action);
|
|
3005
|
+
return runHumanDecisionAction(action, () => ctx.humanDecisionAdapter.initializeSplit({
|
|
3006
|
+
operationId: str(p.operationId) ?? "",
|
|
3007
|
+
expectedRecordRevision: numOf(p.expectedRecordRevision),
|
|
3008
|
+
expectedProjectionDigest: str(p.expectedProjectionDigest) ?? "",
|
|
3009
|
+
execution: p.execution,
|
|
3010
|
+
manifest: p.manifest,
|
|
3011
|
+
receipt: p.receipt,
|
|
3012
|
+
currentTargetDigest: str(p.currentTargetDigest) ?? "",
|
|
3013
|
+
now: str(p.now) ?? new Date().toISOString(),
|
|
3014
|
+
activeWriterOperationId: str(p.activeWriterOperationId) ?? null,
|
|
3015
|
+
activeProviderOperationId: str(p.activeProviderOperationId) ?? null,
|
|
3016
|
+
}));
|
|
3017
|
+
}
|
|
2865
3018
|
default: {
|
|
2866
3019
|
// M5 official-command coverage uses one generic tail for newly registered
|
|
2867
3020
|
// CLI actions. It remains inside dispatchOperatorAction (the sole dispatch
|
|
@@ -3246,7 +3399,14 @@ async function dispatchBootstrapFromPrd(ctx, p) {
|
|
|
3246
3399
|
const title = identity.title;
|
|
3247
3400
|
const run = ctx.runCommand ?? ((a, o) => ctx.client.run(a, o));
|
|
3248
3401
|
// Standard lifecycle entry: task advance create + PRD import(s).
|
|
3249
|
-
|
|
3402
|
+
// Preserve an explicitly selected business workflow from the very first
|
|
3403
|
+
// task.json write. The task lifecycle otherwise defaults new tasks to
|
|
3404
|
+
// standard, which caused frontend tasks to be promoted only during the
|
|
3405
|
+
// final intake pass.
|
|
3406
|
+
const createArgs = ["task", "advance", taskId, title];
|
|
3407
|
+
appendExplicitTaskKindArg(createArgs, taskKind, taskKindExplicit);
|
|
3408
|
+
createArgs.push("--json");
|
|
3409
|
+
const advanceCreate = await run(createArgs, {
|
|
3250
3410
|
cwd: ctx.repoRoot,
|
|
3251
3411
|
artifactName: "bootstrap-task-advance-create",
|
|
3252
3412
|
expectJson: true,
|
|
@@ -3280,7 +3440,7 @@ async function dispatchBootstrapFromPrd(ctx, p) {
|
|
|
3280
3440
|
const staged = await stageUtf8Text(ctx.appData, text, {
|
|
3281
3441
|
extension: ".md",
|
|
3282
3442
|
});
|
|
3283
|
-
const
|
|
3443
|
+
const importArgs = [
|
|
3284
3444
|
"task",
|
|
3285
3445
|
"advance",
|
|
3286
3446
|
taskId,
|
|
@@ -3288,8 +3448,10 @@ async function dispatchBootstrapFromPrd(ctx, p) {
|
|
|
3288
3448
|
staged.path,
|
|
3289
3449
|
"--role",
|
|
3290
3450
|
role,
|
|
3291
|
-
|
|
3292
|
-
|
|
3451
|
+
];
|
|
3452
|
+
appendExplicitTaskKindArg(importArgs, taskKind, taskKindExplicit);
|
|
3453
|
+
importArgs.push("--json");
|
|
3454
|
+
const imported = await run(importArgs, {
|
|
3293
3455
|
cwd: ctx.repoRoot,
|
|
3294
3456
|
artifactName: `bootstrap-task-advance-prd-${label}`,
|
|
3295
3457
|
expectJson: true,
|
|
@@ -98,9 +98,7 @@ function getNpmRegistry() {
|
|
|
98
98
|
/** 从 npm 配置的仓库拉取某包的全部已发布版本 + latest。 */
|
|
99
99
|
export async function listPiPluginVersions(name) {
|
|
100
100
|
const registry = (await getNpmRegistry()).replace(/\/+$/, "");
|
|
101
|
-
|
|
102
|
-
// packument 的形式一致,npmjs.org 与内网 Nexus(要求 %2F 不还原)都接受。
|
|
103
|
-
const url = `${registry}/${encodeURIComponent(name)}`;
|
|
101
|
+
const url = `${registry}/${encodeURIComponent(name).replace(/%2F/g, "/")}`;
|
|
104
102
|
try {
|
|
105
103
|
const res = await fetch(url, {
|
|
106
104
|
headers: { accept: "application/json", "user-agent": "loop-agent/1.0" },
|
|
@@ -778,6 +778,9 @@ export async function createConsoleServer(options) {
|
|
|
778
778
|
appData: startup.appData,
|
|
779
779
|
operations: startup.operations,
|
|
780
780
|
events: startup.events,
|
|
781
|
+
splitOrchestrator: startup.splitOrchestrator,
|
|
782
|
+
humanDecisionAdapter: startup.humanDecisionAdapter,
|
|
783
|
+
materializer: startup.materializer,
|
|
781
784
|
getReadiness,
|
|
782
785
|
operatorSessionId,
|
|
783
786
|
registry,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-DPz__z7r.js";import{p as f}from"./chunk-JWPE2WC7-BB41iK5t.js";import{_ as n,l as o}from"./mermaid.core-Cxg8kX8V.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-CTuBy0Or.js";import"./index-DLt4ZFvD.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-
|
|
1
|
+
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-Cxg8kX8V.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|