mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 19:35:28 -06:00
fix: preserve ACP metadata key during deleted-agent checks
This commit is contained in:
@@ -3068,7 +3068,7 @@ export const chatHandlers: GatewayRequestHandlers = {
|
||||
},
|
||||
);
|
||||
const sessionLoadMs = roundedChatSendTimingMs(performance.now() - sessionLoadStartedAtMs);
|
||||
const { cfg, entry, canonicalKey: sessionKey } = sessionLoadResult;
|
||||
const { cfg, entry, canonicalKey: sessionKey, legacyKey } = sessionLoadResult;
|
||||
const selectedAgent = validateChatSelectedAgent({
|
||||
cfg,
|
||||
requestedSessionKey: rawSessionKey,
|
||||
@@ -3080,7 +3080,9 @@ export const chatHandlers: GatewayRequestHandlers = {
|
||||
}
|
||||
const requestedSessionId = normalizeOptionalText(p.sessionId);
|
||||
const backingSessionId = entry?.sessionId ?? requestedSessionId;
|
||||
const deletedAgentId = resolveDeletedAgentIdFromSessionKey(cfg, sessionKey, entry);
|
||||
const deletedAgentId = resolveDeletedAgentIdFromSessionKey(cfg, sessionKey, entry, {
|
||||
acpMetadataSessionKey: legacyKey ?? sessionKey,
|
||||
});
|
||||
if (deletedAgentId !== null) {
|
||||
respond(
|
||||
false,
|
||||
|
||||
@@ -785,9 +785,11 @@ async function handleSessionSend(params: {
|
||||
}
|
||||
const requestedAgentId = requestedAgent.agentId;
|
||||
const loaded = loadSessionEntry(key, { agentId: requestedAgentId });
|
||||
let { entry, canonicalKey, storePath } = loaded;
|
||||
let { entry, canonicalKey, storePath, legacyKey } = loaded;
|
||||
// Reject sends/steers targeting sessions whose owning agent was deleted (#65524).
|
||||
const deletedAgentId = resolveDeletedAgentIdFromSessionKey(cfg, canonicalKey, entry);
|
||||
const deletedAgentId = resolveDeletedAgentIdFromSessionKey(cfg, canonicalKey, entry, {
|
||||
acpMetadataSessionKey: legacyKey ?? canonicalKey,
|
||||
});
|
||||
if (deletedAgentId !== null) {
|
||||
params.respond(
|
||||
false,
|
||||
|
||||
@@ -923,6 +923,7 @@ export function resolveDeletedAgentIdFromSessionKey(
|
||||
cfg: OpenClawConfig,
|
||||
sessionKey: string,
|
||||
entry?: SessionEntry | null,
|
||||
options?: { acpMetadataSessionKey?: string | null },
|
||||
): string | null {
|
||||
const parsed = parseAgentSessionKey(sessionKey);
|
||||
if (!parsed) {
|
||||
@@ -932,8 +933,15 @@ export function resolveDeletedAgentIdFromSessionKey(
|
||||
// Free ACP runtime keys use agent:<harnessId>:acp:<uuid>, but key shape is
|
||||
// not proof: ACP bridge sessions can use ACP-shaped keys without SessionAcpMeta.
|
||||
// Configured acp:binding keys stay owner-scoped even when ACP metadata exists.
|
||||
const acpMetadataSessionKey = normalizeOptionalString(options?.acpMetadataSessionKey);
|
||||
const acpMeta =
|
||||
entry?.acp ?? readAcpSessionMetaForEntry({ sessionKey, entry: entry ?? undefined });
|
||||
entry?.acp ??
|
||||
(acpMetadataSessionKey
|
||||
? readAcpSessionMetaForEntry({
|
||||
sessionKey: acpMetadataSessionKey,
|
||||
entry: entry ?? undefined,
|
||||
})
|
||||
: readAcpSessionMeta({ sessionKey, cfg }));
|
||||
if (acpMeta) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -261,6 +261,43 @@ describe("resolveSessionKeyFromResolveParams store canonicalization", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves migrated ACP harness keys when metadata remains under the matched store key", async () => {
|
||||
await withStateDirEnv("openclaw-sessions-resolve-acp-harness-legacy-", async () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
agents: { list: [{ id: "main", default: true }] },
|
||||
};
|
||||
const acpKey = "agent:claude:acp:33333333-3333-4333-8333-333333333333";
|
||||
const legacyAcpKey = "agent:CLAUDE:acp:33333333-3333-4333-8333-333333333333";
|
||||
const claudeStorePath = resolveStorePath(cfg.session?.store, { agentId: "claude" });
|
||||
await saveSessionStore(claudeStorePath, {
|
||||
[legacyAcpKey]: {
|
||||
sessionId: "sess-acp-harness-legacy",
|
||||
label: "claude-delegate-legacy",
|
||||
updatedAt: freshUpdatedAt(),
|
||||
},
|
||||
});
|
||||
writeAcpSessionMetaForMigration({
|
||||
sessionKey: legacyAcpKey,
|
||||
sessionId: "sess-acp-harness-legacy",
|
||||
meta: {
|
||||
backend: "acpx",
|
||||
agent: "claude",
|
||||
runtimeSessionName: legacyAcpKey,
|
||||
mode: "oneshot",
|
||||
state: "idle",
|
||||
lastActivityAt: freshUpdatedAt(),
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
resolveSessionKeyFromResolveParams({
|
||||
cfg,
|
||||
p: { key: acpKey },
|
||||
}),
|
||||
).resolves.toEqual({ ok: true, key: acpKey });
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects ACP-shaped bridge sessions without ACP runtime metadata under deleted agents", async () => {
|
||||
await withStateDirEnv("openclaw-sessions-resolve-acp-bridge-deleted-", async () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
|
||||
@@ -43,8 +43,9 @@ function validateSessionAgentExists(
|
||||
cfg: OpenClawConfig,
|
||||
key: string,
|
||||
entry?: SessionEntry | null,
|
||||
options?: { acpMetadataSessionKey?: string | null },
|
||||
): SessionsResolveResult | null {
|
||||
const deletedAgentId = resolveDeletedAgentIdFromSessionKey(cfg, key, entry);
|
||||
const deletedAgentId = resolveDeletedAgentIdFromSessionKey(cfg, key, entry, options);
|
||||
if (deletedAgentId === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -142,6 +143,7 @@ export async function resolveSessionKeyFromResolveParams(params: {
|
||||
cfg,
|
||||
target.canonicalKey,
|
||||
store[target.canonicalKey],
|
||||
{ acpMetadataSessionKey: target.canonicalKey },
|
||||
);
|
||||
if (agentCheck) {
|
||||
return agentCheck;
|
||||
@@ -174,6 +176,7 @@ export async function resolveSessionKeyFromResolveParams(params: {
|
||||
cfg,
|
||||
target.canonicalKey,
|
||||
migratedStore[target.canonicalKey],
|
||||
{ acpMetadataSessionKey: legacyKey },
|
||||
);
|
||||
if (agentCheckLegacy) {
|
||||
return agentCheckLegacy;
|
||||
|
||||
Reference in New Issue
Block a user