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.
Files changed (87) hide show
  1. package/.opencode/skills/codebase-review-swarm/SKILL.md +18 -2
  2. package/.opencode/skills/codebase-review-swarm/references/review-protocol-v8.2.md +18 -2
  3. package/.opencode/skills/swarm-implement/SKILL.md +17 -2
  4. package/dist/background/completion-observer.d.ts +17 -7
  5. package/dist/background/pending-delegations.d.ts +305 -30
  6. package/dist/background/pr-event-subscribers.d.ts +1 -1
  7. package/dist/cli/{config-doctor-8nbdwnn6.js → config-doctor-4avzz3rh.js} +3 -3
  8. package/dist/cli/{core-egwpry19.js → core-d6yd2jzv.js} +3 -3
  9. package/dist/cli/{curation-policy-gae3xm5d.js → curation-policy-8vkzjtwb.js} +7 -7
  10. package/dist/cli/{curator-e88bm4mg.js → curator-3vsvhwav.js} +31 -28
  11. package/dist/cli/{curator-drift-ea0p8nh1.js → curator-drift-9q6f85bm.js} +7 -7
  12. package/dist/cli/{curator-llm-factory-1a7esr69.js → curator-llm-factory-2qk8q4y9.js} +31 -28
  13. package/dist/cli/{dispatch-3mhgqj2x.js → dispatch-gxbeb2ps.js} +2 -2
  14. package/dist/cli/{evidence-summary-service-nhsmsap8.js → evidence-summary-service-mrzsjpg4.js} +13 -12
  15. package/dist/cli/{gate-evidence-cy2te3x8.js → gate-evidence-b5v1xvxp.js} +4 -4
  16. package/dist/cli/guardrail-explain-tw521x47.js +57 -0
  17. package/dist/cli/{guardrail-log-fjgyx76r.js → guardrail-log-e3jrwdw8.js} +5 -5
  18. package/dist/cli/{hive-promoter-2b04e0wh.js → hive-promoter-sdy5qjzn.js} +31 -28
  19. package/dist/cli/{index-66zght9d.js → index-0541h76w.js} +2 -2
  20. package/dist/cli/{index-g4mnf3p5.js → index-1j3682j8.js} +5 -5
  21. package/dist/cli/{index-mmvaf4t6.js → index-1v5mby7t.js} +25 -3
  22. package/dist/cli/{index-j5pnah1w.js → index-2j8f71c3.js} +12 -12
  23. package/dist/cli/{index-17zjp67y.js → index-2t1n9k7b.js} +4 -4
  24. package/dist/cli/{index-mwqfyjja.js → index-358f9h8p.js} +48406 -48697
  25. package/dist/cli/{index-hfpw5m0e.js → index-463k8mfr.js} +3 -3
  26. package/dist/cli/{index-2tn5h2zp.js → index-4p02dp92.js} +6 -6
  27. package/dist/cli/{index-7ftthvqt.js → index-6b69mp0r.js} +8 -8
  28. package/dist/cli/{index-bz08438z.js → index-8t9xpn1a.js} +7 -7
  29. package/dist/cli/{index-sp7tm8t7.js → index-9z1q181f.js} +1 -1
  30. package/dist/cli/index-b4z7s917.js +80 -0
  31. package/dist/cli/index-b8zgtz81.js +123 -0
  32. package/dist/cli/{index-tj2mkfb3.js → index-bq2848a3.js} +1 -1
  33. package/dist/cli/{index-kc1m6vpr.js → index-c0e3vhv9.js} +2 -2
  34. package/dist/cli/{index-q27bajqb.js → index-cfkvc5q9.js} +3 -3
  35. package/dist/cli/{index-7mkpsw6g.js → index-dd7ttx8x.js} +1 -1
  36. package/dist/cli/{index-r260ybay.js → index-k742ecbm.js} +178 -222
  37. package/dist/cli/{index-cpest8sa.js → index-kwm7hz18.js} +5 -5
  38. package/dist/cli/{index-0vd91yb8.js → index-m8ajf6wb.js} +3 -3
  39. package/dist/cli/{index-mhcge2ry.js → index-mz6sqtm9.js} +33 -30
  40. package/dist/cli/{index-74tptrss.js → index-p8hezdtz.js} +1 -1
  41. package/dist/cli/{index-sw82jvf2.js → index-pz49rx4y.js} +2 -2
  42. package/dist/cli/{index-3x761jqv.js → index-wk1h5ec2.js} +1 -1
  43. package/dist/cli/{index-wzb4dv5s.js → index-y2esw64s.js} +4 -4
  44. package/dist/cli/index-y3v1404y.js +1625 -0
  45. package/dist/cli/{index-wz36r0fk.js → index-zjw32rv9.js} +2 -2
  46. package/dist/cli/index.js +31 -28
  47. package/dist/cli/{knowledge-escalator-hzkw1ban.js → knowledge-escalator-v09w0jmg.js} +8 -8
  48. package/dist/cli/{knowledge-events-pbycdhqd.js → knowledge-events-qp19r2he.js} +6 -6
  49. package/dist/cli/{knowledge-link-4yd2z8ev.js → knowledge-link-nc4y2zkq.js} +5 -5
  50. package/dist/cli/{knowledge-store-45jawb2p.js → knowledge-store-n5pvv0s3.js} +6 -6
  51. package/dist/cli/{knowledge-validator-ge576f8n.js → knowledge-validator-qg31ycbb.js} +9 -9
  52. package/dist/cli/pending-delegations-v9e40hfk.js +96 -0
  53. package/dist/cli/{pr-subscriptions-zqbmctwz.js → pr-subscriptions-ztq40hrt.js} +7 -7
  54. package/dist/cli/{scan-cursor-kvw872be.js → scan-cursor-wtzfvkh2.js} +7 -7
  55. package/dist/cli/{schema-a2zm2m3r.js → schema-erk5bz43.js} +2 -2
  56. package/dist/cli/{scope-persistence-5qypzxxm.js → scope-persistence-hz3zmmsv.js} +5 -5
  57. package/dist/cli/{skill-generator-ch79hvxh.js → skill-generator-nxs65tq3.js} +10 -10
  58. package/dist/cli/worktree-collision-ownership-c4bdnjtx.js +221 -0
  59. package/dist/evaluation/contracts.d.ts +1 -1
  60. package/dist/evaluation/public-api.d.ts +1 -1
  61. package/dist/hooks/delegation-gate/worktree-collision-ownership.d.ts +28 -0
  62. package/dist/hooks/delegation-gate/worktree-isolation.d.ts +63 -1
  63. package/dist/hooks/delegation-gate/worktree-merge-status.d.ts +14 -0
  64. package/dist/hooks/delegation-gate/worktree-ownership-tag.d.ts +19 -0
  65. package/dist/hooks/delegation-gate/worktree-provisioning-owner.d.ts +30 -0
  66. package/dist/hooks/delegation-gate.d.ts +14 -5
  67. package/dist/hooks/guardrails/messages-transform.d.ts +10 -0
  68. package/dist/hooks/guardrails/stored-input-args.d.ts +6 -0
  69. package/dist/hooks/index.d.ts +1 -1
  70. package/dist/hooks/init-orphan-recovery.d.ts +17 -0
  71. package/dist/index.js +486 -479
  72. package/dist/memory/schema.d.ts +4 -4
  73. package/dist/prm/escalation.d.ts +1 -11
  74. package/dist/prm/index.d.ts +1 -0
  75. package/dist/prm/types.d.ts +0 -2
  76. package/dist/session/snapshot-writer.d.ts +3 -0
  77. package/dist/state.d.ts +54 -1
  78. package/dist/tools/dispatch-lanes.d.ts +1 -1
  79. package/dist/tools/epic-record-divergence.d.ts +5 -5
  80. package/dist/utils/advisory-queue.d.ts +60 -0
  81. package/dist/worktree/merge.d.ts +47 -1
  82. package/package.json +1 -1
  83. package/dist/cli/guardrail-explain-zgja2b9m.js +0 -54
  84. package/dist/cli/pending-delegations-kxy8mdqg.js +0 -75
  85. package/dist/cli/{index-9f71ye47.js → index-13xxjfhn.js} +3 -3
  86. package/dist/cli/{index-s80bsjkj.js → index-2ghkk9ve.js} +6 -6
  87. 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: "reject" | "inconclusive" | "accept";
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<void>;
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 a background swarm delegation would advance
51
- * Stage B / record gate evidence before any review/test output exists. Until swarm can
52
- * correlate the deferred completion safely (a separate, spike-gated PR), background
53
- * swarm delegations are fail-closed-blocked. We do NOT silently coerce `background` to
54
- * false the unsupported capability is surfaced explicitly.
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.
@@ -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 {};