opencode-swarm 7.122.0 → 7.122.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/dist/cli/{config-doctor-404c8mf8.js → config-doctor-3ksh1534.js} +2 -2
- package/dist/cli/{core-xnwasfpc.js → core-cwgb7ev8.js} +1 -1
- package/dist/cli/{curation-policy-cj1tpzr5.js → curation-policy-4j4qce5a.js} +5 -5
- package/dist/cli/{curator-llm-factory-8rg0f77t.js → curator-llm-factory-x769hn66.js} +22 -20
- package/dist/cli/{curator-3rze3pd4.js → curator-qfh07497.js} +22 -20
- package/dist/cli/{evidence-summary-service-k14ykm03.js → evidence-summary-service-96ndczbk.js} +7 -5
- package/dist/cli/{gate-evidence-tbpezk28.js → gate-evidence-xqks3kk2.js} +3 -3
- package/dist/cli/{guardrail-explain-hdsxa2c5.js → guardrail-explain-kj0e9r25.js} +23 -21
- package/dist/cli/{guardrail-log-dhpp0kmn.js → guardrail-log-z291m4gw.js} +3 -3
- package/dist/cli/{hive-promoter-dh38qd9m.js → hive-promoter-00mktxkk.js} +22 -20
- package/dist/cli/{index-zvae4m8g.js → index-17eybc3z.js} +1 -1
- package/dist/cli/{index-kyvg2cmp.js → index-19zn67q6.js} +1 -1
- package/dist/cli/{index-rbfq77sz.js → index-2j25br0p.js} +12 -10
- package/dist/cli/{index-9xpgytj9.js → index-3rr0ese8.js} +1 -1
- package/dist/cli/{index-gr7ssnj9.js → index-5he0kwnx.js} +3 -3
- package/dist/cli/{index-68aggz0v.js → index-6srk9a5q.js} +2 -2
- package/dist/cli/{index-qg11yf8q.js → index-6xnxz6z0.js} +1 -1
- package/dist/cli/{index-emkb0bbe.js → index-7cspygmb.js} +277 -673
- package/dist/cli/{index-3r1w7xky.js → index-cqsak7bf.js} +2 -2
- package/dist/cli/index-ctpgsj84.js +547 -0
- package/dist/cli/index-d6h5hq91.js +564 -0
- package/dist/cli/{index-vqcgmy4y.js → index-det5jwfk.js} +1 -1
- package/dist/cli/{index-3sx27bxt.js → index-dywekeen.js} +3 -3
- package/dist/cli/{index-wtn8wf7h.js → index-gw3m5fw0.js} +1 -1
- package/dist/cli/{index-rkd6qwjh.js → index-hns7xjm7.js} +2 -2
- package/dist/cli/{index-xdsj4qtd.js → index-j9kxwshw.js} +5 -5
- package/dist/cli/{index-jzpv74mq.js → index-jm81nhks.js} +24 -22
- package/dist/cli/{index-ahxrne50.js → index-kanrv3sv.js} +160 -58
- package/dist/cli/{index-j33zb3cx.js → index-m0bmh3w0.js} +4 -4
- package/dist/cli/index-m1abzg93.js +68 -0
- package/dist/cli/{index-ny13nr0x.js → index-n2712byd.js} +6 -6
- package/dist/cli/{index-3jvqw5hy.js → index-q27bajqb.js} +1 -1
- package/dist/cli/{index-qt2yaghg.js → index-vcgfqd8f.js} +1 -63
- package/dist/cli/{index-tftv9xh9.js → index-wr8qgpz5.js} +1 -1
- package/dist/cli/index.js +22 -20
- package/dist/cli/{knowledge-escalator-sya06p12.js → knowledge-escalator-dyxsm78k.js} +6 -6
- package/dist/cli/{knowledge-events-xzps61a9.js → knowledge-events-36q6nvae.js} +4 -4
- package/dist/cli/{knowledge-link-sy85fw3w.js → knowledge-link-4yd2z8ev.js} +3 -3
- package/dist/cli/{knowledge-store-qg66a93n.js → knowledge-store-e07b4s54.js} +4 -4
- package/dist/cli/{knowledge-validator-gar6wv4f.js → knowledge-validator-sfyf7wn2.js} +7 -7
- package/dist/cli/pr-subscriptions-x653151h.js +1 -1
- package/dist/cli/{scan-cursor-nx5y5k7r.js → scan-cursor-hm0aqqae.js} +5 -5
- package/dist/cli/{schema-vwxpsk6j.js → schema-13bnng2n.js} +1 -1
- package/dist/cli/scope-persistence-d9erwnvb.js +36 -0
- package/dist/cli/{skill-generator-nt5qxafq.js → skill-generator-g6r52x94.js} +8 -8
- package/dist/config/constants.d.ts +3 -2
- package/dist/hooks/delegation-gate.d.ts +19 -1
- package/dist/hooks/guardrails/nontransient-circuit.d.ts +47 -0
- package/dist/hooks/scope-guard.d.ts +9 -2
- package/dist/hooks/write-target-resolver.d.ts +30 -0
- package/dist/index.js +405 -369
- package/dist/sandbox/win32/restricted-environment-executor.d.ts +3 -2
- package/dist/scope/scope-binding.d.ts +89 -0
- package/dist/scope/scope-persistence.d.ts +53 -12
- package/dist/state.d.ts +25 -1
- package/dist/tools/declare-scope.d.ts +5 -1
- package/dist/tools/file-extractor-planner.d.ts +31 -0
- package/dist/tools/file-extractor.d.ts +2 -7
- package/package.json +1 -1
- package/dist/cli/index-51gg9947.js +0 -254
- package/dist/cli/scope-persistence-mfr36yc9.js +0 -20
- package/dist/cli/{index-h07wn84h.js → index-7mkpsw6g.js} +3 -3
- package/dist/cli/{pending-delegations-0z3znsec.js → pending-delegations-68cbaqv2.js} +1 -1
|
@@ -71,7 +71,7 @@ export declare class WindowsSandboxExecutor implements SandboxExecutor {
|
|
|
71
71
|
*
|
|
72
72
|
* The wrapper:
|
|
73
73
|
* - Sets scoped temp directory (%TEMP%, %TMP%)
|
|
74
|
-
* -
|
|
74
|
+
* - Preserves syntactically safe local PATH entries after system paths
|
|
75
75
|
* - Removes dangerous environment variables that could be used to bypass restrictions
|
|
76
76
|
* - Applies per-call envOverrides (set or unset)
|
|
77
77
|
* - Executes PowerShell-native cmdlets (filesystem cmdlets only) via Invoke-Expression,
|
|
@@ -99,7 +99,8 @@ export declare class WindowsSandboxExecutor implements SandboxExecutor {
|
|
|
99
99
|
* Return environment variable overrides required for the Windows sandbox.
|
|
100
100
|
*
|
|
101
101
|
* Security measures:
|
|
102
|
-
* - PATH
|
|
102
|
+
* - PATH starts with essential Windows system directories, then retains
|
|
103
|
+
* syntactically safe local inherited tool directories
|
|
103
104
|
* - TEMP/TMP are set to null (will be set to scoped temp at runtime via wrapCommand)
|
|
104
105
|
* - Dangerous variables that don't apply to Windows are cleared for completeness
|
|
105
106
|
*/
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Plan } from '../config/plan-schema';
|
|
2
|
+
export declare const MAX_PENDING_SCOPE_BINDINGS = 256;
|
|
3
|
+
export type ScopeBindingSource = 'declare_scope' | 'plan' | 'file_directive' | 'worktree_derived';
|
|
4
|
+
export interface ScopeBinding {
|
|
5
|
+
version: 2;
|
|
6
|
+
workspaceIdentity: string;
|
|
7
|
+
planId: string;
|
|
8
|
+
planStructureHash: string;
|
|
9
|
+
taskId: string;
|
|
10
|
+
ownerSessionId: string;
|
|
11
|
+
ownerMessageId: string;
|
|
12
|
+
/** Exact Task tool call that activated this binding. Declarations omit it. */
|
|
13
|
+
dispatchCallId?: string;
|
|
14
|
+
activation: 'declaration' | 'pending_child' | 'active';
|
|
15
|
+
parentOwnerSessionId?: string;
|
|
16
|
+
parentCallId?: string;
|
|
17
|
+
source: ScopeBindingSource;
|
|
18
|
+
files: string[];
|
|
19
|
+
declaredAt: number;
|
|
20
|
+
expiresAt: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function canonicalWorkspaceIdentity(directory: string): string | null;
|
|
23
|
+
export declare function normalizeScopeFiles(files: readonly string[]): string[] | null;
|
|
24
|
+
export declare function createScopeBinding(input: {
|
|
25
|
+
directory: string;
|
|
26
|
+
plan: Plan;
|
|
27
|
+
taskId: string;
|
|
28
|
+
files: readonly string[];
|
|
29
|
+
ownerSessionId: string;
|
|
30
|
+
ownerMessageId: string;
|
|
31
|
+
source: Exclude<ScopeBindingSource, 'worktree_derived'>;
|
|
32
|
+
dispatchCallId?: string;
|
|
33
|
+
activation?: ScopeBinding['activation'];
|
|
34
|
+
ttlMs?: number;
|
|
35
|
+
}): ScopeBinding | null;
|
|
36
|
+
export declare function registerScopeBinding(binding: ScopeBinding): void;
|
|
37
|
+
export declare function getScopeBinding(input: {
|
|
38
|
+
directory: string;
|
|
39
|
+
plan: Plan;
|
|
40
|
+
taskId: string;
|
|
41
|
+
ownerSessionId: string;
|
|
42
|
+
ownerMessageId?: string;
|
|
43
|
+
}): ScopeBinding | null;
|
|
44
|
+
/** Resolve the one Task-correlated authorization for an executing coder. */
|
|
45
|
+
export declare function getAuthorizedScopeBinding(input: {
|
|
46
|
+
directory: string;
|
|
47
|
+
plan: Plan;
|
|
48
|
+
taskId: string;
|
|
49
|
+
activeSessionId: string;
|
|
50
|
+
}): ScopeBinding | null;
|
|
51
|
+
export declare function getAuthorizedScopeBindingByPlanIdentity(input: {
|
|
52
|
+
directory: string;
|
|
53
|
+
planId: string;
|
|
54
|
+
planStructureHash: string;
|
|
55
|
+
taskId: string;
|
|
56
|
+
activeSessionId: string;
|
|
57
|
+
}): ScopeBinding | null;
|
|
58
|
+
export declare function clearScopeBindings(predicate?: (binding: ScopeBinding) => boolean): ScopeBinding[];
|
|
59
|
+
/**
|
|
60
|
+
* Atomically claim the pending scope for the exact Task call whose upstream
|
|
61
|
+
* metadata identified the actual OpenCode child session. The call id is part of
|
|
62
|
+
* the authorization identity; parent-session FIFO guessing is never used.
|
|
63
|
+
*/
|
|
64
|
+
export declare function claimScopeBindingForChild(input: {
|
|
65
|
+
directory: string;
|
|
66
|
+
parentSessionId: string;
|
|
67
|
+
childSessionId: string;
|
|
68
|
+
dispatchCallId: string;
|
|
69
|
+
}): {
|
|
70
|
+
previous: ScopeBinding;
|
|
71
|
+
claimed: ScopeBinding;
|
|
72
|
+
} | null;
|
|
73
|
+
export declare function deriveChildScopeBinding(parent: ScopeBinding, input: {
|
|
74
|
+
childDirectory: string;
|
|
75
|
+
childSessionId: string;
|
|
76
|
+
parentCallId: string;
|
|
77
|
+
}): ScopeBinding;
|
|
78
|
+
export declare function resolveCoderScopeSources(input: {
|
|
79
|
+
explicitFiles: readonly string[] | null | undefined;
|
|
80
|
+
planFiles: readonly string[] | null | undefined;
|
|
81
|
+
fileDirectiveFiles: readonly string[] | null | undefined;
|
|
82
|
+
}): {
|
|
83
|
+
ok: true;
|
|
84
|
+
files: string[];
|
|
85
|
+
source: 'declare_scope' | 'plan' | 'file_directive';
|
|
86
|
+
} | {
|
|
87
|
+
ok: false;
|
|
88
|
+
code: 'SCOPE_NOT_DECLARED' | 'SCOPE_CONFLICT';
|
|
89
|
+
};
|
|
@@ -23,31 +23,35 @@
|
|
|
23
23
|
* * plan.json size capped at MAX_PLAN_BYTES (DoS cap)
|
|
24
24
|
* * Windows reserved device names rejected (CON, NUL, LPT1, …)
|
|
25
25
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
26
|
+
* AUTHORITY BOUNDARY:
|
|
27
|
+
* - Direct-write tools and statically detected shell/interpreter write targets
|
|
28
|
+
* resolve through the same active Task-correlated binding.
|
|
29
|
+
* - A coder write with no binding or an unverifiable target fails closed with
|
|
30
|
+
* SCOPE_NOT_DECLARED/SCOPE_VIOLATION. Shell syntax is not an authority bypass.
|
|
31
|
+
* - This is command-level containment, not an operating-system syscall sandbox.
|
|
32
|
+
*
|
|
33
|
+
* RESIDUAL RISKS:
|
|
34
|
+
* 1. Platform-portability of symlink guards:
|
|
32
35
|
* - realpath resolves POSIX symlinks and Windows junctions, but the
|
|
33
36
|
* Windows behaviour is not covered by CI (Linux-only test matrix).
|
|
34
37
|
* - O_NOFOLLOW is a no-op on Windows (falls back to 0). The realpath
|
|
35
38
|
* containment check on `.swarm/scopes/` remains the primary guard
|
|
36
39
|
* on that platform; leaf-file TOCTOU on Windows is not closed.
|
|
37
|
-
*
|
|
40
|
+
* 2. Stale lockfile DoS: a crashed writer leaves a lock for up to
|
|
38
41
|
* LOCK_STALE_MS (30s). During that window, concurrent `declare_scope`
|
|
39
42
|
* calls fail silently and the architect relies on in-memory state.
|
|
40
43
|
* Acceptable because in-memory state remains authoritative inside the
|
|
41
44
|
* live process; disk is a fallback.
|
|
42
|
-
*
|
|
45
|
+
* 3. Temp-file leak: a crash between `Bun.write(tmp)` and `renameSync`
|
|
43
46
|
* leaves `scope-{id}.json.tmp.{ts}.{rand}` files. No sweeper runs today;
|
|
44
47
|
* accumulation is bounded by `/swarm close` (which rm -rf's .swarm/scopes/).
|
|
45
48
|
*
|
|
46
|
-
* NOT a standalone security boundary.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* remains the durable long-term boundary.
|
|
49
|
+
* NOT a standalone syscall security boundary. Shell-write detection and the
|
|
50
|
+
* central write-target resolver provide command-level interception; this module
|
|
51
|
+
* supplies the durable, identity-bound authority they consume.
|
|
50
52
|
*/
|
|
53
|
+
import { type Plan } from '../config/plan-schema';
|
|
54
|
+
import { type ScopeBinding } from './scope-binding';
|
|
51
55
|
declare const SCOPE_SCHEMA_VERSION: 1;
|
|
52
56
|
export interface PersistedScope {
|
|
53
57
|
version: typeof SCOPE_SCHEMA_VERSION;
|
|
@@ -56,6 +60,7 @@ export interface PersistedScope {
|
|
|
56
60
|
expiresAt: number;
|
|
57
61
|
files: string[];
|
|
58
62
|
}
|
|
63
|
+
export type PersistedScopeBinding = ScopeBinding;
|
|
59
64
|
/**
|
|
60
65
|
* Write declared scope to `.swarm/scopes/scope-{taskId}.json` atomically.
|
|
61
66
|
* Safe to call concurrently — proper-lockfile serialises writers per-file.
|
|
@@ -64,6 +69,42 @@ export interface PersistedScope {
|
|
|
64
69
|
* hard requirement. In-memory state remains authoritative for the live process.
|
|
65
70
|
*/
|
|
66
71
|
export declare function writeScopeToDisk(directory: string, taskId: string, files: string[], ttlMs?: number): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Persist an identity-bound v2 authorization record. The v1 writer remains for
|
|
74
|
+
* compatibility with older non-authorization consumers, but strict Task
|
|
75
|
+
* preflight and declare_scope use only this schema.
|
|
76
|
+
*/
|
|
77
|
+
export declare function writeScopeBindingToDisk(directory: string, binding: ScopeBinding): Promise<void>;
|
|
78
|
+
/** Read and fully validate a v2 binding; v1 records never authorize here. */
|
|
79
|
+
export declare function readScopeBindingFromDisk(input: {
|
|
80
|
+
directory: string;
|
|
81
|
+
taskId: string;
|
|
82
|
+
plan: Plan;
|
|
83
|
+
ownerSessionId: string;
|
|
84
|
+
parentCallId?: string;
|
|
85
|
+
requireDispatchCorrelation?: boolean;
|
|
86
|
+
requireDeclaration?: boolean;
|
|
87
|
+
}): ScopeBinding | null;
|
|
88
|
+
/** Remove one identity-bound authorization without disturbing sibling sessions. */
|
|
89
|
+
export declare function clearScopeBindingFromDisk(input: {
|
|
90
|
+
directory: string;
|
|
91
|
+
taskId: string;
|
|
92
|
+
ownerSessionId: string;
|
|
93
|
+
}): void;
|
|
94
|
+
export declare function resolveAuthorizedScopeBinding(input: {
|
|
95
|
+
directory: string;
|
|
96
|
+
taskId: string;
|
|
97
|
+
activeSessionId: string;
|
|
98
|
+
}): ScopeBinding | null;
|
|
99
|
+
/**
|
|
100
|
+
* Recover one unambiguous child binding after an in-memory session restart.
|
|
101
|
+
* Every candidate is still checked against current plan identity, exact child
|
|
102
|
+
* session ownership, Task-call correlation, TTL, and active state.
|
|
103
|
+
*/
|
|
104
|
+
export declare function resolveAuthorizedScopeBindingForSession(input: {
|
|
105
|
+
directory: string;
|
|
106
|
+
activeSessionId: string;
|
|
107
|
+
}): ScopeBinding | null;
|
|
67
108
|
/**
|
|
68
109
|
* Read persisted scope for a task. Returns null on:
|
|
69
110
|
* - file missing
|
package/dist/state.d.ts
CHANGED
|
@@ -86,6 +86,13 @@ export interface AgentSessionState {
|
|
|
86
86
|
lastInvocationIdByAgent: Record<string, number>;
|
|
87
87
|
/** Active invocation windows keyed by "${agentName}:${invId}" */
|
|
88
88
|
windows: Record<string, InvocationWindow>;
|
|
89
|
+
/**
|
|
90
|
+
* In-memory only circuit for known non-transient tool failures. Deliberately
|
|
91
|
+
* omitted from snapshots so a restarted host never inherits a stale stop.
|
|
92
|
+
*/
|
|
93
|
+
nonTransientCircuit?: NonTransientCircuitState;
|
|
94
|
+
/** Bounded in-memory correlation for original commands replaced by wrappers. */
|
|
95
|
+
pendingToolExecutions?: Map<string, PendingToolExecution>;
|
|
89
96
|
/** Last tool-call threshold at which a compaction hint was issued */
|
|
90
97
|
lastCompactionHint: number;
|
|
91
98
|
/** Count of architect direct writes to non-.swarm/ files */
|
|
@@ -265,6 +272,22 @@ export interface AgentSessionState {
|
|
|
265
272
|
*/
|
|
266
273
|
cachedCohortId?: string;
|
|
267
274
|
}
|
|
275
|
+
export type NonTransientErrorCategory = 'shell_parse_error' | 'command_not_found' | 'sandbox_wrapper_failure' | 'general_permanent';
|
|
276
|
+
export interface NonTransientCircuitState {
|
|
277
|
+
ownerAgent: string;
|
|
278
|
+
ownerInvocationId: number;
|
|
279
|
+
category: NonTransientErrorCategory | null;
|
|
280
|
+
sameCategoryCount: number;
|
|
281
|
+
hardStop: boolean;
|
|
282
|
+
lastSignal: string | null;
|
|
283
|
+
}
|
|
284
|
+
export interface PendingToolExecution {
|
|
285
|
+
tool: string;
|
|
286
|
+
originalCommand: string;
|
|
287
|
+
sandboxWrapped: boolean;
|
|
288
|
+
ownerAgent: string;
|
|
289
|
+
ownerInvocationId: number;
|
|
290
|
+
}
|
|
268
291
|
/**
|
|
269
292
|
* Represents a single agent invocation window with isolated guardrail budgets.
|
|
270
293
|
* Each time the architect delegates to an agent, a new window is created.
|
|
@@ -475,7 +498,8 @@ export declare function updateAgentEventTime(sessionId: string): void;
|
|
|
475
498
|
/**
|
|
476
499
|
* Begin a new invocation window for the given agent.
|
|
477
500
|
* Increments invocation ID, creates fresh budget counters.
|
|
478
|
-
* Returns null for architect (unlimited, no window)
|
|
501
|
+
* Returns null for architect (unlimited, no budget window), while still
|
|
502
|
+
* advancing the invocation identity and clearing invocation-local safety state.
|
|
479
503
|
*
|
|
480
504
|
* @param sessionId - Session identifier
|
|
481
505
|
* @param agentName - Agent name (with or without swarm prefix)
|
|
@@ -43,7 +43,11 @@ export declare function validateFiles(files: string[]): string[];
|
|
|
43
43
|
* @param fallbackDir - Fallback directory for plan lookup
|
|
44
44
|
* @returns DeclareScopeResult with success status and details
|
|
45
45
|
*/
|
|
46
|
-
export declare function executeDeclareScope(args: DeclareScopeArgs, fallbackDir?: string
|
|
46
|
+
export declare function executeDeclareScope(args: DeclareScopeArgs, fallbackDir?: string, owner?: {
|
|
47
|
+
sessionID: string;
|
|
48
|
+
messageID: string;
|
|
49
|
+
agentName?: string;
|
|
50
|
+
}): Promise<DeclareScopeResult>;
|
|
47
51
|
/**
|
|
48
52
|
* Tool definition for declare_scope
|
|
49
53
|
*/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface ExtractCodeBlocksArgs {
|
|
2
|
+
content?: string;
|
|
3
|
+
output_dir?: string;
|
|
4
|
+
prefix?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface PlannedExtractFile {
|
|
7
|
+
filename: string;
|
|
8
|
+
absolutePath: string;
|
|
9
|
+
relativePath: string;
|
|
10
|
+
code: string;
|
|
11
|
+
}
|
|
12
|
+
export type ExtractCodeBlocksPlan = {
|
|
13
|
+
status: 'ready';
|
|
14
|
+
targetDir: string;
|
|
15
|
+
files: PlannedExtractFile[];
|
|
16
|
+
} | {
|
|
17
|
+
status: 'noop';
|
|
18
|
+
reason: 'content-required' | 'no-code-blocks';
|
|
19
|
+
} | {
|
|
20
|
+
status: 'invalid';
|
|
21
|
+
reason: string;
|
|
22
|
+
};
|
|
23
|
+
/** Extract a deterministic filename from code content or context. */
|
|
24
|
+
export declare function extractFilename(code: string, language: string, index: number): string;
|
|
25
|
+
export declare function planExtractCodeBlocks(args: ExtractCodeBlocksArgs, directory: string): ExtractCodeBlocksPlan;
|
|
26
|
+
/**
|
|
27
|
+
* Plan once per tool-argument object so every pre-execution guard and the tool
|
|
28
|
+
* execution itself use the exact same collision-resolved targets.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getOrPlanExtractCodeBlocks(args: ExtractCodeBlocksArgs, directory: string): ExtractCodeBlocksPlan;
|
|
31
|
+
export declare function consumeExtractCodeBlocksPlan(args: ExtractCodeBlocksArgs, directory: string): ExtractCodeBlocksPlan;
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import type { ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
5
|
-
export declare function extractFilename(code: string, language: string, index: number): string;
|
|
6
|
-
/**
|
|
7
|
-
* Extract code blocks from content and save to files
|
|
8
|
-
*/
|
|
2
|
+
export { extractFilename } from './file-extractor-planner';
|
|
3
|
+
/** Extract code blocks from content and save them as one atomic write set. */
|
|
9
4
|
export declare const extract_code_blocks: ToolDefinition;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.122.
|
|
3
|
+
"version": "7.122.1",
|
|
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",
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import {
|
|
3
|
-
require_proper_lockfile
|
|
4
|
-
} from "./index-7g4c7s5r.js";
|
|
5
|
-
import {
|
|
6
|
-
bunWrite
|
|
7
|
-
} from "./index-qe3v54nb.js";
|
|
8
|
-
import {
|
|
9
|
-
__toESM
|
|
10
|
-
} from "./index-a76rekgs.js";
|
|
11
|
-
|
|
12
|
-
// src/scope/scope-persistence.ts
|
|
13
|
-
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
14
|
-
import * as fs from "fs";
|
|
15
|
-
import * as path from "path";
|
|
16
|
-
var SCOPE_SCHEMA_VERSION = 1;
|
|
17
|
-
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
18
|
-
var LOCK_STALE_MS = 30 * 1000;
|
|
19
|
-
var SCOPES_DIR = ".swarm/scopes";
|
|
20
|
-
var MAX_FILES_PER_SCOPE = 1e4;
|
|
21
|
-
var MAX_PLAN_BYTES = 10 * 1024 * 1024;
|
|
22
|
-
var MAX_SCOPE_BYTES = 2 * 1024 * 1024;
|
|
23
|
-
var WINDOWS_RESERVED = new Set([
|
|
24
|
-
"CON",
|
|
25
|
-
"PRN",
|
|
26
|
-
"AUX",
|
|
27
|
-
"NUL",
|
|
28
|
-
"COM1",
|
|
29
|
-
"COM2",
|
|
30
|
-
"COM3",
|
|
31
|
-
"COM4",
|
|
32
|
-
"COM5",
|
|
33
|
-
"COM6",
|
|
34
|
-
"COM7",
|
|
35
|
-
"COM8",
|
|
36
|
-
"COM9",
|
|
37
|
-
"LPT1",
|
|
38
|
-
"LPT2",
|
|
39
|
-
"LPT3",
|
|
40
|
-
"LPT4",
|
|
41
|
-
"LPT5",
|
|
42
|
-
"LPT6",
|
|
43
|
-
"LPT7",
|
|
44
|
-
"LPT8",
|
|
45
|
-
"LPT9"
|
|
46
|
-
]);
|
|
47
|
-
function getScopesDir(directory) {
|
|
48
|
-
return path.join(directory, SCOPES_DIR);
|
|
49
|
-
}
|
|
50
|
-
function isSafeTaskId(taskId) {
|
|
51
|
-
if (typeof taskId !== "string")
|
|
52
|
-
return false;
|
|
53
|
-
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(taskId))
|
|
54
|
-
return false;
|
|
55
|
-
const head = taskId.replace(/\.+$/, "").split(".")[0].toUpperCase();
|
|
56
|
-
if (WINDOWS_RESERVED.has(head))
|
|
57
|
-
return false;
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
function isScopesDirSafe(directory, scopesDir) {
|
|
61
|
-
try {
|
|
62
|
-
const resolvedWorkspace = fs.realpathSync(directory);
|
|
63
|
-
const resolvedScopes = fs.realpathSync(scopesDir);
|
|
64
|
-
const rel = path.relative(resolvedWorkspace, resolvedScopes);
|
|
65
|
-
return rel.length > 0 && !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
66
|
-
} catch {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function getScopeFilePath(directory, taskId) {
|
|
71
|
-
if (!isSafeTaskId(taskId)) {
|
|
72
|
-
throw new Error(`Invalid taskId for scope persistence: ${taskId}`);
|
|
73
|
-
}
|
|
74
|
-
return path.join(getScopesDir(directory), `scope-${taskId}.json`);
|
|
75
|
-
}
|
|
76
|
-
async function writeScopeToDisk(directory, taskId, files, ttlMs = DEFAULT_TTL_MS) {
|
|
77
|
-
if (!isSafeTaskId(taskId))
|
|
78
|
-
return;
|
|
79
|
-
if (!Array.isArray(files) || files.length === 0)
|
|
80
|
-
return;
|
|
81
|
-
if (files.length > MAX_FILES_PER_SCOPE)
|
|
82
|
-
return;
|
|
83
|
-
const scopesDir = getScopesDir(directory);
|
|
84
|
-
const scopePath = getScopeFilePath(directory, taskId);
|
|
85
|
-
try {
|
|
86
|
-
fs.mkdirSync(scopesDir, { recursive: true });
|
|
87
|
-
} catch {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (!isScopesDirSafe(directory, scopesDir))
|
|
91
|
-
return;
|
|
92
|
-
const now = Date.now();
|
|
93
|
-
const payload = {
|
|
94
|
-
version: SCOPE_SCHEMA_VERSION,
|
|
95
|
-
taskId,
|
|
96
|
-
declaredAt: now,
|
|
97
|
-
expiresAt: now + ttlMs,
|
|
98
|
-
files: [...files]
|
|
99
|
-
};
|
|
100
|
-
const content = JSON.stringify(payload, null, 2);
|
|
101
|
-
try {
|
|
102
|
-
const flags = fs.constants.O_WRONLY | fs.constants.O_CREAT;
|
|
103
|
-
const nofollow = fs.constants.O_NOFOLLOW ?? 0;
|
|
104
|
-
const fd = fs.openSync(scopePath, flags | nofollow);
|
|
105
|
-
fs.closeSync(fd);
|
|
106
|
-
} catch {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
let release;
|
|
110
|
-
try {
|
|
111
|
-
release = await import_proper_lockfile.default.lock(scopePath, {
|
|
112
|
-
stale: LOCK_STALE_MS,
|
|
113
|
-
retries: { retries: 3, minTimeout: 50, maxTimeout: 200 },
|
|
114
|
-
realpath: false
|
|
115
|
-
});
|
|
116
|
-
await atomicWrite(scopePath, content);
|
|
117
|
-
} catch {} finally {
|
|
118
|
-
if (release) {
|
|
119
|
-
try {
|
|
120
|
-
await release();
|
|
121
|
-
} catch {}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
async function atomicWrite(targetPath, content) {
|
|
126
|
-
const tempPath = `${targetPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
|
|
127
|
-
try {
|
|
128
|
-
await bunWrite(tempPath, content);
|
|
129
|
-
fs.renameSync(tempPath, targetPath);
|
|
130
|
-
} finally {
|
|
131
|
-
try {
|
|
132
|
-
fs.unlinkSync(tempPath);
|
|
133
|
-
} catch {}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
function readScopeFromDisk(directory, taskId) {
|
|
137
|
-
if (!isSafeTaskId(taskId))
|
|
138
|
-
return null;
|
|
139
|
-
const scopesDir = getScopesDir(directory);
|
|
140
|
-
if (!isScopesDirSafe(directory, scopesDir))
|
|
141
|
-
return null;
|
|
142
|
-
const scopePath = getScopeFilePath(directory, taskId);
|
|
143
|
-
const nofollow = fs.constants.O_NOFOLLOW ?? 0;
|
|
144
|
-
let fd;
|
|
145
|
-
try {
|
|
146
|
-
fd = fs.openSync(scopePath, fs.constants.O_RDONLY | nofollow);
|
|
147
|
-
} catch {
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
let raw;
|
|
151
|
-
try {
|
|
152
|
-
const stat = fs.fstatSync(fd);
|
|
153
|
-
if (!stat.isFile())
|
|
154
|
-
return null;
|
|
155
|
-
if (stat.size > MAX_SCOPE_BYTES)
|
|
156
|
-
return null;
|
|
157
|
-
const buf = Buffer.alloc(stat.size);
|
|
158
|
-
fs.readSync(fd, buf, 0, stat.size, 0);
|
|
159
|
-
raw = buf.toString("utf-8");
|
|
160
|
-
} catch {
|
|
161
|
-
return null;
|
|
162
|
-
} finally {
|
|
163
|
-
try {
|
|
164
|
-
fs.closeSync(fd);
|
|
165
|
-
} catch {}
|
|
166
|
-
}
|
|
167
|
-
if (!raw.trim())
|
|
168
|
-
return null;
|
|
169
|
-
let parsed;
|
|
170
|
-
try {
|
|
171
|
-
parsed = JSON.parse(raw);
|
|
172
|
-
} catch {
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
if (parsed.version !== SCOPE_SCHEMA_VERSION)
|
|
176
|
-
return null;
|
|
177
|
-
if (parsed.taskId !== taskId)
|
|
178
|
-
return null;
|
|
179
|
-
const now = Date.now();
|
|
180
|
-
if (typeof parsed.declaredAt !== "number" || parsed.declaredAt > now) {
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
if (typeof parsed.expiresAt !== "number" || now >= parsed.expiresAt) {
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
if (!Array.isArray(parsed.files))
|
|
187
|
-
return null;
|
|
188
|
-
if (parsed.files.length > MAX_FILES_PER_SCOPE)
|
|
189
|
-
return null;
|
|
190
|
-
const files = parsed.files.filter((f) => typeof f === "string");
|
|
191
|
-
return files.length > 0 ? files : null;
|
|
192
|
-
}
|
|
193
|
-
function readPlanScope(directory, taskId) {
|
|
194
|
-
if (!isSafeTaskId(taskId))
|
|
195
|
-
return null;
|
|
196
|
-
try {
|
|
197
|
-
const planPath = path.join(directory, ".swarm", "plan.json");
|
|
198
|
-
const stat = fs.statSync(planPath);
|
|
199
|
-
if (!stat.isFile())
|
|
200
|
-
return null;
|
|
201
|
-
if (stat.size > MAX_PLAN_BYTES)
|
|
202
|
-
return null;
|
|
203
|
-
const raw = fs.readFileSync(planPath, "utf-8");
|
|
204
|
-
const plan = JSON.parse(raw);
|
|
205
|
-
for (const phase of plan.phases ?? []) {
|
|
206
|
-
for (const task of phase.tasks ?? []) {
|
|
207
|
-
if (task.id !== taskId)
|
|
208
|
-
continue;
|
|
209
|
-
const ft = task.files_touched;
|
|
210
|
-
if (Array.isArray(ft) && ft.length > 0) {
|
|
211
|
-
if (ft.length > MAX_FILES_PER_SCOPE)
|
|
212
|
-
return null;
|
|
213
|
-
return ft.filter((f) => typeof f === "string");
|
|
214
|
-
}
|
|
215
|
-
if (typeof ft === "string" && ft.length > 0)
|
|
216
|
-
return [ft];
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
return null;
|
|
221
|
-
} catch {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
function clearScopeForTask(directory, taskId) {
|
|
226
|
-
if (!isSafeTaskId(taskId))
|
|
227
|
-
return;
|
|
228
|
-
try {
|
|
229
|
-
fs.unlinkSync(getScopeFilePath(directory, taskId));
|
|
230
|
-
} catch {}
|
|
231
|
-
}
|
|
232
|
-
function clearAllScopes(directory) {
|
|
233
|
-
try {
|
|
234
|
-
fs.rmSync(getScopesDir(directory), { recursive: true, force: true });
|
|
235
|
-
} catch {}
|
|
236
|
-
}
|
|
237
|
-
function resolveScopeWithFallbacks(input) {
|
|
238
|
-
const { directory, taskId, inMemoryScope, pendingMapScope } = input;
|
|
239
|
-
if (inMemoryScope && inMemoryScope.length > 0)
|
|
240
|
-
return inMemoryScope;
|
|
241
|
-
if (taskId) {
|
|
242
|
-
const disk = readScopeFromDisk(directory, taskId);
|
|
243
|
-
if (disk && disk.length > 0)
|
|
244
|
-
return disk;
|
|
245
|
-
const plan = readPlanScope(directory, taskId);
|
|
246
|
-
if (plan && plan.length > 0)
|
|
247
|
-
return plan;
|
|
248
|
-
}
|
|
249
|
-
if (pendingMapScope && pendingMapScope.length > 0)
|
|
250
|
-
return pendingMapScope;
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export { writeScopeToDisk, readScopeFromDisk, readPlanScope, clearScopeForTask, clearAllScopes, resolveScopeWithFallbacks };
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import {
|
|
3
|
-
clearAllScopes,
|
|
4
|
-
clearScopeForTask,
|
|
5
|
-
readPlanScope,
|
|
6
|
-
readScopeFromDisk,
|
|
7
|
-
resolveScopeWithFallbacks,
|
|
8
|
-
writeScopeToDisk
|
|
9
|
-
} from "./index-51gg9947.js";
|
|
10
|
-
import"./index-7g4c7s5r.js";
|
|
11
|
-
import"./index-qe3v54nb.js";
|
|
12
|
-
import"./index-a76rekgs.js";
|
|
13
|
-
export {
|
|
14
|
-
writeScopeToDisk,
|
|
15
|
-
resolveScopeWithFallbacks,
|
|
16
|
-
readScopeFromDisk,
|
|
17
|
-
readPlanScope,
|
|
18
|
-
clearScopeForTask,
|
|
19
|
-
clearAllScopes
|
|
20
|
-
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import {
|
|
3
|
-
withEvidenceLock
|
|
4
|
-
} from "./index-76kvm9z5.js";
|
|
5
2
|
import {
|
|
6
3
|
invalidateCachedArtifact
|
|
7
4
|
} from "./index-fsrp8wp3.js";
|
|
5
|
+
import {
|
|
6
|
+
withEvidenceLock
|
|
7
|
+
} from "./index-76kvm9z5.js";
|
|
8
8
|
import {
|
|
9
9
|
bunWrite
|
|
10
10
|
} from "./index-qe3v54nb.js";
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
init_logger,
|
|
9
9
|
warn
|
|
10
10
|
} from "./index-zgwm4ryv.js";
|
|
11
|
+
import"./index-fsrp8wp3.js";
|
|
11
12
|
import {
|
|
12
13
|
withEvidenceLock
|
|
13
14
|
} from "./index-76kvm9z5.js";
|
|
14
|
-
import"./index-fsrp8wp3.js";
|
|
15
15
|
import"./index-q2rrv0cy.js";
|
|
16
16
|
import {
|
|
17
17
|
exports_external
|