Files
openclaw/src/agents/internal-session-effects.ts
Peter Steinberger 4c7efd2673 refactor(agents): privatize session state types (#106672)
* refactor(agents): privatize session state types

* chore(deadcode): shrink agents export baseline
2026-07-13 11:04:21 -07:00

131 lines
4.2 KiB
TypeScript

import { resolveInternalSessionEffectsIdentity } from "../config/sessions/internal-session-key.js";
/** Manages hidden SQLite sessions used for suppressed agent side effects. */
import {
applySessionEntryLifecycleMutation,
forkSessionFromParentTranscript,
loadExactSessionEntry,
replaceTranscriptEvents,
upsertSessionEntry,
} from "../config/sessions/session-accessor.js";
import { formatSqliteSessionFileMarker } from "../config/sessions/sqlite-marker.js";
import { createSessionTranscriptHeader } from "../config/sessions/transcript-header.js";
import type { SessionEntry } from "../config/sessions/types.js";
import type { AgentRunSessionTarget } from "./run-session-target.js";
type InternalSessionEffectsTarget = Required<
Pick<AgentRunSessionTarget, "agentId" | "sessionId" | "sessionKey" | "storePath">
> & {
sessionEntry: SessionEntry;
sessionFile: string;
};
type InternalSessionEffectsSource = Required<
Pick<AgentRunSessionTarget, "agentId" | "sessionId" | "sessionKey" | "storePath">
>;
/** Resolves the deterministic SQLite target owned by one internal-effects run. */
export function resolveInternalSessionEffectsTarget(params: {
agentId: string;
runId: string;
storePath: string;
}): Required<Pick<AgentRunSessionTarget, "agentId" | "sessionId" | "sessionKey" | "storePath">> {
return {
agentId: params.agentId,
storePath: params.storePath,
...resolveInternalSessionEffectsIdentity(params),
};
}
function toInternalSessionEffectsTarget(params: {
agentId: string;
entry: SessionEntry;
sessionKey: string;
storePath: string;
}): InternalSessionEffectsTarget {
return {
agentId: params.agentId,
sessionId: params.entry.sessionId,
sessionKey: params.sessionKey,
storePath: params.storePath,
sessionEntry: params.entry,
sessionFile: formatSqliteSessionFileMarker({
agentId: params.agentId,
sessionId: params.entry.sessionId,
storePath: params.storePath,
}),
};
}
/** Creates or reopens the hidden SQLite session owned by one internal-effects run. */
export async function prepareInternalSessionEffectsSession(params: {
agentId: string;
cwd?: string;
runId: string;
source?: InternalSessionEffectsSource;
storePath: string;
}): Promise<InternalSessionEffectsTarget> {
const scope = resolveInternalSessionEffectsTarget(params);
const existing = loadExactSessionEntry(scope)?.entry;
if (existing?.sessionId === scope.sessionId) {
return toInternalSessionEffectsTarget({
agentId: params.agentId,
entry: existing,
sessionKey: scope.sessionKey,
storePath: params.storePath,
});
}
const fork = params.source
? await forkSessionFromParentTranscript({
agentId: params.source.agentId,
parentEntry: { sessionId: params.source.sessionId, updatedAt: Date.now() },
parentSessionKey: params.source.sessionKey,
sessionKey: scope.sessionKey,
storePath: params.source.storePath,
targetSessionId: scope.sessionId,
targetStorePath: params.storePath,
})
: undefined;
if (fork?.status !== "created") {
await replaceTranscriptEvents(scope, [
createSessionTranscriptHeader({ cwd: params.cwd, sessionId: scope.sessionId }),
]);
}
const now = Date.now();
const entry = await upsertSessionEntry(scope, {
sessionId: scope.sessionId,
sessionStartedAt: now,
updatedAt: now,
});
if (!entry) {
throw new Error(`Failed to create internal SQLite session for run ${params.runId}`);
}
return toInternalSessionEffectsTarget({
agentId: params.agentId,
entry,
sessionKey: scope.sessionKey,
storePath: params.storePath,
});
}
/** Hard-deletes a run-owned hidden session and its SQLite transcript rows. */
export async function removeInternalSessionEffectsSession(
target: AgentRunSessionTarget | undefined,
): Promise<void> {
if (!target?.sessionKey || !target.storePath) {
return;
}
await applySessionEntryLifecycleMutation({
...(target.agentId ? { agentId: target.agentId } : {}),
storePath: target.storePath,
removals: [
{
sessionKey: target.sessionKey,
...(target.sessionId ? { expectedSessionId: target.sessionId } : {}),
archiveRemovedTranscript: false,
},
],
skipMaintenance: true,
});
}