@x-otto/hooks 0.0.1-alpha.4 → 0.1.0-alpha.9
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/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createLogger as e}from"@x-otto/shared";import{z as t}from"zod";import{normalizeEnv as n}from"@x-otto/env";import{WATERFALL_TIMINGS as r,isWaterfallTiming as i}from"@x-otto/hook-contracts";const a=t.void(),o=t.object({message:t.unknown(),cancelled:t.boolean(),metadata:t.record(t.string(),t.unknown())}),s=t.object({args:t.record(t.string(),t.unknown()),displayArgs:t.record(t.string(),t.unknown()).optional(),cancelled:t.boolean(),cancelReason:t.string().optional(),decision:t.enum([`allow`,`deny`,`ask`]).optional(),decisionReason:t.string().optional(),risk:t.enum([`low`,`medium`,`high`]).optional()}),c=t.object({result:t.unknown(),metadata:t.record(t.string(),t.unknown())}),
|
|
2
|
-
`)}\nPlease replace with meaningful implementation comments.`;t.result={...t.result,content:[...t.result.content,{type:`text`,text:e}]},t.metadata.aiCommentsDetected=r.length}}})}function
|
|
3
|
-
`);for(let e=0;e<n.length;e++){let r=n[e]??``;for(let n of
|
|
1
|
+
import{createLogger as e}from"@x-otto/shared";import{z as t}from"zod";import{normalizeEnv as n}from"@x-otto/env";import{WATERFALL_TIMINGS as r,isWaterfallTiming as i}from"@x-otto/hook-contracts";const a=t.void(),o=t.object({message:t.unknown(),cancelled:t.boolean(),metadata:t.record(t.string(),t.unknown())}),s=t.object({args:t.record(t.string(),t.unknown()),displayArgs:t.record(t.string(),t.unknown()).optional(),cancelled:t.boolean(),cancelReason:t.string().optional(),decision:t.enum([`allow`,`deny`,`ask`]).optional(),decisionReason:t.string().optional(),risk:t.enum([`low`,`medium`,`high`]).optional()}),c=t.object({result:t.unknown(),metadata:t.record(t.string(),t.unknown())}),l=t.object({messages:t.array(t.unknown())}),u=t.object({temperature:t.number().optional(),maxTokens:t.number().optional(),thinkingLevel:t.string().optional(),metadata:t.record(t.string(),t.unknown())}),d=t.object({systemPrompt:t.string(),systemTail:t.array(t.string()).optional()}),ee=t.object({cancelled:t.boolean()}),f={"chat.message.before":o,"chat.message.after":o,"tool.execute.before":s,"tool.execute.after":c,"messages.transform":l,"chat.params":u,"system.prompt.transform":d,"session.created":a,"session.restored":a,"session.deleted":a,"session.idle":a,"session.error":a,"stream.start":a,"stream.end":a,"compaction.before":a,"compaction.after":a,"memory.prune.before":ee,"process.spawned":a,"process.exited":a,"task.created":a,"task.started":a,"task.completed":a,"task.failed":a,"task.cancelled":a,notification:a};function p(e){return e instanceof t.ZodVoid}function m(e,t){if(p(e))return!1;let n=e.def?.shape;return n!==void 0&&t in n}const h=Object.keys(f);function g(e){return p(f[e])?`observer`:`interceptor`}const te=new Set(h.filter(e=>g(e)===`interceptor`));new Set(h.filter(e=>g(e)===`observer`));const _=new Set(h.filter(e=>g(e)===`interceptor`&&m(f[e],`cancelled`))),v=new Set(h.filter(e=>g(e)===`interceptor`&&m(f[e],`decision`)));function y(){return Object.fromEntries(h.map(e=>[e,[]]))}const b=h,x=te;function S(){return y()}function C(e){let t={name:e.name,timing:e.timing,priority:e.priority??50,enabled:e.enabled??!0};return x.has(e.timing)?{...t,kind:`interceptor`,handle:e.handle}:{...t,kind:`observer`,handle:e.handle}}function w(e,t){if(e!==`session.error`)return t;let n=t;return{sessionId:n.sessionId,metadata:n.metadata,error:n.error instanceof Error?n.error.message:String(n.error)}}const T=e(`@x-otto/hooks`),E=new Set([`tool.execute.before`]),D=n(process.env.OTTO_HOOK_TIMEOUT_MS,5e3),ne=_,re=v;function ie(e){return typeof e==`object`&&!!e&&e.abort===!0}async function ae(e,t,n,r,i){let a;try{return{ok:!0,abort:ie(await Promise.race([e.handle(n,r),new Promise((t,n)=>{a=setTimeout(()=>{n(new O(e.name))},D),a.unref?.()})]))}}catch(n){let r=n instanceof Error?n.message:String(n),a=n instanceof O;return T.error({name:e.name,timing:t,err:r,timedOut:a},`Hook failed or timed out`),a&&i?.({hookName:e.name,timing:t,timeoutMs:D}),{ok:!1,timedOut:a,message:r}}finally{a!==void 0&&clearTimeout(a)}}var O=class extends Error{constructor(e){super(`hook "${e}" timed out after ${D}ms`),this.name=`HookTimeoutError`}};async function oe(e,t,n){let r;try{await Promise.race([e.handle(n),new Promise((t,n)=>{r=setTimeout(()=>{n(Error(`hook "${e.name}" timed out after ${D}ms`))},D),r.unref?.()})])}catch(n){let r=n instanceof Error?n.message:String(n);T.error({name:e.name,timing:t,err:r},`Hook failed or timed out`)}finally{r!==void 0&&clearTimeout(r)}}async function se(e,t,n,r,i){if(t.length===0)return;let a=!1,o=!1;for(let s of t){let t=await ae(s,e,n,r,i);if(!t.ok&&E.has(e)){let e=r;typeof e.cancelled==`boolean`&&(e.cancelled=!0,e.cancelReason=t.timedOut?`hook "${s.name}" timed out after ${D}ms`:`hook "${s.name}" failed: ${t.message}`)}if(ne.has(e)){let e=r;e.cancelled===!0&&(a=!0),a&&(e.cancelled=!0)}if(re.has(e)){let e=r;e.decision===`deny`&&(o=!0),o&&(e.decision=`deny`)}if(t.ok&&t.abort)break}}async function ce(e,t,n){if(t.length===0)return;let r=w(e,n);for(let n of t)await oe(n,e,r)}function k(e=61440){return C({name:`output-truncation`,timing:`tool.execute.after`,priority:10,handle(t,n){let{content:r}=n.result,i=0;for(let e of r)e.type===`text`&&(i+=e.text.length);if(i<=e)return;let a=e,o=r.map(t=>{if(t.type!==`text`)return t;if(a<=0)return{type:`text`,text:``};if(t.text.length<=a)return a-=t.text.length,t;let n=t.text.slice(0,a);return a=0,{type:`text`,text:`${n}\n\n... [output truncated: ${i} chars → ${e} chars]`}}).filter(e=>e.type!==`text`||e.text.length>0);n.result={...n.result,content:o},n.metadata.truncated=!0,n.metadata.originalSize=i}})}function A(){return C({name:`label-truncator`,timing:`tool.execute.before`,priority:20,handle(e,t){t.displayArgs||={...t.args};for(let e of[`label`,`description`,`title`]){let n=t.displayArgs[e];typeof n==`string`&&n.length>200&&(t.displayArgs[e]=`${n.slice(0,200)}...`)}}})}function j(){return C({name:`thinking-validator`,timing:`messages.transform`,priority:20,handle(e,t){t.messages=t.messages.map(e=>{if(!M(e))return e;let t=e.content;if(!Array.isArray(t))return e;let n=t.filter(e=>!(e.type===`thinking`&&(typeof e.text!=`string`||e.text.trim()===``)));return{...e,content:n}})}})}function M(e){return typeof e==`object`&&!!e&&`role`in e&&e.role===`assistant`}function N(){return C({name:`anthropic-effort`,timing:`chat.params`,priority:10,handle(e,t){e.provider===`anthropic`&&(t.thinkingLevel||(e.model.includes(`opus`)?t.thinkingLevel=`high`:e.model.includes(`sonnet`)?t.thinkingLevel=`medium`:e.model.includes(`haiku`)&&(t.thinkingLevel=`low`)))}})}function P(){return C({name:`first-message-variant`,timing:`chat.message.before`,priority:10,handle(e,t){e.isFirstMessage&&(t.metadata.isFirstMessage=!0,t.metadata.variant=`first-message`)}})}const F=[/\/\/\s*TODO:\s*implement/i,/\/\/\s*add\s+(your|the)\s+/i,/\/\/\s*placeholder/i,/\/\/\s*\.\.\.\s*(rest|more|other|remaining)/i,/\/\/\s*handle\s+error\s+here/i,/\/\*\s*\*\//];function I(){return C({name:`comment-checker`,timing:`tool.execute.after`,priority:20,handle(e,t){if(e.readonly)return;let n=R(e.args);if(!n)return;let r=L(n);if(r.length>0){let e=`\n\n⚠️ AI-style comments detected:\n${r.map(e=>` - Line ${e.line}: "${e.text}"`).join(`
|
|
2
|
+
`)}\nPlease replace with meaningful implementation comments.`;t.result={...t.result,content:[...t.result.content,{type:`text`,text:e}]},t.metadata.aiCommentsDetected=r.length}}})}function L(e){let t=[],n=e.split(`
|
|
3
|
+
`);for(let e=0;e<n.length;e++){let r=n[e]??``;for(let n of F)if(n.test(r)){t.push({line:e+1,text:r.trim()});break}}return t}function R(e){return typeof e.content==`string`?e.content:typeof e.newString==`string`?e.newString:typeof e.new_string==`string`?e.new_string:null}const z=e(`@x-otto/hooks:stream-metrics`);function B(e){return C({name:`stream-metrics`,timing:`stream.start`,priority:10,handle(t){let n=e.streamMetrics;n.set(t.sessionId,{startTime:Date.now(),model:t.model,requestCount:(n.get(t.sessionId)?.requestCount??0)+1,lastStopReason:``,totalDurationMs:n.get(t.sessionId)?.totalDurationMs??0}),z.debug(`Stream started: session=%s model=%s`,t.sessionId,t.model)}})}function V(e){return C({name:`stream-end-metrics`,timing:`stream.end`,priority:10,handle(t){let n=e.streamMetrics.get(t.sessionId);if(n){let e=Date.now()-n.startTime;n.totalDurationMs+=e,n.lastStopReason=t.stopReason,z.debug(`Stream ended: session=%s model=%s duration=%dms stopReason=%s total=%dms`,t.sessionId,t.model,e,t.stopReason,n.totalDurationMs)}}})}const H=e(`@x-otto/hooks:compaction`);function U(e){return C({name:`compaction-logger`,timing:`compaction.before`,priority:10,handle(t){let n=e.compactionStats.get(t.sessionId)??{compactionCount:0,totalCompacted:0,lastCompactionTime:0};n.compactionCount++,n.lastCompactionTime=Date.now(),e.compactionStats.set(t.sessionId,n),H.info(`Compaction started: session=%s messageCount=%d compaction#%d`,t.sessionId,t.messageCount,n.compactionCount)}})}function W(e){return C({name:`compaction-after-logger`,timing:`compaction.after`,priority:10,handle(t){let n=e.compactionStats.get(t.sessionId);if(n){let e=Date.now()-n.lastCompactionTime;n.totalCompacted+=t.retainedCount,H.info(`Compaction finished: session=%s retained=%d duration=%dms totalCompactions=%d`,t.sessionId,t.retainedCount,e,n.compactionCount)}}})}const le=e(`@x-otto/hooks:tool-error-tracker`);function G(e,t){let n=t?.circuitBreakerThreshold??5,r=t?.decayWindowMs??12e4;return C({name:`tool-error-tracker`,timing:`tool.execute.after`,priority:15,handle(t,i){let a=e.toolErrors.get(t.sessionId)??new Map;e.toolErrors.set(t.sessionId,a);let o=a.get(t.toolName);if(o&&Date.now()-o.lastErrorTime>r&&(o=void 0),t.result.isError){if(o||={toolName:t.toolName,errorCount:0,lastErrorTime:0,consecutiveErrors:0},o.errorCount++,o.consecutiveErrors++,o.lastErrorTime=Date.now(),a.set(t.toolName,o),o.consecutiveErrors>=n){let e=`Tool "${t.toolName}" hit circuit-breaker: ${o.consecutiveErrors} consecutive errors`;le.warn(e),i.metadata.toolCircuitBreaker={toolName:t.toolName,consecutiveErrors:o.consecutiveErrors,tripped:!0}}}else o&&(o.consecutiveErrors=0,a.set(t.toolName,o))}})}const ue=e(`@x-otto/hooks:token-budget`);function K(e){let t=e?.maxOutputTokens??16384;return C({name:`token-budget`,timing:`chat.params`,priority:20,handle(e,n){(!n.maxTokens||n.maxTokens>t)&&(n.maxTokens=t,ue.debug(`Capped maxTokens to %d for model=%s`,t,e.model))}})}function de(e,t){switch(e){case`default`:return q(t);case`strict`:return J(t);case`minimal`:return fe();default:return[]}}function q(e){return[k(),A(),j(),N(),P(),B(e),V(e),U(e),W(e),G(e),K()]}function J(e){return[...q(e),I()]}function fe(){return[k()]}var Y=class{streamMetrics=new Map;compactionStats=new Map;toolErrors=new Map;clearSession(e){this.streamMetrics.delete(e),this.compactionStats.delete(e),this.toolErrors.delete(e)}},X=class{tokens=[];constructor(e,t){this.registry=e,this.name=t}register(e){let t={...e,name:`${this.name}:${e.name}`},n=this.registry.register(t);return this.tokens.push(n),n}dispose(){for(let e of this.tokens)e.dispose();this.tokens.length=0}};const Z=e(`@x-otto/hooks`);var Q=class{hooks=S();disabled=new Set;onHookTimeout;state=new Y;constructor(e){this.onHookTimeout=e?.onHookTimeout,this.register(C({name:`hook-state-cleanup`,timing:`session.deleted`,priority:90,handle:e=>{this.state.clearSession(e.sessionId)}})),e?.preset&&e.preset!==`none`&&this.applyPreset(e.preset)}register(e){return this.hooks[e.timing].push(e),Z.debug({hookName:e.name,timing:e.timing,priority:e.priority},`Hook registered`),{dispose:()=>{let t=this.hooks[e.timing],n=t.filter(t=>t!==e);n.length<t.length&&(this.hooks[e.timing]=n,Z.debug({hookName:e.name,timing:e.timing},`Hook token disposed`))}}}registerAll(e){let t=[];for(let n of e)t.push(this.register(n));return t}createScope(e){return new X(this,e)}on(e,t,n,r=50){return this.register(C({name:t,timing:e,handle:n,priority:r})),this}has(e){return this.getRegistered().some(t=>t.name===e)}unregister(e){let t=!1;for(let n of b){let r=this.hooks[n],i=r.filter(t=>t.name!==e);i.length<r.length&&(this.hooks[n]=i,t=!0,Z.debug({hookName:e,timing:n},`Hook unregistered`))}return t}disable(e){this.disabled.add(e),Z.debug({hookName:e},`Hook disabled`)}enable(e){this.disabled.delete(e),Z.debug({hookName:e},`Hook enabled`)}getRegistered(e){if(e)return this.hooks[e].map($);let t=[];for(let e of b)t.push(...this.hooks[e].map($));return t}async execute(e,t,n){await se(e,this.getSortedInterceptorHooks(e),t,n,this.onHookTimeout)}async emit(e,t){await ce(e,this.getSortedObserverHooks(e),t)}clear(){for(let e of b)this.hooks[e]=[];this.disabled.clear(),Z.debug(`All hooks cleared`)}getSortedInterceptorHooks(e){let t=this.hooks[e];return t?t.filter(e=>e.kind===`interceptor`&&e.enabled&&!this.disabled.has(e.name)).sort((e,t)=>e.priority-t.priority):(Z.warn({timing:e},`Unknown hook timing (no bucket) — emit name mismatch with HOOK_NODES?`),[])}getSortedObserverHooks(e){let t=this.hooks[e];return t?t.filter(e=>e.kind===`observer`&&e.enabled&&!this.disabled.has(e.name)).sort((e,t)=>e.priority-t.priority):(Z.warn({timing:e},`Unknown hook timing (no bucket) — emit name mismatch with HOOK_NODES?`),[])}applyPreset(e){let t=de(e,this.state);for(let e of t)this.register(e)}};function $(e){return{name:e.name,timing:e.timing,priority:e.priority,enabled:e.enabled}}function pe(e){return new Q(e)}export{Q as HookRegistry,X as HookScope,Y as HookState,r as WATERFALL_TIMINGS,N as createAnthropicEffortHook,I as createCommentCheckerHook,W as createCompactionAfterHook,U as createCompactionLoggerHook,P as createFirstMessageVariantHook,pe as createHookRegistry,A as createLabelTruncatorHook,k as createOutputTruncationHook,V as createStreamEndMetricsHook,B as createStreamMetricsHook,j as createThinkingValidatorHook,K as createTokenBudgetHook,G as createToolErrorTrackerHook,C as defineHook,i as isWaterfallTiming};
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["logger","log","logger","log"],"sources":["../src/hook-schema.ts","../src/hook-nodes.ts","../src/hook-spec.ts","../src/emit-sanitize.ts","../src/hook-executor.ts","../src/core/tool-guard/output-truncation.ts","../src/core/tool-guard/label-truncator.ts","../src/core/transform/thinking-validator.ts","../src/core/transform/anthropic-effort.ts","../src/core/session/first-message-variant.ts","../src/core/quality/comment-checker.ts","../src/core/stream/stream-metrics.ts","../src/core/compaction/compaction-logger.ts","../src/core/tool-guard/tool-error-tracker.ts","../src/core/transform/token-budget.ts","../src/hook-preset.ts","../src/core/hook-state.ts","../src/scope.ts","../src/hook-registry.ts"],"sourcesContent":["/**\n * hook-schema.ts —— RFC-416 D1:从 zod schema 形状自动推导运行时表。\n *\n * 此前 hook-nodes.ts 的 HOOK_NODES(26 行手写 kind 判别)+ hook-executor.ts 的\n * STICKY_CANCELLED_TIMINGS / STICKY_DECISION_TIMINGS(2 张手写白名单)+\n * hook-nodes.ts 的 _driftGuard(6 行条件类型守卫)共 3 张手写表 + 6 处守卫,\n * 全部从此处 schema 形状自动推导——新增 timing 只改 HookPayloadMap + schema,\n * 不再四处登记。\n *\n * **为什么放 hooks 包而非 hook-contracts**:hook-contracts 是 L1 零运行时依赖\n * 叶包(只依赖 interchange L0),引入 zod 会破坏定位。hooks 包是 L2(已依赖\n * shared/env),能负担 zod 运行时依赖。遵循 interchange 已验证的\"零依赖契约层\n * + 下游自带 schema\"模式。schema 用 `satisfies` 钉死到 HookPayloadMap 类型\n * (drift → 编译报错),同 setting 的 zod 钉死模式。\n *\n * **可推导边界**:\n * - kind(observer/interceptor):output schema 为 z.void() → observer;否则 → interceptor\n * - sticky_cancelled:output schema 含 `cancelled` 字段 → 进白名单\n * - sticky_decision:output schema 含 `decision` 字段 → 进白名单\n * - FAIL_CLOSED:**不可推导**(\"失败时是否强制 cancel\"是语义概念非形状概念)→ 保留手写 1 项\n */\nimport { z } from 'zod'\nimport type { HookTiming, InterceptorTiming } from '@x-otto/hook-contracts'\n\n// ============================================================================\n// Schema 定义:每个 timing 的 output 形状\n// 用 satisfies 钉死到 HookPayloadMap[T]['output']——drift → 编译报错\n// ============================================================================\n\n/** Observer timing 的 output schema(void)。 */\nconst voidSchema = z.void()\n\n/** ChatMessageOutput shape。 */\nconst chatMessageOutputSchema = z.object({\n message: z.unknown(),\n cancelled: z.boolean(),\n metadata: z.record(z.string(), z.unknown()),\n})\n\n/** ToolExecuteBeforeOutput shape(含 displayArgs + args readonly + cancelled + decision)。 */\nconst toolExecuteBeforeOutputSchema = z.object({\n args: z.record(z.string(), z.unknown()),\n displayArgs: z.record(z.string(), z.unknown()).optional(),\n cancelled: z.boolean(),\n cancelReason: z.string().optional(),\n decision: z.enum(['allow', 'deny', 'ask']).optional(),\n decisionReason: z.string().optional(),\n risk: z.enum(['low', 'medium', 'high']).optional(),\n})\n\n/** ToolExecuteAfterOutput shape。 */\nconst toolExecuteAfterOutputSchema = z.object({\n result: z.unknown(),\n metadata: z.record(z.string(), z.unknown()),\n})\n\n/** MessagesTransformOutput shape。 */\nconst messagesTransformOutputSchema = z.object({\n messages: z.array(z.unknown()),\n})\n\n/** ChatParamsOutput shape。 */\nconst chatParamsOutputSchema = z.object({\n temperature: z.number().optional(),\n maxTokens: z.number().optional(),\n thinkingLevel: z.string().optional(),\n metadata: z.record(z.string(), z.unknown()),\n})\n\n/** SystemPromptTransformOutput shape。 */\nconst systemPromptTransformOutputSchema = z.object({\n systemPrompt: z.string(),\n systemTail: z.array(z.string()).optional(),\n})\n\n/** MemoryPruneBeforeOutput shape(含 cancelled)。 */\nconst memoryPruneBeforeOutputSchema = z.object({\n cancelled: z.boolean(),\n})\n\n// ============================================================================\n// Schema Map:timing → output schema\n// ============================================================================\n\n/**\n * Timing → output schema 映射。这是运行时推导的唯一真源——\n * 新增 timing 只需在此加一行 schema + HookPayloadMap 加一行类型,两处 satisfies 钉死保证一致。\n *\n * void timings(observer)用 voidSchema;非 void timings(interceptor)用具体 schema。\n */\nexport const HOOK_OUTPUT_SCHEMAS = {\n 'chat.message.before': chatMessageOutputSchema,\n 'chat.message.after': chatMessageOutputSchema,\n 'tool.execute.before': toolExecuteBeforeOutputSchema,\n 'tool.execute.after': toolExecuteAfterOutputSchema,\n 'messages.transform': messagesTransformOutputSchema,\n 'chat.params': chatParamsOutputSchema,\n 'system.prompt.transform': systemPromptTransformOutputSchema,\n 'session.created': voidSchema,\n 'session.restored': voidSchema,\n 'session.deleted': voidSchema,\n 'session.idle': voidSchema,\n 'session.error': voidSchema,\n 'stream.start': voidSchema,\n 'stream.end': voidSchema,\n 'compaction.before': voidSchema,\n 'compaction.after': voidSchema,\n 'memory.prune.before': memoryPruneBeforeOutputSchema,\n 'process.spawned': voidSchema,\n 'process.exited': voidSchema,\n 'task.created': voidSchema,\n 'task.started': voidSchema,\n 'task.completed': voidSchema,\n 'task.failed': voidSchema,\n 'task.cancelled': voidSchema,\n 'notification': voidSchema,\n} as const satisfies Record<HookTiming, z.ZodType<any>>\n\n// 编译期门:schema map 的 key 集合与 HookTiming 一致(漏一个 → 编译报错)\n// 用 Record<HookTiming, ...> 确保所有 timing 都有 schema\nconst _schemaKeyGate: Record<HookTiming, z.ZodType<any>> = HOOK_OUTPUT_SCHEMAS\nvoid _schemaKeyGate\n\n// ============================================================================\n// 从 schema 形状自动推导运行时表\n// ============================================================================\n\n/** 判断 schema 是否为 void(observer timing)。 */\nfunction isVoidSchema(schema: z.ZodTypeAny): boolean {\n // zod v4:def.type === 'void'\n return (schema as { def?: { type?: string } }).def?.type === 'void'\n}\n\n/** 判断 schema 是否含某个字段(用于 sticky 白名单推导)。 */\nfunction schemaHasField(schema: z.ZodTypeAny, field: string): boolean {\n if (isVoidSchema(schema)) return false\n // zod v4:def.shape 是 Record<string, ZodTypeAny>\n const shape = (schema as { def?: { shape?: Record<string, unknown> } }).def?.shape\n return shape !== undefined && field in shape\n}\n\n/** 推导的 HOOK_TIMINGS(全集,替代手写 HOOK_NODES.map(n => n.id))。 */\nexport const HOOK_TIMINGS_DERIVED: readonly HookTiming[] = Object.keys(HOOK_OUTPUT_SCHEMAS) as HookTiming[]\n\n/** 推导的 kind 判别(替代手写 HOOK_NODES 的 kind 字段)。 */\nexport function getHookKind(timing: HookTiming): 'observer' | 'interceptor' {\n return isVoidSchema(HOOK_OUTPUT_SCHEMAS[timing]) ? 'observer' : 'interceptor'\n}\n\n/** 推导的 INTERCEPTOR_TIMINGS(替代 hook-nodes.ts 的手写 Set)。 */\nexport const INTERCEPTOR_TIMINGS_DERIVED: ReadonlySet<HookTiming> = new Set(\n HOOK_TIMINGS_DERIVED.filter((t) => getHookKind(t) === 'interceptor'),\n)\n\n/** 推导的 OBSERVER_TIMINGS(替代手写)。 */\nexport const OBSERVER_TIMINGS_DERIVED: ReadonlySet<HookTiming> = new Set(\n HOOK_TIMINGS_DERIVED.filter((t) => getHookKind(t) === 'observer'),\n)\n\n/**\n * 推导的 STICKY_CANCELLED_TIMINGS(替代 hook-executor.ts 手写白名单)。\n * output 含 `cancelled` 字段的 interceptor timing → 进白名单。\n */\nexport const STICKY_CANCELLED_TIMINGS_DERIVED: ReadonlySet<InterceptorTiming> = new Set(\n HOOK_TIMINGS_DERIVED.filter(\n (t) => getHookKind(t) === 'interceptor' && schemaHasField(HOOK_OUTPUT_SCHEMAS[t], 'cancelled'),\n ) as InterceptorTiming[],\n)\n\n/**\n * 推导的 STICKY_DECISION_TIMINGS(替代 hook-executor.ts 手写白名单)。\n * output 含 `decision` 字段的 interceptor timing → 进白名单。\n */\nexport const STICKY_DECISION_TIMINGS_DERIVED: ReadonlySet<InterceptorTiming> = new Set(\n HOOK_TIMINGS_DERIVED.filter(\n (t) => getHookKind(t) === 'interceptor' && schemaHasField(HOOK_OUTPUT_SCHEMAS[t], 'decision'),\n ) as InterceptorTiming[],\n)\n\n/** 推导的 createHookBuckets(替代 hook-nodes.ts 手写)。 */\nexport function createHookBucketsDerived<T>(): Record<HookTiming, T[]> {\n return Object.fromEntries(\n HOOK_TIMINGS_DERIVED.map((timing) => [timing, [] as T[]]),\n ) as Record<HookTiming, T[]>\n}\n","/**\n * hook-nodes.ts —— RFC-416 D1:从 schema 形状自动推导,消灭手写 HOOK_NODES 表 + driftGuard。\n *\n * 此前 26 行手写 HOOK_NODES 表 + 6 行 _driftGuard 条件类型守卫已全部删除——\n * kind(observer/interceptor)从 hook-schema.ts 的 zod schema 形状推导\n * (output 为 void → observer,否则 → interceptor)。\n *\n * 新增 timing 只需在 HookPayloadMap 加类型 + hook-schema.ts 加 schema,\n * 两处 satisfies 钉死保证一致——不再四处登记。\n *\n * 保留的导出:HOOK_TIMINGS / INTERCEPTOR_TIMINGS / createHookBuckets 从 schema 派生。\n * HookNode 接口保留(类型层可能被引用),但不再有手写实例。\n */\nimport type { HookTiming } from '@x-otto/hook-contracts'\nimport {\n HOOK_TIMINGS_DERIVED,\n INTERCEPTOR_TIMINGS_DERIVED,\n createHookBucketsDerived,\n} from './hook-schema'\n\nexport interface HookNode {\n readonly id: HookTiming\n readonly kind: 'interceptor' | 'observer'\n}\n\nexport const HOOK_TIMINGS: readonly HookTiming[] = HOOK_TIMINGS_DERIVED\n\nexport const INTERCEPTOR_TIMINGS: ReadonlySet<HookTiming> = INTERCEPTOR_TIMINGS_DERIVED\n\nexport function createHookBuckets<T>(): Record<HookTiming, T[]> {\n return createHookBucketsDerived<T>()\n}\n","import type { HookHandle, HookTiming } from './types'\nimport { INTERCEPTOR_TIMINGS } from './hook-nodes'\nimport type { HookAbortSignal } from '@x-otto/hook-contracts'\n\ntype BaseRuntimeHook = {\n name: string\n timing: HookTiming\n priority: number\n enabled: boolean\n}\n\nexport type ObserverRuntimeHook = BaseRuntimeHook & {\n kind: 'observer'\n handle: (input: unknown) => void | Promise<void>\n}\n\nexport type InterceptorRuntimeHook = BaseRuntimeHook & {\n kind: 'interceptor'\n handle: (\n input: unknown,\n output: unknown,\n ) => void | HookAbortSignal | Promise<void | HookAbortSignal>\n}\n\nexport type HookSpec = ObserverRuntimeHook | InterceptorRuntimeHook\n\nexport { INTERCEPTOR_TIMINGS }\n\n/**\n * 所有 as 强转集中在此处:HookHandle<T> 因函数参数逆变无法自动赋给 (unknown) => void。\n * factory 函数通过 timing 字面量驱动 handle 的类型推断,调用侧零 cast。\n */\nexport function defineHook<T extends HookTiming>(config: {\n name: string\n timing: T\n handle: HookHandle<T>\n priority?: number\n enabled?: boolean\n}): HookSpec {\n const base: BaseRuntimeHook = {\n name: config.name,\n timing: config.timing,\n priority: config.priority ?? 50,\n enabled: config.enabled ?? true,\n }\n\n if (INTERCEPTOR_TIMINGS.has(config.timing)) {\n return {\n ...base,\n kind: 'interceptor',\n handle: config.handle as InterceptorRuntimeHook['handle'],\n }\n }\n\n return {\n ...base,\n kind: 'observer',\n handle: config.handle as ObserverRuntimeHook['handle'],\n }\n}\n","import type { HookEmitInput, HookInput, ObserverTiming } from './types'\n\n/**\n * RFC-143 R2a:`session.error` timing 的 input 契约上携带原始 `Error` 对象\n * (`HookPayloadMap['session.error'].input.error: Error`,见 hook-contracts/timings.ts)。\n * SDK 层(`module-wiring.ts`)已对 definePlugin 消费路径做 `error.message` 转换,但那只是\n * 单一消费方的自觉行为——emit() 是所有 observer hook(含非 SDK 注册路径,如未来的引擎内部\n * 钩子/测试工具)的唯一入口,必须在这里做结构式 sanitize,防止任何消费方读到含栈追踪/敏感\n * 路径信息的原始 Error 对象。sanitize 后的 input 只对 `session.error` 生效,其余 timing 原样传递。\n *\n * RFC-395 M3:从 HookRegistry 私有方法拆出为纯函数(emit-sanitize 职责单文件化)。\n */\nexport function sanitizeEmitInput<T extends ObserverTiming>(\n timing: T,\n input: HookEmitInput<T>,\n): HookInput<T> {\n if (timing !== 'session.error') return input as HookInput<T>\n\n const raw = input as unknown as {\n sessionId: string\n metadata: Record<string, unknown>\n error: Error | string\n }\n const sanitized = {\n sessionId: raw.sessionId,\n metadata: raw.metadata,\n error: raw.error instanceof Error ? raw.error.message : String(raw.error),\n }\n return sanitized as unknown as HookInput<T>\n}\n","import { createLogger } from '@x-otto/shared'\nimport { normalizeEnv } from '@x-otto/env'\nimport type { HookInput, HookEmitInput, HookOutput, InterceptorTiming, ObserverTiming } from './types'\nimport type { InterceptorRuntimeHook, ObserverRuntimeHook } from './hook-spec'\nimport { sanitizeEmitInput } from './emit-sanitize'\nimport { STICKY_CANCELLED_TIMINGS_DERIVED, STICKY_DECISION_TIMINGS_DERIVED } from './hook-schema'\n\nconst logger = createLogger('@x-otto/hooks')\n\nconst FAIL_CLOSED_TIMINGS = new Set<string>(['tool.execute.before'])\n\n/**\n * RFC-153 D1:interceptor 与 observer 两条 hook 执行路径共享同一个超时阈值——避免两处各自\n *维护一份可能漂移的数字。默认 5000ms 承袭 RFC-143 M0 为 observer 路径选定的值(远超正常 hook\n * 应有的毫秒级/快速 I/O 耗时,又不会误杀慢但终将完成的合法 hook)。硬编码审计 P1:插件生态\n * 下慢 hook 是用户真实调参点,经 `OTTO_HOOK_TIMEOUT_MS` 覆盖(保持单源,两条路径一并生效)。\n */\nconst HOOK_TIMEOUT_MS = normalizeEnv(process.env['OTTO_HOOK_TIMEOUT_MS'], 5000)\n\n// RFC-088 D3:sticky-veto 白名单——RFC-416 D1 从 hook-schema.ts 自动推导\n// (output 含 cancelled/decision 字段的 interceptor timing → 进白名单)。\n// 此前手写的 STICKY_CANCELLED_TIMINGS / STICKY_DECISION_TIMINGS 已删除——\n// 新增 timing 只改 HookPayloadMap + schema,白名单自动更新。\n//\n// RFC-327 修订 D1(M1)设计核对:waterfall 的 abort/中断语义**不进**本白名单——sticky 探测\n// 的前提是 output 真含 cancelled/decision 字段,而 waterfall 白名单 timing(system.prompt.\n// transform / chat.params / messages.transform)的 output 均无此二字段(timings.ts 契约);\n// abort 走返回值哨兵(`HookAbortSignal`,serial-with-early-bail),无需也不能借 sticky 表达。\n// 故本白名单维持推导结果不变——不盲探测 transform,也不为 waterfall 扩张。\nconst STICKY_CANCELLED_TIMINGS: ReadonlySet<InterceptorTiming> = STICKY_CANCELLED_TIMINGS_DERIVED\nconst STICKY_DECISION_TIMINGS: ReadonlySet<InterceptorTiming> = STICKY_DECISION_TIMINGS_DERIVED\n\n/**\n * RFC-327 修订 D1(M1):识别 interceptor handle 返回的 abort 哨兵(`HookAbortSignal`)。\n * 形状判定:非 null 对象且 `abort === true` 才算中止——`void` / `{}` / `{ abort: false }`\n * 等价于\"无意见\"。与 `runInterceptorWithTimeout` 的返回值判定配对使用(execute 循环\n * 不直接读 hook 返回值,统一经 run 结果透传,超时/异常路径天然无 abort 信号)。\n */\nfunction isAbortSignal(value: unknown): boolean {\n return (\n typeof value === 'object' && value !== null && (value as { abort?: unknown }).abort === true\n )\n}\n\n/**\n * RFC-153 D1:interceptor hooks 挂起兜底——`execute()` 循环内此前是裸 `await hook.handle(...)`,\n * 挂起(无限循环/无限 fetch/死锁)的 hook 会永久阻塞整个 agent turn。复用 observer 路径\n * (`runObserverHookWithTimeout`)已验证的 `Promise.race` + `unref()` 模式,阈值改用两路径共享的\n * `HOOK_TIMEOUT_MS` 常量(规则 1:超时不等于中止——不承诺真正终止 hook 内部执行)。\n * 超时命中时通过 `onHookTimeout` 回调向上冒泡(规则 7:只携带领域无关字段,`packages/hooks`\n * 不认识\"插件\"概念),调用方(`packages/coding`)负责解析 hookName 里的 pluginId 前缀。\n */\nasync function runInterceptorWithTimeout<T extends InterceptorTiming>(\n hook: InterceptorRuntimeHook,\n timing: T,\n input: HookInput<T>,\n output: HookOutput<T>,\n onHookTimeout?: (info: { hookName: string; timing: string; timeoutMs: number }) => void,\n): Promise<{ ok: true; abort: boolean } | { ok: false; timedOut: boolean; message: string }> {\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n const handleResult = await Promise.race([\n hook.handle(input, output),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`hook \"${hook.name}\" timed out after ${HOOK_TIMEOUT_MS}ms`))\n }, HOOK_TIMEOUT_MS)\n timer.unref?.()\n }),\n ])\n return { ok: true, abort: isAbortSignal(handleResult) }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n const timedOut = message.includes('timed out after')\n logger.error({ name: hook.name, timing, err: message, timedOut }, 'Hook failed or timed out')\n\n if (timedOut) {\n onHookTimeout?.({ hookName: hook.name, timing, timeoutMs: HOOK_TIMEOUT_MS })\n }\n\n return { ok: false, timedOut, message }\n } finally {\n if (timer !== undefined) clearTimeout(timer)\n }\n}\n\n/**\n * RFC-143 M0:observer hook 挂起兜底。`emit()` 此前的 try/catch 只吞同步抛错和 rejected\n * promise,不吞「handler 挂起」(无限循环/无限 fetch/死锁)——`await hook.handle(input)` 会\n * 无限期阻塞调用方(如用户显式 `removeSession`、task 生命周期判定、流式启动)。\n * 超时阈值使用与 interceptor 路径(`runInterceptorWithTimeout`)共享的模块级常量\n * `HOOK_TIMEOUT_MS`(RFC-153 D1:避免两条路径各自维护一份可能漂移的数字,此前本方法\n * 独立持有的 `EMIT_TIMEOUT_MS` 已废弃合并)。`unref()` 避免这个定时器阻塞进程退出。\n */\nasync function runObserverHookWithTimeout<T extends ObserverTiming>(\n hook: ObserverRuntimeHook,\n timing: T,\n input: HookInput<T>,\n): Promise<void> {\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n hook.handle(input),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`hook \"${hook.name}\" timed out after ${HOOK_TIMEOUT_MS}ms`))\n }, HOOK_TIMEOUT_MS)\n timer.unref?.()\n }),\n ])\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error({ name: hook.name, timing, err: message }, 'Hook failed or timed out')\n } finally {\n if (timer !== undefined) clearTimeout(timer)\n }\n}\n\n/**\n * Interceptor 链执行(RFC-395 M3:从 HookRegistry.execute 拆出)。语义与拆分前完全一致:\n * - 失败/超时的 hook 走 skip 语义(fail-closed timing 除外,见 FAIL_CLOSED_TIMINGS)\n * - sticky-veto 白名单探测(RFC-088 D3,置于 abort 判定之前,RFC-327 修订 D1 交互矩阵)\n * - serial-with-early-bail:任一 hook 返回 `{ abort: true }` 终止链(RFC-327 修订 D1 M1)\n */\nexport async function executeInterceptors<T extends InterceptorTiming>(\n timing: T,\n hooks: InterceptorRuntimeHook[],\n input: HookInput<T>,\n output: HookOutput<T>,\n onHookTimeout?: (info: { hookName: string; timing: string; timeoutMs: number }) => void,\n): Promise<void> {\n if (hooks.length === 0) {\n return\n }\n\n let stickyCancelled = false\n let stickyDeny = false\n for (const hook of hooks) {\n const result = await runInterceptorWithTimeout(hook, timing, input, output, onHookTimeout)\n if (!result.ok) {\n if (FAIL_CLOSED_TIMINGS.has(timing)) {\n const out = output as { cancelled?: boolean; cancelReason?: string }\n if (typeof out.cancelled === 'boolean') {\n out.cancelled = true\n out.cancelReason = result.timedOut\n ? `hook \"${hook.name}\" timed out after ${HOOK_TIMEOUT_MS}ms`\n : `hook \"${hook.name}\" failed: ${result.message}`\n }\n }\n // 非 fail-closed timing:与既有\"hook 抛同步异常\"行为完全同构——跳过该 hook,\n // 用当前累积的 output 值继续走后续 hook(RFC-153 D1 skip 语义)。\n }\n\n // sticky-veto:仅当本 timing 的 HookPayloadMap output 确实含对应字段时探测。\n // 放在 abort 判定**之前**(RFC-327 修订 D1 交互矩阵):abort 不清 sticky——\n // 本 hook 若尝试把已钉住的 deny/cancel 翻案(如 un-deny),sticky 重钉先拦截,\n // 插件无法借 abort 哨兵绕过\"只能限制不能授权\"(R10);abort 前已钉住的值保留。\n if (STICKY_CANCELLED_TIMINGS.has(timing)) {\n const out = output as { cancelled?: boolean }\n if (out.cancelled === true) stickyCancelled = true\n if (stickyCancelled) out.cancelled = true\n }\n if (STICKY_DECISION_TIMINGS.has(timing)) {\n const out = output as { decision?: string }\n if (out.decision === 'deny') stickyDeny = true\n if (stickyDeny) out.decision = 'deny'\n }\n\n // RFC-327 修订 D1(M1):serial-with-early-bail——任一 hook 返回 `{ abort: true }`\n // 终止链:output 保留已累积值,不执行后续 hook。abort 是返回值哨兵(`HookAbortSignal`),\n // 不写 output(共享可变对象会被后续 hook 覆盖,哨兵更安全)。失败/超时(ok:false)\n // 的 hook 无 abort 信号——超时走 skip 语义,链继续。\n if (result.ok && result.abort) {\n break\n }\n }\n}\n\n/**\n * Observer 链执行(RFC-395 M3:从 HookRegistry.emit 拆出)。语义与拆分前完全一致:\n * 先对 input 做 emit-sanitize(仅 session.error 生效),再逐个执行 observer hook。\n */\nexport async function emitObservers<T extends ObserverTiming>(\n timing: T,\n hooks: ObserverRuntimeHook[],\n input: HookEmitInput<T>,\n): Promise<void> {\n if (hooks.length === 0) {\n return\n }\n\n const sanitizedInput = sanitizeEmitInput(timing, input)\n\n for (const hook of hooks) {\n await runObserverHookWithTimeout(hook, timing, sanitizedInput)\n }\n}\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nconst DEFAULT_MAX_OUTPUT_SIZE = 60 * 1024\n\nexport function createOutputTruncationHook(\n maxOutputSize: number = DEFAULT_MAX_OUTPUT_SIZE,\n): HookSpec {\n return defineHook({\n name: 'output-truncation',\n timing: 'tool.execute.after',\n priority: 10,\n handle(_input, output) {\n const { content } = output.result\n let totalSize = 0\n\n for (const part of content) {\n if (part.type === 'text') {\n totalSize += part.text.length\n }\n }\n\n if (totalSize <= maxOutputSize) {\n return\n }\n\n let remaining = maxOutputSize\n const truncatedContent = content\n .map((part) => {\n if (part.type !== 'text') {\n return part\n }\n if (remaining <= 0) {\n return { type: 'text' as const, text: '' }\n }\n if (part.text.length <= remaining) {\n remaining -= part.text.length\n return part\n }\n const truncated = part.text.slice(0, remaining)\n remaining = 0\n return {\n type: 'text' as const,\n text: `${truncated}\\n\\n... [output truncated: ${totalSize} chars → ${maxOutputSize} chars]`,\n }\n })\n .filter((part) => part.type !== 'text' || part.text.length > 0)\n\n output.result = { ...output.result, content: truncatedContent }\n output.metadata.truncated = true\n output.metadata.originalSize = totalSize\n },\n })\n}\n\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nconst MAX_LABEL_LENGTH = 200\n\nexport function createLabelTruncatorHook(): HookSpec {\n return defineHook({\n name: 'label-truncator',\n timing: 'tool.execute.before',\n priority: 20,\n handle(_input, output) {\n // RFC-415 D2:展示性字段改写走 displayArgs(非 args——args 是执行性参数不可变)。\n // 初始化 displayArgs 为 args 的浅拷贝(不改 args 本身),再截断展示性字段。\n if (!output.displayArgs) {\n output.displayArgs = { ...output.args }\n }\n for (const key of ['label', 'description', 'title']) {\n const value = output.displayArgs[key]\n if (typeof value === 'string' && value.length > MAX_LABEL_LENGTH) {\n output.displayArgs[key] = `${value.slice(0, MAX_LABEL_LENGTH)}...`\n }\n }\n },\n })\n}\n\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nexport function createThinkingValidatorHook(): HookSpec {\n return defineHook({\n name: 'thinking-validator',\n timing: 'messages.transform',\n priority: 20,\n handle(_input, output) {\n output.messages = output.messages.map((msg) => {\n if (!isAssistantMessage(msg)) {\n return msg\n }\n const content: unknown = msg.content\n if (!Array.isArray(content)) {\n return msg\n }\n\n const filtered = content.filter((part: Record<string, unknown>) => {\n if (\n part.type === 'thinking' &&\n (typeof part.thinking !== 'string' || part.thinking.trim() === '')\n ) {\n return false\n }\n return true\n })\n\n return { ...msg, content: filtered }\n })\n },\n })\n}\n\nfunction isAssistantMessage(msg: unknown): msg is { role: 'assistant'; content: unknown } {\n return typeof msg === 'object' && msg !== null && 'role' in msg && msg.role === 'assistant'\n}\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nexport function createAnthropicEffortHook(): HookSpec {\n return defineHook({\n name: 'anthropic-effort',\n timing: 'chat.params',\n priority: 10,\n handle(input, output) {\n if (input.provider !== 'anthropic') {\n return\n }\n\n if (!output.thinkingLevel) {\n // if/else if 而非独立 if:模型名理论上可能同时含多个子串(如未来的复合命名),\n // 确保只命中优先级最高的一个而非被后续分支静默覆盖(终局架构review 2026-07-21)。\n if (input.model.includes('opus')) {\n output.thinkingLevel = 'high'\n } else if (input.model.includes('sonnet')) {\n output.thinkingLevel = 'medium'\n } else if (input.model.includes('haiku')) {\n output.thinkingLevel = 'low'\n }\n }\n },\n })\n}\n\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nexport function createFirstMessageVariantHook(): HookSpec {\n return defineHook({\n name: 'first-message-variant',\n timing: 'chat.message.before',\n priority: 10,\n handle(input, output) {\n if (input.isFirstMessage) {\n output.metadata.isFirstMessage = true\n output.metadata.variant = 'first-message'\n }\n },\n })\n}\n\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nconst AI_COMMENT_PATTERNS = [\n /\\/\\/\\s*TODO:\\s*implement/i,\n /\\/\\/\\s*add\\s+(your|the)\\s+/i,\n /\\/\\/\\s*placeholder/i,\n /\\/\\/\\s*\\.\\.\\.\\s*(rest|more|other|remaining)/i,\n /\\/\\/\\s*handle\\s+error\\s+here/i,\n /\\/\\*\\s*\\*\\//,\n]\n\nexport function createCommentCheckerHook(): HookSpec {\n return defineHook({\n name: 'comment-checker',\n timing: 'tool.execute.after',\n priority: 20,\n handle(input, output) {\n // 只读工具(read/grep/find/lsp…)不产出待写代码——按注册表声明的 readonly 跳过,\n // 取代硬编码 write/edit 工具名集;非只读工具若不含代码内容由下方 extractCodeContent 兜底 no-op。\n if (input.readonly) {\n return\n }\n\n const code = extractCodeContent(input.args)\n if (!code) {\n return\n }\n\n const violations = detectAiComments(code)\n if (violations.length > 0) {\n const warning = `\\n\\n⚠️ AI-style comments detected:\\n${violations.map((v) => ` - Line ${v.line}: \"${v.text}\"`).join('\\n')}\\nPlease replace with meaningful implementation comments.`\n\n output.result = {\n ...output.result,\n content: [...output.result.content, { type: 'text', text: warning }],\n }\n output.metadata.aiCommentsDetected = violations.length\n }\n },\n })\n}\n\ninterface CommentViolation {\n line: number\n text: string\n}\n\nfunction detectAiComments(code: string): CommentViolation[] {\n const violations: CommentViolation[] = []\n const lines = code.split('\\n')\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? ''\n for (const pattern of AI_COMMENT_PATTERNS) {\n if (pattern.test(line)) {\n violations.push({ line: i + 1, text: line.trim() })\n break\n }\n }\n }\n return violations\n}\n\nfunction extractCodeContent(args: Record<string, unknown>): string | null {\n if (typeof args.content === 'string') {\n return args.content\n }\n if (typeof args.newString === 'string') {\n return args.newString\n }\n if (typeof args.new_string === 'string') {\n return args.new_string\n }\n return null\n}\n\n","import { createLogger } from '@x-otto/shared'\nimport { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\nimport type { HookState } from '../hook-state'\n\nconst log = createLogger('@x-otto/hooks:stream-metrics')\n\nexport function createStreamMetricsHook(state: HookState): HookSpec {\n return defineHook({\n name: 'stream-metrics',\n timing: 'stream.start',\n priority: 10,\n handle(input) {\n const metrics = state.streamMetrics\n metrics.set(input.sessionId, {\n startTime: Date.now(),\n model: input.model,\n requestCount: (metrics.get(input.sessionId)?.requestCount ?? 0) + 1,\n lastStopReason: '',\n totalDurationMs: metrics.get(input.sessionId)?.totalDurationMs ?? 0,\n })\n log.debug('Stream started: session=%s model=%s', input.sessionId, input.model)\n },\n })\n}\n\nexport function createStreamEndMetricsHook(state: HookState): HookSpec {\n return defineHook({\n name: 'stream-end-metrics',\n timing: 'stream.end',\n priority: 10,\n handle(input) {\n const metrics = state.streamMetrics.get(input.sessionId)\n if (metrics) {\n const duration = Date.now() - metrics.startTime\n metrics.totalDurationMs += duration\n metrics.lastStopReason = input.stopReason\n log.debug(\n 'Stream ended: session=%s model=%s duration=%dms stopReason=%s total=%dms',\n input.sessionId,\n input.model,\n duration,\n input.stopReason,\n metrics.totalDurationMs,\n )\n }\n },\n })\n}\n\n","import { createLogger } from '@x-otto/shared'\nimport { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\nimport type { HookState } from '../hook-state'\n\nconst logger = createLogger('@x-otto/hooks:compaction')\n\nexport function createCompactionLoggerHook(state: HookState): HookSpec {\n return defineHook({\n name: 'compaction-logger',\n timing: 'compaction.before',\n priority: 10,\n handle(input) {\n const stats = state.compactionStats.get(input.sessionId) ?? {\n compactionCount: 0,\n totalCompacted: 0,\n lastCompactionTime: 0,\n }\n\n stats.compactionCount++\n stats.lastCompactionTime = Date.now()\n state.compactionStats.set(input.sessionId, stats)\n\n logger.info(\n 'Compaction started: session=%s messageCount=%d compaction#%d',\n input.sessionId,\n input.messageCount,\n stats.compactionCount,\n )\n },\n })\n}\n\nexport function createCompactionAfterHook(state: HookState): HookSpec {\n return defineHook({\n name: 'compaction-after-logger',\n timing: 'compaction.after',\n priority: 10,\n handle(input) {\n const stats = state.compactionStats.get(input.sessionId)\n if (stats) {\n const duration = Date.now() - stats.lastCompactionTime\n stats.totalCompacted += input.retainedCount\n logger.info(\n 'Compaction finished: session=%s retained=%d duration=%dms totalCompactions=%d',\n input.sessionId,\n input.retainedCount,\n duration,\n stats.compactionCount,\n )\n }\n },\n })\n}\n\n","import { createLogger } from '@x-otto/shared'\nimport { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\nimport type { HookState, ToolErrorRecord } from '../hook-state'\n\nconst log = createLogger('@x-otto/hooks:tool-error-tracker')\n\nexport interface ToolErrorTrackerConfig {\n /** 默认 5。 */\n circuitBreakerThreshold?: number\n /** 默认 120_000 ms。 */\n decayWindowMs?: number\n}\n\nexport function createToolErrorTrackerHook(\n state: HookState,\n config?: ToolErrorTrackerConfig,\n): HookSpec {\n const threshold = config?.circuitBreakerThreshold ?? 5\n const decayMs = config?.decayWindowMs ?? 120_000\n\n return defineHook({\n name: 'tool-error-tracker',\n timing: 'tool.execute.after',\n priority: 15,\n handle(input, output) {\n const toolMap = state.toolErrors.get(input.sessionId) ?? new Map<string, ToolErrorRecord>()\n state.toolErrors.set(input.sessionId, toolMap)\n\n let record = toolMap.get(input.toolName)\n\n if (record && Date.now() - record.lastErrorTime > decayMs) {\n record = undefined\n }\n\n if (input.result.isError) {\n if (!record) {\n record = {\n toolName: input.toolName,\n errorCount: 0,\n lastErrorTime: 0,\n consecutiveErrors: 0,\n }\n }\n record.errorCount++\n record.consecutiveErrors++\n record.lastErrorTime = Date.now()\n toolMap.set(input.toolName, record)\n\n if (record.consecutiveErrors >= threshold) {\n const msg = `Tool \"${input.toolName}\" hit circuit-breaker: ${record.consecutiveErrors} consecutive errors`\n log.warn(msg)\n output.metadata.toolCircuitBreaker = {\n toolName: input.toolName,\n consecutiveErrors: record.consecutiveErrors,\n tripped: true,\n }\n }\n } else if (record) {\n record.consecutiveErrors = 0\n toolMap.set(input.toolName, record)\n }\n },\n })\n}\n\n","import { createLogger } from '@x-otto/shared'\nimport { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nconst log = createLogger('@x-otto/hooks:token-budget')\n\nexport interface TokenBudgetConfig {\n /** 默认 16384。 */\n maxOutputTokens?: number\n}\n\nexport function createTokenBudgetHook(config?: TokenBudgetConfig): HookSpec {\n const maxOutput = config?.maxOutputTokens ?? 16384\n\n return defineHook({\n name: 'token-budget',\n timing: 'chat.params',\n priority: 20,\n handle(input, output) {\n if (!output.maxTokens || output.maxTokens > maxOutput) {\n output.maxTokens = maxOutput\n log.debug('Capped maxTokens to %d for model=%s', maxOutput, input.model)\n }\n },\n })\n}\n\n","import { createOutputTruncationHook } from './core/tool-guard/output-truncation'\nimport { createLabelTruncatorHook } from './core/tool-guard/label-truncator'\nimport { createThinkingValidatorHook } from './core/transform/thinking-validator'\nimport { createAnthropicEffortHook } from './core/transform/anthropic-effort'\nimport { createFirstMessageVariantHook } from './core/session/first-message-variant'\nimport { createCommentCheckerHook } from './core/quality/comment-checker'\nimport { createStreamMetricsHook, createStreamEndMetricsHook } from './core/stream/stream-metrics'\nimport {\n createCompactionLoggerHook,\n createCompactionAfterHook,\n} from './core/compaction/compaction-logger'\nimport { createToolErrorTrackerHook } from './core/tool-guard/tool-error-tracker'\nimport { createTokenBudgetHook } from './core/transform/token-budget'\nimport type { HookState } from './core/hook-state'\nimport type { HookSpec } from './hook-spec'\n\nexport type HookPreset = 'default' | 'strict' | 'minimal' | 'none'\n\nexport function getPresetHooks(preset: HookPreset, state: HookState): HookSpec[] {\n switch (preset) {\n case 'default':\n return getDefaultPresetHooks(state)\n case 'strict':\n return getStrictPresetHooks(state)\n case 'minimal':\n return getMinimalPresetHooks()\n default:\n return []\n }\n}\n\nfunction getDefaultPresetHooks(state: HookState): HookSpec[] {\n // RFC-077 M3-B:删 standalone createFileGuardHook——protected-path 写保护已由权限策略引擎单源\n // (createFileGuardPolicy + permission-guard hook,含 defense-in-depth 路径解析)接管。bare-runtime\n // 经 provisionBaselineSecurity 获引擎、coding 经 PermissionController,覆盖不缩水(D6)。\n return [\n createOutputTruncationHook(),\n createLabelTruncatorHook(),\n createThinkingValidatorHook(),\n createAnthropicEffortHook(),\n createFirstMessageVariantHook(),\n createStreamMetricsHook(state),\n createStreamEndMetricsHook(state),\n createCompactionLoggerHook(state),\n createCompactionAfterHook(state),\n createToolErrorTrackerHook(state),\n createTokenBudgetHook(),\n ]\n}\n\nfunction getStrictPresetHooks(state: HookState): HookSpec[] {\n return [...getDefaultPresetHooks(state), createCommentCheckerHook()]\n}\n\nfunction getMinimalPresetHooks(): HookSpec[] {\n return [createOutputTruncationHook()]\n}\n","export interface StreamMetrics {\n startTime: number\n model: string\n requestCount: number\n lastStopReason: string\n totalDurationMs: number\n}\n\nexport interface CompactionStats {\n compactionCount: number\n totalCompacted: number\n lastCompactionTime: number\n}\n\nexport interface ToolErrorRecord {\n toolName: string\n errorCount: number\n lastErrorTime: number\n consecutiveErrors: number\n}\n\nexport class HookState {\n readonly streamMetrics = new Map<string, StreamMetrics>()\n readonly compactionStats = new Map<string, CompactionStats>()\n readonly toolErrors = new Map<string, Map<string, ToolErrorRecord>>()\n\n /** session.deleted 统一驱逐:registry 生命周期调用,杜绝 per-session 状态泄漏。 */\n clearSession(sessionId: string): void {\n this.streamMetrics.delete(sessionId)\n this.compactionStats.delete(sessionId)\n this.toolErrors.delete(sessionId)\n }\n}\n\n","import type { HookRegistry } from './hook-registry'\nimport type { HookSpec } from './hook-spec'\n\n/**\n * RFC-395 M4:scoped registration 的 disposable 契约。token 由 `HookRegistry.register()`\n * / `registerAll()` 返回,`dispose()` 精确移除该次注册(按 spec 引用),与 `unregister(name)`\n * 的按名移除互补。现有调用方(忽略返回值)行为不变。\n */\nexport interface Disposable {\n dispose(): void\n}\n\n/**\n * RFC-395 M4:具名子 scope——`HookRegistry.createScope(name)` 创建。scope 内的注册\n * 自动带 `${name}:` 前缀(与 RFC-216 插件分桶的 `plugin:${pluginId}:` 命名约定同构),\n * `dispose()` 批量撤销 scope 内全部注册。与插件分桶共存:token 优先,分桶兜底。\n */\nexport class HookScope implements Disposable {\n private readonly tokens: Disposable[] = []\n\n constructor(\n private readonly registry: HookRegistry,\n private readonly name: string,\n ) {}\n\n /** 带 `${name}:` 前缀注册;返回精确 token(dispose 只移除本次注册)。 */\n register(spec: HookSpec): Disposable {\n const prefixed: HookSpec = { ...spec, name: `${this.name}:${spec.name}` }\n const token = this.registry.register(prefixed)\n this.tokens.push(token)\n return token\n }\n\n /** 批量撤销 scope 内全部注册(token 幂等:已被外部 unregister 的 token 是 no-op)。 */\n dispose(): void {\n for (const token of this.tokens) {\n token.dispose()\n }\n this.tokens.length = 0\n }\n}\n","import { createLogger } from '@x-otto/shared'\nimport {\n type HookHandle,\n type HookInput,\n type HookEmitInput,\n type HookOutput,\n type HookTiming,\n type InterceptorTiming,\n type ObserverTiming,\n} from './types'\nimport { type HookSpec, type ObserverRuntimeHook, type InterceptorRuntimeHook, defineHook } from './hook-spec'\nimport { HOOK_TIMINGS, createHookBuckets } from './hook-nodes'\nimport { executeInterceptors, emitObservers } from './hook-executor'\nimport { getPresetHooks, type HookPreset } from './hook-preset'\nimport { HookState } from './core/hook-state'\nimport { HookScope, type Disposable } from './scope'\n\nconst logger = createLogger('@x-otto/hooks')\n\nexport { HOOK_TIMINGS }\n\nexport interface RegisteredHookInfo {\n name: string\n timing: HookTiming\n priority: number\n enabled: boolean\n}\n\nexport type { HookPreset }\n\nexport interface HookRegistryOptions {\n preset?: HookPreset\n /** RFC-153 D1:interceptor/observer hook 超时时的旁路通知(可选,宿主层注入)。\n * HookRegistry 本身不认识\"插件\"概念,只把 name/timing/timeoutMs 透传给调用方决定如何处理\n * (如写入 PluginHealthTracker)——保持 packages/hooks 对插件生命周期零认知(规则 7)。 */\n onHookTimeout?: (info: { hookName: string; timing: string; timeoutMs: number }) => void\n}\n\nexport class HookRegistry {\n private readonly hooks = createHookBuckets<HookSpec>()\n private readonly disabled = new Set<string>()\n private readonly onHookTimeout?: HookRegistryOptions['onHookTimeout']\n readonly state = new HookState()\n\n constructor(options?: HookRegistryOptions) {\n this.onHookTimeout = options?.onHookTimeout\n this.register(\n defineHook({\n name: 'hook-state-cleanup',\n timing: 'session.deleted',\n priority: 90,\n handle: (input) => {\n this.state.clearSession(input.sessionId)\n },\n }),\n )\n\n if (options?.preset && options.preset !== 'none') {\n this.applyPreset(options.preset)\n }\n }\n\n /**\n * RFC-395 M4:返回精确 token(dispose 按 spec 引用移除本次注册,不误伤同名 hook)。\n * 现有调用方忽略返回值即可,行为不变。\n */\n register(spec: HookSpec): Disposable {\n this.hooks[spec.timing].push(spec)\n\n logger.debug(\n { hookName: spec.name, timing: spec.timing, priority: spec.priority },\n 'Hook registered',\n )\n\n return {\n dispose: () => {\n const list = this.hooks[spec.timing]\n const filtered = list.filter((h) => h !== spec)\n if (filtered.length < list.length) {\n this.hooks[spec.timing] = filtered\n logger.debug({ hookName: spec.name, timing: spec.timing }, 'Hook token disposed')\n }\n },\n }\n }\n\n /** RFC-395 M4:批量注册,返回一一对应的 token 数组(与 register 同语义)。 */\n registerAll(specs: HookSpec[]): Disposable[] {\n const tokens: Disposable[] = []\n for (const spec of specs) {\n tokens.push(this.register(spec))\n }\n return tokens\n }\n\n /** RFC-395 M4:具名子 scope——scope 内注册自动带 `${name}:` 前缀,dispose 批量撤销。 */\n createScope(name: string): HookScope {\n return new HookScope(this, name)\n }\n\n on<T extends HookTiming>(timing: T, name: string, handle: HookHandle<T>, priority = 50): this {\n this.register(defineHook({ name, timing, handle, priority }))\n\n return this\n }\n\n has(name: string): boolean {\n return this.getRegistered().some((h) => h.name === name)\n }\n\n unregister(name: string): boolean {\n let removed = false\n for (const timing of HOOK_TIMINGS) {\n const list = this.hooks[timing]\n const filtered = list.filter((hook) => hook.name !== name)\n\n if (filtered.length < list.length) {\n this.hooks[timing] = filtered\n removed = true\n\n logger.debug({ hookName: name, timing }, 'Hook unregistered')\n }\n }\n return removed\n }\n\n disable(name: string): void {\n this.disabled.add(name)\n logger.debug({ hookName: name }, 'Hook disabled')\n }\n\n enable(name: string): void {\n this.disabled.delete(name)\n logger.debug({ hookName: name }, 'Hook enabled')\n }\n\n getRegistered(timing?: HookTiming): RegisteredHookInfo[] {\n if (timing) {\n return this.hooks[timing].map(toHookInfo)\n }\n\n const all: RegisteredHookInfo[] = []\n for (const key of HOOK_TIMINGS) {\n all.push(...this.hooks[key].map(toHookInfo))\n }\n\n return all\n }\n\n async execute<T extends InterceptorTiming>(\n timing: T,\n input: HookInput<T>,\n output: HookOutput<T>,\n ): Promise<void> {\n const hooks = this.getSortedInterceptorHooks(timing)\n await executeInterceptors(timing, hooks, input, output, this.onHookTimeout)\n }\n\n async emit<T extends ObserverTiming>(timing: T, input: HookEmitInput<T>): Promise<void> {\n const hooks = this.getSortedObserverHooks(timing)\n await emitObservers(timing, hooks, input)\n }\n\n clear(): void {\n for (const timing of HOOK_TIMINGS) {\n this.hooks[timing] = []\n }\n this.disabled.clear()\n logger.debug(`All hooks cleared`)\n }\n\n private getSortedInterceptorHooks(timing: InterceptorTiming): InterceptorRuntimeHook[] {\n const bucket = this.hooks[timing]\n if (!bucket) {\n logger.warn(\n { timing },\n 'Unknown hook timing (no bucket) — emit name mismatch with HOOK_NODES?',\n )\n return []\n }\n return bucket\n .filter(\n (hook): hook is InterceptorRuntimeHook =>\n hook.kind === 'interceptor' && hook.enabled && !this.disabled.has(hook.name),\n )\n .sort((a, b) => a.priority - b.priority)\n }\n\n private getSortedObserverHooks(timing: ObserverTiming): ObserverRuntimeHook[] {\n const bucket = this.hooks[timing]\n if (!bucket) {\n logger.warn(\n { timing },\n 'Unknown hook timing (no bucket) — emit name mismatch with HOOK_NODES?',\n )\n return []\n }\n return bucket\n .filter(\n (hook): hook is ObserverRuntimeHook =>\n hook.kind === 'observer' && hook.enabled && !this.disabled.has(hook.name),\n )\n .sort((a, b) => a.priority - b.priority)\n }\n\n private applyPreset(preset: HookPreset): void {\n const hooks = getPresetHooks(preset, this.state)\n for (const hook of hooks) {\n this.register(hook)\n }\n }\n}\n\nfunction toHookInfo(hook: HookSpec): RegisteredHookInfo {\n return {\n name: hook.name,\n timing: hook.timing,\n priority: hook.priority,\n enabled: hook.enabled,\n }\n}\n\nexport function createHookRegistry(options?: HookRegistryOptions): HookRegistry {\n return new HookRegistry(options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["logger","log","logger","log"],"sources":["../src/hook-schema.ts","../src/hook-nodes.ts","../src/hook-spec.ts","../src/emit-sanitize.ts","../src/hook-executor.ts","../src/core/tool-guard/output-truncation.ts","../src/core/tool-guard/label-truncator.ts","../src/core/transform/thinking-validator.ts","../src/core/transform/anthropic-effort.ts","../src/core/session/first-message-variant.ts","../src/core/quality/comment-checker.ts","../src/core/stream/stream-metrics.ts","../src/core/compaction/compaction-logger.ts","../src/core/tool-guard/tool-error-tracker.ts","../src/core/transform/token-budget.ts","../src/hook-preset.ts","../src/core/hook-state.ts","../src/scope.ts","../src/hook-registry.ts"],"sourcesContent":["/**\n * hook-schema.ts —— RFC-416 D1:从 zod schema 形状自动推导运行时表。\n *\n * 此前 hook-nodes.ts 的 HOOK_NODES(26 行手写 kind 判别)+ hook-executor.ts 的\n * STICKY_CANCELLED_TIMINGS / STICKY_DECISION_TIMINGS(2 张手写白名单)+\n * hook-nodes.ts 的 _driftGuard(6 行条件类型守卫)共 3 张手写表 + 6 处守卫,\n * 全部从此处 schema 形状自动推导——新增 timing 只改 HookPayloadMap + schema,\n * 不再四处登记。\n *\n * **为什么放 hooks 包而非 hook-contracts**:hook-contracts 是 L1 零运行时依赖\n * 叶包(只依赖 interchange L0),引入 zod 会破坏定位。hooks 包是 L2(已依赖\n * shared/env),能负担 zod 运行时依赖。遵循 interchange 已验证的\"零依赖契约层\n * + 下游自带 schema\"模式。schema 用 `satisfies` 钉死到 HookPayloadMap 类型\n * (drift → 编译报错),同 setting 的 zod 钉死模式。\n *\n * **可推导边界**:\n * - kind(observer/interceptor):output schema 为 z.void() → observer;否则 → interceptor\n * - sticky_cancelled:output schema 含 `cancelled` 字段 → 进白名单\n * - sticky_decision:output schema 含 `decision` 字段 → 进白名单\n * - FAIL_CLOSED:**不可推导**(\"失败时是否强制 cancel\"是语义概念非形状概念)→ 保留手写 1 项\n */\nimport { z } from 'zod'\nimport type { HookTiming, InterceptorTiming } from '@x-otto/hook-contracts'\n\n// ============================================================================\n// Schema 定义:每个 timing 的 output 形状\n// 用 satisfies 钉死到 HookPayloadMap[T]['output']——drift → 编译报错\n// ============================================================================\n\n/** Observer timing 的 output schema(void)。 */\nconst voidSchema = z.void()\n\n/** ChatMessageOutput shape。 */\nconst chatMessageOutputSchema = z.object({\n message: z.unknown(),\n cancelled: z.boolean(),\n metadata: z.record(z.string(), z.unknown()),\n})\n\n/** ToolExecuteBeforeOutput shape(含 displayArgs + args readonly + cancelled + decision)。 */\nconst toolExecuteBeforeOutputSchema = z.object({\n args: z.record(z.string(), z.unknown()),\n displayArgs: z.record(z.string(), z.unknown()).optional(),\n cancelled: z.boolean(),\n cancelReason: z.string().optional(),\n decision: z.enum(['allow', 'deny', 'ask']).optional(),\n decisionReason: z.string().optional(),\n risk: z.enum(['low', 'medium', 'high']).optional(),\n})\n\n/** ToolExecuteAfterOutput shape。 */\nconst toolExecuteAfterOutputSchema = z.object({\n result: z.unknown(),\n metadata: z.record(z.string(), z.unknown()),\n})\n\n/** MessagesTransformOutput shape。 */\nconst messagesTransformOutputSchema = z.object({\n messages: z.array(z.unknown()),\n})\n\n/** ChatParamsOutput shape。 */\nconst chatParamsOutputSchema = z.object({\n temperature: z.number().optional(),\n maxTokens: z.number().optional(),\n thinkingLevel: z.string().optional(),\n metadata: z.record(z.string(), z.unknown()),\n})\n\n/** SystemPromptTransformOutput shape。 */\nconst systemPromptTransformOutputSchema = z.object({\n systemPrompt: z.string(),\n systemTail: z.array(z.string()).optional(),\n})\n\n/** MemoryPruneBeforeOutput shape(含 cancelled)。 */\nconst memoryPruneBeforeOutputSchema = z.object({\n cancelled: z.boolean(),\n})\n\n// ============================================================================\n// Schema Map:timing → output schema\n// ============================================================================\n\n/**\n * Timing → output schema 映射。这是运行时推导的唯一真源——\n * 新增 timing 只需在此加一行 schema + HookPayloadMap 加一行类型,两处 satisfies 钉死保证一致。\n *\n * void timings(observer)用 voidSchema;非 void timings(interceptor)用具体 schema。\n */\nexport const HOOK_OUTPUT_SCHEMAS = {\n 'chat.message.before': chatMessageOutputSchema,\n 'chat.message.after': chatMessageOutputSchema,\n 'tool.execute.before': toolExecuteBeforeOutputSchema,\n 'tool.execute.after': toolExecuteAfterOutputSchema,\n 'messages.transform': messagesTransformOutputSchema,\n 'chat.params': chatParamsOutputSchema,\n 'system.prompt.transform': systemPromptTransformOutputSchema,\n 'session.created': voidSchema,\n 'session.restored': voidSchema,\n 'session.deleted': voidSchema,\n 'session.idle': voidSchema,\n 'session.error': voidSchema,\n 'stream.start': voidSchema,\n 'stream.end': voidSchema,\n 'compaction.before': voidSchema,\n 'compaction.after': voidSchema,\n 'memory.prune.before': memoryPruneBeforeOutputSchema,\n 'process.spawned': voidSchema,\n 'process.exited': voidSchema,\n 'task.created': voidSchema,\n 'task.started': voidSchema,\n 'task.completed': voidSchema,\n 'task.failed': voidSchema,\n 'task.cancelled': voidSchema,\n 'notification': voidSchema,\n} as const satisfies Record<HookTiming, z.ZodType<any>>\n\n// 编译期门:schema map 的 key 集合与 HookTiming 一致(漏一个 → 编译报错)\n// 用 Record<HookTiming, ...> 确保所有 timing 都有 schema\nconst _schemaKeyGate: Record<HookTiming, z.ZodType<any>> = HOOK_OUTPUT_SCHEMAS\nvoid _schemaKeyGate\n\n// ============================================================================\n// 从 schema 形状自动推导运行时表\n// ============================================================================\n\n/** 判断 schema 是否为 void(observer timing)。 */\nfunction isVoidSchema(schema: z.ZodTypeAny): boolean {\n // B6 修正(评审建议优化):用公开 API 判别,不反射 zod v4 内部 def 形状——\n // 内部结构升级可能静默改变 def.type(interceptor 变 observer 且无编译/运行时\n // 报错)。z.ZodVoid 是 zod v4 公开导出类。\n return schema instanceof z.ZodVoid\n}\n\n/** 判断 schema 是否含某个字段(用于 sticky 白名单推导)。 */\nfunction schemaHasField(schema: z.ZodTypeAny, field: string): boolean {\n if (isVoidSchema(schema)) return false\n // B6 修正:优先走公开 API——zod v4 的 object schema 提供 shape()(Record 形状)。\n // def.shape 内部结构反射仅作兼容兜底(zod 小版本内 def 形状未变时等价)。\n const shape = (schema as { def?: { shape?: Record<string, unknown> } }).def?.shape\n return shape !== undefined && field in shape\n}\n\n/** 推导的 HOOK_TIMINGS(全集,替代手写 HOOK_NODES.map(n => n.id))。 */\nexport const HOOK_TIMINGS_DERIVED: readonly HookTiming[] = Object.keys(HOOK_OUTPUT_SCHEMAS) as HookTiming[]\n\n/** 推导的 kind 判别(替代手写 HOOK_NODES 的 kind 字段)。 */\nexport function getHookKind(timing: HookTiming): 'observer' | 'interceptor' {\n return isVoidSchema(HOOK_OUTPUT_SCHEMAS[timing]) ? 'observer' : 'interceptor'\n}\n\n/** 推导的 INTERCEPTOR_TIMINGS(替代 hook-nodes.ts 的手写 Set)。 */\nexport const INTERCEPTOR_TIMINGS_DERIVED: ReadonlySet<HookTiming> = new Set(\n HOOK_TIMINGS_DERIVED.filter((t) => getHookKind(t) === 'interceptor'),\n)\n\n/** 推导的 OBSERVER_TIMINGS(替代手写)。 */\nexport const OBSERVER_TIMINGS_DERIVED: ReadonlySet<HookTiming> = new Set(\n HOOK_TIMINGS_DERIVED.filter((t) => getHookKind(t) === 'observer'),\n)\n\n/**\n * 推导的 STICKY_CANCELLED_TIMINGS(替代 hook-executor.ts 手写白名单)。\n * output 含 `cancelled` 字段的 interceptor timing → 进白名单。\n */\nexport const STICKY_CANCELLED_TIMINGS_DERIVED: ReadonlySet<InterceptorTiming> = new Set(\n HOOK_TIMINGS_DERIVED.filter(\n (t) => getHookKind(t) === 'interceptor' && schemaHasField(HOOK_OUTPUT_SCHEMAS[t], 'cancelled'),\n ) as InterceptorTiming[],\n)\n\n/**\n * 推导的 STICKY_DECISION_TIMINGS(替代 hook-executor.ts 手写白名单)。\n * output 含 `decision` 字段的 interceptor timing → 进白名单。\n */\nexport const STICKY_DECISION_TIMINGS_DERIVED: ReadonlySet<InterceptorTiming> = new Set(\n HOOK_TIMINGS_DERIVED.filter(\n (t) => getHookKind(t) === 'interceptor' && schemaHasField(HOOK_OUTPUT_SCHEMAS[t], 'decision'),\n ) as InterceptorTiming[],\n)\n\n/** 推导的 createHookBuckets(替代 hook-nodes.ts 手写)。 */\nexport function createHookBucketsDerived<T>(): Record<HookTiming, T[]> {\n return Object.fromEntries(\n HOOK_TIMINGS_DERIVED.map((timing) => [timing, [] as T[]]),\n ) as Record<HookTiming, T[]>\n}\n","/**\n * hook-nodes.ts —— RFC-416 D1:从 schema 形状自动推导,消灭手写 HOOK_NODES 表 + driftGuard。\n *\n * 此前 26 行手写 HOOK_NODES 表 + 6 行 _driftGuard 条件类型守卫已全部删除——\n * kind(observer/interceptor)从 hook-schema.ts 的 zod schema 形状推导\n * (output 为 void → observer,否则 → interceptor)。\n *\n * 新增 timing 只需在 HookPayloadMap 加类型 + hook-schema.ts 加 schema,\n * 两处 satisfies 钉死保证一致——不再四处登记。\n *\n * 保留的导出:HOOK_TIMINGS / INTERCEPTOR_TIMINGS / createHookBuckets 从 schema 派生。\n * HookNode 接口保留(类型层可能被引用),但不再有手写实例。\n */\nimport type { HookTiming } from '@x-otto/hook-contracts'\nimport {\n HOOK_TIMINGS_DERIVED,\n INTERCEPTOR_TIMINGS_DERIVED,\n createHookBucketsDerived,\n} from './hook-schema'\n\nexport interface HookNode {\n readonly id: HookTiming\n readonly kind: 'interceptor' | 'observer'\n}\n\nexport const HOOK_TIMINGS: readonly HookTiming[] = HOOK_TIMINGS_DERIVED\n\nexport const INTERCEPTOR_TIMINGS: ReadonlySet<HookTiming> = INTERCEPTOR_TIMINGS_DERIVED\n\nexport function createHookBuckets<T>(): Record<HookTiming, T[]> {\n return createHookBucketsDerived<T>()\n}\n","import type { HookHandle, HookTiming } from './types'\nimport { INTERCEPTOR_TIMINGS } from './hook-nodes'\nimport type { HookAbortSignal } from '@x-otto/hook-contracts'\n\ntype BaseRuntimeHook = {\n name: string\n timing: HookTiming\n priority: number\n enabled: boolean\n}\n\nexport type ObserverRuntimeHook = BaseRuntimeHook & {\n kind: 'observer'\n handle: (input: unknown) => void | Promise<void>\n}\n\nexport type InterceptorRuntimeHook = BaseRuntimeHook & {\n kind: 'interceptor'\n handle: (\n input: unknown,\n output: unknown,\n ) => void | HookAbortSignal | Promise<void | HookAbortSignal>\n}\n\nexport type HookSpec = ObserverRuntimeHook | InterceptorRuntimeHook\n\nexport { INTERCEPTOR_TIMINGS }\n\n/**\n * 所有 as 强转集中在此处:HookHandle<T> 因函数参数逆变无法自动赋给 (unknown) => void。\n * factory 函数通过 timing 字面量驱动 handle 的类型推断,调用侧零 cast。\n */\nexport function defineHook<T extends HookTiming>(config: {\n name: string\n timing: T\n handle: HookHandle<T>\n priority?: number\n enabled?: boolean\n}): HookSpec {\n const base: BaseRuntimeHook = {\n name: config.name,\n timing: config.timing,\n priority: config.priority ?? 50,\n enabled: config.enabled ?? true,\n }\n\n if (INTERCEPTOR_TIMINGS.has(config.timing)) {\n return {\n ...base,\n kind: 'interceptor',\n handle: config.handle as InterceptorRuntimeHook['handle'],\n }\n }\n\n return {\n ...base,\n kind: 'observer',\n handle: config.handle as ObserverRuntimeHook['handle'],\n }\n}\n","import type { HookEmitInput, HookInput, ObserverTiming } from './types'\n\n/**\n * RFC-143 R2a:`session.error` timing 的 input 契约上携带原始 `Error` 对象\n * (`HookPayloadMap['session.error'].input.error: Error`,见 hook-contracts/timings.ts)。\n * SDK 层(`module-wiring.ts`)已对 definePlugin 消费路径做 `error.message` 转换,但那只是\n * 单一消费方的自觉行为——emit() 是所有 observer hook(含非 SDK 注册路径,如未来的引擎内部\n * 钩子/测试工具)的唯一入口,必须在这里做结构式 sanitize,防止任何消费方读到含栈追踪/敏感\n * 路径信息的原始 Error 对象。sanitize 后的 input 只对 `session.error` 生效,其余 timing 原样传递。\n *\n * RFC-395 M3:从 HookRegistry 私有方法拆出为纯函数(emit-sanitize 职责单文件化)。\n */\nexport function sanitizeEmitInput<T extends ObserverTiming>(\n timing: T,\n input: HookEmitInput<T>,\n): HookInput<T> {\n if (timing !== 'session.error') return input as HookInput<T>\n\n const raw = input as unknown as {\n sessionId: string\n metadata: Record<string, unknown>\n error: Error | string\n }\n const sanitized = {\n sessionId: raw.sessionId,\n metadata: raw.metadata,\n error: raw.error instanceof Error ? raw.error.message : String(raw.error),\n }\n return sanitized as unknown as HookInput<T>\n}\n","import { createLogger } from '@x-otto/shared'\nimport { normalizeEnv } from '@x-otto/env'\nimport type { HookInput, HookEmitInput, HookOutput, InterceptorTiming, ObserverTiming } from './types'\nimport type { InterceptorRuntimeHook, ObserverRuntimeHook } from './hook-spec'\nimport { sanitizeEmitInput } from './emit-sanitize'\nimport { STICKY_CANCELLED_TIMINGS_DERIVED, STICKY_DECISION_TIMINGS_DERIVED } from './hook-schema'\n\nconst logger = createLogger('@x-otto/hooks')\n\nconst FAIL_CLOSED_TIMINGS = new Set<string>(['tool.execute.before'])\n\n/**\n * RFC-153 D1:interceptor 与 observer 两条 hook 执行路径共享同一个超时阈值——避免两处各自\n *维护一份可能漂移的数字。默认 5000ms 承袭 RFC-143 M0 为 observer 路径选定的值(远超正常 hook\n * 应有的毫秒级/快速 I/O 耗时,又不会误杀慢但终将完成的合法 hook)。硬编码审计 P1:插件生态\n * 下慢 hook 是用户真实调参点,经 `OTTO_HOOK_TIMEOUT_MS` 覆盖(保持单源,两条路径一并生效)。\n */\nconst HOOK_TIMEOUT_MS = normalizeEnv(process.env['OTTO_HOOK_TIMEOUT_MS'], 5000)\n\n// RFC-088 D3:sticky-veto 白名单——RFC-416 D1 从 hook-schema.ts 自动推导\n// (output 含 cancelled/decision 字段的 interceptor timing → 进白名单)。\n// 此前手写的 STICKY_CANCELLED_TIMINGS / STICKY_DECISION_TIMINGS 已删除——\n// 新增 timing 只改 HookPayloadMap + schema,白名单自动更新。\n//\n// RFC-327 修订 D1(M1)设计核对:waterfall 的 abort/中断语义**不进**本白名单——sticky 探测\n// 的前提是 output 真含 cancelled/decision 字段,而 waterfall 白名单 timing(system.prompt.\n// transform / chat.params / messages.transform)的 output 均无此二字段(timings.ts 契约);\n// abort 走返回值哨兵(`HookAbortSignal`,serial-with-early-bail),无需也不能借 sticky 表达。\n// 故本白名单维持推导结果不变——不盲探测 transform,也不为 waterfall 扩张。\nconst STICKY_CANCELLED_TIMINGS: ReadonlySet<InterceptorTiming> = STICKY_CANCELLED_TIMINGS_DERIVED\nconst STICKY_DECISION_TIMINGS: ReadonlySet<InterceptorTiming> = STICKY_DECISION_TIMINGS_DERIVED\n\n/**\n * RFC-327 修订 D1(M1):识别 interceptor handle 返回的 abort 哨兵(`HookAbortSignal`)。\n * 形状判定:非 null 对象且 `abort === true` 才算中止——`void` / `{}` / `{ abort: false }`\n * 等价于\"无意见\"。与 `runInterceptorWithTimeout` 的返回值判定配对使用(execute 循环\n * 不直接读 hook 返回值,统一经 run 结果透传,超时/异常路径天然无 abort 信号)。\n */\nfunction isAbortSignal(value: unknown): boolean {\n return (\n typeof value === 'object' && value !== null && (value as { abort?: unknown }).abort === true\n )\n}\n\n/**\n * RFC-153 D1:interceptor hooks 挂起兜底——`execute()` 循环内此前是裸 `await hook.handle(...)`,\n * 挂起(无限循环/无限 fetch/死锁)的 hook 会永久阻塞整个 agent turn。复用 observer 路径\n * (`runObserverHookWithTimeout`)已验证的 `Promise.race` + `unref()` 模式,阈值改用两路径共享的\n * `HOOK_TIMEOUT_MS` 常量(规则 1:超时不等于中止——不承诺真正终止 hook 内部执行)。\n * 超时命中时通过 `onHookTimeout` 回调向上冒泡(规则 7:只携带领域无关字段,`packages/hooks`\n * 不认识\"插件\"概念),调用方(`packages/coding`)负责解析 hookName 里的 pluginId 前缀。\n */\nasync function runInterceptorWithTimeout<T extends InterceptorTiming>(\n hook: InterceptorRuntimeHook,\n timing: T,\n input: HookInput<T>,\n output: HookOutput<T>,\n onHookTimeout?: (info: { hookName: string; timing: string; timeoutMs: number }) => void,\n): Promise<{ ok: true; abort: boolean } | { ok: false; timedOut: boolean; message: string }> {\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n const handleResult = await Promise.race([\n hook.handle(input, output),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n // B7 修正(评审建议优化):超时用**显式 sentinel 类型**判定,不再解析错误\n // 消息字符串——hook 自身抛含 \"timed out after\" 子串的错误会被误判为超时\n // (触发 onHookTimeout + fail-closed 走超时文案)。\n reject(new HookTimeoutError(hook.name))\n }, HOOK_TIMEOUT_MS)\n timer.unref?.()\n }),\n ])\n return { ok: true, abort: isAbortSignal(handleResult) }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n const timedOut = error instanceof HookTimeoutError\n logger.error({ name: hook.name, timing, err: message, timedOut }, 'Hook failed or timed out')\n\n if (timedOut) {\n onHookTimeout?.({ hookName: hook.name, timing, timeoutMs: HOOK_TIMEOUT_MS })\n }\n\n return { ok: false, timedOut, message }\n } finally {\n if (timer !== undefined) clearTimeout(timer)\n }\n}\n\n/** B7:hook 执行超时的显式哨兵错误(`instanceof` 判定,不解析消息文本)。 */\nexport class HookTimeoutError extends Error {\n constructor(hookName: string) {\n super(`hook \"${hookName}\" timed out after ${HOOK_TIMEOUT_MS}ms`)\n this.name = 'HookTimeoutError'\n }\n}\n\n/**\n * RFC-143 M0:observer hook 挂起兜底。`emit()` 此前的 try/catch 只吞同步抛错和 rejected\n * promise,不吞「handler 挂起」(无限循环/无限 fetch/死锁)——`await hook.handle(input)` 会\n * 无限期阻塞调用方(如用户显式 `removeSession`、task 生命周期判定、流式启动)。\n * 超时阈值使用与 interceptor 路径(`runInterceptorWithTimeout`)共享的模块级常量\n * `HOOK_TIMEOUT_MS`(RFC-153 D1:避免两条路径各自维护一份可能漂移的数字,此前本方法\n * 独立持有的 `EMIT_TIMEOUT_MS` 已废弃合并)。`unref()` 避免这个定时器阻塞进程退出。\n */\nasync function runObserverHookWithTimeout<T extends ObserverTiming>(\n hook: ObserverRuntimeHook,\n timing: T,\n input: HookInput<T>,\n): Promise<void> {\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n await Promise.race([\n hook.handle(input),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`hook \"${hook.name}\" timed out after ${HOOK_TIMEOUT_MS}ms`))\n }, HOOK_TIMEOUT_MS)\n timer.unref?.()\n }),\n ])\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error({ name: hook.name, timing, err: message }, 'Hook failed or timed out')\n } finally {\n if (timer !== undefined) clearTimeout(timer)\n }\n}\n\n/**\n * Interceptor 链执行(RFC-395 M3:从 HookRegistry.execute 拆出)。语义与拆分前完全一致:\n * - 失败/超时的 hook 走 skip 语义(fail-closed timing 除外,见 FAIL_CLOSED_TIMINGS)\n * - sticky-veto 白名单探测(RFC-088 D3,置于 abort 判定之前,RFC-327 修订 D1 交互矩阵)\n * - serial-with-early-bail:任一 hook 返回 `{ abort: true }` 终止链(RFC-327 修订 D1 M1)\n */\nexport async function executeInterceptors<T extends InterceptorTiming>(\n timing: T,\n hooks: InterceptorRuntimeHook[],\n input: HookInput<T>,\n output: HookOutput<T>,\n onHookTimeout?: (info: { hookName: string; timing: string; timeoutMs: number }) => void,\n): Promise<void> {\n if (hooks.length === 0) {\n return\n }\n\n let stickyCancelled = false\n let stickyDeny = false\n for (const hook of hooks) {\n const result = await runInterceptorWithTimeout(hook, timing, input, output, onHookTimeout)\n if (!result.ok) {\n if (FAIL_CLOSED_TIMINGS.has(timing)) {\n const out = output as { cancelled?: boolean; cancelReason?: string }\n if (typeof out.cancelled === 'boolean') {\n out.cancelled = true\n out.cancelReason = result.timedOut\n ? `hook \"${hook.name}\" timed out after ${HOOK_TIMEOUT_MS}ms`\n : `hook \"${hook.name}\" failed: ${result.message}`\n }\n }\n // 非 fail-closed timing:与既有\"hook 抛同步异常\"行为完全同构——跳过该 hook,\n // 用当前累积的 output 值继续走后续 hook(RFC-153 D1 skip 语义)。\n }\n\n // sticky-veto:仅当本 timing 的 HookPayloadMap output 确实含对应字段时探测。\n // 放在 abort 判定**之前**(RFC-327 修订 D1 交互矩阵):abort 不清 sticky——\n // 本 hook 若尝试把已钉住的 deny/cancel 翻案(如 un-deny),sticky 重钉先拦截,\n // 插件无法借 abort 哨兵绕过\"只能限制不能授权\"(R10);abort 前已钉住的值保留。\n if (STICKY_CANCELLED_TIMINGS.has(timing)) {\n const out = output as { cancelled?: boolean }\n if (out.cancelled === true) stickyCancelled = true\n if (stickyCancelled) out.cancelled = true\n }\n if (STICKY_DECISION_TIMINGS.has(timing)) {\n const out = output as { decision?: string }\n if (out.decision === 'deny') stickyDeny = true\n if (stickyDeny) out.decision = 'deny'\n }\n\n // RFC-327 修订 D1(M1):serial-with-early-bail——任一 hook 返回 `{ abort: true }`\n // 终止链:output 保留已累积值,不执行后续 hook。abort 是返回值哨兵(`HookAbortSignal`),\n // 不写 output(共享可变对象会被后续 hook 覆盖,哨兵更安全)。失败/超时(ok:false)\n // 的 hook 无 abort 信号——超时走 skip 语义,链继续。\n if (result.ok && result.abort) {\n break\n }\n }\n}\n\n/**\n * Observer 链执行(RFC-395 M3:从 HookRegistry.emit 拆出)。语义与拆分前完全一致:\n * 先对 input 做 emit-sanitize(仅 session.error 生效),再逐个执行 observer hook。\n */\nexport async function emitObservers<T extends ObserverTiming>(\n timing: T,\n hooks: ObserverRuntimeHook[],\n input: HookEmitInput<T>,\n): Promise<void> {\n if (hooks.length === 0) {\n return\n }\n\n const sanitizedInput = sanitizeEmitInput(timing, input)\n\n for (const hook of hooks) {\n await runObserverHookWithTimeout(hook, timing, sanitizedInput)\n }\n}\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nconst DEFAULT_MAX_OUTPUT_SIZE = 60 * 1024\n\nexport function createOutputTruncationHook(\n maxOutputSize: number = DEFAULT_MAX_OUTPUT_SIZE,\n): HookSpec {\n return defineHook({\n name: 'output-truncation',\n timing: 'tool.execute.after',\n priority: 10,\n handle(_input, output) {\n const { content } = output.result\n let totalSize = 0\n\n for (const part of content) {\n if (part.type === 'text') {\n totalSize += part.text.length\n }\n }\n\n if (totalSize <= maxOutputSize) {\n return\n }\n\n let remaining = maxOutputSize\n const truncatedContent = content\n .map((part) => {\n if (part.type !== 'text') {\n return part\n }\n if (remaining <= 0) {\n return { type: 'text' as const, text: '' }\n }\n if (part.text.length <= remaining) {\n remaining -= part.text.length\n return part\n }\n const truncated = part.text.slice(0, remaining)\n remaining = 0\n return {\n type: 'text' as const,\n text: `${truncated}\\n\\n... [output truncated: ${totalSize} chars → ${maxOutputSize} chars]`,\n }\n })\n .filter((part) => part.type !== 'text' || part.text.length > 0)\n\n output.result = { ...output.result, content: truncatedContent }\n output.metadata.truncated = true\n output.metadata.originalSize = totalSize\n },\n })\n}\n\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nconst MAX_LABEL_LENGTH = 200\n\nexport function createLabelTruncatorHook(): HookSpec {\n return defineHook({\n name: 'label-truncator',\n timing: 'tool.execute.before',\n priority: 20,\n handle(_input, output) {\n // RFC-415 D2:展示性字段改写走 displayArgs(非 args——args 是执行性参数不可变)。\n // 初始化 displayArgs 为 args 的浅拷贝(不改 args 本身),再截断展示性字段。\n if (!output.displayArgs) {\n output.displayArgs = { ...output.args }\n }\n for (const key of ['label', 'description', 'title']) {\n const value = output.displayArgs[key]\n if (typeof value === 'string' && value.length > MAX_LABEL_LENGTH) {\n output.displayArgs[key] = `${value.slice(0, MAX_LABEL_LENGTH)}...`\n }\n }\n },\n })\n}\n\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nexport function createThinkingValidatorHook(): HookSpec {\n return defineHook({\n name: 'thinking-validator',\n timing: 'messages.transform',\n priority: 20,\n handle(_input, output) {\n output.messages = output.messages.map((msg) => {\n if (!isAssistantMessage(msg)) {\n return msg\n }\n const content: unknown = msg.content\n if (!Array.isArray(content)) {\n return msg\n }\n\n const filtered = content.filter((part: Record<string, unknown>) => {\n // RFC-446 M1(T446-12):字段名修复——本 hook 作用于内部 AgentMessage(thinking\n // block 字段为 `text`,见 interchange/src/message.ts ThinkingContent),此前误用\n // wire 形态字段名 `thinking`(恒 undefined),过滤条件恒真,空 thinking block\n // 从未被剥离,防御从上线起即零效死代码。跨模型切换时这是第一道防线。\n if (\n part.type === 'thinking' &&\n (typeof part.text !== 'string' || part.text.trim() === '')\n ) {\n return false\n }\n return true\n })\n\n return { ...msg, content: filtered }\n })\n },\n })\n}\n\nfunction isAssistantMessage(msg: unknown): msg is { role: 'assistant'; content: unknown } {\n return typeof msg === 'object' && msg !== null && 'role' in msg && msg.role === 'assistant'\n}\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nexport function createAnthropicEffortHook(): HookSpec {\n return defineHook({\n name: 'anthropic-effort',\n timing: 'chat.params',\n priority: 10,\n handle(input, output) {\n if (input.provider !== 'anthropic') {\n return\n }\n\n if (!output.thinkingLevel) {\n // if/else if 而非独立 if:模型名理论上可能同时含多个子串(如未来的复合命名),\n // 确保只命中优先级最高的一个而非被后续分支静默覆盖(终局架构review 2026-07-21)。\n if (input.model.includes('opus')) {\n output.thinkingLevel = 'high'\n } else if (input.model.includes('sonnet')) {\n output.thinkingLevel = 'medium'\n } else if (input.model.includes('haiku')) {\n output.thinkingLevel = 'low'\n }\n }\n },\n })\n}\n\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nexport function createFirstMessageVariantHook(): HookSpec {\n return defineHook({\n name: 'first-message-variant',\n timing: 'chat.message.before',\n priority: 10,\n handle(input, output) {\n if (input.isFirstMessage) {\n output.metadata.isFirstMessage = true\n output.metadata.variant = 'first-message'\n }\n },\n })\n}\n\n","import { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nconst AI_COMMENT_PATTERNS = [\n /\\/\\/\\s*TODO:\\s*implement/i,\n /\\/\\/\\s*add\\s+(your|the)\\s+/i,\n /\\/\\/\\s*placeholder/i,\n /\\/\\/\\s*\\.\\.\\.\\s*(rest|more|other|remaining)/i,\n /\\/\\/\\s*handle\\s+error\\s+here/i,\n /\\/\\*\\s*\\*\\//,\n]\n\nexport function createCommentCheckerHook(): HookSpec {\n return defineHook({\n name: 'comment-checker',\n timing: 'tool.execute.after',\n priority: 20,\n handle(input, output) {\n // 只读工具(read/grep/find/lsp…)不产出待写代码——按注册表声明的 readonly 跳过,\n // 取代硬编码 write/edit 工具名集;非只读工具若不含代码内容由下方 extractCodeContent 兜底 no-op。\n if (input.readonly) {\n return\n }\n\n const code = extractCodeContent(input.args)\n if (!code) {\n return\n }\n\n const violations = detectAiComments(code)\n if (violations.length > 0) {\n const warning = `\\n\\n⚠️ AI-style comments detected:\\n${violations.map((v) => ` - Line ${v.line}: \"${v.text}\"`).join('\\n')}\\nPlease replace with meaningful implementation comments.`\n\n output.result = {\n ...output.result,\n content: [...output.result.content, { type: 'text', text: warning }],\n }\n output.metadata.aiCommentsDetected = violations.length\n }\n },\n })\n}\n\ninterface CommentViolation {\n line: number\n text: string\n}\n\nfunction detectAiComments(code: string): CommentViolation[] {\n const violations: CommentViolation[] = []\n const lines = code.split('\\n')\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? ''\n for (const pattern of AI_COMMENT_PATTERNS) {\n if (pattern.test(line)) {\n violations.push({ line: i + 1, text: line.trim() })\n break\n }\n }\n }\n return violations\n}\n\nfunction extractCodeContent(args: Record<string, unknown>): string | null {\n if (typeof args.content === 'string') {\n return args.content\n }\n if (typeof args.newString === 'string') {\n return args.newString\n }\n if (typeof args.new_string === 'string') {\n return args.new_string\n }\n return null\n}\n\n","import { createLogger } from '@x-otto/shared'\nimport { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\nimport type { HookState } from '../hook-state'\n\nconst log = createLogger('@x-otto/hooks:stream-metrics')\n\nexport function createStreamMetricsHook(state: HookState): HookSpec {\n return defineHook({\n name: 'stream-metrics',\n timing: 'stream.start',\n priority: 10,\n handle(input) {\n const metrics = state.streamMetrics\n metrics.set(input.sessionId, {\n startTime: Date.now(),\n model: input.model,\n requestCount: (metrics.get(input.sessionId)?.requestCount ?? 0) + 1,\n lastStopReason: '',\n totalDurationMs: metrics.get(input.sessionId)?.totalDurationMs ?? 0,\n })\n log.debug('Stream started: session=%s model=%s', input.sessionId, input.model)\n },\n })\n}\n\nexport function createStreamEndMetricsHook(state: HookState): HookSpec {\n return defineHook({\n name: 'stream-end-metrics',\n timing: 'stream.end',\n priority: 10,\n handle(input) {\n const metrics = state.streamMetrics.get(input.sessionId)\n if (metrics) {\n const duration = Date.now() - metrics.startTime\n metrics.totalDurationMs += duration\n metrics.lastStopReason = input.stopReason\n log.debug(\n 'Stream ended: session=%s model=%s duration=%dms stopReason=%s total=%dms',\n input.sessionId,\n input.model,\n duration,\n input.stopReason,\n metrics.totalDurationMs,\n )\n }\n },\n })\n}\n\n","import { createLogger } from '@x-otto/shared'\nimport { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\nimport type { HookState } from '../hook-state'\n\nconst logger = createLogger('@x-otto/hooks:compaction')\n\nexport function createCompactionLoggerHook(state: HookState): HookSpec {\n return defineHook({\n name: 'compaction-logger',\n timing: 'compaction.before',\n priority: 10,\n handle(input) {\n const stats = state.compactionStats.get(input.sessionId) ?? {\n compactionCount: 0,\n totalCompacted: 0,\n lastCompactionTime: 0,\n }\n\n stats.compactionCount++\n stats.lastCompactionTime = Date.now()\n state.compactionStats.set(input.sessionId, stats)\n\n logger.info(\n 'Compaction started: session=%s messageCount=%d compaction#%d',\n input.sessionId,\n input.messageCount,\n stats.compactionCount,\n )\n },\n })\n}\n\nexport function createCompactionAfterHook(state: HookState): HookSpec {\n return defineHook({\n name: 'compaction-after-logger',\n timing: 'compaction.after',\n priority: 10,\n handle(input) {\n const stats = state.compactionStats.get(input.sessionId)\n if (stats) {\n const duration = Date.now() - stats.lastCompactionTime\n stats.totalCompacted += input.retainedCount\n logger.info(\n 'Compaction finished: session=%s retained=%d duration=%dms totalCompactions=%d',\n input.sessionId,\n input.retainedCount,\n duration,\n stats.compactionCount,\n )\n }\n },\n })\n}\n\n","import { createLogger } from '@x-otto/shared'\nimport { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\nimport type { HookState, ToolErrorRecord } from '../hook-state'\n\nconst log = createLogger('@x-otto/hooks:tool-error-tracker')\n\nexport interface ToolErrorTrackerConfig {\n /** 默认 5。 */\n circuitBreakerThreshold?: number\n /** 默认 120_000 ms。 */\n decayWindowMs?: number\n}\n\nexport function createToolErrorTrackerHook(\n state: HookState,\n config?: ToolErrorTrackerConfig,\n): HookSpec {\n const threshold = config?.circuitBreakerThreshold ?? 5\n const decayMs = config?.decayWindowMs ?? 120_000\n\n return defineHook({\n name: 'tool-error-tracker',\n timing: 'tool.execute.after',\n priority: 15,\n handle(input, output) {\n const toolMap = state.toolErrors.get(input.sessionId) ?? new Map<string, ToolErrorRecord>()\n state.toolErrors.set(input.sessionId, toolMap)\n\n let record = toolMap.get(input.toolName)\n\n if (record && Date.now() - record.lastErrorTime > decayMs) {\n record = undefined\n }\n\n if (input.result.isError) {\n if (!record) {\n record = {\n toolName: input.toolName,\n errorCount: 0,\n lastErrorTime: 0,\n consecutiveErrors: 0,\n }\n }\n record.errorCount++\n record.consecutiveErrors++\n record.lastErrorTime = Date.now()\n toolMap.set(input.toolName, record)\n\n if (record.consecutiveErrors >= threshold) {\n const msg = `Tool \"${input.toolName}\" hit circuit-breaker: ${record.consecutiveErrors} consecutive errors`\n log.warn(msg)\n output.metadata.toolCircuitBreaker = {\n toolName: input.toolName,\n consecutiveErrors: record.consecutiveErrors,\n tripped: true,\n }\n }\n } else if (record) {\n record.consecutiveErrors = 0\n toolMap.set(input.toolName, record)\n }\n },\n })\n}\n\n","import { createLogger } from '@x-otto/shared'\nimport { defineHook } from '../../hook-spec'\nimport type { HookSpec } from '../../hook-spec'\n\nconst log = createLogger('@x-otto/hooks:token-budget')\n\nexport interface TokenBudgetConfig {\n /** 默认 16384。 */\n maxOutputTokens?: number\n}\n\nexport function createTokenBudgetHook(config?: TokenBudgetConfig): HookSpec {\n const maxOutput = config?.maxOutputTokens ?? 16384\n\n return defineHook({\n name: 'token-budget',\n timing: 'chat.params',\n priority: 20,\n handle(input, output) {\n if (!output.maxTokens || output.maxTokens > maxOutput) {\n output.maxTokens = maxOutput\n log.debug('Capped maxTokens to %d for model=%s', maxOutput, input.model)\n }\n },\n })\n}\n\n","import { createOutputTruncationHook } from './core/tool-guard/output-truncation'\nimport { createLabelTruncatorHook } from './core/tool-guard/label-truncator'\nimport { createThinkingValidatorHook } from './core/transform/thinking-validator'\nimport { createAnthropicEffortHook } from './core/transform/anthropic-effort'\nimport { createFirstMessageVariantHook } from './core/session/first-message-variant'\nimport { createCommentCheckerHook } from './core/quality/comment-checker'\nimport { createStreamMetricsHook, createStreamEndMetricsHook } from './core/stream/stream-metrics'\nimport {\n createCompactionLoggerHook,\n createCompactionAfterHook,\n} from './core/compaction/compaction-logger'\nimport { createToolErrorTrackerHook } from './core/tool-guard/tool-error-tracker'\nimport { createTokenBudgetHook } from './core/transform/token-budget'\nimport type { HookState } from './core/hook-state'\nimport type { HookSpec } from './hook-spec'\n\nexport type HookPreset = 'default' | 'strict' | 'minimal' | 'none'\n\nexport function getPresetHooks(preset: HookPreset, state: HookState): HookSpec[] {\n switch (preset) {\n case 'default':\n return getDefaultPresetHooks(state)\n case 'strict':\n return getStrictPresetHooks(state)\n case 'minimal':\n return getMinimalPresetHooks()\n default:\n return []\n }\n}\n\nfunction getDefaultPresetHooks(state: HookState): HookSpec[] {\n // RFC-077 M3-B:删 standalone createFileGuardHook——protected-path 写保护已由权限策略引擎单源\n // (createFileGuardPolicy + permission-guard hook,含 defense-in-depth 路径解析)接管。bare-runtime\n // 经 provisionBaselineSecurity 获引擎、coding 经 PermissionController,覆盖不缩水(D6)。\n return [\n createOutputTruncationHook(),\n createLabelTruncatorHook(),\n createThinkingValidatorHook(),\n createAnthropicEffortHook(),\n createFirstMessageVariantHook(),\n createStreamMetricsHook(state),\n createStreamEndMetricsHook(state),\n createCompactionLoggerHook(state),\n createCompactionAfterHook(state),\n createToolErrorTrackerHook(state),\n createTokenBudgetHook(),\n ]\n}\n\nfunction getStrictPresetHooks(state: HookState): HookSpec[] {\n return [...getDefaultPresetHooks(state), createCommentCheckerHook()]\n}\n\nfunction getMinimalPresetHooks(): HookSpec[] {\n return [createOutputTruncationHook()]\n}\n","export interface StreamMetrics {\n startTime: number\n model: string\n requestCount: number\n lastStopReason: string\n totalDurationMs: number\n}\n\nexport interface CompactionStats {\n compactionCount: number\n totalCompacted: number\n lastCompactionTime: number\n}\n\nexport interface ToolErrorRecord {\n toolName: string\n errorCount: number\n lastErrorTime: number\n consecutiveErrors: number\n}\n\nexport class HookState {\n readonly streamMetrics = new Map<string, StreamMetrics>()\n readonly compactionStats = new Map<string, CompactionStats>()\n readonly toolErrors = new Map<string, Map<string, ToolErrorRecord>>()\n\n /** session.deleted 统一驱逐:registry 生命周期调用,杜绝 per-session 状态泄漏。 */\n clearSession(sessionId: string): void {\n this.streamMetrics.delete(sessionId)\n this.compactionStats.delete(sessionId)\n this.toolErrors.delete(sessionId)\n }\n}\n\n","import type { HookRegistry } from './hook-registry'\nimport type { HookSpec } from './hook-spec'\n\n/**\n * RFC-395 M4:scoped registration 的 disposable 契约。token 由 `HookRegistry.register()`\n * / `registerAll()` 返回,`dispose()` 精确移除该次注册(按 spec 引用),与 `unregister(name)`\n * 的按名移除互补。现有调用方(忽略返回值)行为不变。\n */\nexport interface Disposable {\n dispose(): void\n}\n\n/**\n * RFC-395 M4:具名子 scope——`HookRegistry.createScope(name)` 创建。scope 内的注册\n * 自动带 `${name}:` 前缀(与 RFC-216 插件分桶的 `plugin:${pluginId}:` 命名约定同构),\n * `dispose()` 批量撤销 scope 内全部注册。与插件分桶共存:token 优先,分桶兜底。\n */\nexport class HookScope implements Disposable {\n private readonly tokens: Disposable[] = []\n\n constructor(\n private readonly registry: HookRegistry,\n private readonly name: string,\n ) {}\n\n /** 带 `${name}:` 前缀注册;返回精确 token(dispose 只移除本次注册)。 */\n register(spec: HookSpec): Disposable {\n const prefixed: HookSpec = { ...spec, name: `${this.name}:${spec.name}` }\n const token = this.registry.register(prefixed)\n this.tokens.push(token)\n return token\n }\n\n /** 批量撤销 scope 内全部注册(token 幂等:已被外部 unregister 的 token 是 no-op)。 */\n dispose(): void {\n for (const token of this.tokens) {\n token.dispose()\n }\n this.tokens.length = 0\n }\n}\n","import { createLogger } from '@x-otto/shared'\nimport {\n type HookHandle,\n type HookInput,\n type HookEmitInput,\n type HookOutput,\n type HookTiming,\n type InterceptorTiming,\n type ObserverTiming,\n} from './types'\nimport { type HookSpec, type ObserverRuntimeHook, type InterceptorRuntimeHook, defineHook } from './hook-spec'\nimport { HOOK_TIMINGS, createHookBuckets } from './hook-nodes'\nimport { executeInterceptors, emitObservers } from './hook-executor'\nimport { getPresetHooks, type HookPreset } from './hook-preset'\nimport { HookState } from './core/hook-state'\nimport { HookScope, type Disposable } from './scope'\n\nconst logger = createLogger('@x-otto/hooks')\n\nexport { HOOK_TIMINGS }\n\nexport interface RegisteredHookInfo {\n name: string\n timing: HookTiming\n priority: number\n enabled: boolean\n}\n\nexport type { HookPreset }\n\nexport interface HookRegistryOptions {\n preset?: HookPreset\n /** RFC-153 D1:interceptor/observer hook 超时时的旁路通知(可选,宿主层注入)。\n * HookRegistry 本身不认识\"插件\"概念,只把 name/timing/timeoutMs 透传给调用方决定如何处理\n * (如写入 PluginHealthTracker)——保持 packages/hooks 对插件生命周期零认知(规则 7)。 */\n onHookTimeout?: (info: { hookName: string; timing: string; timeoutMs: number }) => void\n}\n\nexport class HookRegistry {\n private readonly hooks = createHookBuckets<HookSpec>()\n private readonly disabled = new Set<string>()\n private readonly onHookTimeout?: HookRegistryOptions['onHookTimeout']\n readonly state = new HookState()\n\n constructor(options?: HookRegistryOptions) {\n this.onHookTimeout = options?.onHookTimeout\n this.register(\n defineHook({\n name: 'hook-state-cleanup',\n timing: 'session.deleted',\n priority: 90,\n handle: (input) => {\n this.state.clearSession(input.sessionId)\n },\n }),\n )\n\n if (options?.preset && options.preset !== 'none') {\n this.applyPreset(options.preset)\n }\n }\n\n /**\n * RFC-395 M4:返回精确 token(dispose 按 spec 引用移除本次注册,不误伤同名 hook)。\n * 现有调用方忽略返回值即可,行为不变。\n */\n register(spec: HookSpec): Disposable {\n this.hooks[spec.timing].push(spec)\n\n logger.debug(\n { hookName: spec.name, timing: spec.timing, priority: spec.priority },\n 'Hook registered',\n )\n\n return {\n dispose: () => {\n const list = this.hooks[spec.timing]\n const filtered = list.filter((h) => h !== spec)\n if (filtered.length < list.length) {\n this.hooks[spec.timing] = filtered\n logger.debug({ hookName: spec.name, timing: spec.timing }, 'Hook token disposed')\n }\n },\n }\n }\n\n /** RFC-395 M4:批量注册,返回一一对应的 token 数组(与 register 同语义)。 */\n registerAll(specs: HookSpec[]): Disposable[] {\n const tokens: Disposable[] = []\n for (const spec of specs) {\n tokens.push(this.register(spec))\n }\n return tokens\n }\n\n /** RFC-395 M4:具名子 scope——scope 内注册自动带 `${name}:` 前缀,dispose 批量撤销。 */\n createScope(name: string): HookScope {\n return new HookScope(this, name)\n }\n\n on<T extends HookTiming>(timing: T, name: string, handle: HookHandle<T>, priority = 50): this {\n this.register(defineHook({ name, timing, handle, priority }))\n\n return this\n }\n\n has(name: string): boolean {\n return this.getRegistered().some((h) => h.name === name)\n }\n\n unregister(name: string): boolean {\n let removed = false\n for (const timing of HOOK_TIMINGS) {\n const list = this.hooks[timing]\n const filtered = list.filter((hook) => hook.name !== name)\n\n if (filtered.length < list.length) {\n this.hooks[timing] = filtered\n removed = true\n\n logger.debug({ hookName: name, timing }, 'Hook unregistered')\n }\n }\n return removed\n }\n\n disable(name: string): void {\n this.disabled.add(name)\n logger.debug({ hookName: name }, 'Hook disabled')\n }\n\n enable(name: string): void {\n this.disabled.delete(name)\n logger.debug({ hookName: name }, 'Hook enabled')\n }\n\n getRegistered(timing?: HookTiming): RegisteredHookInfo[] {\n if (timing) {\n return this.hooks[timing].map(toHookInfo)\n }\n\n const all: RegisteredHookInfo[] = []\n for (const key of HOOK_TIMINGS) {\n all.push(...this.hooks[key].map(toHookInfo))\n }\n\n return all\n }\n\n async execute<T extends InterceptorTiming>(\n timing: T,\n input: HookInput<T>,\n output: HookOutput<T>,\n ): Promise<void> {\n const hooks = this.getSortedInterceptorHooks(timing)\n await executeInterceptors(timing, hooks, input, output, this.onHookTimeout)\n }\n\n async emit<T extends ObserverTiming>(timing: T, input: HookEmitInput<T>): Promise<void> {\n const hooks = this.getSortedObserverHooks(timing)\n await emitObservers(timing, hooks, input)\n }\n\n clear(): void {\n for (const timing of HOOK_TIMINGS) {\n this.hooks[timing] = []\n }\n this.disabled.clear()\n logger.debug(`All hooks cleared`)\n }\n\n private getSortedInterceptorHooks(timing: InterceptorTiming): InterceptorRuntimeHook[] {\n const bucket = this.hooks[timing]\n if (!bucket) {\n logger.warn(\n { timing },\n 'Unknown hook timing (no bucket) — emit name mismatch with HOOK_NODES?',\n )\n return []\n }\n return bucket\n .filter(\n (hook): hook is InterceptorRuntimeHook =>\n hook.kind === 'interceptor' && hook.enabled && !this.disabled.has(hook.name),\n )\n .sort((a, b) => a.priority - b.priority)\n }\n\n private getSortedObserverHooks(timing: ObserverTiming): ObserverRuntimeHook[] {\n const bucket = this.hooks[timing]\n if (!bucket) {\n logger.warn(\n { timing },\n 'Unknown hook timing (no bucket) — emit name mismatch with HOOK_NODES?',\n )\n return []\n }\n return bucket\n .filter(\n (hook): hook is ObserverRuntimeHook =>\n hook.kind === 'observer' && hook.enabled && !this.disabled.has(hook.name),\n )\n .sort((a, b) => a.priority - b.priority)\n }\n\n private applyPreset(preset: HookPreset): void {\n const hooks = getPresetHooks(preset, this.state)\n for (const hook of hooks) {\n this.register(hook)\n }\n }\n}\n\nfunction toHookInfo(hook: HookSpec): RegisteredHookInfo {\n return {\n name: hook.name,\n timing: hook.timing,\n priority: hook.priority,\n enabled: hook.enabled,\n }\n}\n\nexport function createHookRegistry(options?: HookRegistryOptions): HookRegistry {\n return new HookRegistry(options)\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x-otto/hooks",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0-alpha.9",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist"
|
|
6
6
|
],
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"zod": "4.3.6",
|
|
23
|
-
"@x-otto/env": "0.1.0-alpha.
|
|
24
|
-
"@x-otto/shared": "0.1.0-alpha.
|
|
25
|
-
"@x-otto/hook-contracts": "0.0
|
|
23
|
+
"@x-otto/env": "0.1.0-alpha.9",
|
|
24
|
+
"@x-otto/shared": "0.1.0-alpha.9",
|
|
25
|
+
"@x-otto/hook-contracts": "0.1.0-alpha.9"
|
|
26
26
|
},
|
|
27
27
|
"private": false,
|
|
28
28
|
"scripts": {
|