diff --git a/docs/.generated/plugin-sdk-api-baseline/agent-harness-runtime.json b/docs/.generated/plugin-sdk-api-baseline/agent-harness-runtime.json index e8f810baf26d..2f483b8214ff 100644 --- a/docs/.generated/plugin-sdk-api-baseline/agent-harness-runtime.json +++ b/docs/.generated/plugin-sdk-api-baseline/agent-harness-runtime.json @@ -1 +1 @@ -{"contentHash":"14115e942e60b33851a102cf0091ec7c6e29e015ad800471c095346de6708e0b","entrypoint":"agent-harness-runtime","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime"} +{"contentHash":"1207fce625d4bdc4d3c369e7ad124d595caa65655a3c754b4a6cd9ff4987c96e","entrypoint":"agent-harness-runtime","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime"} diff --git a/docs/.generated/plugin-sdk-api-baseline/agent-harness.json b/docs/.generated/plugin-sdk-api-baseline/agent-harness.json index 8eaa0707bc1c..f7975f6b6698 100644 --- a/docs/.generated/plugin-sdk-api-baseline/agent-harness.json +++ b/docs/.generated/plugin-sdk-api-baseline/agent-harness.json @@ -1 +1 @@ -{"contentHash":"944bceb3db8adc5516b7f80933a3a3dd483cfeb162b102f78e7f31e40286ac54","entrypoint":"agent-harness","importSpecifier":"openclaw/plugin-sdk/agent-harness"} +{"contentHash":"87cbc73539e9269ad9c50075617ae88a788dbdf5f36b0353a9ede3d5274b23d1","entrypoint":"agent-harness","importSpecifier":"openclaw/plugin-sdk/agent-harness"} diff --git a/docs/.generated/plugin-sdk-api-baseline/agent-runtime.json b/docs/.generated/plugin-sdk-api-baseline/agent-runtime.json index 47362a80d35d..796dc895d01a 100644 --- a/docs/.generated/plugin-sdk-api-baseline/agent-runtime.json +++ b/docs/.generated/plugin-sdk-api-baseline/agent-runtime.json @@ -1 +1 @@ -{"contentHash":"e0ee84e5e66b6c524a69e4d5791ee3d962859937e572b452db54b926f57219a3","entrypoint":"agent-runtime","importSpecifier":"openclaw/plugin-sdk/agent-runtime"} +{"contentHash":"454a3daca1cb018f303ce3dbcb8ec4ea86dbec180623822c5ca5250953402415","entrypoint":"agent-runtime","importSpecifier":"openclaw/plugin-sdk/agent-runtime"} diff --git a/docs/.generated/plugin-sdk-api-baseline/channel-core.json b/docs/.generated/plugin-sdk-api-baseline/channel-core.json index aa8fe65c5203..d78923298116 100644 --- a/docs/.generated/plugin-sdk-api-baseline/channel-core.json +++ b/docs/.generated/plugin-sdk-api-baseline/channel-core.json @@ -1 +1 @@ -{"contentHash":"0841384461391ee65d656cdcce3e956417be3a861f6a7dcde8b7592e951c8381","entrypoint":"channel-core","importSpecifier":"openclaw/plugin-sdk/channel-core"} +{"contentHash":"4f2a3bb150dc6146acb955e014280553d93eb81a65dd688ac31dbc2994861eff","entrypoint":"channel-core","importSpecifier":"openclaw/plugin-sdk/channel-core"} diff --git a/docs/.generated/plugin-sdk-api-baseline/channel-entry-contract.json b/docs/.generated/plugin-sdk-api-baseline/channel-entry-contract.json index a9e2f879fe4a..66eaeee07500 100644 --- a/docs/.generated/plugin-sdk-api-baseline/channel-entry-contract.json +++ b/docs/.generated/plugin-sdk-api-baseline/channel-entry-contract.json @@ -1 +1 @@ -{"contentHash":"87519437ca1f4ed669866f5efa28ab61836ead8c9251f2d401d755b43debdaa4","entrypoint":"channel-entry-contract","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract"} +{"contentHash":"6383e2142e408da60738625a3088e49ab5e4330235185aee229d635c31210518","entrypoint":"channel-entry-contract","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract"} diff --git a/docs/.generated/plugin-sdk-api-baseline/channel-inbound.json b/docs/.generated/plugin-sdk-api-baseline/channel-inbound.json index edcdd7b13ba0..13da9ad43bc2 100644 --- a/docs/.generated/plugin-sdk-api-baseline/channel-inbound.json +++ b/docs/.generated/plugin-sdk-api-baseline/channel-inbound.json @@ -1 +1 @@ -{"contentHash":"2598df91c020fd173ef4c0b171718a4fd90e90599536ff71ca3f4d1b3adf97fe","entrypoint":"channel-inbound","importSpecifier":"openclaw/plugin-sdk/channel-inbound"} +{"contentHash":"334b7b3c52e2b74c4540b10d94b3bc31ef6278324faa9cc48834b8599100267d","entrypoint":"channel-inbound","importSpecifier":"openclaw/plugin-sdk/channel-inbound"} diff --git a/docs/.generated/plugin-sdk-api-baseline/channel-message.json b/docs/.generated/plugin-sdk-api-baseline/channel-message.json index 26bd09664577..960e4e919946 100644 --- a/docs/.generated/plugin-sdk-api-baseline/channel-message.json +++ b/docs/.generated/plugin-sdk-api-baseline/channel-message.json @@ -1 +1 @@ -{"contentHash":"b6454c9a2e9fd243ec392fb51d1542fc176f350bc682213813f0ea7fc0166af6","entrypoint":"channel-message","importSpecifier":"openclaw/plugin-sdk/channel-message"} +{"contentHash":"682e3809e8e7425ca4153c8fab7506066aecaef876dd5db03bee0be2933792b8","entrypoint":"channel-message","importSpecifier":"openclaw/plugin-sdk/channel-message"} diff --git a/docs/.generated/plugin-sdk-api-baseline/channel-outbound.json b/docs/.generated/plugin-sdk-api-baseline/channel-outbound.json index 284d2110e135..1a86492c816c 100644 --- a/docs/.generated/plugin-sdk-api-baseline/channel-outbound.json +++ b/docs/.generated/plugin-sdk-api-baseline/channel-outbound.json @@ -1 +1 @@ -{"contentHash":"6236a390dab6017b4eef42591729660b3e114b43ae34798d41482670a3ee0613","entrypoint":"channel-outbound","importSpecifier":"openclaw/plugin-sdk/channel-outbound"} +{"contentHash":"65dfbfffad17e99767475777a5971239e3709b828bd32cd0956da07cf77ba561","entrypoint":"channel-outbound","importSpecifier":"openclaw/plugin-sdk/channel-outbound"} diff --git a/docs/.generated/plugin-sdk-api-baseline/channel-pairing.json b/docs/.generated/plugin-sdk-api-baseline/channel-pairing.json index 1d795b91ddc3..651d8f0f190c 100644 --- a/docs/.generated/plugin-sdk-api-baseline/channel-pairing.json +++ b/docs/.generated/plugin-sdk-api-baseline/channel-pairing.json @@ -1 +1 @@ -{"contentHash":"16fa9e31deb0b1f53caedf3ec5ab54f4c1f0ab62c2e6816cec9f2abf31782b65","entrypoint":"channel-pairing","importSpecifier":"openclaw/plugin-sdk/channel-pairing"} +{"contentHash":"eb3ff0bf0aafe996ccfe512e5c7c9f6643240e07666e15f1990cf24f2d894e16","entrypoint":"channel-pairing","importSpecifier":"openclaw/plugin-sdk/channel-pairing"} diff --git a/docs/.generated/plugin-sdk-api-baseline/channel-plugin-common.json b/docs/.generated/plugin-sdk-api-baseline/channel-plugin-common.json index 1d4d94c9e702..d0f6b4d3ae91 100644 --- a/docs/.generated/plugin-sdk-api-baseline/channel-plugin-common.json +++ b/docs/.generated/plugin-sdk-api-baseline/channel-plugin-common.json @@ -1 +1 @@ -{"contentHash":"b2bbf7a51a586c54b554475998b6a259be1e7fc7db9604c2e1205f56ff35f704","entrypoint":"channel-plugin-common","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common"} +{"contentHash":"2a38a18ec11bc0cda62fe57565c4785305a80fd6607339029d5cd2d996d15b23","entrypoint":"channel-plugin-common","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common"} diff --git a/docs/.generated/plugin-sdk-api-baseline/config-runtime.json b/docs/.generated/plugin-sdk-api-baseline/config-runtime.json index ddade9a935b2..527627add23e 100644 --- a/docs/.generated/plugin-sdk-api-baseline/config-runtime.json +++ b/docs/.generated/plugin-sdk-api-baseline/config-runtime.json @@ -1 +1 @@ -{"contentHash":"0032a0767e4ddf214eb517cfe7352cd3c682a936d8c33e5602b0a9813052b11d","entrypoint":"config-runtime","importSpecifier":"openclaw/plugin-sdk/config-runtime"} +{"contentHash":"587ba844fed5f7487a219d52c217e7f17ea60c00a32f7e9480d3652bb406b796","entrypoint":"config-runtime","importSpecifier":"openclaw/plugin-sdk/config-runtime"} diff --git a/docs/.generated/plugin-sdk-api-baseline/core.json b/docs/.generated/plugin-sdk-api-baseline/core.json index 43593b38c137..d8ef1751d628 100644 --- a/docs/.generated/plugin-sdk-api-baseline/core.json +++ b/docs/.generated/plugin-sdk-api-baseline/core.json @@ -1 +1 @@ -{"contentHash":"c5239579070b39b2d63a3cf6b9faa66152e5d71f64d73716a84c4cc510054d2d","entrypoint":"core","importSpecifier":"openclaw/plugin-sdk/core"} +{"contentHash":"b2c4f491e5ce945f9d2c882ddfc3ca5d14ea83aef36413068ef4ba8985cbbc9e","entrypoint":"core","importSpecifier":"openclaw/plugin-sdk/core"} diff --git a/docs/.generated/plugin-sdk-api-baseline/discord.json b/docs/.generated/plugin-sdk-api-baseline/discord.json index bdff73f27fa2..b4b0fe460766 100644 --- a/docs/.generated/plugin-sdk-api-baseline/discord.json +++ b/docs/.generated/plugin-sdk-api-baseline/discord.json @@ -1 +1 @@ -{"contentHash":"fe90342ef9fd6a4173e1feed09852380f256fafafb7836bc2c1df7400a8bcf3c","entrypoint":"discord","importSpecifier":"openclaw/plugin-sdk/discord"} +{"contentHash":"762beb61043c5c24153cd19b05994db159e7c9fd7736aefa04449d09100c412d","entrypoint":"discord","importSpecifier":"openclaw/plugin-sdk/discord"} diff --git a/docs/.generated/plugin-sdk-api-baseline/gateway-runtime.json b/docs/.generated/plugin-sdk-api-baseline/gateway-runtime.json index 7bc27301cb45..cea4ac7a107e 100644 --- a/docs/.generated/plugin-sdk-api-baseline/gateway-runtime.json +++ b/docs/.generated/plugin-sdk-api-baseline/gateway-runtime.json @@ -1 +1 @@ -{"contentHash":"737ad952cf6bb0f9d65d97bd17498af686dae166716274017fd26e5f602981d2","entrypoint":"gateway-runtime","importSpecifier":"openclaw/plugin-sdk/gateway-runtime"} +{"contentHash":"e6d85aba0cc65a4acbfb40ebcb26efda2cc7510f646141180d50cec22c07f1c0","entrypoint":"gateway-runtime","importSpecifier":"openclaw/plugin-sdk/gateway-runtime"} diff --git a/docs/.generated/plugin-sdk-api-baseline/inbound-reply-dispatch.json b/docs/.generated/plugin-sdk-api-baseline/inbound-reply-dispatch.json index bb4ed7388124..52b375c8797f 100644 --- a/docs/.generated/plugin-sdk-api-baseline/inbound-reply-dispatch.json +++ b/docs/.generated/plugin-sdk-api-baseline/inbound-reply-dispatch.json @@ -1 +1 @@ -{"contentHash":"23c052386a61874b2f9d19f872a90a9e6863971e812e573d8271b89d92181e30","entrypoint":"inbound-reply-dispatch","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch"} +{"contentHash":"e9396f9a763e82a68d9307d60b5fbc2e63adc76c060aedc6675cd7760f92642d","entrypoint":"inbound-reply-dispatch","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch"} diff --git a/docs/.generated/plugin-sdk-api-baseline/meeting-runtime.json b/docs/.generated/plugin-sdk-api-baseline/meeting-runtime.json index 739db11b8784..f4c08d899e1a 100644 --- a/docs/.generated/plugin-sdk-api-baseline/meeting-runtime.json +++ b/docs/.generated/plugin-sdk-api-baseline/meeting-runtime.json @@ -1 +1 @@ -{"contentHash":"f31959d35168e31228f318289d78117945dcd79e8ff5c57473636025c7f8d881","entrypoint":"meeting-runtime","importSpecifier":"openclaw/plugin-sdk/meeting-runtime"} +{"contentHash":"66a282f06206f78b88fba05f82e74e40d19188896b39d4af4fc80fc4eb3e3279","entrypoint":"meeting-runtime","importSpecifier":"openclaw/plugin-sdk/meeting-runtime"} diff --git a/docs/.generated/plugin-sdk-api-baseline/plugin-command-runtime.json b/docs/.generated/plugin-sdk-api-baseline/plugin-command-runtime.json index ef98be4c926b..3316374c0ba5 100644 --- a/docs/.generated/plugin-sdk-api-baseline/plugin-command-runtime.json +++ b/docs/.generated/plugin-sdk-api-baseline/plugin-command-runtime.json @@ -1 +1 @@ -{"contentHash":"3d8b13b477b5468df65ba2e282d73a99bf6e4422fe71a3811be8456318e79688","entrypoint":"plugin-command-runtime","importSpecifier":"openclaw/plugin-sdk/plugin-command-runtime"} +{"contentHash":"c8a32cced54577967d7086f920ddb577287b2e5098adac880d1845d4525c2541","entrypoint":"plugin-command-runtime","importSpecifier":"openclaw/plugin-sdk/plugin-command-runtime"} diff --git a/docs/.generated/plugin-sdk-api-baseline/plugin-entry.json b/docs/.generated/plugin-sdk-api-baseline/plugin-entry.json index a223fb67cf34..64e847bf48a4 100644 --- a/docs/.generated/plugin-sdk-api-baseline/plugin-entry.json +++ b/docs/.generated/plugin-sdk-api-baseline/plugin-entry.json @@ -1 +1 @@ -{"contentHash":"76391f09ded09871b1a5ac2fbbf2bd9901f55c529cdd97c00a0111aeb319ba33","entrypoint":"plugin-entry","importSpecifier":"openclaw/plugin-sdk/plugin-entry"} +{"contentHash":"f202742b71496ec4042df3121e8f6e99b27e6c5b9db430b2e3bef81ce6c8d87e","entrypoint":"plugin-entry","importSpecifier":"openclaw/plugin-sdk/plugin-entry"} diff --git a/docs/.generated/plugin-sdk-api-baseline/plugin-runtime.json b/docs/.generated/plugin-sdk-api-baseline/plugin-runtime.json index dd0f36b2aae7..71d19dfba892 100644 --- a/docs/.generated/plugin-sdk-api-baseline/plugin-runtime.json +++ b/docs/.generated/plugin-sdk-api-baseline/plugin-runtime.json @@ -1 +1 @@ -{"contentHash":"d05730f5b90c0164e719e97694582d27f15ec4442c8d764044b0960a6e575c93","entrypoint":"plugin-runtime","importSpecifier":"openclaw/plugin-sdk/plugin-runtime"} +{"contentHash":"012ae0b34dd7ae900b8c899967f4230a11482228e1f844f1445a3bd6aaab87b5","entrypoint":"plugin-runtime","importSpecifier":"openclaw/plugin-sdk/plugin-runtime"} diff --git a/docs/.generated/plugin-sdk-api-baseline/provider-catalog-runtime.json b/docs/.generated/plugin-sdk-api-baseline/provider-catalog-runtime.json index 3035a6691b16..c3e8f23a39b2 100644 --- a/docs/.generated/plugin-sdk-api-baseline/provider-catalog-runtime.json +++ b/docs/.generated/plugin-sdk-api-baseline/provider-catalog-runtime.json @@ -1 +1 @@ -{"contentHash":"ef4d5d011283052e9b24d4bf57fba52e38b48aef38e1be322273eaf290f3eb85","entrypoint":"provider-catalog-runtime","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime"} +{"contentHash":"b8196472e5e369784943e6c4e6b001e976b95871c0ee31005f2cdf78404f9c37","entrypoint":"provider-catalog-runtime","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime"} diff --git a/docs/.generated/plugin-sdk-api-baseline/runtime-store.json b/docs/.generated/plugin-sdk-api-baseline/runtime-store.json index f47f42a76b13..5670570f226f 100644 --- a/docs/.generated/plugin-sdk-api-baseline/runtime-store.json +++ b/docs/.generated/plugin-sdk-api-baseline/runtime-store.json @@ -1 +1 @@ -{"contentHash":"17e1939c2b38eba30dcd117a8448a54760b29cb13bd3e0cc9beb2795b0dee438","entrypoint":"runtime-store","importSpecifier":"openclaw/plugin-sdk/runtime-store"} +{"contentHash":"63d8847d2564ebc8e8e33a3aab6aa09b196bb2c9f028e9342bc9967c33a194d4","entrypoint":"runtime-store","importSpecifier":"openclaw/plugin-sdk/runtime-store"} diff --git a/docs/.generated/plugin-sdk-api-baseline/session-catalog.json b/docs/.generated/plugin-sdk-api-baseline/session-catalog.json index 55123550f743..2e1a8efe4145 100644 --- a/docs/.generated/plugin-sdk-api-baseline/session-catalog.json +++ b/docs/.generated/plugin-sdk-api-baseline/session-catalog.json @@ -1 +1 @@ -{"contentHash":"838421524189909bb222d8249ede8f53dadfdf9c73a32258c5287b35a2db21d5","entrypoint":"session-catalog","importSpecifier":"openclaw/plugin-sdk/session-catalog"} +{"contentHash":"8a19841ae6719cf88f86feb2ef6a73707d49486e8f75f9e547f565b64e1fb1af","entrypoint":"session-catalog","importSpecifier":"openclaw/plugin-sdk/session-catalog"} diff --git a/docs/.generated/plugin-sdk-api-baseline/text-runtime.json b/docs/.generated/plugin-sdk-api-baseline/text-runtime.json index 15dd77047eb2..0a08d62533cc 100644 --- a/docs/.generated/plugin-sdk-api-baseline/text-runtime.json +++ b/docs/.generated/plugin-sdk-api-baseline/text-runtime.json @@ -1 +1 @@ -{"contentHash":"c5f29e3b48cb9a92111bf565f739875a32fc3e19a009dd0bab3c27bbb71f7583","entrypoint":"text-runtime","importSpecifier":"openclaw/plugin-sdk/text-runtime"} +{"contentHash":"42848f114357ea560ab6bd4f695693ce6e2671d45efa57ffde171efd5d25f717","entrypoint":"text-runtime","importSpecifier":"openclaw/plugin-sdk/text-runtime"} diff --git a/docs/.generated/plugin-sdk-api-baseline/tool-plugin.json b/docs/.generated/plugin-sdk-api-baseline/tool-plugin.json index 56480a251689..6d2475eb1cbc 100644 --- a/docs/.generated/plugin-sdk-api-baseline/tool-plugin.json +++ b/docs/.generated/plugin-sdk-api-baseline/tool-plugin.json @@ -1 +1 @@ -{"contentHash":"a72d078274268d0e5b2c1a18a4787980c55e61479d3efb7a11dfc7303ac28d28","entrypoint":"tool-plugin","importSpecifier":"openclaw/plugin-sdk/tool-plugin"} +{"contentHash":"a7b7110ac3274061034766039c5e40e27c111f1b32666e5885938d2746305867","entrypoint":"tool-plugin","importSpecifier":"openclaw/plugin-sdk/tool-plugin"} diff --git a/docs/.generated/plugin-sdk-api-baseline/webhook-ingress.json b/docs/.generated/plugin-sdk-api-baseline/webhook-ingress.json index 0ee3dd8ff3b0..cd94992797c2 100644 --- a/docs/.generated/plugin-sdk-api-baseline/webhook-ingress.json +++ b/docs/.generated/plugin-sdk-api-baseline/webhook-ingress.json @@ -1 +1 @@ -{"contentHash":"9d8a7eec9dba85091301963980960ddb8993aaae52972fb245f25c5bd608b150","entrypoint":"webhook-ingress","importSpecifier":"openclaw/plugin-sdk/webhook-ingress"} +{"contentHash":"de0cfbef34e85ffb3b4fc69a290fdb470606c3dbafa25078662567fd8b5b202b","entrypoint":"webhook-ingress","importSpecifier":"openclaw/plugin-sdk/webhook-ingress"} diff --git a/packages/memory-host-sdk/src/host/openclaw-runtime-auth.ts b/packages/memory-host-sdk/src/host/openclaw-runtime-auth.ts index 92088d951d52..6e7964fba26f 100644 --- a/packages/memory-host-sdk/src/host/openclaw-runtime-auth.ts +++ b/packages/memory-host-sdk/src/host/openclaw-runtime-auth.ts @@ -1,6 +1,6 @@ // Memory Host SDK module implements openclaw runtime auth behavior. import { requireApiKey } from "../../../../src/agents/model-auth-runtime-shared.js"; -import type { resolveApiKeyForProvider as ResolveApiKeyForProvider } from "../../../../src/agents/model-auth.js"; +import type { resolveApiKeyForProviderCore as ResolveApiKeyForProvider } from "../../../../src/agents/model-auth.js"; // Lazy auth facade so memory host helpers avoid eager model-auth module loading. @@ -9,5 +9,5 @@ export { requireApiKey }; /** Resolve a provider API key through the core model-auth runtime. */ export const resolveApiKeyForProvider: typeof ResolveApiKeyForProvider = async (...args) => { const auth = await import("../../../../src/agents/model-auth.js"); - return auth.resolveApiKeyForProvider(...args); + return auth.resolveApiKeyForProviderCore(...args); }; diff --git a/packages/memory-host-sdk/src/host/session-files.test.ts b/packages/memory-host-sdk/src/host/session-files.test.ts index a095179c8555..b192fcd88bc8 100644 --- a/packages/memory-host-sdk/src/host/session-files.test.ts +++ b/packages/memory-host-sdk/src/host/session-files.test.ts @@ -13,7 +13,7 @@ import { appendTranscriptMessage, persistSessionTranscriptTurn, resetSessionEntryLifecycle, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../../../src/config/sessions/session-accessor.js"; import { closeOpenClawAgentDatabasesForTest } from "../../../../src/state/openclaw-agent-db.js"; import { closeOpenClawStateDatabaseForTest } from "../../../../src/state/openclaw-state-db.js"; @@ -93,11 +93,11 @@ function requireSessionEntry(entry: SessionFileEntry | null): SessionFileEntry { async function upsertTestSessionEntries( storePath: string, - entries: Record[1]>, + entries: Record[1]>, ): Promise { fsSync.mkdirSync(path.dirname(storePath), { recursive: true }); for (const [sessionKey, entry] of Object.entries(entries)) { - await upsertSessionEntry({ sessionKey, storePath }, entry); + await upsertSessionEntryCore({ sessionKey, storePath }, entry); } } @@ -153,7 +153,7 @@ describe("listSessionTranscriptCorpusEntriesForAgent", () => { const sessionKey = "agent:main:main"; fsSync.mkdirSync(sessionsDir, { recursive: true }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "retained-old", updatedAt: 10 }, ); @@ -268,7 +268,10 @@ describe("listSessionTranscriptCorpusEntriesForAgent", () => { const updatedAt = Date.parse("2026-06-25T12:00:00.000Z"); fsSync.mkdirSync(sessionsDir, { recursive: true }); - await upsertSessionEntry({ agentId: "main", sessionKey, storePath }, { sessionId, updatedAt }); + await upsertSessionEntryCore( + { agentId: "main", sessionKey, storePath }, + { sessionId, updatedAt }, + ); await persistSessionTranscriptTurn( { agentId: "main", sessionId, sessionKey, storePath }, { @@ -364,7 +367,7 @@ describe("listSessionTranscriptCorpusEntriesForAgent", () => { `${sessionId}.jsonl.deleted.2026-06-25T12-01-00.000Z`, ); fsSync.mkdirSync(sessionsDir, { recursive: true }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); diff --git a/packages/memory-host-sdk/src/host/session-files.windows-ownership.test.ts b/packages/memory-host-sdk/src/host/session-files.windows-ownership.test.ts index b522d8683d2d..9d25aeed65bc 100644 --- a/packages/memory-host-sdk/src/host/session-files.windows-ownership.test.ts +++ b/packages/memory-host-sdk/src/host/session-files.windows-ownership.test.ts @@ -6,7 +6,7 @@ import { clearRuntimeConfigSnapshot, } from "openclaw/plugin-sdk/runtime-config-snapshot"; import { describe, expect, it, vi } from "vitest"; -import { upsertSessionEntry } from "../../../../src/config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../../../../src/config/sessions/session-accessor.js"; import { closeOpenClawAgentDatabasesForTest } from "../../../../src/state/openclaw-agent-db.js"; import { closeOpenClawStateDatabaseForTest } from "../../../../src/state/openclaw-state-db.js"; import { createTempDirTracker } from "../../../../test/helpers/temp-dir.js"; @@ -124,7 +124,7 @@ describe("memory session directory ownership", () => { const storePath = path.join(sessionsDir, "sessions.json"); const sessionKey = "agent:main:chat:windows-transcript"; fsSync.mkdirSync(sessionsDir, { recursive: true }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "active", updatedAt: 1 }, ); diff --git a/scripts/lib/export-name-collision-baseline.json b/scripts/lib/export-name-collision-baseline.json index 820f7dbe5e05..fe51488c7066 100644 --- a/scripts/lib/export-name-collision-baseline.json +++ b/scripts/lib/export-name-collision-baseline.json @@ -1,402 +1 @@ -[ - { - "name": "resolveAdvertisedLanHost", - "files": [ - "src/infra/advertised-lan-host.ts", - "src/plugin-sdk/gateway-runtime.ts" - ], - "sdk": true - }, - { - "name": "resolveApiKeyForProvider", - "files": [ - "src/agents/model-auth-provider.ts", - "src/plugin-sdk/provider-auth-runtime.ts" - ], - "sdk": true - }, - { - "name": "resolveConcreteSessionStorePath", - "files": [ - "src/config/sessions/session-accessor.transcript-target.ts", - "src/gateway/session-utils-store.ts" - ] - }, - { - "name": "resolveConfiguredFallbackModel", - "files": [ - "src/agents/embedded-agent-runner/model.configured-fallback.ts", - "src/auto-reply/reply/agent-runner-core.ts" - ] - }, - { - "name": "resolveDirectStatusReplyForSession", - "files": [ - "src/plugin-sdk/command-status-runtime.ts", - "src/plugin-sdk/command-status.runtime.ts" - ], - "sdk": true - }, - { - "name": "resolveEffectivePluginActivationState", - "files": [ - "src/plugins/config-policy.ts", - "src/plugins/config-state.ts" - ] - }, - { - "name": "resolveEffectiveToolInventory", - "files": [ - "src/agents/tools-effective-inventory.ts", - "src/gateway/server-methods/__mocks__/tools-effective.runtime.ts" - ] - }, - { - "name": "resolveEffectiveToolInventoryRuntimeModelContextAsync", - "files": [ - "src/agents/tools-effective-inventory.ts", - "src/gateway/server-methods/__mocks__/tools-effective.runtime.ts" - ] - }, - { - "name": "resolveGatewayServiceProbeHosts", - "files": [ - "src/daemon/gateway-service-probe-hosts.ts", - "src/daemon/test-helpers/schtasks-fixtures.ts" - ] - }, - { - "name": "resolveHeartbeatPrompt", - "files": [ - "src/auto-reply/heartbeat.ts", - "src/infra/heartbeat-runner-config.ts" - ], - "sdk": true - }, - { - "name": "resolveMarketplaceInstallShortcut", - "files": [ - "src/cli/plugins-cli-test-helpers.ts", - "src/plugins/marketplace.ts" - ] - }, - { - "name": "resolveMemorySlotDecision", - "files": [ - "src/plugins/config-policy.ts", - "src/plugins/config-state.ts" - ] - }, - { - "name": "resolveNode", - "files": [ - "src/agents/tools/nodes-utils.ts", - "src/cli/nodes-cli/rpc.ts" - ] - }, - { - "name": "resolveNodeId", - "files": [ - "src/agents/tools/nodes-utils.ts", - "src/cli/nodes-cli/rpc.ts" - ] - }, - { - "name": "resolveOpenClawMetadata", - "files": [ - "src/hooks/frontmatter.ts", - "src/skills/loading/frontmatter.ts" - ] - }, - { - "name": "resolveProviderThinkingProfile", - "files": [ - "src/plugins/provider-runtime.ts", - "src/plugins/provider-thinking.ts" - ] - }, - { - "name": "resolveSecretInputString", - "files": [ - "src/config/types.secrets.ts", - "src/secrets/resolve-secret-input-string.ts" - ], - "sdk": true - }, - { - "name": "resolveSecretPlanTargetByPath", - "files": [ - "src/plugin-sdk/secret-ref-runtime.ts", - "src/secrets/target-registry-query.ts" - ], - "sdk": true - }, - { - "name": "resolveSessionCatalogCreateTarget", - "files": [ - "src/gateway/server-methods/session-catalog.ts", - "src/plugins/runtime/runtime-agent-session-catalog.ts" - ] - }, - { - "name": "resolveSessionFilePath", - "files": [ - "src/config/sessions/paths.ts", - "src/plugin-sdk/session-store-runtime.ts" - ], - "sdk": true - }, - { - "name": "resolveSessionMcpConfigSummary", - "files": [ - "src/agents/agent-bundle-mcp-runtime-config.ts", - "src/gateway/server-methods/__mocks__/tools-effective.runtime.ts" - ] - }, - { - "name": "resolveStorePath", - "files": [ - "src/config/sessions/paths.ts", - "src/plugin-sdk/session-store-runtime.ts" - ], - "sdk": true - }, - { - "name": "restorePersistedInstalledPluginIndexIfCurrent", - "files": [ - "src/cli/plugins-cli-test-helpers.ts", - "src/plugins/installed-plugin-index-store.ts" - ] - }, - { - "name": "runExec", - "files": [ - "src/agents/code-mode-execution.ts", - "src/process/exec.ts" - ], - "sdk": true - }, - { - "name": "runGitUpdate", - "files": [ - "src/cli/update-cli/update-command-git.ts", - "src/infra/update-runner-git.ts" - ] - }, - { - "name": "runModelsAuthLoginFlow", - "files": [ - "src/commands/models/auth.ts", - "src/plugin-sdk/provider-auth-login-flow-runtime.ts" - ], - "sdk": true - }, - { - "name": "runSystemAgentWithInference", - "files": [ - "src/cli/program.test-mocks.ts", - "src/commands/system-agent-with-inference.ts" - ] - }, - { - "name": "runTui", - "files": [ - "src/cli/program.test-mocks.ts", - "src/tui/tui.ts" - ] - }, - { - "name": "safeParseJson", - "files": [ - "src/gateway/server-json.ts", - "src/utils.ts" - ], - "sdk": true - }, - { - "name": "saveJsonFile", - "files": [ - "src/infra/json-file.ts", - "src/plugin-sdk/json-store.ts" - ], - "sdk": true - }, - { - "name": "sendDurableMessageBatch", - "files": [ - "src/channels/message/send.ts", - "src/plugin-sdk/channel-outbound.ts" - ], - "sdk": true - }, - { - "name": "setupCommand", - "files": [ - "src/cli/program.test-mocks.ts", - "src/commands/setup.ts" - ] - }, - { - "name": "setupWizardCommand", - "files": [ - "src/cli/program.test-mocks.ts", - "src/commands/onboard.ts" - ] - }, - { - "name": "sha256HexPrefix", - "files": [ - "src/infra/crypto-digest.ts", - "src/logging/redact-identifier.ts" - ], - "sdk": true - }, - { - "name": "stripTargetKindPrefix", - "files": [ - "src/infra/outbound/channel-target-prefix.ts", - "src/plugin-sdk/core.ts" - ], - "sdk": true - }, - { - "name": "textToSpeech", - "files": [ - "src/tts/tts-synthesis.ts", - "src/tts/tts.ts" - ], - "sdk": true - }, - { - "name": "theme", - "files": [ - "src/agents/modes/interactive/theme/theme.ts", - "src/tui/theme/theme.ts" - ], - "sdk": true - }, - { - "name": "toDotPath", - "files": [ - "src/cli/config-cli-path.ts", - "src/secrets/shared.ts" - ] - }, - { - "name": "toPosixPath", - "files": [ - "src/daemon/output.ts", - "src/shared/ignore-rules.ts" - ] - }, - { - "name": "triggerInternalHook", - "files": [ - "src/agents/embedded-agent-runner/compact.hooks.harness.ts", - "src/hooks/internal-hooks.ts" - ], - "sdk": true - }, - { - "name": "updateNpmInstalledHookPacks", - "files": [ - "src/cli/plugins-cli-test-helpers.ts", - "src/hooks/update.ts" - ] - }, - { - "name": "updateNpmInstalledPlugins", - "files": [ - "src/cli/plugins-cli-test-helpers.ts", - "src/plugins/update-installed.ts" - ] - }, - { - "name": "upsertSessionEntry", - "files": [ - "src/config/sessions/session-accessor.sqlite-entry.ts", - "src/plugin-sdk/session-store-runtime.ts" - ], - "sdk": true - }, - { - "name": "VERSION", - "files": [ - "src/agents/config.ts", - "src/version.ts" - ], - "sdk": true - }, - { - "name": "withDurableMessageSendContext", - "files": [ - "src/channels/message/send.ts", - "src/plugin-sdk/channel-outbound.ts" - ], - "sdk": true - }, - { - "name": "withServer", - "files": [ - "src/gateway/test-with-server.ts", - "src/plugin-sdk/test-helpers/http-test-server.ts" - ], - "sdk": true - }, - { - "name": "withTempDir", - "files": [ - "src/infra/install-source-utils.ts", - "src/test-helpers/temp-dir.ts", - "src/test-utils/temp-dir.ts" - ], - "sdk": true - }, - { - "name": "withTempHome", - "files": [ - "src/config/test-helpers.ts", - "src/plugin-sdk/test-helpers/temp-home.ts" - ], - "sdk": true - }, - { - "name": "withTimeout", - "files": [ - "src/infra/provider-usage.shared.ts", - "src/node-host/with-timeout.ts" - ], - "sdk": true - }, - { - "name": "withTrustedEnvProxyGuardedFetchMode", - "files": [ - "src/infra/net/fetch-guard.ts", - "src/plugin-sdk/fetch-runtime.ts" - ], - "sdk": true - }, - { - "name": "writeConfigFile", - "files": [ - "src/cli/plugins-cli-test-helpers.ts", - "src/config/io.runtime.ts" - ], - "sdk": true - }, - { - "name": "writePersistedInstalledPluginIndexInstallRecordsWithLease", - "files": [ - "src/cli/plugins-cli-test-helpers.ts", - "src/plugins/installed-plugin-index-records.ts" - ] - }, - { - "name": "writeSkill", - "files": [ - "src/skills/test-support/e2e-test-helpers.ts", - "src/skills/test-support/test-helpers.ts" - ], - "sdk": true - } -] +[] diff --git a/scripts/lib/wrapper-shadowing-baseline.json b/scripts/lib/wrapper-shadowing-baseline.json index 56c1b9220b4b..1fd1b336f971 100644 --- a/scripts/lib/wrapper-shadowing-baseline.json +++ b/scripts/lib/wrapper-shadowing-baseline.json @@ -29,17 +29,6 @@ "wrapped": "src/agents/model-selection-resolve.ts", "wrapper": "src/agents/model-selection.ts" }, - { - "name": "resolveApiKeyForProvider", - "wrapped": "src/agents/model-auth-provider.ts", - "wrapper": "src/plugins/runtime/runtime-model-auth.runtime.ts", - "via": "src/agents/model-auth.ts" - }, - { - "name": "resolveHeartbeatPrompt", - "wrapped": "src/auto-reply/heartbeat.ts", - "wrapper": "src/infra/heartbeat-runner-config.ts" - }, { "name": "resolvePluginProviders", "wrapped": "src/plugins/providers.runtime.ts", @@ -55,16 +44,6 @@ "wrapped": "src/plugins/provider-wizard.ts", "wrapper": "src/plugins/provider-auth-choice.runtime.ts" }, - { - "name": "resolveSecretPlanTargetByPath", - "wrapped": "src/secrets/target-registry-query.ts", - "wrapper": "src/plugin-sdk/secret-ref-runtime.ts" - }, - { - "name": "resolveSessionFilePath", - "wrapped": "src/config/sessions/paths.ts", - "wrapper": "src/plugin-sdk/session-store-runtime.ts" - }, { "name": "resolveSessionKeyForRequest", "wrapped": "src/agents/command/session.ts", @@ -75,11 +54,6 @@ "wrapped": "src/config/sessions/store-entry.ts", "wrapper": "src/plugin-sdk/session-store-runtime.ts" }, - { - "name": "resolveStorePath", - "wrapped": "src/config/sessions/paths.ts", - "wrapper": "src/plugin-sdk/session-store-runtime.ts" - }, { "name": "runProviderModelSelectedHook", "wrapped": "src/plugins/provider-wizard.ts", @@ -90,19 +64,9 @@ "wrapped": "src/security/audit.ts", "wrapper": "src/security/audit.runtime.ts" }, - { - "name": "sha256HexPrefix", - "wrapped": "src/infra/crypto-digest.ts", - "wrapper": "src/logging/redact-identifier.ts" - }, { "name": "shouldSuppressBuiltInModel", "wrapped": "src/agents/model-suppression.ts", "wrapper": "src/agents/model-suppression.runtime.ts" - }, - { - "name": "textToSpeech", - "wrapped": "src/tts/tts-synthesis.ts", - "wrapper": "src/tts/tts.ts" } ] diff --git a/scripts/sqlite-session-entry-cache-lifetime-proof.ts b/scripts/sqlite-session-entry-cache-lifetime-proof.ts index 20b758aa6b4b..e06e01f3751b 100644 --- a/scripts/sqlite-session-entry-cache-lifetime-proof.ts +++ b/scripts/sqlite-session-entry-cache-lifetime-proof.ts @@ -24,7 +24,7 @@ if (typeof forceGc !== "function") { const dataRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-sqlite-entry-cache-proof-")); const importSource = async (relativePath: string) => import(pathToFileURL(path.join(repoRoot, relativePath)).href); -const { upsertSessionEntry } = (await importSource( +const { upsertSessionEntryCore } = (await importSource( "src/config/sessions/session-accessor.js", )) as SessionAccessorModule; const { readSessionEntryCache } = (await importSource( @@ -72,7 +72,7 @@ try { ); for (const [index, scope] of readOnlyScopes.entries()) { fs.mkdirSync(scope.env.OPENCLAW_STATE_DIR, { recursive: true }); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { label: `${index}:${"x".repeat(readOnlyPayloadBytes)}`, sessionId: `read-only-${index}`, updatedAt: index + 1, @@ -106,7 +106,7 @@ try { for (let index = 0; index < lruHandleCount; index += 1) { const scope = createScope("lru", index); fs.mkdirSync(scope.env.OPENCLAW_STATE_DIR, { recursive: true }); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { label: `${index}:${"y".repeat(lruPayloadBytes)}`, sessionId: `lru-${index}`, updatedAt: index + 1, diff --git a/src/acp/event-ledger.test.ts b/src/acp/event-ledger.test.ts index d8fa799ae391..0fd961824567 100644 --- a/src/acp/event-ledger.test.ts +++ b/src/acp/event-ledger.test.ts @@ -3,7 +3,7 @@ import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { requireNodeSqlite } from "../infra/node-sqlite.js"; import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { createInMemoryAcpEventLedger, createSqliteAcpEventLedger } from "./event-ledger.js"; describe("ACP event ledger", () => { @@ -113,7 +113,7 @@ describe("ACP event ledger", () => { }); it("persists SQLite-backed replay state across ledger instances", async () => { - await withTempDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => { const databasePath = path.join(dir, "openclaw.sqlite"); const first = createSqliteAcpEventLedger({ path: databasePath, now: () => 1000 }); await first.startSession({ @@ -149,7 +149,7 @@ describe("ACP event ledger", () => { }); it("marks SQLite-backed replay incomplete when event retention truncates history", async () => { - await withTempDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => { const ledger = createSqliteAcpEventLedger({ path: path.join(dir, "openclaw.sqlite"), maxEventsPerSession: 1, @@ -184,7 +184,7 @@ describe("ACP event ledger", () => { }); it("keeps footprint aggregates consistent while the byte budget evicts", async () => { - await withTempDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => { const databasePath = path.join(dir, "openclaw.sqlite"); const ledger = createSqliteAcpEventLedger({ path: databasePath, diff --git a/src/acp/persistent-bindings.types.ts b/src/acp/persistent-bindings.types.ts index 23a25433d910..71331a8e2a80 100644 --- a/src/acp/persistent-bindings.types.ts +++ b/src/acp/persistent-bindings.types.ts @@ -5,7 +5,7 @@ import { normalizeOptionalString as normalizeText, } from "@openclaw/normalization-core/string-coerce"; import type { ChannelId } from "../channels/plugins/types.public.js"; -import { sha256HexPrefix } from "../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../infra/crypto-digest.js"; import type { SessionBindingRecord } from "../infra/outbound/session-binding-service.js"; import { normalizeAccountId, resolveAgentIdFromSessionKey } from "../routing/session-key.js"; import { sanitizeAgentId } from "../routing/session-key.js"; @@ -69,7 +69,7 @@ function buildBindingHash(params: { accountId: string; conversationId: string; }): string { - return sha256HexPrefix(`${params.channel}:${params.accountId}:${params.conversationId}`, 16); + return sha256HexPrefixCore(`${params.channel}:${params.accountId}:${params.conversationId}`, 16); } /** Builds the stable generated ACP session key for a configured binding. */ diff --git a/src/acp/runtime/session-meta-store.ts b/src/acp/runtime/session-meta-store.ts index ed8bff909299..9af525328281 100644 --- a/src/acp/runtime/session-meta-store.ts +++ b/src/acp/runtime/session-meta-store.ts @@ -2,7 +2,7 @@ import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce"; import { resolveDefaultAgentId } from "../../agents/agent-scope-config.js"; import { getRuntimeConfig } from "../../config/config.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { listSessionEntryKeysReadOnly, loadExactSessionEntryReadOnly, @@ -70,7 +70,7 @@ export function resolveSessionStorePathForAcp(params: { return { cfg, agentId, - storePath: resolveStorePath(cfg.session?.store, { agentId, env: params.env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId, env: params.env }), }; } diff --git a/src/acp/runtime/session-meta.test.ts b/src/acp/runtime/session-meta.test.ts index 20bcf59bcc22..a5e194cb3a44 100644 --- a/src/acp/runtime/session-meta.test.ts +++ b/src/acp/runtime/session-meta.test.ts @@ -7,7 +7,7 @@ import { loadSessionEntry, replaceSessionEntry } from "../../config/sessions/ses import type { SessionEntry } from "../../config/sessions/types.js"; import { closeOpenClawAgentDatabasesForTest } from "../../state/openclaw-agent-db.js"; import { closeOpenClawStateDatabaseForTest } from "../../state/openclaw-state-db.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { listAcpSessionEntries, readAcpSessionEntry, @@ -52,7 +52,7 @@ describe("ACP session metadata SQLite store", () => { }); it("persists ACP metadata in SQLite without writing sessions.json acp blocks", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = path.join(dir, "state", "openclaw.sqlite"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -102,7 +102,7 @@ describe("ACP session metadata SQLite store", () => { }); it("clears legacy embedded ACP metadata through the session accessor", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = path.join(dir, "state", "openclaw.sqlite"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -151,7 +151,7 @@ describe("ACP session metadata SQLite store", () => { }); it("creates a session-store row for new SQLite ACP sessions without embedding ACP metadata", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = path.join(dir, "state", "openclaw.sqlite"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -182,7 +182,7 @@ describe("ACP session metadata SQLite store", () => { }); it("normalizes ACP metadata lookups and writes to the resolved session-store key", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = path.join(dir, "state", "openclaw.sqlite"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -275,7 +275,7 @@ describe("ACP session metadata SQLite store", () => { }); it("keeps SQLite ACP metadata visible when legacy store keys are canonicalized", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = path.join(dir, "state", "openclaw.sqlite"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -320,7 +320,7 @@ describe("ACP session metadata SQLite store", () => { }); it("binds ACP metadata to the final accessor-selected entry for alias writes", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = path.join(dir, "state", "openclaw.sqlite"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -373,7 +373,7 @@ describe("ACP session metadata SQLite store", () => { }); it("ignores SQLite ACP metadata rows from an older lifecycle revision", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = path.join(dir, "state", "openclaw.sqlite"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -427,7 +427,7 @@ describe("ACP session metadata SQLite store", () => { }); it("reads ACP metadata rows written with the legacy session-id binding", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = path.join(dir, "state", "openclaw.sqlite"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -504,7 +504,7 @@ describe("ACP session metadata SQLite store", () => { }); it("keeps a session-id fence when ACP metadata is written before a lifecycle revision", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const databasePath = path.join(dir, "state", "openclaw.sqlite"); const sessionKey = "agent:codex:acp:pre-revision"; writeAcpSessionMetaForMigration({ @@ -548,7 +548,7 @@ describe("ACP session metadata SQLite store", () => { }); it("repairs ACP metadata rows when session-store keys are canonicalized", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = path.join(dir, "state", "openclaw.sqlite"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -601,7 +601,7 @@ describe("ACP session metadata SQLite store", () => { }); it("lists SQLite ACP rows while joining current session-store entries", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = path.join(dir, "state", "openclaw.sqlite"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -652,7 +652,7 @@ describe("ACP session metadata SQLite store", () => { }); it("honors OPENCLAW_STATE_DIR when joining listed SQLite rows to session stores", async () => { - await withTempDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-acp-meta-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir } as NodeJS.ProcessEnv; const cfg = {} as OpenClawConfig; const sessionKey = "agent:codex:acp:s1"; diff --git a/src/agents/agent-project-settings-snapshot.ts b/src/agents/agent-project-settings-snapshot.ts index 988234067d91..d42c247a2b48 100644 --- a/src/agents/agent-project-settings-snapshot.ts +++ b/src/agents/agent-project-settings-snapshot.ts @@ -7,7 +7,7 @@ import { createSubsystemLogger } from "../logging/subsystem.js"; import type { BundleMcpServerConfig } from "../plugins/bundle-mcp.js"; import { normalizePluginsConfigWithResolver, - resolveEffectivePluginActivationState, + resolvePolicyPluginActivationState, } from "../plugins/config-policy.js"; import { getCurrentPluginMetadataSnapshot } from "../plugins/current-plugin-metadata-snapshot.js"; import { @@ -150,7 +150,7 @@ export function loadEnabledBundleAgentSettingsSnapshot(params: { if (record.format !== "bundle" || settingsFiles.length === 0) { continue; } - const activationState = resolveEffectivePluginActivationState({ + const activationState = resolvePolicyPluginActivationState({ id: record.id, origin: record.origin, config: normalizedPlugins, diff --git a/src/agents/auth-profiles/legacy-source-diagnostic.test.ts b/src/agents/auth-profiles/legacy-source-diagnostic.test.ts index d59248a5dc45..da8155cedcf6 100644 --- a/src/agents/auth-profiles/legacy-source-diagnostic.test.ts +++ b/src/agents/auth-profiles/legacy-source-diagnostic.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { assertAuthProfileMigrationReady, clearAuthProfileMigrationDiagnostics, @@ -15,7 +15,7 @@ afterEach(() => { describe("listAuthProfileStoresRequiringMigration", () => { it("reports only credential sources without marking runtime migration state", async () => { - await withTempDir({ prefix: "openclaw-auth-migration-diagnostic-" }, async (root) => { + await withTestDir({ prefix: "openclaw-auth-migration-diagnostic-" }, async (root) => { const credentialAgentDir = path.join(root, "credential-agent"); const authStateAgentDir = path.join(root, "auth-state-agent"); const env = { OPENCLAW_STATE_DIR: path.join(root, "state") }; diff --git a/src/agents/auth-profiles/oauth.openai-codex-refresh-fallback.test.ts b/src/agents/auth-profiles/oauth.openai-codex-refresh-fallback.test.ts index a7d2b7724d03..d7b502ee498d 100644 --- a/src/agents/auth-profiles/oauth.openai-codex-refresh-fallback.test.ts +++ b/src/agents/auth-profiles/oauth.openai-codex-refresh-fallback.test.ts @@ -22,7 +22,7 @@ import { clearRuntimeAuthProfileStoreSnapshots } from "./runtime-snapshots.js"; import { ensureAuthProfileStore, saveAuthProfileStore } from "./store.js"; import type { AuthProfileStore, OAuthCredential } from "./types.js"; let resolveApiKeyForProfile: typeof import("./oauth.js").resolveApiKeyForProfile; -let resolveApiKeyForProvider: typeof import("../model-auth.js").resolveApiKeyForProvider; +let resolveApiKeyForProviderCore: typeof import("../model-auth.js").resolveApiKeyForProviderCore; let hasAvailableAuthForProvider: typeof import("../model-auth.js").hasAvailableAuthForProvider; let markAuthProfileSuccess: typeof import("./profiles.js").markAuthProfileSuccess; type GetOAuthApiKey = typeof import("../../llm/oauth.js").getOAuthApiKey; @@ -159,7 +159,8 @@ describe("resolveApiKeyForProfile openai refresh fallback", () => { beforeAll(async () => { tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-refresh-fallback-")); ({ resolveApiKeyForProfile } = await import("./oauth.js")); - ({ hasAvailableAuthForProvider, resolveApiKeyForProvider } = await import("../model-auth.js")); + ({ hasAvailableAuthForProvider, resolveApiKeyForProviderCore } = + await import("../model-auth.js")); ({ markAuthProfileSuccess } = await import("./profiles.js")); }); @@ -683,7 +684,7 @@ describe("resolveApiKeyForProfile openai refresh fallback", () => { }); await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "openai", store: ensureAuthProfileStore(agentDir), profileId, @@ -776,7 +777,7 @@ describe("resolveApiKeyForProfile openai refresh fallback", () => { ); await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "openai", agentDir, }), @@ -808,7 +809,7 @@ describe("resolveApiKeyForProfile openai refresh fallback", () => { }); await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "openai", modelApi: "openai-responses", agentDir, @@ -830,7 +831,7 @@ describe("resolveApiKeyForProfile openai refresh fallback", () => { ); await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "openai", modelApi: "openai-responses", profileId, @@ -1109,7 +1110,7 @@ describe("resolveApiKeyForProfile openai refresh fallback", () => { ); }); - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openai", store: ensureAuthProfileStore(agentDir), agentDir, diff --git a/src/agents/auth-profiles/sqlite.ts b/src/agents/auth-profiles/sqlite.ts index 66cb097ed6a5..46eea26cd7f7 100644 --- a/src/agents/auth-profiles/sqlite.ts +++ b/src/agents/auth-profiles/sqlite.ts @@ -7,7 +7,7 @@ import fs from "node:fs"; import path from "node:path"; import type { DatabaseSync } from "node:sqlite"; import { safeParseJson } from "@openclaw/normalization-core"; -import { sha256HexPrefix } from "../../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../../infra/crypto-digest.js"; import { clearNodeSqliteKyselyCacheForDatabase, enableNodeSqliteKyselyStatementCache, @@ -62,7 +62,7 @@ function inferAgentIdFromDir(agentDir: string): string { return parent; } } - return `custom-${sha256HexPrefix(normalized, 12)}`; + return `custom-${sha256HexPrefixCore(normalized, 12)}`; } // The auth database lives in the agent dir and shares the openclaw-agent schema diff --git a/src/agents/bash-tools.exec-approval-followup.ts b/src/agents/bash-tools.exec-approval-followup.ts index 50d3c1ec5125..51a7da95fa70 100644 --- a/src/agents/bash-tools.exec-approval-followup.ts +++ b/src/agents/bash-tools.exec-approval-followup.ts @@ -8,7 +8,7 @@ import { normalizeOptionalString, } from "@openclaw/normalization-core/string-coerce"; import { sliceUtf16Safe } from "@openclaw/normalization-core/utf16-slice"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { loadSessionEntryReadOnly } from "../config/sessions/session-accessor.js"; import { getGatewayRecoveryRuntime } from "../gateway/server-recovery-runtime-context.js"; import { emitDiagnosticEvent } from "../infra/diagnostic-events.js"; @@ -154,7 +154,7 @@ function isExecApprovalFollowupDirectDeliveryStale(params: { return false; } try { - const storePath = resolveStorePath(normalizeOptionalString(params.sessionStore), { + const storePath = resolveSessionStorePathCore(normalizeOptionalString(params.sessionStore), { agentId: resolveAgentIdFromSessionKey(sessionKey), }); const resolvedSessionId = normalizeOptionalString( diff --git a/src/agents/bootstrap-files.test.ts b/src/agents/bootstrap-files.test.ts index ff25891c8357..062ae36a8052 100644 --- a/src/agents/bootstrap-files.test.ts +++ b/src/agents/bootstrap-files.test.ts @@ -5,7 +5,7 @@ import path from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { - upsertSessionEntry, + upsertSessionEntryCore, type SessionTranscriptRuntimeTarget, } from "../config/sessions/session-accessor.js"; import { @@ -663,7 +663,7 @@ describe("hasCompletedBootstrapTurn", () => { sessionKey: "agent:main:bootstrap-turn", storePath: path.join(tmpDir, "sessions.json"), }; - await upsertSessionEntry(sessionTarget, { + await upsertSessionEntryCore(sessionTarget, { sessionId: sessionTarget.sessionId, updatedAt: Date.now(), }); diff --git a/src/agents/btw-transcript.ts b/src/agents/btw-transcript.ts index 302d6ee46a0d..4fa05a7a6e26 100644 --- a/src/agents/btw-transcript.ts +++ b/src/agents/btw-transcript.ts @@ -2,7 +2,7 @@ * Reads prior session transcript context for `/btw` side-question handoffs. */ import { - resolveSessionFilePath, + resolveSessionFilePathCore, resolveSessionFilePathOptions, type SessionEntry as StoredSessionEntry, } from "../config/sessions.js"; @@ -38,7 +38,7 @@ export function resolveBtwSessionTranscriptPath(params: { agentId, storePath: params.storePath, }); - return resolveSessionFilePath(params.sessionId, params.sessionEntry, pathOpts); + return resolveSessionFilePathCore(params.sessionId, params.sessionEntry, pathOpts); } catch (error) { diag.debug( `resolveSessionTranscriptPath failed: sessionId=${params.sessionId} err=${String(error)}`, diff --git a/src/agents/cli-runner.reliability.test.ts b/src/agents/cli-runner.reliability.test.ts index 9c649f96ffec..8fdcdfe5aa20 100644 --- a/src/agents/cli-runner.reliability.test.ts +++ b/src/agents/cli-runner.reliability.test.ts @@ -14,7 +14,7 @@ import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js"; import { loadSessionEntry, loadTranscriptEvents, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { CURRENT_SESSION_VERSION } from "../config/sessions/version.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; @@ -389,7 +389,7 @@ async function seedSqliteSessionEntry(params: { sessionFile: string; storePath: string; }): Promise { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:main", @@ -4024,7 +4024,7 @@ describe("runCliAgent reliability", () => { const hookRunner = { hasHooks: vi.fn((hookName: string) => hookName === "before_agent_run"), runBeforeAgentRun: vi.fn(async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "replacement-session", updatedAt: 2 }, ); diff --git a/src/agents/cli-runner.ts b/src/agents/cli-runner.ts index 3be78f9f2251..da6028b34617 100644 --- a/src/agents/cli-runner.ts +++ b/src/agents/cli-runner.ts @@ -3,7 +3,7 @@ */ import { setReplyPayloadMetadata, type ReplyPayload } from "../auto-reply/reply-payload.js"; import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { patchSessionEntryCore } from "../config/sessions/session-accessor.js"; import { appendExactAssistantMessageToSessionTranscript } from "../config/sessions/transcript.js"; import { buildGenericCliContextEngineHostSupport } from "../context-engine/host-compat.js"; @@ -1089,7 +1089,7 @@ export async function runPreparedCliAgent( sessionKey, storePath: params.storePath ?? - resolveStorePath(params.config?.session?.store, { + resolveSessionStorePathCore(params.config?.session?.store, { agentId, }), }; diff --git a/src/agents/cli-runner/session-history.ts b/src/agents/cli-runner/session-history.ts index 0db367664ef2..5b31a8c58fae 100644 --- a/src/agents/cli-runner/session-history.ts +++ b/src/agents/cli-runner/session-history.ts @@ -6,7 +6,7 @@ import fsp from "node:fs/promises"; import path from "node:path"; import { sliceUtf16Safe, truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice"; import { - resolveSessionFilePath, + resolveSessionFilePathCore, resolveSessionFilePathOptions, } from "../../config/sessions/paths.js"; import { @@ -425,7 +425,7 @@ function resolveSafeCliSessionFile(params: { agentId: sessionAgentId ?? defaultAgentId, storePath: params.config?.session?.store, }); - const sessionFile = resolveSessionFilePath( + const sessionFile = resolveSessionFilePathCore( params.sessionId, { sessionFile: params.sessionFile }, pathOptions, diff --git a/src/agents/code-mode-execution.ts b/src/agents/code-mode-execution.ts index ddbcc981f2e9..dd75947441e8 100644 --- a/src/agents/code-mode-execution.ts +++ b/src/agents/code-mode-execution.ts @@ -48,7 +48,7 @@ import { resolveSwarmConfig } from "./subagents/swarm/swarm-config.js"; import { ToolSearchRuntime, type ToolSearchToolContext } from "./tool-search.js"; import { ToolInputError } from "./tools/common.js"; -export async function runExec(params: { +export async function runCodeModeExec(params: { toolCallId: string; ctx: ToolSearchToolContext; code: string; diff --git a/src/agents/code-mode.ts b/src/agents/code-mode.ts index 8af7e530bb55..36a0612e0330 100644 --- a/src/agents/code-mode.ts +++ b/src/agents/code-mode.ts @@ -17,7 +17,7 @@ import { isCodeModeControlTool, markCodeModeControlTool, } from "./code-mode-control-tools.js"; -import { runExec, runWait } from "./code-mode-execution.js"; +import { runCodeModeExec, runWait } from "./code-mode-execution.js"; import { createHeadlessAbortScope, runCodeModeScriptHeadless } from "./code-mode-headless.js"; import { describeCodeModeNamespacesForPrompt } from "./code-mode-namespaces.js"; import { @@ -210,7 +210,7 @@ export function createCodeModeTools(ctx: CodeModeToolContext): AnyAgentTool[] { const executionContext = getAgentToolExecutionContext(); let runtime: ToolSearchRuntime | undefined; const result = normalizeCodeModeTimeoutResult( - await runExec({ + await runCodeModeExec({ toolCallId, ctx, code: input.code, diff --git a/src/agents/command/delivery.ts b/src/agents/command/delivery.ts index 91fbc344f1ac..69543b74bb4a 100644 --- a/src/agents/command/delivery.ts +++ b/src/agents/command/delivery.ts @@ -23,7 +23,7 @@ import { import { resolveResponsePrefixTemplate } from "../../auto-reply/reply/response-prefix-template.js"; import { createChannelReplyTransform } from "../../channels/message/reply-transform.js"; import { - sendDurableMessageBatch, + sendDurableMessageBatchCore, serializeDurableMessagePayloadOutcomes, type SerializedDurableMessagePayloadOutcome, } from "../../channels/message/runtime.js"; @@ -60,7 +60,7 @@ import { isAgentRunRestartAbortReason } from "../run-termination.js"; import type { AgentCommandOpts } from "./types.js"; type RunResult = Awaited>; -type DurableSendResult = Awaited>; +type DurableSendResult = Awaited>; function createRestartOnlyAbortSignal(source: AbortSignal | undefined): { signal?: AbortSignal; @@ -949,7 +949,7 @@ export async function deliverAgentCommandResult( const restartAbort = createRestartOnlyAbortSignal(opts.abortSignal); let send: DurableSendResult; try { - send = await sendDurableMessageBatch({ + send = await sendDurableMessageBatchCore({ cfg, channel: deliveryChannel, to: deliveryTarget, diff --git a/src/agents/command/session.provider-owned-reset.test.ts b/src/agents/command/session.provider-owned-reset.test.ts index cc199cae61b7..1d488dc7a690 100644 --- a/src/agents/command/session.provider-owned-reset.test.ts +++ b/src/agents/command/session.provider-owned-reset.test.ts @@ -16,7 +16,7 @@ vi.mock("../../config/sessions/session-accessor.js", () => ({ })); vi.mock("../../config/sessions/paths.js", () => ({ - resolveStorePath: () => "/stores/main.json", + resolveSessionStorePathCore: () => "/stores/main.json", })); vi.mock("../../config/sessions/lifecycle.js", async () => { diff --git a/src/agents/command/session.resolve-session-key.test.ts b/src/agents/command/session.resolve-session-key.test.ts index 7954a39d812e..84b56226dba7 100644 --- a/src/agents/command/session.resolve-session-key.test.ts +++ b/src/agents/command/session.resolve-session-key.test.ts @@ -19,7 +19,7 @@ vi.mock("../../config/sessions/session-accessor.js", () => ({ })); vi.mock("../../config/sessions/paths.js", () => ({ - resolveStorePath: (_store?: string, params?: { agentId?: string }) => + resolveSessionStorePathCore: (_store?: string, params?: { agentId?: string }) => `/stores/${params?.agentId ?? "main"}.json`, })); diff --git a/src/agents/command/session.ts b/src/agents/command/session.ts index 595ee4569491..6c50fed30a99 100644 --- a/src/agents/command/session.ts +++ b/src/agents/command/session.ts @@ -19,7 +19,7 @@ import { resolveAgentIdFromSessionKey, resolveExplicitAgentSessionKey, } from "../../config/sessions/main-session.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { evaluateSessionFreshness, resolveSessionResetPolicy, @@ -171,7 +171,7 @@ function collectSessionIdMatchesForRequest(opts: { if (agentId === opts.storeAgentId) { continue; } - const candidateStorePath = resolveStorePath(opts.cfg.session?.store, { agentId }); + const candidateStorePath = resolveSessionStorePathCore(opts.cfg.session?.store, { agentId }); addMatches( loadCommandSessionStore({ agentId, @@ -199,7 +199,7 @@ export function resolveStoredSessionKeyForSessionId(opts: { const storeAgentId = opts.agentId?.trim() ? normalizeAgentId(opts.agentId) : resolveDefaultAgentId(opts.cfg); - const storePath = resolveStorePath(opts.cfg.session?.store, { + const storePath = resolveSessionStorePathCore(opts.cfg.session?.store, { agentId: storeAgentId, }); const sessionStore = loadCommandSessionStore({ @@ -255,7 +255,7 @@ export function resolveSessionKeyForRequest(opts: { ? (requestedAgentId ?? defaultAgentId) : resolveAgentIdFromSessionKey(explicitSessionKey, defaultAgentId) : (requestedAgentId ?? defaultAgentId); - const storePath = resolveStorePath(sessionCfg?.store, { + const storePath = resolveSessionStorePathCore(sessionCfg?.store, { agentId: storeAgentId, }); const loadOptions = opts.clone === false ? { clone: false as const } : undefined; diff --git a/src/agents/config.ts b/src/agents/config.ts index 344e5dedb5f6..abd4b6d2fe21 100644 --- a/src/agents/config.ts +++ b/src/agents/config.ts @@ -101,7 +101,7 @@ const pkg = JSON.parse(readFileSync(getPackageJsonPath(), "utf-8")) as PackageJs const openClawConfigName: string | undefined = pkg.openclawConfig?.name; export const APP_NAME: string = openClawConfigName || "openclaw"; export const CONFIG_DIR_NAME: string = pkg.openclawConfig?.configDir || ".openclaw"; -export const VERSION: string = pkg.version || "0.0.0"; +export const PACKAGE_MANIFEST_VERSION: string = pkg.version || "0.0.0"; const ENV_AGENT_DIR = `${APP_NAME.toUpperCase()}_AGENT_DIR`; diff --git a/src/agents/embedded-agent-helpers/openai.ts b/src/agents/embedded-agent-helpers/openai.ts index 1ff0570f7d4e..6624680564dd 100644 --- a/src/agents/embedded-agent-helpers/openai.ts +++ b/src/agents/embedded-agent-helpers/openai.ts @@ -1,7 +1,7 @@ /** * Normalizes OpenAI Responses reasoning/tool-call history for safe replay. */ -import { sha256HexPrefix } from "../../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../../infra/crypto-digest.js"; import type { AgentMessage } from "../runtime/index.js"; type OpenAIThinkingBlock = { @@ -99,7 +99,7 @@ function isOpenAIToolCallType(type: unknown): boolean { } function shortOpenAIResponsesIdHash(id: string): string { - return sha256HexPrefix(id, 10); + return sha256HexPrefixCore(id, 10); } function sanitizeOpenAIResponsesIdTail(value: string): string { diff --git a/src/agents/embedded-agent-runner.e2e.test.ts b/src/agents/embedded-agent-runner.e2e.test.ts index b245f40a910a..782e56143abd 100644 --- a/src/agents/embedded-agent-runner.e2e.test.ts +++ b/src/agents/embedded-agent-runner.e2e.test.ts @@ -176,7 +176,7 @@ type TestRunEmbeddedAgent = ( let runEmbeddedAgent: TestRunEmbeddedAgent; let SessionManager: typeof import("openclaw/plugin-sdk/agent-sessions").SessionManager; let loadTranscriptEvents: typeof import("../config/sessions/session-accessor.js").loadTranscriptEvents; -let upsertSessionEntry: typeof import("../config/sessions/session-accessor.js").upsertSessionEntry; +let upsertSessionEntryCore: typeof import("../config/sessions/session-accessor.js").upsertSessionEntryCore; let resolveAgentRunSessionTarget: typeof import("./run-session-target.js").resolveAgentRunSessionTarget; let e2eWorkspace: EmbeddedAgentRunnerTestWorkspace | undefined; let agentDir: string; @@ -200,7 +200,7 @@ beforeAll(async () => { (await import("./embedded-agent-runner/run.js")).runEmbeddedAgent, ); ({ SessionManager } = await import("openclaw/plugin-sdk/agent-sessions")); - ({ loadTranscriptEvents, upsertSessionEntry } = + ({ loadTranscriptEvents, upsertSessionEntryCore } = await import("../config/sessions/session-accessor.js")); ({ resolveAgentRunSessionTarget } = await import("./run-session-target.js")); e2eWorkspace = await createEmbeddedAgentRunnerTestWorkspace("openclaw-embedded-agent-"); @@ -258,7 +258,7 @@ const createPersistedTestSessionManager = async (params: { sessionKey: string; }) => { const target = await resolveTestSessionTarget(params); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: target.agentId, sessionKey: target.sessionKey, storePath: target.storePath }, { sessionId: target.sessionId, updatedAt: Date.now() }, ); diff --git a/src/agents/embedded-agent-runner/compact.hooks.harness.ts b/src/agents/embedded-agent-runner/compact.hooks.harness.ts index f7f0bc2221e2..7f5f93c58db0 100644 --- a/src/agents/embedded-agent-runner/compact.hooks.harness.ts +++ b/src/agents/embedded-agent-runner/compact.hooks.harness.ts @@ -77,7 +77,7 @@ export const sessionCompactImpl = vi.fn(async () => ({ })); export const sessionManualCompactionMock = vi.fn(); export const sessionAutomaticCompactionMock = vi.fn(); -export const triggerInternalHook: Mock<(event?: unknown) => void> = vi.fn(); +export const triggerInternalHookMock: Mock<(event?: unknown) => void> = vi.fn(); const sanitizeSessionHistoryMock = vi.fn( async (params: { messages: unknown[] }) => params.messages, ); @@ -615,7 +615,7 @@ export function resetCompactHooksHarnessMocks(): void { details: { ok: true }, }); - triggerInternalHook.mockReset(); + triggerInternalHookMock.mockReset(); resetCompactSessionStateMocks(); createOpenClawCodingToolsMock.mockReset(); createOpenClawCodingToolsMock.mockReturnValue([]); @@ -717,7 +717,7 @@ export async function loadCompactHooksHarness(): Promise<{ ); return { ...actual, - triggerInternalHook, + triggerInternalHook: triggerInternalHookMock, }; }); diff --git a/src/agents/embedded-agent-runner/compact.hooks.test.ts b/src/agents/embedded-agent-runner/compact.hooks.test.ts index f06fbedf53d8..9dce2cd30198 100644 --- a/src/agents/embedded-agent-runner/compact.hooks.test.ts +++ b/src/agents/embedded-agent-runner/compact.hooks.test.ts @@ -8,7 +8,7 @@ import type { AgentMessage } from "openclaw/plugin-sdk/agent-core"; import { beforeAll, beforeEach, describe, expect, it, vi, type Mock } from "vitest"; import { createDeferred } from "../../../test/helpers/promise.js"; import { createReplyOperation } from "../../auto-reply/reply/reply-run-registry.js"; -import { upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../../config/sessions/session-accessor.js"; import { closeOpenClawAgentDatabasesForTest } from "../../state/openclaw-agent-db.js"; import { acquireAgentRunPreparedModelRuntimeMock, @@ -56,7 +56,7 @@ import { sessionMessages, sessionCompactImpl, sessionManualCompactionMock, - triggerInternalHook, + triggerInternalHookMock, } from "./compact.hooks.harness.js"; import { abortEmbeddedAgentRun, @@ -254,7 +254,7 @@ function createPreparedCodexCompactionPlans(modelId = "gpt-5.5") { } const sessionHook = (action: string): SessionHookEvent | undefined => - triggerInternalHook.mock.calls.find((call) => { + triggerInternalHookMock.mock.calls.find((call) => { const event = call[0] as SessionHookEvent | undefined; return event?.type === "session" && event.action === action; })?.[0] as SessionHookEvent | undefined; @@ -314,7 +314,7 @@ beforeEach(() => { describe("compactEmbeddedAgentSessionDirect hooks", () => { beforeEach(() => { - triggerInternalHook.mockClear(); + triggerInternalHookMock.mockClear(); hookRunner.hasHooks.mockReset(); hookRunner.runBeforeCompaction.mockReset(); hookRunner.runAfterCompaction.mockReset(); @@ -1876,7 +1876,7 @@ describe("compactEmbeddedAgentSessionDirect hooks", () => { it("forwards internal compaction hook messages to the caller", async () => { const onHookMessages = vi.fn(); - triggerInternalHook.mockImplementation((event: unknown) => { + triggerInternalHookMock.mockImplementation((event: unknown) => { const hookEvent = event as { action?: string; messages?: string[] }; hookEvent.messages?.push(`${hookEvent.action} notice`); }); @@ -2386,7 +2386,7 @@ describe("compactEmbeddedAgentSession hooks (ownsCompaction engine)", () => { const sessionId = "9d6c8436-7cb2-4bd5-a302-e33305bfc8c4"; const sessionKey = "agent:main:telegram:direct:reporter"; try { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); @@ -4337,7 +4337,7 @@ describe("compactEmbeddedAgentSession hooks (ownsCompaction engine)", () => { }, } as never); try { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: delegatedSessionKey, @@ -4381,7 +4381,7 @@ describe("compactEmbeddedAgentSession hooks (ownsCompaction engine)", () => { }, } as never); try { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: delegatedSessionKey, storePath }, { sessionId: "stored-session", updatedAt: 1 }, ); @@ -4482,7 +4482,7 @@ describe("compactEmbeddedAgentSession hooks (ownsCompaction engine)", () => { }, } as never); try { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: TEST_SESSION_KEY, storePath }, { sessionId: TEST_SESSION_ID, updatedAt: 1 }, ); diff --git a/src/agents/embedded-agent-runner/compaction-safety-timeout.ts b/src/agents/embedded-agent-runner/compaction-safety-timeout.ts index a18abb06529f..9352d53870ca 100644 --- a/src/agents/embedded-agent-runner/compaction-safety-timeout.ts +++ b/src/agents/embedded-agent-runner/compaction-safety-timeout.ts @@ -5,7 +5,7 @@ import { finiteSecondsToTimerSafeMilliseconds } from "@openclaw/normalization-co import type { OpenClawConfig } from "../../config/types.openclaw.js"; import type { CompactResult, ContextEngine } from "../../context-engine/types.js"; import { createAbortError } from "../../infra/abort-signal.js"; -import { withTimeout } from "../../node-host/with-timeout.js"; +import { runAbortableTimeout } from "../../node-host/with-timeout.js"; const EMBEDDED_COMPACTION_TIMEOUT_MS = 180_000; @@ -47,7 +47,7 @@ export async function compactWithSafetyTimeout( } }; - return await withTimeout( + return await runAbortableTimeout( async (timeoutSignal) => { let timeoutListener: (() => void) | undefined; let externalAbortListener: (() => void) | undefined; diff --git a/src/agents/embedded-agent-runner/context-engine-maintenance.ts b/src/agents/embedded-agent-runner/context-engine-maintenance.ts index 4915b4adc5fb..2b80301d464a 100644 --- a/src/agents/embedded-agent-runner/context-engine-maintenance.ts +++ b/src/agents/embedded-agent-runner/context-engine-maintenance.ts @@ -3,7 +3,7 @@ */ import { randomUUID } from "node:crypto"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { publishTranscriptUpdate } from "../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { resolveContextEngineOwnerPluginId } from "../../context-engine/registry.js"; @@ -254,7 +254,7 @@ function buildContextEngineMaintenanceRuntimeContext( const runtimeStorePath = params.sessionTarget?.storePath ?? (runtimeAgentId - ? resolveStorePath(params.config?.session?.store, { agentId: runtimeAgentId }) + ? resolveSessionStorePathCore(params.config?.session?.store, { agentId: runtimeAgentId }) : undefined); let runtimeTarget: Awaited> | undefined; let sessionManager = params.sessionManager; diff --git a/src/agents/embedded-agent-runner/model.configured-fallback.ts b/src/agents/embedded-agent-runner/model.configured-fallback.ts index e13977481c9e..923cec031c79 100644 --- a/src/agents/embedded-agent-runner/model.configured-fallback.ts +++ b/src/agents/embedded-agent-runner/model.configured-fallback.ts @@ -38,7 +38,7 @@ import { type ManifestModelCatalogProviderAliasMetadata, } from "./model.static-catalog.js"; -export function resolveConfiguredFallbackModel(params: { +export function buildConfiguredFallbackModel(params: { provider: string; modelId: string; cfg?: OpenClawConfig; diff --git a/src/agents/embedded-agent-runner/model.registry-resolution.ts b/src/agents/embedded-agent-runner/model.registry-resolution.ts index 81d1b3cc7531..0625d8c45e23 100644 --- a/src/agents/embedded-agent-runner/model.registry-resolution.ts +++ b/src/agents/embedded-agent-runner/model.registry-resolution.ts @@ -9,7 +9,7 @@ import { normalizeStaticProviderModelId } from "../model-ref-shared.js"; import { normalizeProviderId } from "../model-selection.js"; import { shouldSuppressBuiltInModel, shouldUnconditionallySuppress } from "../model-suppression.js"; import { listOpenAIAuthProfileProvidersForAgentRuntime } from "../openai-routing.js"; -import { resolveConfiguredFallbackModel } from "./model.configured-fallback.js"; +import { buildConfiguredFallbackModel } from "./model.configured-fallback.js"; import { applyConfiguredProviderOverrides, findInlineModelMatch, @@ -463,7 +463,7 @@ export function resolveModelWithPreparedRegistry( } return params.skipConfiguredFallback ? undefined - : resolveConfiguredFallbackModel({ + : buildConfiguredFallbackModel({ ...params, providerMetadataOwners: getRegistryProviderMetadataOwners(params.modelRegistry), }); diff --git a/src/agents/embedded-agent-runner/model.ts b/src/agents/embedded-agent-runner/model.ts index 49e027ad579a..5b3556dd8070 100644 --- a/src/agents/embedded-agent-runner/model.ts +++ b/src/agents/embedded-agent-runner/model.ts @@ -20,7 +20,7 @@ import { type ModelRegistry, } from "../sessions/index.js"; import { mergeModelMediaInput } from "./model.compat.js"; -import { resolveConfiguredFallbackModel } from "./model.configured-fallback.js"; +import { buildConfiguredFallbackModel } from "./model.configured-fallback.js"; import { applyConfiguredProviderOverrides, resolveConfiguredProviderConfig, @@ -422,7 +422,7 @@ export async function resolveModelAsync( model = await resolveStaticCatalogFallbackModel(); } if (!model && !explicitModel && options?.allowBundledStaticCatalogFallback) { - model = resolveConfiguredFallbackModel({ + model = buildConfiguredFallbackModel({ provider: normalizedRef.provider, modelId: normalizedRef.model, cfg, diff --git a/src/agents/embedded-agent-runner/run/attempt-dispatch-preparation.ts b/src/agents/embedded-agent-runner/run/attempt-dispatch-preparation.ts index df11c3d79490..aaf64a63610d 100644 --- a/src/agents/embedded-agent-runner/run/attempt-dispatch-preparation.ts +++ b/src/agents/embedded-agent-runner/run/attempt-dispatch-preparation.ts @@ -1,5 +1,5 @@ import fs from "node:fs/promises"; -import { resolveStorePath } from "../../../config/sessions.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions.js"; import { resolveSessionTranscriptRuntimeTarget } from "../../../config/sessions/session-accessor.js"; import type { resolveContextEngine } from "../../../context-engine/registry.js"; import { attachModelProviderRuntimePluginHandle } from "../../../plugins/provider-hook-runtime.js"; @@ -124,7 +124,7 @@ export async function prepareAndDispatchEmbeddedRunAttempt(input: { agentId: workspaceResolution.agentId, sessionId: sessionPromptState.sessionId, sessionKey: resolvedSessionKey, - storePath: resolveStorePath(params.config?.session?.store, { + storePath: resolveSessionStorePathCore(params.config?.session?.store, { agentId: workspaceResolution.agentId, }), }) diff --git a/src/agents/embedded-agent-runner/run/attempt-history.ts b/src/agents/embedded-agent-runner/run/attempt-history.ts index 87eb9ce06e11..41a30614d140 100644 --- a/src/agents/embedded-agent-runner/run/attempt-history.ts +++ b/src/agents/embedded-agent-runner/run/attempt-history.ts @@ -7,7 +7,7 @@ import { buildHierarchyReinforcementMessage } from "../../../auto-reply/handoff- import { filterHeartbeatTranscriptArtifacts } from "../../../auto-reply/heartbeat-filter.js"; import { formatContextJsonBlock } from "../../../auto-reply/reply/channel-prompt-context.js"; import { markInboundContextLabel } from "../../../auto-reply/reply/inbound-context-marker.js"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { listSessionEntriesReadOnly, updateSessionEntry, @@ -461,7 +461,7 @@ export async function prepareEmbeddedAttemptHistory(input: { }); if (attempt.sessionKey && !isSettledTurnFinalization) { - const storePath = resolveStorePath(attempt.config?.session?.store, { + const storePath = resolveSessionStorePathCore(attempt.config?.session?.store, { agentId: input.sessionAgentId, }); const sessionEntry = await loadAttemptSessionEntryAfterQuotaMaintenance({ diff --git a/src/agents/embedded-agent-runner/run/attempt-phase-lifecycle.test.ts b/src/agents/embedded-agent-runner/run/attempt-phase-lifecycle.test.ts index 847688226eb5..a051955d4923 100644 --- a/src/agents/embedded-agent-runner/run/attempt-phase-lifecycle.test.ts +++ b/src/agents/embedded-agent-runner/run/attempt-phase-lifecycle.test.ts @@ -4,7 +4,7 @@ import { useAutoCleanupTempDirTracker } from "../../../../test/helpers/temp-dir. import { appendTranscriptMessage, readActiveTranscriptEntryAnchor, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../../config/sessions/session-accessor.js"; import { createUserTurnTranscriptRecorder } from "../../../sessions/user-turn-transcript.js"; import { closeOpenClawAgentDatabasesForTest } from "../../../state/openclaw-agent-db.js"; @@ -313,7 +313,7 @@ describe("embedded attempt phase lifecycle state", () => { sessionKey: "agent:main:main", storePath: path.join(dir, "sessions.json"), }; - await upsertSessionEntry(target, { sessionId: target.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 1 }); const userMessage = { role: "user" as const, content: "hello", timestamp: 1 }; const persistedUser = await appendTranscriptMessage(target, { cwd: dir, diff --git a/src/agents/embedded-agent-runner/run/attempt-transcript-helpers.ts b/src/agents/embedded-agent-runner/run/attempt-transcript-helpers.ts index f8fa71e45ab5..54016be4ee7d 100644 --- a/src/agents/embedded-agent-runner/run/attempt-transcript-helpers.ts +++ b/src/agents/embedded-agent-runner/run/attempt-transcript-helpers.ts @@ -1,5 +1,5 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { loadSessionEntry, loadTranscriptEvents, @@ -143,7 +143,7 @@ export async function resolveAttemptTrajectorySessionFile(params: { }): Promise { const storePath = params.sessionTarget?.storePath ?? - resolveStorePath(params.config?.session?.store, { agentId: params.agentId }); + resolveSessionStorePathCore(params.config?.session?.store, { agentId: params.agentId }); if (!storePath || !params.sessionKey) { return params.sessionFile; } @@ -181,7 +181,7 @@ export async function resolveExistingAttemptTranscriptState(params: { const agentId = normalizeOptionalString(params.sessionTarget?.agentId) ?? params.agentId; const storePath = normalizeOptionalString(params.sessionTarget?.storePath) ?? - resolveStorePath(params.config?.session?.store, { agentId }); + resolveSessionStorePathCore(params.config?.session?.store, { agentId }); const sessionId = normalizeOptionalString(params.sessionTarget?.sessionId) ?? params.sessionId; const sessionKey = normalizeOptionalString(params.sessionTarget?.sessionKey) ?? diff --git a/src/agents/embedded-agent-runner/run/attempt-transcript-persistence.e2e.test.ts b/src/agents/embedded-agent-runner/run/attempt-transcript-persistence.e2e.test.ts index 887cf100a4c4..cb608aefd4f3 100644 --- a/src/agents/embedded-agent-runner/run/attempt-transcript-persistence.e2e.test.ts +++ b/src/agents/embedded-agent-runner/run/attempt-transcript-persistence.e2e.test.ts @@ -4,7 +4,7 @@ import { afterEach, describe, expect, it } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../../../test/helpers/temp-dir.js"; import { appendTranscriptMessage, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../../config/sessions/session-accessor.js"; import { SessionManager } from "../../sessions/session-manager.js"; import { flushSessionManagerTranscript } from "./attempt-transcript-helpers.js"; @@ -47,7 +47,7 @@ describe("embedded attempt transcript persistence", () => { sessionKey: "agent:main:embedded-generation", storePath, }; - await upsertSessionEntry(target, { + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 1, }); diff --git a/src/agents/embedded-agent-runner/run/session-bootstrap.ts b/src/agents/embedded-agent-runner/run/session-bootstrap.ts index 7df55078e69d..28e1b921d066 100644 --- a/src/agents/embedded-agent-runner/run/session-bootstrap.ts +++ b/src/agents/embedded-agent-runner/run/session-bootstrap.ts @@ -1,7 +1,10 @@ import path from "node:path"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { sanitizeForLog } from "../../../../packages/terminal-core/src/ansi.js"; -import { resolveStorePath, SESSION_TOTAL_TOKENS_VERSION } from "../../../config/sessions.js"; +import { + resolveSessionStorePathCore, + SESSION_TOTAL_TOKENS_VERSION, +} from "../../../config/sessions.js"; import { parseSqliteSessionFileMarker } from "../../../config/sessions/legacy-sqlite-marker.js"; import { listSessionEntriesCore, @@ -107,7 +110,7 @@ export function buildContextEngineCompactionSessionTarget(params: { const storePath = targetStorePath ?? marker?.storePath ?? - resolveStorePath(params.config?.session?.store, { agentId }); + resolveSessionStorePathCore(params.config?.session?.store, { agentId }); return { agentId, sessionId: targetSessionId ?? marker?.sessionId ?? params.sessionId, @@ -139,7 +142,9 @@ export async function resetNoRealConversationTokenSnapshot(params: { if (!params.sessionKey) { return; } - const storePath = resolveStorePath(params.config?.session?.store, { agentId: params.agentId }); + const storePath = resolveSessionStorePathCore(params.config?.session?.store, { + agentId: params.agentId, + }); try { await updateSessionEntry( { @@ -222,7 +227,7 @@ export function assertAgentHarnessRunAdmission( const admissionAgentId = params.agentId ?? resolveAgentIdFromSessionKey(sessionKey); const storePath = normalizeOptionalString(params.sessionTarget?.storePath) ?? - resolveStorePath(params.config?.session?.store, { agentId: admissionAgentId }); + resolveSessionStorePathCore(params.config?.session?.store, { agentId: admissionAgentId }); const durableEntry = loadSessionEntry({ ...(admissionAgentId ? { agentId: admissionAgentId } : {}), readConsistency: "latest", diff --git a/src/agents/embedded-agent-runner/runs.force-clear-terminal.test.ts b/src/agents/embedded-agent-runner/runs.force-clear-terminal.test.ts index ae9d21e03b51..844572953ab4 100644 --- a/src/agents/embedded-agent-runner/runs.force-clear-terminal.test.ts +++ b/src/agents/embedded-agent-runner/runs.force-clear-terminal.test.ts @@ -7,7 +7,10 @@ import { } from "../../auto-reply/reply/reply-run-registry.js"; import { testing as replyRunTesting } from "../../auto-reply/reply/reply-run-registry.test-support.js"; import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } from "../../config/io.js"; -import { loadSessionEntry, upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { + loadSessionEntry, + upsertSessionEntryCore, +} from "../../config/sessions/session-accessor.js"; import { createOpenClawTestState, type OpenClawTestState, @@ -248,7 +251,7 @@ describe("force-clear terminal state persistence", () => { const sessionId = "session-1"; const startedAt = Date.now() - 60_000; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId, @@ -284,7 +287,7 @@ describe("force-clear terminal state persistence", () => { const startedAt = Date.now() - 60_000; const handle = createRunHandle(); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId, updatedAt: startedAt, startedAt, status: "running" }, ); @@ -331,7 +334,7 @@ describe("force-clear terminal state persistence", () => { const sessionId = "session-no-key"; const sessionKey = "agent:main:no-key"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId, @@ -360,7 +363,7 @@ describe("force-clear terminal state persistence", () => { const oldSessionId = "session-old"; const newSessionId = "session-new"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: oldSessionId, @@ -371,7 +374,7 @@ describe("force-clear terminal state persistence", () => { setActiveEmbeddedRun(oldSessionId, createRunHandle(), sessionKey); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: newSessionId, @@ -400,7 +403,7 @@ describe("force-clear terminal state persistence", () => { const sessionId = "session-reused"; const replacement = createRunHandle(); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId, @@ -433,7 +436,7 @@ describe("force-clear terminal state persistence", () => { const newSessionId = "session-new-owner"; const replacement = createRunHandle(); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: oldSessionId, diff --git a/src/agents/embedded-agent-runner/runs.ts b/src/agents/embedded-agent-runner/runs.ts index 9bf9624e2890..4470df84189a 100644 --- a/src/agents/embedded-agent-runner/runs.ts +++ b/src/agents/embedded-agent-runner/runs.ts @@ -23,7 +23,7 @@ import { waitForReplyRunEndBySessionId, } from "../../auto-reply/reply/reply-run-registry.js"; import { getRuntimeConfig } from "../../config/io.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { loadSessionEntry, updateSessionEntry } from "../../config/sessions/session-accessor.js"; import type { InternalSessionEntry } from "../../config/sessions/types.js"; import { @@ -978,7 +978,7 @@ function tryLoadForceClearSessionSnapshot( try { const cfg = getRuntimeConfig(); const agentId = resolveAgentIdFromSessionKey(sessionKey); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); const entry = loadSessionEntry({ sessionKey, storePath }); if (!entry || entry.status !== "running") { return undefined; diff --git a/src/agents/embedded-agent-subscribe.handlers.compaction.runtime.ts b/src/agents/embedded-agent-subscribe.handlers.compaction.runtime.ts index c34a2a2e9a58..2ea21ee4b817 100644 --- a/src/agents/embedded-agent-subscribe.handlers.compaction.runtime.ts +++ b/src/agents/embedded-agent-subscribe.handlers.compaction.runtime.ts @@ -1,7 +1,7 @@ /** * Runtime helpers for reconciling compaction counts after subscribe events. */ -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { updateSessionEntry } from "../config/sessions/session-accessor.js"; /** Persist the highest observed compaction count after a successful subscribed run. */ @@ -16,7 +16,7 @@ export default async function reconcileSessionStoreCompactionCountAfterSuccess(p if (!sessionKey || observedCompactionCount <= 0) { return undefined; } - const storePath = resolveStorePath(configStore, { agentId }); + const storePath = resolveSessionStorePathCore(configStore, { agentId }); const nextEntry = await updateSessionEntry({ sessionKey, storePath }, async (entry) => { // The live stream and store can both observe compactions. Keep the max so // late lower-count updates cannot make future resume labels regress. diff --git a/src/agents/harness/agent-end-side-effects.no-verbatim-capture.test.ts b/src/agents/harness/agent-end-side-effects.no-verbatim-capture.test.ts index 2c1345da3763..8decd3aa35fe 100644 --- a/src/agents/harness/agent-end-side-effects.no-verbatim-capture.test.ts +++ b/src/agents/harness/agent-end-side-effects.no-verbatim-capture.test.ts @@ -4,7 +4,7 @@ // "That's wrong — not the 12–34k figure I told you." into live proposals named // after slugified message fragments ("12-34k-figure-told"). import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../../config/sessions/session-accessor.js"; import { listSkillProposals } from "../../skills/workshop/service.js"; import { createOpenClawTestState, @@ -70,7 +70,7 @@ describe("agent-end proposal provenance", () => { "#4242 Vendor Blue 7: Also when reading comments make sure to read the comments they responded to also.", ])("creates no proposal from chat text without a model review: %s", async (userText) => { const sessionKey = `agent:main:no-verbatim-capture-${String(++sessionKeyIndex)}`; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: `session-${sessionKey}`, updatedAt: 1 }, ); diff --git a/src/agents/harness/context-engine-transcript-cursor.test.ts b/src/agents/harness/context-engine-transcript-cursor.test.ts index f8527eb2e081..8909684d4b6b 100644 --- a/src/agents/harness/context-engine-transcript-cursor.test.ts +++ b/src/agents/harness/context-engine-transcript-cursor.test.ts @@ -10,7 +10,7 @@ import { describe, expect, it } from "vitest"; import { appendTranscriptMessage, replaceTranscriptEvents, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import type { ContextEngine, ContextEngineSessionTarget } from "../../context-engine/types.js"; import { createUserTurnTranscriptRecorder } from "../../sessions/user-turn-transcript.js"; @@ -108,7 +108,7 @@ describe("context engine transcript cursor contract", () => { > = async () => undefined; try { - await upsertSessionEntry(target, { sessionId: target.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 10 }); const first = await appendTranscriptMessage(target, { message: { role: "user", content: "first" }, now: 1_000, diff --git a/src/agents/harness/context-engine-turn-attempt.test.ts b/src/agents/harness/context-engine-turn-attempt.test.ts index 7b26928ad427..8f6f7846940e 100644 --- a/src/agents/harness/context-engine-turn-attempt.test.ts +++ b/src/agents/harness/context-engine-turn-attempt.test.ts @@ -5,7 +5,7 @@ import { appendTranscriptMessage, readActiveTranscriptEntryAnchor, readClosedTranscriptTurn, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import type { ContextEngine } from "../../context-engine/types.js"; import { @@ -113,7 +113,7 @@ async function createAcceptedTurnFixture(params: { sessionKey: `agent:main:${params.sessionId}`, storePath: path.join(tempDir, "sessions.json"), }; - await upsertSessionEntry(target, { sessionId: target.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 1 }); let parentId: string | undefined; for (const [index, content] of params.prefix.entries()) { const entry = await appendTranscriptMessage(target, { @@ -257,7 +257,7 @@ describe("accepted context-engine turn finalization", () => { sessionKey: "agent:main:accepted-turn", storePath: path.join(tempDir, "sessions.json"), }; - await upsertSessionEntry(target, { sessionId: target.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 1 }); const prior = await appendTranscriptMessage(target, { message: { role: "assistant", content: "prior" }, now: 1_000, diff --git a/src/agents/harness/context-engine-turn-outbox.test.ts b/src/agents/harness/context-engine-turn-outbox.test.ts index 35afbdbc48d2..4c001285ac65 100644 --- a/src/agents/harness/context-engine-turn-outbox.test.ts +++ b/src/agents/harness/context-engine-turn-outbox.test.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import { appendTranscriptMessage, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import type { TranscriptTurnAdmission, @@ -193,7 +193,7 @@ describe("context-engine turn outbox", () => { sessionKey: "agent:main:recovered-turn", storePath: path.join(stateDir, "sessions.json"), }; - await upsertSessionEntry(target, { sessionId: target.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 1 }); const admitted = await appendTranscriptMessage(target, { message: { role: "user", content: "first" }, now: 1_000, @@ -333,7 +333,7 @@ describe("context-engine turn outbox", () => { sessionKey: "agent:main:unaccepted-turn", storePath: path.join(stateDir, "sessions.json"), }; - await upsertSessionEntry(target, { sessionId: target.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 1 }); const admitted = await appendTranscriptMessage(target, { message: { role: "user", content: "first" }, now: 1_000, diff --git a/src/agents/internal-session-effects.test.ts b/src/agents/internal-session-effects.test.ts index 4c9443c5935d..c11041c49c6b 100644 --- a/src/agents/internal-session-effects.test.ts +++ b/src/agents/internal-session-effects.test.ts @@ -7,13 +7,13 @@ import { loadExactSessionEntry, loadTranscriptEvents, persistSessionResetLifecycle, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { closeOpenClawAgentDatabasesForTest, resolveIncognitoOpenClawAgentSqlitePath, } from "../state/openclaw-agent-db.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withEnvAsync } from "../test-utils/env.js"; import { prepareInternalSessionEffectsSession, @@ -41,7 +41,7 @@ describe("internal session effects", () => { }); it("does not archive an incognito internal-effects transcript during rotation", async () => { - await withTempDir({ prefix: "openclaw-incognito-internal-rotation-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-incognito-internal-rotation-" }, async (dir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: dir }, async () => { const storePath = resolveIncognitoOpenClawAgentSqlitePath({ agentId: "main" }); try { @@ -61,7 +61,7 @@ describe("internal session effects", () => { })}\n`, "utf8", ); - const previousEntry = await upsertSessionEntry(target, { + const previousEntry = await upsertSessionEntryCore(target, { ...target.sessionEntry, sessionFile: previousTranscript, }); @@ -96,7 +96,7 @@ describe("internal session effects", () => { }); it("creates a hidden deterministic SQLite session", async () => { - await withTempDir({ prefix: "openclaw-internal-session-effects-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-internal-session-effects-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const target = await prepareInternalSessionEffectsSession({ agentId: "main", @@ -130,7 +130,7 @@ describe("internal session effects", () => { }); it("escapes the reserved prefix for a durable internal-effects run id", async () => { - await withTempDir({ prefix: "openclaw-internal-session-effects-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-internal-session-effects-" }, async (dir) => { const target = resolveInternalSessionEffectsTarget({ agentId: "main", runId: "incognito-not-private", @@ -144,7 +144,7 @@ describe("internal session effects", () => { }); it("forks visible SQLite history into the hidden session", async () => { - await withTempDir({ prefix: "openclaw-internal-session-effects-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-internal-session-effects-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const source = { agentId: "main", @@ -152,7 +152,7 @@ describe("internal session effects", () => { sessionKey: "agent:main:main", storePath, }; - await upsertSessionEntry(source, { sessionId: source.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(source, { sessionId: source.sessionId, updatedAt: 1 }); await appendTranscriptMessage(source, { cwd: dir, message: { content: "stored", role: "assistant", timestamp: 2 }, @@ -180,7 +180,7 @@ describe("internal session effects", () => { }); it("hard-deletes the hidden entry and transcript rows", async () => { - await withTempDir({ prefix: "openclaw-internal-session-effects-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-internal-session-effects-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const target = await prepareInternalSessionEffectsSession({ agentId: "main", diff --git a/src/agents/internal-session-effects.ts b/src/agents/internal-session-effects.ts index 623d42156bb8..9231b78cc027 100644 --- a/src/agents/internal-session-effects.ts +++ b/src/agents/internal-session-effects.ts @@ -5,7 +5,7 @@ import { forkSessionFromParentTranscript, loadExactSessionEntry, replaceTranscriptEvents, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { buildSessionCreationStamp } from "../config/sessions/session-entry-provenance.js"; import { createSessionTranscriptHeader } from "../config/sessions/transcript-header.js"; @@ -96,7 +96,7 @@ export async function prepareInternalSessionEffectsSession(params: { ]); } const now = Date.now(); - const entry = await upsertSessionEntry(scope, { + const entry = await upsertSessionEntryCore(scope, { ...buildSessionCreationStamp({ via: "internal", actor: { type: "system" } }), delivery: { kind: "internal" }, sessionId: scope.sessionId, diff --git a/src/agents/live-model-switch.test.ts b/src/agents/live-model-switch.test.ts index bca6a1ec03f9..5132dd59b49d 100644 --- a/src/agents/live-model-switch.test.ts +++ b/src/agents/live-model-switch.test.ts @@ -40,7 +40,7 @@ vi.mock("../config/sessions/session-accessor.js", () => { }); vi.mock("../config/sessions/paths.js", () => ({ - resolveStorePath: (...args: unknown[]) => state.resolveStorePathMock(...args), + resolveSessionStorePathCore: (...args: unknown[]) => state.resolveStorePathMock(...args), })); let mod: typeof import("./live-model-switch.js"); diff --git a/src/agents/live-model-switch.ts b/src/agents/live-model-switch.ts index 92383ef9b73f..04789fa2018a 100644 --- a/src/agents/live-model-switch.ts +++ b/src/agents/live-model-switch.ts @@ -4,7 +4,7 @@ import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { resolveSessionAuthProfileOverrideSource } from "../config/sessions/auth-profile-override-provenance.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { loadSessionEntry, loadSessionEntryReadOnly, @@ -45,7 +45,7 @@ function resolveLiveSessionModelSelection(params: { return null; } const agentId = normalizeOptionalString(params.agentId); - const storePath = resolveStorePath(cfg.session?.store, { + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId, }); const entry = loadSessionEntryReadOnly({ @@ -190,7 +190,7 @@ export function shouldSwitchToLiveModel(params: { if (!cfg || !sessionKey) { return undefined; } - const storePath = resolveStorePath(cfg.session?.store, { + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: params.agentId?.trim(), }); const entry = loadSessionEntry({ @@ -271,7 +271,7 @@ export async function consolidateLiveModelSwitchAfterRun(params: { config: cfg, agentId: params.agentId, }); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); if (!storePath) { return; } @@ -319,7 +319,7 @@ export async function clearLiveModelSwitchPending(params: { if (!cfg || !sessionKey) { return; } - const storePath = resolveStorePath(cfg.session?.store, { + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: params.agentId?.trim(), }); if (!storePath) { diff --git a/src/agents/main-session-recovery/main-session-restart-recovery-shared.ts b/src/agents/main-session-recovery/main-session-restart-recovery-shared.ts index ccd6795f0883..9288da02030f 100644 --- a/src/agents/main-session-recovery/main-session-restart-recovery-shared.ts +++ b/src/agents/main-session-recovery/main-session-restart-recovery-shared.ts @@ -2,7 +2,7 @@ import path from "node:path"; import { resolveStateDir } from "../../config/paths.js"; import { listConfiguredSessionStoreAgentIds, - resolveStorePath, + resolveSessionStorePathCore, type InternalSessionEntry as SessionEntry, resolveAllAgentSessionStoreTargetsSync, } from "../../config/sessions.js"; @@ -94,7 +94,7 @@ export async function resolveRestartRecoveryStorePaths(params: { const configuredAgentIds = listConfiguredSessionStoreAgentIds(params.cfg); const configuredStorePaths = new Set( configuredAgentIds.map((agentId) => - path.resolve(resolveStorePath(params.cfg?.session?.store, { agentId, env })), + path.resolve(resolveSessionStorePathCore(params.cfg?.session?.store, { agentId, env })), ), ); const configuredAgentIdSet = new Set(configuredAgentIds); diff --git a/src/agents/model-auth-model.ts b/src/agents/model-auth-model.ts index 42ed3a83d9b7..852874a7a1fa 100644 --- a/src/agents/model-auth-model.ts +++ b/src/agents/model-auth-model.ts @@ -29,7 +29,7 @@ import { import { isAuthModeAllowedForModel } from "./model-auth-openai.js"; import * as authConfig from "./model-auth-provider-config.js"; import { - resolveApiKeyForProvider, + resolveApiKeyForProviderCore, resolveScopedAuthProfileStore, type ProviderCredentialPrecedence, } from "./model-auth-provider.js"; @@ -246,7 +246,7 @@ export async function getApiKeyForModelCore(params: { skipSetupProviderFallback?: boolean; secretSentinels?: boolean; }): Promise { - return resolveApiKeyForProvider({ + return resolveApiKeyForProviderCore({ provider: params.model.provider, cfg: params.cfg, profileId: params.profileId, diff --git a/src/agents/model-auth-provider.ts b/src/agents/model-auth-provider.ts index b7817dc82ebf..eb1943e20233 100644 --- a/src/agents/model-auth-provider.ts +++ b/src/agents/model-auth-provider.ts @@ -73,7 +73,7 @@ export function resolveScopedAuthProfileStore(params: { } /** Resolves the credential that should be used for one provider request. */ -export async function resolveApiKeyForProvider(params: { +export async function resolveApiKeyForProviderCore(params: { provider: string; cfg?: OpenClawConfig; profileId?: string; @@ -176,7 +176,7 @@ export async function resolveApiKeyForProvider(params: { modelApi: params.modelApi, }) ) { - return resolveApiKeyForProvider({ + return resolveApiKeyForProviderCore({ ...params, store, profileId: undefined, @@ -249,7 +249,7 @@ export async function resolveApiKeyForProvider(params: { mode: resolvedMode, }) ) { - return resolveApiKeyForProvider({ ...params, credentialPrecedence: "profile-first" }); + return resolveApiKeyForProviderCore({ ...params, credentialPrecedence: "profile-first" }); } return { apiKey: authConfig.sentinelizeConfigSecretRefEnvApiKey({ diff --git a/src/agents/model-auth.oauth-refresh-fail-closed.test.ts b/src/agents/model-auth.oauth-refresh-fail-closed.test.ts index 49dc8e98e518..dfb5d45340e9 100644 --- a/src/agents/model-auth.oauth-refresh-fail-closed.test.ts +++ b/src/agents/model-auth.oauth-refresh-fail-closed.test.ts @@ -15,7 +15,7 @@ vi.mock("./auth-profiles.js", async (importActual) => { }; }); -const { resolveApiKeyForProvider } = await import("./model-auth.js"); +const { resolveApiKeyForProviderCore } = await import("./model-auth.js"); function withEnv(key: string, value: string, fn: () => Promise): Promise { const previous = process.env[key]; @@ -33,7 +33,7 @@ afterEach(() => { authProfileMocks.resolveApiKeyForProfile.mockReset(); }); -describe("resolveApiKeyForProvider OAuth refresh failure ordering", () => { +describe("resolveApiKeyForProviderCore OAuth refresh failure ordering", () => { it("does not fall back to env after a configured OAuth profile refresh fails", async () => { const profileId = "openai:oauth-refresh"; const refreshFailure = new OAuthRefreshFailureError({ @@ -59,7 +59,7 @@ describe("resolveApiKeyForProvider OAuth refresh failure ordering", () => { await withEnv("OPENAI_API_KEY", "fallback-must-not-be-used", async () => { await expect( (async () => { - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: "openai", cfg: { auth: { diff --git a/src/agents/model-auth.profiles.test.ts b/src/agents/model-auth.profiles.test.ts index 60170f39a362..7b2f107c22a9 100644 --- a/src/agents/model-auth.profiles.test.ts +++ b/src/agents/model-auth.profiles.test.ts @@ -22,7 +22,7 @@ import { hasAvailableAuthForProvider, hasRuntimeAvailableProviderAuth, isConfigBackedInlineProviderApiKey, - resolveApiKeyForProvider, + resolveApiKeyForProviderCore, resolveEnvApiKey, resolveModelAuthMode, } from "./model-auth.js"; @@ -312,7 +312,7 @@ const BEDROCK_PROVIDER_CFG_WITH_PROFILE = { } as const; async function resolveBedrockProvider() { - return resolveApiKeyForProvider({ + return resolveApiKeyForProviderCore({ provider: "amazon-bedrock", store: { version: 1, profiles: {} }, cfg: BEDROCK_PROVIDER_CFG as never, @@ -332,7 +332,7 @@ async function expectBedrockAuthSource(params: { } it("resolves config-only aws-sdk profiles without stored credentials", async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "amazon-bedrock", profileId: "amazon-bedrock:default", store: { version: 1, profiles: {} }, @@ -346,7 +346,7 @@ it("resolves config-only aws-sdk profiles without stored credentials", async () }); it("uses configured aws-sdk profile order without stored credentials", async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "amazon-bedrock", store: { version: 1, profiles: {} }, cfg: BEDROCK_PROVIDER_CFG_WITH_PROFILE as never, @@ -395,7 +395,7 @@ async function resolveDemoLocalApiKey(params: { configuredApiKey: string; }) { return await withEnvAsync({ DEMO_LOCAL_API_KEY: params.envApiKey }, async () => { - return await resolveApiKeyForProvider({ + return await resolveApiKeyForProviderCore({ provider: "demo-local", store: buildDemoLocalStore(params.storedKeys), cfg: buildDemoLocalProviderCfg(params.configuredApiKey), @@ -591,7 +591,7 @@ describe("getApiKeyForModelCore", () => { }, }; - const resolved = await resolveApiKeyForProvider({ provider: "xai", cfg }); + const resolved = await resolveApiKeyForProviderCore({ provider: "xai", cfg }); expect(resolved.apiKey).toBe("configured-agent-key"); expect(resolved.source).toBe("profile:xai:default"); }, @@ -635,9 +635,9 @@ describe("getApiKeyForModelCore", () => { }; await withEnvAsync({ DEMO_LOCAL_API_KEY: "env-demo-key" }, async () => { - await expect(resolveApiKeyForProvider({ provider: "demo-local", cfg })).rejects.toThrow( - /Inline API key for provider "demo-local" is temporarily disabled/, - ); + await expect( + resolveApiKeyForProviderCore({ provider: "demo-local", cfg }), + ).rejects.toThrow(/Inline API key for provider "demo-local" is temporarily disabled/); }); }, ); @@ -667,7 +667,7 @@ describe("getApiKeyForModelCore", () => { }, }; - await expect(resolveApiKeyForProvider({ provider: "xai", cfg })).rejects.toThrow( + await expect(resolveApiKeyForProviderCore({ provider: "xai", cfg })).rejects.toThrow( `agentDir: ${configuredAgentDir}`, ); }, @@ -696,7 +696,7 @@ describe("getApiKeyForModelCore", () => { }, }); - const resolved = await resolveApiKeyForProvider({ provider: "openai" }); + const resolved = await resolveApiKeyForProviderCore({ provider: "openai" }); expect(resolved).toMatchObject({ apiKey: oauthFixture.access, @@ -726,7 +726,7 @@ describe("getApiKeyForModelCore", () => { }, }, async () => { - await expect(resolveApiKeyForProvider({ provider: "openai" })).rejects.toMatchObject({ + await expect(resolveApiKeyForProviderCore({ provider: "openai" })).rejects.toMatchObject({ code: "missing-provider-auth", message: expect.stringContaining('No API key found for provider "openai".'), provider: "openai", @@ -755,7 +755,7 @@ describe("getApiKeyForModelCore", () => { agentEnv: "main", }, async () => { - const resolved = await resolveApiKeyForProvider({ provider: "claude-cli" }); + const resolved = await resolveApiKeyForProviderCore({ provider: "claude-cli" }); expect(resolved.apiKey).toBe("claude-cli-access"); expect(resolved.profileId).toBe("anthropic:claude-cli"); expect(resolved.source).toBe("profile:anthropic:claude-cli"); @@ -778,7 +778,7 @@ describe("getApiKeyForModelCore", () => { async () => { let error: unknown = null; try { - await resolveApiKeyForProvider({ + await resolveApiKeyForProviderCore({ provider: "zai", store: { version: 1, profiles: {} }, }); @@ -798,7 +798,7 @@ describe("getApiKeyForModelCore", () => { Z_AI_API_KEY: "zai-test-key", // pragma: allowlist secret }, async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "zai", store: { version: 1, profiles: {} }, }); @@ -815,7 +815,7 @@ describe("getApiKeyForModelCore", () => { Z_AI_API_KEY: undefined, }, async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "zai", store: { version: 1, @@ -837,7 +837,7 @@ describe("getApiKeyForModelCore", () => { it("keeps stored provider auth ahead of env by default", async () => { await withEnvAsync({ OPENAI_API_KEY: "env-openai-key" }, async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openai", store: { version: 1, @@ -858,7 +858,7 @@ describe("getApiKeyForModelCore", () => { it("supports env-first precedence for live auth probes", async () => { await withEnvAsync({ OPENAI_API_KEY: "env-openai-key" }, async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openai", credentialPrecedence: "env-first", store: { @@ -892,7 +892,7 @@ describe("getApiKeyForModelCore", () => { try { await withEnvAsync({ WORKSPACE_CLOUD_CREDENTIALS: credentialsPath }, async () => { const store = { version: 1 as const, profiles: {} }; - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "workspace-cloud", cfg, store, @@ -1107,7 +1107,7 @@ describe("getApiKeyForModelCore", () => { it("resolves Synthetic API key from env", async () => { await withEnvAsync({ [envVar("SYNTHETIC", "API", "KEY")]: "synthetic-test-key" }, async () => { // pragma: allowlist secret - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "synthetic", store: { version: 1, profiles: {} }, }); @@ -1119,7 +1119,7 @@ describe("getApiKeyForModelCore", () => { it("resolves Qianfan API key from env", async () => { await withEnvAsync({ [envVar("QIANFAN", "API", "KEY")]: "qianfan-test-key" }, async () => { // pragma: allowlist secret - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "qianfan", store: { version: 1, profiles: {} }, }); @@ -1133,7 +1133,7 @@ describe("getApiKeyForModelCore", () => { { [envVar("MODELSTUDIO", "API", "KEY")]: "modelstudio-test-key" }, async () => { // pragma: allowlist secret - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "qwen", store: { version: 1, profiles: {} }, }); @@ -1145,7 +1145,7 @@ describe("getApiKeyForModelCore", () => { it("resolves plugin-owned synthetic local auth for a configured provider without apiKey", async () => { await withEnvAsync({ DEMO_LOCAL_API_KEY: undefined }, async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "demo-local", store: { version: 1, profiles: {} }, cfg: { @@ -1169,7 +1169,7 @@ describe("getApiKeyForModelCore", () => { it("does not mint synthetic local auth for empty provider stubs", async () => { await withEnvAsync({ DEMO_LOCAL_API_KEY: undefined }, async () => { await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "demo-local", store: { version: 1, profiles: {} }, cfg: { @@ -1190,7 +1190,7 @@ describe("getApiKeyForModelCore", () => { it("prefers explicit provider env auth over synthetic local key", async () => { await withEnvAsync({ [envVar("DEMO", "LOCAL", "API", "KEY")]: "env-demo-key" }, async () => { // pragma: allowlist secret - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "demo-local", store: { version: 1, profiles: {} }, cfg: { @@ -1235,7 +1235,7 @@ describe("getApiKeyForModelCore", () => { it("blocks explicit configured apiKey while its inline provider cooldown is active", async () => { const usageId = resolveInlineProviderApiKeyUsageId("demo-local"); await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "demo-local", store: { version: 1, @@ -1290,7 +1290,7 @@ describe("getApiKeyForModelCore", () => { }; await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "inline-cloud", store, cfg, @@ -1370,7 +1370,7 @@ describe("getApiKeyForModelCore", () => { await expect( hasAvailableAuthForProvider({ provider: "inline-cloud", store, cfg }), ).resolves.toBe(true); - const resolved = await resolveApiKeyForProvider({ provider: "inline-cloud", store, cfg }); + const resolved = await resolveApiKeyForProviderCore({ provider: "inline-cloud", store, cfg }); expect(resolved.apiKey).toBe("stored-cloud-key"); expect(resolved.source).toBe("profile:inline-cloud:default"); }); @@ -1403,7 +1403,7 @@ describe("getApiKeyForModelCore", () => { }; await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "inline-cloud", store, cfg, @@ -1449,7 +1449,7 @@ describe("getApiKeyForModelCore", () => { }); it("defers plugin-owned synthetic profile markers without core provider branching", async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "demo-local", store: { version: 1, @@ -1482,7 +1482,7 @@ describe("getApiKeyForModelCore", () => { it("still throws when no env/profile/config provider auth is available", async () => { await withEnvAsync({ DEMO_LOCAL_API_KEY: undefined }, async () => { await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "demo-local", store: { version: 1, profiles: {} }, }), @@ -1493,7 +1493,7 @@ describe("getApiKeyForModelCore", () => { it("resolves Vercel AI Gateway API key from env", async () => { await withEnvAsync({ [envVar("AI_GATEWAY", "API", "KEY")]: "gateway-test-key" }, async () => { // pragma: allowlist secret - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "vercel-ai-gateway", store: { version: 1, profiles: {} }, }); @@ -1541,7 +1541,7 @@ describe("getApiKeyForModelCore", () => { it("accepts VOYAGE_API_KEY for voyage", async () => { await withEnvAsync({ [envVar("VOYAGE", "API", "KEY")]: "voyage-test-key" }, async () => { // pragma: allowlist secret - const voyage = await resolveApiKeyForProvider({ + const voyage = await resolveApiKeyForProviderCore({ provider: "voyage", store: { version: 1, profiles: {} }, }); @@ -1899,11 +1899,11 @@ describe("getApiKeyForModelCore", () => { }); }); -describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference", () => { +describe("resolveApiKeyForProviderCore — per-entry apiKey as profile ID reference", () => { it("resolves actual credential when per-entry apiKey matches a profile ID in the store", async () => { // Scenario from #67423: openrouter-minimax.apiKey = "openrouter:key-b" // should resolve the actual key from that profile, not use the string literally. - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openrouter-minimax", cfg: { models: { @@ -1941,7 +1941,7 @@ describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference" }); it("does not treat a literal API key as a profile ID when no matching profile exists", async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openrouter-minimax", cfg: { models: { @@ -1973,7 +1973,7 @@ describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference" it("does not treat env SecretRef ids as profile references", async () => { await withEnvAsync({ OPENROUTER_PROFILE: "sk-or-env-secret" }, async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openrouter-minimax", cfg: { models: { @@ -2010,7 +2010,7 @@ describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference" it("keeps env-first precedence ahead of per-entry profile references", async () => { await withEnvAsync({ OPENAI_API_KEY: "sk-env-first" }, async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openai", credentialPrecedence: "env-first", cfg: { @@ -2074,7 +2074,7 @@ describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference" }, }; - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openai", credentialPrecedence: "env-first", cfg, @@ -2092,7 +2092,7 @@ describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference" it("does not bleed auth.order canonical provider profiles into a per-entry provider", async () => { // auth.order.openrouter should not be selected when resolving openrouter-minimax // that has its own per-entry apiKey = "openrouter:key-b" profile reference. - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openrouter-minimax", cfg: { models: { @@ -2148,7 +2148,7 @@ describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference" // Before the fix the explicit `auth: "api-key"` early-return short-circuited // resolveUsableCustomProviderApiKey and sent "openrouter:key-b" as a literal bearer // before the profile-ref logic could run. Verify the profile-ref lookup wins. - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openrouter-minimax", cfg: { models: { @@ -2187,7 +2187,7 @@ describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference" it("applies model auth-mode guards to per-entry token profile references", async () => { await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "openai", modelApi: "openai-responses", cfg: { @@ -2218,7 +2218,7 @@ describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference" it("throws when matched profile is an OAuth credential routed to an api-key provider (clawsweeper P1)", async () => { await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "openrouter-minimax", cfg: { models: { @@ -2252,7 +2252,7 @@ describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference" it("throws when a bearer profile points at a different provider endpoint", async () => { await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "custom-proxy", cfg: { models: { @@ -2291,7 +2291,7 @@ describe("resolveApiKeyForProvider — per-entry apiKey as profile ID reference" // `resolveUsableCustomProviderApiKey` and send "openrouter:key-b" itself as the // literal bearer — the original #67423 failure mode. Verify it throws instead. await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "openrouter-minimax", cfg: { models: { diff --git a/src/agents/model-auth.test.ts b/src/agents/model-auth.test.ts index a412ed74aabc..b60ac72dcf5c 100644 --- a/src/agents/model-auth.test.ts +++ b/src/agents/model-auth.test.ts @@ -180,7 +180,7 @@ let hasUsableCustomProviderApiKey: typeof import("./model-auth.js").hasUsableCus let hasSyntheticLocalProviderAuthConfig: typeof import("./model-auth.js").hasSyntheticLocalProviderAuthConfig; let requireApiKey: typeof import("./model-auth.js").requireApiKey; let getApiKeyForModelCore: typeof import("./model-auth.js").getApiKeyForModelCore; -let resolveApiKeyForProvider: typeof import("./model-auth.js").resolveApiKeyForProvider; +let resolveApiKeyForProviderCore: typeof import("./model-auth.js").resolveApiKeyForProviderCore; let resolveAwsSdkEnvVarName: typeof import("./model-auth.js").resolveAwsSdkEnvVarName; let resolveModelAuthMode: typeof import("./model-auth.js").resolveModelAuthMode; let resolveUsableCustomProviderApiKey: typeof import("./model-auth.js").resolveUsableCustomProviderApiKey; @@ -213,7 +213,7 @@ beforeAll(async () => { getApiKeyForModelCore, hasUsableCustomProviderApiKey, requireApiKey, - resolveApiKeyForProvider, + resolveApiKeyForProviderCore, resolveAwsSdkEnvVarName, resolveModelAuthMode, resolveUsableCustomProviderApiKey, @@ -300,7 +300,7 @@ async function resolveCustomProviderAuth( modelId?: string, modelName?: string, ) { - return resolveApiKeyForProvider({ + return resolveApiKeyForProviderCore({ provider, cfg: { models: { @@ -313,7 +313,7 @@ async function resolveCustomProviderAuth( } function expectAuthFields( - auth: Awaited>, + auth: Awaited>, expected: { apiKey: string; mode: "api-key" | "oauth"; @@ -328,7 +328,7 @@ function expectAuthFields( } function expectSecretSentinelAuth( - auth: Awaited>, + auth: Awaited>, expected: { value: string; source: string; mode: "api-key" | "oauth" }, ) { const apiKey = auth.apiKey; @@ -645,7 +645,7 @@ describe("resolveUsableCustomProviderApiKey", () => { const previous = process.env.OPENAI_API_KEY; process.env.OPENAI_API_KEY = "sk-secretref-env-first"; // pragma: allowlist secret try { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ cfg: { models: { providers: { @@ -878,7 +878,7 @@ describe("resolveUsableCustomProviderApiKey", () => { }); }); -describe("resolveApiKeyForProvider", () => { +describe("resolveApiKeyForProviderCore", () => { it("does not fall back to env or profiles after an explicit provider SecretRef fails", async () => { const sourceConfig = { models: { @@ -905,7 +905,7 @@ describe("resolveApiKeyForProvider", () => { await withEnv("OPENAI_API_KEY", "must-not-be-used", async () => { await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "openai", cfg: sourceConfig, store: { @@ -959,7 +959,7 @@ describe("resolveApiKeyForProvider", () => { await withEnv("OPENAI_API_KEY", "must-not-be-used", async () => { await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "openai", cfg: sourceConfig, store: { version: 1, profiles: {} }, @@ -1019,7 +1019,7 @@ describe("resolveApiKeyForProvider", () => { }; await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "anthropic", profileId: healthyProfileId, cfg, @@ -1032,7 +1032,7 @@ describe("resolveApiKeyForProvider", () => { await withEnv("OPENAI_API_KEY", "unused", async () => { await expect( (async () => { - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: "openai", cfg, store, @@ -1051,7 +1051,7 @@ describe("resolveApiKeyForProvider", () => { it("keeps plain environment credentials as plaintext", async () => { const resolved = await withEnv("OPENAI_API_KEY", "sk-plain-env-key", () => - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "openai", store: { version: 1, profiles: {} }, }), @@ -1086,7 +1086,7 @@ describe("resolveApiKeyForProvider", () => { }, agentDir, ); - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openai", profileId, secretSentinels: true, @@ -1126,7 +1126,7 @@ describe("resolveApiKeyForProvider", () => { }, agentDir, ); - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openai", profileId, store, @@ -1139,7 +1139,7 @@ describe("resolveApiKeyForProvider", () => { it("reuses plugin fallback auth without a models.providers entry", async () => { const resolved = await withoutEnv("PLUGIN_WEB_API_KEY", () => - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "plugin-web", cfg: { plugins: { @@ -1195,7 +1195,7 @@ describe("resolveApiKeyForProvider", () => { setRuntimeConfigSnapshot(runtimeConfig, sourceConfig); const resolved = await withoutEnv("PLUGIN_WEB_API_KEY", () => - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "plugin-web", cfg: sourceConfig, secretSentinels: true, @@ -1248,7 +1248,7 @@ describe("resolveApiKeyForProvider", () => { }; setRuntimeConfigSnapshot(runtimeConfig, sourceConfig); - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "cliproxyapi", cfg: sourceConfig, secretSentinels: true, @@ -1301,7 +1301,7 @@ describe("resolveApiKeyForProvider", () => { }; setRuntimeConfigSnapshot(runtimeConfig, sourceConfig); - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "cliproxyapi", cfg: structuredClone(runtimeConfig), secretSentinels: true, @@ -1314,7 +1314,7 @@ describe("resolveApiKeyForProvider", () => { mode: "api-key", }); - const preferred = await resolveApiKeyForProvider({ + const preferred = await resolveApiKeyForProviderCore({ provider: "cliproxyapi", cfg: structuredClone(runtimeConfig), preferredProfile: "cliproxyapi:preferred", @@ -1382,7 +1382,7 @@ describe("resolveApiKeyForProvider", () => { }; await expect( - resolveApiKeyForProvider({ provider: "cliproxyapi", cfg: sourceConfig, store }), + resolveApiKeyForProviderCore({ provider: "cliproxyapi", cfg: sourceConfig, store }), ).rejects.toThrow(/Inline API key for provider "cliproxyapi" is temporarily disabled/); await expect( hasAvailableAuthForProvider({ provider: "cliproxyapi", cfg: sourceConfig, store }), @@ -1413,7 +1413,7 @@ describe("resolveApiKeyForProvider", () => { }; await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "cliproxyapi", cfg: sourceConfig, store: { version: 1, profiles: {} }, @@ -1465,7 +1465,7 @@ describe("resolveApiKeyForProvider", () => { ); await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "cliproxyapi", cfg: sourceConfig, store: { version: 1, profiles: {} }, @@ -1476,7 +1476,7 @@ describe("resolveApiKeyForProvider", () => { it("does not reuse plugin fallback auth when the plugin is disabled", async () => { await expect( withoutEnv("PLUGIN_WEB_API_KEY", () => - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "plugin-web", cfg: { plugins: { @@ -1499,7 +1499,7 @@ describe("resolveApiKeyForProvider", () => { }); it("reuses plugin-owned native CLI auth", async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "native-cli", cfg: { agents: { @@ -1521,7 +1521,7 @@ describe("resolveApiKeyForProvider", () => { }); it("reuses the loaded auth profile store after deferring an explicit synthetic profile", async () => { - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: "custom-auth", profileId: "custom-auth:synthetic", store: { @@ -1549,7 +1549,7 @@ describe("resolveApiKeyForProvider", () => { }); it("prefers explicit api-key provider config over ambient auth profiles", async () => { - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openai", cfg: { models: { @@ -1707,7 +1707,7 @@ describe("resolveApiKeyForProvider", () => { sourceConfig, ); - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "cliproxyapi", cfg: sourceConfig, secretSentinels: true, @@ -1778,7 +1778,7 @@ describe("resolveApiKeyForProvider", () => { it("prefers non-secret local env markers over ambient profiles", async () => { const resolved = await withEnv("OLLAMA_API_KEY", "ollama-local", () => - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "ollama", store: { version: 1, @@ -1801,7 +1801,7 @@ describe("resolveApiKeyForProvider", () => { }); }); -describe("resolveApiKeyForProvider – synthetic local auth for custom providers", () => { +describe("resolveApiKeyForProviderCore – synthetic local auth for custom providers", () => { it("recognizes local baseUrl variants for synthetic auth config", () => { const localBaseUrls = [ "http://127.0.0.1:8080/v1", @@ -1875,7 +1875,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers ).toBe(false); await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "my-remote", cfg: { models: { @@ -1903,7 +1903,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers }); it("preserves custom named Ollama providers with explicit local marker auth", async () => { - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: "ollama-remote", cfg: { models: { @@ -1938,7 +1938,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers }); it("uses Ollama plugin synthetic auth for custom private provider ids without apiKey", async () => { - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: "ollama-gpu1", cfg: { models: { @@ -1998,7 +1998,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers sourceConfig, ); - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: "ollama-gpu1", cfg: sourceConfig, secretSentinels: true, @@ -2060,7 +2060,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers }); it("accepts non-secret local markers for private LAN custom OpenAI-compatible providers", async () => { - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: "custom-192-168-0-222-11434", cfg: { models: { @@ -2097,7 +2097,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers it.each(["docker.orb.internal", "host.docker.internal", "host.orb.internal"])( "accepts ollama-local marker auth for host-backed alias %s", async (hostname) => { - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: "ollama", cfg: { models: { @@ -2134,7 +2134,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers it("does not accept non-secret local markers for remote custom providers", async () => { await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "custom-remote", cfg: { models: { @@ -2168,7 +2168,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers delete process.env.OPENAI_API_KEY; try { await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "custom", cfg: { models: { @@ -2205,7 +2205,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers it("does not synthesize local auth when auth mode explicitly requires oauth", async () => { await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "custom", cfg: { models: { @@ -2234,7 +2234,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers }); it("uses explicit aws-sdk auth for local baseUrl overrides", async () => { - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: "amazon-bedrock", cfg: { models: { diff --git a/src/agents/model-auth.ts b/src/agents/model-auth.ts index 50d543e26ff0..fbdbf7d011be 100644 --- a/src/agents/model-auth.ts +++ b/src/agents/model-auth.ts @@ -29,7 +29,7 @@ export { shouldPreferExplicitConfigApiKeyAuth, } from "./model-auth-provider-config.js"; export type { ProviderEntryApiKeyBindingResolution } from "./model-auth-provider-config.js"; -export { resolveApiKeyForProvider } from "./model-auth-provider.js"; +export { resolveApiKeyForProviderCore } from "./model-auth-provider.js"; export type { ProviderCredentialPrecedence } from "./model-auth-provider.js"; export { createRuntimeProviderAuthLookup, diff --git a/src/agents/model-auth.workspace-plugin.test.ts b/src/agents/model-auth.workspace-plugin.test.ts index aeeef35affa1..201c9e972c91 100644 --- a/src/agents/model-auth.workspace-plugin.test.ts +++ b/src/agents/model-auth.workspace-plugin.test.ts @@ -9,7 +9,7 @@ import type { AuthProfileStore } from "./auth-profiles.js"; import { resolveEnvApiKey } from "./model-auth-env.js"; import { hasAvailableAuthForProvider, - resolveApiKeyForProvider, + resolveApiKeyForProviderCore, resolveModelAuthMode, } from "./model-auth.js"; import { hasAuthForModelProvider } from "./model-provider-auth.js"; @@ -85,7 +85,7 @@ describe("workspace plugin model auth evidence", () => { source: "workspace cloud credentials", }); await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "workspace-cloud", cfg, workspaceDir, diff --git a/src/agents/model-scan.ts b/src/agents/model-scan.ts index 0acf39dad833..e2429ea5e3aa 100644 --- a/src/agents/model-scan.ts +++ b/src/agents/model-scan.ts @@ -27,7 +27,7 @@ import { cancelUnreadResponseBody } from "../infra/http-body.js"; import { readResponseWithLimit } from "../infra/http-body.js"; import "../llm/ai-transport-host.js"; import type { Context, Model, Tool } from "../llm/types.js"; -import { withTimeout } from "../node-host/with-timeout.js"; +import { runAbortableTimeout } from "../node-host/with-timeout.js"; import { inferParamBFromIdOrName } from "../shared/model-param-b.js"; const OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models"; @@ -213,7 +213,7 @@ async function fetchOpenRouterModels( try { // fetch resolves after headers, so keep the shared timeout active until // the provider-controlled catalog body has been consumed. - return await withTimeout( + return await runAbortableTimeout( async (signal) => { res = await fetchImpl(OPENROUTER_MODELS_URL, { headers: { Accept: "application/json" }, @@ -307,7 +307,7 @@ async function probeTool( }; const startedAt = Date.now(); try { - const message = await withTimeout( + const message = await runAbortableTimeout( (signal) => complete(model, context, { apiKey, @@ -359,7 +359,7 @@ async function probeImage( }; const startedAt = Date.now(); try { - await withTimeout( + await runAbortableTimeout( (signal) => complete(model, context, { apiKey, diff --git a/src/agents/models-config.e2e-harness.ts b/src/agents/models-config.e2e-harness.ts index 16930931aaa9..e75ff95ab50c 100644 --- a/src/agents/models-config.e2e-harness.ts +++ b/src/agents/models-config.e2e-harness.ts @@ -5,7 +5,7 @@ import { afterEach, beforeEach } from "vitest"; import { clearConfigCache, clearRuntimeConfigSnapshot } from "../config/config.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; -import { withTempHome as withTempHomeBase } from "../plugin-sdk/test-helpers/temp-home.js"; +import { withTempHomeCore as withTempHomeBase } from "../plugin-sdk/test-helpers/temp-home.js"; import { resetPluginLoaderTestStateForTest } from "../plugins/loader.test-fixtures.js"; import { resetModelsJsonReadyCacheForTest } from "./models-config-state.test-support.js"; diff --git a/src/agents/modes/interactive/components/diff.ts b/src/agents/modes/interactive/components/diff.ts index 0e37809d4c80..c03580dde536 100644 --- a/src/agents/modes/interactive/components/diff.ts +++ b/src/agents/modes/interactive/components/diff.ts @@ -4,7 +4,7 @@ * Produces colored line and intra-line highlights for the Pi TUI review surfaces. */ import { diffWords } from "diff"; -import { theme } from "../theme/theme.js"; +import { interactiveAgentTheme as theme } from "../theme/theme.js"; /** * Parse diff line to extract prefix, line number, and content. diff --git a/src/agents/modes/interactive/components/keybinding-hints.ts b/src/agents/modes/interactive/components/keybinding-hints.ts index 15519fe5e509..7e70b2c1082a 100644 --- a/src/agents/modes/interactive/components/keybinding-hints.ts +++ b/src/agents/modes/interactive/components/keybinding-hints.ts @@ -3,7 +3,7 @@ */ import { getKeybindings, type Keybinding, type KeyId } from "@earendil-works/pi-tui"; -import { theme } from "../theme/theme.js"; +import { interactiveAgentTheme as theme } from "../theme/theme.js"; function formatKeyPart(part: string): string { return process.platform === "darwin" && part.toLowerCase() === "alt" ? "option" : part; diff --git a/src/agents/modes/interactive/theme/theme.ts b/src/agents/modes/interactive/theme/theme.ts index 9cc44cbaae3a..ebd02459d383 100644 --- a/src/agents/modes/interactive/theme/theme.ts +++ b/src/agents/modes/interactive/theme/theme.ts @@ -545,7 +545,7 @@ const THEME_KEY = Symbol.for("openclaw:agent-theme"); // Export theme as a getter that reads from globalThis // This ensures all module instances (tsx, jiti) see the same theme -export const theme: Theme = new Proxy({} as Theme, { +export const interactiveAgentTheme: Theme = new Proxy({} as Theme, { get(_target, prop) { const t = (globalThis as Record)[THEME_KEY]; if (!t) { @@ -607,12 +607,12 @@ export function highlightCode(code: string, lang?: string): string[] { // auto-detection is unreliable and can misidentify prose as AppleScript, // LiveCodeServer, etc., coloring random English words as keywords. if (!validLang) { - return code.split("\n").map((line) => theme.fg("mdCodeBlock", line)); + return code.split("\n").map((line) => interactiveAgentTheme.fg("mdCodeBlock", line)); } const opts = { language: validLang, ignoreIllegals: true, - theme: getCliHighlightTheme(theme), + theme: getCliHighlightTheme(interactiveAgentTheme), }; try { return highlight(code, opts).split("\n"); diff --git a/src/agents/openclaw-plugin-tools.ts b/src/agents/openclaw-plugin-tools.ts index 8d8af3192f5d..400d931eb714 100644 --- a/src/agents/openclaw-plugin-tools.ts +++ b/src/agents/openclaw-plugin-tools.ts @@ -11,7 +11,7 @@ import type { AuthProfileStore } from "./auth-profiles/types.js"; import { createRuntimeProviderAuthLookup, hasRuntimeAvailableProviderAuth, - resolveApiKeyForProvider as resolveProviderAuth, + resolveApiKeyForProviderCore as resolveProviderAuth, } from "./model-auth.js"; import { createNodePluginTools } from "./node-plugin-tools.js"; import { diff --git a/src/agents/openclaw-tools.session-status.test.ts b/src/agents/openclaw-tools.session-status.test.ts index ee74ee66b9f2..e915b3a18bce 100644 --- a/src/agents/openclaw-tools.session-status.test.ts +++ b/src/agents/openclaw-tools.session-status.test.ts @@ -183,7 +183,7 @@ function createSessionsModuleMock() { } : null; }, - resolveStorePath: resolveMockStorePath, + resolveSessionStorePathCore: resolveMockStorePath, }; } @@ -339,7 +339,7 @@ vi.mock("../plugins/plugin-metadata-snapshot.js", async (importOriginal) => ({ vi.mock("../plugins/provider-thinking.js", () => ({ resolveProviderBinaryThinking: () => undefined, resolveProviderDefaultThinkingLevel: () => undefined, - resolveProviderThinkingProfile: () => undefined, + resolveEffectiveThinkingProfile: () => undefined, resolveProviderXHighThinking: () => undefined, })); // Keep provider-runtime/plugin activation out of this focused tool test. The diff --git a/src/agents/openclaw-tools.sessions.test.ts b/src/agents/openclaw-tools.sessions.test.ts index 17e6f6b63eb0..71c494efb4a4 100644 --- a/src/agents/openclaw-tools.sessions.test.ts +++ b/src/agents/openclaw-tools.sessions.test.ts @@ -8,7 +8,7 @@ import type { ChannelMessagingAdapter } from "../channels/plugins/types.public.j import type { OpenClawConfig } from "../config/config.js"; import { appendTranscriptMessage, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { createSessionVisibilityChecker } from "../plugin-sdk/session-visibility.js"; import { @@ -569,7 +569,7 @@ describe("sessions tools", () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-sessions-list-preview-")); const storePath = path.join(tmpDir, "sessions.json"); try { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:main", storePath }, { sessionId: "visible", updatedAt: 20 }, ); @@ -581,7 +581,7 @@ describe("sessions tools", () => { { agentId: "main", sessionId: "visible", sessionKey: "agent:main:main", storePath }, { cwd: tmpDir, message: { role: "assistant", content: "Visible latest reply" } }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "other", sessionKey: "agent:other:main", storePath }, { sessionId: "hidden", updatedAt: 21 }, ); @@ -1149,7 +1149,7 @@ describe("sessions tools", () => { const requesterSessionKey = "agent:main:clickclack:discussion-proof"; const targetSessionKey = "agent:main:main"; const expectedSessionId = "scoped-main-incarnation"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: targetSessionKey, storePath }, { sessionId: expectedSessionId, updatedAt: 1 }, ); @@ -1799,7 +1799,7 @@ describe("sessions tools", () => { const targetSessionId = "exact-cron-run-incarnation"; const queueMessage = vi.fn(async () => {}); try { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "leasing-ops", sessionKey: runScopedTargetKey, storePath }, { sessionId: targetSessionId, updatedAt: 1 }, ); diff --git a/src/agents/openclaw-tools.subagents.sessions-spawn.test-harness.ts b/src/agents/openclaw-tools.subagents.sessions-spawn.test-harness.ts index 6949557e23c7..8c6fb7a29b92 100644 --- a/src/agents/openclaw-tools.subagents.sessions-spawn.test-harness.ts +++ b/src/agents/openclaw-tools.subagents.sessions-spawn.test-harness.ts @@ -393,7 +393,7 @@ vi.mock("../config/sessions.js", () => ({ agentId: string; }) => `agent:${params.agentId}:${params.cfg?.session?.mainKey ?? "main"}`, resolveExistingAgentSessionStoreTargetsSync: () => [], - resolveStorePath: () => "/tmp/openclaw-sessions-spawn-test-store.json", + resolveSessionStorePathCore: () => "/tmp/openclaw-sessions-spawn-test-store.json", updateSessionStore: async ( _storePath: string, mutator: (store: typeof hoisted.sessionStore) => void | Promise, diff --git a/src/agents/prepared-model-runtime.build.ts b/src/agents/prepared-model-runtime.build.ts index 09b7ebfde13a..302e604c0f8f 100644 --- a/src/agents/prepared-model-runtime.build.ts +++ b/src/agents/prepared-model-runtime.build.ts @@ -1,6 +1,6 @@ import { performance } from "node:perf_hooks"; import pLimit from "p-limit"; -import { withTimeout } from "../node-host/with-timeout.js"; +import { runAbortableTimeout } from "../node-host/with-timeout.js"; import { runTasksWithConcurrency } from "../utils/run-with-concurrency.js"; import { resolveUsableAgentCredentialModes } from "./agent-auth-credentials.js"; import { getPreparedRuntimeAuthMaterializations } from "./auth-profiles/runtime-materializations.js"; @@ -528,7 +528,7 @@ export function startSerializedSnapshotBuildBatch( }); } return { - pending: withTimeout( + pending: runAbortableTimeout( async () => { const { actualBuild } = await startBuild; return await actualBuild; diff --git a/src/agents/run-session-target.test.ts b/src/agents/run-session-target.test.ts index bc007c198ab3..3a960f33fe72 100644 --- a/src/agents/run-session-target.test.ts +++ b/src/agents/run-session-target.test.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } from "../config/io.js"; import { formatSqliteSessionFileMarker } from "../config/sessions/legacy-sqlite-marker.js"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveAgentRunSessionTarget as resolveAgentRunSessionTargetImpl } from "./run-session-target.js"; @@ -92,7 +92,7 @@ describe("agent run session target", () => { const storePath = path.join(tempDir, "existing", "sessions.json"); const sessionId = "2fb701ef-6425-4c48-9b6f-5a170aa2477e"; const sessionKey = "agent:main:telegram:direct:reporter"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); @@ -110,7 +110,7 @@ describe("agent run session target", () => { const storePath = path.join(tempDir, "runtime-config", "sessions.json"); const sessionId = "7ef14ab2-4801-40e1-9c56-83f9250c1706"; const sessionKey = "agent:main:discord:direct:reporter"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); @@ -209,11 +209,11 @@ describe("agent run session target", () => { it("recovers the stored key from a legacy SQLite marker", async () => { const storePath = path.join(tempDir, "legacy", "sessions.json"); const sessionKey = "agent:main:dashboard:legacy-session"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "legacy-session", updatedAt: 1 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:legacy-session", storePath }, { sessionId: "legacy-session", updatedAt: 2 }, ); diff --git a/src/agents/run-session-target.ts b/src/agents/run-session-target.ts index 3dea8a52b47c..b2665f747cc1 100644 --- a/src/agents/run-session-target.ts +++ b/src/agents/run-session-target.ts @@ -2,7 +2,7 @@ import path from "node:path"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { getRuntimeConfig } from "../config/io.js"; import { parseSqliteSessionFileMarker } from "../config/sessions/legacy-sqlite-marker.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { listSessionEntriesCore, resolveTranscriptSessionKeyBySessionId, @@ -133,7 +133,7 @@ export async function resolveAgentRunSessionTarget(params: { const lookupStorePath = targetStorePath ?? legacyMarker?.storePath ?? - resolveStorePath(config.session?.store, { agentId: lookupAgentId }); + resolveSessionStorePathCore(config.session?.store, { agentId: lookupAgentId }); const storedSessionKey = params.missingSessionKey === "resolve-existing" && !targetSessionKey && @@ -205,7 +205,7 @@ export async function resolveAgentRunSessionTarget(params: { const storePath = targetStorePath ?? legacyMarker?.storePath ?? - resolveStorePath(config.session?.store, { agentId: effectiveAgentId }); + resolveSessionStorePathCore(config.session?.store, { agentId: effectiveAgentId }); return await resolveSessionTranscriptRuntimeTarget({ ...(effectiveAgentId ? { agentId: effectiveAgentId } : {}), sessionId, @@ -224,7 +224,9 @@ export async function resolveAgentRunSessionTarget(params: { }); } - const storePath = resolveStorePath(config.session?.store, { agentId: effectiveAgentId }); + const storePath = resolveSessionStorePathCore(config.session?.store, { + agentId: effectiveAgentId, + }); return await resolveSessionTranscriptRuntimeTarget({ ...(effectiveAgentId ? { agentId: effectiveAgentId } : {}), sessionId, diff --git a/src/agents/sandbox/fs-bridge-mutation-helper.test.ts b/src/agents/sandbox/fs-bridge-mutation-helper.test.ts index 14550b5096c9..d3a5d0ebc427 100644 --- a/src/agents/sandbox/fs-bridge-mutation-helper.test.ts +++ b/src/agents/sandbox/fs-bridge-mutation-helper.test.ts @@ -4,7 +4,7 @@ import { spawnSync } from "node:child_process"; import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { buildPinnedWritePlan, SANDBOX_CREATE_EXISTS_EXIT_CODE, @@ -173,7 +173,7 @@ const FORCED_EXDEV_WITH_SOURCE_REPLACEMENT_MUTATION_PYTHON = FORCED_EXDEV_MUTATI describe("sandbox pinned mutation helper", () => { it("writes through a pinned directory fd", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); await fs.mkdir(workspace, { recursive: true }); @@ -187,7 +187,7 @@ describe("sandbox pinned mutation helper", () => { }); it("creates a new file through a pinned directory fd", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); await fs.mkdir(workspace, { recursive: true }); @@ -201,7 +201,7 @@ describe("sandbox pinned mutation helper", () => { }); it("creates a file whose basename approaches the filesystem component limit", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const basename = "n".repeat(240); await fs.mkdir(workspace, { recursive: true }); @@ -214,7 +214,7 @@ describe("sandbox pinned mutation helper", () => { }); it("falls back when Linux libc does not export renameat2", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const filePath = path.join(workspace, "note.txt"); await fs.mkdir(workspace, { recursive: true }); @@ -232,7 +232,7 @@ describe("sandbox pinned mutation helper", () => { }); it("falls back when Linux renameat2 is unsupported at runtime", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const filePath = path.join(workspace, "note.txt"); await fs.mkdir(workspace, { recursive: true }); @@ -250,7 +250,7 @@ describe("sandbox pinned mutation helper", () => { }); it("removes a staging directory when opening it fails", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); await fs.mkdir(workspace, { recursive: true }); @@ -267,7 +267,7 @@ describe("sandbox pinned mutation helper", () => { }); it("refuses to create over an existing file and leaves it untouched", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const filePath = path.join(workspace, "note.txt"); await fs.mkdir(workspace, { recursive: true }); @@ -281,7 +281,7 @@ describe("sandbox pinned mutation helper", () => { }); it("removes private staging when writing fails before publication", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const filePath = path.join(workspace, "note.txt"); await fs.mkdir(workspace, { recursive: true }); @@ -300,7 +300,7 @@ describe("sandbox pinned mutation helper", () => { }); it("preserves a destination raced into a staged exclusive create", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const filePath = path.join(workspace, "note.txt"); await fs.mkdir(workspace, { recursive: true }); @@ -318,7 +318,7 @@ describe("sandbox pinned mutation helper", () => { }); it("fails when the staged exclusive-create pathname is substituted", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const filePath = path.join(workspace, "note.txt"); await fs.mkdir(workspace, { recursive: true }); @@ -339,7 +339,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "preserves existing target file mode while writing", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const filePath = path.join(workspace, "note.txt"); await fs.mkdir(workspace, { recursive: true }); @@ -359,7 +359,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "keeps restrictive existing target file mode while writing", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const filePath = path.join(workspace, "secret.txt"); await fs.mkdir(workspace, { recursive: true }); @@ -379,7 +379,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "reads through a pinned directory fd and rejects hardlinked files", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const nested = path.join(workspace, "nested"); await fs.mkdir(nested, { recursive: true }); @@ -402,7 +402,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "bounds pinned file reads and rejects growth on the opened descriptor", async () => { - await withTempDir({ prefix: "openclaw-mutation-bounded-read-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-bounded-read-" }, async (root) => { const workspace = path.join(root, "workspace"); await fs.mkdir(workspace, { recursive: true }); await fs.writeFile(path.join(workspace, "exact.txt"), "hello", "utf8"); @@ -451,7 +451,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "copies regular files atomically and rejects hardlinked sources", async () => { - await withTempDir({ prefix: "openclaw-mutation-copy-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-copy-" }, async (root) => { const sourceRoot = path.join(root, "source"); const destinationRoot = path.join(root, "destination"); const sourcePath = path.join(sourceRoot, "payload.bin"); @@ -499,7 +499,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "removes a partial temporary file when streaming copy fails", async () => { - await withTempDir({ prefix: "openclaw-mutation-copy-failure-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-copy-failure-" }, async (root) => { const sourceRoot = path.join(root, "source"); const destinationRoot = path.join(root, "destination"); await fs.mkdir(sourceRoot, { recursive: true }); @@ -526,7 +526,7 @@ describe("sandbox pinned mutation helper", () => { ); it.runIf(process.platform !== "win32")("rejects non-regular files while reading", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); await fs.mkdir(workspace, { recursive: true }); await fs.mkdir(path.join(workspace, "folder"), { recursive: true }); @@ -539,7 +539,7 @@ describe("sandbox pinned mutation helper", () => { }); it.runIf(process.platform !== "win32")("rejects FIFO reads without blocking", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-fifo-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-fifo-" }, async (root) => { const workspace = path.join(root, "workspace"); const fifoPath = path.join(workspace, "live.pipe"); await fs.mkdir(workspace, { recursive: true }); @@ -565,7 +565,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "preserves stdin payload bytes when the pinned write plan runs through sh", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); await fs.mkdir(workspace, { recursive: true }); @@ -587,7 +587,7 @@ describe("sandbox pinned mutation helper", () => { async () => { // The helper must fail before creating temp files when a parent path is a // symlink to another host directory. - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const outside = path.join(root, "outside"); await fs.mkdir(workspace, { recursive: true }); @@ -603,7 +603,7 @@ describe("sandbox pinned mutation helper", () => { ); it.runIf(process.platform !== "win32")("rejects symlink segments during mkdirp", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const outside = path.join(root, "outside"); await fs.mkdir(workspace, { recursive: true }); @@ -618,7 +618,7 @@ describe("sandbox pinned mutation helper", () => { }); it.runIf(process.platform !== "win32")("remove unlinks the symlink itself", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const outside = path.join(root, "outside"); await fs.mkdir(workspace, { recursive: true }); @@ -639,7 +639,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "rejects symlink destination parents during rename", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const workspace = path.join(root, "workspace"); const outside = path.join(root, "outside"); await fs.mkdir(workspace, { recursive: true }); @@ -670,7 +670,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "copies directories across different mount roots during rename fallback", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const sourceRoot = path.join(root, "source"); const destRoot = path.join(root, "dest"); await fs.mkdir(path.join(sourceRoot, "dir", "nested"), { recursive: true }); @@ -700,7 +700,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "rejects hardlinked files during rename EXDEV fallback", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const sourceRoot = path.join(root, "source"); const destRoot = path.join(root, "dest"); const outsideRoot = path.join(root, "outside"); @@ -736,7 +736,7 @@ describe("sandbox pinned mutation helper", () => { async () => { // EXDEV fallback copies first and removes only after validation; failures // must not delete or partially replace the source tree. - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const sourceRoot = path.join(root, "source"); const destRoot = path.join(root, "dest"); const outsideRoot = path.join(root, "outside"); @@ -778,7 +778,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "preserves source entries created after the directory rename fallback copy phase", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const sourceRoot = path.join(root, "source"); const destRoot = path.join(root, "dest"); await fs.mkdir(path.join(sourceRoot, "dir", "nested"), { recursive: true }); @@ -813,7 +813,7 @@ describe("sandbox pinned mutation helper", () => { it.runIf(process.platform !== "win32")( "preserves source entries replaced after the directory rename fallback copy phase", async () => { - await withTempDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { + await withTestDir({ prefix: "openclaw-mutation-helper-" }, async (root) => { const sourceRoot = path.join(root, "source"); const destRoot = path.join(root, "dest"); await fs.mkdir(path.join(sourceRoot, "dir", "nested"), { recursive: true }); diff --git a/src/agents/sessions/extension-sdk.ts b/src/agents/sessions/extension-sdk.ts index de00578431f6..1d632723a8f9 100644 --- a/src/agents/sessions/extension-sdk.ts +++ b/src/agents/sessions/extension-sdk.ts @@ -6,7 +6,7 @@ * so importing loader-owned modules here creates runtime cycles. */ -export { getAgentDir, VERSION } from "../config.js"; +export { getAgentDir, PACKAGE_MANIFEST_VERSION as VERSION } from "../config.js"; export * from "./auth-storage.js"; export * from "./bash-executor.js"; export * from "./compaction/index.js"; diff --git a/src/agents/sessions/extensions/runner.ts b/src/agents/sessions/extensions/runner.ts index 624c980c5e5d..6930175c8036 100644 --- a/src/agents/sessions/extensions/runner.ts +++ b/src/agents/sessions/extensions/runner.ts @@ -4,7 +4,7 @@ import type { KeyId } from "@earendil-works/pi-tui"; import type { ImageContent, Model } from "../../../llm/types.js"; -import { type Theme, theme } from "../../modes/interactive/theme/theme.js"; +import { interactiveAgentTheme as theme, type Theme } from "../../modes/interactive/theme/theme.js"; import type { AgentMessage } from "../../runtime/index.js"; import type { ResourceDiagnostic } from "../diagnostics.js"; import type { KeybindingsConfig } from "../keybindings.js"; diff --git a/src/agents/sessions/package-manager.ts b/src/agents/sessions/package-manager.ts index 5e3b2768d060..1d907101718f 100644 --- a/src/agents/sessions/package-manager.ts +++ b/src/agents/sessions/package-manager.ts @@ -18,7 +18,11 @@ import { homedir } from "node:os"; import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path"; import { minimatch } from "minimatch"; import { isDefaultStateDir } from "../../config/paths.js"; -import { addIgnoreRules, toPosixPath, type IgnoreMatcher } from "../../shared/ignore-rules.js"; +import { + addIgnoreRules, + normalizeNativePathSeparators, + type IgnoreMatcher, +} from "../../shared/ignore-rules.js"; import { CONFIG_DIR_NAME } from "../config.js"; import { type GitSource, parseGitUrl } from "../utils/git.js"; import { canonicalizePath, isLocalPath } from "../utils/paths.js"; @@ -213,7 +217,7 @@ function collectFiles( } } - const relPath = toPosixPath(relative(root, fullPath)); + const relPath = normalizeNativePathSeparators(relative(root, fullPath)); const ignorePath = isDir ? `${relPath}/` : relPath; if (ig.ignores(ignorePath)) { continue; @@ -271,7 +275,7 @@ function collectSkillEntries( } } - const relPath = toPosixPath(relative(root, fullPath)); + const relPath = normalizeNativePathSeparators(relative(root, fullPath)); if (isFile && !ig.ignores(relPath)) { entries.push(fullPath); return entries; @@ -303,7 +307,7 @@ function collectSkillEntries( } } - const relPath = toPosixPath(relative(root, fullPath)); + const relPath = normalizeNativePathSeparators(relative(root, fullPath)); if ( mode === "openclaw" && dir === root && @@ -404,7 +408,7 @@ function collectTopLevelAutoResourceEntries( } } - const relPath = toPosixPath(relative(dir, fullPath)); + const relPath = normalizeNativePathSeparators(relative(dir, fullPath)); if (ig.ignores(relPath)) { continue; } @@ -502,7 +506,7 @@ function collectAutoExtensionEntries(dir: string): string[] { } } - const relPath = toPosixPath(relative(dir, fullPath)); + const relPath = normalizeNativePathSeparators(relative(dir, fullPath)); const ignorePath = isDir ? `${relPath}/` : relPath; if (ig.ignores(ignorePath)) { continue; @@ -560,13 +564,19 @@ function isRealPathWithinRoot(root: string, candidate: string): boolean { function getMatchCandidates(filePath: string, baseDir: string, includeNames: boolean): string[] { const name = basename(filePath); - const candidates = [toPosixPath(relative(baseDir, filePath)), toPosixPath(filePath)]; + const candidates = [ + normalizeNativePathSeparators(relative(baseDir, filePath)), + normalizeNativePathSeparators(filePath), + ]; if (includeNames) { candidates.push(name); } if (name === "SKILL.md") { const parentDir = dirname(filePath); - candidates.push(toPosixPath(relative(baseDir, parentDir)), toPosixPath(parentDir)); + candidates.push( + normalizeNativePathSeparators(relative(baseDir, parentDir)), + normalizeNativePathSeparators(parentDir), + ); if (includeNames) { candidates.push(basename(parentDir)); } @@ -576,13 +586,15 @@ function getMatchCandidates(filePath: string, baseDir: string, includeNames: boo function matchesAnyPattern(filePath: string, patterns: string[], baseDir: string): boolean { const candidates = getMatchCandidates(filePath, baseDir, true); - return patterns.some((pattern) => minimatch.match(candidates, toPosixPath(pattern)).length > 0); + return patterns.some( + (pattern) => minimatch.match(candidates, normalizeNativePathSeparators(pattern)).length > 0, + ); } function normalizeExactPattern(pattern: string): string { const normalized = pattern.startsWith("./") || pattern.startsWith(".\\") ? pattern.slice(2) : pattern; - return toPosixPath(normalized); + return normalizeNativePathSeparators(normalized); } function matchesAnyExactPattern(filePath: string, patterns: string[], baseDir: string): boolean { diff --git a/src/agents/sessions/session-manager.fork-rebase.test.ts b/src/agents/sessions/session-manager.fork-rebase.test.ts index d0b87e6071bb..d9af22ff8fbf 100644 --- a/src/agents/sessions/session-manager.fork-rebase.test.ts +++ b/src/agents/sessions/session-manager.fork-rebase.test.ts @@ -5,7 +5,7 @@ import { useAutoCleanupTempDirTracker } from "../../../test/helpers/temp-dir.js" import { appendTranscriptMessage, loadTranscriptEvents, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import { SessionManager, type SessionMessageEntry } from "./session-manager.js"; @@ -20,7 +20,7 @@ describe("SessionManager stale-parent rebase", () => { sessionKey: "agent:main:stale-active-parent", storePath: path.join(dir, "sessions.json"), }; - await upsertSessionEntry(target, { sessionId: target.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 1 }); const base = await appendTranscriptMessage(target, { eventId: "base", message: { role: "user", content: "base", timestamp: 1 }, @@ -70,7 +70,7 @@ describe("SessionManager stale-parent rebase", () => { sessionKey: "agent:main:deliberate-manager-branch", storePath: path.join(dir, "sessions.json"), }; - await upsertSessionEntry(target, { sessionId: target.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 1 }); const base = await appendTranscriptMessage(target, { eventId: "branch-base", message: { role: "user", content: "base", timestamp: 1 }, @@ -101,7 +101,7 @@ describe("SessionManager stale-parent rebase", () => { sessionKey: "agent:main:unrelated-explicit-parent", storePath: path.join(dir, "sessions.json"), }; - await upsertSessionEntry(target, { sessionId: target.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 1 }); const firstRoot = await appendTranscriptMessage(target, { eventId: "first-root", message: { role: "user", content: "first", timestamp: 1 }, diff --git a/src/agents/sessions/session-manager.persistence-compat.test.ts b/src/agents/sessions/session-manager.persistence-compat.test.ts index a3cf5be65be5..b400986f3495 100644 --- a/src/agents/sessions/session-manager.persistence-compat.test.ts +++ b/src/agents/sessions/session-manager.persistence-compat.test.ts @@ -11,7 +11,7 @@ import { import { appendTranscriptMessage, loadTranscriptEvents, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import { CURRENT_SESSION_VERSION, SessionManager } from "./session-manager.js"; @@ -45,7 +45,7 @@ describe("SessionManager persistence compatibility", () => { const sessionKey = "agent:main:dashboard:sqlite-remove-trailing"; const marker = formatSqliteSessionFileMarker({ agentId: "main", sessionId, storePath }); const scope = { agentId: "main", sessionId, sessionKey, storePath }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionFile: marker, sessionId, updatedAt: 10 }, ); diff --git a/src/agents/sessions/session-manager.test.ts b/src/agents/sessions/session-manager.test.ts index 3beedae91956..a6be3a68ff76 100644 --- a/src/agents/sessions/session-manager.test.ts +++ b/src/agents/sessions/session-manager.test.ts @@ -14,7 +14,7 @@ import { readTranscriptRawDelta, replaceTranscriptEventsSync, updateSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import { buildSessionContext, @@ -44,7 +44,7 @@ describe("SessionManager.open", () => { sessionId, storePath, }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionFile: marker, @@ -155,7 +155,7 @@ describe("SessionManager.open", () => { const sessionId = "legacy-persisted-session"; const sessionKey = "agent:main:legacy-persisted-session"; const scope = { agentId: "main", sessionId, sessionKey, storePath }; - await upsertSessionEntry(scope, { sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId, updatedAt: 1 }); replaceTranscriptEventsSync(scope, [ { type: "session", @@ -185,7 +185,7 @@ describe("SessionManager.open", () => { sessionKey: "agent:main:current-persisted-session", storePath, }; - await upsertSessionEntry(currentScope, { sessionId: currentScope.sessionId, updatedAt: 2 }); + await upsertSessionEntryCore(currentScope, { sessionId: currentScope.sessionId, updatedAt: 2 }); const currentManager = SessionManager.open(currentScope, dir); expect(() => currentManager.setSessionTarget(scope)).toThrow( "require doctor/import migration before runtime use", @@ -233,7 +233,7 @@ describe("SessionManager.open", () => { sessionKey: "agent:main:dashboard:sqlite-leaf-control", storePath: path.join(dir, "sessions.json"), }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 1, }); @@ -271,7 +271,7 @@ describe("SessionManager.open", () => { sessionKey: "agent:main:dashboard:sqlite-model-change-first", storePath: path.join(dir, "sessions.json"), }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 1, }); @@ -336,7 +336,7 @@ describe("SessionManager.open", () => { sessionKey: "agent:main:sqlite-empty-existing-row", storePath: path.join(dir, "sessions.json"), }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "sqlite-existing-row", updatedAt: 123, label: "preserved", @@ -356,7 +356,7 @@ describe("SessionManager.open", () => { sessionKey: "agent:main:sqlite-rebound-before-header", storePath: path.join(dir, "sessions.json"), }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "sqlite-current-owner", updatedAt: 456, label: "preserved", @@ -418,8 +418,8 @@ describe("SessionManager.open", () => { sessionKey: "agent:main:second-target", storePath, }; - await upsertSessionEntry(firstTarget, { sessionId: firstTarget.sessionId, updatedAt: 1 }); - await upsertSessionEntry(secondTarget, { sessionId: secondTarget.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(firstTarget, { sessionId: firstTarget.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(secondTarget, { sessionId: secondTarget.sessionId, updatedAt: 1 }); await appendTranscriptMessage(firstTarget, { cwd: path.join(dir, "first-workspace"), message: { role: "user", content: "first" }, @@ -446,7 +446,7 @@ describe("SessionManager.open", () => { sessionKey: "agent:main:prompt-reload", storePath: path.join(dir, "sessions.json"), }; - await upsertSessionEntry(target, { sessionId: target.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 1 }); const manager = SessionManager.open(target, dir); const firstId = manager.appendMessage({ role: "user", content: "first", timestamp: 1 }); const external = await appendTranscriptMessage(target, { @@ -476,8 +476,8 @@ describe("SessionManager.open", () => { sessionKey: "agent:main:header-target", storePath, }; - await upsertSessionEntry(firstTarget, { sessionId: firstTarget.sessionId, updatedAt: 1 }); - await upsertSessionEntry(secondTarget, { sessionId: secondTarget.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(firstTarget, { sessionId: firstTarget.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(secondTarget, { sessionId: secondTarget.sessionId, updatedAt: 1 }); const manager = SessionManager.open(firstTarget, dir); const firstId = manager.appendMessage({ role: "user", content: "first", timestamp: 1 }); manager.appendLeafControl({ targetId: firstId, appendParentId: firstId, appendMode: "side" }); @@ -560,7 +560,7 @@ describe("SessionManager.open", () => { storePath: path.join(dir, "sessions.json"), }; const marker = formatSqliteSessionFileMarker(scope); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionFile: marker, sessionId: scope.sessionId, updatedAt: 1, @@ -627,7 +627,7 @@ describe("SessionManager.open", () => { idempotencyKey: "runtime-user-parent:user", timestamp: 1, }; - await upsertSessionEntry(scope, { sessionFile: marker, sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionFile: marker, sessionId, updatedAt: 1 }); await appendTranscriptMessage(scope, { cwd: dir, eventId: "pre-persisted-user", @@ -733,7 +733,7 @@ describe("SessionManager.open", () => { const sessionId = "sqlite-opaque-header"; const sessionKey = "agent:main:dashboard:sqlite-opaque-header"; const marker = formatSqliteSessionFileMarker({ agentId: "main", sessionId, storePath }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionFile: marker, sessionId, updatedAt: 10 }, ); @@ -759,7 +759,7 @@ describe("SessionManager.open", () => { const sessionKey = "agent:main:dashboard:sqlite-prompt-release-rebound"; const marker = formatSqliteSessionFileMarker({ agentId: "main", sessionId, storePath }); const scope = { agentId: "main", sessionId, sessionKey, storePath }; - await upsertSessionEntry(scope, { sessionFile: marker, sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionFile: marker, sessionId, updatedAt: 10 }); const user = await appendTranscriptMessage(scope, { cwd: dir, eventId: "rebound-user", @@ -772,7 +772,7 @@ describe("SessionManager.open", () => { parentId: user.messageId, }); const sessionManager = openMarker(marker, sessionKey, dir); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "replacement-session", updatedAt: 20 }, ); @@ -819,7 +819,7 @@ describe("SessionManager.open", () => { storePath, }); const scope = { agentId: "main", sessionId, sessionKey, storePath }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionFile: marker, @@ -870,7 +870,7 @@ describe("SessionManager.open", () => { storePath, }); const scope = { agentId: "main", sessionId, sessionKey, storePath }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { delivery: { kind: "internal" }, @@ -931,7 +931,7 @@ describe("SessionManager.open", () => { const sessionKey = "agent:main:dashboard:sqlite-branch-race-source"; const marker = formatSqliteSessionFileMarker({ agentId: "main", sessionId, storePath }); const scope = { agentId: "main", sessionId, sessionKey, storePath }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { lifecycleRevision: "branch-original-revision", sessionFile: marker, sessionId, @@ -1001,7 +1001,7 @@ describe("SessionManager.open", () => { sessionId, storePath, }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionFile: marker, diff --git a/src/agents/sessions/session-manager.user-idempotency.test.ts b/src/agents/sessions/session-manager.user-idempotency.test.ts index e477bab3c56c..46bfd3397ae8 100644 --- a/src/agents/sessions/session-manager.user-idempotency.test.ts +++ b/src/agents/sessions/session-manager.user-idempotency.test.ts @@ -6,7 +6,7 @@ import { formatSqliteSessionFileMarker } from "../../config/sessions/legacy-sqli import { appendTranscriptMessage, loadTranscriptEvents, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import { SessionManager } from "./session-manager.js"; @@ -77,7 +77,7 @@ describe("SessionManager user idempotency", () => { idempotencyKey: "runtime-user-ancestor:user", timestamp: 1, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionFile: formatSqliteSessionFileMarker(scope), sessionId: scope.sessionId, updatedAt: 1, @@ -125,7 +125,7 @@ describe("SessionManager user idempotency", () => { idempotencyKey: "runtime-user-concurrent-ingress:user", timestamp: 1, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionFile: formatSqliteSessionFileMarker(scope), sessionId: scope.sessionId, updatedAt: 1, @@ -194,7 +194,7 @@ describe("SessionManager user idempotency", () => { idempotencyKey: "runtime-user-setup-metadata:user", timestamp: 1, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionFile: formatSqliteSessionFileMarker(scope), sessionId: scope.sessionId, updatedAt: 1, diff --git a/src/agents/sessions/tools/bash.ts b/src/agents/sessions/tools/bash.ts index dc9a9323c466..640fb8761882 100644 --- a/src/agents/sessions/tools/bash.ts +++ b/src/agents/sessions/tools/bash.ts @@ -13,7 +13,7 @@ import { releaseChildProcessOutputAfterExit } from "../../../process/child-proce import { spawnCommand } from "../../../process/exec.js"; import { keyHint } from "../../modes/interactive/components/keybinding-hints.js"; import { truncateToVisualLines } from "../../modes/interactive/components/visual-truncate.js"; -import { theme } from "../../modes/interactive/theme/theme.js"; +import { interactiveAgentTheme as theme } from "../../modes/interactive/theme/theme.js"; import type { AgentTool } from "../../runtime/index.js"; import { buildShellCommandInvocation, diff --git a/src/agents/sessions/tools/edit.ts b/src/agents/sessions/tools/edit.ts index 84c4d15bf820..b9235b13eb52 100644 --- a/src/agents/sessions/tools/edit.ts +++ b/src/agents/sessions/tools/edit.ts @@ -284,7 +284,7 @@ function getRenderablePreviewInput( function formatEditCall( args: RenderableEditArgs | undefined, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, ): string { const invalidArg = invalidArgText(theme); const rawPath = str(args?.file_path ?? args?.path); @@ -297,7 +297,7 @@ function formatEditCall( function formatEditResult( preview: EditPreview | undefined, result: EditToolResultLike, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, isError: boolean, ): string | undefined { const previewDiff = preview && !("error" in preview) ? preview.diff : undefined; @@ -324,7 +324,7 @@ function formatEditResult( function getEditHeaderBg( preview: EditPreview | undefined, settledError: boolean | undefined, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, ): (text: string) => string { if (preview) { if ("error" in preview) { @@ -341,7 +341,7 @@ function getEditHeaderBg( function buildEditCallComponent( component: EditCallRenderComponent, args: RenderableEditArgs | undefined, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, ): EditCallRenderComponent { component.setBgFn(getEditHeaderBg(component.preview, component.settledError, theme)); component.clear(); diff --git a/src/agents/sessions/tools/find.ts b/src/agents/sessions/tools/find.ts index 3fb69dd11672..3423cdeca52c 100644 --- a/src/agents/sessions/tools/find.ts +++ b/src/agents/sessions/tools/find.ts @@ -10,7 +10,7 @@ import { spawnCommand } from "../../../process/exec.js"; * * Searches files by glob through fd/local operations and returns bounded, renderable results. */ -import { toPosixPath } from "../../../shared/ignore-rules.js"; +import { normalizeNativePathSeparators } from "../../../shared/ignore-rules.js"; import type { AgentTool } from "../../runtime/index.js"; import { ensureTool } from "../../utils/tools-manager.js"; import type { ToolDefinition, ToolRenderResultOptions } from "../extensions/types.js"; @@ -77,7 +77,7 @@ export interface FindToolOptions { function formatFindCall( args: { pattern: string; path?: string; limit?: number } | undefined, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, ): string { const pattern = str(args?.pattern); const rawPath = str(args?.path); @@ -101,7 +101,7 @@ function formatFindResult( details?: FindToolDetails; }, options: ToolRenderResultOptions, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, showImages: boolean, ): string { const resultLimit = result.details?.resultLimitReached; @@ -233,9 +233,9 @@ export function createFindToolDefinition( // Relativize paths against the search root for stable output. const relativized = results.slice(0, observationLimit).map((p) => { if (p.startsWith(searchPath)) { - return toPosixPath(p.slice(searchPath.length + 1)); + return normalizeNativePathSeparators(p.slice(searchPath.length + 1)); } - return toPosixPath(path.relative(searchPath, p)); + return normalizeNativePathSeparators(path.relative(searchPath, p)); }); settle(() => resolve( @@ -367,7 +367,7 @@ export function createFindToolDefinition( if (hadTrailingSlash && !relativePath.endsWith("/")) { relativePath += "/"; } - relativized.push(toPosixPath(relativePath)); + relativized.push(normalizeNativePathSeparators(relativePath)); } settle(() => diff --git a/src/agents/sessions/tools/grep.ts b/src/agents/sessions/tools/grep.ts index 455d1ff0f1f5..d40414d73784 100644 --- a/src/agents/sessions/tools/grep.ts +++ b/src/agents/sessions/tools/grep.ts @@ -74,7 +74,7 @@ export interface GrepToolOptions { function formatGrepCall( args: { pattern: string; path?: string; glob?: string; limit?: number } | undefined, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, ): string { const pattern = str(args?.pattern); const rawPath = str(args?.path); @@ -102,7 +102,7 @@ function formatGrepResult( details?: GrepToolDetails; }, options: ToolRenderResultOptions, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, showImages: boolean, ): string { const matchLimit = result.details?.matchLimitReached; diff --git a/src/agents/sessions/tools/ls.ts b/src/agents/sessions/tools/ls.ts index 6ad11146dd65..497edbdd819b 100644 --- a/src/agents/sessions/tools/ls.ts +++ b/src/agents/sessions/tools/ls.ts @@ -57,7 +57,7 @@ export interface LsToolOptions { function formatLsCall( args: { path?: string; limit?: number } | undefined, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, ): string { const rawPath = str(args?.path); const path = rawPath !== null ? shortenPath(rawPath || ".") : null; @@ -76,7 +76,7 @@ function formatLsResult( details?: LsToolDetails; }, options: ToolRenderResultOptions, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, showImages: boolean, ): string { const entryLimit = result.details?.entryLimitReached; diff --git a/src/agents/sessions/tools/read.ts b/src/agents/sessions/tools/read.ts index 222c9bfab665..f2fe0b149eb9 100644 --- a/src/agents/sessions/tools/read.ts +++ b/src/agents/sessions/tools/read.ts @@ -17,7 +17,7 @@ import { * * Reads text and image files through local or injected operations with highlighting, resizing, and bounded output. */ -import { toPosixPath } from "../../../shared/ignore-rules.js"; +import { normalizeNativePathSeparators } from "../../../shared/ignore-rules.js"; import { levenshteinDistance } from "../../../shared/levenshtein-distance.js"; import { getReadmePath } from "../../config.js"; import { keyHint, keyText } from "../../modes/interactive/components/keybinding-hints.js"; @@ -275,7 +275,7 @@ function getOpenClawDocsClassification( return undefined; } - const label = toPosixPath(relativePath); + const label = normalizeNativePathSeparators(relativePath); if (label === "README.md" || label.startsWith("docs/") || label.startsWith("examples/")) { return { kind: "docs", label }; } diff --git a/src/agents/sessions/tools/write.ts b/src/agents/sessions/tools/write.ts index 9a5474e3fe24..20c37ee00f03 100644 --- a/src/agents/sessions/tools/write.ts +++ b/src/agents/sessions/tools/write.ts @@ -254,7 +254,7 @@ function trimTrailingEmptyLines(lines: string[]): string[] { function formatWriteCall( args: { path?: string; file_path?: string; content?: string } | undefined, options: ToolRenderResultOptions, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, cache: WriteHighlightCache | undefined, ): string { const rawPath = str(args?.file_path ?? args?.path); @@ -295,7 +295,7 @@ function formatWriteResult( }>; isError?: boolean; }, - theme: typeof import("../../modes/interactive/theme/theme.js").theme, + theme: typeof import("../../modes/interactive/theme/theme.js").interactiveAgentTheme, ): string | undefined { if (!result.isError) { return undefined; diff --git a/src/agents/simple-completion-runtime.test.ts b/src/agents/simple-completion-runtime.test.ts index b0f934deeff7..a41ddd51f85d 100644 --- a/src/agents/simple-completion-runtime.test.ts +++ b/src/agents/simple-completion-runtime.test.ts @@ -71,7 +71,7 @@ vi.mock("./model-auth.js", () => ({ `No API key resolved for provider "${provider}" (auth mode: ${auth.mode}, checked: ${auth.source}).`, ), getApiKeyForModelCore: hoisted.getApiKeyForModelMock, - resolveApiKeyForProvider: hoisted.getApiKeyForModelMock, + resolveApiKeyForProviderCore: hoisted.getApiKeyForModelMock, applyLocalNoAuthHeaderOverride: hoisted.applyLocalNoAuthHeaderOverrideMock, })); diff --git a/src/agents/simple-completion-runtime.ts b/src/agents/simple-completion-runtime.ts index ba0353e1ca1c..33dcad4075c2 100644 --- a/src/agents/simple-completion-runtime.ts +++ b/src/agents/simple-completion-runtime.ts @@ -36,7 +36,7 @@ import { applyLocalNoAuthHeaderOverride, formatMissingAuthError, getApiKeyForModelCore, - resolveApiKeyForProvider, + resolveApiKeyForProviderCore, type ResolvedProviderAuth, } from "./model-auth.js"; import { splitTrailingAuthProfile } from "./model-ref-profile.js"; @@ -294,7 +294,7 @@ export async function prepareSimpleCompletionModel(params: { : undefined; try { auth = resolvesAuthBeforePhysicalRoute - ? await resolveApiKeyForProvider({ + ? await resolveApiKeyForProviderCore({ provider: initialModel.provider, cfg: params.cfg, agentDir: params.agentDir, diff --git a/src/agents/subagent-orphan-recovery.restart-integration.test.ts b/src/agents/subagent-orphan-recovery.restart-integration.test.ts index b9971fe9b788..82945bdba84b 100644 --- a/src/agents/subagent-orphan-recovery.restart-integration.test.ts +++ b/src/agents/subagent-orphan-recovery.restart-integration.test.ts @@ -4,7 +4,7 @@ import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { getRuntimeConfig, setRuntimeConfigSnapshot } from "../config/config.js"; -import { resolveAgentIdFromSessionKey, resolveStorePath } from "../config/sessions.js"; +import { resolveAgentIdFromSessionKey, resolveSessionStorePathCore } from "../config/sessions.js"; import type { CallGatewayOptions } from "../gateway/call.js"; import type { GatewayRecoveryRuntime } from "../gateway/server-instance-runtime.types.js"; import { @@ -50,7 +50,7 @@ function consumeRecoveryAdmission(payload: Record): SessionWork const sessionKey = String(payload.sessionKey); const sessionId = String(payload.expectedExistingSessionId); const agentId = resolveAgentIdFromSessionKey(sessionKey); - const scope = resolveStorePath(getRuntimeConfig().session?.store, { agentId }); + const scope = resolveSessionStorePathCore(getRuntimeConfig().session?.store, { agentId }); const admission = consumeSessionWorkAdmissionHandoff({ handoffId: String(payload.internalRuntimeHandoffId), scope, diff --git a/src/agents/subagents/announce/subagent-announce-delivery.runtime.ts b/src/agents/subagents/announce/subagent-announce-delivery.runtime.ts index 2af44f08de6c..a66881050073 100644 --- a/src/agents/subagents/announce/subagent-announce-delivery.runtime.ts +++ b/src/agents/subagents/announce/subagent-announce-delivery.runtime.ts @@ -5,7 +5,10 @@ * message routing, queue settings, hooks, and embedded-run state. */ export { getRuntimeConfig } from "../../../config/config.js"; -export { resolveAgentIdFromSessionKey, resolveStorePath } from "../../../config/sessions.js"; +export { + resolveAgentIdFromSessionKey, + resolveSessionStorePathCore, +} from "../../../config/sessions.js"; export { loadSessionEntryReadOnly as loadSessionEntry } from "../../../config/sessions/session-accessor.js"; export { callGateway } from "../../../gateway/call.js"; export { dispatchGatewayMethodInProcess } from "../../../gateway/server-plugins.js"; diff --git a/src/agents/subagents/announce/subagent-announce-delivery.ts b/src/agents/subagents/announce/subagent-announce-delivery.ts index d8a8b946d9a5..fe79d3368fca 100644 --- a/src/agents/subagents/announce/subagent-announce-delivery.ts +++ b/src/agents/subagents/announce/subagent-announce-delivery.ts @@ -75,7 +75,7 @@ import { resolveAgentIdFromSessionKey, resolveExternalBestEffortDeliveryTarget, resolveQueueSettings, - resolveStorePath, + resolveSessionStorePathCore, sendMessage, } from "./subagent-announce-delivery.runtime.js"; import { @@ -575,7 +575,7 @@ export function loadRequesterSessionEntry(requesterSessionKey: string) { const cfg = subagentAnnounceDeliveryDeps.getRuntimeConfig(); const canonicalKey = resolveRequesterStoreKey(cfg, requesterSessionKey); const agentId = resolveAgentIdFromSessionKey(canonicalKey, resolveDefaultAgentId(cfg)); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); const entry = subagentAnnounceDeliveryDeps.loadSessionEntry({ storePath, sessionKey: canonicalKey, @@ -587,7 +587,7 @@ export function loadRequesterSessionEntry(requesterSessionKey: string) { export function loadSessionEntryByKey(sessionKey: string) { const cfg = subagentAnnounceDeliveryDeps.getRuntimeConfig(); const agentId = resolveAgentIdFromSessionKey(sessionKey, resolveDefaultAgentId(cfg)); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); return subagentAnnounceDeliveryDeps.loadSessionEntry({ storePath, sessionKey, diff --git a/src/agents/subagents/announce/subagent-announce-origin.ts b/src/agents/subagents/announce/subagent-announce-origin.ts index 9c01ecfefbf1..1129ca02d748 100644 --- a/src/agents/subagents/announce/subagent-announce-origin.ts +++ b/src/agents/subagents/announce/subagent-announce-origin.ts @@ -15,7 +15,7 @@ import { } from "../../../channels/route-projection.js"; import type { SessionEntry } from "../../../config/sessions/types.js"; import { - stripTargetKindPrefix, + stripOutboundTargetKindPrefix, stripTargetProviderPrefix, stripTargetTopicSuffix, } from "../../../infra/outbound/channel-target-prefix.js"; @@ -55,7 +55,10 @@ function normalizeAnnounceRouteTarget(context?: DeliveryContext): string | undef ? getLoadedChannelPluginForRead(channel as ChannelId)?.messaging : undefined; const route = stripTargetTopicSuffix( - stripTargetKindPrefix(stripTargetProviderPrefix(rawTo, channel ?? ""), ["group", "channel"]), + stripOutboundTargetKindPrefix(stripTargetProviderPrefix(rawTo, channel ?? ""), [ + "group", + "channel", + ]), ); const normalized = messaging?.normalizeTarget?.(route) ?? route; return normalized || undefined; diff --git a/src/agents/subagents/announce/subagent-announce-output.test-support.ts b/src/agents/subagents/announce/subagent-announce-output.test-support.ts index 56738b57967c..78f70e1f2103 100644 --- a/src/agents/subagents/announce/subagent-announce-output.test-support.ts +++ b/src/agents/subagents/announce/subagent-announce-output.test-support.ts @@ -8,7 +8,7 @@ type OutputDeps = Pick< | "readSubagentSessionEntry" | "readSessionMessagesAsync" | "resolveAgentIdFromSessionKey" - | "resolveStorePath" + | "resolveSessionStorePathCore" >; type Testing = { diff --git a/src/agents/subagents/announce/subagent-announce-output.test.ts b/src/agents/subagents/announce/subagent-announce-output.test.ts index ef3fadb76f0f..dbcb91c26c41 100644 --- a/src/agents/subagents/announce/subagent-announce-output.test.ts +++ b/src/agents/subagents/announce/subagent-announce-output.test.ts @@ -17,7 +17,7 @@ type ReadSessionMessagesAsync = typeof import("./subagent-announce.runtime.js").readSessionMessagesAsync; type ResolveAgentIdFromSessionKey = typeof import("./subagent-announce.runtime.js").resolveAgentIdFromSessionKey; -type ResolveStorePath = typeof import("./subagent-announce.runtime.js").resolveStorePath; +type ResolveStorePath = typeof import("./subagent-announce.runtime.js").resolveSessionStorePathCore; function installOutputDeps(params: { messages: Array; @@ -97,7 +97,7 @@ describe("buildCompactAnnounceStatsLine", () => { totalTokens: 999_999, })) as ReadSessionEntry, resolveAgentIdFromSessionKey: (() => "main") as ResolveAgentIdFromSessionKey, - resolveStorePath: (() => "/tmp/openclaw-session-store") as ResolveStorePath, + resolveSessionStorePathCore: (() => "/tmp/openclaw-session-store") as ResolveStorePath, }); await expect( diff --git a/src/agents/subagents/announce/subagent-announce-output.ts b/src/agents/subagents/announce/subagent-announce-output.ts index 2ad939926584..bf709ee82153 100644 --- a/src/agents/subagents/announce/subagent-announce-output.ts +++ b/src/agents/subagents/announce/subagent-announce-output.ts @@ -28,7 +28,7 @@ import { readSubagentSessionEntry, readSessionMessagesAsync, resolveAgentIdFromSessionKey, - resolveStorePath, + resolveSessionStorePathCore, } from "./subagent-announce.runtime.js"; import { assistantCallsSessionsYield, isSessionsYieldToolResult } from "./subagent-yield-output.js"; @@ -50,7 +50,7 @@ type SubagentAnnounceOutputDeps = { readSubagentSessionEntry: typeof readSubagentSessionEntry; readSessionMessagesAsync: typeof readSessionMessagesAsync; resolveAgentIdFromSessionKey: typeof resolveAgentIdFromSessionKey; - resolveStorePath: typeof resolveStorePath; + resolveSessionStorePathCore: typeof resolveSessionStorePathCore; }; const defaultSubagentAnnounceOutputDeps: SubagentAnnounceOutputDeps = { @@ -59,7 +59,7 @@ const defaultSubagentAnnounceOutputDeps: SubagentAnnounceOutputDeps = { readSubagentSessionEntry, readSessionMessagesAsync, resolveAgentIdFromSessionKey, - resolveStorePath, + resolveSessionStorePathCore, }; let subagentAnnounceOutputDeps: SubagentAnnounceOutputDeps = defaultSubagentAnnounceOutputDeps; @@ -649,7 +649,9 @@ export async function buildCompactAnnounceStatsLine(params: { }) { const cfg = subagentAnnounceOutputDeps.getRuntimeConfig(); const agentId = subagentAnnounceOutputDeps.resolveAgentIdFromSessionKey(params.sessionKey); - const storePath = subagentAnnounceOutputDeps.resolveStorePath(cfg.session?.store, { agentId }); + const storePath = subagentAnnounceOutputDeps.resolveSessionStorePathCore(cfg.session?.store, { + agentId, + }); let entry = subagentAnnounceOutputDeps.readSubagentSessionEntry(storePath, params.sessionKey); const tokenWaitAttempts = isFastTestMode() ? 1 : 3; for (let attempt = 0; attempt < tokenWaitAttempts; attempt += 1) { diff --git a/src/agents/subagents/announce/subagent-announce.format.e2e.test.ts b/src/agents/subagents/announce/subagent-announce.format.e2e.test.ts index 3da83d5e0f23..8c4265eadb95 100644 --- a/src/agents/subagents/announce/subagent-announce.format.e2e.test.ts +++ b/src/agents/subagents/announce/subagent-announce.format.e2e.test.ts @@ -139,7 +139,7 @@ const agentSpy = vi.fn(async (_req: AgentCallRequest) => visibleAgentResponse()) const sendSpy = vi.fn(async (_req: AgentCallRequest) => ({ runId: "send-main", status: "ok" })); const sessionsDeleteSpy = vi.fn((_req: AgentCallRequest) => undefined); const resolveAgentIdFromSessionKeySpy = vi.spyOn(configSessions, "resolveAgentIdFromSessionKey"); -const resolveStorePathSpy = vi.spyOn(configSessions, "resolveStorePath"); +const resolveStorePathSpy = vi.spyOn(configSessions, "resolveSessionStorePathCore"); const resolveMainSessionKeySpy = vi.spyOn(configSessions, "resolveMainSessionKey"); const callGatewaySpy = vi.spyOn(gatewayCall, "callGateway"); const getGlobalHookRunnerSpy = vi.spyOn(hookRunnerGlobal, "getGlobalHookRunner"); @@ -432,7 +432,7 @@ describe("subagent announce formatting", () => { getRuntimeConfig: () => configOverride, readSubagentSessionEntry: (_storePath, sessionKey) => loadSessionStoreFixture()[sessionKey], resolveAgentIdFromSessionKey: () => "main", - resolveStorePath: () => "/tmp/sessions.json", + resolveSessionStorePathCore: () => "/tmp/sessions.json", }); resolveAgentIdFromSessionKeySpy.mockReset().mockImplementation(() => "main"); resolveStorePathSpy.mockReset().mockImplementation(() => "/tmp/sessions.json"); diff --git a/src/agents/subagents/announce/subagent-announce.requester-settle-wake.test.ts b/src/agents/subagents/announce/subagent-announce.requester-settle-wake.test.ts index 6b03342f1aba..7122548ea554 100644 --- a/src/agents/subagents/announce/subagent-announce.requester-settle-wake.test.ts +++ b/src/agents/subagents/announce/subagent-announce.requester-settle-wake.test.ts @@ -47,7 +47,7 @@ vi.mock("./subagent-announce.runtime.js", () => ({ readSubagentSessionEntry: vi.fn(() => undefined), resolveAgentIdFromSessionKey: vi.fn(() => "main"), resolveMainSessionKey: vi.fn(() => "agent:main:main"), - resolveStorePath: vi.fn(() => "/tmp/sessions.json"), + resolveSessionStorePathCore: vi.fn(() => "/tmp/sessions.json"), waitForEmbeddedAgentRunEnd: vi.fn(async () => true), })); diff --git a/src/agents/subagents/announce/subagent-announce.runtime.ts b/src/agents/subagents/announce/subagent-announce.runtime.ts index 8b40642f5346..69c2a8fc8c29 100644 --- a/src/agents/subagents/announce/subagent-announce.runtime.ts +++ b/src/agents/subagents/announce/subagent-announce.runtime.ts @@ -5,7 +5,10 @@ * IO without changing the announce logic itself. */ export { getRuntimeConfig } from "../../../config/config.js"; -export { resolveAgentIdFromSessionKey, resolveStorePath } from "../../../config/sessions.js"; +export { + resolveAgentIdFromSessionKey, + resolveSessionStorePathCore, +} from "../../../config/sessions.js"; import { loadSessionEntry } from "../../../config/sessions/session-accessor.js"; export function readSubagentSessionEntry(storePath: string, sessionKey: string) { diff --git a/src/agents/subagents/announce/subagent-announce.test-support.ts b/src/agents/subagents/announce/subagent-announce.test-support.ts index 631926325117..088777fae299 100644 --- a/src/agents/subagents/announce/subagent-announce.test-support.ts +++ b/src/agents/subagents/announce/subagent-announce.test-support.ts @@ -14,7 +14,7 @@ type DeliveryRuntimeMockOptions = { loadSessionStore: (storePath: string) => unknown; resolveAgentIdFromSessionKey: (sessionKey: string) => string; resolveMainSessionKey: (cfg: unknown) => string; - resolveStorePath: (store: unknown, options: unknown) => string; + resolveSessionStorePathCore: (store: unknown, options: unknown) => string; isEmbeddedAgentRunActive: (sessionId: string) => boolean; queueEmbeddedAgentMessageWithOutcome: ( sessionId: string, @@ -79,7 +79,7 @@ export function createSubagentAnnounceDeliveryRuntimeMock(options: DeliveryRunti loadSessionStore: options.loadSessionStore, resolveAgentIdFromSessionKey: options.resolveAgentIdFromSessionKey, resolveMainSessionKey: options.resolveMainSessionKey, - resolveStorePath: options.resolveStorePath, + resolveSessionStorePathCore: options.resolveSessionStorePathCore, isEmbeddedAgentRunActive: options.isEmbeddedAgentRunActive, queueEmbeddedAgentMessageWithOutcome: options.queueEmbeddedAgentMessageWithOutcome, formatEmbeddedAgentQueueFailureSummary: (outcome: { reason?: string; sessionId?: string }) => diff --git a/src/agents/subagents/announce/subagent-announce.test.ts b/src/agents/subagents/announce/subagent-announce.test.ts index 5e1c6425b222..915620f68e2b 100644 --- a/src/agents/subagents/announce/subagent-announce.test.ts +++ b/src/agents/subagents/announce/subagent-announce.test.ts @@ -76,7 +76,8 @@ vi.mock("./subagent-announce.runtime.js", () => ({ resolveAgentIdFromSessionKey: (sessionKey: string) => resolveAgentIdFromSessionKeyMock(sessionKey), resolveMainSessionKey: (cfg: unknown) => resolveMainSessionKeyMock(cfg), - resolveStorePath: (store: unknown, options: unknown) => resolveStorePathMock(store, options), + resolveSessionStorePathCore: (store: unknown, options: unknown) => + resolveStorePathMock(store, options), waitForEmbeddedAgentRunEnd: (sessionId: string, timeoutMs?: number) => waitForEmbeddedAgentRunEndMock(sessionId, timeoutMs), })); @@ -89,7 +90,8 @@ vi.mock("./subagent-announce-delivery.runtime.js", () => resolveAgentIdFromSessionKey: (sessionKey: string) => resolveAgentIdFromSessionKeyMock(sessionKey), resolveMainSessionKey: (cfg: unknown) => resolveMainSessionKeyMock(cfg), - resolveStorePath: (store: unknown, options: unknown) => resolveStorePathMock(store, options), + resolveSessionStorePathCore: (store: unknown, options: unknown) => + resolveStorePathMock(store, options), isEmbeddedAgentRunActive: (sessionId: string) => isEmbeddedAgentRunActiveMock(sessionId), queueEmbeddedAgentMessageWithOutcome: (sessionId: string, text: string, options?: unknown) => queueEmbeddedAgentMessageWithOutcomeMock(sessionId, text, options), diff --git a/src/agents/subagents/announce/subagent-announce.timeout.test.ts b/src/agents/subagents/announce/subagent-announce.timeout.test.ts index 428c4b8c6ea0..d69ff5f47009 100644 --- a/src/agents/subagents/announce/subagent-announce.timeout.test.ts +++ b/src/agents/subagents/announce/subagent-announce.timeout.test.ts @@ -91,7 +91,7 @@ vi.mock("./subagent-announce-delivery.runtime.js", () => loadSessionStore: () => sessionStore, resolveAgentIdFromSessionKey: () => "main", resolveMainSessionKey: () => "agent:main:main", - resolveStorePath: () => "/tmp/sessions-main.json", + resolveSessionStorePathCore: () => "/tmp/sessions-main.json", isEmbeddedAgentRunActive: (sessionId: string) => isEmbeddedAgentRunActiveMock(sessionId), queueEmbeddedAgentMessageWithOutcome: (sessionId: string) => ({ queued: false, @@ -190,7 +190,7 @@ vi.mock("./subagent-announce.runtime.js", () => ({ readSessionMessagesAsync: vi.fn(async () => []), readSubagentSessionEntry: (_storePath: string, sessionKey: string) => sessionStore[sessionKey], resolveAgentIdFromSessionKey: () => "main", - resolveStorePath: () => "/tmp/sessions-main.json", + resolveSessionStorePathCore: () => "/tmp/sessions-main.json", resolveMainSessionKey: () => "agent:main:main", isEmbeddedAgentRunActive: (sessionId: string) => isEmbeddedAgentRunActiveMock(sessionId), waitForEmbeddedAgentRunEnd: (sessionId: string, timeoutMs?: number) => diff --git a/src/agents/subagents/registry/subagent-control.ts b/src/agents/subagents/registry/subagent-control.ts index ff99fe15ff79..68108792bcb7 100644 --- a/src/agents/subagents/registry/subagent-control.ts +++ b/src/agents/subagents/registry/subagent-control.ts @@ -5,7 +5,7 @@ import { resolveSubagentLabel, sortSubagentRuns, } from "../../../auto-reply/reply/subagents-utils.js"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { loadSessionEntry, patchSessionEntryCore, @@ -1290,7 +1290,9 @@ export async function sendControlledSubagentMessage(params: { const targetSessionKey = params.entry.childSessionKey; const parsed = parseAgentSessionKey(targetSessionKey); - const storePath = resolveStorePath(params.cfg.session?.store, { agentId: parsed?.agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { + agentId: parsed?.agentId, + }); const targetSessionEntry = loadSessionEntry({ storePath, sessionKey: targetSessionKey, diff --git a/src/agents/subagents/registry/subagent-list.ts b/src/agents/subagents/registry/subagent-list.ts index 2e2610dc652c..e9da357cbde7 100644 --- a/src/agents/subagents/registry/subagent-list.ts +++ b/src/agents/subagents/registry/subagent-list.ts @@ -8,7 +8,7 @@ import { resolveSubagentLabel, sortSubagentRuns, } from "../../../auto-reply/reply/subagents-utils.js"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { listSessionEntriesReadOnly } from "../../../config/sessions/session-accessor.js"; import type { SessionEntry } from "../../../config/sessions/types.js"; import type { OpenClawConfig } from "../../../config/types.openclaw.js"; @@ -66,7 +66,7 @@ type SessionEntryResolution = { }; function resolveStorePathForKey(cfg: OpenClawConfig, parsed?: ParsedAgentSessionKey | null) { - return resolveStorePath(cfg.session?.store, { + return resolveSessionStorePathCore(cfg.session?.store, { agentId: parsed?.agentId, }); } diff --git a/src/agents/subagents/registry/subagent-registry-helpers.ts b/src/agents/subagents/registry/subagent-registry-helpers.ts index cebedec8c661..02d7a6df5a51 100644 --- a/src/agents/subagents/registry/subagent-registry-helpers.ts +++ b/src/agents/subagents/registry/subagent-registry-helpers.ts @@ -8,7 +8,10 @@ import path from "node:path"; import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice"; import { DEFAULT_SUBAGENT_ARCHIVE_AFTER_MINUTES } from "../../../config/agent-limits.js"; import { getRuntimeConfig } from "../../../config/config.js"; -import { resolveAgentIdFromSessionKey, resolveStorePath } from "../../../config/sessions.js"; +import { + resolveAgentIdFromSessionKey, + resolveSessionStorePathCore, +} from "../../../config/sessions.js"; import { patchSessionEntryCore } from "../../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../../config/types.openclaw.js"; import { computeBackoff } from "../../../infra/backoff.js"; @@ -108,7 +111,7 @@ export async function persistSubagentSessionTiming( const cfg = getRuntimeConfig(); const agentId = resolveAgentIdFromSessionKey(childSessionKey); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); const startedAt = getSubagentSessionStartedAt(entry); const endedAt = typeof entry.execution.endedAt === "number" && Number.isFinite(entry.execution.endedAt) diff --git a/src/agents/subagents/registry/subagent-registry-lifecycle-delivery.ts b/src/agents/subagents/registry/subagent-registry-lifecycle-delivery.ts index d4ab859efe4b..a22afd3c1f9a 100644 --- a/src/agents/subagents/registry/subagent-registry-lifecycle-delivery.ts +++ b/src/agents/subagents/registry/subagent-registry-lifecycle-delivery.ts @@ -1,6 +1,6 @@ import { uniqueStrings } from "@openclaw/normalization-core/string-normalization"; import { sliceUtf16Safe } from "@openclaw/normalization-core/utf16-slice"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { loadSessionEntryReadOnly, type SessionTranscriptRuntimeTarget, @@ -311,7 +311,7 @@ export const freezeRunResultAtCompletion = async ( const sessionKey = transcriptTarget?.sessionKey ?? entry.childSessionKey; const configuredStorePath = agentId ? (transcriptTarget?.storePath ?? - resolveStorePath(params.getRuntimeConfig().session?.store, { agentId })) + resolveSessionStorePathCore(params.getRuntimeConfig().session?.store, { agentId })) : undefined; const storePath = configuredStorePath ? resolveSessionStorePathForScope({ diff --git a/src/agents/subagents/registry/subagent-registry-restart-recovery.test.ts b/src/agents/subagents/registry/subagent-registry-restart-recovery.test.ts index f783193183a6..9f8e80741eee 100644 --- a/src/agents/subagents/registry/subagent-registry-restart-recovery.test.ts +++ b/src/agents/subagents/registry/subagent-registry-restart-recovery.test.ts @@ -26,7 +26,7 @@ vi.mock("../../../config/config.js", () => ({ })); vi.mock("../../../config/sessions.js", () => ({ resolveAgentIdFromSessionKey: () => "main", - resolveStorePath: () => "/tmp/subagent-recovery.sqlite", + resolveSessionStorePathCore: () => "/tmp/subagent-recovery.sqlite", })); vi.mock("../../../config/sessions/session-accessor.js", () => ({ loadSessionEntry: mocks.loadSessionEntry, diff --git a/src/agents/subagents/registry/subagent-registry-restart-recovery.ts b/src/agents/subagents/registry/subagent-registry-restart-recovery.ts index a967f65b0662..4485807348be 100644 --- a/src/agents/subagents/registry/subagent-registry-restart-recovery.ts +++ b/src/agents/subagents/registry/subagent-registry-restart-recovery.ts @@ -1,5 +1,8 @@ import { getRuntimeConfig } from "../../../config/config.js"; -import { resolveAgentIdFromSessionKey, resolveStorePath } from "../../../config/sessions.js"; +import { + resolveAgentIdFromSessionKey, + resolveSessionStorePathCore, +} from "../../../config/sessions.js"; import { loadSessionEntry, patchSessionEntryCore, @@ -299,7 +302,7 @@ export async function recoverInterruptedSubagentRow( } try { const agentId = resolveAgentIdFromSessionKey(childSessionKey); - const storePath = resolveStorePath(getRuntimeConfig().session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(getRuntimeConfig().session?.store, { agentId }); const sessionEntry = loadSessionEntry({ storePath, sessionKey: childSessionKey, diff --git a/src/agents/subagents/registry/subagent-registry-sweep-kill.ts b/src/agents/subagents/registry/subagent-registry-sweep-kill.ts index dfc395c14f4c..00978542df0c 100644 --- a/src/agents/subagents/registry/subagent-registry-sweep-kill.ts +++ b/src/agents/subagents/registry/subagent-registry-sweep-kill.ts @@ -1,5 +1,8 @@ import { getRuntimeConfig } from "../../../config/config.js"; -import { resolveAgentIdFromSessionKey, resolveStorePath } from "../../../config/sessions.js"; +import { + resolveAgentIdFromSessionKey, + resolveSessionStorePathCore, +} from "../../../config/sessions.js"; import { isAgentEventLifecycleGenerationCurrent } from "../../../infra/agent-events.js"; import { getAgentRunContext } from "../../../infra/agent-run-registry.js"; import { runExclusiveSessionLifecycleMutation } from "../../../sessions/session-lifecycle-admission.js"; @@ -162,7 +165,7 @@ export async function reconcileDurableSubagentKillIntent(params: { getLatestSubagentRunByChildSessionKeyFromRuns(childRuns(), params.entry.childSessionKey) === params.entry; const cfg = getRuntimeConfig(); - const storePath = resolveStorePath(cfg.session?.store, { + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: resolveAgentIdFromSessionKey(params.entry.childSessionKey), }); const ownsSessionIncarnation = () => { diff --git a/src/agents/subagents/registry/subagent-registry.announce-loop-guard.test.ts b/src/agents/subagents/registry/subagent-registry.announce-loop-guard.test.ts index 3551f944ee99..59acf7aa1977 100644 --- a/src/agents/subagents/registry/subagent-registry.announce-loop-guard.test.ts +++ b/src/agents/subagents/registry/subagent-registry.announce-loop-guard.test.ts @@ -37,7 +37,7 @@ vi.mock("../../../config/sessions.js", () => ({ return match?.[1] ?? "main"; }, resolveMainSessionKey: () => "agent:main:main", - resolveStorePath: () => "/tmp/test-store", + resolveSessionStorePathCore: () => "/tmp/test-store", updateSessionStore: mocks.updateSessionStore, })); diff --git a/src/agents/subagents/registry/subagent-registry.lifecycle-retry-grace.e2e.test.ts b/src/agents/subagents/registry/subagent-registry.lifecycle-retry-grace.e2e.test.ts index a3876f7b25a5..03fb1b863db8 100644 --- a/src/agents/subagents/registry/subagent-registry.lifecycle-retry-grace.e2e.test.ts +++ b/src/agents/subagents/registry/subagent-registry.lifecycle-retry-grace.e2e.test.ts @@ -99,7 +99,7 @@ const loadConfigMock = vi.fn(() => ({ vi.mock("../../../config/sessions.js", () => ({ loadSessionStore: vi.fn(() => sessionStore), resolveAgentIdFromSessionKey: (key: string) => key.match(/^agent:([^:]+)/)?.[1] ?? "main", - resolveStorePath: () => "/tmp/test-store", + resolveSessionStorePathCore: () => "/tmp/test-store", resolveMainSessionKey: () => "agent:main:main", updateSessionStore: vi.fn(), })); @@ -203,7 +203,7 @@ describe("subagent registry lifecycle error grace", () => { loadConfigMock as typeof import("../../../config/config.js").getRuntimeConfig, readSubagentSessionEntry: (_storePath, sessionKey) => sessionStore[sessionKey], resolveAgentIdFromSessionKey: (key) => key?.match(/^agent:([^:]+)/)?.[1] ?? "main", - resolveStorePath: () => "/tmp/test-store", + resolveSessionStorePathCore: () => "/tmp/test-store", }); }); diff --git a/src/agents/subagents/registry/subagent-registry.steer-restart.test.ts b/src/agents/subagents/registry/subagent-registry.steer-restart.test.ts index 505863b5498d..d384db03058f 100644 --- a/src/agents/subagents/registry/subagent-registry.steer-restart.test.ts +++ b/src/agents/subagents/registry/subagent-registry.steer-restart.test.ts @@ -78,7 +78,7 @@ vi.mock("../../../config/sessions.js", () => { return match?.[1] ?? "main"; }, resolveMainSessionKey: () => "agent:main:main", - resolveStorePath: () => "/tmp/test-store", + resolveSessionStorePathCore: () => "/tmp/test-store", updateSessionStore: vi.fn(), }; }); diff --git a/src/agents/subagents/registry/subagent-registry.test.ts b/src/agents/subagents/registry/subagent-registry.test.ts index 8542e94a42ef..bb1416ae9a25 100644 --- a/src/agents/subagents/registry/subagent-registry.test.ts +++ b/src/agents/subagents/registry/subagent-registry.test.ts @@ -238,7 +238,7 @@ vi.mock("../../../config/config.js", () => { vi.mock("../../../config/sessions.js", () => ({ loadSessionStore: mocks.loadSessionStore, resolveAgentIdFromSessionKey: mocks.resolveAgentIdFromSessionKey, - resolveStorePath: mocks.resolveStorePath, + resolveSessionStorePathCore: mocks.resolveStorePath, updateSessionStore: mocks.updateSessionStore, })); diff --git a/src/agents/subagents/registry/subagent-session-reconciliation.ts b/src/agents/subagents/registry/subagent-session-reconciliation.ts index 728ef0b066d5..2697f59d901a 100644 --- a/src/agents/subagents/registry/subagent-session-reconciliation.ts +++ b/src/agents/subagents/registry/subagent-session-reconciliation.ts @@ -7,7 +7,7 @@ import { asFiniteNumber } from "@openclaw/normalization-core/number-coercion"; import { getRuntimeConfig } from "../../../config/config.js"; import { resolveAgentIdFromSessionKey, - resolveStorePath, + resolveSessionStorePathCore, type SessionEntry, } from "../../../config/sessions.js"; import { @@ -95,7 +95,7 @@ export function loadSubagentSessionEntry(params: { } const agentId = resolveAgentIdFromSessionKey(key); const cfg = params.cfg ?? getRuntimeConfig(); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); let store = params.storeCache?.get(storePath); if (!store) { store = Object.fromEntries( @@ -120,7 +120,7 @@ function loadSubagentSessionEntryForAccessor(params: { } const agentId = resolveAgentIdFromSessionKey(key); const cfg = params.cfg ?? getRuntimeConfig(); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); return loadSessionEntryReadOnly({ storePath, sessionKey: key, diff --git a/src/agents/subagents/spawn/acp-parent-stream-store.sqlite.test.ts b/src/agents/subagents/spawn/acp-parent-stream-store.sqlite.test.ts index 5276a1840ab5..023409e7506c 100644 --- a/src/agents/subagents/spawn/acp-parent-stream-store.sqlite.test.ts +++ b/src/agents/subagents/spawn/acp-parent-stream-store.sqlite.test.ts @@ -5,7 +5,7 @@ import { closeOpenClawAgentDatabasesForTest, runOpenClawAgentWriteTransaction, } from "../../../state/openclaw-agent-db.js"; -import { withTempDir } from "../../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../../test-helpers/temp-dir.js"; import { recordAcpParentStreamEvents } from "./acp-parent-stream-store.sqlite.js"; import { listAcpParentStreamEventsForTest } from "./acp-parent-stream-store.sqlite.test-support.js"; @@ -15,7 +15,7 @@ describe("ACP parent stream SQLite store", () => { }); it("orders run events and removes them with the child session", async () => { - await withTempDir({ prefix: "openclaw-acp-parent-stream-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-acp-parent-stream-" }, async (stateDir) => { const options = { agentId: "codex", env: { ...process.env, OPENCLAW_STATE_DIR: stateDir }, @@ -78,7 +78,7 @@ describe("ACP parent stream SQLite store", () => { }); it("drops unserializable events without blocking later diagnostics", async () => { - await withTempDir({ prefix: "openclaw-acp-parent-stream-invalid-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-acp-parent-stream-invalid-" }, async (stateDir) => { const options = { agentId: "codex", env: { ...process.env, OPENCLAW_STATE_DIR: stateDir }, diff --git a/src/agents/subagents/spawn/acp-spawn-heartbeat.ts b/src/agents/subagents/spawn/acp-spawn-heartbeat.ts index 48a2a1ba9159..e44e6a850d5d 100644 --- a/src/agents/subagents/spawn/acp-spawn-heartbeat.ts +++ b/src/agents/subagents/spawn/acp-spawn-heartbeat.ts @@ -1,7 +1,7 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { DEFAULT_HEARTBEAT_EVERY } from "../../../auto-reply/heartbeat.js"; import { parseDurationMs } from "../../../cli/parse-duration.js"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { loadSessionEntryReadOnly } from "../../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../../config/types.openclaw.js"; import { areHeartbeatsEnabled } from "../../../infra/heartbeat-wake.js"; @@ -90,7 +90,7 @@ export function hasSessionLocalHeartbeatRelayRoute(params: { return false; } - const storePath = resolveStorePath(params.cfg.session?.store, { + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId: params.requesterAgentId, }); const parentEntry = loadSessionEntryReadOnly({ diff --git a/src/agents/subagents/spawn/acp-spawn-requester.ts b/src/agents/subagents/spawn/acp-spawn-requester.ts index 8abd0a022493..aac79825e399 100644 --- a/src/agents/subagents/spawn/acp-spawn-requester.ts +++ b/src/agents/subagents/spawn/acp-spawn-requester.ts @@ -1,6 +1,6 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { readAcpSessionMeta } from "../../../acp/runtime/session-meta.js"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { listSessionEntriesReadOnly, loadSessionEntryReadOnly, @@ -220,7 +220,9 @@ export function validateAcpResumeSessionOwnership(params: { }; } - const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.targetAgentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { + agentId: params.targetAgentId, + }); for (const { sessionKey, entry } of listSessionEntriesReadOnly({ storePath, clone: false })) { const acp = readAcpSessionMeta({ sessionKey, cfg: params.cfg }); if (!sessionEntryMatchesAcpResumeSessionId(acp, resumeSessionId)) { diff --git a/src/agents/subagents/spawn/acp-spawn-runtime.ts b/src/agents/subagents/spawn/acp-spawn-runtime.ts index eeca4b615b53..adc0989bb413 100644 --- a/src/agents/subagents/spawn/acp-spawn-runtime.ts +++ b/src/agents/subagents/spawn/acp-spawn-runtime.ts @@ -15,7 +15,7 @@ import { resolveThreadBindingIdleTimeoutMsForChannel, resolveThreadBindingMaxAgeMsForChannel, } from "../../../channels/thread-bindings-policy.js"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { loadSessionEntry } from "../../../config/sessions/session-accessor.js"; import type { SessionEntry } from "../../../config/sessions/types.js"; import type { OpenClawConfig } from "../../../config/types.openclaw.js"; @@ -151,7 +151,9 @@ export async function initializeAcpSpawnRuntime(params: { modelExplicit?: boolean; cwd?: string; }): Promise { - const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.targetAgentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { + agentId: params.targetAgentId, + }); let sessionEntry = loadSessionEntry({ storePath, sessionKey: params.sessionKey, diff --git a/src/agents/subagents/spawn/acp-spawn.test.ts b/src/agents/subagents/spawn/acp-spawn.test.ts index e1e951d7e5c7..6792818fb3f3 100644 --- a/src/agents/subagents/spawn/acp-spawn.test.ts +++ b/src/agents/subagents/spawn/acp-spawn.test.ts @@ -114,7 +114,7 @@ const hoisted = vi.hoisted(() => { listSessionEntriesReadOnly: listMockEntries, loadSessionEntry: loadMockEntry, loadSessionEntryReadOnly: loadMockEntry, - upsertSessionEntry: async (scope: unknown, patch: SessionEntry) => + upsertSessionEntryCore: async (scope: unknown, patch: SessionEntry) => await upsertSessionEntryMock(scope, patch), resolveSessionTranscriptRuntimeTarget: async (scope: { agentId: string; @@ -197,7 +197,7 @@ vi.mock("../../../channels/plugins/registry.js", () => ({ })); vi.mock("../../../config/sessions/paths.js", () => ({ - resolveStorePath: hoisted.resolveStorePathMock, + resolveSessionStorePathCore: hoisted.resolveStorePathMock, })); vi.mock("../../../config/sessions/session-accessor.js", () => hoisted.createSessionAccessorMock()); @@ -206,7 +206,7 @@ vi.mock("../../../config/sessions.js", () => ({ loadSessionStore: hoisted.loadSessionStoreMock, resolveAgentIdFromSessionKey: (sessionKey: string) => sessionKey.match(/^agent:([^:]+)/)?.[1] ?? "main", - resolveStorePath: hoisted.resolveStorePathMock, + resolveSessionStorePathCore: hoisted.resolveStorePathMock, })); vi.mock("../../../config/config.js", () => ({ diff --git a/src/agents/subagents/spawn/acp-spawn.ts b/src/agents/subagents/spawn/acp-spawn.ts index eb771af4b916..73fdc32bcdd0 100644 --- a/src/agents/subagents/spawn/acp-spawn.ts +++ b/src/agents/subagents/spawn/acp-spawn.ts @@ -6,10 +6,10 @@ import type { AcpSpawnRuntimeCloseHandle } from "../../../acp/control-plane/spaw import { cleanupFailedAcpSpawn } from "../../../acp/control-plane/spawn.js"; import { isAcpEnabledByPolicy, resolveAcpAgentPolicyError } from "../../../acp/policy.js"; import { getRuntimeConfig } from "../../../config/config.js"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { loadSessionEntryReadOnly, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../../config/sessions/session-accessor.js"; import { buildSessionCreationStamp } from "../../../config/sessions/session-entry-provenance.js"; import type { SessionEntry } from "../../../config/sessions/types.js"; @@ -495,7 +495,7 @@ export async function spawnAcpDirect( via: "spawn", actor: { type: "agent", id: requesterInternalKey }, }); - const storePath = resolveStorePath(cfg.session?.store, { agentId: targetAgentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: targetAgentId }); const childSessionPatch = admission.childSessionPatch ? { spawnDepth: admission.childSessionPatch.spawnDepth, @@ -506,7 +506,7 @@ export async function spawnAcpDirect( } : {}; childCreationEntry = - (await upsertSessionEntry( + (await upsertSessionEntryCore( { storePath, sessionKey }, { ...creationStamp, diff --git a/src/agents/subagents/spawn/subagent-capabilities.ts b/src/agents/subagents/spawn/subagent-capabilities.ts index bd314304ffc0..63005ebeb581 100644 --- a/src/agents/subagents/spawn/subagent-capabilities.ts +++ b/src/agents/subagents/spawn/subagent-capabilities.ts @@ -12,7 +12,7 @@ import { normalizeOptionalString, } from "@openclaw/normalization-core/string-coerce"; import { DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH } from "../../../config/agent-limits.js"; -import { resolveStorePath } from "../../../config/sessions.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions.js"; import type { OpenClawConfig } from "../../../config/types.openclaw.js"; import { isAcpSessionKey, @@ -134,7 +134,9 @@ function resolveSessionCapabilityEntry(params: { if (!parsed?.agentId) { return undefined; } - const storePath = resolveStorePath(params.cfg.session?.store, { agentId: parsed.agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { + agentId: parsed.agentId, + }); const store = readSubagentSessionStore(storePath, parsed.agentId); return store[params.sessionKey] ?? findSubagentSessionEntryById(store, params.sessionKey); } @@ -167,7 +169,9 @@ export function resolveSubagentCapabilityStore( if (!parsed?.agentId) { return undefined; } - const storePath = resolveStorePath(opts.cfg.session?.store, { agentId: parsed.agentId }); + const storePath = resolveSessionStorePathCore(opts.cfg.session?.store, { + agentId: parsed.agentId, + }); return readSubagentSessionStore(storePath, parsed.agentId); } diff --git a/src/agents/subagents/spawn/subagent-depth.ts b/src/agents/subagents/spawn/subagent-depth.ts index cb1799a35df6..0e1b4eb95ca4 100644 --- a/src/agents/subagents/spawn/subagent-depth.ts +++ b/src/agents/subagents/spawn/subagent-depth.ts @@ -4,7 +4,7 @@ * Reads persisted session store state to recover spawn depth and parent lineage across restarts. */ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { listSessionEntriesReadOnly } from "../../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../../config/types.openclaw.js"; import { parseStrictNonNegativeInteger } from "../../../infra/parse-finite-number.js"; @@ -105,7 +105,9 @@ function resolveEntryForSessionKey(params: { if (!parsed?.agentId) { continue; } - const storePath = resolveStorePath(params.cfg.session?.store, { agentId: parsed.agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { + agentId: parsed.agentId, + }); let store = params.cache.get(storePath); if (!store) { store = readSubagentSessionStore(storePath, parsed.agentId); diff --git a/src/agents/subagents/spawn/subagent-spawn-session-patch.ts b/src/agents/subagents/spawn/subagent-spawn-session-patch.ts index c0d8edaf56a7..ecfb8e0350de 100644 --- a/src/agents/subagents/spawn/subagent-spawn-session-patch.ts +++ b/src/agents/subagents/spawn/subagent-spawn-session-patch.ts @@ -12,7 +12,10 @@ import { } from "../../inherited-tool-deny.js"; import { getSubagentSpawnDeps } from "./subagent-spawn-deps.js"; import { splitModelRef } from "./subagent-spawn-plan.js"; -import { resolveGatewaySessionStoreTarget, upsertSessionEntry } from "./subagent-spawn.runtime.js"; +import { + resolveGatewaySessionStoreTarget, + upsertSessionEntryCore, +} from "./subagent-spawn.runtime.js"; function buildDirectChildSessionPatch(patch: Record): Partial { const entry: Partial = {}; @@ -154,7 +157,7 @@ export async function createInitialSubagentSession(params: { cfg: params.cfg, key: params.childSessionKey, }); - const entry = await upsertSessionEntry( + const entry = await upsertSessionEntryCore( { storePath: target.storePath, sessionKey: target.canonicalKey, @@ -189,7 +192,7 @@ export async function persistInitialChildSessionRuntimeModel(params: { cfg: params.cfg, key: params.childSessionKey, }); - await upsertSessionEntry( + await upsertSessionEntryCore( { storePath: target.storePath, sessionKey: target.canonicalKey, diff --git a/src/agents/subagents/spawn/subagent-spawn.runtime.ts b/src/agents/subagents/spawn/subagent-spawn.runtime.ts index 70cdd2c3c4c5..65b9059dc477 100644 --- a/src/agents/subagents/spawn/subagent-spawn.runtime.ts +++ b/src/agents/subagents/spawn/subagent-spawn.runtime.ts @@ -6,7 +6,7 @@ export { getRuntimeConfig } from "../../../config/config.js"; export { loadSessionEntryReadOnly as loadSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../../config/sessions/session-accessor.js"; export { forkSessionEntryFromParent } from "../../../auto-reply/reply/session-fork.js"; export { ensureContextEnginesInitialized } from "../../../context-engine/init.js"; diff --git a/src/agents/subagents/spawn/subagent-spawn.test-helpers.ts b/src/agents/subagents/spawn/subagent-spawn.test-helpers.ts index a9071e693ce4..da8fc171a09d 100644 --- a/src/agents/subagents/spawn/subagent-spawn.test-helpers.ts +++ b/src/agents/subagents/spawn/subagent-spawn.test-helpers.ts @@ -315,7 +315,7 @@ export async function loadSubagentSpawnModuleForTest(params: { // Real scope resolver: spawn's admin-tier pinning depends on params-aware // sessions.patch policy, so a stub here would hide policy regressions. resolveLeastPrivilegeOperatorScopesForMethod, - upsertSessionEntry: async ( + upsertSessionEntryCore: async ( scope: { storePath?: string; sessionKey: string }, patch: Record, ) => { diff --git a/src/agents/tool-call-id.ts b/src/agents/tool-call-id.ts index a51ab9122545..108afe9e6b77 100644 --- a/src/agents/tool-call-id.ts +++ b/src/agents/tool-call-id.ts @@ -9,7 +9,7 @@ import { * * Keeps provider-specific id formats replay-safe while preserving allowed native ids. */ -import { sha256HexPrefix } from "../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../infra/crypto-digest.js"; import { isThinkingLikeBlock } from "./thinking-block.js"; import { isAllowedToolCallName, normalizeAllowedToolNames } from "./tool-call-shared.js"; @@ -165,7 +165,7 @@ function collectReplaySafeThinkingToolIds( } function shortHash(text: string, length = 8): string { - return sha256HexPrefix(text, length); + return sha256HexPrefixCore(text, length); } function isNativeAnthropicToolUseId(id: string): boolean { diff --git a/src/agents/tools/generated-media-batch-persistence.test.ts b/src/agents/tools/generated-media-batch-persistence.test.ts index a4df6e532dc9..60f0144d3a44 100644 --- a/src/agents/tools/generated-media-batch-persistence.test.ts +++ b/src/agents/tools/generated-media-batch-persistence.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import { describe, expect, it } from "vitest"; import { createDeferred } from "../../../test/helpers/promise.js"; import { resolveMediaBufferPath, saveMediaBuffer, type SavedMedia } from "../../media/store.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { withEnvAsync } from "../../test-utils/env.js"; import { persistGeneratedMediaBatch } from "./generated-media-batch-persistence.js"; @@ -16,7 +16,7 @@ async function expectSavedMediaMissing(saved: SavedMedia): Promise { describe("persistGeneratedMediaBatch filesystem rollback", () => { it("removes a real file when a later sequential save fails", async () => { - await withTempDir({ prefix: "openclaw-generated-media-batch-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-generated-media-batch-" }, async (stateDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: stateDir }, async () => { const payload = Buffer.from("persisted before failure"); let saved: SavedMedia | undefined; @@ -47,7 +47,7 @@ describe("persistGeneratedMediaBatch filesystem rollback", () => { }); it("drains and removes a real concurrent write that finishes after failure", async () => { - await withTempDir({ prefix: "openclaw-generated-media-batch-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-generated-media-batch-" }, async (stateDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: stateDir }, async () => { const releaseLateSave = createDeferred(); const payload = Buffer.from("persisted after failure"); diff --git a/src/agents/tools/goal-tools.test.ts b/src/agents/tools/goal-tools.test.ts index 40a156630de8..813e630ab1d6 100644 --- a/src/agents/tools/goal-tools.test.ts +++ b/src/agents/tools/goal-tools.test.ts @@ -4,10 +4,10 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { loadSessionEntry, - upsertSessionEntry as upsertAccessorSessionEntry, + upsertSessionEntryCore as upsertAccessorSessionEntry, } from "../../config/sessions/session-accessor.js"; import type { SessionEntry } from "../../config/sessions/types.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; @@ -47,7 +47,7 @@ describe("goal tools", () => { // Budget-limited status can be derived for display without mutating the // stored active goal record. const { config, template } = await createStoreConfig(); - const storePath = resolveStorePath(template, { agentId: "research" }); + const storePath = resolveSessionStorePathCore(template, { agentId: "research" }); await upsertSessionEntry({ storePath, sessionKey: "global", @@ -94,7 +94,7 @@ describe("goal tools", () => { config, }); - const researchStorePath = resolveStorePath(template, { agentId: "research" }); + const researchStorePath = resolveSessionStorePathCore(template, { agentId: "research" }); await upsertSessionEntry({ storePath: researchStorePath, sessionKey: "global", @@ -102,7 +102,7 @@ describe("goal tools", () => { }); await tool.execute("call-1", { objective: "ship global work" }); - const mainStorePath = resolveStorePath(template, { agentId: "main" }); + const mainStorePath = resolveSessionStorePathCore(template, { agentId: "main" }); expect( getSessionEntry({ storePath: researchStorePath, sessionKey: "global" })?.goal?.objective, ).toBe("ship global work"); @@ -122,7 +122,7 @@ describe("goal tools", () => { config, }); - const storePath = resolveStorePath(template, { agentId: "research" }); + const storePath = resolveSessionStorePathCore(template, { agentId: "research" }); await upsertSessionEntry({ storePath, sessionKey: "global", @@ -148,7 +148,7 @@ describe("goal tools", () => { config, }); - const opsStorePath = resolveStorePath(template, { agentId: "ops" }); + const opsStorePath = resolveSessionStorePathCore(template, { agentId: "ops" }); await upsertSessionEntry({ storePath: opsStorePath, sessionKey: "agent:ops:main", @@ -156,7 +156,7 @@ describe("goal tools", () => { }); await tool.execute("call-1", { objective: "ship ops work" }); - const researchStorePath = resolveStorePath(template, { agentId: "research" }); + const researchStorePath = resolveSessionStorePathCore(template, { agentId: "research" }); expect( getSessionEntry({ storePath: opsStorePath, sessionKey: "agent:ops:main" })?.goal?.objective, ).toBe("ship ops work"); @@ -167,7 +167,7 @@ describe("goal tools", () => { it("tells the model to send the requested final reply after completing a goal", async () => { const { config, template } = await createStoreConfig(); - const storePath = resolveStorePath(template, { agentId: "research" }); + const storePath = resolveSessionStorePathCore(template, { agentId: "research" }); const options = { agentSessionKey: "global", runSessionKey: "global", diff --git a/src/agents/tools/goal-tools.ts b/src/agents/tools/goal-tools.ts index d3c79891624d..272a8e03d194 100644 --- a/src/agents/tools/goal-tools.ts +++ b/src/agents/tools/goal-tools.ts @@ -10,7 +10,7 @@ import { MODEL_UPDATABLE_SESSION_GOAL_STATUSES, updateSessionGoalStatus, } from "../../config/sessions/goals.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { normalizeAgentId, parseAgentSessionKey } from "../../routing/session-key.js"; import { stringEnum } from "../schema/typebox.js"; @@ -68,7 +68,7 @@ function resolveGoalSessionScope(options: GoalToolOptions): GoalSessionScope { return { sessionKey, agentId, - storePath: resolveStorePath(options.config?.session?.store, { + storePath: resolveSessionStorePathCore(options.config?.session?.store, { agentId, }), }; diff --git a/src/agents/tools/nodes-tool-commands.ts b/src/agents/tools/nodes-tool-commands.ts index 0d70fc1031d7..39000e36efc5 100644 --- a/src/agents/tools/nodes-tool-commands.ts +++ b/src/agents/tools/nodes-tool-commands.ts @@ -17,7 +17,7 @@ import { import type { GatewayCallOptions } from "./gateway.js"; import { callGatewayTool } from "./gateway.js"; import { POLICY_REDIRECT_INVOKE_COMMANDS } from "./nodes-tool-media.js"; -import { resolveNodeId } from "./nodes-utils.js"; +import { resolveAgentNodeId } from "./nodes-utils.js"; const BLOCKED_INVOKE_COMMANDS = new Set(["system.run", "system.run.prepare"]); const DEDICATED_TOOL_INVOKE_COMMANDS = new Map([ @@ -181,7 +181,7 @@ export async function executeNodeCommandAction(params: { } case "invoke": { const node = readToolStringParam(params.input, "node", { required: true }); - const nodeId = await resolveNodeId(params.gatewayOpts, node); + const nodeId = await resolveAgentNodeId(params.gatewayOpts, node); const invokeCommand = readToolStringParam(params.input, "invokeCommand", { required: true }); const invokeCommandNormalized = normalizeLowercaseStringOrEmpty(invokeCommand); if (BLOCKED_INVOKE_COMMANDS.has(invokeCommandNormalized)) { @@ -248,7 +248,7 @@ async function invokeNodeCommandPayload(params: { command: string; commandParams?: Record; }): Promise { - const nodeId = await resolveNodeId(params.gatewayOpts, params.node); + const nodeId = await resolveAgentNodeId(params.gatewayOpts, params.node); const raw = await callGatewayTool<{ payload: unknown }>("node.invoke", params.gatewayOpts, { nodeId, command: params.command, diff --git a/src/agents/tools/nodes-tool-media.ts b/src/agents/tools/nodes-tool-media.ts index 0cbeaf633e38..521c49285e9b 100644 --- a/src/agents/tools/nodes-tool-media.ts +++ b/src/agents/tools/nodes-tool-media.ts @@ -41,7 +41,7 @@ import { } from "./common.js"; import type { GatewayCallOptions } from "./gateway.js"; import { callGatewayTool } from "./gateway.js"; -import { resolveNode, resolveNodeId } from "./nodes-utils.js"; +import { resolveAgentNode, resolveAgentNodeId } from "./nodes-utils.js"; export const MEDIA_INVOKE_ACTIONS = { "camera.snap": "camera_snap", @@ -151,7 +151,7 @@ async function executeCameraSnap({ imageSanitization, }: ExecuteNodeMediaActionParams): Promise> { const node = requireString(params, "node"); - const resolvedNode = await resolveNode(gatewayOpts, node); + const resolvedNode = await resolveAgentNode(gatewayOpts, node); const nodeId = resolvedNode.nodeId; const facingRaw = normalizeLowercaseStringOrEmpty(params.facing) || "front"; const facing = @@ -243,7 +243,7 @@ async function executePhotosLatest({ imageSanitization, }: ExecuteNodeMediaActionParams): Promise> { const node = requireString(params, "node"); - const resolvedNode = await resolveNode(gatewayOpts, node); + const resolvedNode = await resolveAgentNode(gatewayOpts, node); const nodeId = resolvedNode.nodeId; const limit = Math.min( readPositiveIntegerParam(params, "limit") ?? DEFAULT_PHOTOS_LIMIT, @@ -331,7 +331,7 @@ async function executeCameraClip({ gatewayOpts, }: ExecuteNodeMediaActionParams): Promise> { const node = requireString(params, "node"); - const resolvedNode = await resolveNode(gatewayOpts, node); + const resolvedNode = await resolveAgentNode(gatewayOpts, node); const nodeId = resolvedNode.nodeId; const facing = normalizeLowercaseStringOrEmpty(params.facing) || "front"; if (facing !== "front" && facing !== "back") { @@ -384,7 +384,7 @@ async function executeScreenRecord({ gatewayOpts, }: ExecuteNodeMediaActionParams): Promise> { const node = requireString(params, "node"); - const nodeId = await resolveNodeId(gatewayOpts, node); + const nodeId = await resolveAgentNodeId(gatewayOpts, node); const durationMs = Math.min( readPositiveIntegerParam(params, "durationMs") ?? (typeof params.duration === "string" ? parseDurationMs(params.duration) : 10_000), @@ -435,7 +435,7 @@ async function executeScreenSnapshot({ gatewayOpts, }: ExecuteNodeMediaActionParams): Promise> { const node = requireString(params, "node"); - const nodeId = await resolveNodeId(gatewayOpts, node); + const nodeId = await resolveAgentNodeId(gatewayOpts, node); const screenIndex = readNonNegativeIntegerParam(params, "screenIndex") ?? 0; const maxWidth = readPositiveIntegerParam(params, "maxWidth"); const outPath = normalizeOptionalString(params.outPath); diff --git a/src/agents/tools/nodes-tool-ptz.test.ts b/src/agents/tools/nodes-tool-ptz.test.ts index 80637bdc53f6..14d858fda41d 100644 --- a/src/agents/tools/nodes-tool-ptz.test.ts +++ b/src/agents/tools/nodes-tool-ptz.test.ts @@ -3,8 +3,8 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; const mocks = vi.hoisted(() => ({ callGatewayTool: vi.fn(), readGatewayCallOptions: vi.fn(() => ({})), - resolveNode: vi.fn(async () => ({ nodeId: "node-1" })), - resolveNodeId: vi.fn(async () => "node-1"), + resolveAgentNode: vi.fn(async () => ({ nodeId: "node-1" })), + resolveAgentNodeId: vi.fn(async () => "node-1"), })); vi.mock("./gateway.js", () => ({ @@ -13,8 +13,8 @@ vi.mock("./gateway.js", () => ({ })); vi.mock("./nodes-utils.js", () => ({ - resolveNode: mocks.resolveNode, - resolveNodeId: mocks.resolveNodeId, + resolveAgentNode: mocks.resolveAgentNode, + resolveAgentNodeId: mocks.resolveAgentNodeId, })); import { executeNodeCommandAction } from "./nodes-tool-commands.js"; @@ -33,7 +33,7 @@ describe("nodes camera_ptz", () => { beforeEach(() => { mocks.callGatewayTool.mockReset(); mocks.callGatewayTool.mockResolvedValue({ payload: { deviceId: "camera-id", axes: {} } }); - mocks.resolveNodeId.mockClear(); + mocks.resolveAgentNodeId.mockClear(); }); it("advertises physical PTZ and its closed operation schema", () => { @@ -154,7 +154,7 @@ describe("nodes camera_ptz", () => { ], ])("validates before invoking the node", async (input, message) => { await expect(execute(input)).rejects.toThrow(message); - expect(mocks.resolveNodeId).not.toHaveBeenCalled(); + expect(mocks.resolveAgentNodeId).not.toHaveBeenCalled(); expect(mocks.callGatewayTool).not.toHaveBeenCalled(); }); }); diff --git a/src/agents/tools/nodes-tool.test.ts b/src/agents/tools/nodes-tool.test.ts index 60fdd56f8d10..7fd5891f191b 100644 --- a/src/agents/tools/nodes-tool.test.ts +++ b/src/agents/tools/nodes-tool.test.ts @@ -8,8 +8,8 @@ const gatewayMocks = vi.hoisted(() => ({ })); const nodeUtilsMocks = vi.hoisted(() => ({ - resolveNodeId: vi.fn(async () => "node-1"), - resolveNode: vi.fn(async () => ({ + resolveAgentNodeId: vi.fn(async () => "node-1"), + resolveAgentNode: vi.fn(async () => ({ nodeId: "node-1", remoteIp: "127.0.0.1", platform: undefined as string | undefined, @@ -91,8 +91,8 @@ vi.mock("./gateway.js", () => ({ })); vi.mock("./nodes-utils.js", () => ({ - resolveNodeId: nodeUtilsMocks.resolveNodeId, - resolveNode: nodeUtilsMocks.resolveNode, + resolveAgentNodeId: nodeUtilsMocks.resolveAgentNodeId, + resolveAgentNode: nodeUtilsMocks.resolveAgentNode, })); vi.mock("../../cli/nodes-camera.js", () => ({ @@ -170,8 +170,8 @@ describe("createNodesTool screen_record duration guardrails", () => { gatewayMocks.callGatewayTool.mockReset(); gatewayMocks.readGatewayCallOptions.mockReset(); gatewayMocks.readGatewayCallOptions.mockReturnValue({}); - nodeUtilsMocks.resolveNodeId.mockClear(); - nodeUtilsMocks.resolveNode.mockClear(); + nodeUtilsMocks.resolveAgentNodeId.mockClear(); + nodeUtilsMocks.resolveAgentNode.mockClear(); screenMocks.parseScreenRecordPayload.mockClear(); screenMocks.screenRecordTempPath.mockClear(); screenMocks.writeScreenRecordToFile.mockClear(); @@ -306,7 +306,7 @@ describe("createNodesTool screen_record duration guardrails", () => { await expect(tool.execute("call-describe", { action: "describe" })).rejects.toThrow( 'node required for describe; call nodes with action="status" to list nodes, then retry with node', ); - expect(nodeUtilsMocks.resolveNodeId).not.toHaveBeenCalled(); + expect(nodeUtilsMocks.resolveAgentNodeId).not.toHaveBeenCalled(); expect(gatewayMocks.callGatewayTool).not.toHaveBeenCalled(); }); @@ -316,7 +316,7 @@ describe("createNodesTool screen_record duration guardrails", () => { await tool.execute("call-describe", { action: "describe", node: "Office Mac" }); - expect(nodeUtilsMocks.resolveNodeId).toHaveBeenCalledWith({}, "Office Mac"); + expect(nodeUtilsMocks.resolveAgentNodeId).toHaveBeenCalledWith({}, "Office Mac"); expect(gatewayMocks.callGatewayTool).toHaveBeenCalledWith( "node.describe", {}, @@ -658,7 +658,7 @@ describe("createNodesTool screen_record duration guardrails", () => { }); it("captures one unknown-position snap for Linux facing requests", async () => { - nodeUtilsMocks.resolveNode.mockResolvedValueOnce({ + nodeUtilsMocks.resolveAgentNode.mockResolvedValueOnce({ nodeId: "linux-node", remoteIp: "127.0.0.1", platform: "linux", @@ -684,7 +684,7 @@ describe("createNodesTool screen_record duration guardrails", () => { }); it("captures an unknown-position clip on Linux without forwarding facing", async () => { - nodeUtilsMocks.resolveNode.mockResolvedValueOnce({ + nodeUtilsMocks.resolveAgentNode.mockResolvedValueOnce({ nodeId: "linux-node", remoteIp: "127.0.0.1", platform: "linux", diff --git a/src/agents/tools/nodes-tool.ts b/src/agents/tools/nodes-tool.ts index 0dc5a1a097a0..8409b73c1399 100644 --- a/src/agents/tools/nodes-tool.ts +++ b/src/agents/tools/nodes-tool.ts @@ -24,7 +24,7 @@ import { gatewayCallOptionSchemaProperties } from "./gateway-schema.js"; import { callGatewayTool, readGatewayCallOptions } from "./gateway.js"; import { executeNodeCommandAction, type NodeCommandAction } from "./nodes-tool-commands.js"; import { executeNodeMediaAction, MEDIA_INVOKE_ACTIONS } from "./nodes-tool-media.js"; -import { resolveNodeId } from "./nodes-utils.js"; +import { resolveAgentNodeId } from "./nodes-utils.js"; const NODES_TOOL_ACTIONS = [ "status", @@ -202,7 +202,7 @@ export function createNodesTool(options?: { 'node required for describe; call nodes with action="status" to list nodes, then retry with node', ); } - const nodeId = await resolveNodeId(gatewayOpts, node); + const nodeId = await resolveAgentNodeId(gatewayOpts, node); return jsonResult(await callGatewayTool("node.describe", gatewayOpts, { nodeId })); } case "pending": @@ -240,7 +240,7 @@ export function createNodesTool(options?: { if (!title.trim() && !body.trim()) { throw new Error("title or body required"); } - const nodeId = await resolveNodeId(gatewayOpts, node); + const nodeId = await resolveAgentNodeId(gatewayOpts, node); await callGatewayTool("node.invoke", gatewayOpts, { nodeId, command: "system.notify", diff --git a/src/agents/tools/nodes-utils.ts b/src/agents/tools/nodes-utils.ts index 9920f067f27c..5c7e129bb3b3 100644 --- a/src/agents/tools/nodes-utils.ts +++ b/src/agents/tools/nodes-utils.ts @@ -247,16 +247,16 @@ export function resolveEligibleNodeFromList( } /** Loads nodes from the Gateway and resolves the requested or default node id. */ -export async function resolveNodeId( +export async function resolveAgentNodeId( opts: GatewayCallOptions, query?: string, allowDefault = false, ) { - return (await resolveNode(opts, query, allowDefault)).nodeId; + return (await resolveAgentNode(opts, query, allowDefault)).nodeId; } /** Loads nodes from the Gateway and returns the requested or default node record. */ -export async function resolveNode( +export async function resolveAgentNode( opts: GatewayCallOptions, query?: string, allowDefault = false, diff --git a/src/agents/tools/scoped-session-access.ts b/src/agents/tools/scoped-session-access.ts index 207ee84060b1..fd1e2b49f96e 100644 --- a/src/agents/tools/scoped-session-access.ts +++ b/src/agents/tools/scoped-session-access.ts @@ -1,4 +1,4 @@ -import { resolveStorePath } from "../../config/sessions.js"; +import { resolveSessionStorePathCore } from "../../config/sessions.js"; import { loadSessionEntry as getSessionEntry } from "../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js"; @@ -17,7 +17,7 @@ export async function runWithScopedSessionAccess(params: { return await params.run(); } const agentId = resolveAgentIdFromSessionKey(params.targetSessionKey); - const storePath = resolveStorePath(params.cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId }); const assertExpectedIncarnation = () => { const current = getSessionEntry({ storePath, sessionKey: params.targetSessionKey }); if (current?.sessionId !== expectedSessionId || current.archivedAt !== undefined) { diff --git a/src/agents/tools/session-status-tool.ts b/src/agents/tools/session-status-tool.ts index 19e2f9b0d9d9..752b1b5c7d87 100644 --- a/src/agents/tools/session-status-tool.ts +++ b/src/agents/tools/session-status-tool.ts @@ -15,7 +15,7 @@ import type { import { getRuntimeConfig } from "../../config/config.js"; import { patchSessionEntryWithKey, - resolveStorePath, + resolveSessionStorePathCore, type SessionEntry, } from "../../config/sessions.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; @@ -715,7 +715,7 @@ export function createSessionStatusTool(opts?: { let agentId = isExplicitAgentKey ? resolveAgentIdFromSessionKey(requestedKeyInput, configuredDefaultAgentId) : requesterAgentId; - let storePath = resolveStorePath(cfg.session?.store, { agentId }); + let storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); let storeScopedRequesterKey = resolveStoreScopedRequesterKey({ requesterKey: effectiveRequesterKey, agentId, @@ -767,7 +767,7 @@ export function createSessionStatusTool(opts?: { requestedKeyRaw = visibleSession.key; requestedKeyInput = requestedKeyRaw.trim(); agentId = resolveAgentIdFromSessionKey(visibleSession.key, configuredDefaultAgentId); - storePath = resolveStorePath(cfg.session?.store, { agentId }); + storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); storeScopedRequesterKey = resolveStoreScopedRequesterKey({ requesterKey: effectiveRequesterKey, agentId, diff --git a/src/agents/tools/sessions-spawn-tool.test.ts b/src/agents/tools/sessions-spawn-tool.test.ts index c9886a4a017b..bddde44acf8a 100644 --- a/src/agents/tools/sessions-spawn-tool.test.ts +++ b/src/agents/tools/sessions-spawn-tool.test.ts @@ -3,8 +3,8 @@ import path from "node:path"; // dispatch, and result details for spawned child sessions. import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { upsertSessionEntry } from "../../config/sessions/session-accessor.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { upsertSessionEntryCore } from "../../config/sessions/session-accessor.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { SWARM_CODE_MODE_IDEMPOTENCY_KEY, SWARM_CODE_MODE_REQUEST_FINGERPRINT, @@ -433,7 +433,7 @@ describe("sessions_spawn tool", () => { }); it("creates visible worktree sessions and registers completion announce", async () => { - await withTempDir({ prefix: "openclaw-visible-spawn-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-visible-spawn-" }, async (dir) => { const callGateway = vi.fn(async () => ({ key: "agent:main:dashboard:child", runStarted: true, @@ -515,10 +515,10 @@ describe("sessions_spawn tool", () => { }); it("rejects a visible spawn before creation when the exact parent incarnation changed", async () => { - await withTempDir({ prefix: "openclaw-visible-spawn-parent-race-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-visible-spawn-parent-race-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const parentSessionKey = "agent:main:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: parentSessionKey, storePath }, { sessionId: "replacement-parent", updatedAt: 2 }, ); @@ -652,7 +652,7 @@ describe("sessions_spawn tool", () => { }); it("rejects cwd escape for sandboxed visible sessions", async () => { - await withTempDir({ prefix: "openclaw-visible-sandbox-cwd-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-visible-sandbox-cwd-" }, async (dir) => { const callGateway = vi.fn(); const tool = createSessionsSpawnTool({ agentSessionKey: "agent:main:main", @@ -682,7 +682,7 @@ describe("sessions_spawn tool", () => { }); it("allows cwd within a sandboxed visible session workspace", async () => { - await withTempDir({ prefix: "openclaw-visible-sandbox-cwd-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-visible-sandbox-cwd-" }, async (dir) => { const workspace = path.join(dir, "workspace"); const cwd = path.join(workspace, "packages", "app"); const callGateway = vi.fn(async () => ({ @@ -1078,14 +1078,14 @@ describe("sessions_spawn tool", () => { }); it("applies spawn depth limits to visible dashboard descendants", async () => { - await withTempDir({ prefix: "openclaw-visible-depth-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-visible-depth-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const childKey = "agent:main:dashboard:child"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:main", storePath }, { sessionId: "root", updatedAt: 1 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: childKey, storePath }, // Canonical spawn-child shape: sessions.create persists explicit // spawnDepth; parentSessionKey alone is UI threading and adds no depth. diff --git a/src/agents/tools/sessions-tool.self-archive.test.ts b/src/agents/tools/sessions-tool.self-archive.test.ts index 884333366172..9f9e3564ed4c 100644 --- a/src/agents/tools/sessions-tool.self-archive.test.ts +++ b/src/agents/tools/sessions-tool.self-archive.test.ts @@ -1,19 +1,22 @@ import path from "node:path"; import { describe, expect, it, vi } from "vitest"; -import { loadSessionEntry, upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { + loadSessionEntry, + upsertSessionEntryCore, +} from "../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { beginSessionWorkAdmission } from "../../sessions/session-lifecycle-admission.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { createSessionsTool } from "./sessions-tool.js"; describe("sessions tool self-archive", () => { it("defers self-archiving until the current agent turn has completed", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-self-archive-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-self-archive-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:self-archive"; const sessionId = "session-self-archive"; const config: OpenClawConfig = { session: { store: storePath } }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); @@ -64,12 +67,12 @@ describe("sessions tool self-archive", () => { }); it("applies other self-patch settings before the deferred archive", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-archive-patch-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-archive-patch-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:archive-patch"; const sessionId = "session-archive-patch"; const config: OpenClawConfig = { session: { store: storePath } }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); @@ -126,12 +129,12 @@ describe("sessions tool self-archive", () => { }); it("does not apply a deferred archive to a replacement session", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-archive-replacement-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-archive-replacement-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:archive-replacement"; const sessionId = "session-before-reset"; const config: OpenClawConfig = { session: { store: storePath } }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); @@ -151,7 +154,7 @@ describe("sessions tool self-archive", () => { try { await admission.run(async () => { await tool.execute("archive-before-reset", { action: "patch", archived: true }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "session-after-reset", updatedAt: 2 }, ); @@ -183,12 +186,12 @@ describe("sessions tool self-archive", () => { }); it("waits for a competing turn before applying a scheduled archive", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-archive-competing-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-archive-competing-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:archive-competing"; const sessionId = "session-archive-competing"; const config: OpenClawConfig = { session: { store: storePath } }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); @@ -242,12 +245,12 @@ describe("sessions tool self-archive", () => { }); it("retries a scheduled archive when a turn races the gateway mutation", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-archive-retry-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-archive-retry-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:archive-retry"; const sessionId = "session-archive-retry"; const config: OpenClawConfig = { session: { store: storePath } }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); @@ -308,12 +311,12 @@ describe("sessions tool self-archive", () => { }); it("retries when a competing turn releases before its archive rejection settles", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-archive-release-race-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-archive-release-race-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:archive-release-race"; const sessionId = "session-archive-release-race"; const config: OpenClawConfig = { session: { store: storePath } }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); @@ -369,11 +372,11 @@ describe("sessions tool self-archive", () => { }); it("retries a scheduled archive after a transient gateway failure", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-archive-transport-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-archive-transport-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:archive-transport"; const sessionId = "session-archive-transport"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); @@ -421,12 +424,12 @@ describe("sessions tool self-archive", () => { it("keeps retrying a projected active run without abandoning its archive", async () => { vi.useFakeTimers(); try { - await withTempDir({ prefix: "openclaw-sessions-tool-archive-projected-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-archive-projected-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:archive-projected"; const sessionId = "session-archive-projected"; const config: OpenClawConfig = { session: { store: storePath } }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); @@ -480,12 +483,12 @@ describe("sessions tool self-archive", () => { }); it("keeps main-session archive validation on the gateway", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-archive-main-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-archive-main-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:main"; const sessionId = "session-main-archive"; const config: OpenClawConfig = { session: { store: storePath } }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); diff --git a/src/agents/tools/sessions-tool.test.ts b/src/agents/tools/sessions-tool.test.ts index bb861f3ef9c2..61f209512e41 100644 --- a/src/agents/tools/sessions-tool.test.ts +++ b/src/agents/tools/sessions-tool.test.ts @@ -4,13 +4,13 @@ import { loadSessionEntry, loadTranscriptEvents, patchSessionEntryCore, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { isAgentSessionModelPatchOrigin } from "../../gateway/session-model-patch-origin.js"; import { GATEWAY_OWNER_ONLY_CORE_TOOLS } from "../../security/dangerous-tools.js"; import { beginSessionWorkAdmission } from "../../sessions/session-lifecycle-admission.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { createAgentPatchedSessionModelRunGuard } from "../session-model-auto-revert.js"; import { createSessionsTool } from "./sessions-tool.js"; @@ -272,14 +272,14 @@ describe("sessions tool", () => { }); it("patches its session, then reverts a failed agent-selected model", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:main"; const cfg: OpenClawConfig = { session: { store: storePath }, agents: { defaults: { model: { primary: "openai/good" } } }, }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "session-main", @@ -411,10 +411,10 @@ describe("sessions tool", () => { }); it("clears the model fallback marker after a successful run", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-success-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-success-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "session-main", @@ -462,10 +462,10 @@ describe("sessions tool", () => { }); it("reverts when the patched model fails but a fallback completes the run", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-fallback-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-fallback-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "session-main", @@ -509,13 +509,13 @@ describe("sessions tool", () => { }); it("promotes the newest validated model across overlapping patches", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-overlap-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-overlap-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:main"; const cfg: OpenClawConfig = { agents: { defaults: { model: { primary: "openai/a" } } }, }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "session-main", @@ -801,11 +801,11 @@ describe("sessions tool", () => { }); it("keeps resolved model and thinking metadata when self-archive is deferred", async () => { - await withTempDir({ prefix: "openclaw-sessions-tool-archive-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-sessions-tool-archive-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:subagent:archive-me"; const sessionId = "archive-me-session"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, updatedAt: 1 }, ); diff --git a/src/agents/tools/sessions-tool.ts b/src/agents/tools/sessions-tool.ts index 952b42192ba2..e05105fd0df4 100644 --- a/src/agents/tools/sessions-tool.ts +++ b/src/agents/tools/sessions-tool.ts @@ -5,7 +5,7 @@ import type { SessionsPatchResult } from "../../../packages/gateway-protocol/src import { SESSION_AGENT_ATTENTION_ICON_IDS } from "../../../packages/gateway-protocol/src/session-agent-status.js"; import { getRuntimeConfig } from "../../config/config.js"; import { resolveAgentMainSessionKey } from "../../config/sessions/main-session.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { loadSessionEntry } from "../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { GatewayTransportError } from "../../gateway/call.js"; @@ -379,7 +379,7 @@ export function createSessionsTool(opts: SessionsToolOptions = {}): AnyAgentTool if (patch.archived === true && key === requesterKey && key !== "global") { const agentId = resolveAgentIdFromSessionKey(key, resolveDefaultAgentId(cfg)); if (key !== resolveAgentMainSessionKey({ cfg, agentId })) { - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); const currentEntry = loadSessionEntry({ agentId, sessionKey: key, storePath }); const released = getCurrentSessionWorkAdmissionRelease({ scope: storePath, diff --git a/src/agents/tools/sessions.test.ts b/src/agents/tools/sessions.test.ts index f9b6253254bc..e6b84993bb77 100644 --- a/src/agents/tools/sessions.test.ts +++ b/src/agents/tools/sessions.test.ts @@ -7,14 +7,14 @@ import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { ChannelMessagingAdapter } from "../../channels/plugins/types.public.js"; import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } from "../../config/io.js"; -import { upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../../config/sessions/session-accessor.js"; import { parseSessionThreadInfo } from "../../config/sessions/thread-info.js"; import { getOwnedSessionTranscriptWriterFence, withOwnedSessionTranscriptWrites, } from "../../config/sessions/transcript-write-context.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { createTestRegistry } from "../../test-utils/channel-plugins.js"; import { extractStoredAssistantText, sanitizeTextContent } from "./chat-history-text.js"; @@ -868,13 +868,13 @@ describe("sessions_send gating", () => { }); it("keeps an exact-incarnation send synchronous to its scoped lifecycle grant", async () => { - await withTempDir({ prefix: "openclaw-exact-session-send-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-exact-session-send-" }, async (dir) => { const { runSessionsSendA2AFlow } = await import("./sessions-send-tool.a2a.js"); vi.mocked(runSessionsSendA2AFlow).mockClear(); const storePath = path.join(dir, "sessions.json"); const targetSessionKey = "agent:main:dashboard:child"; const targetSessionId = "child-incarnation"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: targetSessionKey, storePath }, { sessionId: targetSessionId, diff --git a/src/agents/tools/swarm-tools.integration.test.ts b/src/agents/tools/swarm-tools.integration.test.ts index 98785cff61fd..e13183dfbc03 100644 --- a/src/agents/tools/swarm-tools.integration.test.ts +++ b/src/agents/tools/swarm-tools.integration.test.ts @@ -1,7 +1,7 @@ import os from "node:os"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { createOpenClawTools } from "../openclaw-tools.js"; import { resetSubagentRegistryForTests, @@ -55,7 +55,7 @@ describe("swarm tools integration", () => { }); it("spawns three mock-model collectors and drains them in first-completion order", async () => { - await withTempDir({ prefix: "openclaw-swarm-tools-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-swarm-tools-" }, async (stateDir) => { vi.stubEnv("OPENCLAW_STATE_DIR", stateDir); const publicToGateway = new Map(); const resultTextBySession = new Map(); diff --git a/src/agents/watched-sessions-prompt.test.ts b/src/agents/watched-sessions-prompt.test.ts index bcaae1f6a641..48d5d024c3dc 100644 --- a/src/agents/watched-sessions-prompt.test.ts +++ b/src/agents/watched-sessions-prompt.test.ts @@ -1,6 +1,6 @@ import { afterAll, afterEach, describe, expect, it, vi } from "vitest"; import { cleanupTempDirs, makeTempDir } from "../../test/helpers/temp-dir.js"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import { buildWatchedSessionsHarnessContext } from "../plugin-sdk/agent-harness-runtime.js"; import { registerMainSessionGroupWatch } from "../sessions/session-state-events.js"; import { closeOpenClawAgentDatabasesForTest } from "../state/openclaw-agent-db.js"; @@ -37,7 +37,7 @@ describe("prepareWatchedSessionsPrompt", () => { stubStateDir(); watchGroup("agent:main:telegram:group:beta"); watchGroup("agent:main:telegram:group:alpha:topic:7"); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: "agent:main:telegram:group:beta" }, { sessionId: "session-beta", displayName: "Family group", updatedAt: 1 }, ); diff --git a/src/auto-reply/reply/abort.test.ts b/src/auto-reply/reply/abort.test.ts index caa6d2eccaa7..290400e30c02 100644 --- a/src/auto-reply/reply/abort.test.ts +++ b/src/auto-reply/reply/abort.test.ts @@ -5,7 +5,7 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } import { resolveSessionAgentId } from "../../agents/agent-scope.js"; import type { SubagentRunRecord } from "../../agents/subagents/registry/subagent-registry.js"; import type { OpenClawConfig } from "../../config/config.js"; -import { resolveStorePath } from "../../config/sessions.js"; +import { resolveSessionStorePathCore } from "../../config/sessions.js"; import { loadSessionEntry, replaceSessionEntry, @@ -280,7 +280,7 @@ describe("abort detection", () => { sessionKey: entry.childSessionKey, config: params.cfg, }); - const storePath = resolveStorePath(params.cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId }); const sessionId = replyRunRegistry.resolveSessionId(entry.childSessionKey) ?? loadSessionEntry({ diff --git a/src/auto-reply/reply/abort.ts b/src/auto-reply/reply/abort.ts index 9c207c0cb9ce..a6ce65dfa60a 100644 --- a/src/auto-reply/reply/abort.ts +++ b/src/auto-reply/reply/abort.ts @@ -19,7 +19,7 @@ import { resolveInternalSessionKey, resolveMainSessionAlias, } from "../../agents/tools/sessions-helpers.js"; -import { resolveStorePath } from "../../config/sessions.js"; +import { resolveSessionStorePathCore } from "../../config/sessions.js"; import { loadSessionEntry, markSessionAbortTarget, @@ -159,7 +159,7 @@ function resolveStoredSessionId(params: { sessionKey: params.sessionKey, config: params.cfg, }); - const storePath = resolveStorePath(params.cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId }); try { return loadSessionEntry({ agentId, @@ -337,7 +337,7 @@ export async function tryFastAbortFromMessage(params: { const requesterSessionKey = targetKey ?? ctx.SessionKey ?? abortKey; if (targetKey) { - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); const abortCutoffForTarget = (target: SessionAbortTargetContext): AbortCutoff | undefined => shouldPersistAbortCutoff({ commandSessionKey, diff --git a/src/auto-reply/reply/agent-runner-core.ts b/src/auto-reply/reply/agent-runner-core.ts index 789fb297cc51..1475d6b9e1aa 100644 --- a/src/auto-reply/reply/agent-runner-core.ts +++ b/src/auto-reply/reply/agent-runner-core.ts @@ -193,7 +193,7 @@ export function hasSuccessfulTerminalSourceReplyDelivery(params: { ); } -export function resolveConfiguredFallbackModel(params: { +export function resolveFallbackOriginModel(params: { run: FollowupRun["run"]; fallbackStateEntry?: SessionEntry; }): { provider: string; model: string; persistedAutoFallback: boolean } { diff --git a/src/auto-reply/reply/agent-runner-direct-runtime-config.test.ts b/src/auto-reply/reply/agent-runner-direct-runtime-config.test.ts index 366d33d158e3..477fb9bf26fd 100644 --- a/src/auto-reply/reply/agent-runner-direct-runtime-config.test.ts +++ b/src/auto-reply/reply/agent-runner-direct-runtime-config.test.ts @@ -4,7 +4,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { FailoverError } from "../../agents/failover-error.js"; import { replaceSessionEntry } from "../../config/sessions/session-accessor.js"; import type { SessionEntry } from "../../config/sessions/types.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { getReplyPayloadMetadata } from "../reply-payload.js"; import type { TemplateContext } from "../templating.js"; import { SILENT_REPLY_TOKEN } from "../tokens.js"; @@ -373,7 +373,7 @@ describe("runReplyAgent runtime config", () => { }); it("rotates, rebinds, and optionally notifies when memory flush is exhausted", async () => { - await withTempDir({ prefix: "openclaw-direct-runtime-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-direct-runtime-" }, async (tempDir) => { const { replyParams, followupRun } = createDirectRuntimeReplyParams({ shouldFollowup: false, isActive: false, diff --git a/src/auto-reply/reply/agent-runner-memory.test.ts b/src/auto-reply/reply/agent-runner-memory.test.ts index 929b4659f90f..c5b170f7a421 100644 --- a/src/auto-reply/reply/agent-runner-memory.test.ts +++ b/src/auto-reply/reply/agent-runner-memory.test.ts @@ -11,7 +11,7 @@ import type { SessionEntry } from "../../config/sessions.js"; import { loadSessionEntry, readTranscriptStatsSync, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import { replaceTranscriptEvents } from "../../config/sessions/session-accessor.sqlite-transcript-write.js"; import { resolveSessionStorePathForScope } from "../../config/sessions/session-store-path.js"; @@ -138,7 +138,7 @@ async function writeTestSessionTranscript(params: { sessionKey, storePath: path.join(params.rootDir, "sessions.json"), }; - await upsertSessionEntry(scope, { sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId, updatedAt: 10 }); await replaceTranscriptEvents(scope, params.events); } @@ -559,7 +559,7 @@ describe("runMemoryFlushIfNeeded", () => { const storePath = path.join(rootDir, "tainted-owner-session.json"); const sessionKey = "agent:main:main"; const scope = { agentId: "main", sessionId: "session", sessionKey, storePath }; - await upsertSessionEntry(scope, { sessionId: "session", updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: "session", updatedAt: 10 }); await replaceTranscriptEvents(scope, [ { type: "message", @@ -2102,7 +2102,7 @@ describe("runMemoryFlushIfNeeded", () => { totalTokensVersion: 1, compactionCount: 0, }; - await upsertSessionEntry({ agentId: "main", sessionKey, storePath }, sessionEntry); + await upsertSessionEntryCore({ agentId: "main", sessionKey, storePath }, sessionEntry); await runPreflightCompactionIfNeeded({ cfg: { agents: { defaults: { compaction: { memoryFlush: {} } } } }, @@ -3208,7 +3208,7 @@ describe("runMemoryFlushIfNeeded", () => { const storePath = path.join(rootDir, "sqlite-codex-byte-guard.json"); const sessionKey = "agent:main:main"; const scope = { agentId: "main", sessionId: "session", sessionKey, storePath }; - await upsertSessionEntry(scope, { sessionId: "session", updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: "session", updatedAt: 10 }); await replaceTranscriptEvents(scope, [ { message: { role: "user", content: "x".repeat(256) }, type: "message" }, ]); @@ -3268,7 +3268,7 @@ describe("runMemoryFlushIfNeeded", () => { const storePath = path.join(rootDir, "sqlite-codex-under-byte-guard.json"); const sessionKey = "agent:main:main"; const scope = { agentId: "main", sessionId: "session", sessionKey, storePath }; - await upsertSessionEntry(scope, { sessionId: "session", updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: "session", updatedAt: 10 }); await replaceTranscriptEvents(scope, [ { message: { role: "user", content: "small" }, type: "message" }, ]); @@ -3329,7 +3329,7 @@ describe("runMemoryFlushIfNeeded", () => { const storePath = path.join(rootDir, "sqlite-cli-owned-session.json"); const sessionKey = "agent:main:main"; const scope = { agentId: "main", sessionId: "session", sessionKey, storePath }; - await upsertSessionEntry(scope, { sessionId: "session", updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: "session", updatedAt: 10 }); await replaceTranscriptEvents(scope, [ { message: { role: "user", content: "x".repeat(256) }, type: "message" }, ]); @@ -3401,7 +3401,7 @@ describe("runMemoryFlushIfNeeded", () => { const storePath = path.join(rootDir, "sqlite-large-session.json"); const sessionKey = "agent:main:main"; const scope = { agentId: "main", sessionId: "session", sessionKey, storePath }; - await upsertSessionEntry(scope, { sessionId: "session", updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: "session", updatedAt: 10 }); await replaceTranscriptEvents(scope, [ { message: { role: "user", content: "x".repeat(256) }, type: "message" }, ]); @@ -3548,7 +3548,7 @@ describe("runMemoryFlushIfNeeded", () => { const storePath = path.join(rootDir, "sqlite-deep-leaf-session.json"); const sessionKey = "agent:main:deep-leaf"; const scope = { agentId: "main", sessionId: "session", sessionKey, storePath }; - await upsertSessionEntry(scope, { sessionId: "session", updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: "session", updatedAt: 10 }); const activeRoot = { type: "message", id: "active-root", @@ -3623,7 +3623,7 @@ describe("runMemoryFlushIfNeeded", () => { const storePath = path.join(rootDir, "sqlite-force-flush-session.json"); const sessionKey = "agent:main:main"; const scope = { agentId: "main", sessionId: "session", sessionKey, storePath }; - await upsertSessionEntry(scope, { sessionId: "session", updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: "session", updatedAt: 10 }); await replaceTranscriptEvents(scope, [ { message: { role: "user", content: "x".repeat(256) }, type: "message" }, ]); diff --git a/src/auto-reply/reply/agent-runner-memory.ts b/src/auto-reply/reply/agent-runner-memory.ts index bddd58136b5d..deed5d0f91e1 100644 --- a/src/auto-reply/reply/agent-runner-memory.ts +++ b/src/auto-reply/reply/agent-runner-memory.ts @@ -36,7 +36,7 @@ import { import { resolveAgentIdFromSessionKey, resolveFreshSessionTotalTokens, - resolveStorePath, + resolveSessionStorePathCore, SESSION_TOTAL_TOKENS_VERSION, type SessionEntry, } from "../../config/sessions.js"; @@ -727,7 +727,7 @@ export async function runPreflightCompactionIfNeeded(params: { sessionKey: compactionSessionKey, storePath: params.storePath ?? - resolveStorePath(params.cfg.session?.store, { agentId: compactionAgentId }), + resolveSessionStorePathCore(params.cfg.session?.store, { agentId: compactionAgentId }), }); const contextWindowTokens = resolveMemoryFlushContextWindowTokens({ diff --git a/src/auto-reply/reply/agent-runner-result-accounting.test.ts b/src/auto-reply/reply/agent-runner-result-accounting.test.ts index 72095d58e268..b1049757a70f 100644 --- a/src/auto-reply/reply/agent-runner-result-accounting.test.ts +++ b/src/auto-reply/reply/agent-runner-result-accounting.test.ts @@ -47,7 +47,7 @@ vi.mock("../fallback-state.js", () => ({ })); vi.mock("./agent-runner-core.js", () => ({ - resolveConfiguredFallbackModel: () => ({ + resolveFallbackOriginModel: () => ({ provider: "anthropic", model: "claude", }), diff --git a/src/auto-reply/reply/agent-runner-result-accounting.ts b/src/auto-reply/reply/agent-runner-result-accounting.ts index 79d6877d5a92..0163a44e0be6 100644 --- a/src/auto-reply/reply/agent-runner-result-accounting.ts +++ b/src/auto-reply/reply/agent-runner-result-accounting.ts @@ -11,7 +11,7 @@ import { shouldPreserveUserFacingSessionStateForInputProvenance } from "../../se import { resolveFallbackTransition } from "../fallback-state.js"; import { normalizeVerboseLevel } from "../thinking.js"; import type { ReplyPayload } from "../types.js"; -import { resolveConfiguredFallbackModel } from "./agent-runner-core.js"; +import { resolveFallbackOriginModel } from "./agent-runner-core.js"; import type { FinalizeReplyAgentRunInput } from "./agent-runner-result.types.js"; import type { AdmittedFollowupTurn, FollowupRunnerParams } from "./followup-turn-admission.js"; import type { FollowupExecutionResult } from "./followup-turn-execution.js"; @@ -176,7 +176,7 @@ export async function accountAgentTurn(context: AgentTurnAccountingContext) { ); const fallbackStateEntry = activeSessionEntry ?? (sessionKey ? activeSessionStore?.[sessionKey] : undefined); - const configuredFallbackModel = resolveConfiguredFallbackModel({ + const configuredFallbackModel = resolveFallbackOriginModel({ run: followupRun.run, fallbackStateEntry, }); diff --git a/src/auto-reply/reply/block-reply-pipeline.ts b/src/auto-reply/reply/block-reply-pipeline.ts index b5293b8aa542..b360a77164e0 100644 --- a/src/auto-reply/reply/block-reply-pipeline.ts +++ b/src/auto-reply/reply/block-reply-pipeline.ts @@ -5,7 +5,7 @@ import { resolveSendableOutboundReplyParts, } from "openclaw/plugin-sdk/reply-payload"; import { logVerbose } from "../../globals.js"; -import { withTimeout } from "../../node-host/with-timeout.js"; +import { runAbortableTimeout } from "../../node-host/with-timeout.js"; import { getReplyPayloadMetadata, isReplyPayloadStatusNotice } from "../reply-payload.js"; import type { ReplyPayload } from "../types.js"; import { createBlockReplyCoalescer } from "./block-reply-coalescer.js"; @@ -145,7 +145,7 @@ export function createBlockReplyPipeline(params: { if (aborted) { return false; } - await withTimeout( + await runAbortableTimeout( async (signal) => { timeoutSignal = signal; await onBlockReply(payload, { diff --git a/src/auto-reply/reply/commands-compact.runtime.ts b/src/auto-reply/reply/commands-compact.runtime.ts index 064b308e4395..dd257c19c21e 100644 --- a/src/auto-reply/reply/commands-compact.runtime.ts +++ b/src/auto-reply/reply/commands-compact.runtime.ts @@ -7,7 +7,6 @@ export { } from "../../agents/embedded-agent.js"; export { resolveFreshSessionTotalTokens, - resolveSessionFilePath, resolveSessionFilePathOptions, } from "../../config/sessions.js"; export { enqueueSystemEvent } from "../../infra/system-events.js"; diff --git a/src/auto-reply/reply/commands-compact.test.ts b/src/auto-reply/reply/commands-compact.test.ts index 4eec907434e1..18b006891b02 100644 --- a/src/auto-reply/reply/commands-compact.test.ts +++ b/src/auto-reply/reply/commands-compact.test.ts @@ -17,7 +17,6 @@ vi.mock("./commands-compact.runtime.js", () => ({ incrementCompactionCount: vi.fn(), isEmbeddedAgentRunAbortableForCompaction: vi.fn().mockReturnValue(false), resolveFreshSessionTotalTokens: vi.fn(() => 12_345), - resolveSessionFilePath: vi.fn(() => "/tmp/session.json"), resolveSessionFilePathOptions: vi.fn(() => ({})), waitForEmbeddedAgentRunEnd: vi.fn().mockResolvedValue(true), })); diff --git a/src/auto-reply/reply/commands-compact.ts b/src/auto-reply/reply/commands-compact.ts index 773ced135a02..d59944284688 100644 --- a/src/auto-reply/reply/commands-compact.ts +++ b/src/auto-reply/reply/commands-compact.ts @@ -26,7 +26,7 @@ import { resolveSessionRuntimeOverrideForProvider, } from "../../agents/session-runtime-compat.js"; import { resolveSessionAuthProfileOverrideSource } from "../../config/sessions/auth-profile-override-provenance.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { resolveSessionStorePathForScope } from "../../config/sessions/session-store-path.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { logVerbose } from "../../globals.js"; @@ -256,7 +256,8 @@ export const handleCompactCommand: CommandHandler = async (params) => { agentId: sessionAgentId, sessionKey: params.sessionKey, storePath: - params.storePath ?? resolveStorePath(params.cfg.session?.store, { agentId: sessionAgentId }), + params.storePath ?? + resolveSessionStorePathCore(params.cfg.session?.store, { agentId: sessionAgentId }), }); const result = await runtime.compactEmbeddedAgentSession({ abortSignal: params.opts?.abortSignal, diff --git a/src/auto-reply/reply/commands-export-common.ts b/src/auto-reply/reply/commands-export-common.ts index 0ab84e9eea76..f9461a32e8df 100644 --- a/src/auto-reply/reply/commands-export-common.ts +++ b/src/auto-reply/reply/commands-export-common.ts @@ -1,7 +1,7 @@ /** Shared export-command parsing and target session resolution helpers. */ import { resolveDefaultSessionStorePath, - resolveSessionFilePath, + resolveSessionFilePathCore, resolveSessionFilePathOptions, } from "../../config/sessions/paths.js"; import { loadSessionEntryReadOnly } from "../../config/sessions/session-accessor.js"; @@ -64,7 +64,7 @@ export function resolveExportCommandSessionTarget( } try { - const sessionFile = resolveSessionFilePath( + const sessionFile = resolveSessionFilePathCore( sessionId, entry, resolveSessionFilePathOptions({ agentId: targetAgentId, storePath }), diff --git a/src/auto-reply/reply/commands-export-session.test.ts b/src/auto-reply/reply/commands-export-session.test.ts index ec2f2c7df44a..ce76eef6a22b 100644 --- a/src/auto-reply/reply/commands-export-session.test.ts +++ b/src/auto-reply/reply/commands-export-session.test.ts @@ -48,7 +48,7 @@ vi.mock("../../acp/runtime/session-meta.js", () => ({ vi.mock("../../config/sessions/paths.js", () => ({ resolveDefaultSessionStorePath: hoisted.resolveDefaultSessionStorePathMock, - resolveSessionFilePath: hoisted.resolveSessionFilePathMock, + resolveSessionFilePathCore: hoisted.resolveSessionFilePathMock, resolveSessionFilePathOptions: hoisted.resolveSessionFilePathOptionsMock, })); diff --git a/src/auto-reply/reply/commands-login.test.ts b/src/auto-reply/reply/commands-login.test.ts index e5894174948c..ae5e4f0b8adf 100644 --- a/src/auto-reply/reply/commands-login.test.ts +++ b/src/auto-reply/reply/commands-login.test.ts @@ -11,7 +11,7 @@ const runModelsAuthLoginFlowMock = vi.hoisted(() => vi.fn()); const updateSessionEntryMock = vi.hoisted(() => vi.fn()); vi.mock("../../commands/models/auth.js", () => ({ - runModelsAuthLoginFlow: (opts: unknown) => runModelsAuthLoginFlowMock(opts), + runModelsAuthLoginFlowCore: (opts: unknown) => runModelsAuthLoginFlowMock(opts), })); vi.mock("../../config/sessions/session-accessor.js", async () => { const actual = await vi.importActual( diff --git a/src/auto-reply/reply/commands-name.test.ts b/src/auto-reply/reply/commands-name.test.ts index 2ae924206e22..a72bf3ce492c 100644 --- a/src/auto-reply/reply/commands-name.test.ts +++ b/src/auto-reply/reply/commands-name.test.ts @@ -2,7 +2,10 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; -import { loadSessionEntry, upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { + loadSessionEntry, + upsertSessionEntryCore, +} from "../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { buildBuiltinChatCommands } from "../commands-registry.shared.js"; import { takeCommandSessionMetadataChanges } from "./command-session-metadata.js"; @@ -85,7 +88,7 @@ describe("name command", () => { it("renames the current session and persists the label", async () => { const storePath = await createStorePath(); - await upsertSessionEntry( + await upsertSessionEntryCore( { storePath, sessionKey }, { sessionId: "sess-main", updatedAt: 1, totalTokens: 0, totalTokensFresh: true }, ); @@ -104,7 +107,7 @@ describe("name command", () => { it("suggests a name without mutating when no argument is given", async () => { const storePath = await createStorePath(); - await upsertSessionEntry( + await upsertSessionEntryCore( { storePath, sessionKey }, { sessionId: "sess-main", updatedAt: 1, totalTokens: 0, totalTokensFresh: true }, ); @@ -122,11 +125,11 @@ describe("name command", () => { it("rejects a label already used by another session", async () => { const storePath = await createStorePath(); const now = Date.now(); - await upsertSessionEntry( + await upsertSessionEntryCore( { storePath, sessionKey }, { sessionId: "sess-main", updatedAt: now, totalTokens: 0, totalTokensFresh: true }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { storePath, sessionKey: "agent:main:web:other" }, { sessionId: "sess-other", @@ -147,7 +150,7 @@ describe("name command", () => { it("reads the persisted name when params.sessionEntry is absent", async () => { const storePath = await createStorePath(); - await upsertSessionEntry( + await upsertSessionEntryCore( { storePath, sessionKey }, { sessionId: "sess-main", @@ -192,7 +195,7 @@ describe("name command", () => { it("does not rename for an unauthorized sender", async () => { const storePath = await createStorePath(); - await upsertSessionEntry( + await upsertSessionEntryCore( { storePath, sessionKey }, { sessionId: "sess-main", updatedAt: 1, totalTokens: 0, totalTokensFresh: true }, ); diff --git a/src/auto-reply/reply/commands-plugin.ts b/src/auto-reply/reply/commands-plugin.ts index a5eec384fccb..3f5a1f7b9e56 100644 --- a/src/auto-reply/reply/commands-plugin.ts +++ b/src/auto-reply/reply/commands-plugin.ts @@ -8,7 +8,7 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { resolveSessionAgentId } from "../../agents/agent-scope.js"; import { formatSqliteSessionFileMarker } from "../../config/sessions/legacy-sqlite-marker.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { resolveSessionStorePathForScope } from "../../config/sessions/session-store-path.js"; import { createPluginCommandRuntime, @@ -46,7 +46,8 @@ export const handlePluginCommand: CommandHandler = async ( agentId: targetAgentId, sessionKey: params.sessionKey, storePath: - params.storePath ?? resolveStorePath(cfg.session?.store, { agentId: targetAgentId }), + params.storePath ?? + resolveSessionStorePathCore(cfg.session?.store, { agentId: targetAgentId }), }), } : undefined; diff --git a/src/auto-reply/reply/commands-session.ts b/src/auto-reply/reply/commands-session.ts index 8dea53d44d98..8f6df44fc662 100644 --- a/src/auto-reply/reply/commands-session.ts +++ b/src/auto-reply/reply/commands-session.ts @@ -20,7 +20,7 @@ import { formatThreadBindingDurationLabel } from "../../channels/thread-bindings import { parseDurationMs } from "../../cli/parse-duration.js"; import { isRestartEnabled } from "../../config/commands.flags.js"; import { extractDeliveryInfo } from "../../config/sessions.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { resolveSessionStorePathForScope } from "../../config/sessions/session-store-path.js"; import { logVerbose } from "../../globals.js"; import { getSessionBindingService } from "../../infra/outbound/session-binding-service.js"; @@ -314,7 +314,9 @@ export const handleUsageCommand: CommandHandler = defineAuthorizedTextCommand( sessionKey: params.sessionKey, storePath: params.storePath ?? - resolveStorePath(params.cfg.session?.store, { agentId: usageAgentId }), + resolveSessionStorePathCore(params.cfg.session?.store, { + agentId: usageAgentId, + }), }), }, } diff --git a/src/auto-reply/reply/commands-subagents/action-info.ts b/src/auto-reply/reply/commands-subagents/action-info.ts index 4613f04021fc..4a374890e41e 100644 --- a/src/auto-reply/reply/commands-subagents/action-info.ts +++ b/src/auto-reply/reply/commands-subagents/action-info.ts @@ -2,7 +2,7 @@ import { timestampMsToIsoString } from "@openclaw/normalization-core/number-coercion"; import { countPendingDescendantRuns } from "../../../agents/subagents/registry/subagent-registry-read.js"; import { resolveSubagentDisplayStatus } from "../../../agents/subagents/registry/subagent-session-metrics.js"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { loadSessionEntryReadOnly } from "../../../config/sessions/session-accessor.js"; import { formatDurationCompact } from "../../../infra/format-time/format-duration.js"; import { formatTimeAgo } from "../../../infra/format-time/format-relative.ts"; @@ -27,7 +27,7 @@ function formatTimestampWithAge(valueMs?: number) { function loadSubagentSessionEntry(params: SubagentsCommandContext["params"], childKey: string) { const parsed = parseAgentSessionKey(childKey); - const storePath = resolveStorePath(params.cfg.session?.store, { + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId: parsed?.agentId, }); return { diff --git a/src/auto-reply/reply/conversation-turn-capture.test.ts b/src/auto-reply/reply/conversation-turn-capture.test.ts index 5a058b7ef6d5..1342e2e7e951 100644 --- a/src/auto-reply/reply/conversation-turn-capture.test.ts +++ b/src/auto-reply/reply/conversation-turn-capture.test.ts @@ -28,7 +28,7 @@ async function setupReefConversation() { const sessionKey = "agent:main:reef:direct:peer-agent"; const sessionId = "reef-session"; const cfg = { session: { store: storePath } } as OpenClawConfig; - await sessionAccessor.upsertSessionEntry( + await sessionAccessor.upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId, @@ -476,7 +476,7 @@ describe("conversation turn capture", () => { const sessionKey = "agent:main:discord:channel:ops-room:thread:user-context"; const sessionId = "discord-thread-session"; const cfg = { session: { store: storePath } } as OpenClawConfig; - await sessionAccessor.upsertSessionEntry( + await sessionAccessor.upsertSessionEntryCore( { ...scope, sessionKey }, { sessionId, diff --git a/src/auto-reply/reply/conversation-turn-capture.ts b/src/auto-reply/reply/conversation-turn-capture.ts index 631d24e8f3c6..c2afb57c88b6 100644 --- a/src/auto-reply/reply/conversation-turn-capture.ts +++ b/src/auto-reply/reply/conversation-turn-capture.ts @@ -8,7 +8,7 @@ import { markConversationDeliverySent, } from "../../config/sessions/conversation-delivery-store.js"; import { conversationIdentityFromMsgContext } from "../../config/sessions/conversation-identity.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { appendTranscriptEventSync, loadSessionEntryReadOnly, @@ -91,7 +91,7 @@ async function capturePendingConversationTurnReplyUnsafe(params: { : normalizeOptionalString(String(params.ctx.MessageThreadId)); const agentId = normalizeOptionalString(params.ctx.AgentId) ?? resolveAgentIdFromSessionKey(sessionKey); - const storePath = resolveStorePath(params.cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId }); const sessionEntry = loadSessionEntryReadOnly({ agentId, sessionKey, diff --git a/src/auto-reply/reply/current-turn-images.test.ts b/src/auto-reply/reply/current-turn-images.test.ts index c41f3b477709..20771b60d3e6 100644 --- a/src/auto-reply/reply/current-turn-images.test.ts +++ b/src/auto-reply/reply/current-turn-images.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { deleteTestEnvValue, setTestEnvValue } from "../../test-utils/env.js"; import type { MsgContext } from "../templating.js"; import { resolveCurrentTurnImages } from "./current-turn-images.js"; @@ -32,7 +32,7 @@ describe("resolveCurrentTurnImages", () => { }); it("hydrates Telegram-style state-relative media into native prompt images", async () => { - await withTempDir({ prefix: "openclaw-current-turn-images-" }, async (base) => { + await withTestDir({ prefix: "openclaw-current-turn-images-" }, async (base) => { const stateDir = path.join(base, "state"); const cwd = path.join(base, "cwd"); const relativePath = "media/inbound/telegram.jpg"; @@ -91,7 +91,7 @@ describe("resolveCurrentTurnImages", () => { expectedMime: "image/png", }, ])("hydrates $name using the verified byte MIME", async (testCase) => { - await withTempDir({ prefix: "openclaw-current-turn-canonical-kind-" }, async (base) => { + await withTestDir({ prefix: "openclaw-current-turn-canonical-kind-" }, async (base) => { const imagePath = path.join(base, testCase.fileName); await fs.writeFile(imagePath, testCase.imageBytes); @@ -126,7 +126,7 @@ describe("resolveCurrentTurnImages", () => { it.each([undefined, "application/pdf", "application/octet-stream", "image/png"] as const)( "never hydrates valid image bytes when the authoritative document MIME is %s", async (contentType) => { - await withTempDir({ prefix: "openclaw-current-turn-document-image-" }, async (base) => { + await withTestDir({ prefix: "openclaw-current-turn-document-image-" }, async (base) => { const documentPath = path.join(base, "report.png"); await fs.writeFile(documentPath, PNG_IMAGE_BYTES); @@ -146,7 +146,7 @@ describe("resolveCurrentTurnImages", () => { it.each([undefined, "application/octet-stream", "binary/octet-stream"] as const)( "hydrates unknown-kind filename images when MIME %s has no concrete category", async (contentType) => { - await withTempDir({ prefix: "openclaw-current-turn-unknown-image-" }, async (base) => { + await withTestDir({ prefix: "openclaw-current-turn-unknown-image-" }, async (base) => { const imagePath = path.join(base, "upload.png"); await fs.writeFile(imagePath, PNG_IMAGE_BYTES); @@ -172,7 +172,7 @@ describe("resolveCurrentTurnImages", () => { it.each(["application/pdf", "application/zip", "text/plain"] as const)( "never hydrates valid PNG bytes when unknown-kind MIME %s declares a document", async (contentType) => { - await withTempDir({ prefix: "openclaw-current-turn-unknown-document-" }, async (base) => { + await withTestDir({ prefix: "openclaw-current-turn-unknown-document-" }, async (base) => { const documentPath = path.join(base, "report.png"); await fs.writeFile(documentPath, PNG_IMAGE_BYTES); @@ -193,7 +193,7 @@ describe("resolveCurrentTurnImages", () => { { name: "PDF", bytes: PDF_BYTES }, { name: "ZIP", bytes: ZIP_BYTES }, ])("rejects $name bytes despite a spoofed image kind, MIME, and filename", async (testCase) => { - await withTempDir({ prefix: "openclaw-current-turn-spoofed-image-" }, async (base) => { + await withTestDir({ prefix: "openclaw-current-turn-spoofed-image-" }, async (base) => { const imagePath = path.join(base, "spoofed.png"); await fs.writeFile(imagePath, testCase.bytes); @@ -217,7 +217,7 @@ describe("resolveCurrentTurnImages", () => { }); it("hydrates AVIF attachments when transport metadata only declares generic bytes", async () => { - await withTempDir({ prefix: "openclaw-current-turn-avif-" }, async (base) => { + await withTestDir({ prefix: "openclaw-current-turn-avif-" }, async (base) => { const imagePath = path.join(base, "photo.avif"); const imageBytes = Buffer.from("avif-image"); await fs.writeFile(imagePath, imageBytes); @@ -242,7 +242,7 @@ describe("resolveCurrentTurnImages", () => { }); it("does not duplicate a prepared host-staged image during runner hydration", async () => { - await withTempDir({ prefix: "openclaw-current-turn-staged-image-" }, async (base) => { + await withTestDir({ prefix: "openclaw-current-turn-staged-image-" }, async (base) => { const stagingRoot = path.join(base, "media", "inbound", "staged"); const imagePath = path.join(stagingRoot, "photo.png"); const imageBytes = Buffer.from("host-staged-image"); @@ -274,7 +274,7 @@ describe("resolveCurrentTurnImages", () => { }); it("does not let a staging root expose sibling workspace images", async () => { - await withTempDir({ prefix: "openclaw-current-turn-staged-image-" }, async (base) => { + await withTestDir({ prefix: "openclaw-current-turn-staged-image-" }, async (base) => { const stagingRoot = path.join(base, "media", "inbound", "staged"); const rejectedPath = path.join(base, "private.png"); await fs.mkdir(stagingRoot, { recursive: true }); @@ -346,7 +346,7 @@ describe("resolveCurrentTurnImages", () => { }); it("appends extracted PDF page images without dropping current image attachments", async () => { - await withTempDir({ prefix: "openclaw-current-turn-pdf-images-" }, async (base) => { + await withTestDir({ prefix: "openclaw-current-turn-pdf-images-" }, async (base) => { const imagePath = path.join(base, "photo.png"); const imageBytes = Buffer.from("current-photo"); await fs.writeFile(imagePath, imageBytes); @@ -391,7 +391,7 @@ describe("resolveCurrentTurnImages", () => { }); it("orders extracted PDF page images before later current image attachments", async () => { - await withTempDir({ prefix: "openclaw-current-turn-pdf-order-" }, async (base) => { + await withTestDir({ prefix: "openclaw-current-turn-pdf-order-" }, async (base) => { const imagePath = path.join(base, "photo.png"); await fs.writeFile(imagePath, "current-photo"); const pdfPage = { diff --git a/src/auto-reply/reply/dispatch-acp-transcript.runtime.ts b/src/auto-reply/reply/dispatch-acp-transcript.runtime.ts index 351330ab9309..5e999e34343e 100644 --- a/src/auto-reply/reply/dispatch-acp-transcript.runtime.ts +++ b/src/auto-reply/reply/dispatch-acp-transcript.runtime.ts @@ -2,7 +2,7 @@ import { resolveAcpSessionCwd } from "@openclaw/acp-core/runtime/session-identifiers"; import { resolveSessionAgentId } from "../../agents/agent-scope.js"; import { persistAcpTurnTranscript } from "../../agents/command/attempt-execution.js"; -import { resolveStorePath } from "../../config/sessions.js"; +import { resolveSessionStorePathCore } from "../../config/sessions.js"; import { loadSessionEntryReadOnly } from "../../config/sessions/session-accessor.js"; import type { SessionAcpMeta } from "../../config/sessions/types.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; @@ -25,7 +25,7 @@ export async function persistAcpDispatchTranscript(params: { sessionKey: params.sessionKey, config: params.cfg, }); - const storePath = resolveStorePath(params.cfg.session?.store, { + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId: sessionAgentId, }); const sessionEntry = loadSessionEntryReadOnly({ diff --git a/src/auto-reply/reply/dispatch-from-config.abort-and-dedupe.test-utils.ts b/src/auto-reply/reply/dispatch-from-config.abort-and-dedupe.test-utils.ts index bbfed8ff7ea3..83f39640db66 100644 --- a/src/auto-reply/reply/dispatch-from-config.abort-and-dedupe.test-utils.ts +++ b/src/auto-reply/reply/dispatch-from-config.abort-and-dedupe.test-utils.ts @@ -975,7 +975,7 @@ describe("dispatchReplyFromConfig", () => { [sourceStorePath]: { [sourceSessionKey]: sourceEntry }, [targetStorePath]: { [boundSessionKey]: targetEntry }, }; - sessionStoreMocks.resolveStorePath.mockImplementation( + sessionStoreMocks.resolveSessionStorePathCore.mockImplementation( (_configuredPath?: unknown, options?: { agentId?: string }) => options?.agentId === "opencode" ? targetStorePath : sourceStorePath, ); diff --git a/src/auto-reply/reply/dispatch-from-config.acp-abort.test.ts b/src/auto-reply/reply/dispatch-from-config.acp-abort.test.ts index a4967d9e149e..43603277a022 100644 --- a/src/auto-reply/reply/dispatch-from-config.acp-abort.test.ts +++ b/src/auto-reply/reply/dispatch-from-config.acp-abort.test.ts @@ -202,7 +202,9 @@ describe("dispatchReplyFromConfig ACP abort", () => { ); sessionStoreMocks.loadSessionStore.mockReset().mockReturnValue({}); sessionStoreMocks.readSessionEntry.mockReset().mockReturnValue(undefined); - sessionStoreMocks.resolveStorePath.mockReset().mockReturnValue("/tmp/mock-sessions.json"); + sessionStoreMocks.resolveSessionStorePathCore + .mockReset() + .mockReturnValue("/tmp/mock-sessions.json"); sessionStoreMocks.resolveSessionStoreEntry.mockReset().mockReturnValue({ existing: undefined }); acpMocks.listAcpSessionEntries.mockReset().mockResolvedValue([]); acpMocks.readAcpSessionEntry.mockReset().mockReturnValue(null); diff --git a/src/auto-reply/reply/dispatch-from-config.context.ts b/src/auto-reply/reply/dispatch-from-config.context.ts index 5bb71dc15e2f..9104a262ffe9 100644 --- a/src/auto-reply/reply/dispatch-from-config.context.ts +++ b/src/auto-reply/reply/dispatch-from-config.context.ts @@ -9,7 +9,10 @@ import { isAcpSessionKey } from "../../routing/session-key.js"; import { resolveCommandTurnTargetSessionKey } from "../command-turn-context.js"; import type { FinalizedMsgContext } from "../templating.js"; import { resolveConversationBindingContextFromMessage } from "./conversation-binding-input.js"; -import { loadSessionStoreEntry, resolveStorePath } from "./dispatch-from-config.runtime.js"; +import { + loadSessionStoreEntry, + resolveSessionStorePathCore, +} from "./dispatch-from-config.runtime.js"; import type { ReplyOperation } from "./reply-run-registry.js"; import { isSlackDirectRoutedThreadTurn } from "./routed-delivery-thread.js"; @@ -66,7 +69,7 @@ export function resolveSessionStoreLookup( return {}; } const agentId = resolveSessionAgentId({ sessionKey, config: cfg, fallbackAgentId: ctx.AgentId }); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); try { const entry = loadSessionStoreEntry({ agentId, diff --git a/src/auto-reply/reply/dispatch-from-config.harness-defaults.ts b/src/auto-reply/reply/dispatch-from-config.harness-defaults.ts index 0deedd791f25..46939198b5ca 100644 --- a/src/auto-reply/reply/dispatch-from-config.harness-defaults.ts +++ b/src/auto-reply/reply/dispatch-from-config.harness-defaults.ts @@ -20,7 +20,10 @@ import { import { isNativeCommandTurn, resolveCommandTurnContext } from "../command-turn-context.js"; import type { FinalizedMsgContext } from "../templating.js"; import { normalizeVerboseLevel } from "../thinking.js"; -import { loadSessionStoreEntry, resolveStorePath } from "./dispatch-from-config.runtime.js"; +import { + loadSessionStoreEntry, + resolveSessionStorePathCore, +} from "./dispatch-from-config.runtime.js"; import type { DispatchFromConfigParams } from "./dispatch-from-config.types.js"; import { resolveStoredModelOverride } from "./stored-model-override.js"; @@ -163,7 +166,7 @@ function resolveStoredModelCandidate(params: { config: params.cfg, fallbackAgentId: params.sessionAgentId, }); - const storePath = resolveStorePath(params.cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId }); return loadSessionStoreEntry({ agentId, storePath, diff --git a/src/auto-reply/reply/dispatch-from-config.reply-dispatch.test.ts b/src/auto-reply/reply/dispatch-from-config.reply-dispatch.test.ts index 462d7cd57026..04167499d353 100644 --- a/src/auto-reply/reply/dispatch-from-config.reply-dispatch.test.ts +++ b/src/auto-reply/reply/dispatch-from-config.reply-dispatch.test.ts @@ -152,7 +152,9 @@ describe("dispatchReplyFromConfig reply_dispatch hook", () => { sessionStoreMocks.readSessionEntry .mockReset() .mockImplementation(() => sessionStoreMocks.currentEntry); - sessionStoreMocks.resolveStorePath.mockReset().mockReturnValue("/tmp/mock-sessions.json"); + sessionStoreMocks.resolveSessionStorePathCore + .mockReset() + .mockReturnValue("/tmp/mock-sessions.json"); sessionStoreMocks.resolveSessionStoreEntry.mockReset().mockReturnValue({ existing: undefined }); sessionStoreMocks.updateSessionEntry.mockClear(); acpManagerRuntimeMocks.getAcpSessionManager.mockReset(); diff --git a/src/auto-reply/reply/dispatch-from-config.runtime.ts b/src/auto-reply/reply/dispatch-from-config.runtime.ts index 6380dd280d7e..20d64cbfe963 100644 --- a/src/auto-reply/reply/dispatch-from-config.runtime.ts +++ b/src/auto-reply/reply/dispatch-from-config.runtime.ts @@ -3,7 +3,7 @@ import { loadSessionEntryReadOnly } from "../../config/sessions/session-accessor.js"; import type { SessionEntry } from "../../config/sessions/types.js"; -export { resolveStorePath } from "../../config/sessions/paths.js"; +export { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; export { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js"; export function loadSessionStoreEntry(params: { diff --git a/src/auto-reply/reply/dispatch-from-config.shared.test-harness.ts b/src/auto-reply/reply/dispatch-from-config.shared.test-harness.ts index 0c53f24b258d..1e3d7e5c3bb5 100644 --- a/src/auto-reply/reply/dispatch-from-config.shared.test-harness.ts +++ b/src/auto-reply/reply/dispatch-from-config.shared.test-harness.ts @@ -156,7 +156,7 @@ const sessionStoreMocks = vi.hoisted(() => ({ loadSessionStoreEntry: vi.fn((..._args: unknown[]) => sessionStoreMocks.currentEntry), loadSessionStore: vi.fn(() => ({})), readSessionEntry: vi.fn(() => sessionStoreMocks.currentEntry), - resolveStorePath: vi.fn(() => "/tmp/mock-sessions.json"), + resolveSessionStorePathCore: vi.fn(() => "/tmp/mock-sessions.json"), resolveSessionStoreEntry: vi.fn( (params: { store: Record>; @@ -523,7 +523,7 @@ vi.mock("./dispatch-from-config.runtime.js", () => ({ loadSessionStore: sessionStoreMocks.loadSessionStore, readSessionEntry: sessionStoreMocks.readSessionEntry, resolveSessionStoreEntry: sessionStoreMocks.resolveSessionStoreEntry, - resolveStorePath: sessionStoreMocks.resolveStorePath, + resolveSessionStorePathCore: sessionStoreMocks.resolveSessionStorePathCore, triggerInternalHook: internalHookMocks.triggerInternalHook, updateSessionStoreEntry: sessionStoreMocks.updateSessionStoreEntry, })); diff --git a/src/auto-reply/reply/dispatch-from-config.test-harness.ts b/src/auto-reply/reply/dispatch-from-config.test-harness.ts index 5d33dfe0471a..6d9bbba97a9f 100644 --- a/src/auto-reply/reply/dispatch-from-config.test-harness.ts +++ b/src/auto-reply/reply/dispatch-from-config.test-harness.ts @@ -564,8 +564,8 @@ export const describe0BeforeEach0 = () => { sessionStoreMocks.loadSessionStore.mockReturnValue({}); sessionStoreMocks.readSessionEntry.mockReset(); sessionStoreMocks.readSessionEntry.mockImplementation(() => sessionStoreMocks.currentEntry); - sessionStoreMocks.resolveStorePath.mockReset(); - sessionStoreMocks.resolveStorePath.mockReturnValue("/tmp/mock-sessions.json"); + sessionStoreMocks.resolveSessionStorePathCore.mockReset(); + sessionStoreMocks.resolveSessionStorePathCore.mockReturnValue("/tmp/mock-sessions.json"); sessionStoreMocks.resolveSessionStoreEntry.mockReset(); sessionStoreMocks.resolveSessionStoreEntry.mockImplementation( (params: { store: Record>; sessionKey: string }) => ({ diff --git a/src/auto-reply/reply/get-reply-fast-path.ts b/src/auto-reply/reply/get-reply-fast-path.ts index 4f779243b24f..9c0711773c65 100644 --- a/src/auto-reply/reply/get-reply-fast-path.ts +++ b/src/auto-reply/reply/get-reply-fast-path.ts @@ -7,7 +7,7 @@ import { import { normalizeChatType } from "../../channels/chat-type.js"; import { normalizeAnyChannelId } from "../../channels/registry.js"; import { applyMergePatch } from "../../config/merge-patch.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { resolveResetPreservedSelection } from "../../config/sessions/reset-preserved-selection.js"; import { loadSessionEntry, @@ -190,7 +190,7 @@ export function initFastReplySessionState(params: { mainKey: cfg.session?.mainKey, agentId, }); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); const sessionStore: Record = Object.fromEntries( listSessionEntriesCore({ storePath }).map(({ sessionKey: entryKey, entry }) => [ entryKey, diff --git a/src/auto-reply/reply/get-reply-run-admission.ts b/src/auto-reply/reply/get-reply-run-admission.ts index a6cc87d4029e..9786252ec407 100644 --- a/src/auto-reply/reply/get-reply-run-admission.ts +++ b/src/auto-reply/reply/get-reply-run-admission.ts @@ -8,7 +8,7 @@ import { hasResolvedThinkingCatalogEntry } from "../../agents/thinking-runtime.j import { resolveSessionAuthProfileOverrideSource } from "../../config/sessions/auth-profile-override-provenance.js"; import { formatSqliteSessionFileMarker } from "../../config/sessions/legacy-sqlite-marker.js"; import { - resolveSessionFilePath, + resolveSessionFilePathCore, resolveSessionFilePathOptions, } from "../../config/sessions/paths.js"; import { loadSessionEntry } from "../../config/sessions/session-accessor.js"; @@ -320,7 +320,7 @@ export async function prepareReplyRunAdmission(context: PreparedReplyRunContext) opts?.onSessionPrepared?.({ sessionKey, sessionId: latestSessionId, storePath }); const sessionFile = storePath ? formatSqliteSessionFileMarker({ agentId, sessionId: latestSessionId, storePath }) - : resolveSessionFilePath(latestSessionId, latestSessionEntry, sessionFilePathOptions); + : resolveSessionFilePathCore(latestSessionId, latestSessionEntry, sessionFilePathOptions); return { sessionEntry: latestSessionEntry, sessionId: latestSessionId, sessionFile }; }; let preparedSessionState = resolvePreparedSessionState(); diff --git a/src/auto-reply/reply/get-reply-run.media-only.test.ts b/src/auto-reply/reply/get-reply-run.media-only.test.ts index 920c894ca4ad..4b2cd142b1e9 100644 --- a/src/auto-reply/reply/get-reply-run.media-only.test.ts +++ b/src/auto-reply/reply/get-reply-run.media-only.test.ts @@ -66,7 +66,7 @@ vi.mock("../../config/sessions/group.js", () => ({ })); vi.mock("../../config/sessions/paths.js", () => ({ - resolveSessionFilePath: vi.fn().mockReturnValue("/tmp/session.jsonl"), + resolveSessionFilePathCore: vi.fn().mockReturnValue("/tmp/session.jsonl"), resolveSessionFilePathOptions: vi.fn().mockReturnValue({}), })); diff --git a/src/auto-reply/reply/group-id.ts b/src/auto-reply/reply/group-id.ts index d5aa12d7fd03..af90e8a42074 100644 --- a/src/auto-reply/reply/group-id.ts +++ b/src/auto-reply/reply/group-id.ts @@ -8,7 +8,7 @@ import { getLoadedChannelPluginForRead } from "../../channels/plugins/registry-l import type { ChannelMessagingAdapter } from "../../channels/plugins/types.public.js"; import { normalizeAnyChannelId } from "../../channels/registry.js"; import { - stripTargetKindPrefix, + stripOutboundTargetKindPrefix, stripTargetProviderPrefix, stripTargetTopicSuffix, } from "../../infra/outbound/channel-target-prefix.js"; @@ -29,7 +29,7 @@ function extractInferredGroupTargetId(params: { continue; } const target = stripTargetTopicSuffix( - stripTargetKindPrefix(stripTargetProviderPrefix(candidate, params.channelId), [ + stripOutboundTargetKindPrefix(stripTargetProviderPrefix(candidate, params.channelId), [ "group", "channel", "conversation", @@ -55,7 +55,7 @@ function extractLegacyParsedGroupTargetId(params: { return undefined; } const target = stripTargetTopicSuffix( - stripTargetKindPrefix(stripTargetProviderPrefix(parsed.to, params.channelId), [ + stripOutboundTargetKindPrefix(stripTargetProviderPrefix(parsed.to, params.channelId), [ "group", "channel", "conversation", diff --git a/src/auto-reply/reply/queue/drain.ts b/src/auto-reply/reply/queue/drain.ts index c67225e67c3b..df971bf0b9eb 100644 --- a/src/auto-reply/reply/queue/drain.ts +++ b/src/auto-reply/reply/queue/drain.ts @@ -4,7 +4,7 @@ import { stableStringify } from "@openclaw/normalization-core"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { runAgentHarnessBeforeMessageWriteHook } from "../../../agents/harness/hook-helpers.js"; import { normalizeChatType } from "../../../channels/chat-type.js"; -import { resolveStorePath } from "../../../config/sessions.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions.js"; import { loadSessionEntryReadOnly } from "../../../config/sessions/session-accessor.js"; // Drains queued follow-up runs while preserving route and session identity. import { @@ -348,7 +348,7 @@ function buildCollectTranscriptPrompt(items: FollowupRun[]): string { function resolveFollowupTranscriptTarget(source: FollowupRun) { const sessionKey = normalizeOptionalString(source.run.sessionKey) ?? source.run.sessionId; - const storePath = resolveStorePath(source.run.config.session?.store, { + const storePath = resolveSessionStorePathCore(source.run.config.session?.store, { agentId: source.run.agentId, }); const sessionEntry = loadSessionEntryReadOnly({ diff --git a/src/auto-reply/reply/reply-state.test.ts b/src/auto-reply/reply/reply-state.test.ts index bdf53d64f836..f335338e9ad5 100644 --- a/src/auto-reply/reply/reply-state.test.ts +++ b/src/auto-reply/reply/reply-state.test.ts @@ -5,7 +5,10 @@ import path from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; import { afterEach, describe, expect, it } from "vitest"; import type { SessionEntry } from "../../config/sessions.js"; -import { loadSessionEntry, upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { + loadSessionEntry, + upsertSessionEntryCore, +} from "../../config/sessions/session-accessor.js"; import { resolveSqliteTargetFromSessionStorePath } from "../../config/sessions/session-sqlite-target.js"; import { resolveSessionStorePathForScope } from "../../config/sessions/session-store-path.js"; import { @@ -41,7 +44,7 @@ async function seedSessionStore(params: { entry: SessionEntry | Record; }) { await fs.mkdir(path.dirname(params.storePath), { recursive: true }); - await upsertSessionEntry( + await upsertSessionEntryCore( { storePath: params.storePath, sessionKey: params.sessionKey }, params.entry as Partial, ); diff --git a/src/auto-reply/reply/route-reply.ts b/src/auto-reply/reply/route-reply.ts index 9621f66d5dde..c6294f7f6e2d 100644 --- a/src/auto-reply/reply/route-reply.ts +++ b/src/auto-reply/reply/route-reply.ts @@ -315,7 +315,7 @@ export async function routeReply(params: RouteReplyParams): Promise ({ vi.mock("../../config/sessions.js", () => ({ updateSessionStore: vi.fn(), - resolveSessionFilePath: vi.fn(), + resolveSessionFilePathCore: vi.fn(), resolveSessionFilePathOptions: vi.fn(), })); diff --git a/src/auto-reply/reply/session.ts b/src/auto-reply/reply/session.ts index 08386f1bac10..a054ff349d10 100644 --- a/src/auto-reply/reply/session.ts +++ b/src/auto-reply/reply/session.ts @@ -19,7 +19,7 @@ import { } from "../../config/sessions/lifecycle.js"; import { canonicalizeMainSessionAlias } from "../../config/sessions/main-session.js"; import { deriveSessionMetaPatch } from "../../config/sessions/metadata.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { resolveResetPreservedSelection } from "../../config/sessions/reset-preserved-selection.js"; import { evaluateSessionFreshness, @@ -319,7 +319,7 @@ function resolveInitSessionStateAttemptContext( storePath: resolveSessionStorePathForScope({ agentId, sessionKey: sessionCtxForState.SessionKey, - storePath: resolveStorePath(cfg.session?.store, { agentId }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId }), }), }; } diff --git a/src/auto-reply/reply/test/session.test-support.ts b/src/auto-reply/reply/test/session.test-support.ts index 2a4eaa5997a9..bcacf4ac49df 100644 --- a/src/auto-reply/reply/test/session.test-support.ts +++ b/src/auto-reply/reply/test/session.test-support.ts @@ -7,7 +7,7 @@ import { listSessionEntriesCore, loadSessionEntry, replaceSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../../config/sessions/session-accessor.js"; import { normalizeLegacySessionEntryDelivery } from "../../../infra/state-migrations.legacy-session-store.js"; import { projectSessionDeliveryFields } from "../../../utils/delivery-context.shared.js"; @@ -45,7 +45,7 @@ export async function writeSessionStore( if (typeof patch.sessionId === "string" && patch.sessionId.trim()) { await replaceSessionEntry({ storePath, sessionKey }, canonical); } else { - await upsertSessionEntry({ storePath, sessionKey }, canonical); + await upsertSessionEntryCore({ storePath, sessionKey }, canonical); } } } diff --git a/src/auto-reply/status.test.ts b/src/auto-reply/status.test.ts index d42ff89c8c6d..3a7b3edf82b8 100644 --- a/src/auto-reply/status.test.ts +++ b/src/auto-reply/status.test.ts @@ -5,7 +5,7 @@ import { normalizeTestText } from "../../test/helpers/normalize-text.js"; import { testing as cliBackendsTesting } from "../agents/cli-backends.test-support.js"; import { getContextWindowCaches, providerContextTokenCacheKey } from "../agents/context-cache.js"; import type { OpenClawConfig } from "../config/config.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { appendTranscriptMessageSync, replaceSessionEntrySync, @@ -1843,7 +1843,7 @@ describe("buildStatusMessage", () => { agentId: params.agentId, sessionId: params.sessionId, sessionKey: `agent:${params.agentId}:main`, - storePath: resolveStorePath(undefined, { agentId: params.agentId }), + storePath: resolveSessionStorePathCore(undefined, { agentId: params.agentId }), }; replaceSessionEntrySync(scope, { sessionId: params.sessionId, updatedAt: Date.now() }); appendTranscriptMessageSync(scope, { @@ -2076,7 +2076,7 @@ describe("buildStatusMessage", () => { agentId: "main", sessionId, sessionKey: "agent:main:main", - storePath: resolveStorePath(undefined, { agentId: "main" }), + storePath: resolveSessionStorePathCore(undefined, { agentId: "main" }), }, { message: { diff --git a/src/auto-reply/thinking.test.ts b/src/auto-reply/thinking.test.ts index 24a49a617ead..32fa5d7280b0 100644 --- a/src/auto-reply/thinking.test.ts +++ b/src/auto-reply/thinking.test.ts @@ -6,7 +6,7 @@ const providerRuntimeMocks = vi.hoisted(() => ({ })); vi.mock("../plugins/provider-thinking.js", () => ({ - resolveProviderThinkingProfile: providerRuntimeMocks.resolveProviderThinkingProfile, + resolveEffectiveThinkingProfile: providerRuntimeMocks.resolveProviderThinkingProfile, })); const { diff --git a/src/auto-reply/thinking.ts b/src/auto-reply/thinking.ts index 179ffa35e9e6..b85f0d23822b 100644 --- a/src/auto-reply/thinking.ts +++ b/src/auto-reply/thinking.ts @@ -33,7 +33,7 @@ import { normalizeOptionalLowercaseString, normalizeOptionalString, } from "@openclaw/normalization-core/string-coerce"; -import { resolveProviderThinkingProfile } from "../plugins/provider-thinking.js"; +import { resolveEffectiveThinkingProfile } from "../plugins/provider-thinking.js"; import type { ProviderThinkingProfile } from "../plugins/provider-thinking.types.js"; /** UI-facing thinking level option. */ @@ -203,10 +203,10 @@ export function resolveThinkingProfile(params: { }; const providerProfile = params.providerPolicySource === "active" - ? resolveProviderThinkingProfile(providerProfileParams, { + ? resolveEffectiveThinkingProfile(providerProfileParams, { allowPublicArtifactFallback: false, }) - : resolveProviderThinkingProfile(providerProfileParams); + : resolveEffectiveThinkingProfile(providerProfileParams); // Any anthropic-messages catalog row routes through the canonical Claude // resolver: Claude families get the proper profile (incl. xhigh/adaptive/max); // non-Claude models on the anthropic-messages transport collapse to the Claude diff --git a/src/auto-reply/usage-bar/template.windows.test.ts b/src/auto-reply/usage-bar/template.windows.test.ts index 95ddc8ae3597..a738d3871114 100644 --- a/src/auto-reply/usage-bar/template.windows.test.ts +++ b/src/auto-reply/usage-bar/template.windows.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { resolveResponseUsageLine } from "../reply/agent-runner-usage-line.js"; import { clearUsageBarTemplateCacheForTest } from "./template.test-support.js"; @@ -23,7 +23,7 @@ describe.runIf(process.platform === "win32")("usage footer Windows home paths", }); it("renders a custom footer loaded through a backslash home prefix", async () => { - await withTempDir({ prefix: "openclaw-usage-footer-home-" }, async (home) => { + await withTestDir({ prefix: "openclaw-usage-footer-home-" }, async (home) => { homeState.home = home; const fileName = "usage café footer.json"; await fs.writeFile( diff --git a/src/channels/conversation-resolution.ts b/src/channels/conversation-resolution.ts index 098f23dbb0e0..b02c8ed471d1 100644 --- a/src/channels/conversation-resolution.ts +++ b/src/channels/conversation-resolution.ts @@ -10,7 +10,7 @@ import { import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveTargetPrefixedChannel, - stripTargetKindPrefix, + stripOutboundTargetKindPrefix, stripTargetProviderPrefix, stripTargetTopicSuffix, } from "../infra/outbound/channel-target-prefix.js"; @@ -189,7 +189,7 @@ function resolveFallbackConversationTargetId(params: { if (!target) { return undefined; } - const withoutKind = stripTargetKindPrefix(target); + const withoutKind = stripOutboundTargetKindPrefix(target); const withoutTopic = params.preserveExplicitTopicSuffix && /:topic:/iu.test(withoutKind) ? withoutKind diff --git a/src/channels/feedback-reflection.test.ts b/src/channels/feedback-reflection.test.ts index c301750d1522..5b1ccc255a0f 100644 --- a/src/channels/feedback-reflection.test.ts +++ b/src/channels/feedback-reflection.test.ts @@ -8,7 +8,9 @@ const loadSessionEntry = vi.hoisted(() => vi.fn()); const readSessionUpdatedAtCore = vi.hoisted(() => vi.fn()); const resolveStorePath = vi.hoisted(() => vi.fn(() => "/state/main/sessions.json")); -vi.mock("../config/sessions/paths.js", () => ({ resolveStorePath })); +vi.mock("../config/sessions/paths.js", () => ({ + resolveSessionStorePathCore: resolveStorePath, +})); vi.mock("../config/sessions/session-accessor.js", () => ({ appendTranscriptEvent, loadSessionEntry, diff --git a/src/channels/feedback-reflection.ts b/src/channels/feedback-reflection.ts index eec175fcf4b2..85a2d7460c77 100644 --- a/src/channels/feedback-reflection.ts +++ b/src/channels/feedback-reflection.ts @@ -1,5 +1,5 @@ import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { appendTranscriptEvent, loadSessionEntryReadOnly, @@ -20,7 +20,9 @@ export async function recordChannelFeedbackEvent(params: { sessionKey: string; event: Parameters[1]; }): Promise { - const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { + agentId: params.agentId, + }); const entry = loadSessionEntryReadOnly({ agentId: params.agentId, sessionKey: params.sessionKey, @@ -185,7 +187,7 @@ export async function runChannelFeedbackReflection(params: { return { status: "complete", learning: parsed.learning, - storePath: resolveStorePath(params.cfg.session?.store, { agentId: params.agentId }), + storePath: resolveSessionStorePathCore(params.cfg.session?.store, { agentId: params.agentId }), followUp: parsed.followUp, userMessage: parsed.userMessage, responseLength: response.trim().length, diff --git a/src/channels/inbound-event/envelope.test.ts b/src/channels/inbound-event/envelope.test.ts index cde21886af95..913ab3a5ebbf 100644 --- a/src/channels/inbound-event/envelope.test.ts +++ b/src/channels/inbound-event/envelope.test.ts @@ -15,7 +15,9 @@ const resolveAgentRoute = vi.hoisted(() => })), ); -vi.mock("../../config/sessions/paths.js", () => ({ resolveStorePath })); +vi.mock("../../config/sessions/paths.js", () => ({ + resolveSessionStorePathCore: resolveStorePath, +})); vi.mock("../../config/sessions/session-accessor.js", () => ({ readSessionUpdatedAtCore })); vi.mock("../../routing/resolve-route.js", () => ({ resolveAgentRoute })); diff --git a/src/channels/inbound-event/envelope.ts b/src/channels/inbound-event/envelope.ts index 3033efa30c12..b9386473ed15 100644 --- a/src/channels/inbound-event/envelope.ts +++ b/src/channels/inbound-event/envelope.ts @@ -3,7 +3,7 @@ import { resolveEnvelopeFormatOptions, type AgentEnvelopeParams, } from "../../auto-reply/envelope.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { readSessionUpdatedAtCore } from "../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { @@ -20,7 +20,7 @@ export function createChannelInboundEnvelopeBuilder(params: { cfg: OpenClawConfig; route: Pick; }) { - const storePath = resolveStorePath(params.cfg.session?.store, { + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId: params.route.agentId, }); const envelope = resolveEnvelopeFormatOptions(params.cfg); diff --git a/src/channels/message/runtime.ts b/src/channels/message/runtime.ts index b3f7606aed30..8dd95a2f6081 100644 --- a/src/channels/message/runtime.ts +++ b/src/channels/message/runtime.ts @@ -1,9 +1,9 @@ // Runtime-only barrel for durable message send helpers. Kept separate from the public message // contract barrel so hot imports can choose delivery runtime without pulling every type export. export { - sendDurableMessageBatch, + sendDurableMessageBatchCore, serializeDurableMessagePayloadOutcomes, - withDurableMessageSendContext, + withDurableMessageSendContextCore, } from "./send.js"; export type { DurableMessageBatchSendParams, diff --git a/src/channels/message/send.test.ts b/src/channels/message/send.test.ts index cbc5e8e323f8..8fcf36679487 100644 --- a/src/channels/message/send.test.ts +++ b/src/channels/message/send.test.ts @@ -13,9 +13,9 @@ vi.mock("../../infra/outbound/deliver.js", () => ({ })); import { - sendDurableMessageBatch, + sendDurableMessageBatchCore as sendDurableMessageBatch, type DurableMessageBatchSendResult, - withDurableMessageSendContext, + withDurableMessageSendContextCore as withDurableMessageSendContext, } from "./send.js"; import type { DurableMessageSendIntent } from "./types.js"; diff --git a/src/channels/message/send.ts b/src/channels/message/send.ts index efb8a57a71ec..f50746ccddcd 100644 --- a/src/channels/message/send.ts +++ b/src/channels/message/send.ts @@ -209,7 +209,7 @@ export type DurableMessageSendContext = MessageSendContext< DurableMessageBatchSendResult >; -export async function withDurableMessageSendContext( +export async function withDurableMessageSendContextCore( params: DurableMessageSendContextParams, run: (ctx: DurableMessageSendContext) => Promise, ): Promise { @@ -393,7 +393,7 @@ export async function withDurableMessageSendContext( } } -export async function sendDurableMessageBatch( +export async function sendDurableMessageBatchCore( params: DurableMessageSendContextParams, ): Promise { const pendingFinalCompletion = params.deliveryCompletion @@ -406,7 +406,7 @@ export async function sendDurableMessageBatch( durability: "required" as const, } : {}; - return await withDurableMessageSendContext( + return await withDurableMessageSendContextCore( { ...params, ...pendingFinalDelivery }, async (ctx) => { const rendered = await ctx.render(); diff --git a/src/channels/session-envelope.test.ts b/src/channels/session-envelope.test.ts index 222b72f9b41e..e6c307c2bde1 100644 --- a/src/channels/session-envelope.test.ts +++ b/src/channels/session-envelope.test.ts @@ -2,14 +2,14 @@ import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { replaceSessionEntry } from "../config/sessions/session-accessor.js"; import { closeOpenClawAgentDatabasesForTest } from "../state/openclaw-agent-db.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { resolveInboundSessionEnvelopeContext } from "./session-envelope.js"; describe("resolveInboundSessionEnvelopeContext", () => { afterEach(() => closeOpenClawAgentDatabasesForTest()); it("reads the previous timestamp from SQLite without a sessions.json file", async () => { - await withTempDir({ prefix: "openclaw-session-envelope-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-envelope-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:telegram:dm:1"; await replaceSessionEntry( diff --git a/src/channels/session-envelope.ts b/src/channels/session-envelope.ts index 9cefebb6dcbc..c4febf5329ae 100644 --- a/src/channels/session-envelope.ts +++ b/src/channels/session-envelope.ts @@ -1,6 +1,6 @@ // Session-envelope context resolver for inbound channel turns. import { resolveEnvelopeFormatOptions } from "../auto-reply/envelope.js"; -import { resolveStorePath } from "../config/sessions.js"; +import { resolveSessionStorePathCore } from "../config/sessions.js"; import { readSessionUpdatedAtCore } from "../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; @@ -10,7 +10,7 @@ export function resolveInboundSessionEnvelopeContext(params: { agentId: string; sessionKey: string; }) { - const storePath = resolveStorePath(params.cfg.session?.store, { + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId: params.agentId, }); return { diff --git a/src/channels/session-meta.ts b/src/channels/session-meta.ts index 2e91a8fe3561..22225637d34f 100644 --- a/src/channels/session-meta.ts +++ b/src/channels/session-meta.ts @@ -19,7 +19,7 @@ export async function recordInboundSessionMetaSafe(params: { onError?: (error: unknown) => void; }): Promise { const runtime = await loadInboundSessionRuntime(); - const storePath = runtime.resolveStorePath(params.cfg.session?.store, { + const storePath = runtime.resolveSessionStorePathCore(params.cfg.session?.store, { agentId: params.agentId, }); try { diff --git a/src/channels/turn/durable-delivery.test.ts b/src/channels/turn/durable-delivery.test.ts index 10c9f8673f43..4ce770ab22c1 100644 --- a/src/channels/turn/durable-delivery.test.ts +++ b/src/channels/turn/durable-delivery.test.ts @@ -18,7 +18,7 @@ vi.mock("../message/send.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - sendDurableMessageBatch: mocks.sendDurableMessageBatch, + sendDurableMessageBatchCore: mocks.sendDurableMessageBatch, }; }); diff --git a/src/channels/turn/durable-delivery.ts b/src/channels/turn/durable-delivery.ts index 07ab92570ce9..50d842116981 100644 --- a/src/channels/turn/durable-delivery.ts +++ b/src/channels/turn/durable-delivery.ts @@ -13,7 +13,7 @@ import { } from "../../infra/outbound/deliver.js"; import { buildOutboundSessionContext } from "../../infra/outbound/session-context.js"; import { deriveDurableFinalDeliveryRequirements } from "../message/capabilities.js"; -import { sendDurableMessageBatch } from "../message/send.js"; +import { sendDurableMessageBatchCore } from "../message/send.js"; import { createChannelDeliveryResultFromReceipt } from "./delivery-result.js"; import type { ChannelDeliveryInfo, ChannelDeliveryResult } from "./types.js"; @@ -99,7 +99,7 @@ function toDeliveryIntent(intent: OutboundDeliveryIntent): ChannelDeliveryResult } function resolveDurableSuppression( - send: Extract>, { status: "suppressed" }>, + send: Extract>, { status: "suppressed" }>, ): NonNullable { const hookEffect = send.payloadOutcomes?.find( (outcome) => outcome.status === "suppressed", @@ -204,7 +204,7 @@ export async function deliverInboundReplyWithMessageSendContextCore( requesterSenderUsername: params.ctxPayload.SenderUsername, requesterSenderE164: params.ctxPayload.SenderE164, }); - const send = await sendDurableMessageBatch({ + const send = await sendDurableMessageBatchCore({ cfg: params.cfg, channel, to, diff --git a/src/channels/turn/lifecycle.ts b/src/channels/turn/lifecycle.ts index 548146516481..3973298077a1 100644 --- a/src/channels/turn/lifecycle.ts +++ b/src/channels/turn/lifecycle.ts @@ -4,7 +4,7 @@ import { suppressPendingFinalDelivery } from "../../auto-reply/reply/dispatch-fr import type { DispatchFromConfigResult } from "../../auto-reply/reply/dispatch-from-config.types.js"; import type { ReplyDispatchKind } from "../../auto-reply/reply/reply-dispatcher.types.js"; import { runWithSessionInitConflictRetry } from "../../auto-reply/reply/session-init-conflict-retry.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { deriveInboundMessageHookContext, resolveInboundReplyHookTarget, @@ -91,7 +91,7 @@ export function assembleResolvedChannelTurn< ...turn, ctxPayload: route.dmScope ? { ...turn.ctxPayload, DmScope: route.dmScope } : turn.ctxPayload, routeSessionKey: route.sessionKey, - storePath: resolveStorePath(cfg.session?.store, { agentId: route.agentId }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: route.agentId }), recordInboundSession, }; } @@ -102,7 +102,7 @@ export function assembleResolvedChannelTurn< cfg, agentId: route.agentId, routeSessionKey: route.sessionKey, - storePath: resolveStorePath(cfg.session?.store, { agentId: route.agentId }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: route.agentId }), recordInboundSession, }; return assembled; diff --git a/src/channels/turn/run-channel-turn.delivery.test.ts b/src/channels/turn/run-channel-turn.delivery.test.ts index 959870f2906c..a382d5c21a56 100644 --- a/src/channels/turn/run-channel-turn.delivery.test.ts +++ b/src/channels/turn/run-channel-turn.delivery.test.ts @@ -62,7 +62,7 @@ vi.mock("../message/send.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - sendDurableMessageBatch, + sendDurableMessageBatchCore: sendDurableMessageBatch, }; }); diff --git a/src/channels/turn/run-channel-turn.finalize.test.ts b/src/channels/turn/run-channel-turn.finalize.test.ts index fcd31bd501a6..75c7458497f8 100644 --- a/src/channels/turn/run-channel-turn.finalize.test.ts +++ b/src/channels/turn/run-channel-turn.finalize.test.ts @@ -62,7 +62,7 @@ vi.mock("../message/send.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - sendDurableMessageBatch, + sendDurableMessageBatchCore: sendDurableMessageBatch, }; }); diff --git a/src/channels/turn/run-channel-turn.pipeline.test.ts b/src/channels/turn/run-channel-turn.pipeline.test.ts index 84965fb04814..0550fb2e9dc2 100644 --- a/src/channels/turn/run-channel-turn.pipeline.test.ts +++ b/src/channels/turn/run-channel-turn.pipeline.test.ts @@ -69,7 +69,7 @@ vi.mock("../message/send.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - sendDurableMessageBatch, + sendDurableMessageBatchCore: sendDurableMessageBatch, }; }); diff --git a/src/cli/capability-cli.test.ts b/src/cli/capability-cli.test.ts index 9861651f3581..724296e4872f 100644 --- a/src/cli/capability-cli.test.ts +++ b/src/cli/capability-cli.test.ts @@ -23,7 +23,7 @@ function runCapability(domain: string, action: string, ...argv: string[]): Promi } function primeOpenAiAuthProfile(mode: "api-key" | "token" = "api-key"): void { - mocks.resolveApiKeyForProvider.mockResolvedValueOnce({ + mocks.resolveApiKeyForProviderCore.mockResolvedValueOnce({ apiKey: mode === "token" ? "profile-openai-token" : "profile-openai-key", source: mode === "token" ? "profile:openai:token" : "profile:openai:qa", mode, @@ -48,7 +48,7 @@ const mocks = vi.hoisted(() => ({ setRuntimeConfigSnapshot: vi.fn(), loadAuthProfileStoreForRuntime: vi.fn(() => ({ profiles: {}, order: {} })), listProfilesForProvider: vi.fn(() => []), - resolveApiKeyForProvider: vi.fn(), + resolveApiKeyForProviderCore: vi.fn(), loadManifestMetadataSnapshot: vi.fn(() => ({ manifestRegistry: { plugins: [] } })), planEffectiveModelCatalogRows: vi.fn< typeof import("../model-catalog/index.js").planEffectiveModelCatalogRows @@ -304,8 +304,8 @@ vi.mock("../agents/auth-profiles.js", () => ({ })); vi.mock("../agents/model-auth.js", () => ({ - resolveApiKeyForProvider: - mocks.resolveApiKeyForProvider as typeof import("../agents/model-auth.js").resolveApiKeyForProvider, + resolveApiKeyForProviderCore: + mocks.resolveApiKeyForProviderCore as typeof import("../agents/model-auth.js").resolveApiKeyForProviderCore, })); vi.mock("../agents/auth-profiles/store.js", () => ({ @@ -528,7 +528,7 @@ describe("capability cli", () => { .mockResolvedValue([{ id: "gpt-5.4", provider: "openai", name: "GPT-5.4" }] as never); mocks.loadAuthProfileStoreForRuntime.mockReset().mockReturnValue({ profiles: {}, order: {} }); mocks.listProfilesForProvider.mockReset().mockReturnValue([]); - mocks.resolveApiKeyForProvider.mockReset().mockRejectedValue(new Error("no auth profile")); + mocks.resolveApiKeyForProviderCore.mockReset().mockRejectedValue(new Error("no auth profile")); mocks.loadManifestMetadataSnapshot .mockReset() .mockReturnValue({ manifestRegistry: { plugins: [] } }); @@ -2616,7 +2616,7 @@ describe("capability cli", () => { "--json", ); - expect(mocks.resolveApiKeyForProvider).toHaveBeenCalledWith( + expect(mocks.resolveApiKeyForProviderCore).toHaveBeenCalledWith( expect.objectContaining({ provider: "openai", cfg: rawConfig, @@ -2640,7 +2640,7 @@ describe("capability cli", () => { await runCapability("tts", "convert", "--text", "hello", "--json"); - expect(mocks.resolveApiKeyForProvider).toHaveBeenCalledWith( + expect(mocks.resolveApiKeyForProviderCore).toHaveBeenCalledWith( expect.objectContaining({ provider: "openai", cfg: rawConfig, @@ -2730,7 +2730,7 @@ describe("capability cli", () => { }; expect(cfg.tts?.providers?.openai?.apiKey).toBe("config-key"); expect(cfg.channels?.discord?.tts?.providers?.openai).toEqual({ speakerVoice: "nova" }); - expect(mocks.resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(mocks.resolveApiKeyForProviderCore).not.toHaveBeenCalled(); }); it("does not hydrate local TTS provider config from token auth profiles", async () => { diff --git a/src/cli/capability-cli/tts-runtime.ts b/src/cli/capability-cli/tts-runtime.ts index bfda0d2ee55f..a9fe3abb17b6 100644 --- a/src/cli/capability-cli/tts-runtime.ts +++ b/src/cli/capability-cli/tts-runtime.ts @@ -5,7 +5,7 @@ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, } from "@openclaw/normalization-core/string-coerce"; -import { resolveApiKeyForProvider } from "../../agents/model-auth.js"; +import { resolveApiKeyForProviderCore } from "../../agents/model-auth.js"; import { getRuntimeConfig } from "../../config/config.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { callGateway } from "../../gateway/call.js"; @@ -204,7 +204,7 @@ async function injectTtsAuthProfileApiKey(params: { if (ttsProviderConfigHasApiKey(existingProviderConfig?.value)) { return params.cfg; } - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: providerId, cfg: params.cfg, credentialPrecedence: "profile-first", diff --git a/src/cli/config-cli-input.ts b/src/cli/config-cli-input.ts index 624207d78012..6b3520fcec10 100644 --- a/src/cli/config-cli-input.ts +++ b/src/cli/config-cli-input.ts @@ -20,12 +20,12 @@ import { validateExecSecretRefId, } from "../secrets/ref-contract.js"; import { resolveConfigSecretTargetByPath } from "../secrets/target-registry.js"; +import { toDotPath } from "../shared/dot-path.js"; import { formatCliCommand } from "./command-format.js"; import { parseConfigSetPath, parseConfigSetValue, type PathSegment, - toDotPath, validatePathSegments, } from "./config-cli-path.js"; import type { ConfigSetDryRunInputMode, ConfigSetDryRunResult } from "./config-set-dryrun.js"; diff --git a/src/cli/config-cli-path.ts b/src/cli/config-cli-path.ts index b2a049de5d55..4edd343d6a7d 100644 --- a/src/cli/config-cli-path.ts +++ b/src/cli/config-cli-path.ts @@ -1,6 +1,7 @@ import { isRecord as isPlainRecord } from "@openclaw/normalization-core/record-coerce"; import JSON5 from "json5"; import { isBlockedObjectKey } from "../infra/prototype-keys.js"; +import { toDotPath } from "../shared/dot-path.js"; import { parseConfigPathArrayIndex } from "../shared/path-array-index.js"; import { formatCliCommand } from "./command-format.js"; import { formatStrictJsonParseFailure } from "./error-format.js"; @@ -595,7 +596,3 @@ export function unsetAtPath(root: Record, path: PathSegment[]): delete record[last]; return { removed: true, leafContainer: "object" }; } - -export function toDotPath(path: readonly PathSegment[]): string { - return path.join("."); -} diff --git a/src/cli/config-cli-runner.ts b/src/cli/config-cli-runner.ts index 087c98b5f064..510e1ade0883 100644 --- a/src/cli/config-cli-runner.ts +++ b/src/cli/config-cli-runner.ts @@ -12,6 +12,7 @@ import { resolveGatewayReloadSettings } from "../gateway/config-reload-settings. import { danger, info } from "../globals.js"; import type { RuntimeEnv } from "../runtime.js"; import { writeRuntimeJson } from "../runtime.js"; +import { toDotPath } from "../shared/dot-path.js"; import { shortenHomePath } from "../utils.js"; import { ConfigSetDryRunValidationError, @@ -28,7 +29,6 @@ import { getAtPath, mergeAtPath, setAtPath, - toDotPath, type JsonSchemaRecord, type PathSegment, unsetAtPath, diff --git a/src/cli/daemon-cli/lifecycle-action-preflight.test.ts b/src/cli/daemon-cli/lifecycle-action-preflight.test.ts index ff75ce3632c5..385f8f6efc0d 100644 --- a/src/cli/daemon-cli/lifecycle-action-preflight.test.ts +++ b/src/cli/daemon-cli/lifecycle-action-preflight.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import { resetConfigRuntimeState } from "../../config/config.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { getServiceActionPreflightFailure } from "./lifecycle-action-preflight.js"; afterEach(() => { @@ -13,7 +13,7 @@ afterEach(() => { async function withIsolatedLifecycleState( run: (params: { agentDir: string }) => Promise, ): Promise { - await withTempDir({ prefix: "openclaw-lifecycle-action-preflight-" }, async (root) => { + await withTestDir({ prefix: "openclaw-lifecycle-action-preflight-" }, async (root) => { const stateDir = path.join(root, "state"); const configPath = path.join(root, "openclaw.json"); const agentDir = path.join(stateDir, "agents", "main", "agent"); diff --git a/src/cli/daemon-cli/status.print.test.ts b/src/cli/daemon-cli/status.print.test.ts index b8dc56af81fa..b2a757043c83 100644 --- a/src/cli/daemon-cli/status.print.test.ts +++ b/src/cli/daemon-cli/status.print.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { withEnv } from "../../test-utils/env.js"; import { formatCliCommand } from "../command-format.js"; import { printDaemonStatus } from "./status.print.js"; @@ -138,7 +138,7 @@ describe("printDaemonStatus", () => { it.skipIf(process.platform !== "win32")( "shortens real Windows home casing aliases in human status", async () => { - await withTempDir({ prefix: "openclaw-home-display-" }, async (home) => { + await withTestDir({ prefix: "openclaw-home-display-" }, async (home) => { const logFile = path.join(home, "logs", "gateway.log"); await fs.promises.mkdir(path.dirname(logFile), { recursive: true }); await fs.promises.writeFile(logFile, "ready", "utf8"); diff --git a/src/cli/exec-approvals-cli.test.ts b/src/cli/exec-approvals-cli.test.ts index 0bd1eaf707a9..647ea2cd3e32 100644 --- a/src/cli/exec-approvals-cli.test.ts +++ b/src/cli/exec-approvals-cli.test.ts @@ -179,7 +179,7 @@ vi.mock("./nodes-cli/rpc.js", async () => { const actual = await vi.importActual("./nodes-cli/rpc.js"); return { ...actual, - resolveNodeId: vi.fn(async () => "node-1"), + resolveCliNodeId: vi.fn(async () => "node-1"), }; }); diff --git a/src/cli/exec-approvals-cli.ts b/src/cli/exec-approvals-cli.ts index 8a110ed0cc2f..f24e7ae5ec46 100644 --- a/src/cli/exec-approvals-cli.ts +++ b/src/cli/exec-approvals-cli.ts @@ -40,7 +40,7 @@ import { import { formatTimeAgo } from "../infra/format-time/format-relative.ts"; import { defaultRuntime } from "../runtime.js"; import { callGatewayFromCli } from "./gateway-rpc.js"; -import { nodesCallOpts, resolveNodeId } from "./nodes-cli/rpc.js"; +import { nodesCallOpts, resolveCliNodeId } from "./nodes-cli/rpc.js"; import type { NodesRpcOpts } from "./nodes-cli/types.js"; import { applyParentDefaultHelpAction } from "./program/parent-default-help.js"; @@ -148,7 +148,7 @@ async function resolveTargetNodeId(opts: ExecApprovalsCliOpts): Promise { const actual = await vi.importActual("./rpc.js"); return { ...actual, - resolveNode: vi.fn(async () => ({ + resolveCliNode: vi.fn(async () => ({ nodeId: "node-abc123", platform: "ios", remoteIp: "198.51.100.42", diff --git a/src/cli/nodes-cli/register.camera.ts b/src/cli/nodes-cli/register.camera.ts index c7cd43d6fc9a..8418404fe689 100644 --- a/src/cli/nodes-cli/register.camera.ts +++ b/src/cli/nodes-cli/register.camera.ts @@ -27,8 +27,8 @@ import { parseOptionalNodeFiniteNumber, parseOptionalNodeNonNegativeInteger, parseOptionalNodePositiveInteger, - resolveNode, - resolveNodeId, + resolveCliNode, + resolveCliNodeId, } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; @@ -57,7 +57,7 @@ export function registerNodesCameraCommands(nodes: Command) { .requiredOption("--node ", "Node id, name, or IP") .action(async (opts: NodesRpcOpts) => { await runNodesCommand("camera list", async () => { - const nodeId = await resolveNodeId(opts, opts.node ?? ""); + const nodeId = await resolveCliNodeId(opts, opts.node ?? ""); const raw = await callNodesGatewayCli( "node.invoke", opts, @@ -128,7 +128,7 @@ export function registerNodesCameraCommands(nodes: Command) { .option("--invoke-timeout ", "Node invoke timeout in ms (default 20000)", "20000") .action(async (opts: NodesRpcOpts) => { await runNodesCommand("camera snap", async () => { - const node = await resolveNode(opts, normalizeOptionalString(opts.node) ?? ""); + const node = await resolveCliNode(opts, normalizeOptionalString(opts.node) ?? ""); const nodeId = node.nodeId; const facingOpt = normalizeLowercaseStringOrEmpty( normalizeOptionalString(opts.facing) ?? "", @@ -233,7 +233,7 @@ export function registerNodesCameraCommands(nodes: Command) { .option("--invoke-timeout ", "Node invoke timeout in ms (default 90000)", "90000") .action(async (opts: NodesRpcOpts & { audio?: boolean }) => { await runNodesCommand("camera clip", async () => { - const node = await resolveNode(opts, normalizeOptionalString(opts.node) ?? ""); + const node = await resolveCliNode(opts, normalizeOptionalString(opts.node) ?? ""); const nodeId = node.nodeId; const facing = parseFacing(opts.facing ?? "front"); const target = resolveCameraClipTarget({ facing, platform: node.platform }); diff --git a/src/cli/nodes-cli/register.invoke.ts b/src/cli/nodes-cli/register.invoke.ts index 160c1d554b97..e18aa035b6b3 100644 --- a/src/cli/nodes-cli/register.invoke.ts +++ b/src/cli/nodes-cli/register.invoke.ts @@ -11,7 +11,7 @@ import { callNodesGatewayCli, nodesCallOpts, parseOptionalNodePositiveInteger, - resolveNodeId, + resolveCliNodeId, } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; @@ -52,7 +52,7 @@ export function registerNodesInvokeCommands(nodes: Command) { ); } const params = parseNodeInvokeParams(opts.params); - const nodeId = await resolveNodeId(opts, nodeQuery); + const nodeId = await resolveCliNodeId(opts, nodeQuery); const timeoutMs = parseOptionalNodePositiveInteger( opts.invokeTimeout, "--invoke-timeout", diff --git a/src/cli/nodes-cli/register.location.ts b/src/cli/nodes-cli/register.location.ts index 8b8d5ea5d59f..efbe59f5905a 100644 --- a/src/cli/nodes-cli/register.location.ts +++ b/src/cli/nodes-cli/register.location.ts @@ -9,7 +9,7 @@ import { nodesCallOpts, parseOptionalNodeNonNegativeInteger, parseOptionalNodePositiveInteger, - resolveNodeId, + resolveCliNodeId, } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; @@ -31,7 +31,7 @@ export function registerNodesLocationCommands(nodes: Command) { .option("--invoke-timeout ", "Node invoke timeout in ms (default 20000)", "20000") .action(async (opts: NodesRpcOpts) => { await runNodesCommand("location get", async () => { - const nodeId = await resolveNodeId(opts, opts.node ?? ""); + const nodeId = await resolveCliNodeId(opts, opts.node ?? ""); const maxAgeMs = parseOptionalNodeNonNegativeInteger(opts.maxAge, "--max-age"); const desiredAccuracyRaw = normalizeOptionalLowercaseString(opts.accuracy); const desiredAccuracy = diff --git a/src/cli/nodes-cli/register.notify.ts b/src/cli/nodes-cli/register.notify.ts index 0744a5bad5af..38cf08837cd5 100644 --- a/src/cli/nodes-cli/register.notify.ts +++ b/src/cli/nodes-cli/register.notify.ts @@ -8,7 +8,7 @@ import { callNodesGatewayCli, nodesCallOpts, parseOptionalNodePositiveInteger, - resolveNodeId, + resolveCliNodeId, } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; @@ -37,7 +37,7 @@ export function registerNodesNotifyCommand(nodes: Command) { .option("--invoke-timeout ", "Node invoke timeout in ms (default 15000)", "15000") .action(async (opts: NodesRpcOpts) => { await runNodesCommand("notify", async () => { - const nodeId = await resolveNodeId(opts, normalizeOptionalString(opts.node) ?? ""); + const nodeId = await resolveCliNodeId(opts, normalizeOptionalString(opts.node) ?? ""); const title = normalizeOptionalString(opts.title) ?? ""; const body = normalizeOptionalString(opts.body) ?? ""; if (!title && !body) { diff --git a/src/cli/nodes-cli/register.pairing.ts b/src/cli/nodes-cli/register.pairing.ts index cc79dae9a9a4..df7a7941b2e3 100644 --- a/src/cli/nodes-cli/register.pairing.ts +++ b/src/cli/nodes-cli/register.pairing.ts @@ -13,7 +13,7 @@ import { callNodesGatewayCli, callNodePairApprovalGatewayCli, nodesCallOpts, - resolveNodeId, + resolveCliNodeId, } from "./rpc.js"; import type { NodesRpcOpts, PendingRequest } from "./types.js"; @@ -210,7 +210,7 @@ export function registerNodesPairingCommands(nodes: Command) { .requiredOption("--node ", "Node id, name, or IP") .action(async (opts: NodesRpcOpts) => { await runNodesCommand("remove", async () => { - const nodeId = await resolveNodeId(opts, normalizeOptionalString(opts.node) ?? ""); + const nodeId = await resolveCliNodeId(opts, normalizeOptionalString(opts.node) ?? ""); if (!nodeId) { defaultRuntime.error( `--node is required. Run ${formatCliCommand("openclaw nodes pending")} to choose a node request.`, @@ -237,7 +237,7 @@ export function registerNodesPairingCommands(nodes: Command) { .requiredOption("--name ", "New display name") .action(async (opts: NodesRpcOpts) => { await runNodesCommand("rename", async () => { - const nodeId = await resolveNodeId(opts, normalizeOptionalString(opts.node) ?? ""); + const nodeId = await resolveCliNodeId(opts, normalizeOptionalString(opts.node) ?? ""); const name = normalizeOptionalString(opts.name) ?? ""; if (!nodeId || !name) { defaultRuntime.error( diff --git a/src/cli/nodes-cli/register.push.ts b/src/cli/nodes-cli/register.push.ts index a031d04b8910..90249dd7cffd 100644 --- a/src/cli/nodes-cli/register.push.ts +++ b/src/cli/nodes-cli/register.push.ts @@ -7,7 +7,7 @@ import type { Command } from "commander"; import type { PushTestResult } from "../../../packages/gateway-protocol/src/index.js"; import { defaultRuntime } from "../../runtime.js"; import { getNodesTheme, runNodesCommand } from "./cli-utils.js"; -import { callNodesGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js"; +import { callNodesGatewayCli, nodesCallOpts, resolveCliNodeId } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; type NodesPushOpts = NodesRpcOpts & { @@ -40,7 +40,7 @@ export function registerNodesPushCommand(nodes: Command) { .option("--environment ", "Override APNs environment") .action(async (opts: NodesPushOpts) => { await runNodesCommand("push", async () => { - const nodeId = await resolveNodeId(opts, normalizeOptionalString(opts.node) ?? ""); + const nodeId = await resolveCliNodeId(opts, normalizeOptionalString(opts.node) ?? ""); const title = normalizeOptionalString(opts.title) || "OpenClaw"; const body = normalizeOptionalString(opts.body) || `Push test for node ${nodeId}`; const environment = normalizeEnvironment(opts.environment); diff --git a/src/cli/nodes-cli/register.screen.ts b/src/cli/nodes-cli/register.screen.ts index bb3948a6a6b2..ac6ac906629a 100644 --- a/src/cli/nodes-cli/register.screen.ts +++ b/src/cli/nodes-cli/register.screen.ts @@ -16,7 +16,7 @@ import { parseOptionalNodeFiniteNumber, parseOptionalNodeNonNegativeInteger, parseOptionalNodePositiveInteger, - resolveNodeId, + resolveCliNodeId, } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; @@ -39,7 +39,7 @@ export function registerNodesScreenCommands(nodes: Command) { .option("--invoke-timeout ", "Node invoke timeout in ms (default 120000)", "120000") .action(async (opts: NodesRpcOpts & { out?: string }) => { await runNodesCommand("screen record", async () => { - const nodeId = await resolveNodeId(opts, opts.node ?? ""); + const nodeId = await resolveCliNodeId(opts, opts.node ?? ""); const durationMs = parseDurationMs(opts.duration ?? ""); const screenIndex = parseOptionalNodeNonNegativeInteger(opts.screen ?? "0", "--screen"); const fps = parseOptionalNodeFiniteNumber(opts.fps ?? "10", "--fps", { diff --git a/src/cli/nodes-cli/rpc.ts b/src/cli/nodes-cli/rpc.ts index fc0ab8d35726..b18d131dd714 100644 --- a/src/cli/nodes-cli/rpc.ts +++ b/src/cli/nodes-cli/rpc.ts @@ -271,8 +271,8 @@ export function unauthorizedHintForMessage(message: string): string | null { } /** Resolve a node query to a node id via live node list or paired-node fallback. */ -export async function resolveNodeId(opts: NodesRpcOpts, query: string) { - return (await resolveNode(opts, query)).nodeId; +export async function resolveCliNodeId(opts: NodesRpcOpts, query: string) { + return (await resolveCliNode(opts, query)).nodeId; } /** Resolve a node through the pairing-aware diagnostics view when available. */ @@ -284,12 +284,12 @@ export async function resolveNodeDiagnosticsId(opts: NodesRpcOpts, query: string if (!isUnknownGatewayMethodError(error, "node.list")) { throw error; } - return await resolveNodeId(opts, query); + return await resolveCliNodeId(opts, query); } } /** Resolve a node query to the best available node record. */ -export async function resolveNode(opts: NodesRpcOpts, query: string): Promise { +export async function resolveCliNode(opts: NodesRpcOpts, query: string): Promise { let nodes: NodeListNode[]; try { const res = await callNodesGatewayCli("node.list", opts, {}); diff --git a/src/cli/plugins-cli-test-helpers.ts b/src/cli/plugins-cli-test-helpers.ts index e2973dad3172..599507e17494 100644 --- a/src/cli/plugins-cli-test-helpers.ts +++ b/src/cli/plugins-cli-test-helpers.ts @@ -94,16 +94,17 @@ export const pluginCliConfigMock: Mock = vi.fn( ); export const readConfigFileSnapshotMock: AsyncUnknownMock = vi.fn(); export const readConfigFileSnapshotForWriteMock: AsyncUnknownMock = vi.fn(); -export const writeConfigFile: AsyncUnknownMock = vi.fn(async () => undefined); +export const configWriteMock: AsyncUnknownMock = vi.fn(async () => undefined); export const replaceConfigFileMock: AsyncUnknownMock = vi.fn( - async (params: { nextConfig: OpenClawConfig }) => await writeConfigFile(params.nextConfig), + async (params: { nextConfig: OpenClawConfig }) => await configWriteMock(params.nextConfig), ) as AsyncUnknownMock; const resolveStateDir: Mock<() => string> = vi.fn(() => "/tmp/openclaw-state"); export const installPluginFromMarketplaceMock: Mock = vi.fn(); export const installPluginFromGitSpecMock: Mock = vi.fn(); const parseGitPluginSpec: Mock = vi.fn(); const listMarketplacePlugins: Mock = vi.fn(); -export const resolveMarketplaceInstallShortcut: Mock = vi.fn(); +export const resolveMarketplaceInstallShortcutMock: Mock = + vi.fn(); export const enablePluginInConfigMock: UnknownMock = vi.fn(); export const recordPluginInstallMock: UnknownMock = vi.fn(); const loadInstalledPluginIndexInstallRecords: AsyncUnknownMock = vi.fn(async () => @@ -116,14 +117,14 @@ const writePersistedInstalledPluginIndexInstallRecords: Mock = vi.fn(async () => null); -export const writePersistedInstalledPluginIndexInstallRecordsWithLease: Mock = +export const writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock: Mock = vi.fn(async (records) => { const previous = await readPersistedInstalledPluginIndexMock(); mockInstalledPluginIndexInstallRecords = clonePluginInstallRecords(records); mockInstalledPluginIndexRevision += 1; return { previous, revision: mockInstalledPluginIndexRevision }; }); -export const restorePersistedInstalledPluginIndexIfCurrent: Mock = +export const restorePersistedInstalledPluginIndexIfCurrentMock: Mock = vi.fn(async (index, expectedRevision) => { if (mockInstalledPluginIndexRevision !== expectedRevision) { return false; @@ -148,8 +149,8 @@ export const clearPluginRegistryLoadCacheMock: UnknownMock = vi.fn(); export const applyExclusiveSlotSelectionMock: UnknownMock = vi.fn(); export const planPluginUninstallMock: UnknownMock = vi.fn(); export const applyPluginUninstallDirectoryRemovalMock: AsyncUnknownMock = vi.fn(); -export const updateNpmInstalledPlugins: Mock = vi.fn(); -export const updateNpmInstalledHookPacks: Mock = vi.fn(); +export const updateNpmInstalledPluginsMock: Mock = vi.fn(); +export const updateNpmInstalledHookPacksMock: Mock = vi.fn(); export const promptYesNoMock: AsyncUnknownMock = vi.fn(); const promptText: AsyncUnknownMock = vi.fn(); export class PromptInputClosedError extends Error { @@ -290,7 +291,7 @@ vi.mock("../config/config.js", () => ({ invokeMock< [OpenClawConfig], ReturnType<(typeof import("../config/config.js"))["writeConfigFile"]> - >(writeConfigFile, config)) as (typeof import("../config/config.js"))["writeConfigFile"], + >(configWriteMock, config)) as (typeof import("../config/config.js"))["writeConfigFile"], replaceConfigFile: (( params: Parameters<(typeof import("../config/config.js"))["replaceConfigFile"]>[0], ) => @@ -318,7 +319,7 @@ vi.mock("../plugins/marketplace.js", () => ({ listMarketplacePlugins: ((...args: Parameters) => listMarketplacePlugins(...args)) as ListMarketplacePluginsFn, resolveMarketplaceInstallShortcut: ((...args: Parameters) => - resolveMarketplaceInstallShortcut(...args)) as ResolveMarketplaceInstallShortcutFn, + resolveMarketplaceInstallShortcutMock(...args)) as ResolveMarketplaceInstallShortcutFn, })); vi.mock("../plugins/enable.js", () => ({ @@ -379,7 +380,7 @@ vi.mock("../plugins/installed-plugin-index-records.js", async (importOriginal) = )) as (...args: unknown[]) => unknown, writePersistedInstalledPluginIndexInstallRecordsWithLease: ((...args: unknown[]) => invokeMock( - writePersistedInstalledPluginIndexInstallRecordsWithLease, + writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock, ...args, )) as (...args: unknown[]) => unknown, recordPluginInstallInRecords: ( @@ -409,9 +410,10 @@ vi.mock("../plugins/installed-plugin-index-store.js", async (importOriginal) => ...args: unknown[] ) => unknown, restorePersistedInstalledPluginIndexIfCurrent: ((...args: unknown[]) => - invokeMock(restorePersistedInstalledPluginIndexIfCurrent, ...args)) as ( - ...args: unknown[] - ) => unknown, + invokeMock( + restorePersistedInstalledPluginIndexIfCurrentMock, + ...args, + )) as (...args: unknown[]) => unknown, }; }); @@ -607,7 +609,7 @@ vi.mock("../plugins/update.js", async (importOriginal) => { Parameters<(typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"]>, ReturnType<(typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"]> >( - updateNpmInstalledPlugins, + updateNpmInstalledPluginsMock, ...args, )) as (typeof import("../plugins/update.js"))["updateNpmInstalledPlugins"], }; @@ -621,7 +623,7 @@ vi.mock("../hooks/update.js", () => ({ Parameters<(typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"]>, ReturnType<(typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"]> >( - updateNpmInstalledHookPacks, + updateNpmInstalledHookPacksMock, ...args, )) as (typeof import("../hooks/update.js"))["updateNpmInstalledHookPacks"], })); @@ -829,21 +831,21 @@ export function resetPluginsCliTestState() { pluginCliConfigMock.mockReset(); readConfigFileSnapshotMock.mockReset(); readConfigFileSnapshotForWriteMock.mockReset(); - writeConfigFile.mockReset(); + configWriteMock.mockReset(); replaceConfigFileMock.mockReset(); resolveStateDir.mockReset(); installPluginFromMarketplaceMock.mockReset(); listMarketplacePlugins.mockReset(); - resolveMarketplaceInstallShortcut.mockReset(); + resolveMarketplaceInstallShortcutMock.mockReset(); enablePluginInConfigMock.mockReset(); recordPluginInstallMock.mockReset(); mockInstalledPluginIndexInstallRecords = {}; mockInstalledPluginIndexRevision = 0; loadInstalledPluginIndexInstallRecords.mockReset(); writePersistedInstalledPluginIndexInstallRecords.mockReset(); - writePersistedInstalledPluginIndexInstallRecordsWithLease.mockReset(); + writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock.mockReset(); readPersistedInstalledPluginIndexMock.mockReset(); - restorePersistedInstalledPluginIndexIfCurrent.mockReset(); + restorePersistedInstalledPluginIndexIfCurrentMock.mockReset(); loadPluginManifestRegistryMock.mockReset(); buildPluginSnapshotReportMock.mockReset(); buildPluginRegistrySnapshotReportMock.mockReset(); @@ -857,8 +859,8 @@ export function resetPluginsCliTestState() { applyExclusiveSlotSelectionMock.mockReset(); planPluginUninstallMock.mockReset(); applyPluginUninstallDirectoryRemovalMock.mockReset(); - updateNpmInstalledPlugins.mockReset(); - updateNpmInstalledHookPacks.mockReset(); + updateNpmInstalledPluginsMock.mockReset(); + updateNpmInstalledHookPacksMock.mockReset(); mockHookInstallRecords = {}; promptText.mockReset(); promptYesNoMock.mockReset(); @@ -906,13 +908,13 @@ export function resetPluginsCliTestState() { }, }; }); - writeConfigFile.mockResolvedValue(undefined); + configWriteMock.mockResolvedValue(undefined); replaceConfigFileMock.mockImplementation( (async (params: { nextConfig: OpenClawConfig }) => - await writeConfigFile(params.nextConfig)) as (...args: unknown[]) => Promise, + await configWriteMock(params.nextConfig)) as (...args: unknown[]) => Promise, ); resolveStateDir.mockReturnValue("/tmp/openclaw-state"); - resolveMarketplaceInstallShortcut.mockResolvedValue(null); + resolveMarketplaceInstallShortcutMock.mockResolvedValue(null); installPluginFromMarketplaceMock.mockResolvedValue({ ok: false, error: "marketplace install failed", @@ -933,13 +935,15 @@ export function resetPluginsCliTestState() { return "/tmp/openclaw-state/openclaw.sqlite"; }); readPersistedInstalledPluginIndexMock.mockResolvedValue(null); - writePersistedInstalledPluginIndexInstallRecordsWithLease.mockImplementation(async (records) => { - const previous = await readPersistedInstalledPluginIndexMock(); - mockInstalledPluginIndexInstallRecords = clonePluginInstallRecords(records); - mockInstalledPluginIndexRevision += 1; - return { previous, revision: mockInstalledPluginIndexRevision }; - }); - restorePersistedInstalledPluginIndexIfCurrent.mockImplementation( + writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock.mockImplementation( + async (records) => { + const previous = await readPersistedInstalledPluginIndexMock(); + mockInstalledPluginIndexInstallRecords = clonePluginInstallRecords(records); + mockInstalledPluginIndexRevision += 1; + return { previous, revision: mockInstalledPluginIndexRevision }; + }, + ); + restorePersistedInstalledPluginIndexIfCurrentMock.mockImplementation( async (index, expectedRevision) => { if (mockInstalledPluginIndexRevision !== expectedRevision) { return false; @@ -1006,12 +1010,12 @@ export function resetPluginsCliTestState() { directoryRemoved: false, warnings: [], }); - updateNpmInstalledPlugins.mockResolvedValue({ + updateNpmInstalledPluginsMock.mockResolvedValue({ outcomes: [], changed: false, config: {} as OpenClawConfig, }); - updateNpmInstalledHookPacks.mockResolvedValue({ + updateNpmInstalledHookPacksMock.mockResolvedValue({ outcomes: [], changed: false, config: {} as OpenClawConfig, diff --git a/src/cli/plugins-cli.install.test.ts b/src/cli/plugins-cli.install.test.ts index 0056f6d0ef51..9714dc518937 100644 --- a/src/cli/plugins-cli.install.test.ts +++ b/src/cli/plugins-cli.install.test.ts @@ -39,8 +39,8 @@ import { runPluginsCommand, runtimeErrors, pluginsCliRuntimeLogs, - writeConfigFile, - writePersistedInstalledPluginIndexInstallRecordsWithLease, + configWriteMock, + writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock, } from "./plugins-cli-test-helpers.js"; const CLI_STATE_ROOT = "/tmp/openclaw-state"; @@ -431,7 +431,7 @@ function hookNpmInstallCall(callIndex = 0): PluginInstallCall { function persistedInstallRecords(callIndex = 0): Record { return mockCallArg( - writePersistedInstalledPluginIndexInstallRecordsWithLease, + writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock, callIndex, ) as Record; } @@ -652,7 +652,7 @@ describe("plugins cli install", () => { expect(installPluginFromNpmSpecMock).not.toHaveBeenCalled(); expect(installPluginFromPathMock).not.toHaveBeenCalled(); expect(installPluginFromMarketplaceMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it.each(["@acme/demo-plugin", "npm:@acme/demo-plugin"])( @@ -671,7 +671,7 @@ describe("plugins cli install", () => { expect(installHooksFromNpmSpecMock).toHaveBeenCalledTimes(1); expect(hookNpmInstallCall().inspection).toBe("package-kind"); expect(installPluginFromNpmSpecMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); @@ -715,7 +715,7 @@ describe("plugins cli install", () => { expect(hookNpmInstallCall().inspection).toBe("package-kind"); expect(hookNpmInstallCall(1).expectedIntegrity).toBe("sha256-demo"); expect(hookNpmInstallCall(1).expectedPackageKind).toBe("hook-only"); - expect(writeConfigFile).toHaveBeenCalledWith(installedCfg); + expect(configWriteMock).toHaveBeenCalledWith(installedCfg); }); it("blocks npm package inspection when plugin and hook config are include-owned", async () => { @@ -736,7 +736,7 @@ describe("plugins cli install", () => { expect(installHooksFromNpmSpecMock).not.toHaveBeenCalled(); expect(installPluginFromNpmSpecMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config hooks are stored in an external or unresolved top-level $include", ); @@ -761,7 +761,7 @@ describe("plugins cli install", () => { expect(installHooksFromNpmSpecMock).toHaveBeenCalledTimes(1); expect(hookNpmInstallCall().inspection).toBe("package-kind"); expect(installPluginFromNpmSpecMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config hooks are stored in an external or unresolved top-level $include", ); @@ -786,7 +786,7 @@ describe("plugins cli install", () => { } expect(installHooksFromPathMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config hooks are stored in an external or unresolved top-level $include", ); @@ -808,7 +808,7 @@ describe("plugins cli install", () => { expect(installHooksFromPathMock).toHaveBeenCalledTimes(1); expect(hookPathInstallCall().inspection).toBe("package-kind"); expect(installPluginFromPathMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config hooks are stored in an external or unresolved top-level $include", ); @@ -856,7 +856,7 @@ describe("plugins cli install", () => { expect(hookPathInstallCall().inspection).toBe("package-kind"); expect(hookPathInstallCall(1).expectedPackageKind).toBe("hook-only"); expect(installPluginFromClawHubMock).not.toHaveBeenCalled(); - expect(writeConfigFile).toHaveBeenCalledWith(installedCfg); + expect(configWriteMock).toHaveBeenCalledWith(installedCfg); }, ); @@ -873,7 +873,7 @@ describe("plugins cli install", () => { expect(installHooksFromNpmSpecMock).not.toHaveBeenCalled(); expect(installPluginFromNpmSpecMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain("unsupported $include shape at the root"); }); @@ -895,7 +895,7 @@ describe("plugins cli install", () => { expect(installHooksFromPathMock).not.toHaveBeenCalled(); expect(installPluginFromPathMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain("unsupported $include shape at the root"); }); @@ -919,7 +919,7 @@ describe("plugins cli install", () => { expect(installHooksFromPathMock).toHaveBeenCalledTimes(1); expect(hookPathInstallCall().inspection).toBe("package-kind"); expect(installPluginFromPathMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); @@ -939,7 +939,7 @@ describe("plugins cli install", () => { expect(installHooksFromNpmSpecMock).toHaveBeenCalledTimes(1); expect(hookNpmInstallCall().inspection).toBe("package-kind"); expect(installPluginFromNpmSpecMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); @@ -964,7 +964,7 @@ describe("plugins cli install", () => { expect(installHooksFromPathMock).toHaveBeenCalledTimes(1); expect(hookPathInstallCall().inspection).toBe("package-kind"); expect(installPluginFromPathMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); @@ -989,7 +989,7 @@ describe("plugins cli install", () => { expect(installHooksFromPathMock).toHaveBeenCalledTimes(1); expect(hookPathInstallCall().inspection).toBe("package-kind"); expect(installPluginFromPathMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); @@ -1089,7 +1089,7 @@ describe("plugins cli install", () => { await expect(runPluginsCommand(commandArgs)).rejects.toThrow("__exit__:1"); expect(installer).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); @@ -1103,7 +1103,7 @@ describe("plugins cli install", () => { await expect(runPluginsCommand(["plugins", "install", "brave"])).rejects.toThrow("__exit__:1"); expect(installPluginFromNpmSpecMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); @@ -1120,7 +1120,7 @@ describe("plugins cli install", () => { await expect(runPluginsCommand(["plugins", "install", pluginId])).rejects.toThrow("__exit__:1"); expect(installPluginFromPathMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); @@ -1155,7 +1155,7 @@ describe("plugins cli install", () => { } expect(installPluginFromMarketplaceMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain("nested $include"); }); @@ -1189,7 +1189,7 @@ describe("plugins cli install", () => { expect(marketplaceInstallCall().marketplace).toBe("local/repo"); expect(marketplaceInstallCall().plugin).toBe("alpha"); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("passes the active profile extensions dir to marketplace installs", async () => { @@ -1239,7 +1239,7 @@ describe("plugins cli install", () => { ); expect(installPluginFromMarketplaceMock).not.toHaveBeenCalled(); expect(installPluginFromNpmSpecMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("installs marketplace plugins and persists plugin index", async () => { @@ -1307,7 +1307,7 @@ describe("plugins cli install", () => { expect(persistedInstallRecord("alpha").source).toBe("marketplace"); expect(persistedInstallRecord("alpha").installPath).toBe(cliInstallPath("alpha")); - expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg); + expect(configWriteMock).toHaveBeenCalledWith(enabledCfg); expect(replaceConfigCall().baseHash).toBe("mock"); expect(replaceConfigCall().nextConfig).toBe(enabledCfg); expect(runtimeLogsContain("slot adjusted")).toBe(true); @@ -1616,7 +1616,7 @@ describe("plugins cli install", () => { ); expect(record.clawpackSize).toBe(4096); expect(readConfigFileSnapshotForWriteMock).toHaveBeenCalledTimes(2); - expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg); + expect(configWriteMock).toHaveBeenCalledWith(enabledCfg); expect(runtimeLogsContain("Installed plugin: demo")).toBe(true); expect(reportClawHubPluginInstallTelemetryMock).toHaveBeenCalledWith({ baseUrl: "https://clawhub.ai", @@ -1628,7 +1628,7 @@ describe("plugins cli install", () => { it("does not report a ClawHub install when durable persistence fails", async () => { primeSuccessfulClawHubPluginInstall(); - writeConfigFile.mockRejectedValueOnce(new Error("persistence failed")); + configWriteMock.mockRejectedValueOnce(new Error("persistence failed")); await expect(runPluginsCommand(["plugins", "install", "clawhub:demo"])).rejects.toThrow( "persistence failed", @@ -1741,8 +1741,8 @@ describe("plugins cli install", () => { await runPluginsCommand(["plugins", "install", pluginId]); - const writtenConfig = writeConfigFile.mock.calls[ - writeConfigFile.mock.calls.length - 1 + const writtenConfig = configWriteMock.mock.calls[ + configWriteMock.mock.calls.length - 1 ]?.[0] as OpenClawConfig; expect(writtenConfig.plugins?.entries?.[pluginId]).toEqual({ enabled: false, @@ -1786,7 +1786,7 @@ describe("plugins cli install", () => { "has invalid configured settings", ); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(enablePluginInConfigMock).not.toHaveBeenCalled(); }); @@ -1824,7 +1824,7 @@ describe("plugins cli install", () => { await runPluginsCommand(["plugins", "install", pluginId]); expect(enablePluginInConfigMock).toHaveBeenCalledTimes(1); - expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg); + expect(configWriteMock).toHaveBeenCalledWith(enabledCfg); expect(runtimeLogsContain("requires configuration first")).toBe(false); }); @@ -1879,7 +1879,7 @@ describe("plugins cli install", () => { expect(record.spec).toBe("@openclaw/brave-plugin"); expect(record.installPath).toBe(cliInstallPath("brave")); expect(record.version).toBe("1.2.3"); - expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg); + expect(configWriteMock).toHaveBeenCalledWith(enabledCfg); }); it("passes third-party external catalog integrity with catalog install trust", async () => { @@ -1957,7 +1957,7 @@ describe("plugins cli install", () => { expect(record.spec).toBe("demo"); expect(record.installPath).toBe(cliInstallPath("demo")); expect(record.version).toBe("1.2.3"); - expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg); + expect(configWriteMock).toHaveBeenCalledWith(enabledCfg); }); it("stores npm resolution metadata without changing the active plugin install selector", async () => { @@ -2006,7 +2006,7 @@ describe("plugins cli install", () => { expect(persistedInstallRecord("demo").source).toBe("npm"); expect(persistedInstallRecord("demo").spec).toBe("demo"); expect(persistedInstallRecord("demo").installPath).toBe(cliInstallPath("demo")); - expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg); + expect(configWriteMock).toHaveBeenCalledWith(enabledCfg); }); it("installs npm-pack archives through npm install semantics", async () => { @@ -2031,7 +2031,7 @@ describe("plugins cli install", () => { expect(record.npmIntegrity).toBe("sha512-pack-demo"); expect(record.npmShasum).toBe("packdemosha"); expect(record.npmTarballName).toBe("openclaw-demo-1.2.3.tgz"); - expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg); + expect(configWriteMock).toHaveBeenCalledWith(enabledCfg); }); it("keeps npm-prefixed official plugin ids on explicit npm semantics", async () => { @@ -2257,7 +2257,7 @@ describe("plugins cli install", () => { expect(npmInstallCall().spec).toBe("memory-lancedb"); expect(installPluginFromClawHubMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(runtimeErrors.at(-1)).toContain("Package not found on npm: memory-lancedb."); expect(runtimeErrors.at(-1)).not.toContain("Also not a valid hook pack"); }); @@ -2295,7 +2295,7 @@ describe("plugins cli install", () => { expect(record.gitUrl).toBe("https://github.com/acme/demo.git"); expect(record.gitRef).toBe("v1.2.3"); expect(record.gitCommit).toBe("abc123"); - expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg); + expect(configWriteMock).toHaveBeenCalledWith(enabledCfg); }); it("rejects --pin for git installs and points at git refs", async () => { @@ -2767,7 +2767,7 @@ describe("plugins cli install", () => { expect(record.resolvedSpec).toBe("@acme/demo-hooks@1.2.3"); expect(record.integrity).toBe("sha256-demo"); expect(record.hooks).toEqual(["command-audit"]); - expect(writeConfigFile).toHaveBeenCalledWith(installedCfg); + expect(configWriteMock).toHaveBeenCalledWith(installedCfg); expect(runtimeLogsContain("Installed hook pack: demo-hooks")).toBe(true); }); diff --git a/src/cli/plugins-cli.policy.test.ts b/src/cli/plugins-cli.policy.test.ts index f0f30bd49eed..89a4af620626 100644 --- a/src/cli/plugins-cli.policy.test.ts +++ b/src/cli/plugins-cli.policy.test.ts @@ -11,7 +11,7 @@ import { runtimeErrors, pluginsCliRuntimeLogs, runPluginsCommand, - writeConfigFile, + configWriteMock, } from "./plugins-cli-test-helpers.js"; const ORIGINAL_OPENCLAW_NIX_MODE = process.env.OPENCLAW_NIX_MODE; @@ -44,13 +44,13 @@ describe("plugins cli policy mutations", () => { } function requireFirstWrittenConfig(): OpenClawConfig { - const call = writeConfigFile.mock.calls[0]; + const call = configWriteMock.mock.calls[0]; if (!call) { - throw new Error("expected writeConfigFile to be called"); + throw new Error("expected configWriteMock to be called"); } const [config] = call; if (!config) { - throw new Error("expected writeConfigFile to receive a config"); + throw new Error("expected configWriteMock to receive a config"); } return config; } @@ -93,7 +93,7 @@ describe("plugins cli policy mutations", () => { explicitSetPaths: [["plugins", "entries", "alpha"]], }, }); - expect(writeConfigFile).toHaveBeenCalledWith(enabledConfig); + expect(configWriteMock).toHaveBeenCalledWith(enabledConfig); expect(refreshPluginRegistryMock).toHaveBeenCalledWith({ config: enabledConfig, installRecords: {}, @@ -132,7 +132,7 @@ describe("plugins cli policy mutations", () => { await expect(runPluginsCommand(["plugins", "enable", "alpha"])).rejects.toThrow("__exit__:1"); expect(replaceConfigFileMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(refreshPluginRegistryMock).not.toHaveBeenCalled(); expect(runtimeErrors).toContain(`Plugin "alpha" could not be enabled (${reason}).`); expect(pluginsCliRuntimeLogs).not.toContain(`Plugin "alpha" could not be enabled (${reason}).`); @@ -154,7 +154,7 @@ describe("plugins cli policy mutations", () => { } expect(enablePluginInConfigMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("refreshes the persisted plugin registry after disabling a plugin", async () => { @@ -218,7 +218,7 @@ describe("plugins cli policy mutations", () => { explicitSetPaths: [["plugins", "entries", pluginId]], }, }); - expect(writeConfigFile).toHaveBeenCalledWith(enabledConfig); + expect(configWriteMock).toHaveBeenCalledWith(enabledConfig); }, ); @@ -263,7 +263,7 @@ describe("plugins cli policy mutations", () => { "Plugin not found: missing-plugin. Run `openclaw plugins list` to see installed plugins, or `openclaw plugins search missing-plugin` to look for installable plugins.", ); expect(enablePluginInConfigMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(refreshPluginRegistryMock).not.toHaveBeenCalled(); }, ); diff --git a/src/cli/plugins-cli.uninstall.test.ts b/src/cli/plugins-cli.uninstall.test.ts index 748bdeeb0b23..95af6dc1d22f 100644 --- a/src/cli/plugins-cli.uninstall.test.ts +++ b/src/cli/plugins-cli.uninstall.test.ts @@ -19,13 +19,13 @@ import { refreshPluginRegistryMock, replaceConfigFileMock, resetPluginsCliTestState, - restorePersistedInstalledPluginIndexIfCurrent, + restorePersistedInstalledPluginIndexIfCurrentMock, runPluginsCommand, runtimeErrors, pluginsCliRuntimeLogs, setInstalledPluginIndexInstallRecords, - writeConfigFile, - writePersistedInstalledPluginIndexInstallRecordsWithLease, + configWriteMock, + writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock, } from "./plugins-cli-test-helpers.js"; const CLI_STATE_ROOT = "/tmp/openclaw-state"; @@ -63,7 +63,7 @@ function expectRuntimeLogIncludes(fragment: string) { } function expectInstallRecordsWrittenWithLease(records: unknown, config: unknown) { - expect(writePersistedInstalledPluginIndexInstallRecordsWithLease).toHaveBeenCalledWith( + expect(writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock).toHaveBeenCalledWith( records, expect.objectContaining({ config, @@ -122,7 +122,7 @@ describe("plugins cli uninstall", () => { expect(planPluginUninstallMock).not.toHaveBeenCalled(); expect(applyPluginUninstallDirectoryRemovalMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("shows uninstall dry-run preview without mutating config or acquiring write mode", async () => { @@ -157,7 +157,7 @@ describe("plugins cli uninstall", () => { expect(buildPluginSnapshotReportMock).toHaveBeenCalledTimes(1); expect(buildPluginDiagnosticsReportMock).not.toHaveBeenCalled(); expect(planPluginUninstallMock).toHaveBeenCalledTimes(1); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(refreshPluginRegistryMock).not.toHaveBeenCalled(); expectRuntimeLogIncludes("Dry run, no changes made."); expectRuntimeLogIncludes("context engine slot"); @@ -198,7 +198,7 @@ describe("plugins cli uninstall", () => { expect(promptYesNoMock).not.toHaveBeenCalled(); expectLatestUninstallPlanParams({ pluginId: "alpha", deleteFiles: false }); expectInstallRecordsWrittenWithLease({}, { plugins: { entries: {} } }); - expect(writeConfigFile).toHaveBeenCalledWith({ + expect(configWriteMock).toHaveBeenCalledWith({ plugins: { entries: {}, }, @@ -308,8 +308,8 @@ describe("plugins cli uninstall", () => { expect(planPluginUninstallMock).not.toHaveBeenCalled(); expect(promptYesNoMock).not.toHaveBeenCalled(); expect(applyPluginUninstallDirectoryRemovalMock).not.toHaveBeenCalled(); - expect(writePersistedInstalledPluginIndexInstallRecordsWithLease).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(refreshPluginRegistryMock).not.toHaveBeenCalled(); }); @@ -401,8 +401,8 @@ describe("plugins cli uninstall", () => { expect(runtimeErrors).toContain( "Error: plugins uninstall requires confirmation input. Re-run in an interactive TTY or pass --force.", ); - expect(writePersistedInstalledPluginIndexInstallRecordsWithLease).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(refreshPluginRegistryMock).not.toHaveBeenCalled(); expect(applyPluginUninstallDirectoryRemovalMock).not.toHaveBeenCalled(); }); @@ -449,7 +449,7 @@ describe("plugins cli uninstall", () => { ).rejects.toThrow("config changed"); expectInstallRecordsWrittenWithLease({}, { plugins: { entries: {} } }); - expect(restorePersistedInstalledPluginIndexIfCurrent).toHaveBeenCalledWith( + expect(restorePersistedInstalledPluginIndexIfCurrentMock).toHaveBeenCalledWith( previousPersistedIndex, expect.any(Number), expect.objectContaining({ @@ -498,15 +498,15 @@ describe("plugins cli uninstall", () => { await runPluginsCommand(["plugins", "uninstall", "alpha", "--force"]); - const configWriteOrder = writeConfigFile.mock.invocationCallOrder[0] ?? 0; + const configWriteOrder = configWriteMock.mock.invocationCallOrder[0] ?? 0; const deleteOrder = applyPluginUninstallDirectoryRemovalMock.mock.invocationCallOrder[0] ?? Number.MAX_SAFE_INTEGER; const finalConfigWriteOrder = - writeConfigFile.mock.invocationCallOrder[1] ?? Number.MAX_SAFE_INTEGER; + configWriteMock.mock.invocationCallOrder[1] ?? Number.MAX_SAFE_INTEGER; const refreshOrder = refreshPluginRegistryMock.mock.invocationCallOrder[0] ?? Number.MAX_SAFE_INTEGER; - expect(writeConfigFile).toHaveBeenCalledTimes(2); + expect(configWriteMock).toHaveBeenCalledTimes(2); expect(applyPluginUninstallDirectoryRemovalMock).toHaveBeenCalledTimes(1); expect(refreshPluginRegistryMock).toHaveBeenCalledTimes(1); expect(deleteOrder).toBeGreaterThan(configWriteOrder); @@ -552,7 +552,7 @@ describe("plugins cli uninstall", () => { "remains disabled and tracked", ); - expect(writeConfigFile).toHaveBeenCalledWith({ + expect(configWriteMock).toHaveBeenCalledWith({ plugins: { entries: { alpha: { enabled: false }, @@ -560,7 +560,7 @@ describe("plugins cli uninstall", () => { installs: installRecords, }, }); - expect(writePersistedInstalledPluginIndexInstallRecordsWithLease).not.toHaveBeenCalled(); + expect(writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock).not.toHaveBeenCalled(); expect(refreshPluginRegistryMock).not.toHaveBeenCalled(); }); @@ -595,7 +595,7 @@ describe("plugins cli uninstall", () => { await runPluginsCommand(["plugins", "uninstall", "alpha", "--force"]); expectLatestUninstallPlanParams({ pluginId: "alpha", deleteFiles: true }); - expect(writeConfigFile).toHaveBeenCalledWith(nextConfig); + expect(configWriteMock).toHaveBeenCalledWith(nextConfig); expect(pluginsCliRuntimeLogs.at(-2)).toContain('Uninstalled plugin "alpha"'); }); @@ -621,7 +621,7 @@ describe("plugins cli uninstall", () => { await runPluginsCommand(["plugins", "uninstall", "alpha", "--force"]); expectLatestUninstallPlanParams({ pluginId: "alpha", deleteFiles: true }); - expect(writeConfigFile).toHaveBeenCalledWith(nextConfig); + expect(configWriteMock).toHaveBeenCalledWith(nextConfig); expect(refreshPluginRegistryMock).toHaveBeenCalledWith({ config: nextConfig, installRecords: {}, @@ -697,7 +697,7 @@ describe("plugins cli uninstall", () => { ), }), ); - expect(writeConfigFile).toHaveBeenCalledWith( + expect(configWriteMock).toHaveBeenCalledWith( expect.objectContaining({ channels: Object.fromEntries( Object.entries(channels).filter(([channelId]) => !channelIds.includes(channelId)), @@ -757,7 +757,7 @@ describe("plugins cli uninstall", () => { deleteFiles: false, }); expectInstallRecordsWrittenWithLease({}, nextConfig); - expect(writeConfigFile).toHaveBeenCalledWith(nextConfig); + expect(configWriteMock).toHaveBeenCalledWith(nextConfig); expectRuntimeLogIncludes("channel config (channels.alpha)"); expect(pluginsCliRuntimeLogs.at(-2)).toContain('Uninstalled plugin "alpha"'); }); diff --git a/src/cli/plugins-cli.update.test.ts b/src/cli/plugins-cli.update.test.ts index 6269f722faf4..c51587a2a97d 100644 --- a/src/cli/plugins-cli.update.test.ts +++ b/src/cli/plugins-cli.update.test.ts @@ -16,16 +16,16 @@ import { refreshPluginRegistryMock, replaceConfigFileMock, resetPluginsCliTestState, - restorePersistedInstalledPluginIndexIfCurrent, + restorePersistedInstalledPluginIndexIfCurrentMock, runPluginsCommand, runtimeErrors, pluginsCliRuntimeLogs, setInstalledPluginIndexInstallRecords, setHookInstallRecords, - updateNpmInstalledHookPacks, - updateNpmInstalledPlugins, - writeConfigFile, - writePersistedInstalledPluginIndexInstallRecordsWithLease, + updateNpmInstalledHookPacksMock, + updateNpmInstalledPluginsMock, + configWriteMock, + writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock, } from "./plugins-cli-test-helpers.js"; import { registerPluginsCli } from "./plugins-cli.js"; @@ -85,7 +85,7 @@ function expectRestartNoticeLogged() { } function expectInstallRecordsWrittenWithLease(records: unknown, config: unknown) { - expect(writePersistedInstalledPluginIndexInstallRecordsWithLease).toHaveBeenCalledWith( + expect(writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock).toHaveBeenCalledWith( records, expect.objectContaining({ config, @@ -166,10 +166,10 @@ function primeBlockedUpdateConfig(section: "hooks" | "plugins", config: OpenClaw function primePluginUpdate( config: OpenClawConfig, - outcomes: Awaited>["outcomes"] = [], + outcomes: Awaited>["outcomes"] = [], changed = false, ): void { - updateNpmInstalledPlugins.mockResolvedValue({ config, changed, outcomes }); + updateNpmInstalledPluginsMock.mockResolvedValue({ config, changed, outcomes }); } function primeBravePluginRecordUpdate(config: OpenClawConfig) { @@ -232,11 +232,11 @@ async function expectSkippedClawHubPluginUpdate(params: { message: params.message, }, ]); - updateNpmInstalledHookPacks.mockResolvedValue({ outcomes: [], changed: false, config }); + updateNpmInstalledHookPacksMock.mockResolvedValue({ outcomes: [], changed: false, config }); await expect(runPluginsCommand(["plugins", "update", "demo"])).rejects.toThrow("__exit__:1"); - expect(writePersistedInstalledPluginIndexInstallRecordsWithLease).not.toHaveBeenCalled(); + expect(writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock).not.toHaveBeenCalled(); expect(pluginsCliRuntimeLogs.at(-1)).toContain(params.expectedLog); } @@ -283,9 +283,9 @@ describe("plugins cli update", () => { } } - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); - expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); + expect(updateNpmInstalledHookPacksMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("previews plugin updates in Nix mode without acquiring a lease or writing state", async () => { @@ -309,13 +309,13 @@ describe("plugins cli update", () => { try { await runPluginsCommand(["plugins", "update", "alpha", "--dry-run"]); - expect(updateNpmInstalledPlugins).toHaveBeenCalledWith( + expect(updateNpmInstalledPluginsMock).toHaveBeenCalledWith( expect.objectContaining({ dryRun: true, pluginIds: ["alpha"] }), ); expect(acquireLease).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(replaceConfigFileMock).not.toHaveBeenCalled(); - expect(writePersistedInstalledPluginIndexInstallRecordsWithLease).not.toHaveBeenCalled(); + expect(writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock).not.toHaveBeenCalled(); expect(refreshPluginRegistryMock).not.toHaveBeenCalled(); expect(pluginsCliRuntimeLogs).toContain("Would update alpha: 1.0.0 -> 1.1.0."); } finally { @@ -340,9 +340,9 @@ describe("plugins cli update", () => { ); expect(runtimeErrors.at(-1)).toContain(`No tracked plugin or hook pack found for "${id}".`); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); - expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); + expect(updateNpmInstalledHookPacksMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("rejects an npm update target shared by multiple tracked plugins", async () => { @@ -374,9 +374,9 @@ describe("plugins cli update", () => { expect(runtimeErrors.at(-1)).toContain( 'No tracked plugin or hook pack found for "@acme/shared@beta".', ); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); - expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); + expect(updateNpmInstalledHookPacksMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("updates tracked hook packs through plugins update", async () => { @@ -393,7 +393,7 @@ describe("plugins cli update", () => { }, }); primePluginUpdate(cfg); - updateNpmInstalledHookPacks.mockResolvedValue({ + updateNpmInstalledHookPacksMock.mockResolvedValue({ config: nextConfig, changed: true, outcomes: [ @@ -407,11 +407,11 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "demo-hooks"]); - const hookUpdateParams = expectSingleCallParams(updateNpmInstalledHookPacks); + const hookUpdateParams = expectSingleCallParams(updateNpmInstalledHookPacksMock); expect(hookUpdateParams.config).toEqual({ ...cfg, plugins: { installs: {} } }); expect(hookUpdateParams.hookIds).toEqual(["demo-hooks"]); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); - expect(writeConfigFile).toHaveBeenCalledWith(nextConfig); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); + expect(configWriteMock).toHaveBeenCalledWith(nextConfig); expect(replaceConfigFileMock).toHaveBeenCalledWith( expect.objectContaining({ nextConfig, baseHash: "update-config" }), ); @@ -459,21 +459,25 @@ describe("plugins cli update", () => { installPath: "/home/test/.openclaw/hooks/new-hooks", }, }); - updateNpmInstalledPlugins.mockImplementation(async (params: { config: OpenClawConfig }) => ({ - config: params.config, - changed: false, - outcomes: [], - })); - updateNpmInstalledHookPacks.mockImplementation(async (params: { config: OpenClawConfig }) => ({ - config: params.config, - changed: false, - outcomes: [], - })); + updateNpmInstalledPluginsMock.mockImplementation( + async (params: { config: OpenClawConfig }) => ({ + config: params.config, + changed: false, + outcomes: [], + }), + ); + updateNpmInstalledHookPacksMock.mockImplementation( + async (params: { config: OpenClawConfig }) => ({ + config: params.config, + changed: false, + outcomes: [], + }), + ); await runPluginsCommand(["plugins", "update", "--all"]); - const pluginUpdateParams = expectSingleCallParams(updateNpmInstalledPlugins); - const hookUpdateParams = expectSingleCallParams(updateNpmInstalledHookPacks); + const pluginUpdateParams = expectSingleCallParams(updateNpmInstalledPluginsMock); + const hookUpdateParams = expectSingleCallParams(updateNpmInstalledHookPacksMock); expect(pluginUpdateParams.config).toEqual({ ...snapshotConfig, messages: { @@ -527,7 +531,7 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "alpha"]); - const updateParams = expectSingleCallParams(updateNpmInstalledPlugins); + const updateParams = expectSingleCallParams(updateNpmInstalledPluginsMock); expect(updateParams.config).toEqual({ ...cfg, plugins: { @@ -553,9 +557,9 @@ describe("plugins cli update", () => { expect(runtimeErrors.at(-1)).toBe( "Cannot update plugins or hooks while the config is invalid.", ); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); - expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); + expect(updateNpmInstalledHookPacksMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("allows index-only legacy id migration when an included plugins section has no references", async () => { @@ -593,10 +597,10 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "--all"]); expect(runtimeErrors).toEqual([]); - expect(updateNpmInstalledPlugins).toHaveBeenCalledOnce(); - expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).toHaveBeenCalledOnce(); + expect(updateNpmInstalledHookPacksMock).not.toHaveBeenCalled(); expectInstallRecordsWrittenWithLease(nextConfig.plugins?.installs, cfg); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("allows scoped non-npm updates beside include-owned plugin config", async () => { @@ -633,9 +637,9 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", pluginId]); expect(runtimeErrors).toEqual([]); - expect(updateNpmInstalledPlugins).toHaveBeenCalledOnce(); + expect(updateNpmInstalledPluginsMock).toHaveBeenCalledOnce(); expectInstallRecordsWrittenWithLease(pluginRecords, cfg); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("does not rewrite source config for persisted install record-only updates", async () => { @@ -674,7 +678,7 @@ describe("plugins cli update", () => { expect(runtimeErrors).toEqual([]); expectInstallRecordsWrittenWithLease(nextRecords, sourceCfg); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(replaceConfigFileMock).not.toHaveBeenCalled(); expect(refreshPluginRegistryMock).toHaveBeenCalledWith({ config: sourceCfg, @@ -800,7 +804,7 @@ describe("plugins cli update", () => { ); expectInstallRecordsWrittenWithLease(nextRecords, cfg); - expect(restorePersistedInstalledPluginIndexIfCurrent).toHaveBeenCalledWith( + expect(restorePersistedInstalledPluginIndexIfCurrentMock).toHaveBeenCalledWith( previousPersistedIndex, expect.any(Number), expect.objectContaining({ @@ -808,7 +812,7 @@ describe("plugins cli update", () => { lease: expect.anything(), }), ); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(replaceConfigFileMock).not.toHaveBeenCalled(); expect(refreshPluginRegistryMock).not.toHaveBeenCalled(); expect(notifyGatewayPluginMetadataChangedMock).not.toHaveBeenCalled(); @@ -862,7 +866,7 @@ describe("plugins cli update", () => { ); expectInstallRecordsWrittenWithLease(nextRecords, cfg); - expect(restorePersistedInstalledPluginIndexIfCurrent).toHaveBeenCalledWith( + expect(restorePersistedInstalledPluginIndexIfCurrentMock).toHaveBeenCalledWith( previousPersistedIndex, expect.any(Number), expect.objectContaining({ @@ -870,7 +874,7 @@ describe("plugins cli update", () => { lease: expect.anything(), }), ); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(replaceConfigFileMock).not.toHaveBeenCalled(); expect(refreshPluginRegistryMock).not.toHaveBeenCalled(); }); @@ -917,7 +921,7 @@ describe("plugins cli update", () => { ); expectInstallRecordsWrittenWithLease(nextRecords, cfg); - expect(restorePersistedInstalledPluginIndexIfCurrent).toHaveBeenCalledWith( + expect(restorePersistedInstalledPluginIndexIfCurrentMock).toHaveBeenCalledWith( previousPersistedIndex, expect.any(Number), expect.objectContaining({ @@ -925,7 +929,7 @@ describe("plugins cli update", () => { lease: expect.anything(), }), ); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(replaceConfigFileMock).not.toHaveBeenCalled(); expect(refreshPluginRegistryMock).not.toHaveBeenCalled(); }); @@ -954,9 +958,9 @@ describe("plugins cli update", () => { expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); - expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); + expect(updateNpmInstalledHookPacksMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("blocks catalog-declared plugin id migration before updater side effects", async () => { @@ -985,9 +989,9 @@ describe("plugins cli update", () => { expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); - expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); + expect(updateNpmInstalledHookPacksMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("blocks managed npm load-path reconciliation before updater side effects", async () => { @@ -1011,7 +1015,7 @@ describe("plugins cli update", () => { expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); }); it.each([ @@ -1063,8 +1067,8 @@ describe("plugins cli update", () => { expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }, ); @@ -1098,8 +1102,8 @@ describe("plugins cli update", () => { expect(runtimeErrors.at(-1)).toContain( "Config plugins are stored in an external or unresolved top-level $include", ); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("ignores retired plugin records during hook-only ownership checks", async () => { @@ -1126,13 +1130,13 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "demo-hooks"]); expect(runtimeErrors).toEqual([]); - const hookUpdateParams = expectSingleCallParams(updateNpmInstalledHookPacks); + const hookUpdateParams = expectSingleCallParams(updateNpmInstalledHookPacksMock); expect(hookUpdateParams.config).toEqual({ ...cfg, plugins: { installs: {} }, }); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("preserves skip behavior for plugin records whose source cannot be updated", async () => { @@ -1155,9 +1159,9 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "--all"]); - expect(updateNpmInstalledPlugins).toHaveBeenCalledOnce(); - expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).toHaveBeenCalledOnce(); + expect(updateNpmInstalledHookPacksMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("preserves skip behavior for ClawHub records missing package metadata", async () => { @@ -1187,9 +1191,9 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "demo"]); expect(runtimeErrors).toEqual([]); - expect(updateNpmInstalledPlugins).toHaveBeenCalledOnce(); - expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).toHaveBeenCalledOnce(); + expect(updateNpmInstalledHookPacksMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("exits when update is called without id and without --all", async () => { @@ -1202,7 +1206,7 @@ describe("plugins cli update", () => { await expect(runPluginsCommand(["plugins", "update"])).rejects.toThrow("__exit__:1"); expect(runtimeErrors.at(-1)).toContain("Provide a plugin or hook-pack id, or use --all."); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); }); it("reports no tracked plugins or hook packs when update --all has empty install records", async () => { @@ -1214,8 +1218,8 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "--all"]); - expect(updateNpmInstalledPlugins).not.toHaveBeenCalled(); - expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled(); + expect(updateNpmInstalledPluginsMock).not.toHaveBeenCalled(); + expect(updateNpmInstalledHookPacksMock).not.toHaveBeenCalled(); expect(pluginsCliRuntimeLogs.at(-1)).toBe("No tracked plugins or hook packs to update."); }); @@ -1235,7 +1239,7 @@ describe("plugins cli update", () => { "--dangerously-force-unsafe-install", ]); - const updateParams = expectSingleCallParams(updateNpmInstalledPlugins); + const updateParams = expectSingleCallParams(updateNpmInstalledPluginsMock); expect(updateParams.config).toEqual(config); expect(updateParams.pluginIds).toEqual(["openclaw-codex-app-server"]); expect(updateParams.dangerouslyForceUnsafeInstall).toBe(true); @@ -1274,7 +1278,7 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "codex"]); - const updateParams = expectSingleCallParams(updateNpmInstalledPlugins); + const updateParams = expectSingleCallParams(updateNpmInstalledPluginsMock); expect(updateParams.pluginIds).toEqual(["codex"]); expect(updateParams.syncOfficialPluginInstalls).toBeUndefined(); expect(updateParams.updateChannel).toBe(updateChannel); @@ -1296,7 +1300,7 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "codex"]); - const updateParams = expectSingleCallParams(updateNpmInstalledPlugins); + const updateParams = expectSingleCallParams(updateNpmInstalledPluginsMock); expect(updateParams.syncOfficialPluginInstalls).toBeUndefined(); expect(updateParams.updateChannel).toBeUndefined(); expect(updateParams.officialPluginUpdateChannel).toBe( @@ -1317,7 +1321,7 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "--all"]); - const updateParams = expectSingleCallParams(updateNpmInstalledPlugins); + const updateParams = expectSingleCallParams(updateNpmInstalledPluginsMock); expect(updateParams.pluginIds).toEqual(["codex"]); expect(updateParams.syncOfficialPluginInstalls).toBe(true); expect(updateParams.officialPluginUpdateChannel).toBe("beta"); @@ -1336,7 +1340,7 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "--all"]); - const updateParams = expectSingleCallParams(updateNpmInstalledPlugins); + const updateParams = expectSingleCallParams(updateNpmInstalledPluginsMock); expect(updateParams.officialPluginUpdateChannel).toBe( resolveRegistryUpdateChannel({ currentVersion: VERSION }), ); @@ -1355,7 +1359,7 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "--all"]); - expect(updateNpmInstalledPlugins).toHaveBeenCalledWith( + expect(updateNpmInstalledPluginsMock).toHaveBeenCalledWith( expect.objectContaining({ officialPluginUpdateChannel: "extended-stable", syncOfficialPluginInstalls: true, @@ -1380,7 +1384,7 @@ describe("plugins cli update", () => { "--acknowledge-clawhub-risk", ]); - expect(updateNpmInstalledPlugins).toHaveBeenCalledWith( + expect(updateNpmInstalledPluginsMock).toHaveBeenCalledWith( expect.objectContaining({ config, pluginIds: ["openclaw-codex-app-server"], @@ -1401,7 +1405,7 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "openclaw-codex-app-server", "--dry-run"]); - const updateParams = expectSingleCallParams(updateNpmInstalledPlugins); + const updateParams = expectSingleCallParams(updateNpmInstalledPluginsMock); expect(updateParams.dryRun).toBe(true); expect(updateParams.acknowledgeClawHubRisk).not.toBe(true); expect(updateParams.onClawHubRisk).toBeUndefined(); @@ -1451,7 +1455,7 @@ describe("plugins cli update", () => { [{ pluginId: "alpha", status: "updated", message: "Updated alpha -> 1.1.0" }], true, ); - updateNpmInstalledHookPacks.mockResolvedValue({ + updateNpmInstalledHookPacksMock.mockResolvedValue({ outcomes: [], changed: false, config: nextRuntimeConfig, @@ -1459,13 +1463,13 @@ describe("plugins cli update", () => { await runPluginsCommand(["plugins", "update", "alpha"]); - const updateParams = expectSingleCallParams(updateNpmInstalledPlugins); + const updateParams = expectSingleCallParams(updateNpmInstalledPluginsMock); expect(updateParams.config).toEqual(runtimeConfig); expect(updateParams.pluginIds).toEqual(["alpha"]); expect(updateParams.dryRun).toBe(false); expectInstallRecordsWrittenWithLease(nextConfig.plugins?.installs, {}); - expect(updateNpmInstalledHookPacks).not.toHaveBeenCalled(); - expect(writeConfigFile).toHaveBeenCalledWith({}); + expect(updateNpmInstalledHookPacksMock).not.toHaveBeenCalled(); + expect(configWriteMock).toHaveBeenCalledWith({}); expect(replaceConfigFileMock).toHaveBeenCalledWith({ nextConfig: {}, baseHash: "update-config", @@ -1522,7 +1526,7 @@ describe("plugins cli update", () => { ], true, ); - updateNpmInstalledHookPacks.mockResolvedValue({ + updateNpmInstalledHookPacksMock.mockResolvedValue({ outcomes: [], changed: false, config: nextConfig, @@ -1579,7 +1583,7 @@ describe("plugins cli update", () => { }, }); primePluginUpdate(cfg); - updateNpmInstalledHookPacks.mockResolvedValue({ + updateNpmInstalledHookPacksMock.mockResolvedValue({ config: cfg, changed: false, outcomes: [ @@ -1595,7 +1599,7 @@ describe("plugins cli update", () => { "__exit__:1", ); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); expect(pluginsCliRuntimeLogs).toContain( 'Failed to update hook pack "demo-hooks": registry timeout', ); diff --git a/src/cli/plugins-install-bundled-failures.test.ts b/src/cli/plugins-install-bundled-failures.test.ts index fe51c43c8d02..35773e2eb75d 100644 --- a/src/cli/plugins-install-bundled-failures.test.ts +++ b/src/cli/plugins-install-bundled-failures.test.ts @@ -5,7 +5,7 @@ import { resetPluginsCliTestState, runPluginsCommand, runtimeErrors, - writeConfigFile, + configWriteMock, } from "./plugins-cli-test-helpers.js"; const { installManagedPluginSourceMock } = vi.hoisted(() => ({ @@ -43,7 +43,7 @@ describe("plugin install bundled failure propagation", () => { }), ); expect(runtimeErrors.at(-1)).toContain("bundled plugin installation failed"); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("fails when an npm package-not-found bundled fallback fails", async () => { @@ -87,6 +87,6 @@ describe("plugin install bundled failure propagation", () => { }), ); expect(runtimeErrors.at(-1)).toContain("bundled fallback installation failed"); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); }); diff --git a/src/cli/plugins-install-preflight.test.ts b/src/cli/plugins-install-preflight.test.ts index 8ca2a902efde..e7ae648b9de8 100644 --- a/src/cli/plugins-install-preflight.test.ts +++ b/src/cli/plugins-install-preflight.test.ts @@ -13,10 +13,10 @@ import { promptYesNoMock, readConfigFileSnapshotForWriteMock, resetPluginsCliTestState, - resolveMarketplaceInstallShortcut, + resolveMarketplaceInstallShortcutMock, runPluginsCommand, runtimeErrors, - writeConfigFile, + configWriteMock, } from "./plugins-cli-test-helpers.js"; import { resolvePluginInstallPreflight } from "./plugins-install-preflight.js"; @@ -40,7 +40,7 @@ function expectNoPluginInstallSideEffects(): void { expect(installPluginFromPathMock).not.toHaveBeenCalled(); expect(installHooksFromNpmSpecMock).not.toHaveBeenCalled(); expect(installHooksFromPathMock).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); } describe("plugin install mutation-free preflight", () => { @@ -54,7 +54,7 @@ describe("plugin install mutation-free preflight", () => { }); it("resolves registered marketplace shorthand before ordinary source classification", async () => { - resolveMarketplaceInstallShortcut.mockResolvedValue({ + resolveMarketplaceInstallShortcutMock.mockResolvedValue({ ok: true, plugin: "superpowers", marketplaceName: "claude-plugins-official", diff --git a/src/cli/program.smoke.test.ts b/src/cli/program.smoke.test.ts index 565e5c7b2851..518deef7092d 100644 --- a/src/cli/program.smoke.test.ts +++ b/src/cli/program.smoke.test.ts @@ -5,11 +5,11 @@ import { programGatewayCallMock, configureCommand, ensureConfigReadyMock, - runSystemAgentWithInference, - runTui, runtime, - setupCommand, - setupWizardCommand, + setupCommandMock, + setupWizardCommandMock, + systemAgentRunMock, + tuiRunMock, } from "./program.test-mocks.js"; vi.mock("./config-cli.js", () => ({ @@ -44,8 +44,8 @@ describe("cli program (smoke)", () => { beforeEach(() => { program = createProgram(); vi.clearAllMocks(); - runTui.mockResolvedValue(undefined); - runSystemAgentWithInference.mockResolvedValue(undefined); + tuiRunMock.mockResolvedValue(undefined); + systemAgentRunMock.mockResolvedValue(undefined); ensureConfigReadyMock.mockResolvedValue(undefined); }); @@ -57,7 +57,7 @@ describe("cli program (smoke)", () => { it("runs tui with explicit timeout override", async () => { await runProgram(["tui", "--timeout-ms", "45000"]); - const options = firstMockArg(runTui) as { + const options = firstMockArg(tuiRunMock) as { timeoutMs?: number; historyLimit?: number; forceProcessExitOnReturn?: boolean; @@ -78,7 +78,7 @@ describe("cli program (smoke)", () => { params: { shortId: "a1166b81", slugHint: "movies" }, }), ); - expect(firstMockArg(runTui)).toMatchObject({ + expect(firstMockArg(tuiRunMock)).toMatchObject({ local: false, session: "agent:main:thread:resolved", }); @@ -100,7 +100,7 @@ describe("cli program (smoke)", () => { params: {}, }), ); - expect(firstMockArg(runTui)).toMatchObject({ + expect(firstMockArg(tuiRunMock)).toMatchObject({ local: false, session: "global", agentId: "ops", @@ -110,7 +110,7 @@ describe("cli program (smoke)", () => { it("leaves tui agent inference unchanged without a URL agent", async () => { await runProgram(["tui"]); - expect(firstMockArg(runTui)).not.toHaveProperty("agentId"); + expect(firstMockArg(tuiRunMock)).not.toHaveProperty("agentId"); }); it("rejects a URL target combined with --url", async () => { @@ -124,12 +124,12 @@ describe("cli program (smoke)", () => { ).rejects.toThrow("exit"); expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("pass one target")); - expect(runTui).not.toHaveBeenCalled(); + expect(tuiRunMock).not.toHaveBeenCalled(); }); it("runs setup one-shot requests", async () => { await runProgram(["setup", "--message", "status"]); - const options = firstMockArg(runSystemAgentWithInference) as { + const options = firstMockArg(systemAgentRunMock) as { message?: string; yes?: boolean; json?: boolean; @@ -137,13 +137,13 @@ describe("cli program (smoke)", () => { expect(options?.message).toBe("status"); expect(options?.yes).toBe(false); expect(options?.json).toBe(false); - expect(runSystemAgentWithInference).toHaveBeenCalledWith(options, runtime); + expect(systemAgentRunMock).toHaveBeenCalledWith(options, runtime); }); it("warns and ignores invalid tui timeout override", async () => { await runProgram(["tui", "--timeout-ms", "nope"]); expect(runtime.error).toHaveBeenCalledWith('warning: invalid --timeout-ms "nope"; ignoring'); - const options = firstMockArg(runTui) as { timeoutMs?: number }; + const options = firstMockArg(tuiRunMock) as { timeoutMs?: number }; expect(options?.timeoutMs).toBeUndefined(); }); @@ -152,13 +152,13 @@ describe("cli program (smoke)", () => { expect(runtime.error).toHaveBeenCalledWith( "Error: --history-limit must be a positive integer.", ); - expect(runTui).not.toHaveBeenCalled(); + expect(tuiRunMock).not.toHaveBeenCalled(); }); it("accepts the maximum Gateway tui history limit", async () => { await runProgram(["tui", "--history-limit", "1000"]); - expect(firstMockArg(runTui)).toMatchObject({ local: false, historyLimit: 1000 }); + expect(firstMockArg(tuiRunMock)).toMatchObject({ local: false, historyLimit: 1000 }); }); it.each([ @@ -168,7 +168,7 @@ describe("cli program (smoke)", () => { ])("preserves oversized history limits for local $entryPoint", async ({ args }) => { await runProgram([...args, "--history-limit", "1001"]); - expect(firstMockArg(runTui)).toMatchObject({ local: true, historyLimit: 1001 }); + expect(firstMockArg(tuiRunMock)).toMatchObject({ local: true, historyLimit: 1001 }); expect(runtime.error).not.toHaveBeenCalled(); }); @@ -176,13 +176,13 @@ describe("cli program (smoke)", () => { await expect(runProgram(["tui", "--history-limit", "1001"])).rejects.toThrow("exit"); expect(runtime.error).toHaveBeenCalledWith("Error: --history-limit must be at most 1000."); - expect(runTui).not.toHaveBeenCalled(); + expect(tuiRunMock).not.toHaveBeenCalled(); }); it("runs setup wizard when wizard flags are present", async () => { await runProgram(["setup", "--remote-url", "ws://example"]); - expect(setupCommand).not.toHaveBeenCalled(); - expect(setupWizardCommand).toHaveBeenCalledTimes(1); + expect(setupCommandMock).not.toHaveBeenCalled(); + expect(setupWizardCommandMock).toHaveBeenCalledTimes(1); }); }); diff --git a/src/cli/program.test-mocks.ts b/src/cli/program.test-mocks.ts index f1bbf676d7d1..f7ad19727b24 100644 --- a/src/cli/program.test-mocks.ts +++ b/src/cli/program.test-mocks.ts @@ -39,11 +39,11 @@ const programMocks = vi.hoisted(() => { }); export const configureCommand = programMocks.configureCommand as AnyMock; -export const setupCommand = programMocks.setupCommand as AnyMock; -export const setupWizardCommand = programMocks.setupWizardCommand as AnyMock; +export const setupCommandMock = programMocks.setupCommand as AnyMock; +export const setupWizardCommandMock = programMocks.setupWizardCommand as AnyMock; export const programGatewayCallMock = programMocks.callGateway as AnyMock; -export const runTui = programMocks.runTui as AnyMock; -export const runSystemAgentWithInference = programMocks.runSystemAgentWithInference as AnyMock; +export const tuiRunMock = programMocks.runTui as AnyMock; +export const systemAgentRunMock = programMocks.runSystemAgentWithInference as AnyMock; export const ensureConfigReadyMock = programMocks.ensureConfigReady as AnyMock; export const runtime = programMocks.runtime as { diff --git a/src/cli/update-cli/shared.command-runner.test.ts b/src/cli/update-cli/shared.command-runner.test.ts index 399885b60b1d..8930a19617bc 100644 --- a/src/cli/update-cli/shared.command-runner.test.ts +++ b/src/cli/update-cli/shared.command-runner.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { defaultRuntime } from "../../runtime.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { createGlobalCommandRunner, parseTimeoutMsOrExit, resolveUpdateRoot } from "./shared.js"; const runCommandWithTimeout = vi.hoisted(() => vi.fn()); @@ -98,7 +98,7 @@ describe("createGlobalCommandRunner", () => { it.runIf(process.platform !== "win32")( "resolves update ownership from the lexical invocation path", async () => { - await withTempDir({ prefix: "openclaw-update-root-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-root-" }, async (base) => { const storeRoot = path.join(base, "store", "openclaw"); const packageRoot = path.join(base, "global", "v11", "install", "node_modules", "openclaw"); await fs.mkdir(path.dirname(packageRoot), { recursive: true }); diff --git a/src/cli/update-cli/update-command-execution.ts b/src/cli/update-cli/update-command-execution.ts index dcd63049f2e7..9b1a92f6712a 100644 --- a/src/cli/update-cli/update-command-execution.ts +++ b/src/cli/update-cli/update-command-execution.ts @@ -12,7 +12,7 @@ import { createBeforeGitMutation, formatSchemaRefusalLines, hasSchemaRefusal, - runGitUpdate, + updateGitInstall, } from "./update-command-git.js"; import { captureOwnedManagedUpdateContext, @@ -212,7 +212,7 @@ export async function executeMutableUpdate(params: { installEnv: params.packageInstallEnv, installTarget: params.packageInstallTarget, }) - : await runGitUpdate({ + : await updateGitInstall({ root: params.root, switchToGit: params.switchToGit, installKind: params.installKind, diff --git a/src/cli/update-cli/update-command-git.ts b/src/cli/update-cli/update-command-git.ts index d46a5d5aaba1..00f7ec26bfea 100644 --- a/src/cli/update-cli/update-command-git.ts +++ b/src/cli/update-cli/update-command-git.ts @@ -116,7 +116,7 @@ export function createBeforeGitMutation(params: { }; } -export async function runGitUpdate(params: { +export async function updateGitInstall(params: { root: string; switchToGit: boolean; installKind: "git" | "package" | "unknown"; diff --git a/src/commands/agent.worktree-race.test.ts b/src/commands/agent.worktree-race.test.ts index 83005604a35d..e546e8d58ad8 100644 --- a/src/commands/agent.worktree-race.test.ts +++ b/src/commands/agent.worktree-race.test.ts @@ -10,7 +10,7 @@ import { prepareAgentCommandExecution } from "../agents/command/prepare.js"; import { ensureAgentWorkspace } from "../agents/workspace.js"; import { getRegistryWorktree } from "../agents/worktrees/registry.js"; import { managedWorktrees } from "../agents/worktrees/service.js"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.sqlite-entry.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.sqlite-entry.js"; import { clearSessionStoreCacheForTest } from "../config/sessions/store-writer-state.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js"; @@ -74,7 +74,7 @@ async function seedSession( spawnedCwd: string, worktree?: { id: string; branch: string; repoRoot: string }, ): Promise { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "session-worktree-race", diff --git a/src/commands/agent/session.test.ts b/src/commands/agent/session.test.ts index 939d1e4a9084..4bfa348e96cc 100644 --- a/src/commands/agent/session.test.ts +++ b/src/commands/agent/session.test.ts @@ -25,7 +25,7 @@ vi.mock("../../config/sessions/session-accessor.js", () => ({ })); vi.mock("../../config/sessions/paths.js", () => ({ - resolveStorePath: mocks.resolveStorePath, + resolveSessionStorePathCore: mocks.resolveStorePath, })); vi.mock("../../agents/agent-scope.js", async () => { diff --git a/src/commands/agents.commands.list.test.ts b/src/commands/agents.commands.list.test.ts index 0b6766bf9865..bc846edaebfc 100644 --- a/src/commands/agents.commands.list.test.ts +++ b/src/commands/agents.commands.list.test.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { OutputRuntimeEnv } from "../runtime.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withEnvAsync } from "../test-utils/env.js"; const { @@ -147,7 +147,7 @@ describe("agentsListCommand", () => { it.skipIf(process.platform !== "win32")( "shortens real Windows home casing aliases in human output", async () => { - await withTempDir({ prefix: "openclaw-home-display-" }, async (home) => { + await withTestDir({ prefix: "openclaw-home-display-" }, async (home) => { const workspace = path.join(home, "workspace"); const agentDir = path.join(home, "agents", "main", "agent"); await fs.promises.mkdir(workspace, { recursive: true }); diff --git a/src/commands/agents.delete.test.ts b/src/commands/agents.delete.test.ts index a3a21df0550f..bc9e508a04f7 100644 --- a/src/commands/agents.delete.test.ts +++ b/src/commands/agents.delete.test.ts @@ -7,7 +7,7 @@ import { resolveDefaultAgentId, toAgentEntriesRecord, } from "../agents/agent-scope-config.js"; -import { resolveStorePath } from "../config/sessions.js"; +import { resolveSessionStorePathCore } from "../config/sessions.js"; import type { SessionEntry } from "../config/sessions.js"; import { listSessionEntriesCore, @@ -105,10 +105,12 @@ async function arrangeAgentsDeleteTest(params: { agents: { ...agents, entries: toAgentEntriesRecord(roster) }, }; const storeAgentId = resolveFixtureStoreAgentId(cfg, deletedAgentId); - const storePath = resolveStorePath(cfg.session?.store, { agentId: deletedAgentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: deletedAgentId }); for (const [sessionKey, entry] of Object.entries(params.sessions)) { const entryAgentId = parseAgentSessionKey(sessionKey)?.agentId ?? storeAgentId; - const entryStorePath = resolveStorePath(cfg.session?.store, { agentId: entryAgentId }); + const entryStorePath = resolveSessionStorePathCore(cfg.session?.store, { + agentId: entryAgentId, + }); await replaceSessionEntry({ agentId: entryAgentId, sessionKey, storePath: entryStorePath }, { ...entry, delivery: { kind: "none" }, @@ -147,7 +149,7 @@ function expectSessionStore( [...agentIds].flatMap((storeAgentId) => listSessionEntriesCore({ agentId: storeAgentId, - storePath: resolveStorePath(cfg.session?.store, { agentId: storeAgentId }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: storeAgentId }), }).map(({ entry, sessionKey }) => [sessionKey, entry]), ), ), diff --git a/src/commands/doctor-auth-oauth-sidecar.ts b/src/commands/doctor-auth-oauth-sidecar.ts index f19ea3bbfc4a..f704d2015c6a 100644 --- a/src/commands/doctor-auth-oauth-sidecar.ts +++ b/src/commands/doctor-auth-oauth-sidecar.ts @@ -10,7 +10,7 @@ import { clearRuntimeAuthProfileStoreSnapshots } from "../agents/auth-profiles/r import { formatCliCommand } from "../cli/command-format.js"; import { resolveOAuthDir, resolveStateDir } from "../config/paths.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; -import { loadJsonFileThroughSymlink, saveJsonFile } from "../infra/json-file.js"; +import { loadJsonFileThroughSymlink, writeJsonTarget } from "../infra/json-file.js"; import { shortenHomePath } from "../utils.js"; import { resolveLegacyAuthProfilesPath as resolveAuthStorePath } from "./doctor-auth-legacy-paths.js"; import type { DoctorPrompter } from "./doctor-prompter.js"; @@ -280,7 +280,7 @@ export async function maybeRepairLegacyOAuthSidecarProfiles(params: { if (!("version" in store.raw)) { store.raw.version = AUTH_STORE_VERSION; } - saveJsonFile(store.authPath, store.raw); + writeJsonTarget(store.authPath, store.raw); for (const [refId, sidecarPath] of storeMigratedSidecarsByRefId) { migratedSidecarsByRefId.set(refId, sidecarPath); } diff --git a/src/commands/doctor-heartbeat-main-session-repair.ts b/src/commands/doctor-heartbeat-main-session-repair.ts index 5e80aa6699e6..6d2bffd5dbf2 100644 --- a/src/commands/doctor-heartbeat-main-session-repair.ts +++ b/src/commands/doctor-heartbeat-main-session-repair.ts @@ -7,7 +7,7 @@ import { isHeartbeatOkResponse, isHeartbeatUserMessage } from "../auto-reply/hea import { formatSessionArchiveTimestamp } from "../config/sessions/artifacts.js"; import { resolveMainSessionKey } from "../config/sessions/main-session.js"; import { - resolveSessionFilePath, + resolveSessionFilePathCore, type resolveSessionFilePathOptions, } from "../config/sessions/paths.js"; import type { SessionEntry } from "../config/sessions/types.js"; @@ -294,7 +294,11 @@ export async function repairHeartbeatPoisonedMainSession(params: { } let transcriptPath: string | undefined; try { - transcriptPath = resolveSessionFilePath(mainEntry.sessionId, mainEntry, params.sessionPathOpts); + transcriptPath = resolveSessionFilePathCore( + mainEntry.sessionId, + mainEntry, + params.sessionPathOpts, + ); } catch { transcriptPath = undefined; } diff --git a/src/commands/doctor-heartbeat-session-target.test.ts b/src/commands/doctor-heartbeat-session-target.test.ts index 55ccab282d92..aea2486641b5 100644 --- a/src/commands/doctor-heartbeat-session-target.test.ts +++ b/src/commands/doctor-heartbeat-session-target.test.ts @@ -3,8 +3,8 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { resolveStorePath } from "../config/sessions/paths.js"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { closeOpenClawAgentDatabasesForTest } from "../state/openclaw-agent-db.js"; import { describeHeartbeatSessionTargetIssues } from "./doctor-heartbeat-session-target.js"; @@ -63,7 +63,7 @@ describe("describeHeartbeatSessionTargetIssues", () => { } function writeStore(cfg: OpenClawConfig, entries: Record) { - const storePath = resolveStorePath(cfg.session?.store, { agentId: "ops" }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: "ops" }); fs.mkdirSync(path.dirname(storePath), { recursive: true }); fs.writeFileSync(storePath, JSON.stringify(entries, null, 2)); } @@ -82,8 +82,8 @@ describe("describeHeartbeatSessionTargetIssues", () => { it("recognizes a SQLite-resident heartbeat target", async () => { const cfg = cfgWithSession("slack:channel:c123"); - const storePath = resolveStorePath(cfg.session?.store, { agentId: "ops" }); - await upsertSessionEntry( + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: "ops" }); + await upsertSessionEntryCore( { agentId: "ops", sessionKey: "agent:ops:slack:channel:c123", storePath }, { sessionId: "sqlite-heartbeat-target", updatedAt: Date.now() }, ); diff --git a/src/commands/doctor-heartbeat-session-target.ts b/src/commands/doctor-heartbeat-session-target.ts index 76c9deab39d1..e94d2ba53898 100644 --- a/src/commands/doctor-heartbeat-session-target.ts +++ b/src/commands/doctor-heartbeat-session-target.ts @@ -3,7 +3,7 @@ import fs from "node:fs"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { listAgentEntries, listAgentIds, resolveAgentConfig } from "../agents/agent-scope.js"; import { canonicalizeMainSessionAlias } from "../config/sessions/main-session.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { loadSessionEntryReadOnly } from "../config/sessions/session-accessor.js"; import type { AgentDefaultsConfig } from "../config/types.agent-defaults.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; @@ -118,7 +118,7 @@ export function describeHeartbeatSessionTargetIssues(cfg: OpenClawConfig): strin continue; } const storeAgentId = resolvedAgentId; - const storePath = resolveStorePath(cfg.session?.store, { agentId: storeAgentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: storeAgentId }); const entry = loadSessionEntryReadOnly({ agentId: storeAgentId, diff --git a/src/commands/doctor-install.test.ts b/src/commands/doctor-install.test.ts index 1a9aae2f8c38..ff56d4826ce9 100644 --- a/src/commands/doctor-install.test.ts +++ b/src/commands/doctor-install.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { note } from "../../packages/terminal-core/src/note.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { noteSourceInstallIssues } from "./doctor-install.js"; vi.mock("../../packages/terminal-core/src/note.js", () => ({ @@ -22,7 +22,7 @@ describe("noteSourceInstallIssues", () => { }); it("does not treat a packaged workspace config as a source checkout", async () => { - await withTempDir({ prefix: "openclaw-doctor-install-" }, async (root) => { + await withTestDir({ prefix: "openclaw-doctor-install-" }, async (root) => { await fs.mkdir(path.join(root, "node_modules"), { recursive: true }); await writeFile(root, "pnpm-workspace.yaml", "packages:\n - .\n"); @@ -33,7 +33,7 @@ describe("noteSourceInstallIssues", () => { }); it("warns source checkouts when node_modules was not installed by pnpm", async () => { - await withTempDir({ prefix: "openclaw-doctor-install-" }, async (root) => { + await withTestDir({ prefix: "openclaw-doctor-install-" }, async (root) => { await fs.mkdir(path.join(root, "node_modules"), { recursive: true }); await writeFile(root, "pnpm-workspace.yaml", "packages:\n - .\n"); await writeFile(root, "src/entry.ts", "export {};\n"); diff --git a/src/commands/doctor-main-session-recovery.test.ts b/src/commands/doctor-main-session-recovery.test.ts index d62db7758c77..62cac883ba9a 100644 --- a/src/commands/doctor-main-session-recovery.test.ts +++ b/src/commands/doctor-main-session-recovery.test.ts @@ -2,7 +2,7 @@ import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; import type { InternalSessionEntry } from "../config/sessions.js"; -import { loadSessionEntry, upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { loadSessionEntry, upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import { closeOpenClawAgentDatabasesForTest } from "../state/openclaw-agent-db.js"; import { noteMainSessionRecoveryIntegrity } from "./doctor-main-session-recovery.js"; @@ -27,7 +27,7 @@ describe("doctor main-session recovery integrity", () => { }); async function writeTombstone(abortedLastRun: boolean): Promise { - await upsertSessionEntry({ agentId, sessionKey, storePath }, { + await upsertSessionEntryCore({ agentId, sessionKey, storePath }, { sessionId: "session-wedged-main", updatedAt: abortedLastRun ? 0 : 1, status: "failed", diff --git a/src/commands/doctor-memory-search.test.ts b/src/commands/doctor-memory-search.test.ts index b899dcb99232..366af6cca171 100644 --- a/src/commands/doctor-memory-search.test.ts +++ b/src/commands/doctor-memory-search.test.ts @@ -17,7 +17,7 @@ const resolveAgentWorkspaceDir = vi.hoisted(() => vi.fn<(_cfg: OpenClawConfig, agentId: string) => string>(() => "/tmp/agent-default/workspace"), ); const resolveMemorySearchConfig = vi.hoisted(() => vi.fn()); -const resolveApiKeyForProvider = vi.hoisted(() => vi.fn()); +const resolveApiKeyForProviderCore = vi.hoisted(() => vi.fn()); const hasAnyAuthProfileStoreSource = vi.hoisted(() => vi.fn(() => true)); const hasAuthProfileStoreSourceForProvider = vi.hoisted(() => vi.fn(() => true)); const isConfiguredAwsSdkAuthProfileForProvider = vi.hoisted(() => vi.fn(() => false)); @@ -46,7 +46,7 @@ vi.mock("../agents/memory-search.js", () => ({ })); vi.mock("../agents/model-auth.js", () => ({ - resolveApiKeyForProvider, + resolveApiKeyForProviderCore, resolveEnvApiKey: vi.fn(() => null), resolveUsableCustomProviderApiKey: vi.fn(() => null), })); @@ -294,8 +294,8 @@ describe("noteMemorySearchHealth", () => { resolveAgentDir.mockClear(); resolveAgentWorkspaceDir.mockClear(); resolveMemorySearchConfig.mockReset(); - resolveApiKeyForProvider.mockReset(); - resolveApiKeyForProvider.mockRejectedValue(new Error("missing key")); + resolveApiKeyForProviderCore.mockReset(); + resolveApiKeyForProviderCore.mockRejectedValue(new Error("missing key")); hasAnyAuthProfileStoreSource.mockReset(); hasAnyAuthProfileStoreSource.mockReturnValue(true); hasAuthProfileStoreSourceForProvider.mockReset(); @@ -359,7 +359,7 @@ describe("noteMemorySearchHealth", () => { await runMemorySearchHealth("none", {}, { fallback: "none" }); expect(note).not.toHaveBeenCalled(); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); }); it("still reports a missing memory backend in intentional FTS-only mode", async () => { @@ -370,7 +370,7 @@ describe("noteMemorySearchHealth", () => { "No active memory plugin is registered for the current config.", "Memory search", ); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); }); it("reports last-known llama.cpp runtime facts from the gateway", async () => { @@ -518,7 +518,7 @@ describe("noteMemorySearchHealth", () => { resolveActiveMemoryBackendConfig.mockReturnValue(null); await runMemorySearchHealth("auto", {}); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); expect(note).toHaveBeenCalledTimes(1); expect(firstNoteMessage()).toContain("No active memory plugin is registered"); }); @@ -557,7 +557,7 @@ describe("noteMemorySearchHealth", () => { resolveActiveMemoryBackendConfig.mockReturnValue(null); const config = { session: { dmScope: "per-peer" }, plugins } as unknown as OpenClawConfig; await runConfiguredMemorySearch("auto", config); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); if (isActive) { expect(note).not.toHaveBeenCalled(); } else { @@ -587,7 +587,7 @@ describe("noteMemorySearchHealth", () => { ])("%s", async (_name, options, shouldWarn) => { resolveActiveMemoryBackendConfig.mockReturnValue(null); await runMemorySearchHealth("auto", options); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); if (shouldWarn) { expect(note).toHaveBeenCalledTimes(1); expect(firstNoteMessage()).toContain("No active memory plugin is registered"); @@ -721,7 +721,7 @@ describe("noteMemorySearchHealth", () => { ])("%s", async (_name, provider, apiKey) => { await runMemorySearchHealth(provider, {}, { remote: { apiKey } }); expect(note).not.toHaveBeenCalled(); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); }); it.each([ @@ -733,13 +733,13 @@ describe("noteMemorySearchHealth", () => { "MISTRAL", ], ])("%s", async (_name, provider, authProvider, envPrefix) => { - resolveApiKeyForProvider.mockResolvedValue({ + resolveApiKeyForProviderCore.mockResolvedValue({ apiKey: "k", source: `env: ${envPrefix}_API_KEY`, mode: "api-key", }); await runMemorySearchHealth(provider); - expect(resolveApiKeyForProvider).toHaveBeenCalledWith({ + expect(resolveApiKeyForProviderCore).toHaveBeenCalledWith({ provider: authProvider, cfg, agentDir: "/tmp/agent-default", @@ -849,7 +849,7 @@ describe("noteMemorySearchHealth", () => { expectFirstNoteContains(...contains); } if (noApiKeyLookup) { - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); } }); @@ -861,7 +861,7 @@ describe("noteMemorySearchHealth", () => { "openai", "/tmp/agent-default", ); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); }); it.each([ @@ -880,7 +880,7 @@ describe("noteMemorySearchHealth", () => { { profileIds }, ); expect(firstNoteMessage()).toContain('provider is set to "openai"'); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); }); it("does not warn for Bedrock aws-sdk provider auth when lint skips profile resolution", async () => { @@ -897,7 +897,7 @@ describe("noteMemorySearchHealth", () => { expect(note).not.toHaveBeenCalled(); expect(hasAuthProfileStoreSourceForProvider).not.toHaveBeenCalled(); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); }); it("does not warn for ordered Bedrock aws-sdk auth profiles when lint skips profile resolution", async () => { @@ -923,7 +923,7 @@ describe("noteMemorySearchHealth", () => { expect(note).not.toHaveBeenCalled(); expect(hasAuthProfileStoreSourceForProvider).not.toHaveBeenCalled(); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); }); it("warns for empty auth profile sources when lint skips profile resolution", async () => { @@ -936,7 +936,7 @@ describe("noteMemorySearchHealth", () => { "openai", "/tmp/agent-default", ); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); }); it("warns without resolving auth profiles when lint skips profile resolution and no auth store exists", async () => { @@ -945,7 +945,7 @@ describe("noteMemorySearchHealth", () => { await runAuthLintHealth("openai"); expectFirstNoteContains('provider is set to "openai"', "OPENAI_API_KEY"); - expect(resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(resolveApiKeyForProviderCore).not.toHaveBeenCalled(); }); it("does not treat built-in OpenAI as key-optional just because models.providers.openai has baseUrl", async () => { @@ -967,7 +967,7 @@ describe("noteMemorySearchHealth", () => { ); expectFirstNoteContains('provider is set to "openai"', "OPENAI_API_KEY"); - expect(resolveApiKeyForProvider).toHaveBeenCalledWith({ + expect(resolveApiKeyForProviderCore).toHaveBeenCalledWith({ provider: "openai", cfg: openaiCfg, agentDir: "/tmp/agent-default", @@ -1021,13 +1021,13 @@ describe("noteMemorySearchHealth", () => { }); it("does not probe unrelated embedding providers for legacy auto mode", async () => { - resolveApiKeyForProvider.mockImplementation(async () => { + resolveApiKeyForProviderCore.mockImplementation(async () => { throw new Error("missing key"); }); await runMemorySearchHealth("auto"); expect(note).toHaveBeenCalledTimes(1); - const providerCalls = resolveApiKeyForProvider.mock.calls as Array<[{ provider: string }]>; + const providerCalls = resolveApiKeyForProviderCore.mock.calls as Array<[{ provider: string }]>; const providersChecked = providerCalls.map(([arg]) => arg.provider); expect(providersChecked).toEqual(["openai"]); }); @@ -1036,7 +1036,7 @@ describe("noteMemorySearchHealth", () => { hasAnyAuthProfileStoreSource.mockReturnValue(false); await runMemorySearchHealth("auto"); - const providerCalls = resolveApiKeyForProvider.mock.calls as Array<[{ provider: string }]>; + const providerCalls = resolveApiKeyForProviderCore.mock.calls as Array<[{ provider: string }]>; const providersChecked = providerCalls.map(([arg]) => arg.provider); expect(providersChecked).toEqual([]); }); diff --git a/src/commands/doctor-memory-search.ts b/src/commands/doctor-memory-search.ts index 61383a0631ad..31a4bdf8050d 100644 --- a/src/commands/doctor-memory-search.ts +++ b/src/commands/doctor-memory-search.ts @@ -19,7 +19,7 @@ import { } from "../agents/auth-profiles.js"; import { resolveMemorySearchConfig } from "../agents/memory-search.js"; import { - resolveApiKeyForProvider, + resolveApiKeyForProviderCore, resolveEnvApiKey, resolveUsableCustomProviderApiKey, } from "../agents/model-auth.js"; @@ -840,7 +840,7 @@ async function hasApiKeyForProvider( return false; } try { - await resolveApiKeyForProvider({ + await resolveApiKeyForProviderCore({ provider: authProviderId, cfg, agentDir, diff --git a/src/commands/doctor-plugin-registry.ts b/src/commands/doctor-plugin-registry.ts index d1008cacaf75..281a5b3defdc 100644 --- a/src/commands/doctor-plugin-registry.ts +++ b/src/commands/doctor-plugin-registry.ts @@ -6,7 +6,7 @@ import { note } from "../../packages/terminal-core/src/note.js"; import { formatCliCommand } from "../cli/command-format.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { HealthFinding, HealthRepairEffect } from "../flows/health-checks.js"; -import { saveJsonFile } from "../infra/json-file.js"; +import { writeJsonTarget } from "../infra/json-file.js"; import { tryReadJsonSync } from "../infra/json-files.js"; import type { BundledPluginSource } from "../plugins/bundled-sources.js"; import { @@ -241,7 +241,7 @@ function removeManagedNpmDependency(params: { ...packageJson, dependencies, }; - saveJsonFile(npmPackageJsonPath, nextPackageJson); + writeJsonTarget(npmPackageJsonPath, nextPackageJson); removeManagedNpmPackageLockDependency(params); fs.rmSync(params.packageDir, { recursive: true, force: true }); const scopeDir = path.dirname(params.packageDir); @@ -288,7 +288,7 @@ function removeManagedNpmPackageLockDependency(params: { } if (changed) { - saveJsonFile(packageLockPath, packageLock); + writeJsonTarget(packageLockPath, packageLock); } } diff --git a/src/commands/doctor-security.test.ts b/src/commands/doctor-security.test.ts index 467710234f2f..c061f4b8d841 100644 --- a/src/commands/doctor-security.test.ts +++ b/src/commands/doctor-security.test.ts @@ -6,7 +6,7 @@ import type { ExecApprovalsFile } from "../infra/exec-approvals-core.js"; import { saveExecApprovals } from "../infra/exec-approvals-store.js"; import { testing as execApprovalsStoreTesting } from "../infra/exec-approvals-store.test-support.js"; import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; const note = vi.hoisted(() => vi.fn()); const pluginRegistry = vi.hoisted(() => ({ list: [] as unknown[] })); @@ -92,7 +92,7 @@ describe("noteSecurityWarnings gateway exposure", () => { file: Record, run: () => Promise, ): Promise { - await withTempDir({ prefix: "openclaw-doctor-security-" }, async (home) => { + await withTestDir({ prefix: "openclaw-doctor-security-" }, async (home) => { process.env.HOME = home; process.env.OPENCLAW_STATE_DIR = path.join(home, ".openclaw"); closeOpenClawStateDatabaseForTest(); diff --git a/src/commands/doctor-session-canonical-keys.retention.test.ts b/src/commands/doctor-session-canonical-keys.retention.test.ts index b7e2859d8f99..4ff4f255d20a 100644 --- a/src/commands/doctor-session-canonical-keys.retention.test.ts +++ b/src/commands/doctor-session-canonical-keys.retention.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { readSessionArchiveContentSync } from "../config/sessions/archive-compression.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { loadExactSessionEntryReadOnly, loadTranscriptEvents, @@ -25,8 +25,8 @@ describe("doctor canonical session-key retention repair", () => { await withStateDirEnv("openclaw-doctor-canonical-cross-store-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const mainStore = resolveStorePath(storeTemplate, { agentId: "main", env }); - const opsStore = resolveStorePath(storeTemplate, { agentId: "ops", env }); + const mainStore = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); + const opsStore = resolveSessionStorePathCore(storeTemplate, { agentId: "ops", env }); const cfg = { agents: { list: [{ id: "main", default: true }, { id: "ops" }] }, session: { mainKey: "shared", store: storeTemplate }, @@ -214,8 +214,8 @@ describe("doctor canonical session-key retention repair", () => { async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const mainStore = resolveStorePath(storeTemplate, { agentId: "main", env }); - const opsStore = resolveStorePath(storeTemplate, { agentId: "ops", env }); + const mainStore = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); + const opsStore = resolveSessionStorePathCore(storeTemplate, { agentId: "ops", env }); const cfg = { agents: { list: [{ id: "main", default: true }, { id: "ops" }] }, session: { mainKey: "work", store: storeTemplate }, diff --git a/src/commands/doctor-session-canonical-keys.test.ts b/src/commands/doctor-session-canonical-keys.test.ts index 8587b3f19760..e83cfa2700b2 100644 --- a/src/commands/doctor-session-canonical-keys.test.ts +++ b/src/commands/doctor-session-canonical-keys.test.ts @@ -1,7 +1,7 @@ import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { buildConversationIdentity } from "../config/sessions/conversation-identity.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { loadTranscriptEvents, loadExactSessionEntryReadOnly, @@ -43,7 +43,7 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-delivery-alias-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const storePath = resolveStorePath(storeTemplate, { agentId: "main", env }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); const cfg = { agents: { list: [{ id: "main", default: true }] }, session: { store: storeTemplate }, @@ -122,7 +122,7 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-batches-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const storePath = resolveStorePath(storeTemplate, { agentId: "main", env }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); const cfg = { agents: { list: [{ id: "main", default: true }] }, session: { store: storeTemplate }, @@ -164,7 +164,7 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-fresh-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const storePath = resolveStorePath(storeTemplate, { agentId: "main", env }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); const cfg = { agents: { list: [{ id: "main", default: true }] }, session: { store: storeTemplate }, @@ -228,7 +228,7 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-global-heartbeat-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const storePath = resolveStorePath(storeTemplate, { agentId: "historian2", env }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId: "historian2", env }); const cfg = { agents: { list: [{ id: "main", default: true }, { id: "historian2" }] }, session: { scope: "global", store: storeTemplate }, @@ -277,7 +277,7 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-recovery-owner-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const storePath = resolveStorePath(storeTemplate, { agentId: "main", env }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); const cfg = { agents: { list: [{ id: "main", default: true }] }, session: { store: storeTemplate }, @@ -336,7 +336,7 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-empty-key-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const storePath = resolveStorePath(storeTemplate, { agentId: "main", env }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); const cfg = { agents: { list: [{ id: "main", default: true }] }, session: { store: storeTemplate }, @@ -408,7 +408,7 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-rehome-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const storePath = resolveStorePath(storeTemplate, { agentId: "main", env }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); const cfg = { agents: { list: [{ id: "main", default: true }] }, session: { mainKey: "work", store: storeTemplate }, @@ -458,7 +458,7 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-members-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const storePath = resolveStorePath(storeTemplate, { agentId: "main", env }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); const cfg = { agents: { list: [{ id: "main", default: true }] }, session: { mainKey: "work", store: storeTemplate }, @@ -517,7 +517,7 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-suggestions-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const storePath = resolveStorePath(storeTemplate, { agentId: "main", env }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); const cfg = { agents: { list: [{ id: "main", default: true }] }, session: { mainKey: "work", store: storeTemplate }, @@ -561,8 +561,8 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-sentinels-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const mainStore = resolveStorePath(storeTemplate, { agentId: "main", env }); - const opsStore = resolveStorePath(storeTemplate, { agentId: "ops", env }); + const mainStore = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); + const opsStore = resolveSessionStorePathCore(storeTemplate, { agentId: "ops", env }); const cfg = { agents: { list: [{ id: "main", default: true }, { id: "ops" }] }, session: { store: storeTemplate }, @@ -614,7 +614,7 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-lineage-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const storePath = resolveStorePath(storeTemplate, { agentId: "main", env }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); const cfg = { agents: { list: [{ id: "main", default: true }] }, session: { store: storeTemplate }, @@ -707,7 +707,7 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-single-alias-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const storePath = resolveStorePath(storeTemplate, { agentId: "main", env }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); const cfg = { agents: { list: [{ id: "main", default: true }] }, session: { mainKey: "work", store: storeTemplate }, @@ -858,8 +858,8 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-wrong-store-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const mainStore = resolveStorePath(storeTemplate, { agentId: "main", env }); - const opsStore = resolveStorePath(storeTemplate, { agentId: "ops", env }); + const mainStore = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); + const opsStore = resolveSessionStorePathCore(storeTemplate, { agentId: "ops", env }); const cfg = { agents: { list: [{ id: "main", default: true }, { id: "ops" }] }, session: { mainKey: "work", store: storeTemplate }, @@ -929,8 +929,8 @@ describe("doctor canonical session-key repair", () => { await withStateDirEnv("openclaw-doctor-canonical-tied-stores-", async ({ stateDir }) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storeTemplate = path.join(stateDir, "agents", "{agentId}", "sessions.json"); - const mainStore = resolveStorePath(storeTemplate, { agentId: "main", env }); - const opsStore = resolveStorePath(storeTemplate, { agentId: "ops", env }); + const mainStore = resolveSessionStorePathCore(storeTemplate, { agentId: "main", env }); + const opsStore = resolveSessionStorePathCore(storeTemplate, { agentId: "ops", env }); const cfg = { agents: { list: [{ id: "main", default: true }, { id: "ops" }] }, session: { mainKey: "shared", store: storeTemplate }, diff --git a/src/commands/doctor-session-canonical-keys.ts b/src/commands/doctor-session-canonical-keys.ts index 03b4e2e4c6e5..83cf1650cf3c 100644 --- a/src/commands/doctor-session-canonical-keys.ts +++ b/src/commands/doctor-session-canonical-keys.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import { resolveAgentMainSessionKey } from "../config/sessions/main-session.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { applySessionEntryLifecycleMutation, copySessionOwnedStateForCanonicalRepair, @@ -238,7 +238,10 @@ function resolveCanonicalDestination(params: { params.sourceAgentId ?? resolveSessionStoreAgentId(params.cfg, params.canonicalKey), ) : resolveSessionStoreAgentId(params.cfg, params.canonicalKey); - const storePath = resolveStorePath(params.cfg.session?.store, { agentId, env: params.env }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { + agentId, + env: params.env, + }); return { agentId, storePath, diff --git a/src/commands/doctor-session-sqlite.test.ts b/src/commands/doctor-session-sqlite.test.ts index 75a32e339dd2..28e4b7c0849f 100644 --- a/src/commands/doctor-session-sqlite.test.ts +++ b/src/commands/doctor-session-sqlite.test.ts @@ -9,7 +9,7 @@ import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; import { CURRENT_SESSION_VERSION, SessionManager } from "../agents/sessions/session-manager.js"; import { loadExactSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.sqlite-entry.js"; import { loadTranscriptEventsSync, @@ -259,7 +259,7 @@ describe("runDoctorSessionSqlite", () => { const stateDir = path.join(tempDir, "state"); const storePath = path.join(stateDir, "agents", "main", "sessions", "sessions.json"); const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", env, sessionKey: "agent:main:main", storePath }, { sessionId: "sqlite-session", updatedAt: Date.now() }, ); @@ -575,7 +575,7 @@ describe("runDoctorSessionSqlite", () => { const store = createLegacyStore({ entryOverrides: { lifecycleRevision: "rev-1" }, }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", env: store.env, @@ -2735,7 +2735,7 @@ describe("runDoctorSessionSqlite", () => { fs.writeFileSync(store.transcriptPath, '{"type":"event","id":"heartbeat"}\n', { mode: 0o600, }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", env: store.env, @@ -3150,7 +3150,7 @@ describe("runDoctorSessionSqlite", () => { it("reports malformed selected legacy transcripts during validation", async () => { const store = createLegacyStore({ transcriptLines: ['{"type":"session"}', "{bad"] }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", env: store.env, diff --git a/src/commands/doctor-session-sqlite.ts b/src/commands/doctor-session-sqlite.ts index cae9f11fbe6f..d0509345ddab 100644 --- a/src/commands/doctor-session-sqlite.ts +++ b/src/commands/doctor-session-sqlite.ts @@ -5,7 +5,7 @@ import { tryResolveDefaultAgentId } from "../agents/agent-scope.js"; import { getRuntimeConfig } from "../config/config.js"; import { resolveStateDir } from "../config/paths.js"; import { parseSqliteSessionFileMarker } from "../config/sessions/legacy-sqlite-marker.js"; -import { resolveSessionFilePath } from "../config/sessions/paths.js"; +import { resolveSessionFilePathCore } from "../config/sessions/paths.js"; import { importSqliteSessionRows } from "../config/sessions/session-accessor.sqlite-import.js"; import { resolveUnsuffixedSqliteTargetFromSessionStorePath } from "../config/sessions/session-sqlite-target.js"; import { normalizeStoreSessionKey } from "../config/sessions/store-entry.js"; @@ -531,7 +531,7 @@ function resolveLegacyTranscriptPath( if (parseSqliteSessionFileMarker(legacySessionFile)) { return undefined; } - const defaultPath = resolveSessionFilePath(entry.sessionId, entry, { + const defaultPath = resolveSessionFilePathCore(entry.sessionId, entry, { agentId: target.agentId, sessionsDir: path.dirname(target.storePath), }); @@ -1147,7 +1147,7 @@ function resolveActiveSqliteTranscriptFile( ): string | undefined { let transcriptPath: string; try { - transcriptPath = resolveSessionFilePath(entry.sessionId, entry, { + transcriptPath = resolveSessionFilePathCore(entry.sessionId, entry, { agentId: target.agentId, sessionsDir: path.dirname(target.storePath), }); diff --git a/src/commands/doctor-session-transcript-headers.test.ts b/src/commands/doctor-session-transcript-headers.test.ts index cf79d676b8e1..032518e49b68 100644 --- a/src/commands/doctor-session-transcript-headers.test.ts +++ b/src/commands/doctor-session-transcript-headers.test.ts @@ -4,7 +4,7 @@ import { SessionManager } from "../agents/sessions/session-manager.js"; import { loadTranscriptEventsSync, replaceTranscriptEventsSync, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { readTranscriptStorageRows } from "../config/sessions/session-accessor.sqlite-read.js"; import { CURRENT_SESSION_VERSION } from "../config/sessions/version.js"; @@ -70,7 +70,7 @@ describe("doctor SQLite session transcript header repair", () => { events: readonly unknown[], options: { spawnedCwd?: string } = { spawnedCwd: SPAWNED_CWD }, ): Promise { - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: SESSION_ID, ...(options.spawnedCwd ? { spawnedCwd: options.spawnedCwd } : {}), updatedAt: 10, diff --git a/src/commands/doctor-state-integrity.test.ts b/src/commands/doctor-state-integrity.test.ts index 3d87c10e750e..c7392f30c229 100644 --- a/src/commands/doctor-state-integrity.test.ts +++ b/src/commands/doctor-state-integrity.test.ts @@ -6,10 +6,10 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { HEARTBEAT_TRANSCRIPT_PROMPT } from "../auto-reply/heartbeat.js"; import type { OpenClawConfig } from "../config/config.js"; import { - resolveStorePath, + resolveSessionStorePathCore, resolveSessionTranscriptsDirForAgent, } from "../config/sessions/paths.js"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import type { SessionEntry } from "../config/sessions/types.js"; import { closeOpenClawAgentDatabasesForTest } from "../state/openclaw-agent-db.js"; import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js"; @@ -65,7 +65,7 @@ async function noteStateIntegrity( function setupSessionState(cfg: OpenClawConfig, env: NodeJS.ProcessEnv, homeDir: string) { const agentId = "main"; const sessionsDir = resolveSessionTranscriptsDirForAgent(agentId, env, () => homeDir); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); fs.mkdirSync(sessionsDir, { recursive: true }); fs.mkdirSync(path.dirname(storePath), { recursive: true }); } @@ -127,7 +127,7 @@ function writeSessionStore( sessions: Record>, ) { setupSessionState(cfg, process.env, process.env.HOME ?? ""); - const storePath = resolveStorePath(cfg.session?.store, { agentId: "main" }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: "main" }); fs.writeFileSync(storePath, JSON.stringify(sessions, null, 2)); } @@ -468,7 +468,7 @@ describe("doctor state integrity oauth dir checks", () => { ); await noteStateIntegrity(cfg, { confirmRuntimeRepair, note: noteMock }); - const storePath = resolveStorePath(cfg.session?.store, { agentId: "main" }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: "main" }); const persisted = JSON.parse(fs.readFileSync(storePath, "utf8")) as Record< string, { abortedLastRun?: boolean; updatedAt?: number } @@ -566,11 +566,11 @@ describe("doctor state integrity oauth dir checks", () => { it("uses SQLite session rows for transcript integrity without orphan false positives", async () => { const cfg: OpenClawConfig = {}; setupSessionState(cfg, process.env, process.env.HOME ?? ""); - const storePath = resolveStorePath(cfg.session?.store, { agentId: "main" }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: "main" }); const sessionsDir = resolveSessionTranscriptsDirForAgent("main", process.env, () => tempHome); const transcriptPath = path.join(sessionsDir, "sqlite-live-session.jsonl"); fs.writeFileSync(transcriptPath, '{"type":"session"}\n'); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:main", storePath }, { sessionFile: transcriptPath, @@ -593,12 +593,12 @@ describe("doctor state integrity oauth dir checks", () => { it("does not require JSONL files for canonical SQLite session rows", async () => { const cfg: OpenClawConfig = {}; setupSessionState(cfg, process.env, process.env.HOME ?? ""); - const storePath = resolveStorePath(cfg.session?.store, { agentId: "main" }); - await upsertSessionEntry( + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: "main" }); + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:main", storePath }, { sessionId: "sqlite-main-session", updatedAt: Date.now() }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:sqlite-only", storePath }, { sessionId: "sqlite-only-session", updatedAt: Date.now() }, ); @@ -741,7 +741,7 @@ describe("doctor state integrity oauth dir checks", () => { ); await noteStateIntegrity(cfg, { confirmRuntimeRepair, note: noteMock }); - const storePath = resolveStorePath(cfg.session?.store, { agentId: "main" }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: "main" }); const store = JSON.parse(fs.readFileSync(storePath, "utf8")) as Record; const recoveredKey = Object.keys(store).find((key) => key.startsWith("agent:main:heartbeat-recovered-"), @@ -783,7 +783,7 @@ describe("doctor state integrity oauth dir checks", () => { const confirmRuntimeRepair = vi.fn(async () => true); await noteStateIntegrity(cfg, { confirmRuntimeRepair, note: noteMock }); - const storePath = resolveStorePath(cfg.session?.store, { agentId: "main" }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: "main" }); const store = JSON.parse(fs.readFileSync(storePath, "utf8")) as Record; expect(store["agent:main:main"]?.sessionId).toBe("mixed-session"); expect(Object.keys(store).filter((key) => key.includes("heartbeat-recovered"))).toEqual([]); @@ -832,7 +832,7 @@ describe("doctor state integrity oauth dir checks", () => { expect(summary?.nonHeartbeatUserMessages).toBe(0); expect(summary?.userMessages).toBe(repeats); - const storePath = resolveStorePath(cfg.session?.store, { agentId: "main" }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: "main" }); const store = JSON.parse(fs.readFileSync(storePath, "utf8")) as Record; expect(store["agent:main:main"]).toBeUndefined(); const recoveredKey = Object.keys(store).find((key) => @@ -884,7 +884,7 @@ describe("doctor state integrity oauth dir checks", () => { expect(hasRepairPromptMessage(confirmRuntimeRepair, "Move heartbeat-owned main session")).toBe( false, ); - const storePath = resolveStorePath(cfg.session?.store, { agentId: "main" }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: "main" }); const store = JSON.parse(fs.readFileSync(storePath, "utf8")) as Record; expect(store["agent:main:main"]?.sessionId).toBe("oversized-record-session"); expect(Object.keys(store).filter((key) => key.includes("heartbeat-recovered"))).toEqual([]); diff --git a/src/commands/doctor-state-integrity.ts b/src/commands/doctor-state-integrity.ts index 54fbc70aa615..b5808a5a61cb 100644 --- a/src/commands/doctor-state-integrity.ts +++ b/src/commands/doctor-state-integrity.ts @@ -26,10 +26,10 @@ import { import { parseSqliteSessionFileMarker } from "../config/sessions/legacy-sqlite-marker.js"; import { resolveMainSessionKey } from "../config/sessions/main-session.js"; import { - resolveSessionFilePath, + resolveSessionFilePathCore, resolveSessionFilePathOptions, resolveSessionTranscriptsDirForAgent, - resolveStorePath, + resolveSessionStorePathCore, } from "../config/sessions/paths.js"; import { applySessionEntryReplacements, @@ -785,7 +785,9 @@ export function detectStateIntegrityHealthIssues( const sessionsDir = agentId ? resolveSessionTranscriptsDirForAgent(agentId, env, homedir) : undefined; - const storePath = agentId ? resolveStorePath(cfg.session?.store, { agentId }) : undefined; + const storePath = agentId + ? resolveSessionStorePathCore(cfg.session?.store, { agentId }) + : undefined; const storeDir = storePath ? path.dirname(storePath) : undefined; const requireOAuthDir = shouldRequireOAuthDir(cfg, env); @@ -1058,7 +1060,9 @@ export async function noteStateIntegrity( const sessionsDir = agentId ? resolveSessionTranscriptsDirForAgent(agentId, env, homedir) : undefined; - const storePath = agentId ? resolveStorePath(cfg.session?.store, { agentId }) : undefined; + const storePath = agentId + ? resolveSessionStorePathCore(cfg.session?.store, { agentId }) + : undefined; const storeDir = storePath ? path.dirname(storePath) : undefined; const absoluteStorePath = storePath ? path.resolve(storePath) : undefined; const displayStateDir = shortenHomePath(stateDir); @@ -1353,7 +1357,7 @@ export async function noteStateIntegrity( if (parseSqliteSessionFileMarker(legacySessionFile)) { return false; } - const transcriptPath = resolveSessionFilePath(sessionId, entry, sessionPathOpts); + const transcriptPath = resolveSessionFilePathCore(sessionId, entry, sessionPathOpts); return !existsFile(transcriptPath); }); if (missing.length > 0) { @@ -1468,7 +1472,7 @@ export async function noteStateIntegrity( // SQLite-owned transcripts live in the agent DB after import. // Do not require the archived legacy JSONL for those sessions. if (mainEntry?.sessionId && !sqliteSessionKeys.has(mainKey)) { - const transcriptPath = resolveSessionFilePath( + const transcriptPath = resolveSessionFilePathCore( mainEntry.sessionId, mainEntry, sessionPathOpts, @@ -1499,7 +1503,7 @@ export async function noteStateIntegrity( try { referencedTranscriptPaths.add( resolveComparableTranscriptPath( - resolveSessionFilePath(entry.sessionId, entry, sessionPathOpts), + resolveSessionFilePathCore(entry.sessionId, entry, sessionPathOpts), ), ); } catch { diff --git a/src/commands/doctor-telegram-general-topic-conversations.test.ts b/src/commands/doctor-telegram-general-topic-conversations.test.ts index dd0e3bcc811a..edf2516b975b 100644 --- a/src/commands/doctor-telegram-general-topic-conversations.test.ts +++ b/src/commands/doctor-telegram-general-topic-conversations.test.ts @@ -2,7 +2,10 @@ import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; import { listConversations } from "../config/sessions/conversation-registry.js"; -import { patchSessionEntryCore, upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { + patchSessionEntryCore, + upsertSessionEntryCore, +} from "../config/sessions/session-accessor.js"; import { getSessionKysely, resolveSqliteReadScope, @@ -49,13 +52,13 @@ describe("doctor Telegram General-topic conversation repair", () => { normalizeSessionDeliveryState({ context: { channel: "telegram", accountId: "default", to, threadId: "1" }, }); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "topic-1-session", updatedAt: 100, chatType: "group", delivery: delivery("telegram:-1001234567890:topic:1"), }); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "topic-1-session", updatedAt: 200, chatType: "group", @@ -152,7 +155,7 @@ describe("doctor Telegram General-topic conversation repair", () => { it("canonicalizes a legacy-only current entry before later session writes", async () => { const sessionKey = "agent:main:telegram:group:-1002223334444:topic:1"; const scope = { agentId: "main", env, sessionKey, storePath }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "legacy-only-session", updatedAt: 100, chatType: "group", diff --git a/src/commands/export-trajectory.test.ts b/src/commands/export-trajectory.test.ts index e5f4661122a8..59b46c475232 100644 --- a/src/commands/export-trajectory.test.ts +++ b/src/commands/export-trajectory.test.ts @@ -32,7 +32,7 @@ vi.mock("../config/sessions/paths.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - resolveStorePath: mocks.resolveStorePath, + resolveSessionStorePathCore: mocks.resolveStorePath, }; }); diff --git a/src/commands/export-trajectory.ts b/src/commands/export-trajectory.ts index 102aabc0894a..1ad74f10477a 100644 --- a/src/commands/export-trajectory.ts +++ b/src/commands/export-trajectory.ts @@ -3,7 +3,7 @@ import path from "node:path"; import { readNonBlankString } from "@openclaw/normalization-core/string-coerce"; import { formatCliCommand } from "../cli/command-format.js"; import { getRuntimeConfig } from "../config/config.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { loadSessionEntryReadOnly, resolveSessionTranscriptReadTarget, @@ -115,8 +115,8 @@ export async function exportTrajectoryCommand( } const targetAgentId = resolvedOpts.agent ?? resolveAgentIdFromSessionKey(sessionKey); const storePath = resolvedOpts.store - ? resolveStorePath(resolvedOpts.store, { agentId: targetAgentId }) - : resolveStorePath(getRuntimeConfig().session?.store, { agentId: targetAgentId }); + ? resolveSessionStorePathCore(resolvedOpts.store, { agentId: targetAgentId }) + : resolveSessionStorePathCore(getRuntimeConfig().session?.store, { agentId: targetAgentId }); // CLI reads must not join the Gateway's writable SQLite lifecycle (#101290). const entry = loadSessionEntryReadOnly({ agentId: targetAgentId, diff --git a/src/commands/health.plugins.test.ts b/src/commands/health.plugins.test.ts index ef3e348f24a6..8d70adc71616 100644 --- a/src/commands/health.plugins.test.ts +++ b/src/commands/health.plugins.test.ts @@ -21,7 +21,7 @@ describe("collectGatewayHealthSnapshot plugin state", () => { loadConfig: () => testConfig, })); vi.doMock("../config/sessions/paths.js", () => ({ - resolveStorePath: () => "/tmp/sessions.json", + resolveSessionStorePathCore: () => "/tmp/sessions.json", })); vi.doMock("../config/sessions/session-accessor.js", () => ({ listSessionEntriesCore: () => [], diff --git a/src/commands/health.snapshot.test.ts b/src/commands/health.snapshot.test.ts index 4674f5ca4291..51d719513903 100644 --- a/src/commands/health.snapshot.test.ts +++ b/src/commands/health.snapshot.test.ts @@ -63,8 +63,8 @@ async function loadFreshHealthModulesForTest() { loadConfig: () => testConfig, })); vi.doMock("../config/sessions.js", () => ({ - resolveStorePath: () => "/tmp/sessions.json", - resolveSessionFilePath: vi.fn(() => "/tmp/sessions.json"), + resolveSessionStorePathCore: () => "/tmp/sessions.json", + resolveSessionFilePathCore: vi.fn(() => "/tmp/sessions.json"), loadSessionStore: () => testStore, saveSessionStore: vi.fn().mockResolvedValue(undefined), readSessionUpdatedAt: vi.fn(() => undefined), @@ -72,7 +72,7 @@ async function loadFreshHealthModulesForTest() { updateLastRoute: vi.fn().mockResolvedValue(undefined), })); vi.doMock("../config/sessions/paths.js", () => ({ - resolveStorePath: () => "/tmp/sessions.json", + resolveSessionStorePathCore: () => "/tmp/sessions.json", })); vi.doMock("../config/sessions/session-accessor.js", () => ({ listSessionEntriesReadOnly: (scope?: { agentId?: string; storePath?: string }) => { diff --git a/src/commands/health.ts b/src/commands/health.ts index 534611c49560..150fa0247b3f 100644 --- a/src/commands/health.ts +++ b/src/commands/health.ts @@ -6,7 +6,7 @@ import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js"; import { listReadOnlyChannelPluginsForConfig } from "../channels/plugins/read-only.js"; import { probeGatewayStatus } from "../cli/daemon-cli/probe.js"; import { withProgress } from "../cli/progress.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { buildGatewayConnectionDetails, @@ -312,7 +312,9 @@ export async function healthCommand( ? agents : await Promise.all( localAgents.ordered.map(async (entry) => { - const storePath = resolveStorePath(cfg.session?.store, { agentId: entry.id }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { + agentId: entry.id, + }); return { agentId: entry.id, name: entry.name, diff --git a/src/commands/models/auth.test.ts b/src/commands/models/auth.test.ts index dcc3e08b95f1..ceb09a23638e 100644 --- a/src/commands/models/auth.test.ts +++ b/src/commands/models/auth.test.ts @@ -272,7 +272,7 @@ const { modelsAuthPasteApiKeyCommand, modelsAuthPasteTokenCommand, modelsAuthSetupTokenCommand, - runModelsAuthLoginFlow, + runModelsAuthLoginFlowCore, } = await import("./auth.js"); function createRuntime(): RuntimeEnv { @@ -863,7 +863,7 @@ describe("modelsAuthLoginCommand", () => { const runtime = createRuntime(); const abortController = new AbortController(); - await runModelsAuthLoginFlow({ + await runModelsAuthLoginFlowCore({ provider: "openai", method: "oauth", config: currentConfig, @@ -898,7 +898,7 @@ describe("modelsAuthLoginCommand", () => { }); await expect( - runModelsAuthLoginFlow({ + runModelsAuthLoginFlowCore({ provider: "openai", method: "oauth", config: currentConfig, diff --git a/src/commands/models/auth.ts b/src/commands/models/auth.ts index 6529d73ecf06..137bab2a5d25 100644 --- a/src/commands/models/auth.ts +++ b/src/commands/models/auth.ts @@ -985,7 +985,7 @@ function maybeLogOpenAICodexNativeSearchTip(runtime: RuntimeEnv, providerId: str ); } -export async function runModelsAuthLoginFlow( +export async function runModelsAuthLoginFlowCore( opts: ModelsAuthLoginFlowOptions, ): Promise { const { config, agentDir, workspaceDir, providers } = await resolveModelsAuthContext({ @@ -1100,7 +1100,7 @@ export async function modelsAuthLoginCommand(opts: LoginOptions, runtime: Runtim ); } - await runModelsAuthLoginFlow({ + await runModelsAuthLoginFlowCore({ ...opts, runtime, prompter: createClackPrompter(), diff --git a/src/commands/models/list.probe.ts b/src/commands/models/list.probe.ts index 5239439165aa..52f0941ab2c6 100644 --- a/src/commands/models/list.probe.ts +++ b/src/commands/models/list.probe.ts @@ -42,7 +42,7 @@ import { loadPreparedModelCatalog } from "../../agents/prepared-model-catalog.js import { resolveProviderIdForAuth } from "../../agents/provider-auth-aliases.js"; import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js"; import { formatCliCommand } from "../../cli/command-format.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { coerceSecretRef, @@ -891,7 +891,7 @@ async function runTargetsWithConcurrency(params: { params.workspaceDir ?? resolveAgentWorkspaceDir(cfg, agentId) ?? resolveDefaultAgentWorkspaceDir(); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); await fs.mkdir(workspaceDir, { recursive: true }); diff --git a/src/commands/models/scan.test.ts b/src/commands/models/scan.test.ts index 6b2f6ebdec76..da7a1452d524 100644 --- a/src/commands/models/scan.test.ts +++ b/src/commands/models/scan.test.ts @@ -6,7 +6,7 @@ import { withEnvAsync } from "../../test-utils/env.js"; const mocks = vi.hoisted(() => ({ loadModelsConfig: vi.fn(), - resolveApiKeyForProvider: vi.fn(), + resolveApiKeyForProviderCore: vi.fn(), scanOpenRouterModels: vi.fn(), })); @@ -15,7 +15,7 @@ vi.mock("./load-config.js", () => ({ })); vi.mock("../../agents/model-auth.js", () => ({ - resolveApiKeyForProvider: mocks.resolveApiKeyForProvider, + resolveApiKeyForProviderCore: mocks.resolveApiKeyForProviderCore, })); vi.mock("../../agents/model-scan.js", () => ({ @@ -81,7 +81,7 @@ describe("models scan command", () => { await modelsScanCommand({ probe: false }, runtime); expect(mocks.loadModelsConfig).not.toHaveBeenCalled(); - expect(mocks.resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(mocks.resolveApiKeyForProviderCore).not.toHaveBeenCalled(); expect(mocks.scanOpenRouterModels).toHaveBeenCalledTimes(1); expect(firstScanRequest().probe).toBe(false); expect(runtime.lines.join("\n")).toContain("metadata only"); @@ -119,7 +119,7 @@ describe("models scan command", () => { "openrouter/zeta/free:free", ]); expect(mocks.loadModelsConfig).not.toHaveBeenCalled(); - expect(mocks.resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(mocks.resolveApiKeyForProviderCore).not.toHaveBeenCalled(); }); it("sanitizes provider-controlled model refs and modality in scan tables", async () => { @@ -148,13 +148,13 @@ describe("models scan command", () => { await withOpenRouterApiKey(undefined, async () => { const runtime = createRuntime(); mocks.loadModelsConfig.mockResolvedValue({}); - mocks.resolveApiKeyForProvider.mockResolvedValue({ apiKey: "" }); + mocks.resolveApiKeyForProviderCore.mockResolvedValue({ apiKey: "" }); mocks.scanOpenRouterModels.mockResolvedValue([scanResult()]); await modelsScanCommand({}, runtime); expect(mocks.loadModelsConfig).toHaveBeenCalledTimes(1); - expect(mocks.resolveApiKeyForProvider).toHaveBeenCalledWith({ + expect(mocks.resolveApiKeyForProviderCore).toHaveBeenCalledWith({ provider: "openrouter", cfg: {}, }); @@ -176,7 +176,7 @@ describe("models scan command", () => { ); expect(mocks.loadModelsConfig).not.toHaveBeenCalled(); - expect(mocks.resolveApiKeyForProvider).not.toHaveBeenCalled(); + expect(mocks.resolveApiKeyForProviderCore).not.toHaveBeenCalled(); expect(mocks.scanOpenRouterModels).toHaveBeenCalledTimes(1); const scanRequest = firstScanRequest(); expect(scanRequest?.apiKey).toBe("sk-or-test"); @@ -214,7 +214,7 @@ describe("models scan command", () => { await withOpenRouterApiKey(undefined, async () => { const runtime = createRuntime(); mocks.loadModelsConfig.mockResolvedValue({}); - mocks.resolveApiKeyForProvider.mockResolvedValue({ apiKey: "" }); + mocks.resolveApiKeyForProviderCore.mockResolvedValue({ apiKey: "" }); await expect(modelsScanCommand({ setDefault: true }, runtime)).rejects.toThrow( /Cannot apply metadata-only OpenRouter scan results/, diff --git a/src/commands/models/scan.ts b/src/commands/models/scan.ts index f0edce5c18f5..a7a4ce119784 100644 --- a/src/commands/models/scan.ts +++ b/src/commands/models/scan.ts @@ -4,7 +4,7 @@ import { getEnvApiKey } from "@openclaw/ai/internal/runtime"; import { styleSelectParams } from "../../../packages/terminal-core/src/prompt-select-styled-params.js"; import { stylePromptTitle } from "../../../packages/terminal-core/src/prompt-style.js"; import { sanitizeTerminalText } from "../../../packages/terminal-core/src/safe-text.js"; -import { resolveApiKeyForProvider } from "../../agents/model-auth.js"; +import { resolveApiKeyForProviderCore } from "../../agents/model-auth.js"; import { type ModelScanResult, scanOpenRouterModels } from "../../agents/model-scan.js"; import { formatCliCommand } from "../../cli/command-format.js"; import { withProgressTotals } from "../../cli/progress.js"; @@ -223,7 +223,7 @@ export async function modelsScanCommand( if (!storedKey) { try { const cfg = await loadModelsConfig({ commandName: "models scan" }); - const resolved = await resolveApiKeyForProvider({ + const resolved = await resolveApiKeyForProviderCore({ provider: "openrouter", cfg, }); diff --git a/src/commands/onboard-browser-handoff.test.ts b/src/commands/onboard-browser-handoff.test.ts index b382364a0ffa..612939c5eb23 100644 --- a/src/commands/onboard-browser-handoff.test.ts +++ b/src/commands/onboard-browser-handoff.test.ts @@ -15,13 +15,13 @@ const sharedMocks = vi.hoisted(() => ({ callGateway: vi.fn(), waitForControlUiDocument: vi.fn(), issueControlUiBrowserHandoff: vi.fn(), - resolveAdvertisedLanHost: vi.fn(), + resolveAdvertisedLanHostCore: vi.fn(), resolveAdvertisedControlUiLinks: vi.fn(), })); vi.mock("../infra/advertised-lan-host.js", async (importOriginal) => ({ ...(await importOriginal()), - resolveAdvertisedLanHost: sharedMocks.resolveAdvertisedLanHost, + resolveAdvertisedLanHostCore: sharedMocks.resolveAdvertisedLanHostCore, })); vi.mock("../gateway/call.js", () => ({ @@ -57,8 +57,8 @@ beforeEach(() => { browserUrl: `${url}#bootstrapToken=one-time-bootstrap`, expiresAtMs: 123_456, })); - sharedMocks.resolveAdvertisedLanHost.mockReset(); - sharedMocks.resolveAdvertisedLanHost.mockResolvedValue(null); + sharedMocks.resolveAdvertisedLanHostCore.mockReset(); + sharedMocks.resolveAdvertisedLanHostCore.mockResolvedValue(null); sharedMocks.resolveAdvertisedControlUiLinks.mockReset(); }); @@ -418,7 +418,9 @@ describe("runBrowserHatchHandoff", () => { tlsConfig: { enabled: true }, sshHint: undefined, }; - sharedMocks.resolveAdvertisedLanHost.mockRejectedValue(new Error("default route unavailable")); + sharedMocks.resolveAdvertisedLanHostCore.mockRejectedValue( + new Error("default route unavailable"), + ); sharedMocks.resolveAdvertisedControlUiLinks.mockImplementation(async (params) => { const { resolveAdvertisedControlUiLinks } = await import("../gateway/control-ui-links.js"); return await resolveAdvertisedControlUiLinks(params); @@ -440,7 +442,7 @@ describe("runBrowserHatchHandoff", () => { ); expect(result).toEqual({ handedOff: true }); - expect(sharedMocks.resolveAdvertisedLanHost).toHaveBeenCalledOnce(); + expect(sharedMocks.resolveAdvertisedLanHostCore).toHaveBeenCalledOnce(); expect(networkInterfaces).toHaveBeenCalled(); const displayed = vi .mocked(prompter.note) diff --git a/src/commands/onboard-helpers.test.ts b/src/commands/onboard-helpers.test.ts index 7ad9f502e6f4..17926bcb9632 100644 --- a/src/commands/onboard-helpers.test.ts +++ b/src/commands/onboard-helpers.test.ts @@ -82,7 +82,7 @@ const mocks = vi.hoisted(() => ({ termination: "exit", })), pickPrimaryTailnetIPv4: vi.fn<() => string | undefined>(() => undefined), - resolveAdvertisedLanHost: vi.fn<() => Promise>(async () => null), + resolveAdvertisedLanHostCore: vi.fn<() => Promise>(async () => null), probeGateway: vi.fn(), deleteWorkspaceState: vi.fn(), prepareWorkspaceStateDeletion: vi.fn((workspaceDir: string) => ({ workspaceDir })), @@ -109,7 +109,7 @@ vi.mock("../infra/tailnet.js", () => ({ })); vi.mock("../infra/advertised-lan-host.js", () => ({ - resolveAdvertisedLanHost: mocks.resolveAdvertisedLanHost, + resolveAdvertisedLanHostCore: mocks.resolveAdvertisedLanHostCore, })); vi.mock("../gateway/probe.js", () => ({ @@ -1069,7 +1069,7 @@ describe("resolveControlUiLinks", () => { }); it("uses route-aware advertised LAN host for display links", async () => { - mocks.resolveAdvertisedLanHost.mockResolvedValueOnce("10.211.55.3"); + mocks.resolveAdvertisedLanHostCore.mockResolvedValueOnce("10.211.55.3"); const links = await resolveAdvertisedControlUiLinks({ port: 18789, @@ -1088,7 +1088,7 @@ describe("resolveControlUiLinks", () => { expect(links.httpUrl).toBe("http://127.0.0.1:18789/"); expect(links.wsUrl).toBe("ws://127.0.0.1:18789"); - expect(mocks.resolveAdvertisedLanHost).not.toHaveBeenCalled(); + expect(mocks.resolveAdvertisedLanHostCore).not.toHaveBeenCalled(); }); }); diff --git a/src/commands/onboarding-plugin-install.test.ts b/src/commands/onboarding-plugin-install.test.ts index d1ca21b192e2..e4472af4ddbf 100644 --- a/src/commands/onboarding-plugin-install.test.ts +++ b/src/commands/onboarding-plugin-install.test.ts @@ -7,7 +7,7 @@ import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveRegistryUpdateChannel } from "../infra/update-channels.js"; import type { PluginEnableResult } from "../plugins/enable.js"; import { resolveNpmInstallSpecsForUpdateChannel } from "../plugins/install-channel-specs.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { VERSION } from "../version.js"; function expectedNpmInstallSpec(spec: string): string { @@ -1240,7 +1240,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("does not offer local installs when the workspace only has a spoofed .git marker", async () => { - await withTempDir({ prefix: "openclaw-onboarding-install-spoofed-git-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-onboarding-install-spoofed-git-" }, async (temp) => { const workspaceDir = path.join(temp, "workspace"); const cwdDir = path.join(temp, "cwd"); const pluginDir = path.join(workspaceDir, "plugins", "demo"); @@ -1298,7 +1298,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("allows local installs for real gitdir checkouts and sanitizes prompt text", async () => { - await withTempDir({ prefix: "openclaw-onboarding-install-gitdir-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-onboarding-install-gitdir-" }, async (temp) => { const workspaceDir = path.join(temp, "workspace"); const pluginDir = path.join(workspaceDir, "plugins", "demo"); await fs.mkdir(pluginDir, { recursive: true }); @@ -1356,7 +1356,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("does not add local plugin paths when enablement is blocked by policy", async () => { - await withTempDir({ prefix: "openclaw-onboarding-install-blocked-enable-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-onboarding-install-blocked-enable-" }, async (temp) => { const workspaceDir = path.join(temp, "workspace"); const pluginDir = path.join(workspaceDir, "plugins", "demo"); await fs.mkdir(pluginDir, { recursive: true }); @@ -1404,7 +1404,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("allows local installs for linked git worktrees", async () => { - await withTempDir({ prefix: "openclaw-onboarding-install-worktree-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-onboarding-install-worktree-" }, async (temp) => { const workspaceDir = path.join(temp, "workspace"); const pluginDir = path.join(workspaceDir, "plugins", "demo"); const commonGitDir = path.join(temp, "repo.git"); @@ -1458,7 +1458,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("records local install source metadata when a local path is selected", async () => { - await withTempDir({ prefix: "openclaw-onboarding-install-local-record-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-onboarding-install-local-record-" }, async (temp) => { const workspaceDir = path.join(temp, "workspace"); const pluginDir = path.join(workspaceDir, "plugins", "demo"); await fs.mkdir(path.join(workspaceDir, ".git"), { recursive: true }); @@ -1514,7 +1514,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("hides the npm download option for bundled plugins so the menu matches non-npm channels", async () => { - await withTempDir({ prefix: "openclaw-onboarding-install-bundled-prompt-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-onboarding-install-bundled-prompt-" }, async (temp) => { const bundledDir = path.join(temp, "dist", "extensions", "tlon"); await fs.mkdir(bundledDir, { recursive: true }); const realBundledDir = await fs.realpath(bundledDir); @@ -1577,7 +1577,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("enables bundled plugins without adding their bundled directory as a local install", async () => { - await withTempDir({ prefix: "openclaw-onboarding-install-bundled-record-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-onboarding-install-bundled-record-" }, async (temp) => { const bundledDir = path.join(temp, "dist", "extensions", "discord"); await fs.mkdir(bundledDir, { recursive: true }); const realBundledDir = await fs.realpath(bundledDir); @@ -1623,7 +1623,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("records local install source metadata when npm install falls back to local", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-onboarding-install-npm-fallback-record-" }, async (temp) => { const workspaceDir = path.join(temp, "workspace"); @@ -1687,7 +1687,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("records absolute local catalog paths as workspace-relative source metadata", async () => { - await withTempDir({ prefix: "openclaw-onboarding-install-portable-record-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-onboarding-install-portable-record-" }, async (temp) => { const workspaceDir = path.join(temp, "workspace"); const pluginDir = path.join(workspaceDir, "plugins", "demo"); await fs.mkdir(path.join(workspaceDir, ".git"), { recursive: true }); @@ -1730,7 +1730,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("keeps local installs available when cwd is a git repo but workspaceDir is not", async () => { - await withTempDir({ prefix: "openclaw-onboarding-install-cwd-git-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-onboarding-install-cwd-git-" }, async (temp) => { const repoDir = path.join(temp, "repo"); const workspaceDir = path.join(temp, "workspace"); const pluginDir = path.join(repoDir, "demo-plugin"); @@ -1784,7 +1784,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("rejects local install paths outside the trusted workspace roots", async () => { - await withTempDir({ prefix: "openclaw-onboarding-install-outside-root-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-onboarding-install-outside-root-" }, async (temp) => { const workspaceDir = path.join(temp, "workspace"); const pluginDir = path.join(temp, "external-plugin"); await fs.mkdir(path.join(workspaceDir, ".git"), { recursive: true }); @@ -1824,7 +1824,7 @@ describe("ensureOnboardingPluginInstalled", () => { }); it("rejects local install paths when relative resolution looks cross-drive", async () => { - await withTempDir({ prefix: "openclaw-onboarding-install-cross-drive-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-onboarding-install-cross-drive-" }, async (temp) => { const workspaceDir = path.join(temp, "workspace"); const pluginDir = path.join(workspaceDir, "plugins", "demo"); await fs.mkdir(path.join(workspaceDir, ".git"), { recursive: true }); diff --git a/src/commands/sandbox-explain.ts b/src/commands/sandbox-explain.ts index 3f09c7a33ad5..d1d565fff957 100644 --- a/src/commands/sandbox-explain.ts +++ b/src/commands/sandbox-explain.ts @@ -27,7 +27,7 @@ import { normalizeAnyChannelId } from "../channels/registry.js"; import { getRuntimeConfig } from "../config/config.js"; import { resolveAgentMainSessionKey, - resolveStorePath, + resolveSessionStorePathCore, type SessionEntry, } from "../config/sessions.js"; import { loadSessionEntryReadOnly } from "../config/sessions/session-accessor.js"; @@ -174,7 +174,7 @@ export async function sandboxExplainCommand( }); const mainSessionKey = sandboxRuntime.mainSessionKey; const sessionIsSandboxed = sandboxRuntime.sandboxed; - const storePath = resolveStorePath(cfg.session?.store, { + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: resolvedAgentId, }); // CLI reads must not join the Gateway's writable SQLite lifecycle (#101290). diff --git a/src/commands/sessions-cleanup.test.ts b/src/commands/sessions-cleanup.test.ts index 5e5352b666bf..e04c96b09496 100644 --- a/src/commands/sessions-cleanup.test.ts +++ b/src/commands/sessions-cleanup.test.ts @@ -36,7 +36,7 @@ vi.mock("./session-store-targets.js", () => ({ vi.mock("../config/sessions.js", () => ({ resolveMaintenanceConfig: mocks.resolveMaintenanceConfig, loadSessionStore: mocks.loadSessionStore, - resolveSessionFilePath: mocks.resolveSessionFilePath, + resolveSessionFilePathCore: mocks.resolveSessionFilePath, resolveSessionFilePathOptions: mocks.resolveSessionFilePathOptions, pruneStaleEntries: mocks.pruneStaleEntries, capEntryCount: mocks.capEntryCount, diff --git a/src/commands/sessions-tail.test.ts b/src/commands/sessions-tail.test.ts index 79df7496221f..ac1138759948 100644 --- a/src/commands/sessions-tail.test.ts +++ b/src/commands/sessions-tail.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import type { SessionEntry } from "../config/sessions/types.js"; import type { RuntimeEnv } from "../runtime.js"; import { closeOpenClawAgentDatabasesForTest } from "../state/openclaw-agent-db.js"; @@ -85,7 +85,7 @@ describe("sessionsTailCommand", () => { key = sessionKey, entry: Partial = {}, ): Promise { - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: key, storePath }, { sessionId: "session-one", @@ -286,7 +286,7 @@ describe("sessionsTailCommand", () => { const opsSessionKey = "agent:ops:telegram:direct:owner"; const opsSessionsDir = path.join(process.env.OPENCLAW_STATE_DIR!, "agents", "ops", "sessions"); const opsStorePath = path.join(opsSessionsDir, "sessions.json"); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: opsSessionKey, storePath: opsStorePath }, { sessionId: "ops-session", updatedAt: 3, status: "done" }, ); diff --git a/src/commands/sessions.default-agent-store.test.ts b/src/commands/sessions.default-agent-store.test.ts index 195e4e813d1e..e1e8b05d7c58 100644 --- a/src/commands/sessions.default-agent-store.test.ts +++ b/src/commands/sessions.default-agent-store.test.ts @@ -27,7 +27,7 @@ vi.mock("../config/sessions.js", async () => { await vi.importActual("../config/sessions.js"); return { ...actual, - resolveStorePath: resolveStorePathMock, + resolveSessionStorePathCore: resolveStorePathMock, }; }); diff --git a/src/commands/status.agent-local.ts b/src/commands/status.agent-local.ts index bc3693652c6f..6d43d329082b 100644 --- a/src/commands/status.agent-local.ts +++ b/src/commands/status.agent-local.ts @@ -3,7 +3,7 @@ import path from "node:path"; import { resolveAgentWorkspaceDir } from "../agents/agent-scope.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { listSessionEntriesReadOnly } from "../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../config/types.js"; import { listGatewayAgentsBasic } from "../gateway/agent-list.js"; @@ -49,7 +49,7 @@ export async function getAgentLocalStatuses( const bootstrapPath = workspaceDir != null ? path.join(workspaceDir, "BOOTSTRAP.md") : null; const bootstrapPending = bootstrapPath != null ? await pathExists(bootstrapPath) : null; - const sessionsPath = resolveStorePath(cfg.session?.store, { agentId }); + const sessionsPath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); const sessions = listSessionEntriesReadOnly({ agentId, storePath: sessionsPath }) // Global/unknown buckets are aggregate compatibility entries, not agent activity. .filter(({ sessionKey }) => sessionKey !== "global" && sessionKey !== "unknown") diff --git a/src/commands/status.service-summary.test.ts b/src/commands/status.service-summary.test.ts index 4365e8ab8df4..9743d8faa6ff 100644 --- a/src/commands/status.service-summary.test.ts +++ b/src/commands/status.service-summary.test.ts @@ -6,7 +6,7 @@ import * as gatewayServiceLayout from "../daemon/service-layout.js"; import type { GatewayServiceEnvArgs } from "../daemon/service-types.js"; import { resolveGatewayService, type GatewayService } from "../daemon/service.js"; import { createMockGatewayService } from "../daemon/service.test-helpers.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withMockedPlatform } from "../test-utils/vitest-spies.js"; import { readServiceStatusSummary } from "./status.service-summary.js"; @@ -145,7 +145,7 @@ describe("readServiceStatusSummary", () => { }); it("includes service layout diagnostics and flags source checkout entrypoints", async () => { - await withTempDir({ prefix: "openclaw-status-service-layout-" }, async (root) => { + await withTestDir({ prefix: "openclaw-status-service-layout-" }, async (root) => { await fs.mkdir(path.join(root, ".git"), { recursive: true }); await fs.mkdir(path.join(root, "src"), { recursive: true }); await fs.mkdir(path.join(root, "extensions"), { recursive: true }); diff --git a/src/commands/status.summary.test.ts b/src/commands/status.summary.test.ts index 666efaa083f8..dd63e4f53f06 100644 --- a/src/commands/status.summary.test.ts +++ b/src/commands/status.summary.test.ts @@ -134,7 +134,7 @@ vi.mock("../config/config.js", () => ({ })); vi.mock("../config/sessions/paths.js", () => ({ - resolveStorePath: vi.fn(() => "/tmp/sessions.json"), + resolveSessionStorePathCore: vi.fn(() => "/tmp/sessions.json"), })); vi.mock("../config/sessions/session-accessor.js", () => ({ @@ -196,7 +196,7 @@ vi.mock("../status/link-channel.js", () => ({ })); const { buildChannelSummary } = await import("../infra/channel-summary.js"); -const { resolveStorePath } = await import("../config/sessions/paths.js"); +const { resolveSessionStorePathCore } = await import("../config/sessions/paths.js"); const { listGatewayAgentsBasic } = await import("../gateway/agent-list.js"); const { resolveLinkChannelContext } = await import("../status/link-channel.js"); let getStatusSummary: typeof import("../status/summary.js").getStatusSummary; @@ -267,7 +267,7 @@ describe("getStatusSummary", () => { : undefined, ); statusSummaryMocks.listSessionEntriesCore.mockReturnValue([]); - vi.mocked(resolveStorePath).mockReturnValue("/tmp/sessions.json"); + vi.mocked(resolveSessionStorePathCore).mockReturnValue("/tmp/sessions.json"); vi.mocked(listGatewayAgentsBasic).mockReturnValue({ defaultId: "main", mainKey: "main", @@ -844,7 +844,7 @@ describe("getStatusSummary", () => { scope: "per-sender", agents: [{ id: "main" }, { id: "ops" }], }); - vi.mocked(resolveStorePath).mockImplementation((_store, opts) => { + vi.mocked(resolveSessionStorePathCore).mockImplementation((_store, opts) => { return `/tmp/${opts?.agentId ?? "main"}/sessions.json`; }); statusSummaryMocks.listSessionEntriesCore.mockImplementation((scope) => @@ -881,7 +881,7 @@ describe("getStatusSummary", () => { scope: "per-sender", agents: [{ id: "main" }, { id: "ops" }], }); - vi.mocked(resolveStorePath).mockReturnValue("/tmp/shared-sessions.json"); + vi.mocked(resolveSessionStorePathCore).mockReturnValue("/tmp/shared-sessions.json"); statusSummaryMocks.listSessionEntriesCore.mockReturnValue( toSessionEntrySummaries({ main: { sessionId: "shared-session", updatedAt: 1 }, diff --git a/src/commands/status.test.ts b/src/commands/status.test.ts index 59579a6dac83..69575fdb9f2e 100644 --- a/src/commands/status.test.ts +++ b/src/commands/status.test.ts @@ -566,7 +566,7 @@ vi.mock("../config/sessions/main-session.js", () => ({ resolveMainSessionKey: mocks.resolveMainSessionKey, })); vi.mock("../config/sessions/paths.js", () => ({ - resolveStorePath: mocks.resolveStorePath, + resolveSessionStorePathCore: mocks.resolveStorePath, })); vi.mock("../config/sessions/session-accessor.js", () => ({ listSessionEntriesCore: (opts?: { storePath?: string }) => diff --git a/src/config/agent-dirs.fs-identity.test.ts b/src/config/agent-dirs.fs-identity.test.ts index 8da014f364c6..c87ad3a61ddd 100644 --- a/src/config/agent-dirs.fs-identity.test.ts +++ b/src/config/agent-dirs.fs-identity.test.ts @@ -1,12 +1,12 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { findDuplicateAgentDirs } from "./agent-dirs.js"; describe("agent directory filesystem identity", () => { it("keeps case-distinct directories separate on a case-sensitive volume", async () => { - await withTempDir({ prefix: "openclaw-agent-dirs-case-" }, async (root) => { + await withTestDir({ prefix: "openclaw-agent-dirs-case-" }, async (root) => { const upper = path.join(root, "AgentState"); const lower = path.join(root, "agentstate"); await fs.mkdir(upper); @@ -33,7 +33,7 @@ describe("agent directory filesystem identity", () => { if (process.platform === "win32") { return; } - await withTempDir({ prefix: "openclaw-agent-dirs-alias-" }, async (root) => { + await withTestDir({ prefix: "openclaw-agent-dirs-alias-" }, async (root) => { const target = path.join(root, "target"); const alias = path.join(root, "alias"); await fs.mkdir(target); @@ -53,7 +53,7 @@ describe("agent directory filesystem identity", () => { }); it("does not create configured missing directories or leave probe entries", async () => { - await withTempDir({ prefix: "openclaw-agent-dirs-missing-" }, async (root) => { + await withTestDir({ prefix: "openclaw-agent-dirs-missing-" }, async (root) => { const upper = path.join(root, "FutureState"); const lower = path.join(root, "futurestate"); diff --git a/src/config/doc-baseline.integration.test.ts b/src/config/doc-baseline.integration.test.ts index 1ab2c499357b..ca0e0114a374 100644 --- a/src/config/doc-baseline.integration.test.ts +++ b/src/config/doc-baseline.integration.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { beforeAll, describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { type ConfigDocBaselineEntry, renderConfigDocBaselineArtifacts, @@ -150,7 +150,7 @@ describe("config doc baseline integration", () => { }); it("supports check mode for stale hash files", async () => { - await withTempDir({ prefix: "openclaw-config-doc-baseline-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-config-doc-baseline-" }, async (tempRoot) => { const rendered = getSharedRendered(); const initial = await writeConfigDocBaselineArtifacts({ @@ -184,7 +184,7 @@ describe("config doc baseline integration", () => { }); it("ratchets config entry count budgets in both directions", async () => { - await withTempDir({ prefix: "openclaw-config-doc-counts-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-config-doc-counts-" }, async (tempRoot) => { const rendered = await getSharedRendered(); const countsPath = path.join(tempRoot, "docs/.generated/config-baseline.counts.json"); diff --git a/src/config/includes.test.ts b/src/config/includes.test.ts index 6fe9e630fcd7..494abe8a1c9f 100644 --- a/src/config/includes.test.ts +++ b/src/config/includes.test.ts @@ -3,7 +3,7 @@ import nodeFs from "node:fs"; import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { collectIncludePathsRecursive } from "./includes-scan.js"; import { CircularIncludeError, @@ -432,7 +432,7 @@ describe("collectIncludePathsRecursive", () => { it.runIf(process.platform !== "win32")( "only reports includes the production resolver can safely open", async () => { - await withTempDir({ prefix: "openclaw-include-scan-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-include-scan-" }, async (tempRoot) => { const configDir = path.join(tempRoot, "config"); const safeIncludePath = path.join(configDir, "safe.json5"); const outsideIncludePath = path.join(tempRoot, "outside.json5"); @@ -457,7 +457,7 @@ describe("collectIncludePathsRecursive", () => { it.runIf(process.platform !== "win32")( "keeps the original root boundary after a parent symlink swap", async () => { - await withTempDir({ prefix: "openclaw-include-root-swap-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-include-root-swap-" }, async (tempRoot) => { const trustedDir = path.join(tempRoot, "trusted"); const outsideDir = path.join(tempRoot, "outside"); const configLink = path.join(tempRoot, "config-link"); @@ -498,7 +498,7 @@ describe("collectIncludePathsRecursive", () => { ); it("honors explicitly allowed include roots", async () => { - await withTempDir({ prefix: "openclaw-include-scan-roots-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-include-scan-roots-" }, async (tempRoot) => { const configDir = path.join(tempRoot, "config"); const sharedDir = path.join(tempRoot, "shared"); const sharedIncludePath = path.join(sharedDir, "shared.json5"); @@ -517,7 +517,7 @@ describe("collectIncludePathsRecursive", () => { }); it("continues past rejected nested includes to later safe siblings", async () => { - await withTempDir({ prefix: "openclaw-include-scan-nested-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-include-scan-nested-" }, async (tempRoot) => { const configDir = path.join(tempRoot, "config"); const nestedDir = path.join(configDir, "nested"); const outerIncludePath = path.join(nestedDir, "outer.json5"); @@ -545,7 +545,7 @@ describe("collectIncludePathsRecursive", () => { }); it("revisits an include reached later at a shallower depth", async () => { - await withTempDir({ prefix: "openclaw-include-scan-depth-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-include-scan-depth-" }, async (tempRoot) => { const configDir = path.join(tempRoot, "config"); const sharedIncludePath = path.join(configDir, "shared.json5"); const leafIncludePath = path.join(configDir, "leaf.json5"); @@ -576,7 +576,7 @@ describe("resolveConfigIncludeWritePath", () => { it.runIf(process.platform !== "win32")( "canonicalizes missing targets through symlinks into allowed roots", async () => { - await withTempDir({ prefix: "openclaw-include-write-path-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-include-write-path-" }, async (tempRoot) => { const configDir = path.join(tempRoot, "config"); const allowedDir = path.join(tempRoot, "allowed"); const linkDir = path.join(configDir, "shared"); @@ -869,7 +869,7 @@ describe("security: path traversal protection (CWE-22)", () => { }); it("allows include files when the config root path is a symlink", async () => { - await withTempDir({ prefix: "openclaw-includes-symlink-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-includes-symlink-" }, async (tempRoot) => { const realRoot = path.join(tempRoot, "real"); const linkRoot = path.join(tempRoot, "link"); await fs.mkdir(path.join(realRoot, "includes"), { recursive: true }); @@ -919,7 +919,7 @@ describe("security: path traversal protection (CWE-22)", () => { if (process.platform === "win32") { return; } - await withTempDir({ prefix: "openclaw-includes-hardlink-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-includes-hardlink-" }, async (tempRoot) => { const configDir = path.join(tempRoot, "config"); const outsideDir = path.join(tempRoot, "outside"); await fs.mkdir(configDir, { recursive: true }); @@ -946,7 +946,7 @@ describe("security: path traversal protection (CWE-22)", () => { }); it("rejects include files larger than the guarded read limit", async () => { - await withTempDir({ prefix: "openclaw-includes-big-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-includes-big-" }, async (tempRoot) => { const configDir = path.join(tempRoot, "config"); await fs.mkdir(configDir, { recursive: true }); await fs.writeFile( @@ -1013,7 +1013,7 @@ describe("OPENCLAW_INCLUDE_ROOTS allowlist", () => { }); it("resolves a symlinked include whose realpath lands inside an allowed root", async () => { - await withTempDir({ prefix: "openclaw-includes-allowed-symlink-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-includes-allowed-symlink-" }, async (tempRoot) => { const configDir = path.join(tempRoot, "config"); const sharedDir = path.join(tempRoot, "shared"); await fs.mkdir(configDir, { recursive: true }); @@ -1038,7 +1038,7 @@ describe("OPENCLAW_INCLUDE_ROOTS allowlist", () => { }); it("rejects a symlinked include that escapes both the config directory and every allowed root", async () => { - await withTempDir({ prefix: "openclaw-includes-allowed-escape-" }, async (tempRoot) => { + await withTestDir({ prefix: "openclaw-includes-allowed-escape-" }, async (tempRoot) => { const configDir = path.join(tempRoot, "config"); const allowedDir = path.join(tempRoot, "allowed"); const offRootDir = path.join(tempRoot, "off-limits"); diff --git a/src/config/load-channel-config-surface.test.ts b/src/config/load-channel-config-surface.test.ts index 86087488ac9f..acde97340f57 100644 --- a/src/config/load-channel-config-surface.test.ts +++ b/src/config/load-channel-config-surface.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { loadChannelConfigSurfaceModule } from "../../scripts/load-channel-config-surface.ts"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; function createDemoModule(repoRoot: string, filename: string, source: string): string { const packageRoot = path.join(repoRoot, "extensions", "demo"); @@ -20,7 +20,7 @@ function createDemoModule(repoRoot: string, filename: string, source: string): s describe("loadChannelConfigSurfaceModule", () => { it("loads TypeScript through plugin SDK aliases", async () => { - await withTempDir({ prefix: "openclaw-config-surface-" }, async (repoRoot) => { + await withTestDir({ prefix: "openclaw-config-surface-" }, async (repoRoot) => { const modulePath = createDemoModule( repoRoot, "config-schema.ts", @@ -43,7 +43,7 @@ describe("loadChannelConfigSurfaceModule", () => { }); it("wraps a raw config schema loaded natively", async () => { - await withTempDir({ prefix: "openclaw-config-surface-" }, async (repoRoot) => { + await withTestDir({ prefix: "openclaw-config-surface-" }, async (repoRoot) => { const modulePath = createDemoModule( repoRoot, "config-schema.mjs", @@ -65,7 +65,7 @@ describe("loadChannelConfigSurfaceModule", () => { }); it("returns null without a config schema export", async () => { - await withTempDir({ prefix: "openclaw-config-surface-" }, async (repoRoot) => { + await withTestDir({ prefix: "openclaw-config-surface-" }, async (repoRoot) => { const modulePath = createDemoModule( repoRoot, "config-schema.mjs", @@ -77,7 +77,7 @@ describe("loadChannelConfigSurfaceModule", () => { }); it("rejects invalid module source", async () => { - await withTempDir({ prefix: "openclaw-config-surface-" }, async (repoRoot) => { + await withTestDir({ prefix: "openclaw-config-surface-" }, async (repoRoot) => { const modulePath = createDemoModule(repoRoot, "config-schema.ts", "export const = ;"); await expect(loadChannelConfigSurfaceModule(modulePath)).rejects.toThrow(); diff --git a/src/config/paths.test.ts b/src/config/paths.test.ts index 1de67155fb40..4e8f1246f3c5 100644 --- a/src/config/paths.test.ts +++ b/src/config/paths.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { resolveLegacyOAuthPath } from "../agents/auth-profiles/legacy-source-diagnostic.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { CONFIG_PATH, DEFAULT_GATEWAY_PORT, @@ -29,7 +29,7 @@ function envWith(overrides: Record): NodeJS.ProcessE describe("default state directory", () => { it("matches filesystem aliases of the default state directory", async () => { - await withTempDir({ prefix: "openclaw-default-state-" }, async (root) => { + await withTestDir({ prefix: "openclaw-default-state-" }, async (root) => { const home = path.join(root, "home"); const defaultStateDir = path.join(home, ".openclaw"); const stateAlias = path.join(home, "state-alias"); @@ -59,7 +59,7 @@ describe("default install identity", () => { }); it("preserves implicit legacy config discovery for the default profile", async () => { - await withTempDir({ prefix: "openclaw-default-install-legacy-config-" }, async (home) => { + await withTestDir({ prefix: "openclaw-default-install-legacy-config-" }, async (home) => { const stateDir = path.join(home, ".openclaw"); const legacyStateDir = path.join(home, ".clawdbot"); const legacyConfigPath = path.join(legacyStateDir, "clawdbot.json"); @@ -144,7 +144,7 @@ describe("default install identity", () => { }); it("accepts the canonical paths a named profile projects", async () => { - await withTempDir({ prefix: "openclaw-profile-install-" }, async (home) => { + await withTestDir({ prefix: "openclaw-profile-install-" }, async (home) => { const defaultStateDir = path.join(home, ".openclaw"); const profileStateDir = path.join(home, ".openclaw-work"); await fs.mkdir(defaultStateDir, { recursive: true }); @@ -501,7 +501,7 @@ describe("state + config path candidates", () => { }); it("prefers ~/.openclaw when it exists and legacy dir is missing", async () => { - await withTempDir({ prefix: "openclaw-state-" }, async (root) => { + await withTestDir({ prefix: "openclaw-state-" }, async (root) => { const newDir = path.join(root, ".openclaw"); await fs.mkdir(newDir, { recursive: true }); const resolved = resolveStateDir({} as NodeJS.ProcessEnv, () => root); @@ -510,7 +510,7 @@ describe("state + config path candidates", () => { }); it("falls back to existing legacy state dir when ~/.openclaw is missing", async () => { - await withTempDir({ prefix: "openclaw-state-legacy-" }, async (root) => { + await withTestDir({ prefix: "openclaw-state-legacy-" }, async (root) => { const legacyDir = path.join(root, ".clawdbot"); await fs.mkdir(legacyDir, { recursive: true }); const resolved = resolveStateDir({} as NodeJS.ProcessEnv, () => root); @@ -519,7 +519,7 @@ describe("state + config path candidates", () => { }); it("CONFIG_PATH prefers existing config when present", async () => { - await withTempDir({ prefix: "openclaw-config-" }, async (root) => { + await withTestDir({ prefix: "openclaw-config-" }, async (root) => { const legacyDir = path.join(root, ".openclaw"); await fs.mkdir(legacyDir, { recursive: true }); const legacyPath = path.join(legacyDir, "openclaw.json"); @@ -531,7 +531,7 @@ describe("state + config path candidates", () => { }); it("respects state dir overrides when config is missing", async () => { - await withTempDir({ prefix: "openclaw-config-override-" }, async (root) => { + await withTestDir({ prefix: "openclaw-config-override-" }, async (root) => { const legacyDir = path.join(root, ".openclaw"); await fs.mkdir(legacyDir, { recursive: true }); const legacyConfig = path.join(legacyDir, "openclaw.json"); diff --git a/src/config/sessions.test.ts b/src/config/sessions.test.ts index 471c6b4d2244..f33ad5f66563 100644 --- a/src/config/sessions.test.ts +++ b/src/config/sessions.test.ts @@ -7,7 +7,7 @@ import { withEnv } from "../test-utils/env.js"; import { buildGroupDisplayName, deriveSessionKey, - resolveSessionFilePath, + resolveSessionFilePathCore, resolveSessionFilePathOptions, resolveSessionKey, resolveSessionTranscriptPath, @@ -219,7 +219,7 @@ describe("sessions", () => { it("uses agent id when resolving session file fallback paths", () => { withStateDir("/custom/state", () => { - const sessionFile = resolveSessionFilePath("sess-2", undefined, { + const sessionFile = resolveSessionFilePathCore("sess-2", undefined, { agentId: "codex", }); expect(sessionFile).toBe( @@ -232,7 +232,7 @@ describe("sessions", () => { const { stateDir, bot2SessionPath } = await createAgentSessionsLayout("cross-agent"); const sessionFile = withStateDir(stateDir, () => // Agent bot1 resolves a sessionFile that belongs to agent bot2 - resolveSessionFilePath("sess-1", { sessionFile: bot2SessionPath }, { agentId: "bot1" }), + resolveSessionFilePathCore("sess-1", { sessionFile: bot2SessionPath }, { agentId: "bot1" }), ); expect(await normalizePathForComparison(sessionFile)).toBe( await normalizePathForComparison(bot2SessionPath), @@ -244,7 +244,7 @@ describe("sessions", () => { const originalBase = path.resolve("/original/state"); const bot2Session = path.join(originalBase, "agents", "bot2", "sessions", "sess-1.jsonl"); // sessionFile was created under a different state dir than current env - const sessionFile = resolveSessionFilePath( + const sessionFile = resolveSessionFilePathCore( "sess-1", { sessionFile: bot2Session }, { agentId: "bot1" }, @@ -257,7 +257,7 @@ describe("sessions", () => { withStateDir(path.resolve("/different/state"), () => { const originalBase = path.resolve("/original/state"); const unsafe = path.join(originalBase, "agents", "bot2", "sessions", "..", "..", "etc"); - const sessionFile = resolveSessionFilePath( + const sessionFile = resolveSessionFilePathCore( "sess-1", { sessionFile: path.join(unsafe, "passwd") }, { agentId: "bot1" }, @@ -277,7 +277,7 @@ describe("sessions", () => { "nested", "sess-1.jsonl", ); - const sessionFile = resolveSessionFilePath( + const sessionFile = resolveSessionFilePathCore( "sess-1", { sessionFile: nested }, { agentId: "bot1" }, @@ -305,7 +305,7 @@ describe("sessions", () => { storePath: mainStorePath, }); - return resolveSessionFilePath("sess-1", { sessionFile: bot2SessionPath }, opts); + return resolveSessionFilePathCore("sess-1", { sessionFile: bot2SessionPath }, opts); }); expect(await normalizePathForComparison(sessionFile)).toBe( await normalizePathForComparison(bot2SessionPath), @@ -315,7 +315,7 @@ describe("sessions", () => { it("falls back to derived transcript path when sessionFile is outside agent sessions directories", async () => { const { stateDir, outsidePath } = await createAgentSessionsLayout("outside-fallback"); const sessionFile = withStateDir(stateDir, () => - resolveSessionFilePath("sess-1", { sessionFile: outsidePath }, { agentId: "bot1" }), + resolveSessionFilePathCore("sess-1", { sessionFile: outsidePath }, { agentId: "bot1" }), ); const expectedPath = path.join(stateDir, "agents", "bot1", "sessions", "sess-1.jsonl"); expect(await normalizePathForComparison(sessionFile)).toBe( diff --git a/src/config/sessions/cleanup-service.ts b/src/config/sessions/cleanup-service.ts index 6ff8ebb77c33..178b17e14667 100644 --- a/src/config/sessions/cleanup-service.ts +++ b/src/config/sessions/cleanup-service.ts @@ -14,7 +14,7 @@ import { type SessionDiskBudgetSweepResult, type SessionUnreferencedArtifactSweepResult, } from "./disk-budget.js"; -import { resolveStorePath } from "./paths.js"; +import { resolveSessionStorePathCore } from "./paths.js"; import { applySessionEntryLifecycleMutation, listSessionEntriesCore, @@ -710,7 +710,9 @@ export async function purgeAgentSessionStoreEntries( typeof storeConfig === "string" && !storeConfig.includes("{agentId}") ? normalizeAgentId(resolveDefaultAgentId(cfg)) : normalizedAgentId; - const storePath = resolveStorePath(cfg.session?.store, { agentId: normalizedAgentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { + agentId: normalizedAgentId, + }); await purgeDeletedAgentSessionEntries({ cfg, agentId: normalizedAgentId, diff --git a/src/config/sessions/combined-store-gateway.ts b/src/config/sessions/combined-store-gateway.ts index ede5303e4b41..195eb7ef9d8e 100644 --- a/src/config/sessions/combined-store-gateway.ts +++ b/src/config/sessions/combined-store-gateway.ts @@ -15,7 +15,7 @@ import { } from "../../routing/session-key.js"; import { listOpenIncognitoAgentDatabases } from "../../state/openclaw-agent-db.js"; import type { OpenClawConfig } from "../types.openclaw.js"; -import { resolveStorePath } from "./paths.js"; +import { resolveSessionStorePathCore } from "./paths.js"; import { countSessionEntryRowsReadOnly, listSessionEntriesCore, @@ -192,7 +192,7 @@ function resolveGatewaySessionStoreTargets( const durableTargets = dedupeSessionStoreTargetsBySqliteTarget( ownerIds.map((agentId) => ({ agentId, - storePath: resolveStorePath(storeConfig, { agentId }), + storePath: resolveSessionStorePathCore(storeConfig, { agentId }), })), { defaultAgentId, @@ -304,7 +304,7 @@ export function loadCombinedSessionStoreForGatewayCore( }); } } - const durableStorePath = resolveStorePath(storeConfig, { agentId: defaultAgentId }); + const durableStorePath = resolveSessionStorePathCore(storeConfig, { agentId: defaultAgentId }); const incognitoStorePaths = mergeOpenIncognitoStores({ cfg, combined, diff --git a/src/config/sessions/conversation-delivery-store.test.ts b/src/config/sessions/conversation-delivery-store.test.ts index 16f261286617..7f95e51d4f7c 100644 --- a/src/config/sessions/conversation-delivery-store.test.ts +++ b/src/config/sessions/conversation-delivery-store.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest"; import { normalizeLegacySessionEntryDelivery } from "../../infra/state-migrations.legacy-session-store.js"; import { buildConversationRef } from "../../routing/conversation-ref.js"; import { closeOpenClawAgentDatabasesForTest } from "../../state/openclaw-agent-db.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import type { DeliveryContext } from "../../utils/delivery-context.types.js"; import { beginConversationDeliveryOperation, @@ -18,7 +18,7 @@ import { import { resolveConversation } from "./conversation-registry.js"; import { deleteSessionEntryLifecycle, - upsertSessionEntry as upsertCanonicalSessionEntry, + upsertSessionEntryCore as upsertCanonicalSessionEntry, } from "./session-accessor.js"; import type { SessionEntry, SessionOrigin } from "./types.js"; @@ -38,7 +38,7 @@ async function withConversationStore( conversationRef: string; }) => Promise | void, ): Promise { - await withTempDir({ prefix: "openclaw-conversation-delivery-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-conversation-delivery-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const scope = { agentId: "main", storePath }; try { diff --git a/src/config/sessions/conversation-registry.test.ts b/src/config/sessions/conversation-registry.test.ts index 803056070151..9c3105a92a46 100644 --- a/src/config/sessions/conversation-registry.test.ts +++ b/src/config/sessions/conversation-registry.test.ts @@ -16,7 +16,7 @@ import { } from "./conversation-registry.js"; import { deleteSessionEntryLifecycle, - upsertSessionEntry as upsertCanonicalSessionEntry, + upsertSessionEntryCore as upsertCanonicalSessionEntry, } from "./session-accessor.js"; import { getSessionKysely, diff --git a/src/config/sessions/conversation-registry.ts b/src/config/sessions/conversation-registry.ts index 5318c82145b2..8212bb7201e6 100644 --- a/src/config/sessions/conversation-registry.ts +++ b/src/config/sessions/conversation-registry.ts @@ -3,7 +3,7 @@ import { executeSqliteQuerySync } from "../../infra/kysely-sync.js"; import { openOpenClawAgentDatabase } from "../../state/openclaw-agent-db.js"; import type { OpenClawConfig } from "../types.openclaw.js"; import type { ConversationIdentity, ConversationKind } from "./conversation-identity.js"; -import { resolveStorePath } from "./paths.js"; +import { resolveSessionStorePathCore } from "./paths.js"; import { upsertConversationIdentity } from "./session-accessor.sqlite-conversation.js"; import { getSessionKysely, @@ -46,7 +46,7 @@ export function resolveConversationRegistryScope(params: { return { agentId: params.agentId, ...(configuredStore - ? { storePath: resolveStorePath(configuredStore, { agentId: params.agentId }) } + ? { storePath: resolveSessionStorePathCore(configuredStore, { agentId: params.agentId }) } : {}), }; } diff --git a/src/config/sessions/delivery-info.test.ts b/src/config/sessions/delivery-info.test.ts index 64012d449dcb..137f2ec28077 100644 --- a/src/config/sessions/delivery-info.test.ts +++ b/src/config/sessions/delivery-info.test.ts @@ -47,7 +47,7 @@ vi.mock("../io.js", () => ({ })); vi.mock("./paths.js", () => ({ - resolveStorePath: (_store?: string, opts?: { agentId?: string }) => + resolveSessionStorePathCore: (_store?: string, opts?: { agentId?: string }) => opts?.agentId === "worker" ? "/tmp/worker-sessions.json" : "/tmp/sessions.json", })); diff --git a/src/config/sessions/delivery-info.ts b/src/config/sessions/delivery-info.ts index 8cbec6d4b0cf..fa9f1871f0a1 100644 --- a/src/config/sessions/delivery-info.ts +++ b/src/config/sessions/delivery-info.ts @@ -8,7 +8,7 @@ import { requiresFoldedSessionKeyAliasProof } from "../../sessions/session-key-u import { deliveryContextFromSession } from "../../utils/delivery-context.shared.js"; import { getRuntimeConfig } from "../io.js"; import type { OpenClawConfig } from "../types.openclaw.js"; -import { resolveStorePath } from "./paths.js"; +import { resolveSessionStorePathCore } from "./paths.js"; import { openSessionEntryReadView, type SessionEntryReadView } from "./session-accessor.js"; import { foldedSessionKeyAliasCandidates, @@ -82,7 +82,7 @@ export function extractDeliveryInfo( function resolveDeliveryStorePaths(cfg: OpenClawConfig, agentId: string): string[] { const paths = new Set(); - paths.add(resolveStorePath(cfg.session?.store, { agentId })); + paths.add(resolveSessionStorePathCore(cfg.session?.store, { agentId })); // Delivery can be restored from any resolved agent target; store order keeps the configured // primary path first while still covering per-agent stores. for (const target of resolveAllAgentSessionStoreTargetsSync(cfg)) { diff --git a/src/config/sessions/disk-budget.test.ts b/src/config/sessions/disk-budget.test.ts index 3251c3c03fe8..c1286df72f44 100644 --- a/src/config/sessions/disk-budget.test.ts +++ b/src/config/sessions/disk-budget.test.ts @@ -10,7 +10,7 @@ import { openOpenClawAgentDatabase, } from "../../state/openclaw-agent-db.js"; import { closeOpenClawStateDatabaseForTest } from "../../state/openclaw-state-db.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { resolveTrajectoryFilePath, resolveTrajectoryPointerFilePath, @@ -65,7 +65,7 @@ function refreshPathBeforeSecondStat(targetPath: string): ReturnType { it("counts the SQLite main file and WAL as physical session usage", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-sqlite-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-sqlite-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = resolveSqliteTargetFromSessionStorePath(storePath).path; if (!databasePath) { @@ -86,7 +86,7 @@ describe("enforceSessionDiskBudget", () => { }); it("excludes migration archives from physical SQLite usage (#106875)", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-sqlite-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-sqlite-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const databasePath = resolveSqliteTargetFromSessionStorePath(storePath).path; if (!databasePath) { @@ -106,7 +106,7 @@ describe("enforceSessionDiskBudget", () => { }); it("counts durable fixed-store agent partitions and their WAL files", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-partition-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-partition-" }, async (dir) => { const stateDir = path.join(dir, "state"); const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const storePath = path.join(dir, "shared.json"); @@ -131,7 +131,7 @@ describe("enforceSessionDiskBudget", () => { }); it("excludes migration archives from the session disk budget (#106875)", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionKey = "agent:main:main"; const sessionId = "keep"; @@ -168,7 +168,7 @@ describe("enforceSessionDiskBudget", () => { }); it("does not treat referenced transcripts with marker-like session IDs as archived artifacts", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionId = "keep.deleted.keep"; const activeKey = "agent:main:main"; @@ -200,7 +200,7 @@ describe("enforceSessionDiskBudget", () => { }); it("removes true archived transcript artifacts while preserving referenced primary transcripts", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionId = "keep"; const transcriptPath = path.join(dir, `${sessionId}.jsonl`); @@ -237,7 +237,7 @@ describe("enforceSessionDiskBudget", () => { }); it("reclaims stale store temps under pressure but never a fresh in-flight one (#56827)", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionId = "keep"; const transcriptPath = path.join(dir, `${sessionId}.jsonl`); @@ -281,7 +281,7 @@ describe("enforceSessionDiskBudget", () => { }); it("preserves runtime-provided session keys when removing entries for disk budget", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const childKey = "agent:main:subagent:pending-budget"; const removableKey = "agent:main:old-removable"; @@ -318,7 +318,7 @@ describe("enforceSessionDiskBudget", () => { }); it("preserves model-locked harness sessions when removing entries for disk budget", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const lockedKey = "agent:main:harness-owned:locked"; const removableKey = "agent:main:old-removable"; @@ -354,7 +354,7 @@ describe("enforceSessionDiskBudget", () => { }); it("accounts for deduped skills prompt blobs before evicting sessions", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const prompt = `\n${"shared prompt\n".repeat(200)}`; const now = Date.now(); @@ -393,7 +393,7 @@ describe("enforceSessionDiskBudget", () => { }); it("removes unreferenced skills prompt blobs when evicting sessions", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const activeKey = "agent:main:active"; const oldKey = "agent:main:old"; @@ -468,7 +468,7 @@ describe("enforceSessionDiskBudget", () => { }); it("preserves fresh unreferenced skills prompt blobs under pressure", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-fresh-prompt-blob-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-fresh-prompt-blob-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const store: Record = { "agent:main:active": { sessionId: "active", updatedAt: Date.now() }, @@ -499,7 +499,7 @@ describe("enforceSessionDiskBudget", () => { }); it("revalidates stale prompt blobs before removing them under pressure", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-revalidate-prompt-blob-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-revalidate-prompt-blob-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const store: Record = { "agent:main:active": { sessionId: "active", updatedAt: Date.now() }, @@ -536,7 +536,7 @@ describe("enforceSessionDiskBudget", () => { }); it("reclaims stale skills prompt blob temps under pressure", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-prompt-temp-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-prompt-temp-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const store: Record = { "agent:main:main": { sessionId: "keep", updatedAt: Date.now() }, @@ -571,7 +571,7 @@ describe("enforceSessionDiskBudget", () => { }); it("removes unreferenced compaction checkpoint artifacts under pressure", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionId = "keep"; const transcriptPath = path.join(dir, `${sessionId}.jsonl`); @@ -632,7 +632,7 @@ describe("enforceSessionDiskBudget", () => { }); it("removes unreferenced trajectory sidecars while preserving referenced ones", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const sessionId = "keep"; const transcriptPath = path.join(dir, `${sessionId}.jsonl`); @@ -679,7 +679,7 @@ describe("enforceSessionDiskBudget", () => { }); it("does not evict protected thread session entries under store pressure", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const protectedKey = "agent:main:slack:channel:C123:thread:1710000000.000100"; const removableKey = "agent:main:subagent:old-worker"; @@ -722,7 +722,7 @@ describe("enforceSessionDiskBudget", () => { }); it("commits the reduced session index before deleting an evicted transcript", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-commit-order-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-commit-order-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const oldKey = "agent:main:subagent:old-worker"; const activeKey = "agent:main:main"; @@ -772,7 +772,7 @@ describe("enforceSessionDiskBudget", () => { }); it("retains the evicted transcript when the index commit fails", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-commit-fail-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-commit-fail-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const oldKey = "agent:main:subagent:old-worker"; const activeKey = "agent:main:main"; @@ -803,7 +803,7 @@ describe("enforceSessionDiskBudget", () => { }); it("retains evicted artifacts when no durable index commit is available", async () => { - await withTempDir({ prefix: "openclaw-disk-budget-missing-commit-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-disk-budget-missing-commit-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const oldKey = "agent:main:subagent:old-worker"; const activeKey = "agent:main:main"; @@ -835,7 +835,7 @@ describe("enforceSessionDiskBudget", () => { describe("pruneUnreferencedSessionArtifacts", () => { it("reclaims stale store temp sidecars but preserves in-flight ones (#56827)", async () => { - await withTempDir({ prefix: "openclaw-prune-temp-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-prune-temp-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const staleTemp = path.join( dir, @@ -871,7 +871,7 @@ describe("pruneUnreferencedSessionArtifacts", () => { }); it("reclaims unreferenced skills prompt blobs during normal artifact cleanup", async () => { - await withTempDir({ prefix: "openclaw-prune-prompt-blob-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-prune-prompt-blob-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const oldKey = "agent:main:old"; const keepKey = "agent:main:keep"; @@ -938,7 +938,7 @@ describe("pruneUnreferencedSessionArtifacts", () => { }); it("preserves fresh unreferenced skills prompt blobs during normal artifact cleanup", async () => { - await withTempDir({ prefix: "openclaw-prune-fresh-prompt-blob-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-prune-fresh-prompt-blob-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const hash = "c".repeat(64); const blobDir = path.join(dir, "skills-prompts", "sha256", hash.slice(0, 2)); @@ -959,7 +959,7 @@ describe("pruneUnreferencedSessionArtifacts", () => { }); it("revalidates stale prompt blobs before removing them during normal artifact cleanup", async () => { - await withTempDir({ prefix: "openclaw-prune-revalidate-prompt-blob-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-prune-revalidate-prompt-blob-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const hash = "e".repeat(64); const blobDir = path.join(dir, "skills-prompts", "sha256", hash.slice(0, 2)); @@ -986,7 +986,7 @@ describe("pruneUnreferencedSessionArtifacts", () => { }); it("reclaims stale skills prompt blob temps during normal artifact cleanup", async () => { - await withTempDir({ prefix: "openclaw-prune-prompt-temp-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-prune-prompt-temp-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const store: Record = { "agent:main:main": { sessionId: "keep", updatedAt: Date.now() }, diff --git a/src/config/sessions/disk-budget.ts b/src/config/sessions/disk-budget.ts index 0b8e5f92d603..0a3893bd3970 100644 --- a/src/config/sessions/disk-budget.ts +++ b/src/config/sessions/disk-budget.ts @@ -20,7 +20,7 @@ import { SESSION_STORE_TEMP_STALE_MS, isTrajectorySessionArtifactName, } from "./artifacts.js"; -import { resolveSessionFilePath } from "./paths.js"; +import { resolveSessionFilePathCore } from "./paths.js"; import { listDurableSqliteTargetPathsForSessionStorePath } from "./session-sqlite-target.js"; import { projectSessionStoreForPersistence } from "./skill-prompt-blobs.js"; import { shouldPreserveMaintenanceEntry } from "./store-maintenance.js"; @@ -151,7 +151,7 @@ function resolveSessionTranscriptPathForEntry(params: { return null; } try { - const resolved = resolveSessionFilePath(params.entry.sessionId, params.entry, { + const resolved = resolveSessionFilePathCore(params.entry.sessionId, params.entry, { sessionsDir: params.sessionsDir, }); const resolvedSessionsDir = canonicalizePathForComparison(params.sessionsDir); diff --git a/src/config/sessions/entry-freshness.test.ts b/src/config/sessions/entry-freshness.test.ts index b16bd89a0343..d74cd6ff159f 100644 --- a/src/config/sessions/entry-freshness.test.ts +++ b/src/config/sessions/entry-freshness.test.ts @@ -4,7 +4,7 @@ import { cleanupTempDirs, makeTempDir } from "../../../test/helpers/temp-dir.js" import { closeOpenClawAgentDatabasesForTest } from "../../state/openclaw-agent-db.js"; import { closeOpenClawStateDatabaseForTest } from "../../state/openclaw-state-db.js"; import { resolveSessionEntryResetFreshness } from "./entry-freshness.js"; -import { appendTranscriptEvent, upsertSessionEntry } from "./session-accessor.js"; +import { appendTranscriptEvent, upsertSessionEntryCore } from "./session-accessor.js"; const DAY_MS = 24 * 60 * 60 * 1000; @@ -49,7 +49,7 @@ describe("resolveSessionEntryResetFreshness", () => { it("uses the configured default agent for an unqualified session key", async () => { const sessionKey = "global"; const now = new Date("2026-01-02T12:00:00Z").getTime(); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "ops", defaultAgentId: "ops", sessionKey, storePath }, { sessionId: "session-global-ops", @@ -75,7 +75,7 @@ describe("resolveSessionEntryResetFreshness", () => { it("resolves stale daily freshness from lifecycle timestamps instead of activity", async () => { const sessionKey = "agent:main:main:thread:100.000"; const now = new Date("2026-01-02T12:00:00Z").getTime(); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "session-stale-thread", @@ -105,7 +105,7 @@ describe("resolveSessionEntryResetFreshness", () => { it("keeps provider-owned sessions fresh when reset policy is implicit", async () => { const sessionKey = "agent:main:main:thread:provider-owned"; const now = new Date("2026-01-02T12:00:00Z").getTime(); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "session-provider-owned", @@ -134,7 +134,7 @@ describe("resolveSessionEntryResetFreshness", () => { it("applies configured reset policies to provider-owned sessions", async () => { const sessionKey = "agent:main:main:thread:provider-owned-configured"; const now = new Date("2026-01-02T12:00:00Z").getTime(); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "session-provider-owned-configured", @@ -166,7 +166,7 @@ describe("resolveSessionEntryResetFreshness", () => { it("resolves fresh daily freshness for active lifecycle timestamps", async () => { const sessionKey = "agent:main:main"; const now = new Date("2026-01-02T12:00:00Z").getTime(); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "session-fresh", @@ -193,7 +193,7 @@ describe("resolveSessionEntryResetFreshness", () => { it("honors reset overrides when resolving entry freshness", async () => { const sessionKey = "agent:main:main:thread:idle"; const now = new Date("2026-01-02T12:00:00Z").getTime(); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "session-idle-stale", @@ -227,7 +227,7 @@ describe("resolveSessionEntryResetFreshness", () => { const sessionKey = "agent:main:main:thread:configured-store"; const now = new Date("2026-01-02T12:00:00Z").getTime(); const configuredStorePath = path.join(tempDir, "configured-sessions.json"); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath: configuredStorePath }, { sessionId: "session-configured-store", @@ -262,7 +262,7 @@ describe("resolveSessionEntryResetFreshness", () => { const now = new Date("2026-01-02T12:00:00Z").getTime(); const headerTimestamp = new Date(now - 2 * DAY_MS).toISOString(); const target = { agentId: "main", sessionId, sessionKey, storePath }; - await upsertSessionEntry(target, { sessionId, updatedAt: now }); + await upsertSessionEntryCore(target, { sessionId, updatedAt: now }); await appendTranscriptEvent(target, { type: "session", version: 3, diff --git a/src/config/sessions/entry-freshness.ts b/src/config/sessions/entry-freshness.ts index baf16aaf3903..f2ef3e5e6f0f 100644 --- a/src/config/sessions/entry-freshness.ts +++ b/src/config/sessions/entry-freshness.ts @@ -4,7 +4,7 @@ import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js"; import type { SessionConfig, SessionResetConfig } from "../types.base.js"; import { getCliSessionBinding } from "./cli-session-binding.js"; import { resolveSessionLifecycleTimestamps } from "./lifecycle.js"; -import { resolveStorePath as resolveSessionStorePath } from "./paths.js"; +import { resolveSessionStorePathCore as resolveSessionStorePath } from "./paths.js"; import { evaluateSessionFreshness, resolveSessionResetPolicy, diff --git a/src/config/sessions/goals.test.ts b/src/config/sessions/goals.test.ts index aad6343a4614..d7dd6c5128ce 100644 --- a/src/config/sessions/goals.test.ts +++ b/src/config/sessions/goals.test.ts @@ -11,7 +11,7 @@ import { } from "./goals.js"; import { loadSessionEntry, - upsertSessionEntry as upsertAccessorSessionEntry, + upsertSessionEntryCore as upsertAccessorSessionEntry, } from "./session-accessor.js"; import { useTempSessionsFixture } from "./test-helpers.js"; import type { SessionEntry } from "./types.js"; diff --git a/src/config/sessions/inbound.runtime.ts b/src/config/sessions/inbound.runtime.ts index 8b2dce837316..001647a29cb5 100644 --- a/src/config/sessions/inbound.runtime.ts +++ b/src/config/sessions/inbound.runtime.ts @@ -1,3 +1,3 @@ // Runtime facade keeping inbound session persistence lazy behind the session accessor. -export { resolveStorePath } from "./paths.js"; +export { resolveSessionStorePathCore } from "./paths.js"; export { recordInboundSessionMeta, updateSessionLastRoute } from "./session-accessor.js"; diff --git a/src/config/sessions/incognito-session-transcript.test.ts b/src/config/sessions/incognito-session-transcript.test.ts index 824148a483e0..dc219cd8292d 100644 --- a/src/config/sessions/incognito-session-transcript.test.ts +++ b/src/config/sessions/incognito-session-transcript.test.ts @@ -7,7 +7,7 @@ import { closeOpenClawAgentDatabasesForTest, resolveIncognitoOpenClawAgentSqlitePath, } from "../../state/openclaw-agent-db.js"; -import { resolveStorePath } from "./paths.js"; +import { resolveSessionStorePathCore } from "./paths.js"; import { createSessionEntryWithTranscript, listSessionEntriesCore, @@ -58,7 +58,7 @@ describe("incognito transcript access", () => { agentId: "main", sessionId: created.entry.sessionId, sessionKey, - storePath: resolveStorePath(undefined, { agentId: "main" }), + storePath: resolveSessionStorePathCore(undefined, { agentId: "main" }), }; const firstTurn = SessionManager.open(target, cwd); firstTurn.appendMessage({ role: "user", content: "first question", timestamp: 1 }); diff --git a/src/config/sessions/lifecycle.test.ts b/src/config/sessions/lifecycle.test.ts index dc5899736c28..51dfe3e1fd10 100644 --- a/src/config/sessions/lifecycle.test.ts +++ b/src/config/sessions/lifecycle.test.ts @@ -12,7 +12,11 @@ import { hasTerminalMainSessionTranscriptNewerThanRegistrySync, resolveSessionLifecycleTimestamps, } from "./lifecycle.js"; -import { appendTranscriptEvent, loadSessionEntry, upsertSessionEntry } from "./session-accessor.js"; +import { + appendTranscriptEvent, + loadSessionEntry, + upsertSessionEntryCore, +} from "./session-accessor.js"; import type { SessionEntry } from "./types.js"; describe("terminal main session transcript freshness", () => { @@ -51,7 +55,7 @@ describe("terminal main session transcript freshness", () => { ...(params.endedAt !== undefined ? { endedAt: params.endedAt } : {}), ...(params.status !== undefined ? { status: params.status } : {}), }; - await upsertSessionEntry({ agentId: "main", sessionKey, storePath }, sessionEntry); + await upsertSessionEntryCore({ agentId: "main", sessionKey, storePath }, sessionEntry); await appendTranscriptEvent( { agentId: "main", sessionId, sessionKey, storePath }, { @@ -158,7 +162,7 @@ describe("terminal main session transcript freshness", () => { }); expect(check(entry, sessionKey)).toBe(true); - await upsertSessionEntry({ agentId: "main", sessionKey, storePath }, entry); + await upsertSessionEntryCore({ agentId: "main", sessionKey, storePath }, entry); const refreshed = loadSessionEntry({ agentId: "main", sessionKey, storePath }); dateNow.mockRestore(); @@ -178,7 +182,7 @@ describe("terminal main session transcript freshness", () => { status: "timeout", updatedAt: Date.now() + 10_000, }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: newerRegistry.sessionKey, storePath }, newerRegistry.entry, ); diff --git a/src/config/sessions/paths.test.ts b/src/config/sessions/paths.test.ts index 6e914ec26ffe..9001f1a5a2c0 100644 --- a/src/config/sessions/paths.test.ts +++ b/src/config/sessions/paths.test.ts @@ -4,8 +4,8 @@ import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { - resolveSessionFilePath, - resolveStorePath, + resolveSessionFilePathCore, + resolveSessionStorePathCore, SessionStoreAgentIdRequiredError, } from "./paths.js"; @@ -28,7 +28,7 @@ describe("resolveSessionFilePath cross-root reroot", () => { fs.writeFileSync(path.join(sessionsDir, "sess-1.jsonl"), "{}\n", "utf8"); const foreign = "/nonexistent-old-root/.openclaw/agents/main/sessions/sess-1.jsonl"; - const resolved = resolveSessionFilePath( + const resolved = resolveSessionFilePathCore( "sess-1", { sessionFile: foreign }, { sessionsDir, agentId: "main" }, @@ -44,7 +44,7 @@ describe("resolveSessionFilePath cross-root reroot", () => { fs.mkdirSync(sessionsDir, { recursive: true }); const foreign = "/nonexistent-old-root/.openclaw/agents/main/sessions/sess-2.jsonl"; - const resolved = resolveSessionFilePath( + const resolved = resolveSessionFilePathCore( "sess-2", { sessionFile: foreign }, { sessionsDir, agentId: "main" }, @@ -57,11 +57,11 @@ describe("resolveSessionFilePath cross-root reroot", () => { describe("resolveStorePath", () => { it("resolves a fixed literal path without an agent owner", () => { const fixed = path.join(path.parse(process.cwd()).root, "shared", "sessions.json"); - expect(resolveStorePath(fixed)).toBe(path.resolve(fixed)); + expect(resolveSessionStorePathCore(fixed)).toBe(path.resolve(fixed)); }); it("throws a typed error when an agent template has no owner", () => { - expect(() => resolveStorePath("/state/agents/{agentId}/sessions.json")).toThrow( + expect(() => resolveSessionStorePathCore("/state/agents/{agentId}/sessions.json")).toThrow( SessionStoreAgentIdRequiredError, ); }); @@ -74,7 +74,7 @@ describe("resolveStorePath", () => { }; const expected = path.join(stateDir, "agents", "work", "sessions", "sessions.json"); - expect(resolveStorePath(undefined, { agentId: "work", env })).toBe(expected); - expect(resolveStorePath("", { agentId: "work", env })).toBe(expected); + expect(resolveSessionStorePathCore(undefined, { agentId: "work", env })).toBe(expected); + expect(resolveSessionStorePathCore("", { agentId: "work", env })).toBe(expected); }); }); diff --git a/src/config/sessions/paths.ts b/src/config/sessions/paths.ts index e9abdda7571f..713ce99bcc15 100644 --- a/src/config/sessions/paths.ts +++ b/src/config/sessions/paths.ts @@ -286,7 +286,7 @@ export function resolveSessionTranscriptPath( ): string { return resolveSessionTranscriptPathInDir(sessionId, resolveAgentSessionsDir(agentId), topicId); } -export function resolveSessionFilePath( +export function resolveSessionFilePathCore( sessionId: string, entry?: object, opts?: SessionFilePathOptions, @@ -317,7 +317,7 @@ export class SessionStoreAgentIdRequiredError extends Error { } /** Resolves fixed literal paths without an owner; derived or templated paths require agentId. */ -export function resolveStorePath( +export function resolveSessionStorePathCore( store?: string, opts?: { agentId?: string; env?: NodeJS.ProcessEnv }, ) { diff --git a/src/config/sessions/session-accessor.conformance.test.ts b/src/config/sessions/session-accessor.conformance.test.ts index 98c3685da8dc..36d303641025 100644 --- a/src/config/sessions/session-accessor.conformance.test.ts +++ b/src/config/sessions/session-accessor.conformance.test.ts @@ -33,7 +33,7 @@ import { replaceSessionEntry, resolveSessionTranscriptRuntimeTarget, updateSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, type ExactSessionEntry, type SessionAccessScope, type SessionEntrySummary, @@ -148,7 +148,7 @@ const publicAccessorAdapter: AccessorAdapter = { loadExactSessionEntry, listSessionEntriesCore, readSessionUpdatedAtCore, - upsertSessionEntry, + upsertSessionEntry: upsertSessionEntryCore, replaceSessionEntry, patchSessionEntryCore, updateSessionEntry, @@ -186,7 +186,7 @@ const sqliteAdapter: AccessorAdapter = { loadExactSessionEntry, listSessionEntriesCore: listSessionEntryRows, readSessionUpdatedAtCore, - upsertSessionEntry, + upsertSessionEntry: upsertSessionEntryCore, replaceSessionEntry, patchSessionEntryCore, updateSessionEntry: patchSessionEntryCore, @@ -595,7 +595,7 @@ describe.each([publicAccessorAdapter, sqliteAdapter])( storePath: legacyStorePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { model: "gpt-5.5", sessionId: "session-1", updatedAt: 10, @@ -634,7 +634,7 @@ describe.each([publicAccessorAdapter, sqliteAdapter])( storePath: customStorePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { model: "gpt-5.5", sessionId: "session-1", updatedAt: 10, @@ -663,7 +663,7 @@ describe.each([publicAccessorAdapter, sqliteAdapter])( storePath: customStorePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { model: "gpt-5.5", sessionId: "support-session", updatedAt: 10, @@ -689,7 +689,7 @@ describe.each([publicAccessorAdapter, sqliteAdapter])( it("parses only the selected SQLite entry across keyed loads", async () => { const scope = sqliteAdapter.entryScope(paths); for (let index = 0; index < 20; index += 1) { - await upsertSessionEntry( + await upsertSessionEntryCore( { ...scope, sessionKey: `agent:main:unrelated-${index}`, @@ -701,7 +701,7 @@ describe.each([publicAccessorAdapter, sqliteAdapter])( }, ); } - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { model: "target", sessionId: "target-session", updatedAt: 100, @@ -749,7 +749,7 @@ describe.each([publicAccessorAdapter, sqliteAdapter])( updatedAt: Date.now() - 31 * 24 * 60 * 60 * 1000, }); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { model: "fresh", sessionId: "fresh-session", updatedAt: Date.now(), @@ -765,7 +765,7 @@ describe.each([publicAccessorAdapter, sqliteAdapter])( it("serializes concurrent SQLite entry patches", async () => { const scope = sqliteAdapter.entryScope(paths); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { model: "base", sessionId: "patch-session", updatedAt: 10, @@ -1210,7 +1210,7 @@ describe("sqlite session normalization", () => { it("maintains normalized session node and window rows", async () => { const env = { ...process.env, OPENCLAW_STATE_DIR: paths.stateDir }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", env, @@ -1335,7 +1335,7 @@ describe("sqlite session normalization", () => { storePath: paths.sqlitePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { delivery: normalizeSessionDeliveryState({ context: { channel: "telegram" } }), chatType: "group", createdVia: "channel", @@ -1356,7 +1356,7 @@ describe("sqlite session normalization", () => { }, ); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { delivery: normalizeSessionDeliveryState({ context: { channel: "telegram" } }), chatType: "group", displayName: "telegram:g-bucephalus-+-topics", @@ -1402,7 +1402,7 @@ describe("sqlite session normalization", () => { storePath: paths.sqlitePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "temporary-session", updatedAt: 10, }); @@ -1422,7 +1422,7 @@ describe("sqlite session normalization", () => { const env = { ...process.env, OPENCLAW_STATE_DIR: paths.stateDir }; const parentKey = "agent:main:parent"; const childKey = "agent:main:subagent:child"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", env, @@ -1490,7 +1490,7 @@ describe("sqlite session normalization", () => { sessionKey: "agent:main:main", storePath: paths.sqlitePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "current-session", updatedAt: 20, }); @@ -2041,7 +2041,7 @@ describe("sqlite session normalization", () => { window_updated_at: 123, }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", env, @@ -2127,7 +2127,7 @@ describe("sqlite session normalization", () => { updatedAt: 10, compactionCheckpoints: [checkpoint], }; - await upsertSessionEntry(sourceEntryScope, sourceEntry); + await upsertSessionEntryCore(sourceEntryScope, sourceEntry); const notify = vi.fn(); const unsubscribe = onSessionIdentityMutation(notify); @@ -2212,7 +2212,7 @@ describe("sqlite session normalization", () => { { type: "message", id: "post-msg", parentId: null, message: { content: "post" } }, { type: "message", id: "skipped-msg", parentId: "post-msg", message: { content: "skip" } }, ]); - await upsertSessionEntry(sourceEntryScope, { + await upsertSessionEntryCore(sourceEntryScope, { sessionId: "source-session", updatedAt: 10, compactionCheckpoints: [checkpoint], @@ -2292,7 +2292,7 @@ describe("sqlite session normalization", () => { { type: "message", id: "post-msg-1", parentId: null, message: { content: "skip" } }, { type: "message", id: "post-msg-2", parentId: "post-msg-1", message: { content: "skip" } }, ]); - await upsertSessionEntry(sourceEntryScope, { + await upsertSessionEntryCore(sourceEntryScope, { label: "Current", sessionId: "current-session", updatedAt: 10, diff --git a/src/config/sessions/session-accessor.entry.ts b/src/config/sessions/session-accessor.entry.ts index da0e19f0e6df..4559415910ca 100644 --- a/src/config/sessions/session-accessor.entry.ts +++ b/src/config/sessions/session-accessor.entry.ts @@ -7,7 +7,7 @@ import { isIncognitoSessionKey, resolveAgentIdFromSessionKey } from "../../routi import { resolveIncognitoOpenClawAgentSqlitePath } from "../../state/openclaw-agent-db.js"; import type { OpenClawConfig } from "../types.openclaw.js"; import { resolveAgentMainSessionKey } from "./main-session.js"; -import { resolveStorePath } from "./paths.js"; +import { resolveSessionStorePathCore } from "./paths.js"; import { clearPluginOwnedSessionState } from "./plugin-host-cleanup.js"; import { copySqliteSessionOwnedStateForCanonicalRepair as copySessionOwnedStateForCanonicalRepair, @@ -34,7 +34,7 @@ import { replaceSessionEntry, replaceSessionEntrySync, resolveSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "./session-accessor.sqlite-entry.js"; import type { SessionAccessScope, @@ -88,7 +88,7 @@ export { // fresh-reads and checks sessionId inside its locked commit, and void/entry has no rebound signal. replaceSessionEntrySync, resolveSessionEntryFromStore, - upsertSessionEntry, + upsertSessionEntryCore, }; /** Resolves a session directly through canonical SQLite row and alias ownership. */ @@ -115,7 +115,10 @@ function resolveLogicalSessionStoreCandidates(params: { const storeConfig = params.cfg.session?.store; const defaultTarget = { agentId: params.agentId, - storePath: resolveStorePath(storeConfig, { agentId: params.agentId, env: params.env }), + storePath: resolveSessionStorePathCore(storeConfig, { + agentId: params.agentId, + env: params.env, + }), }; if (!isStorePathTemplate(storeConfig)) { return [defaultTarget]; @@ -212,7 +215,7 @@ export function resolveSessionEntryCandidateTarget( const incognitoAgentId = incognitoKey ? resolveAgentIdFromSessionKey(incognitoKey) : undefined; const storePath = incognitoAgentId ? resolveIncognitoOpenClawAgentSqlitePath({ agentId: incognitoAgentId, env: scope.env }) - : resolveStorePath(scope.cfg.session?.store, { + : resolveSessionStorePathCore(scope.cfg.session?.store, { agentId: scope.agentId, env: scope.env, }); @@ -294,7 +297,7 @@ function resolveSessionEntryStoreTarget( }); const fallback = candidates[0] ?? { agentId, - storePath: resolveStorePath(scope.cfg.session?.store, { agentId, env: scope.env }), + storePath: resolveSessionStorePathCore(scope.cfg.session?.store, { agentId, env: scope.env }), }; let selectedStorePath = fallback.storePath; let selectedMatch = findCanonicalSessionEntryMatch( diff --git a/src/config/sessions/session-accessor.readonly.test.ts b/src/config/sessions/session-accessor.readonly.test.ts index 074a409e6c99..4c859264fe3d 100644 --- a/src/config/sessions/session-accessor.readonly.test.ts +++ b/src/config/sessions/session-accessor.readonly.test.ts @@ -16,7 +16,7 @@ import { listSessionEntriesCore, listSessionEntriesReadOnly, resolveTranscriptSessionKeyBySessionId, - upsertSessionEntry, + upsertSessionEntryCore, } from "./session-accessor.js"; const tempDirs: string[] = []; @@ -44,11 +44,11 @@ describe("session accessor readonly listing", () => { const env = { OPENCLAW_STATE_DIR: stateDir }; const listScope = { agentId: "worker-1", env }; - await upsertSessionEntry( + await upsertSessionEntryCore( { ...listScope, sessionKey: "agent:worker-1:main" }, { sessionId: "session-1", updatedAt: 10 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { ...listScope, sessionKey: "agent:worker-1:telegram:dm:42" }, { sessionId: "session-2", updatedAt: 20 }, ); @@ -94,7 +94,7 @@ describe("session accessor readonly listing", () => { expect(hasSessionEntriesByStatusReadOnly({ agentId, env }, ["running"])).toBe(false); expect(countRegisteredAgentDatabases(env)).toBe(0); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId, env, sessionKey: "agent:worker-1:main" }, { sessionId: "session-1", status: "running", updatedAt: 10 }, ); @@ -125,7 +125,7 @@ describe("session accessor readonly listing", () => { const env = { OPENCLAW_STATE_DIR: stateDir }; const agentId = "worker-1"; const sessionKey = "agent:worker-1:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId, env, sessionKey }, { sessionId: "session-1", updatedAt: 1 }, ); @@ -144,7 +144,7 @@ describe("session accessor readonly listing", () => { const agentId = "worker-1"; const scope = { agentId, env }; - await upsertSessionEntry( + await upsertSessionEntryCore( { ...scope, sessionKey: "agent:worker-1:main" }, { sessionId: "session-1", updatedAt: 10 }, ); diff --git a/src/config/sessions/session-accessor.reply-init-concurrency.test.ts b/src/config/sessions/session-accessor.reply-init-concurrency.test.ts index 2fa7d1914a59..5885d88e15ed 100644 --- a/src/config/sessions/session-accessor.reply-init-concurrency.test.ts +++ b/src/config/sessions/session-accessor.reply-init-concurrency.test.ts @@ -11,7 +11,7 @@ import { loadSessionEntry, loadTranscriptEvents, updateSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, withTranscriptWriteLock, } from "./session-accessor.js"; import { replaceTranscriptEvents } from "./session-accessor.sqlite-transcript-write.js"; @@ -462,7 +462,7 @@ describe("session accessor cross-process concurrency", () => { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-reply-init-")); const storePath = path.join(tempDir, "sessions.json"); try { - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: SESSION_KEY, storePath }, { sessionId: "existing-session", @@ -524,7 +524,7 @@ describe("session accessor cross-process concurrency", () => { storePath, }; try { - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId, updatedAt: Date.now(), }); @@ -604,7 +604,7 @@ describe("session accessor cross-process concurrency", () => { ]; try { - await upsertSessionEntry(scope, { sessionId, updatedAt: Date.now() }); + await upsertSessionEntryCore(scope, { sessionId, updatedAt: Date.now() }); await withTranscriptWriteLock(scope, async (transcript) => { await transcript.replaceEvents(replacement); }); @@ -635,7 +635,7 @@ describe("session accessor cross-process concurrency", () => { }, ]; try { - await upsertSessionEntry(scope, { sessionId, updatedAt: Date.now() }); + await upsertSessionEntryCore(scope, { sessionId, updatedAt: Date.now() }); const result = await runConcurrencyScenario( { kind: "transcript-rewrite", @@ -687,7 +687,7 @@ describe("session accessor cross-process concurrency", () => { }; try { - await upsertSessionEntry(scope, { sessionId, updatedAt: Date.now() }); + await upsertSessionEntryCore(scope, { sessionId, updatedAt: Date.now() }); await replaceTranscriptEvents(scope, [ { type: "session", version: 3, id: sessionId }, { diff --git a/src/config/sessions/session-accessor.sqlite-data-version.test.ts b/src/config/sessions/session-accessor.sqlite-data-version.test.ts index 665a88d476e5..920033a580e6 100644 --- a/src/config/sessions/session-accessor.sqlite-data-version.test.ts +++ b/src/config/sessions/session-accessor.sqlite-data-version.test.ts @@ -16,7 +16,7 @@ import { listSessionEntriesCore, loadSessionEntry, openSessionEntryReadView, - upsertSessionEntry, + upsertSessionEntryCore, } from "./session-accessor.js"; import { readSessionEntryCache } from "./session-accessor.sqlite-entry-cache.js"; import { ensureTranscriptSessionRoot } from "./session-accessor.sqlite-transcript-state.js"; @@ -154,7 +154,7 @@ function createSessionScope(label: string) { describe("SQLite session entry cache", () => { it("keeps sqlite-entry-cache list projections shallow, lazy, and memoized per key", async () => { const scope = createSessionScope("lazy-list-projection"); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { label: "projected", sessionId: "lazy-list-projection", updatedAt: 1, @@ -201,8 +201,8 @@ describe("SQLite session entry cache", () => { it("reuses parsed entries on the second list", async () => { const scope = createSessionScope("second-list"); - await upsertSessionEntry(scope, { label: "first", sessionId: "first", updatedAt: 1 }); - await upsertSessionEntry( + await upsertSessionEntryCore(scope, { label: "first", sessionId: "first", updatedAt: 1 }); + await upsertSessionEntryCore( { ...scope, sessionKey: "agent:main:second-list-2" }, { label: "second", sessionId: "second", updatedAt: 2 }, ); @@ -219,7 +219,7 @@ describe("SQLite session entry cache", () => { it("keeps same-path caches isolated by live connection", async () => { const scope = createSessionScope("connection-identity"); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { label: "connection-identity", sessionId: "connection-identity", updatedAt: 1, @@ -261,12 +261,12 @@ describe("SQLite session entry cache", () => { it("does not revalidate session nodes after a same-connection transcript write", async () => { const scope = createSessionScope("same-connection-non-entry"); const siblingScope = { ...scope, sessionKey: "agent:main:same-connection-non-entry-2" }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { label: "projection-probe-first", sessionId: "same-connection-non-entry", updatedAt: 1, }); - await upsertSessionEntry(siblingScope, { + await upsertSessionEntryCore(siblingScope, { label: "projection-probe-second", sessionId: "same-connection-non-entry-2", updatedAt: 1, @@ -293,12 +293,12 @@ describe("SQLite session entry cache", () => { it("fully reloads after another connection commits", async () => { const scope = createSessionScope("external-write"); const siblingScope = { ...scope, sessionKey: "agent:main:external-write-sibling" }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { label: "projection-probe-before", sessionId: "external", updatedAt: 1, }); - await upsertSessionEntry(siblingScope, { + await upsertSessionEntryCore(siblingScope, { label: "projection-probe-sibling", sessionId: "external-sibling", updatedAt: 1, @@ -341,12 +341,12 @@ describe("SQLite session entry cache", () => { it("fully reloads a cross-connection same-millisecond entry rewrite", async () => { const scope = createSessionScope("external-same-ms"); const siblingScope = { ...scope, sessionKey: "agent:main:external-same-ms-sibling" }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { label: "projection-probe-before", sessionId: "external-same-ms", updatedAt: 1_000, }); - await upsertSessionEntry(siblingScope, { + await upsertSessionEntryCore(siblingScope, { label: "projection-probe-sibling", sessionId: "external-same-ms-sibling", updatedAt: 1_000, @@ -387,12 +387,12 @@ describe("SQLite session entry cache", () => { it("fully reloads when an external commit races incremental row reads", async () => { const scope = createSessionScope("external-race"); const siblingScope = { ...scope, sessionKey: "agent:main:external-race-sibling" }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { label: "local-before", sessionId: "external-race-local", updatedAt: 1, }); - await upsertSessionEntry(siblingScope, { + await upsertSessionEntryCore(siblingScope, { label: "external-before", sessionId: "external-race-sibling", updatedAt: 1, @@ -444,12 +444,12 @@ describe("SQLite session entry cache", () => { it("incrementally handles added and removed keys on the cached connection", async () => { const scope = createSessionScope("same-connection-keys"); const removedScope = { ...scope, sessionKey: "agent:main:same-connection-removed" }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { label: "projection-probe-kept", sessionId: "same-connection-kept", updatedAt: 1, }); - await upsertSessionEntry(removedScope, { + await upsertSessionEntryCore(removedScope, { label: "projection-probe-removed", sessionId: "same-connection-removed", updatedAt: 1, @@ -497,7 +497,7 @@ describe("SQLite session entry cache", () => { const scope = createSessionScope("changed-key-scaling"); const rowCount = 24; for (let index = 0; index < rowCount; index++) { - await upsertSessionEntry( + await upsertSessionEntryCore( { ...scope, sessionKey: `agent:main:changed-key-scaling-${index}` }, { label: `projection-probe-${index}`, @@ -535,12 +535,12 @@ describe("SQLite session entry cache", () => { it("patches only the tracked row after a same-process upsert", async () => { const scope = createSessionScope("write-through"); const siblingScope = { ...scope, sessionKey: "agent:main:write-through-sibling" }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { label: "projection-probe-before", sessionId: "write-through", updatedAt: 1, }); - await upsertSessionEntry(siblingScope, { + await upsertSessionEntryCore(siblingScope, { label: "projection-probe-sibling", sessionId: "write-through-sibling", updatedAt: 1, @@ -550,7 +550,7 @@ describe("SQLite session entry cache", () => { parseSessionEntryCalls.mockClear(); listProjectionCalls.mockClear(); - await upsertSessionEntry(scope, { label: "projection-probe-after", updatedAt: 2 }); + await upsertSessionEntryCore(scope, { label: "projection-probe-after", updatedAt: 2 }); parseSessionEntryCalls.mockClear(); listProjectionCalls.mockClear(); const after = listSessionEntriesCore({ ...scope, clone: false, projection: "list" }); @@ -568,7 +568,7 @@ describe("SQLite session entry cache", () => { it("adds a tracked upsert to a warm snapshot without reparsing siblings", async () => { const scope = createSessionScope("write-through-insert"); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { label: "projection-probe-existing", sessionId: "write-through-existing", updatedAt: 1, @@ -579,7 +579,7 @@ describe("SQLite session entry cache", () => { parseSessionEntryCalls.mockClear(); listProjectionCalls.mockClear(); - await upsertSessionEntry(insertedScope, { + await upsertSessionEntryCore(insertedScope, { label: "projection-probe-inserted", sessionId: "write-through-inserted", updatedAt: 2, @@ -599,8 +599,8 @@ describe("SQLite session entry cache", () => { it("does not let a tracked write mask an earlier raw connection write", async () => { const scope = createSessionScope("raw-before-tracked"); const trackedScope = { ...scope, sessionKey: "agent:main:tracked-after-raw" }; - await upsertSessionEntry(scope, { label: "raw-before", sessionId: "raw", updatedAt: 1 }); - await upsertSessionEntry(trackedScope, { + await upsertSessionEntryCore(scope, { label: "raw-before", sessionId: "raw", updatedAt: 1 }); + await upsertSessionEntryCore(trackedScope, { label: "tracked-before", sessionId: "tracked", updatedAt: 1, @@ -612,7 +612,7 @@ describe("SQLite session entry cache", () => { database.db .prepare("UPDATE session_nodes SET entry_json = ?, updated_at = ? WHERE session_key = ?") .run(JSON.stringify(rawEntry), rawEntry.updatedAt, scope.sessionKey); - await upsertSessionEntry(trackedScope, { label: "tracked-after", updatedAt: 2 }); + await upsertSessionEntryCore(trackedScope, { label: "tracked-after", updatedAt: 2 }); parseSessionEntryCalls.mockClear(); const entries = listSessionEntriesCore(scope); @@ -629,7 +629,7 @@ describe("SQLite session entry cache", () => { it("invalidates cached keys when transcript creation inserts a placeholder node", async () => { const scope = createSessionScope("placeholder-key"); - await upsertSessionEntry(scope, { sessionId: "entry", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: "entry", updatedAt: 1 }); const database = openOpenClawAgentDatabase(scope); const before = readSessionEntryCache(database, { cache: true }); expect(before.keys).toEqual([scope.sessionKey]); @@ -655,7 +655,7 @@ describe("SQLite session entry cache", () => { it("bypasses the cache in a transaction and reuses the persisted snapshot after rollback", async () => { const scope = createSessionScope("transaction-rollback"); - await upsertSessionEntry(scope, { label: "before", sessionId: "rollback", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { label: "before", sessionId: "rollback", updatedAt: 1 }); const borrowedBefore = openSessionEntryReadView(scope).get(scope.sessionKey); expect(borrowedBefore?.label).toBe("before"); if (!borrowedBefore) { @@ -682,7 +682,7 @@ describe("SQLite session entry cache", () => { it("isolates cloned results while borrowed views retain stable references", async () => { const scope = createSessionScope("clone-borrow"); - await upsertSessionEntry(scope, { label: "original", sessionId: "clone", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { label: "original", sessionId: "clone", updatedAt: 1 }); const cloned = listSessionEntriesCore(scope)[0]?.entry; expect(cloned).toBeDefined(); @@ -699,7 +699,7 @@ describe("SQLite session entry cache", () => { it("honors latest reads after an untracked own-connection write", async () => { const scope = createSessionScope("latest"); - await upsertSessionEntry(scope, { label: "cached", sessionId: "latest", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { label: "cached", sessionId: "latest", updatedAt: 1 }); expect(listSessionEntriesCore(scope)[0]?.entry.label).toBe("cached"); const database = openOpenClawAgentDatabase(scope); diff --git a/src/config/sessions/session-accessor.sqlite-entry.ts b/src/config/sessions/session-accessor.sqlite-entry.ts index 9612bca99399..01cd00271e66 100644 --- a/src/config/sessions/session-accessor.sqlite-entry.ts +++ b/src/config/sessions/session-accessor.sqlite-entry.ts @@ -409,7 +409,7 @@ export function readSessionUpdatedAtCore(scope: SessionAccessScope): number | un } /** Applies a partial entry update to the additive SQLite session store. */ -export async function upsertSessionEntry( +export async function upsertSessionEntryCore( scope: SessionAccessScope, patch: Partial, ): Promise { diff --git a/src/config/sessions/session-accessor.sqlite-message-cut.test.ts b/src/config/sessions/session-accessor.sqlite-message-cut.test.ts index 7716987b7daa..99ad09892e75 100644 --- a/src/config/sessions/session-accessor.sqlite-message-cut.test.ts +++ b/src/config/sessions/session-accessor.sqlite-message-cut.test.ts @@ -22,7 +22,7 @@ import { rewindSessionToMessage, switchSessionBranch, updateSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "./session-accessor.js"; import type { InternalSessionEntry } from "./types.js"; @@ -58,7 +58,7 @@ async function createSiblingSession(params: { sessionKey: string; }) { const scope = { agentId, ...params }; - await upsertSessionEntry(scope, { sessionId: params.sessionId, updatedAt: Date.now() }); + await upsertSessionEntryCore(scope, { sessionId: params.sessionId, updatedAt: Date.now() }); await appendTranscriptEvent(scope, { type: "session", id: params.sessionId, @@ -101,7 +101,7 @@ async function createSession(options: { activeLeafTarget?: string } = {}) { sessionId, updatedAt: Date.now(), }; - await upsertSessionEntry(scope, entry); + await upsertSessionEntryCore(scope, entry); for (const event of [ { type: "session", id: sessionId, version: 3, timestamp: "2026-07-18T00:00:00.000Z" }, { @@ -229,7 +229,7 @@ describe("SQLite session message cuts", () => { if (!sourceEntry) { throw new Error("expected source session entry"); } - await upsertSessionEntry({ agentId, env, sessionKey: aliasKey }, sourceEntry); + await upsertSessionEntryCore({ agentId, env, sessionKey: aliasKey }, sourceEntry); const fullTranscriptLoads = trackFullTranscriptLoads(env); await listSessionBranches({ agentId, env, sessionKey }); diff --git a/src/config/sessions/session-accessor.test.ts b/src/config/sessions/session-accessor.test.ts index 8eeea569e335..6e16e1e6f982 100644 --- a/src/config/sessions/session-accessor.test.ts +++ b/src/config/sessions/session-accessor.test.ts @@ -69,7 +69,7 @@ import { trimSessionTranscriptForManualCompact, updateSessionEntry, updateSessionLastRoute, - upsertSessionEntry, + upsertSessionEntryCore, } from "./session-accessor.js"; import { readSessionEntryCount, @@ -192,7 +192,7 @@ describe("session accessor seam", () => { }, }); - await upsertSessionEntry(scope, { sessionId: "replacement-session", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: "replacement-session", updatedAt: 1 }); expect(appendTranscriptEventSync(scope, event)).toEqual({ ok: false, error: { @@ -203,7 +203,7 @@ describe("session accessor seam", () => { }, }); - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 2 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 2 }); expect(appendTranscriptEventSync(scope, event)).toEqual({ ok: true, value: true }); expect(appendTranscriptEventSync(scope, event)).toEqual({ ok: true, value: false }); }); @@ -214,7 +214,7 @@ describe("session accessor seam", () => { storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { model: "gpt-5.5", sessionId: "session-1", updatedAt: 10, @@ -237,7 +237,7 @@ describe("session accessor seam", () => { }, ]); - await upsertSessionEntry(scope, { model: "sonnet-4.6", updatedAt: 20 }); + await upsertSessionEntryCore(scope, { model: "sonnet-4.6", updatedAt: 20 }); expect(loadSessionEntry(scope)).toMatchObject({ model: "sonnet-4.6", @@ -323,7 +323,7 @@ describe("session accessor seam", () => { sessionId: "created-by-session", updatedAt: 10, } as SessionEntry & { createdBy: { id: string } }); - await upsertSessionEntry({ sessionKey, storePath }, { label: "rewritten" }); + await upsertSessionEntryCore({ sessionKey, storePath }, { label: "rewritten" }); const databasePath = expectDefined( resolveSqliteTargetFromSessionStorePath(storePath, { agentId: "main" }).path, "createdBy database path", @@ -345,7 +345,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:main", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "history-old", updatedAt: 10, pluginOwnerId: "history-owner", @@ -356,7 +356,7 @@ describe("session accessor seam", () => { { message: { role: "assistant", content: "old transcript" } }, ); await replaceSessionEntry(scope, { sessionId: "history-old", updatedAt: 15 }); - await upsertSessionEntry(scope, { sessionId: "history-new", updatedAt: 20 }); + await upsertSessionEntryCore(scope, { sessionId: "history-new", updatedAt: 20 }); await appendTranscriptMessage( { ...scope, sessionId: "history-new" }, { message: { role: "assistant", content: "new transcript" } }, @@ -385,7 +385,7 @@ describe("session accessor seam", () => { const transcriptTimes = new Map( instances.map((instance) => [instance.sessionId, instance.updatedAtMs]), ); - await upsertSessionEntry(scope, { label: "renamed", updatedAt: Date.now() + 60_000 }); + await upsertSessionEntryCore(scope, { label: "renamed", updatedAt: Date.now() + 60_000 }); expect( new Map( listSessionTranscriptInstances({ agentId: "main", storePath }).map((instance) => [ @@ -481,7 +481,7 @@ describe("session accessor seam", () => { it("keeps migrated unknown provenance unknown while the session remains current", async () => { const sessionKey = "agent:main:migrated-plugin"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "migrated-plugin-session", @@ -517,7 +517,7 @@ describe("session accessor seam", () => { ]), ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "migrated-plugin-session", @@ -536,7 +536,7 @@ describe("session accessor seam", () => { ]), ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "replacement-session", updatedAt: 20 }, ); @@ -556,7 +556,7 @@ describe("session accessor seam", () => { // Transcript reads resolve to the SQLite transcript rows for the resolved // agent-scoped session; there is no legacy custom sessionFile read path. - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: "agent:main:main", storePath }, { sessionId: "session-events", updatedAt: 10 }, ); @@ -583,7 +583,7 @@ describe("session accessor seam", () => { const header = { type: "session", id: "session-find", timestamp: 1 }; const older = { type: "message", id: "m1", message: { role: "assistant", tag: "old" } }; const newer = { type: "message", id: "m2", message: { role: "assistant", tag: "new" } }; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: "agent:main:main", storePath }, { sessionId: "session-find", updatedAt: 10 }, ); @@ -623,7 +623,7 @@ describe("session accessor seam", () => { it("opens a borrowed read view with raw exact-key probes and deferred enumeration", async () => { const mixedKey = "agent:main:matrix:channel:!RoomAbC:example.org"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: mixedKey, storePath }, { sessionId: "mixed-session", updatedAt: 10 }, ); @@ -646,11 +646,11 @@ describe("session accessor seam", () => { const mixedKey = "agent:voice:agent:other:matrix:channel:!RoomAbC:example.org"; const lowerKey = "agent:voice:agent:other:matrix:channel:!Roomabc:example.org"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: mixedKey, storePath }, { sessionId: "mixed-session", updatedAt: 10 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: lowerKey, storePath }, { sessionId: "lower-session", updatedAt: 20 }, ); @@ -932,7 +932,7 @@ describe("session accessor seam", () => { it("does not parse unrelated blobs across canonical candidate and transcript reads", async () => { const sessionKey = "agent:main:focused-session"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "focused-session", updatedAt: 42 }, ); @@ -982,7 +982,7 @@ describe("session accessor seam", () => { it("resolves non-main candidate entries from custom agent store templates", async () => { const storeTemplate = path.join(tempDir, "{agentId}.json"); const supportStorePath = path.join(tempDir, "support.json"); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "support", sessionKey: "agent:support:main", @@ -1015,7 +1015,7 @@ describe("session accessor seam", () => { it("resolves non-main logical entries from custom agent store templates", async () => { const storeTemplate = path.join(tempDir, "{agentId}.json"); const supportStorePath = path.join(tempDir, "support.json"); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "support", sessionKey: "agent:support:main", @@ -1050,7 +1050,7 @@ describe("session accessor seam", () => { storePath, }; - const inserted = await upsertSessionEntry(scope, { model: "gpt-5.5" }); + const inserted = await upsertSessionEntryCore(scope, { model: "gpt-5.5" }); expect(inserted?.sessionId).toMatch( /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, @@ -1105,7 +1105,7 @@ describe("session accessor seam", () => { sessionId: "default-store-turn-session", sessionKey: "agent:main:default-store-turn", }; - await upsertSessionEntry( + await upsertSessionEntryCore( { ...scope, storePath: expectedStorePath }, { sessionId: scope.sessionId, @@ -1440,7 +1440,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:main", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "pending-session", updatedAt: 10, initializationPending: true, @@ -1468,7 +1468,7 @@ describe("session accessor seam", () => { it("rejects stale reply session initialization snapshots without writing", async () => { const sessionKey = "agent:main:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "first-session", @@ -1476,7 +1476,7 @@ describe("session accessor seam", () => { }, ); const snapshot = loadMainInitializationSnapshot(sessionKey); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "second-session", @@ -1530,7 +1530,7 @@ describe("session accessor seam", () => { ] as const)("$name", async ({ initial, concurrent, prepared, expected }) => { const sessionKey = "agent:main:main"; const scope = { sessionKey, storePath }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "existing-session", updatedAt: 10, ...initial, @@ -1567,7 +1567,7 @@ describe("session accessor seam", () => { it("does not restore pending final delivery metadata cleared after the snapshot", async () => { const sessionKey = "agent:main:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "existing-session", @@ -1620,7 +1620,7 @@ describe("session accessor seam", () => { it("does not merge old-session delivery metadata into a rotated session", async () => { const sessionKey = "agent:main:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "old-session", @@ -1714,20 +1714,20 @@ describe("session accessor seam", () => { it("normalizes alias inputs before writes and rejects invalid owners", async () => { for (const sessionKey of ["main", "agent:ops:main ", "agent:OPS:upper"]) { await expect( - upsertSessionEntry( + upsertSessionEntryCore( { agentId: "ops", sessionKey, storePath }, { sessionId: "legacy-ops-session", updatedAt: 10 }, ), ).resolves.toMatchObject({ sessionId: "legacy-ops-session" }); } await expect( - upsertSessionEntry( + upsertSessionEntryCore( { agentId: "ops", sessionKey: "", storePath }, { sessionId: "empty-session", updatedAt: 10 }, ), ).rejects.toMatchObject({ code: "SESSION_CANONICAL_KEY_MIGRATION_REQUIRED" }); await expect( - upsertSessionEntry( + upsertSessionEntryCore( { agentId: "ops", sessionKey: "agent:main:wrong-owner", storePath }, { sessionId: "wrong-owner-session", updatedAt: 10 }, ), @@ -1752,7 +1752,7 @@ describe("session accessor seam", () => { const sessionId = `${canonicalKey}-session`; insertRawEntry(storedKey, sessionId, 5); await expect( - upsertSessionEntry( + upsertSessionEntryCore( { agentId: "ops", sessionKey: canonicalKey, storePath }, { sessionId: "new-session", updatedAt: 10 }, ), @@ -1769,11 +1769,11 @@ describe("session accessor seam", () => { it("supports multiple logical agents in an explicit shared SQLite store", async () => { const sharedStorePath = path.join(tempDir, "shared.sqlite"); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:shared", storePath: sharedStorePath }, { sessionId: "main-shared", updatedAt: 10 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "ops", sessionKey: "agent:ops:shared", storePath: sharedStorePath }, { sessionId: "ops-shared", updatedAt: 20 }, ); @@ -1787,7 +1787,7 @@ describe("session accessor seam", () => { it("rejects reply session initialization when the entry is deleted during prepare", async () => { const sessionKey = "agent:main:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "first-session", @@ -1831,7 +1831,7 @@ describe("session accessor seam", () => { await expect(updateSessionEntry(scope, () => ({ model: "gpt-5.5" }))).resolves.toBeNull(); expect(listSessionEntriesCore({ storePath })).toEqual([]); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "session-1", updatedAt: 10, }); @@ -1856,7 +1856,7 @@ describe("session accessor seam", () => { storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { model: "gpt-5.5", providerOverride: "openai", sessionId: "session-1", @@ -1929,7 +1929,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:main", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "session-1", updatedAt: 10, }); @@ -1951,7 +1951,7 @@ describe("session accessor seam", () => { storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "session-1", updatedAt: 10, }); @@ -2120,11 +2120,11 @@ describe("session accessor seam", () => { it("ignores runtime-only alias rekey fields on public exact replacements", async () => { const canonicalKey = "agent:main:runtime-canonical"; const aliasKey = "agent:main:runtime-alias"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: canonicalKey, storePath }, { sessionId: "runtime-canonical", updatedAt: 1 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: aliasKey, storePath }, { sessionId: "runtime-alias", updatedAt: 2 }, ); @@ -2152,7 +2152,7 @@ describe("session accessor seam", () => { it("projects ordered patches against one mutable store view", async () => { const keys = ["a", "b", "c", "d"].map((suffix) => `agent:main:batch-${suffix}`); for (const [index, sessionKey] of keys.entries()) { - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: `batch-${index}`, updatedAt: index + 1 }, ); @@ -2221,11 +2221,11 @@ describe("session accessor seam", () => { const canonicalKey = "agent:main:replacement-canonical"; const previousKey = "agent:main:replacement-previous"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: canonicalKey, storePath }, { sessionId: "canonical-older", updatedAt: 10 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: previousKey, storePath }, { sessionId: "rekeyed", updatedAt: 20 }, ); @@ -2279,11 +2279,11 @@ describe("session accessor seam", () => { it("rejects internal canonical targets and alias sources without changing rows or events", async () => { const visibleKey = "agent:main:replacement-visible"; const internalKey = "agent:main:internal-session-effects:replacement-guard"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: visibleKey, storePath }, { label: "Visible", sessionId: "replacement-visible", updatedAt: 10 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: internalKey, storePath }, { label: "Internal", sessionId: "replacement-internal", updatedAt: 20 }, ); @@ -2327,15 +2327,15 @@ describe("session accessor seam", () => { const exactKey = "agent:main:replacement-rollback-exact"; const canonicalKey = "agent:main:replacement-rollback-canonical"; const previousKey = "agent:main:replacement-rollback-previous"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: exactKey, storePath }, { label: "Exact original", sessionId: "rollback-exact", updatedAt: 10 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: canonicalKey, storePath }, { label: "Canonical original", sessionId: "rollback-canonical", updatedAt: 20 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: previousKey, storePath }, { label: "Previous original", sessionId: "rollback-previous", updatedAt: 30 }, ); @@ -2409,7 +2409,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:replacement-prepare", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { model: "base", sessionId: "replacement-prepare", updatedAt: 10, @@ -2465,7 +2465,7 @@ describe("session accessor seam", () => { it("does not hold a write transaction while awaiting a lifecycle entry builder", async () => { const sessionKey = "agent:main:lifecycle-prepare"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { model: "base", sessionId: "lifecycle-prepare", updatedAt: 10 }, ); @@ -2511,7 +2511,7 @@ describe("session accessor seam", () => { it("rejects a lifecycle projection when its source row changes", async () => { const scope = { sessionKey: "agent:main:lifecycle-stale", storePath }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { model: "base", sessionId: "lifecycle-stale", updatedAt: 10, @@ -2570,7 +2570,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:preserve", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { restartRecoveryDeliveryContext: { channel: "whatsapp", to: "+15551234567", @@ -2613,7 +2613,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:cleanup", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10, }); @@ -2680,7 +2680,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:guarded-delete", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { lifecycleRevision: "original-revision", sessionId: scope.sessionId, updatedAt: 10, @@ -2732,7 +2732,7 @@ describe("session accessor seam", () => { messageCount: 1, unwindowedMessageCount: 1, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { contextBudgetStatus, inputTokens: 10, outputTokens: 20, @@ -2811,7 +2811,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:main", }; const records = createManualCompactRecords(sessionId); - await upsertSessionEntry(scope, { sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId, updatedAt: 1 }); await replaceTranscriptEvents(scope, records as Parameters[1]); const archiveDirPath = path.join(stateDir, "agents", "main", "sessions"); fs.writeFileSync(archiveDirPath, "not a directory"); @@ -2846,7 +2846,7 @@ describe("session accessor seam", () => { storePath, }; const records = createManualCompactRecords(sessionId); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { inputTokens: 10, outputTokens: 20, sessionId, @@ -2909,7 +2909,7 @@ describe("session accessor seam", () => { const scope = { agentId: "main", sessionId, sessionKey: "agent:main:main", storePath }; const records = createManualCompactRecords(sessionId); const archiveContent = `${records.map((record) => JSON.stringify(record)).join("\n")}\n`; - await upsertSessionEntry( + await upsertSessionEntryCore( scope, conflict === "metadata" ? { sessionId, totalTokens: 30, totalTokensFresh: true, updatedAt: 100 } @@ -3035,7 +3035,7 @@ describe("session accessor seam", () => { message: { role: "user", content: "kept after", timestamp: 5 }, }, ]; - await upsertSessionEntry(scope, { sessionFile, sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionFile, sessionId, updatedAt: 1 }); await replaceTranscriptEvents(scope, records as Parameters[1]); await expect( @@ -3063,7 +3063,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:lock-order", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10, }); @@ -3134,7 +3134,7 @@ describe("session accessor seam", () => { sessionKey: `agent:main:ordered-turn-${guarded ? "guarded" : "ordinary"}`, storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { lifecycleRevision: "ordered-turn-revision", sessionId: scope.sessionId, updatedAt: 10, @@ -3276,7 +3276,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:legacy-unsequenced-turn", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { lifecycleRevision: "legacy-unsequenced-revision", sessionId: scope.sessionId, updatedAt: 10, @@ -3381,7 +3381,7 @@ describe("session accessor seam", () => { sessionKey: `agent:main:diverging-turn-${guarded ? "guarded" : "ordinary"}`, storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { lifecycleRevision: "diverging-turn-revision", sessionId: scope.sessionId, updatedAt: 10, @@ -3467,7 +3467,7 @@ describe("session accessor seam", () => { sessionKey: `agent:main:replayed-turn-${guarded ? "guarded" : "ordinary"}`, storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10, }); @@ -3530,7 +3530,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:reordered-replay", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10, }); @@ -3570,7 +3570,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:redacted-replay", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10, }); @@ -3604,7 +3604,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:main", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10, }); @@ -3669,7 +3669,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:expected", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10, }); @@ -3709,7 +3709,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:admission", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, status: "done", updatedAt: 10, @@ -3847,7 +3847,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:main", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10, }); @@ -3893,7 +3893,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:predicate-rebind", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { lifecycleRevision: "predicate-revision", sessionId: scope.sessionId, updatedAt: 10, @@ -3950,7 +3950,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:predicate-context", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10, lastRunError: "private entry state", @@ -3981,7 +3981,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:same-owner", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, abortedLastRun: true, restartRecoveryDeliveryRunId: "recovery-run", @@ -4059,7 +4059,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:main", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { lifecycleRevision: "original-revision", sessionId: scope.sessionId, updatedAt: 10, @@ -4187,7 +4187,7 @@ describe("session accessor seam", () => { storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, sessionFile: staleSessionFile, updatedAt: 10, @@ -4231,7 +4231,7 @@ describe("session accessor seam", () => { sessionKey: "agent:main:main", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10, }); @@ -4331,7 +4331,7 @@ describe("session accessor seam", () => { it("does not expose legacy custom transcript paths as read fallbacks after SQLite migration", async () => { const legacyTranscript = path.join(tempDir, "custom-topic-transcript.jsonl"); const sessionKey = "agent:main:telegram:group:1:topic:9"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "custom-topic-session", @@ -4375,7 +4375,7 @@ describe("session accessor seam", () => { it("does not trust a preloaded entry for a different session id", async () => { const sessionKey = "agent:main:mismatched-preloaded-read"; - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId: "stored-session", diff --git a/src/config/sessions/session-accessor.transcript-target.ts b/src/config/sessions/session-accessor.transcript-target.ts index fe478403034c..6ad24ea252b3 100644 --- a/src/config/sessions/session-accessor.transcript-target.ts +++ b/src/config/sessions/session-accessor.transcript-target.ts @@ -1,7 +1,7 @@ import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js"; import { resolveOpenClawAgentSqlitePath } from "../../state/openclaw-agent-db.js"; import { getRuntimeConfig } from "../io.js"; -import { resolveStorePath } from "./paths.js"; +import { resolveSessionStorePathCore } from "./paths.js"; import { resolveSessionEntrySelection } from "./session-accessor.entry.js"; import { resolveSqliteTranscriptScope, @@ -30,7 +30,7 @@ function resolveRuntimeContext( } const configuredStorePath = resolveConcreteSessionStorePath(scope.storePath) ?? - resolveStorePath(getRuntimeConfig().session?.store, { agentId, env: scope.env }); + resolveSessionStorePathCore(getRuntimeConfig().session?.store, { agentId, env: scope.env }); const storePath = resolveSessionStorePathForScope({ agentId, env: scope.env, @@ -76,7 +76,7 @@ export function resolveSessionTranscriptReadTarget( } const configuredStorePath = resolveConcreteSessionStorePath(scope.storePath) ?? - resolveStorePath(getRuntimeConfig().session?.store, { agentId, env: scope.env }); + resolveSessionStorePathCore(getRuntimeConfig().session?.store, { agentId, env: scope.env }); const storePath = resolveSessionStorePathForScope({ agentId, env: scope.env, diff --git a/src/config/sessions/session-accessor.transcript-turn.ts b/src/config/sessions/session-accessor.transcript-turn.ts index 74e26ca758b2..8553264653ef 100644 --- a/src/config/sessions/session-accessor.transcript-turn.ts +++ b/src/config/sessions/session-accessor.transcript-turn.ts @@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto"; import { resolveDefaultAgentId } from "../../agents/agent-scope-config.js"; import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js"; import { getRuntimeConfig } from "../io.js"; -import { resolveStorePath } from "./paths.js"; +import { resolveSessionStorePathCore } from "./paths.js"; import { updateSessionEntry } from "./session-accessor.entry-mutation.js"; import { loadSessionEntryReadOnly, @@ -323,7 +323,7 @@ async function resolveTranscriptTurnTarget( } const storePath = scope.storePath ?? - resolveStorePath(getRuntimeConfig().session?.store, { + resolveSessionStorePathCore(getRuntimeConfig().session?.store, { agentId, env: scope.env, }); diff --git a/src/config/sessions/session-accessor.ts b/src/config/sessions/session-accessor.ts index 5c6f2e1b4a74..c4596a181c7b 100644 --- a/src/config/sessions/session-accessor.ts +++ b/src/config/sessions/session-accessor.ts @@ -154,7 +154,7 @@ export { resolveSessionEntryCandidateTarget, resolveSessionEntrySelection, updateResolvedSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "./session-accessor.entry.js"; export { createSessionEntryWithTranscript, diff --git a/src/config/sessions/session-reset-boundary-event.test.ts b/src/config/sessions/session-reset-boundary-event.test.ts index 652f01501cee..6211562d4163 100644 --- a/src/config/sessions/session-reset-boundary-event.test.ts +++ b/src/config/sessions/session-reset-boundary-event.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { buildSessionResetBoundaryPlan } from "./session-reset-boundary-event.js"; function message(params: { @@ -118,7 +118,7 @@ describe("reset boundary planning", () => { }); it("seeds only the bounded replay tail from a legacy transcript", async () => { - await withTempDir({ prefix: "openclaw-reset-boundary-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-reset-boundary-" }, async (dir) => { const sessionFile = path.join(dir, "legacy.jsonl"); const records = Array.from({ length: 20 }, (_, index) => message({ @@ -157,7 +157,7 @@ describe("reset boundary planning", () => { }); it("respects legacy reset cuts and reparents the selected tail", async () => { - await withTempDir({ prefix: "openclaw-reset-boundary-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-reset-boundary-" }, async (dir) => { const sessionFile = path.join(dir, "legacy-reset.jsonl"); const oldUser = message({ id: "legacy-old-user", diff --git a/src/config/sessions/session-sharing-store.test.ts b/src/config/sessions/session-sharing-store.test.ts index 6ffaefb3286a..e0f99d3ac8b3 100644 --- a/src/config/sessions/session-sharing-store.test.ts +++ b/src/config/sessions/session-sharing-store.test.ts @@ -3,11 +3,11 @@ import { closeOpenClawAgentDatabasesForTest, openOpenClawAgentDatabase, } from "../../state/openclaw-agent-db.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { deleteSessionEntryLifecycle, loadSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "./session-accessor.js"; import { addSessionMember, @@ -21,10 +21,10 @@ afterEach(() => closeOpenClawAgentDatabasesForTest()); describe("session sharing store", () => { it("keeps deterministic membership rows", async () => { - await withTempDir({ prefix: "openclaw-session-sharing-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-sharing-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir }; const scope = { agentId: "main", env, sessionKey: "agent:main:main" }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "session-main", updatedAt: 1, visibility: "shared", @@ -61,10 +61,10 @@ describe("session sharing store", () => { }); it("does not recreate a missing canonical membership table", async () => { - await withTempDir({ prefix: "openclaw-session-sharing-missing-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-sharing-missing-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir }; const scope = { agentId: "main", env, sessionKey: "agent:main:main" }; - await upsertSessionEntry(scope, { sessionId: "session-main", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: "session-main", updatedAt: 1 }); const database = openOpenClawAgentDatabase({ agentId: "main", env }); database.db.exec("DROP TABLE session_members;"); @@ -78,10 +78,10 @@ describe("session sharing store", () => { }); it("refuses member writes whose expected session instance no longer matches", async () => { - await withTempDir({ prefix: "openclaw-session-sharing-instance-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-sharing-instance-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir }; const scope = { agentId: "main", env, sessionKey: "agent:main:main" }; - await upsertSessionEntry(scope, { sessionId: "session-b", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: "session-b", updatedAt: 1 }); // A write authorized against a now-replaced instance must not mutate the // live one under the same key. @@ -110,10 +110,10 @@ describe("session sharing store", () => { }); it("drops members when the session instance is replaced under the same key", async () => { - await withTempDir({ prefix: "openclaw-session-sharing-recreate-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-sharing-recreate-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir }; const scope = { agentId: "main", env, sessionKey: "agent:main:main" }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "session-a", updatedAt: 1, visibility: "read-only", @@ -126,7 +126,7 @@ describe("session sharing store", () => { // Reusing the canonical key with a new sessionId is a fresh session; a // stale member must not inherit access, and the replacement must start // shared even if the recreated entry copied a restricted visibility. - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "session-b", updatedAt: 3, visibility: "read-only", @@ -141,16 +141,16 @@ describe("session sharing store", () => { expect( addSessionMember(scope, { identityId: "guest", addedBy: "owner", addedAt: 4 }).inserted, ).toBe(true); - await upsertSessionEntry(scope, { sessionId: "session-b", updatedAt: 5 }); + await upsertSessionEntryCore(scope, { sessionId: "session-b", updatedAt: 5 }); expect(isSessionMember(scope, "guest")).toBe(true); }); }); it("rejects stale member writes after entry-only deletion leaves a placeholder", async () => { - await withTempDir({ prefix: "openclaw-session-sharing-placeholder-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-sharing-placeholder-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir }; const scope = { agentId: "main", env, sessionKey: "agent:main:main" }; - await upsertSessionEntry(scope, { sessionId: "session-a", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: "session-a", updatedAt: 1 }); expect( addSessionMember(scope, { identityId: "guest", addedBy: "owner", addedAt: 2 }).inserted, ).toBe(true); @@ -178,7 +178,7 @@ describe("session sharing store", () => { }), ).toThrow(/session changed/); - await upsertSessionEntry(scope, { sessionId: "session-b", updatedAt: 3 }); + await upsertSessionEntryCore(scope, { sessionId: "session-b", updatedAt: 3 }); expect(listSessionMembers(scope)).toEqual([]); }); }); diff --git a/src/config/sessions/session-store-path.ts b/src/config/sessions/session-store-path.ts index a5abf6139717..d38a7152243b 100644 --- a/src/config/sessions/session-store-path.ts +++ b/src/config/sessions/session-store-path.ts @@ -1,7 +1,7 @@ import { isIncognitoSessionKey, resolveAgentIdFromSessionKey } from "../../routing/session-key.js"; import { resolveIncognitoOpenClawAgentSqlitePath } from "../../state/openclaw-agent-db.js"; import { getRuntimeConfig } from "../io.js"; -import { resolveStorePath } from "./paths.js"; +import { resolveSessionStorePathCore } from "./paths.js"; type SessionStorePathScope = { agentId?: string; @@ -25,7 +25,7 @@ export function resolveSessionStorePathForScope(scope: SessionStorePathScope): s return scope.storePath; } const agentId = scope.agentId ?? resolveAgentIdFromSessionKey(scope.sessionKey); - return resolveStorePath(getRuntimeConfig().session?.store, { + return resolveSessionStorePathCore(getRuntimeConfig().session?.store, { agentId, env: scope.env, }); diff --git a/src/config/sessions/session-suggestion-store.test.ts b/src/config/sessions/session-suggestion-store.test.ts index 7bd60922a2e8..03fdc7e363dd 100644 --- a/src/config/sessions/session-suggestion-store.test.ts +++ b/src/config/sessions/session-suggestion-store.test.ts @@ -3,8 +3,8 @@ import { closeOpenClawAgentDatabasesForTest, openOpenClawAgentDatabase, } from "../../state/openclaw-agent-db.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; -import { upsertSessionEntry } from "./session-accessor.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; +import { upsertSessionEntryCore } from "./session-accessor.js"; import { addSessionSuggestion, claimSessionSuggestionDispatch, @@ -41,10 +41,10 @@ afterEach(() => closeOpenClawAgentDatabasesForTest()); describe("session suggestion store", () => { it("keeps deterministic rows and resolves only pending suggestions", async () => { - await withTempDir({ prefix: "openclaw-session-suggestions-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-suggestions-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir }; const scope = { agentId: "main", env, sessionKey: "agent:main:main" }; - await upsertSessionEntry(scope, { sessionId: "session-a", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: "session-a", updatedAt: 1 }); expect(listSessionSuggestions(scope)).toEqual([]); addSessionSuggestion(scope, { @@ -90,10 +90,10 @@ describe("session suggestion store", () => { }); it("does not recreate a missing canonical suggestions table", async () => { - await withTempDir({ prefix: "openclaw-session-suggestions-missing-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-suggestions-missing-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir }; const scope = { agentId: "main", env, sessionKey: "agent:main:main" }; - await upsertSessionEntry(scope, { sessionId: "session-a", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: "session-a", updatedAt: 1 }); const database = openOpenClawAgentDatabase({ agentId: "main", env }); database.db.exec("DROP TABLE session_suggestions;"); @@ -109,10 +109,10 @@ describe("session suggestion store", () => { }); it("binds writes to the session instance and clears rows on replacement", async () => { - await withTempDir({ prefix: "openclaw-session-suggestions-reset-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-suggestions-reset-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir }; const scope = { agentId: "main", env, sessionKey: "agent:main:main" }; - await upsertSessionEntry(scope, { sessionId: "session-a", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: "session-a", updatedAt: 1 }); addSessionSuggestion(scope, { id: "suggestion", authorId: "alice", @@ -127,16 +127,16 @@ describe("session suggestion store", () => { }), ).toThrow(/session changed/); - await upsertSessionEntry(scope, { sessionId: "session-b", updatedAt: 2 }); + await upsertSessionEntryCore(scope, { sessionId: "session-b", updatedAt: 2 }); expect(listSessionSuggestions(scope)).toEqual([]); }); }); it("bounds pending suggestions per author", async () => { - await withTempDir({ prefix: "openclaw-session-suggestions-limit-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-suggestions-limit-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir }; const scope = { agentId: "main", env, sessionKey: "agent:main:main" }; - await upsertSessionEntry(scope, { sessionId: "session-a", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: "session-a", updatedAt: 1 }); for (let index = 0; index < MAX_PENDING_SESSION_SUGGESTIONS_PER_AUTHOR; index += 1) { addSessionSuggestion(scope, { id: `suggestion-${index}`, @@ -170,10 +170,10 @@ describe("session suggestion store", () => { }); it("prunes old resolved suggestions on subsequent writes", async () => { - await withTempDir({ prefix: "openclaw-session-suggestions-retention-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-suggestions-retention-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir }; const scope = { agentId: "main", env, sessionKey: "agent:main:main" }; - await upsertSessionEntry(scope, { sessionId: "session-a", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: "session-a", updatedAt: 1 }); for (let index = 0; index <= MAX_RETAINED_RESOLVED_SESSION_SUGGESTIONS; index += 1) { const id = `resolved-${index}`; addSessionSuggestion(scope, { @@ -199,10 +199,10 @@ describe("session suggestion store", () => { }); it("durably claims dispatch and permits only same-action stale recovery", async () => { - await withTempDir({ prefix: "openclaw-session-suggestions-claim-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-session-suggestions-claim-" }, async (dir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: dir }; const scope = { agentId: "main", env, sessionKey: "agent:main:main" }; - await upsertSessionEntry(scope, { sessionId: "session-a", updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: "session-a", updatedAt: 1 }); addSessionSuggestion(scope, { id: "claimed", authorId: "alice", diff --git a/src/config/sessions/sessions.test.ts b/src/config/sessions/sessions.test.ts index e3f42c0ee494..64291fdd9e08 100644 --- a/src/config/sessions/sessions.test.ts +++ b/src/config/sessions/sessions.test.ts @@ -6,7 +6,7 @@ import { withTempDirSync } from "../../test-helpers/temp-dir.js"; import type { SessionConfig } from "../types.base.js"; import { resolveSessionWorkStartError } from "./lifecycle.js"; import { - resolveSessionFilePath, + resolveSessionFilePathCore, resolveSessionFilePathOptions, resolveSessionTranscriptPathInDir, validateSessionId, @@ -196,7 +196,7 @@ describe("session path safety", () => { it("falls back to derived path when sessionFile is outside known agent sessions dirs", () => { const sessionsDir = "/tmp/openclaw/agents/main/sessions"; - const resolved = resolveSessionFilePath( + const resolved = resolveSessionFilePathCore( "sess-1", { sessionFile: "/tmp/openclaw/agents/work/not-sessions/abc-123.jsonl" }, { sessionsDir }, @@ -223,7 +223,11 @@ describe("session path safety", () => { fs.symlinkSync(realRoot, aliasRoot, "dir"); const viaAlias = path.join(aliasRoot, "agents", "main", "sessions", "sess-1.jsonl"); fs.writeFileSync(path.join(sessionsDir, "sess-1.jsonl"), ""); - const resolved = resolveSessionFilePath("sess-1", { sessionFile: viaAlias }, { sessionsDir }); + const resolved = resolveSessionFilePathCore( + "sess-1", + { sessionFile: viaAlias }, + { sessionsDir }, + ); expect(fs.realpathSync(resolved)).toBe( fs.realpathSync(path.join(sessionsDir, "sess-1.jsonl")), ); @@ -244,7 +248,7 @@ describe("session path safety", () => { const symlinkPath = path.join(sessionsDir, "escaped.jsonl"); fs.symlinkSync(outsideFile, symlinkPath, "file"); - const resolved = resolveSessionFilePath( + const resolved = resolveSessionFilePathCore( "sess-1", { sessionFile: symlinkPath }, { sessionsDir }, diff --git a/src/config/sessions/store-temp-cleanup.test.ts b/src/config/sessions/store-temp-cleanup.test.ts index 7b319cf7b1f3..bae79faf6c94 100644 --- a/src/config/sessions/store-temp-cleanup.test.ts +++ b/src/config/sessions/store-temp-cleanup.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { SESSION_STORE_TEMP_STALE_MS } from "./artifacts.js"; import { sweepOrphanSessionStoreTemps } from "./store-temp-cleanup.js"; @@ -16,7 +16,7 @@ async function writeAt(filePath: string, contents: string, mtimeMs: number): Pro describe("sweepOrphanSessionStoreTemps", () => { it("removes stale current and legacy temps while preserving fresh and unrelated files", async () => { - await withTempDir({ prefix: "store-temp-cleanup" }, async (dir) => { + await withTestDir({ prefix: "store-temp-cleanup" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const staleMtime = NOW_MS - SESSION_STORE_TEMP_STALE_MS - 1; const staleCurrent = `${storePath}.123.${UUID}.tmp`; @@ -44,7 +44,7 @@ describe("sweepOrphanSessionStoreTemps", () => { ["corrupt", "{not-json"], ["non-record", "[]"], ])("preserves recovery candidates when the primary store is %s", async (_label, primary) => { - await withTempDir({ prefix: "store-temp-recovery" }, async (dir) => { + await withTestDir({ prefix: "store-temp-recovery" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const candidate = `${storePath}.123.${UUID}.tmp`; if (primary !== undefined) { @@ -58,7 +58,7 @@ describe("sweepOrphanSessionStoreTemps", () => { }); it("matches a custom store basename", async () => { - await withTempDir({ prefix: "store-temp-custom" }, async (dir) => { + await withTestDir({ prefix: "store-temp-custom" }, async (dir) => { const storePath = path.join(dir, "custom-store.json"); const candidate = `${storePath}.123.${UUID}.tmp`; await fs.writeFile(storePath, "{}"); diff --git a/src/config/sessions/store.pruning.test.ts b/src/config/sessions/store.pruning.test.ts index 9a71c3e646f9..c18c4154ced9 100644 --- a/src/config/sessions/store.pruning.test.ts +++ b/src/config/sessions/store.pruning.test.ts @@ -5,7 +5,7 @@ import path from "node:path"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { saveLegacySessionStore as saveSessionStore } from "../../infra/state-migrations.legacy-session-store.js"; import { beginSessionWorkAdmission } from "../../sessions/session-lifecycle-admission.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { createFixtureSuite } from "../../test-utils/fixture-suite.js"; import { normalizeSessionDeliveryState } from "../../utils/delivery-context.shared.js"; import { enforceSessionDiskBudget } from "./disk-budget.js"; @@ -952,7 +952,7 @@ describe("resolveMaintenanceConfigFromInput", () => { }); it("retains session history when a zero maxDiskBytes disables the budget", async () => { - await withTempDir({ prefix: "openclaw-zero-disk-budget-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-zero-disk-budget-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const transcriptPath = path.join(dir, "old-session.jsonl"); await fs.writeFile(transcriptPath, JSON.stringify({ role: "user", content: "hello" })); diff --git a/src/config/sessions/targets-read-availability.ts b/src/config/sessions/targets-read-availability.ts index a19e46d17558..68602b58ca04 100644 --- a/src/config/sessions/targets-read-availability.ts +++ b/src/config/sessions/targets-read-availability.ts @@ -4,7 +4,7 @@ import { resolveDefaultAgentId } from "../../agents/agent-scope.js"; import { normalizeAgentId, parseAgentSessionKey } from "../../routing/session-key.js"; import { withOpenClawAgentDatabaseReadOnly } from "../../state/openclaw-agent-db-readonly.js"; import type { OpenClawConfig } from "../types.openclaw.js"; -import { resolveStorePath } from "./paths.js"; +import { resolveSessionStorePathCore } from "./paths.js"; import { readSessionEntryKeys } from "./session-accessor.sqlite-entry-store.js"; import { resolveSqliteTargetFromSessionStorePath } from "./session-sqlite-target.js"; import { @@ -86,12 +86,12 @@ function resolveFixedSessionStoreTargetsReadOnly( const defaultAgentId = resolveDefaultAgentId(cfg); const fixedTarget = { agentId: requested, - storePath: resolveStorePath(storeConfig, { agentId: requested, env }), + storePath: resolveSessionStorePathCore(storeConfig, { agentId: requested, env }), }; try { const configuredTargets = listConfiguredSessionStoreAgentIds(cfg).map((configuredAgentId) => ({ agentId: configuredAgentId, - storePath: resolveStorePath(storeConfig, { agentId: configuredAgentId, env }), + storePath: resolveSessionStorePathCore(storeConfig, { agentId: configuredAgentId, env }), })); if (!configuredTargets.some((target) => normalizeAgentId(target.agentId) === requested)) { configuredTargets.push(fixedTarget); @@ -142,7 +142,7 @@ export function resolveExistingAgentSessionStoreTargetsReadOnlyResult( } const configuredTarget = { agentId: requested, - storePath: resolveStorePath(cfg.session?.store, { agentId: requested, env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: requested, env }), }; const targets = dedupeTargetsByStorePath([ configuredTarget, diff --git a/src/config/sessions/targets.test.ts b/src/config/sessions/targets.test.ts index 67bd4b81041d..c630edd35806 100644 --- a/src/config/sessions/targets.test.ts +++ b/src/config/sessions/targets.test.ts @@ -9,7 +9,7 @@ import { } from "../../state/openclaw-agent-db-registry.js"; import { resolveOpenClawStateSqlitePath } from "../../state/openclaw-state-db.paths.js"; import type { OpenClawConfig } from "../config.js"; -import { resolveStorePath } from "./paths.js"; +import { resolveSessionStorePathCore } from "./paths.js"; import { listSessionEntriesReadOnly, replaceSessionEntry } from "./session-accessor.js"; import { resolveSqliteTargetFromSessionStorePath } from "./session-sqlite-target.js"; import { @@ -103,11 +103,11 @@ describe("resolveSessionStoreTargets", () => { expect(targets).toEqual([ { agentId: "main", - storePath: resolveStorePath(cfg.session?.store, { agentId: "main", env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: "main", env }), }, { agentId: "work", - storePath: resolveStorePath(cfg.session?.store, { agentId: "work", env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: "work", env }), }, ]); }); @@ -136,23 +136,23 @@ describe("resolveSessionStoreTargets", () => { expect(targets).toEqual([ { agentId: "ops", - storePath: resolveStorePath(cfg.session?.store, { agentId: "ops", env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: "ops", env }), }, { agentId: "review", - storePath: resolveStorePath(cfg.session?.store, { agentId: "review", env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: "review", env }), }, { agentId: "claude", - storePath: resolveStorePath(cfg.session?.store, { agentId: "claude", env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: "claude", env }), }, { agentId: "gemini", - storePath: resolveStorePath(cfg.session?.store, { agentId: "gemini", env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: "gemini", env }), }, { agentId: "opencode", - storePath: resolveStorePath(cfg.session?.store, { agentId: "opencode", env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: "opencode", env }), }, ]); }); @@ -703,8 +703,12 @@ describe("resolveExistingAgentSessionStoreTargetsSync", () => { agents: { list: [{ id: "main", default: true }] }, session: { store: storeTemplate }, }; - const legacyStorePath = resolveStorePath(storeTemplate, { agentId: "retired-legacy" }); - const sqliteStorePath = resolveStorePath(storeTemplate, { agentId: "retired-sqlite" }); + const legacyStorePath = resolveSessionStorePathCore(storeTemplate, { + agentId: "retired-legacy", + }); + const sqliteStorePath = resolveSessionStorePathCore(storeTemplate, { + agentId: "retired-sqlite", + }); await fs.mkdir(path.dirname(legacyStorePath), { recursive: true }); await fs.writeFile( legacyStorePath, @@ -990,7 +994,7 @@ describe("resolveAllAgentSessionStoreCandidateTargetsSync", () => { await withTempHome(async (home) => { const stateDir = path.join(home, ".openclaw"); const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; - const storePath = resolveStorePath(undefined, { agentId: "main", env }); + const storePath = resolveSessionStorePathCore(undefined, { agentId: "main", env }); expect( resolveAllAgentSessionStoreCandidateTargetsSync( diff --git a/src/config/sessions/targets.ts b/src/config/sessions/targets.ts index 14348ffa3d24..8b9e1945c959 100644 --- a/src/config/sessions/targets.ts +++ b/src/config/sessions/targets.ts @@ -16,7 +16,7 @@ import { } from "../../state/openclaw-agent-db-registry.js"; import { resolveStateDir } from "../paths.js"; import type { OpenClawConfig } from "../types.openclaw.js"; -import { resolveAgentsDirFromSessionStorePath, resolveStorePath } from "./paths.js"; +import { resolveAgentsDirFromSessionStorePath, resolveSessionStorePathCore } from "./paths.js"; import { readSessionEntryKeys } from "./session-accessor.sqlite-entry-store.js"; import { listDurableSqliteTargetOwnersForSessionStorePath, @@ -129,7 +129,10 @@ export function listKnownSessionStoreAgentIds( const isSameDatabasePath = createOpenClawAgentDatabasePathMatcher(); const ids = new Set(listConfiguredSessionStoreAgentIds(cfg)); if (!isPerAgentSessionStoreConfig(cfg.session?.store)) { - const storePath = resolveStorePath(cfg.session?.store, { agentId: defaultAgentId, env }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { + agentId: defaultAgentId, + env, + }); const durableTarget = resolveSqliteTargetFromSessionStorePath(storePath, { agentId: defaultAgentId, defaultAgentId, @@ -170,7 +173,7 @@ export function listKnownSessionStoreAgentIds( } for (const registered of listOpenClawRegisteredAgentDatabases({ env })) { const agentId = normalizeAgentId(registered.agentId); - const storePath = resolveStorePath(cfg.session?.store, { agentId, env }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId, env }); const expectedPath = resolveSqliteTargetFromSessionStorePath(storePath, { agentId, defaultAgentId, @@ -327,7 +330,7 @@ function resolveExplicitSessionStoreTarget(params: { env: NodeJS.ProcessEnv; store: string; }): SessionStoreTarget { - const storePath = resolveStorePath(params.store, { + const storePath = resolveSessionStorePathCore(params.store, { agentId: params.defaultAgentId, env: params.env, }); @@ -422,11 +425,11 @@ export function resolveExistingAgentSessionStoreTargetsSync( if (!isPerAgentSessionStoreConfig(storeConfig)) { const fixedTarget = { agentId: requested, - storePath: resolveStorePath(storeConfig, { agentId: requested, env }), + storePath: resolveSessionStorePathCore(storeConfig, { agentId: requested, env }), }; const configuredTargets = listConfiguredSessionStoreAgentIds(cfg).map((configuredAgentId) => ({ agentId: configuredAgentId, - storePath: resolveStorePath(storeConfig, { agentId: configuredAgentId, env }), + storePath: resolveSessionStorePathCore(storeConfig, { agentId: configuredAgentId, env }), })); if (!configuredTargets.some((target) => normalizeAgentId(target.agentId) === requested)) { configuredTargets.push(fixedTarget); @@ -472,7 +475,7 @@ export function resolveExistingAgentSessionStoreTargetsSync( } const requestedTarget = { agentId: requested, - storePath: resolveStorePath(storeConfig, { agentId: requested, env }), + storePath: resolveSessionStorePathCore(storeConfig, { agentId: requested, env }), }; // Directory discovery cannot enumerate arbitrary templates. Keep an existing retired store // visible by checking the requested agent's deterministic target alongside discovered stores. @@ -580,8 +583,8 @@ export function resolveAgentSessionStoreTargetsSync( const env = params.env ?? process.env; const requested = normalizeAgentId(agentId); const storePaths = new Set([ - resolveStorePath(cfg.session?.store, { agentId: requested, env }), - resolveStorePath(undefined, { agentId: requested, env }), + resolveSessionStorePathCore(cfg.session?.store, { agentId: requested, env }), + resolveSessionStorePathCore(undefined, { agentId: requested, env }), ]); const targets: SessionStoreTarget[] = []; const realAgentsRoots = new Map(); @@ -681,7 +684,7 @@ export function resolveSessionStoreTargets( if (allAgents) { const targets = listConfiguredSessionStoreAgentIds(cfg).map((agentId) => ({ agentId, - storePath: resolveStorePath(cfg.session?.store, { agentId, env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId, env }), })); return dedupeSessionStoreTargetsBySqliteTarget(targets, { defaultAgentId, @@ -703,7 +706,7 @@ export function resolveSessionStoreTargets( return [ { agentId: requested, - storePath: resolveStorePath(cfg.session?.store, { agentId: requested, env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: requested, env }), }, ]; } @@ -711,7 +714,7 @@ export function resolveSessionStoreTargets( return [ { agentId: defaultAgentId, - storePath: resolveStorePath(cfg.session?.store, { agentId: defaultAgentId, env }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: defaultAgentId, env }), }, ]; } diff --git a/src/config/sessions/transcript.ts b/src/config/sessions/transcript.ts index 87439a9a3870..981097a56bab 100644 --- a/src/config/sessions/transcript.ts +++ b/src/config/sessions/transcript.ts @@ -25,7 +25,7 @@ import { parseSqliteSessionFileMarker, type SqliteSessionFileMarker, } from "./legacy-sqlite-marker.js"; -import { resolveDefaultSessionStorePath, resolveStorePath } from "./paths.js"; +import { resolveDefaultSessionStorePath, resolveSessionStorePathCore } from "./paths.js"; import { loadSessionEntryReadOnly, loadTranscriptEvents, @@ -592,7 +592,8 @@ export async function appendExactAssistantMessageToSessionTranscript(params: { const storeAgentId = transcriptAgentId ?? resolveAgentIdFromSessionKey(sessionKey, configuredDefaultAgentId); const storePath = - params.storePath ?? resolveStorePath(params.config?.session?.store, { agentId: storeAgentId }); + params.storePath ?? + resolveSessionStorePathCore(params.config?.session?.store, { agentId: storeAgentId }); const resolved = resolveSessionEntrySelection({ ...(transcriptAgentId ? { agentId: transcriptAgentId } : {}), sessionKey, diff --git a/src/context-engine/context-engine.test.ts b/src/context-engine/context-engine.test.ts index bae0b98c29c7..72652cd67331 100644 --- a/src/context-engine/context-engine.test.ts +++ b/src/context-engine/context-engine.test.ts @@ -8,7 +8,7 @@ import { createContextEngineLogicalTurnLease, selectContextEngineForTranscriptHost, } from "../agents/harness/context-engine-logical-turn.js"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import { SessionTranscriptReadFenceError } from "../config/sessions/session-transcript-read-fence.js"; import type { MemoryCitationsMode } from "../config/types.memory.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; @@ -370,11 +370,11 @@ describe("Engine contract tests", () => { const storePath = path.join(root, "agents", "main", "sessions", "sessions.json"); const sessionKey = "agent:main:successor"; try { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "before-compaction", updatedAt: 1 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:aaa-successor-alias", storePath }, { sessionId: "after-compaction", updatedAt: 2 }, ); diff --git a/src/cron/delivery.ts b/src/cron/delivery.ts index 4534d3bd2954..452102f06dfe 100644 --- a/src/cron/delivery.ts +++ b/src/cron/delivery.ts @@ -1,7 +1,7 @@ /** Sends cron announce payloads and best-effort failure notifications. */ import type { ReplyPayload } from "../auto-reply/reply-payload.js"; -import { sendDurableMessageBatch } from "../channels/message/runtime.js"; +import { sendDurableMessageBatchCore } from "../channels/message/runtime.js"; import type { CliDeps } from "../cli/deps.types.js"; import { createOutboundSendDeps } from "../cli/outbound-send-deps.js"; import type { OpenClawConfig } from "../config/types.js"; @@ -101,7 +101,7 @@ async function deliverCronAnnouncePayload(params: { }): Promise { // Cron delivery is durable and non-best-effort for primary announces; partial // channel failure must surface as a cron run failure. - const send = await sendDurableMessageBatch({ + const send = await sendDurableMessageBatchCore({ cfg: params.cfg, channel: params.delivery.resolvedTarget.channel, to: params.delivery.resolvedTarget.to, diff --git a/src/cron/isolated-agent.session-identity.test.ts b/src/cron/isolated-agent.session-identity.test.ts index 9d1d25c86dfe..b9545df8ee87 100644 --- a/src/cron/isolated-agent.session-identity.test.ts +++ b/src/cron/isolated-agent.session-identity.test.ts @@ -5,7 +5,7 @@ import path from "node:path"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import * as modelThinkingDefault from "../agents/model-thinking-default.js"; import { SessionManager } from "../agents/sessions/index.js"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import { runCronIsolatedAgentTurn } from "./isolated-agent.js"; import { makeCfg, @@ -394,7 +394,7 @@ describe("runCronIsolatedAgentTurn session identity", () => { await useRealCronSessionState(); await withTempHome(async (home) => { const storePath = await writeSessionStore(home, { lastProvider: "webchat", lastTo: "" }); - await upsertSessionEntry( + await upsertSessionEntryCore( { storePath, sessionKey: "agent:main:cron:job-1" }, { sessionId: "old", diff --git a/src/cron/isolated-agent/delivery-dispatch.ts b/src/cron/isolated-agent/delivery-dispatch.ts index d3a369504a21..2da53bbe5789 100644 --- a/src/cron/isolated-agent/delivery-dispatch.ts +++ b/src/cron/isolated-agent/delivery-dispatch.ts @@ -1,7 +1,7 @@ /** Dispatches isolated cron output to direct delivery, mirrors, and follow-up queues. */ import type { NormalizeReplySkipReason } from "../../auto-reply/reply/normalize-reply.js"; import { isSilentReplyText, SILENT_REPLY_TOKEN } from "../../auto-reply/tokens.js"; -import { resolveStorePath } from "../../config/sessions/inbound.runtime.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/inbound.runtime.js"; import { formatErrorMessage } from "../../infra/errors.js"; import type { NormalizedOutboundPayload, @@ -164,7 +164,7 @@ export async function dispatchCronDelivery( createOutboundSendDeps, resolveAgentOutboundIdentity, resolveCronChannelReplyTransform, - sendDurableMessageBatch, + sendDurableMessageBatchCore, } = await deliveryOutboundRuntimeLoader.load(); const payloadNormalization = normalizeDirectCronDeliveryPayloads({ deliveryPayloads, @@ -301,7 +301,7 @@ export async function dispatchCronDelivery( : undefined; const runDelivery = async () => { attemptedPayloadsForMirror.length = 0; - const send = await sendDurableMessageBatch({ + const send = await sendDurableMessageBatchCore({ cfg: params.cfgWithAgentDefaults, channel: delivery.channel, to: delivery.to, @@ -448,7 +448,7 @@ export async function dispatchCronDelivery( // Keep cron delivery mirrors text-first: non-audio attachment names // are folded into mirrorText so media does not replace delivered text. mediaUrls: undefined, - storePath: resolveStorePath(params.cfgWithAgentDefaults.session?.store, { + storePath: resolveSessionStorePathCore(params.cfgWithAgentDefaults.session?.store, { // This mirror already carries the admitted run owner. Re-parsing a // route alias can reject legacy keys or select a different store. agentId: params.agentId, diff --git a/src/cron/isolated-agent/delivery-outbound.runtime.ts b/src/cron/isolated-agent/delivery-outbound.runtime.ts index 96e7fceb3871..28260e79b78f 100644 --- a/src/cron/isolated-agent/delivery-outbound.runtime.ts +++ b/src/cron/isolated-agent/delivery-outbound.runtime.ts @@ -6,7 +6,7 @@ import { normalizeAnyChannelId } from "../../channels/registry-normalize.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; export { createOutboundSendDeps } from "../../cli/outbound-send-deps.js"; -export { sendDurableMessageBatch } from "../../channels/message/runtime.js"; +export { sendDurableMessageBatchCore } from "../../channels/message/runtime.js"; export { type OutboundDeliveryResult } from "../../infra/outbound/deliver.js"; export { resolveAgentOutboundIdentity } from "../../infra/outbound/identity.js"; export { buildOutboundSessionContext } from "../../infra/outbound/session-context.js"; diff --git a/src/cron/isolated-agent/delivery-route-session-key.ts b/src/cron/isolated-agent/delivery-route-session-key.ts index 144d0637ae78..2b6d40ad008b 100644 --- a/src/cron/isolated-agent/delivery-route-session-key.ts +++ b/src/cron/isolated-agent/delivery-route-session-key.ts @@ -1,5 +1,5 @@ import { - stripTargetKindPrefix, + stripOutboundTargetKindPrefix, stripTargetProviderPrefix, } from "../../infra/outbound/channel-target-prefix.js"; import { parseAgentSessionKey } from "../../routing/session-key.js"; @@ -28,7 +28,7 @@ export function selectCronRouteCurrentSessionKey( const conversation = /^([^:]+):(direct|group|channel):([^:]+)(?::thread:[^:]+)?$/i.exec( parsedBound.rest, ); - const targetPeerId = stripTargetKindPrefix( + const targetPeerId = stripOutboundTargetKindPrefix( stripTargetProviderPrefix(deliveryTarget, deliveryProvider), ); if ( diff --git a/src/cron/isolated-agent/delivery-target.inheritance-safety.test.ts b/src/cron/isolated-agent/delivery-target.inheritance-safety.test.ts index e6cb412cbfee..0ee0214e8619 100644 --- a/src/cron/isolated-agent/delivery-target.inheritance-safety.test.ts +++ b/src/cron/isolated-agent/delivery-target.inheritance-safety.test.ts @@ -29,7 +29,7 @@ vi.mock("../../config/sessions/delivery-info.js", () => ({ })); vi.mock("../../config/sessions/paths.js", () => ({ - resolveStorePath: vi.fn().mockReturnValue("/tmp/test-store.json"), + resolveSessionStorePathCore: vi.fn().mockReturnValue("/tmp/test-store.json"), })); vi.mock("../../config/sessions/session-accessor.js", () => { diff --git a/src/cron/isolated-agent/delivery-target.test.ts b/src/cron/isolated-agent/delivery-target.test.ts index eb297fcc0045..b0d5f508df07 100644 --- a/src/cron/isolated-agent/delivery-target.test.ts +++ b/src/cron/isolated-agent/delivery-target.test.ts @@ -31,7 +31,7 @@ vi.mock("../../config/sessions/delivery-info.js", () => ({ })); vi.mock("../../config/sessions/paths.js", () => ({ - resolveStorePath: vi.fn().mockReturnValue("/tmp/test-store.json"), + resolveSessionStorePathCore: vi.fn().mockReturnValue("/tmp/test-store.json"), })); vi.mock("../../config/sessions/session-accessor.js", () => { diff --git a/src/cron/isolated-agent/delivery-target.ts b/src/cron/isolated-agent/delivery-target.ts index d3fc8bb0ba58..aac25f9c9786 100644 --- a/src/cron/isolated-agent/delivery-target.ts +++ b/src/cron/isolated-agent/delivery-target.ts @@ -4,7 +4,7 @@ import { uniqueStrings } from "@openclaw/normalization-core/string-normalization import { resolveExplicitDeliveryTargetCompat } from "../../channels/plugins/target-parsing-loaded.js"; import type { ChannelId } from "../../channels/plugins/types.public.js"; import { resolveAgentMainSessionKey } from "../../config/sessions/main-session.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { loadSessionEntryReadOnly } from "../../config/sessions/session-accessor.js"; import type { SessionEntry } from "../../config/sessions/types.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; @@ -138,7 +138,7 @@ export async function resolveDeliveryTarget( const sessionCfg = cfg.session; const mainSessionKey = resolveAgentMainSessionKey({ cfg, agentId }); - const storePath = resolveStorePath(sessionCfg?.store, { agentId }); + const storePath = resolveSessionStorePathCore(sessionCfg?.store, { agentId }); // Look up thread-specific session first (e.g. agent:main:main:thread:1234), // then fall back to the main session entry. diff --git a/src/cron/isolated-agent/session.test.ts b/src/cron/isolated-agent/session.test.ts index ad1068e1566a..1e5f1e032763 100644 --- a/src/cron/isolated-agent/session.test.ts +++ b/src/cron/isolated-agent/session.test.ts @@ -8,9 +8,9 @@ import { projectSessionDeliveryFields } from "../../utils/delivery-context.share import type { DeliveryContext } from "../../utils/delivery-context.types.js"; vi.mock("../../config/sessions/paths.js", () => ({ - resolveStorePath: vi.fn().mockReturnValue("/tmp/test-store.json"), + resolveSessionStorePathCore: vi.fn().mockReturnValue("/tmp/test-store.json"), resolveSessionFilePathOptions: vi.fn().mockReturnValue({ sessionsDir: "/tmp" }), - resolveSessionFilePath: vi.fn((sessionId: string) => `/tmp/${sessionId}.jsonl`), + resolveSessionFilePathCore: vi.fn((sessionId: string) => `/tmp/${sessionId}.jsonl`), })); vi.mock("../../config/sessions/reset-policy.js", () => ({ diff --git a/src/cron/isolated-agent/session.ts b/src/cron/isolated-agent/session.ts index ebf78d11aee4..21f4c014f565 100644 --- a/src/cron/isolated-agent/session.ts +++ b/src/cron/isolated-agent/session.ts @@ -9,7 +9,7 @@ import { resolveSessionWorkStartError, } from "../../config/sessions/lifecycle.js"; import { hasSessionAutoModelFallbackProvenance } from "../../config/sessions/model-override-provenance.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { evaluateSessionFreshness, resolveSessionResetPolicy, @@ -149,7 +149,7 @@ export function resolveCronSession(params: { store?: Record; }) { const sessionCfg = params.cfg.session; - const storePath = resolveStorePath(sessionCfg?.store, { + const storePath = resolveSessionStorePathCore(sessionCfg?.store, { agentId: params.agentId, }); const store = diff --git a/src/cron/service/timer.test.ts b/src/cron/service/timer.test.ts index 3aea905e8486..177b30f28dc5 100644 --- a/src/cron/service/timer.test.ts +++ b/src/cron/service/timer.test.ts @@ -2,7 +2,7 @@ import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import { createDeferred } from "../../../test/helpers/promise.js"; -import { upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../../config/sessions/session-accessor.js"; import { setupCronServiceSuite, writeCronStoreSnapshot } from "../../cron/service.test-harness.js"; import { createCronServiceState as createCronServiceStateBase } from "../../cron/service/state.js"; import { executeJobCore, onTimer } from "../../cron/service/timer.test-support.js"; @@ -125,7 +125,7 @@ describe("cron service timer seam coverage", () => { }; const cronRunSessionKey = `agent:main-pr-router:cron:main-heartbeat-job:run:${now}`; const sessionStorePath = path.join(path.dirname(path.dirname(storePath)), "sessions.json"); - await upsertSessionEntry( + await upsertSessionEntryCore( { storePath: sessionStorePath, sessionKey: "agent:main-pr-router:main" }, { sessionId: "main-pr-router-session", diff --git a/src/daemon/launchd-install.ts b/src/daemon/launchd-install.ts index b1b0f36091dd..f72c55fc8efb 100644 --- a/src/daemon/launchd-install.ts +++ b/src/daemon/launchd-install.ts @@ -28,7 +28,7 @@ import { writeLaunchAgentPlist, } from "./launchd-service-files.js"; import { assertNoSystemLaunchDaemonOwnership } from "./launchd-system.js"; -import { formatLine, toPosixPath, writeFormattedLines } from "./output.js"; +import { formatLine, normalizeWindowsPathSeparators, writeFormattedLines } from "./output.js"; import { resolveDaemonHomeDir } from "./paths.js"; import type { GatewayServiceEnv, @@ -59,7 +59,7 @@ export async function uninstallLaunchAgent({ return; } - const home = toPosixPath(resolveDaemonHomeDir(env)); + const home = normalizeWindowsPathSeparators(resolveDaemonHomeDir(env)); const trashDir = path.posix.join(home, ".Trash"); const dest = path.join(trashDir, `${label}.plist`); try { diff --git a/src/daemon/launchd-service-files.ts b/src/daemon/launchd-service-files.ts index f96a74a79307..a608e0fd417c 100644 --- a/src/daemon/launchd-service-files.ts +++ b/src/daemon/launchd-service-files.ts @@ -11,7 +11,7 @@ import { readLaunchAgentProgramArgumentsFromFile, } from "./launchd-plist.js"; import { assertNoSystemLaunchDaemonOwnership } from "./launchd-system.js"; -import { formatLine, toPosixPath } from "./output.js"; +import { formatLine, normalizeWindowsPathSeparators } from "./output.js"; import { resolveDaemonHomeDir, resolveGatewayStateDir } from "./paths.js"; import { resolveGatewaySupervisorLogPaths } from "./restart-logs.js"; import type { @@ -33,7 +33,7 @@ export function resolveLaunchAgentPlistPathForLabel( env: Record, label: string, ): string { - const home = toPosixPath(resolveDaemonHomeDir(env)); + const home = normalizeWindowsPathSeparators(resolveDaemonHomeDir(env)); return path.posix.join(home, "Library", "LaunchAgents", `${label}.plist`); } @@ -351,7 +351,7 @@ export async function writeLaunchAgentPlist({ await ensureSecureDirectory(logDir); const plistPath = resolveLaunchAgentPlistPathForLabel(env, label); - const home = toPosixPath(resolveDaemonHomeDir(env)); + const home = normalizeWindowsPathSeparators(resolveDaemonHomeDir(env)); const libraryDir = path.posix.join(home, "Library"); await ensureSecureDirectory(home); await ensureSecureDirectory(libraryDir); diff --git a/src/daemon/output.ts b/src/daemon/output.ts index 85470d03addd..91c248b9af1b 100644 --- a/src/daemon/output.ts +++ b/src/daemon/output.ts @@ -2,7 +2,7 @@ import { colorize, isRich, theme } from "../../packages/terminal-core/src/theme.js"; /** Normalizes Windows separators for command output paths. */ -export const toPosixPath = (value: string) => value.replace(/\\/g, "/"); +export const normalizeWindowsPathSeparators = (value: string) => value.replace(/\\/g, "/"); /** Formats a labeled daemon output line with terminal-aware styling. */ export function formatLine(label: string, value: string): string { diff --git a/src/daemon/runtime-hints.ts b/src/daemon/runtime-hints.ts index b8b8c984dd71..487813c90037 100644 --- a/src/daemon/runtime-hints.ts +++ b/src/daemon/runtime-hints.ts @@ -1,10 +1,10 @@ /** Builds platform-specific log and start hints for daemon status output. */ -import { toPosixPath } from "./output.js"; +import { normalizeWindowsPathSeparators } from "./output.js"; import { resolveGatewayRestartLogPath, resolveGatewaySupervisorLogPaths } from "./restart-logs.js"; // macOS display paths should not keep Windows drive prefixes from mocked envs. function toDarwinDisplayPath(value: string): string { - return toPosixPath(value).replace(/^[A-Za-z]:/, ""); + return normalizeWindowsPathSeparators(value).replace(/^[A-Za-z]:/, ""); } export function buildPlatformRuntimeLogHints(params: { diff --git a/src/daemon/systemd-service-files.ts b/src/daemon/systemd-service-files.ts index 3776c8082178..745cee2ea752 100644 --- a/src/daemon/systemd-service-files.ts +++ b/src/daemon/systemd-service-files.ts @@ -5,7 +5,7 @@ import { normalizeStringEntries } from "@openclaw/normalization-core/string-norm import { isUnresolvedShellReference } from "../config/state-dir-dotenv.js"; import { splitArgsPreservingQuotes } from "./arg-split.js"; import { resolveGatewaySystemdServiceName } from "./constants.js"; -import { toPosixPath } from "./output.js"; +import { normalizeWindowsPathSeparators } from "./output.js"; import { resolveDaemonHomeDir } from "./paths.js"; import type { GatewayServiceCommandConfig, @@ -18,7 +18,7 @@ const SYSTEMD_GATEWAY_DOTENV_FILENAME = "gateway.systemd.env"; const SYSTEMD_NODE_DOTENV_FILENAME = "node.systemd.env"; export function resolveSystemdUnitPathForName(env: GatewayServiceEnv, name: string): string { - const home = toPosixPath(resolveDaemonHomeDir(env)); + const home = normalizeWindowsPathSeparators(resolveDaemonHomeDir(env)); return path.posix.join(home, ".config", "systemd", "user", `${name}.service`); } @@ -148,7 +148,7 @@ export function isNodeSystemdEnvironment(env: GatewayServiceEnv): boolean { function expandSystemdSpecifier(input: string, env: GatewayServiceEnv): string { // Support the common unit-specifier used in user services. - return input.replaceAll("%h", toPosixPath(resolveDaemonHomeDir(env))); + return input.replaceAll("%h", normalizeWindowsPathSeparators(resolveDaemonHomeDir(env))); } function parseEnvironmentFileSpecs(raw: string): string[] { diff --git a/src/daemon/test-helpers/schtasks-base-mocks.ts b/src/daemon/test-helpers/schtasks-base-mocks.ts index 8e8215485e09..af938d23e783 100644 --- a/src/daemon/test-helpers/schtasks-base-mocks.ts +++ b/src/daemon/test-helpers/schtasks-base-mocks.ts @@ -1,9 +1,9 @@ /** Base Vitest mocks for Windows schtasks daemon tests. */ import { vi } from "vitest"; import { + gatewayServiceProbeHostsMock, inspectPortUsageMock, killProcessTreeMock, - resolveGatewayServiceProbeHosts, schtasksCalls, schtasksResponses, } from "./schtasks-fixtures.js"; @@ -22,7 +22,7 @@ vi.mock("../../infra/ports-inspect.js", () => ({ })); vi.mock("../gateway-service-probe-hosts.js", () => ({ - resolveGatewayServiceProbeHosts: () => resolveGatewayServiceProbeHosts(), + resolveGatewayServiceProbeHosts: () => gatewayServiceProbeHostsMock(), })); vi.mock("../../process/kill-tree.js", () => ({ diff --git a/src/daemon/test-helpers/schtasks-fixtures.ts b/src/daemon/test-helpers/schtasks-fixtures.ts index 1e31a2c1debe..c5dfa8cceeea 100644 --- a/src/daemon/test-helpers/schtasks-fixtures.ts +++ b/src/daemon/test-helpers/schtasks-fixtures.ts @@ -14,7 +14,7 @@ export const schtasksCalls: string[][] = []; export const inspectPortUsageMock: MockFn< (port: number, options?: { probeHosts?: readonly string[] }) => Promise > = vi.fn(); -export const resolveGatewayServiceProbeHosts: MockFn<() => Promise> = vi.fn(); +export const gatewayServiceProbeHostsMock: MockFn<() => Promise> = vi.fn(); export const killProcessTreeMock: MockFn = vi.fn(); /** Runs a test with Windows-like daemon environment paths and cleans the temp dir. */ @@ -40,8 +40,8 @@ export function resetSchtasksBaseMocks() { schtasksResponses.length = 0; schtasksCalls.length = 0; inspectPortUsageMock.mockReset(); - resolveGatewayServiceProbeHosts.mockReset(); - resolveGatewayServiceProbeHosts.mockResolvedValue(["127.0.0.1"]); + gatewayServiceProbeHostsMock.mockReset(); + gatewayServiceProbeHostsMock.mockResolvedValue(["127.0.0.1"]); killProcessTreeMock.mockReset(); } diff --git a/src/gateway/agent-turn/agent-request-preflight.ts b/src/gateway/agent-turn/agent-request-preflight.ts index 1e0f63504199..5eccfcef49ae 100644 --- a/src/gateway/agent-turn/agent-request-preflight.ts +++ b/src/gateway/agent-turn/agent-request-preflight.ts @@ -10,7 +10,10 @@ import { } from "../../agents/subagents/registry/subagent-registry-memory.js"; import { resolveSwarmConfig } from "../../agents/subagents/swarm/swarm-config.js"; import { validateStructuredOutputSchema } from "../../agents/subagents/swarm/swarm-output-schema.js"; -import { resolveAgentIdFromSessionKey, resolveStorePath } from "../../config/sessions.js"; +import { + resolveAgentIdFromSessionKey, + resolveSessionStorePathCore, +} from "../../config/sessions.js"; import { loadSessionEntry } from "../../config/sessions/session-accessor.js"; import { isMainSessionRestartRecoveryInputProvenance, @@ -72,7 +75,7 @@ export function prepareAgentRequestPreflight(params: { const persistedCollectorSession = !collectorSession && requestSessionKey && isSubagentSessionKey(requestSessionKey) ? loadSessionEntry({ - storePath: resolveStorePath(cfg.session?.store, { + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: resolveAgentIdFromSessionKey(requestSessionKey, resolveDefaultAgentId(cfg)), }), sessionKey: requestSessionKey, diff --git a/src/gateway/assistant-identity.test.ts b/src/gateway/assistant-identity.test.ts index a694ff18f2ed..d2f3a01f709f 100644 --- a/src/gateway/assistant-identity.test.ts +++ b/src/gateway/assistant-identity.test.ts @@ -7,7 +7,7 @@ import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { AVATAR_MAX_DATA_URL_CHARS } from "../shared/avatar-limits.js"; import { AVATAR_MAX_BYTES } from "../shared/avatar-policy.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { DEFAULT_ASSISTANT_IDENTITY, resolveAssistantIdentity } from "./assistant-identity.js"; describe("resolveAssistantIdentity", () => { @@ -120,7 +120,7 @@ describe("resolveAssistantIdentity", () => { }); it("preserves an exact shared-cap IDENTITY.md data URL without truncation", async () => { - await withTempDir({ prefix: "openclaw-assistant-identity-cap-" }, async (workspace) => { + await withTestDir({ prefix: "openclaw-assistant-identity-cap-" }, async (workspace) => { const dataUrl = `data:image/svg+xml;base64,${Buffer.alloc(AVATAR_MAX_BYTES).toString("base64")}`; expect(dataUrl).toHaveLength(AVATAR_MAX_DATA_URL_CHARS); await fs.writeFile(path.join(workspace, "IDENTITY.md"), `- Avatar: ${dataUrl}\n`); @@ -130,7 +130,7 @@ describe("resolveAssistantIdentity", () => { }); it("rejects an oversized IDENTITY.md data URL without truncating it", async () => { - await withTempDir({ prefix: "openclaw-assistant-identity-overflow-" }, async (workspace) => { + await withTestDir({ prefix: "openclaw-assistant-identity-overflow-" }, async (workspace) => { const exact = `data:image/svg+xml;base64,${Buffer.alloc(AVATAR_MAX_BYTES).toString("base64")}`; const oversized = `${exact}A`; expect(oversized).toHaveLength(AVATAR_MAX_DATA_URL_CHARS + 1); @@ -144,7 +144,7 @@ describe("resolveAssistantIdentity", () => { }); it("rejects a non-image IDENTITY.md data URL and uses its emoji fallback", async () => { - await withTempDir({ prefix: "openclaw-assistant-identity-data-type-" }, async (workspace) => { + await withTestDir({ prefix: "openclaw-assistant-identity-data-type-" }, async (workspace) => { await fs.writeFile( path.join(workspace, "IDENTITY.md"), "- Avatar: data:text/plain,avatar\n- Emoji: 🦞\n", @@ -167,7 +167,7 @@ describe("resolveAssistantIdentity", () => { ); it("lets a valid IDENTITY.md avatar win when the agent URI scheme is unsupported", async () => { - await withTempDir({ prefix: "openclaw-assistant-identity-fallback-" }, async (workspace) => { + await withTestDir({ prefix: "openclaw-assistant-identity-fallback-" }, async (workspace) => { await fs.writeFile(path.join(workspace, "IDENTITY.md"), "- Avatar: identity.png\n"); const cfg: OpenClawConfig = { agents: { diff --git a/src/gateway/board-store.ts b/src/gateway/board-store.ts index d1ee1c3aeff6..42de41d464e4 100644 --- a/src/gateway/board-store.ts +++ b/src/gateway/board-store.ts @@ -1,6 +1,6 @@ import { SqliteBoardStore } from "../boards/sqlite-board-store.js"; import { getRuntimeConfig } from "../config/io.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { resolveSqliteTargetFromSessionStorePath } from "../config/sessions/session-sqlite-target.js"; import { resolveSessionStoreAgentId, resolveSessionStoreKey } from "./session-store-key.js"; @@ -9,7 +9,7 @@ export const boardStore = new SqliteBoardStore({ const cfg = getRuntimeConfig(); const canonicalSessionKey = resolveSessionStoreKey({ cfg, sessionKey }); const agentId = resolveSessionStoreAgentId(cfg, canonicalSessionKey); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); const databasePath = resolveSqliteTargetFromSessionStorePath(storePath, { agentId }).path; return { agentId, diff --git a/src/gateway/boot.test.ts b/src/gateway/boot.test.ts index d7f5ffd9c415..2c24462eceb8 100644 --- a/src/gateway/boot.test.ts +++ b/src/gateway/boot.test.ts @@ -18,7 +18,7 @@ vi.mock("../commands/agent.js", () => ({ const { runBootOnce } = await import("./boot.js"); const { resolveAgentIdFromSessionKey, resolveAgentMainSessionKey, resolveMainSessionKey } = await import("../config/sessions/main-session.js"); -const { resolveStorePath } = await import("../config/sessions/paths.js"); +const { resolveSessionStorePathCore } = await import("../config/sessions/paths.js"); const { applySessionEntryLifecycleMutation, listSessionEntriesCore, @@ -46,7 +46,7 @@ describe("runBootOnce", () => { const resolveMainStore = (cfg: OpenClawConfig = testConfig()) => { const sessionKey = resolveMainSessionKey(cfg); const agentId = resolveAgentIdFromSessionKey(sessionKey); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); const bootSessionKey = `agent:${agentId}:boot`; return { sessionKey, bootSessionKey, storePath }; }; diff --git a/src/gateway/boot.ts b/src/gateway/boot.ts index ea112aba1b7c..9ac35455ab9d 100644 --- a/src/gateway/boot.ts +++ b/src/gateway/boot.ts @@ -17,7 +17,7 @@ import { resolveAgentMainSessionKey, resolveMainSessionKey, } from "../config/sessions/main-session.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { preserveTemporarySessionMapping } from "../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { formatErrorMessage } from "../infra/errors.js"; @@ -137,7 +137,7 @@ export async function runBootOnce(params: { const message = buildBootPrompt(result.content ?? ""); const sessionId = generateBootSessionId(); const agentId = resolveAgentIdFromSessionKey(sessionKey); - const storePath = resolveStorePath(params.cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId }); const mappingPreservation = await preserveTemporarySessionMapping( { storePath, sessionKey }, diff --git a/src/gateway/control-ui-links.ts b/src/gateway/control-ui-links.ts index d0479b6bff1d..deb77888ca1d 100644 --- a/src/gateway/control-ui-links.ts +++ b/src/gateway/control-ui-links.ts @@ -1,6 +1,6 @@ // Control UI link builder for local, LAN, tailnet, and custom gateway binds. import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; -import { resolveAdvertisedLanHost } from "../infra/advertised-lan-host.js"; +import { resolveAdvertisedLanHostCore } from "../infra/advertised-lan-host.js"; import { inspectBestEffortPrimaryTailnetIPv4, pickBestEffortPrimaryLanIPv4, @@ -57,7 +57,7 @@ export async function resolveAdvertisedControlUiLinks( params: ControlUiLinkParams, ): Promise { const advertisedLanHost = - params.bind === "lan" ? await resolveAdvertisedLanHost().catch(() => null) : null; + params.bind === "lan" ? await resolveAdvertisedLanHostCore().catch(() => null) : null; return resolveControlUiLinks({ ...params, advertisedLanHost, diff --git a/src/gateway/credentials-secret-inputs.ts b/src/gateway/credentials-secret-inputs.ts index 11af5507587b..b28428d2bd9b 100644 --- a/src/gateway/credentials-secret-inputs.ts +++ b/src/gateway/credentials-secret-inputs.ts @@ -2,7 +2,7 @@ // Resolves SecretRefs before applying Gateway credential precedence rules. import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveSecretInputRef } from "../config/types.secrets.js"; -import { resolveSecretInputString } from "../secrets/resolve-secret-input-string.js"; +import { materializeSecretInput } from "../secrets/resolve-secret-input-string.js"; import { GatewaySecretRefUnavailableError, resolveExplicitGatewayAuth, @@ -51,7 +51,7 @@ async function resolveGatewaySecretInputString(params: { path: string; env: NodeJS.ProcessEnv; }): Promise { - const value = await resolveSecretInputString({ + const value = await materializeSecretInput({ config: params.config, value: params.value, env: params.env, diff --git a/src/gateway/gateway-acp-spawn-defaults.live.test.ts b/src/gateway/gateway-acp-spawn-defaults.live.test.ts index 64dbb1e41a41..4ba7daf9324d 100644 --- a/src/gateway/gateway-acp-spawn-defaults.live.test.ts +++ b/src/gateway/gateway-acp-spawn-defaults.live.test.ts @@ -14,7 +14,7 @@ import { prepareSystemAgentRunAdmission } from "../agents/admitted-run-context.j import { isLiveTestEnabled, readLiveTestConfig } from "../agents/live-test-helpers.js"; import { spawnAcpDirect } from "../agents/subagents/spawn/acp-spawn.js"; import { clearConfigCache, clearRuntimeConfigSnapshot } from "../config/config.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { loadSessionEntry } from "../config/sessions/session-accessor.js"; import type { SessionEntry } from "../config/sessions/types.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; @@ -213,7 +213,7 @@ async function waitForSessionEntry(params: { timeoutMs?: number; }): Promise { const timeoutMs = params.timeoutMs ?? 20_000; - const storePath = resolveStorePath(params.cfg.session?.store, { agentId: "codex" }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId: "codex" }); const startedAt = Date.now(); while (Date.now() - startedAt < timeoutMs) { const entry = loadSessionEntry({ diff --git a/src/gateway/gateway-models.profiles.live.test.ts b/src/gateway/gateway-models.profiles.live.test.ts index 7877a479a1a3..63e1a3d6b000 100644 --- a/src/gateway/gateway-models.profiles.live.test.ts +++ b/src/gateway/gateway-models.profiles.live.test.ts @@ -73,7 +73,7 @@ import { isTruthyEnvValue } from "../infra/env.js"; import type { ModelRegistry } from "../llm/model-registry.js"; import { redactSecrets } from "../logging/redact.js"; import { normalizeGoogleModelId } from "../plugin-sdk/google-model-id.js"; -import { resolveProviderThinkingProfile } from "../plugins/provider-runtime.js"; +import { resolveRuntimeThinkingProfile } from "../plugins/provider-runtime.js"; import { LEGACY_IMPLICIT_AGENT_ID as DEFAULT_AGENT_ID } from "../routing/session-key.js"; import { stripAssistantInternalScaffolding } from "../shared/text/assistant-visible-text.js"; import { findFinalTagMatches, stripFinalTags } from "../shared/text/final-tags.js"; @@ -4278,7 +4278,7 @@ function resolveGatewayLiveModelThinkingLevel(params: { if (!isGatewayLiveThinkingLevel(normalized)) { return requestedLevel; } - const profile = resolveProviderThinkingProfile({ + const profile = resolveRuntimeThinkingProfile({ provider: model.provider, config: params.cfg, context: { diff --git a/src/gateway/gateway.compaction-hot-reload.e2e.test.ts b/src/gateway/gateway.compaction-hot-reload.e2e.test.ts index 4abcaa543589..a67875c94949 100644 --- a/src/gateway/gateway.compaction-hot-reload.e2e.test.ts +++ b/src/gateway/gateway.compaction-hot-reload.e2e.test.ts @@ -10,12 +10,12 @@ import { writeConfigFile, } from "../config/config.js"; import { resetConfigOverrides } from "../config/runtime-overrides.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { appendTranscriptMessage, loadSessionEntry, loadTranscriptEventsSync, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { clearSessionStoreCacheForTest } from "../config/sessions/store-writer-state.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; @@ -216,9 +216,9 @@ describe("gateway compaction hot reload", () => { agentId: "dev", sessionId, sessionKey, - storePath: resolveStorePath(undefined, { agentId: "dev" }), + storePath: resolveSessionStorePathCore(undefined, { agentId: "dev" }), }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId, updatedAt: Date.now(), compactionCount: 0, diff --git a/src/gateway/health/collector.ts b/src/gateway/health/collector.ts index d4cf31f4a121..f9aeb50d94d9 100644 --- a/src/gateway/health/collector.ts +++ b/src/gateway/health/collector.ts @@ -6,7 +6,7 @@ import { resolveChannelDefaultAccountId } from "../../channels/plugins/helpers.j import { listReadOnlyChannelPluginsForConfig } from "../../channels/plugins/read-only.js"; import { buildChannelAccountSnapshotFromAccount } from "../../channels/plugins/status.js"; import type { ChannelAccountSnapshot } from "../../channels/plugins/types.public.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { listContextEngineQuarantines } from "../../context-engine/registry.js"; import { isDiagnosticFlagEnabled } from "../../infra/diagnostic-flags.js"; @@ -205,7 +205,7 @@ export async function collectGatewayHealthSnapshot(params: { const sessionCache = new Map(); const agents: AgentHealthSummary[] = []; for (const entry of ordered) { - const storePath = resolveStorePath(cfg.session?.store, { agentId: entry.id }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: entry.id }); const sessionCacheKey = `${storePath}\0${entry.id}`; const sessions = sessionCache.get(sessionCacheKey) ?? (await buildHealthSessionSummary(storePath, entry.id)); @@ -225,7 +225,7 @@ export async function collectGatewayHealthSnapshot(params: { const sessions = defaultAgent?.sessions ?? (await buildHealthSessionSummary( - resolveStorePath(cfg.session?.store, { agentId: defaultAgentId }), + resolveSessionStorePathCore(cfg.session?.store, { agentId: defaultAgentId }), defaultAgentId, )); diff --git a/src/gateway/minimal-gateway.test-helpers.ts b/src/gateway/minimal-gateway.test-helpers.ts index 6cd4a3fa83c2..27434a8ed17e 100644 --- a/src/gateway/minimal-gateway.test-helpers.ts +++ b/src/gateway/minimal-gateway.test-helpers.ts @@ -114,7 +114,7 @@ export async function startMinimalRealGateway( } try { for (const session of sessions) { - await sessionStore.upsertSessionEntry( + await sessionStore.upsertSessionEntryCore( { agentId: session.agentId, sessionKey: toAgentRequestSessionKey(session.key)!, diff --git a/src/gateway/server-cron.ts b/src/gateway/server-cron.ts index ee2b70361733..c45305a3da3b 100644 --- a/src/gateway/server-cron.ts +++ b/src/gateway/server-cron.ts @@ -12,7 +12,7 @@ import { resolveAgentIdFromSessionKey, resolveAgentMainSessionKey, } from "../config/sessions.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { listConfiguredSessionStoreAgentIds, listKnownSessionStoreAgentIds, @@ -494,7 +494,7 @@ export function buildGatewayCronService(params: { const defaultAgentId = resolveDefaultAgentId(params.cfg); const resolveSessionStorePath = (agentId?: string) => - resolveStorePath(params.cfg.session?.store, { + resolveSessionStorePathCore(params.cfg.session?.store, { agentId: agentId ?? defaultAgentId, }); const sessionStorePath = resolveSessionStorePath(defaultAgentId); diff --git a/src/gateway/server-json.ts b/src/gateway/server-json.ts index f6748cac53b3..7f1944bf6638 100644 --- a/src/gateway/server-json.ts +++ b/src/gateway/server-json.ts @@ -3,7 +3,7 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; /** Safely parses an optional JSON string, returning a payloadJSON wrapper on parse failure. */ -export function safeParseJson(value: string | null | undefined): unknown { +export function parseGatewayPayload(value: string | null | undefined): unknown { const trimmed = normalizeOptionalString(value); if (!trimmed) { return undefined; diff --git a/src/gateway/server-methods.authorization.test.ts b/src/gateway/server-methods.authorization.test.ts index e7993e05a82a..af6dd9986208 100644 --- a/src/gateway/server-methods.authorization.test.ts +++ b/src/gateway/server-methods.authorization.test.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { loadSessionEntry, patchSessionEntryCore, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { applySessionEntryCanonicalReplacements } from "../config/sessions/session-accessor.sqlite-replacement-projection.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; @@ -257,7 +257,7 @@ describe("gateway method authorization", () => { it("rejects a mutation when its authorized session instance is replaced before commit", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:commit-bound-authorization"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-shared", @@ -323,7 +323,7 @@ describe("gateway method authorization", () => { }); await handlerStarted; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-draft-replacement", @@ -370,7 +370,7 @@ describe("sessions.patchMany orchestration", () => { it("preserves request-order outcomes while isolating expected-identity failures", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { for (let index = 0; index < 3; index += 1) { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: `agent:main:batch-${index}` }, { sessionId: `session-${index}`, @@ -438,7 +438,7 @@ describe("sessions.patchMany orchestration", () => { it("projects non-archive patches in request order against prior successes", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { for (let index = 0; index < 2; index += 1) { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: `agent:main:label-${index}` }, { sessionId: `session-label-${index}`, updatedAt: 1 }, ); @@ -474,7 +474,7 @@ describe("sessions.patchMany orchestration", () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKeys = [0, 1].map((index) => `agent:main:label-race-${index}`); for (const [index, sessionKey] of sessionKeys.entries()) { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: `session-label-race-${index}`, updatedAt: 1 }, ); @@ -534,11 +534,11 @@ describe("sessions.patchMany orchestration", () => { it("checks labels against untouched sessions in the store snapshot", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:label-owner" }, { label: "Existing label", sessionId: "session-label-owner", updatedAt: 1 }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:label-target" }, { sessionId: "session-label-target", updatedAt: 1 }, ); @@ -574,12 +574,12 @@ describe("sessions.patchMany orchestration", () => { const canonicalKey = "agent:main:work"; const conflictingAlias = "agent:main:main"; const siblingKeys = ["agent:main:alias-race-before", "agent:main:alias-race-after"]; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: canonicalKey }, { sessionId: "session-alias-race-canonical", updatedAt: 1 }, ); for (const [index, sessionKey] of siblingKeys.entries()) { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: `session-alias-race-sibling-${index}`, updatedAt: 1 }, ); @@ -677,7 +677,7 @@ describe("sessions.patchMany orchestration", () => { } satisfies OpenClawConfig; const canonicalKey = "agent:main:work"; const conflictingAlias = "agent:main:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: canonicalKey }, { sessionId: "session-single-alias-race-canonical", updatedAt: 1 }, ); @@ -755,7 +755,7 @@ describe("sessions.patchMany orchestration", () => { it("isolates a target authorization race from sibling patches", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { for (let index = 0; index < 3; index += 1) { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: `agent:main:race-${index}` }, { sessionId: `session-race-${index}`, updatedAt: 1 }, ); @@ -817,7 +817,7 @@ describe("sessions.patchMany orchestration", () => { it("isolates archive preparation authorization per target and continues in input order", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { for (let index = 0; index < 3; index += 1) { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: `agent:main:archive-auth-${index}` }, { sessionId: `session-archive-auth-${index}`, updatedAt: 1 }, ); @@ -889,7 +889,7 @@ describe("sessions.patchMany orchestration", () => { it("converts an unexpected target exception into an ordered isolated failure", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { for (let index = 0; index < 3; index += 1) { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: `agent:main:throw-${index}` }, { sessionId: `session-throw-${index}`, updatedAt: 1 }, ); diff --git a/src/gateway/server-methods/__mocks__/tools-effective.runtime.ts b/src/gateway/server-methods/__mocks__/tools-effective.runtime.ts index a8e517f9da73..57331d9d9dee 100644 --- a/src/gateway/server-methods/__mocks__/tools-effective.runtime.ts +++ b/src/gateway/server-methods/__mocks__/tools-effective.runtime.ts @@ -51,15 +51,15 @@ export const toolsEffectiveGlobalAgentRuntimeMocks = { ), }; -export const resolveEffectiveToolInventory = - toolsEffectiveGlobalAgentRuntimeMocks.resolveEffectiveToolInventory; -export const resolveEffectiveToolInventoryRuntimeModelContextAsync = - toolsEffectiveGlobalAgentRuntimeMocks.resolveEffectiveToolInventoryRuntimeModelContextAsync; -export const resolveSessionMcpConfigSummary = vi.fn(() => ({ - fingerprint: "mcp:0", - serverNames: [] as string[], -})); export const toolsEffectiveRuntimeMockModule = { + resolveEffectiveToolInventory: + toolsEffectiveGlobalAgentRuntimeMocks.resolveEffectiveToolInventory, + resolveEffectiveToolInventoryRuntimeModelContextAsync: + toolsEffectiveGlobalAgentRuntimeMocks.resolveEffectiveToolInventoryRuntimeModelContextAsync, + resolveSessionMcpConfigSummary: vi.fn(() => ({ + fingerprint: "mcp:0", + serverNames: [] as string[], + })), buildBundleMcpToolsFromCatalog: vi.fn(() => []), applyFinalEffectiveToolPolicy: vi.fn( (params: { bundledTools: unknown[] }) => params.bundledTools, diff --git a/src/gateway/server-methods/agent.base.test-utils.ts b/src/gateway/server-methods/agent.base.test-utils.ts index 799040eaa2c7..5c3625ec83cc 100644 --- a/src/gateway/server-methods/agent.base.test-utils.ts +++ b/src/gateway/server-methods/agent.base.test-utils.ts @@ -11,7 +11,7 @@ import { interruptSessionWorkAdmissions, runExclusiveSessionLifecycleMutation, } from "../../sessions/session-lifecycle-admission.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { normalizeSessionDeliveryState } from "../../utils/delivery-context.shared.js"; import { getAgentTestMocks, @@ -1250,7 +1250,7 @@ describe("gateway agent handler", () => { }); it("does not pass a text-only user-turn recorder for offloaded image agent runs", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-offloaded-image-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-offloaded-image-" }, async (root) => { useTestStateDir(root); mockMainSessionEntry({ sessionId: "existing-session-id", @@ -1465,7 +1465,7 @@ describe("gateway agent handler", () => { sizeBytes: 64, }); - await withTempDir({ prefix: "openclaw-gateway-terminal-main-newer-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-terminal-main-newer-" }, async (root) => { const sessionsDir = `${root}/sessions`; const sessionFile = "terminal-main-session.jsonl"; mocks.loadSessionEntry.mockReturnValue({ @@ -1524,7 +1524,7 @@ describe("gateway agent handler", () => { setDateOnlyFakeClockActive(true); vi.setSystemTime(now); - await withTempDir({ prefix: "openclaw-gateway-terminal-main-fresh-marker-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-terminal-main-fresh-marker-" }, async (root) => { const sessionsDir = `${root}/sessions`; await fs.mkdir(sessionsDir, { recursive: true }); const sessionFile = "terminal-main-session.jsonl"; @@ -1590,7 +1590,7 @@ describe("gateway agent handler", () => { setDateOnlyFakeClockActive(true); vi.setSystemTime(now); - await withTempDir( + await withTestDir( { prefix: "openclaw-gateway-terminal-main-explicit-resume-" }, async (root) => { const sessionsDir = `${root}/sessions`; @@ -1662,7 +1662,7 @@ describe("gateway agent handler", () => { setDateOnlyFakeClockActive(true); vi.setSystemTime(now); - await withTempDir( + await withTestDir( { prefix: `openclaw-gateway-terminal-main-${runKind}-reuse-` }, async (root) => { const sessionsDir = `${root}/sessions`; diff --git a/src/gateway/server-methods/agent.media-and-routing.test-utils.ts b/src/gateway/server-methods/agent.media-and-routing.test-utils.ts index 9a6fda961606..84a6d4cb5a5b 100644 --- a/src/gateway/server-methods/agent.media-and-routing.test-utils.ts +++ b/src/gateway/server-methods/agent.media-and-routing.test-utils.ts @@ -12,7 +12,7 @@ import { resetGatewayWorkAdmission, waitForActiveGatewayRootWork, } from "../../process/gateway-work-admission.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { registerSubagentCompletionToolHandoff } from "../subagent-completion-tool-handoff.js"; import { getAgentTestMocks, @@ -57,7 +57,7 @@ describe("gateway agent handler", () => { setDateOnlyFakeClockActive(true); vi.setSystemTime(now); - await withTempDir({ prefix: "openclaw-gateway-failed-default-session-file-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-failed-default-session-file-" }, async (root) => { const sessionsDir = `${root}/sessions`; await fs.mkdir(sessionsDir, { recursive: true }); mocks.readTranscriptStatsSync.mockReturnValue({ eventCount: 1, maxSeq: 1, sizeBytes: 32 }); @@ -126,7 +126,7 @@ describe("gateway agent handler", () => { setDateOnlyFakeClockActive(true); vi.setSystemTime(now); - await withTempDir({ prefix: "openclaw-gateway-stale-failed-session-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-stale-failed-session-" }, async (root) => { const sessionsDir = `${root}/sessions`; const storePath = `${sessionsDir}/sessions.json`; await fs.mkdir(sessionsDir, { recursive: true }); @@ -202,7 +202,7 @@ describe("gateway agent handler", () => { setDateOnlyFakeClockActive(true); vi.setSystemTime(now); - await withTempDir({ prefix: "openclaw-gateway-failed-session-file-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-failed-session-file-" }, async (root) => { const sessionsDir = `${root}/sessions`; await fs.mkdir(sessionsDir, { recursive: true }); mocks.readTranscriptStatsSync.mockReturnValue({ eventCount: 1, maxSeq: 1, sizeBytes: 32 }); @@ -926,7 +926,7 @@ describe("gateway agent handler", () => { it("recovers terminal failed agent API sessions with SQLite transcript rows", async () => { const sessionId = "failed-agent-session"; - await withTempDir({ prefix: "openclaw-gateway-terminal-recovery-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-terminal-recovery-" }, async (root) => { const sessionsDir = `${root}/sessions`; await fs.mkdir(sessionsDir, { recursive: true }); mocks.readTranscriptStatsSync.mockReturnValue({ eventCount: 1, maxSeq: 1, sizeBytes: 32 }); diff --git a/src/gateway/server-methods/agent.reset-and-identity.test-utils.ts b/src/gateway/server-methods/agent.reset-and-identity.test-utils.ts index b2a92d09ed02..2fd079b4f907 100644 --- a/src/gateway/server-methods/agent.reset-and-identity.test-utils.ts +++ b/src/gateway/server-methods/agent.reset-and-identity.test-utils.ts @@ -4,7 +4,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import { AVATAR_MAX_BYTES } from "../../shared/avatar-policy.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { normalizeSessionDeliveryState } from "../../utils/delivery-context.shared.js"; import { REAL_PNG, @@ -889,7 +889,7 @@ describe("gateway agent handler", () => { }); it("returns workspace-relative avatars as data URLs in agent.identity.get", async () => { - await withTempDir({ prefix: "openclaw-agent-avatar-" }, async (workspace) => { + await withTestDir({ prefix: "openclaw-agent-avatar-" }, async (workspace) => { await fs.mkdir(path.join(workspace, "avatars"), { recursive: true }); await fs.writeFile(path.join(workspace, "avatars", "main.png"), "avatar", "utf8"); mocks.loadConfigReturn = { @@ -945,7 +945,7 @@ describe("gateway agent handler", () => { }); it("replaces rejected local avatar paths with the default instead of a protected route", async () => { - await withTempDir({ prefix: "openclaw-agent-avatar-missing-" }, async (workspace) => { + await withTestDir({ prefix: "openclaw-agent-avatar-missing-" }, async (workspace) => { mocks.loadConfigReturn = { agents: { defaults: { workspace }, @@ -968,7 +968,7 @@ describe("gateway agent handler", () => { }); it("inlines a workspace-local avatar in agent.identity.get (#97602)", async () => { - await withTempDir({ prefix: "openclaw-agent-identity-avatar-" }, async (workspace) => { + await withTestDir({ prefix: "openclaw-agent-identity-avatar-" }, async (workspace) => { await fs.writeFile(`${workspace}/avatar.png`, REAL_PNG); mocks.loadConfigReturn = { agents: { @@ -994,7 +994,7 @@ describe("gateway agent handler", () => { }); it("reports a hardlinked avatar as unreadable in agent.identity.get", async () => { - await withTempDir({ prefix: "openclaw-agent-identity-hardlink-" }, async (workspace) => { + await withTestDir({ prefix: "openclaw-agent-identity-hardlink-" }, async (workspace) => { await fs.writeFile(`${workspace}/original.png`, REAL_PNG); await fs.link(`${workspace}/original.png`, `${workspace}/avatar.png`); mocks.loadConfigReturn = { @@ -1021,7 +1021,7 @@ describe("gateway agent handler", () => { }); it("bounds an agent.identity.get avatar that grows after its descriptor is pinned", async () => { - await withTempDir({ prefix: "openclaw-agent-identity-growth-" }, async (workspace) => { + await withTestDir({ prefix: "openclaw-agent-identity-growth-" }, async (workspace) => { const avatarPath = `${workspace}/avatar.png`; await fs.writeFile(avatarPath, REAL_PNG); mocks.loadConfigReturn = { @@ -1058,7 +1058,7 @@ describe("gateway agent handler", () => { }); it("keeps configured emoji precedence free of file metadata in agent.identity.get", async () => { - await withTempDir({ prefix: "openclaw-agent-identity-emoji-" }, async (workspace) => { + await withTestDir({ prefix: "openclaw-agent-identity-emoji-" }, async (workspace) => { await fs.writeFile(`${workspace}/identity.png`, REAL_PNG); await fs.writeFile(`${workspace}/IDENTITY.md`, "- Avatar: identity.png\n"); mocks.loadConfigReturn = { diff --git a/src/gateway/server-methods/agent.sessions-and-models.test-utils.ts b/src/gateway/server-methods/agent.sessions-and-models.test-utils.ts index 9184bcb388fc..0950bd9700ef 100644 --- a/src/gateway/server-methods/agent.sessions-and-models.test-utils.ts +++ b/src/gateway/server-methods/agent.sessions-and-models.test-utils.ts @@ -17,7 +17,7 @@ import { markTaskTerminalById, } from "../../tasks/task-registry.js"; import { setDetachedTaskLifecycleRuntime } from "../../tasks/task-runtime.test-helpers.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { dispatchAgentRunFromGateway } from "../agent-turn/agent-run-dispatch.js"; import { createAgentTurnIo } from "../agent-turn/io.js"; import { registerPluginSubagentRunFromGateway } from "./agent-task-tracking.js"; @@ -156,7 +156,7 @@ describe("gateway agent handler", () => { }); it("terminalizes successful async gateway agent runs in the shared task registry", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-task-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-task-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); primeMainAgentRun(); @@ -187,7 +187,7 @@ describe("gateway agent handler", () => { { identity: "astral suffix", runId: "x".repeat(10) + "😀abc" }, { identity: "astral prefix and suffix", runId: "abc😀" + "x".repeat(10) + "😀xyz" }, ])("tracks plugin subagent $identity runs through the registry", async ({ runId }) => { - await withTempDir({ prefix: "openclaw-gateway-plugin-subagent-task-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-plugin-subagent-task-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); resetSubagentRegistryForTests({ persist: false }); @@ -328,7 +328,7 @@ describe("gateway agent handler", () => { }); it("registers host-owned requester lineage for plugin subagent completion", async () => { - await withTempDir({ prefix: "openclaw-gateway-plugin-subagent-requester-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-plugin-subagent-requester-" }, async (root) => { useTestStateDir(root); resetSubagentRegistryForTests({ persist: false }); const childSessionKey = "agent:work:subagent:plugin-completion"; @@ -372,7 +372,7 @@ describe("gateway agent handler", () => { }); it("registers normally when a follow-up to a paused session names its own requester", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-gateway-plugin-subagent-own-requester-" }, async (root) => { useTestStateDir(root); @@ -421,7 +421,7 @@ describe("gateway agent handler", () => { }); it("still adopts the paused owner for a default follow-up after a requester-bound sibling", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-gateway-plugin-subagent-mixed-delivery-" }, async (root) => { useTestStateDir(root); @@ -483,7 +483,7 @@ describe("gateway agent handler", () => { }); it("rejects plugin SDK subagent registration and adoption when persistence fails", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-gateway-plugin-subagent-registry-fail-" }, async (root) => { useTestStateDir(root); @@ -642,7 +642,7 @@ describe("gateway agent handler", () => { }); it("terminalizes failed async gateway agent runs in the shared task registry", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-task-error-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-task-error-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); primeMainAgentRun(); @@ -669,7 +669,7 @@ describe("gateway agent handler", () => { }); it("preserves aborted async gateway agent runs as cancelled", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-task-aborted-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-task-aborted-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); primeMainAgentRun(); @@ -969,7 +969,7 @@ describe("gateway agent handler", () => { }); it("classifies RPC-aborted async gateway agent rejections as cancelled", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-task-abort-error-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-task-abort-error-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); primeMainAgentRun(); @@ -1015,7 +1015,7 @@ describe("gateway agent handler", () => { }); it("classifies an unsignaled AbortError task as cancelled without changing the error wire", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-task-plain-abort-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-task-plain-abort-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); primeMainAgentRun(); @@ -1052,7 +1052,7 @@ describe("gateway agent handler", () => { }); it("preserves restart ownership for aborted async gateway agent rejections", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-task-restart-abort-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-task-restart-abort-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); primeMainAgentRun(); @@ -1093,7 +1093,7 @@ describe("gateway agent handler", () => { }); it("classifies timeout async gateway agent rejections as timed out", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-task-timeout-error-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-task-timeout-error-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); primeMainAgentRun(); @@ -1139,7 +1139,7 @@ describe("gateway agent handler", () => { }); it("classifies wrapped rejections after gateway timeout as timed out", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-gateway-agent-task-wrapped-timeout-error-" }, async (root) => { useTestStateDir(root); @@ -1190,7 +1190,7 @@ describe("gateway agent handler", () => { }); it("does not hide provider timeout async gateway agent rejections", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-task-provider-timeout-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-task-provider-timeout-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); primeMainAgentRun(); @@ -1370,7 +1370,7 @@ describe("gateway agent handler", () => { }); it("does not overwrite operator-cancelled async gateway agent tasks after late completion", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-task-cancelled-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-task-cancelled-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); primeMainAgentRun(); @@ -2450,7 +2450,7 @@ describe("gateway agent handler", () => { }); it("dispatches async gateway agent task creation through the detached task runtime seam", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-seam-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-seam-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); primeMainAgentRun(); @@ -2548,7 +2548,7 @@ describe("gateway agent handler", () => { }; it("suppresses the gateway CLI task row for confirmed ACP manual-spawn child turns", async () => { - await withTempDir({ prefix: "openclaw-gateway-acp-suppress-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-acp-suppress-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); const childSessionKey = "agent:main:acp:child-confirmed"; @@ -2573,7 +2573,7 @@ describe("gateway agent handler", () => { }); it("keeps a host-owned subagent run to its pre-registered task row", async () => { - await withTempDir({ prefix: "openclaw-gateway-subagent-owner-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-subagent-owner-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); const childSessionKey = "agent:main:subagent:owned"; @@ -2605,7 +2605,7 @@ describe("gateway agent handler", () => { }); it("keeps CLI tracking when a non-backend operator-write caller sets acpTurnSource", async () => { - await withTempDir({ prefix: "openclaw-gateway-acp-operator-write-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-acp-operator-write-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); const childSessionKey = "agent:main:acp:child-operator-write"; @@ -2645,7 +2645,7 @@ describe("gateway agent handler", () => { }); it("keeps CLI tracking for ACP-shaped manual-spawn turns without persisted ACP metadata", async () => { - await withTempDir({ prefix: "openclaw-gateway-acp-no-meta-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-acp-no-meta-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); const childSessionKey = "agent:main:acp:child-missing-meta"; @@ -2680,7 +2680,7 @@ describe("gateway agent handler", () => { }); it("keeps dispatch and CLI tracking when ACP metadata read fails", async () => { - await withTempDir({ prefix: "openclaw-gateway-acp-meta-throw-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-acp-meta-throw-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); const childSessionKey = "agent:main:acp:child-meta-throw"; @@ -2731,7 +2731,7 @@ describe("gateway agent handler", () => { }); it("keeps CLI tracking for ACP-shaped turns that are not manual spawns", async () => { - await withTempDir({ prefix: "openclaw-gateway-acp-not-manual-spawn-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-acp-not-manual-spawn-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); const childSessionKey = "agent:main:acp:child-not-spawn"; @@ -2761,7 +2761,7 @@ describe("gateway agent handler", () => { }); it("does not affect plugin-subagent tracking for confirmed ACP conditions", async () => { - await withTempDir({ prefix: "openclaw-gateway-acp-plugin-subagent-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-acp-plugin-subagent-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); resetSubagentRegistryForTests({ persist: false }); @@ -2814,7 +2814,7 @@ describe("gateway agent handler", () => { }); it("logs a swallowed finalize error without blocking the background run", async () => { - await withTempDir({ prefix: "openclaw-gateway-agent-finalize-throw-" }, async (root) => { + await withTestDir({ prefix: "openclaw-gateway-agent-finalize-throw-" }, async (root) => { useTestStateDir(root); resetAgentTaskRegistryForTests(); primeMainAgentRun(); diff --git a/src/gateway/server-methods/agent.test-harness.ts b/src/gateway/server-methods/agent.test-harness.ts index 73dd98b25360..08fd0caabc96 100644 --- a/src/gateway/server-methods/agent.test-harness.ts +++ b/src/gateway/server-methods/agent.test-harness.ts @@ -289,13 +289,15 @@ vi.mock("../../channels/message/runtime.js", async () => { ); return { ...actual, - sendDurableMessageBatch: (...args: Parameters) => { + sendDurableMessageBatchCore: ( + ...args: Parameters + ) => { const override = mocks.sendDurableMessageBatch.getMockImplementation(); return override ? (mocks.sendDurableMessageBatch(...args) as ReturnType< - typeof actual.sendDurableMessageBatch + typeof actual.sendDurableMessageBatchCore >) - : actual.sendDurableMessageBatch(...args); + : actual.sendDurableMessageBatchCore(...args); }, }; }); diff --git a/src/gateway/server-methods/chat.directive-tags.test.ts b/src/gateway/server-methods/chat.directive-tags.test.ts index dd6ac7f83b7e..654319ddee70 100644 --- a/src/gateway/server-methods/chat.directive-tags.test.ts +++ b/src/gateway/server-methods/chat.directive-tags.test.ts @@ -37,7 +37,7 @@ import { replaceSessionEntry, type SessionAccessScope, type SessionTranscriptReadScope, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import { waitForSessionTranscriptIndexReconcile } from "../../config/sessions/session-transcript-reconcile.js"; import { resolveMirroredTranscriptText } from "../../config/sessions/transcript-mirror.js"; @@ -740,7 +740,7 @@ function sessionEntryScope(): SessionAccessScope { } async function seedSqliteSessionEntry(entry: Record = {}): Promise { - await upsertSessionEntry(sessionEntryScope(), { + await upsertSessionEntryCore(sessionEntryScope(), { sessionId: mockState.sessionId, ...entry, }); diff --git a/src/gateway/server-methods/device-management-security.ts b/src/gateway/server-methods/device-management-security.ts index f44ecb69b64c..45426093f6a2 100644 --- a/src/gateway/server-methods/device-management-security.ts +++ b/src/gateway/server-methods/device-management-security.ts @@ -1,4 +1,4 @@ -import { sha256HexPrefix } from "../../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../../infra/crypto-digest.js"; import { emitTrustedSecurityEvent, type DiagnosticSecurityEventInput, @@ -12,7 +12,7 @@ function hashDeviceSecurityId(value: string | undefined): string | undefined { if (!normalized) { return undefined; } - return `sha256:${sha256HexPrefix(normalized, 12)}`; + return `sha256:${sha256HexPrefixCore(normalized, 12)}`; } export function emitDeviceManagementSecurityEvent(params: { diff --git a/src/gateway/server-methods/exec-approvals.ts b/src/gateway/server-methods/exec-approvals.ts index dcfbdf45a033..cd99ebf19efc 100644 --- a/src/gateway/server-methods/exec-approvals.ts +++ b/src/gateway/server-methods/exec-approvals.ts @@ -28,7 +28,7 @@ import { resolveBaseHashParam } from "./base-hash.js"; import { respondUnavailableOnNodeInvokeErrorWithProvenance, respondUnavailableOnThrow, - safeParseJson, + parseGatewayPayload, } from "./nodes.helpers.js"; import type { GatewayRequestContext, GatewayRequestHandlers, RespondFn } from "./types.js"; import { assertValidParams, type Validator } from "./validation.js"; @@ -259,7 +259,7 @@ export const execApprovalsHandlers: GatewayRequestHandlers = { isMacAppNode(nodeSession) ? { includeResolvedDefaults: true } : {}, // Node invocations can return structured payloads or JSON strings // depending on the transport; normalize before echoing the RPC response. - readPayload: (res) => (res.payloadJSON ? safeParseJson(res.payloadJSON) : res.payload), + readPayload: (res) => (res.payloadJSON ? parseGatewayPayload(res.payloadJSON) : res.payload), validatePayload: validateExecApprovalsNodeSnapshot, }); }, @@ -277,7 +277,7 @@ export const execApprovalsHandlers: GatewayRequestHandlers = { "native" in parsedParams ? { ...parsedParams.native, baseHash: parsedParams.baseHash } : { file: parsedParams.file, baseHash: parsedParams.baseHash }, - readPayload: (res) => (res.payloadJSON ? safeParseJson(res.payloadJSON) : res.payload), + readPayload: (res) => (res.payloadJSON ? parseGatewayPayload(res.payloadJSON) : res.payload), }); }, }; diff --git a/src/gateway/server-methods/nodes.helpers.ts b/src/gateway/server-methods/nodes.helpers.ts index 2095bd5135e6..fd16251a2ebc 100644 --- a/src/gateway/server-methods/nodes.helpers.ts +++ b/src/gateway/server-methods/nodes.helpers.ts @@ -9,7 +9,7 @@ import { formatValidationErrors, type ValidationError, } from "../../../packages/gateway-protocol/src/validation-errors.js"; -export { safeParseJson } from "../server-json.js"; +export { parseGatewayPayload } from "../server-json.js"; import { formatForLog } from "../ws-log.js"; import type { RespondFn } from "./types.js"; diff --git a/src/gateway/server-methods/nodes.invoke.ts b/src/gateway/server-methods/nodes.invoke.ts index 431215b8e0d0..d2de3dba9d4c 100644 --- a/src/gateway/server-methods/nodes.invoke.ts +++ b/src/gateway/server-methods/nodes.invoke.ts @@ -30,7 +30,7 @@ import { respondInvalidParams, respondUnavailableOnNodeInvokeErrorWithProvenance, respondUnavailableOnThrow, - safeParseJson, + parseGatewayPayload, } from "./nodes.helpers.js"; import { isForwardedNodeInvokeApprovalAuthorityActive, @@ -473,7 +473,7 @@ export const nodeInvokeHandlers: GatewayRequestHandlers = { return; } const payload = policyResult.payloadJSON - ? safeParseJson(policyResult.payloadJSON) + ? parseGatewayPayload(policyResult.payloadJSON) : policyResult.payload; emitTalkPttNodeEvent({ context, @@ -660,7 +660,7 @@ export const nodeInvokeHandlers: GatewayRequestHandlers = { } return; } - const payload = res.payloadJSON ? safeParseJson(res.payloadJSON) : res.payload; + const payload = res.payloadJSON ? parseGatewayPayload(res.payloadJSON) : res.payload; emitTalkPttNodeEvent({ context, nodeId, diff --git a/src/gateway/server-methods/send.ts b/src/gateway/server-methods/send.ts index 01d194986054..5c3a09965770 100644 --- a/src/gateway/server-methods/send.ts +++ b/src/gateway/server-methods/send.ts @@ -13,7 +13,7 @@ import { validateSendParams, } from "../../../packages/gateway-protocol/src/index.js"; import { resolveSessionAgentId } from "../../agents/agent-scope.js"; -import { sendDurableMessageBatch } from "../../channels/message/runtime.js"; +import { sendDurableMessageBatchCore } from "../../channels/message/runtime.js"; import type { ConversationReadInvocationOrigin } from "../../channels/plugins/conversation-read-origin.js"; import { resolveChannelDefaultAccountId } from "../../channels/plugins/helpers.js"; import { dispatchChannelMessageAction } from "../../channels/plugins/message-action-dispatch.js"; @@ -1337,7 +1337,7 @@ export const sendHandlers: GatewayRequestHandlers = { if (!authorize()) { return createGatewayInflightAuthorityFailure({ context, dedupeKey, channel }); } - const send = await sendDurableMessageBatch({ + const send = await sendDurableMessageBatchCore({ cfg, channel, to: deliveryTarget, diff --git a/src/gateway/server-methods/session-catalog.test.ts b/src/gateway/server-methods/session-catalog.test.ts index 7b5ef008f6e1..fdaaf28d9704 100644 --- a/src/gateway/server-methods/session-catalog.test.ts +++ b/src/gateway/server-methods/session-catalog.test.ts @@ -58,7 +58,7 @@ vi.mock("../../config/sessions/session-accessor.js", async (importOriginal) => { return { ...actual, listSessionEntriesReadOnly: hoisted.listSessionEntriesReadOnly }; }); -const { resolveSessionCatalogCreateTarget, sessionCatalogHandlers } = +const { resolveRegisteredCatalogCreateTarget, sessionCatalogHandlers } = await import("./session-catalog.js"); function provider( @@ -781,7 +781,7 @@ describe("session catalog Gateway methods", () => { }, ]; - expect(resolveSessionCatalogCreateTarget("claude", "research", {})).toEqual({ + expect(resolveRegisteredCatalogCreateTarget("claude", "research", {})).toEqual({ ok: true, target: { model: "anthropic/claude-opus-4-8", @@ -789,7 +789,7 @@ describe("session catalog Gateway methods", () => { pluginOwnerId: "anthropic", }, }); - expect(resolveSessionCatalogCreateTarget("missing", "research", {})).toEqual({ + expect(resolveRegisteredCatalogCreateTarget("missing", "research", {})).toEqual({ ok: false, message: "unknown session catalog: missing", unknownCatalog: true, diff --git a/src/gateway/server-methods/session-catalog.ts b/src/gateway/server-methods/session-catalog.ts index baa832523760..ed761f7aefd4 100644 --- a/src/gateway/server-methods/session-catalog.ts +++ b/src/gateway/server-methods/session-catalog.ts @@ -216,7 +216,7 @@ function resolveProviderCreateTarget( } /** Resolves a catalog-owned create target at the start of sessions.create. */ -export function resolveSessionCatalogCreateTarget( +export function resolveRegisteredCatalogCreateTarget( catalogId: string, agentId: string, config: OpenClawConfig, @@ -596,7 +596,7 @@ export const sessionCatalogHandlers: GatewayRequestHandlers = { "sessions.catalog.startTerminal": catalogStartHandler( resolveSessionCatalogProvider, - resolveSessionCatalogCreateTarget, + resolveRegisteredCatalogCreateTarget, ), "sessions.catalog.archive": async ({ params, respond }) => { diff --git a/src/gateway/server-methods/sessions-create.ts b/src/gateway/server-methods/sessions-create.ts index baaa0f92748e..953be7a596f8 100644 --- a/src/gateway/server-methods/sessions-create.ts +++ b/src/gateway/server-methods/sessions-create.ts @@ -40,7 +40,7 @@ import { loadSessionEntryReadOnly, resolveGatewaySessionStoreTarget } from "../s import { resolveSessionPatchModelSelection } from "../sessions-patch.js"; import { createAgentRuntimeAuthorityGuard } from "./agent-runtime-authority.js"; import { chatHandlers } from "./chat.js"; -import { resolveSessionCatalogCreateTarget } from "./session-catalog.js"; +import { resolveRegisteredCatalogCreateTarget } from "./session-catalog.js"; import { emitSessionsChanged } from "./session-change-event.js"; import { captureCreatedSessionDiffBaseline } from "./session-create-diff-baseline.js"; import { @@ -95,7 +95,7 @@ export const sessionCreateHandlers: GatewayRequestHandlers = { } const catalogTarget = catalogId && catalogAgentId - ? resolveSessionCatalogCreateTarget(catalogId, catalogAgentId, cfg) + ? resolveRegisteredCatalogCreateTarget(catalogId, catalogAgentId, cfg) : undefined; if (catalogTarget && !catalogTarget.ok) { respond( diff --git a/src/gateway/server-methods/sessions-mutations.archive-attribution.test.ts b/src/gateway/server-methods/sessions-mutations.archive-attribution.test.ts index c19120cc758a..bac94a0d1ca8 100644 --- a/src/gateway/server-methods/sessions-mutations.archive-attribution.test.ts +++ b/src/gateway/server-methods/sessions-mutations.archive-attribution.test.ts @@ -4,7 +4,7 @@ import { listSessionEntriesCore, loadSessionEntry, loadTranscriptEvents, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import { addSessionMember, @@ -92,7 +92,7 @@ describe("sessions.patch archive attribution", () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:archive-attribution"; const sessionId = "session-archive-attribution"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId, updatedAt: 1, pinnedAt: 2 }, ); @@ -145,7 +145,7 @@ describe("sessions.patch archive attribution", () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:solo-archive"; const sessionId = "session-solo-archive"; - await upsertSessionEntry({ agentId: "main", sessionKey }, { sessionId, updatedAt: 1 }); + await upsertSessionEntryCore({ agentId: "main", sessionKey }, { sessionId, updatedAt: 1 }); await patchSession({ key: sessionKey, archived: true }, client()); @@ -160,14 +160,14 @@ describe("sessions.patch archive attribution", () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const canonicalKey = "agent:main:alias-happy-archive"; const aliasKey = "alias-happy-archive"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: canonicalKey }, { sessionId: "session-canonical-happy-archive", updatedAt: 1, }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: aliasKey }, { sessionId: "session-alias-happy-archive", updatedAt: 2 }, ); @@ -187,7 +187,7 @@ describe("sessions.patch archive attribution", () => { const canonicalKey = "agent:main:alias-archive"; const aliasKey = "alias-archive"; const memberId = "profile-member"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: canonicalKey }, { sessionId: "session-canonical-before-archive", @@ -195,7 +195,7 @@ describe("sessions.patch archive attribution", () => { label: "canonical", }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: aliasKey }, { sessionId: "session-alias-before-archive", diff --git a/src/gateway/server-methods/sessions-mutations.perf.test.ts b/src/gateway/server-methods/sessions-mutations.perf.test.ts index 5c7b03cb824d..ea8268e1c367 100644 --- a/src/gateway/server-methods/sessions-mutations.perf.test.ts +++ b/src/gateway/server-methods/sessions-mutations.perf.test.ts @@ -5,7 +5,7 @@ import { appendTranscriptMessage, loadSessionEntry, loadTranscriptEvents, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import type { CronJob } from "../../cron/types.js"; import { @@ -57,12 +57,12 @@ function humanClient(): GatewayClient { test("single non-label sessions.patch avoids a whole-store projection", async () => { await withOpenClawTestState({ scenario: "minimal" }, async (state) => { const targetKey = "agent:main:single-patch-target"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: targetKey }, { sessionId: "session-single-patch-target", updatedAt: 1 }, ); for (let index = 0; index < 20; index += 1) { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: `agent:main:single-patch-unrelated-${index}` }, { sessionId: `session-single-patch-unrelated-${index}`, updatedAt: index + 2 }, ); @@ -121,7 +121,7 @@ test("sessions.patchMany archives 30 human sessions without transcript hydration const transcriptTails = new Map(); for (const [index, target] of targets.entries()) { const sessionId = `session-archive-perf-${index}`; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: target.key }, { sessionId, updatedAt: index + 1 }, ); diff --git a/src/gateway/server-methods/sessions-mutations.sticky-model.test.ts b/src/gateway/server-methods/sessions-mutations.sticky-model.test.ts index 88d46cf0657d..06af380a7913 100644 --- a/src/gateway/server-methods/sessions-mutations.sticky-model.test.ts +++ b/src/gateway/server-methods/sessions-mutations.sticky-model.test.ts @@ -1,5 +1,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { loadSessionEntry, upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { + loadSessionEntry, + upsertSessionEntryCore, +} from "../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { closeOpenClawAgentDatabasesForTest } from "../../state/openclaw-agent-db.js"; import { withOpenClawTestState } from "../../test-utils/openclaw-test-state.js"; @@ -106,7 +109,7 @@ describe("sessions.patch sticky model persistence", () => { "persists an accepted model for the resolved $agentId agent", async ({ agentId, sessionKey }) => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId, sessionKey }, { sessionId: `session-${agentId}`, updatedAt: 1 }, ); @@ -122,7 +125,7 @@ describe("sessions.patch sticky model persistence", () => { it("keeps a write-scoped model switch session-only without persisting the configured default", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:dm:non-admin"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-non-admin", updatedAt: 1 }, ); @@ -143,7 +146,7 @@ describe("sessions.patch sticky model persistence", () => { it("returns session success and warns when the sticky config write fails", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:dm:write-failure"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-write-failure", updatedAt: 1 }, ); @@ -171,7 +174,7 @@ describe("sessions.patch sticky model persistence", () => { ])("does not persist when model is $name", async ({ patch }) => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:dm:no-sticky"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-main", diff --git a/src/gateway/server-methods/sessions-read-cache.test.ts b/src/gateway/server-methods/sessions-read-cache.test.ts index 2f0b5239cf22..f79c0f17dacf 100644 --- a/src/gateway/server-methods/sessions-read-cache.test.ts +++ b/src/gateway/server-methods/sessions-read-cache.test.ts @@ -9,7 +9,7 @@ import { loadSessionEntry, persistSessionTranscriptTurn, replaceSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import { waitForSessionTranscriptIndexReconcile } from "../../config/sessions/session-transcript-reconcile.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; @@ -108,7 +108,7 @@ async function seedSessions(): Promise { const config: OpenClawConfig = { agents: { list: [{ id: "main", default: true }, { id: "work" }] }, }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:active" }, { sessionId: "main-active", @@ -117,7 +117,7 @@ async function seedSessions(): Promise { visibility: "shared", }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:draft" }, { sessionId: "main-draft", @@ -126,7 +126,7 @@ async function seedSessions(): Promise { visibility: "draft", }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:archived" }, { sessionId: "main-archived", @@ -136,7 +136,7 @@ async function seedSessions(): Promise { visibility: "shared", }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "work", sessionKey: "agent:work:active" }, { sessionId: "work-active", @@ -299,7 +299,7 @@ describe("sessions.list single-flight", () => { const request = { archived: "all" as const, limit: 100 }; const first = await listSessions({ client, context, request }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:external" }, { sessionId: "main-external", @@ -380,7 +380,7 @@ describe("sessions.list single-flight", () => { const { clock, config } = await seedSessionsWithActivityTimes(); const parentSessionKey = "agent:main:active"; const childSessionKey = "agent:main:zzz-child"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: childSessionKey }, { sessionId: "completed-hidden-child", @@ -524,7 +524,7 @@ describe("sessions.list single-flight", () => { const { clock, config } = await seedSessionsWithActivityTimes(); const parentSessionKey = "agent:main:active"; const childSessionKey = "agent:main:child"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: childSessionKey }, { sessionId: "completed-child", @@ -674,7 +674,7 @@ describe("sessions.list single-flight", () => { ["second", 600], ["first", 700], ] as const) { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: `agent:main:page-${name}` }, { sessionId: `page-${name}`, @@ -697,7 +697,7 @@ describe("sessions.list single-flight", () => { request: { agentId: "main", archived: "all", limit: 1 }, }); await vi.waitFor(() => expect(loader.rowCalls).toHaveBeenCalledOnce()); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:page-first" }, { visibility: "draft", updatedAt: 800 }, ); @@ -755,7 +755,7 @@ describe("sessions.list single-flight", () => { const beforeMutation = listSessions({ client, context, request }); await vi.waitFor(() => expect(loader.rowCalls).toHaveBeenCalledTimes(1)); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:created-mid-list" }, { sessionId: "created-mid-list", updatedAt: 500, visibility: "shared" }, ); diff --git a/src/gateway/server-methods/sessions-read.ts b/src/gateway/server-methods/sessions-read.ts index 99a1c8f4da08..f78d3d76dbd9 100644 --- a/src/gateway/server-methods/sessions-read.ts +++ b/src/gateway/server-methods/sessions-read.ts @@ -17,7 +17,7 @@ import { isPerAgentSessionStoreConfig, listSessionMembershipKeys, resolveExistingAgentSessionStoreTargetsSync, - resolveStorePath, + resolveSessionStorePathCore, runSessionsCleanup, serializeSessionCleanupResult, type SessionEntry, @@ -162,7 +162,7 @@ export const sessionReadHandlers: GatewayRequestHandlers = { restrictIncognito && configured && scopedSessionKeys === undefined ? listSessionEntriesReadOnly({ agentId, - storePath: resolveStorePath(cfg.session?.store, { agentId }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId }), }) .map((entry) => entry.sessionKey) .filter(canSearchSessionKey) diff --git a/src/gateway/server-methods/sessions-rewind.test.ts b/src/gateway/server-methods/sessions-rewind.test.ts index df0a56954ab6..a60492c035b3 100644 --- a/src/gateway/server-methods/sessions-rewind.test.ts +++ b/src/gateway/server-methods/sessions-rewind.test.ts @@ -35,7 +35,7 @@ import { appendTranscriptMessage, listSessionEntriesCore, loadSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import { createEmptyPluginRegistry } from "../../plugins/registry-empty.js"; import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../../plugins/runtime.js"; @@ -68,7 +68,7 @@ beforeEach(async () => { }); setActivePluginRegistry(createEmptyPluginRegistry()); vi.stubEnv("OPENCLAW_STATE_DIR", tempDirs.make("openclaw-rewind-handler-")); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: sourceSessionId, diff --git a/src/gateway/server-methods/sessions-sharing.test.ts b/src/gateway/server-methods/sessions-sharing.test.ts index 30d3ad3cb5e1..edcc1e3a7b9f 100644 --- a/src/gateway/server-methods/sessions-sharing.test.ts +++ b/src/gateway/server-methods/sessions-sharing.test.ts @@ -4,7 +4,7 @@ import { loadSessionEntry, loadTranscriptEvents, patchSessionEntryCore, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import { addSessionMember, @@ -124,7 +124,7 @@ describe("session sharing handlers", () => { it("keeps hidden incognito rows from changing non-owner list path metadata", async () => { await withOpenClawTestState({ scenario: "minimal" }, async (state) => { const incognitoKey = "agent:main:dashboard:incognito-private"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:main" }, { sessionId: "session-main", updatedAt: 1 }, ); @@ -148,7 +148,7 @@ describe("session sharing handlers", () => { }; const before = await listFor(viewer); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: incognitoKey, @@ -178,7 +178,7 @@ describe("session sharing handlers", () => { it("rejects a visibility mutation when the queued session instance changed", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:stale-sharing-mutation"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-authorized", @@ -220,7 +220,7 @@ describe("session sharing handlers", () => { const sessionKey = "agent:main:main"; const owner = { id: "owner@example.com", label: "Owner" }; const outsider = identifiedClient("outsider"); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-main", @@ -264,7 +264,7 @@ describe("session sharing handlers", () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:shared-member"; const memberIdentity = { id: "member@example.com", label: "Member" }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-shared-member", @@ -303,7 +303,7 @@ describe("session sharing handlers", () => { it("drops a session flipped to draft during the list await from a non-owner", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:mid-await-draft"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-mid-await", @@ -385,7 +385,7 @@ describe("session sharing handlers", () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const hiddenKey = "agent:main:mid-await-paged-draft"; const visibleKey = "agent:main:mid-await-paged-visible"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: hiddenKey }, { sessionId: "session-mid-await-paged-draft", @@ -394,7 +394,7 @@ describe("session sharing handlers", () => { visibility: "shared", }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: visibleKey }, { sessionId: "session-mid-await-paged-visible", @@ -444,7 +444,7 @@ describe("session sharing handlers", () => { if (!selectable) { throw new Error("expected member profile in picker identities"); } - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-profile-member", @@ -480,11 +480,11 @@ describe("session sharing handlers", () => { it("authorizes board tickets against their signed agent-relative session", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "global" }, { sessionId: "session-main-global", updatedAt: 1, visibility: "shared" }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "work", sessionKey: "global" }, { sessionId: "session-work-global", @@ -538,7 +538,7 @@ describe("session sharing handlers", () => { const owner = { id: "owner@example.com", label: "Owner" }; const memberIdentity = { id: "member@example.com", label: "Member" }; const memberClient = identifiedClient(memberIdentity.id, memberIdentity.label); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-member-transition", @@ -613,7 +613,7 @@ describe("session sharing handlers", () => { it("persists visibility and membership changes as transcript system notes", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-main", updatedAt: 1 }, ); @@ -668,7 +668,7 @@ describe("session sharing handlers", () => { ); const restrictedKey = "agent:main:restricted"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: restrictedKey }, { sessionId: "session-restricted", diff --git a/src/gateway/server-methods/sessions-suggestions.test.ts b/src/gateway/server-methods/sessions-suggestions.test.ts index a2da2130d895..e51a050b0853 100644 --- a/src/gateway/server-methods/sessions-suggestions.test.ts +++ b/src/gateway/server-methods/sessions-suggestions.test.ts @@ -4,7 +4,7 @@ import { clearActiveEmbeddedRun, setActiveEmbeddedRun, } from "../../agents/embedded-agent-runner/runs.js"; -import { upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../../config/sessions/session-accessor.js"; import { addSessionMember } from "../../config/sessions/session-sharing-store.js"; import { addSessionSuggestion, @@ -79,7 +79,7 @@ const defaultSuggestionSession = { } as const; function upsertDefaultSuggestionSession() { - return upsertSessionEntry({ agentId: "main", sessionKey }, defaultSuggestionSession); + return upsertSessionEntryCore({ agentId: "main", sessionKey }, defaultSuggestionSession); } function client(profileId: string, displayName: string, admin = false): GatewayClient { @@ -205,7 +205,7 @@ describe("session suggestion handlers", () => { it("hides draft suggestions from members while owner and admin can list", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const draftKey = "agent:main:draft-suggestions"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: draftKey }, { sessionId: "session-draft", @@ -287,7 +287,7 @@ describe("session suggestion handlers", () => { it("keeps incognito suggestion and typing surfaces admin-only", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const incognitoKey = "agent:main:dashboard:incognito-suggestions"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: incognitoKey }, { sessionId: "session-incognito", @@ -356,7 +356,7 @@ describe("session suggestion handlers", () => { it("rejects archived suggestion creation and non-dismiss resolutions", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const archivedKey = "agent:main:archived-suggestions"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: archivedKey }, { sessionId: "session-archived", @@ -707,7 +707,7 @@ describe("session suggestion handlers", () => { ); expect(notViewing.responses[0]?.[1]).toEqual({ ok: true, broadcast: false }); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-main", @@ -870,7 +870,7 @@ describe("session suggestion handlers", () => { it("returns a structured error when the session is replaced after dispatch", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-before-dispatch", @@ -898,7 +898,7 @@ describe("session suggestion handlers", () => { ); await vi.waitFor(() => expect(mocks.handleChatSend).toHaveBeenCalledOnce()); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-after-dispatch", @@ -929,7 +929,7 @@ describe("session suggestion handlers", () => { "maps a session replacement during %s to the structured terminal error", async (phase) => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-race", @@ -984,7 +984,7 @@ describe("session suggestion handlers", () => { it("keeps an unexpected claim-release failure retryable", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-release-failure", diff --git a/src/gateway/server-methods/sessions-typing.test.ts b/src/gateway/server-methods/sessions-typing.test.ts index b3d819e678d0..58b70005423e 100644 --- a/src/gateway/server-methods/sessions-typing.test.ts +++ b/src/gateway/server-methods/sessions-typing.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { closeOpenClawAgentDatabasesForTest } from "../../state/openclaw-agent-db.js"; import { withOpenClawTestState } from "../../test-utils/openclaw-test-state.js"; @@ -98,7 +98,7 @@ describe("session typing handler", () => { const cfg = { agents: { list: [{ id: "main", default: true }, { id: "work" }] }, } satisfies OpenClawConfig; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId, sessionKey: "global" }, { sessionId: `session-${agentId}`, @@ -136,7 +136,7 @@ describe("session typing handler", () => { vi.useFakeTimers(); vi.setSystemTime(10_000); const sessionKey = "agent:main:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-main", @@ -185,7 +185,7 @@ describe("session typing handler", () => { vi.setSystemTime(15_000); const sessionKey = "agent:main:typing-instance"; const writeSession = (sessionId: string, updatedAt: number) => - upsertSessionEntry( + upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId, @@ -258,7 +258,7 @@ describe("session typing handler", () => { vi.setSystemTime(20_000); const sessionKey = "agent:main:typing-reset"; const scope = { agentId: "main", sessionKey }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "session-before-reset", updatedAt: 1, createdActor: { type: "human", id: "owner" }, @@ -285,7 +285,7 @@ describe("session typing handler", () => { ok: true, broadcast: false, }); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: "session-after-reset", updatedAt: 2, createdActor: { type: "human", id: "owner" }, diff --git a/src/gateway/server-methods/system-agent-reset-boundary.test.ts b/src/gateway/server-methods/system-agent-reset-boundary.test.ts index 90d8c7023a26..853f1ccf7892 100644 --- a/src/gateway/server-methods/system-agent-reset-boundary.test.ts +++ b/src/gateway/server-methods/system-agent-reset-boundary.test.ts @@ -11,7 +11,7 @@ import { SystemAgentChatEngine } from "../../system-agent/chat-engine.js"; import { SystemAgentInferenceUnavailableError } from "../../system-agent/inference-error.js"; import { createSystemAgentVerifiedInferenceTestFixture } from "../../system-agent/system-agent.test-helpers.js"; import { appendTranscriptTurn, readTranscriptTail } from "../../system-agent/transcript-store.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { systemAgentHandlers, type SystemAgentChatSession } from "./system-agent.js"; import type { GatewayClient, GatewayRequestContext } from "./types.js"; @@ -122,7 +122,7 @@ function nextSessionSeed() { * directory's lifetime or its removal fails on platforms that lock open files. */ async function withTranscriptState(prefix: string, run: () => Promise): Promise { - await withTempDir({ prefix }, async (stateDir) => { + await withTestDir({ prefix }, async (stateDir) => { process.env.OPENCLAW_STATE_DIR = stateDir; try { await run(); diff --git a/src/gateway/server-methods/system-changes.test.ts b/src/gateway/server-methods/system-changes.test.ts index a7d41927a642..1112f3297559 100644 --- a/src/gateway/server-methods/system-changes.test.ts +++ b/src/gateway/server-methods/system-changes.test.ts @@ -11,7 +11,7 @@ import { SYSTEM_AGENT_AUDIT_SCOPE, type SystemAgentAuditEntry, } from "../../system-agent/audit.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { listSystemChanges, SYSTEM_CHANGE_MAX_RAW_SCAN_PER_SCOPE } from "./system-changes.js"; function configRecord( @@ -36,7 +36,7 @@ describe("openclaw.changes.list", () => { }); it("merges journals, collapses matching writes, and skips non-history records", async () => { - await withTempDir({ prefix: "openclaw-system-changes-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-changes-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const systemStore = createSqliteAuditRecordStore({ scope: SYSTEM_AGENT_AUDIT_SCOPE, @@ -159,7 +159,7 @@ describe("openclaw.changes.list", () => { }); it("classifies legacy redacted argv when origin is absent", async () => { - await withTempDir({ prefix: "openclaw-system-changes-argv-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-changes-argv-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const store = createSqliteAuditRecordStore({ scope: CONFIG_AUDIT_SCOPE, @@ -203,7 +203,7 @@ describe("openclaw.changes.list", () => { }); it("keeps pages newest-first while suppressing an older collapse partner", async () => { - await withTempDir({ prefix: "openclaw-system-changes-collapse-cursor-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-changes-collapse-cursor-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const systemStore = createSqliteAuditRecordStore({ scope: SYSTEM_AGENT_AUDIT_SCOPE, @@ -288,7 +288,7 @@ describe("openclaw.changes.list", () => { }); it("keeps an outside-window repeated transition on a later page", async () => { - await withTempDir({ prefix: "openclaw-system-changes-pending-window-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-changes-pending-window-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const systemStore = createSqliteAuditRecordStore({ scope: SYSTEM_AGENT_AUDIT_SCOPE, @@ -367,7 +367,7 @@ describe("openclaw.changes.list", () => { }); it("does not collapse a repeated transition outside the operation window", async () => { - await withTempDir({ prefix: "openclaw-system-changes-collapse-window-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-changes-collapse-window-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const systemStore = createSqliteAuditRecordStore({ scope: SYSTEM_AGENT_AUDIT_SCOPE, @@ -411,7 +411,7 @@ describe("openclaw.changes.list", () => { }); it("prefers an in-window match over an older repeated transition", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-system-changes-repeated-transition-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; @@ -488,7 +488,7 @@ describe("openclaw.changes.list", () => { }); it("paginates equal timestamps by per-scope sequence and freezes the scope heads", async () => { - await withTempDir({ prefix: "openclaw-system-changes-cursor-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-changes-cursor-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const store = createSqliteAuditRecordStore({ scope: CONFIG_AUDIT_SCOPE, @@ -535,7 +535,7 @@ describe("openclaw.changes.list", () => { }); it("freezes an untouched scope before the first page is emitted", async () => { - await withTempDir({ prefix: "openclaw-system-changes-frozen-heads-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-changes-frozen-heads-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const systemStore = createSqliteAuditRecordStore({ scope: SYSTEM_AGENT_AUDIT_SCOPE, @@ -598,7 +598,7 @@ describe("openclaw.changes.list", () => { }); it("uses store insertion order when a producer clock moves backwards", async () => { - await withTempDir({ prefix: "openclaw-system-changes-insertion-order-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-changes-insertion-order-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const store = createSqliteAuditRecordStore({ scope: CONFIG_AUDIT_SCOPE, @@ -645,7 +645,7 @@ describe("openclaw.changes.list", () => { }); it("bounds filtered journal scans and resumes from the scanned frontier", async () => { - await withTempDir({ prefix: "openclaw-system-changes-scan-budget-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-changes-scan-budget-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const configStore = createSqliteAuditRecordStore({ scope: CONFIG_AUDIT_SCOPE, diff --git a/src/gateway/server-methods/system-info.test.ts b/src/gateway/server-methods/system-info.test.ts index 7b9fb3aafd8c..9bf052657619 100644 --- a/src/gateway/server-methods/system-info.test.ts +++ b/src/gateway/server-methods/system-info.test.ts @@ -7,14 +7,14 @@ import { getGatewayProcessInstanceId } from "../process-instance.js"; import type { GatewayRequestHandlerOptions } from "./types.js"; const mocks = vi.hoisted(() => ({ - resolveAdvertisedLanHost: vi.fn(async () => "192.168.1.20"), + resolveAdvertisedLanHostCore: vi.fn(async () => "192.168.1.20"), })); // Keep every real export available: other modules in the import graph may pull // parse/select helpers from this module, and a partial factory would break them. vi.mock("../../infra/advertised-lan-host.js", async (importOriginal) => ({ ...(await importOriginal()), - resolveAdvertisedLanHost: mocks.resolveAdvertisedLanHost, + resolveAdvertisedLanHostCore: mocks.resolveAdvertisedLanHostCore, })); import { systemHandlers } from "./system.js"; @@ -41,7 +41,7 @@ describe("system.info", () => { )(request); expect(respond).toHaveBeenCalledTimes(2); - expect(mocks.resolveAdvertisedLanHost).toHaveBeenCalledTimes(1); + expect(mocks.resolveAdvertisedLanHostCore).toHaveBeenCalledTimes(1); const [ok, payload, error] = respond.mock.calls[0] ?? []; expect(ok).toBe(true); expect(error).toBeUndefined(); diff --git a/src/gateway/server-methods/system.ts b/src/gateway/server-methods/system.ts index e8ebacea8d32..2bf005f7aa11 100644 --- a/src/gateway/server-methods/system.ts +++ b/src/gateway/server-methods/system.ts @@ -23,7 +23,7 @@ import { } from "../../agents/utility-model.js"; import { resolveGatewayPort, resolveStateDir } from "../../config/paths.js"; import { resolveMainSessionKeyFromConfig } from "../../config/sessions.js"; -import { resolveAdvertisedLanHost } from "../../infra/advertised-lan-host.js"; +import { resolveAdvertisedLanHostCore } from "../../infra/advertised-lan-host.js"; import { loadOrCreateProcessDeviceIdentity, publicKeyRawBase64UrlFromPem, @@ -48,7 +48,7 @@ let advertisedLanHostPromise: Promise | null = null; function resolveCachedAdvertisedLanHost(): Promise { // Route discovery may spawn a platform command. Keep the result process-stable // so each visible Settings page does not repeat that work every ten seconds. - advertisedLanHostPromise ??= resolveAdvertisedLanHost().catch(() => null); + advertisedLanHostPromise ??= resolveAdvertisedLanHostCore().catch(() => null); return advertisedLanHostPromise; } diff --git a/src/gateway/server-methods/task-suggestions.test.ts b/src/gateway/server-methods/task-suggestions.test.ts index f455b209957a..cd48db757915 100644 --- a/src/gateway/server-methods/task-suggestions.test.ts +++ b/src/gateway/server-methods/task-suggestions.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { GATEWAY_CLIENT_CAPS } from "../../../packages/gateway-protocol/src/client-info.js"; -import { upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../../config/sessions/session-accessor.js"; import { clearAgentRunContext, registerAgentRunContext } from "../../infra/agent-run-registry.js"; import { closeOpenClawAgentDatabasesForTest } from "../../state/openclaw-agent-db.js"; import { withOpenClawTestState } from "../../test-utils/openclaw-test-state.js"; @@ -533,7 +533,7 @@ describe("task suggestion gateway methods", () => { it("sends an idle session acceptance as a new turn and replays its source key", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: SOURCE_SESSION_KEY }, { sessionId: "source-session", updatedAt: 1 }, ); @@ -566,7 +566,7 @@ describe("task suggestion gateway methods", () => { it("steers a session acceptance into its one exact active run", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: SOURCE_SESSION_KEY }, { sessionId: "source-session", updatedAt: 1 }, ); @@ -598,7 +598,7 @@ describe("task suggestion gateway methods", () => { { label: "no exact run ID", runIds: [], projected: true }, ])("rejects an active session with $label and restores the suggestion", async (testCase) => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: SOURCE_SESSION_KEY }, { sessionId: "source-session", updatedAt: 1 }, ); @@ -678,7 +678,7 @@ describe("task suggestion gateway methods", () => { it("restores a session-mode suggestion after delivery failure without deleting its source", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: SOURCE_SESSION_KEY }, { sessionId: "source-session", updatedAt: 1 }, ); diff --git a/src/gateway/server-methods/usage.test.ts b/src/gateway/server-methods/usage.test.ts index 5a71f0bcac50..d7de6c3351ee 100644 --- a/src/gateway/server-methods/usage.test.ts +++ b/src/gateway/server-methods/usage.test.ts @@ -7,7 +7,7 @@ import { expectDefined } from "@openclaw/normalization-core"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { ErrorCodes, errorShape } from "../../../packages/gateway-protocol/src/index.js"; import type { OpenClawConfig } from "../../config/config.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; vi.mock("../../infra/session-cost-usage.js", async () => { const actual = await vi.importActual( @@ -689,7 +689,7 @@ describe("gateway usage helpers", () => { }); it("does not project local avatar bytes for usage-only agent enumeration", async () => { - await withTempDir({ prefix: "openclaw-usage-avatar-" }, async (workspace) => { + await withTestDir({ prefix: "openclaw-usage-avatar-" }, async (workspace) => { await fs.writeFile(`${workspace}/avatar.png`, "avatar"); const config: OpenClawConfig = { agents: { diff --git a/src/gateway/server-methods/usage.ts b/src/gateway/server-methods/usage.ts index 019e29f355b0..9994721cea49 100644 --- a/src/gateway/server-methods/usage.ts +++ b/src/gateway/server-methods/usage.ts @@ -11,7 +11,7 @@ import { import { listAgentIds, resolveDefaultAgentId } from "../../agents/agent-scope.js"; import { parseSqliteSessionFileMarker } from "../../config/sessions/legacy-sqlite-marker.js"; import { - resolveSessionFilePath, + resolveSessionFilePathCore, resolveSessionFilePathOptions, } from "../../config/sessions/paths.js"; import type { SessionEntry } from "../../config/sessions/types.js"; @@ -156,7 +156,7 @@ function resolveSessionUsageTarget( : resolveExistingUsageSessionFile({ agentId, sessionId, - sessionFile: resolveSessionFilePath( + sessionFile: resolveSessionFilePathCore( sessionId, undefined, resolveSessionFilePathOptions({ storePath, agentId }), diff --git a/src/gateway/server-node-events.runtime.ts b/src/gateway/server-node-events.runtime.ts index d31cf40c5fcf..dce95ba44ac8 100644 --- a/src/gateway/server-node-events.runtime.ts +++ b/src/gateway/server-node-events.runtime.ts @@ -3,11 +3,11 @@ // helper before node traffic is actually handled. export { resolveSessionAgentId } from "../agents/agent-scope.js"; export { normalizeChannelId } from "../channels/plugins/index.js"; -export { sendDurableMessageBatch } from "../channels/message/runtime.js"; +export { sendDurableMessageBatchCore } from "../channels/message/runtime.js"; export { createOutboundSendDeps } from "../cli/outbound-send-deps.js"; export { agentCommandFromIngress } from "../commands/agent.js"; export { getRuntimeConfig } from "../config/io.js"; -export { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +export { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; export { loadOrCreateProcessDeviceIdentity } from "../infra/device-identity.js"; export { requestHeartbeat } from "../infra/heartbeat-wake.js"; export { buildOutboundSessionContext } from "../infra/outbound/session-context.js"; diff --git a/src/gateway/server-node-events.test.ts b/src/gateway/server-node-events.test.ts index 4ac38a5b4b42..67c66a47f39d 100644 --- a/src/gateway/server-node-events.test.ts +++ b/src/gateway/server-node-events.test.ts @@ -102,7 +102,7 @@ const runtimeMocks = vi.hoisted(() => ({ "[image attachment omitted: durable managed media claim unavailable]", loadOrCreateProcessDeviceIdentity: loadOrCreateProcessDeviceIdentityMock, loadSessionEntry: vi.fn((sessionKey: string) => buildSessionLookup(sessionKey)), - upsertSessionEntry: vi.fn(), + upsertSessionEntryCore: vi.fn(), normalizeChannelId: normalizeChannelIdMock, normalizeMainKey: vi.fn((key?: string | null) => key?.trim() || "agent:main:main"), normalizeRpcAttachmentsToChatAttachments: vi.fn((attachments?: unknown[]) => attachments ?? []), @@ -150,7 +150,10 @@ const runtimeMocks = vi.hoisted(() => ({ }), })); -vi.mock("./server-node-events.runtime.js", () => runtimeMocks); +vi.mock("./server-node-events.runtime.js", () => ({ + ...runtimeMocks, + sendDurableMessageBatchCore: runtimeMocks.sendDurableMessageBatch, +})); vi.mock("../infra/device-pairing.js", () => ({ updatePairedDevicePresence: updatePairedDevicePresenceMock, })); @@ -170,7 +173,7 @@ const enqueueSystemEventMock = runtimeMocks.enqueueSystemEvent; const requestHeartbeatMock = runtimeMocks.requestHeartbeat; const loadConfigMock = runtimeMocks.getRuntimeConfig; const agentCommandMock = runtimeMocks.agentCommandFromIngress; -const upsertSessionEntryMock = runtimeMocks.upsertSessionEntry; +const upsertSessionEntryMock = runtimeMocks.upsertSessionEntryCore; const loadSessionEntryMock = runtimeMocks.loadSessionEntry; const registerApnsRegistrationVi = runtimeMocks.registerApnsRegistration; const normalizeChannelIdVi = runtimeMocks.normalizeChannelId; diff --git a/src/gateway/server-node-events.ts b/src/gateway/server-node-events.ts index 73e04b46a867..26c009739a96 100644 --- a/src/gateway/server-node-events.ts +++ b/src/gateway/server-node-events.ts @@ -51,8 +51,8 @@ import { resolveSessionAgentId, resolveSessionModelRef, persistInboundImagesForTranscript, - sendDurableMessageBatch, - upsertSessionEntry, + sendDurableMessageBatchCore, + upsertSessionEntryCore, } from "./server-node-events.runtime.js"; const MAX_EXEC_EVENT_OUTPUT_CHARS = 180; @@ -386,7 +386,7 @@ async function touchSessionStore(params: { if (!storePath) { return; } - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: params.canonicalKey, storePath, @@ -515,7 +515,7 @@ async function sendReceiptAck(params: { cfg: params.cfg, sessionKey: params.sessionKey, }); - const send = await sendDurableMessageBatch({ + const send = await sendDurableMessageBatchCore({ cfg: params.cfg, channel: params.channel, to: resolved.to, diff --git a/src/gateway/server-plugin-in-process-dispatch.authorization.test.ts b/src/gateway/server-plugin-in-process-dispatch.authorization.test.ts index f9249539a38c..7fd53117583c 100644 --- a/src/gateway/server-plugin-in-process-dispatch.authorization.test.ts +++ b/src/gateway/server-plugin-in-process-dispatch.authorization.test.ts @@ -5,7 +5,7 @@ import { GATEWAY_CLIENT_MODES, } from "../../packages/gateway-protocol/src/client-info.js"; import { PROTOCOL_VERSION } from "../../packages/gateway-protocol/src/version.js"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import { withPluginRuntimeGatewayRequestScope } from "../plugins/runtime/gateway-request-scope.js"; import { withOpenClawTestState } from "../test-utils/openclaw-test-state.js"; import type { GatewayRequestContext, GatewayRequestOptions } from "./server-methods/types.js"; @@ -117,7 +117,7 @@ describe("typed in-process agent authorization", () => { it("rejects a nonparticipant agent turn before preflight", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:private-draft"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "private-draft-session", diff --git a/src/gateway/server-restart-sentinel-notice.test.ts b/src/gateway/server-restart-sentinel-notice.test.ts index 6006a3cba4c9..df13c29753f7 100644 --- a/src/gateway/server-restart-sentinel-notice.test.ts +++ b/src/gateway/server-restart-sentinel-notice.test.ts @@ -24,7 +24,7 @@ const mocks = vi.hoisted(() => ({ })); vi.mock("../channels/message/runtime.js", () => ({ - sendDurableMessageBatch: mocks.sendDurableMessageBatch, + sendDurableMessageBatchCore: mocks.sendDurableMessageBatch, })); vi.mock("../infra/outbound/deliver.js", () => ({ diff --git a/src/gateway/server-restart-sentinel-notice.ts b/src/gateway/server-restart-sentinel-notice.ts index beefd3c96385..57fc14eec7ed 100644 --- a/src/gateway/server-restart-sentinel-notice.ts +++ b/src/gateway/server-restart-sentinel-notice.ts @@ -1,5 +1,5 @@ // Durable outbound notice ownership for restart-sentinel recovery. -import { sendDurableMessageBatch } from "../channels/message/runtime.js"; +import { sendDurableMessageBatchCore } from "../channels/message/runtime.js"; import type { CliDeps } from "../cli/deps.types.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { @@ -285,7 +285,7 @@ export async function deliverRestartSentinelNotice( cfg: params.cfg, sessionKey: params.sessionKey, }); - const send = await sendDurableMessageBatch({ + const send = await sendDurableMessageBatchCore({ cfg: params.cfg, channel: params.channel, to: params.to, diff --git a/src/gateway/server-restart-sentinel.test.ts b/src/gateway/server-restart-sentinel.test.ts index 5702cc10224c..b0961ba7131f 100644 --- a/src/gateway/server-restart-sentinel.test.ts +++ b/src/gateway/server-restart-sentinel.test.ts @@ -230,7 +230,7 @@ vi.mock("../config/sessions/transcript.js", () => ({ vi.mock("../config/sessions.js", () => ({ resolveMainSessionKeyFromConfig: mocks.resolveMainSessionKeyFromConfig, - resolveStorePath: vi.fn(() => "/tmp/sessions.json"), + resolveSessionStorePathCore: vi.fn(() => "/tmp/sessions.json"), })); vi.mock("../config/sessions/thread-info.js", () => ({ @@ -364,7 +364,7 @@ vi.mock("../infra/outbound/deliver-queue-admission.js", () => ({ })); vi.mock("../channels/message/runtime.js", () => ({ - sendDurableMessageBatch: vi.fn(async (params: Record) => { + sendDurableMessageBatchCore: vi.fn(async (params: Record) => { try { const results = await mocks.deliverOutboundPayloads(params); return { status: "sent", results }; diff --git a/src/gateway/server-startup-secret-owner-isolation.test.ts b/src/gateway/server-startup-secret-owner-isolation.test.ts index 73291a5565c1..1579b206525f 100644 --- a/src/gateway/server-startup-secret-owner-isolation.test.ts +++ b/src/gateway/server-startup-secret-owner-isolation.test.ts @@ -8,7 +8,7 @@ import { resolveDefaultAgentDir } from "../agents/agent-scope-config.js"; import { getRuntimeAuthProfileStoreSnapshotCore } from "../agents/auth-profiles/runtime-snapshots.js"; import { saveAuthProfileStore } from "../agents/auth-profiles/store.js"; import { resolveMemorySearchConfig } from "../agents/memory-search.js"; -import { resolveApiKeyForProvider } from "../agents/model-auth.js"; +import { resolveApiKeyForProviderCore } from "../agents/model-auth.js"; import { resolveSandboxContext } from "../agents/sandbox/context.js"; import type { OpenClawConfig } from "../config/config.js"; import { resolveAuthProfileSecretOwnerId } from "../secrets/runtime-auth-profile-owner.js"; @@ -598,7 +598,7 @@ describe("Gateway startup SecretRef owner isolation", () => { const request = vi.fn(); await expect( (async () => { - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: "openai", cfg: active.config, store, diff --git a/src/gateway/server.sessions.archive-lifecycle.test.ts b/src/gateway/server.sessions.archive-lifecycle.test.ts index 91916cd147fe..1aa099d4fe20 100644 --- a/src/gateway/server.sessions.archive-lifecycle.test.ts +++ b/src/gateway/server.sessions.archive-lifecycle.test.ts @@ -1,7 +1,7 @@ // Archive lifecycle tests protect fence-before-cancel, terminal drains, and sentinels. import { afterEach, expect, test, vi } from "vitest"; import { SessionManager } from "../agents/sessions/session-manager.js"; -import { loadSessionEntry, upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { loadSessionEntry, upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import { onAgentEvent } from "../infra/agent-events.js"; import { beginSessionWorkAdmission, @@ -1033,7 +1033,7 @@ test("sessions.patch rejects a generation replaced after the exact preparation r }, ); await vi.waitFor(() => expect(active.controller.signal.aborted).toBe(true)); - await upsertSessionEntry( + await upsertSessionEntryCore( { storePath, sessionKey }, { sessionId: "session-archive-generation-replacement", updatedAt: 2 }, ); diff --git a/src/gateway/server.sessions.compaction.test.ts b/src/gateway/server.sessions.compaction.test.ts index bc591a87125f..614eece00780 100644 --- a/src/gateway/server.sessions.compaction.test.ts +++ b/src/gateway/server.sessions.compaction.test.ts @@ -22,7 +22,7 @@ import { loadTranscriptEvents, patchSessionEntryCore as patchAccessorSessionEntry, replaceSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { enqueueCommandInLane, @@ -34,7 +34,7 @@ import { isSessionWorkAdmissionActive, runExclusiveSessionLifecycleMutation, } from "../sessions/session-lifecycle-admission.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { embeddedRunMock, onceMessage, @@ -141,7 +141,7 @@ async function seedSessionEntry(params: { sessionKey: string; storePath: string; }): Promise { - await upsertSessionEntry( + await upsertSessionEntryCore( { ...(params.agentId ? { agentId: params.agentId } : {}), sessionKey: params.sessionKey, @@ -562,7 +562,7 @@ test("sessions.compact without maxLines runs embedded manual compaction for chec sessionKey: "agent:main:main", storePath, }; - await upsertSessionEntry(sessionScope, { + await upsertSessionEntryCore(sessionScope, { ...sessionStoreEntry("sess-main", { spawnedCwd: "/tmp/task-repo", thinkingLevel: "medium", @@ -1790,7 +1790,7 @@ test("sessions.compact maxLines does not interrupt an active run when no transcr }); test("sessions.patch preserves nested model ids under provider overrides", async () => { - await withTempDir({ prefix: "openclaw-gw-sessions-nested-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-gw-sessions-nested-" }, async (dir) => { const storePath = path.join(dir, "sessions.json"); const runtimeConfig = { agents: { diff --git a/src/gateway/server.sessions.create.test.ts b/src/gateway/server.sessions.create.test.ts index f676f281c419..15dffa6a28fa 100644 --- a/src/gateway/server.sessions.create.test.ts +++ b/src/gateway/server.sessions.create.test.ts @@ -21,7 +21,7 @@ import { loadCombinedSessionStoreForGatewayCore } from "../config/sessions/combi import { loadSessionEntry, loadTranscriptEvents, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { resolveSqliteTargetFromSessionStorePath } from "../config/sessions/session-sqlite-target.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; @@ -359,7 +359,7 @@ test("sessions.create keeps incognito rows process-local through list, spawn, re error: { code: "INVALID_REQUEST", message: "incognito sessions are web-only" }, }); const durableSubagentKey = "agent:main:subagent:durable-existing"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: durableSubagentKey, storePath }, { sessionId: "durable-subagent", updatedAt: Date.now() }, ); @@ -404,7 +404,7 @@ test("sessions.create keeps incognito rows process-local through list, spawn, re ); expect(afterReset.payload?.sessions.some((session) => session.key === key)).toBe(false); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: key, storePath }, { sessionId: "rematerialized-incognito", updatedAt: Date.now() }, ); diff --git a/src/gateway/server.sessions.permissions-hooks.test.ts b/src/gateway/server.sessions.permissions-hooks.test.ts index 5bfc71bffd42..566cbcdd003f 100644 --- a/src/gateway/server.sessions.permissions-hooks.test.ts +++ b/src/gateway/server.sessions.permissions-hooks.test.ts @@ -16,7 +16,7 @@ import { import { listSessionEntriesCore, loadSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { isSessionPatchEvent } from "../hooks/internal-hooks.js"; import { requireGatewayRecord } from "./test-helpers.assertions.js"; @@ -62,7 +62,7 @@ async function createPermissionCheckpointStore() { throw new Error("expected legacy checkpoint fixture"); } - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: "agent:main:main", storePath }, sessionStoreEntry(fixture.sessionId, { sessionFile: fixture.sessionFile, @@ -92,7 +92,7 @@ async function createPermissionCheckpointStore() { ], }), ); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: "agent:main:discord:group:dev", storePath }, sessionStoreEntry("sess-group"), ); diff --git a/src/gateway/server.tools-catalog.test.ts b/src/gateway/server.tools-catalog.test.ts index 34da212d168c..efd9a215f1fd 100644 --- a/src/gateway/server.tools-catalog.test.ts +++ b/src/gateway/server.tools-catalog.test.ts @@ -3,13 +3,13 @@ */ import { describe, expect, it } from "vitest"; import { connectOk, installGatewayTestHooks, rpcReq } from "./test-helpers.js"; -import { withServer } from "./test-with-server.js"; +import { withGatewayClient } from "./test-with-server.js"; installGatewayTestHooks({ scope: "suite" }); describe("gateway tools.catalog", () => { it("returns core catalog data and includes tts", async () => { - await withServer(async (ws) => { + await withGatewayClient(async (ws) => { await connectOk(ws, { token: "secret", scopes: ["operator.read"] }); const res = await rpcReq<{ agentId?: string; @@ -31,7 +31,7 @@ describe("gateway tools.catalog", () => { }); it("supports includePlugins=false and rejects unknown agent ids", async () => { - await withServer(async (ws) => { + await withGatewayClient(async (ws) => { await connectOk(ws, { token: "secret", scopes: ["operator.read"] }); const noPlugins = await rpcReq<{ diff --git a/src/gateway/server/ws-connection/connect-auth-security.ts b/src/gateway/server/ws-connection/connect-auth-security.ts index 4fcd296d5e42..a8fedd3c23b6 100644 --- a/src/gateway/server/ws-connection/connect-auth-security.ts +++ b/src/gateway/server/ws-connection/connect-auth-security.ts @@ -1,5 +1,5 @@ // Gateway WebSocket authentication security events keep identifiers redacted. -import { sha256HexPrefix } from "../../../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../../../infra/crypto-digest.js"; import { emitTrustedSecurityEvent, type DiagnosticSecurityEventInput, @@ -10,7 +10,7 @@ function hashGatewaySecurityId(value: string | undefined): string | undefined { if (!normalized) { return undefined; } - return `sha256:${sha256HexPrefix(normalized, 12)}`; + return `sha256:${sha256HexPrefixCore(normalized, 12)}`; } export function emitGatewayAuthSecurityEvent(params: { diff --git a/src/gateway/session-compaction-checkpoints.test.ts b/src/gateway/session-compaction-checkpoints.test.ts index 09fea0e5f48d..bf7cc6903ae3 100644 --- a/src/gateway/session-compaction-checkpoints.test.ts +++ b/src/gateway/session-compaction-checkpoints.test.ts @@ -16,7 +16,7 @@ import { loadSessionEntry, loadTranscriptEvents, updateSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { createFileBackedCompactionCheckpointStore, @@ -90,7 +90,7 @@ describe("session-compaction-checkpoints", () => { storePath, }); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId, sessionFile: marker, updatedAt: Date.now(), @@ -137,7 +137,7 @@ describe("session-compaction-checkpoints", () => { entryId: sourceLeafId, }, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId, sessionFile: marker, updatedAt: Date.now(), @@ -202,7 +202,7 @@ describe("session-compaction-checkpoints", () => { lifecycleRevision: "checkpoint-original-revision", sessionId, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { ...expectedState, updatedAt: 10, }); @@ -234,7 +234,7 @@ describe("session-compaction-checkpoints", () => { entryId: sourceMessage.messageId, }, }; - await upsertSessionEntry(scope, { compactionCheckpoints: [checkpoint] }); + await upsertSessionEntryCore(scope, { compactionCheckpoints: [checkpoint] }); let releaseOwnerChange = () => {}; const ownerChangeGate = new Promise((resolve) => { @@ -307,7 +307,7 @@ describe("session-compaction-checkpoints", () => { }); const staleSessionFile = path.join(dir, "stale-transcript.jsonl"); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId, sessionFile: staleSessionFile, updatedAt: Date.now(), @@ -372,7 +372,7 @@ describe("session-compaction-checkpoints", () => { leafId: sourceEntryId, }, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId, sessionFile: staleSessionFile, updatedAt: Date.now(), @@ -450,7 +450,7 @@ describe("session-compaction-checkpoints", () => { .join("\n") + "\n", "utf-8", ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: MAIN_AGENT_ID, sessionKey, @@ -611,7 +611,7 @@ describe("session-compaction-checkpoints", () => { sessionKey: "agent:main:structured-leaf-session", storePath: path.join(dir, "sessions.json"), }; - await upsertSessionEntry(target, { sessionId: target.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(target, { sessionId: target.sessionId, updatedAt: 1 }); await appendTranscriptEvent(target, { type: "session", version: CURRENT_SESSION_VERSION, diff --git a/src/gateway/session-companion-ask.ts b/src/gateway/session-companion-ask.ts index e2db5c1c4e31..2439291f4a4a 100644 --- a/src/gateway/session-companion-ask.ts +++ b/src/gateway/session-companion-ask.ts @@ -13,7 +13,7 @@ import { stripToolMessages, } from "../agents/tools/chat-history-text.js"; import { resolveUtilityModelRefForAgent } from "../agents/utility-model.js"; -import { resolveStorePath } from "../config/sessions.js"; +import { resolveSessionStorePathCore } from "../config/sessions.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { Message, Usage } from "../llm/types.js"; import { redactToolPayloadText } from "../logging/redact.js"; @@ -252,7 +252,9 @@ async function defaultRun(params: SessionCompanionRunParams): Promise { throw new Error("Session companion has no current question."); } const runId = `session-companion-${randomUUID()}`; - const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { + agentId: params.agentId, + }); const { prepareInternalSessionEffectsSession, removeInternalSessionEffectsSession } = await import("../agents/internal-session-effects.js"); const target = await prepareInternalSessionEffectsSession({ diff --git a/src/gateway/session-create-service.ts b/src/gateway/session-create-service.ts index a8c3e2995381..3b7c2b65d7fc 100644 --- a/src/gateway/session-create-service.ts +++ b/src/gateway/session-create-service.ts @@ -34,7 +34,7 @@ import { } from "../auto-reply/reply/session-fork.js"; import type { SessionEntry } from "../config/sessions.js"; import { resolveAgentMainSessionKey } from "../config/sessions/main-session.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { createSessionEntryWithTranscript, listSessionEntriesReadOnly, @@ -376,7 +376,7 @@ export async function createGatewaySession(params: { error: errorShape(ErrorCodes.INVALID_REQUEST, "incognito sessions are web-only"), }; } - const durableStorePath = resolveStorePath(params.cfg.session?.store, { agentId }); + const durableStorePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId }); const durableEntryExists = listSessionEntriesReadOnly({ agentId, storePath: durableStorePath, diff --git a/src/gateway/session-sharing-store-cache.test.ts b/src/gateway/session-sharing-store-cache.test.ts index 7d1018384093..d0e3df672398 100644 --- a/src/gateway/session-sharing-store-cache.test.ts +++ b/src/gateway/session-sharing-store-cache.test.ts @@ -42,7 +42,7 @@ describe("session mutation authorization store caches", () => { it("fails a patchMany request when a nested target is incognito", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:dashboard:incognito-patch-many"; - await sessionAccessor.upsertSessionEntry( + await sessionAccessor.upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-incognito", updatedAt: 1, incognito: true }, ); @@ -63,11 +63,11 @@ describe("session mutation authorization store caches", () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sharedKey = "agent:main:batch-shared"; const draftKey = "agent:main:batch-private"; - await sessionAccessor.upsertSessionEntry( + await sessionAccessor.upsertSessionEntryCore( { agentId: "main", sessionKey: sharedKey }, { sessionId: "session-shared", updatedAt: 1, visibility: "shared" }, ); - await sessionAccessor.upsertSessionEntry( + await sessionAccessor.upsertSessionEntryCore( { agentId: "main", sessionKey: draftKey }, { sessionId: "session-private", @@ -98,7 +98,7 @@ describe("session mutation authorization store caches", () => { it("fences a replaced patchMany target with padded identity fields", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:padded-batch-target"; - await sessionAccessor.upsertSessionEntry( + await sessionAccessor.upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-original", updatedAt: 1, visibility: "shared" }, ); @@ -118,7 +118,7 @@ describe("session mutation authorization store caches", () => { const authorization = result.authorization!; expect(() => authorization.assertTargetCurrent(target)).not.toThrow(); - await sessionAccessor.upsertSessionEntry( + await sessionAccessor.upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-replacement", updatedAt: 2, visibility: "shared" }, ); @@ -132,7 +132,7 @@ describe("session mutation authorization store caches", () => { it("bounds malformed patchMany target discovery before schema validation", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const hiddenKey = "agent:main:dashboard:incognito-over-limit"; - await sessionAccessor.upsertSessionEntry( + await sessionAccessor.upsertSessionEntryCore( { agentId: "main", sessionKey: hiddenKey }, { sessionId: "session-hidden", updatedAt: 1, incognito: true }, ); @@ -159,7 +159,7 @@ describe("session mutation authorization store caches", () => { ["agent:main:cache-one", "session-cache-one"], ["agent:main:cache-two", "session-cache-two"], ] as const) { - await sessionAccessor.upsertSessionEntry( + await sessionAccessor.upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId, updatedAt: 1, visibility: "shared", category: "Cache Test" }, ); @@ -224,7 +224,7 @@ describe("session mutation authorization store caches", () => { }, ])("matches uncached $name authorization", async ({ sessionKey, entry }) => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await sessionAccessor.upsertSessionEntry({ agentId: "main", sessionKey }, entry); + await sessionAccessor.upsertSessionEntryCore({ agentId: "main", sessionKey }, entry); const cfg = {}; const requestClient = identifiedClient("viewer@example.com"); const uncachedError = authorizeResolvedSessionMutation({ diff --git a/src/gateway/session-sharing.test.ts b/src/gateway/session-sharing.test.ts index 4694f258c597..e637406912f8 100644 --- a/src/gateway/session-sharing.test.ts +++ b/src/gateway/session-sharing.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it } from "vitest"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import { addSessionMember } from "../config/sessions/session-sharing-store.js"; import { closeOpenClawAgentDatabasesForTest } from "../state/openclaw-agent-db.js"; import { withOpenClawTestState } from "../test-utils/openclaw-test-state.js"; @@ -161,7 +161,7 @@ describe("session sharing policy", () => { incognito: true as const, createdActor: { type: "human" as const, id: "owner@example.com" }, }; - await upsertSessionEntry({ agentId: "main", sessionKey }, entry); + await upsertSessionEntryCore({ agentId: "main", sessionKey }, entry); const owner = client({ user: "owner@example.com" }); const viewer = client({ user: "viewer@example.com" }); const admin = client({ user: "admin@example.com", scopes: ["operator.admin"] }); @@ -224,11 +224,11 @@ describe("session sharing policy", () => { it("keeps agent scope for indirect run and approval authorization", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "global" }, { sessionId: "session-main-global", updatedAt: 1, visibility: "shared" }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "work", sessionKey: "global" }, { sessionId: "session-work-global", @@ -237,7 +237,7 @@ describe("session sharing policy", () => { createdActor: { type: "human", id: "owner@example.com" }, }, ); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: "agent:main:solo-draft" }, { sessionId: "session-solo-draft", updatedAt: 1, visibility: "draft" }, ); @@ -307,7 +307,7 @@ describe("session sharing policy", () => { it("limits suggestion events to participants and the suggestion author", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:suggestions"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-suggestions", @@ -352,7 +352,7 @@ describe("session sharing policy", () => { it("keeps draft typing events owner and admin only", async () => { await withOpenClawTestState({ scenario: "minimal" }, async () => { const sessionKey = "agent:main:draft-typing"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey }, { sessionId: "session-draft", diff --git a/src/gateway/session-transcript-files.fs.ts b/src/gateway/session-transcript-files.fs.ts index 6c5b303639de..200c2fe5cb2b 100644 --- a/src/gateway/session-transcript-files.fs.ts +++ b/src/gateway/session-transcript-files.fs.ts @@ -12,7 +12,7 @@ import { } from "../config/sessions/artifacts.js"; import { extractGeneratedTranscriptSessionId } from "../config/sessions/generated-transcript-session-id.js"; import { - resolveSessionFilePath, + resolveSessionFilePathCore, resolveSessionTranscriptPath, resolveSessionTranscriptPathInDir, } from "../config/sessions/paths.js"; @@ -84,19 +84,19 @@ export function resolveSessionTranscriptCandidates( const sessionsDir = path.dirname(storePath); if (sessionFile && sessionFileState !== "stale") { pushCandidate(() => - resolveSessionFilePath(sessionId, { sessionFile }, { sessionsDir, agentId }), + resolveSessionFilePathCore(sessionId, { sessionFile }, { sessionsDir, agentId }), ); } pushCandidate(() => resolveSessionTranscriptPathInDir(sessionId, sessionsDir)); if (sessionFile && sessionFileState === "stale") { pushCandidate(() => - resolveSessionFilePath(sessionId, { sessionFile }, { sessionsDir, agentId }), + resolveSessionFilePathCore(sessionId, { sessionFile }, { sessionsDir, agentId }), ); } } else if (sessionFile) { if (agentId) { if (sessionFileState !== "stale") { - pushCandidate(() => resolveSessionFilePath(sessionId, { sessionFile }, { agentId })); + pushCandidate(() => resolveSessionFilePathCore(sessionId, { sessionFile }, { agentId })); } } else { const trimmed = sessionFile.trim(); @@ -109,7 +109,7 @@ export function resolveSessionTranscriptCandidates( if (agentId) { pushCandidate(() => resolveSessionTranscriptPath(sessionId, agentId)); if (sessionFile && sessionFileState === "stale") { - pushCandidate(() => resolveSessionFilePath(sessionId, { sessionFile }, { agentId })); + pushCandidate(() => resolveSessionFilePathCore(sessionId, { sessionFile }, { agentId })); } } diff --git a/src/gateway/session-transcript-readers.test.ts b/src/gateway/session-transcript-readers.test.ts index e5977f34fbb6..caf5c2d37fe7 100644 --- a/src/gateway/session-transcript-readers.test.ts +++ b/src/gateway/session-transcript-readers.test.ts @@ -7,7 +7,7 @@ import * as sessionAccessor from "../config/sessions/session-accessor.js"; import { persistSessionTranscriptTurn, replaceTranscriptEvents, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { waitForSessionTranscriptIndexReconcile } from "../config/sessions/session-transcript-reconcile.js"; import { @@ -308,7 +308,7 @@ describe("session transcript reader facade", () => { })}\n`, "utf-8", ); - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey, storePath }, { sessionId, diff --git a/src/gateway/session-utils-projection.ts b/src/gateway/session-utils-projection.ts index 6233fddc2f2c..90c1c597fe0a 100644 --- a/src/gateway/session-utils-projection.ts +++ b/src/gateway/session-utils-projection.ts @@ -4,7 +4,8 @@ import { resolveContextTokensForModel } from "../agents/context.js"; import { normalizeStoredOverrideModel } from "../agents/model-selection.js"; import { resolveSessionModelRef } from "../agents/session-model-ref.js"; import { buildSubagentSessionListReadIndex } from "../agents/subagents/registry/subagent-registry-read.js"; -import { resolveStorePath, type SessionEntry } from "../config/sessions.js"; +import { resolveSessionStorePathCore, type SessionEntry } from "../config/sessions.js"; +import { resolveConcreteSessionStorePath } from "../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { normalizeAgentId, parseAgentSessionKey } from "../routing/session-key.js"; import { readRecentSessionUsageFromTranscript as readScopedRecentSessionUsageFromTranscript } from "./session-transcript-readers.js"; @@ -20,7 +21,6 @@ import { resolveRuntimeChildSessionKeys, resolveStoreChildSessionKeysFromCandidates, } from "./session-utils-core.js"; -import { resolveConcreteSessionStorePath } from "./session-utils-store.js"; export function buildSessionListRowContext(params: { store: Record; @@ -174,7 +174,7 @@ export function resolveTranscriptUsageFallback(params: { : normalizeAgentId(params.agentId ?? resolveDefaultAgentId(params.cfg)); const storePath = resolveConcreteSessionStorePath(params.storePath) ?? - resolveStorePath(params.cfg.session?.store, { agentId }); + resolveSessionStorePathCore(params.cfg.session?.store, { agentId }); let snapshot: ReturnType; try { snapshot = readScopedRecentSessionUsageFromTranscript( diff --git a/src/gateway/session-utils-store-lookup.ts b/src/gateway/session-utils-store-lookup.ts index 1955b2ef6750..568324fbeca7 100644 --- a/src/gateway/session-utils-store-lookup.ts +++ b/src/gateway/session-utils-store-lookup.ts @@ -4,7 +4,7 @@ import { isConfiguredSessionStoreAgentId, resolveAgentMainSessionKey, resolveExistingAgentSessionStoreTargetsSync, - resolveStorePath, + resolveSessionStorePathCore, type SessionEntry, type SessionStoreTarget, } from "../config/sessions.js"; @@ -111,7 +111,7 @@ function resolveGatewaySessionStoreCandidates( const storeConfig = cfg.session?.store; const fallback = { agentId, - storePath: resolveStorePath(storeConfig, { agentId }), + storePath: resolveSessionStorePathCore(storeConfig, { agentId }), }; const discovery = { existing: resolveExistingAgentSessionStoreTargetsSync(cfg, agentId), diff --git a/src/gateway/session-utils-store.ts b/src/gateway/session-utils-store.ts index 060f4c823e46..774c617e6063 100644 --- a/src/gateway/session-utils-store.ts +++ b/src/gateway/session-utils-store.ts @@ -253,18 +253,6 @@ export function isGroupOrChannelDisplaySession( ); } -function isStorePathTemplate(store?: string): boolean { - return typeof store === "string" && store.includes("{agentId}"); -} - -export function resolveConcreteSessionStorePath(storePath: string | undefined): string | undefined { - const trimmed = storePath?.trim(); - if (!trimmed || trimmed === "(multiple)" || isStorePathTemplate(trimmed)) { - return undefined; - } - return trimmed; -} - function normalizeFallbackList(values: readonly string[]): string[] { const out: string[] = []; const seen = new Set(); diff --git a/src/gateway/session-utils.single-row-cache.test.ts b/src/gateway/session-utils.single-row-cache.test.ts index 8ffe393a4777..743185932e3f 100644 --- a/src/gateway/session-utils.single-row-cache.test.ts +++ b/src/gateway/session-utils.single-row-cache.test.ts @@ -4,7 +4,7 @@ import { afterEach, describe, expect, test, vi } from "vitest"; import { resetConfigRuntimeState, setRuntimeConfigSnapshot } from "../config/config.js"; import type { OpenClawConfig } from "../config/config.js"; -import { resolveStorePath, type SessionEntry } from "../config/sessions.js"; +import { resolveSessionStorePathCore, type SessionEntry } from "../config/sessions.js"; import { replaceSessionEntry, updateSessionEntry } from "../config/sessions/session-accessor.js"; import { resetPluginRuntimeStateForTest } from "../plugins/runtime.js"; import { withStateDirEnv } from "../test-helpers/state-dir-env.js"; @@ -110,7 +110,7 @@ async function withSingleRowCacheStore( setRuntimeConfigSnapshot(cfg, cfg); await run({ now: Math.floor(Date.now() / 1_000) * 1_000 + 100, - storePath: resolveStorePath(cfg.session?.store, { agentId: MAIN_AGENT_ID }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: MAIN_AGENT_ID }), }); }); } diff --git a/src/gateway/sessions-patch.test.ts b/src/gateway/sessions-patch.test.ts index 6f5991af99b5..42778defe86a 100644 --- a/src/gateway/sessions-patch.test.ts +++ b/src/gateway/sessions-patch.test.ts @@ -38,7 +38,7 @@ const acpSessionMetaMocks = vi.hoisted(() => ({ })); const providerThinkingMocks = vi.hoisted(() => ({ resolveProviderThinkingProfile: - vi.fn(), + vi.fn(), })); vi.mock("../acp/runtime/session-meta.js", () => ({ @@ -47,7 +47,7 @@ vi.mock("../acp/runtime/session-meta.js", () => ({ // This suite owns patch projection; provider policy artifacts have dedicated contract coverage. vi.mock("../plugins/provider-thinking.js", () => ({ - resolveProviderThinkingProfile: providerThinkingMocks.resolveProviderThinkingProfile, + resolveEffectiveThinkingProfile: providerThinkingMocks.resolveProviderThinkingProfile, })); const SUBAGENT_MODEL = "synthetic/hf:moonshotai/Kimi-K2.7-Code"; diff --git a/src/gateway/sessions-resolve-store.test.ts b/src/gateway/sessions-resolve-store.test.ts index 6c75878f19cb..d64ec53a4710 100644 --- a/src/gateway/sessions-resolve-store.test.ts +++ b/src/gateway/sessions-resolve-store.test.ts @@ -5,7 +5,7 @@ import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { ErrorCodes } from "../../packages/gateway-protocol/src/index.js"; import { writeAcpSessionMetaForMigration } from "../acp/runtime/session-meta.js"; -import { resolveStorePath, type SessionEntry } from "../config/sessions.js"; +import { resolveSessionStorePathCore, type SessionEntry } from "../config/sessions.js"; import { replaceSessionEntry } from "../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { closeOpenClawAgentDatabasesForTest } from "../state/openclaw-agent-db.js"; @@ -89,7 +89,7 @@ describe("resolveSessionKeyFromResolveParams store canonicalization", () => { const cfg: OpenClawConfig = { agents: { list: [{ id: "main", default: true }, { id: "work" }] }, }; - const workStorePath = resolveStorePath(cfg.session?.store, { agentId: "work" }); + const workStorePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: "work" }); await seedSessionStore(workStorePath, { "agent:work:target": { sessionId: "sess-shared", @@ -132,14 +132,14 @@ describe("resolveSessionKeyFromResolveParams store canonicalization", () => { agents: { list: [{ id: "main", default: true }, { id: "work" }] }, }; const updatedAt = freshUpdatedAt(); - await seedSessionStore(resolveStorePath(cfg.session?.store, { agentId: "main" }), { + await seedSessionStore(resolveSessionStorePathCore(cfg.session?.store, { agentId: "main" }), { "main-target": { sessionId: "sess-shared", label: "shared-label", updatedAt, }, }); - await seedSessionStore(resolveStorePath(cfg.session?.store, { agentId: "work" }), { + await seedSessionStore(resolveSessionStorePathCore(cfg.session?.store, { agentId: "work" }), { "work-target": { sessionId: "sess-shared", label: "shared-label", @@ -185,7 +185,7 @@ describe("resolveSessionKeyFromResolveParams store canonicalization", () => { session: { mainKey: "main", store: undefined }, agents: { list: [{ id: "ops", default: true }] }, } satisfies OpenClawConfig; - await seedSessionStore(resolveStorePath(cfg.session?.store, { agentId: "main" }), { + await seedSessionStore(resolveSessionStorePathCore(cfg.session?.store, { agentId: "main" }), { "agent:main:guildchat:direct:u1": { sessionId: "sess-stale-main", label: "stale-main", @@ -213,7 +213,9 @@ describe("resolveSessionKeyFromResolveParams store canonicalization", () => { const cfg: OpenClawConfig = { agents: { list: [{ id: "ops", default: true }] }, }; - const staleMainStorePath = resolveStorePath(cfg.session?.store, { agentId: "main" }); + const staleMainStorePath = resolveSessionStorePathCore(cfg.session?.store, { + agentId: "main", + }); await seedSessionStore(staleMainStorePath, { "agent:main:main": { sessionId: "sess-discovered-main", @@ -256,7 +258,9 @@ describe("resolveSessionKeyFromResolveParams store canonicalization", () => { agents: { list: [{ id: "main", default: true }] }, }; const acpKey = "agent:claude:acp:11111111-1111-4111-8111-111111111111"; - const claudeStorePath = resolveStorePath(cfg.session?.store, { agentId: "claude" }); + const claudeStorePath = resolveSessionStorePathCore(cfg.session?.store, { + agentId: "claude", + }); await seedSessionStore(claudeStorePath, { [acpKey]: { sessionId: "sess-acp-harness", @@ -307,7 +311,9 @@ describe("resolveSessionKeyFromResolveParams store canonicalization", () => { }; const acpKey = "agent:claude:acp:44444444-4444-4444-8444-444444444444"; const legacyAcpKey = "agent:CLAUDE:acp:44444444-4444-4444-8444-444444444444"; - const claudeStorePath = resolveStorePath(cfg.session?.store, { agentId: "claude" }); + const claudeStorePath = resolveSessionStorePathCore(cfg.session?.store, { + agentId: "claude", + }); await seedSessionStore(claudeStorePath, { [acpKey]: { sessionId: "sess-acp-harness-partial", @@ -350,7 +356,9 @@ describe("resolveSessionKeyFromResolveParams store canonicalization", () => { agents: { list: [{ id: "main", default: true }] }, }; const acpBridgeKey = "agent:deleted-agent:acp:bridge-session-without-runtime-meta"; - const deletedStorePath = resolveStorePath(cfg.session?.store, { agentId: "deleted-agent" }); + const deletedStorePath = resolveSessionStorePathCore(cfg.session?.store, { + agentId: "deleted-agent", + }); await seedSessionStore(deletedStorePath, { [acpBridgeKey]: { sessionId: "sess-acp-bridge-deleted", @@ -395,7 +403,9 @@ describe("resolveSessionKeyFromResolveParams store canonicalization", () => { agents: { list: [{ id: "main", default: true }] }, }; const acpBindingKey = "agent:deleted-agent:acp:binding:discord:default:feedface"; - const deletedStorePath = resolveStorePath(cfg.session?.store, { agentId: "deleted-agent" }); + const deletedStorePath = resolveSessionStorePathCore(cfg.session?.store, { + agentId: "deleted-agent", + }); await seedSessionStore(deletedStorePath, { [acpBindingKey]: { sessionId: "sess-acp-binding-deleted", @@ -439,14 +449,18 @@ describe("resolveSessionKeyFromResolveParams store canonicalization", () => { const cfg: OpenClawConfig = { agents: { list: [{ id: "ops", default: true }] }, }; - const liveDefaultStorePath = resolveStorePath(cfg.session?.store, { agentId: "ops" }); + const liveDefaultStorePath = resolveSessionStorePathCore(cfg.session?.store, { + agentId: "ops", + }); await seedSessionStore(liveDefaultStorePath, { "agent:ops:main": { sessionId: "sess-live-default", updatedAt: freshUpdatedAt(), }, }); - const staleMainStorePath = resolveStorePath(cfg.session?.store, { agentId: "main" }); + const staleMainStorePath = resolveSessionStorePathCore(cfg.session?.store, { + agentId: "main", + }); await seedSessionStore(staleMainStorePath, { "agent:main:main": { sessionId: "sess-deleted-main", diff --git a/src/gateway/test-with-server.ts b/src/gateway/test-with-server.ts index 0eaf9fdeb7a0..63a62d7d8a83 100644 --- a/src/gateway/test-with-server.ts +++ b/src/gateway/test-with-server.ts @@ -8,7 +8,7 @@ type GatewayWs = Awaited>["ws"]; type GatewayServer = Awaited>["server"]; /** Starts a gateway, connects a client, runs the callback, and closes resources. */ -export async function withServer(run: (ws: GatewayWs) => Promise): Promise { +export async function withGatewayClient(run: (ws: GatewayWs) => Promise): Promise { const { server, ws, envSnapshot } = await startServerWithClient("secret"); try { return await run(ws); diff --git a/src/gateway/worker-environments/bootstrap.test.ts b/src/gateway/worker-environments/bootstrap.test.ts index 55af01237ece..a9aa5ae72081 100644 --- a/src/gateway/worker-environments/bootstrap.test.ts +++ b/src/gateway/worker-environments/bootstrap.test.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { describe, expect, it } from "vitest"; import type { WorkerSshEndpoint } from "../../plugins/types.js"; import { runCommandWithTimeout, type SpawnResult } from "../../process/exec.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { bootstrapWorker as bootstrapWorkerCore } from "./bootstrap.js"; import { createWorkerBundleProducer, type WorkerInstallationArtifact } from "./bundle.js"; @@ -543,7 +543,7 @@ describe("bootstrapWorker", () => { it.skipIf(process.platform === "win32")( "reuses and finally cleans the operation upload across ambiguous candidate attempts", async () => { - await withTempDir({ prefix: "openclaw-worker-bootstrap-script-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bootstrap-script-" }, async (root) => { const packageRoot = path.join(root, "package"); const remoteHome = path.join(root, "remote-home"); await fs.mkdir(path.join(packageRoot, "dist"), { recursive: true }); @@ -712,7 +712,7 @@ describe("bootstrapWorker", () => { it.skipIf(process.platform === "win32")( "fails closed instead of following a poisoned incoming directory", async () => { - await withTempDir({ prefix: "openclaw-worker-bootstrap-path-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bootstrap-path-" }, async (root) => { const remoteHome = path.join(root, "remote-home"); const unrelated = path.join(root, "unrelated"); const bootstrapRoot = path.join(remoteHome, ".openclaw-worker"); @@ -743,7 +743,7 @@ describe("bootstrapWorker", () => { it.skipIf(process.platform === "win32")( "does not follow a poisoned bootstrap root during terminal cleanup", async () => { - await withTempDir({ prefix: "openclaw-worker-bootstrap-cleanup-root-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bootstrap-cleanup-root-" }, async (root) => { const remoteHome = path.join(root, "remote-home"); const unrelated = path.join(root, "unrelated"); const incoming = path.join(unrelated, ".incoming"); @@ -776,7 +776,7 @@ describe("bootstrapWorker", () => { it.skipIf(process.platform === "win32")( "verifies npm installs from the packaged dist inventory", async () => { - await withTempDir({ prefix: "openclaw-worker-bootstrap-npm-inventory-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bootstrap-npm-inventory-" }, async (root) => { const packageRoot = path.join(root, "package"); const remoteHome = path.join(root, "remote-home"); await fs.mkdir(path.join(packageRoot, "dist"), { recursive: true }); diff --git a/src/gateway/worker-environments/bundle.test.ts b/src/gateway/worker-environments/bundle.test.ts index 5ac8105dc0c8..3b58bf1feab0 100644 --- a/src/gateway/worker-environments/bundle.test.ts +++ b/src/gateway/worker-environments/bundle.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import * as tar from "tar"; import { describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { createWorkerBundleProducer, resolveWorkerNpmInstallationArtifact, @@ -59,7 +59,7 @@ function bundleArtifact(overrides: Partial = {}): WorkerBu describe("worker bundle producer", () => { it("hashes the same file manifest deterministically", async () => { - await withTempDir({ prefix: "openclaw-worker-bundle-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bundle-" }, async (root) => { const packageA = path.join(root, "package-a"); const packageB = path.join(root, "package-b"); const files = [ @@ -94,7 +94,7 @@ describe("worker bundle producer", () => { }); it("keeps lazy Control UI assets out of worker identity", async () => { - await withTempDir({ prefix: "openclaw-worker-bundle-control-ui-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bundle-control-ui-" }, async (root) => { const packageRoot = path.join(root, "package"); const cacheDir = path.join(root, "cache"); await writeFixture(packageRoot, [["dist/entry.js", "export const entry = true;\n"]]); @@ -118,7 +118,7 @@ describe("worker bundle producer", () => { }); it("prunes workspace deps and lifecycle fields from dev manifests", async () => { - await withTempDir({ prefix: "openclaw-worker-bundle-prune-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bundle-prune-" }, async (root) => { const packageRoot = path.join(root, "package"); await writeFixture(packageRoot, [["dist/entry.js", "export const entry = true;\n"]]); await fs.writeFile( @@ -155,7 +155,7 @@ describe("worker bundle producer", () => { }); it("vendors workspace packages that the shipped dist imports", async () => { - await withTempDir({ prefix: "openclaw-worker-bundle-vendor-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bundle-vendor-" }, async (root) => { const packageRoot = path.join(root, "package"); await writeFixture(packageRoot, [ ["dist/entry.js", 'import { fake } from "@openclaw/fake-pkg";\nexport { fake };\n'], @@ -229,7 +229,7 @@ describe("worker bundle producer", () => { }); it("fails closed when a dist-referenced workspace package is not installed", async () => { - await withTempDir({ prefix: "openclaw-worker-bundle-vendor-missing-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bundle-vendor-missing-" }, async (root) => { const packageRoot = path.join(root, "package"); await writeFixture(packageRoot, [ ["dist/entry.js", 'import "@openclaw/fake-pkg";\nexport {};\n'], @@ -257,7 +257,7 @@ describe("worker bundle producer", () => { }); it("changes the hash when file contents change", async () => { - await withTempDir({ prefix: "openclaw-worker-bundle-change-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bundle-change-" }, async (root) => { const packageRoot = path.join(root, "package"); const cacheDir = path.join(root, "cache"); await writeFixture(packageRoot, [["dist/entry.js", "export const value = 1;\n"]]); @@ -277,7 +277,7 @@ describe("worker bundle producer", () => { }); it("archives the staged bytes when the source changes during packaging", async () => { - await withTempDir({ prefix: "openclaw-worker-bundle-mutation-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bundle-mutation-" }, async (root) => { const baselineRoot = path.join(root, "baseline"); const packageRoot = path.join(root, "package"); const originalContents = "export const value = 'before';\n"; @@ -322,7 +322,7 @@ describe("worker bundle producer", () => { }); it("owns one immutable build snapshot for its lifecycle", async () => { - await withTempDir({ prefix: "openclaw-worker-bundle-cache-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bundle-cache-" }, async (root) => { const packageRoot = path.join(root, "package"); await writeFixture(packageRoot, [["dist/entry.js", "export {};\n"]]); const producer = createWorkerBundleProducer({ @@ -343,7 +343,7 @@ describe("worker bundle producer", () => { }); it("retries after a failed preparation without polling a successful snapshot", async () => { - await withTempDir({ prefix: "openclaw-worker-bundle-retry-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bundle-retry-" }, async (root) => { const packageRoot = path.join(root, "package"); const producer = createWorkerBundleProducer({ packageRoot, @@ -362,7 +362,7 @@ describe("worker bundle producer", () => { }); it("replaces a corrupt content-addressed cache entry", async () => { - await withTempDir({ prefix: "openclaw-worker-bundle-corrupt-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bundle-corrupt-" }, async (root) => { const packageRoot = path.join(root, "package"); const cacheDir = path.join(root, "cache"); await writeFixture(packageRoot, [["dist/entry.js", "export {};\n"]]); @@ -382,7 +382,7 @@ describe("worker bundle producer", () => { }); it.skipIf(process.platform === "win32")("rejects symlinked runtime files", async () => { - await withTempDir({ prefix: "openclaw-worker-bundle-symlink-" }, async (root) => { + await withTestDir({ prefix: "openclaw-worker-bundle-symlink-" }, async (root) => { const packageRoot = path.join(root, "package"); await writeFixture(packageRoot, [["dist/entry.js", "export {};\n"]]); await fs.rename( @@ -400,7 +400,7 @@ describe("worker bundle producer", () => { describe("worker npm installation artifact", () => { it("uses an exact registry-proven gateway package", async () => { - await withTempDir({ prefix: "openclaw-worker-npm-release-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-worker-npm-release-" }, async (packageRoot) => { await writeFixture(packageRoot, [["dist/entry.js", "export {};\n"]]); const packageIntegrity = `sha512-${Buffer.alloc(64).toString("base64")}`; const verifyRelease = vi.fn(async () => packageIntegrity); @@ -448,7 +448,7 @@ describe("worker npm installation artifact", () => { }); it("rejects a source checkout even when its version is published", async () => { - await withTempDir({ prefix: "openclaw-worker-npm-source-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-worker-npm-source-" }, async (packageRoot) => { await writeFixture(packageRoot, [["dist/entry.js", "export {};\n"]]); await fs.mkdir(path.join(packageRoot, ".git")); const verifyRelease = vi.fn(async () => `sha512-${Buffer.alloc(64).toString("base64")}`); diff --git a/src/gateway/worker-environments/live-events.test.ts b/src/gateway/worker-environments/live-events.test.ts index 1e0a3148137c..659e8e43d67e 100644 --- a/src/gateway/worker-environments/live-events.test.ts +++ b/src/gateway/worker-environments/live-events.test.ts @@ -155,7 +155,7 @@ describe("worker live events", () => { target, }); const create = (updatedAt = 20) => - sessions.upsertSessionEntry( + sessions.upsertSessionEntryCore( { agentId: "main", sessionKey: KEY, storePath: store }, { sessionId: SID, updatedAt }, ); diff --git a/src/gateway/worker-environments/transcript-commit.test.ts b/src/gateway/worker-environments/transcript-commit.test.ts index 427aed7bd39c..f0ed64ab5618 100644 --- a/src/gateway/worker-environments/transcript-commit.test.ts +++ b/src/gateway/worker-environments/transcript-commit.test.ts @@ -13,7 +13,7 @@ import { loadTranscriptEvents, resolveSessionTranscriptRuntimeTarget, updateSessionEntry, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { onSessionTranscriptUpdate } from "../../sessions/transcript-events.js"; @@ -175,7 +175,7 @@ describe("worker transcript commit application", () => { store: path.join(root, "agents", "{agentId}", "sessions", "sessions.json"), }, }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: SESSION_KEY, storePath }, { lifecycleRevision: "worker-original-revision", diff --git a/src/gateway/worker-environments/worker-turn-launcher.test.ts b/src/gateway/worker-environments/worker-turn-launcher.test.ts index 149dcf256629..df0fe1f7ce8c 100644 --- a/src/gateway/worker-environments/worker-turn-launcher.test.ts +++ b/src/gateway/worker-environments/worker-turn-launcher.test.ts @@ -25,7 +25,7 @@ import { configureExecutionIdentityAdmissionSink, type ExecutionIdentityAdmissionWork, } from "../../audit/execution-identity-admission.js"; -import { upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../../config/sessions/session-accessor.js"; import { type AgentEventPayload, getAgentEventLifecycleGeneration, @@ -155,7 +155,7 @@ describe("worker turn launcher", () => { sessionKey: SESSION_KEY, storePath: path.join(root, "sessions.json"), }; - await upsertSessionEntry(sessionTarget, { + await upsertSessionEntryCore(sessionTarget, { sessionId: SESSION_ID, updatedAt: Date.now(), }); @@ -186,7 +186,7 @@ describe("worker turn launcher", () => { } it("rejects a transcript target after its session key is rebound", async () => { - await upsertSessionEntry(sessionTarget, { + await upsertSessionEntryCore(sessionTarget, { sessionId: "replacement-session", updatedAt: Date.now() + 1, }); @@ -687,7 +687,7 @@ describe("worker turn launcher", () => { sessionKey: "agent:worker-agent:worker-turn", }; sessionFile = sessionTarget.sessionKey; - await upsertSessionEntry(sessionTarget, { + await upsertSessionEntryCore(sessionTarget, { sessionId: SESSION_ID, updatedAt: Date.now(), }); diff --git a/src/hooks/bundled/session-memory/handler.ts b/src/hooks/bundled/session-memory/handler.ts index 14c2b9a0da03..97e0be85cecb 100644 --- a/src/hooks/bundled/session-memory/handler.ts +++ b/src/hooks/bundled/session-memory/handler.ts @@ -15,7 +15,7 @@ import { } from "../../../agents/agent-scope.js"; import { resolveUserTimezone } from "../../../agents/date-time.js"; import { resolveStateDir } from "../../../config/paths.js"; -import { resolveStorePath } from "../../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../config/sessions/paths.js"; import { loadTranscriptEvents, readSessionTranscriptBoundedMessageTailPage, @@ -284,7 +284,8 @@ async function saveSessionMemoryNow( agentId, sessionId: currentSessionId, sessionKey: event.sessionKey, - storePath: contextStorePath ?? resolveStorePath(cfg?.session?.store, { agentId }), + storePath: + contextStorePath ?? resolveSessionStorePathCore(cfg?.session?.store, { agentId }), }, messageCount, capturedEvents, @@ -402,7 +403,7 @@ const saveSessionToMemory: HookHandler = (event) => { const storePath = typeof context.storePath === "string" && context.storePath.trim() ? context.storePath.trim() - : resolveStorePath(cfg?.session?.store, { agentId }); + : resolveSessionStorePathCore(cfg?.session?.store, { agentId }); const hookConfig = resolveHookConfig(cfg, "session-memory"); const messageCount = typeof hookConfig?.messages === "number" && hookConfig.messages > 0 diff --git a/src/hooks/frontmatter.test.ts b/src/hooks/frontmatter.test.ts index edb0cd0d12a5..eaa2eaadd92d 100644 --- a/src/hooks/frontmatter.test.ts +++ b/src/hooks/frontmatter.test.ts @@ -3,7 +3,7 @@ import { expectDefined } from "@openclaw/normalization-core"; import { describe, expect, it } from "vitest"; import { parseHookFrontmatter, - resolveOpenClawMetadata, + resolveHookManifestMetadata, resolveHookInvocationPolicy, } from "./frontmatter.js"; import type { OpenClawHookMetadata } from "./types.js"; @@ -163,7 +163,7 @@ description: 'single-quoted' }); }); -describe("resolveOpenClawMetadata", () => { +describe("resolveHookManifestMetadata", () => { it("extracts openclaw metadata from parsed frontmatter", () => { const frontmatter = { name: "test-hook", @@ -179,7 +179,7 @@ describe("resolveOpenClawMetadata", () => { }), }; - const result = resolveOpenClawMetadata(frontmatter); + const result = resolveHookManifestMetadata(frontmatter); const openclaw = requireOpenClawMetadata(result); expect(openclaw.emoji).toBe("🔥"); expect(openclaw.events).toEqual(["command:new", "command:reset"]); @@ -189,7 +189,7 @@ describe("resolveOpenClawMetadata", () => { it("returns undefined when metadata is missing", () => { const frontmatter = { name: "no-metadata" }; - const result = resolveOpenClawMetadata(frontmatter); + const result = resolveHookManifestMetadata(frontmatter); expect(result).toBeUndefined(); }); @@ -197,7 +197,7 @@ describe("resolveOpenClawMetadata", () => { const frontmatter = { metadata: JSON.stringify({ other: "data" }), }; - const result = resolveOpenClawMetadata(frontmatter); + const result = resolveHookManifestMetadata(frontmatter); expect(result).toBeUndefined(); }); @@ -205,7 +205,7 @@ describe("resolveOpenClawMetadata", () => { const frontmatter = { metadata: "not valid json {", }; - const result = resolveOpenClawMetadata(frontmatter); + const result = resolveHookManifestMetadata(frontmatter); expect(result).toBeUndefined(); }); @@ -222,7 +222,7 @@ describe("resolveOpenClawMetadata", () => { }), }; - const result = resolveOpenClawMetadata(frontmatter); + const result = resolveHookManifestMetadata(frontmatter); expect(result?.install).toHaveLength(2); expect(expectDefined(result?.install?.[0], "result?.install?.[0] test invariant").kind).toBe( "bundled", @@ -245,7 +245,7 @@ describe("resolveOpenClawMetadata", () => { }), }; - const result = resolveOpenClawMetadata(frontmatter); + const result = resolveHookManifestMetadata(frontmatter); expect(result?.os).toEqual(["darwin", "linux"]); }); @@ -276,7 +276,7 @@ metadata: '"command:reset"', ); - const openclaw = requireOpenClawMetadata(resolveOpenClawMetadata(frontmatter)); + const openclaw = requireOpenClawMetadata(resolveHookManifestMetadata(frontmatter)); expect(openclaw.emoji).toBe("💾"); expect(openclaw.events).toEqual(["command:new", "command:reset", "session:auto-reset"]); expect(openclaw.requires?.config).toEqual(["workspace.dir"]); @@ -296,7 +296,7 @@ metadata: --- `; const frontmatter = parseHookFrontmatter(content); - const openclaw = resolveOpenClawMetadata(frontmatter); + const openclaw = resolveHookManifestMetadata(frontmatter); expect(openclaw?.emoji).toBe("disk"); expect(openclaw?.events).toEqual(["command:new"]); }); diff --git a/src/hooks/frontmatter.ts b/src/hooks/frontmatter.ts index c8f82a2cf237..dc83757c2cde 100644 --- a/src/hooks/frontmatter.ts +++ b/src/hooks/frontmatter.ts @@ -48,7 +48,7 @@ function parseInstallSpec(input: unknown): HookInstallSpec | undefined { } /** Resolve OpenClaw hook metadata from the manifest block in HOOK.md frontmatter. */ -export function resolveOpenClawMetadata( +export function resolveHookManifestMetadata( frontmatter: ParsedHookFrontmatter, ): OpenClawHookMetadata | undefined { const metadataObj = resolveOpenClawManifestBlock({ frontmatter }); diff --git a/src/hooks/plugin-hooks.ts b/src/hooks/plugin-hooks.ts index 4a7e31b068a8..d737de099ac0 100644 --- a/src/hooks/plugin-hooks.ts +++ b/src/hooks/plugin-hooks.ts @@ -5,9 +5,9 @@ import type { OpenClawConfig } from "../config/types.openclaw.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; import { normalizePluginsConfigWithResolver, - resolveEffectivePluginActivationState, - resolveMemorySlotDecision, + resolvePolicyPluginActivationState, } from "../plugins/config-policy.js"; +import { resolveMemorySlotDecision } from "../plugins/config-state.js"; import { loadPluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js"; import { hasKind } from "../plugins/slots.js"; import { isPathInsideWithRealpath } from "../security/scan-paths.js"; @@ -51,7 +51,7 @@ export function resolvePluginHookDirs(params: { if (!record.hooks || record.hooks.length === 0) { continue; } - const activationState = resolveEffectivePluginActivationState({ + const activationState = resolvePolicyPluginActivationState({ id: record.id, origin: record.origin, config: normalizedPlugins, diff --git a/src/hooks/session-auto-reset.ts b/src/hooks/session-auto-reset.ts index 042f83119ea1..1cf38b7d8edb 100644 --- a/src/hooks/session-auto-reset.ts +++ b/src/hooks/session-auto-reset.ts @@ -1,6 +1,6 @@ import { resolveAgentWorkspaceDir, resolveSessionAgentId } from "../agents/agent-scope.js"; import { parseSqliteSessionFileMarker } from "../config/sessions/legacy-sqlite-marker.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { logVerbose } from "../globals.js"; import type { PluginHookSessionEndReason } from "../plugins/hook-types.js"; @@ -53,7 +53,7 @@ export function emitSessionAutoResetHook(params: { storePath: params.storePath ?? marker?.storePath ?? - resolveStorePath(params.cfg.session?.store, { agentId }), + resolveSessionStorePathCore(params.cfg.session?.store, { agentId }), sessionEntry: { sessionId: params.sessionId, sessionFile: params.sessionFile, diff --git a/src/hooks/workspace.ts b/src/hooks/workspace.ts index 1023f0b088a4..3004ebffd6bc 100644 --- a/src/hooks/workspace.ts +++ b/src/hooks/workspace.ts @@ -14,7 +14,7 @@ import { resolveBundledHooksDir } from "./bundled-dir.js"; import { parseHookFrontmatter, resolveHookInvocationPolicy, - resolveOpenClawMetadata, + resolveHookManifestMetadata, } from "./frontmatter.js"; import { resolvePluginHookDirs } from "./plugin-hooks.js"; import { resolveHookEntries } from "./policy.js"; @@ -218,7 +218,7 @@ function loadHookEntriesFromDir(params: { pluginId: params.pluginId, }, frontmatter, - metadata: resolveOpenClawMetadata(frontmatter), + metadata: resolveHookManifestMetadata(frontmatter), invocation: resolveHookInvocationPolicy(frontmatter), }; return entry; diff --git a/src/infra/advertised-lan-host.test.ts b/src/infra/advertised-lan-host.test.ts index 7f3b7508f7fb..f4447091458e 100644 --- a/src/infra/advertised-lan-host.test.ts +++ b/src/infra/advertised-lan-host.test.ts @@ -1,9 +1,9 @@ // Tests route-aware LAN advertisement host selection. import { describe, expect, it, vi } from "vitest"; -import { resolveAdvertisedLanHost } from "./advertised-lan-host.js"; +import { resolveAdvertisedLanHostCore } from "./advertised-lan-host.js"; import type { NetworkInterfacesSnapshot } from "./network-interfaces.js"; -type ResolveOptions = NonNullable[0]>; +type ResolveOptions = NonNullable[0]>; type RouteRunner = NonNullable; function ipv4(address: string, family: "IPv4" | 4 = "IPv4") { @@ -30,7 +30,7 @@ describe("advertised LAN host", () => { const runner = createRouteRunner(""); await expect( - resolveAdvertisedLanHost({ + resolveAdvertisedLanHostCore({ platform: "aix", runCommandWithTimeout: runner, networkInterfaces: () => @@ -54,7 +54,7 @@ describe("advertised LAN host", () => { ); await expect( - resolveAdvertisedLanHost({ + resolveAdvertisedLanHostCore({ platform: "win32", runCommandWithTimeout: runner, networkInterfaces: () => @@ -79,7 +79,7 @@ describe("advertised LAN host", () => { it("falls back to interface order when Linux route hints do not match", async () => { await expect( - resolveAdvertisedLanHost({ + resolveAdvertisedLanHostCore({ platform: "linux", runCommandWithTimeout: createRouteRunner("default via 100.64.0.1 dev tailscale0 metric 10"), networkInterfaces: () => @@ -93,7 +93,7 @@ describe("advertised LAN host", () => { it("uses the macOS default-route interface", async () => { await expect( - resolveAdvertisedLanHost({ + resolveAdvertisedLanHostCore({ platform: "darwin", runCommandWithTimeout: createRouteRunner(" route to: default\ninterface: en9\n"), networkInterfaces: () => @@ -107,7 +107,7 @@ describe("advertised LAN host", () => { it("uses the first Linux default-route interface", async () => { await expect( - resolveAdvertisedLanHost({ + resolveAdvertisedLanHostCore({ platform: "linux", runCommandWithTimeout: createRouteRunner( "default via 192.168.1.1 dev wlan0 proto dhcp metric 600\ndefault via 10.0.0.1 dev eth0 metric 1000", @@ -129,7 +129,7 @@ describe("advertised LAN host", () => { })); await expect( - resolveAdvertisedLanHost({ + resolveAdvertisedLanHostCore({ platform: "win32", runCommandWithTimeout: runner, networkInterfaces: () => diff --git a/src/infra/advertised-lan-host.ts b/src/infra/advertised-lan-host.ts index a98d1a7fcb2e..1ff9b2721458 100644 --- a/src/infra/advertised-lan-host.ts +++ b/src/infra/advertised-lan-host.ts @@ -224,7 +224,7 @@ async function resolveDefaultRouteHints(params: { return []; } -export async function resolveAdvertisedLanHost( +export async function resolveAdvertisedLanHostCore( options: ResolveAdvertisedLanHostOptions = {}, ): Promise { const candidates = listAdvertisedLanHostCandidates( diff --git a/src/infra/approval-request-account-binding.ts b/src/infra/approval-request-account-binding.ts index 3b476ae67339..d905a8692696 100644 --- a/src/infra/approval-request-account-binding.ts +++ b/src/infra/approval-request-account-binding.ts @@ -1,6 +1,6 @@ // Matches approval requests against channel account and session bindings. import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { loadSessionEntryReadOnly } from "../config/sessions/session-accessor.js"; import type { SessionEntry } from "../config/sessions/types.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; @@ -123,7 +123,7 @@ export function resolvePersistedApprovalRequestSessionEntry(params: { } const parsed = parseAgentSessionKey(sessionKey); const agentId = parsed?.agentId ?? params.request.request.agentId ?? "main"; - const storePath = resolveStorePath(params.cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId }); const entry = loadSessionEntryReadOnly({ storePath, sessionKey, diff --git a/src/infra/boundary-path.test.ts b/src/infra/boundary-path.test.ts index cd7569eab94b..c8266d39c955 100644 --- a/src/infra/boundary-path.test.ts +++ b/src/infra/boundary-path.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { resolveRootPath, resolveRootPathSync } from "./boundary-path.js"; import { isPathInside } from "./path-guards.js"; @@ -20,7 +20,7 @@ describe("resolveRootPath", () => { return; } - await withTempDir({ prefix: "openclaw-boundary-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-boundary-path-" }, async (base) => { const root = path.join(base, "workspace"); const targetDir = path.join(root, "target-dir"); const linkPath = path.join(root, "alias"); @@ -47,7 +47,7 @@ describe("resolveRootPath", () => { return; } - await withTempDir({ prefix: "openclaw-boundary-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-boundary-path-" }, async (base) => { const root = path.join(base, "workspace"); const outside = path.join(base, "outside"); const linkPath = path.join(root, "alias-out"); @@ -78,7 +78,7 @@ describe("resolveRootPath", () => { return; } - await withTempDir({ prefix: "openclaw-boundary-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-boundary-path-" }, async (base) => { const root = path.join(base, "workspace"); const outside = path.join(base, "outside"); const outsideFile = path.join(outside, "target.txt"); @@ -114,7 +114,7 @@ describe("resolveRootPath", () => { return; } - await withTempDir({ prefix: "openclaw-boundary-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-boundary-path-" }, async (base) => { const root = path.join(base, "workspace"); const aliasRoot = path.join(base, "workspace-alias"); const fileName = "plugin.js"; @@ -145,7 +145,7 @@ describe("resolveRootPath", () => { return; } - await withTempDir({ prefix: "openclaw-boundary-path-fuzz-" }, async (base) => { + await withTestDir({ prefix: "openclaw-boundary-path-fuzz-" }, async (base) => { const root = path.join(base, "workspace"); const outside = path.join(base, "outside"); const safeTarget = path.join(root, "safe-target"); diff --git a/src/infra/brew.test.ts b/src/infra/brew.test.ts index 8536d8481654..f0057b86c3b9 100644 --- a/src/infra/brew.test.ts +++ b/src/infra/brew.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withEnvAsync } from "../test-utils/env.js"; import { resolveBrewExecutable, resolveBrewPathDirs } from "./brew.js"; @@ -31,7 +31,7 @@ describe("brew helpers", () => { } it("resolves brew from ~/.linuxbrew/bin when executable exists", async () => { - await withTempDir({ prefix: "openclaw-brew-" }, async (tmp) => { + await withTestDir({ prefix: "openclaw-brew-" }, async (tmp) => { const homebrewBin = path.join(tmp, ".linuxbrew", "bin"); const brewPath = path.join(homebrewBin, "brew"); await writeExecutable(brewPath); @@ -43,7 +43,7 @@ describe("brew helpers", () => { }); it("resolves brew from absolute PATH entries for non-standard installs", async () => { - await withTempDir({ prefix: "openclaw-brew-" }, async (tmp) => { + await withTestDir({ prefix: "openclaw-brew-" }, async (tmp) => { const customBin = path.join(tmp, "custom-homebrew", "bin"); const customBrew = path.join(customBin, "brew"); await writeExecutable(customBrew); @@ -55,7 +55,7 @@ describe("brew helpers", () => { }); it("ignores HOMEBREW_BREW_FILE and HOMEBREW_PREFIX by default", async () => { - await withTempDir({ prefix: "openclaw-brew-" }, async (tmp) => { + await withTestDir({ prefix: "openclaw-brew-" }, async (tmp) => { const explicit = path.join(tmp, "custom", "brew"); const prefix = path.join(tmp, "prefix"); const prefixBin = path.join(prefix, "bin"); @@ -86,7 +86,7 @@ describe("brew helpers", () => { }); it("ignores blank HOMEBREW_BREW_FILE and HOMEBREW_PREFIX values", async () => { - await withTempDir({ prefix: "openclaw-brew-" }, async (tmp) => { + await withTestDir({ prefix: "openclaw-brew-" }, async (tmp) => { const homebrewBin = path.join(tmp, ".linuxbrew", "bin"); const brewPath = path.join(homebrewBin, "brew"); await writeExecutable(brewPath); @@ -110,7 +110,7 @@ describe("brew helpers", () => { }); it("does not resolve brew from PATH entries", async () => { - await withTempDir({ prefix: "openclaw-brew-" }, async (tmp) => { + await withTestDir({ prefix: "openclaw-brew-" }, async (tmp) => { const pathBin = path.join(tmp, "path-bin"); const pathBrew = path.join(pathBin, "brew"); await writeExecutable(pathBrew); diff --git a/src/infra/build-stamp.test.ts b/src/infra/build-stamp.test.ts index 5e32e138c9f2..480e19494163 100644 --- a/src/infra/build-stamp.test.ts +++ b/src/infra/build-stamp.test.ts @@ -3,11 +3,11 @@ import fs from "node:fs/promises"; import { describe, expect, it } from "vitest"; import { writeBuildStamp } from "../../scripts/build-stamp.mts"; import { BUILD_STAMP_FILE } from "../../scripts/lib/local-build-metadata-paths.mts"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; describe("build-stamp script", () => { it("writes dist/.buildstamp with the current git head", async () => { - await withTempDir({ prefix: "openclaw-build-stamp-" }, async (tmp) => { + await withTestDir({ prefix: "openclaw-build-stamp-" }, async (tmp) => { const stampPath = writeBuildStamp({ cwd: tmp, now: () => 1_700_000_000_000, diff --git a/src/infra/clawhub-client.test.ts b/src/infra/clawhub-client.test.ts index 2697e58f60e4..7ada3994624b 100644 --- a/src/infra/clawhub-client.test.ts +++ b/src/infra/clawhub-client.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { captureEnv, setTestEnvValue } from "../test-utils/env.js"; import { fetchClawHubSkillInstallResolution, @@ -80,7 +80,7 @@ describe("clawhub client", () => { }); it("loads ClawHub request auth from config.json", async () => { - await withTempDir({ prefix: "openclaw-clawhub-config-" }, async (configRoot) => { + await withTestDir({ prefix: "openclaw-clawhub-config-" }, async (configRoot) => { const configPath = path.join(configRoot, "clawhub", "config.json"); process.env.CLAWHUB_CONFIG_PATH = configPath; await fs.mkdir(path.dirname(configPath), { recursive: true }); @@ -95,7 +95,7 @@ describe("clawhub client", () => { }); it("loads ClawHub request auth from the legacy config path override", async () => { - await withTempDir({ prefix: "openclaw-clawdhub-config-" }, async (configRoot) => { + await withTestDir({ prefix: "openclaw-clawdhub-config-" }, async (configRoot) => { const configPath = path.join(configRoot, "config.json"); process.env.CLAWDHUB_CONFIG_PATH = configPath; await fs.writeFile(configPath, JSON.stringify({ token: "fixture-legacy-token" }), "utf8"); @@ -107,7 +107,7 @@ describe("clawhub client", () => { it.runIf(process.platform === "darwin")( "loads ClawHub request auth from the macOS Application Support path", async () => { - await withTempDir({ prefix: "openclaw-clawhub-home-" }, async (fakeHome) => { + await withTestDir({ prefix: "openclaw-clawhub-home-" }, async (fakeHome) => { const configPath = path.join( fakeHome, "Library", @@ -131,8 +131,8 @@ describe("clawhub client", () => { it.runIf(process.platform === "darwin")( "falls back to XDG_CONFIG_HOME for ClawHub request auth on macOS", async () => { - await withTempDir({ prefix: "openclaw-clawhub-home-" }, async (fakeHome) => { - await withTempDir({ prefix: "openclaw-clawhub-xdg-" }, async (xdgRoot) => { + await withTestDir({ prefix: "openclaw-clawhub-home-" }, async (fakeHome) => { + await withTestDir({ prefix: "openclaw-clawhub-xdg-" }, async (xdgRoot) => { const configPath = path.join(xdgRoot, "clawhub", "config.json"); const homedirSpy = vi.spyOn(os, "homedir").mockReturnValue(fakeHome); setTestEnvValue("XDG_CONFIG_HOME", xdgRoot); diff --git a/src/infra/crypto-digest.test.ts b/src/infra/crypto-digest.test.ts index 9dfc65407bd7..58ab53e999ec 100644 --- a/src/infra/crypto-digest.test.ts +++ b/src/infra/crypto-digest.test.ts @@ -1,14 +1,14 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { sha256Base64, sha256Base64Url, sha256Base64UrlPrefix, sha256File, sha256Hex, - sha256HexPrefix, + sha256HexPrefixCore, } from "./crypto-digest.js"; const HOSTILE_BYTES = Uint8Array.from([0, 255, 128, 195, 40, 226, 40, 161]); @@ -31,12 +31,12 @@ describe("crypto digest helpers", () => { }); it("returns an exact hexadecimal prefix", () => { - expect(sha256HexPrefix(HOSTILE_BYTES, 12)).toBe(HOSTILE_BYTES_SHA256.slice(0, 12)); + expect(sha256HexPrefixCore(HOSTILE_BYTES, 12)).toBe(HOSTILE_BYTES_SHA256.slice(0, 12)); expect(sha256Base64UrlPrefix(HOSTILE_BYTES, 12)).toBe("vYi9pIAlu894"); }); it("streams file bytes through the same digest contract", async () => { - await withTempDir({ prefix: "openclaw-crypto-digest-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-crypto-digest-" }, async (dir) => { const filePath = path.join(dir, "hostile.bin"); await fs.writeFile(filePath, HOSTILE_BYTES); @@ -45,7 +45,7 @@ describe("crypto digest helpers", () => { }); it("preserves stream failure context when hashing a file", async () => { - await withTempDir({ prefix: "openclaw-crypto-digest-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-crypto-digest-" }, async (dir) => { const filePath = path.join(dir, "missing.bin"); await expect(sha256File(filePath)).rejects.toMatchObject({ diff --git a/src/infra/crypto-digest.ts b/src/infra/crypto-digest.ts index 571abbe9bfce..2e14cc8d0711 100644 --- a/src/infra/crypto-digest.ts +++ b/src/infra/crypto-digest.ts @@ -19,7 +19,7 @@ export function sha256Base64UrlPrefix(input: DigestInput, length: number): strin return sha256Base64Url(input).slice(0, length); } -export function sha256HexPrefix(input: DigestInput, length: number): string { +export function sha256HexPrefixCore(input: DigestInput, length: number): string { return sha256Hex(input).slice(0, length); } diff --git a/src/infra/detect-package-manager.test.ts b/src/infra/detect-package-manager.test.ts index 1db7aee0ffff..da020ebaf252 100644 --- a/src/infra/detect-package-manager.test.ts +++ b/src/infra/detect-package-manager.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withEnvAsync } from "../test-utils/env.js"; import { detectPackageManager } from "./detect-package-manager.js"; @@ -10,7 +10,7 @@ async function withPackageManagerRoot( files: Array<{ path: string; content: string }>, run: (root: string) => Promise, ): Promise { - return await withTempDir({ prefix: "openclaw-detect-pm-" }, async (root) => { + return await withTestDir({ prefix: "openclaw-detect-pm-" }, async (root) => { for (const file of files) { const target = path.join(root, file.path); await fs.mkdir(path.dirname(target), { recursive: true }); @@ -110,7 +110,7 @@ describe("detectPackageManager", () => { )( "detects $manager ownership for $packageFormat packages in $layout layouts", async ({ manager, layout, shrinkwrap }) => { - await withTempDir({ prefix: `openclaw-detect-pm-${manager}-` }, async (base) => { + await withTestDir({ prefix: `openclaw-detect-pm-${manager}-` }, async (base) => { const bunInstall = path.join(base, "custom-bun-home"); const nodeModulesRoot = manager === "bun" @@ -137,7 +137,7 @@ describe("detectPackageManager", () => { ); it("keeps pnpm 11 ownership through markerless global virtual-store symlinks", async () => { - await withTempDir({ prefix: "openclaw-detect-pm-pnpm-global-store-" }, async (base) => { + await withTestDir({ prefix: "openclaw-detect-pm-pnpm-global-store-" }, async (base) => { const globalRoot = path.join(base, "pnpm-home", "global", "v11"); const installRoot = path.join(globalRoot, "install-openclaw"); const linkedPackageRoot = path.join(installRoot, "node_modules", "openclaw"); diff --git a/src/infra/device-identity-coordinator-paths.ts b/src/infra/device-identity-coordinator-paths.ts index 958d3e12581f..8719a2e39c9a 100644 --- a/src/infra/device-identity-coordinator-paths.ts +++ b/src/infra/device-identity-coordinator-paths.ts @@ -1,11 +1,11 @@ import path from "node:path"; import { resolveGatewayLockDir } from "../config/paths.js"; import { resolvePathViaExistingAncestorSync } from "./boundary-path.js"; -import { sha256HexPrefix } from "./crypto-digest.js"; +import { sha256HexPrefixCore } from "./crypto-digest.js"; function resolveDeviceIdentityCoordinatorFilename(databasePath: string): string { const canonicalPath = resolvePathViaExistingAncestorSync(databasePath); - const databaseHash = sha256HexPrefix(canonicalPath, 8); + const databaseHash = sha256HexPrefixCore(canonicalPath, 8); return `device-identity.${databaseHash}.lock.sqlite`; } diff --git a/src/infra/diagnostic-error-metadata.ts b/src/infra/diagnostic-error-metadata.ts index 7015fa711841..3838796e8aa9 100644 --- a/src/infra/diagnostic-error-metadata.ts +++ b/src/infra/diagnostic-error-metadata.ts @@ -1,5 +1,5 @@ // Extracts provider diagnostic metadata from error objects and text. -import { sha256HexPrefix } from "./crypto-digest.js"; +import { sha256HexPrefixCore } from "./crypto-digest.js"; const HTTP_STATUS_MIN = 100; const HTTP_STATUS_MAX = 599; @@ -85,7 +85,7 @@ function normalizeProviderRequestId(value: unknown): string | undefined { } function hashDiagnosticIdentifier(value: string): string { - return `sha256:${sha256HexPrefix(value, REQUEST_ID_HASH_PREFIX_LEN)}`; + return `sha256:${sha256HexPrefixCore(value, REQUEST_ID_HASH_PREFIX_LEN)}`; } function readDirectProviderRequestId(err: unknown): string | undefined { diff --git a/src/infra/exec-approval-forwarder.runtime.ts b/src/infra/exec-approval-forwarder.runtime.ts index dfd406464ab4..2f5f0e12efcf 100644 --- a/src/infra/exec-approval-forwarder.runtime.ts +++ b/src/infra/exec-approval-forwarder.runtime.ts @@ -1,4 +1,4 @@ // Lazy runtime imports keep approval forwarding testable without eagerly loading // channel delivery code. export { resolveExecApprovalSessionTarget } from "./exec-approval-session-target.js"; -export { sendDurableMessageBatch } from "../channels/message/runtime.js"; +export { sendDurableMessageBatchCore } from "../channels/message/runtime.js"; diff --git a/src/infra/exec-approval-forwarder.ts b/src/infra/exec-approval-forwarder.ts index 0cd8ea387550..b7c2e8b0afbf 100644 --- a/src/infra/exec-approval-forwarder.ts +++ b/src/infra/exec-approval-forwarder.ts @@ -48,7 +48,7 @@ import { // chat targets, then sends resolution/expiry notices to the same targets. const log = createSubsystemLogger("gateway/exec-approvals"); type DeliverApprovalPayloads = - typeof import("../channels/message/runtime.js").sendDurableMessageBatch; + typeof import("../channels/message/runtime.js").sendDurableMessageBatchCore; type MaybePromise = T | Promise; type ResolveSessionTargetFn = (params: { cfg: OpenClawConfig; @@ -781,8 +781,8 @@ export function createExecApprovalForwarder( const deliver = deps.deliver ?? (async (params) => { - const { sendDurableMessageBatch } = await loadExecApprovalForwarderRuntime(); - return sendDurableMessageBatch(params); + const { sendDurableMessageBatchCore } = await loadExecApprovalForwarderRuntime(); + return sendDurableMessageBatchCore(params); }); const nowMs = deps.nowMs ?? Date.now; const resolveSessionTarget = deps.resolveSessionTarget ?? defaultResolveSessionTarget; diff --git a/src/infra/exec-approval-session-target.test.ts b/src/infra/exec-approval-session-target.test.ts index ca2fd0b0f147..39cd4308e13b 100644 --- a/src/infra/exec-approval-session-target.test.ts +++ b/src/infra/exec-approval-session-target.test.ts @@ -10,7 +10,7 @@ import { parseRawSessionConversationRef, parseThreadSessionSuffix, } from "../sessions/session-key-utils.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { doesApprovalRequestMatchChannelAccount, doesApprovalRequestSelectChannelAccount, @@ -278,7 +278,7 @@ describe("exec approval session target", () => { }; it("returns null for blank session keys, missing entries, and unresolved targets", async () => { - await withTempDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { + await withTestDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { const storePath = path.join(tmpDir, "sessions.json"); const cfg = await writeStoreFile(storePath, { "agent:main:main": { @@ -301,7 +301,7 @@ describe("exec approval session target", () => { }); it("prefers turn-source routing over stale session delivery state", async () => { - await withTempDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { + await withTestDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { const storePath = path.join(tmpDir, "sessions.json"); const cfg = await writeStoreFile(storePath, { "agent:main:main": { @@ -378,7 +378,7 @@ describe("exec approval session target", () => { ] satisfies PlaceholderStoreCase[])( "$name", async ({ relativeStoreDir, entries, request, expected }) => { - await withTempDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { + await withTestDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { const cfg = await writeStoreFile( path.join(tmpDir, relativeStoreDir, "sessions.json"), entries, @@ -390,7 +390,7 @@ describe("exec approval session target", () => { ); it("preserves string thread ids from the session store", async () => { - await withTempDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { + await withTestDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { const storePath = path.join(tmpDir, "sessions.json"); const cfg = await writeStoreFile(storePath, { "agent:main:main": { @@ -493,7 +493,7 @@ describe("exec approval session target", () => { }); it("falls back to the stored session binding when turn source uses another channel", async () => { - await withTempDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { + await withTestDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { const storePath = path.join(tmpDir, "sessions.json"); const cfg = await writeStoreFile(storePath, { "agent:main:main": { @@ -523,7 +523,7 @@ describe("exec approval session target", () => { }); it("falls back to the session-bound account when no turn-source account is present", async () => { - await withTempDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { + await withTestDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { const storePath = path.join(tmpDir, "sessions.json"); const cfg = await writeStoreFile(storePath, { "agent:main:main": { @@ -550,7 +550,7 @@ describe("exec approval session target", () => { }); it("prefers explicit turn-source accounts over stale session account bindings", async () => { - await withTempDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { + await withTestDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { const storePath = path.join(tmpDir, "sessions.json"); const cfg = await writeStoreFile(storePath, { "agent:main:main": { @@ -579,7 +579,7 @@ describe("exec approval session target", () => { }); it("reconciles plugin-request turn source and session origin targets through the shared helper", async () => { - await withTempDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { + await withTestDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { const storePath = path.join(tmpDir, "sessions.json"); const cfg = await writeStoreFile(storePath, { "agent:main:main": { @@ -600,7 +600,7 @@ describe("exec approval session target", () => { }); it("returns null when explicit turn source conflicts with the session-bound origin target", async () => { - await withTempDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { + await withTestDir({ prefix: "openclaw-exec-approval-session-target-" }, async (tmpDir) => { const storePath = path.join(tmpDir, "sessions.json"); const cfg = await writeStoreFile(storePath, { "agent:main:main": { diff --git a/src/infra/exec-approvals-allow-always.ts b/src/infra/exec-approvals-allow-always.ts index 308984af6034..64fd468c2aa9 100644 --- a/src/infra/exec-approvals-allow-always.ts +++ b/src/infra/exec-approvals-allow-always.ts @@ -1,4 +1,4 @@ -import { sha256HexPrefix } from "./crypto-digest.js"; +import { sha256HexPrefixCore } from "./crypto-digest.js"; // Owns durable approval matching and allow-always persistence. import { canonicalizeExecApprovalPolicyRules } from "./exec-approval-policy-snapshot.js"; import type { ExecApprovalPolicySnapshot } from "./exec-approval-policy-snapshot.js"; @@ -45,11 +45,11 @@ export function hasDurableExecApproval(params: { // already hold `=command:` entries in this format; changing the input // silently orphans every persisted exact-command grant. function buildDurableCommandApprovalPattern(commandText: string): string { - return `=command:${sha256HexPrefix(commandText, 16)}`; + return `=command:${sha256HexPrefixCore(commandText, 16)}`; } function buildNodeCommandApprovalPattern(commandText: string): string { - return `=node-command:${sha256HexPrefix(commandText, 16)}`; + return `=node-command:${sha256HexPrefixCore(commandText, 16)}`; } export function hasNodeCommandAllowAlwaysMarker(params: { diff --git a/src/infra/exec-safe-bin-runtime-policy.test.ts b/src/infra/exec-safe-bin-runtime-policy.test.ts index 5df6b1229388..441ede8235b5 100644 --- a/src/infra/exec-safe-bin-runtime-policy.test.ts +++ b/src/infra/exec-safe-bin-runtime-policy.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { isInterpreterLikeSafeBin, listInterpreterLikeSafeBins, @@ -139,7 +139,7 @@ describe("exec safe-bin runtime policy", () => { it.runIf(process.platform !== "win32")( "expands trusted package-manager symlink dirs to current safe-bin target dirs", async () => { - await withTempDir({ prefix: "openclaw-safe-bin-trusted-symlink-" }, async (root) => { + await withTestDir({ prefix: "openclaw-safe-bin-trusted-symlink-" }, async (root) => { const trustedDir = path.join(root, "bin"); const targetDir = path.join(root, "cellar", "jq", "1.7.1", "bin"); const target = path.join(targetDir, "jq"); @@ -170,7 +170,7 @@ describe("exec safe-bin runtime policy", () => { it.runIf(process.platform !== "win32")( "refreshes trusted package-manager target dirs when safe-bin symlinks retarget", async () => { - await withTempDir({ prefix: "openclaw-safe-bin-trusted-retarget-" }, async (root) => { + await withTestDir({ prefix: "openclaw-safe-bin-trusted-retarget-" }, async (root) => { const trustedDir = path.join(root, "bin"); const targetDir1 = path.join(root, "cellar", "jq", "1.7.1", "bin"); const targetDir2 = path.join(root, "cellar", "jq", "1.8.0", "bin"); @@ -229,7 +229,7 @@ describe("exec safe-bin runtime policy", () => { it.runIf(process.platform !== "win32")( "does not derive target-dir trust from non-executable safe-bin links", async () => { - await withTempDir({ prefix: "openclaw-safe-bin-trusted-nonexec-" }, async (root) => { + await withTestDir({ prefix: "openclaw-safe-bin-trusted-nonexec-" }, async (root) => { const trustedDir = path.join(root, "bin"); const nonExecutableDir = path.join(root, "targets", "nonexec"); const directoryTarget = path.join(root, "targets", "directory"); @@ -289,7 +289,7 @@ describe("exec safe-bin runtime policy", () => { if (process.platform === "win32") { return; } - await withTempDir({ prefix: "openclaw-safe-bin-runtime-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-safe-bin-runtime-" }, async (dir) => { try { await fs.chmod(dir, 0o777); const onWarning = vi.fn(); diff --git a/src/infra/exec-safe-bin-trust.test.ts b/src/infra/exec-safe-bin-trust.test.ts index e2cbb2d6aac4..d36de924e1af 100644 --- a/src/infra/exec-safe-bin-trust.test.ts +++ b/src/infra/exec-safe-bin-trust.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withEnv } from "../test-utils/env.js"; import { getTrustedSafeBinDirs, @@ -73,7 +73,7 @@ describe("exec safe bin trust", () => { }); it("matches trusted dirs through path-local case folding on case-insensitive filesystems", async () => { - await withTempDir({ prefix: "OpenClaw-Safe-Bin-" }, async (dir) => { + await withTestDir({ prefix: "OpenClaw-Safe-Bin-" }, async (dir) => { const swapped = swapAsciiCase(dir); if (swapped === dir) { return; @@ -105,7 +105,7 @@ describe("exec safe bin trust", () => { }); it("keeps case-distinct trusted dirs separate on case-sensitive filesystems", async () => { - await withTempDir({ prefix: "openclaw-safe-bin-case-" }, async (parent) => { + await withTestDir({ prefix: "openclaw-safe-bin-case-" }, async (parent) => { const trustedDir = path.join(parent, "ToolBin"); const untrustedDir = path.join(parent, "toolbin"); await fs.mkdir(trustedDir); @@ -143,7 +143,7 @@ describe("exec safe bin trust", () => { if (process.platform === "win32") { return; } - await withTempDir({ prefix: "openclaw-safe-bin-trust-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-safe-bin-trust-" }, async (dir) => { try { await fs.chmod(dir, 0o777); const hits = listWritableExplicitTrustedSafeBinDirs([dir]); diff --git a/src/infra/executable-path.test.ts b/src/infra/executable-path.test.ts index dbe70362622f..1e75a74de7c7 100644 --- a/src/infra/executable-path.test.ts +++ b/src/infra/executable-path.test.ts @@ -4,7 +4,7 @@ import nodeFs from "node:fs"; import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withMockedPlatform } from "../test-utils/vitest-spies.js"; import { clearExecutablePathCache, @@ -29,7 +29,7 @@ function restoreEnvValue(name: string, value: string | undefined): void { describe("executable path helpers", () => { it("detects regular files and rejects directories", async () => { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const execPath = path.join(base, "tool"); const filePath = path.join(base, "plain.txt"); const dirPath = path.join(base, "dir"); @@ -46,7 +46,7 @@ describe("executable path helpers", () => { }); it("resolves executables from PATH entries and cwd-relative paths", async () => { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const binDir = path.join(base, "bin"); const cwd = path.join(base, "cwd"); await fs.mkdir(binDir, { recursive: true }); @@ -70,7 +70,7 @@ describe("executable path helpers", () => { }); it("memoizes PATH hits and misses until explicit invalidation", async () => { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const binDir = path.join(base, "bin"); await fs.mkdir(binDir); const executable = path.join(binDir, "runner"); @@ -95,7 +95,7 @@ describe("executable path helpers", () => { }); it("slides PATH hit and miss expiry for steady pollers", async () => { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const binDir = path.join(base, "bin"); await fs.mkdir(binDir); const executable = path.join(binDir, "runner"); @@ -128,7 +128,7 @@ describe("executable path helpers", () => { }); it("does not reuse relative PATH probes after cwd changes", async () => { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const firstCwd = path.join(base, "first"); const secondCwd = path.join(base, "second"); const relativeBin = "bin"; @@ -151,7 +151,7 @@ describe("executable path helpers", () => { }); it("resolves absolute, home-relative, and Path-cased env executables", async () => { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const homeDir = path.join(base, "home"); const binDir = path.join(base, "bin"); await fs.mkdir(homeDir, { recursive: true }); @@ -233,7 +233,7 @@ describe("resolveExecutable", () => { it("resolves to the first .cmd result from PATH on Windows without executing where.exe", async () => { await withMockedPlatform("win32", async () => { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const binDir = path.join(base, "bin"); await fs.mkdir(binDir, { recursive: true }); const cmdPath = path.join(binDir, "gcloud.cmd"); @@ -257,7 +257,7 @@ describe("resolveExecutable", () => { it("falls back to .exe when no .cmd match exists on Windows", async () => { await withMockedPlatform("win32", async () => { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const binDir = path.join(base, "bin"); await fs.mkdir(binDir, { recursive: true }); const exePath = path.join(binDir, "tailscale.exe"); @@ -276,7 +276,7 @@ describe("resolveExecutable", () => { it("falls back to first PATH result when no .cmd or .exe match exists on Windows", async () => { await withMockedPlatform("win32", async () => { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const binDir = path.join(base, "bin"); await fs.mkdir(binDir, { recursive: true }); const ps1Path = path.join(binDir, "gcloud.ps1"); @@ -307,7 +307,7 @@ describe("caller env PATHEXT propagation", () => { it.runIf(process.platform === "win32")( "accepts an explicit native executable when PATHEXT omits its suffix", async () => { - await withTempDir({ prefix: "openclaw-explicit-exe-" }, async (base) => { + await withTestDir({ prefix: "openclaw-explicit-exe-" }, async (base) => { const executable = path.join(base, "trusted-probe.exe"); await fs.copyFile(process.execPath, executable); const env = { ...process.env, PATH: base, PATHEXT: ".CMD" }; @@ -327,7 +327,7 @@ describe("caller env PATHEXT propagation", () => { ); it("keeps POSIX execute-permission checks for explicit native-looking paths", async () => { - await withTempDir({ prefix: "openclaw-explicit-posix-" }, async (base) => { + await withTestDir({ prefix: "openclaw-explicit-posix-" }, async (base) => { const executable = path.join(base, "trusted-probe.exe"); await fs.writeFile(executable, "not executable\n", "utf8"); const accessSpy = vi.spyOn(nodeFs, "accessSync").mockImplementation(() => { @@ -348,7 +348,7 @@ describe("caller env PATHEXT propagation", () => { const orig = process.env.PATHEXT; process.env.PATHEXT = ".TXT"; try { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const binDir = path.join(base, "bin"); await fs.mkdir(binDir, { recursive: true }); @@ -369,7 +369,7 @@ describe("caller env PATHEXT propagation", () => { }); it("keeps PATHEXT checks for non-native explicit PATH extensions", async () => { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const binDir = path.join(base, "bin"); await fs.mkdir(binDir, { recursive: true }); const exePath = path.join(binDir, "tool.exe"); @@ -391,7 +391,7 @@ describe("caller env PATHEXT propagation", () => { const orig = process.env.PATHEXT; process.env.PATHEXT = ".TXT"; try { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const ps1File = path.join(base, "script.ps1"); await fs.writeFile(ps1File, 'Write-Output "ok"\n', "utf8"); @@ -410,7 +410,7 @@ describe("caller env PATHEXT propagation", () => { const orig = process.env.PATHEXT; process.env.PATHEXT = ".TXT"; try { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const binDir = path.join(base, "bin"); await fs.mkdir(binDir, { recursive: true }); const ps1Path = path.join(binDir, "runner.ps1"); @@ -429,7 +429,7 @@ describe("caller env PATHEXT propagation", () => { }); it("resolveExecutablePath with path separator falls back to process.env when no caller env given", async () => { - await withTempDir({ prefix: "openclaw-exec-path-" }, async (base) => { + await withTestDir({ prefix: "openclaw-exec-path-" }, async (base) => { const ps1File = path.join(base, "script.ps1"); await fs.writeFile(ps1File, 'Write-Output "ok"\n', "utf8"); diff --git a/src/infra/gateway-lock.ts b/src/infra/gateway-lock.ts index 3a06cf2943c0..259ba92d6d28 100644 --- a/src/infra/gateway-lock.ts +++ b/src/infra/gateway-lock.ts @@ -13,7 +13,7 @@ import { z } from "zod"; import { resolveConfigPath, resolveGatewayLockDir, resolveStateDir } from "../config/paths.js"; import { getFileLockProcessStartTime, isPidAlive } from "../shared/pid-alive.js"; import { safeParseJsonWithSchema } from "../utils/zod-parse.js"; -import { sha256HexPrefix } from "./crypto-digest.js"; +import { sha256HexPrefixCore } from "./crypto-digest.js"; import { createFileLockManager } from "./file-lock-manager.js"; import { isGatewayArgv, @@ -315,7 +315,7 @@ function resolveGatewayLockPaths(env: NodeJS.ProcessEnv, suppliedLockDir?: strin const stateDir = canonicalizeStateDir(resolvedStateDir); const lockDir = suppliedLockDir ?? resolveGatewayLockDir(stateDir); const configPath = resolveConfigPath(env, resolvedStateDir); - const configHash = sha256HexPrefix(configPath, 8); + const configHash = sha256HexPrefixCore(configPath, 8); return { configLockPath: path.join(lockDir, `gateway.${configHash}.lock`), configPath, diff --git a/src/infra/git-root.test.ts b/src/infra/git-root.test.ts index 43e23eeb0adc..c684f6185596 100644 --- a/src/infra/git-root.test.ts +++ b/src/infra/git-root.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { findGitRoot, resolveGitHeadPath } from "./git-root.js"; async function expectGitRootResolution(params: { @@ -11,7 +11,7 @@ async function expectGitRootResolution(params: { temp: string, ) => Promise<{ startPath: string; expectedRoot: string | null; expectedHead: string | null }>; }): Promise { - await withTempDir({ prefix: `openclaw-${params.label}-` }, async (temp) => { + await withTestDir({ prefix: `openclaw-${params.label}-` }, async (temp) => { const { startPath, expectedRoot, expectedHead } = await params.setup(temp); expect(findGitRoot(startPath)).toBe(expectedRoot); expect(resolveGitHeadPath(startPath)).toBe(expectedHead); @@ -102,7 +102,7 @@ describe("git-root", () => { }); it("respects maxDepth traversal limit", async () => { - await withTempDir({ prefix: "openclaw-git-root-depth-" }, async (temp) => { + await withTestDir({ prefix: "openclaw-git-root-depth-" }, async (temp) => { const repoRoot = path.join(temp, "repo"); const nested = path.join(repoRoot, "a", "b", "c"); await fs.mkdir(path.join(repoRoot, ".git"), { recursive: true }); diff --git a/src/infra/heartbeat-outcome-store.test.ts b/src/infra/heartbeat-outcome-store.test.ts index e49772cfa007..9787cbafc4ec 100644 --- a/src/infra/heartbeat-outcome-store.test.ts +++ b/src/infra/heartbeat-outcome-store.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import { closeOpenClawAgentDatabasesForTest, openOpenClawAgentDatabase, @@ -20,7 +20,7 @@ async function createEnv(): Promise { const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-heartbeat-outcome-")); tempDirs.push(stateDir); const env = { OPENCLAW_STATE_DIR: stateDir }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", env, sessionKey: "agent:main:main" }, { sessionId: "heartbeat-outcome-test", updatedAt: 1 }, ); diff --git a/src/infra/heartbeat-runner-config.ts b/src/infra/heartbeat-runner-config.ts index 50c158b6a7ec..9b371365040d 100644 --- a/src/infra/heartbeat-runner-config.ts +++ b/src/infra/heartbeat-runner-config.ts @@ -213,7 +213,7 @@ function resolveHeartbeatPromptRaw(cfg: OpenClawConfig, heartbeat?: HeartbeatCon return heartbeat?.prompt ?? cfg.agents?.defaults?.heartbeat?.prompt; } -export function resolveHeartbeatPrompt(cfg: OpenClawConfig, heartbeat?: HeartbeatConfig) { +export function resolveConfiguredHeartbeatPrompt(cfg: OpenClawConfig, heartbeat?: HeartbeatConfig) { return resolveHeartbeatPromptText(resolveHeartbeatPromptRaw(cfg, heartbeat)); } diff --git a/src/infra/heartbeat-runner-delivery.ts b/src/infra/heartbeat-runner-delivery.ts index 9433426e7f4b..524e3e46caf7 100644 --- a/src/infra/heartbeat-runner-delivery.ts +++ b/src/infra/heartbeat-runner-delivery.ts @@ -5,7 +5,7 @@ import { import { replaceGenericExternalRunFailureText } from "../agents/failover/user-copy.js"; import { copyReplyPayloadMetadata, getReplyPayloadMetadata } from "../auto-reply/reply-payload.js"; import { buildRecoverablePendingFinalDeliveryText } from "../auto-reply/reply/pending-final-delivery.js"; -import { sendDurableMessageBatch } from "../channels/message/runtime.js"; +import { sendDurableMessageBatchCore } from "../channels/message/runtime.js"; import { patchSessionEntryCore } from "../config/sessions/session-accessor.js"; import type { SessionEntry } from "../config/sessions/types.js"; import { formatErrorMessage } from "./errors.js"; @@ -232,7 +232,7 @@ export async function finalizeHeartbeatOutcome(params: { ...(failureChannel !== "none" && failureTarget ? { deliver: async () => { - const send = await sendDurableMessageBatch({ + const send = await sendDurableMessageBatchCore({ cfg, channel: failureChannel, to: failureTarget, @@ -411,7 +411,7 @@ export async function finalizeHeartbeatOutcome(params: { } } - const send = await sendDurableMessageBatch({ + const send = await sendDurableMessageBatchCore({ cfg, channel: delivery.channel, to: delivery.to, diff --git a/src/infra/heartbeat-runner-execution.ts b/src/infra/heartbeat-runner-execution.ts index ac8cdcfae0ca..ad01b325a513 100644 --- a/src/infra/heartbeat-runner-execution.ts +++ b/src/infra/heartbeat-runner-execution.ts @@ -27,7 +27,7 @@ import { import type { ChannelHeartbeatDeps } from "../channels/plugins/types.public.js"; import { createReplyPrefixContext } from "../channels/reply-prefix.js"; import { getRuntimeConfig } from "../config/config.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { applySessionEntryLifecycleMutation, loadExactSessionEntry, @@ -509,7 +509,7 @@ export async function prepareHeartbeatRunStage(wake: ReadyHeartbeatWake) { configuredSessionKey: configuredSession.sessionKey, sessionEntry: entry, }); - const isolatedStorePath = resolveStorePath(cfg.session?.store, { agentId }); + const isolatedStorePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); const staleIsolatedSessionKey = resolveStaleHeartbeatIsolatedSessionKey({ sessionKey, isolatedSessionKey, diff --git a/src/infra/heartbeat-runner-prompt.ts b/src/infra/heartbeat-runner-prompt.ts index 2bb4e6175ee2..a98f7626a217 100644 --- a/src/infra/heartbeat-runner-prompt.ts +++ b/src/infra/heartbeat-runner-prompt.ts @@ -13,7 +13,7 @@ import { } from "./heartbeat-events-filter.js"; import { heartbeatLog, - resolveHeartbeatPrompt, + resolveConfiguredHeartbeatPrompt, resolveHeartbeatResponseToolPrompt, type HeartbeatConfig, } from "./heartbeat-runner-config.js"; @@ -283,7 +283,7 @@ ${completionInstruction}`; }) : baseUsesHeartbeatResponseTool ? resolveHeartbeatResponseToolPrompt(params.cfg, params.heartbeat) - : resolveHeartbeatPrompt(params.cfg, params.heartbeat); + : resolveConfiguredHeartbeatPrompt(params.cfg, params.heartbeat); const basePromptWithDirectives = appendHeartbeatScratch( basePrompt, params.heartbeatScratchContent, diff --git a/src/infra/heartbeat-runner-run.ts b/src/infra/heartbeat-runner-run.ts index 7352c4987e61..dea656c30438 100644 --- a/src/infra/heartbeat-runner-run.ts +++ b/src/infra/heartbeat-runner-run.ts @@ -1,7 +1,7 @@ import { resolveResponsePrefixTemplate } from "../auto-reply/reply/response-prefix-template.js"; import { resolveSourceReplyDeliveryMode } from "../auto-reply/reply/source-reply-delivery-mode.js"; import { HEARTBEAT_TOKEN } from "../auto-reply/tokens.js"; -import { sendDurableMessageBatch } from "../channels/message/runtime.js"; +import { sendDurableMessageBatchCore } from "../channels/message/runtime.js"; import { formatErrorMessage } from "./errors.js"; import { emitHeartbeatEvent, resolveIndicatorType } from "./heartbeat-events.js"; import { @@ -115,7 +115,7 @@ export async function runHeartbeatOnce(opts: HeartbeatRunOptions): Promise { }, session: { scope: "global", dmScope: "per-channel-peer", store: storeTemplate }, }; - const mainStorePath = resolveStorePath(storeTemplate, { agentId: "main" }); - const historianStorePath = resolveStorePath(storeTemplate, { agentId: "historian2" }); + const mainStorePath = resolveSessionStorePathCore(storeTemplate, { agentId: "main" }); + const historianStorePath = resolveSessionStorePathCore(storeTemplate, { + agentId: "historian2", + }); await seedSessionStore(mainStorePath, "global", { lastChannel: "slack", lastProvider: "slack", @@ -122,7 +124,7 @@ describe("runHeartbeatOnce identity", () => { }, session: { scope: "global", store: storeTemplate }, }; - const hooksStorePath = resolveStorePath(storeTemplate, { agentId: "hooks" }); + const hooksStorePath = resolveSessionStorePathCore(storeTemplate, { agentId: "hooks" }); await seedSessionStore(hooksStorePath, "global", {}); enqueueSystemEvent("Mapped hook wake", { sessionKey: "global" }); expect(peekSystemEventEntries("global").map((event) => event.text)).toEqual([ @@ -164,8 +166,16 @@ describe("runHeartbeatOnce identity", () => { }, session: { scope: "global", store: storeTemplate }, }; - await seedSessionStore(resolveStorePath(storeTemplate, { agentId: "alpha" }), "global", {}); - await seedSessionStore(resolveStorePath(storeTemplate, { agentId: "beta" }), "global", {}); + await seedSessionStore( + resolveSessionStorePathCore(storeTemplate, { agentId: "alpha" }), + "global", + {}, + ); + await seedSessionStore( + resolveSessionStorePathCore(storeTemplate, { agentId: "beta" }), + "global", + {}, + ); // Two hook agents complete before the coalesced wakes fire; both events // land in the shared `global` queue with per-agent ownership. enqueueSystemEvent( diff --git a/src/infra/heartbeat-runner.reply-admission.test.ts b/src/infra/heartbeat-runner.reply-admission.test.ts index 8d04229b02df..20192c2d9516 100644 --- a/src/infra/heartbeat-runner.reply-admission.test.ts +++ b/src/infra/heartbeat-runner.reply-admission.test.ts @@ -1,7 +1,7 @@ import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { markCompleteReplyConfig } from "../auto-reply/reply/get-reply-fast-path.test-support.js"; -import { resolveStorePath } from "../config/sessions.js"; +import { resolveSessionStorePathCore } from "../config/sessions.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { runHeartbeatOnce, type HeartbeatDeps } from "./heartbeat-runner.js"; import { getReplyFromConfig } from "./heartbeat-runner.runtime.js"; @@ -104,8 +104,16 @@ describe("heartbeat reply admission", () => { }, { runtimeMode: "full" }, ) as OpenClawConfig; - await seedSessionStore(resolveStorePath(storeTemplate, { agentId: "alpha" }), "global", {}); - await seedSessionStore(resolveStorePath(storeTemplate, { agentId: "beta" }), "global", {}); + await seedSessionStore( + resolveSessionStorePathCore(storeTemplate, { agentId: "alpha" }), + "global", + {}, + ); + await seedSessionStore( + resolveSessionStorePathCore(storeTemplate, { agentId: "beta" }), + "global", + {}, + ); enqueueSystemEvent( "Alpha hook finished", withSystemEventOwner({ sessionKey: "global" }, "alpha"), diff --git a/src/infra/heartbeat-runner.returns-default-unset.test.ts b/src/infra/heartbeat-runner.returns-default-unset.test.ts index 146b35e185c5..bbe03ed79294 100644 --- a/src/infra/heartbeat-runner.returns-default-unset.test.ts +++ b/src/infra/heartbeat-runner.returns-default-unset.test.ts @@ -11,7 +11,7 @@ import { resolveAgentIdFromSessionKey, resolveAgentMainSessionKey, resolveMainSessionKey, - resolveStorePath, + resolveSessionStorePathCore, } from "../config/sessions.js"; import { getActivePluginRegistry, setActivePluginRegistry } from "../plugins/runtime.js"; import { buildAgentPeerSessionKey } from "../routing/session-key.js"; @@ -27,7 +27,7 @@ import { type HeartbeatDeps, isHeartbeatEnabledForAgent, resolveHeartbeatIntervalMs, - resolveHeartbeatPrompt, + resolveConfiguredHeartbeatPrompt, resolveHeartbeatSummaryForAgent, runHeartbeatOnce, } from "./heartbeat-runner.js"; @@ -419,7 +419,7 @@ describe("resolveHeartbeatIntervalMs", () => { }); }); -describe("resolveHeartbeatPrompt", () => { +describe("resolveConfiguredHeartbeatPrompt", () => { it.each([ { name: "default prompt", cfg: {} as OpenClawConfig, expected: HEARTBEAT_PROMPT }, { @@ -430,7 +430,7 @@ describe("resolveHeartbeatPrompt", () => { expected: "ping", }, ])("uses $name", ({ cfg, expected }) => { - expect(resolveHeartbeatPrompt(cfg)).toBe(expected); + expect(resolveConfiguredHeartbeatPrompt(cfg)).toBe(expected); }); }); @@ -1168,7 +1168,7 @@ describe("runHeartbeatOnce", () => { session: { store: storeTemplate }, }; const sessionKey = resolveAgentMainSessionKey({ cfg, agentId }); - const storePath = resolveStorePath(storeTemplate, { agentId }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId }); const sessionsDir = path.dirname(storePath); const sessionId = "sid-ops"; const sessionFile = path.join(sessionsDir, `${sessionId}.jsonl`); @@ -1656,7 +1656,7 @@ describe("runHeartbeatOnce", () => { }; const sessionKey = resolveMainSessionKey(cfg); const agentId = resolveAgentIdFromSessionKey(sessionKey); - const storePath = resolveStorePath(storeTemplate, { agentId }); + const storePath = resolveSessionStorePathCore(storeTemplate, { agentId }); await seedWhatsAppSession(storePath, sessionKey); replySpy.mockResolvedValue({ text: "Hello from heartbeat" }); diff --git a/src/infra/heartbeat-runner.ts b/src/infra/heartbeat-runner.ts index 28f528d6c80f..1c0aca5ab4ba 100644 --- a/src/infra/heartbeat-runner.ts +++ b/src/infra/heartbeat-runner.ts @@ -2,7 +2,7 @@ export type { HeartbeatDeps } from "./heartbeat-runner-execution.js"; export { resolveHeartbeatAgents, - resolveHeartbeatPrompt, + resolveConfiguredHeartbeatPrompt, resolveHeartbeatSchedulerSeed, } from "./heartbeat-runner-config.js"; export { runHeartbeatOnce } from "./heartbeat-runner-run.js"; diff --git a/src/infra/install-flow.test.ts b/src/infra/install-flow.test.ts index 09ddf66797b3..7297216d9bd3 100644 --- a/src/infra/install-flow.test.ts +++ b/src/infra/install-flow.test.ts @@ -2,13 +2,13 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import * as archive from "./archive.js"; import { resolveExistingInstallPath, withExtractedArchiveRoot } from "./install-flow.js"; import * as installSource from "./install-source-utils.js"; async function runExtractedArchiveFailureCase(configureArchive: () => void) { - vi.spyOn(installSource, "withTempDir").mockImplementation( + vi.spyOn(installSource, "withInstallWorkspace").mockImplementation( async (_prefix, fn) => await fn("/tmp/openclaw-install-flow"), ); configureArchive(); @@ -26,7 +26,7 @@ function firstMockCall(mock: { mock: { calls: T[] } }): T | describe("resolveExistingInstallPath", () => { it("returns resolved path and stat for existing files", async () => { - await withTempDir({ prefix: "openclaw-install-flow-" }, async (fixtureRoot) => { + await withTestDir({ prefix: "openclaw-install-flow-" }, async (fixtureRoot) => { const filePath = path.join(fixtureRoot, "plugin.tgz"); await fs.writeFile(filePath, "archive"); @@ -42,7 +42,7 @@ describe("resolveExistingInstallPath", () => { }); it("returns a path-not-found error for missing paths", async () => { - await withTempDir({ prefix: "openclaw-install-flow-" }, async (fixtureRoot) => { + await withTestDir({ prefix: "openclaw-install-flow-" }, async (fixtureRoot) => { const missing = path.join(fixtureRoot, "missing.tgz"); const result = await resolveExistingInstallPath(missing); @@ -66,7 +66,7 @@ describe("withExtractedArchiveRoot", () => { const extractDir = path.join(tmpRoot, "extract"); const packageRoot = path.join(extractDir, "package"); const withTempDirSpy = vi - .spyOn(installSource, "withTempDir") + .spyOn(installSource, "withInstallWorkspace") .mockImplementation(async (_prefix, fn) => await fn(tmpRoot)); const extractSpy = vi.spyOn(archive, "extractArchive").mockResolvedValue(undefined); const resolveRootSpy = vi.spyOn(archive, "resolvePackedRootDir").mockResolvedValue(packageRoot); diff --git a/src/infra/install-flow.ts b/src/infra/install-flow.ts index 9de20be6642c..f1c71549ecec 100644 --- a/src/infra/install-flow.ts +++ b/src/infra/install-flow.ts @@ -5,7 +5,7 @@ import path from "node:path"; import { resolveUserPath } from "../utils.js"; import { type ArchiveLogger, extractArchive, resolvePackedRootDir } from "./archive.js"; import { pathExists } from "./fs-safe.js"; -import { withTempDir } from "./install-source-utils.js"; +import { withInstallWorkspace } from "./install-source-utils.js"; // Install-flow helpers validate local install paths and unpack archives inside // temporary workspaces before handing the resolved package root to callers. @@ -41,7 +41,7 @@ export async function withExtractedArchiveRoot( rootMarkers?: readonly string[]; onExtracted: (rootDir: string) => Promise; }): Promise { - return await withTempDir(params.tempDirPrefix, async (tmpDir) => { + return await withInstallWorkspace(params.tempDirPrefix, async (tmpDir) => { const extractDir = path.join(tmpDir, "extract"); await fs.mkdir(extractDir, { recursive: true }); diff --git a/src/infra/install-safe-path.test.ts b/src/infra/install-safe-path.test.ts index 00a706ad640c..ff41f27be021 100644 --- a/src/infra/install-safe-path.test.ts +++ b/src/infra/install-safe-path.test.ts @@ -4,7 +4,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { assertCanonicalPathWithinBase, packageNameMatchesId, @@ -127,7 +127,7 @@ describe("resolveSafeInstallDir", () => { describe("assertCanonicalPathWithinBase", () => { it("accepts in-base directories", async () => { - await withTempDir({ prefix: "openclaw-install-safe-" }, async (baseDir) => { + await withTestDir({ prefix: "openclaw-install-safe-" }, async (baseDir) => { const candidate = path.join(baseDir, "tools"); await fs.mkdir(candidate, { recursive: true }); await expect( @@ -141,7 +141,7 @@ describe("assertCanonicalPathWithinBase", () => { }); it("accepts missing candidate paths when their parent stays in base", async () => { - await withTempDir({ prefix: "openclaw-install-safe-" }, async (baseDir) => { + await withTestDir({ prefix: "openclaw-install-safe-" }, async (baseDir) => { const candidate = path.join(baseDir, "tools", "plugin"); await fs.mkdir(path.dirname(candidate), { recursive: true }); await expect( @@ -155,7 +155,7 @@ describe("assertCanonicalPathWithinBase", () => { }); it("rejects non-directory base paths", async () => { - await withTempDir({ prefix: "openclaw-install-safe-" }, async (baseDir) => { + await withTestDir({ prefix: "openclaw-install-safe-" }, async (baseDir) => { const baseFile = path.join(baseDir, "not-a-dir"); await fs.writeFile(baseFile, "nope", "utf-8"); await expect( @@ -169,7 +169,7 @@ describe("assertCanonicalPathWithinBase", () => { }); it("rejects non-directory candidate paths inside the base", async () => { - await withTempDir({ prefix: "openclaw-install-safe-" }, async (baseDir) => { + await withTestDir({ prefix: "openclaw-install-safe-" }, async (baseDir) => { const candidate = path.join(baseDir, "file.txt"); await fs.writeFile(candidate, "nope", "utf-8"); await expect( @@ -185,8 +185,8 @@ describe("assertCanonicalPathWithinBase", () => { it.skipIf(!canCreateDirectorySymlinks)( "rejects symlinked candidate directories that escape the base", async () => { - await withTempDir({ prefix: "openclaw-install-safe-" }, async (baseDir) => { - await withTempDir({ prefix: "openclaw-install-safe-outside-" }, async (outsideDir) => { + await withTestDir({ prefix: "openclaw-install-safe-" }, async (baseDir) => { + await withTestDir({ prefix: "openclaw-install-safe-outside-" }, async (outsideDir) => { const linkDir = path.join(baseDir, "alias"); await fs.symlink(outsideDir, linkDir, directorySymlinkType); await expect( @@ -204,7 +204,7 @@ describe("assertCanonicalPathWithinBase", () => { it.skipIf(!canCreateDirectorySymlinks)( "accepts symlinked base directories when the target stays in the real base", async () => { - await withTempDir({ prefix: "openclaw-install-safe-" }, async (parentDir) => { + await withTestDir({ prefix: "openclaw-install-safe-" }, async (parentDir) => { const realBaseDir = path.join(parentDir, "real-base"); const symlinkBaseDir = path.join(parentDir, "base-link"); await fs.mkdir(realBaseDir, { recursive: true }); @@ -223,7 +223,7 @@ describe("assertCanonicalPathWithinBase", () => { it.skipIf(!canCreateDirectorySymlinks)( "rejects nested symlinked candidate directories", async () => { - await withTempDir({ prefix: "openclaw-install-safe-" }, async (parentDir) => { + await withTestDir({ prefix: "openclaw-install-safe-" }, async (parentDir) => { const realBaseDir = path.join(parentDir, "base"); const nestedRealDir = path.join(parentDir, "nested-real"); const nestedSymlinkDir = path.join(realBaseDir, "nested-link"); diff --git a/src/infra/install-source-utils.chmod.test.ts b/src/infra/install-source-utils.chmod.test.ts index 5b64e65fc891..14dfd0f570b6 100644 --- a/src/infra/install-source-utils.chmod.test.ts +++ b/src/infra/install-source-utils.chmod.test.ts @@ -13,9 +13,9 @@ vi.mock("./tmp-openclaw-dir.js", async (importOriginal) => { }; }); -import { withTempDir } from "./install-source-utils.js"; +import { withInstallWorkspace } from "./install-source-utils.js"; -describe("withTempDir private root", () => { +describe("withInstallWorkspace private root", () => { const tempDirs = useAutoCleanupTempDirTracker(afterEach); it.runIf(process.platform !== "win32")( @@ -31,7 +31,7 @@ describe("withTempDir private root", () => { resolvePreferredOpenClawTmpDirMock.mockReturnValue(mockOpenClawDir); let observedDir = ""; - const value = await withTempDir("openclaw-test-", async (tmpDir) => { + const value = await withInstallWorkspace("openclaw-test-", async (tmpDir) => { observedDir = tmpDir; expect(path.dirname(tmpDir)).toBe(canonicalOpenClawDir); await fs.writeFile(path.join(tmpDir, "marker.txt"), "ok"); diff --git a/src/infra/install-source-utils.test.ts b/src/infra/install-source-utils.test.ts index 758deb846e9b..e8b8ec046fe9 100644 --- a/src/infra/install-source-utils.test.ts +++ b/src/infra/install-source-utils.test.ts @@ -8,7 +8,7 @@ import { resolveArchiveSourcePath, resolveNpmPackArchiveMetadata, resolveNpmSpecMetadata, - withTempDir, + withInstallWorkspace, } from "./install-source-utils.js"; const execFileSyncMock = vi.hoisted(() => vi.fn(() => "/tmp/openclaw-test-global-npmrc\n")); @@ -132,12 +132,12 @@ afterEach(async () => { await tempDirs.cleanup(); }); -describe("withTempDir", () => { +describe("withInstallWorkspace", () => { it("creates a temp dir and always removes it after callback", async () => { let observedDir = ""; const markerFile = "marker.txt"; - const value = await withTempDir("openclaw-install-source-utils-", async (tmpDir) => { + const value = await withInstallWorkspace("openclaw-install-source-utils-", async (tmpDir) => { observedDir = tmpDir; await fs.writeFile(path.join(tmpDir, markerFile), "ok", "utf-8"); await expect(fs.readFile(path.join(tmpDir, markerFile), "utf8")).resolves.toBe("ok"); diff --git a/src/infra/install-source-utils.ts b/src/infra/install-source-utils.ts index 9e339af65083..175f4b49181b 100644 --- a/src/infra/install-source-utils.ts +++ b/src/infra/install-source-utils.ts @@ -197,7 +197,7 @@ export type NpmIntegrityDrift = { }; /** Runs a callback in a private temp directory and removes it afterward. */ -export async function withTempDir( +export async function withInstallWorkspace( prefix: string, fn: (tmpDir: string) => Promise, options?: { rootDir?: string }, diff --git a/src/infra/install-target.test.ts b/src/infra/install-target.test.ts index 145830146a9b..aee1e9a84b26 100644 --- a/src/infra/install-target.test.ts +++ b/src/infra/install-target.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; const pathExistsMock = vi.hoisted(() => vi.fn()); const resolveSafeInstallDirMock = vi.hoisted(() => vi.fn()); @@ -27,7 +27,7 @@ beforeEach(() => { describe("resolveCanonicalInstallTarget", () => { it("creates the base dir and returns early for invalid install ids", async () => { - await withTempDir({ prefix: "openclaw-install-target-" }, async (root) => { + await withTestDir({ prefix: "openclaw-install-target-" }, async (root) => { const baseDir = path.join(root, "plugins"); resolveSafeInstallDirMock.mockReturnValueOnce({ ok: false, @@ -50,7 +50,7 @@ describe("resolveCanonicalInstallTarget", () => { }); it("returns canonical boundary errors for Error and non-Error throws", async () => { - await withTempDir({ prefix: "openclaw-install-target-" }, async (baseDir) => { + await withTestDir({ prefix: "openclaw-install-target-" }, async (baseDir) => { const targetDir = path.join(baseDir, "demo"); resolveSafeInstallDirMock.mockReturnValue({ ok: true, @@ -80,7 +80,7 @@ describe("resolveCanonicalInstallTarget", () => { }); it("returns the resolved target path on success", async () => { - await withTempDir({ prefix: "openclaw-install-target-" }, async (baseDir) => { + await withTestDir({ prefix: "openclaw-install-target-" }, async (baseDir) => { const targetDir = path.join(baseDir, "demo"); resolveSafeInstallDirMock.mockReturnValueOnce({ ok: true, diff --git a/src/infra/json-file.test.ts b/src/infra/json-file.test.ts index e72f26e2fef9..8371bb62d809 100644 --- a/src/infra/json-file.test.ts +++ b/src/infra/json-file.test.ts @@ -2,8 +2,8 @@ import fs from "node:fs"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; -import { loadJsonFileThroughSymlink, saveJsonFile } from "./json-file.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; +import { loadJsonFileThroughSymlink, writeJsonTarget } from "./json-file.js"; const SAVED_PAYLOAD = { enabled: true, count: 2 }; const PREVIOUS_JSON = '{"enabled":false}\n'; @@ -19,7 +19,7 @@ function writeExistingJson(pathname: string) { async function withJsonPath( run: (params: { root: string; pathname: string }) => Promise | T, ): Promise { - return withTempDir({ prefix: "openclaw-json-file-" }, async (root) => + return withTestDir({ prefix: "openclaw-json-file-" }, async (root) => run({ root, pathname: path.join(root, "config.json") }), ); } @@ -32,7 +32,7 @@ async function withJsonSymlink( linkPath: string; }) => Promise | T, ): Promise { - return withTempDir({ prefix: "openclaw-json-file-" }, async (root) => { + return withTestDir({ prefix: "openclaw-json-file-" }, async (root) => { const targetDir = path.join(root, "target"); return run({ root, @@ -79,9 +79,9 @@ describe("json-file helpers", () => { }); it("creates parent dirs, writes a trailing newline, and loads the saved object", async () => { - await withTempDir({ prefix: "openclaw-json-file-" }, async (root) => { + await withTestDir({ prefix: "openclaw-json-file-" }, async (root) => { const pathname = path.join(root, "nested", "config.json"); - saveJsonFile(pathname, SAVED_PAYLOAD); + writeJsonTarget(pathname, SAVED_PAYLOAD); const raw = fs.readFileSync(pathname, "utf8"); expect(raw.endsWith("\n")).toBe(true); @@ -110,7 +110,7 @@ describe("json-file helpers", () => { ])("writes the latest payload for $name", async ({ setup }) => { await withJsonPath(({ pathname }) => { setup(pathname); - saveJsonFile(pathname, SAVED_PAYLOAD); + writeJsonTarget(pathname, SAVED_PAYLOAD); expect(loadJsonFileThroughSymlink(pathname)).toEqual(SAVED_PAYLOAD); }); }); @@ -120,7 +120,7 @@ describe("json-file helpers", () => { writeExistingJson(pathname); const renameSpy = vi.spyOn(fs, "renameSync"); - saveJsonFile(pathname, SAVED_PAYLOAD); + writeJsonTarget(pathname, SAVED_PAYLOAD); const renameCall = renameSpy.mock.calls.find(([, target]) => target === pathname); expect(renameCall?.[0]).toMatch(new RegExp(`^${escapeRegExp(pathname)}\\..+\\.tmp$`)); @@ -137,7 +137,7 @@ describe("json-file helpers", () => { writeExistingJson(targetPath); fs.symlinkSync(targetPath, linkPath); - saveJsonFile(linkPath, SAVED_PAYLOAD); + writeJsonTarget(linkPath, SAVED_PAYLOAD); expectSavedPayloadThroughSymlink(linkPath, targetPath); }); @@ -151,7 +151,7 @@ describe("json-file helpers", () => { fs.mkdirSync(targetDir, { recursive: true }); fs.symlinkSync(targetPath, linkPath); - saveJsonFile(linkPath, SAVED_PAYLOAD); + writeJsonTarget(linkPath, SAVED_PAYLOAD); expectSavedPayloadThroughSymlink(linkPath, targetPath); }); @@ -161,7 +161,7 @@ describe("json-file helpers", () => { it.runIf(process.platform !== "win32")( "does not create missing target directories through an existing symlink", async () => { - await withTempDir({ prefix: "openclaw-json-file-" }, async (root) => { + await withTestDir({ prefix: "openclaw-json-file-" }, async (root) => { const missingTargetDir = path.join(root, "missing-target"); const targetPath = path.join(missingTargetDir, "config.json"); const linkPath = path.join(root, "config-link.json"); @@ -169,12 +169,12 @@ describe("json-file helpers", () => { let saveError: unknown; try { - saveJsonFile(linkPath, SAVED_PAYLOAD); + writeJsonTarget(linkPath, SAVED_PAYLOAD); } catch (error) { saveError = error; } if (saveError === undefined) { - throw new Error("Expected saveJsonFile to fail"); + throw new Error("Expected writeJsonTarget to fail"); } expect((saveError as { code?: unknown }).code).toBe("ENOENT"); expect(fs.existsSync(missingTargetDir)).toBe(false); @@ -192,7 +192,7 @@ describe("json-file helpers", () => { throw err; }); - saveJsonFile(pathname, SAVED_PAYLOAD); + writeJsonTarget(pathname, SAVED_PAYLOAD); expect(renameSpy).toHaveBeenCalled(); expect(loadJsonFileThroughSymlink(pathname)).toEqual(SAVED_PAYLOAD); diff --git a/src/infra/json-file.ts b/src/infra/json-file.ts index 0dc73c2d0bae..34f4b475b0de 100644 --- a/src/infra/json-file.ts +++ b/src/infra/json-file.ts @@ -30,7 +30,7 @@ function resolveJsonSaveTarget(pathname: string): string { return target; } -export function saveJsonFile(pathname: string, data: unknown): void { +export function writeJsonTarget(pathname: string, data: unknown): void { writeJsonSync(resolveJsonSaveTarget(pathname), data); } diff --git a/src/infra/json-files.test.ts b/src/infra/json-files.test.ts index 4237d17ff9b0..022d69931016 100644 --- a/src/infra/json-files.test.ts +++ b/src/infra/json-files.test.ts @@ -2,7 +2,7 @@ import fsPromises from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { JsonFileReadError, createAsyncLock, @@ -47,13 +47,13 @@ describe("json file helpers", () => { expected: null, }, ])("$name", async ({ setup, expected }) => { - await withTempDir({ prefix: "openclaw-json-files-" }, async (base) => { + await withTestDir({ prefix: "openclaw-json-files-" }, async (base) => { await expect(readJsonFile(await setup(base))).resolves.toEqual(expected); }); }); it("reads durable json strictly while allowing missing files", async () => { - await withTempDir({ prefix: "openclaw-json-files-" }, async (base) => { + await withTestDir({ prefix: "openclaw-json-files-" }, async (base) => { const validPath = path.join(base, "valid.json"); const invalidPath = path.join(base, "invalid.json"); const missingPath = path.join(base, "missing.json"); @@ -74,7 +74,7 @@ describe("json file helpers", () => { }); it("writes json atomically with pretty formatting and optional trailing newline", async () => { - await withTempDir({ prefix: "openclaw-json-files-" }, async (base) => { + await withTestDir({ prefix: "openclaw-json-files-" }, async (base) => { const filePath = path.join(base, "nested", "config.json"); await writeJsonAtomic( @@ -93,7 +93,7 @@ describe("json file helpers", () => { { input: "hello", expected: "hello\n" }, { input: "hello\n", expected: "hello\n" }, ])("writes text atomically for %j", async ({ input, expected }) => { - await withTempDir({ prefix: "openclaw-json-files-" }, async (base) => { + await withTestDir({ prefix: "openclaw-json-files-" }, async (base) => { const filePath = path.join(base, "nested", "note.txt"); await writeTextAtomic(filePath, input, { trailingNewline: true }); await expect(fsPromises.readFile(filePath, "utf8")).resolves.toBe(expected); @@ -101,7 +101,7 @@ describe("json file helpers", () => { }); it("can skip durable fsync work for hot state writes", async () => { - await withTempDir({ prefix: "openclaw-json-files-" }, async (base) => { + await withTestDir({ prefix: "openclaw-json-files-" }, async (base) => { const filePath = path.join(base, "state.json"); // fs-safe 0.5.2 opens pinned descriptors even for non-durable writes, so @@ -115,7 +115,7 @@ describe("json file helpers", () => { }); it("preserves text when Windows rename reports EPERM", async () => { - await withTempDir({ prefix: "openclaw-json-files-" }, async (base) => { + await withTestDir({ prefix: "openclaw-json-files-" }, async (base) => { const filePath = path.join(base, "state.json"); await fsPromises.writeFile(filePath, "old", "utf8"); @@ -131,7 +131,7 @@ describe("json file helpers", () => { }); it("stages the atomic temp with a caller-provided prefix (#56827)", async () => { - await withTempDir({ prefix: "openclaw-json-files-" }, async (base) => { + await withTestDir({ prefix: "openclaw-json-files-" }, async (base) => { const filePath = path.join(base, "sessions.json"); // Spy without mocking: rename is still performed, but we capture the staged // temp path (its source) to confirm the prefix is applied. @@ -154,7 +154,7 @@ describe("json file helpers", () => { }); it("refuses Windows copy fallback through symlink destinations", async () => { - await withTempDir({ prefix: "openclaw-json-files-" }, async (base) => { + await withTestDir({ prefix: "openclaw-json-files-" }, async (base) => { const filePath = path.join(base, "state.json"); const outsidePath = path.join(base, "outside.json"); await fsPromises.writeFile(outsidePath, "outside", "utf8"); diff --git a/src/infra/jsonl-socket.test.ts b/src/infra/jsonl-socket.test.ts index f86372b5a7db..6d9e67def590 100644 --- a/src/infra/jsonl-socket.test.ts +++ b/src/infra/jsonl-socket.test.ts @@ -2,7 +2,7 @@ import net from "node:net"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { requestJsonlSocket } from "./jsonl-socket.js"; async function listenOnSocket(server: net.Server, socketPath: string): Promise { @@ -28,7 +28,7 @@ function acceptDoneValue(msg: unknown): number | null | undefined { describe.runIf(process.platform !== "win32")("requestJsonlSocket", () => { it("ignores malformed and non-accepted lines until one is accepted", async () => { - await withTempDir({ prefix: "openclaw-jsonl-socket-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-jsonl-socket-" }, async (dir) => { const socketPath = path.join(dir, "socket.sock"); const server = net.createServer((socket) => { socket.on("data", () => { @@ -58,7 +58,7 @@ describe.runIf(process.platform !== "win32")("requestJsonlSocket", () => { }); it("half-closes the write side after sending the request line", async () => { - await withTempDir({ prefix: "openclaw-jsonl-socket-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-jsonl-socket-" }, async (dir) => { const socketPath = path.join(dir, "socket.sock"); let receivedBuffer: string | null = null; const server = net.createServer((socket) => { @@ -93,7 +93,7 @@ describe.runIf(process.platform !== "win32")("requestJsonlSocket", () => { }); it("returns null on timeout and on socket errors", async () => { - await withTempDir({ prefix: "openclaw-jsonl-socket-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-jsonl-socket-" }, async (dir) => { const socketPath = path.join(dir, "socket.sock"); const server = net.createServer(() => { // Intentionally never reply. @@ -128,7 +128,7 @@ describe.runIf(process.platform !== "win32")("requestJsonlSocket", () => { }); it("returns null when the socket closes without an accepted response", async () => { - await withTempDir({ prefix: "openclaw-jsonl-socket-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-jsonl-socket-" }, async (dir) => { const socketPath = path.join(dir, "socket.sock"); const server = net.createServer((socket) => { socket.on("data", () => { diff --git a/src/infra/net/proxy/external-proxy.e2e.test.ts b/src/infra/net/proxy/external-proxy.e2e.test.ts index cac3340bf2f6..770ebf841cee 100644 --- a/src/infra/net/proxy/external-proxy.e2e.test.ts +++ b/src/infra/net/proxy/external-proxy.e2e.test.ts @@ -9,7 +9,7 @@ import { join } from "node:path"; import type { Duplex } from "node:stream"; import { afterEach, describe, expect, it } from "vitest"; import { WebSocketServer } from "ws"; -import { withTempDir } from "../../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../../test-helpers/temp-dir.js"; import { createNodeEvalArgs } from "../../../test-utils/node-process.js"; import { resolveSystemBin } from "../../resolve-system-bin.js"; import { resolvePreferredOpenClawTmpDir } from "../../tmp-openclaw-dir.js"; @@ -104,7 +104,7 @@ function createDiscordTlsFixture(dir: string): DiscordTlsFixture { async function withDiscordTlsFixture( run: (fixture: DiscordTlsFixture) => Promise, ): Promise { - return await withTempDir( + return await withTestDir( { prefix: "openclaw-discord-tls-", parentDir: resolvePreferredOpenClawTmpDir(), diff --git a/src/infra/npm-pack-install.test.ts b/src/infra/npm-pack-install.test.ts index 78542f821ad9..55389922fb10 100644 --- a/src/infra/npm-pack-install.test.ts +++ b/src/infra/npm-pack-install.test.ts @@ -1,6 +1,6 @@ // Covers npm package archive installation helpers. import { beforeEach, describe, expect, it, vi } from "vitest"; -import { packNpmSpecToArchive, withTempDir } from "./install-source-utils.js"; +import { packNpmSpecToArchive, withInstallWorkspace } from "./install-source-utils.js"; import type { NpmIntegrityDriftPayload } from "./npm-integrity.js"; import { finalizeNpmSpecArchiveInstall, @@ -13,9 +13,11 @@ vi.mock("./install-source-utils.js", async () => { ); return { ...actual, - withTempDir: vi.fn(async (_prefix: string, fn: (tmpDir: string) => Promise) => { - return await fn("/tmp/openclaw-npm-pack-install-test"); - }), + withInstallWorkspace: vi.fn( + async (_prefix: string, fn: (tmpDir: string) => Promise) => { + return await fn("/tmp/openclaw-npm-pack-install-test"); + }, + ), packNpmSpecToArchive: vi.fn(), }; }); @@ -76,7 +78,7 @@ describe("installFromNpmSpecArchiveWithInstaller", () => { beforeEach(() => { vi.mocked(packNpmSpecToArchive).mockClear(); - vi.mocked(withTempDir).mockClear(); + vi.mocked(withInstallWorkspace).mockClear(); }); it("returns pack errors without invoking installer", async () => { @@ -87,7 +89,7 @@ describe("installFromNpmSpecArchiveWithInstaller", () => { expect(result).toEqual({ ok: false, error: "pack failed" }); expect(installFromArchive).not.toHaveBeenCalled(); - expect(withTempDir).toHaveBeenCalledWith("openclaw-test-", expect.any(Function)); + expect(withInstallWorkspace).toHaveBeenCalledWith("openclaw-test-", expect.any(Function)); }); it("rejects unsupported npm specs before packing", async () => { diff --git a/src/infra/npm-pack-install.ts b/src/infra/npm-pack-install.ts index 0a0c1fbab55c..427e0da40431 100644 --- a/src/infra/npm-pack-install.ts +++ b/src/infra/npm-pack-install.ts @@ -3,7 +3,7 @@ import { type NpmIntegrityDrift, type NpmSpecResolution, packNpmSpecToArchive, - withTempDir, + withInstallWorkspace, } from "./install-source-utils.js"; import { type NpmIntegrityDriftPayload, @@ -117,7 +117,7 @@ async function installFromNpmSpecArchive(params warn?: (message: string) => void; installFromArchive: (params: { archivePath: string }) => Promise; }): Promise> { - return await withTempDir(params.tempDirPrefix, async (tmpDir) => { + return await withInstallWorkspace(params.tempDirPrefix, async (tmpDir) => { const parsedSpec = parseRegistryNpmSpec(params.spec); if (!parsedSpec) { return { diff --git a/src/infra/outbound/channel-target-prefix.ts b/src/infra/outbound/channel-target-prefix.ts index f7c47b66260e..a91a82cc156c 100644 --- a/src/infra/outbound/channel-target-prefix.ts +++ b/src/infra/outbound/channel-target-prefix.ts @@ -28,7 +28,7 @@ export function stripTargetProviderPrefix(raw: string, ...providers: string[]): } /** Removes generic target-kind prefixes such as room:, thread:, or user:. */ -export function stripTargetKindPrefix( +export function stripOutboundTargetKindPrefix( raw: string, kinds: readonly string[] = ["channel", "conversation", "dm", "group", "room", "thread", "user"], ): string { diff --git a/src/infra/outbound/conversation-delivery.ts b/src/infra/outbound/conversation-delivery.ts index 96102eb77f3a..3150ece20fc3 100644 --- a/src/infra/outbound/conversation-delivery.ts +++ b/src/infra/outbound/conversation-delivery.ts @@ -12,7 +12,7 @@ import { type ConversationDeliveryStoreScope, } from "../../config/sessions/conversation-delivery-store.js"; import type { ConversationRecord } from "../../config/sessions/conversation-registry.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import type { MessageActionResult } from "./message-action-contracts.js"; import { runMessageAction } from "./message-action-runner.js"; @@ -73,7 +73,9 @@ function resolveConversationDeliveryStoreScope( agentId: context.agentId, // Recovery cannot serialize process.env. Persist the resolved marker path // so it reopens the same per-agent SQLite database after restart. - storePath: resolveStorePath(context.config.session?.store, { agentId: context.agentId }), + storePath: resolveSessionStorePathCore(context.config.session?.store, { + agentId: context.agentId, + }), }; } diff --git a/src/infra/outbound/delivery-queue.recovery.test.ts b/src/infra/outbound/delivery-queue.recovery.test.ts index 0d26120eaa33..a8f2818312eb 100644 --- a/src/infra/outbound/delivery-queue.recovery.test.ts +++ b/src/infra/outbound/delivery-queue.recovery.test.ts @@ -11,7 +11,7 @@ import { markConversationDeliveryRejected, markConversationDeliverySuppressed, } from "../../config/sessions/conversation-delivery-store.js"; -import { upsertSessionEntry } from "../../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../../config/sessions/session-accessor.js"; import { buildConversationRef } from "../../routing/conversation-ref.js"; import { closeOpenClawAgentDatabasesForTest } from "../../state/openclaw-agent-db.js"; import { openOpenClawStateDatabase } from "../../state/openclaw-state-db.js"; @@ -313,7 +313,7 @@ describe("delivery-queue recovery", () => { kind: "direct", peerId: "peer-agent", }); - await upsertSessionEntry( + await upsertSessionEntryCore( { ...scope, sessionKey: "agent:main:reef:direct:peer-agent" }, { sessionId: "reef-session", diff --git a/src/infra/outbound/message-action-tts.ts b/src/infra/outbound/message-action-tts.ts index 55fee4b1ff6b..daca3db493bd 100644 --- a/src/infra/outbound/message-action-tts.ts +++ b/src/infra/outbound/message-action-tts.ts @@ -1,7 +1,7 @@ // Message-action TTS helpers lazily apply session/config driven speech output // to send payloads without loading TTS providers for ordinary sends. import type { ReplyPayload } from "../../auto-reply/reply-payload.js"; -import { resolveStorePath } from "../../config/sessions.js"; +import { resolveSessionStorePathCore } from "../../config/sessions.js"; import { loadSessionEntryReadOnly } from "../../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import type { TtsAutoMode } from "../../config/types.tts.js"; @@ -24,7 +24,9 @@ function resolveMessageActionSessionTtsAuto(params: { return undefined; } try { - const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.agentId }); + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { + agentId: params.agentId, + }); return loadSessionEntryReadOnly({ agentId: params.agentId, sessionKey, diff --git a/src/infra/outbound/message.ts b/src/infra/outbound/message.ts index 65dff10be24a..77e712ba3912 100644 --- a/src/infra/outbound/message.ts +++ b/src/infra/outbound/message.ts @@ -4,7 +4,7 @@ import type { ReplyPayload } from "../../auto-reply/reply-payload.js"; import type { ChatType } from "../../channels/chat-type.js"; import { deriveDurableFinalDeliveryRequirementsForBatch } from "../../channels/message/capabilities.js"; import { - sendDurableMessageBatch, + sendDurableMessageBatchCore, serializeDurableMessagePayloadOutcomes, type SerializedDurableMessagePayloadOutcome, } from "../../channels/message/runtime.js"; @@ -389,7 +389,7 @@ export async function sendMessage(params: MessageSendParams): Promise { it("binds a discovered canonical peer through a different delivery alias", async () => { const sessionKey = "agent:main:main"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, storePath }, { sessionId: "shared-main-session", diff --git a/src/infra/outbound/outbound-session.test.ts b/src/infra/outbound/outbound-session.test.ts index 8260ef72ac78..d897be9e1b30 100644 --- a/src/infra/outbound/outbound-session.test.ts +++ b/src/infra/outbound/outbound-session.test.ts @@ -42,7 +42,7 @@ function firstMockArg( } vi.mock("../../config/sessions/inbound.runtime.js", () => ({ - resolveStorePath: mocks.resolveStorePath, + resolveSessionStorePathCore: mocks.resolveStorePath, updateSessionLastRoute: mocks.updateSessionLastRoute, })); diff --git a/src/infra/outbound/outbound-session.ts b/src/infra/outbound/outbound-session.ts index 5238706ae6aa..9910c52f3416 100644 --- a/src/infra/outbound/outbound-session.ts +++ b/src/infra/outbound/outbound-session.ts @@ -7,7 +7,10 @@ import type { ChatType } from "../../channels/chat-type.js"; import { getChannelPlugin } from "../../channels/plugins/index.js"; import type { ChannelPlugin } from "../../channels/plugins/types.plugin.js"; import type { ChannelId } from "../../channels/plugins/types.public.js"; -import { resolveStorePath, updateSessionLastRoute } from "../../config/sessions/inbound.runtime.js"; +import { + resolveSessionStorePathCore, + updateSessionLastRoute, +} from "../../config/sessions/inbound.runtime.js"; import type { SessionEntry } from "../../config/sessions/types.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import type { RoutePeer } from "../../routing/resolve-route.js"; @@ -235,7 +238,7 @@ type OutboundSessionEntryParams = { async function persistOutboundSessionEntry( params: OutboundSessionEntryParams, ): Promise { - const storePath = resolveStorePath(params.cfg.session?.store, { + const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId: resolveAgentIdFromSessionKey(params.route.sessionKey), }); const ctx: MsgContext = { diff --git a/src/infra/outbound/source-reply-mirror.ts b/src/infra/outbound/source-reply-mirror.ts index adece1efd19d..454d9074553f 100644 --- a/src/infra/outbound/source-reply-mirror.ts +++ b/src/infra/outbound/source-reply-mirror.ts @@ -12,7 +12,7 @@ import type { ChannelId } from "../../channels/plugins/types.public.js"; import { resolveChannelThreadAddressing } from "../../channels/thread-addressing.js"; import type { InternalChannelThreadingToolContext } from "../../channels/threading-tool-context-internal.js"; import { appendAssistantMessageToSessionTranscript } from "../../config/sessions.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { beginRestartRecoveryTerminalDelivery, cancelRestartRecoveryTerminalDelivery, @@ -234,7 +234,7 @@ function resolveTerminalSourceReplyDeliveryReceipt( sessionId: params.sessionId, sessionKey: params.sessionKey, sourceTurnId, - storePath: resolveStorePath(params.cfg.session?.store, { agentId }), + storePath: resolveSessionStorePathCore(params.cfg.session?.store, { agentId }), toolCallId, }; } diff --git a/src/infra/package-dist-inventory.test.ts b/src/infra/package-dist-inventory.test.ts index ea6b2ca4de5e..477c17aad2ab 100644 --- a/src/infra/package-dist-inventory.test.ts +++ b/src/infra/package-dist-inventory.test.ts @@ -10,7 +10,7 @@ import { writePackageDistInventory, writePackageDistInventoryForPublish, } from "../../scripts/lib/package-dist-inventory.ts"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { collectPackageDistInventory, readPackageDistInventoryIfPresent, @@ -18,7 +18,7 @@ import { describe("package dist inventory", () => { it("tracks missing and stale dist files", async () => { - await withTempDir({ prefix: "openclaw-dist-inventory-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-dist-inventory-" }, async (packageRoot) => { const currentFile = path.join(packageRoot, "dist", "current-BR6xv1a1.js"); await fs.mkdir(path.dirname(currentFile), { recursive: true }); await fs.writeFile(currentFile, "export {};\n", "utf8"); @@ -44,7 +44,7 @@ describe("package dist inventory", () => { }); it("keeps the pending install guard outside the expected inventory", async () => { - await withTempDir({ prefix: "openclaw-dist-install-guard-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-dist-install-guard-" }, async (packageRoot) => { const currentFile = path.join(packageRoot, "dist", "current.js"); await fs.mkdir(path.dirname(currentFile), { recursive: true }); await fs.writeFile(currentFile, "export {};\n", "utf8"); @@ -66,7 +66,7 @@ describe("package dist inventory", () => { }); it("keeps npm-omitted dist artifacts out of the inventory", async () => { - await withTempDir({ prefix: "openclaw-dist-inventory-pack-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-dist-inventory-pack-" }, async (packageRoot) => { const packagedQaChannelRuntime = path.join( packageRoot, "dist", @@ -158,7 +158,7 @@ describe("package dist inventory", () => { }); it("honors package files exclusions when writing the dist inventory", async () => { - await withTempDir({ prefix: "openclaw-dist-inventory-package-files-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-dist-inventory-package-files-" }, async (packageRoot) => { const packagedRuntime = path.join(packageRoot, "dist", "plugin-sdk", "runtime.js"); const omittedTestRuntime = path.join( packageRoot, @@ -222,7 +222,7 @@ describe("package dist inventory", () => { }); it("keeps transient plugin dependency trees out of the inventory", async () => { - await withTempDir({ prefix: "openclaw-dist-inventory-plugin-deps-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-dist-inventory-plugin-deps-" }, async (packageRoot) => { const realFile = path.join(packageRoot, "dist", "index.js"); const rootDependencyPackage = path.join( packageRoot, @@ -253,7 +253,7 @@ describe("package dist inventory", () => { }); it("omits packaged extension node_modules while keeping extension runtime files", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-dist-inventory-extension-node-modules-" }, async (packageRoot) => { const extensionRuntime = path.join( @@ -296,7 +296,7 @@ describe("package dist inventory", () => { }); it("keeps publishable externalized bundled plugin dist trees out of the inventory", async () => { - await withTempDir({ prefix: "openclaw-dist-inventory-externalized-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-dist-inventory-externalized-" }, async (packageRoot) => { const externalizedRuntime = path.join( packageRoot, "dist", @@ -367,7 +367,7 @@ describe("package dist inventory", () => { }); it("keeps publishable core-package runtime plugin dist trees in the inventory", async () => { - await withTempDir({ prefix: "openclaw-dist-inventory-core-runtime-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-dist-inventory-core-runtime-" }, async (packageRoot) => { const coreRuntime = path.join(packageRoot, "dist", "extensions", "core-chat", "index.js"); const corePackageJson = path.join(packageRoot, "extensions", "core-chat", "package.json"); @@ -421,7 +421,7 @@ describe("package dist inventory", () => { }); it("rejects pre-populated install-stage debris before writing an inventory", async () => { - await withTempDir({ prefix: "openclaw-dist-inventory-stage-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-dist-inventory-stage-" }, async (packageRoot) => { for (const relativePath of [ "dist/extensions/brave/.openclaw-install-stage/package.json", "dist/extensions/browser/.openclaw-install-stage-AbC123/node_modules/playwright-core/package.json", @@ -438,7 +438,7 @@ describe("package dist inventory", () => { }); it("rejects mixed-case install-stage debris on case-sensitive builders", async () => { - await withTempDir({ prefix: "openclaw-dist-inventory-stage-case-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-dist-inventory-stage-case-" }, async (packageRoot) => { const stagedFile = path.join( packageRoot, "Dist", @@ -457,14 +457,14 @@ describe("package dist inventory", () => { }); it("returns null when the inventory is missing", async () => { - await withTempDir({ prefix: "openclaw-dist-inventory-missing-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-dist-inventory-missing-" }, async (packageRoot) => { await fs.mkdir(path.join(packageRoot, "dist"), { recursive: true }); await expect(readPackageDistInventoryIfPresent(packageRoot)).resolves.toBeNull(); }); }); it("rejects symlinked dist entries", async () => { - await withTempDir({ prefix: "openclaw-dist-inventory-symlink-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-dist-inventory-symlink-" }, async (packageRoot) => { const distDir = path.join(packageRoot, "dist"); await fs.mkdir(distDir, { recursive: true }); await fs.writeFile(path.join(packageRoot, "escape.js"), "export {};\n", "utf8"); diff --git a/src/infra/package-json.test.ts b/src/infra/package-json.test.ts index 795da4ef0137..7e50b77e4d4b 100644 --- a/src/infra/package-json.test.ts +++ b/src/infra/package-json.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { readPackageManagerSpec, readPackageName, readPackageVersion } from "./package-json.js"; async function expectPackageMeta(params: { @@ -16,7 +16,7 @@ async function expectPackageMeta(params: { describe("package-json helpers", () => { it("reads package version and trims package name", async () => { - await withTempDir({ prefix: "openclaw-package-json-" }, async (root) => { + await withTestDir({ prefix: "openclaw-package-json-" }, async (root) => { await fs.writeFile( path.join(root, "package.json"), JSON.stringify({ @@ -78,7 +78,7 @@ describe("package-json helpers", () => { ])( "returns normalized nulls for $name", async ({ writePackageJson, expectedVersion, expectedName }) => { - await withTempDir({ prefix: "openclaw-package-json-" }, async (root) => { + await withTestDir({ prefix: "openclaw-package-json-" }, async (root) => { await writePackageJson(root); await expectPackageMeta({ root, diff --git a/src/infra/package-update-steps.pnpm11-guard.test.ts b/src/infra/package-update-steps.pnpm11-guard.test.ts index 903e367719bd..6cd01bfa5aa2 100644 --- a/src/infra/package-update-steps.pnpm11-guard.test.ts +++ b/src/infra/package-update-steps.pnpm11-guard.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { writePackageDistInventory } from "../../scripts/lib/package-dist-inventory.ts"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { runGlobalPackageUpdateSteps } from "./package-update-steps.js"; import type { CommandRunner, ResolvedGlobalInstallTarget } from "./update-global.js"; @@ -66,7 +66,7 @@ async function expectPathMissing(filePath: string): Promise { describe("pnpm 11 isolated install preflight", () => { it("rejects grouped installs before dropping sibling packages", async () => { - await withTempDir({ prefix: "openclaw-package-update-pnpm-group-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-pnpm-group-" }, async (base) => { const globalRoot = path.join(base, "pnpm-home", "global", "v11"); await writePnpmIsolatedPackage({ globalRoot, @@ -107,7 +107,7 @@ describe("pnpm 11 isolated install preflight", () => { }); it("rejects multiple standalone installs before an alias-wide update", async () => { - await withTempDir({ prefix: "openclaw-package-update-pnpm-multiple-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-pnpm-multiple-" }, async (base) => { const globalRoot = path.join(base, "pnpm-home", "global", "v11"); await writePnpmIsolatedPackage({ globalRoot, @@ -147,7 +147,7 @@ describe("pnpm 11 isolated install preflight", () => { }); it("rejects an orphaned invoking install before manager probes", async () => { - await withTempDir({ prefix: "openclaw-package-update-pnpm-invoking-orphan-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-pnpm-invoking-orphan-" }, async (base) => { const globalRoot = path.join(base, "pnpm-home", "global", "v11"); const packageRoot = path.join(globalRoot, "orphan", "node_modules", "openclaw"); await writePackageRoot(packageRoot, "1.0.0"); @@ -178,7 +178,7 @@ describe("pnpm 11 isolated install preflight", () => { }); it("rejects an orphan whose package symlink shares the active store target", async () => { - await withTempDir({ prefix: "openclaw-package-update-pnpm-shared-store-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-pnpm-shared-store-" }, async (base) => { const globalRoot = path.join(base, "pnpm-home", "global", "v11"); const activeInstallRoot = path.join(globalRoot, "active"); const orphanInstallRoot = path.join(globalRoot, "orphan"); @@ -234,7 +234,7 @@ describe("pnpm 11 isolated install preflight", () => { }); it("uses the owner-reported custom bin without changing pnpm command resolution", async () => { - await withTempDir({ prefix: "openclaw-package-update-pnpm-isolated-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-pnpm-isolated-" }, async (base) => { const globalDir = path.join(base, "pnpm-home", "global"); const globalRoot = path.join(globalDir, "v11"); const ownerBinDir = path.join(base, "custom-global-bin"); @@ -382,7 +382,7 @@ describe("pnpm 11 isolated install preflight", () => { }); it("accepts a replacement pnpm project that reuses the same shared-store package", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-package-update-pnpm-shared-replacement-" }, async (base) => { const globalDir = path.join(base, "pnpm-home", "global"); @@ -469,7 +469,7 @@ describe("pnpm 11 isolated install preflight", () => { }); it("preserves pnpm local specs before mutating from the owner root", async () => { - await withTempDir({ prefix: "openclaw-package-update-pnpm-relative-spec-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-pnpm-relative-spec-" }, async (base) => { const globalDir = path.join(base, "pnpm-home", "global"); const globalRoot = path.join(globalDir, "v11"); const globalBinDir = path.join(base, "pnpm-home", "bin"); @@ -576,7 +576,7 @@ describe("pnpm 11 isolated install preflight", () => { }); it("probes pnpm from its owner root before rejecting a mismatched major", async () => { - await withTempDir({ prefix: "openclaw-package-update-pnpm-major-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-pnpm-major-" }, async (base) => { const globalRoot = path.join(base, "pnpm-home", "global", "v11"); const globalBinDir = path.join(base, "pnpm-home", "bin"); const { packageRoot } = await writePnpmIsolatedPackage({ @@ -630,7 +630,7 @@ describe("pnpm 11 isolated install preflight", () => { }); it("rejects a pnpm command that owns another global root", async () => { - await withTempDir({ prefix: "openclaw-package-update-pnpm-root-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-pnpm-root-" }, async (base) => { const globalRoot = path.join(base, "owner", "global", "v11"); const otherGlobalRoot = path.join(base, "other", "global", "v11"); const { packageRoot } = await writePnpmIsolatedPackage({ @@ -669,7 +669,7 @@ describe("pnpm 11 isolated install preflight", () => { }); it("rejects a pnpm update that leaves only an orphaned old package root", async () => { - await withTempDir({ prefix: "openclaw-package-update-pnpm-orphan-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-pnpm-orphan-" }, async (base) => { const globalRoot = path.join(base, "pnpm-home", "global", "v11"); const globalBinDir = path.join(base, "pnpm-home", "bin"); const { activeLink, packageRoot } = await writePnpmIsolatedPackage({ @@ -730,7 +730,7 @@ describe("pnpm 11 isolated install preflight", () => { }); it("retries interrupted pnpm package lifecycle repair", async () => { - await withTempDir({ prefix: "openclaw-package-update-pnpm-lifecycle-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-pnpm-lifecycle-" }, async (base) => { const globalRoot = path.join(base, "global"); const packageRoot = path.join(globalRoot, "openclaw"); await writePackageRoot(packageRoot, "1.0.0"); diff --git a/src/infra/package-update-steps.test.ts b/src/infra/package-update-steps.test.ts index 25d4a9466613..308d769de050 100644 --- a/src/infra/package-update-steps.test.ts +++ b/src/infra/package-update-steps.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { writePackageDistInventory } from "../../scripts/lib/package-dist-inventory.ts"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { markPackagePostInstallDoctorAdvisory, runGlobalPackageUpdateSteps, @@ -157,7 +157,7 @@ describe("markPackagePostInstallDoctorAdvisory", () => { describe("runGlobalPackageUpdateSteps", () => { it("installs npm updates into a clean staged prefix before swapping the global package", async () => { - await withTempDir({ prefix: "openclaw-package-update-staged-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-staged-" }, async (base) => { const prefix = path.join(base, "prefix"); const globalRoot = path.join(prefix, "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -235,7 +235,7 @@ describe("runGlobalPackageUpdateSteps", () => { it.runIf(process.platform !== "win32")( "swaps npm package roots that contain package-manager hardlinks", async () => { - await withTempDir({ prefix: "openclaw-package-update-hardlinks-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-hardlinks-" }, async (base) => { const prefix = path.join(base, "prefix"); const globalRoot = path.join(prefix, "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -288,7 +288,7 @@ describe("runGlobalPackageUpdateSteps", () => { ); it("swaps staged npm updates into an explicitly selected direct node_modules root", async () => { - await withTempDir({ prefix: "openclaw-package-update-direct-root-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-direct-root-" }, async (base) => { const managedRoot = path.join(base, ".openclaw", "npm", "node_modules"); const packageRoot = path.join(managedRoot, "openclaw"); await writePackageRoot(packageRoot, "1.0.0"); @@ -343,7 +343,7 @@ describe("runGlobalPackageUpdateSteps", () => { }); it("accepts v-prefixed exact npm specs when verifying staged installs", async () => { - await withTempDir({ prefix: "openclaw-package-update-v-prefix-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-v-prefix-" }, async (base) => { const prefix = path.join(base, "prefix"); const globalRoot = path.join(prefix, "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -399,7 +399,7 @@ describe("runGlobalPackageUpdateSteps", () => { ])( "accepts concrete version $installedVersion staged from $installSpec", async ({ installSpec, installedVersion }) => { - await withTempDir({ prefix: "openclaw-package-update-moving-spec-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-moving-spec-" }, async (base) => { const prefix = path.join(base, "prefix"); const globalRoot = path.join(prefix, "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -451,7 +451,7 @@ describe("runGlobalPackageUpdateSteps", () => { ); it("packs npm GitHub specs before installing into the staged prefix", async () => { - await withTempDir({ prefix: "openclaw-package-update-npm-pack-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-npm-pack-" }, async (base) => { const prefix = path.join(base, "prefix"); const globalRoot = path.join(prefix, "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -569,7 +569,7 @@ describe("runGlobalPackageUpdateSteps", () => { ] as const)( "packs additional npm git source spec forms before install: $name", async ({ sourceSpec }) => { - await withTempDir({ prefix: "openclaw-package-update-npm-pack-variant-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-npm-pack-variant-" }, async (base) => { const globalRoot = path.join(base, "prefix", "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); await writePackageRoot(packageRoot, "1.0.0"); @@ -634,7 +634,7 @@ describe("runGlobalPackageUpdateSteps", () => { ); it("swaps staged npm package roots through the copy fallback when rename crosses devices", async () => { - await withTempDir({ prefix: "openclaw-package-update-exdev-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-exdev-" }, async (base) => { const prefix = path.join(base, "prefix"); const globalRoot = path.join(prefix, "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -697,7 +697,7 @@ describe("runGlobalPackageUpdateSteps", () => { }); it("stages pnpm-detected updates through npm when the global root has npm prefix layout", async () => { - await withTempDir({ prefix: "openclaw-package-update-pnpm-staged-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-pnpm-staged-" }, async (base) => { const prefix = path.join(base, "prefix"); const globalRoot = path.join(prefix, "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -753,7 +753,7 @@ describe("runGlobalPackageUpdateSteps", () => { it("keeps Windows pnpm global roots on the pnpm update path", async () => { const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32"); try { - await withTempDir({ prefix: "openclaw-package-update-win32-pnpm-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-win32-pnpm-" }, async (base) => { const globalDir = path.join(base, "pnpm", "global"); const globalRoot = path.join(globalDir, "5", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -802,7 +802,7 @@ describe("runGlobalPackageUpdateSteps", () => { }); it("keeps a successful staged swap when old package cleanup hits a transient Windows native module error", async () => { - await withTempDir({ prefix: "openclaw-package-update-staged-cleanup-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-staged-cleanup-" }, async (base) => { const prefix = path.join(base, "prefix"); const globalRoot = path.join(prefix, "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -867,7 +867,7 @@ describe("runGlobalPackageUpdateSteps", () => { }); it("does not run post-verify work when staged npm verification fails", async () => { - await withTempDir({ prefix: "openclaw-package-update-verify-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-verify-" }, async (base) => { const prefix = path.join(base, "prefix"); const globalRoot = path.join(prefix, "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -922,7 +922,7 @@ describe("runGlobalPackageUpdateSteps", () => { it.runIf(process.platform !== "win32")( "restores the existing bin shim when staged shim replacement fails", async () => { - await withTempDir({ prefix: "openclaw-package-update-shim-rollback-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-shim-rollback-" }, async (base) => { const prefix = path.join(base, "prefix"); const globalRoot = path.join(prefix, "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -991,7 +991,7 @@ describe("runGlobalPackageUpdateSteps", () => { ); it("cleans the staged npm prefix when the install command throws", async () => { - await withTempDir({ prefix: "openclaw-package-update-cleanup-" }, async (base) => { + await withTestDir({ prefix: "openclaw-package-update-cleanup-" }, async (base) => { const prefix = path.join(base, "prefix"); const globalRoot = path.join(prefix, "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); diff --git a/src/infra/path-alias-guards.test.ts b/src/infra/path-alias-guards.test.ts index df0f72a80d00..b5f5109d9355 100644 --- a/src/infra/path-alias-guards.test.ts +++ b/src/infra/path-alias-guards.test.ts @@ -2,11 +2,11 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { assertNoPathAliasEscape } from "./path-alias-guards.js"; async function withAliasRoot(cb: (root: string) => Promise): Promise { - await withTempDir({ prefix: "openclaw-path-alias-", subdir: "root" }, cb); + await withTestDir({ prefix: "openclaw-path-alias-", subdir: "root" }, cb); } describe("assertNoPathAliasEscape", () => { diff --git a/src/infra/provider-usage.load.ts b/src/infra/provider-usage.load.ts index 5175ed59fad9..3a7d0715d61d 100644 --- a/src/infra/provider-usage.load.ts +++ b/src/infra/provider-usage.load.ts @@ -12,7 +12,7 @@ import { PROVIDER_USAGE_TIMEOUT_MS, ignoredErrors, providerUsageLabel, - withTimeout, + raceUsageTimeout, } from "./provider-usage.shared.js"; import type { ProviderUsageSnapshot, @@ -142,7 +142,7 @@ export async function loadProviderUsageSummary( windows: [], error, }); - return withTimeout( + return raceUsageTimeout( fetchProviderUsageSnapshot({ auth, config, diff --git a/src/infra/provider-usage.shared.test.ts b/src/infra/provider-usage.shared.test.ts index 9947ef246cc8..8ccd719b2e63 100644 --- a/src/infra/provider-usage.shared.test.ts +++ b/src/infra/provider-usage.shared.test.ts @@ -1,7 +1,7 @@ // Covers shared provider usage helpers. import { afterEach, describe, expect, it, vi } from "vitest"; import { MAX_TIMER_TIMEOUT_MS } from "../shared/number-coercion.js"; -import { clampPercent, resolveUsageProviderId, withTimeout } from "./provider-usage.shared.js"; +import { clampPercent, raceUsageTimeout, resolveUsageProviderId } from "./provider-usage.shared.js"; describe("provider-usage.shared", () => { afterEach(() => { @@ -57,10 +57,10 @@ describe("provider-usage.shared", () => { }, ])("$name", async ({ promise, expected, error }) => { if (error) { - await expect(withTimeout(promise(), 100, "fallback")).rejects.toThrow(error); + await expect(raceUsageTimeout(promise(), 100, "fallback")).rejects.toThrow(error); return; } - await expect(withTimeout(promise(), 100, "fallback")).resolves.toBe(expected); + await expect(raceUsageTimeout(promise(), 100, "fallback")).resolves.toBe(expected); }); it("returns fallback when timeout wins", async () => { @@ -68,7 +68,7 @@ describe("provider-usage.shared", () => { const late = new Promise((resolve) => { setTimeout(() => resolve("late"), 50); }); - const result = withTimeout(late, 1, "fallback"); + const result = raceUsageTimeout(late, 1, "fallback"); await vi.advanceTimersByTimeAsync(1); await expect(result).resolves.toBe("fallback"); }); @@ -77,7 +77,11 @@ describe("provider-usage.shared", () => { vi.useFakeTimers(); const setTimeoutSpy = vi.spyOn(globalThis, "setTimeout"); - const result = withTimeout(new Promise(() => {}), Number.MAX_SAFE_INTEGER, "fallback"); + const result = raceUsageTimeout( + new Promise(() => {}), + Number.MAX_SAFE_INTEGER, + "fallback", + ); expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), MAX_TIMER_TIMEOUT_MS); @@ -88,7 +92,7 @@ describe("provider-usage.shared", () => { it("clears the timeout after successful work", async () => { const clearTimeoutSpy = vi.spyOn(globalThis, "clearTimeout"); - await expect(withTimeout(Promise.resolve("ok"), 100, "fallback")).resolves.toBe("ok"); + await expect(raceUsageTimeout(Promise.resolve("ok"), 100, "fallback")).resolves.toBe("ok"); expect(clearTimeoutSpy).toHaveBeenCalledTimes(1); }); diff --git a/src/infra/provider-usage.shared.ts b/src/infra/provider-usage.shared.ts index 9dd714e829d7..250fd3f3baca 100644 --- a/src/infra/provider-usage.shared.ts +++ b/src/infra/provider-usage.shared.ts @@ -78,7 +78,11 @@ export const clampPercent = (value: number) => Math.max(0, Math.min(100, Number.isFinite(value) ? value : 0)); /** Resolves a promise with a fallback when usage collection exceeds the timeout. */ -export const withTimeout = async (work: Promise, ms: number, fallback: T): Promise => { +export const raceUsageTimeout = async ( + work: Promise, + ms: number, + fallback: T, +): Promise => { let timeout: NodeJS.Timeout | undefined; const timeoutMs = resolveTimerTimeoutMs(ms, 1); try { diff --git a/src/infra/push-web-store.ts b/src/infra/push-web-store.ts index e3daa6fa3738..43ea160b6970 100644 --- a/src/infra/push-web-store.ts +++ b/src/infra/push-web-store.ts @@ -6,7 +6,7 @@ import { runOpenClawStateWriteTransaction, type OpenClawStateDatabaseOptions, } from "../state/openclaw-state-db.js"; -import { sha256HexPrefix } from "./crypto-digest.js"; +import { sha256HexPrefixCore } from "./crypto-digest.js"; import { executeSqliteQuerySync, executeSqliteQueryTakeFirstSync, @@ -55,7 +55,7 @@ function webPushStateDatabaseOptions(stateDir?: string): OpenClawStateDatabaseOp } export function hashWebPushEndpoint(endpoint: string): string { - return sha256HexPrefix(endpoint, 32); + return sha256HexPrefixCore(endpoint, 32); } export function isValidWebPushEndpoint(endpoint: string): boolean { diff --git a/src/infra/replace-file.test.ts b/src/infra/replace-file.test.ts index 08313f88d6d7..a8ec03ccbc80 100644 --- a/src/infra/replace-file.test.ts +++ b/src/infra/replace-file.test.ts @@ -2,14 +2,14 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { movePathWithCopyFallback } from "./replace-file.js"; describe("movePathWithCopyFallback", () => { it.runIf(process.platform !== "win32")( "rejects hardlinked source files when requested", async () => { - await withTempDir({ prefix: "openclaw-replace-file-" }, async (root) => { + await withTestDir({ prefix: "openclaw-replace-file-" }, async (root) => { const sourceDir = path.join(root, "source"); const targetDir = path.join(root, "target"); const sourceFile = path.join(sourceDir, "file.txt"); diff --git a/src/infra/restart-sentinel.test.ts b/src/infra/restart-sentinel.test.ts index 5dbed929be0a..62dd52284f95 100644 --- a/src/infra/restart-sentinel.test.ts +++ b/src/infra/restart-sentinel.test.ts @@ -35,7 +35,7 @@ import { closeOpenClawStateDatabaseForTest, openOpenClawStateDatabase, } from "../state/openclaw-state-db.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withEnvAsync } from "../test-utils/env.js"; import { executeSqliteQuerySync, @@ -71,7 +71,7 @@ beforeEach(() => { }); async function withRestartSentinelStateDir(run: () => Promise): Promise { - await withTempDir({ prefix: "openclaw-sentinel-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-sentinel-" }, async (tempDir) => { try { await withEnvAsync({ OPENCLAW_STATE_DIR: tempDir }, run); } finally { @@ -550,7 +550,7 @@ describe("restart sentinel", () => { }, ] as const)("persists the verified Git install receipt after a $name", async (testCase) => { await withRestartSentinelStateDir(async () => { - await withTempDir({ prefix: "openclaw-install-root-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-install-root-" }, async (tempDir) => { const installRoot = path.join(tempDir, "checkout"); const installAlias = path.join(tempDir, "checkout-alias"); await fs.mkdir(installRoot); @@ -665,7 +665,7 @@ describe("restart sentinel", () => { it("rejects the same Git revision when the restarted checkout root differs", async () => { await withRestartSentinelStateDir(async () => { - await withTempDir({ prefix: "openclaw-install-root-mismatch-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-install-root-mismatch-" }, async (tempDir) => { const expectedRoot = path.join(tempDir, "expected"); const runningRoot = path.join(tempDir, "running"); await fs.mkdir(expectedRoot); diff --git a/src/infra/run-node.test.ts b/src/infra/run-node.test.ts index 9cf8555e0e94..6e52bdd3fa99 100644 --- a/src/infra/run-node.test.ts +++ b/src/infra/run-node.test.ts @@ -21,11 +21,11 @@ import { resolveRuntimePostBuildRequirement, runNodeMain, } from "../../scripts/run-node.mts"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; const it = baseIt.extend<{ tmp: string }>({ tmp: async ({ task: _task }, use) => { - await withTempDir({ prefix: "openclaw-run-node-" }, use); + await withTestDir({ prefix: "openclaw-run-node-" }, use); }, }); @@ -1540,7 +1540,7 @@ describe("run-node script", () => { { label: "remote agent", args: ["agent", "--message", "hello"] }, { label: "dashboard", args: ["dashboard", "--no-open", "--yes"] }, ])("does not rebuild for $label calls against an existing dirty dist", async ({ args }) => { - await withTempDir({ prefix: "openclaw-run-node-" }, async (tmp) => { + await withTestDir({ prefix: "openclaw-run-node-" }, async (tmp) => { await setupStampedProject(tmp, { files: { [RUNTIME_POSTBUILD_STAMP]: '{"head":"abc123"}\n' }, trackConfig: true, diff --git a/src/infra/session-cost-usage-collection.ts b/src/infra/session-cost-usage-collection.ts index 952774707b5e..6f157779dd52 100644 --- a/src/infra/session-cost-usage-collection.ts +++ b/src/infra/session-cost-usage-collection.ts @@ -19,7 +19,7 @@ import { } from "../config/sessions/legacy-sqlite-marker.js"; import { resolveDefaultSessionStorePath, - resolveSessionFilePath, + resolveSessionFilePathCore, resolveSessionTranscriptsDirForAgent, } from "../config/sessions/paths.js"; import { @@ -387,7 +387,7 @@ export function resolveExistingUsageSessionFile(params: { const candidate = params.sessionFile ?? (sessionId - ? resolveSessionFilePath(sessionId, params.sessionEntry, { + ? resolveSessionFilePathCore(sessionId, params.sessionEntry, { agentId: params.agentId, }) : undefined); diff --git a/src/infra/session-cost-usage.test.ts b/src/infra/session-cost-usage.test.ts index fc37c342659e..a9be830055e1 100644 --- a/src/infra/session-cost-usage.test.ts +++ b/src/infra/session-cost-usage.test.ts @@ -9,7 +9,7 @@ import { encodeSessionArchiveContent } from "../config/sessions/archive-compress import { appendTranscriptMessage, persistSessionTranscriptTurn, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import type { SessionEntry } from "../config/sessions/types.js"; import { @@ -220,7 +220,7 @@ describe("session cost usage", () => { ...sessionTarget, sessionKey: "agent:main:mapped-other-cost", }; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: mismatchedTarget.sessionKey, @@ -463,7 +463,7 @@ describe("session cost usage", () => { const sessionFile = `sqlite:main:${sessionId}:${storePath}`; await withStateDir(root, async () => { - await upsertSessionEntry({ sessionKey, storePath }, { sessionId, updatedAt: now }); + await upsertSessionEntryCore({ sessionKey, storePath }, { sessionId, updatedAt: now }); await persistSessionTranscriptTurn( { agentId: "main", sessionId, sessionKey, storePath }, { diff --git a/src/infra/session-delivery-queue-runtime.test.ts b/src/infra/session-delivery-queue-runtime.test.ts index 496171a3f673..d0a75aadee9f 100644 --- a/src/infra/session-delivery-queue-runtime.test.ts +++ b/src/infra/session-delivery-queue-runtime.test.ts @@ -1,6 +1,6 @@ // Covers same-process scheduling for durable session delivery retries. import { afterEach, describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withEnvAsync } from "../test-utils/env.js"; import { schedulePendingSessionDeliveries, @@ -34,7 +34,7 @@ afterEach(() => { describe("session delivery queue runtime", () => { it("drains a newly scheduled durable entry", async () => { vi.useFakeTimers(); - await withTempDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: tempDir }, async () => { const id = await enqueueSessionDelivery({ kind: "agentTurn", @@ -59,7 +59,7 @@ describe("session delivery queue runtime", () => { it("retries a transient initial queue lookup failure", async () => { vi.useFakeTimers(); - await withTempDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: tempDir }, async () => { const id = await enqueueSessionDelivery({ kind: "agentTurn", @@ -90,7 +90,7 @@ describe("session delivery queue runtime", () => { it("holds a claimed entry until release then rearms it immediately", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date("2026-07-15T00:00:00.000Z")); - await withTempDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: tempDir }, async () => { const { id } = await enqueueClaimedSessionDelivery( { @@ -121,7 +121,7 @@ describe("session delivery queue runtime", () => { it("coalesces duplicate schedules while the same entry is draining", async () => { vi.useFakeTimers(); - await withTempDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: tempDir }, async () => { const id = await enqueueSessionDelivery({ kind: "agentTurn", @@ -157,7 +157,7 @@ describe("session delivery queue runtime", () => { it("retries a failed agent turn after durable backoff", async () => { vi.useFakeTimers(); - await withTempDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: tempDir }, async () => { const id = await enqueueSessionDelivery({ kind: "agentTurn", @@ -190,7 +190,7 @@ describe("session delivery queue runtime", () => { it("rearms a pending entry after a transient reload failure", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date("2026-07-15T00:00:00.000Z")); - await withTempDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: tempDir }, async () => { const id = await enqueueSessionDelivery({ kind: "agentTurn", @@ -225,7 +225,7 @@ describe("session delivery queue runtime", () => { it("backs off after a drain-level failure leaves retry metadata unchanged", async () => { vi.useFakeTimers(); - await withTempDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: tempDir }, async () => { const id = await enqueueSessionDelivery({ kind: "agentTurn", @@ -256,7 +256,7 @@ describe("session delivery queue runtime", () => { it("backs off after a no-op drain leaves an immediately due row pending", async () => { vi.useFakeTimers(); - await withTempDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: tempDir }, async () => { const id = await enqueueSessionDelivery({ kind: "agentTurn", @@ -286,7 +286,7 @@ describe("session delivery queue runtime", () => { it("reschedules pending entries after the runtime owner restarts", async () => { vi.useFakeTimers(); - await withTempDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: tempDir }, async () => { await enqueueSessionDelivery({ kind: "agentTurn", @@ -312,7 +312,7 @@ describe("session delivery queue runtime", () => { it("retries a transient startup pending-entry scan failure", async () => { vi.useFakeTimers(); - await withTempDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-runtime-" }, async (tempDir) => { await withEnvAsync({ OPENCLAW_STATE_DIR: tempDir }, async () => { await enqueueSessionDelivery({ kind: "agentTurn", diff --git a/src/infra/session-delivery-queue.recovery.persistence.test.ts b/src/infra/session-delivery-queue.recovery.persistence.test.ts index 9a6914848019..b607c22bcb40 100644 --- a/src/infra/session-delivery-queue.recovery.persistence.test.ts +++ b/src/infra/session-delivery-queue.recovery.persistence.test.ts @@ -1,7 +1,7 @@ // Covers real session-delivery retry failures against the persistent SQLite queue. import { describe, expect, it, vi } from "vitest"; import { openOpenClawStateDatabase } from "../state/openclaw-state-db.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { drainPendingSessionDeliveries, enqueueSessionDelivery, @@ -13,7 +13,7 @@ describe("session-delivery recovery persistence", () => { it.each(["startup", "targeted drain"] as const)( "surfaces real SQLite retry-persistence failures during %s recovery", async (mode) => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "systemEvent", diff --git a/src/infra/session-delivery-queue.recovery.test.ts b/src/infra/session-delivery-queue.recovery.test.ts index 7f99461b9eee..1d958bcd5c0f 100644 --- a/src/infra/session-delivery-queue.recovery.test.ts +++ b/src/infra/session-delivery-queue.recovery.test.ts @@ -2,7 +2,7 @@ import { MAX_DATE_TIMESTAMP_MS } from "@openclaw/normalization-core/number-coercion"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { controlNextRecoverySleep } from "../../test/helpers/infra/delivery-recovery.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { upsertDeliveryQueueEntry } from "./delivery-queue-sqlite.js"; const RECOVERY_REPLAY_SPACING_MS = 250; const sleepMock = vi.hoisted(() => vi.fn<(ms: number) => Promise>()); @@ -33,7 +33,7 @@ describe("session-delivery queue recovery", () => { }); it("replays and acks pending entries on recovery", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { await enqueueSessionDelivery( { kind: "systemEvent", @@ -64,7 +64,7 @@ describe("session-delivery queue recovery", () => { }); it("lets the delivery owner persist its fence at the side-effect boundary", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -94,7 +94,7 @@ describe("session-delivery queue recovery", () => { }); it("retries settlement cleanup without replaying a delivered side effect", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -146,7 +146,7 @@ describe("session-delivery queue recovery", () => { }); it("retries dead-letter cleanup without replaying an ambiguous agent turn", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -182,7 +182,7 @@ describe("session-delivery queue recovery", () => { }); it("cleans an acknowledged tombstone without replaying delivery", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { await enqueueSessionDelivery( { kind: "agentTurn", @@ -227,7 +227,7 @@ describe("session-delivery queue recovery", () => { }); it("drains an exhausted acknowledged tombstone without replay or backoff", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -278,7 +278,7 @@ describe("session-delivery queue recovery", () => { vi.setSystemTime(startedAt); try { const controlledSleep = controlNextRecoverySleep(sleepMock); - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { await enqueueSessionDelivery( { kind: "systemEvent", @@ -331,7 +331,7 @@ describe("session-delivery queue recovery", () => { vi.setSystemTime(startedAt); try { const controlledSleep = controlNextRecoverySleep(sleepMock); - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { for (const text of ["first", "second", "third"]) { await enqueueSessionDelivery( { @@ -378,7 +378,7 @@ describe("session-delivery queue recovery", () => { vi.useFakeTimers(); vi.setSystemTime(new Date(MAX_DATE_TIMESTAMP_MS)); - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { await enqueueSessionDelivery( { kind: "systemEvent", @@ -413,7 +413,7 @@ describe("session-delivery queue recovery", () => { }); it("keeps failed entries queued with retry metadata for later recovery", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { await enqueueSessionDelivery( { kind: "agentTurn", @@ -448,7 +448,7 @@ describe("session-delivery queue recovery", () => { }); it("leaves pre-dispatch failures retryable without claiming side-effect ownership", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { await enqueueSessionDelivery( { kind: "agentTurn", @@ -477,7 +477,7 @@ describe("session-delivery queue recovery", () => { }); it("releases attempt ownership only for an explicitly safe retry", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { await enqueueSessionDelivery( { kind: "agentTurn", @@ -504,7 +504,7 @@ describe("session-delivery queue recovery", () => { }); it("defers active agent ownership without consuming retry budget", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -532,7 +532,7 @@ describe("session-delivery queue recovery", () => { }); it("does not charge retry budget twice after a charged transition failure", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -563,7 +563,7 @@ describe("session-delivery queue recovery", () => { }); it("does not report an explicitly dead-lettered delivery as recovered", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -592,7 +592,7 @@ describe("session-delivery queue recovery", () => { }); it("uses the entry retry budget when draining entries", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -630,7 +630,7 @@ describe("session-delivery queue recovery", () => { }); it("settles entries moved to failed after drain retry exhaustion", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -662,7 +662,7 @@ describe("session-delivery queue recovery", () => { }); it("settles entries moved to failed after startup retry exhaustion", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -698,7 +698,7 @@ describe("session-delivery queue recovery", () => { vi.useFakeTimers(); } try { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -753,7 +753,7 @@ describe("session-delivery queue recovery", () => { ); it("dead-letters a started agent turn after its bounded reconciliation fails", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -800,7 +800,7 @@ describe("session-delivery queue recovery", () => { vi.useFakeTimers(); vi.setSystemTime(new Date("2026-04-23T00:00:00.000Z")); - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { await enqueueSessionDelivery( { kind: "systemEvent", diff --git a/src/infra/session-delivery-queue.storage.test.ts b/src/infra/session-delivery-queue.storage.test.ts index 16404a8316a0..8a91fe2a61e4 100644 --- a/src/infra/session-delivery-queue.storage.test.ts +++ b/src/infra/session-delivery-queue.storage.test.ts @@ -1,7 +1,7 @@ // Covers session delivery queue persistence state transitions. import { describe, expect, it } from "vitest"; import { openOpenClawStateDatabase } from "../state/openclaw-state-db.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { advanceSessionDeliveryAgentRun, completeSessionDelivery, @@ -40,7 +40,7 @@ describe("session-delivery queue storage", () => { } it("dedupes entries when an idempotency key is reused", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const firstId = await enqueueSessionDelivery( { kind: "agentTurn", @@ -68,7 +68,7 @@ describe("session-delivery queue storage", () => { }); it("grants one initial-attempt lease and releases it for recovery", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const payload = { kind: "agentTurn" as const, sessionKey: "agent:main:main", @@ -93,7 +93,7 @@ describe("session-delivery queue storage", () => { }); it("reports a dead-letter conflict instead of claiming it as pending", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const payload = { kind: "agentTurn" as const, sessionKey: "agent:main:main", @@ -113,7 +113,7 @@ describe("session-delivery queue storage", () => { }); it("lets an explicit enqueue revive a failed idempotency key", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const payload = { kind: "systemEvent" as const, sessionKey: "agent:main:main", @@ -130,7 +130,7 @@ describe("session-delivery queue storage", () => { }); it("never revives a failed permanent producer intent", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const payload = { kind: "systemEvent" as const, sessionKey: "agent:main:main", @@ -148,7 +148,7 @@ describe("session-delivery queue storage", () => { }); it("reports a completed conflict after acknowledgement", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const payload = { kind: "agentTurn" as const, sessionKey: "agent:main:main", @@ -173,7 +173,7 @@ describe("session-delivery queue storage", () => { }); it("atomically repairs unreadable pending JSON for an idempotent enqueue", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const payload = { kind: "systemEvent" as const, sessionKey: "agent:main:main", @@ -198,7 +198,7 @@ describe("session-delivery queue storage", () => { }); it("persists retry metadata and retains acked idempotency tombstones", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "systemEvent", @@ -220,7 +220,7 @@ describe("session-delivery queue storage", () => { }); it("retains ambiguous attempt ownership and clears it only for a safe retry", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -253,7 +253,7 @@ describe("session-delivery queue storage", () => { }); it("records which agent run attempt consumed retry budget", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -281,7 +281,7 @@ describe("session-delivery queue storage", () => { }); it("persists agent-loop routing and provenance for restart replay", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { await enqueueSessionDelivery( { kind: "agentTurn", @@ -318,7 +318,7 @@ describe("session-delivery queue storage", () => { }); it("advances only the agent run attempt and can focus its retry media", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "agentTurn", @@ -358,7 +358,7 @@ describe("session-delivery queue storage", () => { }); it("moves entries into completed idempotency state", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const id = await enqueueSessionDelivery( { kind: "systemEvent", @@ -375,7 +375,7 @@ describe("session-delivery queue storage", () => { }); it("retains a permanent completion receipt", async () => { - await withTempDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-session-delivery-" }, async (tempDir) => { const payload = { kind: "systemEvent" as const, sessionKey: "agent:main:main", diff --git a/src/infra/session-maintenance-warning.ts b/src/infra/session-maintenance-warning.ts index 167c0eb06afc..883a91941dc2 100644 --- a/src/infra/session-maintenance-warning.ts +++ b/src/infra/session-maintenance-warning.ts @@ -121,12 +121,12 @@ export async function deliverSessionMaintenanceWarning(params: WarningParams): P } try { - const { sendDurableMessageBatch } = await loadDeliverRuntime(); + const { sendDurableMessageBatchCore } = await loadDeliverRuntime(); const outboundSession = buildOutboundSessionContext({ cfg: params.cfg, sessionKey: params.sessionKey, }); - const send = await sendDurableMessageBatch({ + const send = await sendDurableMessageBatchCore({ cfg: params.cfg, channel, to: target.to, diff --git a/src/infra/sqlite-audit-record-store.test.ts b/src/infra/sqlite-audit-record-store.test.ts index db646a90eef6..624eb1ae386f 100644 --- a/src/infra/sqlite-audit-record-store.test.ts +++ b/src/infra/sqlite-audit-record-store.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from "vitest"; import { closeOpenClawStateDatabase } from "../state/openclaw-state-db.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { createSqliteAuditRecordStore } from "./sqlite-audit-record-store.js"; describe("SQLite audit record store", () => { @@ -9,7 +9,7 @@ describe("SQLite audit record store", () => { }); it("keeps the newest configured number of rows per scope", async () => { - await withTempDir({ prefix: "openclaw-audit-store-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-audit-store-" }, async (stateDir) => { const store = createSqliteAuditRecordStore<{ value: number }>({ scope: "bounded-test", maxEntries: 2, @@ -26,7 +26,7 @@ describe("SQLite audit record store", () => { }); it("reads bounded newest-first pages by sequence", async () => { - await withTempDir({ prefix: "openclaw-audit-store-latest-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-audit-store-latest-" }, async (stateDir) => { const store = createSqliteAuditRecordStore<{ value: number }>({ scope: "latest-test", maxEntries: 10, @@ -48,7 +48,7 @@ describe("SQLite audit record store", () => { }); it("preserves insertion order and prunes the oldest row when timestamps tie", async () => { - await withTempDir({ prefix: "openclaw-audit-store-ties-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-audit-store-ties-" }, async (stateDir) => { const store = createSqliteAuditRecordStore<{ value: number }>({ scope: "tied-timestamps", maxEntries: 2, @@ -65,7 +65,7 @@ describe("SQLite audit record store", () => { }); it("prunes by insertion order when wall-clock timestamps move", async () => { - await withTempDir({ prefix: "openclaw-audit-store-clock-skew-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-audit-store-clock-skew-" }, async (stateDir) => { const store = createSqliteAuditRecordStore<{ value: number }>({ scope: "clock-skew", maxEntries: 2, @@ -81,7 +81,7 @@ describe("SQLite audit record store", () => { }); it("commits batch inserts with one retention pass", async () => { - await withTempDir({ prefix: "openclaw-audit-store-batch-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-audit-store-batch-" }, async (stateDir) => { const store = createSqliteAuditRecordStore<{ value: number }>({ scope: "batch-test", maxEntries: 2, @@ -99,7 +99,7 @@ describe("SQLite audit record store", () => { }); it("keeps keyed mutations atomic without changing insertion age", async () => { - await withTempDir({ prefix: "openclaw-audit-store-upsert-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-audit-store-upsert-" }, async (stateDir) => { const store = createSqliteAuditRecordStore<{ value: number }>({ scope: "upsert-test", maxEntries: 2, @@ -147,7 +147,7 @@ describe("SQLite audit record store", () => { }); it("orders legacy batches before existing runtime rows", async () => { - await withTempDir({ prefix: "openclaw-audit-store-legacy-order-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-audit-store-legacy-order-" }, async (stateDir) => { const store = createSqliteAuditRecordStore<{ value: number }>({ scope: "legacy-order", maxEntries: 4, diff --git a/src/infra/stable-node-path.test.ts b/src/infra/stable-node-path.test.ts index c994f324f607..de23620ad6fe 100644 --- a/src/infra/stable-node-path.test.ts +++ b/src/infra/stable-node-path.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { resolveStableNodePath } from "./stable-node-path.js"; describe("resolveStableNodePath", () => { @@ -11,7 +11,7 @@ describe("resolveStableNodePath", () => { }); it("prefers the Homebrew opt symlink for default and versioned formulas", async () => { - await withTempDir({ prefix: "openclaw-stable-node-" }, async (prefix) => { + await withTestDir({ prefix: "openclaw-stable-node-" }, async (prefix) => { const defaultNode = path.join(prefix, "Cellar", "node", "25.9.0", "bin", "node"); const versionedNode = path.join(prefix, "Cellar", "node@22", "22.22.3", "bin", "node"); const optDefault = path.join(prefix, "opt", "node", "bin", "node"); @@ -28,7 +28,7 @@ describe("resolveStableNodePath", () => { }); it("falls back to the bin symlink for the default formula, otherwise original path", async () => { - await withTempDir({ prefix: "openclaw-stable-node-" }, async (prefix) => { + await withTestDir({ prefix: "openclaw-stable-node-" }, async (prefix) => { const defaultNode = path.join(prefix, "Cellar", "node", "25.9.0", "bin", "node"); const versionedNode = path.join(prefix, "Cellar", "node@22", "22.22.3", "bin", "node"); const binNode = path.join(prefix, "bin", "node"); diff --git a/src/infra/state-migrations.acp-replay.test.ts b/src/infra/state-migrations.acp-replay.test.ts index 8014d8593ccc..8c7ffd13d13b 100644 --- a/src/infra/state-migrations.acp-replay.test.ts +++ b/src/infra/state-migrations.acp-replay.test.ts @@ -7,7 +7,7 @@ import { closeOpenClawStateDatabaseForTest, openOpenClawStateDatabase, } from "../state/openclaw-state-db.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { detectLegacyAcpReplayLedger, migrateLegacyAcpReplayLedger, @@ -56,7 +56,7 @@ describe("legacy ACP replay doctor migration", () => { }); it("detects legacy state only for explicit doctor repair", async () => { - await withTempDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { await writeLegacyStore(stateDir); expect(detectLegacyAcpReplayLedger({ stateDir }).hasLegacy).toBe(false); expect( @@ -66,7 +66,7 @@ describe("legacy ACP replay doctor migration", () => { }); it("imports, verifies, and removes the retired JSON ledger", async () => { - await withTempDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { const sourcePath = await writeLegacyStore(stateDir); const result = await migrateLegacyAcpReplayLedger({ detected: detectLegacyAcpReplayLedger({ @@ -114,7 +114,7 @@ describe("legacy ACP replay doctor migration", () => { }); it("resumes a claimed source without deleting a replacement ledger", async () => { - await withTempDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { const sourcePath = await writeLegacyStore(stateDir); const claimPath = `${sourcePath}.doctor-import`; await fs.rename(sourcePath, claimPath); @@ -169,7 +169,7 @@ describe("legacy ACP replay doctor migration", () => { }); it("retains malformed state without partially importing it", async () => { - await withTempDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { const sourcePath = await writeLegacyStore(stateDir, { ...legacyStore(), sessions: { broken: { sessionId: "broken" } }, @@ -194,7 +194,7 @@ describe("legacy ACP replay doctor migration", () => { }); it("removes a retry source when its prior import already exists", async () => { - await withTempDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const ledger = createSqliteAcpEventLedger({ env, now: () => 1_000 }); await ledger.startSession({ @@ -250,7 +250,7 @@ describe("legacy ACP replay doctor migration", () => { }); it("retains a conflicting retry source instead of discarding changed events", async () => { - await withTempDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; const ledger = createSqliteAcpEventLedger({ env, now: () => 2_000 }); await ledger.startSession({ @@ -300,7 +300,7 @@ describe("legacy ACP replay doctor migration", () => { }); it("retains a source containing an impossible zero event sequence", async () => { - await withTempDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { const store = legacyStore(); store.sessions["session-1"].events[0]!.seq = 0; const sourcePath = await writeLegacyStore(stateDir, store); @@ -320,7 +320,7 @@ describe("legacy ACP replay doctor migration", () => { }); it("runtime ignores the retired JSON ledger until doctor imports it", async () => { - await withTempDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-acp-replay-migration-" }, async (stateDir) => { await writeLegacyStore(stateDir); await expect( createSqliteAcpEventLedger({ diff --git a/src/infra/state-migrations.audit-backup.test.ts b/src/infra/state-migrations.audit-backup.test.ts index 5c632e2253bb..6d4415f2c912 100644 --- a/src/infra/state-migrations.audit-backup.test.ts +++ b/src/infra/state-migrations.audit-backup.test.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; import { afterEach, describe, expect, it } from "vitest"; import { resetPluginStateStoreForTests } from "../plugin-state/plugin-state-store.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { createLegacyAuditBackupSnapshots, hasLegacyAuditBackupSources, @@ -85,7 +85,7 @@ describe("legacy audit raw backup snapshots", () => { }); it("propagates audit-directory inspection failures", async () => { - await withTempDir({ prefix: "openclaw-audit-backup-inspection-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-audit-backup-inspection-" }, async (stateDir) => { await fs.writeFile(path.join(stateDir, "logs"), "not a directory"); await expect(hasLegacyAuditBackupSources(stateDir)).rejects.toMatchObject({ @@ -95,7 +95,7 @@ describe("legacy audit raw backup snapshots", () => { }); it("captures an active legacy source before the later SQLite snapshot", async () => { - await withTempDir({ prefix: "openclaw-audit-backup-active-" }, async (rootDir) => { + await withTestDir({ prefix: "openclaw-audit-backup-active-" }, async (rootDir) => { const stateDir = path.join(rootDir, "state"); const tempDir = path.join(rootDir, "backup-temp"); const sourcePath = path.join(stateDir, "logs", "config-audit.jsonl"); @@ -116,7 +116,7 @@ describe("legacy audit raw backup snapshots", () => { }); it("captures a stable active prefix while an old writer keeps appending", async () => { - await withTempDir({ prefix: "openclaw-audit-backup-appending-" }, async (rootDir) => { + await withTestDir({ prefix: "openclaw-audit-backup-appending-" }, async (rootDir) => { const stateDir = path.join(rootDir, "state"); const tempDir = path.join(rootDir, "backup-temp"); const sourcePath = path.join(stateDir, "logs", "config-audit.jsonl"); @@ -153,7 +153,7 @@ describe("legacy audit raw backup snapshots", () => { }); it("captures an unimported append without archiving its secret", async () => { - await withTempDir({ prefix: "openclaw-audit-backup-" }, async (rootDir) => { + await withTestDir({ prefix: "openclaw-audit-backup-" }, async (rootDir) => { const stateDir = path.join(rootDir, "state"); const tempDir = path.join(rootDir, "backup-temp"); const rawPath = path.join(stateDir, "logs", "config-audit.jsonl.migrated.raw"); @@ -175,7 +175,7 @@ describe("legacy audit raw backup snapshots", () => { }); it("reconstructs a scrub-in-progress source and sanitizes its later append", async () => { - await withTempDir({ prefix: "openclaw-audit-backup-recovery-" }, async (rootDir) => { + await withTestDir({ prefix: "openclaw-audit-backup-recovery-" }, async (rootDir) => { const stateDir = path.join(rootDir, "state"); const tempDir = path.join(rootDir, "backup-temp"); const rawPath = path.join(stateDir, "logs", "config-audit.jsonl.migrated.raw"); @@ -212,7 +212,7 @@ describe("legacy audit raw backup snapshots", () => { }); it("ignores a stale restore journal after the raw archive is replaced", async () => { - await withTempDir({ prefix: "openclaw-audit-backup-stale-journal-" }, async (rootDir) => { + await withTestDir({ prefix: "openclaw-audit-backup-stale-journal-" }, async (rootDir) => { const stateDir = path.join(rootDir, "state"); const tempDir = path.join(rootDir, "backup-temp"); const rawPath = path.join(stateDir, "logs", "config-audit.jsonl.migrated.raw"); diff --git a/src/infra/state-migrations.audit.test-support.ts b/src/infra/state-migrations.audit.test-support.ts index a927a6889a35..7537618b0a4a 100644 --- a/src/infra/state-migrations.audit.test-support.ts +++ b/src/infra/state-migrations.audit.test-support.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { vi } from "vitest"; import { listConfigAuditRecordsForTests } from "../config/io.audit.test-support.js"; import { listSystemAgentAuditEntriesForTests } from "../system-agent/audit.test-support.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { detectLegacyAuditLogs, migrateLegacyAuditLogs } from "./state-migrations.audit-logs.js"; const AUDIT_SCRUB_PATTERN = Buffer.from(" \t".repeat(16)); @@ -121,7 +121,7 @@ export class AuditMigrationFixture { export const withAuditMigrationFixture = ( run: (fixture: AuditMigrationFixture) => Promise, ): Promise => - withTempDir({ prefix: "openclaw-audit-migration-" }, (stateDir) => + withTestDir({ prefix: "openclaw-audit-migration-" }, (stateDir) => run(new AuditMigrationFixture(stateDir)), ); diff --git a/src/infra/state-migrations.fs.test.ts b/src/infra/state-migrations.fs.test.ts index b2c62b5e540a..91d7da5b152e 100644 --- a/src/infra/state-migrations.fs.test.ts +++ b/src/infra/state-migrations.fs.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { ensureMigrationDir, existsDir, @@ -13,7 +13,7 @@ import { describe("state migration fs helpers", () => { it("reads directories safely and creates missing directories", async () => { - await withTempDir({ prefix: "openclaw-state-migrations-fs-" }, async (base) => { + await withTestDir({ prefix: "openclaw-state-migrations-fs-" }, async (base) => { const nested = path.join(base, "nested"); expect(safeReadDir(nested)).toStrictEqual([]); @@ -27,7 +27,7 @@ describe("state migration fs helpers", () => { }); it("distinguishes files from directories", async () => { - await withTempDir({ prefix: "openclaw-state-migrations-fs-" }, async (base) => { + await withTestDir({ prefix: "openclaw-state-migrations-fs-" }, async (base) => { const filePath = path.join(base, "store.json"); const dirPath = path.join(base, "dir"); fs.writeFileSync(filePath, "{}", "utf8"); @@ -40,7 +40,7 @@ describe("state migration fs helpers", () => { }); it("parses json5 session stores and rejects invalid shapes", async () => { - await withTempDir({ prefix: "openclaw-state-migrations-fs-" }, async (base) => { + await withTestDir({ prefix: "openclaw-state-migrations-fs-" }, async (base) => { const okPath = path.join(base, "store.json"); const jsonPath = path.join(base, "plain.json"); const badPath = path.join(base, "bad.json"); diff --git a/src/infra/state-migrations.legacy-session-store.test.ts b/src/infra/state-migrations.legacy-session-store.test.ts index e722b10487b9..c0344e894723 100644 --- a/src/infra/state-migrations.legacy-session-store.test.ts +++ b/src/infra/state-migrations.legacy-session-store.test.ts @@ -2,14 +2,14 @@ import type { MakeDirectoryOptions, Mode, PathLike } from "node:fs"; import fs from "node:fs/promises"; import path from "node:path"; import { expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { loadLegacySessionStore, saveLegacySessionStore, } from "./state-migrations.legacy-session-store.js"; it("stages prompt blobs after a recreated session directory", async () => { - await withTempDir({ prefix: "openclaw-legacy-session-store-" }, async (root) => { + await withTestDir({ prefix: "openclaw-legacy-session-store-" }, async (root) => { const storeDir = path.join(root, "sessions"); const storePath = path.join(storeDir, "sessions.json"); const sessionKey = "agent:main:main"; @@ -56,7 +56,7 @@ it("stages prompt blobs after a recreated session directory", async () => { }); it("normalizes file-era rows and drops malformed entries", async () => { - await withTempDir({ prefix: "openclaw-legacy-session-normalize-" }, async (root) => { + await withTestDir({ prefix: "openclaw-legacy-session-normalize-" }, async (root) => { const storePath = path.join(root, "sessions.json"); await fs.writeFile( storePath, @@ -97,7 +97,7 @@ it("normalizes file-era rows and drops malformed entries", async () => { }); it("normalizes compatibility writes before persistence", async () => { - await withTempDir({ prefix: "openclaw-legacy-session-write-" }, async (root) => { + await withTestDir({ prefix: "openclaw-legacy-session-write-" }, async (root) => { const storePath = path.join(root, "sessions.json"); const store = { malformed: null, diff --git a/src/infra/state-migrations.media-persistence-targets.test.ts b/src/infra/state-migrations.media-persistence-targets.test.ts index fbc08c734b4a..631f71a43b0a 100644 --- a/src/infra/state-migrations.media-persistence-targets.test.ts +++ b/src/infra/state-migrations.media-persistence-targets.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { cleanupTempDirs, makeTempDir } from "../../test/helpers/temp-dir.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { resolveSqliteTargetFromSessionStorePath } from "../config/sessions/session-sqlite-target.js"; import { registerOpenClawAgentDatabase, @@ -208,10 +208,13 @@ describe("media persistence migration targets", () => { makeTempDir(tempDirs, "media-persistence-custom-store-"), ); const env = { OPENCLAW_STATE_DIR: stateDir }; - const storePath = resolveStorePath(path.join(customRoot, "{agentId}", "sessions.json"), { - agentId: "main", - env, - }); + const storePath = resolveSessionStorePathCore( + path.join(customRoot, "{agentId}", "sessions.json"), + { + agentId: "main", + env, + }, + ); const databasePath = resolveSqliteTargetFromSessionStorePath(storePath, { agentId: "main", defaultAgentId: "main", diff --git a/src/infra/state-migrations.orphan-keys.test.ts b/src/infra/state-migrations.orphan-keys.test.ts index 27a5fa41fb78..42dde7a982eb 100644 --- a/src/infra/state-migrations.orphan-keys.test.ts +++ b/src/infra/state-migrations.orphan-keys.test.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { EMPTY_LEGACY_SESSION_SURFACES } from "../plugins/legacy-session-surfaces.types.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { migrateOrphanedSessionKeys, resolveSessionStoreOwnership, @@ -43,7 +43,7 @@ function requireStoreEntry( async function withStateFixture( run: (params: { tmpDir: string; stateDir: string }) => Promise, ): Promise { - await withTempDir({ prefix: "orphan-keys-test-" }, async (tmpDir) => { + await withTestDir({ prefix: "orphan-keys-test-" }, async (tmpDir) => { const stateDir = path.join(tmpDir, ".openclaw"); fs.mkdirSync(stateDir, { recursive: true }); await run({ tmpDir, stateDir }); diff --git a/src/infra/state-migrations.state-dir.test.ts b/src/infra/state-migrations.state-dir.test.ts index 1be8d1519e00..61d22336b963 100644 --- a/src/infra/state-migrations.state-dir.test.ts +++ b/src/infra/state-migrations.state-dir.test.ts @@ -9,7 +9,7 @@ import { writePersistedInstalledPluginIndex, } from "../plugins/installed-plugin-index-store.js"; import { runOpenClawStateWriteTransaction } from "../state/openclaw-state-db.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { autoMigrateLegacyStateDir, resetAutoMigrateLegacyStateDirForTest, @@ -17,7 +17,7 @@ import { async function withStateDirFixture(run: (root: string) => Promise): Promise { try { - await withTempDir({ prefix: "openclaw-state-dir-" }, async (root) => { + await withTestDir({ prefix: "openclaw-state-dir-" }, async (root) => { await run(root); }); } finally { diff --git a/src/infra/state-migrations.workspace-setup-sandbox.test.ts b/src/infra/state-migrations.workspace-setup-sandbox.test.ts index 6643fc1b7186..4507a64975e2 100644 --- a/src/infra/state-migrations.workspace-setup-sandbox.test.ts +++ b/src/infra/state-migrations.workspace-setup-sandbox.test.ts @@ -13,7 +13,7 @@ import { } from "../agents/workspace-state-store.js"; import { listSessionEntryKeysReadOnly, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { parseAgentSessionKey } from "../routing/session-key.js"; @@ -61,7 +61,7 @@ describe("sandbox workspace Doctor migration", () => { async function registerSandboxSession(sessionKey: string) { const agentId = parseAgentSessionKey(sessionKey)?.agentId ?? "main"; const sessionHash = createHash("sha256").update(sessionKey).digest("hex").slice(0, 12); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId, env: process.env, sessionKey }, { sessionId: `workspace-migration-proof-${sessionHash}`, updatedAt: 1 }, ); diff --git a/src/infra/state-migrations.workspace-setup-sandbox.ts b/src/infra/state-migrations.workspace-setup-sandbox.ts index 9f321620ff87..8842e540e07a 100644 --- a/src/infra/state-migrations.workspace-setup-sandbox.ts +++ b/src/infra/state-migrations.workspace-setup-sandbox.ts @@ -7,7 +7,7 @@ import { import { resolveSandboxConfigForAgent } from "../agents/sandbox/config.js"; import { resolveSandboxRuntimeStatus } from "../agents/sandbox/runtime-status.js"; import { resolveSandboxWorkspaceLayoutPaths } from "../agents/sandbox/shared.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { listSessionEntryKeysReadOnly } from "../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { parseAgentSessionKey } from "../routing/session-key.js"; @@ -53,7 +53,10 @@ export function listSandboxWorkspaceDirs(params: { const sessionKeys = listSessionEntryKeysReadOnly({ agentId, env: params.env, - storePath: resolveStorePath(params.cfg.session?.store, { agentId, env: params.env }), + storePath: resolveSessionStorePathCore(params.cfg.session?.store, { + agentId, + env: params.env, + }), }); for (const sessionKey of sessionKeys) { const sessionAgentId = parseAgentSessionKey(sessionKey)?.agentId; diff --git a/src/infra/update-check.test.ts b/src/infra/update-check.test.ts index 4e86c62cd080..411664e94fd7 100644 --- a/src/infra/update-check.test.ts +++ b/src/infra/update-check.test.ts @@ -6,7 +6,7 @@ import path from "node:path"; import { pathToFileURL } from "node:url"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { runCommandWithTimeout } from "../process/exec.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withEnvAsync } from "../test-utils/env.js"; import { useMockHttp } from "../test-utils/mock-http.js"; import { fetchNpmPackageTargetStatus } from "./update-check-package-target.js"; @@ -162,7 +162,7 @@ describe("resolveNpmChannelTag", () => { }); it("uses npm global scope, user config auth, and ignores project npmrc for real metadata", async () => { - await withTempDir({ prefix: "openclaw-update-check-npm-view-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-check-npm-view-" }, async (base) => { const requests: Array<{ url: string; authorization?: string }> = []; const server = http.createServer((req, res) => { requests.push({ @@ -628,7 +628,7 @@ describe("formatGitInstallLabel", () => { describe("checkUpdateStatus", () => { it("uses a matching receipt upstream only for the detached installed revision", async () => { - await withTempDir({ prefix: "openclaw-update-check-receipt-fallback-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-check-receipt-fallback-" }, async (base) => { const sourceRoot = path.join(base, "source"); const localRoot = path.join(base, "local"); await initGitRepo(sourceRoot); @@ -695,7 +695,7 @@ describe("checkUpdateStatus", () => { }); it("does not treat stale remote refs as current when fetch fails", async () => { - await withTempDir({ prefix: "openclaw-update-check-fetch-failure-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-check-fetch-failure-" }, async (base) => { const remoteRoot = path.join(base, "remote"); const localRoot = path.join(base, "local"); await initGitRepo(remoteRoot); @@ -724,7 +724,7 @@ describe("checkUpdateStatus", () => { }); it("does not report divergence for unrelated histories", async () => { - await withTempDir({ prefix: "openclaw-update-check-unrelated-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-check-unrelated-" }, async (base) => { const localRoot = path.join(base, "local"); const remoteRoot = path.join(base, "remote"); await initGitRepo(localRoot); @@ -760,7 +760,7 @@ describe("checkUpdateStatus", () => { }); it("reports divergence only when shallow history retains a merge base", async () => { - await withTempDir({ prefix: "openclaw-update-check-shallow-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-check-shallow-" }, async (base) => { const sourceRoot = path.join(base, "source"); await initGitRepo(sourceRoot); await commitGit(sourceRoot, "common base"); @@ -859,7 +859,7 @@ describe("checkUpdateStatus", () => { }); it("detects package installs for non-git roots", async () => { - await withTempDir({ prefix: "openclaw-update-check-" }, async (root) => { + await withTestDir({ prefix: "openclaw-update-check-" }, async (root) => { await fs.writeFile( path.join(root, "package.json"), JSON.stringify({ packageManager: "npm@10.0.0" }), @@ -884,7 +884,7 @@ describe("checkUpdateStatus", () => { }); it("resolves a status registry channel after detecting the install kind", async () => { - await withTempDir({ prefix: "openclaw-update-check-registry-channel-" }, async (root) => { + await withTestDir({ prefix: "openclaw-update-check-registry-channel-" }, async (root) => { await fs.writeFile( path.join(root, "package.json"), JSON.stringify({ packageManager: "npm@10.0.0" }), @@ -925,7 +925,7 @@ describe("checkUpdateStatus", () => { expectedLockfile: "bun.lock", }, ])("reports dependency status for Bun's $name", async ({ lockfiles, expectedLockfile }) => { - await withTempDir({ prefix: "openclaw-update-check-bun-" }, async (root) => { + await withTestDir({ prefix: "openclaw-update-check-bun-" }, async (root) => { await fs.writeFile( path.join(root, "package.json"), JSON.stringify({ name: "openclaw", packageManager: "bun@1.2.0" }), @@ -962,7 +962,7 @@ describe("checkUpdateStatus", () => { ])( "detects lockless OpenClaw $manager installs despite packed pnpm metadata", async ({ manager, expectedLockfile }) => { - await withTempDir({ prefix: `openclaw-update-check-lockless-${manager}-` }, async (base) => { + await withTestDir({ prefix: `openclaw-update-check-lockless-${manager}-` }, async (base) => { const bunInstall = path.join(base, "custom-bun-home"); const root = manager === "bun" @@ -997,7 +997,7 @@ describe("checkUpdateStatus", () => { ); it("reports missing and stale dependency markers for package installs", async () => { - await withTempDir({ prefix: "openclaw-update-check-deps-" }, async (root) => { + await withTestDir({ prefix: "openclaw-update-check-deps-" }, async (root) => { await fs.writeFile( path.join(root, "package.json"), JSON.stringify({ name: "openclaw", packageManager: "pnpm@11.2.2" }), @@ -1051,7 +1051,7 @@ describe("checkUpdateStatus", () => { }); it("treats symlinked git installs as git roots", async () => { - await withTempDir({ prefix: "openclaw-update-check-git-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-check-git-" }, async (base) => { const repoRoot = path.join(base, "repo"); const linkedRoot = path.join(base, "linked-openclaw"); await fs.mkdir(repoRoot, { recursive: true }); @@ -1076,7 +1076,7 @@ describe("checkUpdateStatus", () => { }); it("reports unsupported_git_channel for Git status without querying npm", async () => { - await withTempDir({ prefix: "openclaw-update-check-git-channel-" }, async (root) => { + await withTestDir({ prefix: "openclaw-update-check-git-channel-" }, async (root) => { await fs.writeFile( path.join(root, "package.json"), JSON.stringify({ name: "openclaw", packageManager: "pnpm@10.0.0" }), diff --git a/src/infra/update-global.pnpm11-discovery.test.ts b/src/infra/update-global.pnpm11-discovery.test.ts index 3391e1f10a3b..b5f0760044a5 100644 --- a/src/infra/update-global.pnpm11-discovery.test.ts +++ b/src/infra/update-global.pnpm11-discovery.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { detectGlobalInstallManagerForRoot, listActivePnpmIsolatedGlobalPackages, @@ -44,7 +44,7 @@ async function writePnpmIsolatedPackage(params: { describe("pnpm 11 global install discovery", () => { it("detects isolated global installs from the active project link", async () => { - await withTempDir({ prefix: "openclaw-update-pnpm-isolated-root-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-pnpm-isolated-root-" }, async (base) => { const npmRoot = path.join(base, "npm", "lib", "node_modules"); const pnpmGlobalDir = path.join(base, "pnpm-home", "global"); const pnpmGlobalRoot = path.join(pnpmGlobalDir, "v11"); @@ -110,7 +110,7 @@ describe("pnpm 11 global install discovery", () => { }); it("prefers the invoking project when multiple installs are active", async () => { - await withTempDir({ prefix: "openclaw-update-pnpm-isolated-owner-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-pnpm-isolated-owner-" }, async (base) => { const pnpmGlobalRoot = path.join(base, "pnpm-home", "global", "v11"); const otherPackageRoot = await writePnpmIsolatedPackage({ globalRoot: pnpmGlobalRoot, @@ -158,7 +158,7 @@ describe("pnpm 11 global install discovery", () => { }); it("does not adopt another pnpm project through a shared-store package symlink", async () => { - await withTempDir({ prefix: "openclaw-update-pnpm-shared-store-owner-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-pnpm-shared-store-owner-" }, async (base) => { const globalRoot = path.join(base, "pnpm-home", "global", "v11"); const activeInstallRoot = path.join(globalRoot, "active"); const orphanInstallRoot = path.join(globalRoot, "orphan"); @@ -215,7 +215,7 @@ describe("pnpm 11 global install discovery", () => { }); it("preserves pnpm 11 ownership when the invoking project is orphaned", async () => { - await withTempDir({ prefix: "openclaw-update-pnpm-isolated-orphan-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-pnpm-isolated-orphan-" }, async (base) => { const pnpmGlobalRoot = path.join(base, "pnpm-home", "global", "v11"); const orphanPackageRoot = path.join(pnpmGlobalRoot, "orphan", "node_modules", "openclaw"); await fs.mkdir(orphanPackageRoot, { recursive: true }); @@ -269,7 +269,7 @@ describe("pnpm 11 global install discovery", () => { }); it("keeps npm ownership when its prefix is named like a pnpm layout", async () => { - await withTempDir({ prefix: "openclaw-update-npm-v11-prefix-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-npm-v11-prefix-" }, async (base) => { const npmPrefix = path.join(base, "v11"); const npmGlobalRoot = path.join(npmPrefix, "lib", "node_modules"); const packageRoot = path.join(npmGlobalRoot, "openclaw"); @@ -312,7 +312,7 @@ describe("pnpm 11 global install discovery", () => { }); it("does not infer pnpm ownership without pnpm node_modules metadata", async () => { - await withTempDir({ prefix: "openclaw-update-pnpm-shape-only-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-pnpm-shape-only-" }, async (base) => { const customGlobalDir = path.join(base, "custom-pnpm"); const customGlobalRoot = path.join(customGlobalDir, "5", "node_modules"); const pkgRoot = path.join(customGlobalRoot, "openclaw"); diff --git a/src/infra/update-global.test.ts b/src/infra/update-global.test.ts index 5d24184f63e0..f4916cb5dc65 100644 --- a/src/infra/update-global.test.ts +++ b/src/infra/update-global.test.ts @@ -10,7 +10,7 @@ import { writePackageDistInventoryForPublish, } from "../../scripts/lib/package-dist-inventory.ts"; import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../plugins/runtime-sidecar-paths.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { captureEnv } from "../test-utils/env.js"; import { withMockedPlatform, @@ -244,7 +244,7 @@ describe("update global helpers", () => { it("resolves portable Git paths from process-local app data only", async () => { await withMockedWindowsPlatform(async () => { - await withTempDir({ prefix: "openclaw-update-portable-git-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-portable-git-" }, async (base) => { envSnapshot = captureEnv(["LOCALAPPDATA"]); const injectedLocalAppData = path.join(base, "injected-local-app-data"); const trustedLocalAppData = path.join(base, "trusted-local-app-data"); @@ -284,7 +284,7 @@ describe("update global helpers", () => { }); it("detects install managers from resolved roots and on-disk presence", async () => { - await withTempDir({ prefix: "openclaw-update-global-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-global-" }, async (base) => { const npmRoot = path.join(base, "npm-root"); const pnpmRoot = path.join(base, "pnpm-root"); const bunRoot = path.join(base, ".bun", "install", "global", "node_modules"); @@ -319,7 +319,7 @@ describe("update global helpers", () => { it("keeps npm self-updates on the running package root when the PATH probe diverges", async () => { await withMockedPlatform("darwin", async () => { - await withTempDir({ prefix: "openclaw-update-ephemeral-probe-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-ephemeral-probe-" }, async (base) => { // The running install lives in an nvm tree while `npm root -g` on // PATH answers with a Homebrew Cellar root — the skew produced when a // per-Node npm shim is executed by a foreign node (e.g. a launchd @@ -362,7 +362,7 @@ describe("update global helpers", () => { it("keeps scoped npm self-updates on the running package root", async () => { await withMockedPlatform("darwin", async () => { - await withTempDir({ prefix: "openclaw-update-scoped-probe-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-scoped-probe-" }, async (base) => { const nvmPrefix = path.join(base, "home", ".nvm", "versions", "node", "v24.5.0"); const nvmRoot = path.join(nvmPrefix, "lib", "node_modules"); const pkgRoot = path.join(nvmRoot, "@scope", "cli"); @@ -400,7 +400,7 @@ describe("update global helpers", () => { it("keeps the npm probe when the package root is not globally installed", async () => { await withMockedPlatform("darwin", async () => { - await withTempDir({ prefix: "openclaw-update-probe-only-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-probe-only-" }, async (base) => { const nvmPrefix = path.join(base, "home", ".nvm", "versions", "node", "v24.5.0"); const nvmRoot = path.join(nvmPrefix, "lib", "node_modules"); const pkgRoot = path.join(base, "checkout", "node_modules", "openclaw"); @@ -427,7 +427,7 @@ describe("update global helpers", () => { it("falls back to the running package root when the npm root probe fails", async () => { await withMockedPlatform("darwin", async () => { - await withTempDir({ prefix: "openclaw-update-probe-failure-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-probe-failure-" }, async (base) => { const globalRoot = path.join(base, "usr", "local", "lib", "node_modules"); const pkgRoot = path.join(globalRoot, "openclaw"); await fs.mkdir(pkgRoot, { recursive: true }); @@ -452,7 +452,7 @@ describe("update global helpers", () => { }); it("does not infer npm ownership from path shape alone when the owning npm binary is absent", async () => { - await withTempDir({ prefix: "openclaw-update-npm-missing-bin-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-npm-missing-bin-" }, async (base) => { const brewRoot = path.join(base, "opt", "homebrew", "lib", "node_modules"); const pkgRoot = path.join(brewRoot, "openclaw"); const pathNpmRoot = path.join(base, "nvm", "lib", "node_modules"); @@ -478,7 +478,7 @@ describe("update global helpers", () => { }); it("honors an explicitly selected direct npm node_modules package root", async () => { - await withTempDir({ prefix: "openclaw-update-managed-service-root-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-managed-service-root-" }, async (base) => { const managedNpmRoot = path.join(base, ".openclaw", "npm", "node_modules"); const pkgRoot = path.join(managedNpmRoot, "openclaw"); const pathNpmRoot = path.join(base, "shell", "lib", "node_modules"); @@ -541,7 +541,7 @@ describe("update global helpers", () => { }); it("preserves bun ownership for direct node_modules package roots", async () => { - await withTempDir({ prefix: "openclaw-update-managed-bun-root-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-managed-bun-root-" }, async (base) => { envSnapshot = captureEnv(["BUN_INSTALL"]); process.env.BUN_INSTALL = path.join(base, ".bun"); const bunRoot = path.join(process.env.BUN_INSTALL, "install", "global", "node_modules"); @@ -569,7 +569,7 @@ describe("update global helpers", () => { }); it("detects custom pnpm global layouts from the running package root", async () => { - await withTempDir({ prefix: "openclaw-update-pnpm-custom-root-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-pnpm-custom-root-" }, async (base) => { const customGlobalDir = path.join(base, "custom-pnpm"); const customGlobalRoot = path.join(customGlobalDir, "5", "node_modules"); const pkgRoot = path.join(customGlobalRoot, "openclaw"); @@ -618,7 +618,7 @@ describe("update global helpers", () => { }); it("detects custom pnpm global layouts from virtual-store package roots", async () => { - await withTempDir({ prefix: "openclaw-update-pnpm-virtual-root-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-pnpm-virtual-root-" }, async (base) => { const customGlobalDir = path.join(base, "custom-pnpm"); const customGlobalRoot = path.join(customGlobalDir, "5", "node_modules"); const pkgRoot = path.join( @@ -813,7 +813,7 @@ describe("update global helpers", () => { }); it("cleans only renamed package directories", async () => { - await withTempDir({ prefix: "openclaw-update-cleanup-" }, async (root) => { + await withTestDir({ prefix: "openclaw-update-cleanup-" }, async (root) => { await fs.mkdir(path.join(root, ".openclaw-123"), { recursive: true }); await fs.mkdir(path.join(root, ".openclaw-456"), { recursive: true }); await fs.writeFile(path.join(root, ".openclaw-file"), "nope", "utf8"); @@ -835,7 +835,7 @@ describe("update global helpers", () => { }); it("checks installed dist against the packaged inventory", async () => { - await withTempDir({ prefix: "openclaw-update-global-pkg-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-update-global-pkg-" }, async (packageRoot) => { await writeGlobalPackageJson(packageRoot); for (const relativePath of BUNDLED_RUNTIME_SIDECAR_PATHS) { const absolutePath = path.join(packageRoot, relativePath); @@ -863,7 +863,7 @@ describe("update global helpers", () => { }); it("rejects a staged package when lifecycle scripts leave the install guard", async () => { - await withTempDir({ prefix: "openclaw-update-global-guard-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-update-global-guard-" }, async (packageRoot) => { await writeGlobalPackageJson(packageRoot, "2026.7.2"); for (const relativePath of BUNDLED_RUNTIME_SIDECAR_PATHS) { const absolutePath = path.join(packageRoot, relativePath); @@ -879,7 +879,7 @@ describe("update global helpers", () => { }); it("reports bundled plugin install stages during installed dist verification", async () => { - await withTempDir({ prefix: "openclaw-update-global-plugin-stage-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-update-global-plugin-stage-" }, async (packageRoot) => { await writeGlobalPackageJson(packageRoot); await fs.mkdir(path.join(packageRoot, "dist", "extensions", "brave"), { recursive: true }); await writePackageDistInventory(packageRoot); @@ -909,7 +909,7 @@ describe("update global helpers", () => { }); it("flags global package roots that resolve into source checkouts", async () => { - await withTempDir({ prefix: "openclaw-update-global-source-checkout-" }, async (base) => { + await withTestDir({ prefix: "openclaw-update-global-source-checkout-" }, async (base) => { const checkoutRoot = path.join(base, "checkout"); const globalRoot = path.join(base, "prefix", "lib", "node_modules"); const packageRoot = path.join(globalRoot, "openclaw"); @@ -929,7 +929,7 @@ describe("update global helpers", () => { }); it("does not require private QA sidecars when the inventory is missing", async () => { - await withTempDir({ prefix: "openclaw-update-global-legacy-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-update-global-legacy-" }, async (packageRoot) => { await writeGlobalPackageJson(packageRoot); await expect(collectInstalledGlobalPackageErrors({ packageRoot })).resolves.toStrictEqual([]); @@ -937,7 +937,7 @@ describe("update global helpers", () => { }); it("fails closed on newer installs when the inventory is missing", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-update-global-missing-inventory-new-" }, async (packageRoot) => { await writeGlobalPackageJson(packageRoot, "2026.4.15"); @@ -950,7 +950,7 @@ describe("update global helpers", () => { }); it("rejects invalid inventory files during global verify", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-update-global-invalid-inventory-" }, async (packageRoot) => { await writeGlobalPackageJson(packageRoot, "2026.4.15"); @@ -969,7 +969,7 @@ describe("update global helpers", () => { }); it("verifies legacy sidecars for installed bundled plugins without inventory", async () => { - await withTempDir({ prefix: "openclaw-update-global-legacy-plugin-" }, async (packageRoot) => { + await withTestDir({ prefix: "openclaw-update-global-legacy-plugin-" }, async (packageRoot) => { await writeGlobalPackageJson(packageRoot); await writeBundledPluginPackageJson(packageRoot, "telegram", "@openclaw/telegram"); @@ -980,7 +980,7 @@ describe("update global helpers", () => { }); it("still enforces critical sidecars when the inventory omits them", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-update-global-critical-sidecars-" }, async (packageRoot) => { await writeGlobalPackageJson(packageRoot, "2026.4.15"); @@ -995,7 +995,7 @@ describe("update global helpers", () => { }); it("ignores stale metadata for non-packaged private QA plugins during inventory verify", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-update-global-stale-private-qa-" }, async (packageRoot) => { await writeGlobalPackageJson(packageRoot, "2026.4.15"); diff --git a/src/infra/update-runner-git.ts b/src/infra/update-runner-git.ts index 8e61b6e5ca73..044e11761453 100644 --- a/src/infra/update-runner-git.ts +++ b/src/infra/update-runner-git.ts @@ -40,7 +40,7 @@ import type { UpdateStepResult, } from "./update-runner-types.js"; -export async function runGitUpdate(params: { +export async function updateGitCheckout(params: { opts: UpdateRunnerOptions; gitRoot: string; runCommand: CommandRunner; diff --git a/src/infra/update-runner.ts b/src/infra/update-runner.ts index 6cafc6fa1bce..9937e361c094 100644 --- a/src/infra/update-runner.ts +++ b/src/infra/update-runner.ts @@ -4,7 +4,7 @@ import { detectGlobalInstallManagerForRoot } from "./update-global.js"; import { resolveUpdateInstallRoot, updateInstallRootsMatch } from "./update-install-root.js"; import { buildUpdateCommandRunner, UPDATE_RUNNER_TIMEOUT_MS } from "./update-runner-command.js"; import { resolveUpdateDoctorExecutionPolicy } from "./update-runner-doctor.js"; -import { runGitUpdate } from "./update-runner-git.js"; +import { updateGitCheckout } from "./update-runner-git.js"; import { runGlobalUpdate } from "./update-runner-global.js"; import { buildStartDirs, @@ -57,7 +57,7 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise< }; } if (gitRoot && pkgRoot && updateInstallRootsMatch(gitRoot, pkgRoot)) { - return await runGitUpdate({ + return await updateGitCheckout({ opts, gitRoot, runCommand, diff --git a/src/infra/watch-node.test.ts b/src/infra/watch-node.test.ts index 46bd70f88a02..7901604dbcaf 100644 --- a/src/infra/watch-node.test.ts +++ b/src/infra/watch-node.test.ts @@ -8,7 +8,7 @@ import { createRequireRecord, bundledPluginFile } from "openclaw/plugin-sdk/test import { describe, expect, it, vi } from "vitest"; import { runNodeWatchedPaths } from "../../scripts/run-node.mts"; import { runWatchMain } from "../../scripts/watch-node.mts"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; const VOICE_CALL_README = bundledPluginFile("voice-call", "README.md"); const VOICE_CALL_MANIFEST = bundledPluginFile("voice-call", "openclaw.plugin.json"); @@ -111,7 +111,7 @@ function requireSpawnEnv(spawn: ReturnType, callIndex: number) { describe("watch-node script", () => { it("wires chokidar watch to run-node with watched source/config paths", async () => { const { child, spawn, watcher, createWatcher, fakeProcess } = createWatchHarness(); - await withTempDir({ prefix: "openclaw-watch-node-" }, async (cwd) => { + await withTestDir({ prefix: "openclaw-watch-node-" }, async (cwd) => { fs.mkdirSync(path.join(cwd, "src", "infra"), { recursive: true }); fs.mkdirSync(path.join(cwd, "extensions", "voice-call"), { recursive: true }); @@ -200,7 +200,7 @@ describe("watch-node script", () => { it("preserves explicit sync I/O trace overrides for gateway watch", async () => { const { child, spawn, createWatcher, fakeProcess } = createWatchHarness(); - await withTempDir({ prefix: "openclaw-watch-node-" }, async (cwd) => { + await withTestDir({ prefix: "openclaw-watch-node-" }, async (cwd) => { const runPromise = runWatch({ args: ["gateway", "--force"], cwd, @@ -758,7 +758,7 @@ describe("watch-node script", () => { it("replaces an existing watcher lock holder before starting", async () => { const { child, spawn, watcher, createWatcher, fakeProcess } = createWatchHarness(); - await withTempDir({ prefix: "openclaw-watch-node-lock-" }, async (cwd) => { + await withTestDir({ prefix: "openclaw-watch-node-lock-" }, async (cwd) => { const lockPath = resolveTestWatchLockPath(cwd, ["gateway", "--force"]); fs.mkdirSync(path.dirname(lockPath), { recursive: true }); fs.writeFileSync( diff --git a/src/library.ts b/src/library.ts index 5fdfce5bdb9a..4eba1f3312ab 100644 --- a/src/library.ts +++ b/src/library.ts @@ -5,7 +5,7 @@ import { createDefaultDeps } from "./cli/deps.js"; import type { promptYesNo as promptYesNoRuntime } from "./cli/prompt.js"; import { waitForever } from "./cli/wait.js"; import { loadConfig } from "./config/config.js"; -import { resolveStorePath } from "./config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "./config/sessions/paths.js"; import { deriveSessionKey, resolveSessionKey } from "./config/sessions/session-key.js"; import type { ensureBinary as ensureBinaryRuntime } from "./infra/binaries.js"; import { @@ -79,6 +79,6 @@ export { normalizeE164, PortInUseError, resolveSessionKey, - resolveStorePath, + resolveSessionStorePathCore as resolveStorePath, waitForever, }; diff --git a/src/logger.test.ts b/src/logger.test.ts index aedd2457defa..68f3b2b9c3af 100644 --- a/src/logger.test.ts +++ b/src/logger.test.ts @@ -12,7 +12,7 @@ import { } from "./logging.js"; import { flushLogger } from "./logging/logger.js"; import type { RuntimeEnv } from "./runtime.js"; -import { withTempDir } from "./test-helpers/temp-dir.js"; +import { withTestDir } from "./test-helpers/temp-dir.js"; describe("logger helpers", () => { afterEach(() => { @@ -50,7 +50,7 @@ describe("logger helpers", () => { }); it("writes to configured log file at configured level", async () => { - await withTempDir({ prefix: "openclaw-log-test-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-log-test-" }, async (dir) => { const logPath = path.join(dir, "openclaw.log"); setLoggerOverride({ level: "info", file: logPath }); fs.writeFileSync(logPath, ""); @@ -64,7 +64,7 @@ describe("logger helpers", () => { }); it("filters messages below configured level", async () => { - await withTempDir({ prefix: "openclaw-log-test-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-log-test-" }, async (dir) => { const logPath = path.join(dir, "openclaw.log"); setLoggerOverride({ level: "warn", file: logPath }); logInfo("info-only"); @@ -77,7 +77,7 @@ describe("logger helpers", () => { }); it("uses daily rolling log files and prunes old ones", async () => { - await withTempDir({ prefix: "openclaw-log-test-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-log-test-" }, async (dir) => { resetLogger(); const today = localDateString(new Date()); const todayPath = path.join(dir, `openclaw-${today}.log`); diff --git a/src/logging/redact-identifier.ts b/src/logging/redact-identifier.ts index 43978cf351e9..c19040884b24 100644 --- a/src/logging/redact-identifier.ts +++ b/src/logging/redact-identifier.ts @@ -1,11 +1,11 @@ // Identifier redaction helpers replace sensitive identifiers with stable hashes. import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; -import { sha256HexPrefix as digestSha256HexPrefix } from "../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../infra/crypto-digest.js"; /** Returns a stable sha256 hex prefix for non-secret identifier correlation. */ export function sha256HexPrefix(value: string, len = 12): string { const safeLen = Number.isFinite(len) ? Math.max(1, Math.floor(len)) : 12; - return digestSha256HexPrefix(value, safeLen); + return sha256HexPrefixCore(value, safeLen); } /** Redacts an identifier to a stable hash label, or "-" for missing values. */ diff --git a/src/media-understanding/apply.echo-transcript.test.ts b/src/media-understanding/apply.echo-transcript.test.ts index 188acc87a15b..696835f2cb6d 100644 --- a/src/media-understanding/apply.echo-transcript.test.ts +++ b/src/media-understanding/apply.echo-transcript.test.ts @@ -14,9 +14,10 @@ import type { MediaUnderstandingProvider } from "./types.js"; // Module mocks // --------------------------------------------------------------------------- -type ResolveApiKeyForProvider = typeof import("../agents/model-auth.js").resolveApiKeyForProvider; +type ResolveApiKeyForProvider = + typeof import("../agents/model-auth.js").resolveApiKeyForProviderCore; -const resolveApiKeyForProviderMock = vi.hoisted(() => +const resolveApiKeyForProviderCoreMock = vi.hoisted(() => vi.fn(async () => ({ apiKey: "test-key", // pragma: allowlist secret source: "test", @@ -25,7 +26,7 @@ const resolveApiKeyForProviderMock = vi.hoisted(() => ); const hasAvailableAuthForProviderMock = vi.hoisted(() => vi.fn(async (...args: Parameters) => { - const resolved = await resolveApiKeyForProviderMock(...args); + const resolved = await resolveApiKeyForProviderCoreMock(...args); return Boolean(resolved?.apiKey); }), ); @@ -160,7 +161,7 @@ describe("applyMediaUnderstanding – echo transcript", () => { beforeAll(async () => { vi.resetModules(); vi.doMock("../agents/model-auth.js", () => ({ - resolveApiKeyForProvider: resolveApiKeyForProviderMock, + resolveApiKeyForProviderCore: resolveApiKeyForProviderCoreMock, hasAvailableAuthForProvider: hasAvailableAuthForProviderMock, isProviderAuthError: (err: unknown, code?: string) => err instanceof Error && @@ -194,7 +195,7 @@ describe("applyMediaUnderstanding – echo transcript", () => { runCommandWithTimeout: runCommandWithTimeoutMock, })); vi.doMock("../channels/message/runtime.js", () => ({ - sendDurableMessageBatch: (...args: unknown[]) => mockDeliverOutboundPayloads(...args), + sendDurableMessageBatchCore: (...args: unknown[]) => mockDeliverOutboundPayloads(...args), })); vi.doMock("../utils/message-channel.js", () => ({ isDeliverableMessageChannel: (channel: string) => channel === "voicechat", @@ -238,7 +239,7 @@ describe("applyMediaUnderstanding – echo transcript", () => { }); beforeEach(() => { - resolveApiKeyForProviderMock.mockClear(); + resolveApiKeyForProviderCoreMock.mockClear(); hasAvailableAuthForProviderMock.mockClear(); getApiKeyForModelMock.mockClear(); readRemoteMediaBufferMock.mockClear(); diff --git a/src/media-understanding/apply.test.ts b/src/media-understanding/apply.test.ts index c906d6785b57..343477b17331 100644 --- a/src/media-understanding/apply.test.ts +++ b/src/media-understanding/apply.test.ts @@ -12,9 +12,10 @@ import { CLI_OUTPUT_MAX_BUFFER } from "./defaults.constants.js"; import { createSafeAudioFixtureBuffer } from "./runner.test-utils.js"; import type { MediaUnderstandingProvider } from "./types.js"; -type ResolveApiKeyForProvider = typeof import("../agents/model-auth.js").resolveApiKeyForProvider; +type ResolveApiKeyForProvider = + typeof import("../agents/model-auth.js").resolveApiKeyForProviderCore; -const resolveApiKeyForProviderMock = vi.hoisted(() => +const resolveApiKeyForProviderCoreMock = vi.hoisted(() => vi.fn(async () => ({ apiKey: "test-key", // pragma: allowlist secret source: "test", @@ -23,7 +24,7 @@ const resolveApiKeyForProviderMock = vi.hoisted(() => ); const hasAvailableAuthForProviderMock = vi.hoisted(() => vi.fn(async (...args: Parameters) => { - const resolved = await resolveApiKeyForProviderMock(...args); + const resolved = await resolveApiKeyForProviderCoreMock(...args); return Boolean(resolved?.apiKey); }), ); @@ -34,7 +35,7 @@ const runExecMock = vi.hoisted(() => vi.fn()); let applyMediaUnderstanding: typeof import("./apply.js").applyMediaUnderstanding; let clearMediaUnderstandingBinaryCacheForTests: typeof import("./runner.test-support.js").clearMediaUnderstandingBinaryCacheForTests; -const mockedResolveApiKey = resolveApiKeyForProviderMock; +const mockedResolveApiKey = resolveApiKeyForProviderCoreMock; const mockedReadRemoteMediaBuffer = readRemoteMediaBufferMock; const mockedRunFfmpeg = runFfmpegMock; const mockedConvertHeicToJpeg = convertHeicToJpegMock; @@ -310,7 +311,7 @@ describe("applyMediaUnderstanding", () => { beforeAll(async () => { vi.resetModules(); vi.doMock("../agents/model-auth.js", () => ({ - resolveApiKeyForProvider: resolveApiKeyForProviderMock, + resolveApiKeyForProviderCore: resolveApiKeyForProviderCoreMock, hasAvailableAuthForProvider: hasAvailableAuthForProviderMock, isProviderAuthError: (err: unknown, code?: string) => err instanceof Error && diff --git a/src/media-understanding/attachments.cache.test.ts b/src/media-understanding/attachments.cache.test.ts index f635146026fb..fd5f5f2a19bb 100644 --- a/src/media-understanding/attachments.cache.test.ts +++ b/src/media-understanding/attachments.cache.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import JSZip from "jszip"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { MediaAttachmentCache } from "./attachments.js"; const readRemoteMediaBufferMock = vi.hoisted(() => vi.fn()); @@ -28,7 +28,7 @@ describe("media understanding attachment MIME detection", () => { }); it("prefers local attachment bytes over conflicting declared MIME", async () => { - await withTempDir({ prefix: "openclaw-media-cache-mime-local-" }, async (base) => { + await withTestDir({ prefix: "openclaw-media-cache-mime-local-" }, async (base) => { const attachmentPath = path.join(base, "photo.jpg"); await fs.writeFile(attachmentPath, PNG_1X1); const cache = new MediaAttachmentCache( diff --git a/src/media-understanding/attachments.file-url.windows.test.ts b/src/media-understanding/attachments.file-url.windows.test.ts index 7a5e27a2255b..bfa3b593e050 100644 --- a/src/media-understanding/attachments.file-url.windows.test.ts +++ b/src/media-understanding/attachments.file-url.windows.test.ts @@ -2,13 +2,13 @@ import fs from "node:fs/promises"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { MediaAttachmentCache } from "./attachments.js"; import { normalizeAttachmentPath } from "./attachments.normalize.js"; describe.runIf(process.platform === "win32")("media attachment Windows file URLs", () => { it("reads a single-slash uppercase file URL through the attachment cache", async () => { - await withTempDir({ prefix: "openclaw-media-file-url-" }, async (base) => { + await withTestDir({ prefix: "openclaw-media-file-url-" }, async (base) => { const filePath = path.join(base, "café photo.png"); const contents = Buffer.from("media-understanding-file-url"); await fs.writeFile(filePath, contents); diff --git a/src/media-understanding/echo-transcript.test.ts b/src/media-understanding/echo-transcript.test.ts index c6ff33234fb0..1a419f4b8903 100644 --- a/src/media-understanding/echo-transcript.test.ts +++ b/src/media-understanding/echo-transcript.test.ts @@ -17,7 +17,7 @@ vi.mock("../infra/outbound/deliver.js", () => ({ })); vi.mock("../channels/message/runtime.js", () => ({ - sendDurableMessageBatch: (...args: unknown[]) => mockDeliverOutboundPayloads(...args), + sendDurableMessageBatchCore: (...args: unknown[]) => mockDeliverOutboundPayloads(...args), })); vi.mock("../utils/message-channel.js", () => ({ diff --git a/src/media-understanding/echo-transcript.ts b/src/media-understanding/echo-transcript.ts index 01b62d76bfdb..2678bc5010b0 100644 --- a/src/media-understanding/echo-transcript.ts +++ b/src/media-understanding/echo-transcript.ts @@ -53,8 +53,8 @@ export async function sendTranscriptEcho(params: { const text = formatEchoTranscript(transcript, params.format ?? DEFAULT_ECHO_TRANSCRIPT_FORMAT); try { - const { sendDurableMessageBatch } = await loadMessageRuntime(); - const send = await sendDurableMessageBatch({ + const { sendDurableMessageBatchCore } = await loadMessageRuntime(); + const send = await sendDurableMessageBatchCore({ cfg, channel: normalizedChannel, to, diff --git a/src/media-understanding/image.runtime-profile.test.ts b/src/media-understanding/image.runtime-profile.test.ts index c1d936e19f7a..b75203a6bacb 100644 --- a/src/media-understanding/image.runtime-profile.test.ts +++ b/src/media-understanding/image.runtime-profile.test.ts @@ -28,7 +28,7 @@ const hoisted = vi.hoisted(() => ({ mode: "oauth", }), ), - resolveApiKeyForProviderMock: vi.fn(async () => ({ + resolveApiKeyForProviderCoreMock: vi.fn(async () => ({ [API_KEY_FIELD]: "test-token", source: "test", mode: "oauth", @@ -51,7 +51,7 @@ const { completeMock, ensureOpenClawModelsJsonMock, getApiKeyForModelMock, - resolveApiKeyForProviderMock, + resolveApiKeyForProviderCoreMock, requireApiKeyMock, setRuntimeApiKeyMock, discoverModelsMock, @@ -119,7 +119,7 @@ vi.mock("../agents/models-config.js", async () => ({ vi.mock("../agents/model-auth.js", () => ({ applySecretRefHeaderSentinels: (model: unknown) => model, getApiKeyForModelCore: getApiKeyForModelMock, - resolveApiKeyForProvider: resolveApiKeyForProviderMock, + resolveApiKeyForProviderCore: resolveApiKeyForProviderCoreMock, [REQUIRE_API_KEY_FIELD]: requireApiKeyMock, })); diff --git a/src/media-understanding/image.runtime-timeout.test.ts b/src/media-understanding/image.runtime-timeout.test.ts index 8f4e8f9ced91..0336e91718e6 100644 --- a/src/media-understanding/image.runtime-timeout.test.ts +++ b/src/media-understanding/image.runtime-timeout.test.ts @@ -23,7 +23,7 @@ const hoisted = vi.hoisted(() => ({ mode: "oauth", }), ), - resolveApiKeyForProviderMock: vi.fn(async () => ({ + resolveApiKeyForProviderCoreMock: vi.fn(async () => ({ [API_KEY_FIELD]: "test-token", source: "test", mode: "oauth", @@ -46,7 +46,7 @@ const { completeMock, ensureOpenClawModelsJsonMock, getApiKeyForModelMock, - resolveApiKeyForProviderMock, + resolveApiKeyForProviderCoreMock, requireApiKeyMock, setRuntimeApiKeyMock, discoverModelsMock, @@ -108,7 +108,7 @@ vi.mock("../agents/models-config.js", async () => ({ vi.mock("../agents/model-auth.js", () => ({ applySecretRefHeaderSentinels: (model: unknown) => model, getApiKeyForModelCore: getApiKeyForModelMock, - resolveApiKeyForProvider: resolveApiKeyForProviderMock, + resolveApiKeyForProviderCore: resolveApiKeyForProviderCoreMock, [REQUIRE_API_KEY_FIELD]: requireApiKeyMock, })); diff --git a/src/media-understanding/image.test.ts b/src/media-understanding/image.test.ts index c661fc39fd75..722be48ca2c5 100644 --- a/src/media-understanding/image.test.ts +++ b/src/media-understanding/image.test.ts @@ -25,7 +25,7 @@ const hoisted = vi.hoisted(() => ({ mode: "oauth", }), ), - resolveApiKeyForProviderMock: vi.fn(async () => ({ + resolveApiKeyForProviderCoreMock: vi.fn(async () => ({ [API_KEY_FIELD]: "test-api-key", // pragma: allowlist secret source: "test", mode: "oauth", @@ -48,7 +48,7 @@ const { completeMock, ensureOpenClawModelsJsonMock, getApiKeyForModelMock, - resolveApiKeyForProviderMock, + resolveApiKeyForProviderCoreMock, requireApiKeyMock, setRuntimeApiKeyMock, discoverModelsMock, @@ -118,7 +118,7 @@ vi.mock("../agents/models-config.js", async () => ({ vi.mock("../agents/model-auth.js", () => ({ applySecretRefHeaderSentinels: (model: unknown) => model, getApiKeyForModelCore: getApiKeyForModelMock, - resolveApiKeyForProvider: resolveApiKeyForProviderMock, + resolveApiKeyForProviderCore: resolveApiKeyForProviderCoreMock, [REQUIRE_API_KEY_FIELD]: requireApiKeyMock, })); @@ -562,7 +562,7 @@ describe("describeImageWithModelCore", () => { model: "MiniMax-VL-01", }); - expect(resolveApiKeyForProviderMock).toHaveBeenCalledWith( + expect(resolveApiKeyForProviderCoreMock).toHaveBeenCalledWith( expect.objectContaining({ provider: "minimax-portal", agentDir: "/tmp/openclaw-agent", @@ -609,7 +609,7 @@ describe("describeImageWithModelCore", () => { model: "MiniMax-VL-01", }); - expect(resolveApiKeyForProviderMock).toHaveBeenCalledWith( + expect(resolveApiKeyForProviderCoreMock).toHaveBeenCalledWith( expect.objectContaining({ provider: "minimax", }), @@ -659,7 +659,7 @@ describe("describeImageWithModelCore", () => { model: "MiniMax-VL-01", }); - expect(resolveApiKeyForProviderMock).toHaveBeenCalledWith( + expect(resolveApiKeyForProviderCoreMock).toHaveBeenCalledWith( expect.objectContaining({ provider: "minimax-cn", }), diff --git a/src/media-understanding/image.ts b/src/media-understanding/image.ts index e249d59acb58..b5860fea24ef 100644 --- a/src/media-understanding/image.ts +++ b/src/media-understanding/image.ts @@ -4,7 +4,7 @@ import { clampPositiveTimerTimeoutMs } from "@openclaw/normalization-core/number import { isPromiseLike } from "@openclaw/normalization-core/promise-like"; import { isRecord } from "@openclaw/normalization-core/record-coerce"; import { isMinimaxVlmModel, minimaxUnderstandImage } from "../agents/minimax-vlm.js"; -import { requireApiKey, resolveApiKeyForProvider } from "../agents/model-auth.js"; +import { requireApiKey, resolveApiKeyForProviderCore } from "../agents/model-auth.js"; import { resolveProviderRequestCapabilities } from "../agents/provider-attribution.js"; import { getModelProviderRequestTransport, @@ -310,7 +310,7 @@ async function resolveMinimaxVlmFallbackRuntime(params: { preferredProfile?: string; }): Promise<{ runtimeValue: string; modelBaseUrl?: string }> { const authProvider = resolveMinimaxVlmAuthProvider(params.cfg, params.provider); - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: authProvider, cfg: params.cfg, secretSentinels: true, diff --git a/src/media-understanding/media-understanding-misc.test.ts b/src/media-understanding/media-understanding-misc.test.ts index 18e857c3183f..1bc5f34e0c79 100644 --- a/src/media-understanding/media-understanding-misc.test.ts +++ b/src/media-understanding/media-understanding-misc.test.ts @@ -6,7 +6,7 @@ import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import * as fsSafe from "../infra/fs-safe.js"; import { saveMediaBuffer } from "../media/store.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { captureEnv, setTestEnvValue } from "../test-utils/env.js"; import { withFetchPreconnect } from "../test-utils/fetch-mock.js"; import { MediaAttachmentCache } from "./attachments.js"; @@ -43,7 +43,7 @@ async function withLocalAttachmentCache( canonicalAttachmentPath: string; }) => Promise, ) { - await withTempDir({ prefix }, async (base) => { + await withTestDir({ prefix }, async (base) => { const allowedRoot = path.join(base, "allowed"); const attachmentPath = path.join(allowedRoot, "voice-note.m4a"); await fs.mkdir(allowedRoot, { recursive: true }); @@ -147,7 +147,7 @@ describe("media understanding attachments SSRF", () => { }); it("resolves relative attachment paths against the provided workspaceDir", async () => { - await withTempDir({ prefix: "openclaw-media-cache-workspace-" }, async (base) => { + await withTestDir({ prefix: "openclaw-media-cache-workspace-" }, async (base) => { const workspaceDir = path.join(base, "workspace"); const attachmentPath = path.join(workspaceDir, "media", "inbound", "report.pdf"); await fs.mkdir(path.dirname(attachmentPath), { recursive: true }); @@ -164,7 +164,7 @@ describe("media understanding attachments SSRF", () => { }); it("resolves each relative attachment against its own workspace", async () => { - await withTempDir({ prefix: "openclaw-media-cache-workspaces-" }, async (base) => { + await withTestDir({ prefix: "openclaw-media-cache-workspaces-" }, async (base) => { const workspaces = ["first", "second"].map((name) => path.join(base, name)); await Promise.all( workspaces.map(async (workspaceDir, index) => { @@ -195,7 +195,7 @@ describe("media understanding attachments SSRF", () => { }); it("resolves existing state-relative media paths when cwd differs from state dir", async () => { - await withTempDir({ prefix: "openclaw-media-cache-state-relative-" }, async (base) => { + await withTestDir({ prefix: "openclaw-media-cache-state-relative-" }, async (base) => { const stateDir = path.join(base, "state"); const cwd = path.join(base, "cwd"); const relativePath = "media/inbound/telegram.jpg"; @@ -217,7 +217,7 @@ describe("media understanding attachments SSRF", () => { }); it("resolves managed inbound media URI attachments", async () => { - await withTempDir({ prefix: "openclaw-media-cache-managed-inbound-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-media-cache-managed-inbound-" }, async (stateDir) => { setTestEnvValue("OPENCLAW_STATE_DIR", stateDir); const saved = await saveMediaBuffer(Buffer.from("managed-media"), "text/plain", "inbound"); @@ -246,7 +246,7 @@ describe("media understanding attachments SSRF", () => { }); it("keeps cwd-relative fallback when a state-relative candidate does not exist", async () => { - await withTempDir({ prefix: "openclaw-media-cache-cwd-relative-" }, async (base) => { + await withTestDir({ prefix: "openclaw-media-cache-cwd-relative-" }, async (base) => { const stateDir = path.join(base, "state"); const cwd = path.join(base, "cwd"); const relativePath = "media/inbound/local.jpg"; @@ -268,7 +268,7 @@ describe("media understanding attachments SSRF", () => { }); it("prefers an existing cwd-relative attachment over a state-relative collision", async () => { - await withTempDir({ prefix: "openclaw-media-cache-relative-collision-" }, async (base) => { + await withTestDir({ prefix: "openclaw-media-cache-relative-collision-" }, async (base) => { const stateDir = path.join(base, "state"); const cwd = path.join(base, "cwd"); const relativePath = "media/inbound/photo.jpg"; @@ -291,7 +291,7 @@ describe("media understanding attachments SSRF", () => { }); it("falls back to state media when a cwd collision is outside allowed roots", async () => { - await withTempDir({ prefix: "openclaw-media-cache-blocked-cwd-collision-" }, async (base) => { + await withTestDir({ prefix: "openclaw-media-cache-blocked-cwd-collision-" }, async (base) => { const stateDir = path.join(base, "state"); const cwd = path.join(base, "cwd"); const relativePath = "media/inbound/photo.jpg"; @@ -327,7 +327,7 @@ describe("media understanding attachments SSRF", () => { }); it("blocks directory attachments even inside configured roots", async () => { - await withTempDir({ prefix: "openclaw-media-cache-dir-" }, async (base) => { + await withTestDir({ prefix: "openclaw-media-cache-dir-" }, async (base) => { const allowedRoot = path.join(base, "allowed"); const attachmentPath = path.join(allowedRoot, "nested"); await fs.mkdir(attachmentPath, { recursive: true }); @@ -346,7 +346,7 @@ describe("media understanding attachments SSRF", () => { if (process.platform === "win32") { return; } - await withTempDir({ prefix: "openclaw-media-cache-symlink-" }, async (base) => { + await withTestDir({ prefix: "openclaw-media-cache-symlink-" }, async (base) => { const allowedRoot = path.join(base, "allowed"); const outsidePath = "/etc/passwd"; const symlinkPath = path.join(allowedRoot, "note.txt"); @@ -415,7 +415,7 @@ describe("media understanding attachments SSRF", () => { }); it("rejects local attachments when canonicalization fails", async () => { - await withTempDir({ prefix: "openclaw-media-cache-realpath-failure-" }, async (base) => { + await withTestDir({ prefix: "openclaw-media-cache-realpath-failure-" }, async (base) => { const allowedRoot = path.join(base, "allowed"); const attachmentPath = path.join(allowedRoot, "voice-note.m4a"); await fs.mkdir(allowedRoot, { recursive: true }); diff --git a/src/media-understanding/media-understanding-url-fallback.test.ts b/src/media-understanding/media-understanding-url-fallback.test.ts index 3ce751b1f653..d5ff351d3cf3 100644 --- a/src/media-understanding/media-understanding-url-fallback.test.ts +++ b/src/media-understanding/media-understanding-url-fallback.test.ts @@ -4,7 +4,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { MediaAttachmentCache } from "./attachments.js"; const readRemoteMediaBufferMock = vi.hoisted(() => vi.fn()); @@ -39,7 +39,7 @@ async function withBlockedLocalAttachmentFallback( prefix: string, run: (params: { cache: MediaAttachmentCache; fallbackUrl: string }) => Promise, ) { - await withTempDir({ prefix }, async (base) => { + await withTestDir({ prefix }, async (base) => { const attachmentRoot = path.join(base, "attachment"); const allowedRoot = path.join(base, "allowed"); const attachmentPath = path.join(attachmentRoot, "voice-note.m4a"); @@ -125,7 +125,7 @@ describe("media understanding attachment URL fallback", () => { }); it("keeps HTTP fallback when the supplied local path is missing", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-media-cache-missing-path-url-fallback-" }, async (base) => { const fallbackUrl = "https://example.com/fallback.jpg"; diff --git a/src/media-understanding/runner.auto-audio.test.ts b/src/media-understanding/runner.auto-audio.test.ts index 3a8ad1369576..07dff054a8ff 100644 --- a/src/media-understanding/runner.auto-audio.test.ts +++ b/src/media-understanding/runner.auto-audio.test.ts @@ -112,14 +112,14 @@ describe("runCapability auto audio entries", () => { it("skips OpenAI audio auto-selection when only ChatGPT OAuth is available", async () => { const modelAuth = await import("../agents/model-auth.js"); const hasAvailableAuthForProvider = vi.mocked(modelAuth.hasAvailableAuthForProvider); - const resolveApiKeyForProvider = vi.mocked(modelAuth.resolveApiKeyForProvider); + const resolveApiKeyForProviderCore = vi.mocked(modelAuth.resolveApiKeyForProviderCore); hasAvailableAuthForProvider.mockImplementation(async (params) => { if (params.provider === "openai") { return params.modelApi === undefined; } return params.provider === "mistral"; }); - resolveApiKeyForProvider.mockImplementation(async (params) => ({ + resolveApiKeyForProviderCore.mockImplementation(async (params) => ({ apiKey: `${params.provider}-key`, source: "test", mode: "api-key", @@ -190,8 +190,8 @@ describe("runCapability auto audio entries", () => { } finally { hasAvailableAuthForProvider.mockReset(); hasAvailableAuthForProvider.mockResolvedValue(true); - resolveApiKeyForProvider.mockReset(); - resolveApiKeyForProvider.mockResolvedValue({ + resolveApiKeyForProviderCore.mockReset(); + resolveApiKeyForProviderCore.mockResolvedValue({ apiKey: "test-key", source: "test", mode: "api-key", @@ -201,8 +201,8 @@ describe("runCapability auto audio entries", () => { it("passes workspaceDir to auto-selected audio provider execution auth", async () => { const modelAuth = await import("../agents/model-auth.js"); - const resolveApiKeyForProvider = vi.mocked(modelAuth.resolveApiKeyForProvider); - resolveApiKeyForProvider.mockClear(); + const resolveApiKeyForProviderCore = vi.mocked(modelAuth.resolveApiKeyForProviderCore); + resolveApiKeyForProviderCore.mockClear(); await withAudioFixture("openclaw-auto-audio-workspace-auth", async ({ ctx, media, cache }) => { const result = await runCapability({ @@ -231,7 +231,7 @@ describe("runCapability auto audio entries", () => { expect(requireCapabilityOutput(result, 0).text).toBe("workspace test-key"); }); - expect(resolveApiKeyForProvider).toHaveBeenCalledWith( + expect(resolveApiKeyForProviderCore).toHaveBeenCalledWith( expect.objectContaining({ provider: "openai", agentDir: "/tmp/openclaw-agent", diff --git a/src/media-understanding/runner.cli-audio.test.ts b/src/media-understanding/runner.cli-audio.test.ts index 98c4da4bde40..5c62a74bed07 100644 --- a/src/media-understanding/runner.cli-audio.test.ts +++ b/src/media-understanding/runner.cli-audio.test.ts @@ -4,7 +4,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withEnvAsync } from "../test-utils/env.js"; import { CLI_OUTPUT_MAX_BUFFER } from "./defaults.constants.js"; import { createMediaAttachmentCache, normalizeMediaAttachments } from "./runner.attachments.js"; @@ -233,7 +233,7 @@ describe("media-understanding CLI audio entry", () => { ])( "projects facts-first and deprecated template variables for $name", async ({ count, leadingEmpty }) => { - await withTempDir({ prefix: "openclaw-cli-media-template-" }, async (base) => { + await withTestDir({ prefix: "openclaw-cli-media-template-" }, async (base) => { const media = await Promise.all( Array.from({ length: count }, async (_, index) => { const mediaPath = path.join(base, `audio-${index}.wav`); diff --git a/src/media-understanding/runner.entries.ts b/src/media-understanding/runner.entries.ts index c87f8eec7431..964253a7af20 100644 --- a/src/media-understanding/runner.entries.ts +++ b/src/media-understanding/runner.entries.ts @@ -562,9 +562,10 @@ async function resolveProviderExecutionAuth(params: { providerConfig, }; }; - const { isProviderAuthError, requireApiKey, resolveApiKeyForProvider } = await loadModelAuth(); + const { isProviderAuthError, requireApiKey, resolveApiKeyForProviderCore } = + await loadModelAuth(); try { - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: params.providerId, cfg: params.cfg, profileId: params.entry.profile, diff --git a/src/media-understanding/runner.local-no-auth.test.ts b/src/media-understanding/runner.local-no-auth.test.ts index c696c293f7db..4c5fd2c8135f 100644 --- a/src/media-understanding/runner.local-no-auth.test.ts +++ b/src/media-understanding/runner.local-no-auth.test.ts @@ -30,8 +30,8 @@ vi.mock("../agents/model-auth.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - resolveApiKeyForProvider: async ( - ...args: Parameters + resolveApiKeyForProviderCore: async ( + ...args: Parameters ) => { if (modelAuthTestControl.forceMissingProvider) { throw new actual.ProviderAuthError( @@ -41,7 +41,7 @@ vi.mock("../agents/model-auth.js", async (importOriginal) => { ); } const [params] = args; - return await actual.resolveApiKeyForProvider({ + return await actual.resolveApiKeyForProviderCore({ ...params, store: modelAuthTestControl.store ?? params.store, }); diff --git a/src/media-understanding/runner.test-mocks.ts b/src/media-understanding/runner.test-mocks.ts index 4748711a2947..914957c7d2f2 100644 --- a/src/media-understanding/runner.test-mocks.ts +++ b/src/media-understanding/runner.test-mocks.ts @@ -18,7 +18,7 @@ export function createAvailableModelAuthMockModule() { // Keep the mock shape aligned with available-model-auth runtime imports. return { hasAvailableAuthForProvider: vi.fn(() => true), - resolveApiKeyForProvider: vi.fn(async () => ({ + resolveApiKeyForProviderCore: vi.fn(async () => ({ apiKey: "test-key", source: "test", mode: "api-key", diff --git a/src/media-understanding/runner.video.test.ts b/src/media-understanding/runner.video.test.ts index 665c2a435e26..5f298b25a30a 100644 --- a/src/media-understanding/runner.video.test.ts +++ b/src/media-understanding/runner.video.test.ts @@ -6,7 +6,7 @@ import { formatMediaUnderstandingBody, } from "../../packages/media-understanding-common/src/format.js"; import type { OpenClawConfig } from "../config/types.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withEnvAsync } from "../test-utils/env.js"; import { runCapability } from "./runner.js"; import { withVideoFixture } from "./runner.test-utils.js"; @@ -103,7 +103,7 @@ describe("runCapability video provider wiring", () => { let seenBaseUrl: string | undefined; let seenHeaders: Record | undefined; - await withTempDir({ prefix: "openclaw-video-auth-" }, async (isolatedAgentDir) => { + await withTestDir({ prefix: "openclaw-video-auth-" }, async (isolatedAgentDir) => { await withVideoFixture("openclaw-video-merge", async ({ ctx, media, cache }) => { const cfg = { models: { @@ -174,7 +174,7 @@ describe("runCapability video provider wiring", () => { }); it("auto-selects moonshot for video when google is unavailable", async () => { - await withTempDir({ prefix: "openclaw-video-agent-" }, async (isolatedAgentDir) => { + await withTestDir({ prefix: "openclaw-video-agent-" }, async (isolatedAgentDir) => { await withEnvAsync( { GEMINI_API_KEY: undefined, @@ -244,7 +244,7 @@ describe("runCapability video provider wiring", () => { it("uses the provider video default when the active provider has no model", async () => { let seenModel: string | undefined; - await withTempDir({ prefix: "openclaw-video-active-provider-" }, async (isolatedAgentDir) => { + await withTestDir({ prefix: "openclaw-video-active-provider-" }, async (isolatedAgentDir) => { await withVideoFixture("openclaw-video-active-default", async ({ ctx, media, cache }) => { const cfg = { models: { @@ -301,7 +301,7 @@ describe("runCapability video provider wiring", () => { it("preserves self-defaulting video providers without registry model metadata", async () => { let seenModel: string | undefined; - await withTempDir( + await withTestDir( { prefix: "openclaw-video-no-default-provider-" }, async (isolatedAgentDir) => { await withVideoFixture("openclaw-video-no-default", async ({ ctx, media, cache }) => { @@ -360,7 +360,7 @@ describe("runCapability video provider wiring", () => { it("resolves provider registry defaultModels.video when a config entry has no explicit model", async () => { let seenModel: string | undefined; - await withTempDir({ prefix: "openclaw-video-entry-default-" }, async (isolatedAgentDir) => { + await withTestDir({ prefix: "openclaw-video-entry-default-" }, async (isolatedAgentDir) => { await withVideoFixture("openclaw-video-entry-default", async ({ ctx, media, cache }) => { const cfg = { models: { @@ -412,10 +412,10 @@ describe("runCapability video provider wiring", () => { it("does not use provider api config as video auth modelApi", async () => { const modelAuth = await import("../agents/model-auth.js"); - const resolveApiKeyForProvider = vi.mocked(modelAuth.resolveApiKeyForProvider); - resolveApiKeyForProvider.mockClear(); + const resolveApiKeyForProviderCore = vi.mocked(modelAuth.resolveApiKeyForProviderCore); + resolveApiKeyForProviderCore.mockClear(); - await withTempDir({ prefix: "openclaw-video-provider-api-" }, async (isolatedAgentDir) => { + await withTestDir({ prefix: "openclaw-video-provider-api-" }, async (isolatedAgentDir) => { await withVideoFixture("openclaw-video-provider-api", async ({ ctx, media, cache }) => { let seenApiKey: string | undefined; const cfg = { @@ -464,7 +464,7 @@ describe("runCapability video provider wiring", () => { }); }); - const firstCall = resolveApiKeyForProvider.mock.calls[0]?.[0]; + const firstCall = resolveApiKeyForProviderCore.mock.calls[0]?.[0]; expect(firstCall?.provider).toBe("openai"); expect(firstCall?.modelApi).toBeUndefined(); }); diff --git a/src/node-host/with-timeout.test.ts b/src/node-host/with-timeout.test.ts index 0150e3b5191f..99d93046804e 100644 --- a/src/node-host/with-timeout.test.ts +++ b/src/node-host/with-timeout.test.ts @@ -1,9 +1,9 @@ /** Tests node-host timeout handling, abort reasons, and cleanup behavior. */ import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { withTimeout } from "./with-timeout.js"; +import { runAbortableTimeout } from "./with-timeout.js"; -describe("node-host withTimeout", () => { +describe("runAbortableTimeout", () => { afterEach(() => { vi.restoreAllMocks(); }); @@ -12,7 +12,7 @@ describe("node-host withTimeout", () => { const setTimeoutSpy = vi.spyOn(globalThis, "setTimeout"); await expect( - withTimeout(async (signal) => { + runAbortableTimeout(async (signal) => { expect(signal?.aborted).toBe(false); return "ok"; }, Number.MAX_SAFE_INTEGER), diff --git a/src/node-host/with-timeout.ts b/src/node-host/with-timeout.ts index bdeaa26b5212..055d109a2337 100644 --- a/src/node-host/with-timeout.ts +++ b/src/node-host/with-timeout.ts @@ -9,7 +9,7 @@ import { toErrorObject } from "../infra/errors.js"; * completion, and preserves object-shaped abort reasons as Error properties. */ /** Run work with an optional timeout and AbortSignal. */ -export async function withTimeout( +export async function runAbortableTimeout( work: (signal: AbortSignal | undefined) => Promise, timeoutMs?: number, label?: string, diff --git a/src/pairing/setup-code.ts b/src/pairing/setup-code.ts index 06ec07357d81..b99c638aa057 100644 --- a/src/pairing/setup-code.ts +++ b/src/pairing/setup-code.ts @@ -18,7 +18,7 @@ import { normalizeSecretInputString, resolveSecretInputRef } from "../config/typ import { materializeGatewayAuthSecretRefs } from "../gateway/auth-config-utils.js"; import { assertExplicitGatewayAuthModeWhenBothConfigured } from "../gateway/auth-mode-policy.js"; import { normalizeWebSocketProtocol } from "../gateway/websocket-protocol.js"; -import { resolveAdvertisedLanHost } from "../infra/advertised-lan-host.js"; +import { resolveAdvertisedLanHostCore } from "../infra/advertised-lan-host.js"; import { issueDeviceBootstrapToken } from "../infra/device-bootstrap.js"; import { pickMatchingExternalInterfaceAddress, @@ -380,7 +380,7 @@ async function resolveGatewayUrl( const advertisedLanHost = cfg.gateway?.bind === "lan" - ? await resolveAdvertisedLanHost({ + ? await resolveAdvertisedLanHostCore({ networkInterfaces: opts.networkInterfaces, runCommandWithTimeout: opts.runCommandWithTimeout, }) diff --git a/src/plugin-sdk/agent-runtime.ts b/src/plugin-sdk/agent-runtime.ts index 7f57aff8571e..73bfdbda0516 100644 --- a/src/plugin-sdk/agent-runtime.ts +++ b/src/plugin-sdk/agent-runtime.ts @@ -30,7 +30,7 @@ export { resolveIdentityNamePrefix, } from "../agents/identity.js"; -export { resolveApiKeyForProvider } from "../agents/model-auth.js"; +export { resolveApiKeyForProviderCore as resolveApiKeyForProvider } from "../agents/model-auth.js"; export { findModelInCatalog, modelSupportsVision } from "../agents/model-catalog.js"; export type { ModelCatalogEntry } from "../agents/model-catalog.js"; export { getPreparedModelCatalogSnapshot, loadPreparedModelCatalog }; diff --git a/src/plugin-sdk/channel-outbound.ts b/src/plugin-sdk/channel-outbound.ts index e5322c3d1d51..27b079f2e8f5 100644 --- a/src/plugin-sdk/channel-outbound.ts +++ b/src/plugin-sdk/channel-outbound.ts @@ -186,7 +186,7 @@ export async function sendDurableMessageBatch( params: DurableMessageSendContextParams, ): Promise { const mod = await loadChannelMessageRuntimeModule(); - return await mod.sendDurableMessageBatch(params); + return await mod.sendDurableMessageBatchCore(params); } /** Runs work inside a durable message send context loaded through the SDK lazy boundary. */ @@ -201,5 +201,5 @@ export async function withDurableMessageSendContext( run: (ctx: DurableMessageSendContext) => Promise, ): Promise { const mod = await loadChannelMessageRuntimeModule(); - return await mod.withDurableMessageSendContext(params, run); + return await mod.withDurableMessageSendContextCore(params, run); } diff --git a/src/plugin-sdk/command-status-runtime.ts b/src/plugin-sdk/command-status-runtime.ts index 738266bfd4d0..0a6c5d887d1f 100644 --- a/src/plugin-sdk/command-status-runtime.ts +++ b/src/plugin-sdk/command-status-runtime.ts @@ -13,5 +13,5 @@ const bindCommandStatusRuntime = createLazyRuntimeMethodBinder(loadCommandStatus export type { ResolveDirectStatusReplyForSessionParams } from "./command-status.runtime.js"; /** Resolves the direct status reply text for a session without eagerly loading runtime code. */ -export const resolveDirectStatusReplyForSession: CommandStatusRuntime["resolveDirectStatusReplyForSession"] = - bindCommandStatusRuntime((runtime) => runtime.resolveDirectStatusReplyForSession); +export const resolveDirectStatusReplyForSession: CommandStatusRuntime["resolveDirectStatusReplyForSessionCore"] = + bindCommandStatusRuntime((runtime) => runtime.resolveDirectStatusReplyForSessionCore); diff --git a/src/plugin-sdk/command-status.runtime.test.ts b/src/plugin-sdk/command-status.runtime.test.ts index 4916b140948c..519ecd7544fc 100644 --- a/src/plugin-sdk/command-status.runtime.test.ts +++ b/src/plugin-sdk/command-status.runtime.test.ts @@ -41,7 +41,7 @@ vi.mock("../auto-reply/reply/directive-handling.levels.js", () => ({ resolveCurrentDirectiveLevels, })); -const { resolveDirectStatusReplyForSession } = await import("./command-status.runtime.js"); +const { resolveDirectStatusReplyForSessionCore } = await import("./command-status.runtime.js"); function expectResolvedReasoningLevel(value: unknown, expected: string) { expect((value as { resolvedReasoningLevel?: unknown }).resolvedReasoningLevel).toBe(expected); @@ -55,7 +55,7 @@ function requireBuildStatusReplyParams(index = 0): unknown { return call[0]; } -describe("resolveDirectStatusReplyForSession", () => { +describe("resolveDirectStatusReplyForSessionCore", () => { beforeEach(() => { buildStatusReply.mockReset(); loadSessionEntry.mockReset(); @@ -101,7 +101,7 @@ describe("resolveDirectStatusReplyForSession", () => { }); it("treats agentCfg reasoningDefault as explicit for direct /status", async () => { - const result = await resolveDirectStatusReplyForSession({ + const result = await resolveDirectStatusReplyForSessionCore({ cfg: {}, sessionKey: "main", channel: "cli", @@ -140,7 +140,7 @@ describe("resolveDirectStatusReplyForSession", () => { currentElevatedLevel: "off", }); - const result = await resolveDirectStatusReplyForSession({ + const result = await resolveDirectStatusReplyForSessionCore({ cfg: {}, sessionKey: "main", channel: "cli", @@ -177,7 +177,7 @@ describe("resolveDirectStatusReplyForSession", () => { currentElevatedLevel: "off", }); - const result = await resolveDirectStatusReplyForSession({ + const result = await resolveDirectStatusReplyForSessionCore({ cfg: {}, sessionKey: "main", channel: "cli", @@ -209,7 +209,7 @@ describe("resolveDirectStatusReplyForSession", () => { currentElevatedLevel: "off", }); - const result = await resolveDirectStatusReplyForSession({ + const result = await resolveDirectStatusReplyForSessionCore({ cfg: {}, sessionKey: "main", channel: "cli", @@ -241,7 +241,7 @@ describe("resolveDirectStatusReplyForSession", () => { currentElevatedLevel: "off", }); - const result = await resolveDirectStatusReplyForSession({ + const result = await resolveDirectStatusReplyForSessionCore({ cfg: {}, sessionKey: "main", channel: "cli", diff --git a/src/plugin-sdk/command-status.runtime.ts b/src/plugin-sdk/command-status.runtime.ts index 3a4addad9745..257c8c739c80 100644 --- a/src/plugin-sdk/command-status.runtime.ts +++ b/src/plugin-sdk/command-status.runtime.ts @@ -35,7 +35,7 @@ export type ResolveDirectStatusReplyForSessionParams = { * Unauthorized requesters may see the session exists, but configured reasoning * state is masked so private agent/session defaults are not leaked. */ -export async function resolveDirectStatusReplyForSession( +export async function resolveDirectStatusReplyForSessionCore( params: ResolveDirectStatusReplyForSessionParams, ): Promise { const requestedSessionKey = params.sessionKey.trim(); diff --git a/src/plugin-sdk/config-runtime.ts b/src/plugin-sdk/config-runtime.ts index 0f7deefd4d51..fd134bb3f5dd 100644 --- a/src/plugin-sdk/config-runtime.ts +++ b/src/plugin-sdk/config-runtime.ts @@ -141,7 +141,7 @@ export { updateSessionLastRoute as updateLastRoute, } from "../config/sessions/session-accessor.js"; export { resolveSessionKey } from "../config/sessions/session-key.js"; -export { resolveStorePath } from "../config/sessions/paths.js"; +export { resolveSessionStorePathCore as resolveStorePath } from "../config/sessions/paths.js"; export type { SessionResetMode } from "../config/sessions/reset.js"; export type { SessionScope } from "../config/sessions/types.js"; export { resolveGroupSessionKey } from "../config/sessions/group.js"; diff --git a/src/plugin-sdk/fetch-runtime.ts b/src/plugin-sdk/fetch-runtime.ts index 94add22a1204..4f9e46ae97b0 100644 --- a/src/plugin-sdk/fetch-runtime.ts +++ b/src/plugin-sdk/fetch-runtime.ts @@ -1,7 +1,5 @@ // Public fetch/proxy helpers for plugins that need wrapped fetch behavior. -import type { GuardedFetchOptions } from "../infra/net/fetch-guard.js"; - export { resolveFetch, wrapFetchWithAbortSignal } from "../infra/fetch.js"; export { createHttp1EnvHttpProxyAgent, @@ -26,11 +24,7 @@ export { export { getProxyUrlFromFetch, makeProxyFetch } from "../infra/net/proxy-fetch.js"; export { createPinnedLookup } from "../infra/net/ssrf.js"; export type { PinnedDispatcherPolicy } from "../infra/net/ssrf.js"; - -type GuardedFetchPresetOptions = Omit< - GuardedFetchOptions, - "mode" | "proxy" | "dangerouslyAllowEnvProxyWithoutPinnedDns" ->; +export { withTrustedEnvProxyGuardedFetchMode } from "../infra/net/fetch-guard.js"; const NULL_BODY_STATUSES = new Set([101, 103, 204, 205, 304]); @@ -91,10 +85,3 @@ export function responseWithRelease(response: Response, release: () => Promise { it("keeps deprecated secret-file result helpers on public SDK subpaths", async () => { - await withTempDir({ prefix: "openclaw-sdk-secret-compat-" }, async (root) => { + await withTestDir({ prefix: "openclaw-sdk-secret-compat-" }, async (root) => { const secretPath = path.join(root, "token.txt"); fs.writeFileSync(secretPath, "secret\n", { mode: 0o600 }); @@ -40,7 +40,7 @@ describe("plugin SDK fs-safe compatibility exports", () => { }); it("keeps root-bounded file-access helpers on file-access-runtime", async () => { - await withTempDir({ prefix: "openclaw-sdk-file-access-compat-" }, async (root) => { + await withTestDir({ prefix: "openclaw-sdk-file-access-compat-" }, async (root) => { await writeFileWithinRoot({ rootDir: root, relativePath: "nested/file.txt", diff --git a/src/plugin-sdk/gateway-runtime.ts b/src/plugin-sdk/gateway-runtime.ts index 0a6f55e74d47..65ff2a430056 100644 --- a/src/plugin-sdk/gateway-runtime.ts +++ b/src/plugin-sdk/gateway-runtime.ts @@ -5,7 +5,7 @@ export type { GatewayRpcOpts } from "../cli/gateway-rpc.js"; export { isLoopbackHost } from "../gateway/net.js"; export async function resolveAdvertisedLanHost(): Promise { const runtime = await import("../infra/advertised-lan-host.js"); - return await runtime.resolveAdvertisedLanHost(); + return await runtime.resolveAdvertisedLanHostCore(); } export { resolveHostedPluginSurfaceUrl } from "../gateway/hosted-plugin-surface-url.js"; export type { HostedPluginSurfaceUrlParams } from "../gateway/hosted-plugin-surface-url.js"; @@ -25,8 +25,8 @@ export type { NodeSession } from "../gateway/node-registry.js"; export { resolveNodeFromNodeList } from "../shared/node-resolve.js"; export type { NodeMatchCandidate } from "../shared/node-match.js"; export { + parseGatewayPayload as safeParseJson, respondUnavailableOnNodeInvokeError, - safeParseJson, } from "../gateway/server-methods/nodes.helpers.js"; export type { GatewayRequestHandlers } from "../gateway/server-methods/types.js"; export { ensureGatewayStartupAuth } from "../gateway/startup-auth.js"; diff --git a/src/plugin-sdk/image-generation-core.auth.runtime.ts b/src/plugin-sdk/image-generation-core.auth.runtime.ts index f7cd7e4008fe..5cf63f37ca53 100644 --- a/src/plugin-sdk/image-generation-core.auth.runtime.ts +++ b/src/plugin-sdk/image-generation-core.auth.runtime.ts @@ -1,4 +1,4 @@ /** * Runtime SDK subpath for resolving image-generation provider API keys. */ -export { resolveApiKeyForProvider } from "../agents/model-auth.js"; +export { resolveApiKeyForProviderCore as resolveApiKeyForProvider } from "../agents/model-auth.js"; diff --git a/src/plugin-sdk/memory-host-core.ts b/src/plugin-sdk/memory-host-core.ts index e93377470654..64cfda4064f7 100644 --- a/src/plugin-sdk/memory-host-core.ts +++ b/src/plugin-sdk/memory-host-core.ts @@ -5,7 +5,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import type { OpenClawConfig } from "../config/config.js"; import { resolveStateDir } from "../config/paths.js"; -import { sha256Hex, sha256HexPrefix } from "../infra/crypto-digest.js"; +import { sha256Hex, sha256HexPrefixCore } from "../infra/crypto-digest.js"; import { syncDirectoryIfSupported } from "../infra/directory-durability.js"; import { isMissingPathError } from "../infra/errors.js"; import { withFileLock } from "../infra/file-lock.js"; @@ -69,8 +69,8 @@ async function resolveMemoryHostEventExportOwner(workspaceDir: string): Promise< const requestedStateDir = path.resolve(resolveStateDir()); await fs.mkdir(requestedStateDir, { recursive: true, mode: 0o700 }); const stateDir = await fs.realpath(requestedStateDir); - const stateHash = sha256HexPrefix(stateDir, 32); - const workspaceHash = sha256HexPrefix(path.resolve(workspaceDir), 32); + const stateHash = sha256HexPrefixCore(stateDir, 32); + const workspaceHash = sha256HexPrefixCore(path.resolve(workspaceDir), 32); const exportDirectory = path.posix.join("memory", "events", stateHash); return { queueKey: `${stateHash}\0${workspaceHash}`, diff --git a/src/plugin-sdk/provider-auth-copilot-cache.ts b/src/plugin-sdk/provider-auth-copilot-cache.ts index 8211787284ff..9ef5a76c4948 100644 --- a/src/plugin-sdk/provider-auth-copilot-cache.ts +++ b/src/plugin-sdk/provider-auth-copilot-cache.ts @@ -3,7 +3,7 @@ import path from "node:path"; import { asDateTimestampMs } from "../../packages/normalization-core/src/number-coercion.js"; import { COPILOT_INTEGRATION_ID } from "../agents/copilot-dynamic-headers.js"; import { resolveStateDir } from "../config/paths.js"; -import { loadJsonFileThroughSymlink, saveJsonFile } from "../infra/json-file.js"; +import { loadJsonFileThroughSymlink, writeJsonTarget } from "../infra/json-file.js"; import { DEFAULT_GITHUB_COPILOT_DOMAIN } from "./github-copilot-domain.js"; const COPILOT_CACHE_NAMESPACE = "github-copilot-token"; @@ -84,7 +84,7 @@ export async function resolveCopilotTokenCache(params: { // Normal runtime state uses the bounded SQLite namespace below. const cachePath = params.cachePath?.trim() || resolveLegacyCopilotTokenCachePath(params.env); const loadJsonFileFn = params.loadJsonFileImpl ?? loadJsonFileThroughSymlink; - const saveJsonFileFn = params.saveJsonFileImpl ?? saveJsonFile; + const saveJsonFileFn = params.saveJsonFileImpl ?? writeJsonTarget; return { path: cachePath, load: () => loadJsonFileFn(cachePath) as CachedCopilotToken | undefined, diff --git a/src/plugin-sdk/provider-auth-login-flow-runtime.ts b/src/plugin-sdk/provider-auth-login-flow-runtime.ts index fb3c87f2aca4..f66f94c6af84 100644 --- a/src/plugin-sdk/provider-auth-login-flow-runtime.ts +++ b/src/plugin-sdk/provider-auth-login-flow-runtime.ts @@ -45,8 +45,8 @@ const bindProviderAuthLoginFlowRuntime = createLazyRuntimeMethodBinder( loadProviderAuthLoginFlowRuntime, ); -export const runModelsAuthLoginFlow: ProviderAuthLoginFlowRuntime["runModelsAuthLoginFlow"] = - bindProviderAuthLoginFlowRuntime((runtime) => runtime.runModelsAuthLoginFlow); +export const runModelsAuthLoginFlow: ProviderAuthLoginFlowRuntime["runModelsAuthLoginFlowCore"] = + bindProviderAuthLoginFlowRuntime((runtime) => runtime.runModelsAuthLoginFlowCore); function resolveCodexLoginProvider(rawProvider: string | undefined): string | null { const normalized = normalizeLowercaseStringOrEmpty(rawProvider ?? "codex").replace(/_/gu, "-"); diff --git a/src/plugin-sdk/provider-auth-runtime.ts b/src/plugin-sdk/provider-auth-runtime.ts index a21b9fd3e189..3e9560b09b88 100644 --- a/src/plugin-sdk/provider-auth-runtime.ts +++ b/src/plugin-sdk/provider-auth-runtime.ts @@ -5,7 +5,7 @@ import { createServer } from "node:http"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; import { ensureAuthProfileStore } from "../agents/auth-profiles/store.js"; -import { resolveApiKeyForProvider as resolveModelApiKeyForProvider } from "../agents/model-auth.js"; +import { resolveApiKeyForProviderCore as resolveModelApiKeyForProvider } from "../agents/model-auth.js"; import { normalizeProviderId } from "../agents/model-selection.js"; import type { OpenClawConfig } from "../config/config.js"; import { startOAuthLoopbackCallbackServer } from "../infra/oauth-loopback-callback.js"; @@ -230,7 +230,8 @@ function isHttpOrigin(value: string): boolean { } } -type ResolveApiKeyForProvider = typeof import("../agents/model-auth.js").resolveApiKeyForProvider; +type ResolveApiKeyForProvider = + typeof import("../agents/model-auth.js").resolveApiKeyForProviderCore; type GetRuntimeAuthForModel = typeof import("../plugins/runtime/runtime-model-auth.runtime.js").getRuntimeAuthForModelCore; type RuntimeModelAuthModule = typeof import("../plugins/runtime/runtime-model-auth.runtime.js"); @@ -266,8 +267,8 @@ export async function resolveApiKeyForProvider( ): Promise>> { const runtimeAuth = await loadRuntimeModelAuthModule(); const resolveApiKeyForProviderLocal = - typeof runtimeAuth.resolveApiKeyForProvider === "function" - ? runtimeAuth.resolveApiKeyForProvider + typeof runtimeAuth.resolveProviderRuntimeApiKey === "function" + ? runtimeAuth.resolveProviderRuntimeApiKey : resolveModelApiKeyForProvider; return resolveApiKeyForProviderLocal(params); } diff --git a/src/plugin-sdk/secret-ref-runtime.ts b/src/plugin-sdk/secret-ref-runtime.ts index 63b1f2e838f5..49d3bb1796d1 100644 --- a/src/plugin-sdk/secret-ref-runtime.ts +++ b/src/plugin-sdk/secret-ref-runtime.ts @@ -15,7 +15,7 @@ import { parsePluginSecretTargetSpecifier, } from "../secrets/plugin-setup-plan.js"; import { createPrivateWindowsPlanFile } from "../secrets/private-plan-file.js"; -import { resolveSecretPlanTargetByPath as resolveSecretPlanTargetByPathInternal } from "../secrets/target-registry-query.js"; +import { resolveSecretPlanTargetByPathCore } from "../secrets/target-registry-query.js"; import { resolveTrustedExecutablePath, resolveTrustedPlanDirectoryPath, @@ -454,7 +454,7 @@ export function resolveSecretPlanTargetByPath(params: { configFile: "openclaw.json" | "auth-profiles.json"; pathSegments: string[]; }): ResolvedSecretPlanTarget | null { - const resolved = resolveSecretPlanTargetByPathInternal(params); + const resolved = resolveSecretPlanTargetByPathCore(params); if (!resolved) { return null; } diff --git a/src/plugin-sdk/session-store-paths.ts b/src/plugin-sdk/session-store-paths.ts index 53daebb3bf46..c454f88ad3c5 100644 --- a/src/plugin-sdk/session-store-paths.ts +++ b/src/plugin-sdk/session-store-paths.ts @@ -4,4 +4,4 @@ // session accessor/state-database graph. Doctor legacy-state closures only need // to locate a store file, and doctor enumeration cold-loads those closures. -export { resolveStorePath } from "../config/sessions/paths.js"; +export { resolveSessionStorePathCore as resolveStorePath } from "../config/sessions/paths.js"; diff --git a/src/plugin-sdk/session-store-runtime.ts b/src/plugin-sdk/session-store-runtime.ts index 2c684151171d..fdf39cd0d98f 100644 --- a/src/plugin-sdk/session-store-runtime.ts +++ b/src/plugin-sdk/session-store-runtime.ts @@ -12,8 +12,10 @@ import { formatSqliteSessionFileMarker, parseSqliteSessionFileMarker, } from "../config/sessions/legacy-sqlite-marker.js"; -import { resolveStorePath as resolveSessionStorePath } from "../config/sessions/paths.js"; -import { resolveSessionFilePath as resolveLegacySessionFilePath } from "../config/sessions/paths.js"; +import { + resolveSessionFilePathCore, + resolveSessionStorePathCore, +} from "../config/sessions/paths.js"; export { SessionStoreAgentIdRequiredError } from "../config/sessions/paths.js"; import { applySessionStoreProjection as applyAccessorSessionStoreProjection, @@ -188,7 +190,7 @@ function materializeLegacyTranscriptFile( sessionId: marker.sessionId, storePath: marker.storePath, } as const; - const transcriptPath = resolveLegacySessionFilePath(marker.sessionId, undefined, { + const transcriptPath = resolveSessionFilePathCore(marker.sessionId, undefined, { agentId: marker.agentId, ...(options?.sessionsDir ? { sessionsDir: options.sessionsDir } : {}), }); @@ -330,7 +332,7 @@ export function resolveSessionFilePath( entry?: { sessionFile?: string }, options?: { agentId?: string; sessionsDir?: string }, ): string { - const resolved = resolveLegacySessionFilePath(sessionId, entry, options); + const resolved = resolveSessionFilePathCore(sessionId, entry, options); return materializeLegacyTranscriptFile(resolved, options); } @@ -346,7 +348,7 @@ export function resolveStorePath( store?: string, options?: { agentId?: string; env?: NodeJS.ProcessEnv }, ): string { - const storePath = resolveSessionStorePath(store, options); + const storePath = resolveSessionStorePathCore(store, options); if (options?.agentId) { legacyStoreAgentIds.set(path.resolve(storePath), options.agentId); } @@ -517,7 +519,7 @@ export async function deleteSessionEntry(params: DeleteSessionEntryParams): Prom const agentId = params.agentId ?? resolveAgentIdFromSessionKey(params.sessionKey); const storePath = params.storePath ?? - resolveSessionStorePath(undefined, { + resolveSessionStorePathCore(undefined, { agentId, env: params.env, }); @@ -565,7 +567,7 @@ export async function cleanupSessionLifecycleArtifacts( ): Promise { const storePath = params.storePath ?? - resolveSessionStorePath(params.sessionStore, { + resolveSessionStorePathCore(params.sessionStore, { agentId: params.agentId, env: params.env, }); diff --git a/src/plugin-sdk/session-transcript-mirror-runtime.test.ts b/src/plugin-sdk/session-transcript-mirror-runtime.test.ts index a171959b89d2..27950fa27adc 100644 --- a/src/plugin-sdk/session-transcript-mirror-runtime.test.ts +++ b/src/plugin-sdk/session-transcript-mirror-runtime.test.ts @@ -1,7 +1,7 @@ import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import { runExclusiveSqliteSessionWrite, resolveSqliteTranscriptScope, @@ -36,7 +36,7 @@ describe("private session transcript mirror runtime", () => { sessionKey: "agent:main:indexed-mirror", storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 1 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 1 }); await withCodexSessionTranscriptMirrorWriteLock(scope, async (locked) => { expect(await locked.readMessageFacts({ idempotencyKeys: ["mirror-user"] })).toEqual({ @@ -176,11 +176,11 @@ describe("private session transcript mirror runtime", () => { sessionId: "requested-session", sessionKey: "agent:main:requested-session", }; - await upsertSessionEntry(admittedScope, { + await upsertSessionEntryCore(admittedScope, { sessionId: admittedScope.sessionId, updatedAt: 1, }); - await upsertSessionEntry(requestedScope, { + await upsertSessionEntryCore(requestedScope, { sessionId: requestedScope.sessionId, updatedAt: 1, }); diff --git a/src/plugin-sdk/session-transcript-runtime-visible-delta.test.ts b/src/plugin-sdk/session-transcript-runtime-visible-delta.test.ts index a21a232b22ef..8c7a23f16d88 100644 --- a/src/plugin-sdk/session-transcript-runtime-visible-delta.test.ts +++ b/src/plugin-sdk/session-transcript-runtime-visible-delta.test.ts @@ -2,7 +2,10 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { appendTranscriptEvent, upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { + appendTranscriptEvent, + upsertSessionEntryCore, +} from "../config/sessions/session-accessor.js"; import { resolveSqliteTranscriptReadScope, toDatabaseOptions, @@ -34,7 +37,7 @@ describe("session transcript visible cursor SDK", () => { sessionKey: "agent:main:visible-delta", storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); const root = await appendSessionTranscriptMessageByIdentity({ ...scope, message: { role: "user", content: "root" }, @@ -195,7 +198,7 @@ describe("session transcript visible cursor SDK", () => { storePath, }; const content = "x".repeat(200); - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); await appendSessionTranscriptMessageByIdentity({ ...scope, message: { role: "user", content }, @@ -265,7 +268,7 @@ describe("session transcript visible cursor SDK", () => { sessionKey: "agent:main:visible-delta-fence", storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); const prior = await appendSessionTranscriptMessageByIdentity({ ...scope, message: { role: "user", content: "same prompt" }, diff --git a/src/plugin-sdk/session-transcript-runtime.read-fence.test.ts b/src/plugin-sdk/session-transcript-runtime.read-fence.test.ts index 47e802b78823..24ba9e7d521a 100644 --- a/src/plugin-sdk/session-transcript-runtime.read-fence.test.ts +++ b/src/plugin-sdk/session-transcript-runtime.read-fence.test.ts @@ -1,7 +1,7 @@ import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import { runWithSessionTranscriptReadFence, SessionTranscriptReadFenceError, @@ -30,7 +30,7 @@ describe("session transcript runtime read fence", () => { sessionKey: "agent:main:fenced-raw", storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); const priorUser = await appendSessionTranscriptMessageByIdentity({ ...scope, message: { role: "user", content: "same prompt" }, @@ -123,7 +123,7 @@ describe("session transcript runtime read fence", () => { sessionKey: "agent:main:fenced-anchor", storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); const admitted = await appendSessionTranscriptMessageByIdentity({ ...scope, message: { role: "user", content: "exact admission" }, diff --git a/src/plugin-sdk/session-transcript-runtime.test.ts b/src/plugin-sdk/session-transcript-runtime.test.ts index e6fdb41a6efb..c6c5b3b92d85 100644 --- a/src/plugin-sdk/session-transcript-runtime.test.ts +++ b/src/plugin-sdk/session-transcript-runtime.test.ts @@ -7,7 +7,7 @@ import { listSessionEntriesCore, loadSessionEntry, replaceTranscriptEvents, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import * as transcriptEvents from "../sessions/transcript-events.js"; import { @@ -51,7 +51,7 @@ describe("session transcript runtime SDK", () => { }; const event = { id: "event-1", type: "metadata" }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); await appendTranscriptEvent(scope, event); const identity = await resolveSessionTranscriptIdentity(scope); @@ -74,7 +74,7 @@ describe("session transcript runtime SDK", () => { storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); await expect(resolveSessionTranscriptIdentity(scope)).resolves.toMatchObject({ memoryKey: "transcript:main:read-only-session", @@ -90,7 +90,7 @@ describe("session transcript runtime SDK", () => { sessionKey: "agent:main:batch", storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); const messages = [ { eventId: "batch-assistant", @@ -126,7 +126,7 @@ describe("session transcript runtime SDK", () => { sessionKey: "agent:main:strict", storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); await expect( appendSessionTranscriptMessageByIdentityStrict({ @@ -142,7 +142,7 @@ describe("session transcript runtime SDK", () => { }), ).resolves.toMatchObject({ kind: "result", result: { appended: true } }); - await upsertSessionEntry(scope, { sessionId: "replacement-session", updatedAt: 20 }); + await upsertSessionEntryCore(scope, { sessionId: "replacement-session", updatedAt: 20 }); await expect( appendSessionTranscriptMessageByIdentityStrict({ ...scope, @@ -158,7 +158,7 @@ describe("session transcript runtime SDK", () => { sessionKey: "agent:main:raw-delta", storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); for (const id of ["event-1", "event-2", "event-3"]) { await appendTranscriptEvent(scope, { id, type: "custom" }); } @@ -214,7 +214,10 @@ describe("session transcript runtime SDK", () => { sessionKey: "agent:main:missing-raw-delta", storePath, }; - await upsertSessionEntry(missingScope, { sessionId: missingScope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(missingScope, { + sessionId: missingScope.sessionId, + updatedAt: 10, + }); await expect( readSessionTranscriptRawDelta({ ...missingScope, maxBytes: 10, maxEvents: 1 }), ).resolves.toEqual({ kind: "missing" }); @@ -331,7 +334,7 @@ describe("session transcript runtime SDK", () => { storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); const root = await appendSessionTranscriptMessageByIdentity({ ...scope, message: { @@ -415,7 +418,7 @@ describe("session transcript runtime SDK", () => { storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); await expect( appendAssistantMirrorMessageByIdentity({ @@ -445,7 +448,7 @@ describe("session transcript runtime SDK", () => { sessionId: "unkeyed-mirror-session", sessionKey: "agent:main:unkeyed", }; - await upsertSessionEntry(unkeyedScope, { + await upsertSessionEntryCore(unkeyedScope, { sessionId: unkeyedScope.sessionId, updatedAt: 20, }); @@ -467,7 +470,7 @@ describe("session transcript runtime SDK", () => { ); expect(unkeyedAssistantMessages).toHaveLength(1); - await upsertSessionEntry(scope, { sessionId: "new-session", updatedAt: 20 }); + await upsertSessionEntryCore(scope, { sessionId: "new-session", updatedAt: 20 }); await expect( appendAssistantMirrorMessageByIdentity({ @@ -485,7 +488,7 @@ describe("session transcript runtime SDK", () => { storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); const active = await appendSessionTranscriptMessageByIdentity({ ...scope, message: { @@ -532,7 +535,7 @@ describe("session transcript runtime SDK", () => { storePath, }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); const firstAssistant = await appendSessionTranscriptMessageByIdentity({ ...scope, message: { @@ -572,7 +575,7 @@ describe("session transcript runtime SDK", () => { internalUpdates.push(update); }); - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); try { await expect( @@ -618,7 +621,7 @@ describe("session transcript runtime SDK", () => { }; const event = { id: "event-locator", type: "metadata" }; - await upsertSessionEntry(scope, { sessionId: scope.sessionId, updatedAt: 10 }); + await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 10 }); await appendTranscriptEvent(scope, event); await expect(readSessionTranscriptEvents(scope)).resolves.toEqual([event]); @@ -635,7 +638,7 @@ describe("session transcript runtime SDK", () => { }; const event = { id: "event-active", type: "metadata" }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionFile: path.join(tempDir, "store-default.jsonl"), sessionId: scope.sessionId, updatedAt: 10, @@ -928,7 +931,7 @@ describe("session transcript runtime SDK", () => { sessionKey: "agent:main:telegram:direct:123", storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionFile: path.join(tempDir, "legacy-file-name.jsonl"), sessionId: scope.sessionId, updatedAt: 10, diff --git a/src/plugin-sdk/test-env.ts b/src/plugin-sdk/test-env.ts index 2fae7c8fcbba..113826d6fb18 100644 --- a/src/plugin-sdk/test-env.ts +++ b/src/plugin-sdk/test-env.ts @@ -12,4 +12,4 @@ export { withTempDir } from "../test-utils/temp-dir.js"; export { useFrozenTime, useRealTime } from "../test-utils/frozen-time.js"; export { withServer } from "./test-helpers/http-test-server.js"; export { createMockIncomingRequest } from "./test-helpers/mock-incoming-request.js"; -export { withTempHome } from "./test-helpers/temp-home.js"; +export { withTempHomeCore as withTempHome } from "./test-helpers/temp-home.js"; diff --git a/src/plugin-sdk/test-helpers/plugin-runtime-mock.ts b/src/plugin-sdk/test-helpers/plugin-runtime-mock.ts index 057a16d5d401..a2c6af84eb15 100644 --- a/src/plugin-sdk/test-helpers/plugin-runtime-mock.ts +++ b/src/plugin-sdk/test-helpers/plugin-runtime-mock.ts @@ -11,7 +11,7 @@ import { import { createChannelReplyPipeline } from "../../channels/message/reply-pipeline.js"; import { resolveSessionEntryResetFreshness } from "../../config/sessions/entry-freshness.js"; import { createChannelRuntimeContextRegistry } from "../../plugins/runtime/channel-runtime-contexts.js"; -import { resolveSessionCatalogCreateTarget } from "../../plugins/runtime/runtime-agent-session-catalog.js"; +import { resolveAgentCatalogCreateTarget } from "../../plugins/runtime/runtime-agent-session-catalog.js"; import type { PluginRuntime } from "../../plugins/runtime/types.js"; import { implicitMentionKindWhen, @@ -529,7 +529,7 @@ export function createPluginRuntimeMock(overrides: DeepPartial = name: "test-agent", })) as unknown as PluginRuntime["agent"]["resolveAgentIdentity"], resolveSessionCatalogCreateTarget: vi.fn( - resolveSessionCatalogCreateTarget, + resolveAgentCatalogCreateTarget, ) as unknown as PluginRuntime["agent"]["resolveSessionCatalogCreateTarget"], resolveThinkingDefault: vi.fn( () => "off", diff --git a/src/plugin-sdk/test-helpers/temp-home.ts b/src/plugin-sdk/test-helpers/temp-home.ts index e7918eab1f74..741622bf60fe 100644 --- a/src/plugin-sdk/test-helpers/temp-home.ts +++ b/src/plugin-sdk/test-helpers/temp-home.ts @@ -102,7 +102,7 @@ async function allocateTempHomeBase(prefix: string): Promise { return base; } -export async function withTempHome( +export async function withTempHomeCore( fn: (home: string) => Promise, opts: { env?: Record; diff --git a/src/plugin-sdk/text-runtime.ts b/src/plugin-sdk/text-runtime.ts index c615eec67e40..2419c07262a3 100644 --- a/src/plugin-sdk/text-runtime.ts +++ b/src/plugin-sdk/text-runtime.ts @@ -69,7 +69,7 @@ export { resolveConfigDir, resolveHomeDir, resolveUserPath, - safeParseJson, + tryParseJson as safeParseJson, shortenHomeInString, shortenHomePath, sleep, diff --git a/src/plugin-sdk/text-utility-runtime.ts b/src/plugin-sdk/text-utility-runtime.ts index f519c5edf615..e6f19fd8d313 100644 --- a/src/plugin-sdk/text-utility-runtime.ts +++ b/src/plugin-sdk/text-utility-runtime.ts @@ -54,7 +54,7 @@ export { resolveConfigDir, resolveHomeDir, resolveUserPath, - safeParseJson, + tryParseJson as safeParseJson, shortenHomeInString, shortenHomePath, sleep, diff --git a/src/plugins/config-policy.ts b/src/plugins/config-policy.ts index ceab6fc05cd9..ae0c01f7acd7 100644 --- a/src/plugins/config-policy.ts +++ b/src/plugins/config-policy.ts @@ -1,7 +1,6 @@ // Evaluates plugin config policy without activating plugin runtime code. import type { OpenClawConfig } from "../config/types.openclaw.js"; import { - resolveMemorySlotDecisionShared, resolvePluginActivationDecisionShared, toPluginActivationState, type PluginActivationStateLike, @@ -13,7 +12,6 @@ import { type NormalizePluginId, type NormalizedPluginsConfig as SharedNormalizedPluginsConfig, } from "./config-normalization-shared.js"; -import type { PluginKind } from "./plugin-kind.types.js"; import type { PluginOrigin } from "./plugin-origin.types.js"; type PluginActivationState = PluginActivationStateLike; @@ -62,17 +60,8 @@ type PolicyEffectiveActivationParams = { autoEnabledReason?: string; }; -export function resolveEffectivePluginActivationState( +export function resolvePolicyPluginActivationState( params: PolicyEffectiveActivationParams, ): PluginActivationState { return resolvePluginActivationState(params); } - -export function resolveMemorySlotDecision(params: { - id: string; - kind?: PluginKind | PluginKind[]; - slot: string | null | undefined; - selectedId: string | null; -}): { enabled: boolean; reason?: string; selected?: boolean } { - return resolveMemorySlotDecisionShared(params); -} diff --git a/src/plugins/git-install.ts b/src/plugins/git-install.ts index de7ad6d3509e..aa18a185229b 100644 --- a/src/plugins/git-install.ts +++ b/src/plugins/git-install.ts @@ -6,9 +6,9 @@ import { redactSensitiveUrlLikeString } from "@openclaw/net-policy/redact-sensit import { hasHttpUrlPrefix } from "@openclaw/net-policy/url-protocol"; import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; import { sanitizeForLog } from "../../packages/terminal-core/src/ansi.js"; -import { sha256HexPrefix } from "../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../infra/crypto-digest.js"; import { pathExists } from "../infra/fs-safe.js"; -import { withTempDir } from "../infra/install-source-utils.js"; +import { withInstallWorkspace } from "../infra/install-source-utils.js"; import { replaceDirectoryAtomic } from "../infra/replace-file.js"; import { createSafeNpmInstallArgs, @@ -242,7 +242,7 @@ function resolveGitInstallRepoDir(params: { }): string { const gitRoot = params.gitDir ? resolveUserPath(params.gitDir) : resolveDefaultPluginGitDir(); const redactedSpec = redactSensitiveUrlLikeString(params.source.normalizedSpec); - return path.join(gitRoot, `git-${sha256HexPrefix(redactedSpec, 16)}`, "repo"); + return path.join(gitRoot, `git-${sha256HexPrefixCore(redactedSpec, 16)}`, "repo"); } async function withGitStagingDir( @@ -250,18 +250,18 @@ async function withGitStagingDir( fn: (tmpDir: string) => Promise, ): Promise { if (!persistentRepoDir) { - return await withTempDir("openclaw-git-plugin-", fn); + return await withInstallWorkspace("openclaw-git-plugin-", fn); } const targetParent = path.dirname(persistentRepoDir); try { await fs.mkdir(targetParent, { recursive: true }); } catch { - return await withTempDir("openclaw-git-plugin-", fn); + return await withInstallWorkspace("openclaw-git-plugin-", fn); } let callbackStarted = false; try { - return await withTempDir( + return await withInstallWorkspace( "openclaw-git-plugin-", async (tmpDir) => { callbackStarted = true; @@ -275,7 +275,7 @@ async function withGitStagingDir( if (callbackStarted) { throw err; } - return await withTempDir("openclaw-git-plugin-", fn); + return await withInstallWorkspace("openclaw-git-plugin-", fn); } } diff --git a/src/plugins/install-npm-pack.ts b/src/plugins/install-npm-pack.ts index 8494e92285e8..8d327cf1ef2d 100644 --- a/src/plugins/install-npm-pack.ts +++ b/src/plugins/install-npm-pack.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { sha256HexPrefix } from "../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../infra/crypto-digest.js"; import { resolveNpmPackArchiveMetadata, type NpmSpecResolution, @@ -80,7 +80,7 @@ async function stageNpmPackArchiveInManagedRoot(params: { > { const archiveStoreDir = path.join(params.npmRoot, MANAGED_NPM_PACK_ARCHIVE_DIR); const identity = params.integrity ?? params.shasum ?? params.tarballName; - const identitySlug = sha256HexPrefix(identity, 16); + const identitySlug = sha256HexPrefixCore(identity, 16); const packageSlug = safePluginInstallFileName(params.packageName) || "plugin"; const versionSlug = safePluginInstallFileName(params.version ?? "pack") || "pack"; const archiveFileName = `${packageSlug}-${versionSlug}-${identitySlug}.tgz`; diff --git a/src/plugins/install-persistence.test.ts b/src/plugins/install-persistence.test.ts index 4a91f9adfd90..446c4e54ac3d 100644 --- a/src/plugins/install-persistence.test.ts +++ b/src/plugins/install-persistence.test.ts @@ -16,8 +16,8 @@ import { resetPluginsCliTestState, pluginsCliRuntimeLogs, setInstalledPluginIndexInstallRecords, - writeConfigFile, - writePersistedInstalledPluginIndexInstallRecordsWithLease, + configWriteMock, + writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock, applyPluginUninstallDirectoryRemovalMock, } from "../cli/plugins-cli-test-helpers.js"; import type { OpenClawConfig } from "../config/config.js"; @@ -130,8 +130,8 @@ describe("persistPluginInstall", () => { expect(next).toEqual(enabledConfig); const persistedRecords = requireMockCallArg( - writePersistedInstalledPluginIndexInstallRecordsWithLease, - "writePersistedInstalledPluginIndexInstallRecordsWithLease", + writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock, + "writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock", ); expect(persistedRecords.alpha).toEqual({ source: "npm", @@ -139,7 +139,7 @@ describe("persistPluginInstall", () => { installPath: "/tmp/alpha", installedAt: "2026-04-25T00:00:00.000Z", }); - expect(writeConfigFile).toHaveBeenCalledWith(enabledConfig); + expect(configWriteMock).toHaveBeenCalledWith(enabledConfig); expect(replaceConfigFileMock).toHaveBeenCalledWith({ nextConfig: enabledConfig, baseHash: "config-1", @@ -909,8 +909,8 @@ describe("persistPluginInstall", () => { 'Installed plugin "needs-config" without enabling it because it requires configuration first.', ); const persistedRecords = requireMockCallArg( - writePersistedInstalledPluginIndexInstallRecordsWithLease, - "writePersistedInstalledPluginIndexInstallRecordsWithLease", + writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock, + "writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock", ); expect(persistedRecords["needs-config"]).toMatchObject({ source: "npm", @@ -965,8 +965,8 @@ describe("persistPluginInstall", () => { ).rejects.toThrow("has invalid configured settings"); expect(enablePluginInConfigMock).not.toHaveBeenCalled(); - expect(writePersistedInstalledPluginIndexInstallRecordsWithLease).not.toHaveBeenCalled(); - expect(writeConfigFile).not.toHaveBeenCalled(); + expect(writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock).not.toHaveBeenCalled(); + expect(configWriteMock).not.toHaveBeenCalled(); }); it("can persist an install record without enabling a plugin that needs config first", async () => { @@ -997,8 +997,8 @@ describe("persistPluginInstall", () => { expect(enablePluginInConfigMock).not.toHaveBeenCalled(); expect(applyExclusiveSlotSelectionMock).not.toHaveBeenCalled(); const persistedRecords = requireMockCallArg( - writePersistedInstalledPluginIndexInstallRecordsWithLease, - "writePersistedInstalledPluginIndexInstallRecordsWithLease", + writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock, + "writePersistedInstalledPluginIndexInstallRecordsWithLeaseMock", ); expect(persistedRecords["memory-lancedb"]).toEqual({ source: "path", @@ -1007,7 +1007,7 @@ describe("persistPluginInstall", () => { installPath: "/app/dist/extensions/memory-lancedb", installedAt: "2026-04-25T00:00:00.000Z", }); - expect(writeConfigFile).toHaveBeenCalledWith(baseConfig); + expect(configWriteMock).toHaveBeenCalledWith(baseConfig); }); it("does not add disabled installs to restrictive allowlists", async () => { diff --git a/src/plugins/provider-runtime.test.ts b/src/plugins/provider-runtime.test.ts index 4a7c551e9711..e8226a45ca6c 100644 --- a/src/plugins/provider-runtime.test.ts +++ b/src/plugins/provider-runtime.test.ts @@ -76,7 +76,7 @@ let resolveProviderStreamFn: typeof import("./provider-runtime.js").resolveProvi let resolveProviderTransportTurnStateWithPlugin: typeof import("./provider-runtime.js").resolveProviderTransportTurnStateWithPlugin; let resolveProviderCacheTtlEligibility: typeof import("./provider-runtime.js").resolveProviderCacheTtlEligibility; let createProviderEmbeddingProvider: typeof import("./provider-runtime.js").createProviderEmbeddingProvider; -let resolveProviderThinkingProfile: typeof import("./provider-runtime.js").resolveProviderThinkingProfile; +let resolveRuntimeThinkingProfile: typeof import("./provider-runtime.js").resolveRuntimeThinkingProfile; let resolveProviderModernModelRef: typeof import("./provider-runtime.js").resolveProviderModernModelRef; let resolveProviderReasoningOutputModeWithPlugin: typeof import("./provider-runtime.js").resolveProviderReasoningOutputModeWithPlugin; let resolveProviderReplayPolicyWithPlugin: typeof import("./provider-runtime.js").resolveProviderReplayPolicyWithPlugin; @@ -327,7 +327,7 @@ describe("provider-runtime", () => { resolveProviderTransportTurnStateWithPlugin, resolveProviderCacheTtlEligibility, createProviderEmbeddingProvider, - resolveProviderThinkingProfile, + resolveRuntimeThinkingProfile, resolveProviderModernModelRef, resolveProviderReasoningOutputModeWithPlugin, resolveProviderReplayPolicyWithPlugin, @@ -1709,7 +1709,7 @@ describe("provider-runtime", () => { }); expect( - resolveProviderThinkingProfile({ + resolveRuntimeThinkingProfile({ provider: "xai", context: { provider: "xai", diff --git a/src/plugins/provider-runtime.ts b/src/plugins/provider-runtime.ts index aa01ff8c0df9..9074fa73e233 100644 --- a/src/plugins/provider-runtime.ts +++ b/src/plugins/provider-runtime.ts @@ -920,7 +920,7 @@ export function resolveProviderCacheTtlEligibility(params: { return resolveProviderRuntimePlugin(params)?.isCacheTtlEligible?.(params.context); } -export function resolveProviderThinkingProfile(params: { +export function resolveRuntimeThinkingProfile(params: { provider: string; config?: OpenClawConfig; workspaceDir?: string; diff --git a/src/plugins/provider-thinking.ts b/src/plugins/provider-thinking.ts index 0baf9d3fa0e6..027a21222563 100644 --- a/src/plugins/provider-thinking.ts +++ b/src/plugins/provider-thinking.ts @@ -20,7 +20,7 @@ type ThinkingHookParams = { }; /** Resolves a provider thinking profile from active plugins or bundled policy surface. */ -export function resolveProviderThinkingProfile( +export function resolveEffectiveThinkingProfile( params: ThinkingHookParams, options?: { allowPublicArtifactFallback?: boolean }, ) { diff --git a/src/plugins/runtime/index.test.ts b/src/plugins/runtime/index.test.ts index 3b9eaf771ee6..a691cc1c0864 100644 --- a/src/plugins/runtime/index.test.ts +++ b/src/plugins/runtime/index.test.ts @@ -21,7 +21,11 @@ const sandboxContextMocks = vi.hoisted(() => ({ resolveSandboxContext: vi.fn(), })); -vi.mock("./runtime-model-auth.runtime.js", () => runtimeModelAuthMocks); +vi.mock("./runtime-model-auth.runtime.js", () => ({ + getApiKeyForModel: runtimeModelAuthMocks.getApiKeyForModel, + getRuntimeAuthForModelCore: runtimeModelAuthMocks.getRuntimeAuthForModelCore, + resolveProviderRuntimeApiKey: runtimeModelAuthMocks.resolveApiKeyForProvider, +})); vi.mock("../../agents/sandbox/context.js", () => sandboxContextMocks); import { createPluginRuntime } from "./index.js"; diff --git a/src/plugins/runtime/index.ts b/src/plugins/runtime/index.ts index 17156c5bde01..c000191a27a2 100644 --- a/src/plugins/runtime/index.ts +++ b/src/plugins/runtime/index.ts @@ -146,7 +146,7 @@ function createRuntimeModelAuth(): PluginRuntime["modelAuth"] { ); const resolveApiKeyForProvider = createLazyRuntimeMethod( loadModelAuthRuntime, - (runtime) => runtime.resolveApiKeyForProvider, + (runtime) => runtime.resolveProviderRuntimeApiKey, ); return { getApiKeyForModel: (params) => diff --git a/src/plugins/runtime/runtime-agent-session-catalog.ts b/src/plugins/runtime/runtime-agent-session-catalog.ts index ee8c24d95555..1b1709c55888 100644 --- a/src/plugins/runtime/runtime-agent-session-catalog.ts +++ b/src/plugins/runtime/runtime-agent-session-catalog.ts @@ -19,7 +19,7 @@ type RuntimeSessionCatalogCreateTargetParams = { * Resolve a synchronous catalog create target through the same model/runtime * policy used by agent turns, without making plugins import that policy graph. */ -export function resolveSessionCatalogCreateTarget( +export function resolveAgentCatalogCreateTarget( params: RuntimeSessionCatalogCreateTargetParams, ): SessionCatalogCreateTarget | undefined { const agentId = params.requestedAgentId ?? resolveDefaultAgentId(params.config); diff --git a/src/plugins/runtime/runtime-agent.ts b/src/plugins/runtime/runtime-agent.ts index 0c7459f6eef7..c642fb6b2a51 100644 --- a/src/plugins/runtime/runtime-agent.ts +++ b/src/plugins/runtime/runtime-agent.ts @@ -17,7 +17,7 @@ import { ensureAgentWorkspace } from "../../agents/workspace.js"; import { normalizeThinkLevel, resolveThinkingProfile } from "../../auto-reply/thinking.js"; import { getRuntimeConfig } from "../../config/config.js"; import { resolveSessionWorkStartError } from "../../config/sessions/lifecycle.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { deleteSessionEntryLifecycle, listSessionEntriesCore as listAccessorSessionEntries, @@ -39,7 +39,7 @@ import { runExclusiveSessionLifecycleMutation, } from "../../sessions/session-lifecycle-admission.js"; import { createLazyRuntimeMethod, createLazyRuntimeModule } from "../../shared/lazy-runtime.js"; -import { resolveSessionCatalogCreateTarget } from "./runtime-agent-session-catalog.js"; +import { resolveAgentCatalogCreateTarget } from "./runtime-agent-session-catalog.js"; import { resolveRuntimeThinkingCatalog } from "./runtime-agent-thinking.js"; import { defineCachedValue } from "./runtime-cache.js"; import type { PluginRuntime } from "./types.js"; @@ -581,7 +581,7 @@ export function createRuntimeAgent(): PluginRuntime["agent"] { resolveAgentDir, resolveAgentWorkspaceDir, resolveAgentIdentity, - resolveSessionCatalogCreateTarget, + resolveSessionCatalogCreateTarget: resolveAgentCatalogCreateTarget, resolveThinkingDefault, normalizeThinkingLevel: normalizeThinkLevel, resolveThinkingPolicy: (params) => { @@ -625,7 +625,7 @@ export function createRuntimeAgent(): PluginRuntime["agent"] { () => (agentRuntime as PluginRuntime["agent"]).runEmbeddedAgent, ); defineCachedValue(agentRuntime, "session", () => ({ - resolveStorePath, + resolveStorePath: resolveSessionStorePathCore, createSessionEntry, getSessionEntry, listSessionEntries, diff --git a/src/plugins/runtime/runtime-channel.ts b/src/plugins/runtime/runtime-channel.ts index 6b127d3bd175..b66905c729e6 100644 --- a/src/plugins/runtime/runtime-channel.ts +++ b/src/plugins/runtime/runtime-channel.ts @@ -60,7 +60,7 @@ import { resolveChannelGroupRequireMention, } from "../../config/group-policy.js"; import { resolveMarkdownTableMode } from "../../config/markdown-tables.js"; -import { resolveStorePath } from "../../config/sessions.js"; +import { resolveSessionStorePathCore } from "../../config/sessions.js"; import { resolveSessionEntryResetFreshness } from "../../config/sessions/entry-freshness.js"; import { readSessionUpdatedAtCore, @@ -82,7 +82,7 @@ import type { PluginRuntime } from "./types.js"; export function createRuntimeChannel(): PluginRuntime["channel"] { const sessionRuntime = { - resolveStorePath, + resolveStorePath: resolveSessionStorePathCore, readSessionUpdatedAt: readSessionUpdatedAtCore, // Plugin runtime property names are a shipped contract; the implementations // route through the session accessor boundary. diff --git a/src/plugins/runtime/runtime-model-auth.runtime.test.ts b/src/plugins/runtime/runtime-model-auth.runtime.test.ts index 47bfbe976b40..359e71879c5d 100644 --- a/src/plugins/runtime/runtime-model-auth.runtime.test.ts +++ b/src/plugins/runtime/runtime-model-auth.runtime.test.ts @@ -9,7 +9,7 @@ const hoisted = vi.hoisted(() => ({ vi.mock("../../agents/model-auth.js", () => ({ getApiKeyForModelCore: hoisted.getApiKeyForModel, - resolveApiKeyForProvider: hoisted.resolveApiKeyForProvider, + resolveApiKeyForProviderCore: hoisted.resolveApiKeyForProvider, })); vi.mock("../provider-runtime.runtime.js", () => ({ @@ -18,7 +18,7 @@ vi.mock("../provider-runtime.runtime.js", () => ({ let getApiKeyForModel: typeof import("./runtime-model-auth.runtime.js").getApiKeyForModel; let getRuntimeAuthForModelCore: typeof import("./runtime-model-auth.runtime.js").getRuntimeAuthForModelCore; -let resolveApiKeyForProvider: typeof import("./runtime-model-auth.runtime.js").resolveApiKeyForProvider; +let resolveProviderRuntimeApiKey: typeof import("./runtime-model-auth.runtime.js").resolveProviderRuntimeApiKey; const MODEL = { id: "github-copilot/gpt-4o", @@ -29,7 +29,7 @@ const MODEL = { describe("runtime-model-auth.runtime", () => { beforeAll(async () => { - ({ getApiKeyForModel, getRuntimeAuthForModelCore, resolveApiKeyForProvider } = + ({ getApiKeyForModel, getRuntimeAuthForModelCore, resolveProviderRuntimeApiKey } = await import("./runtime-model-auth.runtime.js")); }); @@ -144,7 +144,7 @@ describe("runtime-model-auth.runtime", () => { source: "env:OPENAI_API_KEY", mode: "api-key", }); - await expect(resolveApiKeyForProvider({ provider: "openai" })).resolves.toEqual({ + await expect(resolveProviderRuntimeApiKey({ provider: "openai" })).resolves.toEqual({ apiKey: "provider-key", source: "env:OPENAI_API_KEY", mode: "api-key", diff --git a/src/plugins/runtime/runtime-model-auth.runtime.ts b/src/plugins/runtime/runtime-model-auth.runtime.ts index b7c8217b89d6..277024e8ac8b 100644 --- a/src/plugins/runtime/runtime-model-auth.runtime.ts +++ b/src/plugins/runtime/runtime-model-auth.runtime.ts @@ -1,8 +1,5 @@ // Runtime model auth helpers expose provider auth resolution to plugin runtimes. -import { - getApiKeyForModelCore, - resolveApiKeyForProvider as resolveProviderApiKey, -} from "../../agents/model-auth.js"; +import { getApiKeyForModelCore, resolveApiKeyForProviderCore } from "../../agents/model-auth.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import type { Model } from "../../llm/types.js"; import { prepareProviderRuntimeAuth } from "../provider-runtime.runtime.js"; @@ -14,10 +11,10 @@ export async function getApiKeyForModel( return getApiKeyForModelCore(params); } -export async function resolveApiKeyForProvider( - params: Parameters[0], -): Promise>> { - return resolveProviderApiKey(params); +export async function resolveProviderRuntimeApiKey( + params: Parameters[0], +): Promise>> { + return resolveApiKeyForProviderCore(params); } /** diff --git a/src/plugins/runtime/types-channel.ts b/src/plugins/runtime/types-channel.ts index 9fa4466204d1..eb6cee2170a1 100644 --- a/src/plugins/runtime/types-channel.ts +++ b/src/plugins/runtime/types-channel.ts @@ -145,7 +145,7 @@ export type PluginRuntimeChannel = { }; session: { /** @deprecated Prefer channel turn helpers that record inbound sessions as part of dispatch. */ - resolveStorePath: typeof import("../../config/sessions/paths.js").resolveStorePath; + resolveStorePath: typeof import("../../config/sessions/paths.js").resolveSessionStorePathCore; readSessionUpdatedAt: ReadSessionUpdatedAt; recordSessionMetaFromInbound: RecordSessionMetaFromInbound; /** @deprecated Prefer channel turn helpers that record inbound sessions as part of dispatch. */ diff --git a/src/plugins/runtime/types-core.ts b/src/plugins/runtime/types-core.ts index 9fe3e393da4f..17b5b4c7d6df 100644 --- a/src/plugins/runtime/types-core.ts +++ b/src/plugins/runtime/types-core.ts @@ -333,7 +333,7 @@ export type PluginRuntimeCore = { resolveAgentWorkspaceDir: typeof import("../../agents/agent-scope.js").resolveAgentWorkspaceDir; resolveAgentIdentity: typeof import("../../agents/identity.js").resolveAgentIdentity; /** Resolve an allowed catalog create target through canonical agent model/runtime policy. */ - resolveSessionCatalogCreateTarget: typeof import("./runtime-agent-session-catalog.js").resolveSessionCatalogCreateTarget; + resolveSessionCatalogCreateTarget: typeof import("./runtime-agent-session-catalog.js").resolveAgentCatalogCreateTarget; resolveThinkingDefault: (params: { cfg: import("../../config/types.openclaw.js").OpenClawConfig; provider: string; @@ -358,7 +358,7 @@ export type PluginRuntimeCore = { resolveCliBackendDispatchEligibility: typeof import("../../agents/embedded-agent-runner/cli-backend-dispatch-eligibility.js").resolveEmbeddedCliBackendDispatchEligibility; ensureAgentWorkspace: typeof import("../../agents/workspace.js").ensureAgentWorkspace; session: { - resolveStorePath: typeof import("../../config/sessions/paths.js").resolveStorePath; + resolveStorePath: typeof import("../../config/sessions/paths.js").resolveSessionStorePathCore; createSessionEntry: ( params: RuntimeCreateSessionEntryParams, ) => Promise; diff --git a/src/secrets/plan.ts b/src/secrets/plan.ts index 44dec41c27dd..9003b27942be 100644 --- a/src/secrets/plan.ts +++ b/src/secrets/plan.ts @@ -4,8 +4,9 @@ import { normalizeStringEntries } from "@openclaw/normalization-core/string-norm import type { SecretProviderConfig, SecretRef } from "../config/types.secrets.js"; import { SecretProviderSchema } from "../config/zod-schema.core.js"; import { isBlockedObjectKey } from "../infra/prototype-keys.js"; +import { toDotPath } from "../shared/dot-path.js"; import { isValidSecretProviderAlias, isValidSecretRef } from "./ref-contract.js"; -import { parseDotPath, toDotPath } from "./shared.js"; +import { parseDotPath } from "./shared.js"; import { resolvePlanTargetAgainstRegistry, type ResolvedPlanTarget } from "./target-registry.js"; /** Registry target id accepted by a secrets apply plan. */ diff --git a/src/secrets/plugin-setup-plan.ts b/src/secrets/plugin-setup-plan.ts index 86a9e37d4835..ecf780e068af 100644 --- a/src/secrets/plugin-setup-plan.ts +++ b/src/secrets/plugin-setup-plan.ts @@ -1,8 +1,9 @@ /** Shared plan construction for plugin-owned SecretRef setup commands. */ import { isValidAgentId } from "@openclaw/normalization-core/agent-id"; import type { PluginIntegrationSecretProviderConfig, SecretRef } from "../config/types.secrets.js"; +import { toDotPath } from "../shared/dot-path.js"; import type { SecretsApplyPlan, SecretsPlanTarget } from "./plan.js"; -import { resolveSecretPlanTargetByPath } from "./target-registry-query.js"; +import { resolveSecretPlanTargetByPathCore } from "./target-registry-query.js"; type PluginSecretRefProviderMapping = { providerId: string; @@ -26,10 +27,6 @@ function parseDotPath(pathname: string): string[] { .filter((segment) => segment.length > 0); } -function toDotPath(segments: string[]): string { - return segments.join("."); -} - export function assertValidPluginSecretProviderAlias(value: string): void { if (!SECRET_PROVIDER_ALIAS_PATTERN.test(value)) { throw new Error( @@ -101,7 +98,7 @@ function createPluginConfigSecretTarget(params: { ) { throw new Error(`Invalid --target config path: ${params.path}`); } - const resolved = resolveSecretPlanTargetByPath({ + const resolved = resolveSecretPlanTargetByPathCore({ configFile: params.agentId ? "auth-profiles.json" : "openclaw.json", pathSegments, }); diff --git a/src/secrets/resolve-secret-input-string.ts b/src/secrets/resolve-secret-input-string.ts index 749719f7268d..18ceabc435a0 100644 --- a/src/secrets/resolve-secret-input-string.ts +++ b/src/secrets/resolve-secret-input-string.ts @@ -15,7 +15,7 @@ type SecretDefaults = NonNullable["defaults"]; * Plugin and gateway callers can override normalization and convert SecretRef resolution errors * into surface-specific failures without duplicating provider lookup behavior. */ -export async function resolveSecretInputString(params: { +export async function materializeSecretInput(params: { config: OpenClawConfig; /** Inline string, SecretInput object, or SecretRef object from config/plugin settings. */ value: unknown; diff --git a/src/secrets/runtime-discord-surface.test.ts b/src/secrets/runtime-discord-surface.test.ts index b87eba55c84d..1639bea7a47e 100644 --- a/src/secrets/runtime-discord-surface.test.ts +++ b/src/secrets/runtime-discord-surface.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import "./runtime-discord.test-support.ts"; import { asConfig, @@ -77,7 +77,7 @@ describe("secrets runtime snapshot discord surface", () => { it.skipIf(process.platform === "win32")( "resolves the implicit default token when named Discord accounts are added", async () => { - await withTempDir({ prefix: "openclaw-discord-secrets-" }, async (root) => { + await withTestDir({ prefix: "openclaw-discord-secrets-" }, async (root) => { const secretsPath = path.join(root, "secrets.json"); await fs.writeFile( secretsPath, diff --git a/src/secrets/runtime.auth-migration-isolation.test.ts b/src/secrets/runtime.auth-migration-isolation.test.ts index e8a48661dc10..181512f74041 100644 --- a/src/secrets/runtime.auth-migration-isolation.test.ts +++ b/src/secrets/runtime.auth-migration-isolation.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -let resolveApiKeyForProvider: typeof import("../agents/model-auth.js").resolveApiKeyForProvider; +let resolveApiKeyForProviderCore: typeof import("../agents/model-auth.js").resolveApiKeyForProviderCore; let closeOpenClawAgentDatabasesForTest: typeof import("../state/openclaw-agent-db.js").closeOpenClawAgentDatabasesForTest; let withOpenClawTestState: typeof import("../test-utils/openclaw-test-state.js").withOpenClawTestState; let activateSecretsRuntimeSnapshot: typeof import("./runtime.js").activateSecretsRuntimeSnapshot; @@ -17,7 +17,7 @@ describe("auth profile migration isolation", () => { clearSecretsRuntimeSnapshot, prepareSecretsRuntimeSnapshot, } = await import("./runtime.js")); - ({ resolveApiKeyForProvider } = await import("../agents/model-auth.js")); + ({ resolveApiKeyForProviderCore } = await import("../agents/model-auth.js")); ({ closeOpenClawAgentDatabasesForTest } = await import("../state/openclaw-agent-db.js")); ({ withOpenClawTestState } = await import("../test-utils/openclaw-test-state.js")); clearSecretsRuntimeSnapshot(); @@ -79,14 +79,14 @@ describe("auth profile migration isolation", () => { activateSecretsRuntimeSnapshot(snapshot); await expect( - resolveApiKeyForProvider({ provider: "openai", agentDir: legacyAgentDir }), + resolveApiKeyForProviderCore({ provider: "openai", agentDir: legacyAgentDir }), ).rejects.toMatchObject({ code: "AUTH_PROFILE_MIGRATION_REQUIRED", action: "openclaw doctor --fix", sourceKinds: ["legacy-auth"], }); await expect( - resolveApiKeyForProvider({ + resolveApiKeyForProviderCore({ provider: "openai", agentDir: healthyAgentDir, profileId: "openai:default", diff --git a/src/secrets/shared.ts b/src/secrets/shared.ts index f0451bf49389..8596c2925721 100644 --- a/src/secrets/shared.ts +++ b/src/secrets/shared.ts @@ -53,13 +53,6 @@ export function parseDotPath(pathname: string): string[] { .filter((segment) => segment.length > 0); } -/** - * Joins config path segments using the secrets command's dotted path format. - */ -export function toDotPath(segments: string[]): string { - return segments.join("."); -} - /** * Atomically writes secret-adjacent text, using the private store for default 0600 files. */ diff --git a/src/secrets/target-registry-query.ts b/src/secrets/target-registry-query.ts index 3bd8ab6605d2..2b41bb60d0f1 100644 --- a/src/secrets/target-registry-query.ts +++ b/src/secrets/target-registry-query.ts @@ -384,7 +384,7 @@ function resolvePlanTargetAgainstEntries( /** * Resolves a plan-capable secret target by owning config document and concrete path. */ -export function resolveSecretPlanTargetByPath(params: { +export function resolveSecretPlanTargetByPathCore(params: { configFile: SecretTargetConfigFile; pathSegments: string[]; }): ResolvedPlanTarget | null { diff --git a/src/secrets/target-registry.test.ts b/src/secrets/target-registry.test.ts index f2751037731b..f2297b025589 100644 --- a/src/secrets/target-registry.test.ts +++ b/src/secrets/target-registry.test.ts @@ -10,7 +10,7 @@ import { getCoreSecretTargetRegistry } from "./target-registry-data.js"; import { discoverConfigSecretTargetsByIds, resolveConfigSecretTargetByPath, - resolveSecretPlanTargetByPath, + resolveSecretPlanTargetByPathCore, } from "./target-registry.js"; describe("secret target registry", () => { @@ -62,11 +62,11 @@ describe("secret target registry", () => { }); it("resolves plan targets by owning config document", () => { - const configTarget = resolveSecretPlanTargetByPath({ + const configTarget = resolveSecretPlanTargetByPathCore({ configFile: "openclaw.json", pathSegments: ["models", "providers", "openai", "apiKey"], }); - const authProfileTarget = resolveSecretPlanTargetByPath({ + const authProfileTarget = resolveSecretPlanTargetByPathCore({ configFile: "auth-profiles.json", pathSegments: ["profiles", "openai:default", "key"], }); diff --git a/src/sessions/session-state-events.test.ts b/src/sessions/session-state-events.test.ts index b21c2a314db6..7815f2b1f2ed 100644 --- a/src/sessions/session-state-events.test.ts +++ b/src/sessions/session-state-events.test.ts @@ -1,7 +1,7 @@ import { afterAll, afterEach, describe, expect, it, vi } from "vitest"; import { cleanupTempDirs, makeTempDir } from "../../test/helpers/temp-dir.js"; import { drainFormattedSystemEvents } from "../auto-reply/reply/session-system-events.js"; -import { upsertSessionEntry } from "../config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { setHeartbeatWakeHandler } from "../infra/heartbeat-wake.js"; import { @@ -104,7 +104,7 @@ async function createWatcherSession( database: ReturnType, watcherSessionKey = watcher, ) { - await upsertSessionEntry( + await upsertSessionEntryCore( { sessionKey: watcherSessionKey, env: database.env }, { sessionId: `session-${watcherSessionKey}`, updatedAt: Date.now() }, ); diff --git a/src/sessions/session-upstream-monitor.test.ts b/src/sessions/session-upstream-monitor.test.ts index dc9fe2ec2d6a..a3ff4e534e57 100644 --- a/src/sessions/session-upstream-monitor.test.ts +++ b/src/sessions/session-upstream-monitor.test.ts @@ -3,7 +3,7 @@ import { createDeferred } from "../../test/helpers/promise.js"; import { cleanupTempDirs, makeTempDir } from "../../test/helpers/temp-dir.js"; import { appendTranscriptMessage, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import { importSessionCatalogHistory } from "../plugins/session-catalog-history-import.js"; import type { SessionCatalogProvider, SessionUpstreamProbe } from "../plugins/session-catalog.js"; @@ -785,7 +785,7 @@ describe("session upstream monitor", () => { const database = createDatabaseOptions(); const sessionKey = "agent:main:adopted:provenance"; const sessionId = "session-provenance"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, env: database.env }, { sessionId, updatedAt: 1 }, ); @@ -830,7 +830,7 @@ describe("session upstream monitor", () => { const database = createDatabaseOptions(); const sessionKey = "agent:main:adopted:catalog-import"; const sessionId = "session-catalog-import"; - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey, env: database.env }, { sessionId, updatedAt: 1 }, ); diff --git a/src/shared/dot-path.ts b/src/shared/dot-path.ts new file mode 100644 index 000000000000..42e28e144d4f --- /dev/null +++ b/src/shared/dot-path.ts @@ -0,0 +1,4 @@ +/** Joins path segments into their dotted-path representation. */ +export function toDotPath(segments: readonly string[]): string { + return segments.join("."); +} diff --git a/src/shared/ignore-rules.ts b/src/shared/ignore-rules.ts index 4aa7238e5428..4f3a60a604f0 100644 --- a/src/shared/ignore-rules.ts +++ b/src/shared/ignore-rules.ts @@ -33,7 +33,7 @@ type IgnoreMatcherState = { const ignoreMatcherStates = new WeakMap(); function normalizeLiteralSubtreePath(pathname: string): string { - const posixPath = toPosixPath(pathname); + const posixPath = normalizeNativePathSeparators(pathname); return posixPath.endsWith("/") ? posixPath.slice(0, -1) : posixPath; } @@ -198,7 +198,7 @@ function parseIgnorePatterns( return { patterns, chars: patternChars }; } -export const toPosixPath = (pathValue: string) => pathValue.split(sep).join("/"); +export const normalizeNativePathSeparators = (pathValue: string) => pathValue.split(sep).join("/"); /** Adds nested ignore-file rules to a matcher using paths relative to the scan root. */ export function addIgnoreRules(dir: string, rootDir: string): IgnoreMatcher; @@ -222,7 +222,7 @@ export function addIgnoreRules( // callers supplying ignorecase:false must carry that fact alongside it. const state = getIgnoreMatcherState(matcher, options?.ignoreCase ?? true); const relativeDir = relative(rootDir, dir); - const prefix = relativeDir ? `${toPosixPath(relativeDir)}/` : ""; + const prefix = relativeDir ? `${normalizeNativePathSeparators(relativeDir)}/` : ""; for (const filename of IGNORE_FILE_NAMES) { const ignorePath = join(dir, filename); diff --git a/src/skills/lifecycle/install.test.ts b/src/skills/lifecycle/install.test.ts index ce8d023d3e65..5652f9b03255 100644 --- a/src/skills/lifecycle/install.test.ts +++ b/src/skills/lifecycle/install.test.ts @@ -9,7 +9,10 @@ import { import { createMockPluginRegistry } from "../../plugins/hooks.test-fixtures.js"; import { captureEnv } from "../../test-utils/env.js"; import { createFixtureSuite } from "../../test-utils/fixture-suite.js"; -import { resolveOpenClawMetadata, resolveSkillInvocationPolicy } from "../loading/frontmatter.js"; +import { + resolveSkillInvocationPolicy, + resolveSkillManifestMetadata, +} from "../loading/frontmatter.js"; import { loadSkillsFromDirSafe, readSkillFrontmatterSafe } from "../loading/local-loader.js"; import { runCommandWithTimeoutMock } from "../test-support/install-test-mocks.js"; import type { SkillEntry } from "../types.js"; @@ -68,7 +71,7 @@ function loadTestWorkspaceSkillEntries(workspaceDir: string): SkillEntry[] { return { skill, frontmatter, - metadata: resolveOpenClawMetadata(frontmatter), + metadata: resolveSkillManifestMetadata(frontmatter), invocation, exposure: { includeInRuntimeRegistry: true, diff --git a/src/skills/lifecycle/source-install.test.ts b/src/skills/lifecycle/source-install.test.ts index c83befaa6a67..c058c67c450b 100644 --- a/src/skills/lifecycle/source-install.test.ts +++ b/src/skills/lifecycle/source-install.test.ts @@ -3,7 +3,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { runCommandWithTimeout } from "../../process/exec.js"; -import { withTempDir } from "../../test-helpers/temp-dir.js"; +import { withTestDir } from "../../test-helpers/temp-dir.js"; import { buildWorkspaceSkillStatus } from "../discovery/status.js"; import { installSkillFromSource } from "./source-install.js"; @@ -95,7 +95,7 @@ function capturePolicyConfig(params: { scriptPath: string; capturePath: string } describe("installSkillFromSource", () => { it("installs a local skill directory using the SKILL.md frontmatter name", async () => { - await withTempDir({ prefix: "openclaw-skill-source-local-" }, async (root) => { + await withTestDir({ prefix: "openclaw-skill-source-local-" }, async (root) => { const workspaceDir = path.join(root, "workspace"); const sourceDir = path.join(root, "source"); await writeSkill(sourceDir, { name: "frontmatter-skill" }); @@ -118,7 +118,7 @@ describe("installSkillFromSource", () => { }); it("uses --as slug override for local skill directories", async () => { - await withTempDir({ prefix: "openclaw-skill-source-as-" }, async (root) => { + await withTestDir({ prefix: "openclaw-skill-source-as-" }, async (root) => { const workspaceDir = path.join(root, "workspace"); const sourceDir = path.join(root, "source"); await writeSkill(sourceDir, { name: "frontmatter-skill" }); @@ -147,7 +147,7 @@ describe("installSkillFromSource", () => { }); it("ignores oversized source-origin metadata while loading skill keys", async () => { - await withTempDir({ prefix: "openclaw-skill-source-origin-cap-" }, async (root) => { + await withTestDir({ prefix: "openclaw-skill-source-origin-cap-" }, async (root) => { const workspaceDir = path.join(root, "workspace"); const sourceDir = path.join(root, "source"); await writeSkill(sourceDir, { name: "frontmatter-skill" }); @@ -173,7 +173,7 @@ describe("installSkillFromSource", () => { }); it("installs git: file repositories and records the resolved commit", async () => { - await withTempDir({ prefix: "openclaw-skill-source-git-" }, async (root) => { + await withTestDir({ prefix: "openclaw-skill-source-git-" }, async (root) => { const workspaceDir = path.join(root, "workspace"); const repoDir = path.join(root, "repo"); await fs.mkdir(repoDir, { recursive: true }); @@ -204,7 +204,7 @@ describe("installSkillFromSource", () => { }); it("isolates git commands from inherited Git hook environment", async () => { - await withTempDir({ prefix: "openclaw-skill-source-git-env-" }, async (root) => { + await withTestDir({ prefix: "openclaw-skill-source-git-env-" }, async (root) => { const workspaceDir = path.join(root, "workspace"); const repoDir = path.join(root, "repo"); const poisonRepoDir = path.join(root, "poison"); @@ -251,7 +251,7 @@ describe("installSkillFromSource", () => { }); it("disables system git config while preserving sanitized git command env", async () => { - await withTempDir({ prefix: "openclaw-skill-source-git-system-config-" }, async (root) => { + await withTestDir({ prefix: "openclaw-skill-source-git-system-config-" }, async (root) => { const workspaceDir = path.join(root, "workspace"); const repoDir = path.join(root, "repo"); const poisonRepoDir = path.join(root, "poison"); @@ -288,7 +288,7 @@ describe("installSkillFromSource", () => { }); it("installs slash-containing git branch refs from fresh clones", async () => { - await withTempDir({ prefix: "openclaw-skill-source-git-ref-" }, async (root) => { + await withTestDir({ prefix: "openclaw-skill-source-git-ref-" }, async (root) => { const workspaceDir = path.join(root, "workspace"); const repoDir = path.join(root, "repo"); await fs.mkdir(repoDir, { recursive: true }); @@ -339,7 +339,7 @@ describe("installSkillFromSource", () => { ] as const)( "reports $name git skill sources with expected mutability to policy", async (entry) => { - await withTempDir({ prefix: "openclaw-skill-source-git-policy-" }, async (root) => { + await withTestDir({ prefix: "openclaw-skill-source-git-policy-" }, async (root) => { const workspaceDir = path.join(root, "workspace"); const repoDir = path.join(root, "repo"); await fs.mkdir(repoDir, { recursive: true }); @@ -371,7 +371,7 @@ describe("installSkillFromSource", () => { ); it("removes stale ClawHub lock tracking after source installs", async () => { - await withTempDir({ prefix: "openclaw-skill-source-untrack-" }, async (root) => { + await withTestDir({ prefix: "openclaw-skill-source-untrack-" }, async (root) => { const workspaceDir = path.join(root, "workspace"); const sourceDir = path.join(root, "source"); await writeSkill(sourceDir, { name: "frontmatter-skill" }); @@ -424,7 +424,7 @@ describe("installSkillFromSource", () => { }); it("rejects missing local skill roots before treating them as ClawHub slugs", async () => { - await withTempDir({ prefix: "openclaw-skill-source-missing-" }, async (root) => { + await withTestDir({ prefix: "openclaw-skill-source-missing-" }, async (root) => { const result = await installSkillFromSource({ workspaceDir: path.join(root, "workspace"), spec: "./missing-skill", @@ -438,7 +438,7 @@ describe("installSkillFromSource", () => { }); it("refuses git specs whose url would be consumed as a git clone option", async () => { - await withTempDir({ prefix: "openclaw-skill-source-opt-inject-" }, async (root) => { + await withTestDir({ prefix: "openclaw-skill-source-opt-inject-" }, async (root) => { const workspaceDir = path.join(root, "workspace"); const payload = path.join(root, "payload.git"); diff --git a/src/skills/lifecycle/source-install.ts b/src/skills/lifecycle/source-install.ts index 6be06afab9fc..7b300b3be927 100644 --- a/src/skills/lifecycle/source-install.ts +++ b/src/skills/lifecycle/source-install.ts @@ -6,7 +6,7 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coe import { sanitizeForLog } from "../../../packages/terminal-core/src/ansi.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { sanitizeHostExecEnv } from "../../infra/host-env-security.js"; -import { withTempDir } from "../../infra/install-source-utils.js"; +import { withInstallWorkspace } from "../../infra/install-source-utils.js"; import { writeJson } from "../../infra/json-files.js"; import { isImmutableGitCommitRef, parseGitPluginSpec } from "../../plugins/git-install.js"; import { runCommandWithTimeout } from "../../process/exec.js"; @@ -276,7 +276,7 @@ async function installGitSkill(params: { return { ok: false, error: `Unsupported git skill spec: ${params.spec}` }; } - return await withTempDir("openclaw-git-skill-", async (tmpDir) => { + return await withInstallWorkspace("openclaw-git-skill-", async (tmpDir) => { const repoDir = path.join(tmpDir, "repo"); const exportDir = path.join(tmpDir, "export"); params.logger?.info?.( diff --git a/src/skills/loading/agents-directory.test.ts b/src/skills/loading/agents-directory.test.ts index 28854d40c3f1..fc9f74c7307d 100644 --- a/src/skills/loading/agents-directory.test.ts +++ b/src/skills/loading/agents-directory.test.ts @@ -3,12 +3,12 @@ import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createTempDirTracker } from "../../../test/helpers/temp-dir.js"; import { setTestEnvValue } from "../../test-utils/env.js"; +import { writeSkill } from "../test-support/e2e-test-helpers.js"; import { restoreMockSkillsHomeEnv, setMockSkillsHomeEnv, type SkillsHomeEnvSnapshot, } from "../test-support/home-env.test-support.js"; -import { writeSkill } from "../test-support/test-helpers.js"; import { buildSkillSnapshot } from "./workspace-skill-prompt.js"; vi.mock("./plugin-skills.js", () => ({ diff --git a/src/skills/loading/frontmatter.test.ts b/src/skills/loading/frontmatter.test.ts index 7d0111e70bfb..dfaf589df79b 100644 --- a/src/skills/loading/frontmatter.test.ts +++ b/src/skills/loading/frontmatter.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import { parseSkillFrontmatter, - resolveOpenClawMetadata, + resolveSkillManifestMetadata, resolveSkillInvocationPolicy, } from "./frontmatter.js"; @@ -155,9 +155,9 @@ description: *missing }); }); -describe("resolveOpenClawMetadata install validation", () => { +describe("resolveSkillManifestMetadata install validation", () => { function resolveInstall(frontmatter: Record) { - return resolveOpenClawMetadata(frontmatter)?.install; + return resolveSkillManifestMetadata(frontmatter)?.install; } it("accepts safe install specs", () => { @@ -228,7 +228,7 @@ user-invocable: true # Creating Payment Credentials `); - const metadata = resolveOpenClawMetadata(frontmatter); + const metadata = resolveSkillManifestMetadata(frontmatter); expect(frontmatter.name).toBe("create-payment-credential"); expect(frontmatter.description).toContain("one-time-use payment credentials"); diff --git a/src/skills/loading/frontmatter.ts b/src/skills/loading/frontmatter.ts index 3f47eaff7b75..1452543d9b44 100644 --- a/src/skills/loading/frontmatter.ts +++ b/src/skills/loading/frontmatter.ts @@ -190,7 +190,7 @@ function parseInstallSpec(input: unknown): SkillInstallSpec | undefined { return spec; } -export function resolveOpenClawMetadata( +export function resolveSkillManifestMetadata( frontmatter: ParsedSkillFrontmatter, ): OpenClawSkillMetadata | undefined { const metadataObj = resolveOpenClawManifestBlock({ frontmatter }); diff --git a/src/skills/loading/plugin-skills.ts b/src/skills/loading/plugin-skills.ts index 34aa691c7753..0653f9ca9c73 100644 --- a/src/skills/loading/plugin-skills.ts +++ b/src/skills/loading/plugin-skills.ts @@ -8,9 +8,9 @@ import { walkDirectorySync } from "../../infra/fs-safe.js"; import { createSubsystemLogger } from "../../logging/subsystem.js"; import { normalizePluginsConfigWithResolver, - resolveEffectivePluginActivationState, - resolveMemorySlotDecision, + resolvePolicyPluginActivationState, } from "../../plugins/config-policy.js"; +import { resolveMemorySlotDecision } from "../../plugins/config-state.js"; import { registerPluginMetadataProcessMemoLifecycleClear } from "../../plugins/plugin-metadata-lifecycle.js"; import { resolvePluginMetadataSnapshot } from "../../plugins/plugin-metadata-snapshot.js"; import { hasKind } from "../../plugins/slots.js"; @@ -86,7 +86,7 @@ export function resolvePluginSkillDirs(params: { if (!record.skills || record.skills.length === 0) { continue; } - const activationState = resolveEffectivePluginActivationState({ + const activationState = resolvePolicyPluginActivationState({ id: record.id, origin: record.origin, config: normalizedPlugins, diff --git a/src/skills/loading/session.test.ts b/src/skills/loading/session.test.ts index 119225db0bc6..d80c812f72f3 100644 --- a/src/skills/loading/session.test.ts +++ b/src/skills/loading/session.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../../test/helpers/temp-dir.js"; -import { parseSkillFrontmatter, resolveOpenClawMetadata } from "./frontmatter.js"; +import { parseSkillFrontmatter, resolveSkillManifestMetadata } from "./frontmatter.js"; import { loadSkills } from "./session.js"; const tempDirs = useAutoCleanupTempDirTracker(afterEach); @@ -85,7 +85,7 @@ disable-model-invocation: true filePath: skillFile, }), ]); - expect(resolveOpenClawMetadata(frontmatter)?.requires?.env).toEqual(["EXAMPLE_VAR"]); + expect(resolveSkillManifestMetadata(frontmatter)?.requires?.env).toEqual(["EXAMPLE_VAR"]); }); it("reports malformed frontmatter by file and keeps loading sibling skills", async () => { diff --git a/src/skills/loading/session.ts b/src/skills/loading/session.ts index d8e7d5536a7e..f0dad13e9d96 100644 --- a/src/skills/loading/session.ts +++ b/src/skills/loading/session.ts @@ -4,7 +4,11 @@ import { CONFIG_DIR_NAME, getAgentDir } from "../../agents/config.js"; import type { ResourceDiagnostic } from "../../agents/sessions/diagnostics.js"; import { createSyntheticSourceInfo, type SourceInfo } from "../../agents/sessions/source-info.js"; import { canonicalizePath } from "../../agents/utils/paths.js"; -import { addIgnoreRules, toPosixPath, type IgnoreMatcher } from "../../shared/ignore-rules.js"; +import { + addIgnoreRules, + normalizeNativePathSeparators, + type IgnoreMatcher, +} from "../../shared/ignore-rules.js"; // Session skill helpers resolve skills attached to a session and its transcript state. import { expandTildePath } from "../../shared/tilde-path.js"; import { getArchivedSkillFiles } from "../workshop/curator.js"; @@ -137,7 +141,7 @@ function loadSkillsFromDirInternal( } } - const relPath = toPosixPath(relative(root, fullPath)); + const relPath = normalizeNativePathSeparators(relative(root, fullPath)); if (!isFile || ig.ignores(relPath)) { continue; } @@ -176,7 +180,7 @@ function loadSkillsFromDirInternal( } } - const relPath = toPosixPath(relative(root, fullPath)); + const relPath = normalizeNativePathSeparators(relative(root, fullPath)); const ignorePath = isDirectory ? `${relPath}/` : relPath; if (ig.ignores(ignorePath)) { continue; diff --git a/src/skills/loading/skill-version.ts b/src/skills/loading/skill-version.ts index 898292fe7faa..83123cc32205 100644 --- a/src/skills/loading/skill-version.ts +++ b/src/skills/loading/skill-version.ts @@ -1,6 +1,6 @@ // Skill prompt versions are deterministic content markers for model-visible skill catalogs. -import { sha256HexPrefix } from "../../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../../infra/crypto-digest.js"; export function computeSkillPromptVersion(content: string): string { - return `sha256:${sha256HexPrefix(content, 16)}`; + return `sha256:${sha256HexPrefixCore(content, 16)}`; } diff --git a/src/skills/loading/workspace-skill-loader.ts b/src/skills/loading/workspace-skill-loader.ts index 1e9c85df8b85..920846dd93b4 100644 --- a/src/skills/loading/workspace-skill-loader.ts +++ b/src/skills/loading/workspace-skill-loader.ts @@ -25,7 +25,7 @@ import { getArchivedSkillFiles } from "../workshop/curator.js"; import { resolveBundledSkillsDir } from "./bundled-dir.js"; import { resolveBundledAllowlist, shouldIncludeSkill } from "./config.js"; import { - resolveOpenClawMetadata, + resolveSkillManifestMetadata, resolveSkillInvocationPolicy, resolveSkillKey, } from "./frontmatter.js"; @@ -153,7 +153,7 @@ function resolveSkillEntryMetadata(params: { frontmatter: ParsedSkillFrontmatter; skillDir: string; }): OpenClawSkillMetadata | undefined { - const metadata = resolveOpenClawMetadata(params.frontmatter); + const metadata = resolveSkillManifestMetadata(params.frontmatter); if (metadata?.skillKey) { return metadata; } diff --git a/src/skills/test-support/test-helpers.ts b/src/skills/test-support/test-helpers.ts index 54b706ee9471..9a996dbd1b22 100644 --- a/src/skills/test-support/test-helpers.ts +++ b/src/skills/test-support/test-helpers.ts @@ -1,31 +1,7 @@ // Skill test helpers build canonical skill fixtures for unit tests. -import fs from "node:fs/promises"; -import path from "node:path"; import { createSyntheticSourceInfo, type Skill } from "../loading/skill-contract.js"; import type { SkillEntry } from "../types.js"; -/** Writes a SKILL.md fixture with frontmatter and optional body. */ -export async function writeSkill(params: { - dir: string; - name: string; - description: string; - body?: string; -}) { - const { dir, name, description, body } = params; - await fs.mkdir(dir, { recursive: true }); - await fs.writeFile( - path.join(dir, "SKILL.md"), - `--- -name: ${name} -description: ${description} ---- - -${body ?? `# ${name}\n`} -`, - "utf-8", - ); -} - export function createCanonicalFixtureSkill(params: { name: string; description: string; diff --git a/src/skills/workshop/history-scan-candidates.ts b/src/skills/workshop/history-scan-candidates.ts index 95e5cff03f61..190cec640444 100644 --- a/src/skills/workshop/history-scan-candidates.ts +++ b/src/skills/workshop/history-scan-candidates.ts @@ -1,4 +1,4 @@ -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { listSessionTranscriptInstances, type SessionTranscriptInstance, @@ -57,7 +57,7 @@ export function selectSkillHistoryScanCandidates(params: { export function listHistoryScanCandidates( params: SkillHistoryScanScope, ): SkillHistoryScanCandidate[] { - const storePath = resolveStorePath(params.config.session?.store, { + const storePath = resolveSessionStorePathCore(params.config.session?.store, { agentId: params.agentId, ...(params.env ? { env: params.env } : {}), }); diff --git a/src/skills/workshop/history-scan-state.ts b/src/skills/workshop/history-scan-state.ts index f80646b8723d..f12b140c5a41 100644 --- a/src/skills/workshop/history-scan-state.ts +++ b/src/skills/workshop/history-scan-state.ts @@ -1,7 +1,7 @@ import { createHash } from "node:crypto"; import path from "node:path"; import { isRecord } from "@openclaw/normalization-core/record-coerce"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { createCorePluginStateSyncKeyedStore, @@ -93,7 +93,7 @@ export function isStoredHistoryScanState(value: unknown): value is StoredSkillHi } function loadHistoryScanState(params: Omit) { - const storePath = resolveStorePath(params.config.session?.store, { + const storePath = resolveSessionStorePathCore(params.config.session?.store, { agentId: params.agentId, ...(params.env ? { env: params.env } : {}), }); diff --git a/src/skills/workshop/history-scan.ts b/src/skills/workshop/history-scan.ts index 69ba71d78581..700bcb91ae60 100644 --- a/src/skills/workshop/history-scan.ts +++ b/src/skills/workshop/history-scan.ts @@ -4,7 +4,7 @@ import { resolveModelAsync } from "../../agents/embedded-agent-runner/model.js"; import { isEmbeddedAgentRunActive } from "../../agents/embedded-agent-runner/runs.js"; import { resolveDefaultModelForAgent } from "../../agents/model-selection-config.js"; import { resolveHeartbeatPrompt } from "../../auto-reply/heartbeat.js"; -import { resolveStorePath } from "../../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../config/sessions/paths.js"; import { toErrorObject } from "../../infra/errors.js"; import { listHistoryScanCandidates, @@ -128,7 +128,7 @@ async function runSkillHistoryScanCore( params: SkillHistoryScanScope, ): Promise { const store = historyScanStore(params.env); - const storePath = resolveStorePath(params.config.session?.store, { + const storePath = resolveSessionStorePathCore(params.config.session?.store, { agentId: params.agentId, ...(params.env ? { env: params.env } : {}), }); @@ -373,7 +373,7 @@ async function runSkillHistoryScanCore( export function runSkillHistoryScan( params: SkillHistoryScanScope, ): Promise { - const storePath = resolveStorePath(params.config.session?.store, { + const storePath = resolveSessionStorePathCore(params.config.session?.store, { agentId: params.agentId, ...(params.env ? { env: params.env } : {}), }); diff --git a/src/state/openclaw-state-db.test.ts b/src/state/openclaw-state-db.test.ts index a7d6fbd1a48d..0e5b4ab7d8f2 100644 --- a/src/state/openclaw-state-db.test.ts +++ b/src/state/openclaw-state-db.test.ts @@ -947,7 +947,7 @@ function runConcurrentSchemaProbe(params: { const [ { resolveGatewayLockDir }, { resolvePathViaExistingAncestorSync }, - { sha256HexPrefix }, + { sha256HexPrefixCore }, { ensurePrivateSqliteCoordinatorDirectory }, { OPENCLAW_SQLITE_BUSY_TIMEOUT_MS }, { resolveOpenClawStateDirForDatabasePath }, @@ -956,7 +956,7 @@ function runConcurrentSchemaProbe(params: { const stateDir = resolveOpenClawStateDirForDatabasePath(canonicalDatabasePath); const coordinatorPath = path.join( resolveGatewayLockDir(stateDir), - \`state-ownership.\${sha256HexPrefix(canonicalDatabasePath, 8)}.lock.sqlite\`, + \`state-ownership.\${sha256HexPrefixCore(canonicalDatabasePath, 8)}.lock.sqlite\`, ); ensurePrivateSqliteCoordinatorDirectory( path.dirname(coordinatorPath), diff --git a/src/state/openclaw-state-ownership.test.ts b/src/state/openclaw-state-ownership.test.ts index 8549c9bb2cb5..f5948396d975 100644 --- a/src/state/openclaw-state-ownership.test.ts +++ b/src/state/openclaw-state-ownership.test.ts @@ -9,7 +9,7 @@ import { } from "../config/io.health-state.js"; import { resolveGatewayLockDir } from "../config/paths.js"; import { resolvePathViaExistingAncestorSync } from "../infra/boundary-path.js"; -import { sha256HexPrefix } from "../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../infra/crypto-digest.js"; import { requireNodeSqlite, resolveImmutableSqliteFileUri } from "../infra/node-sqlite.js"; import { withEnv } from "../test-utils/env.js"; import { withOpenClawStateStartupMigrationCheckpointDatabase } from "./openclaw-state-db-startup-checkpoint.js"; @@ -93,7 +93,7 @@ function resolveExpectedOwnershipCoordinatorPath(databasePath: string): string { const stateDir = resolveOpenClawStateDirForDatabasePath(canonicalDatabasePath); return path.join( resolveGatewayLockDir(stateDir), - `state-ownership.${sha256HexPrefix(canonicalDatabasePath, 8)}.lock.sqlite`, + `state-ownership.${sha256HexPrefixCore(canonicalDatabasePath, 8)}.lock.sqlite`, ); } diff --git a/src/state/openclaw-state-ownership.ts b/src/state/openclaw-state-ownership.ts index 5563e02f1b9e..ef4374314995 100644 --- a/src/state/openclaw-state-ownership.ts +++ b/src/state/openclaw-state-ownership.ts @@ -4,7 +4,7 @@ import type { DatabaseSync } from "node:sqlite"; import { isRecord } from "@openclaw/normalization-core/record-coerce"; import { resolveGatewayLockDir } from "../config/paths.js"; import { resolvePathViaExistingAncestorSync } from "../infra/boundary-path.js"; -import { sha256HexPrefix } from "../infra/crypto-digest.js"; +import { sha256HexPrefixCore } from "../infra/crypto-digest.js"; import { isGatewayExternallySupervised } from "../infra/gateway-supervision.js"; import { openNodeSqliteDatabase, @@ -179,7 +179,7 @@ function resolveOpenClawStateOwnershipCoordinatorPath(databasePath: string): str const stateDir = resolveOpenClawStateDirForDatabasePath(canonicalDatabasePath); return path.join( resolveGatewayLockDir(stateDir), - `state-ownership.${sha256HexPrefix(canonicalDatabasePath, 8)}.lock.sqlite`, + `state-ownership.${sha256HexPrefixCore(canonicalDatabasePath, 8)}.lock.sqlite`, ); } diff --git a/src/status/status-runtime-lines.ts b/src/status/status-runtime-lines.ts index 8fb5d63f270b..1c8b3ecdc2ac 100644 --- a/src/status/status-runtime-lines.ts +++ b/src/status/status-runtime-lines.ts @@ -1,6 +1,9 @@ import os from "node:os"; import type { SessionEntry } from "../config/sessions.js"; -import { resolveSessionFilePath, resolveSessionFilePathOptions } from "../config/sessions/paths.js"; +import { + resolveSessionFilePathCore, + resolveSessionFilePathOptions, +} from "../config/sessions/paths.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { formatDurationCompact } from "../infra/format-time/format-duration.ts"; import { formatMissingCostEntries } from "../infra/session-cost-usage-totals.js"; @@ -36,7 +39,7 @@ async function resolveSessionCostLine(params: { sessionFile = resolveExistingUsageSessionFile({ sessionId, sessionEntry: params.sessionEntry, - sessionFile: resolveSessionFilePath(sessionId, params.sessionEntry, pathOpts), + sessionFile: resolveSessionFilePathCore(sessionId, params.sessionEntry, pathOpts), agentId: params.agentId, }); } catch { diff --git a/src/status/summary.ts b/src/status/summary.ts index 14c42d2f16c1..c4c00a78cb35 100644 --- a/src/status/summary.ts +++ b/src/status/summary.ts @@ -11,7 +11,7 @@ import { hasSessionActiveAutoModelFallback, hasSessionAutoModelFallbackProvenance, } from "../config/sessions/model-override-provenance.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { listSessionEntriesReadOnly, loadExactSessionEntryReadOnly, @@ -550,7 +550,7 @@ export async function getStatusSummary( const storeSources = agentList.agents.map((agent) => ({ agentId: agent.id, - storePath: resolveStorePath(cfg.session?.store, { agentId: agent.id }), + storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId: agent.id }), })); const paths = new Set(); const pathCounts = new Map(); @@ -561,7 +561,7 @@ export async function getStatusSummary( const byAgent = await Promise.all( agentList.agents.map(async (agent) => { - const storePath = resolveStorePath(cfg.session?.store, { agentId: agent.id }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId: agent.id }); const candidates = loadSessionCandidates(storePath, agent.id); const sessions = await buildSessionRows( selectRecentSessionCandidates(candidates, RECENT_SESSION_LIMIT), diff --git a/src/system-agent/audit.test.ts b/src/system-agent/audit.test.ts index 3c816133a7f8..ea0c2b2c1e26 100644 --- a/src/system-agent/audit.test.ts +++ b/src/system-agent/audit.test.ts @@ -1,7 +1,7 @@ // OpenClaw audit tests cover SQLite-backed rescue audit scenarios. import { afterEach, describe, expect, it, vi } from "vitest"; import { resetPluginStateStoreForTests } from "../plugin-state/plugin-state-store.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { appendSystemAgentAuditEntry, SYSTEM_AGENT_AUDIT_STORE_LABEL } from "./audit.js"; import { listSystemAgentAuditEntriesForTests } from "./audit.test-support.js"; @@ -18,7 +18,7 @@ describe("OpenClaw audit log", () => { }); it("writes records into shared SQLite state", async () => { - await withTempDir({ prefix: "openclaw-audit-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-audit-" }, async (tempDir) => { vi.stubEnv("OPENCLAW_STATE_DIR", tempDir); const auditStore = await appendSystemAgentAuditEntry({ diff --git a/src/system-agent/operations-parse.ts b/src/system-agent/operations-parse.ts index e340fe438716..d6c29e53c37e 100644 --- a/src/system-agent/operations-parse.ts +++ b/src/system-agent/operations-parse.ts @@ -36,7 +36,7 @@ export type SystemAgentCommandDeps = { readConfigFileSnapshot?: typeof import("../config/config.js").readConfigFileSnapshot; ensureAuthProfileStore?: typeof import("../agents/auth-profiles/store.js").ensureAuthProfileStore; resolveCliAuthBindingFingerprint?: typeof import("../agents/cli-auth-epoch.js").resolveCliAuthBindingFingerprint; - resolveApiKeyForProvider?: typeof import("../agents/model-auth.js").resolveApiKeyForProvider; + resolveApiKeyForProvider?: typeof import("../agents/model-auth.js").resolveApiKeyForProviderCore; formatOverview?: SystemAgentOverviewFormatter; loadOverview?: SystemAgentOverviewLoader; createAgent?: typeof import("../agents/agent-create.js").createAgent; diff --git a/src/system-agent/rescue-channel.live.test.ts b/src/system-agent/rescue-channel.live.test.ts index 99b657f6b598..fe5be670a2fa 100644 --- a/src/system-agent/rescue-channel.live.test.ts +++ b/src/system-agent/rescue-channel.live.test.ts @@ -6,7 +6,7 @@ import type { CommandContext } from "../auto-reply/reply/commands-types.js"; import { clearConfigCache } from "../config/config.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resetPluginStateStoreForTests } from "../plugin-state/plugin-state-store.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { deleteTestEnvValue, setTestEnvValue } from "../test-utils/env.js"; import { listSystemAgentAuditEntriesForTests } from "./audit.test-support.js"; import { runSystemAgentRescueMessage } from "./rescue-message.js"; @@ -70,7 +70,7 @@ describeLive("OpenClaw live rescue channel smoke", () => { }); it("handles /openclaw status and a persistent approval roundtrip", async () => { - await withTempDir({ prefix: "openclaw-live-rescue-" }, async (tempDir) => { + await withTestDir({ prefix: "openclaw-live-rescue-" }, async (tempDir) => { const configPath = path.join(tempDir, "openclaw.json"); setTestEnvValue("OPENCLAW_STATE_DIR", tempDir); setTestEnvValue("OPENCLAW_CONFIG_PATH", configPath); diff --git a/src/system-agent/setup-inference-core.ts b/src/system-agent/setup-inference-core.ts index 6d09c1996c6e..370d73480a6f 100644 --- a/src/system-agent/setup-inference-core.ts +++ b/src/system-agent/setup-inference-core.ts @@ -259,7 +259,7 @@ export type ActivateSetupInferenceDeps = { resolveCliAuthBindingFingerprint?: typeof import("../agents/cli-auth-epoch.js").resolveCliAuthBindingFingerprint; resolveCliRuntimeArtifactFingerprint?: typeof import("../agents/cli-auth-epoch.js").resolveCliRuntimeArtifactFingerprint; resolveCliRuntimeOwnerFingerprint?: typeof import("../agents/cli-auth-epoch.js").resolveCliRuntimeOwnerFingerprint; - resolveApiKeyForProvider?: typeof import("../agents/model-auth.js").resolveApiKeyForProvider; + resolveApiKeyForProvider?: typeof import("../agents/model-auth.js").resolveApiKeyForProviderCore; resolvePluginMetadataSnapshot?: typeof import("../plugins/plugin-metadata-snapshot.js").resolvePluginMetadataSnapshot; readCodexCliActiveApiKey?: typeof readCodexCliActiveApiKey; loadPluginRegistrySnapshot?: SystemAgentVerifiedInferenceDeps["loadPluginRegistrySnapshot"]; diff --git a/src/system-agent/transcript-store.test.ts b/src/system-agent/transcript-store.test.ts index 5b74bc23b0e3..eb1ee5accd3a 100644 --- a/src/system-agent/transcript-store.test.ts +++ b/src/system-agent/transcript-store.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it } from "vitest"; import { closeOpenClawStateDatabase } from "../state/openclaw-state-db.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { appendTranscriptTurn, readTranscriptTail } from "./transcript-store.js"; // Mirrors the store's internal retention bound (kept module-local there). @@ -12,7 +12,7 @@ describe("system-agent transcript store", () => { }); it("appends turns and returns a bounded tail oldest-first", async () => { - await withTempDir({ prefix: "openclaw-system-agent-transcript-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-agent-transcript-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; appendTranscriptTurn({ role: "assistant", text: "welcome", at: 1 }, { env }); appendTranscriptTurn({ role: "user", text: "status", at: 2 }, { env }); @@ -28,7 +28,7 @@ describe("system-agent transcript store", () => { }); it("prunes the oldest rows beyond the rolling retention limit", async () => { - await withTempDir({ prefix: "openclaw-system-agent-transcript-prune-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-agent-transcript-prune-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; for (let index = 0; index <= SYSTEM_AGENT_TRANSCRIPT_MAX_ENTRIES; index += 1) { appendTranscriptTurn({ role: "user", text: `turn-${index}`, at: index }, { env }); @@ -42,7 +42,7 @@ describe("system-agent transcript store", () => { }); it("hides reset markers and seeds only turns after a marker within the tail window", async () => { - await withTempDir({ prefix: "openclaw-system-agent-transcript-reset-" }, async (stateDir) => { + await withTestDir({ prefix: "openclaw-system-agent-transcript-reset-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; appendTranscriptTurn({ role: "user", text: "before reset", at: 1 }, { env }); appendTranscriptTurn({ role: "assistant", text: "old answer", at: 2 }, { env }); @@ -65,7 +65,7 @@ describe("system-agent transcript store", () => { }); it("does not let a reset marker older than the requested tail truncate newer turns", async () => { - await withTempDir( + await withTestDir( { prefix: "openclaw-system-agent-transcript-old-reset-" }, async (stateDir) => { const env = { ...process.env, OPENCLAW_STATE_DIR: stateDir }; diff --git a/src/system-agent/verified-inference.ts b/src/system-agent/verified-inference.ts index 277ced0b3d08..1d9bd3709541 100644 --- a/src/system-agent/verified-inference.ts +++ b/src/system-agent/verified-inference.ts @@ -22,7 +22,7 @@ import type { ExpectedAgentHarnessRuntimeArtifact } from "../agents/harness/runt import { resolveAgentHarnessOwnerPluginIds } from "../agents/harness/runtime-plugin.js"; import type { AgentHarnessAuthBindingFingerprintParams } from "../agents/harness/types.js"; import type { ResolvedProviderAuth } from "../agents/model-auth-runtime-shared.js"; -import { resolveApiKeyForProvider } from "../agents/model-auth.js"; +import { resolveApiKeyForProviderCore } from "../agents/model-auth.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { normalizePluginsConfig } from "../plugins/config-state.js"; import { passesManifestOwnerBasePolicy } from "../plugins/manifest-owner-policy.js"; @@ -140,7 +140,7 @@ export type SystemAgentVerifiedInferenceDeps = SystemAgentConfiguredRouteDeps & resolveCliAuthBindingFingerprint?: typeof resolveCliAuthBindingFingerprint; resolveCliRuntimeOwnerFingerprint?: typeof resolveCliRuntimeOwnerFingerprint; resolveCliRuntimeArtifactFingerprint?: typeof resolveCliRuntimeArtifactFingerprint; - resolveApiKeyForProvider?: typeof resolveApiKeyForProvider; + resolveApiKeyForProvider?: typeof resolveApiKeyForProviderCore; validateAgentHarnessRuntimeArtifact?: (params: { harnessId: string; artifact: AgentHarnessRuntimeArtifactBinding; @@ -285,7 +285,7 @@ async function resolveCurrentRuntimeOwnerFingerprint(params: { if (params.kind !== "aws-sdk") { return undefined; } - const resolveAuth = params.deps.resolveApiKeyForProvider ?? resolveApiKeyForProvider; + const resolveAuth = params.deps.resolveApiKeyForProvider ?? resolveApiKeyForProviderCore; const auth = await resolveAuth({ provider: params.route.provider, cfg: params.route.runConfig, @@ -525,7 +525,7 @@ async function resolveCurrentAuthFingerprint(params: { (authCredential?.type === "api_key" && !authCredential.key) || (authCredential?.type === "token" && !authCredential.token); if (needsMaterializedSecret) { - const resolveAuth = params.deps.resolveApiKeyForProvider ?? resolveApiKeyForProvider; + const resolveAuth = params.deps.resolveApiKeyForProvider ?? resolveApiKeyForProviderCore; resolvedAuth = await resolveAuth({ provider: params.route.provider, cfg: params.route.runConfig, @@ -597,7 +597,7 @@ async function resolveCurrentAuthFingerprint(params: { if (!params.modelId || !params.modelApi) { return undefined; } - const resolveAuth = params.deps.resolveApiKeyForProvider ?? resolveApiKeyForProvider; + const resolveAuth = params.deps.resolveApiKeyForProvider ?? resolveApiKeyForProviderCore; const auth = await resolveAuth({ provider: params.route.provider, cfg: params.route.runConfig, @@ -628,7 +628,7 @@ async function resolveCurrentAuthFingerprint(params: { if (!params.modelId || !params.modelApi) { return undefined; } - const resolveAuth = params.deps.resolveApiKeyForProvider ?? resolveApiKeyForProvider; + const resolveAuth = params.deps.resolveApiKeyForProvider ?? resolveApiKeyForProviderCore; const auth = await resolveAuth({ provider: params.route.provider, cfg: params.route.runConfig, diff --git a/src/talk/client-voice-mutation-digest-owner.ts b/src/talk/client-voice-mutation-digest-owner.ts index 810feb4ab30f..c058f4a90fde 100644 --- a/src/talk/client-voice-mutation-digest-owner.ts +++ b/src/talk/client-voice-mutation-digest-owner.ts @@ -55,8 +55,8 @@ export async function deliverClientVoiceMutationDigest( if (!target.channel || target.channel === "webchat" || !target.to) { return; } - const { sendDurableMessageBatch } = await import("../channels/message/runtime.js"); - const send = await sendDurableMessageBatch({ + const { sendDurableMessageBatchCore } = await import("../channels/message/runtime.js"); + const send = await sendDurableMessageBatchCore({ cfg: config, channel: target.channel, to: target.to, diff --git a/src/talk/client-voice-session.digest-retry.test.ts b/src/talk/client-voice-session.digest-retry.test.ts index 7e948efb74f8..9add7ba0407b 100644 --- a/src/talk/client-voice-session.digest-retry.test.ts +++ b/src/talk/client-voice-session.digest-retry.test.ts @@ -26,7 +26,9 @@ const { sendDurableMessageBatch } = vi.hoisted(() => ({ sendDurableMessageBatch: vi.fn(async () => ({ status: "sent" })), })); -vi.mock("../channels/message/runtime.js", () => ({ sendDurableMessageBatch })); +vi.mock("../channels/message/runtime.js", () => ({ + sendDurableMessageBatchCore: sendDurableMessageBatch, +})); const envSnapshot = captureEnv(["OPENCLAW_STATE_DIR"]); let tempDir: string; diff --git a/src/talk/client-voice-session.test.ts b/src/talk/client-voice-session.test.ts index e14415d09df8..f479ec5f725f 100644 --- a/src/talk/client-voice-session.test.ts +++ b/src/talk/client-voice-session.test.ts @@ -55,7 +55,9 @@ vi.mock("../config/sessions/session-accessor.js", async (importOriginal) => { sessionAccessorMocks.appendTranscriptMessage.mockImplementation(actual.appendTranscriptMessage); return { ...actual, appendTranscriptMessage: sessionAccessorMocks.appendTranscriptMessage }; }); -vi.mock("../channels/message/runtime.js", () => ({ sendDurableMessageBatch })); +vi.mock("../channels/message/runtime.js", () => ({ + sendDurableMessageBatchCore: sendDurableMessageBatch, +})); const envSnapshot = captureEnv(["OPENCLAW_STATE_DIR"]); let tempDir: string; diff --git a/src/tasks/cron-run-continuation-cleanup.test.ts b/src/tasks/cron-run-continuation-cleanup.test.ts index 015abf5ec399..ead999fb9954 100644 --- a/src/tasks/cron-run-continuation-cleanup.test.ts +++ b/src/tasks/cron-run-continuation-cleanup.test.ts @@ -10,7 +10,9 @@ const mocks = vi.hoisted(() => ({ })); vi.mock("../config/config.js", () => ({ getRuntimeConfig: () => ({}) })); -vi.mock("../config/sessions/paths.js", () => ({ resolveStorePath: () => "/tmp/sessions.json" })); +vi.mock("../config/sessions/paths.js", () => ({ + resolveSessionStorePathCore: () => "/tmp/sessions.json", +})); vi.mock("../config/sessions/session-accessor.js", () => ({ deleteSessionEntryLifecycle: mocks.deleteEntry, loadSessionEntry: mocks.loadEntry, diff --git a/src/tasks/cron-run-continuation-cleanup.ts b/src/tasks/cron-run-continuation-cleanup.ts index cc93d906f01f..5be4d268aa26 100644 --- a/src/tasks/cron-run-continuation-cleanup.ts +++ b/src/tasks/cron-run-continuation-cleanup.ts @@ -1,6 +1,6 @@ /** Removes an idle exact-run continuation through the session lifecycle owner. */ import { getRuntimeConfig } from "../config/config.js"; -import { resolveStorePath } from "../config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../config/sessions/paths.js"; import { deleteSessionEntryLifecycle, loadSessionEntry, @@ -54,7 +54,7 @@ export async function removeCronRunContinuationSessionIfIdle( } const agentId = resolveAgentIdFromSessionKey(sessionKey); const cfg = getRuntimeConfig(); - const storePath = resolveStorePath(cfg.session?.store, { agentId }); + const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId }); const entry = loadSessionEntry({ agentId, sessionKey, diff --git a/src/tasks/task-registry.maintenance.ts b/src/tasks/task-registry.maintenance.ts index 9ced46864a42..f4cf04e959f6 100644 --- a/src/tasks/task-registry.maintenance.ts +++ b/src/tasks/task-registry.maintenance.ts @@ -12,7 +12,7 @@ import { formatSubagentRecoveryWedgedReason, isSubagentRecoveryWedgedEntry, } from "../agents/subagents/registry/subagent-recovery-state.js"; -import { resolveStorePath } from "../config/sessions.js"; +import { resolveSessionStorePathCore } from "../config/sessions.js"; import type { SessionEntry } from "../config/sessions.js"; import { listSessionEntriesReadOnly, @@ -97,7 +97,7 @@ type TaskRegistryMaintenanceRuntime = { listSessionBindingsBySession?: ReturnType["listBySession"]; unbindSessionBindings?: ReturnType["unbind"]; listSessionEntries: typeof listSessionEntriesReadOnly; - resolveStorePath: typeof resolveStorePath; + resolveStorePath: typeof resolveSessionStorePathCore; deriveSessionChatTypeFromKey?: typeof deriveSessionChatTypeFromKey; isCronJobActive: typeof isCronJobActive; getAgentRunContext: typeof getAgentRunContext; @@ -136,7 +136,7 @@ const defaultTaskRegistryMaintenanceRuntime: TaskRegistryMaintenanceRuntime = { getSessionBindingService().listBySession(sessionKey), unbindSessionBindings: (input) => getSessionBindingService().unbind(input), listSessionEntries: listSessionEntriesReadOnly, - resolveStorePath, + resolveStorePath: resolveSessionStorePathCore, deriveSessionChatTypeFromKey, isCronJobActive, getAgentRunContext, diff --git a/src/tasks/task-registry.test.ts b/src/tasks/task-registry.test.ts index f5ae737c0d37..eb1ff2c6f00e 100644 --- a/src/tasks/task-registry.test.ts +++ b/src/tasks/task-registry.test.ts @@ -21,7 +21,7 @@ import { tryBeginGatewaySuspendAdmission, } from "../process/gateway-work-admission.js"; import type { ParsedAgentSessionKey } from "../routing/session-key.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { withEnvAsync } from "../test-utils/env.js"; import { CRON_TASK_KIND } from "./cron-task-contract.js"; import { SUBAGENT_KILL_TASK_ERROR } from "./detached-task-runtime-contract.js"; @@ -465,7 +465,7 @@ async function withTaskRegistryTempDir( run: (root: string) => Promise, options?: { durableStore?: boolean }, ): Promise { - return await withTempDir({ prefix: "openclaw-task-registry-" }, async (root) => { + return await withTestDir({ prefix: "openclaw-task-registry-" }, async (root) => { return await withEnvAsync({ OPENCLAW_STATE_DIR: root }, async () => { resetTaskRegistryForTests({ persist: false }); resetTaskFlowRegistryForTests({ persist: false }); diff --git a/src/test-helpers/temp-dir.test.ts b/src/test-helpers/temp-dir.test.ts index 4665f09ab646..fb892baa8ae7 100644 --- a/src/test-helpers/temp-dir.test.ts +++ b/src/test-helpers/temp-dir.test.ts @@ -4,7 +4,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; -import { withTempDir, withTempDirSync } from "./temp-dir.js"; +import { withTempDirSync, withTestDir } from "./temp-dir.js"; const parentRoots: string[] = []; @@ -27,11 +27,11 @@ afterEach(async () => { ); }); -describe("withTempDir", () => { +describe("withTestDir", () => { it("removes the cached async prefix root when the case finishes", async () => { const parentDir = await makeParentRoot(); - await withTempDir({ prefix: "openclaw-leak-check-", parentDir }, async (dir) => { + await withTestDir({ prefix: "openclaw-leak-check-", parentDir }, async (dir) => { await fs.writeFile(path.join(dir, "marker.txt"), "ok"); }); @@ -45,12 +45,12 @@ describe("withTempDir", () => { releaseFirst = resolve; }); - const first = withTempDir({ prefix: "openclaw-shared-root-", parentDir }, async (dir) => { + const first = withTestDir({ prefix: "openclaw-shared-root-", parentDir }, async (dir) => { await fs.writeFile(path.join(dir, "first.txt"), "ok"); await firstCanFinish; }); - await withTempDir({ prefix: "openclaw-shared-root-", parentDir }, async (dir) => { + await withTestDir({ prefix: "openclaw-shared-root-", parentDir }, async (dir) => { await fs.writeFile(path.join(dir, "second.txt"), "ok"); await expect(fs.readdir(parentDir)).resolves.toHaveLength(1); }); diff --git a/src/test-helpers/temp-dir.ts b/src/test-helpers/temp-dir.ts index 1c30039cb0fc..86e73a8c6e97 100644 --- a/src/test-helpers/temp-dir.ts +++ b/src/test-helpers/temp-dir.ts @@ -113,7 +113,7 @@ function releaseSyncPrefixRoot(options: { prefix: string; parentDir?: string }) }); } -export async function withTempDir( +export async function withTestDir( options: { prefix: string; parentDir?: string; diff --git a/src/trajectory/cleanup.test.ts b/src/trajectory/cleanup.test.ts index 1c6e6eee86d5..a8a2605be80f 100644 --- a/src/trajectory/cleanup.test.ts +++ b/src/trajectory/cleanup.test.ts @@ -4,7 +4,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { formatSqliteSessionFileMarker } from "../config/sessions/legacy-sqlite-marker.js"; -import { withTempDir } from "../test-helpers/temp-dir.js"; +import { withTestDir } from "../test-helpers/temp-dir.js"; import { removeRemovedSessionTrajectoryArtifacts, removeSessionTrajectoryArtifacts, @@ -46,7 +46,7 @@ async function expectPathMissing(targetPath: string): Promise { describe("trajectory cleanup", () => { it("removes adjacent trajectory sidecars for a deleted session", async () => { - await withTempDir({ prefix: "openclaw-trajectory-cleanup-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-trajectory-cleanup-" }, async (dir) => { const sessionId = "session-1"; const storePath = path.join(dir, "sessions.json"); const sessionFile = path.join(dir, `${sessionId}.jsonl`); @@ -69,7 +69,7 @@ describe("trajectory cleanup", () => { }); it("removes legacy runtime sidecars for SQLite marker sessions", async () => { - await withTempDir({ prefix: "openclaw-trajectory-cleanup-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-trajectory-cleanup-" }, async (dir) => { const sessionId = "session-1"; const storePath = path.join(dir, "sessions.json"); const sessionFile = formatSqliteSessionFileMarker({ @@ -94,7 +94,7 @@ describe("trajectory cleanup", () => { }); it("skips removed sessions still referenced by surviving store rows", async () => { - await withTempDir({ prefix: "openclaw-trajectory-cleanup-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-trajectory-cleanup-" }, async (dir) => { const sessionId = "shared-session"; const storePath = path.join(dir, "sessions.json"); const sessionFile = path.join(dir, `${sessionId}.jsonl`); @@ -117,7 +117,7 @@ describe("trajectory cleanup", () => { }); it("only removes external pointer targets that prove they belong to the session", async () => { - await withTempDir({ prefix: "openclaw-trajectory-cleanup-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-trajectory-cleanup-" }, async (dir) => { const sessionId = "session-2"; const sessionsDir = path.join(dir, "sessions"); const storePath = path.join(sessionsDir, "sessions.json"); @@ -172,7 +172,7 @@ describe("trajectory cleanup", () => { }); it("ignores oversized trajectory pointers while still removing the sidecar", async () => { - await withTempDir({ prefix: "openclaw-trajectory-cleanup-" }, async (dir) => { + await withTestDir({ prefix: "openclaw-trajectory-cleanup-" }, async (dir) => { const sessionId = "session-oversized-pointer"; const sessionsDir = path.join(dir, "sessions"); const storePath = path.join(sessionsDir, "sessions.json"); diff --git a/src/trajectory/cleanup.ts b/src/trajectory/cleanup.ts index 7fe983a882f2..4e703c0ad8fb 100644 --- a/src/trajectory/cleanup.ts +++ b/src/trajectory/cleanup.ts @@ -3,7 +3,7 @@ import fs from "node:fs"; import path from "node:path"; import { isRecord } from "@openclaw/normalization-core/record-coerce"; import { parseSqliteSessionFileMarker } from "../config/sessions/legacy-sqlite-marker.js"; -import { resolveSessionFilePath } from "../config/sessions/paths.js"; +import { resolveSessionFilePathCore } from "../config/sessions/paths.js"; import { readFileWindowFullySync } from "../infra/file-read.js"; import { isPathInside } from "../infra/path-guards.js"; import { readRegularFileSync } from "../infra/regular-file.js"; @@ -146,7 +146,7 @@ function resolveRemovedSessionFile(params: { storePath: string; }): string | null { try { - return resolveSessionFilePath( + return resolveSessionFilePathCore( params.sessionId, params.sessionFile ? { sessionFile: params.sessionFile } : undefined, { sessionsDir: path.dirname(params.storePath) }, diff --git a/src/tts/tts-payload.ts b/src/tts/tts-payload.ts index 9e180e6182ff..1d23f9f2a451 100644 --- a/src/tts/tts-payload.ts +++ b/src/tts/tts-payload.ts @@ -26,7 +26,7 @@ import { resolveTtsSettingsSnapshot, type ResolvedTtsConfig, } from "./tts-settings.js"; -import { textToSpeech, type TtsAudioPersistence } from "./tts-synthesis.js"; +import { textToSpeechCore, type TtsAudioPersistence } from "./tts-synthesis.js"; let lastTtsAttempt: TtsStatusEntry | undefined; @@ -217,7 +217,7 @@ export async function maybeApplyTtsToPayloadCore( } const ttsStart = Date.now(); - const result = await textToSpeech( + const result = await textToSpeechCore( { text: textForAudio, cfg, diff --git a/src/tts/tts-runtime-fallbacks.test.ts b/src/tts/tts-runtime-fallbacks.test.ts index 8a6198544226..6befb97e8b6f 100644 --- a/src/tts/tts-runtime-fallbacks.test.ts +++ b/src/tts/tts-runtime-fallbacks.test.ts @@ -38,7 +38,7 @@ import { const routedPayloads = vi.hoisted(() => [] as ReplyPayload[]); vi.mock("../channels/message/runtime.js", () => ({ - sendDurableMessageBatch: async ({ payloads }: { payloads: ReplyPayload[] }) => { + sendDurableMessageBatchCore: async ({ payloads }: { payloads: ReplyPayload[] }) => { routedPayloads.push(...payloads); return { status: "sent", results: [{ messageId: "tts-route-1" }] }; }, diff --git a/src/tts/tts-runtime.test-support.ts b/src/tts/tts-runtime.test-support.ts index 3cb6d4cf56ac..c8f9458a72f9 100644 --- a/src/tts/tts-runtime.test-support.ts +++ b/src/tts/tts-runtime.test-support.ts @@ -136,7 +136,7 @@ export const { textToSpeechTelephony, } = await import("./runtime-api.js"); export const { maybeApplyTtsToPayloadCore } = await import("./tts-payload.js"); -export const { textToSpeech: textToSpeechCore } = await import("./tts-synthesis.js"); +export const { textToSpeechCore } = await import("./tts-synthesis.js"); export const CODE_HEAVY_SPOKEN_FALLBACK = CODE_HEAVY_SPOKEN_FALLBACK_CORE; export const MAX_TIMER_TIMEOUT_MS = MAX_TIMER_TIMEOUT_MS_CORE; diff --git a/src/tts/tts-synthesis.ts b/src/tts/tts-synthesis.ts index 57d72f41bfd8..b93b515eea93 100644 --- a/src/tts/tts-synthesis.ts +++ b/src/tts/tts-synthesis.ts @@ -78,7 +78,7 @@ export function shouldDeliverTtsAsVoice(params: { return params.voiceCompatible === true || delivery.transcodesAudio === true; } -export async function textToSpeech( +export async function textToSpeechCore( params: { text: string; cfg: OpenClawConfig; diff --git a/src/tts/tts.ts b/src/tts/tts.ts index a46db92a3640..aff64af267bb 100644 --- a/src/tts/tts.ts +++ b/src/tts/tts.ts @@ -7,7 +7,7 @@ import { } from "./runtime-api.js"; import { persistTtsAudioToMediaStore } from "./tts-audio-store.js"; import { maybeApplyTtsToPayloadCore } from "./tts-payload.js"; -import { textToSpeech as textToSpeechCore } from "./tts-synthesis.js"; +import { textToSpeechCore } from "./tts-synthesis.js"; setSpeechRuntimeAvailabilityGuard(() => { assertSecretOwnerAvailable("capability", "tts"); diff --git a/src/tui/components/assistant-message.ts b/src/tui/components/assistant-message.ts index 63cc05777def..f959e43cff86 100644 --- a/src/tui/components/assistant-message.ts +++ b/src/tui/components/assistant-message.ts @@ -1,6 +1,6 @@ // Assistant message component renders assistant responses and spacing in the TUI log. import { Container, Spacer } from "@earendil-works/pi-tui"; -import { markdownTheme, theme } from "../theme/theme.js"; +import { markdownTheme, tuiTheme as theme } from "../theme/theme.js"; import { HyperlinkMarkdown } from "./hyperlink-markdown.js"; export class AssistantMessageComponent extends Container { diff --git a/src/tui/components/btw-inline-message.ts b/src/tui/components/btw-inline-message.ts index f6c20a5c001e..73164ecbe96d 100644 --- a/src/tui/components/btw-inline-message.ts +++ b/src/tui/components/btw-inline-message.ts @@ -1,6 +1,6 @@ // BTW inline message component renders compact aside messages in chat. import { Container, Spacer, Text } from "@earendil-works/pi-tui"; -import { theme } from "../theme/theme.js"; +import { tuiTheme as theme } from "../theme/theme.js"; import { sanitizeRenderableLine, sanitizeRenderableText } from "../tui-formatters.js"; import { AssistantMessageComponent } from "./assistant-message.js"; diff --git a/src/tui/components/chat-log.ts b/src/tui/components/chat-log.ts index ca1aab42d81a..cef956c54e68 100644 --- a/src/tui/components/chat-log.ts +++ b/src/tui/components/chat-log.ts @@ -1,7 +1,7 @@ // Chat log component lays out conversation messages for the TUI viewport. import type { Component } from "@earendil-works/pi-tui"; import { Container, Spacer, Text } from "@earendil-works/pi-tui"; -import { theme } from "../theme/theme.js"; +import { tuiTheme as theme } from "../theme/theme.js"; import { sanitizeRenderableText } from "../tui-formatters.js"; import { AssistantMessageComponent } from "./assistant-message.js"; import { BtwInlineMessage } from "./btw-inline-message.js"; diff --git a/src/tui/components/tool-execution.ts b/src/tui/components/tool-execution.ts index 97c02f169462..7e4493b63416 100644 --- a/src/tui/components/tool-execution.ts +++ b/src/tui/components/tool-execution.ts @@ -2,7 +2,7 @@ import { Box, Container, Spacer, Text, truncateToWidth } from "@earendil-works/pi-tui"; import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice"; import { formatToolDetail, resolveToolDisplay } from "../../agents/tool-display.js"; -import { markdownTheme, theme } from "../theme/theme.js"; +import { markdownTheme, tuiTheme as theme } from "../theme/theme.js"; import * as tuiFormatters from "../tui-formatters.js"; import { HyperlinkMarkdown } from "./hyperlink-markdown.js"; diff --git a/src/tui/components/user-message.ts b/src/tui/components/user-message.ts index a48a0f55b91b..6f6549154567 100644 --- a/src/tui/components/user-message.ts +++ b/src/tui/components/user-message.ts @@ -1,5 +1,5 @@ // User message component renders user-authored chat entries in the TUI log. -import { theme } from "../theme/theme.js"; +import { tuiTheme as theme } from "../theme/theme.js"; import { MarkdownMessageComponent } from "./markdown-message.js"; /** Markdown chat-log row styled as user input. */ diff --git a/src/tui/embedded-backend.test.ts b/src/tui/embedded-backend.test.ts index cf1bbbd76304..15acce40750e 100644 --- a/src/tui/embedded-backend.test.ts +++ b/src/tui/embedded-backend.test.ts @@ -125,7 +125,7 @@ vi.mock("../config/sessions.js", () => ({ goal ? `Goal: ${goal.objective ?? ""}` : "No goal for this session.", getSessionGoal: (...args: unknown[]) => getSessionGoalMock(...args), resolveAgentMainSessionKey: () => "agent:main:main", - resolveStorePath: () => "/tmp/openclaw-sessions.json", + resolveSessionStorePathCore: () => "/tmp/openclaw-sessions.json", updateSessionGoalObjective: (...args: unknown[]) => updateSessionGoalObjectiveMock(...args), updateSessionGoalStatus: (...args: unknown[]) => updateSessionGoalStatusMock(...args), updateSessionStore: (...args: unknown[]) => updateSessionStoreMock(...args), diff --git a/src/tui/theme/theme.test.ts b/src/tui/theme/theme.test.ts index ff85abf2aec6..e4f7eff2966c 100644 --- a/src/tui/theme/theme.test.ts +++ b/src/tui/theme/theme.test.ts @@ -8,7 +8,7 @@ import { afterAll, afterEach, describe, expect, it } from "vitest"; const originalChalkLevel = chalk.level; chalk.level = 3; -const { markdownTheme, searchableSelectListTheme, selectListTheme, theme } = +const { markdownTheme, searchableSelectListTheme, selectListTheme, tuiTheme } = await import("./theme.js"); const stripAnsi = (str: string) => @@ -49,26 +49,26 @@ function colorFromStyle(style: (text: string) => string, layer: 38 | 48): string function readActivePalette(mod: ThemeModule) { return { - text: colorFromStyle(mod.theme.fg, 38), - dim: colorFromStyle(mod.theme.dim, 38), - accent: colorFromStyle(mod.theme.accent, 38), - accentSoft: colorFromStyle(mod.theme.accentSoft, 38), - border: colorFromStyle(mod.theme.border, 38), - userBg: colorFromStyle(mod.theme.userBg, 48), - userText: colorFromStyle(mod.theme.userText, 38), - systemText: colorFromStyle(mod.theme.system, 38), - toolPendingBg: colorFromStyle(mod.theme.toolPendingBg, 48), - toolSuccessBg: colorFromStyle(mod.theme.toolSuccessBg, 48), - toolErrorBg: colorFromStyle(mod.theme.toolErrorBg, 48), - toolTitle: colorFromStyle(mod.theme.toolTitle, 38), - toolOutput: colorFromStyle(mod.theme.toolOutput, 38), + text: colorFromStyle(mod.tuiTheme.fg, 38), + dim: colorFromStyle(mod.tuiTheme.dim, 38), + accent: colorFromStyle(mod.tuiTheme.accent, 38), + accentSoft: colorFromStyle(mod.tuiTheme.accentSoft, 38), + border: colorFromStyle(mod.tuiTheme.border, 38), + userBg: colorFromStyle(mod.tuiTheme.userBg, 48), + userText: colorFromStyle(mod.tuiTheme.userText, 38), + systemText: colorFromStyle(mod.tuiTheme.system, 38), + toolPendingBg: colorFromStyle(mod.tuiTheme.toolPendingBg, 48), + toolSuccessBg: colorFromStyle(mod.tuiTheme.toolSuccessBg, 48), + toolErrorBg: colorFromStyle(mod.tuiTheme.toolErrorBg, 48), + toolTitle: colorFromStyle(mod.tuiTheme.toolTitle, 38), + toolOutput: colorFromStyle(mod.tuiTheme.toolOutput, 38), quote: colorFromStyle(mod.markdownTheme.quote, 38), quoteBorder: colorFromStyle(mod.markdownTheme.quoteBorder, 38), code: colorFromStyle(mod.markdownTheme.code, 38), codeBorder: colorFromStyle(mod.markdownTheme.codeBlockBorder, 38), link: colorFromStyle(mod.markdownTheme.link, 38), - error: colorFromStyle(mod.theme.error, 38), - success: colorFromStyle(mod.theme.success, 38), + error: colorFromStyle(mod.tuiTheme.error, 38), + success: colorFromStyle(mod.tuiTheme.success, 38), }; } @@ -141,8 +141,8 @@ describe("markdownTheme", () => { describe("theme", () => { it("keeps assistant text in terminal default foreground", () => { - expect(theme.assistantText("hello")).toBe("hello"); - expect(stripAnsi(theme.assistantText("hello"))).toBe("hello"); + expect(tuiTheme.assistantText("hello")).toBe("hello"); + expect(stripAnsi(tuiTheme.assistantText("hello"))).toBe("hello"); }); }); @@ -277,8 +277,8 @@ describe("light background detection", () => { it("keeps assistantText as identity in both modes", async () => { const lightMod = await importThemeWithEnv({ OPENCLAW_THEME: "light" }); const darkMod = await importThemeWithEnv({ OPENCLAW_THEME: "dark" }); - expect(lightMod.theme.assistantText("hello")).toBe("hello"); - expect(darkMod.theme.assistantText("hello")).toBe("hello"); + expect(lightMod.tuiTheme.assistantText("hello")).toBe("hello"); + expect(darkMod.tuiTheme.assistantText("hello")).toBe("hello"); }); }); diff --git a/src/tui/theme/theme.ts b/src/tui/theme/theme.ts index 332768e389a6..bc6bd01d352a 100644 --- a/src/tui/theme/theme.ts +++ b/src/tui/theme/theme.ts @@ -146,7 +146,7 @@ function highlightCode(code: string): string[] { return code.split("\n").map((line) => fg(palette.code)(line)); } -export const theme = { +export const tuiTheme = { fg: fg(palette.text), assistantText: (text: string) => text, dim: fg(palette.dim), diff --git a/src/tui/tui-plugin-approvals.ts b/src/tui/tui-plugin-approvals.ts index 8ea559dfeaa5..0b47f8a1f91d 100644 --- a/src/tui/tui-plugin-approvals.ts +++ b/src/tui/tui-plugin-approvals.ts @@ -10,7 +10,7 @@ import { asOptionalObjectRecord } from "@openclaw/normalization-core/record-coer import { isApprovalStaleError } from "../infra/approval-errors.js"; import { formatErrorMessage } from "../infra/errors.js"; import { createTuiRefreshCoalescer } from "./coalesced-refresh.js"; -import { selectListTheme, theme } from "./theme/theme.js"; +import { selectListTheme, tuiTheme as theme } from "./theme/theme.js"; import type { TuiApprovalDecision, TuiBackend, TuiPluginApproval } from "./tui-backend.js"; import { sanitizeRenderableText } from "./tui-formatters.js"; diff --git a/src/tui/tui-task-suggestions.ts b/src/tui/tui-task-suggestions.ts index 97ed717c8f57..d2132eb31566 100644 --- a/src/tui/tui-task-suggestions.ts +++ b/src/tui/tui-task-suggestions.ts @@ -10,7 +10,7 @@ import { asOptionalObjectRecord } from "@openclaw/normalization-core/record-coer import type { TaskSuggestion } from "../../packages/gateway-protocol/src/index.js"; import { formatErrorMessage } from "../infra/errors.js"; import { createTuiRefreshCoalescer } from "./coalesced-refresh.js"; -import { selectListTheme, theme } from "./theme/theme.js"; +import { selectListTheme, tuiTheme as theme } from "./theme/theme.js"; import type { TuiBackend, TuiTaskSuggestionAcceptMode } from "./tui-backend.js"; import { sanitizeRenderableText } from "./tui-formatters.js"; diff --git a/src/tui/tui.ts b/src/tui/tui.ts index c7d2e0fb880e..3adcdb40c51f 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -41,7 +41,7 @@ import { getSlashCommands, shouldSubmitExactArgumentCompletion } from "./command import { ChatLog } from "./components/chat-log.js"; import { CustomEditor } from "./components/custom-editor.js"; import { resolveLocalRunShutdownGraceMs } from "./local-run-shutdown.js"; -import { editorTheme, theme } from "./theme/theme.js"; +import { editorTheme, tuiTheme as theme } from "./theme/theme.js"; import { sanitizeAutocompleteProvider } from "./tui-autocomplete.js"; import type { TuiBackend } from "./tui-backend.js"; import { createCommandHandlers } from "./tui-command-handlers.js"; diff --git a/src/utils.test.ts b/src/utils.test.ts index 202a6bf64a78..df92dfa52473 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import { isAbortError } from "./infra/abort-signal.js"; import { MAX_TIMER_TIMEOUT_MS } from "./shared/number-coercion.js"; -import { withTempDir } from "./test-helpers/temp-dir.js"; +import { withTestDir } from "./test-helpers/temp-dir.js"; import { withEnv } from "./test-utils/env.js"; import { CONFIG_DIR, @@ -21,7 +21,7 @@ import { describe("ensureDir", () => { it("creates nested directory", async () => { - await withTempDir({ prefix: "openclaw-test-" }, async (tmp) => { + await withTestDir({ prefix: "openclaw-test-" }, async (tmp) => { const target = path.join(tmp, "nested", "dir"); await ensureDir(target); expect(fs.existsSync(target)).toBe(true); @@ -135,7 +135,7 @@ describe("normalizeE164", () => { describe("resolveConfigDir", () => { it("prefers ~/.openclaw when legacy dir is missing", async () => { - await withTempDir({ prefix: "openclaw-config-dir-" }, async (root) => { + await withTestDir({ prefix: "openclaw-config-dir-" }, async (root) => { const newDir = path.join(root, ".openclaw"); await fs.promises.mkdir(newDir, { recursive: true }); const resolved = resolveConfigDir({} as NodeJS.ProcessEnv, () => root); @@ -213,7 +213,7 @@ describe("shortenHomePath", () => { it.skipIf(process.platform !== "win32")( "shortens real extended-length Windows home aliases without exposing the absolute path", async () => { - await withTempDir({ prefix: "openclaw-home-display-" }, async (home) => { + await withTestDir({ prefix: "openclaw-home-display-" }, async (home) => { const workspace = path.join(home, "workspace"); await fs.promises.mkdir(workspace); const extendedAlias = `\\\\?\\${workspace.toUpperCase()}`; @@ -254,7 +254,7 @@ describe("shortenHomeInString", () => { it.skipIf(process.platform !== "win32")( "shortens real Windows home casing aliases inside diagnostic text", async () => { - await withTempDir({ prefix: "openclaw-home-display-" }, async (home) => { + await withTestDir({ prefix: "openclaw-home-display-" }, async (home) => { const homeAlias = home.toUpperCase(); expect(fs.statSync(homeAlias).isDirectory()).toBe(true); diff --git a/src/utils.ts b/src/utils.ts index b9a6dd9d127c..a80f3ddd3e9c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -38,7 +38,7 @@ export const clamp = clampNumber; * Safely parse JSON, returning null on error instead of throwing. */ // oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- JSON parsing helper lets callers ascribe the expected payload type. -export function safeParseJson(raw: string): T | null { +export function tryParseJson(raw: string): T | null { try { return JSON.parse(raw) as T; } catch { diff --git a/src/worker/worker.fault-injection.test.ts b/src/worker/worker.fault-injection.test.ts index 1e79b13aa49a..2a0956a36e80 100644 --- a/src/worker/worker.fault-injection.test.ts +++ b/src/worker/worker.fault-injection.test.ts @@ -20,7 +20,7 @@ import { createOperationalRunInstanceRef } from "../agents/admitted-run-context. import { SessionManager } from "../agents/sessions/session-manager.js"; import { resolveSessionTranscriptRuntimeTarget, - upsertSessionEntry, + upsertSessionEntryCore, } from "../config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { @@ -236,7 +236,7 @@ class ComposedGatewayHarness { ); const sessionsDir = path.join(root, "agents", "main", "sessions"); const storePath = path.join(sessionsDir, "sessions.json"); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: SESSION_KEY, storePath }, { sessionId: SESSION_ID, updatedAt: 1 }, ); diff --git a/test/e2e/qa-lab/runtime/gateway-agent-artifact-apis.e2e.test.ts b/test/e2e/qa-lab/runtime/gateway-agent-artifact-apis.e2e.test.ts index ef688ee72f48..b881ee870728 100644 --- a/test/e2e/qa-lab/runtime/gateway-agent-artifact-apis.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/gateway-agent-artifact-apis.e2e.test.ts @@ -4,10 +4,10 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { clearConfigCache, clearRuntimeConfigSnapshot } from "../../../../src/config/config.js"; -import { resolveStorePath } from "../../../../src/config/sessions/paths.js"; +import { resolveSessionStorePathCore } from "../../../../src/config/sessions/paths.js"; import { appendTranscriptMessage, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../../../src/config/sessions/session-accessor.js"; import { clearSessionStoreCacheForTest } from "../../../../src/config/sessions/store-writer-state.js"; import { createManagedOutgoingMediaBlocks } from "../../../../src/gateway/managed-image-attachments.js"; @@ -358,9 +358,9 @@ describe("Gateway agent and artifact APIs", () => { const sessionKey = "agent:main:artifact-api"; const sessionId = "gateway-agent-artifact-session"; const messageId = "gateway-agent-artifact-message"; - const storePath = resolveStorePath(undefined, { agentId: "main" }); + const storePath = resolveSessionStorePathCore(undefined, { agentId: "main" }); const scope = { agentId: "main", sessionId, sessionKey, storePath }; - await upsertSessionEntry(scope, { sessionId, updatedAt: Date.now() }); + await upsertSessionEntryCore(scope, { sessionId, updatedAt: Date.now() }); const task = createTaskRecord({ runtime: "cli", requesterSessionKey: sessionKey, diff --git a/test/e2e/qa-lab/runtime/gateway-usage-memory-apis.e2e.test.ts b/test/e2e/qa-lab/runtime/gateway-usage-memory-apis.e2e.test.ts index 7011db14081c..af32eb9bc65b 100644 --- a/test/e2e/qa-lab/runtime/gateway-usage-memory-apis.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/gateway-usage-memory-apis.e2e.test.ts @@ -8,7 +8,7 @@ import { } from "../../../../src/config/sessions/legacy-sqlite-marker.js"; import { persistSessionTranscriptTurn, - upsertSessionEntry, + upsertSessionEntryCore, } from "../../../../src/config/sessions/session-accessor.js"; import type { OpenClawConfig } from "../../../../src/config/types.openclaw.js"; import { READ_SCOPE } from "../../../../src/gateway/method-scopes.js"; @@ -92,7 +92,7 @@ async function seedCompletedUsageSession(state: OpenClawTestState): Promise<{ storePath, }; - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: FIXTURE_SESSION_ID, sessionFile, startedAt: FIXTURE_STARTED_AT, @@ -125,7 +125,7 @@ async function seedCompletedUsageSession(state: OpenClawTestState): Promise<{ }); expect(turn.appendedCount).toBe(2); - await upsertSessionEntry(scope, { + await upsertSessionEntryCore(scope, { sessionId: FIXTURE_SESSION_ID, sessionFile, startedAt: FIXTURE_STARTED_AT, diff --git a/test/e2e/qa-lab/runtime/session-transcript-cli-product-proof.e2e.test.ts b/test/e2e/qa-lab/runtime/session-transcript-cli-product-proof.e2e.test.ts index 82fac72bbc77..53c037aa3366 100644 --- a/test/e2e/qa-lab/runtime/session-transcript-cli-product-proof.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/session-transcript-cli-product-proof.e2e.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; -import { upsertSessionEntry } from "../../../../src/config/sessions/session-accessor.js"; +import { upsertSessionEntryCore } from "../../../../src/config/sessions/session-accessor.js"; import { closeOpenClawAgentDatabasesForTest } from "../../../../src/state/openclaw-agent-db.js"; import { closeOpenClawStateDatabaseForTest } from "../../../../src/state/openclaw-state-db.js"; import type { @@ -112,7 +112,7 @@ describe("session and transcript child CLI product proof", () => { }); instance.state.applyEnv(); - await upsertSessionEntry( + await upsertSessionEntryCore( { agentId: "main", sessionKey: SESSION_KEY, diff --git a/test/helpers/acp-manager-task-state.ts b/test/helpers/acp-manager-task-state.ts index 423d9300000d..afa5bb4316cd 100644 --- a/test/helpers/acp-manager-task-state.ts +++ b/test/helpers/acp-manager-task-state.ts @@ -5,7 +5,7 @@ import { resetTaskFlowRegistryForTests, resetTaskRegistryForTests, } from "../../src/tasks/task-runtime.test-helpers.js"; -import { withTempDir } from "../../src/test-helpers/temp-dir.js"; +import { withTestDir } from "../../src/test-helpers/temp-dir.js"; import { captureEnv, setTestEnvValue } from "../../src/test-utils/env.js"; import { installInMemoryTaskRegistryRuntime } from "../../src/test-utils/task-registry-runtime.js"; @@ -21,7 +21,7 @@ export function resetAcpManagerTaskStateForTests(): void { export async function withAcpManagerTaskStateDir( run: (root: string) => Promise, ): Promise { - await withTempDir({ prefix: "openclaw-acp-manager-task-" }, async (root) => { + await withTestDir({ prefix: "openclaw-acp-manager-task-" }, async (root) => { const envSnapshot = captureEnv(["OPENCLAW_STATE_DIR"]); setTestEnvValue("OPENCLAW_STATE_DIR", root); resetAcpManagerTaskStateForTests(); diff --git a/test/image-generation.runtime.live.test.ts b/test/image-generation.runtime.live.test.ts index cafd9e3c92ff..1933c75b6428 100644 --- a/test/image-generation.runtime.live.test.ts +++ b/test/image-generation.runtime.live.test.ts @@ -8,7 +8,7 @@ import { resolveDefaultAgentDir } from "../src/agents/agent-scope.js"; import { isBillingErrorMessage } from "../src/agents/failover/classify.js"; import { collectProviderApiKeys } from "../src/agents/live-auth-keys.js"; import { isLiveProfileKeyModeEnabled, isLiveTestEnabled } from "../src/agents/live-test-helpers.js"; -import { resolveApiKeyForProvider } from "../src/agents/model-auth.js"; +import { resolveApiKeyForProviderCore } from "../src/agents/model-auth.js"; import { loadConfig, type OpenClawConfig } from "../src/config/config.js"; import { DEFAULT_LIVE_IMAGE_MODELS, @@ -259,7 +259,7 @@ describeLive("image generation live (provider sweep)", () => { }); let authLabel; try { - const auth = await resolveApiKeyForProvider({ + const auth = await resolveApiKeyForProviderCore({ provider: providerCase.providerId, cfg, agentDir,