opencode-swarm 7.152.1 → 7.153.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.
Files changed (121) hide show
  1. package/.opencode/skills/swarm-pr-review/SKILL.md +10 -10
  2. package/dist/background/pending-delegations.d.ts +7 -0
  3. package/dist/cli/{capability-probe-va4kk8vg.js → capability-probe-5wa7mcp6.js} +3 -2
  4. package/dist/cli/{config-doctor-bbqmye7a.js → config-doctor-ze21qs6b.js} +3 -3
  5. package/dist/cli/{core-fh4e6ex0.js → core-qdmhh1kx.js} +2 -2
  6. package/dist/cli/{curation-policy-nem98yc9.js → curation-policy-twa6agf8.js} +7 -7
  7. package/dist/cli/{curator-909s9dvs.js → curator-hxb3wsxr.js} +32 -33
  8. package/dist/cli/curator-llm-factory-s8atmc2n.js +64 -0
  9. package/dist/cli/{evidence-summary-service-kqw4y0yr.js → evidence-summary-service-wzzym9r7.js} +15 -15
  10. package/dist/cli/{gate-evidence-ycts62pj.js → gate-evidence-f71dn9vv.js} +8 -8
  11. package/dist/cli/guardrail-explain-5kzeem4m.js +65 -0
  12. package/dist/cli/{guardrail-log-e1tmdxxa.js → guardrail-log-38jnajpa.js} +6 -6
  13. package/dist/cli/{guardrail-reset-gdnaxxvb.js → guardrail-reset-wzvtmpn2.js} +32 -33
  14. package/dist/cli/{hive-promoter-d0xydg6r.js → hive-promoter-55qcqgfx.js} +32 -33
  15. package/dist/cli/{index-x6803sxh.js → index-0tyc8kdg.js} +2 -2
  16. package/dist/cli/{index-z8s2asdk.js → index-0tzv5zg2.js} +22 -150
  17. package/dist/cli/{index-3djxegsc.js → index-1akn19mp.js} +1 -1
  18. package/dist/cli/{index-q77e475x.js → index-1dtshrzg.js} +5 -5
  19. package/dist/cli/{index-73pmnsjg.js → index-1gzdnjg9.js} +2 -2
  20. package/dist/cli/{index-4tvvh7pn.js → index-23rneydq.js} +36 -35
  21. package/dist/cli/{index-z4bg1hv8.js → index-2mqmvxac.js} +9 -2
  22. package/dist/cli/index-3cegckkq.js +239 -0
  23. package/dist/cli/{index-pvrap7xg.js → index-3ttktt56.js} +1 -1
  24. package/dist/cli/{index-85gkedsf.js → index-4fwbbad3.js} +521 -15
  25. package/dist/cli/{index-qqrdmaxq.js → index-4g0g3dqz.js} +2 -2
  26. package/dist/cli/{index-44xdtqz8.js → index-4pjkdcgc.js} +1 -1
  27. package/dist/cli/{index-29vd7v3e.js → index-6vgb5fyb.js} +1 -1
  28. package/dist/cli/{index-sg2sx2q7.js → index-6ybjrqf7.js} +13095 -9360
  29. package/dist/cli/{index-txbd25bc.js → index-891g7hbf.js} +1 -1
  30. package/dist/cli/{index-02ajc6d1.js → index-8v9sj4aq.js} +2 -2
  31. package/dist/cli/{index-z9fyd0d6.js → index-9v8d3c0k.js} +450 -48
  32. package/dist/cli/{index-26vyr198.js → index-af0cqe6n.js} +16 -16
  33. package/dist/cli/{index-ryecg2st.js → index-ajkxx2k6.js} +3 -3
  34. package/dist/cli/{index-k3d919yf.js → index-gn26q90p.js} +85 -24
  35. package/dist/cli/{index-0tfgn5x0.js → index-k10qtq71.js} +9 -9
  36. package/dist/cli/{index-xyfdmrv7.js → index-mb7b1gye.js} +3 -3
  37. package/dist/cli/{index-0q2mjxp2.js → index-meg33yyv.js} +1 -1
  38. package/dist/cli/{index-cpg98bh1.js → index-myt57pht.js} +1 -1
  39. package/dist/cli/{index-2e1294d1.js → index-n5pxkera.js} +16 -10
  40. package/dist/cli/{index-n0vc5b5t.js → index-n823qsae.js} +16 -7
  41. package/dist/cli/{index-6zkm4p7p.js → index-pk1gbqmq.js} +1 -1
  42. package/dist/cli/{index-nd8tkb3g.js → index-smqsp8hs.js} +5 -5
  43. package/dist/cli/{index-7fh82sm1.js → index-td0jz8xe.js} +6 -6
  44. package/dist/cli/{index-dn6yt0xg.js → index-tdzbm974.js} +7 -7
  45. package/dist/cli/{index-rq6aetkv.js → index-v689bmr2.js} +4 -4
  46. package/dist/cli/{index-79g6edht.js → index-x3y6zhgj.js} +1 -1
  47. package/dist/cli/{index-aw492hpn.js → index-x8vw6ap1.js} +58 -41
  48. package/dist/cli/{index-96m8ckpq.js → index-xkjrg9k4.js} +4 -4
  49. package/dist/cli/{index-mken64yj.js → index-zppxpc3y.js} +2 -2
  50. package/dist/cli/index.js +32 -33
  51. package/dist/cli/{knowledge-escalator-4egcr928.js → knowledge-escalator-w05ztgvk.js} +13 -13
  52. package/dist/cli/{knowledge-events-kdyv4jzn.js → knowledge-events-pwm9jc0m.js} +11 -11
  53. package/dist/cli/{knowledge-link-h8cwj7af.js → knowledge-link-ge0rddxw.js} +6 -6
  54. package/dist/cli/{knowledge-store-dq3cvg53.js → knowledge-store-4xv65tj2.js} +7 -7
  55. package/dist/cli/{knowledge-validator-4bhtden9.js → knowledge-validator-0ttfsca7.js} +9 -9
  56. package/dist/cli/{model-preflight-7ea00h34.js → model-preflight-b95w8wxy.js} +1 -1
  57. package/dist/cli/{pending-delegations-a2nmssr1.js → pending-delegations-nk78wx7h.js} +4 -4
  58. package/dist/cli/{pr-subscriptions-7879nq5k.js → pr-subscriptions-dq1f61ye.js} +4 -4
  59. package/dist/cli/{runner-w81n9js3.js → runner-n50w1y4w.js} +7 -7
  60. package/dist/cli/{scan-cursor-2eezv81a.js → scan-cursor-b6n15w8d.js} +8 -8
  61. package/dist/cli/{schema-v4qcqhek.js → schema-jkbfwqr2.js} +2 -2
  62. package/dist/cli/{scope-persistence-y2r227r0.js → scope-persistence-5xt2m5ep.js} +10 -10
  63. package/dist/cli/{skill-generator-hhzxvb3h.js → skill-generator-3keadpf7.js} +15 -15
  64. package/dist/cli/{telemetry-52pw5xac.js → telemetry-hh1j99za.js} +1 -1
  65. package/dist/cli/{worktree-collision-ownership-p31zm5k0.js → worktree-collision-ownership-4drcjymy.js} +15 -10
  66. package/dist/cli/{worktree-isolation-ve338ez6.js → worktree-isolation-1ymaxn4h.js} +32 -33
  67. package/dist/commands/approve-write.d.ts +1 -0
  68. package/dist/commands/index.d.ts +1 -0
  69. package/dist/commands/lanes.d.ts +25 -0
  70. package/dist/commands/registry.d.ts +7 -0
  71. package/dist/config/schema.d.ts +39 -4
  72. package/dist/consensus/contracts.d.ts +14 -0
  73. package/dist/consensus/corpus.d.ts +16 -0
  74. package/dist/hooks/delegation-gate/worktree-isolation.d.ts +50 -3
  75. package/dist/hooks/delegation-gate/worktree-provisioning-owner.d.ts +40 -2
  76. package/dist/hooks/delegation-gate/worktree-recovery-authority.d.ts +201 -0
  77. package/dist/hooks/full-auto-permission.d.ts +7 -0
  78. package/dist/hooks/guardrails/index.d.ts +2 -1
  79. package/dist/hooks/guardrails/tool-before.d.ts +4 -0
  80. package/dist/hooks/init-orphan-recovery.d.ts +5 -1
  81. package/dist/hooks/pr-workflow-gate.d.ts +12 -0
  82. package/dist/hooks/scope-guard.d.ts +1 -3
  83. package/dist/hooks/trajectory-logger.d.ts +22 -6
  84. package/dist/hooks/trajectory-step-state.d.ts +43 -2
  85. package/dist/index.js +645 -622
  86. package/dist/learning/provenance.d.ts +9 -0
  87. package/dist/observability/catalog.d.ts +6 -4
  88. package/dist/prm/index.d.ts +3 -3
  89. package/dist/prm/replay.d.ts +29 -2
  90. package/dist/prm/trajectory-store.d.ts +210 -20
  91. package/dist/sandbox/capability-probe.d.ts +40 -1
  92. package/dist/sandbox/executor.d.ts +26 -1
  93. package/dist/sandbox/linux/bubblewrap-executor.d.ts +3 -1
  94. package/dist/sandbox/macos/sandbox-exec-executor.d.ts +3 -1
  95. package/dist/sandbox/skip-state.d.ts +24 -0
  96. package/dist/sandbox/win32/native-sandbox-executor.d.ts +5 -1
  97. package/dist/security/command-classifier.d.ts +22 -0
  98. package/dist/security/protected-input-integrity.d.ts +19 -0
  99. package/dist/security/protected-path-policy.d.ts +9 -0
  100. package/dist/security/write-authority.d.ts +46 -0
  101. package/dist/services/diagnose-service.d.ts +1 -1
  102. package/dist/services/skill-generator.d.ts +2 -0
  103. package/dist/services/skill-improver.d.ts +69 -0
  104. package/dist/telemetry.d.ts +21 -1
  105. package/dist/tools/dispatch-lanes.d.ts +47 -0
  106. package/dist/tools/repo-graph/query.d.ts +2 -1
  107. package/dist/tools/repo-graph/types.d.ts +64 -0
  108. package/dist/tools/repo-graph.d.ts +1 -1
  109. package/dist/tools/skill-improve.d.ts +7 -0
  110. package/dist/utils/atomic-write.d.ts +2 -0
  111. package/dist/utils/canonical-root.d.ts +31 -0
  112. package/dist/workflow/coder-settlement.d.ts +2 -0
  113. package/dist/worktree/index.d.ts +2 -2
  114. package/dist/worktree/merge.d.ts +47 -0
  115. package/package.json +2 -1
  116. package/dist/cli/curator-llm-factory-462rysph.js +0 -65
  117. package/dist/cli/guardrail-explain-7620hasq.js +0 -66
  118. package/dist/cli/index-3g0xk6tk.js +0 -219
  119. package/dist/cli/index-fxvgaeq4.js +0 -125
  120. package/dist/cli/worktree-provisioning-owner-cve95q4f.js +0 -22
  121. package/dist/cli/{curator-drift-2h1d2fnq.js → curator-drift-h57z6tm3.js} +1 -1
@@ -17,6 +17,7 @@
17
17
  * supply `producedAt` — callers that need determinism pass it explicitly.
18
18
  */
19
19
  import { z } from 'zod';
20
+ import { type WriteAuthorityOrigin } from '../security/write-authority.js';
20
21
  /**
21
22
  * Hard cap on entries retained per reference class. Sorting happens before the
22
23
  * cap so truncation is deterministic rather than insertion-order dependent.
@@ -41,6 +42,12 @@ export declare const LearningProvenanceV1Schema: z.ZodObject<{
41
42
  writeOrigin: z.ZodObject<{
42
43
  sessionId: z.ZodOptional<z.ZodString>;
43
44
  agentRole: z.ZodOptional<z.ZodString>;
45
+ authority: z.ZodOptional<z.ZodEnum<{
46
+ critic_approved: "critic_approved";
47
+ autonomous: "autonomous";
48
+ optimizer_proposed: "optimizer_proposed";
49
+ human_approved: "human_approved";
50
+ }>>;
44
51
  producedAt: z.ZodISODateTime;
45
52
  }, z.core.$strict>;
46
53
  }, z.core.$strict>;
@@ -56,6 +63,7 @@ export interface LearningProvenanceV1 {
56
63
  writeOrigin: {
57
64
  sessionId?: string;
58
65
  agentRole?: string;
66
+ authority?: WriteAuthorityOrigin;
59
67
  producedAt: string;
60
68
  };
61
69
  }
@@ -72,6 +80,7 @@ export interface LearningProvenanceInput {
72
80
  export interface LearningWriteOriginInput {
73
81
  sessionId?: string;
74
82
  agentRole?: string;
83
+ authority?: WriteAuthorityOrigin;
75
84
  producedAt?: string;
76
85
  }
77
86
  /**
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The event catalog (issue #2029).
3
3
  *
4
- * Exactly 49 entries, matching the `TelemetryEvent` union at
4
+ * Exactly 50 entries, matching the `TelemetryEvent` union at
5
5
  * `src/telemetry.ts:15-131`. Thirty-eight predate the #2029 contract; the 39th is
6
6
  * `agent_conflict_detected` (previously emitted through a force-cast past the
7
7
  * type system), the 40th is `close_archive_result` (issue #2030 — the
@@ -16,9 +16,11 @@
16
16
  * `skill_usage_health` storage audit for the issue-#2038
17
17
  * `.swarm/skill-usage.jsonl` store and its authoritative pending sidecar,
18
18
  * the 48th is the bounded `core_events_health` storage audit for the
19
- * issue-#2039 `.swarm/events.jsonl` store, and the 49th is the bounded
19
+ * issue-#2039 `.swarm/events.jsonl` store, the 49th is the bounded
20
20
  * `shell_audit_health` storage audit for the issue-#2040
21
- * `.swarm/session/shell-audit.jsonl` security-audit store. These late additions are instances
21
+ * `.swarm/session/shell-audit.jsonl` security-audit store, and the 50th is
22
+ * the bounded `trajectory_health` storage audit for the issue-#2041
23
+ * `.swarm/trajectories/` PRM session store. These late additions are instances
22
24
  * of the defect class this contract exists to close: an event kind entering
23
25
  * the stream with no registration.
24
26
  *
@@ -73,7 +75,7 @@ export interface CatalogEntry {
73
75
  /**
74
76
  * Whether an event of this kind must carry `trace.parentSpanId`.
75
77
  *
76
- * `false` for all 49 entries today, and that is a truthful statement about
78
+ * `false` for all 50 entries today, and that is a truthful statement about
77
79
  * the current system rather than a placeholder: no producer supplies a
78
80
  * parent span, so `createObservation` never sets one. Setting this to `true`
79
81
  * for a kind whose producer cannot supply a parent would make every
@@ -26,7 +26,7 @@ import { formatCourseCorrectionForInjection, generateCourseCorrection } from './
26
26
  import { EscalationTracker } from './escalation';
27
27
  import { detectPatterns } from './pattern-detector';
28
28
  import { recordReplayEntry, startReplayRecording } from './replay';
29
- import { cleanupOldTrajectoryFiles, clearTrajectoryCache, getInMemoryTrajectory, readTrajectory } from './trajectory-store';
29
+ import { clearTrajectoryCache, getInMemoryTrajectory, readTrajectoryWithCoverage, scheduleTrajectoryCleanup } from './trajectory-store';
30
30
  import type { PrmConfig } from './types';
31
31
  /**
32
32
  * Test-only dependency-injection seam — see `gitignore-warning.ts:_internals`.
@@ -38,12 +38,12 @@ import type { PrmConfig } from './types';
38
38
  */
39
39
  export declare const _internals: {
40
40
  getAgentSession: typeof getAgentSession;
41
- readTrajectory: typeof readTrajectory;
41
+ readTrajectoryWithCoverage: typeof readTrajectoryWithCoverage;
42
42
  getInMemoryTrajectory: typeof getInMemoryTrajectory;
43
43
  detectPatterns: typeof detectPatterns;
44
44
  generateCourseCorrection: typeof generateCourseCorrection;
45
45
  formatCourseCorrectionForInjection: typeof formatCourseCorrectionForInjection;
46
- cleanupOldTrajectoryFiles: typeof cleanupOldTrajectoryFiles;
46
+ scheduleTrajectoryCleanup: typeof scheduleTrajectoryCleanup;
47
47
  clearTrajectoryCache: typeof clearTrajectoryCache;
48
48
  recordReplayEntry: typeof recordReplayEntry;
49
49
  startReplayRecording: typeof startReplayRecording;
@@ -60,8 +60,25 @@ declare function sanitizeFilename(input: string): string;
60
60
  */
61
61
  export declare function startReplayRecording(sessionID: string, directory: string): Promise<string | null>;
62
62
  /**
63
- * Appends a ReplayEntry to the replay artifact file.
64
- * Non-blocking: errors are caught and logged, never thrown.
63
+ * Hard budgets for replay artifacts (issue #2041 Required 1 — the PRM budget
64
+ * set covers replays as well as trajectories). Replays are write-only
65
+ * best-effort diagnostics: at the cap, further entries for that artifact are
66
+ * skipped with a one-time warning rather than rotated (rotation would create
67
+ * unbounded sibling files, which is exactly what this cap exists to prevent).
68
+ * The 7-day age sweep + per-directory count cap in trajectory-store's
69
+ * `cleanupOldTrajectoryFiles` bound the directory as a whole.
70
+ */
71
+ export declare const REPLAY_LIMITS: {
72
+ /** Per-artifact byte ceiling. */
73
+ readonly maxBytes: number;
74
+ /** Stat cadence per artifact (bytes are tracked in memory between stats). */
75
+ readonly checkIntervalEntries: 16;
76
+ /** Bound on tracked artifacts (Invariant 8). */
77
+ readonly maxTrackedArtifacts: 256;
78
+ };
79
+ /**
80
+ * Appends a ReplayEntry to the replay artifact file, enforcing the per-artifact
81
+ * byte cap. Non-blocking: errors are caught and logged, never thrown.
65
82
  *
66
83
  * @param artifactPath - Path to the replay artifact file
67
84
  * @param sessionID - Session identifier
@@ -72,5 +89,15 @@ export declare const _test_exports: {
72
89
  readonly isPathSafe: typeof isPathSafe;
73
90
  readonly isWithinReplaysDir: typeof isWithinReplaysDir;
74
91
  readonly sanitizeFilename: typeof sanitizeFilename;
92
+ readonly REPLAY_LIMITS: {
93
+ /** Per-artifact byte ceiling. */
94
+ readonly maxBytes: number;
95
+ /** Stat cadence per artifact (bytes are tracked in memory between stats). */
96
+ readonly checkIntervalEntries: 16;
97
+ /** Bound on tracked artifacts (Invariant 8). */
98
+ readonly maxTrackedArtifacts: 256;
99
+ };
100
+ /** Test isolation: drop per-artifact byte-cap bookkeeping. */
101
+ readonly resetReplayByteTracking: () => void;
75
102
  };
76
103
  export {};
@@ -1,40 +1,215 @@
1
1
  /**
2
- * TRAJECTORY STORE (Session-Level)
2
+ * TRAJECTORY STORE (Session-Level) — bounded (issue #2041)
3
3
  *
4
4
  * Per-session trajectory storage for PRM pattern detection.
5
- * Writes to .swarm/trajectories/{sessionId}.jsonl
5
+ * Writes to .swarm/trajectories/{sessionId}.jsonl (+ a small
6
+ * {sessionId}.jsonl.meta.json checkpoint and a transient {sessionId}.jsonl.lock).
7
+ *
8
+ * Bounded session-trajectory contract (mirrors the #2040 shell-audit store):
9
+ * - ONE knob, `maxLines`, governs BOTH the in-memory cache trim and the disk
10
+ * compaction: over budget, the newest floor(maxLines/2) entries are retained
11
+ * (the cache has always used exactly this rule — the disk now matches it).
12
+ * - The disk byte ceiling sessionMaxBytesFor(maxLines) is SOVEREIGN and is
13
+ * enforced at APPEND time (the stat needed for torn-tail re-framing doubles
14
+ * as the size probe): over ceiling, compaction runs before the append.
15
+ * - A line-count check runs every `checkIntervalAppends` appends (the only
16
+ * part that needs a read; it is tail-bounded, never a whole-file read).
17
+ * - Readers (`readTrajectory`, `getCurrentStep`) read a bounded tail window and
18
+ * disclose coverage; `getCurrentStep` also consults the atomically persisted
19
+ * checkpoint so a restart never scans history to continue step numbering.
20
+ * - Crash semantics: compaction publishes via tmp+rename (a partial rewrite is
21
+ * never visible); the checkpoint is written after the data rewrite under the
22
+ * same lock, and merges `max(previous, observed)` so it can only ratchet UP.
23
+ * Compaction keeps the NEWEST entries, so the global max step always survives
24
+ * on disk — except when every retained line is corrupt, in which case the
25
+ * checkpoint alone preserves step continuity (that is why it exists).
26
+ * - Two writers: an in-process per-key promise chain (same-process appends
27
+ * never burn the cross-process retry budget) plus a per-file `.lock`
28
+ * (wx existence create — the PID inside is diagnostic only, never
29
+ * liveness-checked — stale-break, bounded retry). Lock exhaustion skips the
30
+ * append with a warning — telemetry loss is preferred over file corruption
31
+ * in this best-effort store — and is counted into `trajectory_health`.
6
32
  *
7
33
  * Coexists with task-level trajectory-logger.ts which writes to
8
- * .swarm/evidence/{taskId}/trajectory.jsonl for audit/evidence.
34
+ * .swarm/evidence/{taskId}/trajectory.jsonl for audit/evidence (bounded by its
35
+ * own write-side truncation; untouched by this contract).
9
36
  */
37
+ import { telemetry } from '../telemetry';
10
38
  import type { TrajectoryEntry } from './types';
39
+ /**
40
+ * Hard budgets for the bounded session-trajectory contract (issue #2041).
41
+ * Exported for tests and for the retention registry's documented contract.
42
+ */
43
+ export declare const TRAJECTORY_LIMITS: {
44
+ /** Line-count compaction check cadence (append-path, amortized). */
45
+ readonly checkIntervalAppends: 25;
46
+ /** Tail-bounded read window for readTrajectoryWithCoverage. */
47
+ readonly readMaxBytes: number;
48
+ /** Tail-bounded read window for getCurrentStep (newest lines carry max step). */
49
+ readonly stepReadMaxBytes: number;
50
+ /** Tail-bounded input window for compaction (legacy files may be huge). */
51
+ readonly compactMaxBytes: number;
52
+ /** A JSONL line longer than this is shed as oversize (append skips it). */
53
+ readonly maxLineBytes: number;
54
+ /** Estimated bytes per entry used to derive the per-session byte ceiling. */
55
+ readonly estimatedLineBytes: 512;
56
+ /** Floor for the derived per-session byte ceiling. */
57
+ readonly minSessionMaxBytes: number;
58
+ /** Per-file cross-process lock: bounded retries, then skip + warn. */
59
+ readonly lockRetries: 20;
60
+ readonly lockRetryDelayMs: 5;
61
+ /** A lock older than this is considered crashed and broken. */
62
+ readonly lockStaleMs: number;
63
+ /** Per-directory session-file count cap (age-sweep backstop). */
64
+ readonly maxFilesPerDir: 200;
65
+ /** Unlinks per cleanup invocation (converges across runs). */
66
+ readonly maxDeletionsPerRun: 256;
67
+ /** Default age sweep horizon for trajectories/ and replays/. */
68
+ readonly defaultMaxAgeDays: 7;
69
+ /** Debounce for the lazily scheduled cleanup pass. */
70
+ readonly cleanupDebounceMs: number;
71
+ /** Cooldown for append_skip health events (lock-exhaust observability). */
72
+ readonly appendSkipEventCooldownMs: 60000;
73
+ };
74
+ /**
75
+ * Sovereign per-session disk byte ceiling, derived from the same `maxLines`
76
+ * knob that governs cache trimming and line-count compaction.
77
+ */
78
+ export declare function sessionMaxBytesFor(maxLines: number): number;
79
+ /** Test/DI seam — see `gitignore-warning.ts:_internals` for the convention. */
80
+ export declare const _internals: {
81
+ telemetry: typeof telemetry;
82
+ };
11
83
  /**
12
84
  * Returns cached trajectory entries for a session (empty array if not cached).
85
+ *
86
+ * @param sessionId - Session identifier
87
+ * @param directory - Workspace root that owns the session's trajectory file
13
88
  */
14
- export declare function getInMemoryTrajectory(sessionId: string): TrajectoryEntry[];
89
+ export declare function getInMemoryTrajectory(sessionId: string, directory: string): TrajectoryEntry[];
15
90
  /**
16
91
  * Clears trajectory cache (for test isolation or session cleanup).
92
+ *
93
+ * With a sessionId but no directory, every root's entry for that session is
94
+ * cleared (suffix scan over the bounded map) — reset paths that legitimately
95
+ * lack a directory still release the cached window.
17
96
  */
18
97
  export declare function clearTrajectoryCache(sessionId?: string): void;
98
+ interface TrajectoryCheckpoint {
99
+ version: 1;
100
+ highestStep: number;
101
+ droppedEntries: number;
102
+ /**
103
+ * Cumulative bytes discarded whole — chiefly the pre-window portion of an
104
+ * oversized file that a tail-bounded compaction rewrote away (shed
105
+ * windowed entries also contribute their bytes here, alongside their
106
+ * counts in droppedEntries: the two metrics are independent dimensions
107
+ * of the same loss). Without this, a compaction whose window kept every
108
+ * windowed entry (droppedEntries delta 0) would silently erase megabytes
109
+ * beyond the window while coverage still reported `complete`. Optional
110
+ * on READ for checkpoints written before this field existed.
111
+ */
112
+ droppedBytes?: number;
113
+ compactedAt: string;
114
+ }
115
+ /**
116
+ * Reads a session's persisted step checkpoint (null when absent/corrupt).
117
+ *
118
+ * Production consumers: `getCurrentStep` (restart continuity) and the consensus
119
+ * corpus's default PRM coverage signal (dropped-by-compaction disclosure).
120
+ */
121
+ export declare function readTrajectoryCheckpoint(sessionId: string, directory: string): Promise<TrajectoryCheckpoint | null>;
122
+ /**
123
+ * One bounded stat of a session's trajectory file (null when absent).
124
+ *
125
+ * Production consumer: the consensus corpus's default PRM coverage verdict —
126
+ * a file larger than the read window means the live bounded read was
127
+ * window-truncated, exactly matching `readTrajectoryWithCoverage`'s coverage
128
+ * semantics so the two paths cannot diverge (implementation-review round 1).
129
+ */
130
+ export declare function trajectoryFileBytes(sessionId: string, directory: string): Promise<number | null>;
19
131
  /**
20
- * Appends a single TrajectoryEntry to the session's trajectory file.
132
+ * Appends a single TrajectoryEntry to the session's trajectory file, enforcing
133
+ * the bounded session-trajectory contract on disk as well as in memory.
21
134
  *
22
135
  * @param sessionId - Session identifier
23
136
  * @param entry - Trajectory entry to append
24
137
  * @param directory - Base directory (workspace root)
25
- * @param maxLines - Maximum lines before in-memory cache trimming (default 1000)
138
+ * @param maxLines - Line budget for BOTH the in-memory cache trim and disk
139
+ * compaction (compaction retains the newest floor(maxLines/2) entries; the
140
+ * derived byte ceiling sessionMaxBytesFor(maxLines) is sovereign)
26
141
  */
27
142
  export declare function appendTrajectoryEntry(sessionId: string, entry: TrajectoryEntry, directory: string, maxLines?: number): Promise<void>;
143
+ export type TrajectoryCoverage = 'complete' | 'truncated' | 'empty';
144
+ export interface TrajectoryReadWithCoverage {
145
+ entries: TrajectoryEntry[];
146
+ coverage: TrajectoryCoverage;
147
+ /** Entries removed by prior compactions (persisted checkpoint total). */
148
+ droppedByCompaction: number;
149
+ skippedMalformed: number;
150
+ }
28
151
  /**
29
- * Reads all TrajectoryEntry records from a session's trajectory file.
152
+ * Tail-bounded read of a session's trajectory with explicit coverage
153
+ * disclosure (issue #2041 Required 3/4). Reads at most
154
+ * TRAJECTORY_LIMITS.readMaxBytes regardless of file size; the returned entries
155
+ * are the newest window in file order.
156
+ *
157
+ * `maxLines` is the SAME knob the append path uses: a cold read populates the
158
+ * in-memory cache trimmed to the configured budget, so a legacy pre-fix file
159
+ * cannot inflate the cache PRM pattern detection reads (review round 1).
160
+ */
161
+ export declare function readTrajectoryWithCoverage(sessionId: string, directory: string, maxLines?: number): Promise<TrajectoryReadWithCoverage>;
162
+ /**
163
+ * Reads the newest bounded window of TrajectoryEntry records from a session's
164
+ * trajectory file.
30
165
  *
31
166
  * @param sessionId - Session identifier
32
167
  * @param directory - Base directory (workspace root)
168
+ * @param maxLines - Optional cache-trim budget (same knob as the append path)
33
169
  * @returns Array of trajectory entries (empty array if file doesn't exist)
34
170
  */
35
- export declare function readTrajectory(sessionId: string, directory: string): Promise<TrajectoryEntry[]>;
171
+ export declare function readTrajectory(sessionId: string, directory: string, maxLines?: number): Promise<TrajectoryEntry[]>;
36
172
  export declare const _test_exports: {
37
173
  readonly MAX_TRACKED_TRAJECTORY_SESSIONS: 500;
174
+ readonly TRAJECTORY_LIMITS: {
175
+ /** Line-count compaction check cadence (append-path, amortized). */
176
+ readonly checkIntervalAppends: 25;
177
+ /** Tail-bounded read window for readTrajectoryWithCoverage. */
178
+ readonly readMaxBytes: number;
179
+ /** Tail-bounded read window for getCurrentStep (newest lines carry max step). */
180
+ readonly stepReadMaxBytes: number;
181
+ /** Tail-bounded input window for compaction (legacy files may be huge). */
182
+ readonly compactMaxBytes: number;
183
+ /** A JSONL line longer than this is shed as oversize (append skips it). */
184
+ readonly maxLineBytes: number;
185
+ /** Estimated bytes per entry used to derive the per-session byte ceiling. */
186
+ readonly estimatedLineBytes: 512;
187
+ /** Floor for the derived per-session byte ceiling. */
188
+ readonly minSessionMaxBytes: number;
189
+ /** Per-file cross-process lock: bounded retries, then skip + warn. */
190
+ readonly lockRetries: 20;
191
+ readonly lockRetryDelayMs: 5;
192
+ /** A lock older than this is considered crashed and broken. */
193
+ readonly lockStaleMs: number;
194
+ /** Per-directory session-file count cap (age-sweep backstop). */
195
+ readonly maxFilesPerDir: 200;
196
+ /** Unlinks per cleanup invocation (converges across runs). */
197
+ readonly maxDeletionsPerRun: 256;
198
+ /** Default age sweep horizon for trajectories/ and replays/. */
199
+ readonly defaultMaxAgeDays: 7;
200
+ /** Debounce for the lazily scheduled cleanup pass. */
201
+ readonly cleanupDebounceMs: number;
202
+ /** Cooldown for append_skip health events (lock-exhaust observability). */
203
+ readonly appendSkipEventCooldownMs: 60000;
204
+ };
205
+ readonly sessionMaxBytesFor: typeof sessionMaxBytesFor;
206
+ readonly getCacheSize: () => number;
207
+ readonly getChainSize: () => number;
208
+ /** Test isolation: the cleanup debounce is module-global state. */
209
+ readonly resetCleanupDebounce: () => void;
210
+ /** Test isolation: lock-skip counters are module-global state. */
211
+ readonly resetLockSkipCounters: () => void;
212
+ readonly appendFileIsTorn: (filePath: string) => Promise<boolean>;
38
213
  };
39
214
  /**
40
215
  * Alias for readTrajectory - retrieves trajectory entries for a session.
@@ -44,17 +219,16 @@ export declare const _test_exports: {
44
219
  * @returns Array of trajectory entries
45
220
  */
46
221
  export declare function getTrajectoryForSession(sessionId: string, directory: string): Promise<TrajectoryEntry[]>;
47
- /**
48
- * Truncates the trajectory file to the newest half if lines exceed maxLines.
49
- *
50
- * @param sessionId - Session identifier
51
- * @param directory - Base directory (workspace root)
52
- * @param maxLines - Maximum number of lines to retain
53
- */
54
- export declare function truncateTrajectoryIfNeeded(sessionId: string, directory: string, maxLines: number): Promise<void>;
55
222
  /**
56
223
  * Returns the highest step number in the session's trajectory.
57
- * Used to determine the next step number when appending.
224
+ * Used to determine the next step number when appending — the production
225
+ * restart-seeding path in trajectory-logger.ts calls this before the first
226
+ * mint of a session so step numbers stay monotonic across process restarts.
227
+ *
228
+ * Bounded: reads at most TRAJECTORY_LIMITS.stepReadMaxBytes of the file
229
+ * (the newest lines carry the max step) and merges the atomically persisted
230
+ * checkpoint, so a restart never scans unbounded history — and the high-water
231
+ * mark survives even a fully-corrupt data file.
58
232
  *
59
233
  * @param sessionId - Session identifier
60
234
  * @param directory - Base directory (workspace root)
@@ -62,8 +236,24 @@ export declare function truncateTrajectoryIfNeeded(sessionId: string, directory:
62
236
  */
63
237
  export declare function getCurrentStep(sessionId: string, directory: string): Promise<number>;
64
238
  /**
65
- * Deletes trajectory and replay files older than maxAgeDays.
66
- * Runs against .swarm/trajectories/ and .swarm/replays/ directories.
67
- * Non-blocking: errors logged, not thrown.
239
+ * Debounced, fire-and-forget cleanup scheduling used by the PRM hook's
240
+ * once-per-session trigger. The plugin's post-resolution init pass calls
241
+ * `cleanupOldTrajectoryFiles` directly (the debounce must not suppress the
242
+ * startup sweep).
243
+ */
244
+ export declare function scheduleTrajectoryCleanup(directory: string): void;
245
+ /**
246
+ * Deletes aged trajectory and replay files, then enforces a per-directory
247
+ * session-file count cap (oldest-first) so a hostile or skewed mtime cannot
248
+ * grow `.swarm/trajectories/` or `.swarm/replays/` without bound. A session's
249
+ * `.meta.json` checkpoint is removed with its `.jsonl`; orphan checkpoints and
250
+ * stale atomic-write `*.tmp` leftovers age out on their own. Bounded to
251
+ * maxDeletionsPerRun unlinks per invocation (converges across runs); every
252
+ * failure is swallowed. Never touches `.swarm/evidence/` (task evidence is a
253
+ * different directory tree governed by its own contract).
254
+ *
255
+ * Replays and trajectories are independent artifacts sharing this sweep: no
256
+ * cross-linkage is enforced or implied.
68
257
  */
69
258
  export declare function cleanupOldTrajectoryFiles(directory: string, maxAgeDays?: number): Promise<void>;
259
+ export {};
@@ -9,6 +9,8 @@
9
9
  * Each probe is bounded to 2 seconds via AbortController to satisfy
10
10
  * Invariant 1 (plugin init is fast, bounded, fail-open).
11
11
  */
12
+ import { spawnSync } from 'node:child_process';
13
+ import * as fs from 'node:fs';
12
14
  /** Possible sandbox status values. */
13
15
  export type SandboxStatus = 'enabled' | 'disabled' | 'unsupported';
14
16
  /**
@@ -20,6 +22,7 @@ export type SandboxStatus = 'enabled' | 'disabled' | 'unsupported';
20
22
  * (issue #1778 H2).
21
23
  */
22
24
  export type SandboxStrength = 'strong' | 'advisory';
25
+ export type SandboxDimensionStrength = 'real' | 'weak' | 'none';
23
26
  /** Result of a sandbox capability probe. */
24
27
  export interface SandboxCapability {
25
28
  /** Whether the sandbox mechanism is available. */
@@ -36,6 +39,37 @@ export interface SandboxCapability {
36
39
  /** Error message from the probe, if any. */
37
40
  error?: string;
38
41
  }
42
+ export interface SandboxCapabilityV1 extends SandboxCapability {
43
+ v: 1;
44
+ filesystem: SandboxDimensionStrength;
45
+ network: SandboxDimensionStrength;
46
+ process: SandboxDimensionStrength;
47
+ effective: SandboxDimensionStrength;
48
+ reasons: string[];
49
+ identity: string;
50
+ }
51
+ export interface SandboxRequirements {
52
+ mode?: 'advisory' | 'required';
53
+ require_filesystem?: boolean;
54
+ require_network?: boolean;
55
+ require_process?: boolean;
56
+ network_mode?: 'off' | 'on';
57
+ network_allowlist?: readonly string[];
58
+ writable_roots?: readonly string[];
59
+ }
60
+ export declare function assessSandboxRequirements(capability: SandboxCapabilityV1, requirements: SandboxRequirements | undefined): {
61
+ satisfied: boolean;
62
+ missing: string[];
63
+ };
64
+ interface BehavioralEvidence {
65
+ filesystem: SandboxDimensionStrength;
66
+ network: SandboxDimensionStrength;
67
+ process: SandboxDimensionStrength;
68
+ reasons: string[];
69
+ }
70
+ declare function currentProbeSourceIdentity(platform: 'linux' | 'darwin' | 'win32'): string;
71
+ declare function resolveWindowsCommandProcessorBinary(): string;
72
+ declare function detectBehavioralEvidence(capability: SandboxCapability): BehavioralEvidence;
39
73
  /** Reset the session-lifetime capability cache. Test-only. */
40
74
  export declare function _resetCapabilityCache(): void;
41
75
  /**
@@ -56,6 +90,11 @@ declare function withProbeTimeout(cmd: string, args: string[], ms: number): Prom
56
90
  */
57
91
  export declare const _internals: {
58
92
  withProbeTimeout: typeof withProbeTimeout;
93
+ spawnSync: typeof spawnSync;
94
+ detectBehavioralEvidence: typeof detectBehavioralEvidence;
95
+ resolveWindowsCommandProcessorBinary: typeof resolveWindowsCommandProcessorBinary;
96
+ currentProbeSourceIdentity: typeof currentProbeSourceIdentity;
97
+ removeProbeTempRoot: typeof fs.rmSync;
59
98
  };
60
99
  /**
61
100
  * Detects the availability of OS-native sandbox mechanisms.
@@ -86,6 +125,6 @@ export declare class SandboxCapabilityProbe {
86
125
  *
87
126
  * @returns A promise that resolves to the sandbox capability result.
88
127
  */
89
- detect(): Promise<SandboxCapability>;
128
+ detect(): Promise<SandboxCapabilityV1>;
90
129
  }
91
130
  export {};
@@ -4,6 +4,7 @@
4
4
  * Provides a unified interface for sandboxed shell command execution across
5
5
  * Linux (Bubblewrap), macOS (sandbox-exec), and Windows (restricted token/Low Integrity).
6
6
  */
7
+ import { type SandboxCapabilityV1, type SandboxRequirements } from './capability-probe';
7
8
  /**
8
9
  * Error thrown when sandbox operations fail.
9
10
  */
@@ -11,6 +12,7 @@ export declare class SandboxError extends Error {
11
12
  readonly code: string;
12
13
  constructor(message: string, code: string);
13
14
  }
15
+ export declare function normalizeSandboxMechanism(mechanism: string): string;
14
16
  /**
15
17
  * Validate that a string is a valid POSIX environment variable name.
16
18
  * POSIX env var names: [a-zA-Z_][a-zA-Z0-9_]*
@@ -43,13 +45,33 @@ export interface SandboxExecutor {
43
45
  * @returns The wrapped command string with sandbox prefix
44
46
  * @throws SandboxError if sandbox cannot wrap the command
45
47
  */
46
- wrapCommand(command: string, scopePaths: string[], tempDir?: string, envOverrides?: Record<string, string | null>): string;
48
+ wrapCommand(command: string, scopePaths: string[], tempDir?: string, envOverrides?: Record<string, string | null>, policy?: SandboxPolicyOptions): string;
47
49
  /**
48
50
  * Get the environment variable overrides for this sandbox.
49
51
  * Returns a record of env vars to set/unset.
50
52
  */
51
53
  getEnvOverrides(): Record<string, string | null>;
52
54
  }
55
+ export interface SandboxPolicyOptions {
56
+ network_mode?: 'off' | 'on';
57
+ network_allowlist?: readonly string[];
58
+ writable_roots?: readonly string[];
59
+ }
60
+ declare function detectSandboxCapability(): Promise<SandboxCapabilityV1>;
61
+ export declare const _internals: {
62
+ detectSandboxCapability: typeof detectSandboxCapability;
63
+ };
64
+ export interface SandboxEnforcementAssessment {
65
+ capability: SandboxCapabilityV1;
66
+ requirements: Required<SandboxRequirements>;
67
+ policy: Required<SandboxPolicyOptions>;
68
+ satisfied: boolean;
69
+ missing: string[];
70
+ supported: boolean;
71
+ unsupported: string[];
72
+ cacheKey: string;
73
+ }
74
+ export declare function assessSandboxEnforcement(requirements: SandboxRequirements | undefined): Promise<SandboxEnforcementAssessment>;
53
75
  /**
54
76
  * Get the platform-appropriate sandbox executor.
55
77
  *
@@ -80,3 +102,6 @@ export declare function _getMacOSSandboxPolicyForTest(): boolean;
80
102
  * @internal
81
103
  */
82
104
  export declare function _resetExecutorCache(): void;
105
+ /** @internal test seam */
106
+ export declare function _resetSandboxAssessmentCache(): void;
107
+ export {};
@@ -75,7 +75,9 @@ export declare class BubblewrapSandboxExecutor implements SandboxExecutor {
75
75
  * @returns A bwrap-wrapped command string ready for shell execution,
76
76
  * or the raw command string when the sandbox is unavailable (passthrough mode)
77
77
  */
78
- wrapCommand(command: string, scopePaths: string[], tempDir?: string, envOverrides?: Record<string, string | null>): string;
78
+ wrapCommand(command: string, scopePaths: string[], tempDir?: string, envOverrides?: Record<string, string | null>, policy?: {
79
+ network_mode?: 'off' | 'on';
80
+ }): string;
79
81
  /**
80
82
  * Return environment variable overrides required for the bubblewrap sandbox.
81
83
  *
@@ -104,7 +104,9 @@ export declare class MacOSSandboxExecutor implements SandboxExecutor {
104
104
  * @returns A sandbox-exec wrapped command string ready for shell execution,
105
105
  * or the raw command string when the sandbox is unavailable (passthrough mode)
106
106
  */
107
- wrapCommand(command: string, scopePaths: string[], tempDir?: string, envOverrides?: Record<string, string | null>): string;
107
+ wrapCommand(command: string, scopePaths: string[], tempDir?: string, envOverrides?: Record<string, string | null>, _policy?: {
108
+ network_mode?: 'off' | 'on';
109
+ }): string;
108
110
  /**
109
111
  * Return environment variable overrides required for the macOS sandbox.
110
112
  *
@@ -0,0 +1,24 @@
1
+ export interface SandboxWrapOutcome {
2
+ sessionID: string;
3
+ callID: string;
4
+ originalCommandHash: number;
5
+ finalCommandHash: number;
6
+ wrapped: boolean;
7
+ capabilityIdentity: string;
8
+ assessmentCacheKey: string;
9
+ reason: string;
10
+ originalCommand: string;
11
+ executorMechanism: string;
12
+ capabilityMechanism: string;
13
+ }
14
+ export declare function recordSandboxWrapOutcome(outcome: SandboxWrapOutcome): void;
15
+ export declare function readSandboxWrapOutcome(sessionID: string, callID: string): SandboxWrapOutcome | null;
16
+ export declare function clearSandboxWrapOutcome(sessionID: string, callID: string): void;
17
+ export declare function getSandboxSkipSummary(sessionID?: string): {
18
+ count: number;
19
+ reasons: readonly string[];
20
+ };
21
+ /** @internal test seam */
22
+ export declare function _resetSandboxWrapOutcomeState(): void;
23
+ /** @internal test seam */
24
+ export declare function _sandboxWrapOutcomeStateSize(): number;
@@ -37,7 +37,11 @@ export declare class NativeWindowsSandboxExecutor implements SandboxExecutor {
37
37
  *
38
38
  * When the runner is unavailable, falls back to the PowerShell wrapper.
39
39
  */
40
- wrapCommand(command: string, scopePaths: string[], tempDir?: string, envOverrides?: Record<string, string | null>): string;
40
+ wrapCommand(command: string, scopePaths: string[], tempDir?: string, envOverrides?: Record<string, string | null>, policy?: {
41
+ network_mode?: 'off' | 'on';
42
+ network_allowlist?: readonly string[];
43
+ writable_roots?: readonly string[];
44
+ }): string;
41
45
  private _wrapWithRunner;
42
46
  getEnvOverrides(): Record<string, string | null>;
43
47
  /**
@@ -0,0 +1,22 @@
1
+ export type CommandRiskCategory = 'catastrophic' | 'destructive' | 'unknown' | 'escalate' | 'safe';
2
+ export interface CommandSegmentClassificationV1 {
3
+ segment: string;
4
+ category: CommandRiskCategory;
5
+ reason: string;
6
+ ruleId?: string;
7
+ }
8
+ export interface CommandClassificationV1 {
9
+ v: 1;
10
+ originalDigest: string;
11
+ aggregate: CommandRiskCategory;
12
+ ambiguous: boolean;
13
+ segments: readonly CommandSegmentClassificationV1[];
14
+ }
15
+ export interface SharedClassifierGuardrailBlockV1 {
16
+ ruleId: string;
17
+ category: Extract<CommandRiskCategory, 'catastrophic' | 'destructive'>;
18
+ destructiveCategory: string;
19
+ message: string;
20
+ }
21
+ export declare function getSharedClassifierGuardrailBlock(classification: Readonly<CommandClassificationV1>): Readonly<SharedClassifierGuardrailBlockV1> | null;
22
+ export declare function classifyCommand(original: string): Readonly<CommandClassificationV1>;
@@ -0,0 +1,19 @@
1
+ export interface ProtectedManifestV1 {
2
+ v: 1;
3
+ root: string;
4
+ digest: string;
5
+ entries: readonly string[];
6
+ }
7
+ /** Snapshot a protected regular-file tree; ambiguity and budget exhaustion fail closed. */
8
+ export declare function snapshotProtectedTree(root: string): Promise<ProtectedManifestV1>;
9
+ export declare function verifyProtectedTree(before: ProtectedManifestV1): Promise<void>;
10
+ /** Mandatory transaction wrapper for governed evaluators. Verification always runs. */
11
+ export declare function withProtectedInputIntegrity<T>(root: string, operation: () => Promise<T>): Promise<T>;
12
+ export interface ProtectedSetManifestV1 {
13
+ v: 1;
14
+ roots: readonly string[];
15
+ states: readonly (ProtectedManifestV1 | 'missing')[];
16
+ }
17
+ export declare function snapshotProtectedSet(roots: readonly string[]): Promise<ProtectedSetManifestV1>;
18
+ export declare function verifyProtectedSet(before: ProtectedSetManifestV1): Promise<void>;
19
+ export declare function withProtectedSetIntegrity<T>(roots: readonly string[], operation: () => Promise<T>): Promise<T>;