












@@ -74,6 +74,32 @@ type LockableFunction = ((...args: unknown[]) => unknown) & {
7474__openclawSessionWriteLockInstalled?: boolean;
7575};
767677+type SessionEventHookContext = {
78+session: unknown;
79+active: boolean;
80+};
81+82+const sessionEventHookContext = new AsyncLocalStorage<SessionEventHookContext>();
83+84+function isProcessingAgentEventInCurrentChain(session: unknown): boolean {
85+const context = sessionEventHookContext.getStore();
86+return context?.active === true && context.session === session;
87+}
88+89+async function withProcessingAgentEvent<T>(
90+session: unknown,
91+run: () => Promise<T> | T,
92+): Promise<T> {
93+const context: SessionEventHookContext = { session, active: true };
94+return await sessionEventHookContext.run(context, async () => {
95+try {
96+return await run();
97+} finally {
98+context.active = false;
99+}
100+});
101+}
102+77103function sessionHasExtensionHandlers(session: SessionEventProcessor, eventType: string): boolean {
78104const extensionRunner = session["_extensionRunner"];
79105const hasHandlers = extensionRunner?.hasHandlers;
@@ -662,6 +688,15 @@ async function waitForSessionEventQueue(session: unknown): Promise<void> {
662688}
663689}
664690691+async function waitForSessionEventQueueBeforeHook(session: unknown): Promise<void> {
692+// Hook calls made by _handleAgentEvent are already inside the current queue
693+// entry. Draining there waits on itself; detached/external hook work still drains.
694+if (isProcessingAgentEventInCurrentChain(session)) {
695+return;
696+}
697+await waitForSessionEventQueue(session);
698+}
699+665700function installAwaitableSessionEventQueue(session: unknown): void {
666701const owner = session as SessionEventQueueBridge;
667702const original = owner["_handleAgentEvent"];
@@ -731,10 +766,14 @@ export function installSessionEventWriteLock(params: {
731766event: unknown,
732767signal?: unknown,
733768) {
734-if (!eventMayReachTranscriptWriters(session, event)) {
735-return await original.call(this, event, signal);
736-}
737-return await params.withSessionWriteLock(async () => await original.call(this, event, signal));
769+return await withProcessingAgentEvent(session, async () => {
770+if (!eventMayReachTranscriptWriters(session, event)) {
771+return await original.call(this, event, signal);
772+}
773+return await params.withSessionWriteLock(
774+async () => await original.call(this, event, signal),
775+);
776+});
738777};
739778wrapped["__openclawSessionEventQueueAwaitInstalled"] =
740779original["__openclawSessionEventQueueAwaitInstalled"];
@@ -756,28 +795,28 @@ export function installSessionExternalHookWriteLock(params: {
756795owner: agent as Record<string, unknown>,
757796key: "beforeToolCall",
758797shouldLock: () => true,
759-waitBeforeLock: () => waitForSessionEventQueue(session),
798+waitBeforeLock: () => waitForSessionEventQueueBeforeHook(session),
760799withSessionWriteLock: params.withSessionWriteLock,
761800});
762801installLockableFunction({
763802owner: agent as Record<string, unknown>,
764803key: "afterToolCall",
765804shouldLock: () => sessionHasExtensionHandlers(session, "tool_result"),
766-waitBeforeLock: () => waitForSessionEventQueue(session),
805+waitBeforeLock: () => waitForSessionEventQueueBeforeHook(session),
767806withSessionWriteLock: params.withSessionWriteLock,
768807});
769808installLockableFunction({
770809owner: agent as Record<string, unknown>,
771810key: "onPayload",
772811shouldLock: () => sessionHasExtensionHandlers(session, "before_provider_request"),
773-waitBeforeLock: () => waitForSessionEventQueue(session),
812+waitBeforeLock: () => waitForSessionEventQueueBeforeHook(session),
774813withSessionWriteLock: params.withSessionWriteLock,
775814});
776815installLockableFunction({
777816owner: agent as Record<string, unknown>,
778817key: "onResponse",
779818shouldLock: () => sessionHasExtensionHandlers(session, "after_provider_response"),
780-waitBeforeLock: () => waitForSessionEventQueue(session),
819+waitBeforeLock: () => waitForSessionEventQueueBeforeHook(session),
781820withSessionWriteLock: params.withSessionWriteLock,
782821});
783822}
이 콘텐츠는 인셔셔RSS(RSS 리더)가 자동으로 집계한 것으로 읽기 참고용입니다. 원문 출처 — 저작권은 원저작자에게 있습니다.