opencode-swarm 7.7.0 → 7.8.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.
@@ -2,9 +2,20 @@
2
2
  * Handles the /swarm full-auto command.
3
3
  * Toggles Full-Auto Mode on or off for the active session.
4
4
  *
5
- * @param directory - Project directory (unused but kept for consistency with other commands)
5
+ * In Full-Auto v2 this also creates a durable run-state record under
6
+ * .swarm/full-auto-state.json so the permission/oversight infrastructure can
7
+ * fail-closed across hooks and across process restarts.
8
+ *
9
+ * H2 fix: durable write happens BEFORE flipping the legacy
10
+ * `session.fullAutoMode` flag. If the durable write fails, the command
11
+ * surfaces the error in its return string and does NOT enable the legacy
12
+ * reactive intercept — preventing a silent fail-open where reactive checks
13
+ * would believe Full-Auto is on while the v2 permission hook sees no
14
+ * durable run.
15
+ *
16
+ * @param directory - Project directory (used to persist Full-Auto run state)
6
17
  * @param args - Optional argument: "on" | "off" | undefined (toggle behavior)
7
18
  * @param sessionID - Session ID for accessing active session state
8
19
  * @returns Feedback message about Full-Auto Mode state
9
20
  */
10
- export declare function handleFullAutoCommand(_directory: string, args: string[], sessionID: string): Promise<string>;
21
+ export declare function handleFullAutoCommand(directory: string, args: string[], sessionID: string): Promise<string>;
@@ -205,9 +205,9 @@ export declare const RetrospectiveEvidenceSchema: z.ZodObject<{
205
205
  other: "other";
206
206
  }>;
207
207
  scope: z.ZodEnum<{
208
- project: "project";
209
208
  global: "global";
210
209
  session: "session";
210
+ project: "project";
211
211
  }>;
212
212
  }, z.core.$strip>>>;
213
213
  approaches_tried: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -651,9 +651,9 @@ export declare const EvidenceSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
651
651
  other: "other";
652
652
  }>;
653
653
  scope: z.ZodEnum<{
654
- project: "project";
655
654
  global: "global";
656
655
  session: "session";
656
+ project: "project";
657
657
  }>;
658
658
  }, z.core.$strip>>>;
659
659
  approaches_tried: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -1070,9 +1070,9 @@ export declare const EvidenceBundleSchema: z.ZodObject<{
1070
1070
  other: "other";
1071
1071
  }>;
1072
1072
  scope: z.ZodEnum<{
1073
- project: "project";
1074
1073
  global: "global";
1075
1074
  session: "session";
1075
+ project: "project";
1076
1076
  }>;
1077
1077
  }, z.core.$strip>>>;
1078
1078
  approaches_tried: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { type AgentName } from './constants';
2
3
  /**
3
4
  * Test-only dependency-injection seam — see `gitignore-warning.ts:_internals`
4
5
  * for the rationale (`mock.module` from `bun:test` leaks across files in
@@ -7,21 +8,62 @@ import { z } from 'zod';
7
8
  */
8
9
  export declare const _internals: {
9
10
  stripKnownSwarmPrefix: typeof stripKnownSwarmPrefix;
11
+ getCanonicalAgentRole: typeof getCanonicalAgentRole;
10
12
  resolveGuardrailsConfig: typeof resolveGuardrailsConfig;
11
13
  };
12
14
  /**
13
- * Strips known Swarm prefixes from agent names to get the canonical agent name.
15
+ * Type guard: returns true when `role` is a canonical agent role recognized
16
+ * by the plugin (e.g. "architect", "coder", "critic_oversight"). User-defined
17
+ * swarm IDs must NOT be treated as canonical roles.
18
+ */
19
+ export declare function isKnownCanonicalRole(role: string): role is AgentName;
20
+ /**
21
+ * Extract the canonical agent role from a generated agent name, using
22
+ * suffix-based matching with longest-suffix-wins.
23
+ *
24
+ * Generated agent names have the shape `<arbitrary user-defined swarm ID>
25
+ * <separator> <canonical role>`. The swarm ID is opaque to the plugin —
26
+ * it can be anything the user configures (e.g. `banana`, `acme-prod`,
27
+ * `customer123`). Only the canonical role suffix is plugin-defined.
28
+ *
29
+ * Behavior:
30
+ * - If `agentName` is itself a canonical role, return it unchanged.
31
+ * - Else, find the longest canonical role `R` such that `agentName`
32
+ * ends with `<sep><R>` for some separator in {"_", "-", " "}; return `R`.
33
+ * - Else, return `agentName` unchanged (caller can detect "no role found"
34
+ * by comparing the result to `agentName`, or by passing the result
35
+ * through `isKnownCanonicalRole`).
14
36
  *
15
- * Strategy:
16
- * 1. First try stripping known prefixes from the front (e.g., 'paid_architect' -> 'architect')
17
- * 2. If that doesn't yield a known agent, check if the name ENDS with a known agent name
18
- * (e.g., 'not-an-architect' -> 'architect', 'team-alpha-reviewer' -> 'reviewer')
37
+ * Optional `generatedAgentNames` argument: when supplied (e.g. from
38
+ * `getAgentConfigs` output at plugin init), the function ONLY infers a role
39
+ * for names actually present in that registry. Bare user-supplied strings
40
+ * like `not_an_architect` are NOT treated as roles unless they appear in
41
+ * the generated-name set. Callers that need this strict behavior should
42
+ * use `resolveGeneratedAgentRole` instead.
19
43
  *
20
- * Supports underscore, hyphen, and space separators.
21
- * Case-insensitive matching, but returns the canonical lowercase agent name.
44
+ * Case-insensitive on the input; the returned role is the canonical
45
+ * lowercase form from `ALL_AGENT_NAMES`.
46
+ */
47
+ export declare function getCanonicalAgentRole(agentName: string, generatedAgentNames?: Iterable<string>): AgentName | string;
48
+ /**
49
+ * Strict variant of {@link getCanonicalAgentRole}: only infers a canonical
50
+ * role when the agent name is actually present in the supplied generated-
51
+ * name registry, OR is itself a canonical role. Use this from callers that
52
+ * must avoid treating arbitrary user-supplied strings as roles.
53
+ */
54
+ export declare function resolveGeneratedAgentRole(agentName: string, generatedAgentNames: Iterable<string>): AgentName | string;
55
+ /**
56
+ * Backward-compatible alias for {@link getCanonicalAgentRole}. The previous
57
+ * implementation stripped a hardcoded list of "known swarm prefixes" from
58
+ * the front of the agent name. That approach was wrong by design: swarm
59
+ * IDs are arbitrary user-defined strings — the plugin cannot enumerate
60
+ * them. The new implementation does suffix-based canonical role extraction
61
+ * (see `getCanonicalAgentRole`) which works for ANY user-defined swarm ID.
22
62
  *
23
- * @param agentName - The potentially prefixed agent name
24
- * @returns The canonical agent name, or the original if no known agent found
63
+ * Existing tests that pass `synthetic_reviewer` / `mega_architect` /
64
+ * `cloud_critic_oversight` / etc. continue to pass because those names
65
+ * end with `<sep><canonical role>`, which is exactly what the suffix
66
+ * extractor matches.
25
67
  */
26
68
  export declare function stripKnownSwarmPrefix(agentName: string): string;
27
69
  export declare const AgentOverrideConfigSchema: z.ZodObject<{
@@ -1073,6 +1115,37 @@ export declare const PluginConfigSchema: z.ZodObject<{
1073
1115
  pause: "pause";
1074
1116
  terminate: "terminate";
1075
1117
  }>>;
1118
+ mode: z.ZodDefault<z.ZodEnum<{
1119
+ strict: "strict";
1120
+ assisted: "assisted";
1121
+ supervised: "supervised";
1122
+ }>>;
1123
+ fail_closed: z.ZodDefault<z.ZodBoolean>;
1124
+ permission_policy: z.ZodDefault<z.ZodObject<{
1125
+ enabled: z.ZodDefault<z.ZodBoolean>;
1126
+ trusted_roots: z.ZodDefault<z.ZodArray<z.ZodString>>;
1127
+ trusted_domains: z.ZodDefault<z.ZodArray<z.ZodString>>;
1128
+ protected_paths: z.ZodDefault<z.ZodArray<z.ZodString>>;
1129
+ allow_defaults: z.ZodDefault<z.ZodBoolean>;
1130
+ }, z.core.$strip>>;
1131
+ denials: z.ZodDefault<z.ZodObject<{
1132
+ max_consecutive: z.ZodDefault<z.ZodNumber>;
1133
+ max_total: z.ZodDefault<z.ZodNumber>;
1134
+ on_limit: z.ZodDefault<z.ZodEnum<{
1135
+ pause: "pause";
1136
+ terminate: "terminate";
1137
+ }>>;
1138
+ }, z.core.$strip>>;
1139
+ oversight: z.ZodDefault<z.ZodObject<{
1140
+ on_plan_change: z.ZodDefault<z.ZodBoolean>;
1141
+ on_task_completion: z.ZodDefault<z.ZodBoolean>;
1142
+ on_phase_boundary: z.ZodDefault<z.ZodBoolean>;
1143
+ on_high_risk_action: z.ZodDefault<z.ZodBoolean>;
1144
+ on_subagent_return_warning: z.ZodDefault<z.ZodBoolean>;
1145
+ every_tool_calls: z.ZodDefault<z.ZodNumber>;
1146
+ every_architect_turns: z.ZodDefault<z.ZodNumber>;
1147
+ every_minutes: z.ZodDefault<z.ZodNumber>;
1148
+ }, z.core.$strip>>;
1076
1149
  }, z.core.$strip>>>;
1077
1150
  }, z.core.$strip>;
1078
1151
  export type PluginConfig = z.infer<typeof PluginConfigSchema>;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Full-Auto v2 oversight cadence.
3
+ *
4
+ * Pure helpers that decide when periodic / risk-triggered oversight should
5
+ * fire. Wired into the existing tool.execute.after flow (counters increment)
6
+ * and into the chat.message transform (architect turn increment) by the
7
+ * orchestrating hook composition in `src/index.ts`.
8
+ *
9
+ * Critic oversight sessions and critic-internal tool calls must be exempt
10
+ * from triggering further Full-Auto oversight. Callers identify those by
11
+ * passing `excludeAgent: true` for the relevant call.
12
+ */
13
+ import type { PluginConfig } from '../config';
14
+ import { dispatchFullAutoOversight } from './oversight';
15
+ import { type FullAutoRunState } from './state';
16
+ export type CadenceTrigger = {
17
+ kind: 'tool_calls';
18
+ threshold: number;
19
+ } | {
20
+ kind: 'architect_turns';
21
+ threshold: number;
22
+ } | {
23
+ kind: 'minutes';
24
+ threshold: number;
25
+ elapsedMinutes: number;
26
+ } | {
27
+ kind: 'consecutive_no_progress';
28
+ threshold: number;
29
+ } | {
30
+ kind: 'denials_near_limit';
31
+ consecutive: number;
32
+ max: number;
33
+ };
34
+ export interface CadenceDecision {
35
+ shouldEscalate: boolean;
36
+ triggers: CadenceTrigger[];
37
+ }
38
+ export declare function evaluateFullAutoCadence(state: FullAutoRunState, config: PluginConfig, now?: number): CadenceDecision;
39
+ /**
40
+ * Convenience: increment the relevant counter and evaluate cadence in one
41
+ * call. Returns undefined when there is no active Full-Auto run.
42
+ */
43
+ export declare function tickAndEvaluate(directory: string, sessionID: string, counter: 'toolCalls' | 'architectTurns', config: PluginConfig): CadenceDecision | undefined;
44
+ /**
45
+ * Tick a counter, evaluate cadence, and — if a trigger fires — dispatch the
46
+ * critic oversight agent in a non-blocking way. The dispatch:
47
+ * - increments the durable oversight counter
48
+ * - writes a `full_auto_oversight` event/evidence record
49
+ * - mutates durable run state (pause / terminate) according to verdict
50
+ *
51
+ * The chat.message and tool.execute.after callers do not await the dispatch
52
+ * — they fire-and-forget. The next tool call by the agent will see the
53
+ * paused/terminated state and surface a structured error.
54
+ *
55
+ * Returns the CadenceDecision so callers can introspect for tests.
56
+ */
57
+ export declare function tickAndMaybeDispatchCadence(directory: string, sessionID: string, counter: 'toolCalls' | 'architectTurns', config: PluginConfig, options?: {
58
+ activeAgent?: string;
59
+ dispatch?: typeof dispatchFullAutoOversight;
60
+ }): CadenceDecision | undefined;
61
+ export declare const _internals: {
62
+ clearInFlight: () => void;
63
+ inFlight: Set<string>;
64
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Full-Auto v2 prompt-injection scanner for tool output.
3
+ *
4
+ * The probe inspects the textual content returned by a tool (web_search,
5
+ * webfetch, fetch, doc_extract, search, evidence dumps, etc.) and detects
6
+ * the most common prompt-injection / exfiltration shapes. It is intentionally
7
+ * pattern-based and conservative: false positives produce a warning but do
8
+ * not block work by themselves; an injection warning combined with a
9
+ * subsequent risky action escalates to the critic.
10
+ */
11
+ export type FullAutoInputWarningCategory = 'instruction_override' | 'system_role_override' | 'credential_request' | 'exfiltration_request' | 'guardrail_disable_request' | 'curl_pipe_shell' | 'untrusted_run_command';
12
+ export interface FullAutoInputWarning {
13
+ category: FullAutoInputWarningCategory;
14
+ matched: string;
15
+ excerpt: string;
16
+ }
17
+ export interface FullAutoInputProbeResult {
18
+ hasWarning: boolean;
19
+ warnings: FullAutoInputWarning[];
20
+ }
21
+ export declare function probeFullAutoInput(text: string): FullAutoInputProbeResult;
22
+ export declare function shouldEscalateAfterWarning(toolName: string, commandOrUrl: string | undefined): boolean;
@@ -0,0 +1,93 @@
1
+ export interface FullAutoCriticResult {
2
+ verdict: string;
3
+ reasoning: string;
4
+ evidenceChecked: string[];
5
+ antiPatternsDetected: string[];
6
+ escalationNeeded: boolean;
7
+ rawResponse: string;
8
+ }
9
+ export type FullAutoTriggerSource = 'text_pattern' | 'tool_action' | 'cadence' | 'subagent_return' | 'phase_boundary' | 'task_completion' | 'risk';
10
+ export interface FullAutoOversightEvent {
11
+ type: 'full_auto_oversight';
12
+ timestamp: string;
13
+ session_id: string;
14
+ plan_id?: string;
15
+ phase?: number;
16
+ task_id?: string;
17
+ trigger_source: FullAutoTriggerSource;
18
+ trigger_reason: string;
19
+ critic_agent: string;
20
+ critic_model: string;
21
+ architect_model?: string;
22
+ verdict: string;
23
+ reasoning: string;
24
+ evidence_checked: string[];
25
+ anti_patterns_detected: string[];
26
+ escalation_needed: boolean;
27
+ decision: string;
28
+ full_auto_status_before?: string;
29
+ full_auto_status_after?: string;
30
+ oversight_sequence: number;
31
+ }
32
+ export interface DispatchFullAutoOversightInput {
33
+ directory: string;
34
+ sessionID: string;
35
+ trigger: string;
36
+ triggerSource: FullAutoTriggerSource;
37
+ phase?: number;
38
+ taskID?: string;
39
+ planID?: string;
40
+ architectOutput?: string;
41
+ actionContext?: Record<string, unknown>;
42
+ criticModel: string;
43
+ oversightAgentName: string;
44
+ architectModel?: string;
45
+ /**
46
+ * Optional Full-Auto config slice. Used to honor `fail_closed` semantics
47
+ * when oversight event/evidence persistence fails (TASK 6). When
48
+ * omitted, the dispatcher defaults to `fail_closed = true`.
49
+ */
50
+ fullAutoConfig?: {
51
+ fail_closed?: boolean;
52
+ };
53
+ }
54
+ export declare function parseFullAutoCriticResponse(rawResponse: string): FullAutoCriticResult;
55
+ /**
56
+ * Append a Full-Auto oversight event to `.swarm/events.jsonl`.
57
+ *
58
+ * TASK 6: persistence failures MUST propagate. When fail_closed is the
59
+ * active policy (the default), an oversight verdict that cannot be
60
+ * durably audited is not a real verdict — the dispatcher converts the
61
+ * thrown error into a BLOCKED/pause outcome.
62
+ *
63
+ * The lock acquisition is best-effort (some platforms / test sandboxes
64
+ * cannot acquire the cross-process lock); the actual append is the
65
+ * mandatory step and any failure throws.
66
+ */
67
+ export declare function writeFullAutoOversightEvent(directory: string, event: FullAutoOversightEvent): Promise<void>;
68
+ /**
69
+ * Persist Full-Auto oversight evidence to `.swarm/evidence/{phase}/full-auto-{seq}.json`.
70
+ *
71
+ * TASK 6: persistence failures MUST propagate. For phase_boundary
72
+ * triggers the evidence write is MANDATORY because phase_complete will
73
+ * later block on the absence of an APPROVED record. The dispatcher
74
+ * converts a thrown error into a BLOCKED/pause outcome under
75
+ * fail_closed = true.
76
+ *
77
+ * Returns `undefined` only when `phase` is undefined (no evidence to
78
+ * write because the trigger isn't phase-scoped). All other failures
79
+ * throw.
80
+ */
81
+ export declare function writeFullAutoOversightEvidence(directory: string, phase: number | undefined, event: FullAutoOversightEvent): Promise<string | undefined>;
82
+ export interface FullAutoOversightOutcome extends FullAutoCriticResult {
83
+ decision: 'allow' | 'deny' | 'pause' | 'escalate_human' | 'pending';
84
+ event: FullAutoOversightEvent;
85
+ evidencePath?: string;
86
+ }
87
+ export declare function dispatchFullAutoOversight(input: DispatchFullAutoOversightInput): Promise<FullAutoOversightOutcome>;
88
+ /**
89
+ * Test-only DI seam.
90
+ */
91
+ export declare const _internals: {
92
+ resetSequence: () => void;
93
+ };
@@ -0,0 +1,7 @@
1
+ import type { PluginConfig } from '../config';
2
+ export interface PhaseApprovalDecision {
3
+ ok: boolean;
4
+ reason?: string;
5
+ evidence?: Record<string, unknown>;
6
+ }
7
+ export declare function verifyFullAutoPhaseApproval(directory: string, sessionID: string | undefined, phase: number, config: PluginConfig): PhaseApprovalDecision;
@@ -0,0 +1,85 @@
1
+ export type FullAutoActionTier = 'safe' | 'local' | 'medium' | 'high';
2
+ export type FullAutoDecision = {
3
+ action: 'allow';
4
+ reason: string;
5
+ tier: 'safe' | 'local';
6
+ } | {
7
+ action: 'deny';
8
+ reason: string;
9
+ code: string;
10
+ recoverable: boolean;
11
+ } | {
12
+ action: 'escalate_critic';
13
+ reason: string;
14
+ risk: 'medium' | 'high';
15
+ context: Record<string, unknown>;
16
+ } | {
17
+ action: 'escalate_human';
18
+ reason: string;
19
+ code: string;
20
+ } | {
21
+ action: 'pause';
22
+ reason: string;
23
+ code: string;
24
+ };
25
+ export interface FullAutoPolicyConfig {
26
+ enabled?: boolean;
27
+ mode?: 'assisted' | 'supervised' | 'strict';
28
+ permission_policy?: {
29
+ enabled?: boolean;
30
+ trusted_roots?: string[];
31
+ trusted_domains?: string[];
32
+ protected_paths?: string[];
33
+ allow_defaults?: boolean;
34
+ };
35
+ oversight?: {
36
+ on_high_risk_action?: boolean;
37
+ on_task_completion?: boolean;
38
+ };
39
+ }
40
+ export interface FullAutoClassifierInput {
41
+ sessionID: string;
42
+ agentName?: string;
43
+ normalizedAgentName?: string;
44
+ toolName: string;
45
+ args: Record<string, unknown> | undefined;
46
+ directory: string;
47
+ workingDirectory?: string;
48
+ declaredScope?: string[] | null;
49
+ currentTaskID?: string | null;
50
+ currentPhase?: number;
51
+ planSummary?: string;
52
+ changedFiles?: string[];
53
+ fullAutoConfig: FullAutoPolicyConfig | undefined;
54
+ }
55
+ export declare function isReadOnlyTool(toolName: string): boolean;
56
+ export declare function isWriteLikeTool(toolName: string): boolean;
57
+ export declare function isSubagentDelegation(toolName: string, args: Record<string, unknown> | undefined): boolean;
58
+ export declare function isProtectedPath(filePath: string, config: FullAutoPolicyConfig | undefined): boolean;
59
+ export declare function classifyPathRisk(filePath: string, context: {
60
+ directory: string;
61
+ declaredScope?: string[] | null;
62
+ }): {
63
+ withinProjectRoot: boolean;
64
+ withinDeclaredScope: boolean | null;
65
+ protected: boolean;
66
+ highRiskBuild: boolean;
67
+ };
68
+ export declare function classifyCommandRisk(command: string, _cwd: string, _context: {
69
+ directory: string;
70
+ }): {
71
+ decision: 'allow' | 'deny' | 'escalate_critic';
72
+ reason: string;
73
+ };
74
+ export declare function classifyFullAutoToolAction(input: FullAutoClassifierInput): FullAutoDecision;
75
+ export interface StructuredDenial {
76
+ full_auto_denial: true;
77
+ tool?: string;
78
+ code: string;
79
+ reason: string;
80
+ recoverable: boolean;
81
+ guidance: string;
82
+ }
83
+ export declare function buildStructuredDenial(decision: Extract<FullAutoDecision, {
84
+ action: 'deny';
85
+ }>, tool?: string): StructuredDenial;
@@ -0,0 +1,121 @@
1
+ export type FullAutoStatus = 'idle' | 'running' | 'paused' | 'terminated';
2
+ export interface FullAutoDenialRecord {
3
+ timestamp: string;
4
+ tool?: string;
5
+ code?: string;
6
+ reason: string;
7
+ }
8
+ export interface FullAutoCounters {
9
+ architectTurns: number;
10
+ toolCalls: number;
11
+ coderDelegations: number;
12
+ reviewerRejections: number;
13
+ testFailures: number;
14
+ oversightChecks: number;
15
+ consecutiveNoProgressTurns: number;
16
+ }
17
+ export interface FullAutoRunState {
18
+ status: FullAutoStatus;
19
+ sessionID: string;
20
+ mode: 'assisted' | 'supervised' | 'strict';
21
+ planID?: string;
22
+ currentPhase?: number;
23
+ currentTaskID?: string;
24
+ startedAt: string;
25
+ updatedAt: string;
26
+ lastOversightAt?: string;
27
+ lastOversightReason?: string;
28
+ lastOversightVerdict?: string;
29
+ denialCounters: {
30
+ consecutive: number;
31
+ total: number;
32
+ };
33
+ denialHistory: FullAutoDenialRecord[];
34
+ counters: FullAutoCounters;
35
+ pauseReason?: string;
36
+ terminateReason?: string;
37
+ }
38
+ export interface FullAutoPersistedState {
39
+ version: 2;
40
+ updatedAt: string;
41
+ /**
42
+ * Monotonic counter for `full_auto_oversight` evidence-file sequencing.
43
+ * Persisted so the per-phase filename `full-auto-{seq}.json` does not
44
+ * collide after a process restart. (C4 fix.)
45
+ */
46
+ oversightSequence?: number;
47
+ sessions: Record<string, FullAutoRunState>;
48
+ }
49
+ export interface FullAutoConfigShape {
50
+ enabled?: boolean;
51
+ mode?: 'assisted' | 'supervised' | 'strict';
52
+ denials?: {
53
+ max_consecutive?: number;
54
+ max_total?: number;
55
+ on_limit?: 'pause' | 'terminate';
56
+ };
57
+ }
58
+ export declare class FullAutoStateUnreadableError extends Error {
59
+ constructor(reason: string);
60
+ }
61
+ export declare function isFullAutoStateUnreadable(): {
62
+ unreadable: boolean;
63
+ reason: string;
64
+ };
65
+ declare function readPersisted(directory: string): FullAutoPersistedState;
66
+ /**
67
+ * Atomically persist Full-Auto durable state.
68
+ *
69
+ * TASK 3 fix: persistence failures MUST propagate. The previous
70
+ * implementation caught and logged write errors, which let
71
+ * `startFullAutoRun` (and the `/swarm full-auto on` command) silently
72
+ * report success even when nothing was written. Callers relied on the
73
+ * durable record to fail-closed; that contract is now enforced.
74
+ *
75
+ * Behavior:
76
+ * - Writes via `tmp -> fsync -> rename`, so a crash mid-write cannot
77
+ * truncate the canonical file.
78
+ * - Keeps `.bak` of the prior canonical file as a recovery hint.
79
+ * - Reads the file back after the rename and confirms the JSON
80
+ * round-trips. Any failure throws.
81
+ */
82
+ declare function writePersisted(directory: string, persisted: FullAutoPersistedState): void;
83
+ export declare function loadFullAutoRunState(directory: string, sessionID: string): FullAutoRunState | undefined;
84
+ export declare function saveFullAutoRunState(directory: string, state: FullAutoRunState): void;
85
+ export declare function startFullAutoRun(directory: string, sessionID: string, config: FullAutoConfigShape | undefined, options?: {
86
+ planID?: string;
87
+ phase?: number;
88
+ taskID?: string;
89
+ }): FullAutoRunState;
90
+ export declare function pauseFullAutoRun(directory: string, sessionID: string, reason: string): FullAutoRunState | undefined;
91
+ export declare function terminateFullAutoRun(directory: string, sessionID: string, reason: string): FullAutoRunState | undefined;
92
+ export declare function isFullAutoRunActive(directory: string, sessionID: string): boolean;
93
+ export type FullAutoCounterKey = keyof FullAutoCounters;
94
+ export declare function incrementFullAutoCounter(directory: string, sessionID: string, counter: FullAutoCounterKey, delta?: number): FullAutoRunState | undefined;
95
+ export declare function recordFullAutoDenial(directory: string, sessionID: string, denial: {
96
+ tool?: string;
97
+ code?: string;
98
+ reason: string;
99
+ }): FullAutoRunState | undefined;
100
+ export declare function resetFullAutoDenials(directory: string, sessionID: string): FullAutoRunState | undefined;
101
+ /**
102
+ * Atomically increment and return the durable oversight-evidence sequence
103
+ * counter. Used by `writeFullAutoOversightEvidence` to produce stable,
104
+ * non-colliding evidence filenames across process restarts. (C4 fix.)
105
+ */
106
+ export declare function nextFullAutoOversightSequence(directory: string): number;
107
+ export declare function recordFullAutoOversight(directory: string, sessionID: string, verdict: string, reason: string): FullAutoRunState | undefined;
108
+ export interface DenialLimitDecision {
109
+ pause: boolean;
110
+ reason?: string;
111
+ mode?: 'pause' | 'terminate';
112
+ }
113
+ export declare function shouldPauseForDenials(state: FullAutoRunState, config: FullAutoConfigShape | undefined): DenialLimitDecision;
114
+ /**
115
+ * Test-only DI seam — same rationale as `src/state.ts:_internals`.
116
+ */
117
+ export declare const _internals: {
118
+ readPersisted: typeof readPersisted;
119
+ writePersisted: typeof writePersisted;
120
+ };
121
+ export {};
@@ -0,0 +1,28 @@
1
+ import type { PluginConfig } from '../config';
2
+ export interface FullAutoDelegationHookOptions {
3
+ config: PluginConfig;
4
+ directory: string;
5
+ }
6
+ interface ToolBeforeInput {
7
+ tool: string;
8
+ sessionID: string;
9
+ callID?: string;
10
+ }
11
+ interface ToolBeforeOutput {
12
+ args: unknown;
13
+ }
14
+ interface ToolAfterInput {
15
+ tool: string;
16
+ sessionID: string;
17
+ callID?: string;
18
+ args?: unknown;
19
+ }
20
+ interface ToolAfterOutput {
21
+ output?: unknown;
22
+ error?: unknown;
23
+ }
24
+ export declare function createFullAutoDelegationHook(options: FullAutoDelegationHookOptions): {
25
+ toolBefore: (input: ToolBeforeInput, output: ToolBeforeOutput) => Promise<void>;
26
+ toolAfter: (input: ToolAfterInput, output: ToolAfterOutput) => Promise<void>;
27
+ };
28
+ export {};
@@ -0,0 +1,27 @@
1
+ import type { PluginConfig } from '../config';
2
+ export interface FullAutoInputProbeHookOptions {
3
+ config: PluginConfig;
4
+ directory: string;
5
+ }
6
+ interface ToolAfterInput {
7
+ tool: string;
8
+ sessionID: string;
9
+ callID?: string;
10
+ }
11
+ interface ToolAfterOutput {
12
+ output?: unknown;
13
+ error?: unknown;
14
+ }
15
+ export interface PendingInputWarning {
16
+ tool: string;
17
+ at: string;
18
+ categories: string[];
19
+ }
20
+ export declare const fullAutoInputWarningStash: Map<string, PendingInputWarning>;
21
+ export declare function setPendingInputWarning(sessionID: string, warning: PendingInputWarning): void;
22
+ export declare function consumePendingInputWarning(sessionID: string): PendingInputWarning | undefined;
23
+ export declare function peekPendingInputWarning(sessionID: string): PendingInputWarning | undefined;
24
+ export declare function createFullAutoInputProbeHook(options: FullAutoInputProbeHookOptions): {
25
+ toolAfter: (input: ToolAfterInput, output: ToolAfterOutput) => Promise<void>;
26
+ };
27
+ export {};
@@ -58,7 +58,7 @@ export declare function injectVerdictIntoMessages(messages: MessageWithParts[],
58
58
  * This function encapsulates the critic invocation and event writing flow.
59
59
  * The critic response is awaited before writing the event to events.jsonl.
60
60
  */
61
- export declare function dispatchCriticAndWriteEvent(directory: string, architectOutput: string, criticContext: string, criticModel: string, escalationType: 'phase_completion' | 'question', interactionCount: number, deadlockCount: number, oversightAgentName: string): Promise<CriticDispatchResult>;
61
+ export declare function dispatchCriticAndWriteEvent(directory: string, architectOutput: string, criticContext: string, criticModel: string, escalationType: 'phase_completion' | 'question', interactionCount: number, deadlockCount: number, oversightAgentName: string, sessionID?: string): Promise<CriticDispatchResult>;
62
62
  /**
63
63
  * Creates the full-auto intercept hook factory.
64
64
  *