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