opencode-swarm 7.133.0 → 7.134.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/skills/codebase-review-swarm/SKILL.md +18 -2
- package/.opencode/skills/codebase-review-swarm/references/review-protocol-v8.2.md +18 -2
- package/.opencode/skills/swarm-implement/SKILL.md +17 -2
- package/dist/background/completion-observer.d.ts +17 -7
- package/dist/background/pending-delegations.d.ts +305 -30
- package/dist/background/pr-event-subscribers.d.ts +1 -1
- package/dist/cli/{config-doctor-8nbdwnn6.js → config-doctor-4avzz3rh.js} +3 -3
- package/dist/cli/{core-egwpry19.js → core-d6yd2jzv.js} +3 -3
- package/dist/cli/{curation-policy-gae3xm5d.js → curation-policy-8vkzjtwb.js} +7 -7
- package/dist/cli/{curator-e88bm4mg.js → curator-3vsvhwav.js} +31 -28
- package/dist/cli/{curator-drift-ea0p8nh1.js → curator-drift-9q6f85bm.js} +7 -7
- package/dist/cli/{curator-llm-factory-1a7esr69.js → curator-llm-factory-2qk8q4y9.js} +31 -28
- package/dist/cli/{dispatch-3mhgqj2x.js → dispatch-gxbeb2ps.js} +2 -2
- package/dist/cli/{evidence-summary-service-nhsmsap8.js → evidence-summary-service-mrzsjpg4.js} +13 -12
- package/dist/cli/{gate-evidence-cy2te3x8.js → gate-evidence-b5v1xvxp.js} +4 -4
- package/dist/cli/guardrail-explain-tw521x47.js +57 -0
- package/dist/cli/{guardrail-log-fjgyx76r.js → guardrail-log-e3jrwdw8.js} +5 -5
- package/dist/cli/{hive-promoter-2b04e0wh.js → hive-promoter-sdy5qjzn.js} +31 -28
- package/dist/cli/{index-66zght9d.js → index-0541h76w.js} +2 -2
- package/dist/cli/{index-g4mnf3p5.js → index-1j3682j8.js} +5 -5
- package/dist/cli/{index-mmvaf4t6.js → index-1v5mby7t.js} +25 -3
- package/dist/cli/{index-j5pnah1w.js → index-2j8f71c3.js} +12 -12
- package/dist/cli/{index-17zjp67y.js → index-2t1n9k7b.js} +4 -4
- package/dist/cli/{index-mwqfyjja.js → index-358f9h8p.js} +48406 -48697
- package/dist/cli/{index-hfpw5m0e.js → index-463k8mfr.js} +3 -3
- package/dist/cli/{index-2tn5h2zp.js → index-4p02dp92.js} +6 -6
- package/dist/cli/{index-7ftthvqt.js → index-6b69mp0r.js} +8 -8
- package/dist/cli/{index-bz08438z.js → index-8t9xpn1a.js} +7 -7
- package/dist/cli/{index-sp7tm8t7.js → index-9z1q181f.js} +1 -1
- package/dist/cli/index-b4z7s917.js +80 -0
- package/dist/cli/index-b8zgtz81.js +123 -0
- package/dist/cli/{index-tj2mkfb3.js → index-bq2848a3.js} +1 -1
- package/dist/cli/{index-kc1m6vpr.js → index-c0e3vhv9.js} +2 -2
- package/dist/cli/{index-q27bajqb.js → index-cfkvc5q9.js} +3 -3
- package/dist/cli/{index-7mkpsw6g.js → index-dd7ttx8x.js} +1 -1
- package/dist/cli/{index-r260ybay.js → index-k742ecbm.js} +178 -222
- package/dist/cli/{index-cpest8sa.js → index-kwm7hz18.js} +5 -5
- package/dist/cli/{index-0vd91yb8.js → index-m8ajf6wb.js} +3 -3
- package/dist/cli/{index-mhcge2ry.js → index-mz6sqtm9.js} +33 -30
- package/dist/cli/{index-74tptrss.js → index-p8hezdtz.js} +1 -1
- package/dist/cli/{index-sw82jvf2.js → index-pz49rx4y.js} +2 -2
- package/dist/cli/{index-3x761jqv.js → index-wk1h5ec2.js} +1 -1
- package/dist/cli/{index-wzb4dv5s.js → index-y2esw64s.js} +4 -4
- package/dist/cli/index-y3v1404y.js +1625 -0
- package/dist/cli/{index-wz36r0fk.js → index-zjw32rv9.js} +2 -2
- package/dist/cli/index.js +31 -28
- package/dist/cli/{knowledge-escalator-hzkw1ban.js → knowledge-escalator-v09w0jmg.js} +8 -8
- package/dist/cli/{knowledge-events-pbycdhqd.js → knowledge-events-qp19r2he.js} +6 -6
- package/dist/cli/{knowledge-link-4yd2z8ev.js → knowledge-link-nc4y2zkq.js} +5 -5
- package/dist/cli/{knowledge-store-45jawb2p.js → knowledge-store-n5pvv0s3.js} +6 -6
- package/dist/cli/{knowledge-validator-ge576f8n.js → knowledge-validator-qg31ycbb.js} +9 -9
- package/dist/cli/pending-delegations-v9e40hfk.js +96 -0
- package/dist/cli/{pr-subscriptions-zqbmctwz.js → pr-subscriptions-ztq40hrt.js} +7 -7
- package/dist/cli/{scan-cursor-kvw872be.js → scan-cursor-wtzfvkh2.js} +7 -7
- package/dist/cli/{schema-a2zm2m3r.js → schema-erk5bz43.js} +2 -2
- package/dist/cli/{scope-persistence-5qypzxxm.js → scope-persistence-hz3zmmsv.js} +5 -5
- package/dist/cli/{skill-generator-ch79hvxh.js → skill-generator-nxs65tq3.js} +10 -10
- package/dist/cli/worktree-collision-ownership-c4bdnjtx.js +221 -0
- package/dist/evaluation/contracts.d.ts +1 -1
- package/dist/evaluation/public-api.d.ts +1 -1
- package/dist/hooks/delegation-gate/worktree-collision-ownership.d.ts +28 -0
- package/dist/hooks/delegation-gate/worktree-isolation.d.ts +63 -1
- package/dist/hooks/delegation-gate/worktree-merge-status.d.ts +14 -0
- package/dist/hooks/delegation-gate/worktree-ownership-tag.d.ts +19 -0
- package/dist/hooks/delegation-gate/worktree-provisioning-owner.d.ts +30 -0
- package/dist/hooks/delegation-gate.d.ts +14 -5
- package/dist/hooks/guardrails/messages-transform.d.ts +10 -0
- package/dist/hooks/guardrails/stored-input-args.d.ts +6 -0
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/init-orphan-recovery.d.ts +17 -0
- package/dist/index.js +486 -479
- package/dist/memory/schema.d.ts +4 -4
- package/dist/prm/escalation.d.ts +1 -11
- package/dist/prm/index.d.ts +1 -0
- package/dist/prm/types.d.ts +0 -2
- package/dist/session/snapshot-writer.d.ts +3 -0
- package/dist/state.d.ts +54 -1
- package/dist/tools/dispatch-lanes.d.ts +1 -1
- package/dist/tools/epic-record-divergence.d.ts +5 -5
- package/dist/utils/advisory-queue.d.ts +60 -0
- package/dist/worktree/merge.d.ts +47 -1
- package/package.json +1 -1
- package/dist/cli/guardrail-explain-zgja2b9m.js +0 -54
- package/dist/cli/pending-delegations-kxy8mdqg.js +0 -75
- package/dist/cli/{index-9f71ye47.js → index-13xxjfhn.js} +3 -3
- package/dist/cli/{index-s80bsjkj.js → index-2ghkk9ve.js} +6 -6
- package/dist/cli/{index-76kvm9z5.js → index-5nybajn9.js} +3 -3
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
scanWorktreeProvisioningOwnersForRecovery
|
|
4
|
+
} from "./index-b4z7s917.js";
|
|
5
|
+
import {
|
|
6
|
+
scanWorktreeMergeFailuresForRecovery
|
|
7
|
+
} from "./index-b8zgtz81.js";
|
|
8
|
+
import {
|
|
9
|
+
scanDelegationFallbacksForRecovery,
|
|
10
|
+
scanDelegationsForRecovery
|
|
11
|
+
} from "./index-y3v1404y.js";
|
|
12
|
+
import"./index-2ghkk9ve.js";
|
|
13
|
+
import"./index-fsrp8wp3.js";
|
|
14
|
+
import"./index-5nybajn9.js";
|
|
15
|
+
import"./index-7g4c7s5r.js";
|
|
16
|
+
import"./index-q2rrv0cy.js";
|
|
17
|
+
import"./index-bpmtbmy9.js";
|
|
18
|
+
import"./index-z6xqpmqg.js";
|
|
19
|
+
import"./index-zjygnfay.js";
|
|
20
|
+
import {
|
|
21
|
+
bunSpawn
|
|
22
|
+
} from "./index-qe3v54nb.js";
|
|
23
|
+
import"./index-zgwm4ryv.js";
|
|
24
|
+
import"./index-a76rekgs.js";
|
|
25
|
+
|
|
26
|
+
// src/hooks/delegation-gate/worktree-collision-ownership.ts
|
|
27
|
+
import * as path2 from "path";
|
|
28
|
+
|
|
29
|
+
// src/hooks/delegation-gate/worktree-ownership-tag.ts
|
|
30
|
+
import * as fs from "fs";
|
|
31
|
+
import * as path from "path";
|
|
32
|
+
var OWNERSHIP_TAG_SCAN_TIMEOUT_MS = 2000;
|
|
33
|
+
var MAX_OWNERSHIP_TAGS = 512;
|
|
34
|
+
function decodeTagPart(value) {
|
|
35
|
+
if (!value || !/^[A-Za-z0-9_-]+$/.test(value))
|
|
36
|
+
return null;
|
|
37
|
+
const decoded = Buffer.from(value, "base64url").toString("utf8");
|
|
38
|
+
return decoded && Buffer.from(decoded, "utf8").toString("base64url") === value ? decoded : null;
|
|
39
|
+
}
|
|
40
|
+
async function scanBackgroundWorktreeOwnershipTagsForRecovery(directory) {
|
|
41
|
+
try {
|
|
42
|
+
fs.statSync(path.join(directory, ".git"));
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error.code === "ENOENT") {
|
|
45
|
+
return { status: "ok", owners: [] };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
status: "uncertain",
|
|
49
|
+
reason: `Git metadata could not be inspected: ${error instanceof Error ? error.message : String(error)}`
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const proc = bunSpawn([
|
|
53
|
+
"git",
|
|
54
|
+
"-C",
|
|
55
|
+
directory,
|
|
56
|
+
"for-each-ref",
|
|
57
|
+
`--count=${MAX_OWNERSHIP_TAGS + 1}`,
|
|
58
|
+
"--format=%(refname:strip=2)%00%(objectname)",
|
|
59
|
+
"refs/tags/swarm-preserved-owner/"
|
|
60
|
+
], {
|
|
61
|
+
stdin: "ignore",
|
|
62
|
+
stdout: "pipe",
|
|
63
|
+
stderr: "ignore",
|
|
64
|
+
timeout: OWNERSHIP_TAG_SCAN_TIMEOUT_MS
|
|
65
|
+
});
|
|
66
|
+
try {
|
|
67
|
+
const exitCode = await proc.exited;
|
|
68
|
+
if (exitCode !== 0) {
|
|
69
|
+
return {
|
|
70
|
+
status: "uncertain",
|
|
71
|
+
reason: `ownership tag scan exited ${exitCode}`
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const lines = (await proc.stdout.text()).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
75
|
+
if (lines.length > MAX_OWNERSHIP_TAGS) {
|
|
76
|
+
return {
|
|
77
|
+
status: "uncertain",
|
|
78
|
+
reason: `ownership tag scan exceeded the ${MAX_OWNERSHIP_TAGS}-tag safety bound`
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const owners = [];
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
const [tag, ref, ...extra] = line.split("\x00");
|
|
84
|
+
const segments = tag?.split("/") ?? [];
|
|
85
|
+
const sessionId = decodeTagPart(segments[1] ?? "");
|
|
86
|
+
const laneId = decodeTagPart(segments[2] ?? "");
|
|
87
|
+
const callDigest = segments[3];
|
|
88
|
+
if (extra.length > 0 || segments.length !== 4 || segments[0] !== "swarm-preserved-owner" || !sessionId || !laneId || !callDigest || !/^[0-9a-f]{12}$/.test(callDigest) || !ref || !/^[0-9a-f]{40,64}$/i.test(ref)) {
|
|
89
|
+
return {
|
|
90
|
+
status: "uncertain",
|
|
91
|
+
reason: `malformed background ownership tag entry "${tag ?? ""}"`
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
owners.push({ sessionId, laneId, callDigest, ref });
|
|
95
|
+
}
|
|
96
|
+
return { status: "ok", owners };
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return {
|
|
99
|
+
status: "uncertain",
|
|
100
|
+
reason: `ownership tag scan failed: ${error instanceof Error ? error.message : String(error)}`
|
|
101
|
+
};
|
|
102
|
+
} finally {
|
|
103
|
+
try {
|
|
104
|
+
proc.kill();
|
|
105
|
+
} catch {}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/hooks/delegation-gate/worktree-collision-ownership.ts
|
|
110
|
+
function normalizedPath(value) {
|
|
111
|
+
const resolved = path2.normalize(path2.resolve(value));
|
|
112
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
113
|
+
}
|
|
114
|
+
function descriptorCoordinateMatch(descriptor, identity) {
|
|
115
|
+
const branchMatches = descriptor.branchName === identity.branchName;
|
|
116
|
+
const pathMatches = normalizedPath(descriptor.worktreePath) === normalizedPath(identity.worktreePath);
|
|
117
|
+
if (!branchMatches && !pathMatches)
|
|
118
|
+
return "none";
|
|
119
|
+
if (!branchMatches || !pathMatches || descriptor.parentSessionId !== identity.parentSessionId || descriptor.taskId !== identity.taskId) {
|
|
120
|
+
return "uncertain";
|
|
121
|
+
}
|
|
122
|
+
return "exact";
|
|
123
|
+
}
|
|
124
|
+
function isProvenSettled(record) {
|
|
125
|
+
const settlement = record.coderSettlement;
|
|
126
|
+
return settlement?.state === "settled" && settlement.outcome?.kind === "standard-worktree" && (settlement.outcome.result === "merged" || settlement.outcome.result === "unchanged");
|
|
127
|
+
}
|
|
128
|
+
function classifyRecord(record, identity, ownerKind) {
|
|
129
|
+
if (!record.worktree)
|
|
130
|
+
return null;
|
|
131
|
+
const coordinates = descriptorCoordinateMatch(record.worktree, identity);
|
|
132
|
+
if (coordinates === "none")
|
|
133
|
+
return null;
|
|
134
|
+
if (coordinates === "uncertain") {
|
|
135
|
+
return {
|
|
136
|
+
status: "uncertain",
|
|
137
|
+
reason: `${ownerKind} background owner has conflicting identity for branch "${identity.branchName}"`
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (isProvenSettled(record))
|
|
141
|
+
return null;
|
|
142
|
+
const lifecycle = record.coderSettlement?.state === "preserved" ? "preserved" : "active";
|
|
143
|
+
return {
|
|
144
|
+
status: "protected",
|
|
145
|
+
ownerKind,
|
|
146
|
+
lifecycle,
|
|
147
|
+
reason: `${ownerKind} background owner ${record.correlationId} is ${record.status}` + (record.coderSettlement ? ` with coder settlement ${record.coderSettlement.state}` : " without a settled workspace outcome")
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async function inspectStandardWorktreeCollisionOwnership(identity) {
|
|
151
|
+
const provisioning = scanWorktreeProvisioningOwnersForRecovery(identity.directory);
|
|
152
|
+
if (provisioning.status === "uncertain")
|
|
153
|
+
return provisioning;
|
|
154
|
+
for (const owner of provisioning.owners) {
|
|
155
|
+
const sameSession = owner.parentSessionId === identity.parentSessionId || owner.worktreeSessionId === identity.parentSessionId;
|
|
156
|
+
if (!sameSession)
|
|
157
|
+
continue;
|
|
158
|
+
if (owner.schemaVersion === 1 || owner.taskId === identity.taskId) {
|
|
159
|
+
return {
|
|
160
|
+
status: "protected",
|
|
161
|
+
ownerKind: "provisioning",
|
|
162
|
+
lifecycle: "active",
|
|
163
|
+
reason: owner.schemaVersion === 1 ? `legacy provisioning owner ${owner.callID} cannot disprove ownership of this same-session lane` : `provisioning owner ${owner.callID} is active for task ${identity.taskId}`
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const fallback = await scanDelegationFallbacksForRecovery(identity.directory);
|
|
168
|
+
if (fallback.status === "uncertain")
|
|
169
|
+
return fallback;
|
|
170
|
+
for (const artifact of fallback.owners) {
|
|
171
|
+
const classified = classifyRecord(artifact.record, identity, "fallback");
|
|
172
|
+
if (classified)
|
|
173
|
+
return classified;
|
|
174
|
+
}
|
|
175
|
+
const primary = scanDelegationsForRecovery(identity.directory);
|
|
176
|
+
if (primary.status === "uncertain")
|
|
177
|
+
return primary;
|
|
178
|
+
for (const record of primary.owners) {
|
|
179
|
+
const classified = classifyRecord(record, identity, "primary");
|
|
180
|
+
if (classified)
|
|
181
|
+
return classified;
|
|
182
|
+
}
|
|
183
|
+
const tags = await scanBackgroundWorktreeOwnershipTagsForRecovery(identity.directory);
|
|
184
|
+
if (tags.status === "uncertain")
|
|
185
|
+
return tags;
|
|
186
|
+
for (const owner of tags.owners) {
|
|
187
|
+
if (owner.sessionId === identity.parentSessionId && owner.laneId === identity.taskId) {
|
|
188
|
+
return {
|
|
189
|
+
status: "protected",
|
|
190
|
+
ownerKind: "ownership-tag",
|
|
191
|
+
lifecycle: "preserved",
|
|
192
|
+
reason: `durable ownership tag ${owner.callDigest} preserves this same-session lane`
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const mergeStatus = scanWorktreeMergeFailuresForRecovery(identity.directory);
|
|
197
|
+
if (mergeStatus.status === "uncertain")
|
|
198
|
+
return mergeStatus;
|
|
199
|
+
for (const [, failure] of mergeStatus.failures) {
|
|
200
|
+
const branchMatches = failure.branch === identity.branchName;
|
|
201
|
+
const pathMatches = typeof failure.worktreePath === "string" && normalizedPath(failure.worktreePath) === normalizedPath(identity.worktreePath);
|
|
202
|
+
if (!branchMatches && !pathMatches)
|
|
203
|
+
continue;
|
|
204
|
+
if (!branchMatches || !pathMatches) {
|
|
205
|
+
return {
|
|
206
|
+
status: "uncertain",
|
|
207
|
+
reason: `merge-status owner has conflicting identity for branch "${identity.branchName}"`
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
status: "protected",
|
|
212
|
+
ownerKind: "merge-status",
|
|
213
|
+
lifecycle: "preserved",
|
|
214
|
+
reason: `merge-status records ${failure.outcome} at stage ${failure.stage}`
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return { status: "unowned" };
|
|
218
|
+
}
|
|
219
|
+
export {
|
|
220
|
+
inspectStandardWorktreeCollisionOwnership
|
|
221
|
+
};
|
|
@@ -278,9 +278,9 @@ export declare const PromotionDecisionV1Schema: z.ZodObject<{
|
|
|
278
278
|
runId: z.ZodString;
|
|
279
279
|
decidedAt: z.ZodISODateTime;
|
|
280
280
|
status: z.ZodEnum<{
|
|
281
|
-
reject: "reject";
|
|
282
281
|
inconclusive: "inconclusive";
|
|
283
282
|
accept: "accept";
|
|
283
|
+
reject: "reject";
|
|
284
284
|
}>;
|
|
285
285
|
reasons: z.ZodArray<z.ZodString>;
|
|
286
286
|
baseline: z.ZodObject<{
|
|
@@ -31,7 +31,7 @@ export declare const evaluationV1: ((options: EvaluateCandidateV1Options) => Pro
|
|
|
31
31
|
decisionId: string;
|
|
32
32
|
runId: string;
|
|
33
33
|
decidedAt: string;
|
|
34
|
-
status: "
|
|
34
|
+
status: "inconclusive" | "accept" | "reject";
|
|
35
35
|
reasons: string[];
|
|
36
36
|
baseline: {
|
|
37
37
|
baselineRunId: string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface StandardWorktreeCollisionIdentity {
|
|
2
|
+
directory: string;
|
|
3
|
+
parentSessionId: string;
|
|
4
|
+
taskId: string;
|
|
5
|
+
branchName: string;
|
|
6
|
+
worktreePath: string;
|
|
7
|
+
}
|
|
8
|
+
export type StandardWorktreeCollisionOwnership = {
|
|
9
|
+
status: 'unowned';
|
|
10
|
+
} | {
|
|
11
|
+
status: 'protected';
|
|
12
|
+
ownerKind: 'primary' | 'fallback' | 'provisioning' | 'ownership-tag' | 'merge-status';
|
|
13
|
+
lifecycle: 'active' | 'preserved';
|
|
14
|
+
reason: string;
|
|
15
|
+
} | {
|
|
16
|
+
status: 'uncertain';
|
|
17
|
+
reason: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Strict restart-time ownership classification for one exact same-session
|
|
21
|
+
* collision. Scanner order is part of the safety contract:
|
|
22
|
+
*
|
|
23
|
+
* provisioning -> fallback -> primary -> ownership tag -> merge status
|
|
24
|
+
*
|
|
25
|
+
* Fallback precedes primary because promotion publishes primary before removing
|
|
26
|
+
* fallback; this ordering cannot observe ownership absent from both stores.
|
|
27
|
+
*/
|
|
28
|
+
export declare function inspectStandardWorktreeCollisionOwnership(identity: StandardWorktreeCollisionIdentity): Promise<StandardWorktreeCollisionOwnership>;
|
|
@@ -9,9 +9,12 @@
|
|
|
9
9
|
* The _internals seam allows test injection of worktree operations.
|
|
10
10
|
*/
|
|
11
11
|
import type { PluginConfig, WorktreeIsolationConfig } from '../../config';
|
|
12
|
+
import { tryAcquireLock } from '../../parallel/file-locks';
|
|
12
13
|
import { bunSpawn } from '../../utils/bun-compat';
|
|
13
14
|
import type { WorktreeHandle } from '../../worktree';
|
|
14
15
|
import { attemptMergeBackFromDirty, cleanupOrphanedBranches, postMergeCleanup, provisionWorktree, removeWorktree, startupOrphanRecovery } from '../../worktree';
|
|
16
|
+
import type { DirtyMergeOptions, MergeOperationProvenance } from '../../worktree/merge';
|
|
17
|
+
import { recordWorktreeProvisioningOwner, removeWorktreeProvisioningOwner } from './worktree-provisioning-owner';
|
|
15
18
|
/**
|
|
16
19
|
* FR-201: Per-lane runtime profile injected into spawned child processes.
|
|
17
20
|
*
|
|
@@ -62,6 +65,37 @@ export interface StandardWorktreeDispatch {
|
|
|
62
65
|
/** Configured worktree-dir override, so cleanup trusts the same base. */
|
|
63
66
|
worktree_dir?: string;
|
|
64
67
|
}
|
|
68
|
+
export interface StandardWorktreeSettlementOptions extends Pick<DirtyMergeOptions, 'operationId' | 'resume' | 'onBeforeMerge'> {
|
|
69
|
+
/**
|
|
70
|
+
* Awaited after Git reports success (including reconciled success) and before
|
|
71
|
+
* destructive worktree/branch cleanup. A rejection preserves the lane.
|
|
72
|
+
*/
|
|
73
|
+
onMerged?: (result: StandardWorktreeMergedSettlement) => Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
export interface StandardWorktreeMergedSettlement {
|
|
76
|
+
outcome: 'merged';
|
|
77
|
+
strategy: string;
|
|
78
|
+
autoCommitted: boolean;
|
|
79
|
+
cleaned: boolean;
|
|
80
|
+
reconciled: boolean;
|
|
81
|
+
provenance?: MergeOperationProvenance;
|
|
82
|
+
}
|
|
83
|
+
export interface StandardWorktreePartialSettlement {
|
|
84
|
+
outcome: 'partial';
|
|
85
|
+
stage: string;
|
|
86
|
+
message: string;
|
|
87
|
+
autoCommitted: boolean;
|
|
88
|
+
cleaned: boolean;
|
|
89
|
+
conflictFiles?: string[];
|
|
90
|
+
provenance?: MergeOperationProvenance;
|
|
91
|
+
}
|
|
92
|
+
export interface StandardWorktreeFailedSettlement {
|
|
93
|
+
outcome: 'failed';
|
|
94
|
+
stage: string;
|
|
95
|
+
message: string;
|
|
96
|
+
provenance?: MergeOperationProvenance;
|
|
97
|
+
}
|
|
98
|
+
export type StandardWorktreeSettlementResult = StandardWorktreeMergedSettlement | StandardWorktreePartialSettlement | StandardWorktreeFailedSettlement;
|
|
65
99
|
export interface AwaitingMergeRecord {
|
|
66
100
|
callID: string;
|
|
67
101
|
parentSessionID: string;
|
|
@@ -130,6 +164,7 @@ export declare function preProvisionCollisionCheck(taskId: string, directory: st
|
|
|
130
164
|
existingBranch?: string;
|
|
131
165
|
ownerSessionId?: string;
|
|
132
166
|
worktreePath?: string;
|
|
167
|
+
uncertainty?: string;
|
|
133
168
|
}>;
|
|
134
169
|
/**
|
|
135
170
|
* FR-001b SC-005: Returns true when `branchName`'s embedded session ID matches
|
|
@@ -140,6 +175,13 @@ export declare function preProvisionCollisionCheck(taskId: string, directory: st
|
|
|
140
175
|
* patterns.
|
|
141
176
|
*/
|
|
142
177
|
export declare function isLaneOwnedByCurrentSession(branchName: string, expectedSessionId: string): boolean;
|
|
178
|
+
declare function inspectStandardWorktreeCollisionOwnership(identity: {
|
|
179
|
+
directory: string;
|
|
180
|
+
parentSessionId: string;
|
|
181
|
+
taskId: string;
|
|
182
|
+
branchName: string;
|
|
183
|
+
worktreePath: string;
|
|
184
|
+
}): Promise<import("./worktree-collision-ownership").StandardWorktreeCollisionOwnership>;
|
|
143
185
|
export declare function precreateStandardWorktreeSession(args: {
|
|
144
186
|
config: PluginConfig;
|
|
145
187
|
directory: string;
|
|
@@ -155,6 +197,19 @@ export declare function precreateStandardWorktreeSession(args: {
|
|
|
155
197
|
files: string[];
|
|
156
198
|
};
|
|
157
199
|
}): Promise<void>;
|
|
200
|
+
/**
|
|
201
|
+
* Create a durable ownership tag without touching an in-flight background
|
|
202
|
+
* coder's index or working tree. This is used only when both correlation
|
|
203
|
+
* stores fail after launch: committing dirty files at that point could race
|
|
204
|
+
* the still-running child, while a tag plus the durable merge-status record
|
|
205
|
+
* makes restart cleanup fail closed.
|
|
206
|
+
*/
|
|
207
|
+
export declare function preserveBackgroundWorktreeOwnershipForCallId(callID: string): Promise<{
|
|
208
|
+
outcome: 'preserved' | 'preserve-failed' | 'not-found';
|
|
209
|
+
tag?: string;
|
|
210
|
+
ref?: string;
|
|
211
|
+
error?: string;
|
|
212
|
+
}>;
|
|
158
213
|
/**
|
|
159
214
|
* FR-001c: Preserves dirty work in a worktree before cleanup on denial or cancellation.
|
|
160
215
|
*
|
|
@@ -223,7 +278,7 @@ export declare function finishStandardWorktreeDispatch(directory: string, dispat
|
|
|
223
278
|
* derived from dispatch.callID (for backward compatibility with test callers
|
|
224
279
|
* that pass StandardWorktreeDispatch objects directly).
|
|
225
280
|
*/
|
|
226
|
-
callID?: string): Promise<
|
|
281
|
+
callID?: string, settlement?: StandardWorktreeSettlementOptions): Promise<StandardWorktreeSettlementResult>;
|
|
227
282
|
/**
|
|
228
283
|
* FR-201: Read and parse a lane runtime profile from disk.
|
|
229
284
|
*
|
|
@@ -264,6 +319,11 @@ export declare const _internals: {
|
|
|
264
319
|
cleanupStandardWorktreeForCallId: typeof cleanupStandardWorktreeForCallId;
|
|
265
320
|
/** FR-001c: preserves dirty worktree state before cleanup on denial/cancellation. */
|
|
266
321
|
preserveDirtyWorktreeForCallId: typeof preserveDirtyWorktreeForCallId;
|
|
322
|
+
/** Background launch durability fallback: tag ownership without racing the child. */
|
|
323
|
+
preserveBackgroundWorktreeOwnershipForCallId: typeof preserveBackgroundWorktreeOwnershipForCallId;
|
|
324
|
+
tryAcquireWorktreeLifecycleLock: typeof tryAcquireLock;
|
|
325
|
+
recordWorktreeProvisioningOwner: typeof recordWorktreeProvisioningOwner;
|
|
326
|
+
removeWorktreeProvisioningOwner: typeof removeWorktreeProvisioningOwner;
|
|
267
327
|
/** FR-001b SC-004: path-based dirty worktree preservation for stale-lane fallback. */
|
|
268
328
|
preserveDirtyWorktreeAtPath: typeof preserveDirtyWorktreeAtPath;
|
|
269
329
|
/** FR-001a: abort entry point for in-flight dispatches. */
|
|
@@ -271,6 +331,8 @@ export declare const _internals: {
|
|
|
271
331
|
standardWorktreeByCallID: Map<string, StandardWorktreeDispatch>;
|
|
272
332
|
/** FR-001b SC-004: pre-provision collision check. */
|
|
273
333
|
preProvisionCollisionCheck: typeof preProvisionCollisionCheck;
|
|
334
|
+
/** Restart-safe durable owner classification for a colliding lane. */
|
|
335
|
+
inspectStandardWorktreeCollisionOwnership: typeof inspectStandardWorktreeCollisionOwnership;
|
|
274
336
|
/** FR-001b SC-005: lane ownership validation. */
|
|
275
337
|
isLaneOwnedByCurrentSession: typeof isLaneOwnedByCurrentSession;
|
|
276
338
|
/** FR-001b: bunSpawn — exposed for test injection so collision check can be mocked. */
|
|
@@ -43,6 +43,13 @@ export interface WorktreeMergeFailure {
|
|
|
43
43
|
/** Timestamp (Date.now()) when merge-back completed (success or failure). */
|
|
44
44
|
completedAt?: number;
|
|
45
45
|
}
|
|
46
|
+
export type WorktreeMergeRecoveryScanResult = {
|
|
47
|
+
status: 'ok';
|
|
48
|
+
failures: Array<[string, WorktreeMergeFailure]>;
|
|
49
|
+
} | {
|
|
50
|
+
status: 'uncertain';
|
|
51
|
+
reason: string;
|
|
52
|
+
};
|
|
46
53
|
declare function getDurableStatusPath(projectDir?: string): string;
|
|
47
54
|
/**
|
|
48
55
|
* Load the durable file into the in-memory map. The clear+repopulate is
|
|
@@ -52,6 +59,13 @@ declare function getDurableStatusPath(projectDir?: string): string;
|
|
|
52
59
|
*/
|
|
53
60
|
declare function loadDurableStatus(statusPath: string): void;
|
|
54
61
|
declare function saveDurableStatus(statusPath: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Strict recovery-only read of durable merge ownership. The normal registry
|
|
64
|
+
* stays fail-open for plugin loadability, while destructive orphan cleanup
|
|
65
|
+
* receives an explicit uncertainty result for read, parse, schema, or cap
|
|
66
|
+
* failures.
|
|
67
|
+
*/
|
|
68
|
+
export declare function scanWorktreeMergeFailuresForRecovery(projectDir: string): WorktreeMergeRecoveryScanResult;
|
|
55
69
|
/**
|
|
56
70
|
* Initialize the durable status path for this project and load existing
|
|
57
71
|
* status from disk. Called from `createDelegationGateHook` before any coder
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface BackgroundWorktreeOwnershipTag {
|
|
2
|
+
sessionId: string;
|
|
3
|
+
laneId: string;
|
|
4
|
+
callDigest: string;
|
|
5
|
+
ref: string;
|
|
6
|
+
}
|
|
7
|
+
export type BackgroundWorktreeOwnershipTagScan = {
|
|
8
|
+
status: 'ok';
|
|
9
|
+
owners: BackgroundWorktreeOwnershipTag[];
|
|
10
|
+
} | {
|
|
11
|
+
status: 'uncertain';
|
|
12
|
+
reason: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Strict, bounded ownership-tag scan shared by init recovery and restart
|
|
16
|
+
* collision classification. Any malformed or unreadable entry is uncertainty:
|
|
17
|
+
* destructive cleanup must never infer absence from incomplete ownership data.
|
|
18
|
+
*/
|
|
19
|
+
export declare function scanBackgroundWorktreeOwnershipTagsForRecovery(directory: string): Promise<BackgroundWorktreeOwnershipTagScan>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const WORKTREE_LIFECYCLE_LOCK_FILE = ".swarm/locks/init-orphan-recovery.lock";
|
|
2
|
+
export interface WorktreeProvisioningOwner {
|
|
3
|
+
schemaVersion: 1 | 2;
|
|
4
|
+
callID: string;
|
|
5
|
+
parentSessionId: string;
|
|
6
|
+
worktreeSessionId: string;
|
|
7
|
+
/** Added in v2 so restart collision checks can identify the exact lane. */
|
|
8
|
+
taskId?: string;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
}
|
|
11
|
+
export type WorktreeProvisioningOwnerScan = {
|
|
12
|
+
status: 'ok';
|
|
13
|
+
owners: WorktreeProvisioningOwner[];
|
|
14
|
+
} | {
|
|
15
|
+
status: 'uncertain';
|
|
16
|
+
reason: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Publish a provisional owner before a standard worktree can be created.
|
|
20
|
+
* Callers hold the shared orphan-recovery lifecycle lock during this atomic
|
|
21
|
+
* write, closing the gap between recovery's owner snapshot and provisioning.
|
|
22
|
+
*/
|
|
23
|
+
export declare function recordWorktreeProvisioningOwner(directory: string, input: Omit<WorktreeProvisioningOwner, 'schemaVersion' | 'createdAt'>): WorktreeProvisioningOwner;
|
|
24
|
+
/** Remove a provisional owner only after durable ownership or cleanup exists. */
|
|
25
|
+
export declare function removeWorktreeProvisioningOwner(directory: string, callID: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Strict recovery scan. Anything except ENOENT is uncertainty because omitting
|
|
28
|
+
* one marker could allow destructive cleanup to race a live worktree.
|
|
29
|
+
*/
|
|
30
|
+
export declare function scanWorktreeProvisioningOwnersForRecovery(directory: string): WorktreeProvisioningOwnerScan;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Warns the architect when coder delegations are too large or batched.
|
|
5
5
|
* Uses experimental.chat.messages.transform to provide non-blocking guidance.
|
|
6
6
|
*/
|
|
7
|
+
import type { BackgroundDelegationRecord } from '../background/pending-delegations.js';
|
|
7
8
|
import type { PluginConfig } from '../config';
|
|
8
9
|
import { loadPlanJsonOnly } from '../plan/manager';
|
|
9
10
|
import type { AgentSessionState } from '../state';
|
|
@@ -47,11 +48,12 @@ export declare function clearPendingCoderScope(): void;
|
|
|
47
48
|
* `Task` with `background=true` (gated upstream by
|
|
48
49
|
* `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true`) returns a "running" placeholder
|
|
49
50
|
* immediately and completes later via synthetic parent injection. The delegation gate
|
|
50
|
-
* treats a `Task` result as completion, so
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
51
|
+
* treats a foreground `Task` result as completion, so the running placeholder must be
|
|
52
|
+
* tracked without terminal side effects. When the opt-in is enabled, trusted deferred
|
|
53
|
+
* completion ingestion performs exact parent correlation, workspace settlement, and
|
|
54
|
+
* role-appropriate state/evidence updates. With the opt-in disabled (the default while
|
|
55
|
+
* upstream remains experimental), background swarm delegations are fail-closed-blocked.
|
|
56
|
+
* We do NOT silently coerce `background` to false.
|
|
55
57
|
*/
|
|
56
58
|
export declare const SWARM_BACKGROUND_TASK_BLOCKED_MESSAGE: string;
|
|
57
59
|
/**
|
|
@@ -324,12 +326,18 @@ export declare const _internals: {
|
|
|
324
326
|
parsePerTaskVerdicts: typeof parsePerTaskVerdicts;
|
|
325
327
|
buildParallelExecutionGuidance: typeof buildParallelExecutionGuidance;
|
|
326
328
|
loadPlanJsonOnly: typeof loadPlanJsonOnly;
|
|
329
|
+
recordPendingDelegationForBackground: typeof import("../background/pending-delegations.js").recordPendingDelegation;
|
|
330
|
+
writeDelegationFallbackForBackground: typeof import("../background/pending-delegations.js").writeDelegationFallback;
|
|
331
|
+
reserveBackgroundCoderSlotForDispatch: typeof import("../background/pending-delegations.js").reserveBackgroundCoderSlot;
|
|
332
|
+
bindBackgroundCoderReservationForDispatch: typeof import("../background/pending-delegations.js").bindBackgroundCoderReservation;
|
|
333
|
+
releaseBackgroundCoderReservationForDispatch: typeof import("../background/pending-delegations.js").releaseBackgroundCoderReservation;
|
|
327
334
|
resetStandardWorktreeIsolationState: typeof resetStandardWorktreeIsolationState;
|
|
328
335
|
PLAN_CRITIC_TASK_SIGNALS: readonly ["critic-gate", "plan critic", "review plan", "review the plan", "plan.md", "approve the plan", "plan approval"];
|
|
329
336
|
provisionWorktree: typeof _wtiInternals.provisionWorktree;
|
|
330
337
|
removeWorktree: typeof _wtiInternals.removeWorktree;
|
|
331
338
|
attemptMergeBackFromDirty: typeof _wtiInternals.attemptMergeBackFromDirty;
|
|
332
339
|
postMergeCleanup: typeof _wtiInternals.postMergeCleanup;
|
|
340
|
+
preserveBackgroundWorktreeOwnershipForCallId: typeof _wtiInternals.preserveBackgroundWorktreeOwnershipForCallId;
|
|
333
341
|
};
|
|
334
342
|
/**
|
|
335
343
|
* Creates the experimental.chat.messages.transform hook for delegation gating.
|
|
@@ -358,4 +366,5 @@ export declare function createDelegationGateHook(config: PluginConfig, directory
|
|
|
358
366
|
childSessionID: string;
|
|
359
367
|
}) => Promise<void>;
|
|
360
368
|
sessionEnded: (sessionID: string, includeOwnedChildren?: boolean) => void;
|
|
369
|
+
backgroundCompletionClaimed: (record: BackgroundDelegationRecord) => void;
|
|
361
370
|
};
|
|
@@ -39,6 +39,16 @@ type ChatMessageLike = {
|
|
|
39
39
|
* never contained, making the guard permanently inert.
|
|
40
40
|
*/
|
|
41
41
|
export declare const RUNAWAY_OUTPUT_ADVISORY_MARKER = "Model is generating analysis without taking action";
|
|
42
|
+
/**
|
|
43
|
+
* Bound a set of advisory strings to a total byte budget, keeping the latest
|
|
44
|
+
* (dropping oldest from the front). Returns the kept entries and whether any
|
|
45
|
+
* were dropped. Never drops below a single entry (a single oversized advisory
|
|
46
|
+
* is kept verbatim rather than rendered empty).
|
|
47
|
+
*/
|
|
48
|
+
export declare function boundAdvisoryBytes(advisories: string[], maxBytes: number): {
|
|
49
|
+
kept: string[];
|
|
50
|
+
truncated: boolean;
|
|
51
|
+
};
|
|
42
52
|
export declare function getMostRecentAssistantText(messages: ChatMessageLike[]): string;
|
|
43
53
|
export declare function isTransientProviderFailureText(text: string): boolean;
|
|
44
54
|
export declare function getProviderFailureFingerprint(text: string): string;
|
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
* input args by callID, used by guardrails for delegation detection
|
|
6
6
|
* and exposed via safe accessor helpers.
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* Max stored callID->args entries before the oldest-inserted is evicted
|
|
10
|
+
* (invariant 8). Larger than the per-session cap because callIDs are per
|
|
11
|
+
* tool-call, not per-session.
|
|
12
|
+
*/
|
|
13
|
+
export declare const MAX_STORED_INPUT_ARGS = 2000;
|
|
8
14
|
/**
|
|
9
15
|
* Retrieves stored input args for a given callID.
|
|
10
16
|
* Used by other hooks (e.g., delegation-gate) to access tool input args.
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { createCompactionCustomizerHook } from './compaction-customizer';
|
|
|
4
4
|
export { createContextBudgetHandler } from './context-budget';
|
|
5
5
|
export { createContextCapsuleInjectHook } from './context-capsule-inject';
|
|
6
6
|
export { createCuratorLLMDelegate } from './curator-llm-factory';
|
|
7
|
-
export { createDelegationGateHook } from './delegation-gate';
|
|
7
|
+
export { createDelegationGateHook, outputLooksLikeBackgroundRunning, } from './delegation-gate';
|
|
8
8
|
export { createDelegationSanitizerHook } from './delegation-sanitizer';
|
|
9
9
|
export { createDelegationTrackerHook } from './delegation-tracker';
|
|
10
10
|
export { extractCurrentPhase, extractCurrentPhaseFromPlan, extractCurrentTask, extractCurrentTaskFromPlan, extractDecisions, extractIncompleteTasks, extractIncompleteTasksFromPlan, extractPatterns, } from './extractors';
|
|
@@ -15,8 +15,19 @@
|
|
|
15
15
|
* reads the advisory file at session-start and pushes messages to pendingAdvisoryMessages.
|
|
16
16
|
*/
|
|
17
17
|
import * as fs from 'node:fs';
|
|
18
|
+
import { scanDelegationFallbacksForRecovery, scanDelegationsForRecovery } from '../background/pending-delegations.js';
|
|
18
19
|
import { isLocked, listActiveLocks, tryAcquireLock } from '../parallel/file-locks';
|
|
19
20
|
import { removeWorktree } from '../worktree/core';
|
|
21
|
+
import { scanWorktreeMergeFailuresForRecovery } from './delegation-gate/worktree-merge-status';
|
|
22
|
+
import { scanWorktreeProvisioningOwnersForRecovery } from './delegation-gate/worktree-provisioning-owner';
|
|
23
|
+
type OwnershipTagSessionScan = {
|
|
24
|
+
status: 'ok';
|
|
25
|
+
sessionIds: string[];
|
|
26
|
+
} | {
|
|
27
|
+
status: 'uncertain';
|
|
28
|
+
reason: string;
|
|
29
|
+
};
|
|
30
|
+
declare function listOwnershipTagSessionIds(directory: string): Promise<OwnershipTagSessionScan>;
|
|
20
31
|
/**
|
|
21
32
|
* Lock file path for the init orphan recovery advisory lock.
|
|
22
33
|
* advisory-only — signals that another process may be actively using the repo.
|
|
@@ -33,6 +44,11 @@ export declare const _internals: {
|
|
|
33
44
|
isLocked: typeof isLocked;
|
|
34
45
|
listActiveLocks: typeof listActiveLocks;
|
|
35
46
|
tryAcquireLock: typeof tryAcquireLock;
|
|
47
|
+
listOwnershipTagSessionIds: typeof listOwnershipTagSessionIds;
|
|
48
|
+
scanDelegationsForRecovery: typeof scanDelegationsForRecovery;
|
|
49
|
+
scanDelegationFallbacksForRecovery: typeof scanDelegationFallbacksForRecovery;
|
|
50
|
+
scanWorktreeMergeFailuresForRecovery: typeof scanWorktreeMergeFailuresForRecovery;
|
|
51
|
+
scanWorktreeProvisioningOwnersForRecovery: typeof scanWorktreeProvisioningOwnersForRecovery;
|
|
36
52
|
};
|
|
37
53
|
export interface InitOrphanRecoveryResult {
|
|
38
54
|
attempted: boolean;
|
|
@@ -63,3 +79,4 @@ export interface InitOrphanRecoveryResult {
|
|
|
63
79
|
* @returns Result object describing what was attempted and any warnings/diagnostics
|
|
64
80
|
*/
|
|
65
81
|
export declare function runInitOrphanRecovery(directory: string): Promise<InitOrphanRecoveryResult>;
|
|
82
|
+
export {};
|