mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 08:22:47 +00:00
fix: run BOOT.md for each configured agent at startup (#20569)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 9098a4cc64
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
@@ -15,6 +15,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
- Gateway/Hooks: run BOOT.md startup checks per configured agent scope, including per-agent session-key resolution, startup-hook regression coverage, and non-success boot outcome logging for diagnosability. (#20569) thanks @mcaxtr.
|
||||||
- Telegram: unify message-like inbound handling so `message` and `channel_post` share the same dedupe/access/media pipeline and remain behaviorally consistent. (#20591) Thanks @obviyus.
|
- Telegram: unify message-like inbound handling so `message` and `channel_post` share the same dedupe/access/media pipeline and remain behaviorally consistent. (#20591) Thanks @obviyus.
|
||||||
- Telegram/Agents: gate exec/bash tool-failure warnings behind verbose mode so default Telegram replies stay clean while verbose sessions still surface diagnostics. (#20560) Thanks @obviyus.
|
- Telegram/Agents: gate exec/bash tool-failure warnings behind verbose mode so default Telegram replies stay clean while verbose sessions still surface diagnostics. (#20560) Thanks @obviyus.
|
||||||
- Gateway/Daemon: forward `TMPDIR` into installed service environments so macOS LaunchAgent gateway runs can open SQLite temp/journal files reliably instead of failing with `SQLITE_CANTOPEN`. (#20512) Thanks @Clawborn.
|
- Gateway/Daemon: forward `TMPDIR` into installed service environments so macOS LaunchAgent gateway runs can open SQLite temp/journal files reliably instead of failing with `SQLITE_CANTOPEN`. (#20512) Thanks @Clawborn.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const agentCommand = vi.fn();
|
|||||||
vi.mock("../commands/agent.js", () => ({ agentCommand }));
|
vi.mock("../commands/agent.js", () => ({ agentCommand }));
|
||||||
|
|
||||||
const { runBootOnce } = await import("./boot.js");
|
const { runBootOnce } = await import("./boot.js");
|
||||||
const { resolveAgentIdFromSessionKey, resolveMainSessionKey } =
|
const { resolveAgentIdFromSessionKey, resolveAgentMainSessionKey, resolveMainSessionKey } =
|
||||||
await import("../config/sessions/main-session.js");
|
await import("../config/sessions/main-session.js");
|
||||||
const { resolveStorePath } = await import("../config/sessions/paths.js");
|
const { resolveStorePath } = await import("../config/sessions/paths.js");
|
||||||
const { loadSessionStore, saveSessionStore } = await import("../config/sessions/store.js");
|
const { loadSessionStore, saveSessionStore } = await import("../config/sessions/store.js");
|
||||||
@@ -99,6 +99,24 @@ describe("runBootOnce", () => {
|
|||||||
await fs.rm(workspaceDir, { recursive: true, force: true });
|
await fs.rm(workspaceDir, { recursive: true, force: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses per-agent session key when agentId is provided", async () => {
|
||||||
|
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-boot-"));
|
||||||
|
await fs.writeFile(path.join(workspaceDir, "BOOT.md"), "Check status.", "utf-8");
|
||||||
|
|
||||||
|
agentCommand.mockResolvedValue(undefined);
|
||||||
|
const cfg = {};
|
||||||
|
const agentId = "ops";
|
||||||
|
await expect(runBootOnce({ cfg, deps: makeDeps(), workspaceDir, agentId })).resolves.toEqual({
|
||||||
|
status: "ran",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(agentCommand).toHaveBeenCalledTimes(1);
|
||||||
|
const perAgentCall = agentCommand.mock.calls[0]?.[0];
|
||||||
|
expect(perAgentCall?.sessionKey).toBe(resolveAgentMainSessionKey({ cfg, agentId }));
|
||||||
|
|
||||||
|
await fs.rm(workspaceDir, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
it("generates new session ID when no existing session exists", async () => {
|
it("generates new session ID when no existing session exists", async () => {
|
||||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-boot-"));
|
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-boot-"));
|
||||||
const content = "Say hello when you wake up.";
|
const content = "Say hello when you wake up.";
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { agentCommand } from "../commands/agent.js";
|
|||||||
import type { OpenClawConfig } from "../config/config.js";
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
import {
|
import {
|
||||||
resolveAgentIdFromSessionKey,
|
resolveAgentIdFromSessionKey,
|
||||||
|
resolveAgentMainSessionKey,
|
||||||
resolveMainSessionKey,
|
resolveMainSessionKey,
|
||||||
} from "../config/sessions/main-session.js";
|
} from "../config/sessions/main-session.js";
|
||||||
import { resolveStorePath } from "../config/sessions/paths.js";
|
import { resolveStorePath } from "../config/sessions/paths.js";
|
||||||
@@ -138,6 +139,7 @@ export async function runBootOnce(params: {
|
|||||||
cfg: OpenClawConfig;
|
cfg: OpenClawConfig;
|
||||||
deps: CliDeps;
|
deps: CliDeps;
|
||||||
workspaceDir: string;
|
workspaceDir: string;
|
||||||
|
agentId?: string;
|
||||||
}): Promise<BootRunResult> {
|
}): Promise<BootRunResult> {
|
||||||
const bootRuntime: RuntimeEnv = {
|
const bootRuntime: RuntimeEnv = {
|
||||||
log: () => {},
|
log: () => {},
|
||||||
@@ -157,7 +159,9 @@ export async function runBootOnce(params: {
|
|||||||
return { status: "skipped", reason: result.status };
|
return { status: "skipped", reason: result.status };
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionKey = resolveMainSessionKey(params.cfg);
|
const sessionKey = params.agentId
|
||||||
|
? resolveAgentMainSessionKey({ cfg: params.cfg, agentId: params.agentId })
|
||||||
|
: resolveMainSessionKey(params.cfg);
|
||||||
const message = buildBootPrompt(result.content ?? "");
|
const message = buildBootPrompt(result.content ?? "");
|
||||||
const sessionId = generateBootSessionId();
|
const sessionId = generateBootSessionId();
|
||||||
const mappingSnapshot = snapshotMainSessionMapping({
|
const mappingSnapshot = snapshotMainSessionMapping({
|
||||||
|
|||||||
@@ -16,4 +16,5 @@ metadata:
|
|||||||
|
|
||||||
# Boot Checklist Hook
|
# Boot Checklist Hook
|
||||||
|
|
||||||
Runs `BOOT.md` every time the gateway starts, if the file exists in the workspace.
|
Runs `BOOT.md` at gateway startup for each configured agent scope, if the file exists in that
|
||||||
|
agent's resolved workspace.
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { CliDeps } from "../../../cli/deps.js";
|
||||||
|
import type { OpenClawConfig } from "../../../config/config.js";
|
||||||
|
|
||||||
|
const runBootOnce = vi.fn();
|
||||||
|
|
||||||
|
vi.mock("../../../gateway/boot.js", () => ({ runBootOnce }));
|
||||||
|
vi.mock("../../../logging/subsystem.js", () => ({
|
||||||
|
createSubsystemLogger: () => ({
|
||||||
|
warn: vi.fn(),
|
||||||
|
debug: vi.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { default: runBootChecklist } = await import("./handler.js");
|
||||||
|
const { clearInternalHooks, createInternalHookEvent, registerInternalHook, triggerInternalHook } =
|
||||||
|
await import("../../internal-hooks.js");
|
||||||
|
|
||||||
|
describe("boot-md startup hook integration", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
runBootOnce.mockReset();
|
||||||
|
clearInternalHooks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
clearInternalHooks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("dispatches gateway:startup through internal hooks and runs BOOT for each configured agent scope", async () => {
|
||||||
|
const cfg = {
|
||||||
|
hooks: { internal: { enabled: true } },
|
||||||
|
agents: {
|
||||||
|
list: [
|
||||||
|
{ id: "main", default: true, workspace: "/ws/main" },
|
||||||
|
{ id: "ops", workspace: "/ws/ops" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
const deps = {} as CliDeps;
|
||||||
|
runBootOnce.mockResolvedValue({ status: "ran" });
|
||||||
|
|
||||||
|
registerInternalHook("gateway:startup", runBootChecklist);
|
||||||
|
const event = createInternalHookEvent("gateway", "startup", "gateway:startup", { cfg, deps });
|
||||||
|
await triggerInternalHook(event);
|
||||||
|
|
||||||
|
expect(runBootOnce).toHaveBeenCalledTimes(2);
|
||||||
|
expect(runBootOnce).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
expect.objectContaining({ cfg, deps, workspaceDir: "/ws/main", agentId: "main" }),
|
||||||
|
);
|
||||||
|
expect(runBootOnce).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
expect.objectContaining({ cfg, deps, workspaceDir: "/ws/ops", agentId: "ops" }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
122
src/hooks/bundled/boot-md/handler.test.ts
Normal file
122
src/hooks/bundled/boot-md/handler.test.ts
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { InternalHookEvent } from "../../internal-hooks.js";
|
||||||
|
|
||||||
|
const runBootOnce = vi.fn();
|
||||||
|
const listAgentIds = vi.fn();
|
||||||
|
const resolveAgentWorkspaceDir = vi.fn();
|
||||||
|
const logWarn = vi.fn();
|
||||||
|
const logDebug = vi.fn();
|
||||||
|
|
||||||
|
vi.mock("../../../gateway/boot.js", () => ({ runBootOnce }));
|
||||||
|
vi.mock("../../../agents/agent-scope.js", () => ({
|
||||||
|
listAgentIds,
|
||||||
|
resolveAgentWorkspaceDir,
|
||||||
|
}));
|
||||||
|
vi.mock("../../../logging/subsystem.js", () => ({
|
||||||
|
createSubsystemLogger: () => ({
|
||||||
|
warn: logWarn,
|
||||||
|
debug: logDebug,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { default: runBootChecklist } = await import("./handler.js");
|
||||||
|
|
||||||
|
function makeEvent(overrides?: Partial<InternalHookEvent>): InternalHookEvent {
|
||||||
|
return {
|
||||||
|
type: "gateway",
|
||||||
|
action: "startup",
|
||||||
|
sessionKey: "test",
|
||||||
|
context: {},
|
||||||
|
timestamp: new Date(),
|
||||||
|
messages: [],
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("boot-md handler", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
logWarn.mockReset();
|
||||||
|
logDebug.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips non-gateway events", async () => {
|
||||||
|
await runBootChecklist(makeEvent({ type: "command", action: "new" }));
|
||||||
|
expect(runBootOnce).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips non-startup actions", async () => {
|
||||||
|
await runBootChecklist(makeEvent({ action: "shutdown" }));
|
||||||
|
expect(runBootOnce).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips when cfg is missing from context", async () => {
|
||||||
|
await runBootChecklist(makeEvent({ context: { workspaceDir: "/tmp" } }));
|
||||||
|
expect(runBootOnce).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("runs boot for each agent", async () => {
|
||||||
|
const cfg = { agents: { list: [{ id: "main" }, { id: "ops" }] } };
|
||||||
|
listAgentIds.mockReturnValue(["main", "ops"]);
|
||||||
|
resolveAgentWorkspaceDir.mockImplementation((_cfg: unknown, id: string) => `/ws/${id}`);
|
||||||
|
runBootOnce.mockResolvedValue({ status: "ran" });
|
||||||
|
|
||||||
|
await runBootChecklist(makeEvent({ context: { cfg } }));
|
||||||
|
|
||||||
|
expect(listAgentIds).toHaveBeenCalledWith(cfg);
|
||||||
|
expect(runBootOnce).toHaveBeenCalledTimes(2);
|
||||||
|
expect(runBootOnce).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ cfg, workspaceDir: "/ws/main", agentId: "main" }),
|
||||||
|
);
|
||||||
|
expect(runBootOnce).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ cfg, workspaceDir: "/ws/ops", agentId: "ops" }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("runs boot for single default agent when no agents configured", async () => {
|
||||||
|
const cfg = {};
|
||||||
|
listAgentIds.mockReturnValue(["main"]);
|
||||||
|
resolveAgentWorkspaceDir.mockReturnValue("/ws/main");
|
||||||
|
runBootOnce.mockResolvedValue({ status: "skipped", reason: "missing" });
|
||||||
|
|
||||||
|
await runBootChecklist(makeEvent({ context: { cfg } }));
|
||||||
|
|
||||||
|
expect(runBootOnce).toHaveBeenCalledTimes(1);
|
||||||
|
expect(runBootOnce).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ cfg, workspaceDir: "/ws/main", agentId: "main" }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs warning details when a per-agent boot run fails", async () => {
|
||||||
|
const cfg = { agents: { list: [{ id: "main" }, { id: "ops" }] } };
|
||||||
|
listAgentIds.mockReturnValue(["main", "ops"]);
|
||||||
|
resolveAgentWorkspaceDir.mockImplementation((_cfg: unknown, id: string) => `/ws/${id}`);
|
||||||
|
runBootOnce
|
||||||
|
.mockResolvedValueOnce({ status: "ran" })
|
||||||
|
.mockResolvedValueOnce({ status: "failed", reason: "agent failed" });
|
||||||
|
|
||||||
|
await runBootChecklist(makeEvent({ context: { cfg } }));
|
||||||
|
|
||||||
|
expect(logWarn).toHaveBeenCalledTimes(1);
|
||||||
|
expect(logWarn).toHaveBeenCalledWith("boot-md failed for agent startup run", {
|
||||||
|
agentId: "ops",
|
||||||
|
workspaceDir: "/ws/ops",
|
||||||
|
reason: "agent failed",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs debug details when a per-agent boot run is skipped", async () => {
|
||||||
|
const cfg = { agents: { list: [{ id: "main" }] } };
|
||||||
|
listAgentIds.mockReturnValue(["main"]);
|
||||||
|
resolveAgentWorkspaceDir.mockReturnValue("/ws/main");
|
||||||
|
runBootOnce.mockResolvedValue({ status: "skipped", reason: "missing" });
|
||||||
|
|
||||||
|
await runBootChecklist(makeEvent({ context: { cfg } }));
|
||||||
|
|
||||||
|
expect(logDebug).toHaveBeenCalledWith("boot-md skipped for agent startup run", {
|
||||||
|
agentId: "main",
|
||||||
|
workspaceDir: "/ws/main",
|
||||||
|
reason: "missing",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,27 +1,44 @@
|
|||||||
import type { CliDeps } from "../../../cli/deps.js";
|
import { listAgentIds, resolveAgentWorkspaceDir } from "../../../agents/agent-scope.js";
|
||||||
import { createDefaultDeps } from "../../../cli/deps.js";
|
import { createDefaultDeps } from "../../../cli/deps.js";
|
||||||
import type { OpenClawConfig } from "../../../config/config.js";
|
|
||||||
import { runBootOnce } from "../../../gateway/boot.js";
|
import { runBootOnce } from "../../../gateway/boot.js";
|
||||||
|
import { createSubsystemLogger } from "../../../logging/subsystem.js";
|
||||||
import type { HookHandler } from "../../hooks.js";
|
import type { HookHandler } from "../../hooks.js";
|
||||||
|
import { isGatewayStartupEvent } from "../../internal-hooks.js";
|
||||||
|
|
||||||
type BootHookContext = {
|
const log = createSubsystemLogger("hooks/boot-md");
|
||||||
cfg?: OpenClawConfig;
|
|
||||||
workspaceDir?: string;
|
|
||||||
deps?: CliDeps;
|
|
||||||
};
|
|
||||||
|
|
||||||
const runBootChecklist: HookHandler = async (event) => {
|
const runBootChecklist: HookHandler = async (event) => {
|
||||||
if (event.type !== "gateway" || event.action !== "startup") {
|
if (!isGatewayStartupEvent(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = (event.context ?? {}) as BootHookContext;
|
if (!event.context.cfg) {
|
||||||
if (!context.cfg || !context.workspaceDir) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deps = context.deps ?? createDefaultDeps();
|
const cfg = event.context.cfg;
|
||||||
await runBootOnce({ cfg: context.cfg, deps, workspaceDir: context.workspaceDir });
|
const deps = event.context.deps ?? createDefaultDeps();
|
||||||
|
const agentIds = listAgentIds(cfg);
|
||||||
|
|
||||||
|
for (const agentId of agentIds) {
|
||||||
|
const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
|
||||||
|
const result = await runBootOnce({ cfg, deps, workspaceDir, agentId });
|
||||||
|
if (result.status === "failed") {
|
||||||
|
log.warn("boot-md failed for agent startup run", {
|
||||||
|
agentId,
|
||||||
|
workspaceDir,
|
||||||
|
reason: result.reason,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (result.status === "skipped") {
|
||||||
|
log.debug("boot-md skipped for agent startup run", {
|
||||||
|
agentId,
|
||||||
|
workspaceDir,
|
||||||
|
reason: result.reason,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default runBootChecklist;
|
export default runBootChecklist;
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import {
|
|||||||
createInternalHookEvent,
|
createInternalHookEvent,
|
||||||
getRegisteredEventKeys,
|
getRegisteredEventKeys,
|
||||||
isAgentBootstrapEvent,
|
isAgentBootstrapEvent,
|
||||||
|
isGatewayStartupEvent,
|
||||||
isMessageReceivedEvent,
|
isMessageReceivedEvent,
|
||||||
isMessageSentEvent,
|
isMessageSentEvent,
|
||||||
registerInternalHook,
|
registerInternalHook,
|
||||||
triggerInternalHook,
|
triggerInternalHook,
|
||||||
unregisterInternalHook,
|
unregisterInternalHook,
|
||||||
type AgentBootstrapHookContext,
|
type AgentBootstrapHookContext,
|
||||||
|
type GatewayStartupHookContext,
|
||||||
type MessageReceivedHookContext,
|
type MessageReceivedHookContext,
|
||||||
type MessageSentHookContext,
|
type MessageSentHookContext,
|
||||||
} from "./internal-hooks.js";
|
} from "./internal-hooks.js";
|
||||||
@@ -185,6 +187,21 @@ describe("hooks", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("isGatewayStartupEvent", () => {
|
||||||
|
it("returns true for gateway:startup events with expected context", () => {
|
||||||
|
const context: GatewayStartupHookContext = {
|
||||||
|
cfg: {},
|
||||||
|
};
|
||||||
|
const event = createInternalHookEvent("gateway", "startup", "gateway:startup", context);
|
||||||
|
expect(isGatewayStartupEvent(event)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns false for non-startup gateway events", () => {
|
||||||
|
const event = createInternalHookEvent("gateway", "shutdown", "gateway:shutdown", {});
|
||||||
|
expect(isGatewayStartupEvent(event)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("isMessageReceivedEvent", () => {
|
describe("isMessageReceivedEvent", () => {
|
||||||
it("returns true for message:received events with expected context", () => {
|
it("returns true for message:received events with expected context", () => {
|
||||||
const context: MessageReceivedHookContext = {
|
const context: MessageReceivedHookContext = {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { WorkspaceBootstrapFile } from "../agents/workspace.js";
|
import type { WorkspaceBootstrapFile } from "../agents/workspace.js";
|
||||||
|
import type { CliDeps } from "../cli/deps.js";
|
||||||
import type { OpenClawConfig } from "../config/config.js";
|
import type { OpenClawConfig } from "../config/config.js";
|
||||||
|
|
||||||
export type InternalHookEventType = "command" | "session" | "agent" | "gateway" | "message";
|
export type InternalHookEventType = "command" | "session" | "agent" | "gateway" | "message";
|
||||||
@@ -25,6 +26,18 @@ export type AgentBootstrapHookEvent = InternalHookEvent & {
|
|||||||
context: AgentBootstrapHookContext;
|
context: AgentBootstrapHookContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GatewayStartupHookContext = {
|
||||||
|
cfg?: OpenClawConfig;
|
||||||
|
deps?: CliDeps;
|
||||||
|
workspaceDir?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GatewayStartupHookEvent = InternalHookEvent & {
|
||||||
|
type: "gateway";
|
||||||
|
action: "startup";
|
||||||
|
context: GatewayStartupHookContext;
|
||||||
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Message Hook Events
|
// Message Hook Events
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -234,6 +247,14 @@ export function isAgentBootstrapEvent(event: InternalHookEvent): event is AgentB
|
|||||||
return Array.isArray(context.bootstrapFiles);
|
return Array.isArray(context.bootstrapFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isGatewayStartupEvent(event: InternalHookEvent): event is GatewayStartupHookEvent {
|
||||||
|
if (event.type !== "gateway" || event.action !== "startup") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const context = event.context as GatewayStartupHookContext | null;
|
||||||
|
return Boolean(context && typeof context === "object");
|
||||||
|
}
|
||||||
|
|
||||||
export function isMessageReceivedEvent(
|
export function isMessageReceivedEvent(
|
||||||
event: InternalHookEvent,
|
event: InternalHookEvent,
|
||||||
): event is MessageReceivedHookEvent {
|
): event is MessageReceivedHookEvent {
|
||||||
|
|||||||
Reference in New Issue
Block a user