@wrongstack/core 0.1.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.
@@ -0,0 +1,71 @@
1
+ import { ay as Token, j as Logger, a1 as TokenCounter, V as SessionStore, m as MemoryStore, v as PermissionPolicy, c as Compactor, P as PathResolver, e as ConfigLoader, R as Renderer, h as InputReader, E as ErrorHandler, O as RetryPolicy, Y as SkillLoader, S as SecretScrubber, s as ModelsRegistry } from '../secret-scrubber-Dax_Ou_o.js';
2
+ export { al as BindOptions, ah as Container, an as Decorator, ai as EventBus, ao as EventLogger, ap as EventMap, aj as EventName, aq as Factory, ak as Listener, ar as Middleware, as as MiddlewareHandler, at as NextFn, au as Pipeline, av as PipelineOptions } from '../secret-scrubber-Dax_Ou_o.js';
3
+ import { S as SystemPromptBuilder } from '../system-prompt-BG3nks8P.js';
4
+
5
+ declare const TOKENS: {
6
+ readonly Logger: Token<Logger>;
7
+ readonly TokenCounter: Token<TokenCounter>;
8
+ readonly SessionStore: Token<SessionStore>;
9
+ readonly MemoryStore: Token<MemoryStore>;
10
+ readonly PermissionPolicy: Token<PermissionPolicy>;
11
+ readonly Compactor: Token<Compactor>;
12
+ readonly PathResolver: Token<PathResolver>;
13
+ readonly ConfigLoader: Token<ConfigLoader>;
14
+ readonly Renderer: Token<Renderer>;
15
+ readonly InputReader: Token<InputReader>;
16
+ readonly ErrorHandler: Token<ErrorHandler>;
17
+ readonly RetryPolicy: Token<RetryPolicy>;
18
+ readonly SkillLoader: Token<SkillLoader>;
19
+ readonly SystemPromptBuilder: Token<SystemPromptBuilder>;
20
+ readonly SecretScrubber: Token<SecretScrubber>;
21
+ readonly ModelsRegistry: Token<ModelsRegistry>;
22
+ };
23
+
24
+ /**
25
+ * RunController centralises abort + cleanup for a single agent run. It
26
+ * wraps a single AbortController and exposes a registry of teardown
27
+ * hooks that fire (LIFO, exactly once) when the run aborts OR ends
28
+ * normally. Anyone holding the controller can:
29
+ *
30
+ * - read `signal` to bail out cooperatively
31
+ * - call `abort(reason?)` to abort the run
32
+ * - call `onAbort(fn)` to register a cleanup hook
33
+ * - call `dispose()` when the run ends normally — this fires the
34
+ * hooks too, so cleanup runs regardless of outcome
35
+ *
36
+ * Hooks must be idempotent and synchronous-or-quick. Errors thrown
37
+ * inside hooks are caught and surfaced through `errorSink` (or the
38
+ * console as a last resort) so one bad hook can't block the others.
39
+ */
40
+ interface RunControllerOptions {
41
+ /** Optional parent signal — abort propagates from parent → this. */
42
+ parentSignal?: AbortSignal;
43
+ /** Receives errors thrown by cleanup hooks. Defaults to console.warn. */
44
+ errorSink?: (err: unknown, where: string) => void;
45
+ }
46
+ declare class RunController {
47
+ private readonly ctrl;
48
+ private readonly hooks;
49
+ private disposed;
50
+ private hooksDrained;
51
+ private readonly errorSink;
52
+ constructor(opts?: RunControllerOptions);
53
+ get signal(): AbortSignal;
54
+ get aborted(): boolean;
55
+ abort(reason?: unknown): void;
56
+ /**
57
+ * Register a teardown hook. Returns an unsubscribe function so callers
58
+ * can opt out before the hook fires (e.g. when a tool finishes cleanly
59
+ * before abort happens).
60
+ */
61
+ onAbort(fn: () => void | Promise<void>): () => void;
62
+ /**
63
+ * Fire cleanup hooks and tear down listeners — called when the run
64
+ * ends *normally* so cleanup happens regardless of outcome. Subsequent
65
+ * aborts become no-ops.
66
+ */
67
+ dispose(): Promise<void>;
68
+ private runHooks;
69
+ }
70
+
71
+ export { RunController, type RunControllerOptions, TOKENS, Token };
@@ -0,0 +1,361 @@
1
+ // src/kernel/container.ts
2
+ var Container = class {
3
+ entries = /* @__PURE__ */ new Map();
4
+ bind(token, factory, opts = {}) {
5
+ if (this.entries.has(token)) {
6
+ throw new Error(`Container: token "${token.description ?? "unknown"}" already bound`);
7
+ }
8
+ this.entries.set(token, {
9
+ factory,
10
+ singleton: opts.singleton ?? true,
11
+ decorators: [],
12
+ owner: opts.owner ?? "core"
13
+ });
14
+ }
15
+ override(token, factory, opts = {}) {
16
+ const existing = this.entries.get(token);
17
+ if (!existing) {
18
+ throw new Error(
19
+ `Container: cannot override "${token.description ?? "unknown"}" \u2014 not bound`
20
+ );
21
+ }
22
+ this.entries.set(token, {
23
+ factory,
24
+ singleton: opts.singleton ?? existing.singleton,
25
+ decorators: existing.decorators,
26
+ owner: opts.owner ?? existing.owner
27
+ });
28
+ }
29
+ decorate(token, decorator, owner = "core") {
30
+ const existing = this.entries.get(token);
31
+ if (!existing) {
32
+ throw new Error(
33
+ `Container: cannot decorate "${token.description ?? "unknown"}" \u2014 not bound`
34
+ );
35
+ }
36
+ existing.decorators.push(decorator);
37
+ existing.cache = void 0;
38
+ existing.owner = `${existing.owner}+${owner}`;
39
+ }
40
+ resolve(token) {
41
+ const entry = this.entries.get(token);
42
+ if (!entry) {
43
+ throw new Error(
44
+ `Container: token "${token.description ?? "unknown"}" not bound`
45
+ );
46
+ }
47
+ if (entry.singleton && entry.cache !== void 0) {
48
+ return entry.cache;
49
+ }
50
+ let value = entry.factory(this);
51
+ for (const d of entry.decorators) {
52
+ value = d(value, this);
53
+ }
54
+ if (entry.singleton) {
55
+ entry.cache = value;
56
+ }
57
+ return value;
58
+ }
59
+ has(token) {
60
+ return this.entries.has(token);
61
+ }
62
+ ownerOf(token) {
63
+ return this.entries.get(token)?.owner;
64
+ }
65
+ /**
66
+ * Remove a token's binding (along with any decorators stacked on it).
67
+ * Returns true if the token existed. Use this to withdraw temporary
68
+ * bindings installed by a short-lived run or plugin — without it, the
69
+ * entry persists in the map forever.
70
+ */
71
+ unbind(token) {
72
+ return this.entries.delete(token);
73
+ }
74
+ /**
75
+ * Drop every binding. Intended for tests and short-lived CLI invocations
76
+ * that rebuild the container from scratch. Production code should prefer
77
+ * `unbind` on the specific tokens it owns.
78
+ */
79
+ clear() {
80
+ this.entries.clear();
81
+ }
82
+ list() {
83
+ return Array.from(this.entries.entries()).map(([token, entry]) => ({
84
+ token,
85
+ owner: entry.owner
86
+ }));
87
+ }
88
+ /**
89
+ * Inspect a binding's full shape, including decorator count and whether
90
+ * a singleton value is cached. Returns null if the token is unbound.
91
+ * Decorator and factory function references are not exposed — only counts
92
+ * and metadata, to keep internal state hidden.
93
+ */
94
+ inspect(token) {
95
+ const entry = this.entries.get(token);
96
+ if (!entry) return null;
97
+ return {
98
+ owner: entry.owner,
99
+ singleton: entry.singleton,
100
+ decoratorCount: entry.decorators.length,
101
+ cached: entry.cache !== void 0
102
+ };
103
+ }
104
+ };
105
+
106
+ // src/kernel/pipeline.ts
107
+ var Pipeline = class {
108
+ chain = [];
109
+ use(mw) {
110
+ this.ensureUnique(mw.name);
111
+ this.chain.push(mw);
112
+ return this;
113
+ }
114
+ prepend(mw) {
115
+ this.ensureUnique(mw.name);
116
+ this.chain.unshift(mw);
117
+ return this;
118
+ }
119
+ /**
120
+ * Insert middleware at an explicit index. Out-of-range indices are clamped.
121
+ * Use this when insertBefore/insertAfter are insufficient (e.g. to place
122
+ * a middleware at a known position regardless of named targets).
123
+ */
124
+ insertAt(index, mw) {
125
+ this.ensureUnique(mw.name);
126
+ const idx = Math.max(0, Math.min(index, this.chain.length));
127
+ this.chain.splice(idx, 0, mw);
128
+ return this;
129
+ }
130
+ /**
131
+ * Insert mw immediately before the first occurrence of target.
132
+ * If called multiple times with the same target, each call inserts
133
+ * before the target's current position — so after insertBefore('B', X)
134
+ * then insertBefore('B', Y), the order is Y → X → B.
135
+ */
136
+ insertBefore(target, mw, opts) {
137
+ this.ensureUnique(mw.name);
138
+ const idx = this.indexOf(target, opts?.optional);
139
+ if (idx === -1) return this;
140
+ this.chain.splice(idx, 0, mw);
141
+ return this;
142
+ }
143
+ /**
144
+ * Insert mw immediately after the first occurrence of target.
145
+ * If called multiple times with the same target, each call inserts
146
+ * after the target's current position — so after insertAfter('B', X)
147
+ * then insertAfter('B', Y), the order is B → X → Y.
148
+ */
149
+ insertAfter(target, mw, opts) {
150
+ this.ensureUnique(mw.name);
151
+ const idx = this.indexOf(target, opts?.optional);
152
+ if (idx === -1) return this;
153
+ this.chain.splice(idx + 1, 0, mw);
154
+ return this;
155
+ }
156
+ replace(target, mw, opts) {
157
+ if (mw.name !== target) this.ensureUnique(mw.name);
158
+ const idx = this.indexOf(target, opts?.optional);
159
+ if (idx === -1) return this;
160
+ this.chain[idx] = mw;
161
+ return this;
162
+ }
163
+ remove(name, opts) {
164
+ const idx = this.indexOf(name, opts?.optional);
165
+ if (idx === -1) return this;
166
+ this.chain.splice(idx, 1);
167
+ return this;
168
+ }
169
+ list() {
170
+ return this.chain.map((m) => m.name);
171
+ }
172
+ size() {
173
+ return this.chain.length;
174
+ }
175
+ /** Return a read-only view suitable for passing to plugins. */
176
+ asReadonly() {
177
+ const self = this;
178
+ return Object.freeze({
179
+ get size() {
180
+ return self.size();
181
+ },
182
+ list() {
183
+ return Object.freeze(self.list());
184
+ },
185
+ run(input) {
186
+ return self.run(input);
187
+ }
188
+ });
189
+ }
190
+ async run(input) {
191
+ let index = -1;
192
+ const chain = this.chain;
193
+ const dispatch = async (i, value) => {
194
+ if (i <= index) {
195
+ throw new Error(`Pipeline: next() called multiple times in "${chain[index]?.name}"`);
196
+ }
197
+ index = i;
198
+ const mw = chain[i];
199
+ if (!mw) return value;
200
+ return mw.handler(value, (v) => dispatch(i + 1, v));
201
+ };
202
+ return dispatch(0, input);
203
+ }
204
+ indexOf(name, optional = false) {
205
+ const idx = this.chain.findIndex((m) => m.name === name);
206
+ if (idx === -1 && !optional) {
207
+ throw new Error(`Pipeline: middleware "${name}" not found`);
208
+ }
209
+ return idx;
210
+ }
211
+ ensureUnique(name) {
212
+ if (this.chain.some((m) => m.name === name)) {
213
+ throw new Error(`Pipeline: middleware "${name}" already registered`);
214
+ }
215
+ }
216
+ };
217
+
218
+ // src/kernel/events.ts
219
+ var EventBus = class {
220
+ listeners = /* @__PURE__ */ new Map();
221
+ logger;
222
+ setLogger(logger) {
223
+ this.logger = logger;
224
+ }
225
+ on(event, fn) {
226
+ let set = this.listeners.get(event);
227
+ if (!set) {
228
+ set = /* @__PURE__ */ new Set();
229
+ this.listeners.set(event, set);
230
+ }
231
+ set.add(fn);
232
+ return () => this.off(event, fn);
233
+ }
234
+ off(event, fn) {
235
+ this.listeners.get(event)?.delete(fn);
236
+ }
237
+ once(event, fn) {
238
+ const wrapper = (payload) => {
239
+ this.off(event, wrapper);
240
+ fn(payload);
241
+ };
242
+ this.on(event, wrapper);
243
+ return () => {
244
+ this.off(event, wrapper);
245
+ };
246
+ }
247
+ emit(event, payload) {
248
+ const set = this.listeners.get(event);
249
+ if (!set) return;
250
+ for (const fn of set) {
251
+ try {
252
+ fn(payload);
253
+ } catch (err) {
254
+ this.logger?.error(`EventBus listener for "${event}" threw`, err);
255
+ }
256
+ }
257
+ }
258
+ clear() {
259
+ this.listeners.clear();
260
+ }
261
+ };
262
+
263
+ // src/kernel/tokens.ts
264
+ var t = (name) => Symbol(name);
265
+ var TOKENS = {
266
+ Logger: t("Logger"),
267
+ TokenCounter: t("TokenCounter"),
268
+ SessionStore: t("SessionStore"),
269
+ MemoryStore: t("MemoryStore"),
270
+ PermissionPolicy: t("PermissionPolicy"),
271
+ Compactor: t("Compactor"),
272
+ PathResolver: t("PathResolver"),
273
+ ConfigLoader: t("ConfigLoader"),
274
+ Renderer: t("Renderer"),
275
+ InputReader: t("InputReader"),
276
+ ErrorHandler: t("ErrorHandler"),
277
+ RetryPolicy: t("RetryPolicy"),
278
+ SkillLoader: t("SkillLoader"),
279
+ SystemPromptBuilder: t("SystemPromptBuilder"),
280
+ SecretScrubber: t("SecretScrubber"),
281
+ ModelsRegistry: t("ModelsRegistry")
282
+ };
283
+
284
+ // src/kernel/run-controller.ts
285
+ var RunController = class {
286
+ ctrl = new AbortController();
287
+ hooks = [];
288
+ disposed = false;
289
+ hooksDrained = false;
290
+ errorSink;
291
+ constructor(opts = {}) {
292
+ this.errorSink = opts.errorSink ?? ((err, where) => {
293
+ console.warn(`[run] cleanup hook failed in ${where}:`, err);
294
+ });
295
+ if (opts.parentSignal) {
296
+ const parent = opts.parentSignal;
297
+ if (parent.aborted) {
298
+ this.ctrl.abort(parent.reason);
299
+ } else {
300
+ const onParentAbort = () => this.ctrl.abort(parent.reason);
301
+ parent.addEventListener("abort", onParentAbort, { once: true });
302
+ this.onAbort(() => parent.removeEventListener("abort", onParentAbort));
303
+ }
304
+ }
305
+ this.ctrl.signal.addEventListener(
306
+ "abort",
307
+ () => {
308
+ void this.runHooks();
309
+ },
310
+ { once: true }
311
+ );
312
+ }
313
+ get signal() {
314
+ return this.ctrl.signal;
315
+ }
316
+ get aborted() {
317
+ return this.ctrl.signal.aborted;
318
+ }
319
+ abort(reason) {
320
+ if (this.ctrl.signal.aborted) return;
321
+ this.ctrl.abort(reason);
322
+ }
323
+ /**
324
+ * Register a teardown hook. Returns an unsubscribe function so callers
325
+ * can opt out before the hook fires (e.g. when a tool finishes cleanly
326
+ * before abort happens).
327
+ */
328
+ onAbort(fn) {
329
+ this.hooks.push(fn);
330
+ return () => {
331
+ const idx = this.hooks.indexOf(fn);
332
+ if (idx !== -1) this.hooks.splice(idx, 1);
333
+ };
334
+ }
335
+ /**
336
+ * Fire cleanup hooks and tear down listeners — called when the run
337
+ * ends *normally* so cleanup happens regardless of outcome. Subsequent
338
+ * aborts become no-ops.
339
+ */
340
+ async dispose() {
341
+ if (this.disposed) return;
342
+ this.disposed = true;
343
+ await this.runHooks();
344
+ }
345
+ async runHooks() {
346
+ if (this.hooksDrained) return;
347
+ this.hooksDrained = true;
348
+ const snapshot = this.hooks.splice(0, this.hooks.length).reverse();
349
+ for (const hook of snapshot) {
350
+ try {
351
+ await hook();
352
+ } catch (err) {
353
+ this.errorSink(err, "RunController.dispose");
354
+ }
355
+ }
356
+ }
357
+ };
358
+
359
+ export { Container, EventBus, Pipeline, RunController, TOKENS };
360
+ //# sourceMappingURL=index.js.map
361
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/kernel/container.ts","../../src/kernel/pipeline.ts","../../src/kernel/events.ts","../../src/kernel/tokens.ts","../../src/kernel/run-controller.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Container — dependency injection with explicit bind / override / decorate.\n *\n * Invariants:\n * bind() — throws if token already bound\n * override() — throws if nothing to replace\n * decorate() — stacks; cached value cleared on register\n */\n\nexport type Token<T> = symbol & { readonly __type?: T };\nexport type Factory<T> = (c: Container) => T;\nexport type Decorator<T> = (inner: T, c: Container) => T;\n\ninterface Entry<T = unknown> {\n factory: Factory<T>;\n singleton: boolean;\n decorators: Decorator<T>[];\n cache?: T;\n owner: string;\n}\n\nexport interface BindOptions {\n singleton?: boolean;\n owner?: string;\n}\n\nexport class Container {\n private readonly entries = new Map<symbol, Entry>();\n\n bind<T>(token: Token<T>, factory: Factory<T>, opts: BindOptions = {}): void {\n if (this.entries.has(token)) {\n throw new Error(`Container: token \"${token.description ?? 'unknown'}\" already bound`);\n }\n this.entries.set(token, {\n factory: factory as Factory<unknown>,\n singleton: opts.singleton ?? true,\n decorators: [],\n owner: opts.owner ?? 'core',\n });\n }\n\n override<T>(token: Token<T>, factory: Factory<T>, opts: BindOptions = {}): void {\n const existing = this.entries.get(token);\n if (!existing) {\n throw new Error(\n `Container: cannot override \"${token.description ?? 'unknown'}\" — not bound`,\n );\n }\n this.entries.set(token, {\n factory: factory as Factory<unknown>,\n singleton: opts.singleton ?? existing.singleton,\n decorators: existing.decorators,\n owner: opts.owner ?? existing.owner,\n });\n }\n\n decorate<T>(token: Token<T>, decorator: Decorator<T>, owner = 'core'): void {\n const existing = this.entries.get(token);\n if (!existing) {\n throw new Error(\n `Container: cannot decorate \"${token.description ?? 'unknown'}\" — not bound`,\n );\n }\n existing.decorators.push(decorator as Decorator<unknown>);\n existing.cache = undefined;\n existing.owner = `${existing.owner}+${owner}`;\n }\n\n resolve<T>(token: Token<T>): T {\n const entry = this.entries.get(token);\n if (!entry) {\n throw new Error(\n `Container: token \"${token.description ?? 'unknown'}\" not bound`,\n );\n }\n if (entry.singleton && entry.cache !== undefined) {\n return entry.cache as T;\n }\n let value: unknown = entry.factory(this);\n for (const d of entry.decorators) {\n value = d(value, this);\n }\n if (entry.singleton) {\n entry.cache = value;\n }\n return value as T;\n }\n\n has<T>(token: Token<T>): boolean {\n return this.entries.has(token);\n }\n\n ownerOf<T>(token: Token<T>): string | undefined {\n return this.entries.get(token)?.owner;\n }\n\n /**\n * Remove a token's binding (along with any decorators stacked on it).\n * Returns true if the token existed. Use this to withdraw temporary\n * bindings installed by a short-lived run or plugin — without it, the\n * entry persists in the map forever.\n */\n unbind<T>(token: Token<T>): boolean {\n return this.entries.delete(token);\n }\n\n /**\n * Drop every binding. Intended for tests and short-lived CLI invocations\n * that rebuild the container from scratch. Production code should prefer\n * `unbind` on the specific tokens it owns.\n */\n clear(): void {\n this.entries.clear();\n }\n\n list(): Array<{ token: symbol; owner: string }> {\n return Array.from(this.entries.entries()).map(([token, entry]) => ({\n token,\n owner: entry.owner,\n }));\n }\n\n /**\n * Inspect a binding's full shape, including decorator count and whether\n * a singleton value is cached. Returns null if the token is unbound.\n * Decorator and factory function references are not exposed — only counts\n * and metadata, to keep internal state hidden.\n */\n inspect<T>(token: Token<T>): {\n owner: string;\n singleton: boolean;\n decoratorCount: number;\n cached: boolean;\n } | null {\n const entry = this.entries.get(token);\n if (!entry) return null;\n return {\n owner: entry.owner,\n singleton: entry.singleton,\n decoratorCount: entry.decorators.length,\n cached: entry.cache !== undefined,\n };\n }\n}\n","/**\n * Pipeline — Koa-style middleware chain with named middleware\n * and position-aware insertion. Generic over input type T.\n */\n\nexport type NextFn<T> = (value: T) => Promise<T>;\nexport type MiddlewareHandler<T> = (value: T, next: NextFn<T>) => Promise<T>;\n\nexport interface Middleware<T> {\n name: string;\n handler: MiddlewareHandler<T>;\n owner?: string;\n}\n\nexport interface PipelineOptions {\n /** When true and the target middleware is not found, operations silently no-op instead of throwing. */\n optional?: boolean;\n}\n\n/**\n * Read-only view of a pipeline. Returned to consumers (plugins, hooks)\n * so they can inspect but not mutate the chain.\n */\nexport interface ReadonlyPipeline<T> {\n readonly size: number;\n list(): readonly string[];\n run(input: T): Promise<T>;\n}\n\nexport class Pipeline<T> {\n private readonly chain: Middleware<T>[] = [];\n\n use(mw: Middleware<T> | Middleware<unknown>): this {\n this.ensureUnique(mw.name);\n this.chain.push(mw as Middleware<T>);\n return this;\n }\n\n prepend(mw: Middleware<T>): this {\n this.ensureUnique(mw.name);\n this.chain.unshift(mw);\n return this;\n }\n\n /**\n * Insert middleware at an explicit index. Out-of-range indices are clamped.\n * Use this when insertBefore/insertAfter are insufficient (e.g. to place\n * a middleware at a known position regardless of named targets).\n */\n insertAt(index: number, mw: Middleware<T>): this {\n this.ensureUnique(mw.name);\n const idx = Math.max(0, Math.min(index, this.chain.length));\n this.chain.splice(idx, 0, mw);\n return this;\n }\n\n /**\n * Insert mw immediately before the first occurrence of target.\n * If called multiple times with the same target, each call inserts\n * before the target's current position — so after insertBefore('B', X)\n * then insertBefore('B', Y), the order is Y → X → B.\n */\n insertBefore(target: string, mw: Middleware<T>, opts?: PipelineOptions): this {\n this.ensureUnique(mw.name);\n const idx = this.indexOf(target, opts?.optional);\n if (idx === -1) return this;\n this.chain.splice(idx, 0, mw);\n return this;\n }\n\n /**\n * Insert mw immediately after the first occurrence of target.\n * If called multiple times with the same target, each call inserts\n * after the target's current position — so after insertAfter('B', X)\n * then insertAfter('B', Y), the order is B → X → Y.\n */\n insertAfter(target: string, mw: Middleware<T>, opts?: PipelineOptions): this {\n this.ensureUnique(mw.name);\n const idx = this.indexOf(target, opts?.optional);\n if (idx === -1) return this;\n this.chain.splice(idx + 1, 0, mw);\n return this;\n }\n\n replace(target: string, mw: Middleware<T>, opts?: PipelineOptions): this {\n if (mw.name !== target) this.ensureUnique(mw.name);\n const idx = this.indexOf(target, opts?.optional);\n if (idx === -1) return this;\n this.chain[idx] = mw;\n return this;\n }\n\n remove(name: string, opts?: PipelineOptions): this {\n const idx = this.indexOf(name, opts?.optional);\n if (idx === -1) return this;\n this.chain.splice(idx, 1);\n return this;\n }\n\n list(): readonly string[] {\n return this.chain.map((m) => m.name);\n }\n\n size(): number {\n return this.chain.length;\n }\n\n /** Return a read-only view suitable for passing to plugins. */\n asReadonly(): ReadonlyPipeline<T> {\n // The returned object's methods close over `this`, so it always sees the live chain.\n // `list()` returns a frozen snapshot to prevent external mutation of the chain.\n const self = this;\n return Object.freeze({\n get size() { return self.size(); },\n list() { return Object.freeze(self.list()); },\n run(input: T) { return self.run(input); },\n });\n }\n\n async run(input: T): Promise<T> {\n let index = -1;\n const chain = this.chain;\n\n const dispatch = async (i: number, value: T): Promise<T> => {\n if (i <= index) {\n throw new Error(`Pipeline: next() called multiple times in \"${chain[index]?.name}\"`);\n }\n index = i;\n const mw = chain[i];\n if (!mw) return value;\n return mw.handler(value, (v) => dispatch(i + 1, v));\n };\n\n return dispatch(0, input);\n }\n\n private indexOf(name: string, optional = false): number {\n const idx = this.chain.findIndex((m) => m.name === name);\n if (idx === -1 && !optional) {\n throw new Error(`Pipeline: middleware \"${name}\" not found`);\n }\n return idx;\n }\n\n private ensureUnique(name: string): void {\n if (this.chain.some((m) => m.name === name)) {\n throw new Error(`Pipeline: middleware \"${name}\" already registered`);\n }\n }\n}\n","/**\n * EventBus — observe-only typed event bus.\n * Subscribers cannot modify or cancel. Subscriber exceptions are caught.\n */\n\nimport type { Usage } from '../types/provider.js';\nimport type { Context } from '../core/context.js';\n\nexport interface EventMap {\n 'session.started': { id: string };\n 'session.ended': { id: string; usage: Usage };\n 'session.damaged': { sessionId: string; detail: string };\n 'iteration.started': { ctx: Context; index: number };\n 'iteration.completed': { ctx: Context; index: number };\n /**\n * Fired when the agent hits its iteration limit. Listeners (CLI/TUI) can\n * call `grant(extra)` to allow more iterations, or `deny()` to stop.\n * If no listener responds within 30s the run ends with 'max_iterations'.\n */\n 'iteration.limit_reached': {\n currentIterations: number;\n currentLimit: number;\n grant: (extraIterations: number) => void;\n deny: () => void;\n };\n 'provider.response': { ctx: Context; usage: Usage; stopReason: string };\n 'provider.text_delta': { ctx: Context; text: string };\n 'provider.tool_use_start': { ctx: Context; id: string; name: string };\n 'provider.tool_use_stop': { ctx: Context; id: string };\n /**\n * Fired before each retry of a failed provider call. `attempt` is 1-based\n * (the first retry is attempt 1, etc.). `description` is the human-readable\n * one-liner from `ProviderError.describe()` — render this in the CLI/TUI\n * instead of grepping logger output for the raw JSON body.\n */\n 'provider.retry': {\n providerId: string;\n attempt: number;\n delayMs: number;\n status: number;\n description: string;\n };\n /**\n * Fired once when a provider call ultimately fails (retries exhausted, or\n * non-retryable error). Same shape as `provider.retry` minus the delay.\n */\n 'provider.error': {\n providerId: string;\n status: number;\n description: string;\n retryable: boolean;\n };\n 'tool.started': { name: string; id: string; input?: unknown };\n /**\n * `output` is a truncated preview of the tool's serialized result text\n * (capped at ~400 chars by the emitter). UIs render this inline in the\n * tool history line without re-fetching from the session log.\n */\n 'tool.executed': {\n name: string;\n durationMs: number;\n ok: boolean;\n input?: unknown;\n output?: string;\n };\n 'token.threshold': { used: number; limit: number };\n 'compaction.fired': { before: number; after: number };\n 'mcp.server.connected': { name: string; toolCount: number };\n 'mcp.server.reconnected': { name: string; toolCount: number };\n 'mcp.server.disconnected': { name: string; reason: string };\n 'token.cost_estimate_unavailable': { model: string };\n error: { err: Error; phase: string };\n}\n\nexport type EventName = keyof EventMap;\nexport type Listener<E extends EventName> = (payload: EventMap[E]) => void;\n\nexport interface EventLogger {\n error(msg: string, ctx?: unknown): void;\n}\n\nexport class EventBus {\n private readonly listeners = new Map<EventName, Set<Listener<EventName>>>();\n private logger?: EventLogger;\n\n setLogger(logger: EventLogger): void {\n this.logger = logger;\n }\n\n on<E extends EventName>(event: E, fn: Listener<E>): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(fn as Listener<EventName>);\n return () => this.off(event, fn);\n }\n\n off<E extends EventName>(event: E, fn: Listener<E>): void {\n this.listeners.get(event)?.delete(fn as Listener<EventName>);\n }\n\n once<E extends EventName>(event: E, fn: Listener<E>): () => void {\n const wrapper: Listener<E> = (payload) => {\n this.off(event, wrapper as Listener<EventName>);\n (fn as Listener<E>)(payload);\n };\n this.on(event, wrapper as Listener<E>);\n return () => {\n this.off(event, wrapper as Listener<EventName>);\n };\n }\n\n emit<E extends EventName>(event: E, payload: EventMap[E]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const fn of set) {\n try {\n (fn as Listener<E>)(payload);\n } catch (err) {\n this.logger?.error(`EventBus listener for \"${event}\" threw`, err);\n }\n }\n }\n\n clear(): void {\n this.listeners.clear();\n }\n}\n","import type { Token } from './container.js';\nimport type { Logger } from '../types/logger.js';\nimport type { TokenCounter } from '../types/token-counter.js';\nimport type { SessionStore } from '../types/session.js';\nimport type { MemoryStore } from '../types/memory.js';\nimport type { PermissionPolicy } from '../types/permission.js';\nimport type { Compactor } from '../types/compactor.js';\nimport type { PathResolver } from '../types/path-resolver.js';\nimport type { ConfigLoader } from '../types/config.js';\nimport type { Renderer } from '../types/renderer.js';\nimport type { InputReader } from '../types/input-reader.js';\nimport type { ErrorHandler } from '../types/error-handler.js';\nimport type { RetryPolicy } from '../types/retry-policy.js';\nimport type { SkillLoader } from '../types/skill.js';\nimport type { SystemPromptBuilder } from '../types/system-prompt.js';\nimport type { SecretScrubber } from '../types/secret-scrubber.js';\nimport type { ModelsRegistry } from '../types/models-registry.js';\n\nconst t = <T>(name: string): Token<T> => Symbol(name) as Token<T>;\n\nexport const TOKENS = {\n Logger: t<Logger>('Logger'),\n TokenCounter: t<TokenCounter>('TokenCounter'),\n SessionStore: t<SessionStore>('SessionStore'),\n MemoryStore: t<MemoryStore>('MemoryStore'),\n PermissionPolicy: t<PermissionPolicy>('PermissionPolicy'),\n Compactor: t<Compactor>('Compactor'),\n PathResolver: t<PathResolver>('PathResolver'),\n ConfigLoader: t<ConfigLoader>('ConfigLoader'),\n Renderer: t<Renderer>('Renderer'),\n InputReader: t<InputReader>('InputReader'),\n ErrorHandler: t<ErrorHandler>('ErrorHandler'),\n RetryPolicy: t<RetryPolicy>('RetryPolicy'),\n SkillLoader: t<SkillLoader>('SkillLoader'),\n SystemPromptBuilder: t<SystemPromptBuilder>('SystemPromptBuilder'),\n SecretScrubber: t<SecretScrubber>('SecretScrubber'),\n ModelsRegistry: t<ModelsRegistry>('ModelsRegistry'),\n} as const;\n","/**\n * RunController centralises abort + cleanup for a single agent run. It\n * wraps a single AbortController and exposes a registry of teardown\n * hooks that fire (LIFO, exactly once) when the run aborts OR ends\n * normally. Anyone holding the controller can:\n *\n * - read `signal` to bail out cooperatively\n * - call `abort(reason?)` to abort the run\n * - call `onAbort(fn)` to register a cleanup hook\n * - call `dispose()` when the run ends normally — this fires the\n * hooks too, so cleanup runs regardless of outcome\n *\n * Hooks must be idempotent and synchronous-or-quick. Errors thrown\n * inside hooks are caught and surfaced through `errorSink` (or the\n * console as a last resort) so one bad hook can't block the others.\n */\nexport interface RunControllerOptions {\n /** Optional parent signal — abort propagates from parent → this. */\n parentSignal?: AbortSignal;\n /** Receives errors thrown by cleanup hooks. Defaults to console.warn. */\n errorSink?: (err: unknown, where: string) => void;\n}\n\nexport class RunController {\n private readonly ctrl = new AbortController();\n private readonly hooks: Array<() => void | Promise<void>> = [];\n private disposed = false;\n private hooksDrained = false;\n private readonly errorSink: (err: unknown, where: string) => void;\n\n constructor(opts: RunControllerOptions = {}) {\n this.errorSink =\n opts.errorSink ??\n ((err, where) => {\n console.warn(`[run] cleanup hook failed in ${where}:`, err);\n });\n if (opts.parentSignal) {\n const parent = opts.parentSignal;\n if (parent.aborted) {\n this.ctrl.abort(parent.reason);\n } else {\n const onParentAbort = () => this.ctrl.abort(parent.reason);\n parent.addEventListener('abort', onParentAbort, { once: true });\n // When this run finishes normally, stop listening on the parent.\n this.onAbort(() => parent.removeEventListener('abort', onParentAbort));\n }\n }\n this.ctrl.signal.addEventListener(\n 'abort',\n () => {\n void this.runHooks();\n },\n { once: true },\n );\n }\n\n get signal(): AbortSignal {\n return this.ctrl.signal;\n }\n\n get aborted(): boolean {\n return this.ctrl.signal.aborted;\n }\n\n abort(reason?: unknown): void {\n if (this.ctrl.signal.aborted) return;\n this.ctrl.abort(reason);\n }\n\n /**\n * Register a teardown hook. Returns an unsubscribe function so callers\n * can opt out before the hook fires (e.g. when a tool finishes cleanly\n * before abort happens).\n */\n onAbort(fn: () => void | Promise<void>): () => void {\n this.hooks.push(fn);\n return () => {\n const idx = this.hooks.indexOf(fn);\n if (idx !== -1) this.hooks.splice(idx, 1);\n };\n }\n\n /**\n * Fire cleanup hooks and tear down listeners — called when the run\n * ends *normally* so cleanup happens regardless of outcome. Subsequent\n * aborts become no-ops.\n */\n async dispose(): Promise<void> {\n if (this.disposed) return;\n this.disposed = true;\n await this.runHooks();\n }\n\n private async runHooks(): Promise<void> {\n if (this.hooksDrained) return;\n this.hooksDrained = true;\n // Snapshot + clear so hooks added during cleanup don't re-fire.\n const snapshot = this.hooks.splice(0, this.hooks.length).reverse();\n for (const hook of snapshot) {\n try {\n await hook();\n } catch (err) {\n this.errorSink(err, 'RunController.dispose');\n }\n }\n }\n}\n"]}