openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
181 lines (180 loc) • 6.52 kB
JavaScript
import { r as defaultRuntime } from "../../runtime-BRg2NubB.mjs";
import { t as formatErrorMessage } from "../../errors-BvOkmQM5.mjs";
import { t as createSubsystemLogger } from "../../subsystem-4NzR43uz.mjs";
import { l as resolveAgentIdFromSessionKey } from "../../session-key-DY3MOX4N.mjs";
import { i as listAgentIds } from "../../agent-roster-DH6r_8f4.mjs";
import { c as resolveSessionStorePathCore } from "../../paths-Bonddxfk.mjs";
import { d as resolveAgentWorkspaceDir } from "../../agent-scope-config-BEk6-ThS.mjs";
import { t as dedupeByKey } from "../../dedupe-by-key-DnqEVrst.mjs";
import "../../agent-scope-bJOokm5r.mjs";
import { s as escapeInternalRuntimeContextDelimiters } from "../../internal-runtime-context-D5iQhnLo.mjs";
import { a as resolveMainSessionKey, r as resolveAgentMainSessionKey } from "../../main-session-BOUJdkxQ.mjs";
import { r as SILENT_REPLY_TOKEN } from "../../tokens-Ljy0DfNV.mjs";
import "../../session-accessor-C3ljJ974.mjs";
import { f as applySessionEntryLifecycleMutation } from "../../session-accessor.entry-mutation-telsi3Ig.mjs";
import { o as isGatewayStartupEvent } from "../../internal-hooks-eyE9vom5.mjs";
import { o as clearBootEchoContextForSession, s as setBootEchoContextForSession } from "../../openclaw-tools-CSSYZDKR.mjs";
import { t as createDefaultDeps } from "../../deps-DRDEKuSb.mjs";
import { i as agentCommandFromSystem } from "../../agent-command-B2Fr8yPI.mjs";
import "../../agent-BubuGqih.mjs";
import { t as runStartupTasks } from "../../startup-tasks-DZn-vdwB.mjs";
import path from "node:path";
import fs from "node:fs/promises";
import crypto from "node:crypto";
import { readRegularFile } from "@openclaw/fs-safe/advanced";
//#region src/gateway/boot.ts
function generateBootSessionId() {
return `boot-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").replace("T", "_").replace("Z", "")}-${crypto.randomUUID().slice(0, 8)}`;
}
const log$1 = createSubsystemLogger("gateway/boot");
const BOOT_FILENAME = "BOOT.md";
function buildBootPrompt(content) {
const runtimeContextFragments = [
{
kind: "runtime-instruction",
text: "You are running a boot check. Follow BOOT.md instructions exactly."
},
{
kind: "runtime-instruction",
text: `BOOT.md:\n${escapeInternalRuntimeContextDelimiters(content)}`
},
{
kind: "runtime-instruction",
text: [
"If BOOT.md asks you to send a message, use the message tool (action=send with channel + target).",
"Use the `target` field (not `to`) for message tool destinations.",
`After sending with the message tool, reply with ONLY: ${SILENT_REPLY_TOKEN}.`,
`If nothing needs attention, reply with ONLY: ${SILENT_REPLY_TOKEN}.`
].join("\n")
}
];
return {
message: "Run the workspace boot check now.",
runtimeContextFragments,
echoContext: runtimeContextFragments.map((fragment) => fragment.text).join("\n\n")
};
}
const MAX_BOOT_FILE_BYTES = 16777216;
async function loadBootFile(workspaceDir) {
const bootPath = path.join(workspaceDir, BOOT_FILENAME);
let buffer;
try {
const resolvedPath = await fs.realpath(bootPath);
({buffer} = await readRegularFile({
filePath: resolvedPath,
maxBytes: MAX_BOOT_FILE_BYTES
}));
} catch (err) {
if (err.code === "ENOENT") return { status: "missing" };
throw err;
}
const content = buffer.toString("utf-8").trim();
return content ? {
status: "ok",
content
} : { status: "empty" };
}
async function runBootOnce(params) {
const bootRuntime = {
log: () => {},
error: (message) => log$1.error(String(message)),
exit: defaultRuntime.exit
};
let result;
try {
result = await loadBootFile(params.workspaceDir);
} catch (err) {
const message = formatErrorMessage(err);
log$1.error(`boot: failed to read ${BOOT_FILENAME}: ${message}`);
return {
status: "failed",
reason: message
};
}
if (result.status !== "ok") return {
status: "skipped",
reason: result.status
};
const mainSessionKey = params.agentId ? resolveAgentMainSessionKey({
cfg: params.cfg,
agentId: params.agentId
}) : resolveMainSessionKey(params.cfg);
const bootPrompt = buildBootPrompt(result.content);
const sessionId = generateBootSessionId();
const agentId = resolveAgentIdFromSessionKey(mainSessionKey);
const sessionKey = `agent:${agentId}:boot:${sessionId}`;
const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId });
let ownedSessionId = sessionId;
let agentFailure;
let cleanupFailure;
setBootEchoContextForSession(sessionKey, bootPrompt.echoContext);
try {
await agentCommandFromSystem({
message: bootPrompt.message,
runtimeContextFragments: bootPrompt.runtimeContextFragments,
sessionKey,
sessionId,
deliver: false,
suppressPromptPersistence: true,
onSessionIdChanged: (nextSessionId) => {
ownedSessionId = nextSessionId;
}
}, { boundary: "gateway.boot" }, bootRuntime, params.deps);
} catch (err) {
agentFailure = formatErrorMessage(err);
log$1.error(`boot: agent run failed: ${agentFailure}`);
} finally {
clearBootEchoContextForSession(sessionKey);
try {
await applySessionEntryLifecycleMutation({
agentId,
storePath,
removals: [{
sessionKey,
expectedSessionId: ownedSessionId,
archiveRemovedTranscript: false
}],
skipMaintenance: true
});
} catch (err) {
cleanupFailure = formatErrorMessage(err);
log$1.error(`boot: failed to clean up session: ${cleanupFailure}`);
}
}
if (!agentFailure && !cleanupFailure) return { status: "ran" };
return {
status: "failed",
reason: [agentFailure ? `agent run failed: ${agentFailure}` : void 0, cleanupFailure ? `session cleanup failed: ${cleanupFailure}` : void 0].filter((part) => Boolean(part)).join("; ")
};
}
//#endregion
//#region src/hooks/bundled/boot-md/handler.ts
const log = createSubsystemLogger("hooks/boot-md");
const runBootChecklist = async (event) => {
if (!isGatewayStartupEvent(event)) return;
if (!event.context.cfg) return;
const cfg = event.context.cfg;
const deps = event.context.deps ?? createDefaultDeps();
const tasks = dedupeByKey(listAgentIds(cfg).map((agentId) => {
return {
agentId,
workspaceDir: resolveAgentWorkspaceDir(cfg, agentId)
};
}), ({ workspaceDir }) => workspaceDir).map(({ agentId, workspaceDir }) => ({
source: "boot-md",
agentId,
workspaceDir,
run: () => runBootOnce({
cfg,
deps,
workspaceDir,
agentId
})
}));
await runStartupTasks({
tasks,
log
});
};
//#endregion
export { runBootChecklist as default };