This commit is contained in:
Kentaro Kuribayashi 2026-01-31 00:09:08 +08:00 committed by GitHub
commit 1b1f60ca3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 3 deletions

View File

@ -235,6 +235,15 @@ Triggered when the gateway starts:
- **`gateway:startup`**: After channels start and hooks are loaded
### Session Events
Triggered during session lifecycle:
- **`session:start`**: When a new session begins (after `/new`, `/reset`, or first message from a new sender)
- Context includes: `sessionEntry`, `sessionId`, `sessionFile`, `commandSource`, `senderId`, `isReset` (true if triggered by reset command), `cfg`
- **`session:end`**: When a session ends (before `/new` or `/reset` clears the session)
- Context includes: `sessionEntry` (previous session), `sessionId`, `sessionFile`, `commandSource`, `senderId`, `reason` ("reset"), `cfg`
### Tool Result Hooks (Plugin API)
These hooks are not event-stream listeners; they let plugins synchronously adjust tool results before OpenClaw persists them.
@ -244,9 +253,6 @@ These hooks are not event-stream listeners; they let plugins synchronously adjus
### Future Events
Planned event types:
- **`session:start`**: When a new session begins
- **`session:end`**: When a session ends
- **`agent:error`**: When an agent encounters an error
- **`message:sent`**: When a message is sent
- **`message:received`**: When a message is received

View File

@ -71,6 +71,21 @@ export async function handleCommands(params: HandleCommandsParams): Promise<Comm
// Trigger internal hook for reset/new commands
if (resetRequested && params.command.isAuthorizedSender) {
const commandAction = resetMatch?.[1] ?? "new";
// Trigger session:end for the previous session before reset
if (params.previousSessionEntry) {
const sessionEndEvent = createInternalHookEvent("session", "end", params.sessionKey ?? "", {
sessionEntry: params.previousSessionEntry,
sessionId: params.previousSessionEntry.sessionId,
sessionFile: params.previousSessionEntry.sessionFile,
commandSource: params.command.surface,
senderId: params.command.senderId,
reason: "reset", // Session ended due to /new or /reset command
cfg: params.cfg,
});
await triggerInternalHook(sessionEndEvent);
}
const hookEvent = createInternalHookEvent("command", commandAction, params.sessionKey ?? "", {
sessionEntry: params.sessionEntry,
previousSessionEntry: params.previousSessionEntry,

View File

@ -18,6 +18,7 @@ import { resolveDefaultModel } from "./directive-handling.js";
import { resolveReplyDirectives } from "./get-reply-directives.js";
import { handleInlineActions } from "./get-reply-inline-actions.js";
import { runPreparedReply } from "./get-reply-run.js";
import { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js";
import { finalizeInboundContext } from "./inbound-context.js";
import { initSessionState } from "./session.js";
import { applyResetModelOverride } from "./session-reset-model.js";
@ -126,6 +127,20 @@ export async function getReplyFromConfig(
bodyStripped,
} = sessionState;
// Trigger session:start for new sessions
if (isNewSession) {
const sessionStartEvent = createInternalHookEvent("session", "start", sessionKey, {
sessionEntry,
sessionId,
sessionFile: sessionEntry.sessionFile,
commandSource: finalized.Surface ?? finalized.Provider,
senderId: finalized.From,
isReset: resetTriggered, // True if session started due to /new or /reset
cfg,
});
await triggerInternalHook(sessionStartEvent);
}
await applyResetModelOverride({
cfg,
resetTriggered,