refactor: canonicalize aliases and classify test suites (#122407)

* refactor: use canonical re-export names

* fix(test): classify suite support as test source

* fix(agents): retarget gateway stub session-entry import

* test(gateway): retarget session-utils mock keys after alias removal
This commit is contained in:
Peter Steinberger
2026-08-11 21:18:34 -07:00
committed by GitHub
parent 6b0be0215f
commit f6fff4f7fd
63 changed files with 191 additions and 149 deletions
+9 -16
View File
@@ -262,8 +262,8 @@
},
{
"files": [
"**/*.test.ts",
"**/*.test.tsx",
"**/*.{test,suite}.ts",
"**/*.{test,suite}.tsx",
"**/*.e2e.test.ts",
"**/*.live.test.ts",
"**/*test-harness.ts",
@@ -282,8 +282,7 @@
"extensions/**/*.{js,ts,mts,cts}"
],
"excludeFiles": [
"**/*.test.*",
"**/*.spec.*",
"**/*.{test,spec,suite}.*",
"**/__generated__/**",
"**/generated/**",
"**/protocol-gen/**",
@@ -304,8 +303,7 @@
"extensions/**/*.{jsx,tsx}"
],
"excludeFiles": [
"**/*.test.*",
"**/*.spec.*",
"**/*.{test,spec,suite}.*",
"**/__generated__/**",
"**/generated/**",
"**/protocol-gen/**",
@@ -326,8 +324,7 @@
"extensions/**/*.{mjs,cjs}"
],
"excludeFiles": [
"**/*.test.*",
"**/*.spec.*",
"**/*.{test,spec,suite}.*",
"**/__generated__/**",
"**/generated/**",
"**/protocol-gen/**",
@@ -342,14 +339,10 @@
},
{
"files": [
"src/**/*.test.*",
"src/**/*.spec.*",
"ui/src/**/*.test.*",
"ui/src/**/*.spec.*",
"packages/**/*.test.*",
"packages/**/*.spec.*",
"extensions/**/*.test.*",
"extensions/**/*.spec.*"
"src/**/*.{test,spec,suite}.*",
"ui/src/**/*.{test,spec,suite}.*",
"packages/**/*.{test,spec,suite}.*",
"extensions/**/*.{test,spec,suite}.*"
],
"excludeFiles": [
"**/__generated__/**",
+2 -2
View File
@@ -23,9 +23,9 @@ const SURFACE_PATTERNS = [
["legacyRootAsset", /^assets\//u],
];
const CHANGED_LANE_TEST_PATH_RE =
/(?:^|\/)(?:test|__tests__)\/|(?:\.|\/)(?:test|spec|e2e|browser\.test)\.[cm]?[jt]sx?$|(?:^|\/)[^/]+\.test-(?:helpers|support)\.[cm]?[jt]sx?$/u;
/(?:^|\/)(?:test|__tests__)\/|(?:\.|\/)(?:test|spec|suite|e2e|browser\.test)\.[cm]?[jt]sx?$|(?:^|\/)[^/]+\.test-(?:helpers|support)\.[cm]?[jt]sx?$/u;
const TEST_ONLY_PATH_RE =
/(^test\/|\/test\/|\/tests\/|(?:^|\/)[^/]+\.(?:test|spec|test-utils|test-(?:helpers|support|harness)|e2e-harness)\.[cm]?[jt]sx?$)/u;
/(^test\/|\/test\/|\/tests\/|(?:^|\/)[^/]+\.(?:test|spec|suite|test-utils|test-(?:helpers|support|harness)|e2e-harness)\.[cm]?[jt]sx?$)/u;
const NATIVE_ONLY_PATH_RE =
/^(?:apps\/android\/|apps\/ios\/|apps\/macos\/|apps\/macos-mlx-tts\/|apps\/shared\/|apps\/swabble\/|Swabble\/|appcast\.xml$)/u;
+6 -1
View File
@@ -6,6 +6,7 @@ import {
findUnmatchedExplicitTestTargets,
hasImportGraphImpactOnTargets,
isTestFileTarget,
isTestSupportFileTarget,
resolveChangedTestTargetPlan,
} from "../test-projects.test-support.mts";
import {
@@ -53,7 +54,11 @@ const splitNodeTestConfigs = new Set(
);
function isTestOnlyPath(changedPath: string) {
return isTestFileTarget(changedPath) || changedPath.startsWith("test/");
return (
isTestFileTarget(changedPath) ||
isTestSupportFileTarget(changedPath) ||
changedPath.startsWith("test/")
);
}
// Inputs `build:ci-artifacts` consumes: runtime/plugin/package sources plus
+2 -2
View File
@@ -1000,12 +1000,12 @@ export function isTestFileTarget(arg: string) {
return /\.(?:test|spec)\.[cm]?[jt]sx?$/u.test(arg);
}
function isTestSupportFileTarget(arg: string) {
export function isTestSupportFileTarget(arg: string) {
if (/(?:^|\/)(?:test-helpers|test-support)(?:\/|$)/u.test(arg)) {
return true;
}
const basename = path.posix.basename(arg).replace(/\.[cm]?[jt]sx?$/u, "");
return /(?:^|[._-])test-(?:helpers|support)(?:[._-]|$)/u.test(basename);
return /(?:^|[._-])(?:suite|test-(?:helpers|support))(?:[._-]|$)/u.test(basename);
}
function isLikelyFileTarget(arg: string) {
@@ -34,7 +34,7 @@ export {
export {
listSessionsFromStoreAsync,
loadCombinedSessionStoreForGatewayCore,
loadSessionEntryReadOnly as loadSessionEntry,
loadGatewaySessionEntryReadOnly as loadSessionEntry,
resolveSessionModelRef,
} from "../../gateway/session-utils.js";
export { resolveSessionKeyFromResolveParams } from "../../gateway/sessions-resolve.js";
+7 -4
View File
@@ -32,7 +32,7 @@ import {
type SessionPullRequestGitContext,
type SessionPullRequestLocalGitDeps,
} from "./control-ui-session-prs-local-git.js";
import { loadSessionEntryReadOnly } from "./session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "./session-utils.js";
const SUCCESS_CACHE_MS = 60_000;
// Back off refetches while GitHub reports quota exhaustion; the UI keeps
@@ -94,9 +94,12 @@ type LoadSessionPullRequestDeps = SessionPullRequestLocalGitDeps & {
function resolveSessionPullRequestGitRoot(
params: ControlUiSessionPullRequestsParams,
): string | null {
const { cfg, entry, storePath, canonicalKey } = loadSessionEntryReadOnly(params.sessionKey, {
agentId: params.agentId,
});
const { cfg, entry, storePath, canonicalKey } = loadGatewaySessionEntryReadOnly(
params.sessionKey,
{
agentId: params.agentId,
},
);
// Same session/agent scoping as sessions.files.*: a missing entry means an
// unknown or deleted session, which must not fall back to some agent
// workspace and surface another checkout's PRs.
@@ -67,7 +67,7 @@ vi.mock("./http-utils.js", () => ({
vi.mock("./session-utils.js", () => ({
loadSessionEntry: loadSessionEntryMock,
loadSessionEntryReadOnly: loadSessionEntryMock,
loadGatewaySessionEntryReadOnly: loadSessionEntryMock,
resolveSessionHistoryTranscriptPathAsync: resolveSessionHistoryTranscriptPathMock,
}));
+3 -3
View File
@@ -68,7 +68,7 @@ import {
import { authorizeOperatorScopesForMethod } from "./method-scopes.js";
import { readSessionMessagesWithSourceAsync } from "./session-transcript-readers.js";
import {
loadSessionEntryReadOnly,
loadGatewaySessionEntryReadOnly,
resolveSessionHistoryTranscriptPathAsync,
} from "./session-utils.js";
@@ -974,7 +974,7 @@ async function getSessionManagedOutgoingAttachmentIndex(
}
const usesRuntimeState = !stateDir || path.resolve(stateDir) === path.resolve(resolveStateDir());
const env = stateDir ? { ...process.env, OPENCLAW_STATE_DIR: stateDir } : process.env;
type SessionEntry = ReturnType<typeof loadSessionEntryReadOnly>["entry"];
type SessionEntry = ReturnType<typeof loadGatewaySessionEntryReadOnly>["entry"];
let matched: { entry: NonNullable<SessionEntry>; storePath: string } | undefined;
for (const target of discovery.targets) {
const exact = loadExactSessionEntryReadOnlyResult({
@@ -1014,7 +1014,7 @@ async function getSessionManagedOutgoingAttachmentIndex(
let entry: SessionEntry = matched?.entry;
let storePath = matched?.storePath ?? discovery.targets[0]?.storePath ?? "";
if (!entry && usesRuntimeState) {
const loaded = loadSessionEntryReadOnly(sessionKey, { agentId: ownerAgentId });
const loaded = loadGatewaySessionEntryReadOnly(sessionKey, { agentId: ownerAgentId });
const exact = loadExactSessionEntryReadOnlyResult({
agentId: ownerAgentId,
clone: false,
+1 -1
View File
@@ -30,7 +30,7 @@ vi.mock("./session-transcript-readers.js", () => ({
}));
vi.mock("./session-utils.js", () => ({
loadSessionEntry: mocks.loadSessionEntry,
loadSessionEntryReadOnly: mocks.loadSessionEntry,
loadGatewaySessionEntryReadOnly: mocks.loadSessionEntry,
}));
import { mintMcpAppViewFromTranscript, restoreMcpAppView } from "./mcp-app-reconstruction.js";
+2 -2
View File
@@ -14,7 +14,7 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
import { resolveAgentIdFromSessionKey } from "../routing/session-key.js";
import { getOrCreatePromise } from "../shared/lazy-promise.js";
import { visitSessionMessagesAsync } from "./session-transcript-readers.js";
import { loadSessionEntryReadOnly } from "./session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "./session-utils.js";
const MCP_APP_RESTORE_IN_FLIGHT_KEY = Symbol.for("openclaw.mcpAppRestoreInFlight");
@@ -252,7 +252,7 @@ async function reconstructMcpAppView(params: {
viewId?: string;
}): Promise<ReconstructionResult | undefined> {
const agentId = resolveAgentIdFromSessionKey(params.sessionKey);
const loaded = loadSessionEntryReadOnly(params.sessionKey, { agentId });
const loaded = loadGatewaySessionEntryReadOnly(params.sessionKey, { agentId });
const sessionId = loaded.entry?.sessionId;
if (!sessionId) {
return undefined;
+1 -1
View File
@@ -75,7 +75,7 @@ vi.mock("./session-utils.js", () => {
}));
return {
loadSessionEntry,
loadSessionEntryReadOnly: loadSessionEntry,
loadGatewaySessionEntryReadOnly: loadSessionEntry,
};
});
+3 -3
View File
@@ -65,7 +65,7 @@ import {
resolveSessionSubscriptionKey,
resolveSessionSubscriptionKeys,
} from "./session-subscription-keys.js";
import { loadSessionEntryReadOnly } from "./session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "./session-utils.js";
import { formatForLog } from "./ws-log.js";
export {
@@ -522,7 +522,7 @@ export function createAgentEventHandler({
event: AgentEventPayload,
): { suppress: boolean } => {
try {
const { entry } = loadSessionEntryReadOnly(sessionKey, {
const { entry } = loadGatewaySessionEntryReadOnly(sessionKey, {
...(agentId ? { agentId } : {}),
clone: false,
});
@@ -1341,7 +1341,7 @@ export function createAgentEventHandler({
return runVerbose ?? "off";
}
try {
const { cfg, entry } = loadSessionEntryReadOnly(sessionKey);
const { cfg, entry } = loadGatewaySessionEntryReadOnly(sessionKey);
const sessionVerbose = normalizeVerboseLevel(entry?.verboseLevel);
const sessionUpdatedAt = typeof entry?.updatedAt === "number" ? entry.updatedAt : undefined;
const sessionChangedAfterRunStarted =
@@ -10,7 +10,7 @@ export { getRegisteredAgentHarness } from "../../../agents/harness/registry.js";
export { resolveReplyToMode } from "../../../auto-reply/reply/reply-threading.js";
export { resolveRuntimeConfigCacheKey } from "../../../config/config.js";
export { deliveryContextFromSession } from "../../../utils/delivery-context.shared.js";
export { loadSessionEntryReadOnly, resolveSessionModelRef } from "../../session-utils.js";
export { loadGatewaySessionEntryReadOnly, resolveSessionModelRef } from "../../session-utils.js";
export const toolsEffectiveGlobalAgentRuntimeMocks = {
resolveEffectiveToolInventory: vi.fn(
+1 -1
View File
@@ -22,7 +22,7 @@ vi.mock("../session-utils.js", async () => {
return {
...actual,
loadSessionEntry: hoisted.loadSessionEntry,
loadSessionEntryReadOnly: hoisted.loadSessionEntry,
loadGatewaySessionEntryReadOnly: hoisted.loadSessionEntry,
};
});
+3 -3
View File
@@ -34,7 +34,7 @@ import {
resolveStoredSessionKeyForAgentStore,
} from "../session-store-key.js";
import { visitSessionMessagesAsync } from "../session-transcript-readers.js";
import { loadSessionEntryReadOnly } from "../session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "../session-utils.js";
import type { GatewayRequestHandlers, RespondFn } from "./types.js";
import { assertValidParams } from "./validation.js";
@@ -526,8 +526,8 @@ async function loadArtifacts(
const scopedGlobalAgentId =
cfg?.session?.scope === "global" && sessionKey === "global" ? resolved.agentId : undefined;
const { storePath, entry } = scopedGlobalAgentId
? loadSessionEntryReadOnly(sessionKey, { agentId: scopedGlobalAgentId })
: loadSessionEntryReadOnly(sessionKey);
? loadGatewaySessionEntryReadOnly(sessionKey, { agentId: scopedGlobalAgentId })
: loadGatewaySessionEntryReadOnly(sessionKey);
const sessionId = entry?.sessionId;
if (!sessionId || !storePath) {
return { sessionKey, artifacts: [] };
@@ -36,7 +36,7 @@ import { capArrayByJsonBytes } from "../session-transcript-readers.js";
import {
buildGatewaySessionInfo,
getSessionDefaults,
loadSessionEntryReadOnly,
loadGatewaySessionEntryReadOnly,
listAgentsForGateway,
resolveSessionModelRef,
resolveSessionStoreKey,
@@ -203,7 +203,7 @@ async function handleChatHistoryRequest({
const { cfg, storePath, store, entry, canonicalKey } = measureDiagnosticsTimelineSpanSync(
`gateway.${method}.session_entry`,
() =>
loadSessionEntryReadOnly(sessionKey, {
loadGatewaySessionEntryReadOnly(sessionKey, {
...sessionLoadOptions,
includeStoreChildEntries: true,
}),
@@ -17,7 +17,7 @@ import {
readSessionMessageByIdAsync,
readSessionMessagesAsync,
} from "../session-transcript-readers.js";
import { loadSessionEntryReadOnly } from "../session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "../session-utils.js";
import { readChatHistoryMessageId } from "./chat-history-pages.js";
import { resolveRequestedChatAgentId, validateChatSelectedAgent } from "./chat-origin-routing.js";
import { normalizeOptionalChatText as normalizeOptionalText } from "./chat-text-normalization.js";
@@ -74,7 +74,10 @@ export const chatMessageGetHandlers: GatewayRequestHandlers = {
agentId: agentIdOverride,
});
const sessionLoadOptions = requestedAgentId ? { agentId: requestedAgentId } : undefined;
const { cfg, storePath, entry } = loadSessionEntryReadOnly(sessionKey, sessionLoadOptions);
const { cfg, storePath, entry } = loadGatewaySessionEntryReadOnly(
sessionKey,
sessionLoadOptions,
);
const selectedAgent = validateChatSelectedAgent({
cfg,
requestedSessionKey: sessionKey,
@@ -256,7 +256,7 @@ vi.mock("../session-utils.js", async () => {
return {
...original,
loadSessionEntry,
loadSessionEntryReadOnly: loadSessionEntry,
loadGatewaySessionEntryReadOnly: loadSessionEntry,
};
});
+2 -2
View File
@@ -17,7 +17,7 @@ import {
import { resolveSessionSubscriptionKeys } from "../session-subscription-keys.js";
import {
loadSessionEntry,
loadSessionEntryReadOnly,
loadGatewaySessionEntryReadOnly,
resolveSessionModelRef,
} from "../session-utils.js";
import { formatForLog } from "../ws-log.js";
@@ -84,7 +84,7 @@ export const chatHandlers: GatewayRequestHandlers = {
// Session entry carries per-session model overrides; utility routing must
// derive its small-model default from the provider this session actually
// uses, not the agent's configured default.
const { cfg: sessionCfg, entry } = loadSessionEntryReadOnly(
const { cfg: sessionCfg, entry } = loadGatewaySessionEntryReadOnly(
params.sessionKey,
selectedAgent.agentId ? { agentId: selectedAgent.agentId } : undefined,
);
+3 -3
View File
@@ -54,7 +54,7 @@ import {
import { parseAgentSessionKey } from "../../sessions/session-key-utils.js";
import { consumeCronCreatorAuthorityGrant } from "../cron-creator-authority-grant.js";
import { getGatewayProcessInstanceId } from "../process-instance.js";
import { loadSessionEntryReadOnly } from "../session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "../session-utils.js";
import {
assertActiveAgentRuntimeAuthority,
hasActiveAgentRuntimeAuthority,
@@ -342,7 +342,7 @@ function assertCronDoesNotTargetAgentHarness(input: {
return;
}
const loaded = loadSessionEntryReadOnly(
const loaded = loadGatewaySessionEntryReadOnly(
targetSessionKey,
input.agentId?.trim() ? { agentId: input.agentId.trim() } : {},
);
@@ -405,7 +405,7 @@ export const cronHandlers: GatewayRequestHandlers = {
const sessionKey = p.sessionKey?.trim() || undefined;
const agentId = p.agentId?.trim() || undefined;
if (sessionKey && isAgentHarnessSessionKey(sessionKey)) {
const loaded = loadSessionEntryReadOnly(sessionKey, agentId ? { agentId } : {});
const loaded = loadGatewaySessionEntryReadOnly(sessionKey, agentId ? { agentId } : {});
const harnessSessionError = loaded.entry
? resolveAgentHarnessSessionStoreEntryError(loaded.canonicalKey, loaded.entry)
: AGENT_HARNESS_SESSION_KEY_RESERVED_MESSAGE;
@@ -52,7 +52,7 @@ vi.mock("../../config/config.js", async () => {
vi.mock("../session-utils.js", () => ({
loadSessionEntry: loadGatewaySessionEntry,
loadSessionEntryReadOnly: loadGatewaySessionEntry,
loadGatewaySessionEntryReadOnly: loadGatewaySessionEntry,
}));
import { cronHandlers } from "./cron.js";
@@ -5,20 +5,20 @@ import { vi } from "vitest";
const deletedAgentSessionMocks = vi.hoisted(() => ({
loadSessionEntry: vi.fn(),
loadSessionEntryReadOnly: vi.fn(),
loadGatewaySessionEntryReadOnly: vi.fn(),
resolveDeletedAgentIdFromSessionKey: vi.fn(),
}));
vi.mock("../session-utils.js", () => ({
loadSessionEntry: deletedAgentSessionMocks.loadSessionEntry,
loadSessionEntryReadOnly: deletedAgentSessionMocks.loadSessionEntryReadOnly,
loadGatewaySessionEntryReadOnly: deletedAgentSessionMocks.loadGatewaySessionEntryReadOnly,
resolveDeletedAgentIdFromSessionKey: deletedAgentSessionMocks.resolveDeletedAgentIdFromSessionKey,
}));
/** Resets mocked deleted-agent session lookups between tests. */
export function resetDeletedAgentSessionMocks(): void {
deletedAgentSessionMocks.loadSessionEntry.mockReset();
deletedAgentSessionMocks.loadSessionEntryReadOnly.mockReset();
deletedAgentSessionMocks.loadGatewaySessionEntryReadOnly.mockReset();
deletedAgentSessionMocks.resolveDeletedAgentIdFromSessionKey.mockReset();
}
@@ -36,7 +36,10 @@ import type { PrepareGatewaySessionLifecycle } from "../session-lifecycle-prepar
import { resolveRequestedSessionAgentId as resolveRequestedGlobalAgentId } from "../session-request-agent.js";
import { resolveSessionStoreAgentId } from "../session-store-key.js";
import { readSessionMessageCountAsync } from "../session-transcript-readers.js";
import { loadSessionEntryReadOnly, resolveGatewaySessionStoreTarget } from "../session-utils.js";
import {
loadGatewaySessionEntryReadOnly,
resolveGatewaySessionStoreTarget,
} from "../session-utils.js";
import { resolveSessionPatchModelSelection } from "../sessions-patch.js";
import { createAgentRuntimeAuthorityGuard } from "./agent-runtime-authority.js";
import { chatHandlers } from "./chat.js";
@@ -288,7 +291,7 @@ export const sessionCreateHandlers: GatewayRequestHandlers = {
!hasInitialTurn &&
cfg.session?.dmScope === "main"
) {
const parent = loadSessionEntryReadOnly(
const parent = loadGatewaySessionEntryReadOnly(
parentSessionKey,
requestedAgent.agentId ? { agentId: requestedAgent.agentId } : undefined,
);
@@ -25,7 +25,7 @@ const hoisted = vi.hoisted(() => ({
vi.mock("../session-utils.js", () => ({
loadSessionEntry: hoisted.loadSessionEntry,
loadSessionEntryReadOnly: hoisted.loadSessionEntry,
loadGatewaySessionEntryReadOnly: hoisted.loadSessionEntry,
}));
vi.mock("../../agents/agent-scope.js", () => ({
+7 -4
View File
@@ -9,7 +9,7 @@ import {
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
import { normalizeAgentId, parseAgentSessionKey } from "../../routing/session-key.js";
import { applySessionDiffBaseline, loadCheckoutDiff } from "../../sessions/session-diff.js";
import { loadSessionEntryReadOnly } from "../session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "../session-utils.js";
import type { GatewayRequestHandlers } from "./types.js";
import { assertValidParams } from "./validation.js";
@@ -25,9 +25,12 @@ export async function loadSessionDiff(params: SessionsDiffParams): Promise<Sessi
deletions: 0,
...(unavailableReason ? { unavailableReason } : {}),
});
const { cfg, entry, storePath, canonicalKey } = loadSessionEntryReadOnly(params.sessionKey, {
agentId: params.agentId,
});
const { cfg, entry, storePath, canonicalKey } = loadGatewaySessionEntryReadOnly(
params.sessionKey,
{
agentId: params.agentId,
},
);
// Same session scoping as sessions.files.*: an unknown session must not fall
// back to some agent workspace and surface another checkout's diff.
if (!entry?.sessionId || !storePath) {
@@ -42,7 +42,7 @@ vi.mock("../session-utils.js", async () => {
return {
...actual,
loadSessionEntry: hoisted.loadSessionEntry,
loadSessionEntryReadOnly: hoisted.loadSessionEntry,
loadGatewaySessionEntryReadOnly: hoisted.loadSessionEntry,
};
});
@@ -32,7 +32,7 @@ vi.mock("../session-utils.js", async () => {
return {
...actual,
loadSessionEntry: hoisted.loadSessionEntry,
loadSessionEntryReadOnly: hoisted.loadSessionEntry,
loadGatewaySessionEntryReadOnly: hoisted.loadSessionEntry,
};
});
+2 -2
View File
@@ -31,7 +31,7 @@ import {
toTranscriptReadScope,
type SessionTranscriptReadScope,
} from "../session-transcript-readers.js";
import { loadSessionEntryReadOnly } from "../session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "../session-utils.js";
import {
execOpenPath,
formatOpenPathError,
@@ -526,7 +526,7 @@ async function toSessionFileEntry(
}
function loadSessionFileRoot(params: { sessionKey: string; agentId?: string }) {
const loaded = loadSessionEntryReadOnly(params.sessionKey, { agentId: params.agentId });
const loaded = loadGatewaySessionEntryReadOnly(params.sessionKey, { agentId: params.agentId });
if (!loaded.entry?.sessionId) {
return { ...loaded, agentId: undefined, root: undefined, fileRoot: undefined };
}
@@ -22,7 +22,7 @@ import { reactivateCompletedSubagentSession } from "../session-subagent-reactiva
import { readSessionMessageCountAsync } from "../session-transcript-readers.js";
import {
loadSessionEntry,
loadSessionEntryReadOnly,
loadGatewaySessionEntryReadOnly,
resolveDeletedAgentIdFromSessionKey,
} from "../session-utils.js";
import { asWorkerInferenceControl } from "../worker-environments/inference-control.js";
@@ -193,7 +193,7 @@ async function createAgentMainSessionForSend(params: {
}
const createdKey = normalizeOptionalString(createResult.payload?.key) ?? params.canonicalKey;
const loaded = loadSessionEntryReadOnly(createdKey);
const loaded = loadGatewaySessionEntryReadOnly(createdKey);
if (!loaded.entry?.sessionId) {
return {
ok: false,
@@ -49,7 +49,7 @@ vi.mock("../session-utils.js", async () => {
loadCombinedSessionStoreForGatewayMock(...args),
loadSessionEntry: (...args: unknown[]) =>
loadSessionEntryMock(...(args as [string, { agentId?: string }?])),
loadSessionEntryReadOnly: (...args: unknown[]) =>
loadGatewaySessionEntryReadOnly: (...args: unknown[]) =>
loadSessionEntryMock(...(args as [string, { agentId?: string }?])),
loadGatewaySessionRow: (...args: unknown[]) => loadGatewaySessionRowMock(...args),
};
@@ -17,7 +17,7 @@ vi.mock("../session-utils.js", async () => {
...actual,
loadSessionEntry: (...args: unknown[]) =>
loadSessionEntryMock(...(args as [string, { agentId?: string }?])),
loadSessionEntryReadOnly: (...args: unknown[]) =>
loadGatewaySessionEntryReadOnly: (...args: unknown[]) =>
loadSessionEntryMock(...(args as [string, { agentId?: string }?])),
};
});
@@ -11,7 +11,7 @@ import { expectSubagentFollowupReactivation } from "./subagent-followup.test-hel
import type { GatewayRequestContext, RespondFn } from "./types.js";
const loadSessionEntryMock = vi.fn();
const loadSessionEntryReadOnlyMock = vi.fn();
const loadGatewaySessionEntryReadOnlyMock = vi.fn();
const readSessionMessageCountAsyncMock = vi.fn();
const loadGatewaySessionRowMock = vi.fn();
const resolveDeletedAgentIdFromSessionKeyMock = vi.fn();
@@ -49,7 +49,8 @@ vi.mock("../../auto-reply/reply/queue/cleanup.js", async () => {
vi.mock("../session-utils.js", () => ({
loadSessionEntry: (...args: unknown[]) => loadSessionEntryMock(...args),
loadSessionEntryReadOnly: (...args: unknown[]) => loadSessionEntryReadOnlyMock(...args),
loadGatewaySessionEntryReadOnly: (...args: unknown[]) =>
loadGatewaySessionEntryReadOnlyMock(...args),
loadGatewaySessionRow: (...args: unknown[]) => loadGatewaySessionRowMock(...args),
resolveDeletedAgentIdFromSessionKey: (...args: unknown[]) =>
resolveDeletedAgentIdFromSessionKeyMock(...args),
@@ -113,7 +114,7 @@ function createRequestContext(overrides: Record<string, unknown> = {}): GatewayR
describe("sessions.send completed subagent follow-up status", () => {
beforeEach(() => {
loadSessionEntryMock.mockReset();
loadSessionEntryReadOnlyMock.mockReset();
loadGatewaySessionEntryReadOnlyMock.mockReset();
readSessionMessageCountAsyncMock.mockReset().mockResolvedValue(0);
loadGatewaySessionRowMock.mockReset();
resolveDeletedAgentIdFromSessionKeyMock.mockReset().mockReturnValue(null);
@@ -23,11 +23,13 @@ vi.mock("../session-utils.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../session-utils.js")>();
return {
...actual,
loadSessionEntryReadOnly: (...args: Parameters<typeof actual.loadSessionEntryReadOnly>) => {
loadGatewaySessionEntryReadOnly: (
...args: Parameters<typeof actual.loadGatewaySessionEntryReadOnly>
) => {
if (sessionReadState.mode === "throw") {
throw new Error("session inspection unavailable");
}
const loaded = actual.loadSessionEntryReadOnly(...args);
const loaded = actual.loadGatewaySessionEntryReadOnly(...args);
return sessionReadState.mode === "present"
? { ...loaded, entry: { sessionId: "surviving-session", updatedAt: 1 } }
: loaded;
@@ -18,7 +18,7 @@ import { resolveSessionWorkStartError } from "../../config/sessions.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { normalizeAgentId, parseAgentSessionKey } from "../../routing/session-key.js";
import { buildDashboardSessionKey } from "../session-create-service.js";
import { loadSessionEntryReadOnly } from "../session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "../session-utils.js";
import {
abandonTaskSuggestionAcceptance,
beginTaskSuggestionAcceptance,
@@ -107,7 +107,7 @@ async function rollbackSuggestedTaskSession(params: {
return false;
}
try {
return !loadSessionEntryReadOnly(params.key, { agentId: params.agentId }).entry;
return !loadGatewaySessionEntryReadOnly(params.key, { agentId: params.agentId }).entry;
} catch {
return false;
}
@@ -358,9 +358,9 @@ async function deliverSuggestedTaskToSourceSession(params: {
const agentId = resolveSuggestionAgentId(params.suggestion, params.options);
const fail = (error: NonNullable<Parameters<RespondFn>[2]>) =>
failSuggestedTaskDelivery({ taskId: params.taskId, options: params.options, error });
let source: ReturnType<typeof loadSessionEntryReadOnly>;
let source: ReturnType<typeof loadGatewaySessionEntryReadOnly>;
try {
source = loadSessionEntryReadOnly(params.suggestion.sessionKey, { agentId });
source = loadGatewaySessionEntryReadOnly(params.suggestion.sessionKey, { agentId });
} catch (error) {
return fail(errorShape(ErrorCodes.UNAVAILABLE, formatErrorMessage(error)));
}
@@ -25,4 +25,4 @@ export {
getActivePluginRegistryVersion,
} from "../../plugins/runtime.js";
export { deliveryContextFromSession } from "../../utils/delivery-context.shared.js";
export { loadSessionEntryReadOnly, resolveSessionModelRef } from "../session-utils.js";
export { loadGatewaySessionEntryReadOnly, resolveSessionModelRef } from "../session-utils.js";
@@ -75,7 +75,7 @@ const runtimeMocks = vi.hoisted(() => ({
vi.mock("./tools-effective.runtime.js", () => ({
...runtimeMocks,
loadSessionEntryReadOnly: runtimeMocks.loadSessionEntry,
loadGatewaySessionEntryReadOnly: runtimeMocks.loadSessionEntry,
}));
const nodePluginToolSnapshotMocks = vi.hoisted(() => ({
@@ -29,7 +29,7 @@ import {
getActivePluginRegistryVersion,
getRegisteredAgentHarness,
listAgentIds,
loadSessionEntryReadOnly,
loadGatewaySessionEntryReadOnly,
peekSessionMcpRuntime,
resolveAgentDir,
resolveAgentWorkspaceDir,
@@ -531,7 +531,7 @@ function resolveTrustedToolsEffectiveContext(params: {
}) {
// The effective tools request is read-only but security-sensitive. Derive
// routing/account/model context from the persisted session, not client params.
const loaded = loadSessionEntryReadOnly(
const loaded = loadGatewaySessionEntryReadOnly(
params.sessionKey,
params.requestedAgentId ? { agentId: params.requestedAgentId } : undefined,
);
@@ -23,7 +23,7 @@ vi.mock("../session-utils.js", async () => {
const actual = await vi.importActual<typeof import("../session-utils.js")>("../session-utils.js");
return {
...actual,
loadSessionEntryReadOnly: vi.fn(actual.loadSessionEntryReadOnly),
loadGatewaySessionEntryReadOnly: vi.fn(actual.loadGatewaySessionEntryReadOnly),
loadCombinedSessionStoreForGatewayCore: vi.fn(() => ({ storePath: "(multiple)", store: {} })),
};
});
@@ -106,7 +106,7 @@ import {
} from "../../infra/session-cost-usage.js";
import {
loadCombinedSessionStoreForGatewayCore,
loadSessionEntryReadOnly,
loadGatewaySessionEntryReadOnly,
} from "../session-utils.js";
import { testApi, usageHandlers } from "./usage.js";
@@ -190,7 +190,7 @@ function expectSuccessfulSessionsUsage(
function mockStoredSession(key: string, sessionId: string) {
const entry = { sessionId, updatedAt: 1_000 };
vi.mocked(loadSessionEntryReadOnly).mockReturnValueOnce({
vi.mocked(loadGatewaySessionEntryReadOnly).mockReturnValueOnce({
cfg: TEST_RUNTIME_CONFIG,
canonicalKey: key,
entry,
+2 -2
View File
@@ -68,7 +68,7 @@ import {
} from "../session-store-key.js";
import {
loadCombinedSessionStoreForGatewayCore,
loadSessionEntryReadOnly,
loadGatewaySessionEntryReadOnly,
} from "../session-utils.js";
import { loadUsageStatusStaleWhileRevalidate } from "./models-auth-status-usage-cache.js";
import type { GatewayRequestHandlers, RespondFn } from "./types.js";
@@ -135,7 +135,7 @@ function resolveSessionUsageTarget(
config: OpenClawConfig,
agentIdHint?: string,
): ResolvedSessionUsageTarget | undefined {
const { canonicalKey, entry, storePath } = loadSessionEntryReadOnly(
const { canonicalKey, entry, storePath } = loadGatewaySessionEntryReadOnly(
key,
agentIdHint ? { agentId: agentIdHint } : undefined,
);
+1 -1
View File
@@ -35,7 +35,7 @@ vi.mock("./session-utils.js", () => ({
attachOpenClawTranscriptMeta: (message: unknown) => message,
loadGatewaySessionRow: loadGatewaySessionRowMock,
loadSessionEntry: () => ({ entry: undefined, storePath: "" }),
loadSessionEntryReadOnly: loadGatewaySessionEntryReadOnlyMock,
loadGatewaySessionEntryReadOnly: loadGatewaySessionEntryReadOnlyMock,
}));
vi.mock("./session-transcript-readers.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./session-transcript-readers.js")>();
+3 -3
View File
@@ -35,7 +35,7 @@ import {
} from "./session-transcript-readers.js";
import {
loadGatewaySessionRow,
loadSessionEntryReadOnly,
loadGatewaySessionEntryReadOnly,
type GatewaySessionRow,
} from "./session-utils.js";
@@ -84,7 +84,7 @@ function readTranscriptUpdateLifecycleOwner(
const storePath = normalizeOptionalString(update.target?.storePath) ?? marker?.storePath;
const entry = storePath
? loadAccessorSessionEntryReadOnly({ agentId, sessionKey, storePath })
: loadSessionEntryReadOnly(sessionKey, agentId ? { agentId } : undefined)?.entry;
: loadGatewaySessionEntryReadOnly(sessionKey, agentId ? { agentId } : undefined)?.entry;
if (!entry || (sessionId && entry.sessionId !== sessionId)) {
return undefined;
}
@@ -300,7 +300,7 @@ async function handleTranscriptUpdateBroadcast(
}),
storePath: updateStorePath,
}
: loadSessionEntryReadOnly(sessionKey, { agentId: routingAgentId });
: loadGatewaySessionEntryReadOnly(sessionKey, { agentId: routingAgentId });
const entry = fallbackTarget?.entry;
const messageSessionId =
compatibleLegacyMarker?.sessionId ??
+3 -3
View File
@@ -12,7 +12,7 @@ import type {
SessionCompanionContextMessage,
SessionCompanionPreparedContext,
} from "./session-companion-state.js";
import { loadSessionEntryReadOnly } from "./session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "./session-utils.js";
const CONTEXT_MAX_MESSAGES = 40;
const CONTEXT_MAX_BYTES = 24 * 1024;
@@ -123,7 +123,7 @@ async function readSessionCompanionContext(params: {
sessionKey: string;
signal?: AbortSignal;
}): Promise<SessionCompanionContextReadResult> {
const loaded = loadSessionEntryReadOnly(params.sessionKey, { agentId: params.agentId });
const loaded = loadGatewaySessionEntryReadOnly(params.sessionKey, { agentId: params.agentId });
const sessionId = loaded.entry?.sessionId?.trim();
if (!sessionId) {
return { kind: "missing" };
@@ -229,6 +229,6 @@ async function readSessionCompanionContext(params: {
export const defaultSessionCompanionContextReader: SessionCompanionContextReader = {
currentSessionId: ({ agentId, sessionKey }) =>
loadSessionEntryReadOnly(sessionKey, { agentId }).entry?.sessionId?.trim() || undefined,
loadGatewaySessionEntryReadOnly(sessionKey, { agentId }).entry?.sessionId?.trim() || undefined,
read: readSessionCompanionContext,
};
+8 -5
View File
@@ -85,7 +85,10 @@ import { resolvePluginSessionOwnershipError } from "./session-plugin-ownership.j
import { resolveRequestedSessionAgentId } from "./session-request-agent.js";
import { isSessionVisibilityAllowed, resolveSessionVisibility } from "./session-sharing.js";
import { resolveSessionStoreKey } from "./session-store-key.js";
import { loadSessionEntryReadOnly, resolveGatewaySessionStoreTarget } from "./session-utils.js";
import {
loadGatewaySessionEntryReadOnly,
resolveGatewaySessionStoreTarget,
} from "./session-utils.js";
import { projectSessionsPatchEntry, resolveSessionPatchModelSelection } from "./sessions-patch.js";
type TrustedCatalogSessionTarget = {
@@ -384,7 +387,7 @@ export async function createGatewaySession(params: {
agentId,
storePath: durableStorePath,
}).some(({ sessionKey }) => sessionKey === explicitTargetKey);
if (durableEntryExists || loadSessionEntryReadOnly(explicitTargetKey).entry) {
if (durableEntryExists || loadGatewaySessionEntryReadOnly(explicitTargetKey).entry) {
return {
ok: false,
error: errorShape(
@@ -491,7 +494,7 @@ export async function createGatewaySession(params: {
}
parentSelectedAgentId = parentRequestedAgent.agentId;
}
const parent = loadSessionEntryReadOnly(
const parent = loadGatewaySessionEntryReadOnly(
parentSessionKey,
parentSelectedAgentId ? { agentId: parentSelectedAgentId } : undefined,
);
@@ -708,7 +711,7 @@ export async function createGatewaySession(params: {
params.fork === true ||
params.authorizedPluginId !== undefined)
) {
const currentParent = loadSessionEntryReadOnly(
const currentParent = loadGatewaySessionEntryReadOnly(
canonicalParentSessionKey,
parentSelectedAgentId ? { agentId: parentSelectedAgentId } : undefined,
);
@@ -793,7 +796,7 @@ export async function createGatewaySession(params: {
}
const target = creationTarget;
const currentTargetEntry = loadSessionEntryReadOnly(target.canonicalKey, {
const currentTargetEntry = loadGatewaySessionEntryReadOnly(target.canonicalKey, {
agentId: target.agentId,
}).entry;
const preparationResult = params.prepareLifecycle
+8 -8
View File
@@ -36,7 +36,7 @@ import {
listSessionsFromStore,
listSessionsFromStoreAsync,
loadSessionEntry,
loadSessionEntryReadOnly,
loadGatewaySessionEntryReadOnly,
resolveCanonicalGatewaySessionStoreKey,
resolveDeletedAgentIdFromSessionKey,
resolveGatewayModelSupportsImages,
@@ -2210,7 +2210,7 @@ describe("gateway session utils", () => {
}
});
test("loadSessionEntryReadOnly does not materialize a missing configured agent", async () => {
test("loadGatewaySessionEntryReadOnly does not materialize a missing configured agent", async () => {
resetConfigRuntimeState();
try {
await withStateDirEnv("session-utils-load-entry-read-only-", async ({ stateDir }) => {
@@ -2223,7 +2223,7 @@ describe("gateway session utils", () => {
} as OpenClawConfig;
setRuntimeConfigSnapshot(cfg, cfg);
const loaded = loadSessionEntryReadOnly("agent:missing:main");
const loaded = loadGatewaySessionEntryReadOnly("agent:missing:main");
expect(loaded.entry).toBeUndefined();
expect(fs.existsSync(path.join(stateDir, "agents", "missing"))).toBe(false);
@@ -2233,7 +2233,7 @@ describe("gateway session utils", () => {
}
});
test("loadSessionEntryReadOnly clones only the selected row and direct children", async () => {
test("loadGatewaySessionEntryReadOnly clones only the selected row and direct children", async () => {
resetConfigRuntimeState();
try {
await withStateDirEnv("session-utils-exact-read-only-", async ({ stateDir }) => {
@@ -2261,7 +2261,7 @@ describe("gateway session utils", () => {
).toContain(childKey);
const cloneSpy = vi.spyOn(globalThis, "structuredClone");
try {
expect(loadSessionEntryReadOnly(childKey, { clone: false }).entry).toMatchObject({
expect(loadGatewaySessionEntryReadOnly(childKey, { clone: false }).entry).toMatchObject({
sessionId: "child",
spawnedBy: parentKey,
});
@@ -2273,7 +2273,7 @@ describe("gateway session utils", () => {
storePath,
}).map((item) => item.sessionKey),
).toEqual([childKey]);
const loaded = loadSessionEntryReadOnly("main", {
const loaded = loadGatewaySessionEntryReadOnly("main", {
includeStoreChildEntries: true,
});
@@ -2322,7 +2322,7 @@ describe("gateway session utils", () => {
expect(spawnedByReads).toBe(1);
});
test("loadSessionEntryReadOnly rejects a persisted main alias", async () => {
test("loadGatewaySessionEntryReadOnly rejects a persisted main alias", async () => {
resetConfigRuntimeState();
try {
await withStateDirEnv("session-utils-exact-alias-children-", async ({ stateDir }) => {
@@ -2345,7 +2345,7 @@ describe("gateway session utils", () => {
setRuntimeConfigSnapshot(cfg, cfg);
expect(() =>
loadSessionEntryReadOnly("main", {
loadGatewaySessionEntryReadOnly("main", {
clone: false,
includeStoreChildEntries: true,
}),
+1 -1
View File
@@ -15,7 +15,7 @@ export { loadCombinedSessionStoreForGatewayCore } from "../config/sessions/combi
export { deriveSessionTitle } from "./session-utils-core.js";
export { resolveDeletedAgentIdFromSessionKey } from "./session-utils-store.js";
export { loadGatewaySessionEntry as loadSessionEntry } from "./session-utils-store.js";
export { loadGatewaySessionEntryReadOnly as loadSessionEntryReadOnly } from "./session-utils-store.js";
export { loadGatewaySessionEntryReadOnly } from "./session-utils-store.js";
export { resolveCanonicalSessionStoreMatchFromStoreKeys } from "./session-utils-store.js";
export { resolveCanonicalSessionEntryFromStoreKeys } from "./session-utils-store.js";
export { resolveCanonicalGatewaySessionStoreKey } from "./session-utils-store.js";
@@ -28,7 +28,7 @@ vi.mock("../session-utils.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../session-utils.js")>();
return {
...actual,
loadSessionEntryReadOnly: (sessionKey: string) => ({
loadGatewaySessionEntryReadOnly: (sessionKey: string) => ({
canonicalKey: sessionKey,
entry: structuredClone(sessionEntries.get(sessionKey)),
}),
@@ -23,7 +23,7 @@ import { sha256Base64Url } from "../../infra/crypto-digest.js";
import { redactSensitiveText } from "../../logging/redact.js";
import { normalizeAgentId } from "../../routing/session-key.js";
import { WORKER_TOOL_NAMES } from "../../worker/tool-authority.js";
import { loadSessionEntryReadOnly } from "../session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "../session-utils.js";
import type { WorkerConnectionIdentity } from "./connection-identity.js";
import type { WorkerSessionPlacementStore } from "./placement-store.js";
import type { WorkerPlacementDispatchContract } from "./service-contract.js";
@@ -150,7 +150,9 @@ export function createWorkerSessionToolExecutor(params: {
}
throwIfAborted(operation.signal);
exactSource({ identity: operation.identity, placements: params.placements });
let loaded = loadSessionEntryReadOnly(operation.childSessionKey, { agentId: targetAgentId });
let loaded = loadGatewaySessionEntryReadOnly(operation.childSessionKey, {
agentId: targetAgentId,
});
let createResponse: Record<string, unknown>;
let creationAttempted = false;
if (loaded.entry?.sessionId) {
@@ -192,7 +194,7 @@ export function createWorkerSessionToolExecutor(params: {
},
);
} catch (error) {
loaded = loadSessionEntryReadOnly(operation.childSessionKey, {
loaded = loadGatewaySessionEntryReadOnly(operation.childSessionKey, {
agentId: targetAgentId,
});
if (!loaded.entry?.sessionId) {
@@ -205,7 +207,9 @@ export function createWorkerSessionToolExecutor(params: {
entry: loaded.entry,
};
}
loaded = loadSessionEntryReadOnly(operation.childSessionKey, { agentId: targetAgentId });
loaded = loadGatewaySessionEntryReadOnly(operation.childSessionKey, {
agentId: targetAgentId,
});
}
const childSessionId = loaded.entry?.sessionId;
if (!childSessionId) {
@@ -1,4 +1,4 @@
import { loadSessionEntryReadOnly } from "../session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "../session-utils.js";
import type { WorkerConnectionIdentity } from "./connection-identity.js";
import type { WorkerSessionPlacementStore } from "./placement-store.js";
@@ -14,7 +14,7 @@ export type WorkerSessionToolSource = {
ownerEpoch: number;
runId: string;
};
entry: NonNullable<ReturnType<typeof loadSessionEntryReadOnly>["entry"]>;
entry: NonNullable<ReturnType<typeof loadGatewaySessionEntryReadOnly>["entry"]>;
};
export type WorkerSessionToolTarget = {
@@ -54,7 +54,9 @@ export function resolveWorkerSessionToolSource(params: {
) {
throw new Error("Worker source session placement changed");
}
const loaded = loadSessionEntryReadOnly(placement.sessionKey, { agentId: placement.agentId });
const loaded = loadGatewaySessionEntryReadOnly(placement.sessionKey, {
agentId: placement.agentId,
});
if (
loaded.canonicalKey !== placement.sessionKey ||
loaded.entry?.sessionId !== identity.sessionId ||
@@ -83,7 +85,7 @@ export function resolveWorkerSessionToolTarget(params: {
requestedSessionKey: string;
placements: WorkerSessionPlacementStore;
}): WorkerSessionToolTarget {
const loaded = loadSessionEntryReadOnly(params.requestedSessionKey);
const loaded = loadGatewaySessionEntryReadOnly(params.requestedSessionKey);
const entry = loaded.entry;
const targetSessionId = entry?.sessionId;
if (
@@ -111,7 +113,7 @@ export function resolveWorkerSessionToolTarget(params: {
);
const parent =
sharedParentIncarnation && sourceParent && sourceParentId
? loadSessionEntryReadOnly(sourceParent)
? loadGatewaySessionEntryReadOnly(sourceParent)
: undefined;
const siblingToSibling = Boolean(
parent &&
@@ -147,7 +149,7 @@ export function assertWorkerSessionToolChild(params: {
sourceSessionId: string;
targetAgentId: string;
}): void {
const loaded = loadSessionEntryReadOnly(params.childSessionKey, {
const loaded = loadGatewaySessionEntryReadOnly(params.childSessionKey, {
agentId: params.targetAgentId,
});
const parent =
+5 -5
View File
@@ -10,19 +10,19 @@ type WriteTextAtomicBeforeRename = (params: {
export {
JsonFileReadError,
readJson,
readJson as readJsonFileStrict,
readJson as readJsonFileStrict, // Sanctioned domain alias.
readJsonIfExists,
readJsonIfExists as readDurableJsonFile,
readJsonIfExists as readDurableJsonFile, // Sanctioned domain alias.
readJsonSync,
readRootJsonObjectSync,
readRootJsonSync,
readRootStructuredFileSync,
tryReadJson,
tryReadJson as readJsonFile,
tryReadJson as readJsonFile, // Sanctioned domain alias.
tryReadJsonSync,
tryReadJsonSync as readJsonFileSync,
tryReadJsonSync as readJsonFileSync, // Sanctioned domain alias.
writeJson,
writeJson as writeJsonAtomic,
writeJson as writeJsonAtomic, // Sanctioned domain alias.
writeJsonSync,
} from "@openclaw/fs-safe/json";
+2 -2
View File
@@ -1,4 +1,4 @@
// OpenClaw root resolution imports fs through this facade so tests can replace
// filesystem behavior without mocking node:fs globally.
export { default as openClawRootFsSync } from "node:fs";
export { default as openClawRootFs } from "node:fs/promises";
export { default as openClawRootFsSync } from "node:fs"; // Sanctioned domain alias.
export { default as openClawRootFs } from "node:fs/promises"; // Sanctioned domain alias.
+1 -1
View File
@@ -17,7 +17,7 @@ export {
readSecretFileSync,
type SecretFileReadOptions,
} from "@openclaw/fs-safe/secret";
export { writeSecretFileAtomic as writePrivateSecretFileAtomic } from "@openclaw/fs-safe/secret";
export { writeSecretFileAtomic as writePrivateSecretFileAtomic } from "@openclaw/fs-safe/secret"; // Sanctioned domain alias.
export type SecretFileReadResult =
| {
@@ -5,7 +5,7 @@ import {
} from "./state-migrations.receipts.js";
import type { LegacyWorkspaceStateSource } from "./state-migrations.workspace-setup.types.js";
export { markLegacyMigrationSourceRemoved as markSourceRemoved } from "./state-migrations.receipts.js";
export { markLegacyMigrationSourceRemoved } from "./state-migrations.receipts.js";
export type MigrationReceipt = {
sourceKey: string;
@@ -27,7 +27,7 @@ import {
} from "./state-migrations.source-snapshot.js";
import type { MigrationMessages } from "./state-migrations.types.js";
import {
markSourceRemoved,
markLegacyMigrationSourceRemoved,
readReceipt,
type MigrationReceipt,
} from "./state-migrations.workspace-setup-receipts.js";
@@ -386,7 +386,7 @@ async function cleanupReceiptSource(params: {
const hasClaim = await sourceClaim.exists(true);
if (!hasSource && !hasClaim) {
if (!params.receipt.removedSource) {
markSourceRemoved(params.receipt.sourceKey, params.env);
markLegacyMigrationSourceRemoved(params.receipt.sourceKey, params.env);
}
return { changes: [], warnings: [] };
}
@@ -433,7 +433,7 @@ async function cleanupReceiptSource(params: {
}
assertConfiguredWorkspaceIdentity(params.source);
await sourceClaim.remove({ skipSourceCheck: true });
markSourceRemoved(params.receipt.sourceKey, params.env);
markLegacyMigrationSourceRemoved(params.receipt.sourceKey, params.env);
return {
changes: [],
warnings: [],
@@ -564,7 +564,7 @@ async function migrateOneSource(params: {
throw new Error("legacy workspace claim changed after import");
}
await sourceClaim.remove({ removeSource: params.removeSource, skipSourceCheck: true });
markSourceRemoved(result.sourceKey, params.env);
markLegacyMigrationSourceRemoved(result.sourceKey, params.env);
} catch (error) {
return {
changes: [],
+3 -3
View File
@@ -1,3 +1,3 @@
export { parseGenerationModelRef as parseImageGenerationModelRef } from "../../packages/media-generation-core/src/model-ref.js";
export { parseGenerationModelRef as parseMusicGenerationModelRef } from "../../packages/media-generation-core/src/model-ref.js";
export { parseGenerationModelRef as parseVideoGenerationModelRef } from "../../packages/media-generation-core/src/model-ref.js";
export { parseGenerationModelRef as parseImageGenerationModelRef } from "../../packages/media-generation-core/src/model-ref.js"; // Sanctioned domain alias.
export { parseGenerationModelRef as parseMusicGenerationModelRef } from "../../packages/media-generation-core/src/model-ref.js"; // Sanctioned domain alias.
export { parseGenerationModelRef as parseVideoGenerationModelRef } from "../../packages/media-generation-core/src/model-ref.js"; // Sanctioned domain alias.
@@ -17,7 +17,7 @@ vi.mock("../auto-reply/reply/commands-status.js", () => ({
}));
vi.mock("../gateway/session-utils.js", () => ({
loadSessionEntryReadOnly: loadSessionEntry,
loadGatewaySessionEntryReadOnly: loadSessionEntry,
}));
vi.mock("../agents/agent-scope.js", () => ({
+2 -2
View File
@@ -8,7 +8,7 @@ import { resolveCurrentDirectiveLevels } from "../auto-reply/reply/directive-han
import { createModelSelectionState } from "../auto-reply/reply/model-selection.js";
import type { ReplyPayload } from "../auto-reply/types.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { loadSessionEntryReadOnly } from "../gateway/session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "../gateway/session-utils.js";
/** Inputs for rendering direct-session status replies outside the active channel turn. */
export type ResolveDirectStatusReplyForSessionParams = {
@@ -43,7 +43,7 @@ export async function resolveDirectStatusReplyForSessionCore(
return undefined;
}
const statusLoaded = loadSessionEntryReadOnly(requestedSessionKey);
const statusLoaded = loadGatewaySessionEntryReadOnly(requestedSessionKey);
const statusCfg = statusLoaded.cfg ?? params.cfg;
const statusSessionKey = statusLoaded.canonicalKey;
const statusEntry = statusLoaded.entry;
+1 -1
View File
@@ -1,5 +1,5 @@
import { createMediaProviderRegistry } from "../media-generation/provider-registry.js";
export { normalizeCapabilityProviderId as normalizeTranscriptSourceProviderId } from "../plugins/provider-registry-shared.js";
export { normalizeCapabilityProviderId as normalizeTranscriptSourceProviderId } from "../plugins/provider-registry-shared.js"; // Sanctioned domain alias.
/** Transcript providers use targeted lookup to avoid broad capability discovery. */
export const {
+1 -1
View File
@@ -232,7 +232,7 @@ vi.mock("../gateway/session-utils.js", () => ({
loadCombinedSessionStoreForGatewayMock(...args),
loadSessionEntry: (sessionKey: string, opts?: { agentId?: string }) =>
loadSessionEntryMock(sessionKey, opts),
loadSessionEntryReadOnly: (sessionKey: string, opts?: { agentId?: string }) =>
loadGatewaySessionEntryReadOnly: (sessionKey: string, opts?: { agentId?: string }) =>
loadSessionEntryMock(sessionKey, opts),
resolveCanonicalGatewaySessionStoreKey: ({ key }: { key: string }) => ({
primaryKey: key,
+3 -3
View File
@@ -75,7 +75,7 @@ import {
listSessionsFromStoreAsync,
loadCombinedSessionStoreForGatewayCore,
loadSessionEntry,
loadSessionEntryReadOnly,
loadGatewaySessionEntryReadOnly,
resolveCanonicalGatewaySessionStoreKey,
resolveGatewaySessionStoreTargetWithStore,
resolveSessionModelRef,
@@ -627,7 +627,7 @@ export class EmbeddedTuiBackend implements TuiBackend {
async loadHistory(opts: { sessionKey: string; agentId?: string; limit?: number }) {
await this.ready;
const loadOptions = opts.agentId ? { agentId: opts.agentId } : undefined;
const { cfg, storePath, store, entry, canonicalKey } = loadSessionEntryReadOnly(
const { cfg, storePath, store, entry, canonicalKey } = loadGatewaySessionEntryReadOnly(
opts.sessionKey,
{ ...loadOptions, includeStoreChildEntries: true },
);
@@ -807,7 +807,7 @@ export class EmbeddedTuiBackend implements TuiBackend {
async resetSession(key: string, reason?: "new" | "reset", opts?: { agentId?: string }) {
await this.ready;
if (loadSessionEntryReadOnly(key, opts).entry?.incognito === true) {
if (loadGatewaySessionEntryReadOnly(key, opts).entry?.incognito === true) {
throw new Error("Incognito sessions cannot reset in place.");
}
const result = await performGatewaySessionReset({
@@ -15,7 +15,7 @@ import { READ_SCOPE } from "../../../../src/gateway/method-scopes.js";
import { clearModelAuthStatusUsageCache } from "../../../../src/gateway/server-methods/models-auth-status-usage-cache.js";
import { testApi as usageTestApi } from "../../../../src/gateway/server-methods/usage.js";
import { startGatewayServer } from "../../../../src/gateway/server.js";
import { loadSessionEntryReadOnly } from "../../../../src/gateway/session-utils.js";
import { loadGatewaySessionEntryReadOnly } from "../../../../src/gateway/session-utils.js";
import {
connectGatewayClient,
disconnectGatewayClient,
@@ -204,7 +204,7 @@ describe("gateway usage and memory APIs", () => {
sessionId: FIXTURE_SESSION_ID,
storePath: databasePath,
});
const storedSession = loadSessionEntryReadOnly(FIXTURE_SESSION_KEY);
const storedSession = loadGatewaySessionEntryReadOnly(FIXTURE_SESSION_KEY);
expect(storedSession).toMatchObject({
entry: {
sessionId: FIXTURE_SESSION_ID,
+6
View File
@@ -41,6 +41,12 @@ describe("changed path facts", () => {
isTestOnly: true,
isNativeOnly: false,
});
expect(getChangedPathFacts("src/gateway/server.auth.control-ui.suite.ts")).toMatchObject({
surface: "source",
isChangedLaneTest: true,
isTestOnly: true,
isNativeOnly: false,
});
expect(getChangedPathFacts("apps/shared/OpenClawKit/Sources/Foo.swift")).toMatchObject({
surface: "app",
isChangedLaneTest: false,
@@ -78,6 +78,9 @@ describe("CI changed Node test plan", () => {
expect(hasBuildArtifactAffectingChange(["src/agents/foo.test.ts", "test/helpers/x.ts"])).toBe(
false,
);
expect(hasBuildArtifactAffectingChange(["src/gateway/server.auth.control-ui.suite.ts"])).toBe(
false,
);
expect(hasBuildArtifactAffectingChange(["src/agents/foo.ts"])).toBe(true);
// Build-input classification: only sources and the build pipeline can
// change dist bytes; repo scripts, workflows, and qa scenarios cannot.
+13 -2
View File
@@ -210,8 +210,8 @@ describe("oxlint config", () => {
},
{
files: [
"**/*.test.ts",
"**/*.test.tsx",
"**/*.{test,suite}.ts",
"**/*.{test,suite}.tsx",
"**/*.e2e.test.ts",
"**/*.live.test.ts",
"**/*test-harness.ts",
@@ -246,6 +246,17 @@ describe("oxlint config", () => {
expect(override.excludeFiles).toContain("ui/src/i18n/locales/**");
expect(override.excludeFiles).toContain("src/wizard/i18n/locales/**");
}
for (const override of scopedBudgets.slice(0, 3)) {
expect(override.excludeFiles).toContain("**/*.{test,spec,suite}.*");
}
expect(scopedBudgets[3]?.files).toEqual(
expect.arrayContaining([
"src/**/*.{test,spec,suite}.*",
"ui/src/**/*.{test,spec,suite}.*",
"packages/**/*.{test,spec,suite}.*",
"extensions/**/*.{test,spec,suite}.*",
]),
);
expect(exactExceptions).toEqual([
{
files: ["extensions/copilot/src/event-bridge.ts"],