@openrouter/agent 0.7.1 → 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.
- package/README.md +248 -17
- package/esm/index.d.ts +8 -5
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +12 -4
- package/esm/index.js.map +1 -1
- package/esm/inner-loop/call-model.d.ts +8 -5
- package/esm/inner-loop/call-model.d.ts.map +1 -1
- package/esm/inner-loop/call-model.js +16 -6
- package/esm/inner-loop/call-model.js.map +1 -1
- package/esm/lib/async-params.d.ts +25 -6
- package/esm/lib/async-params.d.ts.map +1 -1
- package/esm/lib/async-params.js +3 -1
- package/esm/lib/async-params.js.map +1 -1
- package/esm/lib/conversation-state.d.ts +44 -1
- package/esm/lib/conversation-state.d.ts.map +1 -1
- package/esm/lib/conversation-state.js +111 -0
- package/esm/lib/conversation-state.js.map +1 -1
- package/esm/lib/hooks-emit.d.ts +50 -0
- package/esm/lib/hooks-emit.d.ts.map +1 -0
- package/esm/lib/hooks-emit.js +307 -0
- package/esm/lib/hooks-emit.js.map +1 -0
- package/esm/lib/hooks-manager.d.ts +141 -0
- package/esm/lib/hooks-manager.d.ts.map +1 -0
- package/esm/lib/hooks-manager.js +282 -0
- package/esm/lib/hooks-manager.js.map +1 -0
- package/esm/lib/hooks-matchers.d.ts +12 -0
- package/esm/lib/hooks-matchers.d.ts.map +1 -0
- package/esm/lib/hooks-matchers.js +26 -0
- package/esm/lib/hooks-matchers.js.map +1 -0
- package/esm/lib/hooks-resolve.d.ts +17 -0
- package/esm/lib/hooks-resolve.d.ts.map +1 -0
- package/esm/lib/hooks-resolve.js +42 -0
- package/esm/lib/hooks-resolve.js.map +1 -0
- package/esm/lib/hooks-schemas.d.ts +188 -0
- package/esm/lib/hooks-schemas.d.ts.map +1 -0
- package/esm/lib/hooks-schemas.js +184 -0
- package/esm/lib/hooks-schemas.js.map +1 -0
- package/esm/lib/hooks-types.d.ts +174 -0
- package/esm/lib/hooks-types.d.ts.map +1 -0
- package/esm/lib/hooks-types.js +45 -0
- package/esm/lib/hooks-types.js.map +1 -0
- package/esm/lib/model-result.d.ts +209 -9
- package/esm/lib/model-result.d.ts.map +1 -1
- package/esm/lib/model-result.js +1397 -348
- package/esm/lib/model-result.js.map +1 -1
- package/esm/lib/reusable-stream.d.ts +6 -0
- package/esm/lib/reusable-stream.d.ts.map +1 -1
- package/esm/lib/reusable-stream.js +8 -0
- package/esm/lib/reusable-stream.js.map +1 -1
- package/esm/lib/tool-executor.d.ts.map +1 -1
- package/esm/lib/tool-executor.js +11 -1
- package/esm/lib/tool-executor.js.map +1 -1
- package/esm/lib/tool-orchestrator.d.ts.map +1 -1
- package/esm/lib/tool-orchestrator.js +4 -1
- package/esm/lib/tool-orchestrator.js.map +1 -1
- package/esm/lib/tool-types.d.ts +137 -45
- package/esm/lib/tool-types.d.ts.map +1 -1
- package/esm/lib/tool-types.js +13 -0
- package/esm/lib/tool-types.js.map +1 -1
- package/esm/lib/tool.d.ts +35 -25
- package/esm/lib/tool.d.ts.map +1 -1
- package/esm/lib/tool.js +19 -0
- package/esm/lib/tool.js.map +1 -1
- package/package.json +7 -3
|
@@ -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"}
|