Merge pull request #4203 from code-yeongyu/fix/continuation-prompt-dispatch-20260520051014
fix(plugin): dispatch synthetic idle hooks
This commit is contained in:
@@ -37,10 +37,6 @@ export function saveInjectedRules(
|
|||||||
sessionID: string,
|
sessionID: string,
|
||||||
data: { contentHashes: Set<string>; realPaths: Set<string> }
|
data: { contentHashes: Set<string>; realPaths: Set<string> }
|
||||||
): void {
|
): void {
|
||||||
if (!existsSync(RULES_INJECTOR_STORAGE)) {
|
|
||||||
mkdirSync(RULES_INJECTOR_STORAGE, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const storageData: InjectedRulesData = {
|
const storageData: InjectedRulesData = {
|
||||||
sessionID,
|
sessionID,
|
||||||
injectedHashes: [...data.contentHashes],
|
injectedHashes: [...data.contentHashes],
|
||||||
@@ -48,7 +44,16 @@ export function saveInjectedRules(
|
|||||||
updatedAt: Date.now(),
|
updatedAt: Date.now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
writeFileSync(getStoragePath(sessionID), JSON.stringify(storageData, null, 2));
|
mkdirSync(RULES_INJECTOR_STORAGE, { recursive: true });
|
||||||
|
try {
|
||||||
|
writeFileSync(getStoragePath(sessionID), JSON.stringify(storageData, null, 2));
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
mkdirSync(RULES_INJECTOR_STORAGE, { recursive: true });
|
||||||
|
writeFileSync(getStoragePath(sessionID), JSON.stringify(storageData, null, 2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearInjectedRules(sessionID: string): void {
|
export function clearInjectedRules(sessionID: string): void {
|
||||||
|
|||||||
@@ -208,6 +208,55 @@ describe("createEventHandler - idle deduplication", () => {
|
|||||||
expect(onEvent.mock.calls[0]?.[0]).toEqual(idleEvent.event)
|
expect(onEvent.mock.calls[0]?.[0]).toEqual(idleEvent.event)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("#given tmux integration enabled #when session.status reports idle #then synthetic idle forwards to tmuxSessionManager.onEvent", async () => {
|
||||||
|
//#given
|
||||||
|
const onEvent = mock<(event: EventInput["event"]) => void>(() => {})
|
||||||
|
const eventHandler = createEventHandler({
|
||||||
|
ctx: asEventHandlerContext({
|
||||||
|
directory: "/tmp",
|
||||||
|
client: {
|
||||||
|
session: {},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
pluginConfig: asPluginConfig({
|
||||||
|
tmux: { enabled: true },
|
||||||
|
}),
|
||||||
|
firstMessageVariantGate: {
|
||||||
|
markSessionCreated: () => {},
|
||||||
|
clear: () => {},
|
||||||
|
},
|
||||||
|
managers: createEventHandlerManagers({
|
||||||
|
tmuxSessionManager: {
|
||||||
|
onEvent,
|
||||||
|
onSessionCreated: async () => {},
|
||||||
|
onSessionDeleted: async () => {},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
hooks: createEventHandlerHooks({}),
|
||||||
|
})
|
||||||
|
|
||||||
|
//#when
|
||||||
|
await eventHandler(asEventHandlerInput({
|
||||||
|
event: {
|
||||||
|
type: "session.status",
|
||||||
|
properties: {
|
||||||
|
sessionID: "ses_tmux_synthetic_idle",
|
||||||
|
status: { type: "idle" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(onEvent).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onEvent.mock.calls[0]?.[0]).toEqual({
|
||||||
|
type: "session.idle",
|
||||||
|
properties: {
|
||||||
|
sessionID: "ses_tmux_synthetic_idle",
|
||||||
|
synthetic: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("#given a readiness retry is pending #when session.idle arrives through the plugin handler #then tmux retry spawns the pane", async () => {
|
it("#given a readiness retry is pending #when session.idle arrives through the plugin handler #then tmux retry spawns the pane", async () => {
|
||||||
//#given
|
//#given
|
||||||
const sessionStatusData: Record<string, { type: string }> = {}
|
const sessionStatusData: Record<string, { type: string }> = {}
|
||||||
|
|||||||
+10
-4
@@ -394,6 +394,12 @@ export function createEventHandler(args: {
|
|||||||
return hooks.sessionRecovery.handleInterruptedToolResultsOnIdle(sessionID);
|
return hooks.sessionRecovery.handleInterruptedToolResultsOnIdle(sessionID);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dispatchIdleOnlyHooks = async (input: EventInput): Promise<void> => {
|
||||||
|
managers.tmuxSessionManager?.onEvent?.(input.event);
|
||||||
|
await runEventHookSafely("teamIdleWakeHint", teamIdleWakeHint, input);
|
||||||
|
await runEventHookSafely("teamMemberStatusHandler", teamMemberStatusHandler, input);
|
||||||
|
};
|
||||||
|
|
||||||
const getFallbackContinuationKeys = (fallbackContext?: FallbackContinuationContext): FallbackContinuationDedupeKeys => {
|
const getFallbackContinuationKeys = (fallbackContext?: FallbackContinuationContext): FallbackContinuationDedupeKeys => {
|
||||||
const agentKey = fallbackContext?.agentName
|
const agentKey = fallbackContext?.agentName
|
||||||
? getAgentConfigKey(fallbackContext.agentName).trim().toLowerCase()
|
? getAgentConfigKey(fallbackContext.agentName).trim().toLowerCase()
|
||||||
@@ -628,7 +634,8 @@ export function createEventHandler(args: {
|
|||||||
if (!shouldDispatchIdleEvent(sessionID, now)) {
|
if (!shouldDispatchIdleEvent(sessionID, now)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await dispatchToHooks(syntheticIdle as EventInput);
|
const syntheticIdleInput = syntheticIdle as EventInput;
|
||||||
|
await dispatchToHooks(syntheticIdleInput);
|
||||||
if (pluginConfig.openclaw) {
|
if (pluginConfig.openclaw) {
|
||||||
await dispatchOpenClawEvent({
|
await dispatchOpenClawEvent({
|
||||||
config: pluginConfig.openclaw,
|
config: pluginConfig.openclaw,
|
||||||
@@ -640,6 +647,7 @@ export function createEventHandler(args: {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
await dispatchIdleOnlyHooks(syntheticIdleInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { event } = input;
|
const { event } = input;
|
||||||
@@ -761,9 +769,7 @@ export function createEventHandler(args: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === "session.idle") {
|
if (event.type === "session.idle") {
|
||||||
managers.tmuxSessionManager?.onEvent?.(event);
|
await dispatchIdleOnlyHooks(input);
|
||||||
await runEventHookSafely("teamIdleWakeHint", teamIdleWakeHint, input);
|
|
||||||
await runEventHookSafely("teamMemberStatusHandler", teamMemberStatusHandler, input);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === "message.updated") {
|
if (event.type === "message.updated") {
|
||||||
|
|||||||
Reference in New Issue
Block a user