opencode-swarm 7.142.0 → 7.143.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/.opencode/skills/execute/SKILL.md +1 -1
- package/.opencode/skills/phase-wrap/SKILL.md +2 -2
- package/dist/cli/{config-doctor-7xqerhnw.js → config-doctor-t1r5qwxk.js} +2 -2
- package/dist/cli/{core-nwmpg9t8.js → core-e30p4vz9.js} +1 -1
- package/dist/cli/{curation-policy-bzk13bj2.js → curation-policy-pkb2eeb1.js} +6 -6
- package/dist/cli/{curator-3wsaz7ay.js → curator-ctasdmmq.js} +29 -29
- package/dist/cli/curator-drift-49p3jnte.js +1 -1
- package/dist/cli/{curator-llm-factory-kbkddqk2.js → curator-llm-factory-y9xg7k6k.js} +29 -29
- package/dist/cli/{evidence-summary-service-wwfvpvxq.js → evidence-summary-service-7bmg5r8y.js} +13 -11
- package/dist/cli/{gate-evidence-3mazsjrq.js → gate-evidence-1v0cj4c6.js} +9 -5
- package/dist/cli/{guardrail-explain-rmz1yjzw.js → guardrail-explain-6x9psbwz.js} +30 -30
- package/dist/cli/{guardrail-log-8m4dnkgd.js → guardrail-log-pma4m8vr.js} +4 -4
- package/dist/cli/{hive-promoter-ss8dm52r.js → hive-promoter-g16grej1.js} +29 -29
- package/dist/cli/{index-kkxt9g8d.js → index-15h4dpsn.js} +3 -3
- package/dist/cli/{index-ae0j9qpc.js → index-1g473508.js} +31 -31
- package/dist/cli/{index-67vxmh1v.js → index-28jr28fg.js} +9 -9
- package/dist/cli/{index-vwyamc2d.js → index-2n8y67b4.js} +2 -2
- package/dist/cli/{index-ad93kz8m.js → index-336ra397.js} +1 -1
- package/dist/cli/{index-8g5aa1s7.js → index-6texwk9p.js} +2 -2
- package/dist/cli/{index-520ev2d7.js → index-7c4bxgdh.js} +1 -1
- package/dist/cli/{index-99eftm6b.js → index-82m5kwtd.js} +1 -1
- package/dist/cli/{index-mbeqw4cb.js → index-96h1v2jw.js} +1 -1
- package/dist/cli/{index-eytxcbm8.js → index-9v53ez3a.js} +6 -6
- package/dist/cli/{index-em1kfnss.js → index-erfnw81g.js} +3 -3
- package/dist/cli/index-fhxh447f.js +1372 -0
- package/dist/cli/{index-z7eeq0tt.js → index-fprxtq3q.js} +137 -36
- package/dist/cli/{index-e4y7mfp0.js → index-g0pbvfhw.js} +1 -1
- package/dist/cli/{index-hncahn40.js → index-htmsm37t.js} +5 -5
- package/dist/cli/{index-bd2h7r2w.js → index-k31en0ja.js} +3 -3
- package/dist/cli/{index-0dvx8f03.js → index-k92cagmt.js} +2 -2
- package/dist/cli/{index-26ynh10w.js → index-keskta0w.js} +9 -9
- package/dist/cli/{index-tzbd5050.js → index-kxpz3ncd.js} +4 -4
- package/dist/cli/{index-56knmbwf.js → index-m4v9kab1.js} +2615 -2048
- package/dist/cli/{index-2p3h7det.js → index-n96ay8w4.js} +1 -1
- package/dist/cli/{index-v8f3wa0e.js → index-q8st0mtw.js} +1 -1
- package/dist/cli/{index-sh3r66tt.js → index-r0tn9kpa.js} +1 -1
- package/dist/cli/{index-338zfvff.js → index-s2rcz51a.js} +1 -1
- package/dist/cli/{index-hgydkvty.js → index-s89y08tk.js} +2 -2
- package/dist/cli/{index-amepv9dh.js → index-sb16w4wj.js} +212 -10
- package/dist/cli/{index-ycd63sz3.js → index-tq5s07ta.js} +1 -1
- package/dist/cli/{index-gcn7tsr3.js → index-w7x7qxz2.js} +1 -1
- package/dist/cli/{index-7c5xtcfm.js → index-x6qq39e9.js} +3 -3
- package/dist/cli/{index-78p6nvcv.js → index-y9h2vqf6.js} +1 -1
- package/dist/cli/index.js +29 -29
- package/dist/cli/{knowledge-escalator-w235g6ce.js → knowledge-escalator-1b2hmcgn.js} +12 -12
- package/dist/cli/{knowledge-events-amn94djz.js → knowledge-events-vx4fqjzb.js} +10 -10
- package/dist/cli/{knowledge-link-xned4jbm.js → knowledge-link-fn5j3r4q.js} +5 -5
- package/dist/cli/{knowledge-store-3vb855ta.js → knowledge-store-t2qyr4yr.js} +6 -6
- package/dist/cli/{knowledge-validator-a8c185hw.js → knowledge-validator-c7871bjc.js} +8 -8
- package/dist/cli/{pending-delegations-8ccjby4b.js → pending-delegations-0hb97ps1.js} +4 -4
- package/dist/cli/{pr-subscriptions-gn7r3bg7.js → pr-subscriptions-b63t252j.js} +4 -4
- package/dist/cli/{runner-rhgkhy5e.js → runner-hrg7h2h4.js} +6 -6
- package/dist/cli/{scan-cursor-vjbjm140.js → scan-cursor-pnbpfjjc.js} +7 -7
- package/dist/cli/{schema-dd8mzm6k.js → schema-47xpcghn.js} +1 -1
- package/dist/cli/{scope-persistence-8j8d4874.js → scope-persistence-wxx1jpfw.js} +6 -6
- package/dist/cli/{skill-generator-a4apb7c2.js → skill-generator-s456hfta.js} +14 -14
- package/dist/cli/{telemetry-k9nxy24s.js → telemetry-xyfb5vm8.js} +1 -1
- package/dist/cli/{worktree-collision-ownership-183jk2dv.js → worktree-collision-ownership-ymdxetnn.js} +4 -4
- package/dist/cli/{worktree-isolation-j71zz7g9.js → worktree-isolation-d0xgp705.js} +29 -29
- package/dist/commands/close.d.ts +20 -2
- package/dist/gate-evidence.d.ts +24 -1
- package/dist/hooks/delegation-gate.d.ts +65 -0
- package/dist/index.js +462 -458
- package/dist/plan/ledger.d.ts +31 -1
- package/dist/plan/manager.d.ts +1 -0
- package/dist/state.d.ts +1 -1
- package/dist/workflow/close-terminal.d.ts +22 -0
- package/dist/workflow/coder-settlement.d.ts +0 -23
- package/dist/workflow/task-repair.d.ts +1 -0
- package/dist/workflow/task-terminal.d.ts +18 -23
- package/dist/workflow/workflow-wal-file.d.ts +20 -0
- package/dist/workflow/workflow-wal-schema.d.ts +67 -0
- package/dist/worktree/merge.d.ts +17 -0
- package/package.json +1 -1
- package/dist/cli/index-h2vpk15y.js +0 -623
|
@@ -1,623 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import {
|
|
3
|
-
validateSwarmPath
|
|
4
|
-
} from "./index-k46me75h.js";
|
|
5
|
-
import {
|
|
6
|
-
atomicWriteFile,
|
|
7
|
-
taskEvidencePath,
|
|
8
|
-
withTaskEvidenceLock
|
|
9
|
-
} from "./index-em1kfnss.js";
|
|
10
|
-
import {
|
|
11
|
-
telemetry
|
|
12
|
-
} from "./index-338zfvff.js";
|
|
13
|
-
import {
|
|
14
|
-
exports_external,
|
|
15
|
-
init_zod
|
|
16
|
-
} from "./index-bpmtbmy9.js";
|
|
17
|
-
|
|
18
|
-
// src/gate-evidence.ts
|
|
19
|
-
init_zod();
|
|
20
|
-
import { mkdirSync, readFileSync, realpathSync } from "fs";
|
|
21
|
-
import * as path from "path";
|
|
22
|
-
|
|
23
|
-
// src/validation/task-id.ts
|
|
24
|
-
var STRICT_TASK_ID_PATTERN = /^\d+\.\d+(\.\d+)*$/;
|
|
25
|
-
var RETRO_TASK_ID_REGEX = /^retro-\d+$/;
|
|
26
|
-
var INTERNAL_TOOL_ID_REGEX = /^(?:sast_scan|quality_budget|syntax_check|placeholder_scan|sbom_generate|build|secretscan)$/;
|
|
27
|
-
var GENERAL_TASK_ID_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
28
|
-
function checkUnsafeChars(taskId) {
|
|
29
|
-
if (!taskId || taskId.length === 0) {
|
|
30
|
-
return "Invalid task ID: empty string";
|
|
31
|
-
}
|
|
32
|
-
if (/\0/.test(taskId)) {
|
|
33
|
-
return "Invalid task ID: contains null bytes";
|
|
34
|
-
}
|
|
35
|
-
for (let i = 0;i < taskId.length; i++) {
|
|
36
|
-
if (taskId.charCodeAt(i) < 32) {
|
|
37
|
-
return "Invalid task ID: contains control characters";
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
if (taskId.includes("..") || taskId.includes("/") || taskId.includes("\\")) {
|
|
41
|
-
return "Invalid task ID: path traversal detected";
|
|
42
|
-
}
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
function isStrictTaskId(taskId) {
|
|
46
|
-
if (!taskId)
|
|
47
|
-
return false;
|
|
48
|
-
const unsafeMsg = checkUnsafeChars(taskId);
|
|
49
|
-
if (unsafeMsg)
|
|
50
|
-
return false;
|
|
51
|
-
return STRICT_TASK_ID_PATTERN.test(taskId);
|
|
52
|
-
}
|
|
53
|
-
function assertStrictTaskId(taskId) {
|
|
54
|
-
if (!isStrictTaskId(taskId)) {
|
|
55
|
-
throw new Error(`Invalid taskId: "${taskId}". Must match N.M or N.M.P (e.g. "1.1", "1.2.3").`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function sanitizeTaskId(taskId) {
|
|
59
|
-
const unsafeMsg = checkUnsafeChars(taskId);
|
|
60
|
-
if (unsafeMsg) {
|
|
61
|
-
throw new Error(unsafeMsg);
|
|
62
|
-
}
|
|
63
|
-
if (STRICT_TASK_ID_PATTERN.test(taskId) || RETRO_TASK_ID_REGEX.test(taskId) || INTERNAL_TOOL_ID_REGEX.test(taskId) || GENERAL_TASK_ID_REGEX.test(taskId)) {
|
|
64
|
-
return taskId;
|
|
65
|
-
}
|
|
66
|
-
throw new Error(`Invalid task ID: must be alphanumeric (ASCII) with optional hyphens, underscores, or dots, got "${taskId}"`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// src/gate-evidence.ts
|
|
70
|
-
var TASK_WORKFLOW_SCHEMA_MARKER = "exact-task-v1";
|
|
71
|
-
var TASK_WORKFLOW_STATES = [
|
|
72
|
-
"idle",
|
|
73
|
-
"coder_delegated",
|
|
74
|
-
"pre_check_passed",
|
|
75
|
-
"reviewer_run",
|
|
76
|
-
"tests_run",
|
|
77
|
-
"rework_required",
|
|
78
|
-
"complete",
|
|
79
|
-
"blocked"
|
|
80
|
-
];
|
|
81
|
-
var TASK_WORKFLOW_OUTCOMES = [
|
|
82
|
-
"none",
|
|
83
|
-
"dispatch_attempted",
|
|
84
|
-
"dispatch_no_mutation",
|
|
85
|
-
"accepted_mutation",
|
|
86
|
-
"accepted_mutation_failed",
|
|
87
|
-
"stage_a_passed",
|
|
88
|
-
"stage_a_failed",
|
|
89
|
-
"stage_b_completed",
|
|
90
|
-
"stage_b_failed",
|
|
91
|
-
"gate_recorded",
|
|
92
|
-
"task_completed",
|
|
93
|
-
"task_blocked",
|
|
94
|
-
"repair_idle"
|
|
95
|
-
];
|
|
96
|
-
function assertTaskEvidenceWriteAllowed(directory, taskId, event) {
|
|
97
|
-
assertValidTaskId(taskId);
|
|
98
|
-
const readWal = (kind, corruptCode) => {
|
|
99
|
-
const walPath = validateSwarmPath(directory, `${kind}/${taskId}.json`);
|
|
100
|
-
let raw;
|
|
101
|
-
try {
|
|
102
|
-
raw = readFileSync(walPath, "utf-8");
|
|
103
|
-
} catch (error) {
|
|
104
|
-
if (error.code === "ENOENT")
|
|
105
|
-
return null;
|
|
106
|
-
throw error;
|
|
107
|
-
}
|
|
108
|
-
try {
|
|
109
|
-
return JSON.parse(raw);
|
|
110
|
-
} catch (error) {
|
|
111
|
-
throw new Error(`${corruptCode}: ${walPath} is not valid JSON (${error instanceof Error ? error.message : String(error)}). Delete this file to allow the task to be repaired again.`);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
const coderWal = readWal("coder-settlements", "CODER_SETTLEMENT_WAL_CORRUPT");
|
|
115
|
-
if ((coderWal?.state === "DISPATCHED" || coderWal?.state === "PREPARED") && !(event != null && event.transitionId === coderWal.transitionId && (event.type === "dispatch_attempted" || event.type === "dispatch_no_mutation" || event.type === "accepted_mutation"))) {
|
|
116
|
-
throw new Error(`CODER_SETTLEMENT_IN_PROGRESS: transition ${String(coderWal.transitionId)} owns evidence for task ${taskId}`);
|
|
117
|
-
}
|
|
118
|
-
const terminalWal = readWal("task-terminals", "TASK_TERMINAL_WAL_CORRUPT");
|
|
119
|
-
if (terminalWal?.state === "PREPARED") {
|
|
120
|
-
const matchingTerminalEvent = event != null && event.transitionId === terminalWal.transitionId && (event.type === "task_completed" && terminalWal.newPlanStatus === "completed" || event.type === "task_blocked" && terminalWal.newPlanStatus === "blocked");
|
|
121
|
-
if (!matchingTerminalEvent) {
|
|
122
|
-
throw new Error(`TASK_TERMINAL_PREPARED: transition ${String(terminalWal.transitionId)} owns evidence for task ${taskId}`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
const repairWal = readWal("task-repairs", "TASK_REPAIR_WAL_CORRUPT");
|
|
126
|
-
if (repairWal?.state === "PREPARED" && !(event?.type === "repair_idle" && event.transitionId === repairWal.transitionId)) {
|
|
127
|
-
throw new Error(`TASK_REPAIR_PREPARED: transition ${String(repairWal.transitionId)} owns evidence for task ${taskId}`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
var GateEvidenceSchema = exports_external.object({
|
|
131
|
-
sessionId: exports_external.string(),
|
|
132
|
-
timestamp: exports_external.string(),
|
|
133
|
-
agent: exports_external.string()
|
|
134
|
-
}).passthrough();
|
|
135
|
-
var TaskWorkflowMetadataSchema = exports_external.object({
|
|
136
|
-
schema: exports_external.literal(TASK_WORKFLOW_SCHEMA_MARKER),
|
|
137
|
-
generation: exports_external.number().int().min(0),
|
|
138
|
-
state: exports_external.enum(TASK_WORKFLOW_STATES),
|
|
139
|
-
retryCount: exports_external.number().int().min(0).default(0),
|
|
140
|
-
retryHistory: exports_external.array(exports_external.enum(TASK_WORKFLOW_OUTCOMES)).max(3).default([]),
|
|
141
|
-
retryEpoch: exports_external.number().int().min(0).default(0),
|
|
142
|
-
lastOutcome: exports_external.enum(TASK_WORKFLOW_OUTCOMES).default("none"),
|
|
143
|
-
lastTransitionId: exports_external.string().min(1).nullable().optional().default(null),
|
|
144
|
-
updatedAt: exports_external.string()
|
|
145
|
-
});
|
|
146
|
-
var TaskEvidenceSchema = exports_external.object({
|
|
147
|
-
taskId: exports_external.string(),
|
|
148
|
-
required_gates: exports_external.array(exports_external.string()).default([]),
|
|
149
|
-
gates: exports_external.record(exports_external.string(), GateEvidenceSchema),
|
|
150
|
-
turbo: exports_external.boolean().optional(),
|
|
151
|
-
test_engineer_exempt: exports_external.boolean().optional(),
|
|
152
|
-
workflow: TaskWorkflowMetadataSchema.optional()
|
|
153
|
-
});
|
|
154
|
-
function parseTaskEvidence(raw, expectedTaskId) {
|
|
155
|
-
const parsed = TaskEvidenceSchema.parse(JSON.parse(raw));
|
|
156
|
-
if (parsed.taskId !== expectedTaskId) {
|
|
157
|
-
throw new Error(`TASK_EVIDENCE_IDENTITY_MISMATCH: expected ${expectedTaskId}, found ${parsed.taskId}`);
|
|
158
|
-
}
|
|
159
|
-
return parsed;
|
|
160
|
-
}
|
|
161
|
-
var DEFAULT_REQUIRED_GATES = ["reviewer", "test_engineer"];
|
|
162
|
-
var DEFAULT_WORKFLOW_STATE = "idle";
|
|
163
|
-
var WORKFLOW_STATE_RANK = {
|
|
164
|
-
idle: 0,
|
|
165
|
-
coder_delegated: 1,
|
|
166
|
-
pre_check_passed: 2,
|
|
167
|
-
reviewer_run: 3,
|
|
168
|
-
tests_run: 4,
|
|
169
|
-
rework_required: 5,
|
|
170
|
-
blocked: 6,
|
|
171
|
-
complete: 7
|
|
172
|
-
};
|
|
173
|
-
function isValidTaskId(taskId) {
|
|
174
|
-
return isStrictTaskId(taskId);
|
|
175
|
-
}
|
|
176
|
-
function assertValidTaskId(taskId) {
|
|
177
|
-
assertStrictTaskId(taskId);
|
|
178
|
-
}
|
|
179
|
-
function createDefaultWorkflowMetadata(nowIso) {
|
|
180
|
-
return {
|
|
181
|
-
schema: TASK_WORKFLOW_SCHEMA_MARKER,
|
|
182
|
-
generation: 0,
|
|
183
|
-
state: DEFAULT_WORKFLOW_STATE,
|
|
184
|
-
retryCount: 0,
|
|
185
|
-
retryHistory: [],
|
|
186
|
-
retryEpoch: 0,
|
|
187
|
-
lastOutcome: "none",
|
|
188
|
-
lastTransitionId: null,
|
|
189
|
-
updatedAt: nowIso
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
function getEventOutcome(event) {
|
|
193
|
-
switch (event.type) {
|
|
194
|
-
case "dispatch_attempted":
|
|
195
|
-
return "dispatch_attempted";
|
|
196
|
-
case "dispatch_no_mutation":
|
|
197
|
-
return "dispatch_no_mutation";
|
|
198
|
-
case "accepted_mutation":
|
|
199
|
-
return event.context?.settlementFailed === true ? "accepted_mutation_failed" : "accepted_mutation";
|
|
200
|
-
case "stage_a_passed":
|
|
201
|
-
return "stage_a_passed";
|
|
202
|
-
case "stage_a_failed":
|
|
203
|
-
return "stage_a_failed";
|
|
204
|
-
case "stage_b_completed":
|
|
205
|
-
return "stage_b_completed";
|
|
206
|
-
case "stage_b_failed":
|
|
207
|
-
return "stage_b_failed";
|
|
208
|
-
case "gate_recorded":
|
|
209
|
-
return "gate_recorded";
|
|
210
|
-
case "task_completed":
|
|
211
|
-
return "task_completed";
|
|
212
|
-
case "task_blocked":
|
|
213
|
-
return "task_blocked";
|
|
214
|
-
case "repair_idle":
|
|
215
|
-
return "repair_idle";
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
function isAuthoritativeWorkflowMetadata(workflow) {
|
|
219
|
-
return workflow?.schema === TASK_WORKFLOW_SCHEMA_MARKER;
|
|
220
|
-
}
|
|
221
|
-
function getTaskWorkflowSnapshot(evidence) {
|
|
222
|
-
if (isAuthoritativeWorkflowMetadata(evidence?.workflow)) {
|
|
223
|
-
return { ...evidence.workflow, authoritative: true };
|
|
224
|
-
}
|
|
225
|
-
return {
|
|
226
|
-
...createDefaultWorkflowMetadata(""),
|
|
227
|
-
authoritative: false
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
function maybeExpandRequiredGates(existing, event) {
|
|
231
|
-
if (event.type === "accepted_mutation") {
|
|
232
|
-
const agentType = event.agentType;
|
|
233
|
-
if (typeof agentType === "string" && agentType.trim() !== "") {
|
|
234
|
-
return existing ? expandRequiredGates(existing.required_gates, agentType, event.context ?? {}) : deriveRequiredGates(agentType, event.context ?? {});
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
if (event.type === "stage_b_completed" && event.ensureDefaultStageB === true) {
|
|
238
|
-
return expandRequiredGates(existing?.required_gates ?? [], "coder", {
|
|
239
|
-
testEngineerExempt: false
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
if (event.type === "stage_b_completed" || event.type === "gate_recorded") {
|
|
243
|
-
return existing ? expandRequiredGates(existing.required_gates, event.gate) : deriveRequiredGates(event.gate);
|
|
244
|
-
}
|
|
245
|
-
return existing?.required_gates ?? [];
|
|
246
|
-
}
|
|
247
|
-
function hasAllRequiredGatesPassed(requiredGates, gates) {
|
|
248
|
-
return requiredGates.length > 0 && requiredGates.every((gate) => gates[gate] != null);
|
|
249
|
-
}
|
|
250
|
-
function shouldTreatGateAsStageB(gate) {
|
|
251
|
-
return gate === "reviewer" || gate === "test_engineer";
|
|
252
|
-
}
|
|
253
|
-
function clearWorkflowGateProof(gates) {
|
|
254
|
-
const retained = { ...gates };
|
|
255
|
-
delete retained.pre_check;
|
|
256
|
-
delete retained.reviewer;
|
|
257
|
-
delete retained.test_engineer;
|
|
258
|
-
delete retained.council;
|
|
259
|
-
delete retained.critic;
|
|
260
|
-
delete retained.critic_sounding_board;
|
|
261
|
-
return retained;
|
|
262
|
-
}
|
|
263
|
-
function assertExpectedGeneration(snapshot, event) {
|
|
264
|
-
if (typeof event.expectedGeneration === "number" && event.expectedGeneration !== snapshot.generation) {
|
|
265
|
-
throw new Error(`TASK_WORKFLOW_GENERATION_MISMATCH: expected ${event.expectedGeneration}, current ${snapshot.generation}`);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
function isDuplicateTransition(snapshot, event) {
|
|
269
|
-
return snapshot.authoritative && typeof event.transitionId === "string" && event.transitionId.length > 0 && snapshot.lastTransitionId === event.transitionId && snapshot.lastOutcome === getEventOutcome(event);
|
|
270
|
-
}
|
|
271
|
-
function reduceTaskWorkflowSnapshot(current, event, context) {
|
|
272
|
-
if ((current.state === "complete" || current.state === "blocked") && event.type !== "repair_idle" && !(current.state === "complete" && event.type === "task_completed" || current.state === "blocked" && event.type === "task_blocked")) {
|
|
273
|
-
throw new Error(`TASK_WORKFLOW_TERMINAL: cannot apply ${event.type} from ${current.state}`);
|
|
274
|
-
}
|
|
275
|
-
const outcome = getEventOutcome(event);
|
|
276
|
-
const base = {
|
|
277
|
-
schema: TASK_WORKFLOW_SCHEMA_MARKER,
|
|
278
|
-
generation: current.generation,
|
|
279
|
-
state: current.state,
|
|
280
|
-
retryCount: current.retryCount,
|
|
281
|
-
retryHistory: current.retryHistory,
|
|
282
|
-
retryEpoch: current.retryEpoch,
|
|
283
|
-
lastOutcome: outcome,
|
|
284
|
-
lastTransitionId: event.transitionId ?? current.lastTransitionId,
|
|
285
|
-
updatedAt: context.nowIso
|
|
286
|
-
};
|
|
287
|
-
switch (event.type) {
|
|
288
|
-
case "dispatch_attempted":
|
|
289
|
-
return base;
|
|
290
|
-
case "dispatch_no_mutation":
|
|
291
|
-
return {
|
|
292
|
-
...base,
|
|
293
|
-
retryCount: Math.min(current.retryCount + 1, 3),
|
|
294
|
-
retryHistory: [...current.retryHistory, outcome].slice(-3),
|
|
295
|
-
retryEpoch: current.retryEpoch || current.generation + 1
|
|
296
|
-
};
|
|
297
|
-
case "stage_b_failed":
|
|
298
|
-
if (current.state !== "pre_check_passed" && current.state !== "reviewer_run" && current.state !== "tests_run" && current.state !== "rework_required") {
|
|
299
|
-
throw new Error(`TASK_WORKFLOW_STAGE_A_REQUIRED: cannot reject ${event.gate} from ${current.state}`);
|
|
300
|
-
}
|
|
301
|
-
return {
|
|
302
|
-
...base,
|
|
303
|
-
state: "rework_required",
|
|
304
|
-
retryCount: Math.min(current.retryCount + 1, 3),
|
|
305
|
-
retryHistory: [...current.retryHistory, outcome].slice(-3),
|
|
306
|
-
retryEpoch: current.retryEpoch || current.generation + 1
|
|
307
|
-
};
|
|
308
|
-
case "accepted_mutation":
|
|
309
|
-
if (event.context?.settlementFailed === true) {
|
|
310
|
-
return {
|
|
311
|
-
...base,
|
|
312
|
-
generation: current.generation + 1,
|
|
313
|
-
state: "rework_required",
|
|
314
|
-
retryCount: Math.min(current.retryCount + 1, 3),
|
|
315
|
-
retryHistory: [...current.retryHistory, outcome].slice(-3),
|
|
316
|
-
retryEpoch: current.retryEpoch || current.generation + 1
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
return {
|
|
320
|
-
...base,
|
|
321
|
-
generation: current.generation + 1,
|
|
322
|
-
state: "coder_delegated"
|
|
323
|
-
};
|
|
324
|
-
case "stage_a_passed":
|
|
325
|
-
if (current.state !== "coder_delegated" && current.state !== "pre_check_passed") {
|
|
326
|
-
throw new Error(`TASK_WORKFLOW_CODER_MUTATION_REQUIRED: cannot pass Stage A from ${current.state}`);
|
|
327
|
-
}
|
|
328
|
-
return {
|
|
329
|
-
...base,
|
|
330
|
-
state: "pre_check_passed"
|
|
331
|
-
};
|
|
332
|
-
case "stage_a_failed":
|
|
333
|
-
if (current.state !== "coder_delegated" && current.state !== "pre_check_passed" && current.state !== "rework_required") {
|
|
334
|
-
throw new Error(`TASK_WORKFLOW_CODER_MUTATION_REQUIRED: cannot fail Stage A from ${current.state}`);
|
|
335
|
-
}
|
|
336
|
-
return {
|
|
337
|
-
...base,
|
|
338
|
-
state: "rework_required",
|
|
339
|
-
retryCount: Math.min(current.retryCount + 1, 3),
|
|
340
|
-
retryHistory: [...current.retryHistory, outcome].slice(-3),
|
|
341
|
-
retryEpoch: current.retryEpoch || current.generation + 1
|
|
342
|
-
};
|
|
343
|
-
case "stage_b_completed":
|
|
344
|
-
if (current.state !== "pre_check_passed" && current.state !== "reviewer_run" && current.state !== "tests_run") {
|
|
345
|
-
throw new Error(`TASK_WORKFLOW_STAGE_A_REQUIRED: cannot record ${event.gate} from ${current.state}`);
|
|
346
|
-
}
|
|
347
|
-
return {
|
|
348
|
-
...base,
|
|
349
|
-
state: hasAllRequiredGatesPassed(context.requiredGates, context.gates) ? "tests_run" : "reviewer_run"
|
|
350
|
-
};
|
|
351
|
-
case "gate_recorded":
|
|
352
|
-
return {
|
|
353
|
-
...base,
|
|
354
|
-
state: current.state
|
|
355
|
-
};
|
|
356
|
-
case "task_completed":
|
|
357
|
-
if (event.qaExempt !== true && current.state !== "tests_run" && current.state !== "complete" && !(current.state === "pre_check_passed" && context.gates.council != null)) {
|
|
358
|
-
throw new Error(`TASK_WORKFLOW_QA_REQUIRED: cannot complete from ${current.state}`);
|
|
359
|
-
}
|
|
360
|
-
return {
|
|
361
|
-
...base,
|
|
362
|
-
state: "complete"
|
|
363
|
-
};
|
|
364
|
-
case "task_blocked":
|
|
365
|
-
return {
|
|
366
|
-
...base,
|
|
367
|
-
state: "blocked"
|
|
368
|
-
};
|
|
369
|
-
case "repair_idle":
|
|
370
|
-
return {
|
|
371
|
-
...base,
|
|
372
|
-
generation: current.generation + 1,
|
|
373
|
-
state: "idle",
|
|
374
|
-
retryCount: 0,
|
|
375
|
-
retryHistory: [],
|
|
376
|
-
retryEpoch: 0
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
function getEvidenceDir(directory) {
|
|
381
|
-
const swarmDir = path.resolve(directory, ".swarm");
|
|
382
|
-
const evidenceDir = path.join(swarmDir, "evidence");
|
|
383
|
-
mkdirSync(evidenceDir, { recursive: true });
|
|
384
|
-
let resolvedSwarmDir;
|
|
385
|
-
let resolvedEvidenceDir;
|
|
386
|
-
try {
|
|
387
|
-
resolvedSwarmDir = path.normalize(realpathSync(swarmDir));
|
|
388
|
-
resolvedEvidenceDir = path.normalize(realpathSync(evidenceDir));
|
|
389
|
-
} catch (error) {
|
|
390
|
-
throw new Error(`Unable to resolve evidence directory: ${error.message}`);
|
|
391
|
-
}
|
|
392
|
-
const swarmPrefix = `${resolvedSwarmDir}${path.sep}`;
|
|
393
|
-
const withinSwarmBoundary = process.platform === "win32" ? resolvedEvidenceDir.toLowerCase().startsWith(swarmPrefix.toLowerCase()) : resolvedEvidenceDir.startsWith(swarmPrefix);
|
|
394
|
-
if (!withinSwarmBoundary) {
|
|
395
|
-
throw new Error(`Evidence path escapes .swarm boundary: ${resolvedEvidenceDir}`);
|
|
396
|
-
}
|
|
397
|
-
return resolvedEvidenceDir;
|
|
398
|
-
}
|
|
399
|
-
function getEvidencePath(directory, taskId) {
|
|
400
|
-
assertValidTaskId(taskId);
|
|
401
|
-
return taskEvidencePath(directory, taskId);
|
|
402
|
-
}
|
|
403
|
-
function readExisting(evidencePath, taskId) {
|
|
404
|
-
try {
|
|
405
|
-
const raw = readFileSync(evidencePath, "utf-8");
|
|
406
|
-
return parseTaskEvidence(raw, taskId);
|
|
407
|
-
} catch (error) {
|
|
408
|
-
if (error.code === "ENOENT")
|
|
409
|
-
return null;
|
|
410
|
-
telemetry.gateParseError(taskId, error);
|
|
411
|
-
throw error;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
function deriveRequiredGates(agentType, context = {}) {
|
|
415
|
-
switch (agentType) {
|
|
416
|
-
case "coder":
|
|
417
|
-
return context.testEngineerExempt === true ? ["reviewer"] : ["reviewer", "test_engineer"];
|
|
418
|
-
case "docs":
|
|
419
|
-
return ["docs"];
|
|
420
|
-
case "designer":
|
|
421
|
-
return ["designer", "reviewer", "test_engineer"];
|
|
422
|
-
case "explorer":
|
|
423
|
-
return ["explorer"];
|
|
424
|
-
case "sme":
|
|
425
|
-
return ["sme"];
|
|
426
|
-
case "reviewer":
|
|
427
|
-
return ["reviewer"];
|
|
428
|
-
case "test_engineer":
|
|
429
|
-
return ["test_engineer"];
|
|
430
|
-
case "critic":
|
|
431
|
-
return ["critic"];
|
|
432
|
-
case "critic_sounding_board":
|
|
433
|
-
return ["critic_sounding_board"];
|
|
434
|
-
case "critic_drift_verifier":
|
|
435
|
-
return ["critic_drift_verifier"];
|
|
436
|
-
case "critic_hallucination_verifier":
|
|
437
|
-
return ["critic_hallucination_verifier"];
|
|
438
|
-
case "critic_architecture_supervisor":
|
|
439
|
-
return ["critic_architecture_supervisor"];
|
|
440
|
-
default:
|
|
441
|
-
return ["reviewer", "test_engineer"];
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
function expandRequiredGates(existingGates, newAgentType, context = {}) {
|
|
445
|
-
const newGates = deriveRequiredGates(newAgentType, context);
|
|
446
|
-
const combined = [...new Set([...existingGates ?? [], ...newGates])];
|
|
447
|
-
return combined.sort();
|
|
448
|
-
}
|
|
449
|
-
function updateEvidenceForTransition(existing, event) {
|
|
450
|
-
const nowIso = new Date().toISOString();
|
|
451
|
-
const requiredGates = maybeExpandRequiredGates(existing, event);
|
|
452
|
-
let gates = {
|
|
453
|
-
...existing?.gates ?? {}
|
|
454
|
-
};
|
|
455
|
-
if (event.type === "accepted_mutation" || event.type === "repair_idle") {
|
|
456
|
-
gates = clearWorkflowGateProof(gates);
|
|
457
|
-
}
|
|
458
|
-
if (event.type === "stage_a_passed") {
|
|
459
|
-
gates = {
|
|
460
|
-
...gates,
|
|
461
|
-
pre_check: {
|
|
462
|
-
sessionId: "system",
|
|
463
|
-
timestamp: nowIso,
|
|
464
|
-
agent: "pre_check"
|
|
465
|
-
}
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
if (event.type === "stage_a_failed") {
|
|
469
|
-
delete gates.pre_check;
|
|
470
|
-
}
|
|
471
|
-
if (event.type === "stage_b_failed") {
|
|
472
|
-
delete gates.reviewer;
|
|
473
|
-
delete gates.test_engineer;
|
|
474
|
-
}
|
|
475
|
-
if (event.type === "stage_b_completed" || event.type === "gate_recorded") {
|
|
476
|
-
gates = {
|
|
477
|
-
...gates,
|
|
478
|
-
[event.gate]: {
|
|
479
|
-
sessionId: event.sessionId,
|
|
480
|
-
timestamp: nowIso,
|
|
481
|
-
agent: event.gate
|
|
482
|
-
}
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
const snapshot = getTaskWorkflowSnapshot(existing);
|
|
486
|
-
assertExpectedGeneration(snapshot, event);
|
|
487
|
-
if (isDuplicateTransition(snapshot, event) && existing) {
|
|
488
|
-
return existing;
|
|
489
|
-
}
|
|
490
|
-
const workflow = reduceTaskWorkflowSnapshot(snapshot, event, {
|
|
491
|
-
requiredGates,
|
|
492
|
-
gates,
|
|
493
|
-
nowIso
|
|
494
|
-
});
|
|
495
|
-
return {
|
|
496
|
-
taskId: existing?.taskId ?? "",
|
|
497
|
-
required_gates: requiredGates,
|
|
498
|
-
gates,
|
|
499
|
-
turbo: "turbo" in event && event.turbo === true ? true : ("turbo" in event) && event.turbo === false ? existing?.turbo : existing?.turbo,
|
|
500
|
-
test_engineer_exempt: (event.type === "dispatch_attempted" || event.type === "dispatch_no_mutation" || event.type === "accepted_mutation") && event.agentType === "coder" ? event.context?.testEngineerExempt === true && !requiredGates.includes("test_engineer") : existing?.test_engineer_exempt,
|
|
501
|
-
workflow
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
async function transitionTaskWorkflowEvidence(directory, taskId, event) {
|
|
505
|
-
assertValidTaskId(taskId);
|
|
506
|
-
let updatedEvidence = null;
|
|
507
|
-
await withTaskEvidenceTransaction(directory, taskId, event.type, async (transaction) => {
|
|
508
|
-
updatedEvidence = await transaction.transition(event);
|
|
509
|
-
});
|
|
510
|
-
return updatedEvidence ?? {
|
|
511
|
-
taskId,
|
|
512
|
-
required_gates: [],
|
|
513
|
-
gates: {},
|
|
514
|
-
workflow: createDefaultWorkflowMetadata(new Date().toISOString())
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
async function withTaskEvidenceTransaction(directory, taskId, agent, callback) {
|
|
518
|
-
assertValidTaskId(taskId);
|
|
519
|
-
return withTaskEvidenceLock(directory, taskId, agent, async () => {
|
|
520
|
-
const resolvedEvidenceDir = getEvidenceDir(directory);
|
|
521
|
-
const evidencePath = path.join(resolvedEvidenceDir, `${taskId}.json`);
|
|
522
|
-
let current = null;
|
|
523
|
-
try {
|
|
524
|
-
current = readExisting(evidencePath, taskId);
|
|
525
|
-
} catch (error) {
|
|
526
|
-
telemetry.gateParseError(taskId, error);
|
|
527
|
-
throw error;
|
|
528
|
-
}
|
|
529
|
-
const persist = async (nextEvidence) => {
|
|
530
|
-
const validated = TaskEvidenceSchema.parse({
|
|
531
|
-
...nextEvidence,
|
|
532
|
-
taskId
|
|
533
|
-
});
|
|
534
|
-
await atomicWriteFile(evidencePath, JSON.stringify(validated, null, 2));
|
|
535
|
-
current = validated;
|
|
536
|
-
return validated;
|
|
537
|
-
};
|
|
538
|
-
return callback({
|
|
539
|
-
taskId,
|
|
540
|
-
read: () => current,
|
|
541
|
-
transition: async (event) => {
|
|
542
|
-
assertTaskEvidenceWriteAllowed(directory, taskId, event);
|
|
543
|
-
const nextEvidence = updateEvidenceForTransition(current, event);
|
|
544
|
-
nextEvidence.taskId = taskId;
|
|
545
|
-
return persist(nextEvidence);
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
async function recordGateEvidence(directory, taskId, gate, sessionId, turbo, options = {}) {
|
|
551
|
-
assertValidTaskId(taskId);
|
|
552
|
-
if (shouldTreatGateAsStageB(gate) && typeof options.expectedGeneration !== "number") {
|
|
553
|
-
throw new Error(`TASK_WORKFLOW_GENERATION_REQUIRED: ${gate} for task ${taskId} must be bound at dispatch`);
|
|
554
|
-
}
|
|
555
|
-
await transitionTaskWorkflowEvidence(directory, taskId, shouldTreatGateAsStageB(gate) ? {
|
|
556
|
-
type: "stage_b_completed",
|
|
557
|
-
gate,
|
|
558
|
-
sessionId,
|
|
559
|
-
turbo,
|
|
560
|
-
ensureDefaultStageB: options.ensureDefaultStageB,
|
|
561
|
-
expectedGeneration: options.expectedGeneration,
|
|
562
|
-
transitionId: options.transitionId
|
|
563
|
-
} : {
|
|
564
|
-
type: "gate_recorded",
|
|
565
|
-
gate,
|
|
566
|
-
sessionId,
|
|
567
|
-
turbo,
|
|
568
|
-
expectedGeneration: options.expectedGeneration,
|
|
569
|
-
transitionId: options.transitionId
|
|
570
|
-
});
|
|
571
|
-
telemetry.gatePassed(sessionId, gate, taskId);
|
|
572
|
-
}
|
|
573
|
-
async function recordAgentDispatch(directory, taskId, agentType, turbo, context = {}) {
|
|
574
|
-
assertValidTaskId(taskId);
|
|
575
|
-
const expectedGeneration = getTaskWorkflowSnapshot(await readTaskEvidence(directory, taskId)).generation;
|
|
576
|
-
await transitionTaskWorkflowEvidence(directory, taskId, agentType === "coder" ? {
|
|
577
|
-
type: "accepted_mutation",
|
|
578
|
-
agentType,
|
|
579
|
-
turbo,
|
|
580
|
-
context,
|
|
581
|
-
expectedGeneration
|
|
582
|
-
} : {
|
|
583
|
-
type: "dispatch_attempted",
|
|
584
|
-
agentType,
|
|
585
|
-
turbo,
|
|
586
|
-
context,
|
|
587
|
-
expectedGeneration
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
async function readTaskEvidence(directory, taskId) {
|
|
591
|
-
try {
|
|
592
|
-
assertValidTaskId(taskId);
|
|
593
|
-
return readExisting(getEvidencePath(directory, taskId), taskId);
|
|
594
|
-
} catch {
|
|
595
|
-
return null;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
function readTaskEvidenceRaw(directory, taskId) {
|
|
599
|
-
assertValidTaskId(taskId);
|
|
600
|
-
const evidencePath = getEvidencePath(directory, taskId);
|
|
601
|
-
try {
|
|
602
|
-
const raw = readFileSync(evidencePath, "utf-8");
|
|
603
|
-
return parseTaskEvidence(raw, taskId);
|
|
604
|
-
} catch (error) {
|
|
605
|
-
const code = error.code;
|
|
606
|
-
if (code === "ENOENT" || code === "ENAMETOOLONG")
|
|
607
|
-
return null;
|
|
608
|
-
throw error;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
async function hasPassedAllGates(directory, taskId) {
|
|
612
|
-
const evidence = await readTaskEvidence(directory, taskId);
|
|
613
|
-
if (!evidence)
|
|
614
|
-
return false;
|
|
615
|
-
if (!Array.isArray(evidence.required_gates) || evidence.required_gates.length === 0)
|
|
616
|
-
return false;
|
|
617
|
-
return evidence.required_gates.every((gate) => evidence.gates[gate] != null);
|
|
618
|
-
}
|
|
619
|
-
function compareTaskWorkflowStateRank(left, right) {
|
|
620
|
-
return WORKFLOW_STATE_RANK[left] - WORKFLOW_STATE_RANK[right];
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
export { sanitizeTaskId, TASK_WORKFLOW_SCHEMA_MARKER, assertTaskEvidenceWriteAllowed, parseTaskEvidence, DEFAULT_REQUIRED_GATES, isValidTaskId, getTaskWorkflowSnapshot, reduceTaskWorkflowSnapshot, deriveRequiredGates, expandRequiredGates, transitionTaskWorkflowEvidence, withTaskEvidenceTransaction, recordGateEvidence, recordAgentDispatch, readTaskEvidence, readTaskEvidenceRaw, hasPassedAllGates, compareTaskWorkflowStateRank };
|