refactor: burn down export name collision baseline (#121870)

* refactor(sessions): distinguish persistence owners

* refactor(config): consolidate legacy record helper

* refactor(sessions): name sqlite number coercion

* chore(scripts): ratchet export collision baseline
This commit is contained in:
Peter Steinberger
2026-08-10 23:10:44 -07:00
committed by GitHub
parent e7350b4ac6
commit 5f562e65f6
27 changed files with 85 additions and 132 deletions
@@ -262,13 +262,6 @@
],
"sdk": true
},
{
"name": "ensureRecord",
"files": [
"src/commands/doctor/shared/legacy-config-record-shared.ts",
"src/config/legacy.shared.ts"
]
},
{
"name": "extractAssistantVisibleText",
"files": [
@@ -681,13 +674,6 @@
"src/cron/run-diagnostics.ts"
]
},
{
"name": "normalizeSqliteNumber",
"files": [
"src/config/sessions/session-accessor.sqlite-normalize.ts",
"src/infra/sqlite-number.ts"
]
},
{
"name": "normalizeStringRecord",
"files": [
@@ -793,14 +779,6 @@
"src/gateway/server-methods/__mocks__/tools-effective.runtime.ts"
]
},
{
"name": "persistSessionEntry",
"files": [
"src/agents/command/attempt-execution.shared.ts",
"src/agents/command/session-helpers.ts",
"src/auto-reply/reply/commands-session-store.ts"
]
},
{
"name": "planPluginUninstall",
"files": [
@@ -162,7 +162,7 @@ vi.mock("./command/attempt-execution.shared.js", async () => {
);
return {
...actual,
persistSessionEntry: (...args: unknown[]) => state.persistSessionEntryMock(...args),
persistAgentSession: (...args: unknown[]) => state.persistSessionEntryMock(...args),
};
});
+4 -3
View File
@@ -37,13 +37,14 @@ import {
} from "./agent-command-restart-recovery.js";
import { runAcpAgentCommand } from "./command/acp-execution.js";
import { repairPendingAssistantTranscriptTurns } from "./command/assistant-transcript-repair.js";
import { persistAgentSession } from "./command/attempt-execution.shared.js";
import { emitIngressModelUsageDiagnostic } from "./command/ingress-diagnostics.js";
import { resolveEmbeddedModelSelection } from "./command/model-selection.js";
import { finalizeEmbeddedAgentCommand } from "./command/post-run.js";
import { prepareAgentCommandExecution } from "./command/prepare.js";
import { runEmbeddedAgentAttempt } from "./command/run-embedded-attempt.js";
import { loadSessionStoreRuntime, resolveAgentCommandDeps } from "./command/runtime-loaders.js";
import { persistSessionEntry, prepareCurrentRunDelivery } from "./command/session-helpers.js";
import { prepareCurrentRunDelivery } from "./command/session-helpers.js";
import { prepareEmbeddedSessionState } from "./command/session-preparation.js";
import { clearRotatedSessionMetadata } from "./command/session.js";
import type {
@@ -354,7 +355,7 @@ async function agentCommandInternal(
suppressTextDelivery: opts.internalDeliverySuppressText,
}),
};
const persisted = await persistSessionEntry({
const persisted = await persistAgentSession({
sessionStore,
sessionKey,
storePath,
@@ -566,7 +567,7 @@ async function agentCommandInternal(
}),
updatedAt: Date.now(),
};
const persisted = await persistSessionEntry({
const persisted = await persistAgentSession({
sessionStore,
sessionKey,
storePath,
@@ -15,14 +15,14 @@ import type { EmbeddedAgentRunResult } from "../embedded-agent.js";
import type { loadManifestModelCatalog } from "../model-catalog.js";
import type { persistCliTurnTranscript } from "./attempt-execution.js";
import type { runAgentAttempt } from "./attempt-execution.runtime.js";
import type { persistSessionEntry } from "./session-helpers.js";
import type { persistAgentSession } from "./attempt-execution.shared.js";
type ProviderModelNormalizationParams = { provider: string; context: { modelId: string } };
type LoadManifestModelCatalogParams = Parameters<typeof loadManifestModelCatalog>[0];
type RunAgentAttempt = typeof runAgentAttempt;
type PersistCliTurnTranscript = typeof persistCliTurnTranscript;
type AppendExactAssistantMessage = typeof appendExactAssistantMessageToSessionTranscript;
type PersistSessionEntry = typeof persistSessionEntry;
type PersistSessionEntry = typeof persistAgentSession;
type CliCompactionParams = {
sessionEntry?: SessionEntry;
sessionKey: string;
@@ -205,13 +205,14 @@ vi.mock("../../config/sessions/transcript.runtime.js", async () => {
};
});
vi.mock("./session-helpers.js", async () => {
const actual =
await vi.importActual<typeof import("./session-helpers.js")>("./session-helpers.js");
vi.mock("./attempt-execution.shared.js", async () => {
const actual = await vi.importActual<typeof import("./attempt-execution.shared.js")>(
"./attempt-execution.shared.js",
);
return {
...actual,
persistSessionEntry: (...args: Parameters<typeof actual.persistSessionEntry>) => {
state.persistSessionEntryReal = actual.persistSessionEntry;
persistAgentSession: (...args: Parameters<typeof actual.persistAgentSession>) => {
state.persistSessionEntryReal = actual.persistAgentSession;
return state.persistSessionEntryMock(...args);
},
};
@@ -4,8 +4,8 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { runAgentHarnessBeforeMessageWriteHook } from "../harness/hook-helpers.js";
import { persistAgentSession } from "./attempt-execution.shared.js";
import { loadTranscriptAppendRuntime } from "./runtime-loaders.js";
import { persistSessionEntry } from "./session-helpers.js";
const log = createSubsystemLogger("agents/assistant-transcript-repair");
@@ -58,7 +58,7 @@ export async function persistAssistantTranscriptRepairRecord(params: {
createdAt: now,
};
try {
await persistSessionEntry({
await persistAgentSession({
sessionStore: context.sessionStore,
sessionKey: context.sessionKey,
storePath: context.storePath,
@@ -148,7 +148,7 @@ export async function repairPendingAssistantTranscriptTurns(params: {
return;
}
try {
await persistSessionEntry({
await persistAgentSession({
sessionStore: context.sessionStore,
sessionKey: context.sessionKey,
storePath: context.storePath,
@@ -11,7 +11,7 @@ import {
INTERNAL_RUNTIME_CONTEXT_END,
} from "../internal-runtime-context.js";
import {
persistSessionEntry,
persistAgentSession,
resolveAcpPromptBody,
resolveInternalEventTranscriptBody,
} from "./attempt-execution.shared.js";
@@ -90,7 +90,7 @@ describe("attempt execution prompt materialization", () => {
});
});
describe("persistSessionEntry", () => {
describe("persistAgentSession", () => {
const sessionKey = "agent:main:main";
it("clears stale local entries when guarded persistence sees no persisted entry", async () => {
@@ -106,7 +106,7 @@ describe("persistSessionEntry", () => {
// A guarded write can decline persistence after rereading disk; local
// memory must be cleared too so later turns do not reuse stale entries.
const persisted = await persistSessionEntry({
const persisted = await persistAgentSession({
sessionStore,
sessionKey,
storePath,
@@ -160,7 +160,7 @@ describe("persistSessionEntry", () => {
await replaceSessionEntry({ sessionKey, storePath }, currentEntry);
const sessionStore = { [sessionKey]: staleEntry };
const persisted = await persistSessionEntry({
const persisted = await persistAgentSession({
sessionStore,
sessionKey,
storePath,
@@ -206,7 +206,7 @@ describe("persistSessionEntry", () => {
await replaceSessionEntry({ sessionKey, storePath }, currentEntry);
const sessionStore = { [sessionKey]: initialEntry };
const persisted = await persistSessionEntry({
const persisted = await persistAgentSession({
sessionStore,
sessionKey,
storePath,
@@ -244,7 +244,7 @@ describe("persistSessionEntry", () => {
};
const sessionStore = { [sessionKey]: staleEntry };
const persisted = await persistSessionEntry({
const persisted = await persistAgentSession({
sessionStore,
sessionKey,
storePath,
@@ -275,14 +275,14 @@ describe("persistSessionEntry", () => {
};
const sessionStore = { [sessionKey]: staleEntry };
const first = await persistSessionEntry({
const first = await persistAgentSession({
sessionStore,
sessionKey,
storePath,
initialEntry: staleEntry,
entry: staleEntry,
});
const second = await persistSessionEntry({
const second = await persistAgentSession({
sessionStore,
sessionKey,
storePath,
@@ -314,7 +314,7 @@ describe("persistSessionEntry", () => {
updatedAt: 1,
};
const persisted = await persistSessionEntry({
const persisted = await persistAgentSession({
sessionStore,
sessionKey,
storePath,
@@ -26,7 +26,7 @@ type PersistSessionEntryParams = {
};
/** Persists one session entry while keeping the caller's in-memory store aligned. */
export async function persistSessionEntry(
export async function persistAgentSession(
params: PersistSessionEntryParams,
): Promise<SessionEntry | undefined> {
let rejectedMissingEntry = false;
+3 -3
View File
@@ -59,6 +59,7 @@ import {
normalizeThinkingCatalogProviders,
resolveEffectiveAgentRuntime,
} from "../thinking-runtime.js";
import { persistAgentSession } from "./attempt-execution.shared.js";
import {
normalizeAgentCommandDefaultModelRef,
normalizeAgentCommandModelRef,
@@ -67,7 +68,6 @@ import {
import { normalizeExplicitOverrideInput } from "./prepare.js";
import type { resolveAgentRunContext } from "./run-context.js";
import { loadTranscriptResolveRuntime } from "./runtime-loaders.js";
import { persistSessionEntry } from "./session-helpers.js";
import type { AgentCommandOpts } from "./types.js";
type AgentRunContext = ReturnType<typeof resolveAgentRunContext>;
@@ -219,7 +219,7 @@ export async function resolveEmbeddedModelSelection(params: {
}
}
if (entryUpdated) {
sessionEntry = await persistSessionEntry({
sessionEntry = await persistAgentSession({
sessionStore: params.sessionStore,
sessionKey: params.sessionKey,
storePath: params.storePath,
@@ -596,7 +596,7 @@ export async function resolveEmbeddedModelSelection(params: {
thinkingLevel: params.thinkOverride,
};
sessionEntry =
(await persistSessionEntry({
(await persistAgentSession({
sessionStore: params.sessionStore,
sessionKey: params.sessionKey,
storePath: params.storePath,
+3 -2
View File
@@ -19,6 +19,7 @@ import { persistPendingFinalDeliveryMarker } from "../pending-final-delivery-mar
import type { AgentRunSessionTarget } from "../run-session-target.js";
import { throwAgentRunRestartAbortReason } from "../run-termination.js";
import { persistAssistantTranscriptRepairRecord } from "./assistant-transcript-repair.js";
import { persistAgentSession } from "./attempt-execution.shared.js";
import type { PreparedAgentCommandExecution } from "./prepare.js";
import type { EmbeddedAgentAttempt } from "./run-embedded-attempt.js";
import {
@@ -26,7 +27,7 @@ import {
loadDeliveryRuntime,
loadSessionStoreRuntime,
} from "./runtime-loaders.js";
import { clearPendingFinalDelivery, persistSessionEntry } from "./session-helpers.js";
import { clearPendingFinalDelivery } from "./session-helpers.js";
import type { EmbeddedSessionState } from "./session-preparation.js";
import type { AgentCommandOpts } from "./types.js";
@@ -383,7 +384,7 @@ export async function finalizeEmbeddedAgentCommand(params: {
!pendingFinalDeliveryMarker.hasSendableFinalPayload &&
entry.pendingFinalDelivery?.kind === "transport-only";
if (deliveryResult?.deliverySucceeded === true || clearStaleTransportOnly) {
sessionEntry = await persistSessionEntry({
sessionEntry = await persistAgentSession({
sessionStore,
sessionKey,
storePath,
+3 -2
View File
@@ -51,12 +51,13 @@ import {
createAgentAttemptLifecycleCallbacks,
type AgentAttemptLifecycleState,
} from "./attempt-callbacks.js";
import { persistAgentSession } from "./attempt-execution.shared.js";
import { createAgentCommandLifecycle } from "./lifecycle.js";
import { normalizeAgentCommandModelRef } from "./model-ref.js";
import type { EmbeddedModelSelection } from "./model-selection.js";
import type { PreparedAgentCommandExecution } from "./prepare.js";
import { loadAttemptExecutionRuntime, type AgentAttemptResult } from "./runtime-loaders.js";
import { persistSessionEntry, resolveInternalSessionEffectsSource } from "./session-helpers.js";
import { resolveInternalSessionEffectsSource } from "./session-helpers.js";
import type { EmbeddedSessionState } from "./session-preparation.js";
import type { AgentCommandOpts } from "./types.js";
@@ -313,7 +314,7 @@ export async function runEmbeddedAgentAttempt(params: {
}
const nextSessionEntry = { ...sessionEntry };
clearAutoFallbackPrimaryProbeSelection(nextSessionEntry);
sessionEntry = await persistSessionEntry({
sessionEntry = await persistAgentSession({
sessionStore,
sessionKey,
storePath,
-17
View File
@@ -19,25 +19,8 @@ import {
isDeliverableMessageChannel,
} from "../../utils/message-channel.js";
import type { AgentRunSessionTarget } from "../run-session-target.js";
import { persistSessionEntry as persistSessionEntryBase } from "./attempt-execution.shared.js";
import type { AgentCommandOpts } from "./types.js";
type PersistSessionEntryParams = {
sessionStore: Record<string, SessionEntry>;
sessionKey: string;
storePath: string;
initialEntry: SessionEntry;
entry: SessionEntry;
};
export async function persistSessionEntry(
params: PersistSessionEntryParams & {
shouldPersist?: (entry: SessionEntry | undefined) => boolean;
},
): Promise<SessionEntry | undefined> {
return await persistSessionEntryBase(params);
}
export function clearPendingFinalDelivery(entry: SessionEntry, updatedAt: number): SessionEntry {
return {
...entry,
+3 -3
View File
@@ -6,9 +6,9 @@ import { registerAgentRunContext } from "../../infra/agent-run-registry.js";
import { applyVerboseOverride } from "../../sessions/level-overrides.js";
import { recordSessionHumanDirectMessage } from "../../sessions/session-state-events.js";
import { resolveEffectiveAgentSkillFilter } from "../../skills/discovery/agent-filter.js";
import { persistAgentSession } from "./attempt-execution.shared.js";
import { resolveAgentRunContext } from "./run-context.js";
import { loadExecDefaultsRuntime, loadSkillsRuntime } from "./runtime-loaders.js";
import { persistSessionEntry } from "./session-helpers.js";
import type { AgentCommandOpts } from "./types.js";
export async function prepareEmbeddedSessionState(params: {
@@ -100,7 +100,7 @@ export async function prepareEmbeddedSessionState(params: {
sessionStartedAt: current.sessionStartedAt ?? now,
skillsSnapshot,
};
sessionEntry = await persistSessionEntry({
sessionEntry = await persistAgentSession({
sessionStore: params.sessionStore,
sessionKey: params.sessionKey,
storePath: params.storePath,
@@ -131,7 +131,7 @@ export async function prepareEmbeddedSessionState(params: {
agentStatus: undefined,
};
applyVerboseOverride(next, params.verboseOverride);
sessionEntry = await persistSessionEntry({
sessionEntry = await persistAgentSession({
sessionStore: params.sessionStore,
sessionKey: params.sessionKey,
storePath: params.storePath,
+2 -2
View File
@@ -8,7 +8,7 @@ import {
import type { SessionEntry } from "../config/sessions/types.js";
import { isSubagentSessionKey } from "../routing/session-key.js";
import type { DeliveryContext } from "../utils/delivery-context.shared.js";
import { persistSessionEntry } from "./command/attempt-execution.shared.js";
import { persistAgentSession } from "./command/attempt-execution.shared.js";
type PersistPendingFinalDeliveryMarkerParams = {
deliver: boolean;
@@ -65,7 +65,7 @@ export async function persistPendingFinalDeliveryMarker(
}
const now = Date.now();
const persisted = await persistSessionEntry({
const persisted = await persistAgentSession({
sessionStore: params.sessionStore,
sessionKey: params.sessionKey,
storePath: params.storePath,
+2 -2
View File
@@ -12,7 +12,7 @@ import {
import { resolveTextCommand } from "../commands-registry.js";
import { resolveCommandSurfaceChannel } from "./channel-context.js";
import { commandReply, defineAuthorizedTextCommand } from "./command-gates.js";
import { persistSessionEntry } from "./commands-session-store.js";
import { persistCommandSession } from "./commands-session-store.js";
import type { CommandHandler, HandleCommandsParams } from "./commands-types.js";
const DOCK_KEY_PREFIX = "dock:";
@@ -165,7 +165,7 @@ export const handleDockCommand: CommandHandler = defineAuthorizedTextCommand(
origin: sessionDeliveryOrigin(sessionEntry),
});
params.sessionEntry = sessionEntry;
const persisted = await persistSessionEntry({
const persisted = await persistCommandSession({
...params,
touchedFields: ["delivery"],
});
@@ -4,7 +4,7 @@ import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import { loadSessionEntry, replaceSessionEntry } from "../../config/sessions/session-accessor.js";
import type { SessionEntry } from "../../config/sessions/types.js";
import { persistAbortTargetEntry, persistSessionEntry } from "./commands-session-store.js";
import { persistAbortTargetEntry, persistCommandSession } from "./commands-session-store.js";
async function withTempStore<T>(run: (storePath: string) => Promise<T>): Promise<T> {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-command-session-store-"));
@@ -27,7 +27,7 @@ describe("commands session store persistence", () => {
const sessionStore: Record<string, SessionEntry> = { [sessionKey]: entry };
await expect(
persistSessionEntry({
persistCommandSession({
allowCreateSessionEntry: true,
sessionEntry: entry,
sessionStore,
@@ -60,7 +60,7 @@ describe("commands session store persistence", () => {
const sessionStore: Record<string, SessionEntry> = { [sessionKey]: entry };
await expect(
persistSessionEntry({
persistCommandSession({
sessionEntry: entry,
sessionStore,
sessionKey,
@@ -88,7 +88,7 @@ describe("commands session store persistence", () => {
delivery: { kind: "none" },
};
const seedEntry = { ...entry };
await persistSessionEntry({
await persistCommandSession({
allowCreateSessionEntry: true,
sessionEntry: seedEntry,
sessionStore: { [sessionKey]: seedEntry },
@@ -99,7 +99,7 @@ describe("commands session store persistence", () => {
const sessionStore: Record<string, SessionEntry> = { [sessionKey]: entry };
await expect(
persistSessionEntry({
persistCommandSession({
sessionEntry: entry,
sessionStore,
sessionKey,
@@ -147,7 +147,7 @@ describe("commands session store persistence", () => {
label: "After rename",
pinnedAt: undefined,
};
await persistSessionEntry({
await persistCommandSession({
allowCreateSessionEntry: true,
sessionEntry: concurrentEntry,
sessionStore: { [sessionKey]: concurrentEntry },
@@ -160,7 +160,7 @@ describe("commands session store persistence", () => {
try {
await expect(
persistSessionEntry({
persistCommandSession({
sessionEntry: entry,
sessionStore,
sessionKey,
@@ -214,7 +214,7 @@ describe("commands session store persistence", () => {
const sessionStore = { [sessionKey]: sessionEntry };
await expect(
persistSessionEntry({
persistCommandSession({
initialSessionEntry: initialEntry,
sessionEntry,
sessionStore,
@@ -246,7 +246,7 @@ describe("commands session store persistence", () => {
const sessionStore = { [sessionKey]: sessionEntry };
await expect(
persistSessionEntry({
persistCommandSession({
initialSessionEntry: initialEntry,
sessionEntry,
sessionStore,
@@ -286,7 +286,7 @@ describe("commands session store persistence", () => {
const sessionStore = { [sessionKey]: sessionEntry };
await expect(
persistSessionEntry({
persistCommandSession({
initialSessionEntry: initialEntry,
sessionEntry,
sessionStore,
@@ -35,7 +35,7 @@ export function resolveCommandSessionEntryForKey(
};
}
export async function persistSessionEntry(params: PersistSessionEntryParams): Promise<boolean> {
export async function persistCommandSession(params: PersistSessionEntryParams): Promise<boolean> {
if (!params.sessionEntry || !params.sessionStore || !params.sessionKey) {
return false;
}
@@ -9,7 +9,7 @@ const persistenceConflictReply = vi.hoisted(() => ({
}));
vi.mock("./commands-session-store.js", () => ({
persistSessionEntry: persistSessionEntryMock,
persistCommandSession: persistSessionEntryMock,
sessionEntryPersistenceConflictReply: () => persistenceConflictReply,
}));
+7 -7
View File
@@ -58,7 +58,7 @@ import {
} from "./command-gates.js";
import { handleAbortTrigger, handleStopCommand } from "./commands-session-abort.js";
import {
persistSessionEntry,
persistCommandSession,
sessionEntryPersistenceConflictReply,
} from "./commands-session-store.js";
import type { CommandHandler, HandleCommandsParams } from "./commands-types.js";
@@ -237,7 +237,7 @@ export const handleActivationCommand: CommandHandler = async (params, allowTextC
params.sessionEntry.groupActivation = activationCommand.mode;
params.sessionEntry.groupActivationNeedsSystemIntro = true;
if (
!(await persistSessionEntry({
!(await persistCommandSession({
...params,
touchedFields: ["groupActivation", "groupActivationNeedsSystemIntro"],
}))
@@ -267,7 +267,7 @@ export const handleSendPolicyCommand: CommandHandler = defineAuthorizedTextComma
} else {
params.sessionEntry.sendPolicy = sendPolicyCommand.mode;
}
if (!(await persistSessionEntry({ ...params, touchedFields: ["sendPolicy"] }))) {
if (!(await persistCommandSession({ ...params, touchedFields: ["sendPolicy"] }))) {
return sessionEntryPersistenceConflictReply();
}
}
@@ -366,7 +366,7 @@ export const handleUsageCommand: CommandHandler = defineAuthorizedTextCommand(
delete targetSessionEntry.responseUsage;
params.sessionStore[params.sessionKey] = targetSessionEntry;
if (
!(await persistSessionEntry({
!(await persistCommandSession({
...params,
sessionEntry: targetSessionEntry,
touchedFields: ["responseUsage"],
@@ -392,7 +392,7 @@ export const handleUsageCommand: CommandHandler = defineAuthorizedTextCommand(
targetSessionEntry.responseUsage = next;
params.sessionStore[params.sessionKey] = targetSessionEntry;
if (
!(await persistSessionEntry({
!(await persistCommandSession({
...params,
sessionEntry: targetSessionEntry,
touchedFields: ["responseUsage"],
@@ -440,7 +440,7 @@ export const handleFastCommand: CommandHandler = defineAuthorizedTextCommand(
if (targetSessionEntry && params.sessionStore && params.sessionKey) {
delete targetSessionEntry.fastMode;
if (
!(await persistSessionEntry({
!(await persistCommandSession({
...params,
sessionEntry: targetSessionEntry,
touchedFields: ["fastMode"],
@@ -457,7 +457,7 @@ export const handleFastCommand: CommandHandler = defineAuthorizedTextCommand(
if (targetSessionEntry && params.sessionStore && params.sessionKey) {
targetSessionEntry.fastMode = nextMode;
if (
!(await persistSessionEntry({
!(await persistCommandSession({
...params,
sessionEntry: targetSessionEntry,
touchedFields: ["fastMode"],
+3 -3
View File
@@ -43,7 +43,7 @@ import {
matchCommandPrefix,
} from "./command-gates.js";
import {
persistSessionEntry,
persistCommandSession,
sessionEntryPersistenceConflictReply,
} from "./commands-session-store.js";
import type { CommandHandler, CommandHandlerResult } from "./commands-types.js";
@@ -205,7 +205,7 @@ async function handleTtsChatAction(
return { shouldContinue: false, reply: ttsUsage() };
}
if (!(await persistSessionEntry({ ...params, touchedFields: ["ttsAuto"] }))) {
if (!(await persistCommandSession({ ...params, touchedFields: ["ttsAuto"] }))) {
return sessionEntryPersistenceConflictReply();
}
return stopWithText(replyText);
@@ -257,7 +257,7 @@ async function handleTtsLatestAction(
params.sessionEntry.lastTtsReadLatestHash = hash;
params.sessionEntry.lastTtsReadLatestAt = Date.now();
if (
!(await persistSessionEntry({
!(await persistCommandSession({
...params,
touchedFields: ["lastTtsReadLatestHash", "lastTtsReadLatestAt"],
}))
@@ -11,17 +11,6 @@ export function cloneRecord<T extends JsonRecord>(value: T | undefined): T {
return { ...value } as T;
}
/** Ensure a nested config value is a mutable record and return it. */
export function ensureRecord(target: JsonRecord, key: string): JsonRecord {
const current = target[key];
if (isRecord(current)) {
return current;
}
const next: JsonRecord = {};
target[key] = next;
return next;
}
/** Own-property guard used by migrations that must preserve falsy values. */
export function hasOwnKey(target: JsonRecord, key: string): boolean {
return Object.hasOwn(target, key);
@@ -1,8 +1,7 @@
// Legacy web tool config migrations into plugin-owned provider config.
import { mergeMissing } from "../../../config/legacy.shared.js";
import { ensureRecord, mergeMissing } from "../../../config/legacy.shared.js";
import {
cloneRecord,
ensureRecord,
hasOwnKey,
isRecord,
type JsonRecord,
@@ -11,7 +11,7 @@ import type {
SessionTranscriptReadScope,
TranscriptEvent,
} from "./session-accessor.sqlite-contract.js";
import { normalizeSqliteNumber } from "./session-accessor.sqlite-normalize.js";
import { coerceSqliteNumber } from "./session-accessor.sqlite-normalize.js";
import {
getSessionKysely,
resolveSqliteTranscriptReadScope,
@@ -183,7 +183,7 @@ function readRawDeltaInTransaction(
.limit(1),
);
const maxSeq = Math.min(
frontier ? normalizeSqliteNumber(frontier.seq) : -1,
frontier ? coerceSqliteNumber(frontier.seq) : -1,
beforeEventSeq === undefined ? Number.POSITIVE_INFINITY : beforeEventSeq - 1,
);
if (cursor.lastSeq > maxSeq) {
@@ -210,8 +210,8 @@ function readRawDeltaInTransaction(
.orderBy("seq", "asc")
.limit(maxEvents + 1),
).rows.map((row) => ({
seq: normalizeSqliteNumber(row.seq),
serializedBytes: normalizeSqliteNumber(row.serialized_bytes),
seq: coerceSqliteNumber(row.seq),
serializedBytes: coerceSqliteNumber(row.serialized_bytes),
}));
let serializedBytes = 0;
@@ -239,7 +239,7 @@ function readRawDeltaInTransaction(
.orderBy("seq", "asc"),
).rows.map((row) => ({
event: JSON.parse(row.event_json) as TranscriptEvent,
seq: normalizeSqliteNumber(row.seq),
seq: coerceSqliteNumber(row.seq),
}));
const nextCursor = encodeRawTranscriptCursor({ ...cursor, lastSeq });
const requiredBytes =
@@ -51,7 +51,7 @@ import {
} from "./session-accessor.sqlite-maintenance.js";
import {
createFallbackSessionEntry,
normalizeSqliteNumber,
coerceSqliteNumber,
} from "./session-accessor.sqlite-normalize.js";
import {
cloneSessionEntry,
@@ -297,7 +297,7 @@ export function countSqliteSessionEntryRowsReadOnly(scope: SessionEntryListScope
.selectFrom("session_nodes")
.select((expression) => expression.fn.countAll<number | bigint>().as("count")),
);
return row ? normalizeSqliteNumber(row.count) : 0;
return row ? coerceSqliteNumber(row.count) : 0;
}, toDatabaseOptions(resolved));
return result.found ? result.value : 0;
}
@@ -411,7 +411,7 @@ export function readSqliteSessionUpdatedAt(scope: SessionAccessScope): number |
const resolved = resolveSqliteScope(scope);
const database = openOpenClawAgentDatabase(toDatabaseOptions(resolved));
const row = readSessionEntryRow(database, resolved.sessionKey)?.row;
return row ? normalizeSqliteNumber(row.updated_at) : undefined;
return row ? coerceSqliteNumber(row.updated_at) : undefined;
}
/** Applies a partial entry update to the additive SQLite session store. */
@@ -31,7 +31,7 @@ import type {
SqliteProjectedLifecycleMutation,
SqliteSessionEntryRemovalPlan,
} from "./session-accessor.sqlite-lifecycle-types.js";
import { normalizeSqliteNumber } from "./session-accessor.sqlite-normalize.js";
import { coerceSqliteNumber } from "./session-accessor.sqlite-normalize.js";
import { loadSqliteTranscriptEventsFromDatabase } from "./session-accessor.sqlite-read.js";
import { collectSqliteSessionStateIdsForEntry } from "./session-accessor.sqlite-references.js";
import { cloneSessionEntry, getSessionKysely } from "./session-accessor.sqlite-scope.js";
@@ -103,7 +103,7 @@ function readSessionTranscriptUpdatedAt(
if (row?.updated_at === null || row?.updated_at === undefined) {
return undefined;
}
return normalizeSqliteNumber(row.updated_at);
return coerceSqliteNumber(row.updated_at);
}
function sqliteTranscriptStateIsReclaimable(params: {
@@ -611,7 +611,7 @@ export function planSqliteSessionLifecycleArtifactCleanup(
!sqliteTranscriptStateIsReclaimable({
database,
// Admission updates the node even when a run has no event yet or reuses old events.
sessionUpdatedAt: normalizeSqliteNumber(row.updated_at),
sessionUpdatedAt: coerceSqliteNumber(row.updated_at),
sessionId: row.current_session_id,
nowMs: params.nowMs,
orphanTranscriptMinAgeMs: params.orphanTranscriptMinAgeMs,
@@ -21,6 +21,6 @@ export function normalizeSqliteChatType(value: unknown): "direct" | "group" | "c
return null;
}
export function normalizeSqliteNumber(value: number | bigint): number {
export function coerceSqliteNumber(value: number | bigint): number {
return typeof value === "bigint" ? Number(value) : value;
}
@@ -19,7 +19,7 @@ import type {
SessionTranscriptStats,
TranscriptEvent,
} from "./session-accessor.sqlite-contract.js";
import { normalizeSqliteNumber } from "./session-accessor.sqlite-normalize.js";
import { coerceSqliteNumber } from "./session-accessor.sqlite-normalize.js";
import {
getSessionKysely,
resolveSqliteTranscriptReadScope,
@@ -127,7 +127,7 @@ export function loadSqliteTranscriptEventRowsAfterSeqSync(
}
return executeSqliteQuerySync(database.db, query.orderBy("seq", "asc")).rows.map((row) => ({
event: JSON.parse(row.event_json) as TranscriptEvent,
seq: normalizeSqliteNumber(row.seq),
seq: coerceSqliteNumber(row.seq),
}));
}
@@ -150,7 +150,7 @@ export function readSqliteTranscriptEventAtSeqSync(
return row
? {
event: JSON.parse(row.event_json) as TranscriptEvent,
seq: normalizeSqliteNumber(row.seq),
seq: coerceSqliteNumber(row.seq),
}
: undefined;
}
@@ -200,7 +200,7 @@ export function readSqliteTranscriptEventRows(
).rows;
return rows.map((row) => ({
eventJson: row.event_json,
seq: normalizeSqliteNumber(row.seq),
seq: coerceSqliteNumber(row.seq),
}));
}
@@ -219,9 +219,9 @@ export function readSqliteTranscriptStorageRows(
.orderBy("seq", "asc"),
).rows;
return rows.map((row) => ({
createdAt: normalizeSqliteNumber(row.created_at),
createdAt: coerceSqliteNumber(row.created_at),
eventJson: row.event_json,
seq: normalizeSqliteNumber(row.seq),
seq: coerceSqliteNumber(row.seq),
}));
}
@@ -6,7 +6,7 @@ import {
} from "../../infra/kysely-sync.js";
import type { OpenClawAgentDatabase } from "../../state/openclaw-agent-db.js";
import { publishSqliteSessionEntryCacheInvalidation } from "./session-accessor.sqlite-entry-cache.js";
import { normalizeSqliteNumber } from "./session-accessor.sqlite-normalize.js";
import { coerceSqliteNumber } from "./session-accessor.sqlite-normalize.js";
import { getSessionKysely, type ResolvedTranscriptScope } from "./session-accessor.sqlite-scope.js";
import { parseSqliteSessionEntryJson } from "./session-accessor.sqlite-status.js";
import {
@@ -203,7 +203,7 @@ export function readNextTranscriptSeq(database: OpenClawAgentDatabase, sessionId
.where("session_id", "=", sessionId),
);
const maxSeq =
row?.max_seq === null || row?.max_seq === undefined ? -1 : normalizeSqliteNumber(row.max_seq);
row?.max_seq === null || row?.max_seq === undefined ? -1 : coerceSqliteNumber(row.max_seq);
return maxSeq + 1;
}