opencode-swarm 7.138.2 → 7.138.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/skills/execute/SKILL.md +11 -2
- package/.opencode/skills/swarm-implement/SKILL.md +1 -1
- package/.opencode/skills/swarm-pr-review/SKILL.md +5 -1
- package/dist/background/pr-review-trigger-contract.d.ts +38 -1
- package/dist/cli/{config-doctor-hzb0bck9.js → config-doctor-31yppse4.js} +2 -2
- package/dist/cli/{core-v6bte964.js → core-65c7rvqf.js} +1 -1
- package/dist/cli/{curation-policy-aj37yvz8.js → curation-policy-hs9pc1rp.js} +2 -2
- package/dist/cli/{curator-jt6dza8y.js → curator-54n1y4j8.js} +16 -15
- package/dist/cli/{curator-llm-factory-m9f4h8t4.js → curator-llm-factory-wm27ra25.js} +16 -15
- package/dist/cli/{evidence-summary-service-6cttrkzw.js → evidence-summary-service-hrxcf29a.js} +1 -1
- package/dist/cli/{guardrail-explain-rks41rpx.js → guardrail-explain-1729nse8.js} +17 -16
- package/dist/cli/{guardrail-log-h39jxftx.js → guardrail-log-yjj5srkw.js} +4 -3
- package/dist/cli/{hive-promoter-34y2x4vt.js → hive-promoter-es2x2b0e.js} +16 -15
- package/dist/cli/{index-49ygvqvm.js → index-1g6xpjk3.js} +4 -4
- package/dist/cli/{index-bakfqtye.js → index-33q2gmgd.js} +1 -1
- package/dist/cli/{index-xm07cqy2.js → index-50mwt8fx.js} +5 -5
- package/dist/cli/index-556ja8an.js +62 -0
- package/dist/cli/{index-r9pn41rj.js → index-57stye65.js} +1 -1
- package/dist/cli/{index-exq36v6w.js → index-5q009kc4.js} +1 -1
- package/dist/cli/{index-89dwrz8n.js → index-a18hk2fw.js} +225 -63
- package/dist/cli/{index-8emf08eb.js → index-a9xny52r.js} +2 -2
- package/dist/cli/{index-yxswbd1t.js → index-c60whfgh.js} +1 -1
- package/dist/cli/{index-gpxg7wx8.js → index-e1kgxchq.js} +30 -68
- package/dist/cli/{index-qt3jjjw9.js → index-gm3pjfhb.js} +271 -94
- package/dist/cli/{index-qg5smxv9.js → index-jbge0dyh.js} +3 -3
- package/dist/cli/{index-s7ac0xrf.js → index-k27a4e5g.js} +18 -17
- package/dist/cli/{index-k6cq9wwh.js → index-m1pwkwfe.js} +1 -1
- package/dist/cli/{index-ze4we97d.js → index-m40ea3zk.js} +1 -1
- package/dist/cli/{index-da98750t.js → index-m8txs2a9.js} +1 -1
- package/dist/cli/{index-y06a43sk.js → index-mycqmsz5.js} +4 -0
- package/dist/cli/index-smsy2p8n.js +1725 -0
- package/dist/cli/{index-d01d4102.js → index-tjwz5tct.js} +2 -2
- package/dist/cli/{index-j0xx9wpj.js → index-y28n2wmq.js} +1 -1
- package/dist/cli/index.js +16 -15
- package/dist/cli/{knowledge-escalator-qmpfh5yz.js → knowledge-escalator-s6z1x65b.js} +3 -3
- package/dist/cli/{knowledge-events-7neg8x0p.js → knowledge-events-nnyn6hst.js} +1 -1
- package/dist/cli/{knowledge-store-a6d4625r.js → knowledge-store-x4yfc7v3.js} +1 -1
- package/dist/cli/{knowledge-validator-q1qrdyvx.js → knowledge-validator-1v047gsa.js} +4 -4
- package/dist/cli/{scan-cursor-3tqshkb2.js → scan-cursor-jxajj6s7.js} +2 -2
- package/dist/cli/{schema-mtyaeh18.js → schema-fj32eyae.js} +1 -1
- package/dist/cli/{scope-persistence-n1bryhzf.js → scope-persistence-w7zen2wz.js} +25 -2
- package/dist/cli/{skill-generator-t3m5qajm.js → skill-generator-rt0jqcsa.js} +5 -5
- package/dist/hooks/guardrails/destructive-command.d.ts +35 -0
- package/dist/hooks/guardrails/file-authority.d.ts +71 -26
- package/dist/hooks/guardrails/index.d.ts +2 -0
- package/dist/hooks/guardrails/tool-before.d.ts +5 -0
- package/dist/hooks/scope-guard.d.ts +3 -1
- package/dist/index.js +408 -396
- package/dist/scope/path-identity.d.ts +27 -0
- package/dist/scope/scope-binding.d.ts +90 -2
- package/dist/scope/scope-lease-renewal.d.ts +28 -0
- package/dist/scope/scope-persistence.d.ts +102 -19
- package/dist/scope/scope-resolution-diagnostic.d.ts +7 -0
- package/dist/tools/declare-scope.d.ts +3 -1
- package/dist/tools/save-plan.d.ts +2 -0
- package/dist/tools/write-pr-review-trigger-eval.d.ts +2 -2
- package/dist/turbo/lean/lane-scope.d.ts +1 -1
- package/package.json +1 -1
- package/dist/cli/index-2y4jy0de.js +0 -642
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
/** Filesystem identity semantics used for comparisons, never for I/O. */
|
|
3
|
+
export type PathFlavor = 'win32' | 'posix';
|
|
4
|
+
export declare function getPathFlavor(pathLib?: Pick<typeof path, 'sep'>): PathFlavor;
|
|
5
|
+
/**
|
|
6
|
+
* Produces a comparison-only path identity. Windows identities case-fold and
|
|
7
|
+
* normalize both slash forms; POSIX identities preserve case.
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizePathIdentity(value: string, flavor?: PathFlavor): string;
|
|
10
|
+
export declare function pathIdentitiesEqual(a: string, b: string, flavor?: PathFlavor): boolean;
|
|
11
|
+
/** True when target is the container itself or a descendant of it. */
|
|
12
|
+
export declare function isPathIdentityWithin(target: string, container: string, flavor?: PathFlavor): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Resolves a candidate and declared entries with one filesystem flavor, then
|
|
15
|
+
* applies the canonical identity containment rule. This is the shared scope
|
|
16
|
+
* predicate for guardrails, completion observers, and authority evaluation.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isPathWithinDeclaredScope(filePath: string, scopeEntries: readonly string[], cwd?: string, flavor?: PathFlavor): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Compares drive/UNC/extended-device roots with Windows case-insensitive
|
|
21
|
+
* identity while retaining POSIX case-sensitive path semantics.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isOnDifferentPathRoot(target: string, cwd: string, flavor?: PathFlavor): boolean;
|
|
24
|
+
/** Returns a safe, caller-facing reason when path text is deceptive. */
|
|
25
|
+
export declare function unsafePathTextReason(value: string): string | null;
|
|
26
|
+
/** Single-line, bounded rendering for untrusted diagnostic fields. */
|
|
27
|
+
export declare function sanitizeDiagnosticText(value: unknown, maxLength?: number): string;
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import type { Plan } from '../config/plan-schema';
|
|
2
2
|
export declare const MAX_PENDING_SCOPE_BINDINGS = 256;
|
|
3
|
+
export declare const DEFAULT_SCOPE_BINDING_TTL_MS: number;
|
|
4
|
+
export declare const MAX_SCOPE_BINDING_TOMBSTONES = 256;
|
|
5
|
+
export type ScopeBindingLifecycleState = 'live' | 'expired' | 'revoked' | 'superseded';
|
|
3
6
|
export type ScopeBindingSource = 'declare_scope' | 'plan' | 'file_directive' | 'pr_feedback' | 'worktree_derived';
|
|
4
7
|
export interface ScopeBinding {
|
|
5
8
|
version: 2;
|
|
9
|
+
/** Stable logical authority identity across lifecycle generations. */
|
|
10
|
+
bindingId: string;
|
|
11
|
+
/** Exact immutable generation identity used by persistence and cleanup. */
|
|
12
|
+
generationId: string;
|
|
13
|
+
/** Monotonic CAS revision within one generation. */
|
|
14
|
+
revision: number;
|
|
15
|
+
lifecycleState: ScopeBindingLifecycleState;
|
|
6
16
|
workspaceIdentity: string;
|
|
7
17
|
planId: string;
|
|
8
18
|
planStructureHash: string;
|
|
@@ -14,14 +24,43 @@ export interface ScopeBinding {
|
|
|
14
24
|
activation: 'declaration' | 'pending_child' | 'active';
|
|
15
25
|
parentOwnerSessionId?: string;
|
|
16
26
|
parentCallId?: string;
|
|
27
|
+
predecessorGenerationId?: string;
|
|
28
|
+
childSessionId?: string;
|
|
17
29
|
/** PR_FEEDBACK bindings carry their parent workflow identity and revision. */
|
|
18
30
|
workflowSessionId?: string;
|
|
19
31
|
workflowRevisionDigest?: string;
|
|
20
32
|
source: ScopeBindingSource;
|
|
21
33
|
files: string[];
|
|
22
34
|
declaredAt: number;
|
|
35
|
+
updatedAt: number;
|
|
36
|
+
leaseStartedAt: number;
|
|
23
37
|
expiresAt: number;
|
|
24
38
|
}
|
|
39
|
+
export declare const MAX_SCOPE_PATH_BYTES = 4096;
|
|
40
|
+
export declare const MAX_SCOPE_FILES_BYTES: number;
|
|
41
|
+
export type ScopeBindingAdmissionResult = {
|
|
42
|
+
ok: true;
|
|
43
|
+
binding: ScopeBinding;
|
|
44
|
+
} | {
|
|
45
|
+
ok: false;
|
|
46
|
+
code: 'SCOPE_BINDING_CAPACITY' | 'SCOPE_BINDING_STALE';
|
|
47
|
+
message: string;
|
|
48
|
+
};
|
|
49
|
+
export type ScopeBindingResolution = {
|
|
50
|
+
status: 'found';
|
|
51
|
+
binding: ScopeBinding;
|
|
52
|
+
} | {
|
|
53
|
+
status: 'expired';
|
|
54
|
+
candidates: ScopeBinding[];
|
|
55
|
+
totalCandidates: number;
|
|
56
|
+
} | {
|
|
57
|
+
status: 'ambiguous';
|
|
58
|
+
candidates: ScopeBinding[];
|
|
59
|
+
totalCandidates: number;
|
|
60
|
+
} | {
|
|
61
|
+
status: 'not_declared';
|
|
62
|
+
};
|
|
63
|
+
export declare function isScopeBindingIdentity(value: unknown): value is string;
|
|
25
64
|
export declare function canonicalWorkspaceIdentity(directory: string): string | null;
|
|
26
65
|
export declare function normalizeScopeFiles(files: readonly string[]): string[] | null;
|
|
27
66
|
export declare function createScopeBinding(input: {
|
|
@@ -36,7 +75,7 @@ export declare function createScopeBinding(input: {
|
|
|
36
75
|
activation?: ScopeBinding['activation'];
|
|
37
76
|
ttlMs?: number;
|
|
38
77
|
}): ScopeBinding | null;
|
|
39
|
-
export declare function registerScopeBinding(binding: ScopeBinding):
|
|
78
|
+
export declare function registerScopeBinding(binding: ScopeBinding): ScopeBindingAdmissionResult;
|
|
40
79
|
/**
|
|
41
80
|
* Resolve the binding created for one fully-approved parent Task dispatch.
|
|
42
81
|
* An already-claimed child binding wins over its pending parent precursor;
|
|
@@ -83,6 +122,14 @@ export declare function getAuthorizedScopeBindingByPlanIdentity(input: {
|
|
|
83
122
|
taskId: string;
|
|
84
123
|
activeSessionId: string;
|
|
85
124
|
}): ScopeBinding | null;
|
|
125
|
+
/** Resolve authorization without collapsing expired and ambiguous states. */
|
|
126
|
+
export declare function resolveAuthorizedScopeBindingByPlanIdentity(input: {
|
|
127
|
+
directory: string;
|
|
128
|
+
planId: string;
|
|
129
|
+
planStructureHash: string;
|
|
130
|
+
taskId: string;
|
|
131
|
+
activeSessionId: string;
|
|
132
|
+
}): ScopeBindingResolution;
|
|
86
133
|
/**
|
|
87
134
|
* Issue #2002 recurrence guardrail (detection half).
|
|
88
135
|
*
|
|
@@ -144,6 +191,22 @@ export declare function describeScopeWorkspaceMismatch(input: {
|
|
|
144
191
|
taskId?: string | null;
|
|
145
192
|
}): string | null;
|
|
146
193
|
export declare function clearScopeBindings(predicate?: (binding: ScopeBinding) => boolean): ScopeBinding[];
|
|
194
|
+
/** Remove one exact generation; sibling generations are never affected. */
|
|
195
|
+
export declare function clearExactScopeBinding(input: {
|
|
196
|
+
bindingId: string;
|
|
197
|
+
generationId: string;
|
|
198
|
+
}): ScopeBinding | null;
|
|
199
|
+
export declare function installScopeBindingTombstone(binding: ScopeBinding): void;
|
|
200
|
+
export declare function hasScopeBindingDenyOverlay(binding: ScopeBinding): boolean;
|
|
201
|
+
/** Fail-closed overlay used only when durable revocation cannot be verified. */
|
|
202
|
+
export declare function installFailedRevocationOverlay(binding: ScopeBinding, reason: Exclude<ScopeBindingLifecycleState, 'live'>): boolean;
|
|
203
|
+
/**
|
|
204
|
+
* Installs an immediate generation-wide deny intent before durable retirement
|
|
205
|
+
* contends with refresh. Unlike the failure-only overlay, cleanup owns the
|
|
206
|
+
* whole exact generation and therefore deliberately follows a newer revision.
|
|
207
|
+
*/
|
|
208
|
+
export declare function installScopeBindingRetirementIntent(binding: Pick<ScopeBinding, 'bindingId' | 'generationId'> & Partial<ScopeBinding>): ScopeBinding | null;
|
|
209
|
+
export declare function updateExactScopeBinding(binding: ScopeBinding): ScopeBindingAdmissionResult;
|
|
147
210
|
/**
|
|
148
211
|
* Atomically claim the pending scope for the exact Task call whose upstream
|
|
149
212
|
* metadata identified the actual OpenCode child session. The call id is part of
|
|
@@ -158,6 +221,11 @@ export declare function claimScopeBindingForChild(input: {
|
|
|
158
221
|
previous: ScopeBinding;
|
|
159
222
|
claimed: ScopeBinding;
|
|
160
223
|
} | null;
|
|
224
|
+
export declare function createClaimedScopeBinding(previous: ScopeBinding, input: {
|
|
225
|
+
parentSessionId: string;
|
|
226
|
+
childSessionId: string;
|
|
227
|
+
dispatchCallId: string;
|
|
228
|
+
}): ScopeBinding;
|
|
161
229
|
export declare function deriveChildScopeBinding(parent: ScopeBinding, input: {
|
|
162
230
|
childDirectory: string;
|
|
163
231
|
childSessionId: string;
|
|
@@ -181,5 +249,25 @@ export declare function resolveCoderScopeSources(input: {
|
|
|
181
249
|
source: 'declare_scope' | 'plan' | 'file_directive';
|
|
182
250
|
} | {
|
|
183
251
|
ok: false;
|
|
184
|
-
code: 'SCOPE_NOT_DECLARED'
|
|
252
|
+
code: 'SCOPE_NOT_DECLARED';
|
|
253
|
+
sources?: ScopeSourceSets;
|
|
254
|
+
} | {
|
|
255
|
+
ok: false;
|
|
256
|
+
code: 'SCOPE_CONFLICT';
|
|
257
|
+
authoritativeSource: 'declare_scope' | 'plan';
|
|
258
|
+
authoritativeFiles: string[];
|
|
259
|
+
conflictingSource: 'plan' | 'file_directive';
|
|
260
|
+
conflictingFiles: string[];
|
|
261
|
+
disagreement: string[];
|
|
262
|
+
sources: ScopeSourceSets;
|
|
185
263
|
};
|
|
264
|
+
interface ScopeSourceSets {
|
|
265
|
+
explicit: string[] | null;
|
|
266
|
+
plan: string[] | null;
|
|
267
|
+
directives: string[] | null;
|
|
268
|
+
}
|
|
269
|
+
/** One bounded renderer shared by normal and PR-feedback preflight paths. */
|
|
270
|
+
export declare function formatCoderScopeConflict(decision: Extract<ReturnType<typeof resolveCoderScopeSources>, {
|
|
271
|
+
ok: false;
|
|
272
|
+
}>): string;
|
|
273
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ScopeBinding } from './scope-binding.js';
|
|
2
|
+
import { refreshScopeBindingLease, type ScopePersistenceResult } from './scope-persistence.js';
|
|
3
|
+
export interface ScopeLeaseCandidateInput {
|
|
4
|
+
callID: string;
|
|
5
|
+
sessionID: string;
|
|
6
|
+
tool: string;
|
|
7
|
+
directory: string;
|
|
8
|
+
binding: ScopeBinding;
|
|
9
|
+
targets: string[];
|
|
10
|
+
args?: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface ScopeLeaseAfterOutput {
|
|
13
|
+
title?: unknown;
|
|
14
|
+
output?: unknown;
|
|
15
|
+
metadata?: unknown;
|
|
16
|
+
}
|
|
17
|
+
export interface ScopeLeaseRenewalTracker {
|
|
18
|
+
remember(input: ScopeLeaseCandidateInput): void;
|
|
19
|
+
consume(input: {
|
|
20
|
+
callID: string;
|
|
21
|
+
sessionID: string;
|
|
22
|
+
tool: string;
|
|
23
|
+
output: ScopeLeaseAfterOutput | null | undefined;
|
|
24
|
+
}): Promise<ScopePersistenceResult | null>;
|
|
25
|
+
}
|
|
26
|
+
type RefreshLease = typeof refreshScopeBindingLease;
|
|
27
|
+
export declare function createScopeLeaseRenewalTracker(refresh?: RefreshLease, now?: () => number): ScopeLeaseRenewalTracker;
|
|
28
|
+
export {};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Scope persistence for #519 (v6.71.1 hotfix).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Retains the legacy v1 `.swarm/scopes/scope-{taskId}.json` projection used by
|
|
5
|
+
* worktree materialization, and persists authorization-capable v2 bindings as
|
|
6
|
+
* exact identity/generation records. Durable v2 state is authoritative across
|
|
7
|
+
* processes; memory is only a cache and can never revive a retired generation.
|
|
7
8
|
*
|
|
8
9
|
* Also exposes a fallback reader that reads `plan.json:phases[].tasks[].files_touched`
|
|
9
10
|
* for the active task, so architect-authored plans become a durable scope source
|
|
@@ -13,7 +14,8 @@
|
|
|
13
14
|
* - Atomic write via temp + rename (POSIX atomic on same filesystem).
|
|
14
15
|
* - File lock via proper-lockfile while writing.
|
|
15
16
|
* - Schema versioning: readers fail closed on unknown version.
|
|
16
|
-
* - TTL: default 24h from declaredAt; expired
|
|
17
|
+
* - Legacy v1 TTL: default 24h from declaredAt; expired projections return null.
|
|
18
|
+
* - V2 lifecycle: typed live/expired/revoked/ambiguous/overloaded decisions.
|
|
17
19
|
* - Symlink guards (defence in depth):
|
|
18
20
|
* * realpath containment check on `.swarm/scopes/` (closes parent-dir attack)
|
|
19
21
|
* * O_NOFOLLOW on both write-create and read-fd (closes leaf-file TOCTOU)
|
|
@@ -31,20 +33,15 @@
|
|
|
31
33
|
* - This is command-level containment, not an operating-system syscall sandbox.
|
|
32
34
|
*
|
|
33
35
|
* RESIDUAL RISKS:
|
|
34
|
-
* 1. Platform-portability of symlink guards:
|
|
36
|
+
* 1. Platform-portability of legacy v1 symlink guards:
|
|
35
37
|
* - realpath resolves POSIX symlinks and Windows junctions, but the
|
|
36
38
|
* Windows behaviour is not covered by CI (Linux-only test matrix).
|
|
37
39
|
* - O_NOFOLLOW is a no-op on Windows (falls back to 0). The realpath
|
|
38
40
|
* containment check on `.swarm/scopes/` remains the primary guard
|
|
39
41
|
* on that platform; leaf-file TOCTOU on Windows is not closed.
|
|
40
|
-
* 2.
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* Acceptable because in-memory state remains authoritative inside the
|
|
44
|
-
* live process; disk is a fallback.
|
|
45
|
-
* 3. Temp-file leak: a crash between `Bun.write(tmp)` and `renameSync`
|
|
46
|
-
* leaves `scope-{id}.json.tmp.{ts}.{rand}` files. No sweeper runs today;
|
|
47
|
-
* accumulation is bounded by `/swarm close` (which rm -rf's .swarm/scopes/).
|
|
42
|
+
* 2. Legacy v1 temp-file leak: a crash between atomic-write steps can leave
|
|
43
|
+
* temporary projection files. V2 lifecycle maintenance is bounded and
|
|
44
|
+
* prunes settled receipts, tombstones, sidecars, and retirement intents.
|
|
48
45
|
*
|
|
49
46
|
* NOT a standalone syscall security boundary. Shell-write detection and the
|
|
50
47
|
* central write-target resolver provide command-level interception; this module
|
|
@@ -53,6 +50,31 @@
|
|
|
53
50
|
import { type Plan } from '../config/plan-schema';
|
|
54
51
|
import { type ScopeBinding } from './scope-binding';
|
|
55
52
|
declare const SCOPE_SCHEMA_VERSION: 1;
|
|
53
|
+
export type ScopePersistenceCode = 'SCOPE_NOT_DECLARED' | 'SCOPE_BINDING_PERSISTENCE_FAILED' | 'SCOPE_BINDING_CAPACITY' | 'SCOPE_BINDING_ALREADY_CLAIMED' | 'SCOPE_BINDING_AMBIGUOUS' | 'SCOPE_BINDING_EXPIRED' | 'SCOPE_BINDING_STORE_OVERLOADED' | 'SCOPE_BINDING_STALE';
|
|
54
|
+
export type ScopePersistenceResult<T = ScopeBinding> = {
|
|
55
|
+
ok: true;
|
|
56
|
+
value: T;
|
|
57
|
+
} | {
|
|
58
|
+
ok: false;
|
|
59
|
+
code: ScopePersistenceCode;
|
|
60
|
+
message: string;
|
|
61
|
+
};
|
|
62
|
+
export type DurableScopeBindingResolution = {
|
|
63
|
+
status: 'found';
|
|
64
|
+
binding: ScopeBinding;
|
|
65
|
+
} | {
|
|
66
|
+
status: 'expired';
|
|
67
|
+
candidates: ScopeBinding[];
|
|
68
|
+
totalCandidates: number;
|
|
69
|
+
} | {
|
|
70
|
+
status: 'ambiguous';
|
|
71
|
+
candidates: ScopeBinding[];
|
|
72
|
+
totalCandidates: number;
|
|
73
|
+
} | {
|
|
74
|
+
status: 'overloaded';
|
|
75
|
+
} | {
|
|
76
|
+
status: 'not_declared';
|
|
77
|
+
};
|
|
56
78
|
export interface PersistedScope {
|
|
57
79
|
version: typeof SCOPE_SCHEMA_VERSION;
|
|
58
80
|
taskId: string;
|
|
@@ -62,11 +84,11 @@ export interface PersistedScope {
|
|
|
62
84
|
}
|
|
63
85
|
export type PersistedScopeBinding = ScopeBinding;
|
|
64
86
|
/**
|
|
65
|
-
* Write
|
|
87
|
+
* Write the legacy v1 `.swarm/scopes/scope-{taskId}.json` projection atomically.
|
|
66
88
|
* Safe to call concurrently — proper-lockfile serialises writers per-file.
|
|
67
89
|
*
|
|
68
|
-
* Silent on I/O failure
|
|
69
|
-
*
|
|
90
|
+
* Silent on I/O failure for backward compatibility. This projection is not an
|
|
91
|
+
* authorization source; v2 callers use the fail-closed exact-generation APIs.
|
|
70
92
|
*/
|
|
71
93
|
export declare function writeScopeToDisk(directory: string, taskId: string, files: string[], ttlMs?: number): Promise<void>;
|
|
72
94
|
/**
|
|
@@ -74,7 +96,9 @@ export declare function writeScopeToDisk(directory: string, taskId: string, file
|
|
|
74
96
|
* compatibility with older non-authorization consumers, but strict Task
|
|
75
97
|
* preflight and declare_scope use only this schema.
|
|
76
98
|
*/
|
|
77
|
-
export declare function writeScopeBindingToDisk(directory: string, binding: ScopeBinding): Promise<
|
|
99
|
+
export declare function writeScopeBindingToDisk(directory: string, binding: ScopeBinding): Promise<ScopePersistenceResult>;
|
|
100
|
+
/** Mandatory transaction: durable verification precedes memory authority. */
|
|
101
|
+
export declare function persistAndRegisterScopeBinding(directory: string, binding: ScopeBinding): Promise<ScopePersistenceResult>;
|
|
78
102
|
/** Read and fully validate a v2 binding; v1 records never authorize here. */
|
|
79
103
|
export declare function readScopeBindingFromDisk(input: {
|
|
80
104
|
directory: string;
|
|
@@ -85,17 +109,67 @@ export declare function readScopeBindingFromDisk(input: {
|
|
|
85
109
|
requireDispatchCorrelation?: boolean;
|
|
86
110
|
requireDeclaration?: boolean;
|
|
87
111
|
}): ScopeBinding | null;
|
|
112
|
+
export declare function resolveScopeBindingFromDisk(input: {
|
|
113
|
+
directory: string;
|
|
114
|
+
taskId: string;
|
|
115
|
+
plan: Plan;
|
|
116
|
+
ownerSessionId: string;
|
|
117
|
+
parentCallId?: string;
|
|
118
|
+
requireDispatchCorrelation?: boolean;
|
|
119
|
+
requireDeclaration?: boolean;
|
|
120
|
+
includeExpired?: boolean;
|
|
121
|
+
}): DurableScopeBindingResolution;
|
|
88
122
|
/** Remove one identity-bound authorization without disturbing sibling sessions. */
|
|
89
123
|
export declare function clearScopeBindingFromDisk(input: {
|
|
90
124
|
directory: string;
|
|
125
|
+
binding: ScopeBinding;
|
|
126
|
+
}): ScopePersistenceResult;
|
|
127
|
+
export declare function tombstoneScopeBinding(directory: string, binding: ScopeBinding, reason: 'expired' | 'revoked' | 'superseded'): Promise<ScopePersistenceResult>;
|
|
128
|
+
/** CAS renewal for one exact active generation. */
|
|
129
|
+
export declare function refreshScopeBindingLease(input: {
|
|
130
|
+
directory: string;
|
|
131
|
+
bindingId: string;
|
|
132
|
+
generationId: string;
|
|
133
|
+
expectedRevision: number;
|
|
134
|
+
activeSessionId: string;
|
|
91
135
|
taskId: string;
|
|
92
|
-
|
|
93
|
-
}):
|
|
136
|
+
ttlMs?: number;
|
|
137
|
+
}): Promise<ScopePersistenceResult>;
|
|
138
|
+
/**
|
|
139
|
+
* Cross-process, successor-first claim transaction. All contenders for one
|
|
140
|
+
* pending generation lock the same stable receipt path.
|
|
141
|
+
*/
|
|
142
|
+
export declare function claimScopeBindingForChildDurably(input: {
|
|
143
|
+
directory: string;
|
|
144
|
+
parentSessionId: string;
|
|
145
|
+
childSessionId: string;
|
|
146
|
+
dispatchCallId: string;
|
|
147
|
+
}): Promise<ScopePersistenceResult<{
|
|
148
|
+
previous: ScopeBinding;
|
|
149
|
+
claimed: ScopeBinding;
|
|
150
|
+
}>>;
|
|
151
|
+
/**
|
|
152
|
+
* Declaration transaction used by declare_scope. Replacement is restricted to
|
|
153
|
+
* the same canonical workspace, task, and owning architect session.
|
|
154
|
+
*/
|
|
155
|
+
export declare function replaceExistingScopeDeclaration(input: {
|
|
156
|
+
directory: string;
|
|
157
|
+
binding: ScopeBinding;
|
|
158
|
+
replaceExisting: boolean;
|
|
159
|
+
}): Promise<ScopePersistenceResult>;
|
|
160
|
+
export declare function flushScopeBindingMaintenance(directory?: string): Promise<void>;
|
|
161
|
+
/** Prune old non-authorizing generations and settled receipts under one lock. */
|
|
162
|
+
export declare function pruneScopeBindingTombstones(directory: string): Promise<ScopePersistenceResult<number>>;
|
|
94
163
|
export declare function resolveAuthorizedScopeBinding(input: {
|
|
95
164
|
directory: string;
|
|
96
165
|
taskId: string;
|
|
97
166
|
activeSessionId: string;
|
|
98
167
|
}): ScopeBinding | null;
|
|
168
|
+
export declare function resolveAuthorizedScopeBindingDetailed(input: {
|
|
169
|
+
directory: string;
|
|
170
|
+
taskId: string;
|
|
171
|
+
activeSessionId: string;
|
|
172
|
+
}): DurableScopeBindingResolution;
|
|
99
173
|
/**
|
|
100
174
|
* Recover one unambiguous child binding after an in-memory session restart.
|
|
101
175
|
* Every candidate is still checked against current plan identity, exact child
|
|
@@ -105,12 +179,21 @@ export declare function resolveAuthorizedScopeBindingForSession(input: {
|
|
|
105
179
|
directory: string;
|
|
106
180
|
activeSessionId: string;
|
|
107
181
|
}): ScopeBinding | null;
|
|
182
|
+
export declare function resolveAuthorizedScopeBindingForSessionDetailed(input: {
|
|
183
|
+
directory: string;
|
|
184
|
+
activeSessionId: string;
|
|
185
|
+
}): DurableScopeBindingResolution;
|
|
108
186
|
/** Recover one exact planless PR_FEEDBACK child binding after plugin restart. */
|
|
109
187
|
export declare function resolveAuthorizedPrFeedbackScopeBindingFromDisk(input: {
|
|
110
188
|
directory: string;
|
|
111
189
|
activeSessionId: string;
|
|
112
190
|
taskId?: string;
|
|
113
191
|
}): ScopeBinding | null;
|
|
192
|
+
export declare const _scopePersistenceInternals: {
|
|
193
|
+
liveBindingCapacity: number;
|
|
194
|
+
bindingFileScanCapacity: number;
|
|
195
|
+
maintenanceFileScanCapacity: number;
|
|
196
|
+
};
|
|
114
197
|
/**
|
|
115
198
|
* Read persisted scope for a task. Returns null on:
|
|
116
199
|
* - file missing
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DurableScopeBindingResolution } from './scope-persistence.js';
|
|
2
|
+
/** One bounded, single-line recovery contract for durable binding lookup failures. */
|
|
3
|
+
export declare function formatScopeResolutionDiagnostic(input: {
|
|
4
|
+
resolution: DurableScopeBindingResolution;
|
|
5
|
+
taskId: string | null;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
}): string | null;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* This tool must be called before delegating to coder to enable scope containment checking.
|
|
5
5
|
*/
|
|
6
6
|
import type { ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
7
|
+
import type { AuthorityConfig } from '../config/schema';
|
|
7
8
|
/**
|
|
8
9
|
* Arguments for the declare_scope tool
|
|
9
10
|
*/
|
|
@@ -11,6 +12,7 @@ export interface DeclareScopeArgs {
|
|
|
11
12
|
taskId: string;
|
|
12
13
|
files: string[];
|
|
13
14
|
whitelist?: string[];
|
|
15
|
+
replace_existing?: boolean;
|
|
14
16
|
working_directory?: string;
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
@@ -47,7 +49,7 @@ export declare function executeDeclareScope(args: DeclareScopeArgs, fallbackDir?
|
|
|
47
49
|
sessionID: string;
|
|
48
50
|
messageID: string;
|
|
49
51
|
agentName?: string;
|
|
50
|
-
}): Promise<DeclareScopeResult>;
|
|
52
|
+
}, authorityConfig?: AuthorityConfig): Promise<DeclareScopeResult>;
|
|
51
53
|
/**
|
|
52
54
|
* Tool definition for declare_scope
|
|
53
55
|
*/
|
|
@@ -23,6 +23,8 @@ export interface SavePlanArgs {
|
|
|
23
23
|
size?: 'small' | 'medium' | 'large';
|
|
24
24
|
depends?: string[];
|
|
25
25
|
acceptance?: string;
|
|
26
|
+
/** Exact project-relative paths this task is expected to modify. */
|
|
27
|
+
files_touched?: string[];
|
|
26
28
|
/** Spec FR-###/SC-### IDs this task maps to (issue #1687, FR-000). */
|
|
27
29
|
fr_refs?: string[];
|
|
28
30
|
}>;
|
|
@@ -27,7 +27,7 @@ declare const WritePrReviewTriggerEvalArgsSchema: z.ZodObject<{
|
|
|
27
27
|
"unclassified-risk": "unclassified-risk";
|
|
28
28
|
}>;
|
|
29
29
|
result: z.ZodLiteral<"MATCHED">;
|
|
30
|
-
evidence: z.ZodString
|
|
30
|
+
evidence: z.ZodOptional<z.ZodString>;
|
|
31
31
|
source_batch_id: z.ZodString;
|
|
32
32
|
source_lane_id: z.ZodString;
|
|
33
33
|
}, z.core.$strict>, z.ZodObject<{
|
|
@@ -45,7 +45,7 @@ declare const WritePrReviewTriggerEvalArgsSchema: z.ZodObject<{
|
|
|
45
45
|
"unclassified-risk": "unclassified-risk";
|
|
46
46
|
}>;
|
|
47
47
|
result: z.ZodLiteral<"NOT_TRIGGERED">;
|
|
48
|
-
evidence: z.ZodString
|
|
48
|
+
evidence: z.ZodOptional<z.ZodString>;
|
|
49
49
|
}, z.core.$strict>], "result">>;
|
|
50
50
|
}, z.core.$strict>;
|
|
51
51
|
export type WritePrReviewTriggerEvalArgs = z.infer<typeof WritePrReviewTriggerEvalArgsSchema>;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* This module publishes that authority using the same primitives as the
|
|
17
17
|
* standard worktree path (`src/hooks/delegation-gate.ts`, the
|
|
18
|
-
* `savePlan` → `deriveChildScopeBinding` →
|
|
18
|
+
* `savePlan` → `deriveChildScopeBinding` → `persistAndRegisterScopeBinding`
|
|
19
19
|
* sequence), so every identity condition in
|
|
20
20
|
* `getAuthorizedScopeBindingByPlanIdentity` is satisfied without weakening or
|
|
21
21
|
* bypassing any of them.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.138.
|
|
3
|
+
"version": "7.138.4",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|