@wrongstack/core 0.310.1 → 0.313.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 (187) hide show
  1. package/dist/chronicle/index.js +84 -6391
  2. package/dist/chronicle/project-server.js +84 -4868
  3. package/dist/chunk-2BUUIMQB.js +225 -0
  4. package/dist/chunk-2FK4WTFF.js +207 -0
  5. package/dist/chunk-2FQL4PDC.js +292 -0
  6. package/dist/chunk-2KKODIQU.js +2256 -0
  7. package/dist/chunk-2YU4WVBW.js +134 -0
  8. package/dist/chunk-3E244UDA.js +356 -0
  9. package/dist/chunk-3XPVB7BA.js +135 -0
  10. package/dist/chunk-4LJJYBAU.js +388 -0
  11. package/dist/chunk-4VWO3ZXL.js +342 -0
  12. package/dist/chunk-4XV6IR37.js +187 -0
  13. package/dist/chunk-4ZHTKJHM.js +183 -0
  14. package/dist/chunk-52RTVH4M.js +361 -0
  15. package/dist/chunk-57GEWK5X.js +26 -0
  16. package/dist/chunk-5DSMEVA4.js +931 -0
  17. package/dist/chunk-5RP3QTRL.js +1223 -0
  18. package/dist/chunk-5S437PRC.js +50 -0
  19. package/dist/chunk-5XDUGTKN.js +511 -0
  20. package/dist/chunk-62V2AFVG.js +422 -0
  21. package/dist/chunk-65RDIOR2.js +2470 -0
  22. package/dist/chunk-6AKOTGLM.js +597 -0
  23. package/dist/chunk-7ZHZE5EO.js +28 -0
  24. package/dist/chunk-A455ATZB.js +140 -0
  25. package/dist/chunk-AD7ZFZCS.js +46 -0
  26. package/dist/chunk-AI4LLQ2U.js +856 -0
  27. package/dist/chunk-ASX6AW36.js +789 -0
  28. package/dist/chunk-AT4V4XGI.js +3522 -0
  29. package/dist/chunk-AWNQ6ED6.js +311 -0
  30. package/dist/chunk-B4AMLZPB.js +9 -0
  31. package/dist/chunk-BAKVJLEV.js +417 -0
  32. package/dist/chunk-BJHVULHO.js +55 -0
  33. package/dist/chunk-CB2HFKTP.js +9 -0
  34. package/dist/chunk-CE4ZOCOI.js +1640 -0
  35. package/dist/chunk-CQQL4ZOQ.js +151 -0
  36. package/dist/chunk-DEIQMBD2.js +65 -0
  37. package/dist/chunk-E6I5RXVK.js +58 -0
  38. package/dist/chunk-EFEHDQGY.js +1589 -0
  39. package/dist/chunk-EODDVJBM.js +82 -0
  40. package/dist/chunk-EPC3GZD4.js +308 -0
  41. package/dist/chunk-FAYRX2AD.js +13 -0
  42. package/dist/chunk-FFSX2QDQ.js +362 -0
  43. package/dist/chunk-FHAMQS7Q.js +5856 -0
  44. package/dist/chunk-FIV4IJL7.js +2263 -0
  45. package/dist/chunk-FNXVT7HF.js +52 -0
  46. package/dist/chunk-FSWCWSTS.js +395 -0
  47. package/dist/chunk-FW5LDDMJ.js +16464 -0
  48. package/dist/chunk-FXQLIX3K.js +211 -0
  49. package/dist/chunk-G5AAKEEZ.js +228 -0
  50. package/dist/chunk-G5S3S5ZN.js +38 -0
  51. package/dist/chunk-GAZEWQEZ.js +231 -0
  52. package/dist/chunk-GFJOMSHG.js +125 -0
  53. package/dist/chunk-GFXZTHNI.js +158 -0
  54. package/dist/chunk-GM54GJBU.js +960 -0
  55. package/dist/chunk-GPQ3NJPR.js +900 -0
  56. package/dist/chunk-GTCU6DZ3.js +1 -0
  57. package/dist/chunk-H5E77KUJ.js +15 -0
  58. package/dist/chunk-H72H4XDU.js +1023 -0
  59. package/dist/chunk-HA4ZYA6T.js +178 -0
  60. package/dist/chunk-I45KAW2N.js +5020 -0
  61. package/dist/chunk-IQ35HMQL.js +1396 -0
  62. package/dist/chunk-IYFBFX34.js +86 -0
  63. package/dist/chunk-JJPMWPB6.js +109 -0
  64. package/dist/chunk-JTHKOYWU.js +663 -0
  65. package/dist/chunk-JVGPR3TS.js +365 -0
  66. package/dist/chunk-JVKB2TBL.js +48 -0
  67. package/dist/chunk-JX7VBZDK.js +31 -0
  68. package/dist/chunk-K3G6JBUC.js +5232 -0
  69. package/dist/chunk-K4YUNKU7.js +589 -0
  70. package/dist/chunk-KBZGE6LN.js +710 -0
  71. package/dist/chunk-KDQJCT2J.js +45 -0
  72. package/dist/chunk-KI4L2DUL.js +560 -0
  73. package/dist/chunk-KJ2ZTQSL.js +47 -0
  74. package/dist/chunk-KSJ5UTP7.js +154 -0
  75. package/dist/chunk-LHOA5DWA.js +28 -0
  76. package/dist/chunk-LX5QWLBL.js +27 -0
  77. package/dist/chunk-LZDDLNCZ.js +279 -0
  78. package/dist/chunk-M2WF6AXG.js +115 -0
  79. package/dist/chunk-M7BS2K6U.js +1 -0
  80. package/dist/chunk-NDBIPFNY.js +102 -0
  81. package/dist/chunk-NEMIWEXC.js +1199 -0
  82. package/dist/chunk-NFAYHPRH.js +323 -0
  83. package/dist/chunk-O37BQ65S.js +21 -0
  84. package/dist/chunk-O74MJHXU.js +187 -0
  85. package/dist/chunk-OJZEX73F.js +112 -0
  86. package/dist/chunk-PAAWLLZX.js +74 -0
  87. package/dist/chunk-PFXFQQRT.js +339 -0
  88. package/dist/chunk-PFZO3C6H.js +14 -0
  89. package/dist/chunk-PHH7LD45.js +113 -0
  90. package/dist/chunk-QRTG4XTI.js +188 -0
  91. package/dist/chunk-QVOVMXJR.js +1351 -0
  92. package/dist/chunk-RCQYT2I4.js +757 -0
  93. package/dist/chunk-RD6DEE36.js +1178 -0
  94. package/dist/chunk-RJIZEJ42.js +30 -0
  95. package/dist/chunk-RPOA7IAE.js +150 -0
  96. package/dist/chunk-SN2DQRL6.js +1172 -0
  97. package/dist/chunk-SQJVFEBV.js +41 -0
  98. package/dist/chunk-SQP64RCS.js +5391 -0
  99. package/dist/chunk-SVT4RNTR.js +49 -0
  100. package/dist/chunk-SWUFN7XM.js +43 -0
  101. package/dist/chunk-SXMVYC5H.js +174 -0
  102. package/dist/chunk-SZ6AYUIZ.js +23 -0
  103. package/dist/chunk-T3KGZBU5.js +3171 -0
  104. package/dist/chunk-TBRL2RCC.js +9 -0
  105. package/dist/chunk-TRT2HRWZ.js +18 -0
  106. package/dist/chunk-TYW2GI5I.js +1077 -0
  107. package/dist/chunk-UIWONWVG.js +4959 -0
  108. package/dist/chunk-VB3H5WWB.js +151 -0
  109. package/dist/chunk-W4A6QA3D.js +258 -0
  110. package/dist/chunk-WBPVKEEE.js +227 -0
  111. package/dist/chunk-WEBMSQP2.js +123 -0
  112. package/dist/chunk-WOEZV3Q4.js +333 -0
  113. package/dist/chunk-WQQON3SV.js +473 -0
  114. package/dist/chunk-WQTOVMGV.js +119 -0
  115. package/dist/chunk-WZO23AWO.js +2779 -0
  116. package/dist/chunk-XBDP4QPW.js +7 -0
  117. package/dist/chunk-XEBHSJLT.js +103 -0
  118. package/dist/chunk-XQ2ECNK3.js +1 -0
  119. package/dist/chunk-XQ2XGIBF.js +2086 -0
  120. package/dist/chunk-Y2GE5TSI.js +355 -0
  121. package/dist/chunk-YBUJCZ57.js +351 -0
  122. package/dist/chunk-YNCVSFGF.js +149 -0
  123. package/dist/chunk-YUPMXOGP.js +282 -0
  124. package/dist/chunk-YY6APYUM.js +49 -0
  125. package/dist/chunk-Z2IJUO45.js +691 -0
  126. package/dist/chunk-ZEHUCJHY.js +129 -0
  127. package/dist/chunk-ZYU2YOJ6.js +1496 -0
  128. package/dist/chunk-ZZ4V3WIZ.js +163 -0
  129. package/dist/coordination/agents/index.js +145 -6260
  130. package/dist/coordination/index.js +365 -34323
  131. package/dist/coordination/mailbox-project-server.js +48 -3220
  132. package/dist/coordination/mailbox-types.d.ts +13 -1
  133. package/dist/coordination/provider-status-tracker.d.ts +4 -0
  134. package/dist/coordination/sqlite-mailbox.d.ts +2 -1
  135. package/dist/core/agent.d.ts +14 -1
  136. package/dist/core/index.js +267 -12479
  137. package/dist/core/model-ref.js +5 -23
  138. package/dist/core/provider-runner.d.ts +36 -1
  139. package/dist/design/index.js +50 -1087
  140. package/dist/execution/index.d.ts +1 -1
  141. package/dist/execution/index.js +1599 -21887
  142. package/dist/execution/prompt-enhancer.js +16 -535
  143. package/dist/execution/retry-policy.d.ts +31 -1
  144. package/dist/extension/index.js +7 -311
  145. package/dist/goal/index.js +33 -3435
  146. package/dist/hooks/index.js +14 -1138
  147. package/dist/hq/index.js +112 -5139
  148. package/dist/hq/protocol.js +21 -668
  149. package/dist/index.js +1410 -92655
  150. package/dist/infrastructure/index.js +64 -4811
  151. package/dist/kernel/events/provider-events.d.ts +15 -0
  152. package/dist/kernel/events/wrongtrace-events.d.ts +26 -0
  153. package/dist/kernel/events.d.ts +16 -7
  154. package/dist/kernel/index.js +14 -992
  155. package/dist/models/index.js +40 -1117
  156. package/dist/notifications/index.js +3 -108
  157. package/dist/observability/index.js +17 -279
  158. package/dist/plugin/index.js +584 -14835
  159. package/dist/prompts/index.js +14 -948
  160. package/dist/registry/index.js +9 -822
  161. package/dist/registry-R23UBE7H.js +14 -0
  162. package/dist/replay/index.js +6 -84
  163. package/dist/review-finding-store-DKAV3AS6.js +21 -0
  164. package/dist/review-report-store-OWY3EC3N.js +19 -0
  165. package/dist/security/index.js +114 -2202
  166. package/dist/session-catalog/index.js +36 -2103
  167. package/dist/session-catalog/project-server.js +42 -1435
  168. package/dist/skills/index.js +35 -1578
  169. package/dist/storage/index.js +182 -15600
  170. package/dist/tasking/index.js +10 -676
  171. package/dist/tools/index.js +52 -9589
  172. package/dist/types/config/autonomy.d.ts +5 -0
  173. package/dist/types/config/tools.d.ts +2 -2
  174. package/dist/types/config/ui.d.ts +1 -1
  175. package/dist/types/errors.d.ts +18 -0
  176. package/dist/types/index.js +121 -1341
  177. package/dist/types/provider-runner.d.ts +8 -0
  178. package/dist/types/session-markers.js +8 -144
  179. package/dist/utils/error.js +3 -4
  180. package/dist/utils/expect-defined.js +3 -9
  181. package/dist/utils/heap-watchdog.js +6 -706
  182. package/dist/utils/index.js +269 -5685
  183. package/dist/utils/sage-output-block.js +5 -42
  184. package/dist/wiring/proxy-rewrite.d.ts +92 -4
  185. package/dist/wiring/proxy-rewrite.js +20 -61
  186. package/dist/worktree/index.js +6 -887
  187. package/package.json +4 -4
@@ -0,0 +1,355 @@
1
+ import {
2
+ ERROR_CODES,
3
+ WrongStackError
4
+ } from "./chunk-WOEZV3Q4.js";
5
+ import {
6
+ toErrorMessage
7
+ } from "./chunk-TBRL2RCC.js";
8
+
9
+ // src/kernel/run-controller.ts
10
+ var RunController = class {
11
+ ctrl = new AbortController();
12
+ hooks = [];
13
+ disposed = false;
14
+ hooksDrained = false;
15
+ errorSink;
16
+ /**
17
+ * Shared promise for the in-flight `runHooks()` call. Set by the abort
18
+ * signal handler or by `dispose()`, whichever fires first. Both callers
19
+ * await the same promise so abort-triggered async cleanup (session flush,
20
+ * socket close) completes before `dispose()` resolves.
21
+ */
22
+ _hooksPromise = null;
23
+ constructor(opts = {}) {
24
+ this.errorSink = opts.errorSink ?? ((err, where) => {
25
+ console.warn(JSON.stringify({
26
+ level: "warn",
27
+ event: "run.cleanup_hook_failed",
28
+ where,
29
+ message: toErrorMessage(err),
30
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
31
+ }));
32
+ });
33
+ if (opts.parentSignal) {
34
+ const parent = opts.parentSignal;
35
+ if (parent.aborted) {
36
+ this.ctrl.abort(parent.reason);
37
+ queueMicrotask(() => {
38
+ void this.runHooks();
39
+ });
40
+ } else {
41
+ const onParentAbort = () => this.ctrl.abort(parent.reason);
42
+ parent.addEventListener("abort", onParentAbort, { once: true });
43
+ this.onAbort(() => parent.removeEventListener("abort", onParentAbort));
44
+ }
45
+ }
46
+ this.ctrl.signal.addEventListener(
47
+ "abort",
48
+ () => {
49
+ this.ensureHooksRun();
50
+ },
51
+ { once: true }
52
+ );
53
+ }
54
+ get signal() {
55
+ return this.ctrl.signal;
56
+ }
57
+ get aborted() {
58
+ return this.ctrl.signal.aborted;
59
+ }
60
+ abort(reason) {
61
+ if (this.ctrl.signal.aborted) return;
62
+ this.ctrl.abort(reason);
63
+ }
64
+ /**
65
+ * Register a teardown hook. Returns an unsubscribe function so callers
66
+ * can opt out before the hook fires (e.g. when a tool finishes cleanly
67
+ * before abort happens).
68
+ *
69
+ * If the controller has already drained its hooks (a prior abort() or
70
+ * dispose() ran), the new hook is fired immediately on a best-effort
71
+ * basis — otherwise a hook registered after teardown would be silently
72
+ * dropped and the resource it cleans up would leak. The returned
73
+ * unsubscribe is a no-op in that case (the hook has already run). Errors
74
+ * from the immediate run are routed through `errorSink` like any other
75
+ * cleanup failure.
76
+ */
77
+ onAbort(fn) {
78
+ if (this.hooksDrained) {
79
+ void (async () => {
80
+ try {
81
+ await fn();
82
+ } catch (err) {
83
+ this.errorSink(err, "RunController.onAbort(post-drain)");
84
+ }
85
+ })();
86
+ return () => {
87
+ };
88
+ }
89
+ this.hooks.push(fn);
90
+ return () => {
91
+ const idx = this.hooks.indexOf(fn);
92
+ if (idx !== -1) this.hooks.splice(idx, 1);
93
+ };
94
+ }
95
+ /**
96
+ * Fire cleanup hooks and tear down listeners — called when the run
97
+ * ends *normally* so cleanup happens regardless of outcome. Subsequent
98
+ * aborts become no-ops.
99
+ */
100
+ async dispose() {
101
+ if (this.disposed) return;
102
+ this.disposed = true;
103
+ await this.ensureHooksRun();
104
+ }
105
+ /**
106
+ * Start running hooks (or wait for an already-in-flight run) and return
107
+ * a promise that settles when all hooks complete. Both the abort signal
108
+ * handler and `dispose()` call this so they share the same underlying
109
+ * promise — abort-triggered async cleanup (session flush, socket close)
110
+ * is guaranteed to finish before `dispose()` resolves.
111
+ */
112
+ ensureHooksRun() {
113
+ if (!this._hooksPromise) {
114
+ this._hooksPromise = this.runHooks();
115
+ }
116
+ return this._hooksPromise;
117
+ }
118
+ async runHooks() {
119
+ if (this.hooksDrained) return;
120
+ this.hooksDrained = true;
121
+ const snapshot = this.hooks.splice(0, this.hooks.length).reverse();
122
+ if (snapshot.length === 0) return;
123
+ await Promise.allSettled(
124
+ snapshot.map(
125
+ (hook) => Promise.resolve().then(() => hook()).catch((err) => {
126
+ this.errorSink(err, "RunController.dispose");
127
+ })
128
+ )
129
+ );
130
+ }
131
+ };
132
+
133
+ // src/kernel/tokens.ts
134
+ var t = (name) => /* @__PURE__ */ Symbol.for(`@wrongstack/core/kernel#${name}`);
135
+ var TOKENS = {
136
+ Logger: t("Logger"),
137
+ TokenCounter: t("TokenCounter"),
138
+ SessionStore: t("SessionStore"),
139
+ /**
140
+ * @deprecated Token name retained for compatibility.
141
+ * The resolved value IS a `MemoryPort`, NOT the legacy `MemoryStore` interface.
142
+ * Callers that cast or destructure as `MemoryStore` will lose `getCapability`,
143
+ * `health`, `dispose`, and the narrowed `withTraceId(): MemoryPort` return type.
144
+ * Port implementations are opaque — query optional features via `getCapability`.
145
+ * Use `TOKENS.MemoryPort` instead.
146
+ */
147
+ MemoryStore: t("MemoryStore"),
148
+ /** Production memory backend. Use instead of the deprecated `MemoryStore` token. */
149
+ MemoryPort: t("MemoryPort"),
150
+ PermissionPolicy: t("PermissionPolicy"),
151
+ Compactor: t("Compactor"),
152
+ PathResolver: t("PathResolver"),
153
+ ConfigLoader: t("ConfigLoader"),
154
+ ConfigStore: t("ConfigStore"),
155
+ FallbackProfileManager: t("FallbackProfileManager"),
156
+ /** Shared (provider, model) health tracker; fallback extensions read/write it. */
157
+ ProviderModelStatusTracker: t("ProviderModelStatusTracker"),
158
+ Renderer: t("Renderer"),
159
+ InputReader: t("InputReader"),
160
+ ErrorHandler: t("ErrorHandler"),
161
+ RetryPolicy: t("RetryPolicy"),
162
+ SkillLoader: t("SkillLoader"),
163
+ PromptLoader: t("PromptLoader"),
164
+ SystemPromptBuilder: t("SystemPromptBuilder"),
165
+ SecretScrubber: t("SecretScrubber"),
166
+ ModelsRegistry: t("ModelsRegistry"),
167
+ ModeStore: t("ModeStore"),
168
+ /** Replaces the entire provider call layer — retry, streaming, tracing. */
169
+ ProviderRunner: t("ProviderRunner"),
170
+ /** Optional git-worktree lifecycle manager (per-phase isolation in Goal). */
171
+ WorktreeManager: t("WorktreeManager"),
172
+ /** Optional global Brain arbiter for policy/decision escalation. */
173
+ BrainArbiter: t("BrainArbiter"),
174
+ /** Lifecycle hook registry (shell + in-process hooks). */
175
+ HookRegistry: t("HookRegistry")
176
+ };
177
+
178
+ // src/kernel/pipeline.ts
179
+ var Pipeline = class {
180
+ chain = [];
181
+ errorHandler;
182
+ /**
183
+ * Optional operational logger. When set, the `swallow` error-boundary path
184
+ * emits a structured warning so a swallowed middleware crash is never
185
+ * completely silent (P2 #7, before-release.md). Without a logger, the
186
+ * swallow path still works but the crash is invisible — host code should
187
+ * always wire one.
188
+ */
189
+ logger;
190
+ /**
191
+ * Install an error boundary. When a middleware throws or rejects, the
192
+ * handler is called and decides whether to swallow (continue with the
193
+ * pre-handler value) or rethrow. Without a handler, errors propagate.
194
+ *
195
+ * Wire one per pipeline at boot — the host CLI typically installs a
196
+ * single boundary that logs to the operational log and emits a
197
+ * `pipeline.error` event for /diag.
198
+ */
199
+ setErrorHandler(handler) {
200
+ this.errorHandler = handler;
201
+ return this;
202
+ }
203
+ /**
204
+ * Set the operational logger used by the swallow error-boundary path.
205
+ * Without this, a swallowed middleware crash is completely silent.
206
+ */
207
+ setLogger(logger) {
208
+ this.logger = logger;
209
+ return this;
210
+ }
211
+ use(mw) {
212
+ this.ensureUnique(mw.name);
213
+ this.chain.push(mw);
214
+ return this;
215
+ }
216
+ prepend(mw) {
217
+ this.ensureUnique(mw.name);
218
+ this.chain.unshift(mw);
219
+ return this;
220
+ }
221
+ /**
222
+ * Insert middleware at an explicit index. Out-of-range indices are clamped.
223
+ * Use this when insertBefore/insertAfter are insufficient (e.g. to place
224
+ * a middleware at a known position regardless of named targets).
225
+ */
226
+ insertAt(index, mw) {
227
+ this.ensureUnique(mw.name);
228
+ const idx = Math.max(0, Math.min(index, this.chain.length));
229
+ this.chain.splice(idx, 0, mw);
230
+ return this;
231
+ }
232
+ /**
233
+ * Insert mw immediately before the first occurrence of target.
234
+ * If called multiple times with the same target, each call inserts
235
+ * before the target's current position — so after insertBefore('B', X)
236
+ * then insertBefore('B', Y), the order is Y → X → B.
237
+ */
238
+ insertBefore(target, mw, opts) {
239
+ this.ensureUnique(mw.name);
240
+ const idx = this.indexOf(target, opts?.optional);
241
+ if (idx === -1) return this;
242
+ this.chain.splice(idx, 0, mw);
243
+ return this;
244
+ }
245
+ /**
246
+ * Insert mw immediately after the first occurrence of target.
247
+ * If called multiple times with the same target, each call inserts
248
+ * after the target's current position — so after insertAfter('B', X)
249
+ * then insertAfter('B', Y), the order is B → X → Y.
250
+ */
251
+ insertAfter(target, mw, opts) {
252
+ this.ensureUnique(mw.name);
253
+ const idx = this.indexOf(target, opts?.optional);
254
+ if (idx === -1) return this;
255
+ this.chain.splice(idx + 1, 0, mw);
256
+ return this;
257
+ }
258
+ replace(target, mw, opts) {
259
+ if (mw.name !== target) this.ensureUnique(mw.name);
260
+ const idx = this.indexOf(target, opts?.optional);
261
+ if (idx === -1) return this;
262
+ this.chain[idx] = mw;
263
+ return this;
264
+ }
265
+ remove(name, opts) {
266
+ const idx = this.indexOf(name, opts?.optional);
267
+ if (idx === -1) return this;
268
+ this.chain.splice(idx, 1);
269
+ return this;
270
+ }
271
+ list() {
272
+ return this.chain.map((m) => m.name);
273
+ }
274
+ size() {
275
+ return this.chain.length;
276
+ }
277
+ /** Return a read-only view suitable for passing to plugins. */
278
+ asReadonly() {
279
+ const self = this;
280
+ const view = Object.freeze({
281
+ get size() {
282
+ return self.chain.length;
283
+ },
284
+ list() {
285
+ return Object.freeze(self.chain.map((m) => m.name));
286
+ },
287
+ run(input) {
288
+ return self.run(input);
289
+ }
290
+ });
291
+ return view;
292
+ }
293
+ async run(input) {
294
+ let index = -1;
295
+ const chain = this.chain;
296
+ const errorHandler = this.errorHandler;
297
+ const dispatch = async (i, value) => {
298
+ if (i <= index) {
299
+ const offender = chain[index]?.name;
300
+ throw new WrongStackError({
301
+ message: `Pipeline: next() called multiple times in "${offender}"`,
302
+ code: ERROR_CODES.VALIDATION_ERROR,
303
+ subsystem: "container",
304
+ context: { middleware: offender }
305
+ });
306
+ }
307
+ index = i;
308
+ const mw = chain[i];
309
+ if (!mw) return value;
310
+ try {
311
+ return await mw.handler(value, (v) => dispatch(i + 1, v));
312
+ } catch (err) {
313
+ if (!errorHandler) throw err;
314
+ const policy = await errorHandler({ middleware: mw.name, owner: mw.owner, err });
315
+ if (policy === "rethrow") throw err;
316
+ this.logger?.warn("pipeline.error", {
317
+ middleware: mw.name,
318
+ owner: mw.owner,
319
+ error: err instanceof Error ? err.message : String(err)
320
+ });
321
+ return value;
322
+ }
323
+ };
324
+ return dispatch(0, input);
325
+ }
326
+ indexOf(name, optional = false) {
327
+ const idx = this.chain.findIndex((m) => m.name === name);
328
+ if (idx === -1 && !optional) {
329
+ throw new WrongStackError({
330
+ message: `Pipeline: middleware "${name}" not found`,
331
+ code: ERROR_CODES.REGISTRY_NOT_FOUND,
332
+ subsystem: "container",
333
+ context: { middleware: name }
334
+ });
335
+ }
336
+ return idx;
337
+ }
338
+ ensureUnique(name) {
339
+ if (this.chain.some((m) => m.name === name)) {
340
+ throw new WrongStackError({
341
+ message: `Pipeline: middleware "${name}" already registered`,
342
+ code: ERROR_CODES.REGISTRY_DUPLICATE,
343
+ subsystem: "container",
344
+ context: { middleware: name }
345
+ });
346
+ }
347
+ }
348
+ };
349
+
350
+ export {
351
+ RunController,
352
+ TOKENS,
353
+ Pipeline
354
+ };
355
+ //# sourceMappingURL=chunk-Y2GE5TSI.js.map
@@ -0,0 +1,351 @@
1
+ import {
2
+ SessionCatalogProjectClient
3
+ } from "./chunk-52RTVH4M.js";
4
+ import {
5
+ sessionCatalogProjectServerMetadataPath
6
+ } from "./chunk-E6I5RXVK.js";
7
+
8
+ // src/session-catalog/registry.ts
9
+ import { randomUUID } from "node:crypto";
10
+ import * as fs from "node:fs/promises";
11
+ import * as path from "node:path";
12
+ var HEARTBEAT_INTERVAL_MS = 5e3;
13
+ var AGENT_WRITE_THROTTLE_MS = 300;
14
+ var MAX_PROJECT_CLIENTS = 128;
15
+ var MAX_GLOBAL_ROOTS = 16;
16
+ var ProjectSessionRegistry = class {
17
+ constructor(globalRoot) {
18
+ this.globalRoot = globalRoot;
19
+ }
20
+ globalRoot;
21
+ instanceId = randomUUID();
22
+ clients = /* @__PURE__ */ new Map();
23
+ current;
24
+ heartbeatTimer;
25
+ agentRevision = 0;
26
+ pendingAgents;
27
+ agentTimer;
28
+ lastAgentWriteAt = 0;
29
+ bindingKey(projectDir) {
30
+ const resolved = path.resolve(projectDir);
31
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
32
+ }
33
+ async closeBinding(binding) {
34
+ if (this.current?.binding === binding) return;
35
+ const key = this.bindingKey(binding.projectDir);
36
+ if (this.clients.get(key) === binding) this.clients.delete(key);
37
+ await binding.client.close();
38
+ }
39
+ binding(projectSlug, projectRoot) {
40
+ const projectDir = path.join(this.globalRoot, "projects", projectSlug);
41
+ const key = this.bindingKey(projectDir);
42
+ let binding = this.clients.get(key);
43
+ if (!binding) {
44
+ if (this.clients.size >= MAX_PROJECT_CLIENTS) {
45
+ const oldest = [...this.clients.entries()].find(
46
+ ([, candidate]) => candidate !== this.current?.binding
47
+ );
48
+ if (oldest) {
49
+ this.clients.delete(oldest[0]);
50
+ void oldest[1].client.close();
51
+ }
52
+ }
53
+ binding = {
54
+ projectDir,
55
+ projectRoot: path.resolve(projectRoot),
56
+ client: new SessionCatalogProjectClient({ projectDir, projectRoot })
57
+ };
58
+ this.clients.set(key, binding);
59
+ } else {
60
+ this.clients.delete(key);
61
+ this.clients.set(key, binding);
62
+ }
63
+ return binding;
64
+ }
65
+ fullEntry(entry) {
66
+ const agents = entry.agents ?? [];
67
+ return {
68
+ ...entry,
69
+ status: agents.some((agent) => agent.status !== "idle") ? "active" : "idle",
70
+ lastHeartbeatAt: (/* @__PURE__ */ new Date()).toISOString(),
71
+ agentCount: agents.length,
72
+ agents
73
+ };
74
+ }
75
+ async register(entry) {
76
+ const full = this.fullEntry(entry);
77
+ if (this.current?.entry.sessionId === full.sessionId && this.current.entry.pid === full.pid) {
78
+ this.current.entry = full;
79
+ this.current.credential = await this.current.binding.client.call("heartbeat", {
80
+ credential: this.current.credential,
81
+ status: full.status
82
+ });
83
+ return;
84
+ }
85
+ const nextBinding = this.binding(full.projectSlug, full.projectRoot);
86
+ let nextCredential;
87
+ try {
88
+ nextCredential = await nextBinding.client.call("claim_new", {
89
+ entry: full,
90
+ ownerInstanceId: this.instanceId
91
+ });
92
+ } catch (error) {
93
+ await this.closeBinding(nextBinding).catch(() => void 0);
94
+ throw error;
95
+ }
96
+ const previous = this.current;
97
+ this.current = { binding: nextBinding, credential: nextCredential, entry: full };
98
+ this.agentRevision = 0;
99
+ this.cancelAgentTimer();
100
+ this.startHeartbeat();
101
+ if (previous) {
102
+ await previous.binding.client.call("release", { credential: previous.credential }).catch(() => void 0);
103
+ if (previous.binding !== nextBinding) await this.closeBinding(previous.binding);
104
+ }
105
+ }
106
+ /** Reserve before transcript hydration; activation swaps ownership only after the writer opened. */
107
+ async reserveResume(target) {
108
+ const binding = this.binding(target.projectSlug, target.projectRoot);
109
+ let reservation;
110
+ try {
111
+ reservation = await binding.client.call("reserve_resume", {
112
+ targetSessionId: target.sessionId,
113
+ requesterInstanceId: this.instanceId,
114
+ ...this.current ? { currentSessionId: this.current.entry.sessionId } : {}
115
+ });
116
+ } catch (error) {
117
+ await this.closeBinding(binding).catch(() => void 0);
118
+ throw error;
119
+ }
120
+ let settled = false;
121
+ return {
122
+ reservation,
123
+ activate: async (registration) => {
124
+ if (settled) throw new Error("Resume reservation is already settled");
125
+ const entry = this.fullEntry(registration);
126
+ const credential = await binding.client.call("activate_reservation", {
127
+ reservation,
128
+ entry
129
+ });
130
+ const previous = this.current;
131
+ this.current = { binding, credential, entry };
132
+ settled = true;
133
+ this.agentRevision = 0;
134
+ this.cancelAgentTimer();
135
+ this.startHeartbeat();
136
+ if (previous) {
137
+ await previous.binding.client.call("release", { credential: previous.credential }).catch(() => void 0);
138
+ if (previous.binding !== binding) await this.closeBinding(previous.binding);
139
+ }
140
+ },
141
+ cancel: async () => {
142
+ if (settled) return;
143
+ settled = true;
144
+ try {
145
+ await binding.client.call("cancel_reservation", {
146
+ reservationId: reservation.reservationId,
147
+ requesterInstanceId: this.instanceId
148
+ }).catch(() => void 0);
149
+ } finally {
150
+ await this.closeBinding(binding).catch(() => void 0);
151
+ }
152
+ }
153
+ };
154
+ }
155
+ async updateAgents(agents) {
156
+ if (!this.current) return;
157
+ this.pendingAgents = agents;
158
+ this.current.entry = {
159
+ ...this.current.entry,
160
+ agents,
161
+ agentCount: agents.length,
162
+ status: agents.some((agent) => agent.status !== "idle") ? "active" : "idle",
163
+ lastHeartbeatAt: (/* @__PURE__ */ new Date()).toISOString()
164
+ };
165
+ const elapsed = Date.now() - this.lastAgentWriteAt;
166
+ if (!this.agentTimer && elapsed >= AGENT_WRITE_THROTTLE_MS) {
167
+ await this.flushAgents();
168
+ return;
169
+ }
170
+ if (!this.agentTimer) {
171
+ this.agentTimer = setTimeout(
172
+ () => {
173
+ this.agentTimer = void 0;
174
+ void this.flushAgents();
175
+ },
176
+ Math.max(0, AGENT_WRITE_THROTTLE_MS - elapsed)
177
+ );
178
+ this.agentTimer.unref?.();
179
+ }
180
+ }
181
+ async flushAgents() {
182
+ const agents = this.pendingAgents;
183
+ const current = this.current;
184
+ if (!agents || !current) return;
185
+ this.pendingAgents = void 0;
186
+ this.lastAgentWriteAt = Date.now();
187
+ const revision = ++this.agentRevision;
188
+ await current.binding.client.call("publish_agents", {
189
+ credential: current.credential,
190
+ revision,
191
+ agents
192
+ });
193
+ }
194
+ async markClosing() {
195
+ this.stopHeartbeat();
196
+ this.cancelAgentTimer();
197
+ if (this.current)
198
+ await this.current.binding.client.call("mark_closing", {
199
+ credential: this.current.credential
200
+ });
201
+ }
202
+ async unregister() {
203
+ this.stopHeartbeat();
204
+ this.cancelAgentTimer();
205
+ const current = this.current;
206
+ this.current = void 0;
207
+ if (current) {
208
+ try {
209
+ await current.binding.client.call("release", { credential: current.credential });
210
+ } finally {
211
+ await this.closeBinding(current.binding);
212
+ }
213
+ }
214
+ }
215
+ async list() {
216
+ const projectsDir = path.join(this.globalRoot, "projects");
217
+ let directories = [];
218
+ try {
219
+ directories = (await fs.readdir(projectsDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).slice(0, 1e3).map((entry) => entry.name);
220
+ } catch {
221
+ return [];
222
+ }
223
+ const snapshots = await Promise.all(
224
+ directories.map(async (slug) => {
225
+ const projectDir = path.join(projectsDir, slug);
226
+ let client;
227
+ try {
228
+ const metadata = JSON.parse(
229
+ await fs.readFile(sessionCatalogProjectServerMetadataPath(projectDir), "utf8")
230
+ );
231
+ if (typeof metadata.projectRoot !== "string" || !metadata.projectRoot) return [];
232
+ client = new SessionCatalogProjectClient({
233
+ projectDir,
234
+ projectRoot: metadata.projectRoot
235
+ });
236
+ return await client.callExisting("list_live", {});
237
+ } catch {
238
+ return [];
239
+ } finally {
240
+ await client?.close().catch(() => void 0);
241
+ }
242
+ })
243
+ );
244
+ return snapshots.flat();
245
+ }
246
+ async listByProject(projectSlug) {
247
+ const current = this.current;
248
+ if (current && path.basename(current.binding.projectDir) === projectSlug) {
249
+ return current.binding.client.call("list_live", {}).catch(() => []);
250
+ }
251
+ const projectDir = path.join(this.globalRoot, "projects", projectSlug);
252
+ let client;
253
+ try {
254
+ const metadata = JSON.parse(
255
+ await fs.readFile(sessionCatalogProjectServerMetadataPath(projectDir), "utf8")
256
+ );
257
+ if (typeof metadata.projectRoot !== "string") return [];
258
+ client = new SessionCatalogProjectClient({ projectDir, projectRoot: metadata.projectRoot });
259
+ return await client.callExisting("list_live", {}).catch(() => []);
260
+ } catch {
261
+ return [];
262
+ } finally {
263
+ await client?.close().catch(() => void 0);
264
+ }
265
+ }
266
+ async get(sessionId) {
267
+ return (await this.list()).find((entry) => entry.sessionId === sessionId);
268
+ }
269
+ subscribeProject(projectSlug, projectRoot, listener) {
270
+ const binding = this.binding(projectSlug, projectRoot);
271
+ return binding.client.subscribe(listener).then((unsubscribe) => async () => {
272
+ await unsubscribe();
273
+ await this.closeBinding(binding);
274
+ });
275
+ }
276
+ get registryPath() {
277
+ return path.join(this.globalRoot, "projects");
278
+ }
279
+ async dispose() {
280
+ await this.unregister().catch(() => void 0);
281
+ await Promise.all([...this.clients.values()].map((binding) => binding.client.close()));
282
+ this.clients.clear();
283
+ }
284
+ /** Whether this facade currently owns a live session lease. */
285
+ ownsSession() {
286
+ return this.current !== void 0;
287
+ }
288
+ startHeartbeat() {
289
+ this.stopHeartbeat();
290
+ this.heartbeatTimer = setInterval(() => {
291
+ void this.heartbeat();
292
+ }, HEARTBEAT_INTERVAL_MS);
293
+ this.heartbeatTimer.unref?.();
294
+ }
295
+ stopHeartbeat() {
296
+ if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
297
+ this.heartbeatTimer = void 0;
298
+ }
299
+ cancelAgentTimer() {
300
+ if (this.agentTimer) clearTimeout(this.agentTimer);
301
+ this.agentTimer = void 0;
302
+ this.pendingAgents = void 0;
303
+ }
304
+ async heartbeat() {
305
+ const current = this.current;
306
+ if (!current) return;
307
+ try {
308
+ const credential = await current.binding.client.call("heartbeat", {
309
+ credential: current.credential,
310
+ status: current.entry.status
311
+ });
312
+ if (this.current === current) current.credential = credential;
313
+ } catch {
314
+ }
315
+ }
316
+ };
317
+ var registries = /* @__PURE__ */ new Map();
318
+ var lastRegistryKey;
319
+ function getProjectSessionRegistry(globalRoot) {
320
+ const key = globalRoot !== void 0 ? path.resolve(globalRoot) : lastRegistryKey;
321
+ if (!key)
322
+ throw new Error("SessionRegistry not initialized. Call getSessionRegistry(globalRoot) first.");
323
+ let registry = registries.get(key);
324
+ if (!registry) {
325
+ if (registries.size >= MAX_GLOBAL_ROOTS) {
326
+ const idle = [...registries.entries()].find(([, candidate]) => !candidate.ownsSession());
327
+ if (!idle)
328
+ throw new Error(`Session Registry global-root limit reached (${MAX_GLOBAL_ROOTS})`);
329
+ registries.delete(idle[0]);
330
+ void idle[1].dispose();
331
+ }
332
+ registry = new ProjectSessionRegistry(key);
333
+ registries.set(key, registry);
334
+ } else {
335
+ registries.delete(key);
336
+ registries.set(key, registry);
337
+ }
338
+ lastRegistryKey = key;
339
+ return registry;
340
+ }
341
+ function hasProjectSessionRegistry(globalRoot) {
342
+ if (globalRoot === void 0) return registries.size > 0;
343
+ return registries.has(path.resolve(globalRoot));
344
+ }
345
+
346
+ export {
347
+ ProjectSessionRegistry,
348
+ getProjectSessionRegistry,
349
+ hasProjectSessionRegistry
350
+ };
351
+ //# sourceMappingURL=chunk-YBUJCZ57.js.map