mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(gateway): include disk-scanned agent IDs in listConfiguredAgentIds (#32831)
Merged via squash.
Prepared head SHA: 2aa58f6afd
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd
This commit is contained in:
@@ -15,6 +15,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
- Docs/security hardening guidance: document Docker `DOCKER-USER` + UFW policy and add cross-linking from Docker install docs for VPS/public-host setups. (#27613) thanks @dorukardahan.
|
||||
- Docs/tool-loop detection config keys: align `docs/tools/loop-detection.md` examples and field names with the current `tools.loopDetection` schema to prevent copy-paste validation failures from outdated keys. (#33182) Thanks @Mylszd.
|
||||
- Gateway/session agent discovery: include disk-scanned agent IDs in `listConfiguredAgentIds` even when `agents.list` is configured, so disk-only/ACP agent sessions remain visible in gateway session aggregation and listings. (#32831) thanks @Sid-Qin.
|
||||
- Discord/inbound debouncer: skip bot-own MESSAGE_CREATE events before they reach the debounce queue to avoid self-triggered slowdowns in busy servers. Thanks @thewilloftheshadow.
|
||||
- Discord/Agent-scoped media roots: pass `mediaLocalRoots` through Discord monitor reply delivery (message + component interaction paths) so local media attachments honor per-agent workspace roots instead of falling back to default global roots. Thanks @thewilloftheshadow.
|
||||
- Discord/slash command handling: intercept text-based slash commands in channels, register plugin commands as native, and send fallback acknowledgments for empty slash runs so interactions do not hang. Thanks @thewilloftheshadow.
|
||||
|
||||
@@ -4,12 +4,14 @@ import path from "node:path";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { SessionEntry } from "../config/sessions.js";
|
||||
import { withStateDirEnv } from "../test-helpers/state-dir-env.js";
|
||||
import {
|
||||
capArrayByJsonBytes,
|
||||
classifySessionKey,
|
||||
deriveSessionTitle,
|
||||
listAgentsForGateway,
|
||||
listSessionsFromStore,
|
||||
loadCombinedSessionStoreForGateway,
|
||||
parseGroupKey,
|
||||
pruneLegacyStoreKeys,
|
||||
resolveGatewaySessionStoreTarget,
|
||||
@@ -310,6 +312,21 @@ describe("gateway session utils", () => {
|
||||
`data:image/png;base64,${Buffer.from("avatar").toString("base64")}`,
|
||||
);
|
||||
});
|
||||
|
||||
test("listAgentsForGateway keeps explicit agents.list scope over disk-only agents (scope boundary)", async () => {
|
||||
await withStateDirEnv("openclaw-agent-list-scope-", async ({ stateDir }) => {
|
||||
fs.mkdirSync(path.join(stateDir, "agents", "main"), { recursive: true });
|
||||
fs.mkdirSync(path.join(stateDir, "agents", "codex"), { recursive: true });
|
||||
|
||||
const cfg = {
|
||||
session: { mainKey: "main" },
|
||||
agents: { list: [{ id: "main", default: true }] },
|
||||
} as OpenClawConfig;
|
||||
|
||||
const { agents } = listAgentsForGateway(cfg);
|
||||
expect(agents.map((agent) => agent.id)).toEqual(["main"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveSessionModelRef", () => {
|
||||
@@ -746,3 +763,45 @@ describe("listSessionsFromStore search", () => {
|
||||
expect(missing?.totalTokensFresh).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("loadCombinedSessionStoreForGateway includes disk-only agents (#32804)", () => {
|
||||
test("ACP agent sessions are visible even when agents.list is configured", async () => {
|
||||
await withStateDirEnv("openclaw-acp-vis-", async ({ stateDir }) => {
|
||||
const agentsDir = path.join(stateDir, "agents");
|
||||
const mainDir = path.join(agentsDir, "main", "sessions");
|
||||
const codexDir = path.join(agentsDir, "codex", "sessions");
|
||||
fs.mkdirSync(mainDir, { recursive: true });
|
||||
fs.mkdirSync(codexDir, { recursive: true });
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(mainDir, "sessions.json"),
|
||||
JSON.stringify({
|
||||
"agent:main:main": { sessionId: "s-main", updatedAt: 100 },
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(codexDir, "sessions.json"),
|
||||
JSON.stringify({
|
||||
"agent:codex:acp-task": { sessionId: "s-codex", updatedAt: 200 },
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const cfg = {
|
||||
session: {
|
||||
mainKey: "main",
|
||||
store: path.join(stateDir, "agents", "{agentId}", "sessions", "sessions.json"),
|
||||
},
|
||||
agents: {
|
||||
list: [{ id: "main", default: true }],
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const { store } = loadCombinedSessionStoreForGateway(cfg);
|
||||
expect(store["agent:main:main"]).toBeDefined();
|
||||
expect(store["agent:codex:acp-task"]).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -310,35 +310,25 @@ function listExistingAgentIdsFromDisk(): string[] {
|
||||
}
|
||||
|
||||
function listConfiguredAgentIds(cfg: OpenClawConfig): string[] {
|
||||
const agents = cfg.agents?.list ?? [];
|
||||
if (agents.length > 0) {
|
||||
const ids = new Set<string>();
|
||||
for (const entry of agents) {
|
||||
if (entry?.id) {
|
||||
ids.add(normalizeAgentId(entry.id));
|
||||
}
|
||||
}
|
||||
const defaultId = normalizeAgentId(resolveDefaultAgentId(cfg));
|
||||
ids.add(defaultId);
|
||||
const sorted = Array.from(ids).filter(Boolean);
|
||||
sorted.sort((a, b) => a.localeCompare(b));
|
||||
return sorted.includes(defaultId)
|
||||
? [defaultId, ...sorted.filter((id) => id !== defaultId)]
|
||||
: sorted;
|
||||
}
|
||||
|
||||
const ids = new Set<string>();
|
||||
const defaultId = normalizeAgentId(resolveDefaultAgentId(cfg));
|
||||
ids.add(defaultId);
|
||||
|
||||
for (const entry of cfg.agents?.list ?? []) {
|
||||
if (entry?.id) {
|
||||
ids.add(normalizeAgentId(entry.id));
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of listExistingAgentIdsFromDisk()) {
|
||||
ids.add(id);
|
||||
}
|
||||
|
||||
const sorted = Array.from(ids).filter(Boolean);
|
||||
sorted.sort((a, b) => a.localeCompare(b));
|
||||
if (sorted.includes(defaultId)) {
|
||||
return [defaultId, ...sorted.filter((id) => id !== defaultId)];
|
||||
}
|
||||
return sorted;
|
||||
return sorted.includes(defaultId)
|
||||
? [defaultId, ...sorted.filter((id) => id !== defaultId)]
|
||||
: sorted;
|
||||
}
|
||||
|
||||
export function listAgentsForGateway(cfg: OpenClawConfig): {
|
||||
|
||||
Reference in New Issue
Block a user