opencode-swarm 7.70.0 → 7.71.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.
@@ -0,0 +1,3 @@
1
+ export declare function handlePostMortemCommand(directory: string, args: string[], options?: {
2
+ sessionID?: string;
3
+ }): Promise<string>;
@@ -264,6 +264,13 @@ export declare const COMMAND_REGISTRY: {
264
264
  readonly aliasOf: "finalize";
265
265
  readonly deprecated: true;
266
266
  };
267
+ readonly 'post-mortem': {
268
+ readonly handler: (ctx: CommandContext) => Promise<string>;
269
+ readonly description: "Run the post-mortem agent: project-end synthesis, queue triage, and final curation pass";
270
+ readonly details: "Reads .swarm/ evidence (knowledge entries, events, curator digests, proposals, retrospectives, drift reports) and produces a post-mortem report at .swarm/post-mortem-{planId}.md. Idempotent: re-runs skip if report exists unless --force is passed.";
271
+ readonly args: "--force";
272
+ readonly category: "core";
273
+ };
267
274
  readonly concurrency: {
268
275
  readonly handler: (ctx: CommandContext) => Promise<string>;
269
276
  readonly description: "Manage runtime concurrency override for plan execution [set|status|reset]";
@@ -12,8 +12,8 @@
12
12
  export declare const QA_AGENTS: readonly ["reviewer", "critic", "critic_oversight"];
13
13
  export declare const PIPELINE_AGENTS: readonly ["explorer", "coder", "test_engineer"];
14
14
  export declare const ORCHESTRATOR_NAME: "architect";
15
- export declare const ALL_SUBAGENT_NAMES: readonly ["sme", "docs", "docs_design", "designer", "critic_sounding_board", "critic_drift_verifier", "critic_hallucination_verifier", "critic_architecture_supervisor", "curator_init", "curator_phase", "council_generalist", "council_skeptic", "council_domain_expert", "skill_improver", "spec_writer", "reviewer", "critic", "critic_oversight", "explorer", "coder", "test_engineer"];
16
- export declare const ALL_AGENT_NAMES: readonly ["architect", "sme", "docs", "docs_design", "designer", "critic_sounding_board", "critic_drift_verifier", "critic_hallucination_verifier", "critic_architecture_supervisor", "curator_init", "curator_phase", "council_generalist", "council_skeptic", "council_domain_expert", "skill_improver", "spec_writer", "reviewer", "critic", "critic_oversight", "explorer", "coder", "test_engineer"];
15
+ export declare const ALL_SUBAGENT_NAMES: readonly ["sme", "docs", "docs_design", "designer", "critic_sounding_board", "critic_drift_verifier", "critic_hallucination_verifier", "critic_architecture_supervisor", "curator_init", "curator_phase", "curator_postmortem", "council_generalist", "council_skeptic", "council_domain_expert", "skill_improver", "spec_writer", "reviewer", "critic", "critic_oversight", "explorer", "coder", "test_engineer"];
16
+ export declare const ALL_AGENT_NAMES: readonly ["architect", "sme", "docs", "docs_design", "designer", "critic_sounding_board", "critic_drift_verifier", "critic_hallucination_verifier", "critic_architecture_supervisor", "curator_init", "curator_phase", "curator_postmortem", "council_generalist", "council_skeptic", "council_domain_expert", "skill_improver", "spec_writer", "reviewer", "critic", "critic_oversight", "explorer", "coder", "test_engineer"];
17
17
  export type QAAgentName = (typeof QA_AGENTS)[number];
18
18
  export type PipelineAgentName = (typeof PIPELINE_AGENTS)[number];
19
19
  export type AgentName = (typeof ALL_AGENT_NAMES)[number];
@@ -617,6 +617,7 @@ export declare const CuratorConfigSchema: z.ZodObject<{
617
617
  enabled: z.ZodDefault<z.ZodBoolean>;
618
618
  init_enabled: z.ZodDefault<z.ZodBoolean>;
619
619
  phase_enabled: z.ZodDefault<z.ZodBoolean>;
620
+ postmortem_enabled: z.ZodDefault<z.ZodBoolean>;
620
621
  max_summary_tokens: z.ZodDefault<z.ZodNumber>;
621
622
  min_knowledge_confidence: z.ZodDefault<z.ZodNumber>;
622
623
  compliance_report: z.ZodDefault<z.ZodBoolean>;
@@ -1597,6 +1598,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
1597
1598
  enabled: z.ZodDefault<z.ZodBoolean>;
1598
1599
  init_enabled: z.ZodDefault<z.ZodBoolean>;
1599
1600
  phase_enabled: z.ZodDefault<z.ZodBoolean>;
1601
+ postmortem_enabled: z.ZodDefault<z.ZodBoolean>;
1600
1602
  max_summary_tokens: z.ZodDefault<z.ZodNumber>;
1601
1603
  min_knowledge_confidence: z.ZodDefault<z.ZodNumber>;
1602
1604
  compliance_report: z.ZodDefault<z.ZodBoolean>;
@@ -7,8 +7,9 @@ import type { CuratorLLMDelegate } from './curator.js';
7
7
  * re-entrancy with the current session's message flow.
8
8
  *
9
9
  * The `mode` parameter determines which registered named agent is used:
10
- * - 'init' → curator_init (e.g. 'curator_init' or 'swarm1_curator_init')
11
- * - 'phase' → curator_phase (e.g. 'curator_phase' or 'swarm1_curator_phase')
10
+ * - 'init' → curator_init (e.g. 'curator_init' or 'swarm1_curator_init')
11
+ * - 'phase' → curator_phase (e.g. 'curator_phase' or 'swarm1_curator_phase')
12
+ * - 'postmortem' → curator_postmortem (e.g. 'curator_postmortem' or 'swarm1_curator_postmortem')
12
13
  *
13
14
  * The optional `sessionId` parameter enables deterministic swarm resolution:
14
15
  * when provided, the factory uses the calling session's registered agent to
@@ -17,4 +18,4 @@ import type { CuratorLLMDelegate } from './curator.js';
17
18
  *
18
19
  * Returns undefined if swarmState.opencodeClient is not set (e.g. in unit tests).
19
20
  */
20
- export declare function createCuratorLLMDelegate(directory: string, mode?: 'init' | 'phase', sessionId?: string): CuratorLLMDelegate | undefined;
21
+ export declare function createCuratorLLMDelegate(directory: string, mode?: 'init' | 'phase' | 'postmortem', sessionId?: string): CuratorLLMDelegate | undefined;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Curator post-mortem — project-end synthesis agent (WP7, issue #1234).
3
+ *
4
+ * Reads structured .swarm/ evidence (knowledge entries, events, curator digests,
5
+ * pending proposals, retrospectives, drift reports) and produces a post-mortem
6
+ * report with: improvement agenda, final curation pass, queue triage, and
7
+ * learning metrics summary.
8
+ *
9
+ * Triggers: phase_complete plan completion, /swarm finalize, /swarm post-mortem.
10
+ * Fail-open: errors never block finalize or phase completion.
11
+ * Outputs route through existing gated paths (knowledge_add, skill proposals,
12
+ * hive promotion) — no new ungated injection source.
13
+ */
14
+ import type { CuratorLLMDelegate } from './curator.js';
15
+ export interface PostMortemResult {
16
+ success: boolean;
17
+ planId: string | null;
18
+ reportPath: string | null;
19
+ summary: string | null;
20
+ warnings: string[];
21
+ }
22
+ export interface PostMortemOptions {
23
+ llmDelegate?: CuratorLLMDelegate;
24
+ force?: boolean;
25
+ }
26
+ interface KnowledgeEventSummary {
27
+ id: string;
28
+ lesson: string;
29
+ applied: number;
30
+ violated: number;
31
+ ignored: number;
32
+ confidence: number;
33
+ status: string;
34
+ }
35
+ declare function collectKnowledgeSummary(directory: string): Promise<KnowledgeEventSummary[]>;
36
+ declare function readJsonlFile(filePath: string): unknown[];
37
+ declare function collectRetrospectives(directory: string): string[];
38
+ declare function collectDriftReports(directory: string): string[];
39
+ declare function collectPendingProposals(directory: string): Array<{
40
+ source: string;
41
+ content: string;
42
+ }>;
43
+ declare function buildDataOnlyReport(planId: string, planSummary: string, knowledgeSummary: KnowledgeEventSummary[], curatorDigest: string | null, proposals: Array<{
44
+ source: string;
45
+ content: string;
46
+ }>, unactionable: unknown[], retrospectives: string[], driftReports: string[]): string;
47
+ declare function assembleLLMInput(planId: string, planSummary: string, knowledgeSummary: KnowledgeEventSummary[], curatorDigest: string | null, proposals: Array<{
48
+ source: string;
49
+ content: string;
50
+ }>, unactionable: unknown[], retrospectives: string[], driftReports: string[]): string;
51
+ export declare function runCuratorPostMortem(directory: string, options?: PostMortemOptions): Promise<PostMortemResult>;
52
+ export declare const _internals: {
53
+ collectKnowledgeSummary: typeof collectKnowledgeSummary;
54
+ collectRetrospectives: typeof collectRetrospectives;
55
+ collectDriftReports: typeof collectDriftReports;
56
+ collectPendingProposals: typeof collectPendingProposals;
57
+ readJsonlFile: typeof readJsonlFile;
58
+ buildDataOnlyReport: typeof buildDataOnlyReport;
59
+ assembleLLMInput: typeof assembleLLMInput;
60
+ };
61
+ export {};
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Worktree Isolation Subsystem
3
+ *
4
+ * Manages standard worktree-backed coder dispatches: provisioning worktrees,
5
+ * tracking dispatches, serializing when capacity is exceeded, and merging
6
+ * results back after coder completion.
7
+ *
8
+ * Extracted from delegation-gate.ts (FR-003) for modularity.
9
+ * The _internals seam allows test injection of worktree operations.
10
+ */
11
+ import type { PluginConfig } from '../../config';
12
+ import type { WorktreeHandle } from '../../worktree';
13
+ import { attemptMergeBackFromDirty, postMergeCleanup, provisionWorktree, removeWorktree } from '../../worktree';
14
+ export declare const MAX_TRACKED_STANDARD_WORKTREE_CALLS = 256;
15
+ export interface StandardWorktreeDispatch {
16
+ callID: string;
17
+ parentSessionID: string;
18
+ taskId: string;
19
+ planTaskId?: string;
20
+ handle: WorktreeHandle;
21
+ mergeStrategy: 'merge' | 'rebase' | 'cherry-pick';
22
+ }
23
+ export declare const standardWorktreeByCallID: Map<string, StandardWorktreeDispatch>;
24
+ export declare const standardWorktreeSerializationSessions: Set<string>;
25
+ export declare function resetStandardWorktreeIsolationState(): void;
26
+ export declare function sanitizeWorktreeTaskId(raw: string): string;
27
+ export declare function precreateStandardWorktreeSession(args: {
28
+ config: PluginConfig;
29
+ directory: string;
30
+ parentSessionID: string;
31
+ callID: string;
32
+ taskId: string;
33
+ planTaskId?: string;
34
+ description?: string;
35
+ outputArgs: Record<string, unknown>;
36
+ }): Promise<void>;
37
+ export declare function finishStandardWorktreeDispatch(directory: string, dispatch: StandardWorktreeDispatch): Promise<void>;
38
+ /**
39
+ * _internals seam for test injection of worktree operations.
40
+ * Tests set these entries on delegation-gate's _internals (which proxies
41
+ * here via getters/setters) to mock worktree provisioning, merge-back, etc.
42
+ */
43
+ export declare const _internals: {
44
+ provisionWorktree: typeof provisionWorktree;
45
+ removeWorktree: typeof removeWorktree;
46
+ attemptMergeBackFromDirty: typeof attemptMergeBackFromDirty;
47
+ postMergeCleanup: typeof postMergeCleanup;
48
+ };
@@ -8,7 +8,9 @@ import type { PluginConfig } from '../config';
8
8
  import { loadPlanJsonOnly } from '../plan/manager';
9
9
  import type { AgentSessionState } from '../state';
10
10
  import type { DelegationEnvelope, EnvelopeValidationResult } from '../types/delegation.js';
11
- import { attemptMergeBackFromDirty, postMergeCleanup, provisionWorktree, removeWorktree } from '../worktree';
11
+ import { resetStandardWorktreeIsolationState } from './delegation-gate/worktree-isolation';
12
+ export { resetStandardWorktreeIsolationState };
13
+ import { _internals as _wtiInternals } from './delegation-gate/worktree-isolation';
12
14
  /**
13
15
  * v6.33.1 CRIT-1: Fallback map for declared coder scope by taskId.
14
16
  * When messagesTransform sets declaredCoderScope on the architect session,
@@ -80,7 +82,6 @@ interface MessageWithParts {
80
82
  info: MessageInfo;
81
83
  parts: MessagePart[];
82
84
  }
83
- export declare function resetStandardWorktreeIsolationState(): void;
84
85
  declare function resolveDelegatedPlanTaskId(args: Record<string, unknown>, knownPlanTaskIds?: ReadonlySet<string>): string | null;
85
86
  declare function buildParallelExecutionGuidance(directory: string | undefined, sessionID: string, session: AgentSessionState): Promise<string | null>;
86
87
  /**
@@ -98,17 +99,21 @@ declare function resolveEvidenceTaskId(args: Record<string, unknown> | undefined
98
99
  * _internals export for testing — do not use in production code.
99
100
  * Exposes resolveEvidenceTaskId, resolveDelegatedPlanTaskId, and
100
101
  * buildParallelExecutionGuidance for unit testing.
102
+ *
103
+ * Worktree operation entries (provisionWorktree, removeWorktree, etc.) proxy
104
+ * to delegation-gate/worktree-isolation._internals via getters/setters so
105
+ * that test mutations on this object propagate to the extracted module.
101
106
  */
102
107
  export declare const _internals: {
103
108
  resolveEvidenceTaskId: typeof resolveEvidenceTaskId;
104
109
  resolveDelegatedPlanTaskId: typeof resolveDelegatedPlanTaskId;
105
110
  buildParallelExecutionGuidance: typeof buildParallelExecutionGuidance;
106
111
  loadPlanJsonOnly: typeof loadPlanJsonOnly;
107
- provisionWorktree: typeof provisionWorktree;
108
- removeWorktree: typeof removeWorktree;
109
- attemptMergeBackFromDirty: typeof attemptMergeBackFromDirty;
110
- postMergeCleanup: typeof postMergeCleanup;
111
112
  resetStandardWorktreeIsolationState: typeof resetStandardWorktreeIsolationState;
113
+ provisionWorktree: typeof _wtiInternals.provisionWorktree;
114
+ removeWorktree: typeof _wtiInternals.removeWorktree;
115
+ attemptMergeBackFromDirty: typeof _wtiInternals.attemptMergeBackFromDirty;
116
+ postMergeCleanup: typeof _wtiInternals.postMergeCleanup;
112
117
  };
113
118
  /**
114
119
  * Creates the experimental.chat.messages.transform hook for delegation gating.
@@ -132,4 +137,3 @@ export declare function createDelegationGateHook(config: PluginConfig, directory
132
137
  args?: Record<string, unknown>;
133
138
  }, output: unknown) => Promise<void>;
134
139
  };
135
- export {};
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Destructive Command Protection Subsystem
3
+ *
4
+ * Cross-platform helpers for detecting and blocking destructive shell commands.
5
+ * Extracted from guardrails.ts (FR-002) — pure mechanical extraction, zero
6
+ * behavior change.
7
+ *
8
+ * Provides:
9
+ * - Command normalization (homoglyph collapsing, escape decoding)
10
+ * - Shell wrapper unwrapping (cmd /c, powershell -Command, bash -c, sudo, etc.)
11
+ * - Compound command segmentation (splitting on ;, &&, ||, |)
12
+ * - Target validation (vars, remote paths, filesystem roots, lstat ancestor walk)
13
+ * - Junction/symlink creation detection
14
+ * - Target extraction for Windows cmd.exe and PowerShell destructive commands
15
+ */
16
+ /**
17
+ * Expanded safe-target allowlist for recursive delete operations.
18
+ * These directory names are safe to delete recursively by name alone.
19
+ * NOTE: Subdirectory paths like node_modules/.cache are NOT safe — the
20
+ * check requires the target be exactly one of these bare names.
21
+ */
22
+ export declare const DC_SAFE_TARGETS: Set<string>;
23
+ /**
24
+ * Normalize a command string for pattern matching:
25
+ * 1. Unicode NFKC normalize (collapses homoglyphs)
26
+ * 2. Detect evasion techniques that exist only to defeat scanners
27
+ *
28
+ * When an evasion technique is detected, the decoded form is returned so
29
+ * that pattern matching can still fire on it. Only fails-closed when the
30
+ * evasion wraps a form we cannot safely decode.
31
+ */
32
+ export declare function dcNormalizeCommand(cmd: string): string;
33
+ /**
34
+ * Strip one layer of a shell wrapper from a command string.
35
+ * Returns the inner command if a wrapper was found, null otherwise.
36
+ *
37
+ * Handles:
38
+ * - cmd /c "..." cmd /k "..."
39
+ * - powershell -Command "..." / -c "..." / -EncodedCommand <b64> / -enc <b64>
40
+ * - pwsh -Command / -c / -EncodedCommand / -enc
41
+ * - bash -c "..." / sh -c / zsh -c
42
+ * - sudo <...> / env VAR=val <...> / time <...> / nohup <...>
43
+ * - wsl -e ... / wsl -- ... / wsl.exe -e ...
44
+ * - PowerShell & { ... } script blocks
45
+ * - PowerShell iex / Invoke-Expression <...>
46
+ * - call <...> (batch)
47
+ */
48
+ export declare function dcStripOneWrapper(cmd: string): string | null;
49
+ /**
50
+ * Recursively unwrap all shell wrappers up to DC_MAX_UNWRAP_DEPTH.
51
+ * Returns the innermost unwrapped command.
52
+ */
53
+ export declare function dcUnwrapWrappers(cmd: string): string;
54
+ /**
55
+ * Split a compound command into individual segments, splitting on:
56
+ * ; && || | newlines
57
+ * Respects double-quoted strings (does not split inside quotes).
58
+ * Returns array of trimmed non-empty segments.
59
+ */
60
+ export declare function dcSplitSegments(cmd: string): string[];
61
+ /**
62
+ * Returns true if a path string contains unexpanded environment variable
63
+ * references that we cannot resolve at check time.
64
+ */
65
+ export declare function dcHasUnresolvableVars(p: string): boolean;
66
+ /**
67
+ * Returns true if the path looks like a remote/network filesystem path.
68
+ */
69
+ export declare function dcIsRemotePath(p: string): boolean;
70
+ /**
71
+ * Walk from target path up to (but not beyond) cwd using synchronous lstat,
72
+ * checking each ancestor for symlinks, junctions, or reparse points.
73
+ *
74
+ * Returns a block reason string if a suspicious ancestor is found, null otherwise.
75
+ * Skips silently on ENOENT (target does not exist — nothing to delete).
76
+ * Fails closed on unexpected lstat errors.
77
+ */
78
+ export declare function dcLstatAncestorWalk(targetPath: string, cwd: string): string | null;
79
+ /**
80
+ * Given a set of raw target strings from a destructive command, apply:
81
+ * 1. Unresolvable-var check (fail closed)
82
+ * 2. Safe-target allowlist check (allow through)
83
+ * 3. Remote filesystem check (block)
84
+ * 4. Unconditional system-path block
85
+ * 5. lstat ancestor walk (block on symlink/junction in chain)
86
+ *
87
+ * Returns a block reason string or null if targets are acceptable.
88
+ */
89
+ export declare function dcValidateTargets(targets: string[], cwd: string): string | null;
90
+ /**
91
+ * Detect Windows junction or symlink CREATION commands.
92
+ * Junction creation followed by recursive deletion of the junction is the
93
+ * exact mechanism of the K2.6 data-loss incident.
94
+ * Block junction/symlink creation where the target resolves outside cwd.
95
+ *
96
+ * Patterns covered:
97
+ * mklink /J <link> <target>
98
+ * mklink /D <link> <target>
99
+ * New-Item -ItemType Junction -Path <link> -Target <target>
100
+ * New-Item -ItemType SymbolicLink -Path <link> -Target <target>
101
+ * ln -s <target> <link> (when target is outside cwd)
102
+ */
103
+ export declare function dcCheckJunctionCreation(segment: string, cwd: string): string | null;
104
+ /**
105
+ * Extract candidate target paths from a destructive Windows cmd.exe command.
106
+ * Returns array of path-like arguments.
107
+ */
108
+ export declare function dcExtractWindowsCmdTargets(segment: string): string[];
109
+ /**
110
+ * Extract candidate target paths from a destructive PowerShell command.
111
+ * Handles both `Remove-Item <path> -Recurse` and `Remove-Item -Recurse <path>` orderings.
112
+ */
113
+ export declare function dcExtractPowerShellTargets(segment: string): string[];
@@ -0,0 +1,152 @@
1
+ /**
2
+ * File Authority Subsystem
3
+ *
4
+ * Extracted from guardrails.ts. Provides file-write authority checking,
5
+ * attestation API, path normalization caching, and glob matching for
6
+ * agent file permissions.
7
+ *
8
+ * All exports are re-exported by the barrel guardrails.ts for backward
9
+ * compatibility.
10
+ */
11
+ import * as path from 'node:path';
12
+ import { type AuthorityConfig } from '../../config/schema';
13
+ import { type FileZone } from '../../context/zone-classifier';
14
+ /**
15
+ * Hashes tool arguments for repetition detection
16
+ * @param args Tool arguments to hash
17
+ * @returns Numeric hash (0 if hashing fails)
18
+ */
19
+ export declare function hashArgs(args: unknown): number;
20
+ /** A record of an agent attesting to (resolving/suppressing/deferring) a finding. */
21
+ export interface AttestationRecord {
22
+ findingId: string;
23
+ agent: string;
24
+ attestation: string;
25
+ action: 'resolve' | 'suppress' | 'defer';
26
+ timestamp: string;
27
+ }
28
+ /**
29
+ * Validates that an attestation string meets the minimum length requirement.
30
+ */
31
+ export declare function validateAttestation(attestation: string, _findingId: string, _agent: string, _action: 'resolve' | 'suppress' | 'defer'): {
32
+ valid: true;
33
+ } | {
34
+ valid: false;
35
+ reason: string;
36
+ };
37
+ /**
38
+ * Appends an attestation record to `.swarm/evidence/attestations.jsonl`.
39
+ */
40
+ export declare function recordAttestation(dir: string, record: AttestationRecord): Promise<void>;
41
+ /**
42
+ * Validates an attestation and, on success, records it; on failure, logs a rejection event.
43
+ */
44
+ export declare function validateAndRecordAttestation(dir: string, findingId: string, agent: string, attestation: string, action: 'resolve' | 'suppress' | 'defer'): Promise<{
45
+ valid: true;
46
+ } | {
47
+ valid: false;
48
+ reason: string;
49
+ }>;
50
+ /**
51
+ * Clears all guardrails caches.
52
+ * Use this for test isolation or when guardrails config reloads at runtime.
53
+ */
54
+ export declare function clearGuardrailsCaches(): void;
55
+ /**
56
+ * Normalizes a file path using fs.realpathSync with caching.
57
+ * This resolves symlinks and normalizes the path for cross-platform consistency.
58
+ * @param filePath The file path to normalize (absolute or relative)
59
+ * @param cwd Working directory for relative paths
60
+ * @returns Normalized absolute path or original on error
61
+ */
62
+ export declare function normalizePathWithCache(filePath: string, cwd: string): string;
63
+ /**
64
+ * Gets or creates a cached picomatch matcher for a glob pattern.
65
+ * @param pattern Glob pattern to compile
66
+ * @param caseInsensitive Whether to use case-insensitive matching (default: true on Windows/macOS)
67
+ * @returns Matcher function that returns true if path matches the pattern
68
+ */
69
+ export declare function getGlobMatcher(pattern: string, caseInsensitive?: boolean): (path: string) => boolean;
70
+ export type AgentRule = {
71
+ readOnly?: boolean;
72
+ blockedExact?: string[];
73
+ allowedExact?: string[];
74
+ blockedPrefix?: string[];
75
+ allowedPrefix?: string[];
76
+ blockedZones?: FileZone[];
77
+ blockedGlobs?: string[];
78
+ allowedGlobs?: string[];
79
+ };
80
+ export declare const DEFAULT_AGENT_AUTHORITY_RULES: Record<string, AgentRule>;
81
+ /**
82
+ * Checks whether a write target path (or any ancestor strictly inside cwd)
83
+ * is a symlink. Writing through a symlink can redirect the write to a
84
+ * location outside the working directory, bypassing scope containment.
85
+ *
86
+ * The walk stops at cwd — cwd itself is NOT lstat'd. A user's chosen
87
+ * working directory may legitimately be reached via a symlink (e.g.,
88
+ * macOS's /tmp → /private/tmp), and that symlink does not constitute a
89
+ * redirect *within* the workspace. Only attacker-plantable symlinks
90
+ * BELOW cwd are relevant to this guard.
91
+ *
92
+ * ENOENT on any node in the chain is allowed — the file/dir doesn't exist yet.
93
+ * Any other lstat error (EPERM, EACCES, ENAMETOOLONG, …) fails closed:
94
+ * an unverifiable ancestor must not be written through, even if the OS
95
+ * would eventually reject the write. Defense-in-depth over optimism.
96
+ *
97
+ * @returns A block reason string if a symlink is detected, null if all clear.
98
+ */
99
+ export declare function checkWriteTargetForSymlink(targetPath: string, cwd: string): string | null;
100
+ /**
101
+ * Builds the effective rules map by merging user-configured rules with defaults.
102
+ * User overrides take precedence for each field.
103
+ */
104
+ export declare function buildEffectiveRules(authorityConfig?: AuthorityConfig): Record<string, AgentRule>;
105
+ /**
106
+ * Returns true when `targetAbsolute` and `cwdAbsolute` resolve to different
107
+ * filesystem roots. On POSIX this is always false (single root `/`); on
108
+ * Windows it is true when the two paths sit on different drive letters or
109
+ * different UNC roots — the symptom Codex flagged on PR #501, where
110
+ * `path.relative('C:\\repo', 'D:\\secret.txt')` returns the absolute
111
+ * `'D:\\secret.txt'` and slips past `startsWith('../')` containment.
112
+ *
113
+ * Exposed (and accepts an injectable `pathLib`) so the cross-drive guard
114
+ * is falsifiable on Linux CI without depending on a Windows runner: tests
115
+ * pass `path.win32` / `path.posix` directly.
116
+ */
117
+ export declare function isOnDifferentFilesystemRoot(targetAbsolute: string, cwdAbsolute: string, pathLib?: Pick<typeof path, 'parse'>): boolean;
118
+ /**
119
+ * Checks file path authority against a pre-computed rules map.
120
+ * Implements DENY-first evaluation order:
121
+ * 1. readOnly - blocks all writes
122
+ * 2. blockedExact - exact path matches (fast path)
123
+ * 3. blockedGlobs - glob pattern matches
124
+ * 4. allowedExact - explicit allow for exact paths
125
+ * 5. blockedZones - zone-based blocking (runs before allowedGlobs so that generated
126
+ * output dirs like dist/ and build/ cannot be bypassed by a glob pattern match)
127
+ * 6. allowedGlobs - explicit allow for glob patterns (overrides blockedPrefix/allowedPrefix
128
+ * but NOT blockedZones, which is already decided in Step 5)
129
+ * 7. blockedPrefix - prefix-based blocking (takes priority over allowedPrefix)
130
+ * 8. allowedPrefix - prefix-based allow (whitelist)
131
+ */
132
+ export declare function checkFileAuthorityWithRules(agentName: string, filePath: string, cwd: string, effectiveRules: Record<string, AgentRule>, options?: {
133
+ declaredScope?: string[] | null;
134
+ }): {
135
+ allowed: true;
136
+ } | {
137
+ allowed: false;
138
+ reason: string;
139
+ zone?: FileZone;
140
+ };
141
+ /**
142
+ * Checks whether the given agent is authorised to write to the given file path.
143
+ */
144
+ export declare function checkFileAuthority(agentName: string, filePath: string, cwd: string, authorityConfig?: AuthorityConfig, options?: {
145
+ declaredScope?: string[] | null;
146
+ }): {
147
+ allowed: true;
148
+ } | {
149
+ allowed: false;
150
+ reason: string;
151
+ zone?: FileZone;
152
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Shared Helper Functions — Guardrails
3
+ *
4
+ * Extracted from tool-before.ts (task 1.4 / FR-005).
5
+ * Contains pure utility functions used by the toolBefore handler
6
+ * and potentially other guardrails submodules.
7
+ */
8
+ /**
9
+ * Checks if a file path is a config file either via zone classification
10
+ * or by matching known verifier config glob patterns.
11
+ */
12
+ export declare function isConfigFilePath(filePath: string, cwd: string, extraGlobs?: readonly string[]): boolean;
13
+ /**
14
+ * Detects if a tool is a write-class tool that modifies file contents.
15
+ */
16
+ export declare function isWriteTool(toolName: string): boolean;
17
+ /**
18
+ * Detects if a file path is outside the .swarm/ directory.
19
+ */
20
+ export declare function isOutsideSwarmDir(filePath: string, directory: string): boolean;
21
+ /**
22
+ * Detects if a file path is source code (not docs, config, or metadata).
23
+ */
24
+ export declare function isSourceCodePath(filePath: string): boolean;
25
+ /**
26
+ * Detect obvious traversal segments regardless of destination file type.
27
+ */
28
+ export declare function hasTraversalSegments(filePath: string): boolean;
29
+ /**
30
+ * Check if a file path is within declared scope entries.
31
+ * Handles both exact matches and directory containment.
32
+ */
33
+ export declare function isInDeclaredScope(filePath: string, scopeEntries: string[], cwd?: string): boolean;
34
+ /**
35
+ * Redacts sensitive values from a shell command string before audit logging.
36
+ * Covers env-var assignments, CLI flags, Bearer/Basic auth, and -H header flags.
37
+ */
38
+ export declare function redactShellCommand(cmd: string): string;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Guardrails Hook Module
3
+ *
4
+ * Circuit breaker for runaway LLM agents. Monitors tool usage via OpenCode Plugin API hooks
5
+ * and implements two-layer protection:
6
+ * - Layer 1 (Soft Warning @ warning_threshold): Sets warning flag for messagesTransform to inject warning
7
+ * - Layer 2 (Hard Block @ 100%): Throws error in toolBefore to block further calls, injects STOP message
8
+ */
9
+ import { extractSwarmIdFromAgentName, getSwarmAgents, resolveFallbackModel } from '../../agents/index';
10
+ import { type AuthorityConfig, type GuardrailsConfig } from '../../config/schema';
11
+ import { dcCheckJunctionCreation } from './destructive-command';
12
+ import { getMostRecentAssistantText, getProviderFailureFingerprint, isTransientProviderFailureText } from './messages-transform';
13
+ export declare const _internals: {
14
+ extractSwarmIdFromAgentName: typeof extractSwarmIdFromAgentName;
15
+ getSwarmAgents: typeof getSwarmAgents;
16
+ getMostRecentAssistantText: typeof getMostRecentAssistantText;
17
+ getProviderFailureFingerprint: typeof getProviderFailureFingerprint;
18
+ isTransientProviderFailureText: typeof isTransientProviderFailureText;
19
+ resolveFallbackModel: typeof resolveFallbackModel;
20
+ dcCheckJunctionCreation: typeof dcCheckJunctionCreation;
21
+ extractErrorSignal: typeof extractErrorSignal;
22
+ };
23
+ /**
24
+ * Issue #853 Layer B: tools that are structurally blocked while
25
+ * `.swarm/spec-staleness.json` exists.
26
+ */
27
+ export declare const SPEC_DRIFT_BLOCKED_TOOLS: Set<string>;
28
+ /**
29
+ * Throw SPEC_DRIFT_BLOCK if the tool is on the block-list and the
30
+ * spec-staleness marker file exists.
31
+ */
32
+ export declare function enforceSpecDriftGate(directory: string | undefined, toolName: string): void;
33
+ /**
34
+ * Extracts bounded provider/error signal from unknown hook error payloads.
35
+ */
36
+ declare function extractErrorSignal(errorContent: unknown): string;
37
+ /**
38
+ * Redacts sensitive values from a shell command string before audit logging.
39
+ */
40
+ export declare function redactShellCommand(cmd: string): string;
41
+ /**
42
+ * Creates guardrails hooks for circuit breaker protection
43
+ * @param directory Working directory from plugin init context (required)
44
+ * @param directoryOrConfig Guardrails configuration object (when passed as second arg, replaces legacy config param)
45
+ * @param config Guardrails configuration (optional)
46
+ * @returns Tool before/after hooks and messages transform hook
47
+ */
48
+ export declare function createGuardrailsHooks(directory: string, directoryOrConfig?: string | GuardrailsConfig, config?: GuardrailsConfig, authorityConfig?: AuthorityConfig): {
49
+ toolBefore: (input: {
50
+ tool: string;
51
+ sessionID: string;
52
+ callID: string;
53
+ }, output: {
54
+ args: unknown;
55
+ }) => Promise<void>;
56
+ toolAfter: (input: {
57
+ tool: string;
58
+ sessionID: string;
59
+ callID: string;
60
+ args?: Record<string, unknown>;
61
+ }, output: {
62
+ title: string;
63
+ output: string;
64
+ metadata: unknown;
65
+ }) => Promise<void>;
66
+ messagesTransform: (input: Record<string, never>, output: {
67
+ messages?: Array<{
68
+ info: {
69
+ role: string;
70
+ agent?: string;
71
+ sessionID?: string;
72
+ };
73
+ parts: Array<{
74
+ type: string;
75
+ text?: string;
76
+ [key: string]: unknown;
77
+ }>;
78
+ }>;
79
+ }) => Promise<void>;
80
+ };
81
+ export {};