@@ -7,18 +7,24 @@ import { withDefaultDeps } from '@memo/core/runtime/defaults'
77import { parseAssistant } from '@memo/core/utils/utils'
88import type { CallToolResult } from '@modelcontextprotocol/sdk/types'
99import type {
10+ ActionPayload ,
1011 AgentSession ,
1112 AgentSessionDeps ,
1213 AgentSessionOptions ,
1314 AgentStepTrace ,
1415 ChatMessage ,
16+ FinalPayload ,
1517 HistoryEvent ,
1618 HistorySink ,
19+ Hook ,
20+ HookMiddleware ,
1721 LLMResponse ,
22+ ObservationPayload ,
1823 ParsedAssistant ,
1924 SessionMode ,
2025 TokenCounter ,
2126 TokenUsage ,
27+ TurnStartPayload ,
2228 TurnResult ,
2329 TurnStatus ,
2430} from '@memo/core/types'
@@ -61,6 +67,22 @@ async function emitEventToSinks(event: HistoryEvent, sinks: HistorySink[]) {
6167 }
6268}
6369
70+ async function runHookChain < TArgs extends any [ ] > ( hook : Hook < TArgs > | undefined , ...args : TArgs ) {
71+ const middlewares = normalizeHook ( hook , args . length )
72+ if ( ! middlewares . length ) return
73+ let idx = - 1
74+ const dispatch = async ( i : number ) : Promise < void > => {
75+ if ( i <= idx ) {
76+ throw new Error ( 'hook middleware 调用顺序错误:next 被重复触发' )
77+ }
78+ idx = i
79+ const fn = middlewares [ i ]
80+ if ( ! fn ) return
81+ await fn ( ( ) => dispatch ( i + 1 ) , ...args )
82+ }
83+ await dispatch ( 0 )
84+ }
85+
6486function flattenCallToolResult ( result : CallToolResult ) : string {
6587 const texts =
6688 result . content ?. flatMap ( ( item ) => {
@@ -94,6 +116,24 @@ function parseToolInput(tool: ToolRegistry[string], rawInput: unknown) {
94116 return { ok : true as const , data : parsed . data }
95117}
96118
119+ function normalizeHook < TArgs extends any [ ] > (
120+ hook : Hook < TArgs > | undefined ,
121+ argsLength : number ,
122+ ) : HookMiddleware < TArgs > [ ] {
123+ if ( ! hook ) return [ ]
124+ const list = Array . isArray ( hook ) ? hook : [ hook ]
125+ return list . map ( ( fn ) => {
126+ // 若函数签名不包含 next,视为普通回调,执行后自动进入下一个 middleware。
127+ if ( fn . length <= argsLength ) {
128+ return async ( next : ( ) => Promise < void > , ...args : TArgs ) => {
129+ await ( fn as ( ...args : TArgs ) => unknown ) ( ...args )
130+ await next ( )
131+ }
132+ }
133+ return fn
134+ } )
135+ }
136+
97137/** 进程内的对话 Session,实现多轮运行与日志写入。 */
98138class AgentSessionImpl implements AgentSession {
99139 public id : string
@@ -154,6 +194,7 @@ class AgentSessionImpl implements AgentSession {
154194 content : input ,
155195 meta : { tokens : { prompt : promptTokens } } ,
156196 } )
197+ await runHookChain < TurnStartPayload > ( this . deps . onTurnStart , { turn, input } )
157198
158199 let finalText = ''
159200 let status : TurnStatus = 'ok'
@@ -176,6 +217,12 @@ class AgentSessionImpl implements AgentSession {
176217 role : 'assistant' ,
177218 meta : { tokens : { prompt : estimatedPrompt } } ,
178219 } )
220+ await runHookChain < FinalPayload > ( this . deps . onFinal , {
221+ turn,
222+ step,
223+ final : limitMessage ,
224+ status,
225+ } )
179226 break
180227 }
181228 if ( this . options . warnPromptTokens && estimatedPrompt > this . options . warnPromptTokens ) {
@@ -201,6 +248,11 @@ class AgentSessionImpl implements AgentSession {
201248 finalText = msg
202249 errorMessage = msg
203250 await this . emitEvent ( 'final' , { turn, content : msg , role : 'assistant' } )
251+ await runHookChain < FinalPayload > ( this . deps . onFinal , {
252+ turn,
253+ final : msg ,
254+ status,
255+ } )
204256 break
205257 }
206258
@@ -247,10 +299,22 @@ class AgentSessionImpl implements AgentSession {
247299 role : 'assistant' ,
248300 meta : { tokens : stepUsage } ,
249301 } )
302+ await runHookChain < FinalPayload > ( this . deps . onFinal , {
303+ turn,
304+ step,
305+ final : parsed . final ,
306+ status,
307+ } )
250308 break
251309 }
252310
253311 if ( parsed . action ) {
312+ await runHookChain < ActionPayload > ( this . deps . onAction , {
313+ turn,
314+ step,
315+ tool : parsed . action . tool ,
316+ input : parsed . action . input ,
317+ } )
254318 await this . emitEvent ( 'action' , {
255319 turn,
256320 step,
@@ -283,7 +347,12 @@ class AgentSessionImpl implements AgentSession {
283347 if ( lastStep ) {
284348 lastStep . observation = observation
285349 }
286- this . deps . onObservation ?.( parsed . action . tool , observation , step )
350+ await runHookChain < ObservationPayload > ( this . deps . onObservation , {
351+ turn,
352+ step,
353+ tool : parsed . action . tool ,
354+ observation,
355+ } )
287356
288357 await this . emitEvent ( 'observation' , {
289358 turn,
@@ -311,6 +380,11 @@ class AgentSessionImpl implements AgentSession {
311380 content : finalText ,
312381 role : 'assistant' ,
313382 } )
383+ await runHookChain < FinalPayload > ( this . deps . onFinal , {
384+ turn,
385+ final : finalText ,
386+ status,
387+ } )
314388 }
315389
316390 await this . emitEvent ( 'turn_end' , {
0 commit comments