mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
ACP: resolve diagnostics from configured session stores
This commit is contained in:
69
src/acp/runtime/session-meta.test.ts
Normal file
69
src/acp/runtime/session-meta.test.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
|
||||
const hoisted = vi.hoisted(() => {
|
||||
const resolveSessionStoreTargetsMock = vi.fn();
|
||||
const loadSessionStoreMock = vi.fn();
|
||||
return {
|
||||
resolveSessionStoreTargetsMock,
|
||||
loadSessionStoreMock,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../../config/sessions.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../../config/sessions.js")>(
|
||||
"../../config/sessions.js",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
resolveSessionStoreTargets: (cfg: OpenClawConfig, opts: unknown) =>
|
||||
hoisted.resolveSessionStoreTargetsMock(cfg, opts),
|
||||
loadSessionStore: (storePath: string) => hoisted.loadSessionStoreMock(storePath),
|
||||
};
|
||||
});
|
||||
|
||||
const { listAcpSessionEntries } = await import("./session-meta.js");
|
||||
|
||||
describe("listAcpSessionEntries", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("reads ACP sessions from resolved configured store targets", async () => {
|
||||
const cfg = {
|
||||
session: {
|
||||
store: "/custom/sessions/{agentId}.json",
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
hoisted.resolveSessionStoreTargetsMock.mockReturnValue([
|
||||
{
|
||||
agentId: "ops",
|
||||
storePath: "/custom/sessions/ops.json",
|
||||
},
|
||||
]);
|
||||
hoisted.loadSessionStoreMock.mockReturnValue({
|
||||
"agent:ops:acp:s1": {
|
||||
updatedAt: 123,
|
||||
acp: {
|
||||
backend: "acpx",
|
||||
agent: "ops",
|
||||
mode: "persistent",
|
||||
state: "idle",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const entries = await listAcpSessionEntries({ cfg });
|
||||
|
||||
expect(hoisted.resolveSessionStoreTargetsMock).toHaveBeenCalledWith(cfg, { allAgents: true });
|
||||
expect(hoisted.loadSessionStoreMock).toHaveBeenCalledWith("/custom/sessions/ops.json");
|
||||
expect(entries).toEqual([
|
||||
expect.objectContaining({
|
||||
cfg,
|
||||
storePath: "/custom/sessions/ops.json",
|
||||
sessionKey: "agent:ops:acp:s1",
|
||||
storeSessionKey: "agent:ops:acp:s1",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,11 @@
|
||||
import path from "node:path";
|
||||
import { resolveAgentSessionDirs } from "../../agents/session-dirs.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { resolveStateDir } from "../../config/paths.js";
|
||||
import { loadSessionStore, resolveStorePath, updateSessionStore } from "../../config/sessions.js";
|
||||
import {
|
||||
loadSessionStore,
|
||||
resolveSessionStoreTargets,
|
||||
resolveStorePath,
|
||||
updateSessionStore,
|
||||
} from "../../config/sessions.js";
|
||||
import {
|
||||
mergeSessionEntry,
|
||||
type SessionAcpMeta,
|
||||
@@ -90,12 +92,11 @@ export async function listAcpSessionEntries(params: {
|
||||
cfg?: OpenClawConfig;
|
||||
}): Promise<AcpSessionStoreEntry[]> {
|
||||
const cfg = params.cfg ?? loadConfig();
|
||||
const stateDir = resolveStateDir(process.env);
|
||||
const sessionDirs = await resolveAgentSessionDirs(stateDir);
|
||||
const storeTargets = resolveSessionStoreTargets(cfg, { allAgents: true });
|
||||
const entries: AcpSessionStoreEntry[] = [];
|
||||
|
||||
for (const sessionsDir of sessionDirs) {
|
||||
const storePath = path.join(sessionsDir, "sessions.json");
|
||||
for (const target of storeTargets) {
|
||||
const storePath = target.storePath;
|
||||
let store: Record<string, SessionEntry>;
|
||||
try {
|
||||
store = loadSessionStore(storePath);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { resolveSessionStoreTargets } from "./session-store-targets.js";
|
||||
import { resolveSessionStoreTargets } from "../config/sessions/targets.js";
|
||||
|
||||
const resolveStorePathMock = vi.hoisted(() => vi.fn());
|
||||
const resolveDefaultAgentIdMock = vi.hoisted(() => vi.fn());
|
||||
const listAgentIdsMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("../config/sessions.js", () => ({
|
||||
vi.mock("../config/sessions/paths.js", () => ({
|
||||
resolveStorePath: resolveStorePathMock,
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,84 +1,9 @@
|
||||
import { listAgentIds, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||
import { resolveStorePath } from "../config/sessions.js";
|
||||
export { resolveSessionStoreTargets } from "../config/sessions.js";
|
||||
import { resolveSessionStoreTargets } from "../config/sessions.js";
|
||||
import type { SessionStoreSelectionOptions, SessionStoreTarget } from "../config/sessions.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { normalizeAgentId } from "../routing/session-key.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
|
||||
export type SessionStoreSelectionOptions = {
|
||||
store?: string;
|
||||
agent?: string;
|
||||
allAgents?: boolean;
|
||||
};
|
||||
|
||||
export type SessionStoreTarget = {
|
||||
agentId: string;
|
||||
storePath: string;
|
||||
};
|
||||
|
||||
function dedupeTargetsByStorePath(targets: SessionStoreTarget[]): SessionStoreTarget[] {
|
||||
const deduped = new Map<string, SessionStoreTarget>();
|
||||
for (const target of targets) {
|
||||
if (!deduped.has(target.storePath)) {
|
||||
deduped.set(target.storePath, target);
|
||||
}
|
||||
}
|
||||
return [...deduped.values()];
|
||||
}
|
||||
|
||||
export function resolveSessionStoreTargets(
|
||||
cfg: OpenClawConfig,
|
||||
opts: SessionStoreSelectionOptions,
|
||||
): SessionStoreTarget[] {
|
||||
const defaultAgentId = resolveDefaultAgentId(cfg);
|
||||
const hasAgent = Boolean(opts.agent?.trim());
|
||||
const allAgents = opts.allAgents === true;
|
||||
if (hasAgent && allAgents) {
|
||||
throw new Error("--agent and --all-agents cannot be used together");
|
||||
}
|
||||
if (opts.store && (hasAgent || allAgents)) {
|
||||
throw new Error("--store cannot be combined with --agent or --all-agents");
|
||||
}
|
||||
|
||||
if (opts.store) {
|
||||
return [
|
||||
{
|
||||
agentId: defaultAgentId,
|
||||
storePath: resolveStorePath(opts.store, { agentId: defaultAgentId }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (allAgents) {
|
||||
const targets = listAgentIds(cfg).map((agentId) => ({
|
||||
agentId,
|
||||
storePath: resolveStorePath(cfg.session?.store, { agentId }),
|
||||
}));
|
||||
return dedupeTargetsByStorePath(targets);
|
||||
}
|
||||
|
||||
if (hasAgent) {
|
||||
const knownAgents = listAgentIds(cfg);
|
||||
const requested = normalizeAgentId(opts.agent ?? "");
|
||||
if (!knownAgents.includes(requested)) {
|
||||
throw new Error(
|
||||
`Unknown agent id "${opts.agent}". Use "openclaw agents list" to see configured agents.`,
|
||||
);
|
||||
}
|
||||
return [
|
||||
{
|
||||
agentId: requested,
|
||||
storePath: resolveStorePath(cfg.session?.store, { agentId: requested }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
agentId: defaultAgentId,
|
||||
storePath: resolveStorePath(cfg.session?.store, { agentId: defaultAgentId }),
|
||||
},
|
||||
];
|
||||
}
|
||||
export type { SessionStoreSelectionOptions, SessionStoreTarget } from "../config/sessions.js";
|
||||
|
||||
export function resolveSessionStoreTargetsOrExit(params: {
|
||||
cfg: OpenClawConfig;
|
||||
|
||||
@@ -11,3 +11,4 @@ export * from "./sessions/transcript.js";
|
||||
export * from "./sessions/session-file.js";
|
||||
export * from "./sessions/delivery-info.js";
|
||||
export * from "./sessions/disk-budget.js";
|
||||
export * from "./sessions/targets.js";
|
||||
|
||||
80
src/config/sessions/targets.ts
Normal file
80
src/config/sessions/targets.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { listAgentIds, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||
import { normalizeAgentId } from "../../routing/session-key.js";
|
||||
import type { OpenClawConfig } from "../types.openclaw.js";
|
||||
import { resolveStorePath } from "./paths.js";
|
||||
|
||||
export type SessionStoreSelectionOptions = {
|
||||
store?: string;
|
||||
agent?: string;
|
||||
allAgents?: boolean;
|
||||
};
|
||||
|
||||
export type SessionStoreTarget = {
|
||||
agentId: string;
|
||||
storePath: string;
|
||||
};
|
||||
|
||||
function dedupeTargetsByStorePath(targets: SessionStoreTarget[]): SessionStoreTarget[] {
|
||||
const deduped = new Map<string, SessionStoreTarget>();
|
||||
for (const target of targets) {
|
||||
if (!deduped.has(target.storePath)) {
|
||||
deduped.set(target.storePath, target);
|
||||
}
|
||||
}
|
||||
return [...deduped.values()];
|
||||
}
|
||||
|
||||
export function resolveSessionStoreTargets(
|
||||
cfg: OpenClawConfig,
|
||||
opts: SessionStoreSelectionOptions,
|
||||
): SessionStoreTarget[] {
|
||||
const defaultAgentId = resolveDefaultAgentId(cfg);
|
||||
const hasAgent = Boolean(opts.agent?.trim());
|
||||
const allAgents = opts.allAgents === true;
|
||||
if (hasAgent && allAgents) {
|
||||
throw new Error("--agent and --all-agents cannot be used together");
|
||||
}
|
||||
if (opts.store && (hasAgent || allAgents)) {
|
||||
throw new Error("--store cannot be combined with --agent or --all-agents");
|
||||
}
|
||||
|
||||
if (opts.store) {
|
||||
return [
|
||||
{
|
||||
agentId: defaultAgentId,
|
||||
storePath: resolveStorePath(opts.store, { agentId: defaultAgentId }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (allAgents) {
|
||||
const targets = listAgentIds(cfg).map((agentId) => ({
|
||||
agentId,
|
||||
storePath: resolveStorePath(cfg.session?.store, { agentId }),
|
||||
}));
|
||||
return dedupeTargetsByStorePath(targets);
|
||||
}
|
||||
|
||||
if (hasAgent) {
|
||||
const knownAgents = listAgentIds(cfg);
|
||||
const requested = normalizeAgentId(opts.agent ?? "");
|
||||
if (!knownAgents.includes(requested)) {
|
||||
throw new Error(
|
||||
`Unknown agent id "${opts.agent}". Use "openclaw agents list" to see configured agents.`,
|
||||
);
|
||||
}
|
||||
return [
|
||||
{
|
||||
agentId: requested,
|
||||
storePath: resolveStorePath(cfg.session?.store, { agentId: requested }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
agentId: defaultAgentId,
|
||||
storePath: resolveStorePath(cfg.session?.store, { agentId: defaultAgentId }),
|
||||
},
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user