mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-20 18:01:39 +00:00
refactor(sessions): reduce accessor boundary debt (#105786)
* refactor(sessions): publish rewrite updates through accessor * refactor(sessions): patch suspensions through accessor * refactor(acp): clarify accessor-backed session loads * test(sessions): ratchet accessor boundary debt * fix(sessions): preserve file-backed rewrite notifications * test(sessions): retain file-backed rewrite debt * fix(sessions): preserve transcript rewrite update identity * test(sessions): restore rewrite debt baseline
This commit is contained in:
committed by
GitHub
parent
6c88469110
commit
f9d7aa286e
@@ -78,6 +78,8 @@ export const allowedSessionStoreRuntimeFileBackedCompatExports = new Set([
|
||||
|
||||
export const migratedSessionAccessorFiles = new Set([
|
||||
"packages/memory-host-sdk/src/host/session-files.ts",
|
||||
"src/acp/control-plane/manager.background-task.ts",
|
||||
"src/acp/control-plane/manager.core.ts",
|
||||
"src/acp/runtime/session-meta.ts",
|
||||
"src/agents/acp-spawn.ts",
|
||||
"src/agents/auth-profiles/session-override.ts",
|
||||
@@ -165,6 +167,7 @@ export const migratedSessionAccessorWriteFiles = new Set([
|
||||
"src/agents/embedded-agent-runner/run/attempt.ts",
|
||||
"src/agents/live-model-switch.ts",
|
||||
"src/agents/main-session-restart-recovery.ts",
|
||||
"src/agents/session-suspension.ts",
|
||||
"src/auto-reply/reply/abort.ts",
|
||||
"src/agents/subagent-control.ts",
|
||||
"src/agents/subagent-registry-helpers.ts",
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
"embeddedAgentSessionTarget": {},
|
||||
"memoryHostSessionCorpus": {},
|
||||
"sessionAccessorRead": {
|
||||
"src/acp/control-plane/manager.background-task.ts": 2,
|
||||
"src/acp/control-plane/manager.core.ts": 1,
|
||||
"src/agents/subagent-announce-output.ts": 3,
|
||||
"src/agents/subagent-announce.test-support.ts": 2,
|
||||
"src/agents/tools/transcripts-tool.ts": 2,
|
||||
@@ -19,7 +17,6 @@
|
||||
"src/config/sessions/transcript.ts": 2
|
||||
},
|
||||
"sessionAccessorWrite": {
|
||||
"src/agents/session-suspension.ts": 2,
|
||||
"src/agents/subagent-spawn.test-helpers.ts": 1,
|
||||
"src/commands/doctor-heartbeat-main-session-repair.ts": 2,
|
||||
"src/commands/doctor-session-snapshots.ts": 2,
|
||||
|
||||
@@ -13,11 +13,11 @@ const LOBSTER = "🦞";
|
||||
const HIGH_SURROGATE_WITHOUT_LOW = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])/;
|
||||
|
||||
function fakeDeps(): AcpSessionManagerDeps {
|
||||
const readSessionEntry = (params: { sessionKey: string }) =>
|
||||
const loadSessionEntry = (params: { sessionKey: string }) =>
|
||||
params.sessionKey === "child-session"
|
||||
? { entry: { spawnedBy: "requester-session" } }
|
||||
: { entry: {} };
|
||||
return { readSessionEntry } as unknown as AcpSessionManagerDeps;
|
||||
return { loadSessionEntry } as unknown as AcpSessionManagerDeps;
|
||||
}
|
||||
|
||||
describe("appendBackgroundTaskProgressSummary", () => {
|
||||
|
||||
@@ -102,7 +102,7 @@ export function resolveBackgroundTaskContext(params: {
|
||||
requestId: string;
|
||||
text: string;
|
||||
}): BackgroundTaskContext | null {
|
||||
const childEntry = params.deps.readSessionEntry({
|
||||
const childEntry = params.deps.loadSessionEntry({
|
||||
cfg: params.cfg,
|
||||
sessionKey: params.sessionKey,
|
||||
})?.entry;
|
||||
@@ -111,7 +111,7 @@ export function resolveBackgroundTaskContext(params: {
|
||||
if (!requesterSessionKey) {
|
||||
return null;
|
||||
}
|
||||
const parentEntry = params.deps.readSessionEntry({
|
||||
const parentEntry = params.deps.loadSessionEntry({
|
||||
cfg: params.cfg,
|
||||
sessionKey: requesterSessionKey,
|
||||
})?.entry;
|
||||
|
||||
@@ -87,7 +87,7 @@ export class AcpSessionManager {
|
||||
sessionKey,
|
||||
};
|
||||
}
|
||||
const stored = this.deps.readSessionEntry({
|
||||
const stored = this.deps.loadSessionEntry({
|
||||
cfg: params.cfg,
|
||||
sessionKey,
|
||||
clone: false,
|
||||
|
||||
@@ -150,7 +150,7 @@ export type TurnLatencyStats = {
|
||||
|
||||
export type AcpSessionManagerDeps = {
|
||||
listAcpSessions: typeof listAcpSessionEntries;
|
||||
readSessionEntry: typeof readAcpSessionEntry;
|
||||
loadSessionEntry: typeof readAcpSessionEntry;
|
||||
upsertSessionMeta: typeof upsertAcpSessionMeta;
|
||||
getRuntimeBackend: typeof getAcpRuntimeBackend;
|
||||
requireRuntimeBackend: typeof requireAcpRuntimeBackend;
|
||||
@@ -205,7 +205,7 @@ export type WithManagerSessionActor = <T>(sessionKey: string, op: () => Promise<
|
||||
|
||||
export const DEFAULT_DEPS: AcpSessionManagerDeps = {
|
||||
listAcpSessions: listAcpSessionEntries,
|
||||
readSessionEntry: readAcpSessionEntry,
|
||||
loadSessionEntry: readAcpSessionEntry,
|
||||
upsertSessionMeta: upsertAcpSessionMeta,
|
||||
getRuntimeBackend: getAcpRuntimeBackend,
|
||||
requireRuntimeBackend: requireAcpRuntimeBackend,
|
||||
|
||||
@@ -5,15 +5,15 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { CommandLane } from "../process/lanes.js";
|
||||
import { MAX_TIMER_TIMEOUT_MS } from "../shared/number-coercion.js";
|
||||
|
||||
const sessionStoreMocks = vi.hoisted(() => ({
|
||||
applySessionStoreEntryPatch: vi.fn(),
|
||||
const sessionAccessorMocks = vi.hoisted(() => ({
|
||||
patchSessionEntry: vi.fn(),
|
||||
}));
|
||||
|
||||
const commandQueueMocks = vi.hoisted(() => ({
|
||||
setCommandLaneConcurrency: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../config/sessions.js", () => sessionStoreMocks);
|
||||
vi.mock("../config/sessions/session-accessor.js", () => sessionAccessorMocks);
|
||||
|
||||
vi.mock("../process/command-queue.js", () => commandQueueMocks);
|
||||
|
||||
@@ -45,7 +45,7 @@ describe("session suspension", () => {
|
||||
vi.clearAllTimers();
|
||||
}
|
||||
vi.useRealTimers();
|
||||
sessionStoreMocks.applySessionStoreEntryPatch.mockClear();
|
||||
sessionAccessorMocks.patchSessionEntry.mockClear();
|
||||
commandQueueMocks.setCommandLaneConcurrency.mockClear();
|
||||
});
|
||||
|
||||
@@ -114,9 +114,10 @@ describe("session suspension", () => {
|
||||
await suspendLane(Number.MAX_SAFE_INTEGER, {} as OpenClawConfig, CommandLane.Main);
|
||||
|
||||
expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), MAX_TIMER_TIMEOUT_MS);
|
||||
const patch = sessionStoreMocks.applySessionStoreEntryPatch.mock.calls[0]?.[0].patch as {
|
||||
const buildPatch = sessionAccessorMocks.patchSessionEntry.mock.calls[0]?.[1] as () => {
|
||||
quotaSuspension?: { expectedResumeBy?: number };
|
||||
};
|
||||
const patch = buildPatch();
|
||||
expect(patch.quotaSuspension?.expectedResumeBy).toBe(1_000 + MAX_TIMER_TIMEOUT_MS);
|
||||
});
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
||||
import path from "node:path";
|
||||
import { resolveAgentMaxConcurrent, resolveSubagentMaxConcurrent } from "../config/agent-limits.js";
|
||||
import { resolveCronMaxConcurrentRuns } from "../config/cron-limits.js";
|
||||
import { applySessionStoreEntryPatch } from "../config/sessions.js";
|
||||
import { patchSessionEntry } from "../config/sessions/session-accessor.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { setCommandLaneConcurrency } from "../process/command-queue.js";
|
||||
@@ -127,12 +127,9 @@ export async function suspendSession(params: SessionSuspensionParams) {
|
||||
const expectedResumeBy = resolveExpiresAtMsFromDurationMs(ttlMs, { nowMs: now }) ?? now;
|
||||
|
||||
try {
|
||||
await applySessionStoreEntryPatch({
|
||||
storePath,
|
||||
sessionKey,
|
||||
skipMaintenance: true,
|
||||
takeCacheOwnership: true,
|
||||
patch: {
|
||||
await patchSessionEntry(
|
||||
{ storePath, sessionKey },
|
||||
() => ({
|
||||
quotaSuspension: {
|
||||
schemaVersion: 1,
|
||||
suspendedAt: now,
|
||||
@@ -144,8 +141,9 @@ export async function suspendSession(params: SessionSuspensionParams) {
|
||||
expectedResumeBy,
|
||||
state: "suspended",
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
{ skipMaintenance: true, takeCacheOwnership: true },
|
||||
);
|
||||
} catch (err) {
|
||||
log.warn("failed to persist quota suspension; not throttling lane", {
|
||||
sessionId: params.sessionId,
|
||||
|
||||
@@ -28,6 +28,8 @@ describe("session accessor boundary guard", () => {
|
||||
expect(migratedSessionAccessorFiles).toEqual(
|
||||
new Set([
|
||||
"packages/memory-host-sdk/src/host/session-files.ts",
|
||||
"src/acp/control-plane/manager.background-task.ts",
|
||||
"src/acp/control-plane/manager.core.ts",
|
||||
"src/acp/runtime/session-meta.ts",
|
||||
"src/agents/acp-spawn.ts",
|
||||
"src/agents/auth-profiles/session-override.ts",
|
||||
@@ -125,6 +127,7 @@ describe("session accessor boundary guard", () => {
|
||||
"src/agents/embedded-agent-subscribe.handlers.compaction.runtime.ts",
|
||||
"src/agents/live-model-switch.ts",
|
||||
"src/agents/main-session-restart-recovery.ts",
|
||||
"src/agents/session-suspension.ts",
|
||||
"src/auto-reply/reply/abort.ts",
|
||||
"src/agents/subagent-control.ts",
|
||||
"src/agents/subagent-registry-helpers.ts",
|
||||
|
||||
Reference in New Issue
Block a user