opencode-swarm 7.74.2 → 7.75.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +771 -128
- package/dist/commands/command-dispatch.d.ts +0 -3
- package/dist/commands/full-auto.d.ts +15 -3
- package/dist/commands/registry.d.ts +3 -3
- package/dist/config/loader.d.ts +6 -11
- package/dist/config/schema.d.ts +22 -0
- package/dist/full-auto/state.d.ts +12 -0
- package/dist/hooks/auto-review.d.ts +82 -0
- package/dist/hooks/review-receipt-collector.d.ts +58 -0
- package/dist/hooks/review-receipt.d.ts +7 -0
- package/dist/index.js +2754 -1631
- package/dist/services/config-doctor.d.ts +15 -0
- package/dist/session/snapshot-reader.d.ts +1 -1
- package/dist/turbo/lean/reviewer.d.ts +3 -1
- package/package.json +1 -1
|
@@ -19,15 +19,12 @@ export declare function normalizeSwarmCommandInput(command: string, argumentText
|
|
|
19
19
|
};
|
|
20
20
|
export declare function canonicalCommandKey(resolved: ResolvedSwarmCommand): string;
|
|
21
21
|
export declare function formatCommandNotFound(tokens: string[]): string;
|
|
22
|
-
export declare function maybeMarkFirstRun(directory: string): boolean;
|
|
23
|
-
export declare function prependWelcome(text: string): string;
|
|
24
22
|
export declare function executeSwarmCommand(args: {
|
|
25
23
|
directory: string;
|
|
26
24
|
agents: Record<string, AgentDefinition>;
|
|
27
25
|
sessionID: string;
|
|
28
26
|
tokens: string[];
|
|
29
27
|
packageRoot?: string;
|
|
30
|
-
includeWelcome?: boolean;
|
|
31
28
|
buildHelpText?: () => string;
|
|
32
29
|
policy?: SwarmCommandPolicy;
|
|
33
30
|
}): Promise<SwarmCommandExecutionResult>;
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Handles the /swarm full-auto command.
|
|
3
|
-
*
|
|
3
|
+
* First-class session toggle for Full-Auto Mode: on / off / status / bare toggle.
|
|
4
|
+
*
|
|
5
|
+
* Full-Auto no longer requires `full_auto.enabled: true` in the plugin config —
|
|
6
|
+
* the v2 hooks are always armed and gated at runtime by the durable per-session
|
|
7
|
+
* run state, so activation is a pure runtime decision (like switching permission
|
|
8
|
+
* modes in other agent CLIs). Administrators can set `full_auto.locked: true`
|
|
9
|
+
* to refuse runtime activation entirely.
|
|
10
|
+
*
|
|
11
|
+
* `on` accepts an optional mode argument (`assisted` | `supervised` | `strict`)
|
|
12
|
+
* that overrides `full_auto.mode` for this run. In every mode the critic
|
|
13
|
+
* reviews escalations on the user's behalf; `strict` routes ALL plan mutations
|
|
14
|
+
* through the critic, `supervised` (default) routes risky/high-impact actions,
|
|
15
|
+
* `assisted` only consults the critic when the deterministic policy escalates.
|
|
4
16
|
*
|
|
5
17
|
* In Full-Auto v2 this also creates a durable run-state record under
|
|
6
18
|
* .swarm/full-auto-state.json so the permission/oversight infrastructure can
|
|
7
19
|
* fail-closed across hooks and across process restarts.
|
|
8
20
|
*
|
|
9
|
-
* H2 fix: durable write happens BEFORE flipping the legacy
|
|
21
|
+
* H2 fix (preserved): durable write happens BEFORE flipping the legacy
|
|
10
22
|
* `session.fullAutoMode` flag. If the durable write fails, the command
|
|
11
23
|
* surfaces the error in its return string and does NOT enable the legacy
|
|
12
24
|
* reactive intercept — preventing a silent fail-open where reactive checks
|
|
@@ -14,7 +26,7 @@
|
|
|
14
26
|
* durable run.
|
|
15
27
|
*
|
|
16
28
|
* @param directory - Project directory (used to persist Full-Auto run state)
|
|
17
|
-
* @param args -
|
|
29
|
+
* @param args - "on [mode]" | "off" | "status" | undefined (toggle behavior)
|
|
18
30
|
* @param sessionID - Session ID for accessing active session state
|
|
19
31
|
* @returns Feedback message about Full-Auto Mode state
|
|
20
32
|
*/
|
|
@@ -501,9 +501,9 @@ export declare const COMMAND_REGISTRY: {
|
|
|
501
501
|
};
|
|
502
502
|
readonly 'full-auto': {
|
|
503
503
|
readonly handler: (ctx: CommandContext) => Promise<string>;
|
|
504
|
-
readonly description: "Toggle Full-Auto Mode for the active session [on|off]";
|
|
505
|
-
readonly args: "on, off";
|
|
506
|
-
readonly details:
|
|
504
|
+
readonly description: "Toggle Full-Auto Mode for the active session [on [mode]|off|status]";
|
|
505
|
+
readonly args: "on [assisted|supervised|strict], off, status";
|
|
506
|
+
readonly details: string;
|
|
507
507
|
readonly category: "utility";
|
|
508
508
|
};
|
|
509
509
|
readonly 'auto-proceed': {
|
package/dist/config/loader.d.ts
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
import { type PluginConfig } from './schema';
|
|
2
2
|
export declare const MAX_CONFIG_FILE_BYTES = 102400;
|
|
3
3
|
export { deepMerge, MAX_MERGE_DEPTH } from '../utils/merge';
|
|
4
|
-
/**
|
|
5
|
-
* Load plugin configuration from user and project config files.
|
|
6
|
-
*
|
|
7
|
-
* Config locations:
|
|
8
|
-
* 1. User config: ~/.config/opencode/opencode-swarm.json
|
|
9
|
-
* 2. Project config: <directory>/.opencode/opencode-swarm.json
|
|
10
|
-
*
|
|
11
|
-
* Project config takes precedence. Nested objects are deep-merged.
|
|
12
|
-
* IMPORTANT: Raw configs are merged BEFORE Zod parsing so that
|
|
13
|
-
* Zod defaults don't override explicit user values.
|
|
14
|
-
*/
|
|
15
4
|
export declare function loadPluginConfig(directory: string): PluginConfig;
|
|
16
5
|
/**
|
|
17
6
|
* Internal variant of loadPluginConfig that also returns loader metadata.
|
|
@@ -21,6 +10,11 @@ export declare function loadPluginConfig(directory: string): PluginConfig;
|
|
|
21
10
|
export declare function loadPluginConfigWithMeta(directory: string): {
|
|
22
11
|
config: PluginConfig;
|
|
23
12
|
loadedFromFile: boolean;
|
|
13
|
+
/** True when a config file existed but could not be loaded (corrupt JSON,
|
|
14
|
+
* oversized, permission error). Consumers with fail-closed semantics —
|
|
15
|
+
* e.g. the Full-Auto `locked` activation guard — must treat this as
|
|
16
|
+
* "config unknown", not "config defaults". */
|
|
17
|
+
configHadErrors: boolean;
|
|
24
18
|
};
|
|
25
19
|
/**
|
|
26
20
|
* Async variant of `loadPluginConfigWithMeta`. Used by the plugin entry
|
|
@@ -29,6 +23,7 @@ export declare function loadPluginConfigWithMeta(directory: string): {
|
|
|
29
23
|
export declare function loadPluginConfigWithMetaAsync(directory: string): Promise<{
|
|
30
24
|
config: PluginConfig;
|
|
31
25
|
loadedFromFile: boolean;
|
|
26
|
+
configHadErrors: boolean;
|
|
32
27
|
}>;
|
|
33
28
|
/**
|
|
34
29
|
* Load custom prompt for an agent from the prompts directory.
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -333,6 +333,17 @@ export declare const ReviewPassesConfigSchema: z.ZodObject<{
|
|
|
333
333
|
security_globs: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
334
334
|
}, z.core.$strip>;
|
|
335
335
|
export type ReviewPassesConfig = z.infer<typeof ReviewPassesConfigSchema>;
|
|
336
|
+
export declare const AutoReviewConfigSchema: z.ZodObject<{
|
|
337
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
338
|
+
trigger: z.ZodDefault<z.ZodEnum<{
|
|
339
|
+
task_completion: "task_completion";
|
|
340
|
+
phase_boundary: "phase_boundary";
|
|
341
|
+
both: "both";
|
|
342
|
+
}>>;
|
|
343
|
+
timeout_ms: z.ZodDefault<z.ZodNumber>;
|
|
344
|
+
max_diff_kb: z.ZodDefault<z.ZodNumber>;
|
|
345
|
+
}, z.core.$strip>;
|
|
346
|
+
export type AutoReviewConfig = z.infer<typeof AutoReviewConfigSchema>;
|
|
336
347
|
export declare const AdversarialDetectionConfigSchema: z.ZodObject<{
|
|
337
348
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
338
349
|
policy: z.ZodDefault<z.ZodEnum<{
|
|
@@ -1392,6 +1403,16 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
1392
1403
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1393
1404
|
skip_in_turbo: z.ZodDefault<z.ZodBoolean>;
|
|
1394
1405
|
}, z.core.$strip>>;
|
|
1406
|
+
auto_review: z.ZodOptional<z.ZodObject<{
|
|
1407
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1408
|
+
trigger: z.ZodDefault<z.ZodEnum<{
|
|
1409
|
+
task_completion: "task_completion";
|
|
1410
|
+
phase_boundary: "phase_boundary";
|
|
1411
|
+
both: "both";
|
|
1412
|
+
}>>;
|
|
1413
|
+
timeout_ms: z.ZodDefault<z.ZodNumber>;
|
|
1414
|
+
max_diff_kb: z.ZodDefault<z.ZodNumber>;
|
|
1415
|
+
}, z.core.$strip>>;
|
|
1395
1416
|
tool_filter: z.ZodOptional<z.ZodObject<{
|
|
1396
1417
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1397
1418
|
overrides: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
@@ -1858,6 +1879,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
1858
1879
|
version_check: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
1859
1880
|
full_auto: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
1860
1881
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1882
|
+
locked: z.ZodDefault<z.ZodBoolean>;
|
|
1861
1883
|
critic_model: z.ZodOptional<z.ZodString>;
|
|
1862
1884
|
max_interactions_per_phase: z.ZodDefault<z.ZodNumber>;
|
|
1863
1885
|
deadlock_threshold: z.ZodDefault<z.ZodNumber>;
|
|
@@ -88,6 +88,18 @@ export declare function startFullAutoRun(directory: string, sessionID: string, c
|
|
|
88
88
|
taskID?: string;
|
|
89
89
|
}): FullAutoRunState;
|
|
90
90
|
export declare function pauseFullAutoRun(directory: string, sessionID: string, reason: string): FullAutoRunState | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Disarm a Full-Auto run in response to an explicit user `off`.
|
|
93
|
+
*
|
|
94
|
+
* Unlike `pauseFullAutoRun` / `terminateFullAutoRun` (system-initiated halts
|
|
95
|
+
* that fail-closed-block non-read-only tools until the user re-enables),
|
|
96
|
+
* disarming returns the session to normal interactive operation: the record
|
|
97
|
+
* transitions to `'idle'`, which every enforcement path treats as
|
|
98
|
+
* "no active Full-Auto run". Counters and denial history are preserved for
|
|
99
|
+
* audit. (Adversarial review F3: `off` must not be a one-way door into a
|
|
100
|
+
* write-blocked session.)
|
|
101
|
+
*/
|
|
102
|
+
export declare function disarmFullAutoRun(directory: string, sessionID: string, reason: string): FullAutoRunState | undefined;
|
|
91
103
|
export declare function terminateFullAutoRun(directory: string, sessionID: string, reason: string): FullAutoRunState | undefined;
|
|
92
104
|
export declare function isFullAutoRunActive(directory: string, sessionID: string): boolean;
|
|
93
105
|
export type FullAutoCounterKey = keyof FullAutoCounters;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-review hook (auto-review machinery, piece B) — opt-in.
|
|
3
|
+
*
|
|
4
|
+
* When `auto_review.enabled` is true, completing a task
|
|
5
|
+
* (`update_task_status` → status 'completed') and/or a phase
|
|
6
|
+
* (`phase_complete`) automatically dispatches the registered reviewer agent
|
|
7
|
+
* over a fresh ephemeral session to review the current execution diff —
|
|
8
|
+
* the same "second model reviews the work in a clean context" pattern used
|
|
9
|
+
* by Claude Code's auto-review and Codex's review model. The reviewer agent
|
|
10
|
+
* carries its own configured model (`agents.reviewer.model`), so the review
|
|
11
|
+
* model is independently configurable from the coder/architect models.
|
|
12
|
+
*
|
|
13
|
+
* The pass is ADVISORY and fully fail-open:
|
|
14
|
+
* - fire-and-forget from `tool.execute.after` (never blocks the tool)
|
|
15
|
+
* - verdicts are persisted as durable review receipts
|
|
16
|
+
* (`.swarm/review-receipts/`, scope-fingerprinted over the diff) and an
|
|
17
|
+
* `auto_review` event is appended to `.swarm/events.jsonl`
|
|
18
|
+
* - a REJECTED or unparseable verdict injects a `[AUTO-REVIEW]` advisory
|
|
19
|
+
* into the architect's next prompt; APPROVED stays silent
|
|
20
|
+
*
|
|
21
|
+
* Bounds (AGENTS.md invariants 3 and 8): the diff subprocess uses execFile
|
|
22
|
+
* with cwd/timeout/maxBuffer and ignored stdin; dispatches are guarded by a
|
|
23
|
+
* per-session in-flight set plus a 60s cooldown in a bounded FIFO map.
|
|
24
|
+
*/
|
|
25
|
+
import { type AutoReviewConfig } from '../config/schema.js';
|
|
26
|
+
/** Test-only: clear module-level dispatch tracking. */
|
|
27
|
+
export declare function resetAutoReviewTracking(): void;
|
|
28
|
+
export type ExecutionDiffResult = {
|
|
29
|
+
status: 'ok';
|
|
30
|
+
diff: string;
|
|
31
|
+
} | {
|
|
32
|
+
status: 'clean';
|
|
33
|
+
} | {
|
|
34
|
+
status: 'error';
|
|
35
|
+
reason: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Collect the execution diff for review: `git diff HEAD` (tracked changes)
|
|
39
|
+
* plus a porcelain summary of untracked files. Distinguishes a clean working
|
|
40
|
+
* tree from collection failures (git missing, timeout, diff exceeding the
|
|
41
|
+
* 2× maxBuffer cap) so events report honestly. Output is truncated to
|
|
42
|
+
* `maxBytes`.
|
|
43
|
+
*/
|
|
44
|
+
declare function computeExecutionDiff(directory: string, maxBytes: number): Promise<ExecutionDiffResult>;
|
|
45
|
+
declare function dispatchReviewer(directory: string, prompt: string, agentName: string, timeoutMs: number): Promise<string>;
|
|
46
|
+
export interface AutoReviewRunInput {
|
|
47
|
+
directory: string;
|
|
48
|
+
sessionID: string;
|
|
49
|
+
trigger: 'task_completion' | 'phase_boundary';
|
|
50
|
+
taskId?: string;
|
|
51
|
+
phase?: number;
|
|
52
|
+
config: Required<Pick<AutoReviewConfig, 'timeout_ms' | 'max_diff_kb'>>;
|
|
53
|
+
injectAdvisory: (sessionId: string, message: string) => void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Execute one auto-review pass: collect diff → dispatch reviewer over an
|
|
57
|
+
* ephemeral session → persist receipt + event → advisory on REJECTED or
|
|
58
|
+
* unparseable output. Fully fail-open; never throws.
|
|
59
|
+
*/
|
|
60
|
+
export declare function runAutoReview(input: AutoReviewRunInput): Promise<void>;
|
|
61
|
+
export interface AutoReviewHookOptions {
|
|
62
|
+
config: AutoReviewConfig;
|
|
63
|
+
directory: string;
|
|
64
|
+
injectAdvisory: (sessionId: string, message: string) => void;
|
|
65
|
+
}
|
|
66
|
+
export declare function createAutoReviewHook(options: AutoReviewHookOptions): {
|
|
67
|
+
toolAfter: (input: {
|
|
68
|
+
tool: string;
|
|
69
|
+
sessionID: string;
|
|
70
|
+
callID?: string;
|
|
71
|
+
}, output: {
|
|
72
|
+
args?: unknown;
|
|
73
|
+
output?: unknown;
|
|
74
|
+
}) => Promise<void>;
|
|
75
|
+
};
|
|
76
|
+
export declare const _internals: {
|
|
77
|
+
computeExecutionDiff: typeof computeExecutionDiff;
|
|
78
|
+
dispatchReviewer: typeof dispatchReviewer;
|
|
79
|
+
runAutoReview: typeof runAutoReview;
|
|
80
|
+
now: () => number;
|
|
81
|
+
};
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reviewer receipt collector (auto-review machinery, piece A).
|
|
3
|
+
*
|
|
4
|
+
* Parses the mandated reviewer OUTPUT FORMAT (`VERDICT:` / `RISK:` /
|
|
5
|
+
* `ISSUES:` / `FIXES:`) from a returning reviewer Task delegation and
|
|
6
|
+
* persists it as a durable review receipt under `.swarm/review-receipts/`
|
|
7
|
+
* via the existing receipt store (scope-fingerprinted over the delegation
|
|
8
|
+
* prompt, which defines the reviewed scope).
|
|
9
|
+
*
|
|
10
|
+
* Before this collector, reviewer verdicts existed only as free text inside
|
|
11
|
+
* the architect's context — re-reviews and drift verification had no durable
|
|
12
|
+
* machine-readable record of what the reviewer decided. Knowledge-directive
|
|
13
|
+
* lines (`DIRECTIVE_COMPLIANCE`) are handled separately by
|
|
14
|
+
* `reviewer-verdict-parser.ts`; this module covers the main verdict block.
|
|
15
|
+
*
|
|
16
|
+
* Fail-open: parsing or persistence failures never block tool execution.
|
|
17
|
+
*/
|
|
18
|
+
export type ParsedReviewSeverity = 'critical' | 'high' | 'medium';
|
|
19
|
+
export interface ParsedReviewIssue {
|
|
20
|
+
/** Raw issue line (trimmed, bullet stripped) */
|
|
21
|
+
text: string;
|
|
22
|
+
/** Severity inferred from a CRITICAL/HIGH/MEDIUM/LOW/INFO tag, default medium */
|
|
23
|
+
severity: ParsedReviewSeverity;
|
|
24
|
+
/** `path:line` reference when one appears in the line */
|
|
25
|
+
location?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ParsedReviewerOutput {
|
|
28
|
+
verdict: 'approved' | 'rejected';
|
|
29
|
+
/** RISK: LOW | MEDIUM | HIGH | CRITICAL (uppercased), when present */
|
|
30
|
+
risk?: string;
|
|
31
|
+
/** Blocking/non-blocking issue lines from the ISSUES section */
|
|
32
|
+
issues: ParsedReviewIssue[];
|
|
33
|
+
/** Required-change lines from the FIXES section */
|
|
34
|
+
fixes: string[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse the reviewer agent's mandated output block. Returns null when no
|
|
38
|
+
* unambiguous line-anchored `VERDICT: APPROVED|REJECTED` is present —
|
|
39
|
+
* including when multiple anchored verdict lines DISAGREE (ambiguous output
|
|
40
|
+
* fails toward "no machine-readable verdict", never toward approval).
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseReviewerOutput(text: string): ParsedReviewerOutput | null;
|
|
43
|
+
export interface ReviewerReceiptInput {
|
|
44
|
+
tool: unknown;
|
|
45
|
+
args?: unknown;
|
|
46
|
+
sessionID?: unknown;
|
|
47
|
+
}
|
|
48
|
+
export interface ReviewerReceiptOutput {
|
|
49
|
+
output?: unknown;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* `tool.execute.after` collector. When a reviewer Task returns, parse its
|
|
53
|
+
* verdict block and persist a durable review receipt. No-op for non-reviewer
|
|
54
|
+
* delegations, missing prompts/outputs, or unparseable verdicts. Never throws.
|
|
55
|
+
*
|
|
56
|
+
* Returns the persisted receipt path (for tests/telemetry) or null.
|
|
57
|
+
*/
|
|
58
|
+
export declare function collectReviewerReceiptAfter(directory: string, input: ReviewerReceiptInput, output: ReviewerReceiptOutput): Promise<string | null>;
|
|
@@ -20,6 +20,13 @@
|
|
|
20
20
|
*
|
|
21
21
|
* Critic drift verification can consume prior receipts as supporting context
|
|
22
22
|
* but MUST NOT blindly trust them — staleness check is mandatory.
|
|
23
|
+
*
|
|
24
|
+
* Scope-description heterogeneity: receipts in one index may be fingerprinted
|
|
25
|
+
* over different canonical contents (e.g. 'reviewer-task-prompt' hashes the
|
|
26
|
+
* delegation prompt; 'auto-review-*-diff' hashes the reviewed diff). A prompt
|
|
27
|
+
* fingerprint does NOT change when the worktree changes, so consumers MUST
|
|
28
|
+
* filter by `scope_fingerprint.scope_description` (or compare like-for-like
|
|
29
|
+
* content) before treating an approved receipt as fresh via isScopeStale().
|
|
23
30
|
*/
|
|
24
31
|
export type ReceiptVerdict = 'rejected' | 'approved';
|
|
25
32
|
/** Identity of the reviewer/curator that produced the receipt. */
|