@openrouter/agent 0.7.2 → 0.8.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 (64) hide show
  1. package/README.md +248 -17
  2. package/esm/index.d.ts +8 -5
  3. package/esm/index.d.ts.map +1 -1
  4. package/esm/index.js +12 -4
  5. package/esm/index.js.map +1 -1
  6. package/esm/inner-loop/call-model.d.ts +8 -5
  7. package/esm/inner-loop/call-model.d.ts.map +1 -1
  8. package/esm/inner-loop/call-model.js +16 -6
  9. package/esm/inner-loop/call-model.js.map +1 -1
  10. package/esm/lib/async-params.d.ts +25 -6
  11. package/esm/lib/async-params.d.ts.map +1 -1
  12. package/esm/lib/async-params.js +3 -1
  13. package/esm/lib/async-params.js.map +1 -1
  14. package/esm/lib/conversation-state.d.ts +44 -1
  15. package/esm/lib/conversation-state.d.ts.map +1 -1
  16. package/esm/lib/conversation-state.js +111 -0
  17. package/esm/lib/conversation-state.js.map +1 -1
  18. package/esm/lib/hooks-emit.d.ts +50 -0
  19. package/esm/lib/hooks-emit.d.ts.map +1 -0
  20. package/esm/lib/hooks-emit.js +307 -0
  21. package/esm/lib/hooks-emit.js.map +1 -0
  22. package/esm/lib/hooks-manager.d.ts +141 -0
  23. package/esm/lib/hooks-manager.d.ts.map +1 -0
  24. package/esm/lib/hooks-manager.js +282 -0
  25. package/esm/lib/hooks-manager.js.map +1 -0
  26. package/esm/lib/hooks-matchers.d.ts +12 -0
  27. package/esm/lib/hooks-matchers.d.ts.map +1 -0
  28. package/esm/lib/hooks-matchers.js +26 -0
  29. package/esm/lib/hooks-matchers.js.map +1 -0
  30. package/esm/lib/hooks-resolve.d.ts +17 -0
  31. package/esm/lib/hooks-resolve.d.ts.map +1 -0
  32. package/esm/lib/hooks-resolve.js +42 -0
  33. package/esm/lib/hooks-resolve.js.map +1 -0
  34. package/esm/lib/hooks-schemas.d.ts +188 -0
  35. package/esm/lib/hooks-schemas.d.ts.map +1 -0
  36. package/esm/lib/hooks-schemas.js +184 -0
  37. package/esm/lib/hooks-schemas.js.map +1 -0
  38. package/esm/lib/hooks-types.d.ts +174 -0
  39. package/esm/lib/hooks-types.d.ts.map +1 -0
  40. package/esm/lib/hooks-types.js +45 -0
  41. package/esm/lib/hooks-types.js.map +1 -0
  42. package/esm/lib/model-result.d.ts +209 -9
  43. package/esm/lib/model-result.d.ts.map +1 -1
  44. package/esm/lib/model-result.js +1397 -348
  45. package/esm/lib/model-result.js.map +1 -1
  46. package/esm/lib/reusable-stream.d.ts +6 -0
  47. package/esm/lib/reusable-stream.d.ts.map +1 -1
  48. package/esm/lib/reusable-stream.js +8 -0
  49. package/esm/lib/reusable-stream.js.map +1 -1
  50. package/esm/lib/tool-executor.d.ts.map +1 -1
  51. package/esm/lib/tool-executor.js +11 -1
  52. package/esm/lib/tool-executor.js.map +1 -1
  53. package/esm/lib/tool-orchestrator.d.ts.map +1 -1
  54. package/esm/lib/tool-orchestrator.js +4 -1
  55. package/esm/lib/tool-orchestrator.js.map +1 -1
  56. package/esm/lib/tool-types.d.ts +137 -45
  57. package/esm/lib/tool-types.d.ts.map +1 -1
  58. package/esm/lib/tool-types.js +13 -0
  59. package/esm/lib/tool-types.js.map +1 -1
  60. package/esm/lib/tool.d.ts +35 -25
  61. package/esm/lib/tool.d.ts.map +1 -1
  62. package/esm/lib/tool.js +19 -0
  63. package/esm/lib/tool.js.map +1 -1
  64. package/package.json +6 -2
@@ -0,0 +1,141 @@
1
+ import type { $ZodType, infer as zodInfer, input as zodInput } from 'zod/v4/core';
2
+ import type { BuiltInHookDefinitions, EmitResult, HookEntry, HookHandler, HookRegistry, HooksManagerOptions } from './hooks-types.js';
3
+ /**
4
+ * Type-level registry of every hook this manager knows: built-ins plus the
5
+ * instance's custom hooks. Each entry carries:
6
+ *
7
+ * - `payload`: the schema OUTPUT -- what handlers receive and `finalPayload`
8
+ * holds (post-validation, transforms/defaults applied)
9
+ * - `payloadIn`: the schema INPUT -- what `emit()` accepts (differs from
10
+ * `payload` for schemas with .transform()/.default()/.coerce). Built-in
11
+ * schemas have no transforms, so input == output there.
12
+ * - `result`: the validated handler result
13
+ */
14
+ type AllHooks<Custom extends HookRegistry> = {
15
+ [K in keyof BuiltInHookDefinitions]: {
16
+ payload: BuiltInHookDefinitions[K]['payload'];
17
+ payloadIn: BuiltInHookDefinitions[K]['payload'];
18
+ result: BuiltInHookDefinitions[K]['result'];
19
+ };
20
+ } & {
21
+ [K in keyof Custom]: {
22
+ payload: zodInfer<Custom[K]['payload']>;
23
+ payloadIn: zodInput<Custom[K]['payload']>;
24
+ result: zodInfer<Custom[K]['result']>;
25
+ };
26
+ };
27
+ /**
28
+ * Module-private key used by `resolveHooks` to call the internal
29
+ * untyped-registration path without exposing it as a public method on the
30
+ * class. See {@link getInternalRegistrar} below.
31
+ */
32
+ declare const INTERNAL_REGISTRAR_KEY: unique symbol;
33
+ type InternalRegistrar = (hookName: string, entry: HookEntry<unknown, unknown>) => () => void;
34
+ /**
35
+ * Typed, extensible hook system for agent lifecycle events.
36
+ *
37
+ * Supports both built-in hooks (PreToolUse, PostToolUse, etc.) and
38
+ * user-defined custom hooks with full type safety.
39
+ */
40
+ export declare class HooksManager<Custom extends HookRegistry = Record<string, never>> {
41
+ private readonly entries;
42
+ private readonly pendingAsync;
43
+ private readonly customHooks;
44
+ private readonly inflightControllers;
45
+ private readonly throwOnHandlerError;
46
+ private sessionId;
47
+ constructor(customHooks?: Custom, options?: HooksManagerOptions);
48
+ /**
49
+ * Set the manager-level default session ID exposed as `context.sessionId`
50
+ * to handler invocations. Payloads deliberately do not carry session
51
+ * identity.
52
+ *
53
+ * This is a single mutable default on the manager instance: when one
54
+ * manager is shared by concurrent runs, callers MUST pass `sessionId` in
55
+ * the per-emit context instead (as the engine does), otherwise the last
56
+ * `setSessionId()` wins and concurrent emits observe the wrong id.
57
+ */
58
+ setSessionId(sessionId: string): void;
59
+ /**
60
+ * Register a handler for a hook. Returns an unsubscribe function.
61
+ */
62
+ on<K extends keyof AllHooks<Custom> & string>(hookName: K, entry: HookEntry<AllHooks<Custom>[K]['payload'], AllHooks<Custom>[K]['result']>): () => void;
63
+ /**
64
+ * Remove a specific handler function from a hook.
65
+ * Returns true if found and removed, false otherwise.
66
+ */
67
+ off<K extends keyof AllHooks<Custom> & string>(hookName: K, handler: HookHandler<AllHooks<Custom>[K]['payload'], AllHooks<Custom>[K]['result']>): boolean;
68
+ /**
69
+ * Remove all handlers for a specific hook, or all handlers if no name given.
70
+ */
71
+ removeAll<K extends keyof AllHooks<Custom> & string>(hookName?: K): void;
72
+ /**
73
+ * Validate the payload (and each handler's result) against the registered
74
+ * Zod schemas, invoke matching handlers, and return results.
75
+ *
76
+ * Payload validation failure is treated according to `throwOnHandlerError`:
77
+ * strict mode re-throws, default mode logs a warning and returns an empty
78
+ * result without invoking any handlers.
79
+ */
80
+ emit<K extends keyof AllHooks<Custom> & string>(hookName: K, payload: AllHooks<Custom>[K]['payloadIn'], emitContext?: {
81
+ toolName?: string;
82
+ /**
83
+ * Session identity for THIS emit's `context.sessionId`, overriding the
84
+ * manager-level default from `setSessionId()`. Pass it whenever the
85
+ * manager instance may be shared across concurrent runs.
86
+ */
87
+ sessionId?: string;
88
+ }): Promise<EmitResult<AllHooks<Custom>[K]['result'], AllHooks<Custom>[K]['payload']>>;
89
+ /**
90
+ * Await all in-flight async handler work. Used for graceful shutdown.
91
+ */
92
+ drain(): Promise<void>;
93
+ /**
94
+ * Abort every in-flight `emit()` by firing their context `signal`s. Handlers
95
+ * that kick off async work should observe `context.signal` to honor this.
96
+ *
97
+ * Does not remove pending async work from `pendingAsync` — callers that want
98
+ * to wait for handlers to wind down should still call `drain()` afterward.
99
+ */
100
+ abortInflight(reason?: unknown): void;
101
+ /**
102
+ * Check if any handlers are registered for a given hook.
103
+ */
104
+ hasHandlers(hookName: string): boolean;
105
+ /**
106
+ * Internal registration path used by `resolveHooks` to translate an
107
+ * InlineHookConfig object into this manager. User code should use `on()`.
108
+ */
109
+ [INTERNAL_REGISTRAR_KEY](hookName: string, entry: HookEntry<unknown, unknown>): () => void;
110
+ private _register;
111
+ private _definitionFor;
112
+ }
113
+ /**
114
+ * Internal: return the private registrar of a HooksManager instance. This
115
+ * lets `resolveHooks` register arbitrary hook names (including unknown ones
116
+ * from an inline config) without exposing a public bypass of the typed `on()`.
117
+ *
118
+ * NOT PART OF THE PUBLIC API.
119
+ */
120
+ export declare function getInternalRegistrar(manager: HooksManager): InternalRegistrar;
121
+ /**
122
+ * Detect a `z.void()` schema (zod v4 core). Result validation is skipped for
123
+ * void-result hooks so side-effect-only handlers can return arbitrary values
124
+ * without tripping validation -- for built-ins and custom hooks alike.
125
+ *
126
+ * Implementation note: `schema._zod.def.type` is zod v4's designated
127
+ * introspection surface for library authors (every `$ZodType` carries a
128
+ * `_zod: $ZodTypeInternals` with a stable `def.type` discriminator). A string
129
+ * check is deliberately preferred over `instanceof $ZodVoid`, which breaks
130
+ * across duplicated zod module instances (dual-package hazard) and mixed
131
+ * zod/v4 vs zod/v4-mini usage. Behavior is pinned by tests in
132
+ * hooks-contract-fixes.test.ts ("void schema detection") so a zod upgrade
133
+ * that restructures the internals fails loudly instead of silently
134
+ * re-enabling result validation on void hooks.
135
+ *
136
+ * Exported for the pinning tests only; NOT re-exported from the package
137
+ * index and NOT part of the public API.
138
+ */
139
+ export declare function isVoidSchema(schema: $ZodType): boolean;
140
+ export {};
141
+ //# sourceMappingURL=hooks-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-manager.d.ts","sourceRoot":"","sources":["../../src/lib/hooks-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,282 @@
1
+ import { safeParse } from 'zod/v4/core';
2
+ import { executeHandlerChain } from './hooks-emit.js';
3
+ import { BUILT_IN_HOOK_NAMES, BUILT_IN_HOOKS } from './hooks-schemas.js';
4
+ //#endregion
5
+ /**
6
+ * Module-private key used by `resolveHooks` to call the internal
7
+ * untyped-registration path without exposing it as a public method on the
8
+ * class. See {@link getInternalRegistrar} below.
9
+ */
10
+ const INTERNAL_REGISTRAR_KEY = Symbol('HooksManager.internalRegistrar');
11
+ /**
12
+ * Typed, extensible hook system for agent lifecycle events.
13
+ *
14
+ * Supports both built-in hooks (PreToolUse, PostToolUse, etc.) and
15
+ * user-defined custom hooks with full type safety.
16
+ */
17
+ export class HooksManager {
18
+ constructor(customHooks, options) {
19
+ this.entries = new Map();
20
+ this.pendingAsync = new Set();
21
+ this.inflightControllers = new Set();
22
+ this.sessionId = '';
23
+ this.throwOnHandlerError = options?.throwOnHandlerError ?? false;
24
+ // Validate custom hook names: no empty names, no collisions with built-ins.
25
+ if (customHooks) {
26
+ for (const name of Object.keys(customHooks)) {
27
+ if (name === '') {
28
+ throw new Error('Custom hook names must be non-empty strings.');
29
+ }
30
+ if (BUILT_IN_HOOK_NAMES.has(name)) {
31
+ throw new Error(`Custom hook name "${name}" collides with a built-in hook. Choose a different name.`);
32
+ }
33
+ }
34
+ this.customHooks = {
35
+ ...customHooks,
36
+ };
37
+ }
38
+ else {
39
+ this.customHooks = {};
40
+ }
41
+ }
42
+ /**
43
+ * Set the manager-level default session ID exposed as `context.sessionId`
44
+ * to handler invocations. Payloads deliberately do not carry session
45
+ * identity.
46
+ *
47
+ * This is a single mutable default on the manager instance: when one
48
+ * manager is shared by concurrent runs, callers MUST pass `sessionId` in
49
+ * the per-emit context instead (as the engine does), otherwise the last
50
+ * `setSessionId()` wins and concurrent emits observe the wrong id.
51
+ */
52
+ setSessionId(sessionId) {
53
+ this.sessionId = sessionId;
54
+ }
55
+ /**
56
+ * Register a handler for a hook. Returns an unsubscribe function.
57
+ */
58
+ on(hookName, entry) {
59
+ return this._register(hookName, entry);
60
+ }
61
+ /**
62
+ * Remove a specific handler function from a hook.
63
+ * Returns true if found and removed, false otherwise.
64
+ */
65
+ off(hookName, handler) {
66
+ const list = this.entries.get(hookName);
67
+ if (!list) {
68
+ return false;
69
+ }
70
+ const idx = list.findIndex((e) => e.handler === handler);
71
+ if (idx === -1) {
72
+ return false;
73
+ }
74
+ list.splice(idx, 1);
75
+ if (list.length === 0) {
76
+ this.entries.delete(hookName);
77
+ }
78
+ return true;
79
+ }
80
+ /**
81
+ * Remove all handlers for a specific hook, or all handlers if no name given.
82
+ */
83
+ removeAll(hookName) {
84
+ if (hookName) {
85
+ this.entries.delete(hookName);
86
+ }
87
+ else {
88
+ this.entries.clear();
89
+ }
90
+ }
91
+ /**
92
+ * Validate the payload (and each handler's result) against the registered
93
+ * Zod schemas, invoke matching handlers, and return results.
94
+ *
95
+ * Payload validation failure is treated according to `throwOnHandlerError`:
96
+ * strict mode re-throws, default mode logs a warning and returns an empty
97
+ * result without invoking any handlers.
98
+ */
99
+ async emit(hookName, payload, emitContext) {
100
+ // Snapshot the entries list so mutations (on()/off()/unsubscribe) triggered
101
+ // by a handler during the chain can't shift indices mid-iteration and
102
+ // silently skip the next handler, or splice in new handlers that were
103
+ // registered after this emit began.
104
+ const list = (this.entries.get(hookName) ?? []).slice();
105
+ // Validate the payload and, on success, feed the PARSED value into the
106
+ // chain. For schemas with .transform() / .default() / .coerce (or that
107
+ // strip unknown keys) the parsed output differs from the raw input: emit
108
+ // accepts the schema INPUT type (`payloadIn`) while handlers and
109
+ // finalPayload carry the schema OUTPUT (`payload`) -- so handlers must
110
+ // receive `parsed.data`, not the raw value, or the runtime shape silently
111
+ // diverges from the static types.
112
+ let chainPayload = payload;
113
+ const definition = this._definitionFor(hookName);
114
+ if (definition) {
115
+ const parsed = safeParse(definition.payload, payload);
116
+ if (!parsed.success) {
117
+ const err = new Error(`[HooksManager] Invalid payload for hook "${hookName}": ${parsed.error.message}`);
118
+ if (this.throwOnHandlerError) {
119
+ throw err;
120
+ }
121
+ console.warn(err.message);
122
+ return {
123
+ results: [],
124
+ pending: [],
125
+ finalPayload: chainPayload,
126
+ blocked: false,
127
+ mutated: false,
128
+ };
129
+ }
130
+ chainPayload = parsed.data;
131
+ }
132
+ const controller = new AbortController();
133
+ this.inflightControllers.add(controller);
134
+ const context = {
135
+ signal: controller.signal,
136
+ hookName,
137
+ sessionId: emitContext?.sessionId ?? this.sessionId,
138
+ };
139
+ let hasDetachedWork = false;
140
+ try {
141
+ // Result validation is skipped for hooks whose result schema is void --
142
+ // decided by SCHEMA SHAPE, not by a hard-coded name list, so custom
143
+ // hooks registered with `result: z.void()` behave exactly like the
144
+ // built-in void hooks (handlers may return arbitrary values that are
145
+ // collected as opaque results without complaint).
146
+ const resultSchema = definition && !isVoidSchema(definition.result) ? definition.result : undefined;
147
+ const result = await executeHandlerChain(list, chainPayload, context, {
148
+ hookName,
149
+ throwOnHandlerError: this.throwOnHandlerError,
150
+ toolName: emitContext?.toolName,
151
+ resultSchema,
152
+ // When a handler's fire-and-forget work exceeds its asyncTimeout,
153
+ // abort this emit's signal so handlers observing context.signal can
154
+ // cancel the stale work cooperatively.
155
+ onAsyncTimeout: () => {
156
+ controller.abort(new Error('Hook async work timed out'));
157
+ },
158
+ });
159
+ // Track pending async work for drain(); each promise self-removes once
160
+ // it settles so the set can't grow unbounded across many emits.
161
+ hasDetachedWork = result.pending.length > 0;
162
+ if (hasDetachedWork) {
163
+ let remaining = result.pending.length;
164
+ for (const p of result.pending) {
165
+ this.pendingAsync.add(p);
166
+ p.finally(() => {
167
+ this.pendingAsync.delete(p);
168
+ // The emit's controller must stay registered until the detached
169
+ // work settles so abortInflight() can reach fire-and-forget
170
+ // handlers that outlive the emit call itself.
171
+ remaining--;
172
+ if (remaining === 0) {
173
+ this.inflightControllers.delete(controller);
174
+ }
175
+ });
176
+ }
177
+ }
178
+ return result;
179
+ }
180
+ finally {
181
+ // With detached work in flight the controller lives until that work
182
+ // settles (see above); otherwise it can be dropped now.
183
+ if (!hasDetachedWork) {
184
+ this.inflightControllers.delete(controller);
185
+ }
186
+ }
187
+ }
188
+ /**
189
+ * Await all in-flight async handler work. Used for graceful shutdown.
190
+ */
191
+ async drain() {
192
+ while (this.pendingAsync.size > 0) {
193
+ const snapshot = Array.from(this.pendingAsync);
194
+ await Promise.allSettled(snapshot);
195
+ }
196
+ }
197
+ /**
198
+ * Abort every in-flight `emit()` by firing their context `signal`s. Handlers
199
+ * that kick off async work should observe `context.signal` to honor this.
200
+ *
201
+ * Does not remove pending async work from `pendingAsync` — callers that want
202
+ * to wait for handlers to wind down should still call `drain()` afterward.
203
+ */
204
+ abortInflight(reason) {
205
+ for (const controller of this.inflightControllers) {
206
+ controller.abort(reason);
207
+ }
208
+ }
209
+ /**
210
+ * Check if any handlers are registered for a given hook.
211
+ */
212
+ hasHandlers(hookName) {
213
+ const list = this.entries.get(hookName);
214
+ return list !== undefined && list.length > 0;
215
+ }
216
+ /**
217
+ * Internal registration path used by `resolveHooks` to translate an
218
+ * InlineHookConfig object into this manager. User code should use `on()`.
219
+ */
220
+ [INTERNAL_REGISTRAR_KEY](hookName, entry) {
221
+ return this._register(hookName, entry);
222
+ }
223
+ _register(hookName, entry) {
224
+ const list = this.entries.get(hookName) ?? [];
225
+ list.push(entry);
226
+ this.entries.set(hookName, list);
227
+ return () => {
228
+ const current = this.entries.get(hookName);
229
+ if (!current) {
230
+ return;
231
+ }
232
+ const idx = current.indexOf(entry);
233
+ if (idx !== -1) {
234
+ current.splice(idx, 1);
235
+ }
236
+ };
237
+ }
238
+ _definitionFor(hookName) {
239
+ const builtIn = BUILT_IN_HOOKS[hookName];
240
+ if (builtIn) {
241
+ return builtIn;
242
+ }
243
+ const custom = this.customHooks[hookName];
244
+ if (custom) {
245
+ return custom;
246
+ }
247
+ return undefined;
248
+ }
249
+ }
250
+ /**
251
+ * Internal: return the private registrar of a HooksManager instance. This
252
+ * lets `resolveHooks` register arbitrary hook names (including unknown ones
253
+ * from an inline config) without exposing a public bypass of the typed `on()`.
254
+ *
255
+ * NOT PART OF THE PUBLIC API.
256
+ */
257
+ export function getInternalRegistrar(manager) {
258
+ return manager[INTERNAL_REGISTRAR_KEY].bind(manager);
259
+ }
260
+ /**
261
+ * Detect a `z.void()` schema (zod v4 core). Result validation is skipped for
262
+ * void-result hooks so side-effect-only handlers can return arbitrary values
263
+ * without tripping validation -- for built-ins and custom hooks alike.
264
+ *
265
+ * Implementation note: `schema._zod.def.type` is zod v4's designated
266
+ * introspection surface for library authors (every `$ZodType` carries a
267
+ * `_zod: $ZodTypeInternals` with a stable `def.type` discriminator). A string
268
+ * check is deliberately preferred over `instanceof $ZodVoid`, which breaks
269
+ * across duplicated zod module instances (dual-package hazard) and mixed
270
+ * zod/v4 vs zod/v4-mini usage. Behavior is pinned by tests in
271
+ * hooks-contract-fixes.test.ts ("void schema detection") so a zod upgrade
272
+ * that restructures the internals fails loudly instead of silently
273
+ * re-enabling result validation on void hooks.
274
+ *
275
+ * Exported for the pinning tests only; NOT re-exported from the package
276
+ * index and NOT part of the public API.
277
+ */
278
+ export function isVoidSchema(schema) {
279
+ const def = schema._zod?.def;
280
+ return def?.type === 'void';
281
+ }
282
+ //# sourceMappingURL=hooks-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-manager.js","sourceRoot":"","sources":["../../src/lib/hooks-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { ToolMatcher } from './hooks-types.js';
2
+ /**
3
+ * Evaluate a ToolMatcher against a tool name.
4
+ *
5
+ * - `undefined` -> wildcard, matches all tools
6
+ * - `string` -> exact match
7
+ * - `RegExp` -> `.test(toolName)`
8
+ * - `function` -> arbitrary predicate (truthy/falsy returns are coerced to
9
+ * boolean so the declared `boolean` return type holds at runtime)
10
+ */
11
+ export declare function matchesTool(matcher: ToolMatcher | undefined, toolName: string): boolean;
12
+ //# sourceMappingURL=hooks-matchers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-matchers.d.ts","sourceRoot":"","sources":["../../src/lib/hooks-matchers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAevF"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Evaluate a ToolMatcher against a tool name.
3
+ *
4
+ * - `undefined` -> wildcard, matches all tools
5
+ * - `string` -> exact match
6
+ * - `RegExp` -> `.test(toolName)`
7
+ * - `function` -> arbitrary predicate (truthy/falsy returns are coerced to
8
+ * boolean so the declared `boolean` return type holds at runtime)
9
+ */
10
+ export function matchesTool(matcher, toolName) {
11
+ if (matcher === undefined) {
12
+ return true;
13
+ }
14
+ if (typeof matcher === 'string') {
15
+ return matcher === toolName;
16
+ }
17
+ if (matcher instanceof RegExp) {
18
+ // RegExps with the /g or /y flag advance `lastIndex` on every `.test()`,
19
+ // so successive emits with the same tool name alternate true/false.
20
+ // Reset so the matcher behaves statelessly regardless of flag.
21
+ matcher.lastIndex = 0;
22
+ return matcher.test(toolName);
23
+ }
24
+ return Boolean(matcher(toolName));
25
+ }
26
+ //# sourceMappingURL=hooks-matchers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-matchers.js","sourceRoot":"","sources":["../../src/lib/hooks-matchers.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,OAAgC,EAAE,QAAgB;IAC5E,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,OAAO,KAAK,QAAQ,CAAC;IAC9B,CAAC;IACD,IAAI,OAAO,YAAY,MAAM,EAAE,CAAC;QAC9B,yEAAyE;QACzE,oEAAoE;QACpE,+DAA+D;QAC/D,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;QACtB,OAAO,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { HooksManager } from './hooks-manager.js';
2
+ import type { InlineHookConfig } from './hooks-types.js';
3
+ /**
4
+ * Normalize a hooks option into a HooksManager instance.
5
+ *
6
+ * - `undefined` -> `undefined` (no hooks)
7
+ * - `HooksManager` -> passthrough
8
+ * - Plain object (InlineHookConfig) -> construct HooksManager, register all entries
9
+ *
10
+ * The inline config surface is typed to only accept built-in hook names, but
11
+ * a generic config coerced through `as` can smuggle typos past the compiler
12
+ * (e.g. `PretoolUse` vs `PreToolUse`) which would silently never fire. We log
13
+ * a warning for any non-built-in key and skip registration for it. Custom
14
+ * hooks must be registered through a `HooksManager` instance via `on()`.
15
+ */
16
+ export declare function resolveHooks(hooks: InlineHookConfig | HooksManager | undefined): HooksManager | undefined;
17
+ //# sourceMappingURL=hooks-resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-resolve.d.ts","sourceRoot":"","sources":["../../src/lib/hooks-resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,KAAK,EAAa,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpE;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,gBAAgB,GAAG,YAAY,GAAG,SAAS,GACjD,YAAY,GAAG,SAAS,CA6B1B"}
@@ -0,0 +1,42 @@
1
+ import { getInternalRegistrar, HooksManager } from './hooks-manager.js';
2
+ import { BUILT_IN_HOOK_NAMES } from './hooks-schemas.js';
3
+ /**
4
+ * Normalize a hooks option into a HooksManager instance.
5
+ *
6
+ * - `undefined` -> `undefined` (no hooks)
7
+ * - `HooksManager` -> passthrough
8
+ * - Plain object (InlineHookConfig) -> construct HooksManager, register all entries
9
+ *
10
+ * The inline config surface is typed to only accept built-in hook names, but
11
+ * a generic config coerced through `as` can smuggle typos past the compiler
12
+ * (e.g. `PretoolUse` vs `PreToolUse`) which would silently never fire. We log
13
+ * a warning for any non-built-in key and skip registration for it. Custom
14
+ * hooks must be registered through a `HooksManager` instance via `on()`.
15
+ */
16
+ export function resolveHooks(hooks) {
17
+ if (!hooks) {
18
+ return undefined;
19
+ }
20
+ if (hooks instanceof HooksManager) {
21
+ return hooks;
22
+ }
23
+ // Inline config -> HooksManager. We register through the internal registrar
24
+ // so we don't have to constrain `resolveHooks` to the typed `on()` surface
25
+ // and so user code can't reach this path.
26
+ const manager = new HooksManager();
27
+ const register = getInternalRegistrar(manager);
28
+ for (const [hookName, entries] of Object.entries(hooks)) {
29
+ if (!entries || !Array.isArray(entries)) {
30
+ continue;
31
+ }
32
+ if (!BUILT_IN_HOOK_NAMES.has(hookName)) {
33
+ console.warn(`[resolveHooks] Ignoring inline hook entry for unknown hook name "${hookName}". Inline config only supports built-in hooks; register custom hooks via a HooksManager instance.`);
34
+ continue;
35
+ }
36
+ for (const entry of entries) {
37
+ register(hookName, entry);
38
+ }
39
+ }
40
+ return manager;
41
+ }
42
+ //# sourceMappingURL=hooks-resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-resolve.js","sourceRoot":"","sources":["../../src/lib/hooks-resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAkD;IAElD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,4EAA4E;IAC5E,2EAA2E;IAC3E,0CAA0C;IAC1C,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC/C,KAAK,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACxC,SAAS;QACX,CAAC;QACD,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvC,OAAO,CAAC,IAAI,CACV,oEAAoE,QAAQ,mGAAmG,CAChL,CAAC;YACF,SAAS;QACX,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,QAAQ,CAAC,QAAQ,EAAE,KAAoC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}