contextos-mcp 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +260 -0
  3. package/bin/mcp.mjs +61 -0
  4. package/bin/swarm.mjs +45 -0
  5. package/dist/agents/aider.d.ts +12 -0
  6. package/dist/agents/aider.js +192 -0
  7. package/dist/agents/aider.js.map +1 -0
  8. package/dist/agents/claude-code.d.ts +9 -0
  9. package/dist/agents/claude-code.js +236 -0
  10. package/dist/agents/claude-code.js.map +1 -0
  11. package/dist/agents/codex.d.ts +14 -0
  12. package/dist/agents/codex.js +203 -0
  13. package/dist/agents/codex.js.map +1 -0
  14. package/dist/agents/direct-llm.d.ts +22 -0
  15. package/dist/agents/direct-llm.js +254 -0
  16. package/dist/agents/direct-llm.js.map +1 -0
  17. package/dist/agents/mock.d.ts +10 -0
  18. package/dist/agents/mock.js +170 -0
  19. package/dist/agents/mock.js.map +1 -0
  20. package/dist/agents/opencode.d.ts +23 -0
  21. package/dist/agents/opencode.js +631 -0
  22. package/dist/agents/opencode.js.map +1 -0
  23. package/dist/agents/provider.d.ts +11 -0
  24. package/dist/agents/provider.js +31 -0
  25. package/dist/agents/provider.js.map +1 -0
  26. package/dist/compression/compressor.d.ts +28 -0
  27. package/dist/compression/compressor.js +268 -0
  28. package/dist/compression/compressor.js.map +1 -0
  29. package/dist/config.d.ts +43 -0
  30. package/dist/config.js +172 -0
  31. package/dist/config.js.map +1 -0
  32. package/dist/contextos/loader.d.ts +46 -0
  33. package/dist/contextos/loader.js +120 -0
  34. package/dist/contextos/loader.js.map +1 -0
  35. package/dist/contextos/selector.d.ts +46 -0
  36. package/dist/contextos/selector.js +80 -0
  37. package/dist/contextos/selector.js.map +1 -0
  38. package/dist/core/action-dispatcher.d.ts +15 -0
  39. package/dist/core/action-dispatcher.js +28 -0
  40. package/dist/core/action-dispatcher.js.map +1 -0
  41. package/dist/core/action-schema.d.ts +72 -0
  42. package/dist/core/action-schema.js +86 -0
  43. package/dist/core/action-schema.js.map +1 -0
  44. package/dist/core/repl-interface.d.ts +48 -0
  45. package/dist/core/repl-interface.js +5 -0
  46. package/dist/core/repl-interface.js.map +1 -0
  47. package/dist/core/repl.d.ts +59 -0
  48. package/dist/core/repl.js +353 -0
  49. package/dist/core/repl.js.map +1 -0
  50. package/dist/core/rlm.d.ts +65 -0
  51. package/dist/core/rlm.js +564 -0
  52. package/dist/core/rlm.js.map +1 -0
  53. package/dist/core/runtime.py +470 -0
  54. package/dist/core/types.d.ts +265 -0
  55. package/dist/core/types.js +19 -0
  56. package/dist/core/types.js.map +1 -0
  57. package/dist/env.d.ts +10 -0
  58. package/dist/env.js +75 -0
  59. package/dist/env.js.map +1 -0
  60. package/dist/hooks/runner.d.ts +43 -0
  61. package/dist/hooks/runner.js +173 -0
  62. package/dist/hooks/runner.js.map +1 -0
  63. package/dist/labs/cli.d.ts +15 -0
  64. package/dist/labs/cli.js +295 -0
  65. package/dist/labs/cli.js.map +1 -0
  66. package/dist/labs/episodic-memory.d.ts +132 -0
  67. package/dist/labs/episodic-memory.js +391 -0
  68. package/dist/labs/episodic-memory.js.map +1 -0
  69. package/dist/labs/interactive-swarm.d.ts +20 -0
  70. package/dist/labs/interactive-swarm.js +1173 -0
  71. package/dist/labs/interactive-swarm.js.map +1 -0
  72. package/dist/labs/interactive.d.ts +10 -0
  73. package/dist/labs/interactive.js +2099 -0
  74. package/dist/labs/interactive.js.map +1 -0
  75. package/dist/labs/ts-analyzer.d.ts +74 -0
  76. package/dist/labs/ts-analyzer.js +379 -0
  77. package/dist/labs/ts-analyzer.js.map +1 -0
  78. package/dist/labs/viewer.d.ts +12 -0
  79. package/dist/labs/viewer.js +1284 -0
  80. package/dist/labs/viewer.js.map +1 -0
  81. package/dist/main.d.ts +15 -0
  82. package/dist/main.js +247 -0
  83. package/dist/main.js.map +1 -0
  84. package/dist/mcp/server.d.ts +16 -0
  85. package/dist/mcp/server.js +192775 -0
  86. package/dist/mcp/server.js.map +1 -0
  87. package/dist/mcp/session.d.ts +117 -0
  88. package/dist/mcp/session.js +450 -0
  89. package/dist/mcp/session.js.map +1 -0
  90. package/dist/mcp/state.d.ts +42 -0
  91. package/dist/mcp/state.js +537 -0
  92. package/dist/mcp/state.js.map +1 -0
  93. package/dist/mcp/tools/contextos.d.ts +17 -0
  94. package/dist/mcp/tools/contextos.js +583 -0
  95. package/dist/mcp/tools/contextos.js.map +1 -0
  96. package/dist/mcp/tools.d.ts +15 -0
  97. package/dist/mcp/tools.js +393 -0
  98. package/dist/mcp/tools.js.map +1 -0
  99. package/dist/orchestration/reviewer-gate.d.ts +43 -0
  100. package/dist/orchestration/reviewer-gate.js +280 -0
  101. package/dist/orchestration/reviewer-gate.js.map +1 -0
  102. package/dist/orchestration/reviewer-prompt.d.ts +18 -0
  103. package/dist/orchestration/reviewer-prompt.js +67 -0
  104. package/dist/orchestration/reviewer-prompt.js.map +1 -0
  105. package/dist/orchestration/verification-runner.d.ts +53 -0
  106. package/dist/orchestration/verification-runner.js +352 -0
  107. package/dist/orchestration/verification-runner.js.map +1 -0
  108. package/dist/plugins/adapter.d.ts +11 -0
  109. package/dist/plugins/adapter.js +11 -0
  110. package/dist/plugins/adapter.js.map +1 -0
  111. package/dist/plugins/core/archive.d.ts +9 -0
  112. package/dist/plugins/core/archive.js +128 -0
  113. package/dist/plugins/core/archive.js.map +1 -0
  114. package/dist/plugins/core/digest.d.ts +11 -0
  115. package/dist/plugins/core/digest.js +70 -0
  116. package/dist/plugins/core/digest.js.map +1 -0
  117. package/dist/plugins/core/grants.d.ts +22 -0
  118. package/dist/plugins/core/grants.js +90 -0
  119. package/dist/plugins/core/grants.js.map +1 -0
  120. package/dist/plugins/core/index.d.ts +10 -0
  121. package/dist/plugins/core/index.js +11 -0
  122. package/dist/plugins/core/index.js.map +1 -0
  123. package/dist/plugins/core/pinning.d.ts +15 -0
  124. package/dist/plugins/core/pinning.js +86 -0
  125. package/dist/plugins/core/pinning.js.map +1 -0
  126. package/dist/plugins/core/types.d.ts +105 -0
  127. package/dist/plugins/core/types.js +22 -0
  128. package/dist/plugins/core/types.js.map +1 -0
  129. package/dist/plugins/core/updater.d.ts +16 -0
  130. package/dist/plugins/core/updater.js +64 -0
  131. package/dist/plugins/core/updater.js.map +1 -0
  132. package/dist/plugins/loader.d.ts +65 -0
  133. package/dist/plugins/loader.js +110 -0
  134. package/dist/plugins/loader.js.map +1 -0
  135. package/dist/prompts/orchestrator.d.ts +6 -0
  136. package/dist/prompts/orchestrator.js +114 -0
  137. package/dist/prompts/orchestrator.js.map +1 -0
  138. package/dist/routing/model-resolver.d.ts +27 -0
  139. package/dist/routing/model-resolver.js +265 -0
  140. package/dist/routing/model-resolver.js.map +1 -0
  141. package/dist/routing/model-router.d.ts +130 -0
  142. package/dist/routing/model-router.js +515 -0
  143. package/dist/routing/model-router.js.map +1 -0
  144. package/dist/runtime/attestations.cjs +260 -0
  145. package/dist/runtime/reviewer-pipeline.cjs +319 -0
  146. package/dist/runtime/sandbox.cjs +468 -0
  147. package/dist/runtime/state-machine.cjs +885 -0
  148. package/dist/runtime/thread-store.cjs +175 -0
  149. package/dist/runtime/transactional-git.cjs +315 -0
  150. package/dist/runtime/verification-pipeline.cjs +435 -0
  151. package/dist/sandbox/execution.d.ts +165 -0
  152. package/dist/sandbox/execution.js +497 -0
  153. package/dist/sandbox/execution.js.map +1 -0
  154. package/dist/security/file-policy.d.ts +22 -0
  155. package/dist/security/file-policy.js +53 -0
  156. package/dist/security/file-policy.js.map +1 -0
  157. package/dist/security/repository-boundary.d.ts +21 -0
  158. package/dist/security/repository-boundary.js +119 -0
  159. package/dist/security/repository-boundary.js.map +1 -0
  160. package/dist/security/secret-filter.d.ts +36 -0
  161. package/dist/security/secret-filter.js +149 -0
  162. package/dist/security/secret-filter.js.map +1 -0
  163. package/dist/swarm.d.ts +14 -0
  164. package/dist/swarm.js +624 -0
  165. package/dist/swarm.js.map +1 -0
  166. package/dist/threads/cache.d.ts +64 -0
  167. package/dist/threads/cache.js +241 -0
  168. package/dist/threads/cache.js.map +1 -0
  169. package/dist/threads/manager.d.ts +94 -0
  170. package/dist/threads/manager.js +914 -0
  171. package/dist/threads/manager.js.map +1 -0
  172. package/dist/ui/banner.d.ts +14 -0
  173. package/dist/ui/banner.js +42 -0
  174. package/dist/ui/banner.js.map +1 -0
  175. package/dist/ui/dashboard.d.ts +33 -0
  176. package/dist/ui/dashboard.js +135 -0
  177. package/dist/ui/dashboard.js.map +1 -0
  178. package/dist/ui/index.d.ts +11 -0
  179. package/dist/ui/index.js +12 -0
  180. package/dist/ui/index.js.map +1 -0
  181. package/dist/ui/log.d.ts +39 -0
  182. package/dist/ui/log.js +126 -0
  183. package/dist/ui/log.js.map +1 -0
  184. package/dist/ui/onboarding.d.ts +14 -0
  185. package/dist/ui/onboarding.js +755 -0
  186. package/dist/ui/onboarding.js.map +1 -0
  187. package/dist/ui/run-log.d.ts +50 -0
  188. package/dist/ui/run-log.js +60 -0
  189. package/dist/ui/run-log.js.map +1 -0
  190. package/dist/ui/spinner.d.ts +41 -0
  191. package/dist/ui/spinner.js +172 -0
  192. package/dist/ui/spinner.js.map +1 -0
  193. package/dist/ui/streaming-feed.d.ts +72 -0
  194. package/dist/ui/streaming-feed.js +386 -0
  195. package/dist/ui/streaming-feed.js.map +1 -0
  196. package/dist/ui/summary.d.ts +18 -0
  197. package/dist/ui/summary.js +113 -0
  198. package/dist/ui/summary.js.map +1 -0
  199. package/dist/ui/text-input.d.ts +19 -0
  200. package/dist/ui/text-input.js +266 -0
  201. package/dist/ui/text-input.js.map +1 -0
  202. package/dist/ui/theme.d.ts +63 -0
  203. package/dist/ui/theme.js +97 -0
  204. package/dist/ui/theme.js.map +1 -0
  205. package/dist/utils/command-exists.d.ts +11 -0
  206. package/dist/utils/command-exists.js +41 -0
  207. package/dist/utils/command-exists.js.map +1 -0
  208. package/dist/utils/process-security.d.ts +19 -0
  209. package/dist/utils/process-security.js +90 -0
  210. package/dist/utils/process-security.js.map +1 -0
  211. package/dist/utils/rolling-buffer.d.ts +16 -0
  212. package/dist/utils/rolling-buffer.js +30 -0
  213. package/dist/utils/rolling-buffer.js.map +1 -0
  214. package/dist/utils/safe-spawn.d.ts +13 -0
  215. package/dist/utils/safe-spawn.js +49 -0
  216. package/dist/utils/safe-spawn.js.map +1 -0
  217. package/dist/worktree/manager.d.ts +69 -0
  218. package/dist/worktree/manager.js +593 -0
  219. package/dist/worktree/manager.js.map +1 -0
  220. package/dist/worktree/merge.d.ts +58 -0
  221. package/dist/worktree/merge.js +389 -0
  222. package/dist/worktree/merge.js.map +1 -0
  223. package/package.json +63 -0
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Thread cache — caches thread results by (task, files, agent) hash.
3
+ *
4
+ * Slate's "subthread reuse" optimization: when the orchestrator spawns
5
+ * an identical thread (same task + same files + same agent), return the
6
+ * cached result instead of re-running the agent. Saves cost and time.
7
+ *
8
+ * Two modes:
9
+ * 1. Session-scoped (default): in-memory Map, cleared on exit.
10
+ * 2. Disk-persistent: reads/writes JSON files in a cache directory,
11
+ * with TTL-based expiry so stale entries don't accumulate.
12
+ *
13
+ * Cache keys are SHA-256 hashes of normalized (task, files, agent, model).
14
+ */
15
+ import type { CompressedResult } from "../core/types.js";
16
+ export interface ThreadCacheEntry {
17
+ result: CompressedResult;
18
+ cachedAt: number;
19
+ hitCount: number;
20
+ fileHashes?: Record<string, string>;
21
+ commitSha?: string;
22
+ }
23
+ /** Compute SHA-256 hash of a file on disk. Returns empty string if file does not exist. */
24
+ export declare function computeFileHash(filePath: string): string;
25
+ /** Compute file hashes for a list of context files inside repoRoot. */
26
+ export declare function computeContextHashes(repoRoot: string, files: string[]): Record<string, string>;
27
+ export interface ThreadCacheStats {
28
+ size: number;
29
+ hits: number;
30
+ misses: number;
31
+ totalSavedMs: number;
32
+ totalSavedUsd: number;
33
+ persistedEntries: number;
34
+ }
35
+ export declare class ThreadCache {
36
+ private cache;
37
+ private hits;
38
+ private misses;
39
+ private totalSavedMs;
40
+ private totalSavedUsd;
41
+ private maxEntries;
42
+ private persistDir?;
43
+ private ttlMs;
44
+ private persistedKeys;
45
+ constructor(maxEntries?: number, persistDir?: string, ttlHours?: number);
46
+ /** Initialize persistent cache — load entries from disk. */
47
+ init(): Promise<void>;
48
+ /**
49
+ * Look up a cached result for the given thread parameters.
50
+ * Returns undefined on cache miss or if context files drifted.
51
+ */
52
+ get(task: string, files: string[], agent: string, model: string, repoRoot?: string, commitSha?: string): CompressedResult | undefined;
53
+ /**
54
+ * Store a thread result in the cache.
55
+ * Only caches successful results — failed threads should be retried.
56
+ */
57
+ set(task: string, files: string[], agent: string, model: string, result: CompressedResult, repoRoot?: string, commitSha?: string): void;
58
+ /** Get cache statistics. */
59
+ getStats(): ThreadCacheStats;
60
+ /** Clear all cached entries (in-memory and on disk). */
61
+ clear(): void;
62
+ private saveDiskEntry;
63
+ private deleteDiskEntry;
64
+ }
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Thread cache — caches thread results by (task, files, agent) hash.
3
+ *
4
+ * Slate's "subthread reuse" optimization: when the orchestrator spawns
5
+ * an identical thread (same task + same files + same agent), return the
6
+ * cached result instead of re-running the agent. Saves cost and time.
7
+ *
8
+ * Two modes:
9
+ * 1. Session-scoped (default): in-memory Map, cleared on exit.
10
+ * 2. Disk-persistent: reads/writes JSON files in a cache directory,
11
+ * with TTL-based expiry so stale entries don't accumulate.
12
+ *
13
+ * Cache keys are SHA-256 hashes of normalized (task, files, agent, model).
14
+ */
15
+ import { createHash } from "node:crypto";
16
+ import * as fs from "node:fs";
17
+ import * as path from "node:path";
18
+ /** Compute SHA-256 hash of a file on disk. Returns empty string if file does not exist. */
19
+ export function computeFileHash(filePath) {
20
+ try {
21
+ if (!fs.existsSync(filePath))
22
+ return "";
23
+ const buffer = fs.readFileSync(filePath);
24
+ return createHash("sha256").update(buffer).digest("hex");
25
+ }
26
+ catch {
27
+ return "";
28
+ }
29
+ }
30
+ /** Compute file hashes for a list of context files inside repoRoot. */
31
+ export function computeContextHashes(repoRoot, files) {
32
+ const hashes = {};
33
+ for (const file of files) {
34
+ const absPath = path.isAbsolute(file) ? file : path.join(repoRoot, file);
35
+ hashes[file] = computeFileHash(absPath);
36
+ }
37
+ return hashes;
38
+ }
39
+ /**
40
+ * Compute a stable cache key from thread parameters.
41
+ * Normalizes inputs: trims task, sorts files, lowercases agent/model.
42
+ */
43
+ function computeCacheKey(task, files, agent, model, repoRoot = "", commitSha = "") {
44
+ const normalized = JSON.stringify({
45
+ task: task.trim(),
46
+ files: [...files].sort(),
47
+ agent: agent.toLowerCase(),
48
+ model: model.toLowerCase(),
49
+ repo: repoRoot.trim().toLowerCase(),
50
+ commit: commitSha.trim(),
51
+ });
52
+ return createHash("sha256").update(normalized).digest("hex");
53
+ }
54
+ export class ThreadCache {
55
+ cache = new Map();
56
+ hits = 0;
57
+ misses = 0;
58
+ totalSavedMs = 0;
59
+ totalSavedUsd = 0;
60
+ maxEntries;
61
+ persistDir;
62
+ ttlMs;
63
+ persistedKeys = new Set();
64
+ constructor(maxEntries = 100, persistDir, ttlHours = 24) {
65
+ this.maxEntries = maxEntries;
66
+ this.persistDir = persistDir;
67
+ this.ttlMs = ttlHours * 60 * 60 * 1000;
68
+ }
69
+ /** Initialize persistent cache — load entries from disk. */
70
+ async init() {
71
+ if (!this.persistDir)
72
+ return;
73
+ fs.mkdirSync(this.persistDir, { recursive: true });
74
+ let files;
75
+ try {
76
+ files = fs.readdirSync(this.persistDir).filter((f) => f.endsWith(".json"));
77
+ }
78
+ catch {
79
+ return;
80
+ }
81
+ const now = Date.now();
82
+ for (const file of files) {
83
+ try {
84
+ const filePath = path.join(this.persistDir, file);
85
+ const raw = fs.readFileSync(filePath, "utf-8");
86
+ const entry = JSON.parse(raw);
87
+ // Skip expired entries
88
+ if (now - entry.cachedAt > this.ttlMs) {
89
+ fs.unlinkSync(filePath);
90
+ continue;
91
+ }
92
+ // Only load successful results
93
+ if (!entry.result.success) {
94
+ fs.unlinkSync(filePath);
95
+ continue;
96
+ }
97
+ this.cache.set(entry.key, {
98
+ result: entry.result,
99
+ cachedAt: entry.cachedAt,
100
+ hitCount: 0,
101
+ fileHashes: entry.fileHashes,
102
+ commitSha: entry.commitSha,
103
+ });
104
+ this.persistedKeys.add(entry.key);
105
+ }
106
+ catch {
107
+ // Skip corrupt files
108
+ }
109
+ }
110
+ // Trim to max capacity
111
+ while (this.cache.size > this.maxEntries) {
112
+ const oldestKey = this.cache.keys().next().value;
113
+ if (oldestKey !== undefined) {
114
+ this.cache.delete(oldestKey);
115
+ this.deleteDiskEntry(oldestKey);
116
+ }
117
+ }
118
+ }
119
+ /**
120
+ * Look up a cached result for the given thread parameters.
121
+ * Returns undefined on cache miss or if context files drifted.
122
+ */
123
+ get(task, files, agent, model, repoRoot = "", commitSha = "") {
124
+ const key = computeCacheKey(task, files, agent, model, repoRoot, commitSha);
125
+ const entry = this.cache.get(key);
126
+ if (!entry) {
127
+ this.misses++;
128
+ return undefined;
129
+ }
130
+ // Check TTL expiry
131
+ if (Date.now() - entry.cachedAt > this.ttlMs) {
132
+ this.cache.delete(key);
133
+ this.deleteDiskEntry(key);
134
+ this.misses++;
135
+ return undefined;
136
+ }
137
+ // Only return successful cached results
138
+ if (!entry.result.success) {
139
+ this.misses++;
140
+ return undefined;
141
+ }
142
+ // Task 2.5e: Context hash verification
143
+ // If context files were recorded, verify that their contents on disk have not drifted
144
+ if (repoRoot && entry.fileHashes && Object.keys(entry.fileHashes).length > 0) {
145
+ for (const [file, recordedHash] of Object.entries(entry.fileHashes)) {
146
+ const absPath = path.isAbsolute(file) ? file : path.join(repoRoot, file);
147
+ const currentHash = computeFileHash(absPath);
148
+ if (currentHash !== recordedHash) {
149
+ // Stale cache hit with drifted context files -> invalidate and miss
150
+ this.cache.delete(key);
151
+ this.deleteDiskEntry(key);
152
+ this.misses++;
153
+ return undefined;
154
+ }
155
+ }
156
+ }
157
+ this.hits++;
158
+ entry.hitCount++;
159
+ this.totalSavedMs += entry.result.durationMs;
160
+ this.totalSavedUsd += entry.result.estimatedCostUsd;
161
+ return entry.result;
162
+ }
163
+ /**
164
+ * Store a thread result in the cache.
165
+ * Only caches successful results — failed threads should be retried.
166
+ */
167
+ set(task, files, agent, model, result, repoRoot = "", commitSha = "") {
168
+ // Don't cache failures
169
+ if (!result.success)
170
+ return;
171
+ // Evict oldest entry if at capacity
172
+ if (this.cache.size >= this.maxEntries) {
173
+ const oldestKey = this.cache.keys().next().value;
174
+ if (oldestKey !== undefined) {
175
+ this.cache.delete(oldestKey);
176
+ this.deleteDiskEntry(oldestKey);
177
+ }
178
+ }
179
+ const key = computeCacheKey(task, files, agent, model, repoRoot, commitSha);
180
+ const now = Date.now();
181
+ const fileHashes = repoRoot && files.length > 0 ? computeContextHashes(repoRoot, files) : undefined;
182
+ this.cache.set(key, {
183
+ result,
184
+ cachedAt: now,
185
+ hitCount: 0,
186
+ fileHashes,
187
+ commitSha,
188
+ });
189
+ // Persist to disk
190
+ this.saveDiskEntry(key, result, now, fileHashes, commitSha);
191
+ }
192
+ /** Get cache statistics. */
193
+ getStats() {
194
+ return {
195
+ size: this.cache.size,
196
+ hits: this.hits,
197
+ misses: this.misses,
198
+ totalSavedMs: this.totalSavedMs,
199
+ totalSavedUsd: this.totalSavedUsd,
200
+ persistedEntries: this.persistedKeys.size,
201
+ };
202
+ }
203
+ /** Clear all cached entries (in-memory and on disk). */
204
+ clear() {
205
+ if (this.persistDir) {
206
+ for (const key of this.cache.keys()) {
207
+ this.deleteDiskEntry(key);
208
+ }
209
+ }
210
+ this.cache.clear();
211
+ }
212
+ // ── Disk persistence ──────────────────────────────────────────────────
213
+ saveDiskEntry(key, result, cachedAt, fileHashes, commitSha) {
214
+ if (!this.persistDir)
215
+ return;
216
+ try {
217
+ const entry = { key, result, cachedAt, fileHashes, commitSha };
218
+ const filePath = path.join(this.persistDir, `${key}.json`);
219
+ fs.writeFileSync(filePath, JSON.stringify(entry), "utf-8");
220
+ this.persistedKeys.add(key);
221
+ }
222
+ catch {
223
+ // Non-fatal
224
+ }
225
+ }
226
+ deleteDiskEntry(key) {
227
+ if (!this.persistDir)
228
+ return;
229
+ try {
230
+ const filePath = path.join(this.persistDir, `${key}.json`);
231
+ if (fs.existsSync(filePath)) {
232
+ fs.unlinkSync(filePath);
233
+ this.persistedKeys.delete(key);
234
+ }
235
+ }
236
+ catch {
237
+ // Non-fatal
238
+ }
239
+ }
240
+ }
241
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/threads/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Thread manager — spawns and manages coding agent threads in isolated worktrees.
3
+ *
4
+ * Features:
5
+ * - AsyncSemaphore for proper concurrency gating (no polling)
6
+ * - AbortSignal propagation for thread cancellation
7
+ * - Per-thread retry logic with exponential backoff
8
+ * - Error classification: retryable (transient) vs fatal (permanent)
9
+ * - Agent/model re-routing on failure (fallback to alternative combos)
10
+ * - Budget tracking and enforcement
11
+ * - Per-thread error isolation
12
+ */
13
+ import type { BudgetState, CompressedResult, SwarmConfig, ThreadConfig, ThreadProgressPhase, ThreadState } from "../core/types.js";
14
+ import type { EpisodicMemory } from "../labs/episodic-memory.js";
15
+ import { WorktreeManager } from "../worktree/manager.js";
16
+ import { type ThreadCacheStats } from "./cache.js";
17
+ /**
18
+ * Promise-based semaphore for concurrency control.
19
+ * acquire() resolves when a slot is available, release() frees a slot.
20
+ */
21
+ export declare class AsyncSemaphore {
22
+ private current;
23
+ private readonly max;
24
+ private waiters;
25
+ constructor(max: number);
26
+ acquire(): Promise<void>;
27
+ release(): void;
28
+ get activeCount(): number;
29
+ get waitingCount(): number;
30
+ }
31
+ export type ThreadProgressCallback = (threadId: string, phase: ThreadProgressPhase, detail?: string) => void;
32
+ export type ThreadOutputCallback = (threadId: string, chunk: string) => void;
33
+ export declare class ThreadManager {
34
+ private repoRoot;
35
+ private threads;
36
+ private totalSpawned;
37
+ private semaphore;
38
+ private worktreeManager;
39
+ private config;
40
+ private budget;
41
+ private threadCache;
42
+ private episodicMemory?;
43
+ private onThreadProgress?;
44
+ private onThreadOutput?;
45
+ private sessionAbort?;
46
+ private threadAbortControllers;
47
+ constructor(repoRoot: string, config: SwarmConfig, onThreadProgress?: ThreadProgressCallback, sessionAbort?: AbortSignal);
48
+ /** Set the output streaming callback for live agent output. */
49
+ setThreadOutputCallback(cb: ThreadOutputCallback): void;
50
+ /** Set the episodic memory store for recording thread outcomes. */
51
+ setEpisodicMemory(memory: EpisodicMemory): void;
52
+ init(): Promise<void>;
53
+ private getCurrentCommitSha;
54
+ /**
55
+ * Spawn a thread — creates a worktree, runs the agent, returns compressed result.
56
+ * Checks the subthread cache first; on cache hit, returns immediately (Slate-style reuse).
57
+ * Retries up to config.thread_retries times on failure.
58
+ * Error-isolated: a failure here never throws — always returns a CompressedResult.
59
+ */
60
+ spawnThread(threadConfig: ThreadConfig): Promise<CompressedResult>;
61
+ /**
62
+ * Execute a single thread attempt. Acquires semaphore, creates worktree,
63
+ * runs agent, captures diff, compresses result.
64
+ */
65
+ private executeThread;
66
+ /** Cancel a specific running thread. */
67
+ cancelThread(threadId: string): boolean;
68
+ /** Cancel all running threads. */
69
+ cancelAll(): void;
70
+ /** Get all thread states. */
71
+ getThreads(): ThreadState[];
72
+ /** Get a specific thread's state. */
73
+ getThread(threadId: string): ThreadState | undefined;
74
+ /** Restore a completed/failed thread from persisted state (server restart recovery). */
75
+ restoreThread(state: ThreadState): void;
76
+ /** Get the worktree manager for merge operations. */
77
+ getWorktreeManager(): WorktreeManager;
78
+ /** Get current budget state. */
79
+ getBudgetState(): BudgetState;
80
+ /** Get subthread cache stats. */
81
+ getCacheStats(): ThreadCacheStats;
82
+ /** Get concurrency stats. */
83
+ getConcurrencyStats(): {
84
+ active: number;
85
+ waiting: number;
86
+ total: number;
87
+ max: number;
88
+ };
89
+ /** Cleanup all worktrees. */
90
+ cleanup(): Promise<void>;
91
+ /** Destroy a specific thread's worktree and branch. */
92
+ destroyWorktree(threadId: string): Promise<void>;
93
+ private failResult;
94
+ }