From db73b59c041c70e0dee91a18e09ca12f06add255 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 11 Aug 2026 08:24:03 -0700 Subject: [PATCH] refactor: burn wrapper shadowing baseline entries (#122040) * refactor: burn wrapper shadowing entries * chore: refresh wrapper shadowing baselines * test: update secrets runtime state mocks * fix(ci): absorb Control UI build identity variance --- docs/.generated/plugin-sdk-api-baseline.jsonl | 40 ++-- scripts/check-control-ui-performance.mts | 11 +- .../lib/export-name-collision-baseline.json | 7 - scripts/lib/wrapper-shadowing-baseline.json | 153 ------------- src/agents/anthropic.setup-token.live.test.ts | 4 +- src/agents/auth-profiles/oauth.ts | 6 +- src/agents/auth-profiles/profiles.test.ts | 2 +- .../auth-profiles/runtime-snapshots.test.ts | 32 +-- src/agents/auth-profiles/runtime-snapshots.ts | 14 +- src/agents/auth-profiles/source-check.ts | 8 +- src/agents/auth-profiles/store.ts | 16 +- ...h-tools.notify-on-exit-ack.test-support.ts | 4 +- src/agents/btw.test.ts | 2 +- .../compact.hooks.harness.ts | 2 +- .../run.overflow-compaction.harness.ts | 2 +- .../run/auth-controller.test.ts | 42 ++-- .../run/auth-controller.ts | 4 +- src/agents/harness/selection.test.ts | 16 +- .../harness/tool-surface-bridge.test.ts | 2 +- src/agents/harness/tool-surface-bridge.ts | 2 +- src/agents/live-cache-test-support.ts | 4 +- src/agents/model-auth-availability.ts | 4 +- src/agents/model-auth-model.ts | 2 +- src/agents/model-auth.profiles.test.ts | 14 +- src/agents/model-auth.test.ts | 16 +- src/agents/model-auth.ts | 2 +- src/agents/model-suppression.runtime.ts | 6 +- src/agents/model-suppression.test.ts | 8 +- src/agents/model-suppression.ts | 2 +- src/agents/models.profiles.live.test.ts | 8 +- .../openai-reasoning-compat.live.test.ts | 6 +- src/agents/openclaw-tools.ts | 4 +- .../prepared-model-runtime.test-harness.ts | 2 +- src/agents/runtime-plan/prepare-auth.test.ts | 6 +- src/agents/runtime-plan/resolve-auth.ts | 16 +- src/agents/sessions/auth-storage.ts | 6 +- src/agents/simple-completion-runtime.test.ts | 4 +- src/agents/simple-completion-runtime.ts | 6 +- src/agents/tool-replay-repair.live.test.ts | 6 +- ...ol.custom-provider-auth.regression.test.ts | 4 +- src/agents/tools/media-tool-shared.ts | 12 +- src/agents/tools/pdf-tool.test-support.ts | 2 +- src/agents/tools/pdf-tool.test.ts | 4 +- src/agents/tools/tool-runtime.helpers.ts | 2 +- .../tools/web-fetch.provider-fallback.test.ts | 2 +- src/agents/tools/web-search.late-bind.test.ts | 10 +- .../tools/web-tool-runtime-context.test.ts | 10 +- src/agents/tools/web-tool-runtime-context.ts | 4 +- .../tools/web-tools.enabled-defaults.test.ts | 6 +- src/auto-reply/reply/commands-gating.test.ts | 2 +- src/auto-reply/reply/commands-mcp.ts | 4 +- src/auto-reply/reply/queue/enqueue.ts | 4 +- .../reply/reply-admission-ticket.ts | 4 +- .../reply-run-registry.message-injection.ts | 4 +- .../reply/reply-run-registry.operation.ts | 4 +- .../reply/reply-run-registry.state.ts | 4 +- src/channels/streaming.ts | 7 +- src/cli/program/command-registry-core.ts | 4 +- src/cli/program/command-suggestions.ts | 8 +- src/cli/program/core-command-descriptors.ts | 2 +- src/cli/program/register.subclis-core.ts | 9 +- src/cli/program/register.subclis.ts | 2 +- .../program/root-command-descriptions.test.ts | 6 +- src/cli/program/root-help.test.ts | 2 +- src/cli/program/root-help.ts | 4 +- src/cli/program/subcli-descriptors.test.ts | 8 +- src/cli/program/subcli-descriptors.ts | 2 +- src/cli/run-main.ts | 10 +- src/commands/doctor-security.ts | 4 +- src/config/channel-config-metadata.ts | 6 +- src/config/doc-baseline.runtime.ts | 16 +- src/config/mcp-config.ts | 4 +- src/config/redact-snapshot.schema.test.ts | 8 +- src/config/redact-snapshot.test.ts | 18 +- src/config/runtime-schema.ts | 18 +- src/config/schema.test.ts | 26 +-- src/config/schema.ts | 2 +- src/gateway/control-ui.ts | 4 +- .../gateway-models.profiles.live.test.ts | 8 +- src/gateway/node-reapproval-coordinator.ts | 8 +- src/gateway/operator-approvals-client.ts | 4 +- src/gateway/server-aux-handlers.test.ts | 4 +- src/gateway/server-aux-handlers.ts | 12 +- src/gateway/server-kernel.ts | 4 +- src/gateway/server-lifecycle.ts | 4 +- .../server-methods.authorization.test.ts | 14 +- .../server-methods/chat-webchat-media.test.ts | 4 +- .../server-methods/config-write-flow.test.ts | 2 +- .../server-methods/config-write-flow.ts | 4 +- .../server-methods/config.shared-auth.test.ts | 2 +- src/gateway/server-methods/secrets.test.ts | 2 +- src/gateway/server-methods/secrets.ts | 4 +- src/gateway/server-reload-managed-secrets.ts | 32 +-- src/gateway/server-reload-managed.ts | 10 +- .../server-startup-config.secrets.test.ts | 90 ++++---- src/gateway/server-startup-config.ts | 18 +- ...ver-startup-secret-owner-isolation.test.ts | 4 +- .../server-worker-environment-startup.ts | 4 +- .../server-worker-placement-startup.test.ts | 14 +- .../server.sessions.archive-lifecycle.test.ts | 26 +-- ....sessions.archive-worker-placement.test.ts | 6 +- ...er.sessions.reset-hooks.succession.test.ts | 8 +- .../message-handler.worker.test.ts | 6 +- src/gateway/worker-environments/tunnel.ts | 6 +- src/infra/exec-approval-channel-runtime.ts | 4 +- src/infra/net/pinned-dispatcher-pool.test.ts | 4 +- src/logging/diagnostic-session-state.ts | 5 - src/logging/diagnostic-support-export.ts | 4 +- src/logging/diagnostic.test.ts | 17 +- src/logging/diagnostic.ts | 3 +- .../apply.echo-transcript.test.ts | 2 +- .../image-model-runtime.ts | 4 +- .../image.runtime-profile.test.ts | 4 +- .../image.runtime-timeout.test.ts | 2 +- src/media-understanding/image.test.ts | 4 +- src/media/local-media-access.ts | 6 +- src/media/web-media.ts | 4 +- src/plugin-sdk/agent-harness-tool-runtime.ts | 6 +- src/plugin-sdk/allow-from.ts | 30 +-- src/plugin-sdk/channel-core.ts | 6 +- src/plugin-sdk/extension-shared.ts | 4 +- src/plugin-sdk/memory-host-core.ts | 2 +- src/plugin-sdk/memory-host-event-export.ts | 6 +- src/plugin-sdk/web-media.ts | 2 +- .../bundled-channel-config-metadata.ts | 2 +- src/plugins/bundled-plugin-metadata.test.ts | 6 +- src/plugins/bundled-plugin-metadata.ts | 4 +- src/plugins/config-normalization-shared.ts | 25 --- src/plugins/config-state.ts | 31 ++- .../manifest-model-id-normalization.ts | 4 +- src/plugins/manifest-registry.test.ts | 8 +- src/plugins/runtime-state.ts | 4 +- src/plugins/runtime-workspace-state.test.ts | 2 +- src/plugins/runtime-workspace-state.ts | 4 +- src/plugins/runtime/index.test.ts | 2 +- .../runtime-model-auth.runtime.test.ts | 2 +- .../runtime/runtime-model-auth.runtime.ts | 10 +- .../runtime/runtime-web-channel-plugin.ts | 8 +- src/secrets/apply.ts | 7 +- src/secrets/audit.ts | 2 +- src/secrets/runtime-command-secrets.ts | 13 +- src/secrets/runtime-owner-assignments.ts | 8 +- .../runtime-provider-failure-fanout.test.ts | 4 +- src/secrets/runtime-state.test.ts | 204 +++++++++--------- src/secrets/runtime-state.ts | 14 +- src/secrets/runtime-web-tools-state.test.ts | 6 +- src/secrets/runtime-web-tools-state.ts | 2 +- src/secrets/runtime-web-tools.test.ts | 4 +- src/secrets/runtime-web-tools.ts | 6 +- src/secrets/runtime.fast-path.test.ts | 12 +- src/secrets/runtime.test.ts | 4 +- src/secrets/runtime.ts | 10 +- src/secrets/storage-scan.ts | 6 - .../audit-channel-account-metadata.test.ts | 6 +- src/security/audit-channel-dm-policy.test.ts | 34 +-- .../audit-channel-readonly-resolution.test.ts | 6 +- ...udit-channel-source-config-discord.test.ts | 4 +- .../audit-channel-source-config-slack.test.ts | 4 +- src/security/audit-channel.collect.runtime.ts | 6 +- src/security/audit-channel.ts | 2 +- src/shared/deferred.test.ts | 8 +- src/shared/deferred.ts | 2 +- src/shared/pending-request-registry.ts | 4 +- src/skills/loading/skills.test.ts | 12 +- src/skills/runtime/env-overrides.runtime.ts | 6 +- src/skills/runtime/env-overrides.ts | 2 +- src/system-agent/operations-execute.ts | 4 +- src/web-fetch/runtime.ts | 5 +- src/web-search/runtime.ts | 8 +- src/wizard/session.ts | 6 +- test/helpers/promise.ts | 2 +- test/scripts/control-ui-performance.test.ts | 15 +- 172 files changed, 769 insertions(+), 943 deletions(-) diff --git a/docs/.generated/plugin-sdk-api-baseline.jsonl b/docs/.generated/plugin-sdk-api-baseline.jsonl index 8fa7f187b7a7..6edcdf51488a 100644 --- a/docs/.generated/plugin-sdk-api-baseline.jsonl +++ b/docs/.generated/plugin-sdk-api-baseline.jsonl @@ -3,12 +3,12 @@ {"contentHash":"0d6827cdd7180482c49dc67aacc2b512e587d3453536081c15d3febaea2f1492","entrypoint":"account-id","importSpecifier":"openclaw/plugin-sdk/account-id"} {"contentHash":"0b4930a77ab3e63bed21a9651a23fb7130f623baf6ce153166cb80a965b0a004","entrypoint":"account-resolution","importSpecifier":"openclaw/plugin-sdk/account-resolution"} {"contentHash":"0349da0a93dadbdcff03ae8064b5066c5f2e1c28ee298bfa9ab3f1f8563300eb","entrypoint":"agent-config-primitives","importSpecifier":"openclaw/plugin-sdk/agent-config-primitives"} -{"contentHash":"b6c5507d2fc6a0467480a468d1131efba94faef5284b78d2dd3e7b8cb2fd3105","entrypoint":"agent-harness","importSpecifier":"openclaw/plugin-sdk/agent-harness"} -{"contentHash":"ec417e2b535f930d1c9d52e4a64375327913b9b92022c583a2e7f7a1ea7a4b25","entrypoint":"agent-harness-runtime","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime"} +{"contentHash":"bf900891a3c6ee746fdf5033882e8ca88bc03ae0b2c971bb5977c0f0db506506","entrypoint":"agent-harness","importSpecifier":"openclaw/plugin-sdk/agent-harness"} +{"contentHash":"4f8b570b60781e746c19cd370d5c5967ad68047b255e81fd29d0dff95bfc9f3e","entrypoint":"agent-harness-runtime","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime"} {"contentHash":"4387e74b1261f632a0e140276c0f8cbbbe848a11f7ad287325538ebbee3b09da","entrypoint":"agent-media-payload","importSpecifier":"openclaw/plugin-sdk/agent-media-payload"} -{"contentHash":"b065a946eca85d03276bc0f6b1c04af1e44ee4ce441e5a5477f13d13aa3de17b","entrypoint":"agent-runtime","importSpecifier":"openclaw/plugin-sdk/agent-runtime"} +{"contentHash":"6fe3155f10712bc5d001f1f69e502f3e1a0b4179bb5ed7100d0ca7df933085be","entrypoint":"agent-runtime","importSpecifier":"openclaw/plugin-sdk/agent-runtime"} {"contentHash":"1f0e51c8b1a58fff13ab7d19806d97bf07fca61a5c7edeb3437cd805775a8320","entrypoint":"agent-scope-runtime","importSpecifier":"openclaw/plugin-sdk/agent-scope-runtime"} -{"contentHash":"ce460dee97b375e50944f4da2ac95fe4c7a3f7be6b435550f54088b16369ed49","entrypoint":"allow-from","importSpecifier":"openclaw/plugin-sdk/allow-from"} +{"contentHash":"1f45bc7ea08bd3029e9a6bfd723f930324df2e7da79e1bd81bfb02f50c57362a","entrypoint":"allow-from","importSpecifier":"openclaw/plugin-sdk/allow-from"} {"contentHash":"c19f8658d0db60b1d3eddfeae9639ca6558592d7d69c6905ab2994b4a572f25b","entrypoint":"allowlist-config-edit","importSpecifier":"openclaw/plugin-sdk/allowlist-config-edit"} {"contentHash":"2800815b9a0dac84fccd0b946bc938ba31ba3915990b899b0ebce3b80744ed47","entrypoint":"approval-auth-runtime","importSpecifier":"openclaw/plugin-sdk/approval-auth-runtime"} {"contentHash":"6fef2bb5627a1f9de6bad01c0ba0d15a4fb64a0f3310c26f1c2cbce67088516c","entrypoint":"approval-client-runtime","importSpecifier":"openclaw/plugin-sdk/approval-client-runtime"} @@ -26,19 +26,19 @@ {"contentHash":"222019df4c06aca4e9bbede0fdea4b946f9b024a1524e3837c3c13be6d3d9e6a","entrypoint":"channel-config-primitives","importSpecifier":"openclaw/plugin-sdk/channel-config-primitives"} {"contentHash":"645ec473e40a04e4cc627d05c9156f66d835eb67dab32166f745f66eadc98880","entrypoint":"channel-config-schema","importSpecifier":"openclaw/plugin-sdk/channel-config-schema"} {"contentHash":"75935c1d837265d191436d43bbfe24c6328b35a45a68cad250c157923badf857","entrypoint":"channel-contract","importSpecifier":"openclaw/plugin-sdk/channel-contract"} -{"contentHash":"d1d17e04a95ebb93d4213caa49e214b9da55163465474c33a42cfab27dd855ee","entrypoint":"channel-core","importSpecifier":"openclaw/plugin-sdk/channel-core"} +{"contentHash":"28666b0cf5d0bdf28648baffffcf1239e4d27727408c785fa06f5c271fc0843a","entrypoint":"channel-core","importSpecifier":"openclaw/plugin-sdk/channel-core"} {"contentHash":"b2d90f161ca88ef2d5ff35f87f160c772bd784a4ebda91b2ab33bf16b6abd843","entrypoint":"channel-dm-policy","importSpecifier":"openclaw/plugin-sdk/channel-dm-policy"} -{"contentHash":"571b88343e5e7028d2dc8a414d3c7c6cfc26c4fe72c296258e615a308fc1b6d5","entrypoint":"channel-entry-contract","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract"} +{"contentHash":"49a549e6a39d55279ecafe650e7e00496ebd6da5266cb11e486f5ee19f53b4c1","entrypoint":"channel-entry-contract","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract"} {"contentHash":"8bc8b784c78a70ee58731aae0be9731c9af01705db00acbafa25de9745a23601","entrypoint":"channel-feedback","importSpecifier":"openclaw/plugin-sdk/channel-feedback"} -{"contentHash":"6da95ea5bb2850b714f47387e1e970b761ac64d02e40531e797d0584e2c6434d","entrypoint":"channel-inbound","importSpecifier":"openclaw/plugin-sdk/channel-inbound"} +{"contentHash":"99cee44d09759fdbb01bc1b50e430933c61c745ec3c7880ea7e2aa63b382c673","entrypoint":"channel-inbound","importSpecifier":"openclaw/plugin-sdk/channel-inbound"} {"contentHash":"87ba6e4e9f2f56621f6d52cc262e8b80fada5e338e841439b4d20983e22dfb82","entrypoint":"channel-inbound-debounce","importSpecifier":"openclaw/plugin-sdk/channel-inbound-debounce"} {"contentHash":"7a69b23fdd7b631ed407d9ae4af55df192d93b61488c81626f041d4199760d95","entrypoint":"channel-ingress-runtime","importSpecifier":"openclaw/plugin-sdk/channel-ingress-runtime"} {"contentHash":"85afc43170761c8d7d6d2d91401b0ad033189636050e8cf85d22132e4aba9f1a","entrypoint":"channel-lifecycle","importSpecifier":"openclaw/plugin-sdk/channel-lifecycle"} {"contentHash":"be803c0341214a8e731f98cb7bf98654410ab1e0254d90f9f1ad9f442f70669f","entrypoint":"channel-logging","importSpecifier":"openclaw/plugin-sdk/channel-logging"} -{"contentHash":"97e407fc8124084452fee297707490622f529565f4ca6f7dbc306b189db429ab","entrypoint":"channel-message","importSpecifier":"openclaw/plugin-sdk/channel-message"} -{"contentHash":"37a8bcb4eadeb02a4b71ebfa56e0a3d97efd52dd63f066a29766880abcb01146","entrypoint":"channel-outbound","importSpecifier":"openclaw/plugin-sdk/channel-outbound"} +{"contentHash":"970ba9dd27f5e4d6a09b0ffec37264cdb3f745a5934e2cb8cc53eee78f2c176a","entrypoint":"channel-message","importSpecifier":"openclaw/plugin-sdk/channel-message"} +{"contentHash":"574fa42cccdf71a38cb19a0662dacbcbd94486069f01fda496e950384897ddbf","entrypoint":"channel-outbound","importSpecifier":"openclaw/plugin-sdk/channel-outbound"} {"contentHash":"fee97710da33e100a8987aed897be17ab3ec4ece9fa09b7eb871a90461c8fca3","entrypoint":"channel-pairing","importSpecifier":"openclaw/plugin-sdk/channel-pairing"} -{"contentHash":"1c135098d104d0b73b95b3fc41ba2c27e6eca836cbb16e21601452219d4d8099","entrypoint":"channel-plugin-common","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common"} +{"contentHash":"897b21d2b152cacd699378eabe938caaee71171ec4002a3dd6dd42efabcda0f7","entrypoint":"channel-plugin-common","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common"} {"contentHash":"350b4986aec115e3674c9dd3712302e7c185dbefc534b65a98a6a84564e65e05","entrypoint":"channel-policy","importSpecifier":"openclaw/plugin-sdk/channel-policy"} {"contentHash":"369721c067caf33fc86e50c7dd5aec3e8371d2e97e2e3c4305751b79da9e7387","entrypoint":"channel-reply-pipeline","importSpecifier":"openclaw/plugin-sdk/channel-reply-pipeline"} {"contentHash":"5181d3f1c6936e1e13a7d3ab4b904f5ae929a1985480c130915710a9bf8e8a25","entrypoint":"channel-runtime-context","importSpecifier":"openclaw/plugin-sdk/channel-runtime-context"} @@ -60,12 +60,12 @@ {"contentHash":"1deb735b023442072fe21294cf4f94af0e44356fd8591ed6a8a9befafc7c3d67","entrypoint":"config-mutation","importSpecifier":"openclaw/plugin-sdk/config-mutation"} {"contentHash":"40df7e972484ae28718fef1095b2b44cb2cc9eadd35464bf28d4ae0d0f0f66b5","entrypoint":"config-runtime","importSpecifier":"openclaw/plugin-sdk/config-runtime"} {"contentHash":"7a5a2f4d3e5e18d211480286d125419ecbf8175bb6f8eeee9a6ee105df03cb3f","entrypoint":"conversation-runtime","importSpecifier":"openclaw/plugin-sdk/conversation-runtime"} -{"contentHash":"ace1613bc3d78ac0daeb15bd9c2c048e5916ac2279095813e72b5ddfcfe68e60","entrypoint":"core","importSpecifier":"openclaw/plugin-sdk/core"} +{"contentHash":"db165d4f6bdb943dae0cb3015cafcb6d94d2482fff712eda98eb5442d418b095","entrypoint":"core","importSpecifier":"openclaw/plugin-sdk/core"} {"contentHash":"184f6a3849b361adfc9db756699787e934e274f05eeedcda04cd2b7cb4f9fe5e","entrypoint":"dedupe-runtime","importSpecifier":"openclaw/plugin-sdk/dedupe-runtime"} {"contentHash":"8ba476d7f0411071797cd4362f3a71720d9d008d54107b2cb4eccb4a4eb0db0a","entrypoint":"device-bootstrap","importSpecifier":"openclaw/plugin-sdk/device-bootstrap"} {"contentHash":"cb6fbd1612e60755c7528a5243cec69c9123904297d2e16a9c716485785be532","entrypoint":"diagnostic-runtime","importSpecifier":"openclaw/plugin-sdk/diagnostic-runtime"} {"contentHash":"f7b6949b179827646d4e047ce4f181550ca8d67bd4537441405d36fb79438556","entrypoint":"directory-runtime","importSpecifier":"openclaw/plugin-sdk/directory-runtime"} -{"contentHash":"ef6120fc7033025caad534b2ab4edbdffde2088e0019654fe8334c9e012917b6","entrypoint":"discord","importSpecifier":"openclaw/plugin-sdk/discord"} +{"contentHash":"4b32922587dab4f465f85b7fea02e3b64d89ebea73a496501aa3c59fa7ef6337","entrypoint":"discord","importSpecifier":"openclaw/plugin-sdk/discord"} {"contentHash":"e0523b4936d7c7ec043265de9e2a8f6289d32dacba0a28c3999ba3a0975dfc21","entrypoint":"error-runtime","importSpecifier":"openclaw/plugin-sdk/error-runtime"} {"contentHash":"5125d71bf8c4aeca728c4b8c73b2984247ba7bcc088a45ee29692ff64e3ebf90","entrypoint":"extension-shared","importSpecifier":"openclaw/plugin-sdk/extension-shared"} {"contentHash":"cbeb81159a1e558846f3cf9eecf3fd5a84858029cfdd9ccf237af428e35e755c","entrypoint":"gateway-method-runtime","importSpecifier":"openclaw/plugin-sdk/gateway-method-runtime"} @@ -75,7 +75,7 @@ {"contentHash":"273ebd4fcac3041e3a136410fa396b9704c73eedb66414723996127ed80db8f3","entrypoint":"hook-runtime","importSpecifier":"openclaw/plugin-sdk/hook-runtime"} {"contentHash":"a6537713e63d101897ef9ddfeaae438a50645e2672be5bdced887c4e8a651b96","entrypoint":"inbound-envelope","importSpecifier":"openclaw/plugin-sdk/inbound-envelope"} {"contentHash":"931a52a774750ab895236a8fa7851781ad0279cb5678b1a78e81af9dd25bb3a4","entrypoint":"inbound-event-delivery","importSpecifier":"openclaw/plugin-sdk/inbound-event-delivery"} -{"contentHash":"699bafa75513e6fffb80ecfc0ccc3f0746fdf291f570e128a1ddfa09c2fac97d","entrypoint":"inbound-reply-dispatch","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch"} +{"contentHash":"c1acc4ca962cb62ae6036308b6d90c2e018420cf8b0f12594b2b876f5a5b0ca2","entrypoint":"inbound-reply-dispatch","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch"} {"contentHash":"b590647f65a2e148ba7a1bc207f70adbd3a57990b41d3bf6b10f61ee8f27ab87","entrypoint":"infra-runtime","importSpecifier":"openclaw/plugin-sdk/infra-runtime"} {"contentHash":"41a1f24a1b745f833d44e622ae1922c9dc66ee5de56f0e967ec1a3f9e03a2d18","entrypoint":"ingress-effect-once","importSpecifier":"openclaw/plugin-sdk/ingress-effect-once"} {"contentHash":"57f79c6e8db72b2806bc8c016066682de71bf690378ccb4403b8da6dd3d38b28","entrypoint":"interactive-runtime","importSpecifier":"openclaw/plugin-sdk/interactive-runtime"} @@ -89,7 +89,7 @@ {"contentHash":"0df97720dfd7b1a5892bbf3d46bc64297468af9b8cbb8720ce0d80d42ff2e59d","entrypoint":"media-store","importSpecifier":"openclaw/plugin-sdk/media-store"} {"contentHash":"684c061bb0d1112872c9b2d5a00e258d14b4ff8649a26dd0cc67995352d8ca8b","entrypoint":"media-understanding","importSpecifier":"openclaw/plugin-sdk/media-understanding"} {"contentHash":"f48e34067ab9b25a232af71d8ce17f9cbf9165ebce0832deaae3c1d9c9aeaab7","entrypoint":"media-understanding-runtime","importSpecifier":"openclaw/plugin-sdk/media-understanding-runtime"} -{"contentHash":"98583ac36cd86b8533f323c2cdb76deb3803b23e10e2fc2cfc177ed7f47d6522","entrypoint":"meeting-runtime","importSpecifier":"openclaw/plugin-sdk/meeting-runtime"} +{"contentHash":"18782530d1c5bb66e086dd0177eb8ee52d152805aa68932a945fbc35178c3b03","entrypoint":"meeting-runtime","importSpecifier":"openclaw/plugin-sdk/meeting-runtime"} {"contentHash":"27509d9bbabc547662a45bf258fa432d1e0c3c3f7c22051858d8a67375486e79","entrypoint":"memory-core-host-engine-foundation","importSpecifier":"openclaw/plugin-sdk/memory-core-host-engine-foundation"} {"contentHash":"ad875662e95df74808357c234ac36825d1ff729165da941180061591525ff47f","entrypoint":"memory-host-core","importSpecifier":"openclaw/plugin-sdk/memory-host-core"} {"contentHash":"0f140018c2c21965b6a8f7a2ac1882babcaf70e733b477bb693e498641629938","entrypoint":"messaging-targets","importSpecifier":"openclaw/plugin-sdk/messaging-targets"} @@ -101,10 +101,10 @@ {"contentHash":"3b2106094ccada38a6736f39abbb5814e776638cdb7f1f6d065576e6f8a2d3b6","entrypoint":"persistent-dedupe","importSpecifier":"openclaw/plugin-sdk/persistent-dedupe"} {"contentHash":"e43fcfe8f88cf8d338c3e3496f3bdd6ec14afc1fcf341e171f7758f5c0190d5b","entrypoint":"plugin-command-runtime","importSpecifier":"openclaw/plugin-sdk/plugin-command-runtime"} {"contentHash":"756175b7ff16cd8456b3c85a87466f077476a9236e098c5545ea8ef31fcff0d2","entrypoint":"plugin-config-runtime","importSpecifier":"openclaw/plugin-sdk/plugin-config-runtime"} -{"contentHash":"e898b4f42be94df7004177ecfe78d9f07eb5ea7156563f9c3d998ce1a9109d67","entrypoint":"plugin-entry","importSpecifier":"openclaw/plugin-sdk/plugin-entry"} -{"contentHash":"998e819896710d288c7eb70eda60d0b2a6e78ad5a196abd67df6644178f73daf","entrypoint":"plugin-runtime","importSpecifier":"openclaw/plugin-sdk/plugin-runtime"} +{"contentHash":"3f92b4ce6954e9bbe85a7675f7ad000e01e6b34049523a9a3e38d23966ad959a","entrypoint":"plugin-entry","importSpecifier":"openclaw/plugin-sdk/plugin-entry"} +{"contentHash":"60b7d6caebf369d35a367e431f77eb3f7e5437870ef1da5f024ea8c71217b1cb","entrypoint":"plugin-runtime","importSpecifier":"openclaw/plugin-sdk/plugin-runtime"} {"contentHash":"d5d21f2a0883eac72d6642d93306c36e2de0f3851286eb3edbe3a2d9d6122dcc","entrypoint":"provider-auth","importSpecifier":"openclaw/plugin-sdk/provider-auth"} -{"contentHash":"62853769aed0937975c704dfef003559b5e9a48ae809cc5f0a1e53632ba17ff7","entrypoint":"provider-catalog-runtime","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime"} +{"contentHash":"36aad63a3fda1116df864fcdaa3c26d2c0d7af809603d3d4afaf96dcccdd8391","entrypoint":"provider-catalog-runtime","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime"} {"contentHash":"aee2a235e7218ed4ba36e80e0a6d67ceb3b5de448f25ebc10d6f70552dc35b8a","entrypoint":"proxy-capture","importSpecifier":"openclaw/plugin-sdk/proxy-capture"} {"contentHash":"55bc0be5ce1d700d8b671efccb69ec14a3c942535c2f77d3ed7ca87809dc1ad9","entrypoint":"question-gateway-runtime","importSpecifier":"openclaw/plugin-sdk/question-gateway-runtime"} {"contentHash":"5263836c817c9391fd5a0efdda50552330dd01e4d6bc9cb4ebba58627c48f098","entrypoint":"reply-chunking","importSpecifier":"openclaw/plugin-sdk/reply-chunking"} @@ -142,11 +142,11 @@ {"contentHash":"09957526abf9619fd2ef095119a7195f2bebf2d8e8b1a5605aee0868152f8b5c","entrypoint":"temp-path","importSpecifier":"openclaw/plugin-sdk/temp-path"} {"contentHash":"4519df78f420290da1fd755f7e4a7de7d09dbc934b3e17fc139aaf64896ffa06","entrypoint":"text-chunking","importSpecifier":"openclaw/plugin-sdk/text-chunking"} {"contentHash":"c5f29e3b48cb9a92111bf565f739875a32fc3e19a009dd0bab3c27bbb71f7583","entrypoint":"text-runtime","importSpecifier":"openclaw/plugin-sdk/text-runtime"} -{"contentHash":"4ce582ca52fde9a7281dcdd3b4b2d3ade5c248a524ceac6049a5f3868da8d81a","entrypoint":"tool-plugin","importSpecifier":"openclaw/plugin-sdk/tool-plugin"} +{"contentHash":"39bc2bdca81266a16eab23271664ad8cbffdf2fc9b7f0f1d04c98c2611c53c5d","entrypoint":"tool-plugin","importSpecifier":"openclaw/plugin-sdk/tool-plugin"} {"contentHash":"17e58e27b06e2470908acd719f64ec2add1f117c1882adad79eb5a3b5bd74312","entrypoint":"tool-results","importSpecifier":"openclaw/plugin-sdk/tool-results"} {"contentHash":"aabfb4eacce1bd5190b2f92189332abe7bbe3a5cea3655babe66bf1909206984","entrypoint":"tool-send","importSpecifier":"openclaw/plugin-sdk/tool-send"} -{"contentHash":"8d432f42395afae27f4a95c897dfca0bf5df6289c0846d326df424ad5f6b491b","entrypoint":"web-media","importSpecifier":"openclaw/plugin-sdk/web-media"} -{"contentHash":"e16eaf86f2bc171afd654f94fdfac5a396ef1371109f0c399fe607345e6de1cf","entrypoint":"webhook-ingress","importSpecifier":"openclaw/plugin-sdk/webhook-ingress"} +{"contentHash":"fa8effcbdf093c93d07526b1655ed5941fd277e484a55e680ad84874a8323867","entrypoint":"web-media","importSpecifier":"openclaw/plugin-sdk/web-media"} +{"contentHash":"1600c91e3364cf9bd9cceaec9403f3c96503a145620e18a30502af3b53571e4c","entrypoint":"webhook-ingress","importSpecifier":"openclaw/plugin-sdk/webhook-ingress"} {"contentHash":"1fe8314d4dfa3d7b536f39ac5622ff688f6a20fa6ae8b8fa4770d4851e85a400","entrypoint":"webhook-request-guards","importSpecifier":"openclaw/plugin-sdk/webhook-request-guards"} {"contentHash":"6091f4048c4464103dfdf659904c6afa6e29c483b9a05c502ab3333df08f942b","entrypoint":"widget-html","importSpecifier":"openclaw/plugin-sdk/widget-html"} {"contentHash":"eafdc7277066acf893bfef9b635689c1a69d6d5af22ad8cee2b47351d9b53ca6","entrypoint":"zod","importSpecifier":"openclaw/plugin-sdk/zod"} diff --git a/scripts/check-control-ui-performance.mts b/scripts/check-control-ui-performance.mts index 55c9a5b5a636..c53218ca4edc 100644 --- a/scripts/check-control-ui-performance.mts +++ b/scripts/check-control-ui-performance.mts @@ -23,6 +23,10 @@ const DEFAULT_STARTUP_BUDGET_BASELINE_PATH = path.resolve( // fixed JS ceiling bounds cumulative creep. export const CONTROL_UI_STARTUP_JS_GZIP_TOLERANCE_BYTES = 1024; +// The startup bundle embeds commit SHA and timestamp identity. Those fixed-length, +// high-entropy values move Linux gzip output by tens of bytes between identical builds. +export const CONTROL_UI_STARTUP_JS_GZIP_IDENTITY_VARIANCE_BYTES = 64; + // Small, explicit headroom over the optimized baseline. Budget changes should // accompany an intentional loading or chunking decision. const controlUiPerformanceBudgets = { @@ -146,10 +150,13 @@ export function evaluateControlUiPerformanceBudgets( startupBudgetBaseline: Readonly | null = null, startupJsTolerance = CONTROL_UI_STARTUP_JS_GZIP_TOLERANCE_BYTES, ) { + const startupJsFixedLimit = + budgets.startupJsGzipBytes + + (startupBudgetBaseline ? CONTROL_UI_STARTUP_JS_GZIP_IDENTITY_VARIANCE_BYTES : 0); const checks: Array<[string, number, number, "count" | "bytes"]> = [ ["startup JS requests", metrics.startup.js.requests, budgets.startupJsRequests, "count"], ["startup CSS requests", metrics.startup.css.requests, budgets.startupCssRequests, "count"], - ["startup JS gzip", metrics.startup.js.gzipBytes, budgets.startupJsGzipBytes, "bytes"], + ["startup JS gzip", metrics.startup.js.gzipBytes, startupJsFixedLimit, "bytes"], ["startup CSS gzip", metrics.startup.css.gzipBytes, budgets.startupCssGzipBytes, "bytes"], ["largest JS gzip", metrics.largest.js.gzipBytes, budgets.largestJsGzipBytes, "bytes"], ["largest CSS gzip", metrics.largest.css.gzipBytes, budgets.largestCssGzipBytes, "bytes"], @@ -226,7 +233,7 @@ export function formatControlUiPerformanceReport( ]; if (startupBudgetBaseline) { lines.push( - ` startup JS gzip vs baseline: ${metrics.startup.js.gzipBytes} B (baseline ${startupBudgetBaseline.startupJsGzipBytes} B + tolerance ${startupJsTolerance} B, ceiling ${budgets.startupJsGzipBytes} B)`, + ` startup JS gzip vs baseline: ${metrics.startup.js.gzipBytes} B (baseline ${startupBudgetBaseline.startupJsGzipBytes} B + tolerance ${startupJsTolerance} B, ceiling ${budgets.startupJsGzipBytes} B + build-identity variance ${CONTROL_UI_STARTUP_JS_GZIP_IDENTITY_VARIANCE_BYTES} B)`, ); } lines.push( diff --git a/scripts/lib/export-name-collision-baseline.json b/scripts/lib/export-name-collision-baseline.json index 49f198224619..a76ae4655d68 100644 --- a/scripts/lib/export-name-collision-baseline.json +++ b/scripts/lib/export-name-collision-baseline.json @@ -7,13 +7,6 @@ ], "sdk": true }, - { - "name": "clearSecretsRuntimeSnapshot", - "files": [ - "src/secrets/runtime-state.ts", - "src/secrets/runtime.ts" - ] - }, { "name": "createPluginLoaderLogger", "files": [ diff --git a/scripts/lib/wrapper-shadowing-baseline.json b/scripts/lib/wrapper-shadowing-baseline.json index ab560c2a7e92..df4926fdbd70 100644 --- a/scripts/lib/wrapper-shadowing-baseline.json +++ b/scripts/lib/wrapper-shadowing-baseline.json @@ -1,157 +1,4 @@ [ - { - "name": "buildConfigSchema", - "wrapped": "src/config/schema.ts", - "wrapper": "src/config/doc-baseline.runtime.ts" - }, - { - "name": "buildShouldSuppressBuiltInModel", - "wrapped": "src/agents/model-suppression.ts", - "wrapper": "src/agents/model-suppression.runtime.ts" - }, - { - "name": "clearRuntimeAuthProfileStoreSnapshot", - "wrapped": "src/agents/auth-profiles/runtime-snapshots.ts", - "wrapper": "src/agents/auth-profiles/store.ts" - }, - { - "name": "clearSecretsRuntimeSnapshot", - "wrapped": "src/secrets/runtime-state.ts", - "wrapper": "src/secrets/runtime.ts" - }, - { - "name": "collectBundledChannelConfigs", - "wrapped": "src/plugins/bundled-channel-config-metadata.ts", - "wrapper": "src/config/doc-baseline.runtime.ts" - }, - { - "name": "collectChannelSchemaMetadata", - "wrapped": "src/config/channel-config-metadata.ts", - "wrapper": "src/config/doc-baseline.runtime.ts" - }, - { - "name": "collectChannelSecurityFindings", - "wrapped": "src/security/audit-channel.ts", - "wrapper": "src/security/audit-channel.collect.runtime.ts" - }, - { - "name": "collectPluginSchemaMetadata", - "wrapped": "src/config/channel-config-metadata.ts", - "wrapper": "src/config/doc-baseline.runtime.ts" - }, - { - "name": "createAgentHarnessToolSurfaceRuntime", - "wrapped": "src/agents/harness/tool-surface-bridge.ts", - "wrapper": "src/plugin-sdk/agent-harness-tool-runtime.ts" - }, - { - "name": "createChannelPluginBase", - "wrapped": "src/plugin-sdk/core.ts", - "wrapper": "src/plugin-sdk/channel-core.ts" - }, - { - "name": "createDeferred", - "wrapped": "src/shared/deferred.ts", - "wrapper": "src/plugin-sdk/extension-shared.ts" - }, - { - "name": "DEFAULT_PROGRESS_DRAFT_LABELS", - "wrapped": "src/shared/progress-labels.ts", - "wrapper": "src/channels/streaming.ts" - }, - { - "name": "getActivePluginRegistryWorkspaceDirFromState", - "wrapped": "src/plugins/runtime-workspace-state.ts", - "wrapper": "src/plugins/runtime-state.ts" - }, - { - "name": "getActiveRuntimeWebToolsMetadata", - "wrapped": "src/secrets/runtime-web-tools-state.ts", - "wrapper": "src/secrets/runtime.ts" - }, - { - "name": "getActiveSecretsRuntimeEnv", - "wrapped": "src/secrets/runtime-state.ts", - "wrapper": "src/secrets/runtime.ts" - }, - { - "name": "getActiveSecretsRuntimeSnapshotRevision", - "wrapped": "src/secrets/runtime-state.ts", - "wrapper": "src/secrets/runtime.ts" - }, - { - "name": "getActiveSecretsRuntimeSnapshot", - "wrapped": "src/secrets/runtime-state.ts", - "wrapper": "src/secrets/runtime.ts" - }, - { - "name": "getActiveSkillEnvKeys", - "wrapped": "src/skills/runtime/env-overrides.ts", - "wrapper": "src/skills/runtime/env-overrides.runtime.ts" - }, - { - "name": "getApiKeyForModel", - "wrapped": "src/agents/model-auth-model.ts", - "wrapper": "src/plugins/runtime/runtime-model-auth.runtime.ts", - "via": "src/agents/model-auth.ts" - }, - { - "name": "getCoreCliCommandNames", - "wrapped": "src/cli/program/core-command-descriptors.ts", - "wrapper": "src/cli/program/command-registry-core.ts" - }, - { - "name": "getDefaultLocalRoots", - "wrapped": "src/media/local-media-access.ts", - "wrapper": "src/plugins/runtime/runtime-web-channel-plugin.ts", - "via": "src/media/web-media.ts" - }, - { - "name": "getDiagnosticSessionStateCountForTest", - "wrapped": "src/logging/diagnostic-session-state.ts", - "wrapper": "src/logging/diagnostic.ts" - }, - { - "name": "getPreparedRuntimeAuthProfileStoreSnapshot", - "wrapped": "src/agents/auth-profiles/runtime-snapshots.ts", - "wrapper": "src/agents/auth-profiles/store.ts" - }, - { - "name": "getRuntimeAuthProfileStoreSnapshot", - "wrapped": "src/agents/auth-profiles/runtime-snapshots.ts", - "wrapper": "src/agents/auth-profiles/store.ts" - }, - { - "name": "getSubCliEntries", - "wrapped": "src/cli/program/subcli-descriptors.ts", - "wrapper": "src/cli/program/register.subclis-core.ts" - }, - { - "name": "hasExplicitPluginConfig", - "wrapped": "src/plugins/config-normalization-shared.ts", - "wrapper": "src/plugins/config-state.ts" - }, - { - "name": "isAllowedParsedChatSender", - "wrapped": "src/channels/plugins/chat-target-prefixes.ts", - "wrapper": "src/plugin-sdk/allow-from.ts" - }, - { - "name": "isBundledChannelEnabledByChannelConfig", - "wrapped": "src/plugins/config-normalization-shared.ts", - "wrapper": "src/plugins/config-state.ts" - }, - { - "name": "isMissingPathError", - "wrapped": "src/infra/errno.ts", - "wrapper": "src/plugin-sdk/memory-host-event-export.ts", - "via": "src/infra/errors.ts" - }, - { - "name": "listAuthProfileStoreAgentDirs", - "wrapped": "src/secrets/auth-store-paths.ts", - "wrapper": "src/secrets/storage-scan.ts" - }, { "name": "loadBundledPluginPublicSurfaceModuleSync", "wrapped": "src/plugin-sdk/facade-loader.ts", diff --git a/src/agents/anthropic.setup-token.live.test.ts b/src/agents/anthropic.setup-token.live.test.ts index f53beebd0a31..c3989ca92cc5 100644 --- a/src/agents/anthropic.setup-token.live.test.ts +++ b/src/agents/anthropic.setup-token.live.test.ts @@ -18,7 +18,7 @@ import { saveAuthProfileStore, } from "./auth-profiles.js"; import { isLiveTestEnabled, readLiveTestConfig } from "./live-test-helpers.js"; -import { getApiKeyForModel, requireApiKey } from "./model-auth.js"; +import { getApiKeyForModelCore, requireApiKey } from "./model-auth.js"; import { normalizeProviderId, parseModelRef } from "./model-selection.js"; import { ensureOpenClawModelsJson } from "./models-config.js"; @@ -205,7 +205,7 @@ describeLive("live anthropic setup-token", () => { ); } - const apiKeyInfo = await getApiKeyForModel({ + const apiKeyInfo = await getApiKeyForModelCore({ model, cfg, profileId: tokenSource.profileId, diff --git a/src/agents/auth-profiles/oauth.ts b/src/agents/auth-profiles/oauth.ts index e247fccc13c6..4e1a25039119 100644 --- a/src/agents/auth-profiles/oauth.ts +++ b/src/agents/auth-profiles/oauth.ts @@ -40,7 +40,7 @@ import { assertNoOAuthSecretRefPolicyViolations } from "./policy.js"; import { clearLastGoodProfileWithLock } from "./profiles.js"; import { suggestOAuthProfileIdForLegacyDefault } from "./repair.js"; import { - getRuntimeAuthProfileStoreSnapshot, + getRuntimeAuthProfileStoreSnapshotCore, hasRuntimeAuthProfileStoreSnapshot, setRuntimeAuthProfileStoreSnapshot, } from "./runtime-snapshots.js"; @@ -311,7 +311,7 @@ function resolveRuntimeAuthProfile(params: { profile: AuthProfileCredential; defaults: SecretDefaults | undefined; }): { profile: AuthProfileCredential; published: boolean } { - const runtimeProfile = getRuntimeAuthProfileStoreSnapshot(params.agentDir)?.profiles[ + const runtimeProfile = getRuntimeAuthProfileStoreSnapshotCore(params.agentDir)?.profiles[ params.profileId ]; const inputRefKey = authProfileSecretRefKey(params.profile, params.defaults); @@ -514,7 +514,7 @@ export async function resolveApiKeyForProfile( params.agentDir !== ownerAgentDir && hasRuntimeAuthProfileStoreSnapshot(params.agentDir) ) { - const snapshot = getRuntimeAuthProfileStoreSnapshot(params.agentDir); + const snapshot = getRuntimeAuthProfileStoreSnapshotCore(params.agentDir); const providerKey = resolveProviderIdForAuth(cred.provider); if (snapshot?.lastGood?.[providerKey] === profileId) { delete snapshot.lastGood[providerKey]; diff --git a/src/agents/auth-profiles/profiles.test.ts b/src/agents/auth-profiles/profiles.test.ts index 6c141671537b..b6c9b0bef179 100644 --- a/src/agents/auth-profiles/profiles.test.ts +++ b/src/agents/auth-profiles/profiles.test.ts @@ -29,7 +29,7 @@ import { } from "./profiles.js"; import { clearRuntimeAuthProfileStoreSnapshots, - getRuntimeAuthProfileStoreSnapshot as getInternalRuntimeAuthProfileStoreSnapshot, + getRuntimeAuthProfileStoreSnapshotCore as getInternalRuntimeAuthProfileStoreSnapshot, getRuntimeAuthProfileStoreCredentialMutationToken, getRuntimeAuthProfileStoreCredentialsRevision, getRuntimeAuthProfileStoreStateMutationToken, diff --git a/src/agents/auth-profiles/runtime-snapshots.test.ts b/src/agents/auth-profiles/runtime-snapshots.test.ts index 929cb1d8f9a1..055595182bc4 100644 --- a/src/agents/auth-profiles/runtime-snapshots.test.ts +++ b/src/agents/auth-profiles/runtime-snapshots.test.ts @@ -12,10 +12,10 @@ import { revokeRuntimeAuthMaterializations, } from "./runtime-materializations.js"; import { - clearRuntimeAuthProfileStoreSnapshot, + clearRuntimeAuthProfileStoreSnapshotCore, clearRuntimeAuthProfileStoreSnapshots, - getPreparedRuntimeAuthProfileStoreSnapshot, - getRuntimeAuthProfileStoreSnapshot, + getPreparedRuntimeAuthProfileStoreSnapshotCore, + getRuntimeAuthProfileStoreSnapshotCore, getRuntimeAuthProfileStoreCredentialsRevision, noteRuntimeAuthProfileStorePersistedMutation, registerRuntimeAuthProfileStoreMutationListener, @@ -171,7 +171,7 @@ describe("runtime auth profile snapshots", () => { }, agentDir, ); - clearRuntimeAuthProfileStoreSnapshot(agentDir); + clearRuntimeAuthProfileStoreSnapshotCore(agentDir); expect(listener).toHaveBeenCalledTimes(2); expect(listener).toHaveBeenNthCalledWith(1, { @@ -223,7 +223,7 @@ describe("runtime auth profile snapshots", () => { try { setRuntimeAuthProfileStoreSnapshot(emptyStore, agentDir); setRuntimeAuthProfileStoreSnapshot(emptyStore, agentDir); - clearRuntimeAuthProfileStoreSnapshot(agentDir); + clearRuntimeAuthProfileStoreSnapshotCore(agentDir); expect(listener).toHaveBeenCalledTimes(2); expect(listener).toHaveBeenNthCalledWith(1, { @@ -268,7 +268,7 @@ describe("runtime auth profile snapshots", () => { ).provider = "mutated"; expectDefined(stored.order?.openai, "stored OpenAI profile order").push("mutated"); - const first = getRuntimeAuthProfileStoreSnapshot(agentDir); + const first = getRuntimeAuthProfileStoreSnapshotCore(agentDir); expectOpenAICodexSnapshotCredential(first, { access: "access-1" }); expect(first?.order?.["openai"]).toEqual(["openai:default"]); @@ -280,7 +280,7 @@ describe("runtime auth profile snapshots", () => { "first OpenAI usage stats", ).lastUsed = 99; - const second = getRuntimeAuthProfileStoreSnapshot(agentDir); + const second = getRuntimeAuthProfileStoreSnapshotCore(agentDir); expectOpenAICodexSnapshotCredential(second, { access: "access-1" }); expect(second?.usageStats?.["openai:default"]?.lastUsed).toBe(1); @@ -292,7 +292,7 @@ describe("runtime auth profile snapshots", () => { replacementCredential.access = "mutated-replacement"; } - const replaced = getRuntimeAuthProfileStoreSnapshot(agentDir); + const replaced = getRuntimeAuthProfileStoreSnapshotCore(agentDir); expectOpenAICodexSnapshotCredential(replaced, { access: "access-2", refresh: "refresh-access-2", @@ -324,7 +324,7 @@ describe("runtime auth profile snapshots", () => { ); setRuntimeAuthProfileStoreSnapshot(createStore("agent"), agentDir); - const prepared = getPreparedRuntimeAuthProfileStoreSnapshot(agentDir, inheritedAuthDir); + const prepared = getPreparedRuntimeAuthProfileStoreSnapshotCore(agentDir, inheritedAuthDir); expectOpenAICodexSnapshotCredential(prepared, { access: "agent" }); expect(prepared?.profiles["anthropic:default"]).toMatchObject({ @@ -333,7 +333,7 @@ describe("runtime auth profile snapshots", () => { key: "inherited-key", }); expect( - getPreparedRuntimeAuthProfileStoreSnapshot( + getPreparedRuntimeAuthProfileStoreSnapshotCore( "/tmp/openclaw-auth-runtime-missing", "/tmp/openclaw-auth-runtime-also-missing", ), @@ -351,13 +351,15 @@ describe("runtime auth profile snapshots", () => { setRuntimeAuthProfileStoreSnapshot(createStore("first"), firstAgentDir); setRuntimeAuthProfileStoreSnapshot(createStore("second"), secondAgentDir); - expect(clearRuntimeAuthProfileStoreSnapshot(firstAgentDir)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(firstAgentDir)).toBeUndefined(); - expectOpenAICodexSnapshotCredential(getRuntimeAuthProfileStoreSnapshot(), { access: "main" }); - expectOpenAICodexSnapshotCredential(getRuntimeAuthProfileStoreSnapshot(secondAgentDir), { + expect(clearRuntimeAuthProfileStoreSnapshotCore(firstAgentDir)).toBe(true); + expect(getRuntimeAuthProfileStoreSnapshotCore(firstAgentDir)).toBeUndefined(); + expectOpenAICodexSnapshotCredential(getRuntimeAuthProfileStoreSnapshotCore(), { + access: "main", + }); + expectOpenAICodexSnapshotCredential(getRuntimeAuthProfileStoreSnapshotCore(secondAgentDir), { access: "second", }); - expect(clearRuntimeAuthProfileStoreSnapshot(firstAgentDir)).toBe(false); + expect(clearRuntimeAuthProfileStoreSnapshotCore(firstAgentDir)).toBe(false); } finally { clearRuntimeAuthProfileStoreSnapshots(); } diff --git a/src/agents/auth-profiles/runtime-snapshots.ts b/src/agents/auth-profiles/runtime-snapshots.ts index 613a077984b2..39b1c6a5d3f6 100644 --- a/src/agents/auth-profiles/runtime-snapshots.ts +++ b/src/agents/auth-profiles/runtime-snapshots.ts @@ -234,7 +234,7 @@ export function registerRuntimeAuthProfileStoreMutationListener( } /** Reads a cloned runtime auth profile store snapshot for an agent dir. */ -export function getRuntimeAuthProfileStoreSnapshot( +export function getRuntimeAuthProfileStoreSnapshotCore( agentDir?: string, ): RuntimeAuthProfileStore | undefined { const store = runtimeAuthStoreSnapshots.get(resolveRuntimeStoreKey(agentDir)); @@ -245,12 +245,12 @@ export function getRuntimeAuthProfileStoreSnapshot( * Reads the effective prepared auth store without falling back to persisted storage. * Lifecycle consumers use this after auth publication so request paths never reopen SQLite. */ -export function getPreparedRuntimeAuthProfileStoreSnapshot( +export function getPreparedRuntimeAuthProfileStoreSnapshotCore( agentDir?: string, inheritedAuthDir?: string, ): AuthProfileStore | undefined { - const inherited = getRuntimeAuthProfileStoreSnapshot(inheritedAuthDir); - const requested = getRuntimeAuthProfileStoreSnapshot(agentDir); + const inherited = getRuntimeAuthProfileStoreSnapshotCore(inheritedAuthDir); + const requested = getRuntimeAuthProfileStoreSnapshotCore(agentDir); if (!agentDir || resolveRuntimeStoreKey(agentDir) === resolveRuntimeStoreKey(inheritedAuthDir)) { return requested ?? inherited; } @@ -280,14 +280,14 @@ export function hasRuntimeAuthProfileStoreSnapshot(agentDir?: string): boolean { /** Returns true when requested or main runtime snapshots contain profiles. */ export function hasAnyRuntimeAuthProfileStoreSource(agentDir?: string): boolean { - const requestedStore = getRuntimeAuthProfileStoreSnapshot(agentDir); + const requestedStore = getRuntimeAuthProfileStoreSnapshotCore(agentDir); if (requestedStore && Object.keys(requestedStore.profiles).length > 0) { return true; } if (!agentDir) { return false; } - const mainStore = getRuntimeAuthProfileStoreSnapshot(); + const mainStore = getRuntimeAuthProfileStoreSnapshotCore(); return Boolean(mainStore && Object.keys(mainStore.profiles).length > 0); } @@ -342,7 +342,7 @@ export function clearRuntimeAuthProfileStoreSnapshots(): void { } /** Clears one runtime auth-profile snapshot without disturbing other active agents. */ -export function clearRuntimeAuthProfileStoreSnapshot(agentDir?: string): boolean { +export function clearRuntimeAuthProfileStoreSnapshotCore(agentDir?: string): boolean { const key = resolveRuntimeStoreKey(agentDir); const store = runtimeAuthStoreSnapshots.get(key); if (!store) { diff --git a/src/agents/auth-profiles/source-check.ts b/src/agents/auth-profiles/source-check.ts index 12fa316b2598..966148e0b759 100644 --- a/src/agents/auth-profiles/source-check.ts +++ b/src/agents/auth-profiles/source-check.ts @@ -6,7 +6,7 @@ import { evaluateStoredCredentialEligibility } from "./credential-state.js"; import { hasLegacyAuthProfileCredentialSource } from "./legacy-source-diagnostic.js"; import { coercePersistedAuthProfileStore } from "./persisted.js"; import { - getRuntimeAuthProfileStoreSnapshot, + getRuntimeAuthProfileStoreSnapshotCore, hasAnyRuntimeAuthProfileStoreSource, } from "./runtime-snapshots.js"; import { @@ -116,7 +116,7 @@ export function hasAnyAuthProfileStoreSource(agentDir?: string): boolean { /** Returns true when the requested agent dir has a local auth profile source. */ export function hasLocalAuthProfileStoreSource(agentDir?: string): boolean { - const runtimeStore = getRuntimeAuthProfileStoreSnapshot(agentDir); + const runtimeStore = getRuntimeAuthProfileStoreSnapshotCore(agentDir); if (runtimeStore && Object.keys(runtimeStore.profiles).length > 0) { return true; } @@ -147,7 +147,7 @@ export function hasAuthProfileStoreSourceForProvider( if (profileIds?.length === 0) { return false; } - const localRuntimeStore = getRuntimeAuthProfileStoreSnapshot(agentDir); + const localRuntimeStore = getRuntimeAuthProfileStoreSnapshotCore(agentDir); if (runtimeStoreHasProviderProfile(localRuntimeStore, provider, profileIds)) { return true; } @@ -164,7 +164,7 @@ export function hasAuthProfileStoreSourceForProvider( if (!agentDir) { return false; } - const mainRuntimeStore = getRuntimeAuthProfileStoreSnapshot(); + const mainRuntimeStore = getRuntimeAuthProfileStoreSnapshotCore(); if (runtimeStoreHasProviderProfile(mainRuntimeStore, provider, profileIds)) { return true; } diff --git a/src/agents/auth-profiles/store.ts b/src/agents/auth-profiles/store.ts index fc8b3f033288..4eecf5654943 100644 --- a/src/agents/auth-profiles/store.ts +++ b/src/agents/auth-profiles/store.ts @@ -44,10 +44,10 @@ import { mergeAuthProfileStores, } from "./persisted.js"; import { - clearRuntimeAuthProfileStoreSnapshot as clearRuntimeAuthProfileStoreSnapshotImpl, + clearRuntimeAuthProfileStoreSnapshotCore, clearRuntimeAuthProfileStoreSnapshots, - getPreparedRuntimeAuthProfileStoreSnapshot as getPreparedRuntimeAuthProfileStoreSnapshotImpl, - getRuntimeAuthProfileStoreSnapshot as getRuntimeAuthProfileStoreSnapshotImpl, + getPreparedRuntimeAuthProfileStoreSnapshotCore, + getRuntimeAuthProfileStoreSnapshotCore, getRuntimeAuthProfileStoreSnapshotRevision, noteRuntimeAuthProfileStorePersistedMutation, listRuntimeAuthProfileStoreSnapshots, @@ -282,8 +282,8 @@ function resolveRuntimeAuthProfileStore( ): AuthProfileStore | null { const mainKey = resolveAuthStorePath(options?.inheritedAuthDir); const requestedKey = resolveAuthStorePath(agentDir); - const mainStore = getRuntimeAuthProfileStoreSnapshotImpl(options?.inheritedAuthDir); - const requestedStore = getRuntimeAuthProfileStoreSnapshotImpl(agentDir); + const mainStore = getRuntimeAuthProfileStoreSnapshotCore(options?.inheritedAuthDir); + const requestedStore = getRuntimeAuthProfileStoreSnapshotCore(agentDir); if (!agentDir || requestedKey === mainKey) { if (!mainStore) { @@ -1317,7 +1317,7 @@ export { export function getRuntimeAuthProfileStoreSnapshot( agentDir?: string, ): AuthProfileStore | undefined { - return getRuntimeAuthProfileStoreSnapshotImpl(agentDir); + return getRuntimeAuthProfileStoreSnapshotCore(agentDir); } /** Return the lifecycle-published effective auth store without persisted fallback reads. */ @@ -1325,14 +1325,14 @@ export function getPreparedRuntimeAuthProfileStoreSnapshot( agentDir?: string, inheritedAuthDir?: string, ): AuthProfileStore | undefined { - return getPreparedRuntimeAuthProfileStoreSnapshotImpl(agentDir, inheritedAuthDir); + return getPreparedRuntimeAuthProfileStoreSnapshotCore(agentDir, inheritedAuthDir); } export { getRuntimeAuthProfileStoreSnapshotRevision }; /** Clear one runtime auth-profile snapshot. */ export function clearRuntimeAuthProfileStoreSnapshot(agentDir?: string): boolean { - return clearRuntimeAuthProfileStoreSnapshotImpl(agentDir); + return clearRuntimeAuthProfileStoreSnapshotCore(agentDir); } function saveAuthProfileStoreInTransaction( diff --git a/src/agents/bash-tools.notify-on-exit-ack.test-support.ts b/src/agents/bash-tools.notify-on-exit-ack.test-support.ts index 1d39edd4e940..8dae4e7bd2c8 100644 --- a/src/agents/bash-tools.notify-on-exit-ack.test-support.ts +++ b/src/agents/bash-tools.notify-on-exit-ack.test-support.ts @@ -1,6 +1,6 @@ import type { ManagedRun } from "../process/supervisor/index.js"; import type { SpawnInput } from "../process/supervisor/types.js"; -import { createDeferred } from "../shared/deferred.js"; +import { createDeferredCore } from "../shared/deferred.js"; import type { DeliveryContext } from "../utils/delivery-context.types.js"; import { markBackgrounded } from "./bash-process-registry.js"; import { runExecProcess } from "./bash-tools.exec-runtime.js"; @@ -14,7 +14,7 @@ export async function startDeferredNotifyRun(params: { sessionKey: string; notifyDeliveryContext?: DeliveryContext; }) { - const exit = createDeferred>>(); + const exit = createDeferredCore>>(); params.spawn.mockImplementationOnce(async (input) => { input.onStdout?.("producer output\n"); return { diff --git a/src/agents/btw.test.ts b/src/agents/btw.test.ts index b8161dd2bff2..938b1be48861 100644 --- a/src/agents/btw.test.ts +++ b/src/agents/btw.test.ts @@ -137,7 +137,7 @@ vi.mock("./model-auth.js", () => ({ ensureAuthProfileStore: (...args: unknown[]) => ensureAuthProfileStoreMock(...args), ensureAuthProfileStoreWithoutExternalProfiles: (...args: unknown[]) => ensureAuthProfileStoreWithoutExternalProfilesMock(...args), - getApiKeyForModel: (...args: unknown[]) => getApiKeyForModelMock(...args), + getApiKeyForModelCore: (...args: unknown[]) => getApiKeyForModelMock(...args), hasUsableCustomProviderApiKey: (...args: unknown[]) => hasUsableCustomProviderApiKeyMock(...args), requireApiKey: (...args: unknown[]) => requireApiKeyMock(...args), resolveProviderEntryApiKeyProfileReference: (params: unknown) => diff --git a/src/agents/embedded-agent-runner/compact.hooks.harness.ts b/src/agents/embedded-agent-runner/compact.hooks.harness.ts index 28e5a4e502a7..f7f0bc2221e2 100644 --- a/src/agents/embedded-agent-runner/compact.hooks.harness.ts +++ b/src/agents/embedded-agent-runner/compact.hooks.harness.ts @@ -781,7 +781,7 @@ export async function loadCompactHooksHarness(): Promise<{ (auth: { mode: string; source: string }, provider: string) => `No API key resolved for provider "${provider}" (auth mode: ${auth.mode}, checked: ${auth.source}).`, ), - getApiKeyForModel: (params: { profileId?: string; allowAuthProfileFallback?: boolean }) => + getApiKeyForModelCore: (params: { profileId?: string; allowAuthProfileFallback?: boolean }) => getApiKeyForModelMock(params), hasUsableCustomProviderApiKey: vi.fn(() => false), resolveProviderEntryApiKeyProfileReference: resolveProviderEntryApiKeyProfileReferenceMock, diff --git a/src/agents/embedded-agent-runner/run.overflow-compaction.harness.ts b/src/agents/embedded-agent-runner/run.overflow-compaction.harness.ts index 59c810e8e599..43dd7eaf16cc 100644 --- a/src/agents/embedded-agent-runner/run.overflow-compaction.harness.ts +++ b/src/agents/embedded-agent-runner/run.overflow-compaction.harness.ts @@ -1004,7 +1004,7 @@ export async function loadRunOverflowCompactionHarness(): Promise<{ ensureAuthProfileStore: mockedEnsureAuthProfileStore, ensureAuthProfileStoreWithoutExternalProfiles: mockedEnsureAuthProfileStoreWithoutExternalProfiles, - getApiKeyForModel: mockedGetApiKeyForModel, + getApiKeyForModelCore: mockedGetApiKeyForModel, hasUsableCustomProviderApiKey: mockedHasUsableCustomProviderApiKey, resolveAuthProfileOrder: mockedResolveAuthProfileOrder, resolveAuthProfileOrderWithMetadata: mockedResolveAuthProfileOrderWithMetadata, diff --git a/src/agents/embedded-agent-runner/run/auth-controller.test.ts b/src/agents/embedded-agent-runner/run/auth-controller.test.ts index 2dfbed50729f..e7bca6227f08 100644 --- a/src/agents/embedded-agent-runner/run/auth-controller.test.ts +++ b/src/agents/embedded-agent-runner/run/auth-controller.test.ts @@ -15,7 +15,7 @@ import type { RuntimeAuthState } from "./helpers.js"; const mocks = vi.hoisted(() => ({ prepareProviderRuntimeAuth: vi.fn(), - getApiKeyForModel: vi.fn(), + getApiKeyForModelCore: vi.fn(), })); vi.mock("../../../plugins/provider-runtime.js", async () => { @@ -32,7 +32,7 @@ vi.mock("../../model-auth.js", async () => { const actual = await vi.importActual("../../model-auth.js"); return { ...actual, - getApiKeyForModel: mocks.getApiKeyForModel, + getApiKeyForModelCore: mocks.getApiKeyForModelCore, }; }); @@ -165,7 +165,7 @@ function createMutableEmbeddedRunAuthController(params: { describe("createEmbeddedRunAuthController", () => { beforeEach(() => { mocks.prepareProviderRuntimeAuth.mockReset(); - mocks.getApiKeyForModel.mockReset(); + mocks.getApiKeyForModelCore.mockReset(); }); it("commits a prepared route only after its credential resolves", async () => { @@ -176,7 +176,7 @@ describe("createEmbeddedRunAuthController", () => { baseUrl: "https://chatgpt.com/backend-api/codex", contextWindow: 272_000, }; - mocks.getApiKeyForModel.mockImplementation(async ({ model }) => { + mocks.getApiKeyForModelCore.mockImplementation(async ({ model }) => { expect(model).toBe(selectedModel); expect(harness.runtimeModel).not.toBe(selectedModel); return { @@ -210,7 +210,7 @@ describe("createEmbeddedRunAuthController", () => { it("rejects credentials whose class does not match the prepared route", async () => { const harness = createMutableAuthControllerHarness(); const commit = vi.fn(); - mocks.getApiKeyForModel.mockResolvedValue({ + mocks.getApiKeyForModelCore.mockResolvedValue({ apiKey: "platform-key", mode: "api-key", source: "config", @@ -243,7 +243,7 @@ describe("createEmbeddedRunAuthController", () => { const harness = createMutableAuthControllerHarness(); const setRuntimeApiKey = vi.fn<(provider: string, apiKey: string) => void>(); - mocks.getApiKeyForModel.mockResolvedValue({ + mocks.getApiKeyForModelCore.mockResolvedValue({ apiKey: "source-api-key", mode: "api-key", profileId: "default", @@ -268,7 +268,7 @@ describe("createEmbeddedRunAuthController", () => { await controller.initializeAuthProfile(); - const apiKeyParams = mocks.getApiKeyForModel.mock.calls.at(0)?.[0] as + const apiKeyParams = mocks.getApiKeyForModelCore.mock.calls.at(0)?.[0] as | { agentDir?: string; workspaceDir?: string } | undefined; expect(apiKeyParams?.agentDir).toBe("/tmp/agent"); @@ -296,7 +296,7 @@ describe("createEmbeddedRunAuthController", () => { refKeys: ["env:default:MISSING_OPENAI_KEY"], reason: "secret reference was not found", }); - mocks.getApiKeyForModel.mockImplementation(async ({ profileId }) => { + mocks.getApiKeyForModelCore.mockImplementation(async ({ profileId }) => { if (profileId === "default") { throw unavailable; } @@ -314,7 +314,7 @@ describe("createEmbeddedRunAuthController", () => { }); await expect(controller.initializeAuthProfile()).rejects.toBe(unavailable); - expect(mocks.getApiKeyForModel).toHaveBeenCalledOnce(); + expect(mocks.getApiKeyForModelCore).toHaveBeenCalledOnce(); expect(mocks.prepareProviderRuntimeAuth).not.toHaveBeenCalled(); }); @@ -328,7 +328,7 @@ describe("createEmbeddedRunAuthController", () => { harness.effectiveModel = baseModel; const setRuntimeApiKey = vi.fn<(provider: string, apiKey: string) => void>(); - mocks.getApiKeyForModel.mockImplementation(async ({ profileId }) => ({ + mocks.getApiKeyForModelCore.mockImplementation(async ({ profileId }) => ({ apiKey: `${String(profileId)}-source-key`, mode: "api-key" as const, profileId, @@ -376,7 +376,7 @@ describe("createEmbeddedRunAuthController", () => { const setRuntimeApiKey = vi.fn<(provider: string, apiKey: string) => void>(); const secret = "runtime-exchange-source-secret"; const sentinel = mintSecretSentinel(secret, { label: "model-auth:custom-openai" }); - mocks.getApiKeyForModel.mockResolvedValue({ + mocks.getApiKeyForModelCore.mockResolvedValue({ apiKey: sentinel, mode: "api-key", source: "profile:custom-openai:default", @@ -395,7 +395,7 @@ describe("createEmbeddedRunAuthController", () => { const controller = createMutableEmbeddedRunAuthController({ harness, setRuntimeApiKey }); await controller.initializeAuthProfile(); - expect(mocks.getApiKeyForModel).toHaveBeenCalledWith( + expect(mocks.getApiKeyForModelCore).toHaveBeenCalledWith( expect.objectContaining({ secretSentinels: true }), ); expect(mocks.prepareProviderRuntimeAuth).toHaveBeenCalledWith( @@ -415,7 +415,7 @@ describe("createEmbeddedRunAuthController", () => { const sentinel = mintSecretSentinel("runtime-source-secret", { label: "model-auth:custom-openai", }); - mocks.getApiKeyForModel.mockResolvedValue({ + mocks.getApiKeyForModelCore.mockResolvedValue({ apiKey: sentinel, mode: "api-key", source: "profile:custom-openai:default", @@ -435,7 +435,7 @@ describe("createEmbeddedRunAuthController", () => { const source = mintSecretSentinel("kill-switch-source-secret", { label: "model-auth:custom-openai", }); - mocks.getApiKeyForModel.mockResolvedValue({ + mocks.getApiKeyForModelCore.mockResolvedValue({ apiKey: source, mode: "api-key", source: "profile:custom-openai:default", @@ -456,7 +456,7 @@ describe("createEmbeddedRunAuthController", () => { const harness = createMutableAuthControllerHarness(); const setRuntimeApiKey = vi.fn<(provider: string, apiKey: string) => void>(); - mocks.getApiKeyForModel.mockResolvedValue({ + mocks.getApiKeyForModelCore.mockResolvedValue({ mode: "api-key", source: "models.providers.custom-openai", }); @@ -561,7 +561,7 @@ describe("createEmbeddedRunAuthController", () => { it("rejects privileged runtime transport overrides on the first auth exchange", async () => { let runtimeModel = createTestModel(); - mocks.getApiKeyForModel.mockResolvedValue({ + mocks.getApiKeyForModelCore.mockResolvedValue({ apiKey: "source-api-key", mode: "api-key", profileId: "default", @@ -642,7 +642,7 @@ describe("createEmbeddedRunAuthController", () => { expiresAt: number; }>(); - mocks.getApiKeyForModel.mockImplementation(async ({ profileId }) => { + mocks.getApiKeyForModelCore.mockImplementation(async ({ profileId }) => { if (profileId === "backup") { return { apiKey: "backup-source-api-key", @@ -744,7 +744,7 @@ describe("createEmbeddedRunAuthController", () => { const harness = createMutableAuthControllerHarness(); const setRuntimeApiKey = vi.fn<(provider: string, apiKey: string) => void>(); - mocks.getApiKeyForModel.mockResolvedValue({ + mocks.getApiKeyForModelCore.mockResolvedValue({ apiKey: undefined, mode: "aws-sdk", source: "aws-sdk default chain", @@ -774,7 +774,7 @@ describe("createEmbeddedRunAuthController", () => { const harness = createMutableAuthControllerHarness(); const setRuntimeApiKey = vi.fn<(provider: string, apiKey: string) => void>(); - mocks.getApiKeyForModel.mockResolvedValue({ + mocks.getApiKeyForModelCore.mockResolvedValue({ apiKey: undefined, mode: "aws-sdk", source: "aws-sdk default chain", @@ -806,7 +806,7 @@ describe("createEmbeddedRunAuthController", () => { refreshTimer: setTimeout(() => undefined, 60_000), }; - mocks.getApiKeyForModel.mockResolvedValue({ + mocks.getApiKeyForModelCore.mockResolvedValue({ apiKey: undefined, mode: "aws-sdk", source: "aws-sdk default chain", @@ -834,7 +834,7 @@ describe("createEmbeddedRunAuthController", () => { const setRuntimeApiKey = vi.fn<(provider: string, apiKey: string) => void>(); const warn = vi.fn<(message: string) => void>(); - mocks.getApiKeyForModel.mockResolvedValue({ + mocks.getApiKeyForModelCore.mockResolvedValue({ apiKey: undefined, mode: "aws-sdk", source: "aws-sdk default chain", diff --git a/src/agents/embedded-agent-runner/run/auth-controller.ts b/src/agents/embedded-agent-runner/run/auth-controller.ts index 0bdcd0c9d52d..10da023ef537 100644 --- a/src/agents/embedded-agent-runner/run/auth-controller.ts +++ b/src/agents/embedded-agent-runner/run/auth-controller.ts @@ -22,7 +22,7 @@ import { FailoverError, resolveFailoverStatus } from "../../failover-error.js"; import { shouldUseTransientCooldownProbeSlot } from "../../failover-policy.js"; import { renderAuthProfileFailoverCopy } from "../../failover/user-copy.js"; import { - getApiKeyForModel, + getApiKeyForModelCore, MissingProviderAuthError, type ResolvedProviderAuth, } from "../../model-auth.js"; @@ -449,7 +449,7 @@ export function createEmbeddedRunAuthController(params: { model = params.getRuntimeModel(), allowAuthProfileFallback?: boolean, ) => { - return getApiKeyForModel({ + return getApiKeyForModelCore({ model, cfg: params.config, profileId: candidate, diff --git a/src/agents/harness/selection.test.ts b/src/agents/harness/selection.test.ts index 75a9ce17a369..3aa1825cd838 100644 --- a/src/agents/harness/selection.test.ts +++ b/src/agents/harness/selection.test.ts @@ -55,7 +55,7 @@ const agentRunAttempt = vi.fn(async () => const compactAuthMocks = vi.hoisted(() => ({ ensureAuthProfileStore: vi.fn(), ensureAuthProfileStoreWithoutExternalProfiles: vi.fn(), - getApiKeyForModel: vi.fn(), + getApiKeyForModelCore: vi.fn(), prepareAgentRuntimeAuth: vi.fn(), resolveModelAsync: vi.fn(), })); @@ -122,7 +122,7 @@ vi.mock("../model-auth.js", async (importOriginal) => ({ ensureAuthProfileStore: compactAuthMocks.ensureAuthProfileStore, ensureAuthProfileStoreWithoutExternalProfiles: compactAuthMocks.ensureAuthProfileStoreWithoutExternalProfiles, - getApiKeyForModel: compactAuthMocks.getApiKeyForModel, + getApiKeyForModelCore: compactAuthMocks.getApiKeyForModelCore, })); vi.mock("../embedded-agent-runner/model.js", () => ({ resolveModelAsync: compactAuthMocks.resolveModelAsync, @@ -174,7 +174,7 @@ beforeEach(async () => { compactAuthMocks.resolveModelAsync.mockResolvedValue({ model: { id: "gpt-5.5", provider: "openai" }, }); - compactAuthMocks.getApiKeyForModel.mockResolvedValue({ apiKey: "test-key" }); + compactAuthMocks.getApiKeyForModelCore.mockResolvedValue({ apiKey: "test-key" }); providerOwnerMocks.resolveProviderRefOwnership.mockReset(); providerOwnerMocks.resolveProviderRefOwnership.mockReturnValue({ status: "unowned" }); contextEngineTurnAttemptMocks.drainPendingContextEngineTurnsBeforeRun @@ -214,7 +214,7 @@ afterEach(() => { agentRunAttempt.mockClear(); compactAuthMocks.prepareAgentRuntimeAuth.mockClear(); compactAuthMocks.resolveModelAsync.mockReset(); - compactAuthMocks.getApiKeyForModel.mockReset(); + compactAuthMocks.getApiKeyForModelCore.mockReset(); compactAuthMocks.ensureAuthProfileStore.mockReset(); compactAuthMocks.ensureAuthProfileStoreWithoutExternalProfiles.mockReset(); providerOwnerMocks.resolveProviderRefOwnership.mockReset(); @@ -2655,7 +2655,7 @@ describe("selectAgentHarness", () => { ).resolves.toEqual({ ok: true, compacted: false }); expect(compactAuthMocks.resolveModelAsync).not.toHaveBeenCalled(); - expect(compactAuthMocks.getApiKeyForModel).not.toHaveBeenCalled(); + expect(compactAuthMocks.getApiKeyForModelCore).not.toHaveBeenCalled(); expect(compact).toHaveBeenCalledWith( expect.objectContaining({ runtimeAuthPlan: expect.objectContaining({ @@ -2881,7 +2881,7 @@ describe("selectAgentHarness", () => { }); it("keeps compaction recoverable when auth profile lookup fails", async () => { - compactAuthMocks.getApiKeyForModel.mockRejectedValue(new Error("missing auth profile")); + compactAuthMocks.getApiKeyForModelCore.mockRejectedValue(new Error("missing auth profile")); const compact = vi.fn>(async () => ({ ok: true, compacted: false, @@ -2958,7 +2958,7 @@ describe("selectAgentHarness", () => { }), ).resolves.toEqual({ ok: true, compacted: false }); - expect(compactAuthMocks.getApiKeyForModel).not.toHaveBeenCalled(); + expect(compactAuthMocks.getApiKeyForModelCore).not.toHaveBeenCalled(); expect(compact).toHaveBeenCalledWith( expect.objectContaining({ resolvedApiKey: "already-resolved", @@ -3069,7 +3069,7 @@ describe("selectAgentHarness", () => { workspaceDir: "/tmp/workspace", }), ); - expect(compactAuthMocks.getApiKeyForModel).toHaveBeenCalledWith( + expect(compactAuthMocks.getApiKeyForModelCore).toHaveBeenCalledWith( expect.objectContaining({ agentDir: expect.any(String), model: expect.objectContaining({ diff --git a/src/agents/harness/tool-surface-bridge.test.ts b/src/agents/harness/tool-surface-bridge.test.ts index 5ae8c6dc62d6..9cd128d52519 100644 --- a/src/agents/harness/tool-surface-bridge.test.ts +++ b/src/agents/harness/tool-surface-bridge.test.ts @@ -10,7 +10,7 @@ import { TOOL_SEARCH_RAW_TOOL_NAME, } from "../tool-search.js"; import { testing } from "../tool-search.test-support.js"; -import { createAgentHarnessToolSurfaceRuntime as createAgentHarnessToolSurfaceRuntimeBase } from "./tool-surface-bridge.js"; +import { createAgentHarnessToolSurfaceRuntimeCore as createAgentHarnessToolSurfaceRuntimeBase } from "./tool-surface-bridge.js"; function createAgentHarnessToolSurfaceRuntime( params: Parameters[0], diff --git a/src/agents/harness/tool-surface-bridge.ts b/src/agents/harness/tool-surface-bridge.ts index 4665396cb26b..d9e6392e8846 100644 --- a/src/agents/harness/tool-surface-bridge.ts +++ b/src/agents/harness/tool-surface-bridge.ts @@ -51,7 +51,7 @@ export type AgentHarnessToolSurfaceRuntime = { toolSearchCatalogExecutor: ToolSearchCatalogToolExecutor | undefined; }; -export function createAgentHarnessToolSurfaceRuntime(params: { +export function createAgentHarnessToolSurfaceRuntimeCore(params: { abortSignal?: AbortSignal; agentId?: string; config?: OpenClawConfig; diff --git a/src/agents/live-cache-test-support.ts b/src/agents/live-cache-test-support.ts index c1dd60960a7c..5bce9348c808 100644 --- a/src/agents/live-cache-test-support.ts +++ b/src/agents/live-cache-test-support.ts @@ -11,7 +11,7 @@ import { resolveDefaultAgentDir } from "./agent-scope.js"; import { collectProviderApiKeys } from "./live-auth-keys.js"; import { isLiveTestEnabled } from "./live-test-helpers.js"; import { - getApiKeyForModel, + getApiKeyForModelCore, isMissingProviderAuthError, isProviderAuthError, requireApiKey, @@ -269,7 +269,7 @@ export async function resolveLiveDirectModelPool(params: { let apiKey: string; try { apiKey = requireApiKey( - await getApiKeyForModel({ + await getApiKeyForModelCore({ model: resolvedModel, cfg, agentDir, diff --git a/src/agents/model-auth-availability.ts b/src/agents/model-auth-availability.ts index 903b55f207cb..3366be21c3f7 100644 --- a/src/agents/model-auth-availability.ts +++ b/src/agents/model-auth-availability.ts @@ -32,7 +32,7 @@ import { resolveStoredCredentialReadOnlyAvailability, } from "./auth-profiles/read-only-availability.js"; import type { RuntimeAuthMaterialization } from "./auth-profiles/runtime-materializations.js"; -import { getRuntimeAuthProfileStoreSnapshot } from "./auth-profiles/runtime-snapshots.js"; +import { getRuntimeAuthProfileStoreSnapshotCore } from "./auth-profiles/runtime-snapshots.js"; import type { AuthProfileCredential, AuthProfileStore } from "./auth-profiles/types.js"; import { isAuthCooldownBypassedForProvider, @@ -183,7 +183,7 @@ export function createModelAuthAvailabilityResolver( const runtimeStore = params.preparedRuntimeAuthStore ?? (params.allowPreparedRuntimeAuth !== false - ? getRuntimeAuthProfileStoreSnapshot(params.agentDir) + ? getRuntimeAuthProfileStoreSnapshotCore(params.agentDir) : undefined); const hydratedProfileIds = new Set(); const sameSecretRef = ( diff --git a/src/agents/model-auth-model.ts b/src/agents/model-auth-model.ts index 30003289291c..42ed3a83d9b7 100644 --- a/src/agents/model-auth-model.ts +++ b/src/agents/model-auth-model.ts @@ -232,7 +232,7 @@ export async function hasAvailableAuthForProvider(params: { } /** Resolves request credentials from the provider attached to a model descriptor. */ -export async function getApiKeyForModel(params: { +export async function getApiKeyForModelCore(params: { model: Model; cfg?: OpenClawConfig; profileId?: string; diff --git a/src/agents/model-auth.profiles.test.ts b/src/agents/model-auth.profiles.test.ts index dc64d9af4436..60170f39a362 100644 --- a/src/agents/model-auth.profiles.test.ts +++ b/src/agents/model-auth.profiles.test.ts @@ -18,7 +18,7 @@ import { resolveInlineProviderApiKeyUsageId } from "./auth-profiles/usage.js"; import type { ClaudeCliCredential } from "./cli-credentials.js"; import { createRuntimeProviderAuthLookup, - getApiKeyForModel, + getApiKeyForModelCore, hasAvailableAuthForProvider, hasRuntimeAvailableProviderAuth, isConfigBackedInlineProviderApiKey, @@ -403,7 +403,7 @@ async function resolveDemoLocalApiKey(params: { }); } -describe("getApiKeyForModel", () => { +describe("getApiKeyForModelCore", () => { it("reads oauth auth-profiles entries from auth-profiles.json via explicit profile", async () => { await withOpenClawTestState( { @@ -432,7 +432,7 @@ describe("getApiKeyForModel", () => { const store = ensureAuthProfileStore(process.env.OPENCLAW_AGENT_DIR, { allowKeychainPrompt: false, }); - const apiKey = await getApiKeyForModel({ + const apiKey = await getApiKeyForModelCore({ model, profileId: "openai:default", store, @@ -460,7 +460,7 @@ describe("getApiKeyForModel", () => { }, }; - const directAuth = await getApiKeyForModel({ + const directAuth = await getApiKeyForModelCore({ model: { id: "chat-latest", provider: "openai", @@ -468,7 +468,7 @@ describe("getApiKeyForModel", () => { } as Model, store, }); - const codexAuth = await getApiKeyForModel({ + const codexAuth = await getApiKeyForModelCore({ model: { id: "gpt-5.5", provider: "openai", @@ -502,7 +502,7 @@ describe("getApiKeyForModel", () => { }; await expect( - getApiKeyForModel({ + getApiKeyForModelCore({ model: { id: "chat-latest", provider: "openai", @@ -528,7 +528,7 @@ describe("getApiKeyForModel", () => { }; await expect( - getApiKeyForModel({ + getApiKeyForModelCore({ model: { id: "gpt-5.5", provider: "openai", diff --git a/src/agents/model-auth.test.ts b/src/agents/model-auth.test.ts index 61012806df6a..a412ed74aabc 100644 --- a/src/agents/model-auth.test.ts +++ b/src/agents/model-auth.test.ts @@ -179,7 +179,7 @@ let hasRuntimeAvailableProviderAuth: typeof import("./model-auth.js").hasRuntime let hasUsableCustomProviderApiKey: typeof import("./model-auth.js").hasUsableCustomProviderApiKey; let hasSyntheticLocalProviderAuthConfig: typeof import("./model-auth.js").hasSyntheticLocalProviderAuthConfig; let requireApiKey: typeof import("./model-auth.js").requireApiKey; -let getApiKeyForModel: typeof import("./model-auth.js").getApiKeyForModel; +let getApiKeyForModelCore: typeof import("./model-auth.js").getApiKeyForModelCore; let resolveApiKeyForProvider: typeof import("./model-auth.js").resolveApiKeyForProvider; let resolveAwsSdkEnvVarName: typeof import("./model-auth.js").resolveAwsSdkEnvVarName; let resolveModelAuthMode: typeof import("./model-auth.js").resolveModelAuthMode; @@ -210,7 +210,7 @@ beforeAll(async () => { hasAvailableAuthForProvider, hasRuntimeAvailableProviderAuth, hasSyntheticLocalProviderAuthConfig, - getApiKeyForModel, + getApiKeyForModelCore, hasUsableCustomProviderApiKey, requireApiKey, resolveApiKeyForProvider, @@ -1586,7 +1586,7 @@ describe("resolveApiKeyForProvider", () => { it("preserves explicit subscription modes for literal provider credentials", async () => { for (const mode of ["oauth", "token"] as const) { const provider = `custom-${mode}`; - const resolved = await getApiKeyForModel({ + const resolved = await getApiKeyForModelCore({ model: { id: "subscription-model", provider, @@ -1617,7 +1617,7 @@ describe("resolveApiKeyForProvider", () => { it("does not reinterpret explicit OpenAI oauth material as a Platform API key", async () => { await expect( - getApiKeyForModel({ + getApiKeyForModelCore({ model: { id: "platform-model", provider: "openai", @@ -1645,7 +1645,7 @@ describe("resolveApiKeyForProvider", () => { "OPENCLAW_TEST_PROVIDER_SUBSCRIPTION_TOKEN", "env-subscription-credential", async () => { - const resolved = await getApiKeyForModel({ + const resolved = await getApiKeyForModelCore({ model: { id: "subscription-model", provider: "custom-token-env", @@ -1758,7 +1758,7 @@ describe("resolveApiKeyForProvider", () => { sourceConfig, ); - const resolved = await getApiKeyForModel({ + const resolved = await getApiKeyForModelCore({ model: { id: "subscription-model", provider: "custom-oauth-ref", @@ -2013,7 +2013,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers }); it("resolves synthetic auth when model overrides api to ollama within a non-ollama provider", async () => { - const auth = await getApiKeyForModel({ + const auth = await getApiKeyForModelCore({ model: { id: "my-router/local-llama", name: "Local Llama", @@ -2254,7 +2254,7 @@ describe("resolveApiKeyForProvider – synthetic local auth for custom providers }); it("uses implicit aws-sdk auth for built-in Bedrock Converse models", async () => { - const auth = await getApiKeyForModel({ + const auth = await getApiKeyForModelCore({ model: { id: "us.anthropic.claude-sonnet-4-6-v1", name: "Claude Sonnet", diff --git a/src/agents/model-auth.ts b/src/agents/model-auth.ts index cdb1cc4e3f1a..50d543e26ff0 100644 --- a/src/agents/model-auth.ts +++ b/src/agents/model-auth.ts @@ -12,7 +12,7 @@ export { applyAuthHeaderOverride, applyLocalNoAuthHeaderOverride, applySecretRefHeaderSentinels, - getApiKeyForModel, + getApiKeyForModelCore, hasAvailableAuthForProvider, resolveModelAuthMode, } from "./model-auth-model.js"; diff --git a/src/agents/model-suppression.runtime.ts b/src/agents/model-suppression.runtime.ts index a0b33bd59ed7..62d4bf5af273 100644 --- a/src/agents/model-suppression.runtime.ts +++ b/src/agents/model-suppression.runtime.ts @@ -4,14 +4,14 @@ * the full suppression implementation at module load. */ import { - buildShouldSuppressBuiltInModel as buildShouldSuppressBuiltInModelImpl, + buildShouldSuppressBuiltInModelCore, shouldSuppressBuiltInModel as shouldSuppressBuiltInModelImpl, } from "./model-suppression.js"; type ShouldSuppressBuiltInModel = typeof import("./model-suppression.js").shouldSuppressBuiltInModel; type BuildShouldSuppressBuiltInModel = - typeof import("./model-suppression.js").buildShouldSuppressBuiltInModel; + typeof import("./model-suppression.js").buildShouldSuppressBuiltInModelCore; /** Runtime-forwarded predicate for hiding bundled models. */ export function shouldSuppressBuiltInModel( @@ -24,5 +24,5 @@ export function shouldSuppressBuiltInModel( export function buildShouldSuppressBuiltInModel( ...args: Parameters ): ReturnType { - return buildShouldSuppressBuiltInModelImpl(...args); + return buildShouldSuppressBuiltInModelCore(...args); } diff --git a/src/agents/model-suppression.test.ts b/src/agents/model-suppression.test.ts index 1145420ae004..cd9a251c476e 100644 --- a/src/agents/model-suppression.test.ts +++ b/src/agents/model-suppression.test.ts @@ -15,7 +15,7 @@ vi.mock("../plugins/manifest-model-suppression.js", () => ({ import { setCurrentPluginMetadataSnapshotState } from "../plugins/current-plugin-metadata-state.js"; import { clearPluginMetadataLifecycleCaches } from "../plugins/plugin-metadata-lifecycle.js"; import { - buildShouldSuppressBuiltInModel, + buildShouldSuppressBuiltInModelCore, shouldSuppressBuiltInModel, } from "./model-suppression.js"; @@ -147,7 +147,7 @@ describe("model suppression", () => { expect(secondResolver).toHaveBeenCalledOnce(); }); - describe("buildShouldSuppressBuiltInModel", () => { + describe("buildShouldSuppressBuiltInModelCore", () => { beforeEach(() => { mocks.buildManifestBuiltInModelSuppressionResolver.mockReset(); }); @@ -160,7 +160,7 @@ describe("model suppression", () => { const config = {}; mocks.buildManifestBuiltInModelSuppressionResolver.mockReturnValueOnce(resolver); - const shouldSuppress = buildShouldSuppressBuiltInModel({ config }); + const shouldSuppress = buildShouldSuppressBuiltInModelCore({ config }); expect(shouldSuppress({ provider: "bedrock", id: "Claude-3" })).toBe(true); expect(shouldSuppress({ provider: "aws-bedrock", id: "claude-4" })).toBe(false); @@ -183,7 +183,7 @@ describe("model suppression", () => { const resolver = vi.fn(); mocks.buildManifestBuiltInModelSuppressionResolver.mockReturnValueOnce(resolver); - const shouldSuppress = buildShouldSuppressBuiltInModel({}); + const shouldSuppress = buildShouldSuppressBuiltInModelCore({}); expect(shouldSuppress({ provider: "openai", id: "" })).toBe(false); expect(shouldSuppress({ provider: "", id: "gpt-5.5" })).toBe(false); diff --git a/src/agents/model-suppression.ts b/src/agents/model-suppression.ts index 07e58a8d2cc3..e981b1bc5c7d 100644 --- a/src/agents/model-suppression.ts +++ b/src/agents/model-suppression.ts @@ -172,7 +172,7 @@ export function buildSuppressedBuiltInModelError(params: { } /** Build a reusable suppression predicate for repeated catalog filtering. */ -export function buildShouldSuppressBuiltInModel(params: { +export function buildShouldSuppressBuiltInModelCore(params: { config?: OpenClawConfig; workspaceDir?: string; }): (input: { provider?: string | null; id?: string | null; baseUrl?: string | null }) => boolean { diff --git a/src/agents/models.profiles.live.test.ts b/src/agents/models.profiles.live.test.ts index 44d61a9c012d..24cd4cbe3d84 100644 --- a/src/agents/models.profiles.live.test.ts +++ b/src/agents/models.profiles.live.test.ts @@ -49,7 +49,7 @@ import { isLiveRateLimitDrift, } from "./live-test-provider-drift.test-support.js"; import { - getApiKeyForModel, + getApiKeyForModelCore, requireApiKey, resolveUsableCustomProviderApiKey, } from "./model-auth.js"; @@ -487,7 +487,7 @@ async function resolveLiveModelApiKeyInfo(params: { model: Model; cfg: OpenClawConfig; requireProfileKeys: boolean; -}): Promise>> { +}): Promise>> { if (isLiveLocalOllamaModel(params.model, params.cfg)) { const configuredKey = canReuseConfiguredLocalOllamaApiKey(params.model, params.cfg) ? resolveUsableCustomProviderApiKey({ @@ -508,7 +508,7 @@ async function resolveLiveModelApiKeyInfo(params: { mode: "api-key", }; } - return await getApiKeyForModel({ + return await getApiKeyForModelCore({ model: params.model, cfg: params.cfg, credentialPrecedence: resolveLiveCredentialPrecedence( @@ -1829,7 +1829,7 @@ describeLive("live models (profile keys)", () => { const skipped: Array<{ model: string; reason: string }> = []; const candidates: Array<{ model: Model; - apiKeyInfo: Awaited>; + apiKeyInfo: Awaited>; }> = []; for (const model of models) { diff --git a/src/agents/openai-reasoning-compat.live.test.ts b/src/agents/openai-reasoning-compat.live.test.ts index 27a8057984fd..4e7e3374c0bc 100644 --- a/src/agents/openai-reasoning-compat.live.test.ts +++ b/src/agents/openai-reasoning-compat.live.test.ts @@ -16,7 +16,7 @@ import { readLiveTestConfig, resolveLiveCredentialPrecedence, } from "./live-test-helpers.js"; -import { getApiKeyForModel, requireApiKey } from "./model-auth.js"; +import { getApiKeyForModelCore, requireApiKey } from "./model-auth.js"; import { ensureOpenClawModelsJson } from "./models-config.js"; const LIVE = isLiveTestEnabled(); @@ -118,7 +118,7 @@ describeLive("openai reasoning compat live", () => { let apiKeyInfo; try { - apiKeyInfo = await getApiKeyForModel({ + apiKeyInfo = await getApiKeyForModelCore({ model, cfg, credentialPrecedence: resolveLiveCredentialPrecedence( @@ -178,7 +178,7 @@ describeLive("openai reasoning compat live", () => { let apiKeyInfo; try { - apiKeyInfo = await getApiKeyForModel({ + apiKeyInfo = await getApiKeyForModelCore({ model, cfg, credentialPrecedence: resolveLiveCredentialPrecedence( diff --git a/src/agents/openclaw-tools.ts b/src/agents/openclaw-tools.ts index 630ad186cc09..153868710153 100644 --- a/src/agents/openclaw-tools.ts +++ b/src/agents/openclaw-tools.ts @@ -14,7 +14,7 @@ import { isEmbeddedMode } from "../infra/embedded-mode.js"; import { formatErrorMessage } from "../infra/errors.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; import { getActiveSecretsRuntimeConfigSnapshot } from "../secrets/runtime-state.js"; -import { getActiveRuntimeWebToolsMetadata } from "../secrets/runtime-web-tools-state.js"; +import { getActiveRuntimeWebToolsMetadataFromState } from "../secrets/runtime-web-tools-state.js"; import { isCronRunSessionKey } from "../sessions/session-key-utils.js"; import type { SkillWorkshopRunOptions } from "../skills/workshop/types.js"; import { resolveTranscriptsConfig } from "../transcripts/config.js"; @@ -265,7 +265,7 @@ export function createOpenClawTools( // Scheduled turns keep delivery routing live, but Gateway authorization remains bound to the // authenticated creator account captured in the immutable scheduled authority envelope. const gatewayCallerAccountId = options?.gatewayCallerAccountId ?? options?.agentAccountId; - const runtimeWebTools = getActiveRuntimeWebToolsMetadata(); + const runtimeWebTools = getActiveRuntimeWebToolsMetadataFromState(); const sandbox = options?.sandboxRoot && options?.sandboxFsBridge ? { root: options.sandboxRoot, bridge: options.sandboxFsBridge } diff --git a/src/agents/prepared-model-runtime.test-harness.ts b/src/agents/prepared-model-runtime.test-harness.ts index fe78e62cd8fd..1dee6c406061 100644 --- a/src/agents/prepared-model-runtime.test-harness.ts +++ b/src/agents/prepared-model-runtime.test-harness.ts @@ -181,7 +181,7 @@ vi.mock("./auth-profiles/runtime-materializations.js", () => ({ })); vi.mock("./auth-profiles/runtime-snapshots.js", () => ({ - getPreparedRuntimeAuthProfileStoreSnapshot: () => preparedModelRuntimeMocks.preparedAuthStore, + getPreparedRuntimeAuthProfileStoreSnapshotCore: () => preparedModelRuntimeMocks.preparedAuthStore, getRuntimeAuthProfileStoreSnapshot: () => preparedModelRuntimeMocks.preparedAuthStore, getRuntimeAuthProfileStoreSnapshotRevision: () => 0, registerRuntimeAuthProfileStoreMutationListener: ( diff --git a/src/agents/runtime-plan/prepare-auth.test.ts b/src/agents/runtime-plan/prepare-auth.test.ts index 815a31e4467f..d821d212a553 100644 --- a/src/agents/runtime-plan/prepare-auth.test.ts +++ b/src/agents/runtime-plan/prepare-auth.test.ts @@ -3,7 +3,7 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js"; import type { Model } from "../../llm/types.js"; import type { AuthProfileStore } from "../auth-profiles.js"; import { resolveAgentHarnessPreparedAuthSupport } from "../harness/support.js"; -import { getApiKeyForModel } from "../model-auth.js"; +import { getApiKeyForModelCore } from "../model-auth.js"; import { agentRuntimeAuthPlanMatchesTarget, canRunPreparedAgentRuntimeAuthAttempt, @@ -1219,7 +1219,7 @@ describe("prepareAgentRuntimeAuthPlan", () => { maxTokens: 128_000, } as Model; const profileAttempt = prepared.attempts[0]; - const profileResolved = await getApiKeyForModel({ + const profileResolved = await getApiKeyForModelCore({ model, cfg: config, profileId: profileAttempt?.profileId, @@ -1417,7 +1417,7 @@ describe("prepareAgentRuntimeAuthPlan", () => { }, }); - const resolved = await getApiKeyForModel({ + const resolved = await getApiKeyForModelCore({ model: { id: "gpt-5.5", name: "GPT-5.5", diff --git a/src/agents/runtime-plan/resolve-auth.ts b/src/agents/runtime-plan/resolve-auth.ts index a7cc62dd3b7e..a5de1ae45f25 100644 --- a/src/agents/runtime-plan/resolve-auth.ts +++ b/src/agents/runtime-plan/resolve-auth.ts @@ -4,7 +4,7 @@ import { SecretSurfaceUnavailableError } from "../../secrets/runtime-degraded-st import { OAuthRefreshFailureError } from "../auth-profiles/oauth-refresh-failure.js"; import type { AuthProfileStore } from "../auth-profiles/types.js"; import { isProfileInCooldown } from "../auth-profiles/usage-state.js"; -import { getApiKeyForModel } from "../model-auth.js"; +import { getApiKeyForModelCore } from "../model-auth.js"; import { providerModelRouteAcceptsAuthMode } from "../provider-model-route-auth.js"; import { shouldForceDirectAuthFallbackModelResolve } from "./credential-scoped-model.js"; import { sameAgentRuntimeAuthModelRoute } from "./model-route.js"; @@ -16,7 +16,7 @@ import { import type { AgentRuntimeAuthPlan } from "./types.js"; type PreparedRuntimeModelAuthResolution = Readonly<{ - auth: Awaited>; + auth: Awaited>; plan: AgentRuntimeAuthPlan; }>; @@ -212,7 +212,7 @@ export function scopeAuthProfileStoreToPreparedPlan( function applyResolvedAuthToPlan(params: { plan: AgentRuntimeAuthPlan; - auth: Awaited>; + auth: Awaited>; candidates: string[]; }): AgentRuntimeAuthPlan { const profileId = params.auth.profileId?.trim(); @@ -242,7 +242,7 @@ function applyResolvedAuthToPlan(params: { function assertResolvedAuthMatchesPreparedRoute(params: { plan: AgentRuntimeAuthPlan; - auth: Awaited>; + auth: Awaited>; }): void { const route = params.plan.modelRoute; if ( @@ -261,7 +261,7 @@ function assertResolvedAuthMatchesPreparedRoute(params: { /** Resolves prepared same-route candidates without pinning the first unresolved profile. */ export async function resolvePreparedRuntimeModelAuth( - params: Omit[0], "profileId"> & { + params: Omit[0], "profileId"> & { plan: AgentRuntimeAuthPlan; }, ): Promise { @@ -275,7 +275,7 @@ export async function resolvePreparedRuntimeModelAuth( if (candidates.length === 0) { // The planner selected direct auth. Resolve only env/config material so an // unrelated full store cannot replace or pre-reject that immutable source. - const auth = await getApiKeyForModel({ + const auth = await getApiKeyForModelCore({ ...authParams, store: { version: 1, profiles: {} }, lockedProfile: false, @@ -286,7 +286,7 @@ export async function resolvePreparedRuntimeModelAuth( return { auth, plan: applyResolvedAuthToPlan({ plan, auth, candidates }) }; } if (plan.forwardedAuthProfileSource !== "auto") { - const auth = await getApiKeyForModel({ + const auth = await getApiKeyForModelCore({ ...authParams, profileId: plan.forwardedAuthProfileId, lockedProfile: Boolean(plan.forwardedAuthProfileId), @@ -314,7 +314,7 @@ export async function resolvePreparedRuntimeModelAuth( let refreshFailure: OAuthRefreshFailureError | undefined; for (const profileId of currentCandidates) { try { - const auth = await getApiKeyForModel({ + const auth = await getApiKeyForModelCore({ ...authParams, profileId, // This loop owns fallback order. Pin each lookup so the generic auth diff --git a/src/agents/sessions/auth-storage.ts b/src/agents/sessions/auth-storage.ts index 70bd37963e67..b2db8de3891d 100644 --- a/src/agents/sessions/auth-storage.ts +++ b/src/agents/sessions/auth-storage.ts @@ -26,7 +26,7 @@ import { } from "../auth-profiles/legacy-source-diagnostic.js"; import { resolveOAuthRefreshLockPath } from "../auth-profiles/paths.js"; import { loadPersistedAuthProfileStore } from "../auth-profiles/persisted.js"; -import { getRuntimeAuthProfileStoreSnapshot } from "../auth-profiles/runtime-snapshots.js"; +import { getRuntimeAuthProfileStoreSnapshotCore } from "../auth-profiles/runtime-snapshots.js"; import { inspectPersistedAuthProfileStateRaw, inspectPersistedAuthProfileStoreRaw, @@ -297,7 +297,7 @@ class SqliteAuthStorageBackend implements AuthStorageBackend { ) {} private resolveMaterializedRuntimeStores(): AuthProfileStore[] { - const current = getRuntimeAuthProfileStoreSnapshot(this.agentDir); + const current = getRuntimeAuthProfileStoreSnapshotCore(this.agentDir); // A current lifecycle snapshot is authoritative, including an unresolved // ref after failed/revoked secrets reload. Prepared data is bootstrap-only. return current ? [current] : this.preparedStore ? [this.preparedStore] : []; @@ -446,7 +446,7 @@ export class AuthStorage { static forAgent(agentDir: string = getAgentDir()): AuthStorage { assertAuthProfileMigrationReady(agentDir); const preparedStore = - getRuntimeAuthProfileStoreSnapshot(agentDir) ?? + getRuntimeAuthProfileStoreSnapshotCore(agentDir) ?? loadAuthProfileStoreForSecretsRuntime(agentDir); assertAuthStorageSecretRefsMaterialized(preparedStore); return new AuthStorage(new SqliteAuthStorageBackend(agentDir, preparedStore), agentDir); diff --git a/src/agents/simple-completion-runtime.test.ts b/src/agents/simple-completion-runtime.test.ts index 69051cfd31ee..b0f934deeff7 100644 --- a/src/agents/simple-completion-runtime.test.ts +++ b/src/agents/simple-completion-runtime.test.ts @@ -70,7 +70,7 @@ vi.mock("./model-auth.js", () => ({ (auth: { source: string; mode: string }, provider: string) => `No API key resolved for provider "${provider}" (auth mode: ${auth.mode}, checked: ${auth.source}).`, ), - getApiKeyForModel: hoisted.getApiKeyForModelMock, + getApiKeyForModelCore: hoisted.getApiKeyForModelMock, resolveApiKeyForProvider: hoisted.getApiKeyForModelMock, applyLocalNoAuthHeaderOverride: hoisted.applyLocalNoAuthHeaderOverrideMock, })); @@ -478,7 +478,7 @@ describe("prepareSimpleCompletionModel", () => { expect(result.model.baseUrl).toBe("https://api.copilot.enterprise.example"); }); - it("returns error when getApiKeyForModel throws", async () => { + it("returns error when getApiKeyForModelCore throws", async () => { hoisted.getApiKeyForModelMock.mockRejectedValueOnce(new Error("Profile not found: copilot")); const result = await prepareSimpleCompletionModel({ diff --git a/src/agents/simple-completion-runtime.ts b/src/agents/simple-completion-runtime.ts index f1650085d133..ba0353e1ca1c 100644 --- a/src/agents/simple-completion-runtime.ts +++ b/src/agents/simple-completion-runtime.ts @@ -35,7 +35,7 @@ import { applySecretRefHeaderSentinels, applyLocalNoAuthHeaderOverride, formatMissingAuthError, - getApiKeyForModel, + getApiKeyForModelCore, resolveApiKeyForProvider, type ResolvedProviderAuth, } from "./model-auth.js"; @@ -306,7 +306,7 @@ export async function prepareSimpleCompletionModel(params: { modelId: initialModel.id, secretSentinels: true, }) - : await getApiKeyForModel({ + : await getApiKeyForModelCore({ model: initialModel, cfg: params.cfg, agentDir: params.agentDir, @@ -396,7 +396,7 @@ export async function prepareSimpleCompletionModel(params: { ), })) ?? initialModel; if (resolvesAuthBeforePhysicalRoute) { - auth = await getApiKeyForModel({ + auth = await getApiKeyForModelCore({ model: resolvedModel, cfg: params.cfg, agentDir: params.agentDir, diff --git a/src/agents/tool-replay-repair.live.test.ts b/src/agents/tool-replay-repair.live.test.ts index 75ce744da845..7bfca32cba82 100644 --- a/src/agents/tool-replay-repair.live.test.ts +++ b/src/agents/tool-replay-repair.live.test.ts @@ -20,7 +20,7 @@ import { resolveLiveCredentialPrecedence, type CompleteSimpleContent, } from "./live-test-helpers.js"; -import { getApiKeyForModel, requireApiKey } from "./model-auth.js"; +import { getApiKeyForModelCore, requireApiKey } from "./model-auth.js"; import { ensureOpenClawModelsJson } from "./models-config.js"; import { transformTransportMessages } from "./transport-message-transform.js"; @@ -261,7 +261,7 @@ describeLive("tool replay repair live", () => { let apiKeyInfo; try { - apiKeyInfo = await getApiKeyForModel({ + apiKeyInfo = await getApiKeyForModelCore({ model, cfg, credentialPrecedence: resolveLiveCredentialPrecedence( @@ -374,7 +374,7 @@ describeLive("tool replay repair live", () => { let apiKeyInfo; try { - apiKeyInfo = await getApiKeyForModel({ + apiKeyInfo = await getApiKeyForModelCore({ model, cfg, credentialPrecedence: resolveLiveCredentialPrecedence( diff --git a/src/agents/tools/image-tool.custom-provider-auth.regression.test.ts b/src/agents/tools/image-tool.custom-provider-auth.regression.test.ts index e22c8baf65a6..614d98d2840b 100644 --- a/src/agents/tools/image-tool.custom-provider-auth.regression.test.ts +++ b/src/agents/tools/image-tool.custom-provider-auth.regression.test.ts @@ -7,7 +7,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; import type { ModelDefinitionConfig } from "../../config/types.models.js"; import type { ImageDescriptionRequest } from "../../plugin-sdk/media-understanding.js"; -import { getApiKeyForModel, hasUsableCustomProviderApiKey } from "../model-auth.js"; +import { getApiKeyForModelCore, hasUsableCustomProviderApiKey } from "../model-auth.js"; import { resolveImageToolFactoryAvailable } from "../openclaw-tools.media-factory-plan.js"; import { createImageTool } from "./image-tool.js"; import { resolveImageModelConfigForTool, testing } from "./image-tool.test-support.js"; @@ -195,7 +195,7 @@ describe("image custom provider auth regression", () => { // but execution still resolves the config-backed image-model key. await withEmptyAgentDir(async (agentDir) => { const cfg = createUserReportedConfig(); - const auth = await getApiKeyForModel({ + const auth = await getApiKeyForModelCore({ model: { id: USER_MODEL, name: USER_MODEL, diff --git a/src/agents/tools/media-tool-shared.ts b/src/agents/tools/media-tool-shared.ts index 477939079ecd..bb8a752617d7 100644 --- a/src/agents/tools/media-tool-shared.ts +++ b/src/agents/tools/media-tool-shared.ts @@ -16,7 +16,7 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js"; import type { SsrFPolicy } from "../../infra/net/ssrf.js"; import type { Model } from "../../llm/types.js"; import { resolveChannelInboundAttachmentRootsForChannel } from "../../media/channel-inbound-roots.js"; -import { getDefaultLocalRoots } from "../../media/local-media-access.js"; +import { getDefaultLocalRootsCore } from "../../media/local-media-access.js"; import { readSnakeCaseParamRaw } from "../../param-key.js"; import { loadCapabilityManifestSnapshot } from "../../plugins/capability-provider-runtime.js"; import { listAvailableManifestContractValues } from "../../plugins/manifest-contract-eligibility.js"; @@ -45,7 +45,11 @@ import { resolveDefaultModelRef, type ToolModelConfig, } from "./model-config.helpers.js"; -import { getApiKeyForModel, normalizeWorkspaceDir, requireApiKey } from "./tool-runtime.helpers.js"; +import { + getApiKeyForModelCore, + normalizeWorkspaceDir, + requireApiKey, +} from "./tool-runtime.helpers.js"; type TextToolAttempt = { provider: string; @@ -597,7 +601,7 @@ export function resolveMediaToolLocalRoots( } // Channel inbound attachment roots stay separate: those paths are scoped to inbound media // access, not broad host-local file reads. - const roots = getDefaultLocalRoots(); + const roots = getDefaultLocalRootsCore(); return uniqueStrings([...roots, ...(workspaceDir ? [workspaceDir] : [])]); } @@ -730,7 +734,7 @@ export async function resolveModelRuntimeApiKey(params: { setRuntimeApiKey: (provider: string, apiKey: string) => void; }; }): Promise { - const apiKeyInfo = await getApiKeyForModel({ + const apiKeyInfo = await getApiKeyForModelCore({ model: params.model, cfg: params.cfg, agentDir: params.agentDir, diff --git a/src/agents/tools/pdf-tool.test-support.ts b/src/agents/tools/pdf-tool.test-support.ts index 8d34e39f5233..45e1ff5f2fff 100644 --- a/src/agents/tools/pdf-tool.test-support.ts +++ b/src/agents/tools/pdf-tool.test-support.ts @@ -110,7 +110,7 @@ export function createPdfToolInfraStub(completeMock: Mock) { wrote: false, }); - vi.spyOn(modelAuth, "getApiKeyForModel").mockResolvedValue({ apiKey: "test-key" } as never); + vi.spyOn(modelAuth, "getApiKeyForModelCore").mockResolvedValue({ apiKey: "test-key" } as never); vi.spyOn(modelAuth, "requireApiKey").mockReturnValue("test-key"); return { loadSpy, release, setRuntimeApiKey }; diff --git a/src/agents/tools/pdf-tool.test.ts b/src/agents/tools/pdf-tool.test.ts index 6686296f97ff..f78e76aa3d52 100644 --- a/src/agents/tools/pdf-tool.test.ts +++ b/src/agents/tools/pdf-tool.test.ts @@ -330,7 +330,7 @@ describe("createPdfTool", () => { const [, loadOptions] = firstMockCall(loadSpy, "loadWebMediaRaw"); expectFields(loadOptions, { maxBytes: 524_288 }); - expect(modelAuth.getApiKeyForModel).toHaveBeenCalledWith( + expect(modelAuth.getApiKeyForModelCore).toHaveBeenCalledWith( expect.objectContaining({ secretSentinels: true }), ); }); @@ -833,7 +833,7 @@ describe("createPdfTool", () => { api: "bedrock-converse-stream", input: ["text", "image"], }); - vi.mocked(modelAuth.getApiKeyForModel).mockResolvedValue({ + vi.mocked(modelAuth.getApiKeyForModelCore).mockResolvedValue({ apiKey: "", source: "aws-sdk default chain", mode: "aws-sdk", diff --git a/src/agents/tools/tool-runtime.helpers.ts b/src/agents/tools/tool-runtime.helpers.ts index 3ceb12a3e080..6e1fbe18a8cf 100644 --- a/src/agents/tools/tool-runtime.helpers.ts +++ b/src/agents/tools/tool-runtime.helpers.ts @@ -4,7 +4,7 @@ * Tools import from this module when they need model auth, fallback, discovery, * sandbox media paths, or workspace helpers without depending on broad agent barrels. */ -export { getApiKeyForModel, requireApiKey } from "../model-auth.js"; +export { getApiKeyForModelCore, requireApiKey } from "../model-auth.js"; export { runWithImageModelFallback } from "../model-fallback-image.js"; export { createSandboxBridgeReadFile, diff --git a/src/agents/tools/web-fetch.provider-fallback.test.ts b/src/agents/tools/web-fetch.provider-fallback.test.ts index 88615f0aaea7..7ec66c683fcb 100644 --- a/src/agents/tools/web-fetch.provider-fallback.test.ts +++ b/src/agents/tools/web-fetch.provider-fallback.test.ts @@ -25,7 +25,7 @@ vi.mock("../../secrets/runtime-state.js", () => ({ getActiveSecretsRuntimeConfigSnapshot: () => runtimeState.activeSecretsRuntimeSnapshot, })); vi.mock("../../secrets/runtime-web-tools-state.js", () => ({ - getActiveRuntimeWebToolsMetadata: () => runtimeState.activeRuntimeWebToolsMetadata, + getActiveRuntimeWebToolsMetadataFromState: () => runtimeState.activeRuntimeWebToolsMetadata, })); describe("web_fetch provider fallback normalization", () => { diff --git a/src/agents/tools/web-search.late-bind.test.ts b/src/agents/tools/web-search.late-bind.test.ts index 42ac9f8fc7c0..adf62cc30dfc 100644 --- a/src/agents/tools/web-search.late-bind.test.ts +++ b/src/agents/tools/web-search.late-bind.test.ts @@ -7,7 +7,7 @@ import { createWebSearchTool } from "./web-search.js"; const mocks = vi.hoisted(() => ({ runWebSearch: vi.fn(), resolveManifestContractOwnerPluginId: vi.fn(), - getActiveRuntimeWebToolsMetadata: vi.fn(), + getActiveRuntimeWebToolsMetadataFromState: vi.fn(), getActiveSecretsRuntimeConfigSnapshot: vi.fn(), })); @@ -21,7 +21,7 @@ vi.mock("../../plugins/plugin-registry.js", () => ({ })); vi.mock("../../secrets/runtime-web-tools-state.js", () => ({ - getActiveRuntimeWebToolsMetadata: mocks.getActiveRuntimeWebToolsMetadata, + getActiveRuntimeWebToolsMetadataFromState: mocks.getActiveRuntimeWebToolsMetadataFromState, })); vi.mock("../../secrets/runtime-state.js", () => ({ @@ -49,8 +49,8 @@ describe("web_search late-bound runtime fallback", () => { }); mocks.resolveManifestContractOwnerPluginId.mockReset(); mocks.resolveManifestContractOwnerPluginId.mockReturnValue(undefined); - mocks.getActiveRuntimeWebToolsMetadata.mockReset(); - mocks.getActiveRuntimeWebToolsMetadata.mockReturnValue(null); + mocks.getActiveRuntimeWebToolsMetadataFromState.mockReset(); + mocks.getActiveRuntimeWebToolsMetadataFromState.mockReturnValue(null); mocks.getActiveSecretsRuntimeConfigSnapshot.mockReset(); mocks.getActiveSecretsRuntimeConfigSnapshot.mockReturnValue(null); }); @@ -136,7 +136,7 @@ describe("web_search late-bound runtime fallback", () => { it("prefers active runtime metadata over options.runtimeWebSearch when present", async () => { // Active runtime metadata reflects the newest credential snapshot; fallback // options only cover tools created before that state exists. - mocks.getActiveRuntimeWebToolsMetadata.mockReturnValue({ + mocks.getActiveRuntimeWebToolsMetadataFromState.mockReturnValue({ search: { selectedProvider: "perplexity", providerConfigured: "perplexity", diff --git a/src/agents/tools/web-tool-runtime-context.test.ts b/src/agents/tools/web-tool-runtime-context.test.ts index 3c488a574f01..b3ecbc25749d 100644 --- a/src/agents/tools/web-tool-runtime-context.test.ts +++ b/src/agents/tools/web-tool-runtime-context.test.ts @@ -7,7 +7,7 @@ import { } from "./web-tool-runtime-context.js"; const mocks = vi.hoisted(() => ({ - getActiveRuntimeWebToolsMetadata: vi.fn(), + getActiveRuntimeWebToolsMetadataFromState: vi.fn(), getActiveSecretsRuntimeConfigSnapshot: vi.fn(), resolveManifestContractOwnerPluginId: vi.fn(), })); @@ -17,7 +17,7 @@ vi.mock("../../plugins/plugin-registry.js", () => ({ })); vi.mock("../../secrets/runtime-web-tools-state.js", () => ({ - getActiveRuntimeWebToolsMetadata: mocks.getActiveRuntimeWebToolsMetadata, + getActiveRuntimeWebToolsMetadataFromState: mocks.getActiveRuntimeWebToolsMetadataFromState, })); vi.mock("../../secrets/runtime-state.js", () => ({ @@ -36,8 +36,8 @@ function latestOwnerLookupParams(): Record { describe("web tool runtime context", () => { beforeEach(() => { - mocks.getActiveRuntimeWebToolsMetadata.mockReset(); - mocks.getActiveRuntimeWebToolsMetadata.mockReturnValue(null); + mocks.getActiveRuntimeWebToolsMetadataFromState.mockReset(); + mocks.getActiveRuntimeWebToolsMetadataFromState.mockReturnValue(null); mocks.getActiveSecretsRuntimeConfigSnapshot.mockReset(); mocks.getActiveSecretsRuntimeConfigSnapshot.mockReturnValue(null); mocks.resolveManifestContractOwnerPluginId.mockReset(); @@ -49,7 +49,7 @@ describe("web tool runtime context", () => { tools: { web: { search: { provider: "perplexity" } } }, }; mocks.getActiveSecretsRuntimeConfigSnapshot.mockReturnValue({ config: runtimeConfig }); - mocks.getActiveRuntimeWebToolsMetadata.mockReturnValue({ + mocks.getActiveRuntimeWebToolsMetadataFromState.mockReturnValue({ search: { providerConfigured: "perplexity", providerSource: "configured", diff --git a/src/agents/tools/web-tool-runtime-context.ts b/src/agents/tools/web-tool-runtime-context.ts index d32ff5e8d495..dc6ae386738a 100644 --- a/src/agents/tools/web-tool-runtime-context.ts +++ b/src/agents/tools/web-tool-runtime-context.ts @@ -6,7 +6,7 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { resolveManifestContractOwnerPluginId } from "../../plugins/plugin-registry.js"; import { getActiveSecretsRuntimeConfigSnapshot } from "../../secrets/runtime-state.js"; -import { getActiveRuntimeWebToolsMetadata } from "../../secrets/runtime-web-tools-state.js"; +import { getActiveRuntimeWebToolsMetadataFromState } from "../../secrets/runtime-web-tools-state.js"; import type { RuntimeWebFetchMetadata, RuntimeWebSearchMetadata, @@ -61,7 +61,7 @@ function resolveWebToolRuntimeContext { const activeWebTools = - params.lateBindRuntimeConfig === true ? getActiveRuntimeWebToolsMetadata() : null; + params.lateBindRuntimeConfig === true ? getActiveRuntimeWebToolsMetadataFromState() : null; // Late-bound metadata wins over constructor-captured metadata for long-lived tool instances. const runtimeMetadata = (activeWebTools?.[params.kind] ?? params.capturedRuntimeMetadata) as | TMetadata diff --git a/src/agents/tools/web-tools.enabled-defaults.test.ts b/src/agents/tools/web-tools.enabled-defaults.test.ts index 9e8971be9ee7..d13966096da0 100644 --- a/src/agents/tools/web-tools.enabled-defaults.test.ts +++ b/src/agents/tools/web-tools.enabled-defaults.test.ts @@ -47,7 +47,7 @@ vi.mock("../../secrets/runtime-state.js", () => ({ vi.mock("../../web-search/runtime.js", async () => { const { getActivePluginRegistry } = await import("../../plugins/runtime.js"); - const { getActiveRuntimeWebToolsMetadata } = + const { getActiveRuntimeWebToolsMetadataFromState } = await import("../../secrets/runtime-web-tools-state.js"); const resolveRuntimeDefinition = (options?: { config?: unknown; @@ -58,8 +58,8 @@ vi.mock("../../web-search/runtime.js", async () => { const providerId = options?.runtimeWebSearch?.selectedProvider ?? options?.runtimeWebSearch?.providerConfigured ?? - getActiveRuntimeWebToolsMetadata()?.search?.selectedProvider ?? - getActiveRuntimeWebToolsMetadata()?.search?.providerConfigured ?? + getActiveRuntimeWebToolsMetadataFromState()?.search?.selectedProvider ?? + getActiveRuntimeWebToolsMetadataFromState()?.search?.providerConfigured ?? readConfiguredSearchProvider(options?.config); const registration = getActivePluginRegistry()?.webSearchProviders.find( (entry) => entry.provider.id === providerId, diff --git a/src/auto-reply/reply/commands-gating.test.ts b/src/auto-reply/reply/commands-gating.test.ts index 8f02436f14d0..7ea5732c4ea3 100644 --- a/src/auto-reply/reply/commands-gating.test.ts +++ b/src/auto-reply/reply/commands-gating.test.ts @@ -134,7 +134,7 @@ vi.mock("../../config/runtime-schema.js", async () => { const actual = await vi.importActual("../../config/schema.js"); return { - loadGatewayRuntimeConfigSchema: () => actual.buildConfigSchema(), + loadGatewayRuntimeConfigSchema: () => actual.buildConfigSchemaCore(), }; }); diff --git a/src/auto-reply/reply/commands-mcp.ts b/src/auto-reply/reply/commands-mcp.ts index c60d9c6bf881..1a6c850b93c7 100644 --- a/src/auto-reply/reply/commands-mcp.ts +++ b/src/auto-reply/reply/commands-mcp.ts @@ -7,7 +7,7 @@ import { } from "../../config/mcp-config.js"; import { redactSensitiveArgv } from "../../config/redact-argv.js"; import { REDACTED_SENTINEL, redactConfigObject } from "../../config/redact-snapshot.js"; -import { buildConfigSchema } from "../../config/schema.js"; +import { buildConfigSchemaCore } from "../../config/schema.js"; import type { ExecApprovalRequest } from "../../infra/exec-approvals.js"; import type { ReplyPayload } from "../types.js"; import { @@ -56,7 +56,7 @@ function redactMcpServersForDisplay(servers: Record): Record }; }; diff --git a/src/auto-reply/reply/queue/enqueue.ts b/src/auto-reply/reply/queue/enqueue.ts index 32854b67b625..67a1823aa5e7 100644 --- a/src/auto-reply/reply/queue/enqueue.ts +++ b/src/auto-reply/reply/queue/enqueue.ts @@ -3,7 +3,7 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coe import { normalizeChatType } from "../../../channels/chat-type.js"; import { logMessageQueuedWithBacklogPolicy } from "../../../logging/diagnostic-runtime.js"; import { channelRouteDedupeKey } from "../../../plugin-sdk/channel-route.js"; -import { createDeferred } from "../../../shared/deferred.js"; +import { createDeferredCore } from "../../../shared/deferred.js"; import { applyQueueDropPolicy, countPendingQueueItems, @@ -159,7 +159,7 @@ export function enqueueFollowupRun( if (!markFollowupRunEnqueued(run)) { return false; } - const { promise: acceptance, resolve: settle } = createDeferred(); + const { promise: acceptance, resolve: settle } = createDeferredCore(); run.steerPending = { predecessor: queue.steerAcceptanceTail, settle }; queue.steerAcceptanceTail = acceptance; appendQueueItem({ diff --git a/src/auto-reply/reply/reply-admission-ticket.ts b/src/auto-reply/reply/reply-admission-ticket.ts index 1b6928f53b1d..55addb1a1195 100644 --- a/src/auto-reply/reply/reply-admission-ticket.ts +++ b/src/auto-reply/reply/reply-admission-ticket.ts @@ -1,5 +1,5 @@ import { normalizeStringifiedEntries } from "@openclaw/normalization-core/string-coerce"; -import { createDeferred } from "../../shared/deferred.js"; +import { createDeferredCore } from "../../shared/deferred.js"; import { resolveGlobalMap } from "../../shared/global-singleton.js"; export const REPLY_ADMISSION_TICKET = Symbol("openclaw.replyAdmissionTicket"); @@ -21,7 +21,7 @@ export function reserveReplyAdmissionTicket( if (keys.length === 0) { return undefined; } - const { promise: completed, resolve: finish } = createDeferred(); + const { promise: completed, resolve: finish } = createDeferredCore(); const predecessors = keys.map((key) => tails.get(key) ?? Promise.resolve()); const owned = keys.map((key, index) => { const tail = predecessors[index]!.then(() => completed); diff --git a/src/auto-reply/reply/reply-run-registry.message-injection.ts b/src/auto-reply/reply/reply-run-registry.message-injection.ts index fb6d8633d791..6f6f7b20426d 100644 --- a/src/auto-reply/reply/reply-run-registry.message-injection.ts +++ b/src/auto-reply/reply/reply-run-registry.message-injection.ts @@ -1,5 +1,5 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; -import { createDeferred } from "../../shared/deferred.js"; +import { createDeferredCore } from "../../shared/deferred.js"; import { replyMessageInjectionTargetOperation, type ReplyBackendHandle, @@ -151,7 +151,7 @@ export function beginReplyMessageInjectionTarget( // sub-10-minute user messages re-arm a wedged run's staleness window forever. // Invoke before the first await. The capability owns the final synchronous // admission check, matching Codex's active-turn lock boundary. - const acceptance = createDeferred(); + const acceptance = createDeferredCore(); let acceptanceSettled = false; const settleAcceptance = (accepted: boolean) => { if (acceptanceSettled) { diff --git a/src/auto-reply/reply/reply-run-registry.operation.ts b/src/auto-reply/reply/reply-run-registry.operation.ts index de7cd833f9d1..64d0df5e534e 100644 --- a/src/auto-reply/reply/reply-run-registry.operation.ts +++ b/src/auto-reply/reply/reply-run-registry.operation.ts @@ -6,7 +6,7 @@ import { import { createAbortError } from "../../infra/abort-signal.js"; import { getAgentEventLifecycleGeneration } from "../../infra/agent-events.js"; import { diagnosticLogger as diag } from "../../logging/diagnostic-runtime.js"; -import { createDeferred } from "../../shared/deferred.js"; +import { createDeferredCore } from "../../shared/deferred.js"; import type { ReplyFollowupAdmissionBarrierTimeoutPolicy } from "./reply-dispatcher.types.js"; import * as replyRunSettle from "./reply-run-finalization-lease.js"; import { @@ -94,7 +94,7 @@ export function createReplyOperation(params: { let retainFailureUntilComplete = false; let terminalRecovery = false; let acceptedSteeredInboundAudio = false; - const ownerSettlement = createDeferred(); + const ownerSettlement = createDeferredCore(); let ownerSettled = false; const settleOwner = () => { if (ownerSettled) { diff --git a/src/auto-reply/reply/reply-run-registry.state.ts b/src/auto-reply/reply/reply-run-registry.state.ts index dedb4e681944..752c70b67070 100644 --- a/src/auto-reply/reply/reply-run-registry.state.ts +++ b/src/auto-reply/reply/reply-run-registry.state.ts @@ -5,7 +5,7 @@ import { markDiagnosticRunProgress, resolveRunStaleThresholdMs, } from "../../logging/diagnostic-run-activity.js"; -import { createDeferred } from "../../shared/deferred.js"; +import { createDeferredCore } from "../../shared/deferred.js"; import { resolveGlobalSingleton } from "../../shared/global-singleton.js"; import { resolveTimerTimeoutMs } from "../../shared/number-coercion.js"; import type { ReplyFollowupAdmissionBarrierTimeoutPolicy } from "./reply-dispatcher.types.js"; @@ -229,7 +229,7 @@ export function registerReplyOperationSuccessorBarrier(params: { sessionKeys: readonly string[]; start: () => PromiseLike; }): void { - const settlement = createDeferred(); + const settlement = createDeferredCore(); const barriers = new Set(); for (const sessionKey of new Set(params.sessionKeys.map(normalizeOptionalString))) { if (sessionKey) { diff --git a/src/channels/streaming.ts b/src/channels/streaming.ts index 663f6904ae75..1be9ced42d5d 100644 --- a/src/channels/streaming.ts +++ b/src/channels/streaming.ts @@ -18,10 +18,7 @@ import type { StreamingMode, TextChunkMode, } from "../config/types.base.js"; -import { - DEFAULT_PROGRESS_DRAFT_LABELS as SHARED_PROGRESS_DRAFT_LABELS, - selectProgressLabel, -} from "../shared/progress-labels.js"; +import { DEFAULT_PROGRESS_DRAFT_LABELS, selectProgressLabel } from "../shared/progress-labels.js"; import { asBoolean } from "../utils/boolean.js"; import { getChannelStreamingConfigObject, @@ -81,7 +78,7 @@ function asCommandTextMode(value: unknown): ChannelStreamingCommandTextMode | un return value === "raw" || value === "status" ? value : undefined; } -export const DEFAULT_PROGRESS_DRAFT_LABELS = SHARED_PROGRESS_DRAFT_LABELS; +export { DEFAULT_PROGRESS_DRAFT_LABELS } from "../shared/progress-labels.js"; // Short enough that a multi-tool turn is never silent, long enough that a // quick answer posts no draft at all: the gate only creates the draft when the diff --git a/src/cli/program/command-registry-core.ts b/src/cli/program/command-registry-core.ts index c0278ca41979..ec3ff903905c 100644 --- a/src/cli/program/command-registry-core.ts +++ b/src/cli/program/command-registry-core.ts @@ -11,7 +11,7 @@ import { import type { ProgramContext } from "./context.js"; import { getCoreCliCommandDescriptors, - getCoreCliCommandNames as getCoreDescriptorNames, + getCoreCliCommandNamesCore, } from "./core-command-descriptors.js"; import { registerCommandGroupByName, @@ -157,7 +157,7 @@ function resolveCoreCommandGroups(ctx: ProgramContext, argv: string[]): CommandG } export function getCoreCliCommandNames(): string[] { - return getCoreDescriptorNames(); + return getCoreCliCommandNamesCore(); } export async function registerCoreCliByName( diff --git a/src/cli/program/command-suggestions.ts b/src/cli/program/command-suggestions.ts index 919facbbc0e6..212ecd3fd696 100644 --- a/src/cli/program/command-suggestions.ts +++ b/src/cli/program/command-suggestions.ts @@ -1,7 +1,7 @@ import { levenshteinDistance } from "../../shared/levenshtein-distance.js"; import { formatCliCommand } from "../command-format.js"; -import { getCoreCliCommandNames } from "./core-command-descriptors.js"; -import { getSubCliEntries } from "./subcli-descriptors.js"; +import { getCoreCliCommandNamesCore } from "./core-command-descriptors.js"; +import { getSubCliEntriesCore } from "./subcli-descriptors.js"; const EXPLICIT_COMMAND_ALIASES = new Map([ ["upgrade", "update"], @@ -23,8 +23,8 @@ export function formatCliCommandSuggestions(input: string): string | undefined { } const knownCommands = uniqueSortedCommandNames([ - ...getCoreCliCommandNames(), - ...getSubCliEntries().map((entry) => entry.name), + ...getCoreCliCommandNamesCore(), + ...getSubCliEntriesCore().map((entry) => entry.name), ]); const explicitAlias = EXPLICIT_COMMAND_ALIASES.get(normalizedInput); if (explicitAlias && knownCommands.includes(explicitAlias)) { diff --git a/src/cli/program/core-command-descriptors.ts b/src/cli/program/core-command-descriptors.ts index d8e8bf8d4908..61caeb9ee9bc 100644 --- a/src/cli/program/core-command-descriptors.ts +++ b/src/cli/program/core-command-descriptors.ts @@ -148,7 +148,7 @@ export function getCoreCliCommandDescriptors(): ReadonlyArray descriptor.name); } diff --git a/src/cli/program/register.subclis-core.ts b/src/cli/program/register.subclis-core.ts index 5ba6f0f6483a..34b816125c9c 100644 --- a/src/cli/program/register.subclis-core.ts +++ b/src/cli/program/register.subclis-core.ts @@ -19,10 +19,7 @@ import { registerCommandGroups, type CommandGroupEntry, } from "./register-command-groups.js"; -import { - getSubCliEntries as getSubCliEntryDescriptors, - type SubCliDescriptor, -} from "./subcli-descriptors.js"; +import { getSubCliEntriesCore, type SubCliDescriptor } from "./subcli-descriptors.js"; export type SubCliRegistrationContext = { purpose?: "runtime" | "completion"; @@ -317,7 +314,7 @@ function resolveSubCliCommandGroups( argv: string[], context: SubCliRegistrationContext = {}, ): CommandGroupEntry[] { - const descriptors = getSubCliEntryDescriptors(); + const descriptors = getSubCliEntriesCore(); const descriptorNames = new Set(descriptors.map((descriptor) => descriptor.name)); return buildCommandGroupEntries( descriptors, @@ -329,7 +326,7 @@ function resolveSubCliCommandGroups( } export function getSubCliEntries(): ReadonlyArray { - return getSubCliEntryDescriptors(); + return getSubCliEntriesCore(); } export async function registerSubCliByNameCore( diff --git a/src/cli/program/register.subclis.ts b/src/cli/program/register.subclis.ts index a5b81ed04bd7..61ccecdbb056 100644 --- a/src/cli/program/register.subclis.ts +++ b/src/cli/program/register.subclis.ts @@ -20,7 +20,7 @@ import { registerSubCliCommandsCore, type SubCliRegistrationContext, } from "./register.subclis-core.js"; -import { getSubCliEntries as getSubCliEntryDescriptors } from "./subcli-descriptors.js"; +import { getSubCliEntriesCore as getSubCliEntryDescriptors } from "./subcli-descriptors.js"; type SubCliRegistrar = ( program: Command, diff --git a/src/cli/program/root-command-descriptions.test.ts b/src/cli/program/root-command-descriptions.test.ts index 28223a1d6779..cbe707e8750e 100644 --- a/src/cli/program/root-command-descriptions.test.ts +++ b/src/cli/program/root-command-descriptions.test.ts @@ -9,7 +9,7 @@ import { getCoreCliCommandNames, registerCoreCliByName } from "./command-registr import { createProgramContext } from "./context.js"; import { getCoreCliCommandDescriptors } from "./core-command-descriptors.js"; import { registerSubCliByName, registerSubCliCommands } from "./register.subclis.js"; -import { getSubCliEntries } from "./subcli-descriptors.js"; +import { getSubCliEntriesCore } from "./subcli-descriptors.js"; const RESERVED_CATALOG_ROOTS = { tool: "reserved so plugin registration cannot claim this unregistered root", @@ -233,7 +233,7 @@ async function registerAllBuiltInCommands(): Promise { for (const name of getCoreCliCommandNames()) { await registerCoreCliByName(program, ctx, name, argv); } - for (const entry of getSubCliEntries()) { + for (const entry of getSubCliEntriesCore()) { await registerSubCliByName(program, entry.name, argv, { purpose: "completion" }); } return program; @@ -295,7 +295,7 @@ describe("root command descriptions", () => { } } - const descriptors = [...getCoreCliCommandDescriptors(), ...getSubCliEntries()]; + const descriptors = [...getCoreCliCommandDescriptors(), ...getSubCliEntriesCore()]; const missing: string[] = []; const mismatches: string[] = []; for (const descriptor of descriptors) { diff --git a/src/cli/program/root-help.test.ts b/src/cli/program/root-help.test.ts index 9973bb14df1f..f2af270bea8f 100644 --- a/src/cli/program/root-help.test.ts +++ b/src/cli/program/root-help.test.ts @@ -38,7 +38,7 @@ vi.mock("./subcli-descriptors.js", () => ({ hasSubcommands: true, }, ], - getSubCliEntries: () => [ + getSubCliEntriesCore: () => [ { name: "config", description: "Manage config", diff --git a/src/cli/program/root-help.ts b/src/cli/program/root-help.ts index b59b503fd2d5..2c7b58c04e79 100644 --- a/src/cli/program/root-help.ts +++ b/src/cli/program/root-help.ts @@ -10,7 +10,7 @@ import { } from "./command-descriptor-utils.js"; import { getCoreCliCommandDescriptors } from "./core-command-descriptors.js"; import { configureProgramHelp, formatProgramHelpOutput } from "./help.js"; -import { getSubCliEntries } from "./subcli-descriptors.js"; +import { getSubCliEntriesCore } from "./subcli-descriptors.js"; /** Options for rendering root help without fully registering the live CLI. */ export type RootHelpRenderOptions = Pick & { @@ -48,7 +48,7 @@ async function buildRootHelpProgram(renderOptions?: RootHelpRenderOptions): Prom program, collectUniqueCommandDescriptors([ getCoreCliCommandDescriptors(), - getSubCliEntries(), + getSubCliEntriesCore(), pluginDescriptors, ]), ); diff --git a/src/cli/program/subcli-descriptors.test.ts b/src/cli/program/subcli-descriptors.test.ts index 0bde846767e2..473109a43a9c 100644 --- a/src/cli/program/subcli-descriptors.test.ts +++ b/src/cli/program/subcli-descriptors.test.ts @@ -45,10 +45,10 @@ describe("sub-cli descriptors", () => { it("keeps the exported descriptor list aligned with private QA visibility when disabled (#83927)", async () => { delete process.env.OPENCLAW_ENABLE_PRIVATE_QA_CLI; - const { SUB_CLI_DESCRIPTORS, getSubCliEntries } = await importSubCliDescriptors(); + const { SUB_CLI_DESCRIPTORS, getSubCliEntriesCore } = await importSubCliDescriptors(); const exportedNames = descriptorNames(SUB_CLI_DESCRIPTORS); - expect(exportedNames).toEqual(descriptorNames(getSubCliEntries())); + expect(exportedNames).toEqual(descriptorNames(getSubCliEntriesCore())); expect(exportedNames).not.toContain("qa"); }); @@ -73,12 +73,12 @@ describe("sub-cli descriptors", () => { const { SUB_CLI_DESCRIPTORS, getSubCliCommandsWithSubcommands, - getSubCliEntries, + getSubCliEntriesCore, getSubCliParentDefaultHelpCommands, } = await importSubCliDescriptors(); const exportedNames = descriptorNames(SUB_CLI_DESCRIPTORS); - expect(exportedNames).toEqual(descriptorNames(getSubCliEntries())); + expect(exportedNames).toEqual(descriptorNames(getSubCliEntriesCore())); expect(exportedNames).toContain("qa"); expect(getSubCliCommandsWithSubcommands()).toContain("qa"); expect(getSubCliParentDefaultHelpCommands()).not.toContain("qa"); diff --git a/src/cli/program/subcli-descriptors.ts b/src/cli/program/subcli-descriptors.ts index 62c7a1b5441c..8167fb4c29f5 100644 --- a/src/cli/program/subcli-descriptors.ts +++ b/src/cli/program/subcli-descriptors.ts @@ -263,7 +263,7 @@ export const SUB_CLI_DESCRIPTORS = filterPrivateQaItems( ); /** Return visible sub-CLI descriptors in help/registration order. */ -export function getSubCliEntries(): ReadonlyArray { +export function getSubCliEntriesCore(): ReadonlyArray { return filterPrivateQaItems( subCliCommandCatalog.getDescriptors(), (descriptor) => descriptor.name, diff --git a/src/cli/run-main.ts b/src/cli/run-main.ts index e659af9eb807..cd3bdc4cc924 100644 --- a/src/cli/run-main.ts +++ b/src/cli/run-main.ts @@ -53,9 +53,9 @@ import { applyCliProfileEnv, parseCliProfileArgs } from "./profile.js"; import { formatCliCommandSuggestions } from "./program/command-suggestions.js"; import { getCoreCliCommandDescriptors, - getCoreCliCommandNames, + getCoreCliCommandNamesCore, } from "./program/core-command-descriptors.js"; -import { getSubCliEntries } from "./program/subcli-descriptors.js"; +import { getSubCliEntriesCore } from "./program/subcli-descriptors.js"; import { resolveMissingPluginCommandMessage, rewriteUpdateFlagArgv, @@ -865,8 +865,8 @@ function shouldBootstrapCliProxyBeforeFastPath(env: NodeJS.ProcessEnv = process. function isKnownBuiltInCommandRoot(primary: string): boolean { return ( - getCoreCliCommandNames().includes(primary) || - getSubCliEntries().some((entry) => entry.name === primary) + getCoreCliCommandNamesCore().includes(primary) || + getSubCliEntriesCore().some((entry) => entry.name === primary) ); } @@ -884,7 +884,7 @@ function resolveBuiltInMachineOutput(argv: string[]): boolean { if (!primary) { return false; } - const descriptor = [...getCoreCliCommandDescriptors(), ...getSubCliEntries()].find( + const descriptor = [...getCoreCliCommandDescriptors(), ...getSubCliEntriesCore()].find( (entry) => entry.name === primary, ); return descriptor ? resolvesMachineOutput(descriptor, argv) : false; diff --git a/src/commands/doctor-security.ts b/src/commands/doctor-security.ts index d694f95eac8c..c7537e30d24a 100644 --- a/src/commands/doctor-security.ts +++ b/src/commands/doctor-security.ts @@ -20,7 +20,7 @@ import { import { isLikelySensitiveModelProviderHeaderName } from "../secrets/model-provider-header-policy.js"; import { hasConfiguredPlaintextSecretValue } from "../secrets/secret-value.js"; import { discoverConfigSecretTargets } from "../secrets/target-registry.js"; -import { collectChannelSecurityFindings } from "../security/audit-channel.js"; +import { collectChannelSecurityFindingsCore } from "../security/audit-channel.js"; import { collectExecFilesystemPolicyDriftHits } from "../security/exec-filesystem-policy.js"; function collectImplicitHeartbeatDirectPolicyWarnings(cfg: OpenClawConfig): string[] { @@ -340,7 +340,7 @@ export async function collectSecurityWarnings( warnings.push(...tokenConflict.warningLines); } - const channelFindings = await collectChannelSecurityFindings({ + const channelFindings = await collectChannelSecurityFindingsCore({ cfg, mode: "doctor", plugins: listReadOnlyChannelPluginsForConfig(cfg, { diff --git a/src/config/channel-config-metadata.ts b/src/config/channel-config-metadata.ts index ca2d42fbf94c..53903be0eebf 100644 --- a/src/config/channel-config-metadata.ts +++ b/src/config/channel-config-metadata.ts @@ -124,7 +124,9 @@ function normalizeCoreOwnedChannelSchema(schema: Record): Recor } /** Collects plugin config UI metadata with deterministic origin precedence and output ordering. */ -export function collectPluginSchemaMetadata(registry: PluginManifestRegistry): PluginUiMetadata[] { +export function collectPluginSchemaMetadataCore( + registry: PluginManifestRegistry, +): PluginUiMetadata[] { const deduped = new Map< string, PluginUiMetadata & { @@ -217,7 +219,7 @@ export function collectChannelSchemaMetadataWithOwnership( } /** Collects public per-channel config UI metadata without internal schema ownership. */ -export function collectChannelSchemaMetadata( +export function collectChannelSchemaMetadataCore( registry: PluginManifestRegistry, ): ChannelUiMetadata[] { return collectChannelSchemaMetadataWithOwnership(registry).map( diff --git a/src/config/doc-baseline.runtime.ts b/src/config/doc-baseline.runtime.ts index 30717b89d25e..c39914f0fc5c 100644 --- a/src/config/doc-baseline.runtime.ts +++ b/src/config/doc-baseline.runtime.ts @@ -1,15 +1,15 @@ // Collects runtime data needed to generate config documentation baselines. -import { collectBundledChannelConfigs as collectBundledChannelConfigsImpl } from "../plugins/bundled-channel-config-metadata.js"; +import { collectBundledChannelConfigsCore } from "../plugins/bundled-channel-config-metadata.js"; import { loadPluginManifestRegistry as loadPluginManifestRegistryImpl } from "../plugins/manifest-registry.js"; import { - collectChannelSchemaMetadata as collectChannelSchemaMetadataImpl, - collectPluginSchemaMetadata as collectPluginSchemaMetadataImpl, + collectChannelSchemaMetadataCore, + collectPluginSchemaMetadataCore, } from "./channel-config-metadata.js"; -import { buildConfigSchema as buildConfigSchemaImpl } from "./schema.js"; +import { buildConfigSchemaCore } from "./schema.js"; /** Runtime facade used by docs baseline generation to keep imports narrow. */ export const loadPluginManifestRegistry = loadPluginManifestRegistryImpl; -export const collectBundledChannelConfigs = collectBundledChannelConfigsImpl; -export const collectChannelSchemaMetadata = collectChannelSchemaMetadataImpl; -export const collectPluginSchemaMetadata = collectPluginSchemaMetadataImpl; -export const buildConfigSchema = buildConfigSchemaImpl; +export const collectBundledChannelConfigs = collectBundledChannelConfigsCore; +export const collectChannelSchemaMetadata = collectChannelSchemaMetadataCore; +export const collectPluginSchemaMetadata = collectPluginSchemaMetadataCore; +export const buildConfigSchema = buildConfigSchemaCore; diff --git a/src/config/mcp-config.ts b/src/config/mcp-config.ts index e4c81c980a80..378cfd4d13f0 100644 --- a/src/config/mcp-config.ts +++ b/src/config/mcp-config.ts @@ -11,7 +11,7 @@ import { import { replaceConfigFile } from "./mutate.js"; import { redactSensitiveArgv } from "./redact-argv.js"; import { REDACTED_SENTINEL, restoreRedactedValues } from "./redact-snapshot.js"; -import { buildConfigSchema } from "./schema.js"; +import { buildConfigSchemaCore } from "./schema.js"; import type { OpenClawConfig } from "./types.openclaw.js"; import { validateConfigObjectWithPlugins } from "./validation.js"; @@ -285,7 +285,7 @@ export async function setConfiguredMcpServer(params: { const restored = restoreRedactedValues( { mcp: { servers: { [name]: argvRestored.server } } }, { mcp: { servers: loaded.mcpServers } }, - buildConfigSchema().uiHints, + buildConfigSchemaCore().uiHints, ); if (!restored.ok) { return { diff --git a/src/config/redact-snapshot.schema.test.ts b/src/config/redact-snapshot.schema.test.ts index 73c2726de253..d5d163b52076 100644 --- a/src/config/redact-snapshot.schema.test.ts +++ b/src/config/redact-snapshot.schema.test.ts @@ -5,7 +5,7 @@ import { describe, expect, it } from "vitest"; import { redactSnapshotTestHints as mainSchemaHints } from "../../test/helpers/config/redact-snapshot-test-hints.js"; import { REDACTED_SENTINEL, redactConfigSnapshot } from "./redact-snapshot.js"; import { makeSnapshot, restoreRedactedValues } from "./redact-snapshot.test-helpers.js"; -import { buildConfigSchema } from "./schema.js"; +import { buildConfigSchemaCore } from "./schema.js"; describe("realredactConfigSnapshot_real", () => { it("main schema redact works (samples)", () => { @@ -50,7 +50,7 @@ describe("realredactConfigSnapshot_real", () => { }); it("redacts bundled channel private keys from generated schema hints", () => { - const hints = buildConfigSchema().uiHints; + const hints = buildConfigSchemaCore().uiHints; const snapshot = makeSnapshot({ channels: { nostr: { @@ -73,7 +73,7 @@ describe("realredactConfigSnapshot_real", () => { }); it("redacts Discord Activity client secrets registered on plain string schemas", () => { - const hints = buildConfigSchema().uiHints; + const hints = buildConfigSchemaCore().uiHints; expect(hints["channels.discord.activities.clientSecret"]?.sensitive).toBe(true); const snapshot = makeSnapshot({ channels: { @@ -93,7 +93,7 @@ describe("realredactConfigSnapshot_real", () => { }); it("redacts and restores web fetch operator headers from generated schema hints", () => { - const hints = buildConfigSchema().uiHints; + const hints = buildConfigSchemaCore().uiHints; expect(hints["tools.web.fetch.headers.*"]?.sensitive).toBe(true); const snapshot = makeSnapshot({ tools: { diff --git a/src/config/redact-snapshot.test.ts b/src/config/redact-snapshot.test.ts index e3bcde48dc66..c3afc717a233 100644 --- a/src/config/redact-snapshot.test.ts +++ b/src/config/redact-snapshot.test.ts @@ -12,7 +12,7 @@ import { restoreRedactedValues, type TestSnapshot, } from "./redact-snapshot.test-helpers.js"; -import { buildConfigSchema } from "./schema.js"; +import { buildConfigSchemaCore } from "./schema.js"; import type { ConfigFileSnapshot, OpenClawConfig } from "./types.openclaw.js"; function expectNestedPairValue( @@ -208,7 +208,7 @@ describe("redactConfigSnapshot", () => { }); it("redacts and restores MCP SSE header values from schema hints", () => { - const hints = buildConfigSchema().uiHints; + const hints = buildConfigSchemaCore().uiHints; const snapshot = makeSnapshot({ mcp: { servers: { @@ -252,7 +252,7 @@ describe("redactConfigSnapshot", () => { }); it("redacts sensitive auth material from MCP SSE URLs", () => { - const hints = buildConfigSchema().uiHints; + const hints = buildConfigSchemaCore().uiHints; const raw = `{ mcp: { servers: { @@ -289,7 +289,7 @@ describe("redactConfigSnapshot", () => { }); it("redacts media request auth and proxy transport secrets from config snapshots", () => { - const hints = buildConfigSchema().uiHints; + const hints = buildConfigSchemaCore().uiHints; const raw = `{ tools: { media: { @@ -346,7 +346,7 @@ describe("redactConfigSnapshot", () => { }); it("redacts model provider request auth secrets from config snapshots", () => { - const hints = buildConfigSchema().uiHints; + const hints = buildConfigSchemaCore().uiHints; const raw = `{ models: { providers: { @@ -394,7 +394,7 @@ describe("redactConfigSnapshot", () => { }); it("redacts model provider local service env values from config snapshots", () => { - const hints = buildConfigSchema().uiHints; + const hints = buildConfigSchemaCore().uiHints; const raw = `{ models: { providers: { @@ -448,7 +448,7 @@ describe("redactConfigSnapshot", () => { }); it("redacts install policy env values from config snapshots", () => { - const hints = buildConfigSchema().uiHints; + const hints = buildConfigSchemaCore().uiHints; const raw = `{ security: { installPolicy: { @@ -498,7 +498,7 @@ describe("redactConfigSnapshot", () => { }); it("redacts model provider request proxy URLs from config snapshots", () => { - const hints = buildConfigSchema().uiHints; + const hints = buildConfigSchemaCore().uiHints; const raw = `{ models: { providers: { @@ -1413,7 +1413,7 @@ describe("redactConfigSnapshot", () => { }); it("redacts browser cdpUrl secrets while preserving bare endpoints", () => { - const hints = buildConfigSchema().uiHints; + const hints = buildConfigSchemaCore().uiHints; const raw = `{ browser: { cdpUrl: "https://user:pass@chrome.browserless.io?token=supersecret123", diff --git a/src/config/runtime-schema.ts b/src/config/runtime-schema.ts index e419cd64f4d1..5730744ede2e 100644 --- a/src/config/runtime-schema.ts +++ b/src/config/runtime-schema.ts @@ -2,12 +2,12 @@ import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js"; import { resolvePluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js"; import { - collectChannelSchemaMetadata, - collectPluginSchemaMetadata, + collectChannelSchemaMetadataCore, + collectPluginSchemaMetadataCore, } from "./channel-config-metadata.js"; import { getRuntimeConfig, readConfigFileSnapshot } from "./config.js"; import type { OpenClawConfig } from "./config.js"; -import { buildConfigSchema, type ConfigSchemaResponse } from "./schema.js"; +import { buildConfigSchemaCore, type ConfigSchemaResponse } from "./schema.js"; // Runtime schemas include currently loaded plugin/channel metadata for accurate UI fields. function loadManifestRegistry(config: OpenClawConfig, env?: NodeJS.ProcessEnv) { @@ -24,9 +24,9 @@ function loadManifestRegistry(config: OpenClawConfig, env?: NodeJS.ProcessEnv) { export function loadGatewayRuntimeConfigSchema(): ConfigSchemaResponse { const config = getRuntimeConfig(); const registry = loadManifestRegistry(config); - return buildConfigSchema({ - plugins: collectPluginSchemaMetadata(registry), - channels: collectChannelSchemaMetadata(registry), + return buildConfigSchemaCore({ + plugins: collectPluginSchemaMetadataCore(registry), + channels: collectChannelSchemaMetadataCore(registry), }); } @@ -36,8 +36,8 @@ export async function readBestEffortRuntimeConfigSchema(): Promise { - type SchemaInput = NonNullable[0]>; - let baseSchema: ReturnType; + type SchemaInput = NonNullable[0]>; + let baseSchema: ReturnType; let pluginUiHintInput: SchemaInput; let tokenHintInput: SchemaInput; let mergedSchemaInput: SchemaInput; @@ -19,7 +19,7 @@ describe("config schema", () => { let cachedMergeInput: SchemaInput; beforeAll(() => { - baseSchema = buildConfigSchema(); + baseSchema = buildConfigSchemaCore(); pluginUiHintInput = { plugins: [ { @@ -471,7 +471,7 @@ describe("config schema", () => { }); it("merges plugin ui hints", () => { - const res = buildConfigSchema(pluginUiHintInput); + const res = buildConfigSchemaCore(pluginUiHintInput); expect(res.uiHints["plugins.entries.voice-call"]?.label).toBe("Voice Call"); expect(res.uiHints["plugins.entries.voice-call.config"]?.label).toBe("Voice Call Config"); @@ -482,13 +482,13 @@ describe("config schema", () => { }); it("does not re-mark existing non-sensitive token-like fields", () => { - const res = buildConfigSchema(tokenHintInput); + const res = buildConfigSchemaCore(tokenHintInput); expect(res.uiHints["plugins.entries.voice-call.config.tokens"]?.sensitive).toBe(false); }); it("merges plugin + channel schemas", () => { - const res = buildConfigSchema(mergedSchemaInput); + const res = buildConfigSchemaCore(mergedSchemaInput); const schema = res.schema as { properties?: Record; @@ -544,7 +544,7 @@ describe("config schema", () => { }); it("omits a single oversized plugin schema from the full schema response", () => { - const res = buildConfigSchema({ + const res = buildConfigSchemaCore({ cache: false, plugins: [ { @@ -572,7 +572,7 @@ describe("config schema", () => { }); it("omits later plugin schemas after the aggregate extension schema budget is exhausted", () => { - const res = buildConfigSchema({ + const res = buildConfigSchemaCore({ cache: false, plugins: Array.from({ length: 40 }, (_, index) => ({ id: `plugin-${index}`, @@ -597,7 +597,7 @@ describe("config schema", () => { }); it("looks up plugin config paths for slash-delimited plugin ids", () => { - const res = buildConfigSchema({ + const res = buildConfigSchemaCore({ plugins: [ { id: "pack/one", @@ -622,7 +622,7 @@ describe("config schema", () => { }); it("adds heartbeat target hints with dynamic channels", () => { - const res = buildConfigSchema(heartbeatChannelInput); + const res = buildConfigSchemaCore(heartbeatChannelInput); const defaultsHint = res.uiHints["agents.defaults.heartbeat.target"]; const entryHint = res.uiHints["agents.entries.*.heartbeat.target"]; @@ -634,10 +634,10 @@ describe("config schema", () => { }); it("caches merged schemas for identical plugin/channel metadata", () => { - const first = buildConfigSchema(cachedMergeInput); + const first = buildConfigSchemaCore(cachedMergeInput); const plugin = expectDefined(cachedMergeInput.plugins?.[0], "cached plugin metadata"); const channel = expectDefined(cachedMergeInput.channels?.[0], "cached channel metadata"); - const second = buildConfigSchema({ + const second = buildConfigSchemaCore({ plugins: [{ ...plugin }], channels: [{ ...channel }], }); diff --git a/src/config/schema.ts b/src/config/schema.ts index 01a95f7cb4a5..22322fda73d4 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -567,7 +567,7 @@ function buildBaseConfigSchema(): ConfigSchemaResponse { return next; } -export function buildConfigSchema(params?: { +export function buildConfigSchemaCore(params?: { plugins?: PluginUiMetadata[]; channels?: ChannelUiMetadata[]; cache?: boolean; diff --git a/src/gateway/control-ui.ts b/src/gateway/control-ui.ts index 67a94809f38a..503573d2875f 100644 --- a/src/gateway/control-ui.ts +++ b/src/gateway/control-ui.ts @@ -21,7 +21,7 @@ import { openLocalFileSafely, FsSafeError } from "../infra/fs-safe.js"; import { safeFileURLToPath } from "../infra/local-file-access.js"; import { verifyPairingToken } from "../infra/pairing-token.js"; import { isWithinDir } from "../infra/path-safety.js"; -import { assertLocalMediaAllowed, getDefaultLocalRoots } from "../media/local-media-access.js"; +import { assertLocalMediaAllowed, getDefaultLocalRootsCore } from "../media/local-media-access.js"; import { getAgentScopedMediaLocalRoots } from "../media/local-roots.js"; import { probePlaybackMediaFileDescriptor, type MediaProbeResult } from "../media/media-probe.js"; import { @@ -699,7 +699,7 @@ export async function handleControlUiAssistantMediaRequest( } const localRoots = opts?.config ? getAgentScopedMediaLocalRoots(opts.config, opts.agentId) - : getDefaultLocalRoots(); + : getDefaultLocalRootsCore(); if (isMetaRequest) { const availability = await resolveAssistantMediaAvailability(source, localRoots); diff --git a/src/gateway/gateway-models.profiles.live.test.ts b/src/gateway/gateway-models.profiles.live.test.ts index 3bfc8813baf8..7877a479a1a3 100644 --- a/src/gateway/gateway-models.profiles.live.test.ts +++ b/src/gateway/gateway-models.profiles.live.test.ts @@ -54,7 +54,7 @@ import { isLiveBillingDrift, isLiveRateLimitDrift, } from "../agents/live-test-provider-drift.test-support.js"; -import { getApiKeyForModel, resolveEnvApiKey } from "../agents/model-auth.js"; +import { getApiKeyForModelCore, resolveEnvApiKey } from "../agents/model-auth.js"; import { normalizeProviderId } from "../agents/model-selection.js"; import { shouldSuppressBuiltInModel } from "../agents/model-suppression.js"; import { ensureOpenClawModelsJson } from "../agents/models-config.js"; @@ -5626,7 +5626,7 @@ describeLive("gateway live (dev agent, profile keys)", () => { const modelRef = `${model.provider}/${model.id}`; try { const apiKeyInfo = await withGatewayLiveSetupTimeout( - getApiKeyForModel({ + getApiKeyForModelCore({ model, cfg, store: authProfileStore, @@ -5763,12 +5763,12 @@ describeLive("gateway live (dev agent, profile keys)", () => { return; } try { - await getApiKeyForModel({ + await getApiKeyForModelCore({ model: anthropic, cfg, credentialPrecedence: LIVE_CREDENTIAL_PRECEDENCE, }); - await getApiKeyForModel({ + await getApiKeyForModelCore({ model: zai, cfg, credentialPrecedence: LIVE_CREDENTIAL_PRECEDENCE, diff --git a/src/gateway/node-reapproval-coordinator.ts b/src/gateway/node-reapproval-coordinator.ts index c2ba227b1e25..cee54a28c865 100644 --- a/src/gateway/node-reapproval-coordinator.ts +++ b/src/gateway/node-reapproval-coordinator.ts @@ -9,7 +9,7 @@ import { type RequestNodePairingResult, } from "../infra/device-pairing-node.js"; import { KeyedAsyncQueue } from "../plugin-sdk/keyed-async-queue.js"; -import { createDeferred, type Deferred } from "../shared/deferred.js"; +import { createDeferredCore, type Deferred } from "../shared/deferred.js"; import { AUTH_RATE_LIMIT_SCOPE_NODE_REAPPROVAL, buildRateLimitIdentityKey, @@ -171,7 +171,7 @@ export function createNodeReapprovalCoordinator( const fingerprint = buildRequestFingerprint(params.input); const state = requestStates.get(nodeId); if (!state) { - const deferred = createDeferred(); + const deferred = createDeferredCore(); const nextState: NodeRequestState = { activeFingerprint: fingerprint }; requestStates.set(nodeId, nextState); startFirstRequest(nodeId, nextState, { @@ -183,13 +183,13 @@ export function createNodeReapprovalCoordinator( return deferred.promise; } if (state.queued?.fingerprint === fingerprint) { - const follower = createDeferred(); + const follower = createDeferredCore(); state.queued.params = params; state.queued.followers.push(follower); return follower.promise; } - const deferred = createDeferred(); + const deferred = createDeferredCore(); if (state.queued) { state.queued.deferred.resolve(null); for (const follower of state.queued.followers) { diff --git a/src/gateway/operator-approvals-client.ts b/src/gateway/operator-approvals-client.ts index 6dc9b5365737..7d5ae103861d 100644 --- a/src/gateway/operator-approvals-client.ts +++ b/src/gateway/operator-approvals-client.ts @@ -6,7 +6,7 @@ import { GATEWAY_CLIENT_NAMES, } from "../../packages/gateway-protocol/src/client-info.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; -import { createDeferred } from "../shared/deferred.js"; +import { createDeferredCore } from "../shared/deferred.js"; import { resolveGatewayClientBootstrap } from "./client-bootstrap.js"; import { startGatewayClientWhenEventLoopReady } from "./client-start-readiness.js"; import { GatewayClient, type GatewayClientOptions } from "./client.js"; @@ -83,7 +83,7 @@ export async function withOperatorApprovalsGatewayClient( }, run: (client: GatewayClient) => Promise, ): Promise { - const ready = createDeferred(); + const ready = createDeferredCore(); const gatewayClient = await createOperatorApprovalsGatewayClient({ config: params.config, diff --git a/src/gateway/server-aux-handlers.test.ts b/src/gateway/server-aux-handlers.test.ts index 2ffa802e7397..d158cff88e0f 100644 --- a/src/gateway/server-aux-handlers.test.ts +++ b/src/gateway/server-aux-handlers.test.ts @@ -25,7 +25,7 @@ vi.mock("../secrets/store/secret-store.js", () => { }); import { getRuntimeAuthProfileStoreCredentialsRevision, - getRuntimeAuthProfileStoreSnapshot, + getRuntimeAuthProfileStoreSnapshotCore, setRuntimeAuthProfileStoreSnapshot, } from "../agents/auth-profiles/runtime-snapshots.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; @@ -750,7 +750,7 @@ describe("gateway aux handlers", () => { required: "gen-old", }); expect( - getRuntimeAuthProfileStoreSnapshot(authAgentDir)?.profiles["openai:default"], + getRuntimeAuthProfileStoreSnapshotCore(authAgentDir)?.profiles["openai:default"], ).toMatchObject({ access: "access-new", refresh: "refresh-new" }); }); diff --git a/src/gateway/server-aux-handlers.ts b/src/gateway/server-aux-handlers.ts index e452ae5cefc5..8de0c2ae8f02 100644 --- a/src/gateway/server-aux-handlers.ts +++ b/src/gateway/server-aux-handlers.ts @@ -21,8 +21,8 @@ import { type CommandSecretAssignment, } from "../secrets/runtime-command-secrets.js"; import { - getActiveSecretsRuntimeSnapshot, - getActiveSecretsRuntimeSnapshotRevision, + getActiveSecretsRuntimeSnapshotState, + getActiveSecretsRuntimeSnapshotRevisionState, type PreparedSecretsRuntimeSnapshot, } from "../secrets/runtime-state.js"; import { createLazyPromise } from "../shared/lazy-runtime.js"; @@ -381,11 +381,11 @@ export function createGatewayAuxHandlers(params: { const restartedChannels = new Set(); try { for (;;) { - const previousSnapshot = getActiveSecretsRuntimeSnapshot(); + const previousSnapshot = getActiveSecretsRuntimeSnapshotState(); if (!previousSnapshot) { throw new Error("Secrets runtime snapshot is not active."); } - const previousSnapshotRevision = getActiveSecretsRuntimeSnapshotRevision(); + const previousSnapshotRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const previousGenerationOwnership = captureSharedGatewaySessionGenerationOwnership( params.sharedGatewaySessionGenerationState, @@ -404,7 +404,7 @@ export function createGatewayAuxHandlers(params: { publishFailureAsDegraded: true, forceColdRefKeys: reloadOptions?.forceColdRefKeys, canPublishFailureAsDegraded: () => - getActiveSecretsRuntimeSnapshotRevision() === previousSnapshotRevision, + getActiveSecretsRuntimeSnapshotRevisionState() === previousSnapshotRevision, }, ); const plan = buildReloadPlan( @@ -425,7 +425,7 @@ export function createGatewayAuxHandlers(params: { activate: true, }, async () => { - publishedSnapshotRevision = getActiveSecretsRuntimeSnapshotRevision(); + publishedSnapshotRevision = getActiveSecretsRuntimeSnapshotRevisionState(); generationOwnership = claimSharedGatewaySessionGenerationIfOwned( params.sharedGatewaySessionGenerationState, previousGenerationOwnership, diff --git a/src/gateway/server-kernel.ts b/src/gateway/server-kernel.ts index ffff1d574e1e..8c180b940de7 100644 --- a/src/gateway/server-kernel.ts +++ b/src/gateway/server-kernel.ts @@ -2,7 +2,7 @@ import { isNixMode } from "../config/paths.js"; import { ensureOpenClawCliOnPath } from "../infra/path-env.js"; import { createSubsystemLogger, runtimeForLogger } from "../logging/subsystem.js"; import { clearPluginMetadataLifecycleCaches } from "../plugins/plugin-metadata-lifecycle.js"; -import { clearSecretsRuntimeSnapshot } from "../secrets/runtime-state.js"; +import { clearSecretsRuntimeSnapshotState } from "../secrets/runtime-state.js"; import { createLazyRuntimeModule } from "../shared/lazy-runtime.js"; import { startGatewayCoreRuntime } from "./server-core-runtime.js"; import { prepareGatewayKernelRequestRuntime } from "./server-kernel-request-runtime.js"; @@ -162,7 +162,7 @@ export async function createGatewayKernel(port = 18789, opts: GatewayServerOptio if (lifecycleRuntime) { await lifecycleRuntime.closeOnStartupFailure(); } else { - clearSecretsRuntimeSnapshot(); + clearSecretsRuntimeSnapshotState(); clearPluginMetadataLifecycleCaches(); } throw error; diff --git a/src/gateway/server-lifecycle.ts b/src/gateway/server-lifecycle.ts index c4bb9d02a422..2c6884428cb9 100644 --- a/src/gateway/server-lifecycle.ts +++ b/src/gateway/server-lifecycle.ts @@ -12,7 +12,7 @@ import { upsertPresence } from "../infra/system-presence.js"; import { startDiagnosticHeartbeat, stopDiagnosticHeartbeat } from "../logging/diagnostic.js"; import type { createSubsystemLogger } from "../logging/subsystem.js"; import { clearPluginMetadataLifecycleCaches } from "../plugins/plugin-metadata-lifecycle.js"; -import { clearSecretsRuntimeSnapshot } from "../secrets/runtime-state.js"; +import { clearSecretsRuntimeSnapshotState } from "../secrets/runtime-state.js"; import { roleScopesAllow } from "../shared/operator-scope-compat.js"; import { recordRemoteNodeInfo, @@ -517,7 +517,7 @@ export async function prepareGatewayLifecycle(params: { await createGatewayCloseHandler({ bonjourStop: runtimeState.bonjourStop, tailscaleCleanup: runtimeState.tailscaleCleanup, - clearSecretsRuntimeSnapshot, + clearSecretsRuntimeSnapshot: clearSecretsRuntimeSnapshotState, channelIds, stopChannel, pluginServices: runtimeState.pluginServices, diff --git a/src/gateway/server-methods.authorization.test.ts b/src/gateway/server-methods.authorization.test.ts index b10c69903f10..2181fd6cb2ad 100644 --- a/src/gateway/server-methods.authorization.test.ts +++ b/src/gateway/server-methods.authorization.test.ts @@ -8,7 +8,7 @@ import { applySessionEntryCanonicalReplacements } from "../config/sessions/sessi import type { OpenClawConfig } from "../config/types.openclaw.js"; import { createEmptyPluginRegistry } from "../plugins/registry-empty.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; -import { createDeferred } from "../shared/deferred.js"; +import { createDeferredCore } from "../shared/deferred.js"; import { withOpenClawTestState } from "../test-utils/openclaw-test-state.js"; import { createGatewayMethodRegistry, @@ -589,8 +589,8 @@ describe("sessions.patchMany orchestration", () => { cfg, key: conflictingAlias, }).storePath; - const writerStarted = createDeferred(); - const insertConflictingAlias = createDeferred(); + const writerStarted = createDeferredCore(); + const insertConflictingAlias = createDeferredCore(); const writer = applySessionEntryCanonicalReplacements({ agentId: "main", sessionKeys: [conflictingAlias], @@ -612,7 +612,7 @@ describe("sessions.patchMany orchestration", () => { }); await writerStarted.promise; - const preflightCompleted = createDeferred(); + const preflightCompleted = createDeferredCore(); const respond = vi.fn(); const request = sessionMutationHandlers["sessions.patchMany"]!({ params: { @@ -686,8 +686,8 @@ describe("sessions.patchMany orchestration", () => { cfg, key: conflictingAlias, }).storePath; - const writerStarted = createDeferred(); - const insertConflictingAlias = createDeferred(); + const writerStarted = createDeferredCore(); + const insertConflictingAlias = createDeferredCore(); const writer = applySessionEntryCanonicalReplacements({ agentId: "main", sessionKeys: [conflictingAlias], @@ -709,7 +709,7 @@ describe("sessions.patchMany orchestration", () => { }); await writerStarted.promise; - const preflightCompleted = createDeferred(); + const preflightCompleted = createDeferredCore(); const respond = vi.fn(); const request = sessionMutationHandlers["sessions.patch"]!({ params: { key: conflictingAlias, pinned: true }, diff --git a/src/gateway/server-methods/chat-webchat-media.test.ts b/src/gateway/server-methods/chat-webchat-media.test.ts index 288ab7671511..dc60ba40f4d3 100644 --- a/src/gateway/server-methods/chat-webchat-media.test.ts +++ b/src/gateway/server-methods/chat-webchat-media.test.ts @@ -6,7 +6,7 @@ import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { getDefaultLocalRoots } from "../../media/local-media-access.js"; +import { getDefaultLocalRootsCore } from "../../media/local-media-access.js"; import { buildWebchatAssistantMessageFromReplyPayloads } from "./chat-webchat-media.js"; async function buildWebchatAudioBlocks( @@ -211,7 +211,7 @@ describe("webchat audio blocks through assistant messages", () => { }); it("falls back to default localRoots when explicit roots are omitted", async () => { - const [defaultRoot] = getDefaultLocalRoots(); + const [defaultRoot] = getDefaultLocalRootsCore(); if (defaultRoot === undefined) { throw new Error("expected default local media root"); } diff --git a/src/gateway/server-methods/config-write-flow.test.ts b/src/gateway/server-methods/config-write-flow.test.ts index 165eb3244d6e..34c280f82a06 100644 --- a/src/gateway/server-methods/config-write-flow.test.ts +++ b/src/gateway/server-methods/config-write-flow.test.ts @@ -25,7 +25,7 @@ vi.mock("../../secrets/runtime-state.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - getActiveSecretsRuntimeSnapshot: () => secretsMocks.activeSnapshot, + getActiveSecretsRuntimeSnapshotState: () => secretsMocks.activeSnapshot, }; }); diff --git a/src/gateway/server-methods/config-write-flow.ts b/src/gateway/server-methods/config-write-flow.ts index ade94cd49350..e00e11478fa2 100644 --- a/src/gateway/server-methods/config-write-flow.ts +++ b/src/gateway/server-methods/config-write-flow.ts @@ -15,7 +15,7 @@ import { writeRestartSentinel, } from "../../infra/restart-sentinel.js"; import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js"; -import { getActiveSecretsRuntimeSnapshot } from "../../secrets/runtime-state.js"; +import { getActiveSecretsRuntimeSnapshotState } from "../../secrets/runtime-state.js"; import { isRecord } from "../../utils.js"; import { resolveEffectiveSharedGatewayAuth, resolveGatewayAuth } from "../auth.js"; import { invalidateConfigGetResponseCache } from "../config-get-response.js"; @@ -137,7 +137,7 @@ export function didActiveSharedGatewayAuthChange(params: { fallbackSource?: OpenClawConfig; next: OpenClawConfig; }): boolean { - const active = getActiveSecretsRuntimeSnapshot(); + const active = getActiveSecretsRuntimeSnapshotState(); if (!active) { return didSharedGatewayAuthChange(params.fallbackPrev, params.next); } diff --git a/src/gateway/server-methods/config.shared-auth.test.ts b/src/gateway/server-methods/config.shared-auth.test.ts index 4b3b37950c20..c823a8317a88 100644 --- a/src/gateway/server-methods/config.shared-auth.test.ts +++ b/src/gateway/server-methods/config.shared-auth.test.ts @@ -79,7 +79,7 @@ vi.mock("../../secrets/runtime.js", () => ({ })); vi.mock("../../secrets/runtime-state.js", () => ({ - getActiveSecretsRuntimeSnapshot: () => null, + getActiveSecretsRuntimeSnapshotState: () => null, })); vi.mock("../../infra/restart.js", () => ({ diff --git a/src/gateway/server-methods/secrets.test.ts b/src/gateway/server-methods/secrets.test.ts index 94c2ce58f0ad..0aa237fbfb70 100644 --- a/src/gateway/server-methods/secrets.test.ts +++ b/src/gateway/server-methods/secrets.test.ts @@ -16,7 +16,7 @@ const storeMocks = vi.hoisted(() => ({ vi.mock("../../secrets/runtime-state.js", () => ({ collectSecretStoreRefKeysInConfig: storeMocks.collectRefKeys, - getActiveSecretsRuntimeSnapshot: storeMocks.getSnapshot, + getActiveSecretsRuntimeSnapshotState: storeMocks.getSnapshot, })); vi.mock("../../secrets/store/secret-store.js", () => { diff --git a/src/gateway/server-methods/secrets.ts b/src/gateway/server-methods/secrets.ts index 928acbdbe02a..c3e7ef10b852 100644 --- a/src/gateway/server-methods/secrets.ts +++ b/src/gateway/server-methods/secrets.ts @@ -16,7 +16,7 @@ import { import { formatErrorMessage as errorMessage } from "../../infra/errors.js"; import { collectSecretStoreRefKeysInConfig, - getActiveSecretsRuntimeSnapshot, + getActiveSecretsRuntimeSnapshotState, } from "../../secrets/runtime-state.js"; import { deleteSecretStoreEntry, @@ -136,7 +136,7 @@ export function createSecretsHandlers(params: { const reloadStoreReference = async ( name: string, ): Promise<{ reloaded: boolean; warningCount?: number }> => { - const snapshot = getActiveSecretsRuntimeSnapshot(); + const snapshot = getActiveSecretsRuntimeSnapshotState(); const refKeys = snapshot ? collectSecretStoreRefKeysInConfig(snapshot.sourceConfig, name) : new Set(); diff --git a/src/gateway/server-reload-managed-secrets.ts b/src/gateway/server-reload-managed-secrets.ts index c984451625fb..74476a5833d6 100644 --- a/src/gateway/server-reload-managed-secrets.ts +++ b/src/gateway/server-reload-managed-secrets.ts @@ -6,9 +6,9 @@ import { } from "../config/config.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { - clearSecretsRuntimeSnapshot, - getActiveSecretsRuntimeSnapshot, - getActiveSecretsRuntimeSnapshotRevision, + clearSecretsRuntimeSnapshotState, + getActiveSecretsRuntimeSnapshotState, + getActiveSecretsRuntimeSnapshotRevisionState, hasActiveSecretsRuntimeSnapshotLineage, hasSameSecretReloadContract, restoreSecretsRuntimeSourceSnapshotIfLineageCurrent, @@ -43,7 +43,7 @@ import { publishRuntimeSecretsStateTransition } from "./server-startup-config.js export function isRuntimeSecretsPreparationCurrent( preparation: CurrentRuntimeSecretsPreparation, ): boolean { - return getActiveSecretsRuntimeSnapshotRevision() === preparation.expectedRevision; + return getActiveSecretsRuntimeSnapshotRevisionState() === preparation.expectedRevision; } async function activateSecretsRuntimeSnapshotIfCurrent( @@ -124,8 +124,8 @@ export function createManagedReloadSecretHandlers(options: { throw new GatewayConfigReloadSupersededError(); } const previousRuntimeSourceConfig = getRuntimeConfigSourceSnapshot(); - const previousSecretsSnapshot = getActiveSecretsRuntimeSnapshot(); - const previousSecretsRevision = getActiveSecretsRuntimeSnapshotRevision(); + const previousSecretsSnapshot = getActiveSecretsRuntimeSnapshotState(); + const previousSecretsRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const previousRuntimeMetadata = getRuntimeConfigSnapshotMetadata(); const nextSecretsSourceConfig = prepareRuntimeCandidate( nextConfig, @@ -158,7 +158,7 @@ export function createManagedReloadSecretHandlers(options: { ) { continue; } - const committedSecretsRevision = getActiveSecretsRuntimeSnapshotRevision(); + const committedSecretsRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const rollbackPublishedSource = async () => { if ( !restoreSecretsRuntimeSourceSnapshotIfLineageCurrent({ @@ -246,7 +246,7 @@ export function createManagedReloadSecretHandlers(options: { if (!activated) { continue; } - const committedSecretsRevision = getActiveSecretsRuntimeSnapshotRevision(); + const committedSecretsRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const rollbackPublishedSource = async () => { if ( !(await restoreSecretsRuntimeSnapshotIfCurrent( @@ -334,8 +334,8 @@ export function createManagedReloadSecretHandlers(options: { if (!transactionOwnership.isCurrent()) { throw new GatewayConfigReloadSupersededError(); } - const previousSnapshot = getActiveSecretsRuntimeSnapshot(); - const previousSnapshotRevision = getActiveSecretsRuntimeSnapshotRevision(); + const previousSnapshot = getActiveSecretsRuntimeSnapshotState(); + const previousSnapshotRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const previousGenerationOwnership = captureSharedGatewaySessionGenerationOwnership( params.sharedGatewaySessionGenerationState, ); @@ -361,7 +361,7 @@ export function createManagedReloadSecretHandlers(options: { if (!transactionOwnership.isCurrent()) { throw new GatewayConfigReloadSupersededError(); } - if (getActiveSecretsRuntimeSnapshotRevision() !== previousSnapshotRevision) { + if (getActiveSecretsRuntimeSnapshotRevisionState() !== previousSnapshotRevision) { continue; } const nextSharedGatewaySessionGeneration = @@ -416,7 +416,7 @@ export function createManagedReloadSecretHandlers(options: { }; const publishRuntime = async () => { runtimeSecretsPublished = true; - publishedSnapshotRevision = getActiveSecretsRuntimeSnapshotRevision(); + publishedSnapshotRevision = getActiveSecretsRuntimeSnapshotRevisionState(); // Claim the generation at the snapshot activation edge, but keep // `required` until the runtime commit succeeds. claimGenerationOwnership(); @@ -461,9 +461,9 @@ export function createManagedReloadSecretHandlers(options: { ); } else if ( publishedSnapshotRevision !== null && - getActiveSecretsRuntimeSnapshotRevision() === publishedSnapshotRevision + getActiveSecretsRuntimeSnapshotRevisionState() === publishedSnapshotRevision ) { - clearSecretsRuntimeSnapshot(); + clearSecretsRuntimeSnapshotState(); snapshotRestored = true; if (generationOwnership) { generationRestored = restoreOwnedCurrentSharedGatewaySessionGeneration( @@ -572,9 +572,9 @@ export function createManagedReloadSecretHandlers(options: { } else if ( publishedSnapshotRevision !== null && generationOwnership && - getActiveSecretsRuntimeSnapshotRevision() === publishedSnapshotRevision + getActiveSecretsRuntimeSnapshotRevisionState() === publishedSnapshotRevision ) { - clearSecretsRuntimeSnapshot(); + clearSecretsRuntimeSnapshotState(); snapshotRestored = true; generationRestored = restoreOwnedCurrentSharedGatewaySessionGeneration( params.sharedGatewaySessionGenerationState, diff --git a/src/gateway/server-reload-managed.ts b/src/gateway/server-reload-managed.ts index 9b805c5e2db9..3c0bb0f388be 100644 --- a/src/gateway/server-reload-managed.ts +++ b/src/gateway/server-reload-managed.ts @@ -1,6 +1,6 @@ import type { OpenClawConfig } from "../config/types.openclaw.js"; import { runWithGatewayIndependentRootWorkAdmission } from "../process/gateway-work-admission.js"; -import { getActiveSecretsRuntimeSnapshotRevision } from "../secrets/runtime-state.js"; +import { getActiveSecretsRuntimeSnapshotRevisionState } from "../secrets/runtime-state.js"; import { resetSkillSnapshotConfigFingerprintCache } from "../skills/runtime/snapshot-config-fingerprint.js"; import { invalidateConfigGetResponseCache } from "./config-get-response.js"; import { @@ -71,26 +71,26 @@ export function startManagedGatewayConfigReloader( if (!transactionOwnership.isCurrent()) { throw new GatewayConfigReloadSupersededError(); } - const expectedRevision = getActiveSecretsRuntimeSnapshotRevision(); + const expectedRevision = getActiveSecretsRuntimeSnapshotRevisionState(); try { const snapshot = await params.activateRuntimeSecrets(config, { ...activationParams, activate: false, canPublishFailureAsDegraded: () => transactionOwnership.isCurrent() && - getActiveSecretsRuntimeSnapshotRevision() === expectedRevision, + getActiveSecretsRuntimeSnapshotRevisionState() === expectedRevision, }); if (!transactionOwnership.isCurrent()) { throw new GatewayConfigReloadSupersededError(); } - return getActiveSecretsRuntimeSnapshotRevision() === expectedRevision + return getActiveSecretsRuntimeSnapshotRevisionState() === expectedRevision ? { snapshot, expectedRevision } : null; } catch (error) { if (!transactionOwnership.isCurrent()) { throw new GatewayConfigReloadSupersededError(); } - if (getActiveSecretsRuntimeSnapshotRevision() !== expectedRevision) { + if (getActiveSecretsRuntimeSnapshotRevisionState() !== expectedRevision) { return null; } throw error; diff --git a/src/gateway/server-startup-config.secrets.test.ts b/src/gateway/server-startup-config.secrets.test.ts index 46ca982715e2..738b0f180eca 100644 --- a/src/gateway/server-startup-config.secrets.test.ts +++ b/src/gateway/server-startup-config.secrets.test.ts @@ -8,7 +8,7 @@ import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; import { loadAuthProfileStoreWithoutExternalProfiles } from "../agents/auth-profiles.js"; import { getRuntimeAuthProfileStoreCredentialsRevision, - getRuntimeAuthProfileStoreSnapshot, + getRuntimeAuthProfileStoreSnapshotCore, setRuntimeAuthProfileStoreSnapshot, } from "../agents/auth-profiles/runtime-snapshots.js"; import { writePersistedAuthProfileStoreRaw } from "../agents/auth-profiles/sqlite.js"; @@ -20,9 +20,9 @@ import { activateProviderAuthRuntimeSnapshot } from "../secrets/runtime-provider import { activateSecretsRuntimeSnapshotState, activateSecretsRuntimeSnapshotStateIfCurrent, - clearSecretsRuntimeSnapshot, - getActiveSecretsRuntimeSnapshot, - getActiveSecretsRuntimeSnapshotRevision, + clearSecretsRuntimeSnapshotState, + getActiveSecretsRuntimeSnapshotState, + getActiveSecretsRuntimeSnapshotRevisionState, } from "../secrets/runtime-state.js"; import type { PreparedSecretsRuntimeSnapshot, SecretResolverWarning } from "../secrets/runtime.js"; import { withEnvAsync } from "../test-utils/env.js"; @@ -427,7 +427,7 @@ describe("gateway startup config secret preflight", () => { const previousSkipProviders = process.env.OPENCLAW_SKIP_PROVIDERS; afterEach(() => { - clearSecretsRuntimeSnapshot(); + clearSecretsRuntimeSnapshotState(); if (previousSkipChannels === undefined) { delete process.env.OPENCLAW_SKIP_CHANNELS; } else { @@ -452,9 +452,9 @@ describe("gateway startup config secret preflight", () => { activateRuntimeSecretsSnapshot, }); activateSecretsRuntimeSnapshotForTest(initial); - const initialRevision = getActiveSecretsRuntimeSnapshotRevision(); + const initialRevision = getActiveSecretsRuntimeSnapshotRevisionState(); activateSecretsRuntimeSnapshotForTest(refreshed); - const refreshedRevision = getActiveSecretsRuntimeSnapshotRevision(); + const refreshedRevision = getActiveSecretsRuntimeSnapshotRevisionState(); await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.(candidate, initialRevision, { @@ -514,7 +514,7 @@ describe("gateway startup config secret preflight", () => { logSecrets, }); activateSecretsRuntimeSnapshotForTest(initial); - const expectedRevision = getActiveSecretsRuntimeSnapshotRevision(); + const expectedRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const activateSnapshotIfCurrent = vi.fn(() => { activateSecretsRuntimeSnapshotForTest(candidate); return true; @@ -576,7 +576,7 @@ describe("gateway startup config secret preflight", () => { publishFailureAsDegraded: true, }), ).rejects.toThrow(failure.message); - const expectedRevision = getActiveSecretsRuntimeSnapshotRevision(); + const expectedRevision = getActiveSecretsRuntimeSnapshotRevisionState(); await expect( activateProviderAuthRuntimeSnapshot({ snapshot: candidate, @@ -636,7 +636,7 @@ describe("gateway startup config secret preflight", () => { await activateProviderAuthRuntimeSnapshot({ snapshot: providerDegraded, - expectedRevision: getActiveSecretsRuntimeSnapshotRevision(), + expectedRevision: getActiveSecretsRuntimeSnapshotRevisionState(), activateSnapshotIfCurrent: () => { activateSecretsRuntimeSnapshotForTest(providerDegraded); return true; @@ -652,7 +652,7 @@ describe("gateway startup config secret preflight", () => { const recovered = preparedSnapshot(config); await activateProviderAuthRuntimeSnapshot({ snapshot: recovered, - expectedRevision: getActiveSecretsRuntimeSnapshotRevision(), + expectedRevision: getActiveSecretsRuntimeSnapshotRevisionState(), activateSnapshotIfCurrent: () => { activateSecretsRuntimeSnapshotForTest(recovered); return true; @@ -689,7 +689,7 @@ describe("gateway startup config secret preflight", () => { await expect( activateProviderAuthRuntimeSnapshot({ snapshot: candidate, - expectedRevision: getActiveSecretsRuntimeSnapshotRevision(), + expectedRevision: getActiveSecretsRuntimeSnapshotRevisionState(), activateSnapshotIfCurrent: () => true, }), ).resolves.toBe(true); @@ -728,7 +728,7 @@ describe("gateway startup config secret preflight", () => { const recovered = preparedSnapshot(config); await activateProviderAuthRuntimeSnapshot({ snapshot: recovered, - expectedRevision: getActiveSecretsRuntimeSnapshotRevision(), + expectedRevision: getActiveSecretsRuntimeSnapshotRevisionState(), activateSnapshotIfCurrent: () => { activateSecretsRuntimeSnapshotForTest(recovered); return true; @@ -797,7 +797,7 @@ describe("gateway startup config secret preflight", () => { const recovered = preparedSnapshot(config); await activateProviderAuthRuntimeSnapshot({ snapshot: recovered, - expectedRevision: getActiveSecretsRuntimeSnapshotRevision(), + expectedRevision: getActiveSecretsRuntimeSnapshotRevisionState(), activateSnapshotIfCurrent: () => { activateSecretsRuntimeSnapshotForTest(recovered); return true; @@ -848,7 +848,7 @@ describe("gateway startup config secret preflight", () => { await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( rolledBackCandidate, - getActiveSecretsRuntimeSnapshotRevision(), + getActiveSecretsRuntimeSnapshotRevisionState(), { reason: "reload", activate: true, deferStatePublication: true }, ), ).resolves.toBe(rolledBackCandidate); @@ -859,7 +859,7 @@ describe("gateway startup config secret preflight", () => { await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( committedCandidate, - getActiveSecretsRuntimeSnapshotRevision(), + getActiveSecretsRuntimeSnapshotRevisionState(), { reason: "reload", activate: true, deferStatePublication: true }, ), ).resolves.toBe(committedCandidate); @@ -918,11 +918,11 @@ describe("gateway startup config secret preflight", () => { await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( degraded, - getActiveSecretsRuntimeSnapshotRevision(), + getActiveSecretsRuntimeSnapshotRevisionState(), { reason: "reload", activate: true, deferStatePublication: true }, ), ).resolves.toBe(degraded); - const outerRevision = getActiveSecretsRuntimeSnapshotRevision(); + const outerRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const descendant: PreparedSecretsRuntimeSnapshot = structuredClone(degraded); descendant.degradedOwners?.push({ ownerKind: "provider", @@ -985,11 +985,11 @@ describe("gateway startup config secret preflight", () => { await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( degraded, - getActiveSecretsRuntimeSnapshotRevision(), + getActiveSecretsRuntimeSnapshotRevisionState(), { reason: "reload", activate: true, deferStatePublication: true }, ), ).resolves.toBe(degraded); - const outerRevision = getActiveSecretsRuntimeSnapshotRevision(); + const outerRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const recovered = preparedSnapshot(config); await expect( @@ -1060,11 +1060,11 @@ describe("gateway startup config secret preflight", () => { await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( providerDegraded, - getActiveSecretsRuntimeSnapshotRevision(), + getActiveSecretsRuntimeSnapshotRevisionState(), { reason: "reload", activate: true, deferStatePublication: true }, ), ).resolves.toBe(providerDegraded); - const outerRevision = getActiveSecretsRuntimeSnapshotRevision(); + const outerRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const recovered = preparedSnapshot(config); await expect( @@ -1126,11 +1126,11 @@ describe("gateway startup config secret preflight", () => { await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( recovered, - getActiveSecretsRuntimeSnapshotRevision(), + getActiveSecretsRuntimeSnapshotRevisionState(), { reason: "reload", activate: true, deferStatePublication: true }, ), ).resolves.toBe(recovered); - const outerRevision = getActiveSecretsRuntimeSnapshotRevision(); + const outerRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const descendant = structuredClone(recovered); await expect( @@ -1208,7 +1208,7 @@ describe("gateway startup config secret preflight", () => { const sourceOnly = preparedSnapshot(stableConfig); activateSecretsRuntimeSnapshotForTest(sourceOnly); - const committedRevision = getActiveSecretsRuntimeSnapshotRevision(); + const committedRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const descendant = structuredClone(sourceOnly); expect( activateSecretsRuntimeSnapshotStateIfCurrent({ @@ -1259,7 +1259,7 @@ describe("gateway startup config secret preflight", () => { activateRuntimeSecretsSnapshot, }); activateSecretsRuntimeSnapshotForTest(initial); - const initialRevision = getActiveSecretsRuntimeSnapshotRevision(); + const initialRevision = getActiveSecretsRuntimeSnapshotRevisionState(); setRuntimeAuthProfileStoreSnapshot( { version: 1, @@ -1283,7 +1283,9 @@ describe("gateway startup config secret preflight", () => { }), ).resolves.toBeNull(); expect(activateRuntimeSecretsSnapshot).not.toHaveBeenCalled(); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:default"]).toMatchObject({ + expect( + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:default"], + ).toMatchObject({ access: "access-new", refresh: "refresh-new", }); @@ -1298,7 +1300,7 @@ describe("gateway startup config secret preflight", () => { activateRuntimeSecretsSnapshot: vi.fn(activateSecretsRuntimeSnapshotForTest), }); activateSecretsRuntimeSnapshotForTest(initial); - const initialRevision = getActiveSecretsRuntimeSnapshotRevision(); + const initialRevision = getActiveSecretsRuntimeSnapshotRevisionState(); let releasePublication: (() => void) | undefined; const publicationBlocked = new Promise((resolve) => { releasePublication = resolve; @@ -1330,7 +1332,7 @@ describe("gateway startup config secret preflight", () => { releasePublication?.(); await candidateActivation; await laterActivation; - expect(getActiveSecretsRuntimeSnapshot()?.config.gateway?.auth?.token).toBe("later-token"); + expect(getActiveSecretsRuntimeSnapshotState()?.config.gateway?.auth?.token).toBe("later-token"); }); it("measures startup auth subphases", async () => { @@ -2092,7 +2094,7 @@ describe("gateway startup config secret preflight", () => { }), ).rejects.toThrow(missingSecretError.message); shouldResolve = true; - const activeRevision = getActiveSecretsRuntimeSnapshotRevision(); + const activeRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const prepared = await activateRuntimeSecrets(sourceConfig, { reason: "restart-check", activate: false, @@ -2140,7 +2142,7 @@ describe("gateway startup config secret preflight", () => { }), ).rejects.toThrow(missingSecretError.message); shouldResolve = true; - const sourceOnlyRevision = getActiveSecretsRuntimeSnapshotRevision(); + const sourceOnlyRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const sourceOnly = await activateRuntimeSecrets(sourceConfig, { reason: "reload", activate: false, @@ -2173,7 +2175,7 @@ describe("gateway startup config secret preflight", () => { "SECRETS_RELOADER_DEGRADED", ]); shouldResolve = true; - const newerRevision = getActiveSecretsRuntimeSnapshotRevision(); + const newerRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const newerPrepared = await activateRuntimeSecrets(sourceConfig, { reason: "reload", activate: false, @@ -2218,8 +2220,8 @@ describe("gateway startup config secret preflight", () => { publishFailureAsDegraded: true, }), ).rejects.toThrow(missingSecretError.message); - const revertedSnapshot = getActiveSecretsRuntimeSnapshot()!; - const revertedRevision = getActiveSecretsRuntimeSnapshotRevision(); + const revertedSnapshot = getActiveSecretsRuntimeSnapshotState()!; + const revertedRevision = getActiveSecretsRuntimeSnapshotRevisionState(); await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( revertedSnapshot, @@ -2267,11 +2269,11 @@ describe("gateway startup config secret preflight", () => { publishFailureAsDegraded: true, }), ).rejects.toThrow(missingSecretError.message); - const unrelatedRevertedSnapshot = getActiveSecretsRuntimeSnapshot()!; + const unrelatedRevertedSnapshot = getActiveSecretsRuntimeSnapshotState()!; await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( unrelatedRevertedSnapshot, - getActiveSecretsRuntimeSnapshotRevision(), + getActiveSecretsRuntimeSnapshotRevisionState(), { reason: "reload", activate: true, deferStatePublication: true }, ), ).resolves.toMatchObject({ sourceConfig }); @@ -2290,8 +2292,8 @@ describe("gateway startup config secret preflight", () => { publishFailureAsDegraded: true, }), ).rejects.toThrow(missingSecretError.message); - const unchangedSnapshot = getActiveSecretsRuntimeSnapshot()!; - const unchangedRevision = getActiveSecretsRuntimeSnapshotRevision(); + const unchangedSnapshot = getActiveSecretsRuntimeSnapshotState()!; + const unchangedRevision = getActiveSecretsRuntimeSnapshotRevisionState(); await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( unchangedSnapshot, @@ -2389,7 +2391,7 @@ describe("gateway startup config secret preflight", () => { await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( revertedSnapshot, - getActiveSecretsRuntimeSnapshotRevision(), + getActiveSecretsRuntimeSnapshotRevisionState(), { reason: "reload", activate: true, deferStatePublication: true }, ), ).resolves.toBe(revertedSnapshot); @@ -2402,7 +2404,7 @@ describe("gateway startup config secret preflight", () => { await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( fullyResolvedSnapshot, - getActiveSecretsRuntimeSnapshotRevision(), + getActiveSecretsRuntimeSnapshotRevisionState(), { reason: "reload", activate: true, deferStatePublication: true }, ), ).resolves.toBe(fullyResolvedSnapshot); @@ -2433,7 +2435,7 @@ describe("gateway startup config secret preflight", () => { await expect( activateRuntimeSecrets.activatePreparedSnapshotIfCurrent?.( secondRevertedSnapshot, - getActiveSecretsRuntimeSnapshotRevision(), + getActiveSecretsRuntimeSnapshotRevisionState(), { reason: "reload", activate: true, deferStatePublication: true }, ), ).resolves.toBe(secondRevertedSnapshot); @@ -2689,8 +2691,8 @@ describe("gateway startup config secret preflight", () => { try { const { - clearSecretsRuntimeSnapshot: clearImportedSecretsRuntimeSnapshot, - getActiveSecretsRuntimeSnapshot: getImportedSecretsRuntimeSnapshot, + clearSecretsRuntimeSnapshotState: clearImportedSecretsRuntimeSnapshot, + getActiveSecretsRuntimeSnapshotState: getImportedSecretsRuntimeSnapshot, } = await import("../secrets/runtime-state.js"); const { getRuntimeConfigSnapshotRefreshHandler } = await import("../config/runtime-snapshot.js"); @@ -2858,7 +2860,7 @@ describe("gateway startup config secret preflight", () => { activate: true, includeAuthStoreRefs: false, }); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:default"]).toEqual( + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:default"]).toEqual( credential, ); }); diff --git a/src/gateway/server-startup-config.ts b/src/gateway/server-startup-config.ts index a326ff3bbf2d..8135c96f4130 100644 --- a/src/gateway/server-startup-config.ts +++ b/src/gateway/server-startup-config.ts @@ -27,8 +27,8 @@ import { import { activateSecretsRuntimeSnapshotState, graftActiveSecretsRuntimeAuthState, - getActiveSecretsRuntimeSnapshot, - getActiveSecretsRuntimeSnapshotRevision, + getActiveSecretsRuntimeSnapshotState, + getActiveSecretsRuntimeSnapshotRevisionState, hasActiveSecretsRuntimeSnapshotLineage, hasSameSecretReloadContract, hasCurrentAuthStoreCredentialsRevision, @@ -268,7 +268,7 @@ export function createRuntimeSecretsActivator(params: { const activationScope = options?.stateScope ?? "full"; if (activationParams.activate && (statePrepared.degradedOwners?.length ?? 0) > 0) { if (activationParams.deferStatePublication === true) { - const activationRevision = getActiveSecretsRuntimeSnapshotRevision(); + const activationRevision = getActiveSecretsRuntimeSnapshotRevisionState(); deferredStateTransitions.set(prepared, { kind: "degraded", activationRevision, @@ -282,7 +282,7 @@ export function createRuntimeSecretsActivator(params: { } else if (activationParams.activate && secretsDegraded) { if (activationParams.deferStatePublication === true) { if (activeDegradationGeneration !== null) { - const activationRevision = getActiveSecretsRuntimeSnapshotRevision(); + const activationRevision = getActiveSecretsRuntimeSnapshotRevisionState(); deferredStateTransitions.set(prepared, { kind: "recovered", activationRevision, @@ -415,7 +415,7 @@ export function createRuntimeSecretsActivator(params: { const prepareRuntimeSecretsSnapshot = params.prepareRuntimeSecretsSnapshot ?? secretsRuntime!.prepareSecretsRuntimeSnapshot; const allowUnavailableSecretOwners = - activationParams.reason !== "startup" || getActiveSecretsRuntimeSnapshot() === null; + activationParams.reason !== "startup" || getActiveSecretsRuntimeSnapshotState() === null; const prepared = await measureDiagnosticsTimelineSpan( "secrets.prepare", () => @@ -485,7 +485,7 @@ export function createRuntimeSecretsActivator(params: { : undefined; return await runWithSecretsActivationLock(async () => { if ( - getActiveSecretsRuntimeSnapshotRevision() !== expectedRevision || + getActiveSecretsRuntimeSnapshotRevisionState() !== expectedRevision || !hasCurrentAuthStoreCredentialsRevision(snapshot) || (canActivate && !canActivate()) ) { @@ -522,7 +522,7 @@ export function createRuntimeSecretsActivator(params: { registerProviderAuthRuntimeSnapshotActivationOwner({ runExclusive: runWithSecretsActivationLock, isCurrent: (snapshot, expectedRevision) => - getActiveSecretsRuntimeSnapshotRevision() === expectedRevision && + getActiveSecretsRuntimeSnapshotRevisionState() === expectedRevision && hasCurrentAuthStoreCredentialsRevision(snapshot), assertValid: (snapshot) => assertRuntimeGatewayAuthNotKnownWeak(snapshot.config), publish: async (snapshot) => { @@ -553,7 +553,7 @@ export function createRuntimeSecretsActivator(params: { options?.sourceOnly === true && options.expectedRevision !== undefined && hasActiveSecretsRuntimeSnapshotLineage(options.expectedRevision); - const activeSnapshot = sourceOnlyOwnsLineage ? getActiveSecretsRuntimeSnapshot() : null; + const activeSnapshot = sourceOnlyOwnsLineage ? getActiveSecretsRuntimeSnapshotState() : null; const sourceOnlyDegradationGeneration = activeDegradationGeneration; const sourceOnlyContractRecovered = activeSnapshot !== null && @@ -574,7 +574,7 @@ export function createRuntimeSecretsActivator(params: { if (!hasActiveSecretsRuntimeSnapshotLineage(transition.activationRevision)) { return; } - const activeSnapshot = getActiveSecretsRuntimeSnapshot(); + const activeSnapshot = getActiveSecretsRuntimeSnapshotState(); if (!activeSnapshot) { return; } diff --git a/src/gateway/server-startup-secret-owner-isolation.test.ts b/src/gateway/server-startup-secret-owner-isolation.test.ts index 84c9922680f0..73291a5565c1 100644 --- a/src/gateway/server-startup-secret-owner-isolation.test.ts +++ b/src/gateway/server-startup-secret-owner-isolation.test.ts @@ -5,7 +5,7 @@ import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; import { resolveDefaultAgentDir } from "../agents/agent-scope-config.js"; -import { getRuntimeAuthProfileStoreSnapshot } from "../agents/auth-profiles/runtime-snapshots.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"; @@ -591,7 +591,7 @@ describe("Gateway startup SecretRef owner isolation", () => { expect(active?.degradedOwners).toMatchObject([ { ownerKind: "account", ownerId, state: "unavailable" }, ]); - const store = getRuntimeAuthProfileStoreSnapshot(agentDir); + const store = getRuntimeAuthProfileStoreSnapshotCore(agentDir); if (!store || !active) { throw new Error("Expected activated Gateway auth profile snapshot"); } diff --git a/src/gateway/server-worker-environment-startup.ts b/src/gateway/server-worker-environment-startup.ts index cec6bb7a5914..c1f0c649966f 100644 --- a/src/gateway/server-worker-environment-startup.ts +++ b/src/gateway/server-worker-environment-startup.ts @@ -3,7 +3,7 @@ import { getRuntimeConfig } from "../config/config.js"; import type { PluginRegistry } from "../plugins/registry-types.js"; import { getActiveSecretsRuntimeConfigSnapshot, - getActiveSecretsRuntimeEnv, + getActiveSecretsRuntimeEnvState, } from "../secrets/runtime-state.js"; import { createLazyRuntimeModule } from "../shared/lazy-runtime.js"; import type { WorkerBundleProducer, WorkerNpmArtifact } from "./worker-environments/bundle.js"; @@ -180,7 +180,7 @@ export async function createGatewayWorkerEnvironmentRuntime(params: { kind: "material", contents: await workerRuntime.resolveSecretRefString(genericKeyRef, { config: getActiveSecretsRuntimeConfigSnapshot()?.sourceConfig ?? getRuntimeConfig(), - env: getActiveSecretsRuntimeEnv(), + env: getActiveSecretsRuntimeEnvState(), }), }), }); diff --git a/src/gateway/server-worker-placement-startup.test.ts b/src/gateway/server-worker-placement-startup.test.ts index e56d1fcc0d59..7533539f2287 100644 --- a/src/gateway/server-worker-placement-startup.test.ts +++ b/src/gateway/server-worker-placement-startup.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from "vitest"; -import { createDeferred } from "../shared/deferred.js"; +import { createDeferredCore } from "../shared/deferred.js"; import { coordinateWorkerPlacementDispatch, type GatewayWorkerPlacementRuntime, @@ -33,8 +33,8 @@ describe("worker placement dispatch coordinator", () => { }); it("coalesces an identical dispatch and rejects a conflicting in-flight request", async () => { - const dispatchStarted = createDeferred(); - const releaseDispatch = createDeferred(); + const dispatchStarted = createDeferredCore(); + const releaseDispatch = createDeferredCore(); const active = { state: "active" }; const dispatch = vi.fn(async () => { dispatchStarted.resolve(); @@ -76,8 +76,8 @@ describe("worker placement dispatch coordinator", () => { }); it("joins a retry before a queued reconciliation after dispatch failure", async () => { - const dispatchStarted = createDeferred(); - const releaseDispatch = createDeferred(); + const dispatchStarted = createDeferredCore(); + const releaseDispatch = createDeferredCore(); const dispatchError = new Error("provision failed"); const dispatch = vi.fn(async () => { dispatchStarted.resolve(); @@ -116,8 +116,8 @@ describe("worker placement dispatch coordinator", () => { }); it("coalesces full sweeps but runs a fresh targeted pass with its environment id", async () => { - const fullSweepStarted = createDeferred(); - const releaseFullSweep = createDeferred(); + const fullSweepStarted = createDeferredCore(); + const releaseFullSweep = createDeferredCore(); const reconcileActive = vi.fn(async (environmentId?: string) => { if (environmentId === undefined) { fullSweepStarted.resolve(); diff --git a/src/gateway/server.sessions.archive-lifecycle.test.ts b/src/gateway/server.sessions.archive-lifecycle.test.ts index 6ff165b3884d..91916cd147fe 100644 --- a/src/gateway/server.sessions.archive-lifecycle.test.ts +++ b/src/gateway/server.sessions.archive-lifecycle.test.ts @@ -8,7 +8,7 @@ import { isSessionLifecycleMutationActive, runExclusiveSessionLifecycleMutation, } from "../sessions/session-lifecycle-admission.js"; -import { createDeferred } from "../shared/deferred.js"; +import { createDeferredCore } from "../shared/deferred.js"; import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js"; import { markChatAbortTerminalPersistenceError } from "./chat-abort-lifecycle-internal.js"; import { registerChatAbortController, removeChatAbortControllerEntry } from "./chat-abort.js"; @@ -65,7 +65,7 @@ function activeRunContext(params: { runId: string; sessionId: string; sessionKey: string; - persistence: ReturnType>; + persistence: ReturnType>; ownerConnId?: string; }) { const chatAbortControllers = new Map(); @@ -306,7 +306,7 @@ test("sessions.patch cancels active work and commits only after admission and te interrupted = true; }, }); - const persistence = createDeferred(); + const persistence = createDeferredCore(); const active = activeRunContext({ runId, sessionId, @@ -385,7 +385,7 @@ test("sharing revocation fences archive before cancellation and forces fresh aut interrupted = true; }, }); - const persistence = createDeferred(); + const persistence = createDeferredCore(); const active = activeRunContext({ runId, sessionId, sessionKey, persistence }); const requestContext = await archiveLifecycleRequestContext(active.context); const placement = workerPlacement({ sessionId, sessionKey, state: "active" }); @@ -410,7 +410,7 @@ test("sharing revocation fences archive before cancellation and forces fresh aut throw new Error("expected resolved sharing target"); } - const releaseAudit = createDeferred(); + const releaseAudit = createDeferredCore(); sessionAuditGate.entered.mockClear(); sessionAuditGate.wait = releaseAudit.promise; let sharing: Promise | undefined; @@ -492,11 +492,11 @@ test("archive retains the lifecycle fence until drain and commit before sharing identities: [sessionKey, sessionId], assertAllowed: () => {}, }); - const persistence = createDeferred(); + const persistence = createDeferredCore(); const active = activeRunContext({ runId, sessionId, sessionKey, persistence }); const requestContext = await archiveLifecycleRequestContext(active.context); let placement = workerPlacement({ sessionId, sessionKey, state: "active" }); - const reclaimGate = createDeferred(); + const reclaimGate = createDeferredCore(); const reclaim = vi.fn(async () => { await reclaimGate.promise; placement = workerPlacement({ sessionId, sessionKey, state: "reclaimed" }); @@ -569,9 +569,9 @@ test("alias archive lets the canonical cloud reclaim barrier reenter without dea const sessionId = "session-archive-cloud-alias"; await writeSessionStore({ entries: { [sessionKey]: sessionStoreEntry(sessionId) } }); let placement = workerPlacement({ sessionId, sessionKey, state: "active" }); - const reclaimEntered = createDeferred(); - const allowNestedReclaim = createDeferred(); - const contenderRelease = createDeferred(); + const reclaimEntered = createDeferredCore(); + const allowNestedReclaim = createDeferredCore(); + const contenderRelease = createDeferredCore(); const reclaim = vi.fn(async () => { reclaimEntered.resolve(); await allowNestedReclaim.promise; @@ -752,7 +752,7 @@ test("sessions.patch returns UNAVAILABLE when terminal persistence fails", async const sessionId = "session-archive-persistence-failure"; const runId = "run-archive-persistence-failure"; await writeSessionStore({ entries: { [sessionKey]: sessionStoreEntry(sessionId) } }); - const persistence = createDeferred(); + const persistence = createDeferredCore(); const active = activeRunContext({ runId, sessionId, sessionKey, persistence }); try { const archive = directSessionReq( @@ -862,7 +862,7 @@ test("sessions.patchMany prepares independent archive drains concurrently and re [secondKey]: sessionStoreEntry(secondSessionId), }, }); - const firstDrained = createDeferred(); + const firstDrained = createDeferredCore(); const firstRelease = vi.fn(); const secondRelease = vi.fn(); const beginInferenceSessionDrain = vi.fn((sessionId: string) => ({ @@ -1012,7 +1012,7 @@ test("sessions.patch rejects a generation replaced after the exact preparation r const sessionId = "session-archive-generation-race"; const runId = "run-archive-generation-race"; await writeSessionStore({ entries: { [sessionKey]: sessionStoreEntry(sessionId) } }); - const persistence = createDeferred(); + const persistence = createDeferredCore(); const active = activeRunContext({ runId, sessionId, sessionKey, persistence }); let placement = workerPlacement({ sessionId, sessionKey, state: "active" }); const dispatch = vi.fn(); diff --git a/src/gateway/server.sessions.archive-worker-placement.test.ts b/src/gateway/server.sessions.archive-worker-placement.test.ts index df6799e93c81..60828b83af8a 100644 --- a/src/gateway/server.sessions.archive-worker-placement.test.ts +++ b/src/gateway/server.sessions.archive-worker-placement.test.ts @@ -1,6 +1,6 @@ import { afterEach, expect, test, vi } from "vitest"; import { loadSessionEntry } from "../config/sessions/session-accessor.js"; -import { createDeferred } from "../shared/deferred.js"; +import { createDeferredCore } from "../shared/deferred.js"; import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js"; import { embeddedRunMock, writeSessionStore } from "./test-helpers.js"; import { @@ -89,7 +89,7 @@ test("sessions.patch reclaims the exact active cloud placement before archive me const sessionId = "session-archive-cloud-active"; await writeSessionStore({ entries: { [sessionKey]: sessionStoreEntry(sessionId) } }); let placement = workerPlacement({ sessionId, sessionKey, state: "active" }); - const reclaimGate = createDeferred(); + const reclaimGate = createDeferredCore(); const reclaim = vi.fn(async () => { await reclaimGate.promise; placement = workerPlacement({ sessionId, sessionKey, state: "reclaimed" }); @@ -200,7 +200,7 @@ test("sessions.patch rejects a placement identity changed during the runtime dra const sessionId = "session-archive-cloud-fresh-placement"; await writeSessionStore({ entries: { [sessionKey]: sessionStoreEntry(sessionId) } }); let placement = workerPlacement({ sessionId, sessionKey, state: "active" }); - const drainGate = createDeferred(); + const drainGate = createDeferredCore(); const drainStarted = vi.fn(); const release = vi.fn(); const reclaim = vi.fn(); diff --git a/src/gateway/server.sessions.reset-hooks.succession.test.ts b/src/gateway/server.sessions.reset-hooks.succession.test.ts index 59aac06ee8a0..abc6f5b29a3b 100644 --- a/src/gateway/server.sessions.reset-hooks.succession.test.ts +++ b/src/gateway/server.sessions.reset-hooks.succession.test.ts @@ -1,7 +1,7 @@ // sessions.create parent-disposition coverage. Kept separate because the main // reset-hook suite is already at its max-lines budget. import { expect, test, vi } from "vitest"; -import { createDeferred } from "../shared/deferred.js"; +import { createDeferredCore } from "../shared/deferred.js"; import { embeddedRunMock } from "./test-helpers.js"; import { writeSessionStore } from "./test-helpers.js"; import { @@ -58,8 +58,8 @@ async function startDeferredSessionCleanup(sessionId: string) { embeddedRunMock.activeIds.add(sessionId); embeddedRunMock.waitResults.set(sessionId, false); - const retirement = createDeferred(); - const retirementStarted = createDeferred(); + const retirement = createDeferredCore(); + const retirementStarted = createDeferredCore(); let retirements = 0; bundleMcpRuntimeMocks.retireSessionMcpRuntime.mockImplementation( async ({ retainAcrossReuse }) => { @@ -195,7 +195,7 @@ test("completed wait keeps cleanup armed when a same-id replacement starts durin providerRuntimeMocks.cleanupSessionResources.mockClear(); embeddedRunMock.activeIds.add(sessionId); embeddedRunMock.waitResults.set(sessionId, true); - const retirement = createDeferred(); + const retirement = createDeferredCore(); let terminalRetirements = 0; bundleMcpRuntimeMocks.retireSessionMcpRuntime.mockImplementation( async ({ retainAcrossReuse }) => { diff --git a/src/gateway/server/ws-connection/message-handler.worker.test.ts b/src/gateway/server/ws-connection/message-handler.worker.test.ts index 856ed1e0a5ee..c5ac480cd248 100644 --- a/src/gateway/server/ws-connection/message-handler.worker.test.ts +++ b/src/gateway/server/ws-connection/message-handler.worker.test.ts @@ -25,7 +25,7 @@ import { resetGatewayWorkAdmission, tryBeginGatewaySuspendAdmission, } from "../../../process/gateway-work-admission.js"; -import { createDeferred } from "../../../shared/deferred.js"; +import { createDeferredCore } from "../../../shared/deferred.js"; import type { WorkerConnectionIdentity } from "../../worker-environments/connection-identity.js"; import { createGatewayWsTestSocket } from "../ws-connection.test-helpers.js"; import type { GatewayWsClient } from "../ws-types.js"; @@ -347,7 +347,7 @@ describe("dedicated worker websocket protocol", () => { }); it("keeps heartbeats flowing while a session operation is pending", async () => { - const operation = createDeferred(); + const operation = createDeferredCore(); const harness = attachHarness({ identity: ATTACHED_IDENTITY, onSessionTool: () => operation.promise, @@ -378,7 +378,7 @@ describe("dedicated worker websocket protocol", () => { it("continues durable session work but suppresses its response after connection cleanup", async () => { let operationStarted = false; let operationSignal: AbortSignal | undefined; - const operation = createDeferred(); + const operation = createDeferredCore(); const harness = attachHarness({ identity: ATTACHED_IDENTITY, onSessionTool: (signal) => { diff --git a/src/gateway/worker-environments/tunnel.ts b/src/gateway/worker-environments/tunnel.ts index de9de64fe66c..f6f76acf2a45 100644 --- a/src/gateway/worker-environments/tunnel.ts +++ b/src/gateway/worker-environments/tunnel.ts @@ -4,7 +4,7 @@ import { withTimeout } from "../../infra/fs-safe.js"; import { createSubsystemLogger } from "../../logging/subsystem.js"; import type { WorkerSshEndpoint } from "../../plugins/types.js"; import type { SpawnResult } from "../../process/exec.js"; -import { createDeferred, type Deferred } from "../../shared/deferred.js"; +import { createDeferredCore, type Deferred } from "../../shared/deferred.js"; import { createWorkerDesktopTunnels } from "./desktop-tunnel.js"; import { advanceWorkerSshAfterTransportExit, @@ -314,7 +314,7 @@ export function createWorkerTunnelManager(options: WorkerTunnelManagerOptions = // Each established child owns one readiness barrier. Replace it as soon as that child // is lost so same-owner callers wait for the reconnect instead of using a stale handle. entry.status = "reconnecting"; - const readiness = createDeferred(); + const readiness = createDeferredCore(); void readiness.promise.catch(() => undefined); entry.readiness = readiness; } @@ -418,7 +418,7 @@ export function createWorkerTunnelManager(options: WorkerTunnelManagerOptions = const environmentKey = stableWorkerPathComponent(request.environmentId, 16); const remoteDirectory = `/tmp/ocw-${environmentKey}-${request.ownerEpoch}`; - const readiness = createDeferred(); + const readiness = createDeferredCore(); void readiness.promise.catch(() => undefined); const entry: TunnelEntry = { environmentId: request.environmentId, diff --git a/src/infra/exec-approval-channel-runtime.ts b/src/infra/exec-approval-channel-runtime.ts index ef768893c21a..fd1c62f332ec 100644 --- a/src/infra/exec-approval-channel-runtime.ts +++ b/src/infra/exec-approval-channel-runtime.ts @@ -6,7 +6,7 @@ import type { GatewayClient, GatewayReconnectPausedInfo } from "../gateway/clien import { isApprovalMethod } from "../gateway/method-scopes.js"; import { createOperatorApprovalsGatewayClient } from "../gateway/operator-approvals-client.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; -import { createDeferred } from "../shared/deferred.js"; +import { createDeferredCore } from "../shared/deferred.js"; import { createPendingApprovalRegistry } from "../shared/pending-approval-registry.js"; import { getGatewayNativeApprovalRuntime } from "./approval-gateway-runtime-context.js"; import { @@ -322,7 +322,7 @@ export function createExecApprovalChannelRuntime< return; } - const ready = createDeferred(); + const ready = createDeferredCore(); let lastConnectError: unknown = null; const client = await createOperatorApprovalsGatewayClient({ diff --git a/src/infra/net/pinned-dispatcher-pool.test.ts b/src/infra/net/pinned-dispatcher-pool.test.ts index 871d07133f0d..f47c763928de 100644 --- a/src/infra/net/pinned-dispatcher-pool.test.ts +++ b/src/infra/net/pinned-dispatcher-pool.test.ts @@ -5,7 +5,7 @@ import { closeProviderTransportDispatcherPool, getProviderTransportDispatcherPool, } from "../../agents/provider-transport-dispatcher-pool.js"; -import { createDeferred } from "../../shared/deferred.js"; +import { createDeferredCore } from "../../shared/deferred.js"; import { PinnedDispatcherPool } from "./pinned-dispatcher-pool.js"; function createDispatcher() { @@ -139,7 +139,7 @@ describe("PinnedDispatcherPool", () => { }); it("does not publish a replacement generation while shutdown is closing", async () => { - const close = createDeferred(); + const close = createDeferredCore(); const pool = getProviderTransportDispatcherPool(); const lease = pool.acquire({ key: "origin-a/pin-a", diff --git a/src/logging/diagnostic-session-state.ts b/src/logging/diagnostic-session-state.ts index e0eb9024be36..f1a45078a7c8 100644 --- a/src/logging/diagnostic-session-state.ts +++ b/src/logging/diagnostic-session-state.ts @@ -198,11 +198,6 @@ export function peekDiagnosticSessionState(ref: SessionRef): SessionState | unde ); } -/** Returns the current state count for pruning tests. */ -export function getDiagnosticSessionStateCountForTest(): number { - return diagnosticSessionStates.size; -} - /** Clears all process-local diagnostic session state for tests. */ export function resetDiagnosticSessionStateForTest(): void { diagnosticSessionStates.clear(); diff --git a/src/logging/diagnostic-support-export.ts b/src/logging/diagnostic-support-export.ts index 1e1356bacc90..fb85573fd216 100644 --- a/src/logging/diagnostic-support-export.ts +++ b/src/logging/diagnostic-support-export.ts @@ -6,7 +6,7 @@ import { asOptionalRecord } from "@openclaw/normalization-core/record-coerce"; import { parseConfigJson5 } from "../config/io.js"; import { resolveConfigPath, resolveStateDir } from "../config/paths.js"; import { redactConfigObject } from "../config/redact-snapshot.js"; -import { buildConfigSchema } from "../config/schema.js"; +import { buildConfigSchemaCore } from "../config/schema.js"; import { isMissingPathError } from "../infra/errors.js"; import { resolveHomeRelativePath } from "../infra/home-dir.js"; import { readRegularFileSync } from "../infra/regular-file.js"; @@ -298,7 +298,7 @@ function sanitizeConfigShape( function sanitizeConfigDetails(parsed: unknown, redaction: SupportRedactionContext): unknown { return sanitizeSupportConfigValue( - redactConfigObject(parsed, buildConfigSchema().uiHints), + redactConfigObject(parsed, buildConfigSchemaCore().uiHints), redaction, ); } diff --git a/src/logging/diagnostic.test.ts b/src/logging/diagnostic.test.ts index 0c26d04207e2..0364e8bcb98b 100644 --- a/src/logging/diagnostic.test.ts +++ b/src/logging/diagnostic.test.ts @@ -36,7 +36,6 @@ import type { StuckSessionRecoveryOutcome } from "./diagnostic-session-recovery. import { diagnosticSessionStates, getDiagnosticSessionState, - getDiagnosticSessionStateCountForTest, peekDiagnosticSessionState, pruneDiagnosticSessionStates, resetDiagnosticSessionStateForTest, @@ -173,12 +172,12 @@ describe("diagnostic session state pruning", () => { it("evicts stale idle session states", () => { getDiagnosticSessionState({ sessionId: "stale-1" }); - expect(getDiagnosticSessionStateCountForTest()).toBe(1); + expect(diagnosticSessionStates.size).toBe(1); vi.advanceTimersByTime(31 * 60 * 1000); getDiagnosticSessionState({ sessionId: "fresh-1" }); - expect(getDiagnosticSessionStateCountForTest()).toBe(1); + expect(diagnosticSessionStates.size).toBe(1); }); it("caps tracked session states to a bounded max", () => { @@ -194,7 +193,7 @@ describe("diagnostic session state pruning", () => { } pruneDiagnosticSessionStates(now + 2002, true); - expect(getDiagnosticSessionStateCountForTest()).toBe(2000); + expect(diagnosticSessionStates.size).toBe(2000); }); it("reuses keyed session state when later looked up by sessionId", () => { @@ -206,7 +205,7 @@ describe("diagnostic session state pruning", () => { expect(bySessionId).toBe(keyed); expect(bySessionId.sessionKey).toBe("agent:main:demo-channel:channel:c1"); - expect(getDiagnosticSessionStateCountForTest()).toBe(1); + expect(diagnosticSessionStates.size).toBe(1); }); it("canonicalizes sessionId-only state when the sessionKey becomes known", () => { @@ -221,7 +220,7 @@ describe("diagnostic session state pruning", () => { expect(diagnosticSessionStates.has("s1")).toBe(false); expect(diagnosticSessionStates.get(sessionKey)).toBe(keyed); expect(getDiagnosticSessionState({ sessionKey })).toBe(keyed); - expect(getDiagnosticSessionStateCountForTest()).toBe(1); + expect(diagnosticSessionStates.size).toBe(1); }); it("merges split sessionId and sessionKey state without leaving stale queued work", () => { @@ -240,13 +239,13 @@ describe("diagnostic session state pruning", () => { expect(merged.queueDepth).toBe(2); expect(merged.state).toBe("processing"); expect(diagnosticSessionStates.has("s1")).toBe(false); - expect(getDiagnosticSessionStateCountForTest()).toBe(1); + expect(diagnosticSessionStates.size).toBe(1); logSessionStateChange({ sessionId: "s1", sessionKey, state: "idle", reason: "run_completed" }); logSessionStateChange({ sessionKey, state: "idle", reason: "message_completed" }); expect(getDiagnosticSessionState({ sessionKey }).queueDepth).toBe(0); - expect(getDiagnosticSessionStateCountForTest()).toBe(1); + expect(diagnosticSessionStates.size).toBe(1); }); }); @@ -2269,7 +2268,7 @@ describe("stuck session diagnostics threshold", () => { } expect(events).toStrictEqual([]); - expect(getDiagnosticSessionStateCountForTest()).toBe(0); + expect(diagnosticSessionStates.size).toBe(0); }); it("checks memory pressure every tick without recording idle samples", () => { diff --git a/src/logging/diagnostic.ts b/src/logging/diagnostic.ts index b4273483ae93..46387c522b45 100644 --- a/src/logging/diagnostic.ts +++ b/src/logging/diagnostic.ts @@ -55,7 +55,6 @@ import type { import { diagnosticSessionStates, getDiagnosticSessionState, - getDiagnosticSessionStateCountForTest as getDiagnosticSessionStateCountForTestImpl, pruneDiagnosticSessionStates, resetDiagnosticSessionStateForTest, type SessionRef, @@ -1393,7 +1392,7 @@ export function stopDiagnosticHeartbeat() { } export function getDiagnosticSessionStateCountForTest(): number { - return getDiagnosticSessionStateCountForTestImpl(); + return diagnosticSessionStates.size; } export function resetDiagnosticStateForTest(): void { diff --git a/src/media-understanding/apply.echo-transcript.test.ts b/src/media-understanding/apply.echo-transcript.test.ts index 04101e402e95..188acc87a15b 100644 --- a/src/media-understanding/apply.echo-transcript.test.ts +++ b/src/media-understanding/apply.echo-transcript.test.ts @@ -180,7 +180,7 @@ describe("applyMediaUnderstanding – echo transcript", () => { resolveAwsSdkEnvVarName: vi.fn(() => undefined), resolveEnvApiKey: vi.fn(() => null), resolveModelAuthMode: vi.fn(() => "api-key"), - getApiKeyForModel: getApiKeyForModelMock, + getApiKeyForModelCore: getApiKeyForModelMock, getCustomProviderApiKey: vi.fn(() => undefined), ensureAuthProfileStore: vi.fn(async () => ({})), resolveAuthProfileOrder: vi.fn(() => []), diff --git a/src/media-understanding/image-model-runtime.ts b/src/media-understanding/image-model-runtime.ts index 5b0be93b02b5..1cc21ee0f336 100644 --- a/src/media-understanding/image-model-runtime.ts +++ b/src/media-understanding/image-model-runtime.ts @@ -4,7 +4,7 @@ import { resolveModelAsync } from "../agents/embedded-agent-runner/model.js"; import { isMinimaxVlmModel } from "../agents/minimax-vlm.js"; import { applySecretRefHeaderSentinels, - getApiKeyForModel, + getApiKeyForModelCore, requireApiKey, } from "../agents/model-auth.js"; import { normalizeModelRef } from "../agents/model-selection.js"; @@ -104,7 +104,7 @@ async function prepareResolvedImageRuntime( ): Promise { let model = resolvedModel; const modelRuntime = getModelRegistryRuntime(modelRegistry); - const apiKeyInfo = await getApiKeyForModel({ + const apiKeyInfo = await getApiKeyForModelCore({ model, cfg: params.cfg, agentDir: params.agentDir, diff --git a/src/media-understanding/image.runtime-profile.test.ts b/src/media-understanding/image.runtime-profile.test.ts index 4807b6661130..c1d936e19f7a 100644 --- a/src/media-understanding/image.runtime-profile.test.ts +++ b/src/media-understanding/image.runtime-profile.test.ts @@ -118,7 +118,7 @@ vi.mock("../agents/models-config.js", async () => ({ vi.mock("../agents/model-auth.js", () => ({ applySecretRefHeaderSentinels: (model: unknown) => model, - getApiKeyForModel: getApiKeyForModelMock, + getApiKeyForModelCore: getApiKeyForModelMock, resolveApiKeyForProvider: resolveApiKeyForProviderMock, [REQUIRE_API_KEY_FIELD]: requireApiKeyMock, })); @@ -273,7 +273,7 @@ describe("describeImageWithModelCore", () => { function getApiKeyForModelCall(index = 0): AuthRequestCall { const call = (getApiKeyForModelMock.mock.calls as unknown[][]).at(index); if (!call) { - throw new Error(`Expected getApiKeyForModel call ${index}`); + throw new Error(`Expected getApiKeyForModelCore call ${index}`); } return call[0] as AuthRequestCall; } diff --git a/src/media-understanding/image.runtime-timeout.test.ts b/src/media-understanding/image.runtime-timeout.test.ts index 58d09b07b44d..8f4e8f9ced91 100644 --- a/src/media-understanding/image.runtime-timeout.test.ts +++ b/src/media-understanding/image.runtime-timeout.test.ts @@ -107,7 +107,7 @@ vi.mock("../agents/models-config.js", async () => ({ vi.mock("../agents/model-auth.js", () => ({ applySecretRefHeaderSentinels: (model: unknown) => model, - getApiKeyForModel: getApiKeyForModelMock, + getApiKeyForModelCore: getApiKeyForModelMock, resolveApiKeyForProvider: resolveApiKeyForProviderMock, [REQUIRE_API_KEY_FIELD]: requireApiKeyMock, })); diff --git a/src/media-understanding/image.test.ts b/src/media-understanding/image.test.ts index 2a26237d8b42..c661fc39fd75 100644 --- a/src/media-understanding/image.test.ts +++ b/src/media-understanding/image.test.ts @@ -117,7 +117,7 @@ vi.mock("../agents/models-config.js", async () => ({ vi.mock("../agents/model-auth.js", () => ({ applySecretRefHeaderSentinels: (model: unknown) => model, - getApiKeyForModel: getApiKeyForModelMock, + getApiKeyForModelCore: getApiKeyForModelMock, resolveApiKeyForProvider: resolveApiKeyForProviderMock, [REQUIRE_API_KEY_FIELD]: requireApiKeyMock, })); @@ -270,7 +270,7 @@ describe("describeImageWithModelCore", () => { function getApiKeyForModelCall(index = 0): AuthRequestCall { const call = (getApiKeyForModelMock.mock.calls as unknown[][]).at(index); if (!call) { - throw new Error(`Expected getApiKeyForModel call ${index}`); + throw new Error(`Expected getApiKeyForModelCore call ${index}`); } return call[0] as AuthRequestCall; } diff --git a/src/media/local-media-access.ts b/src/media/local-media-access.ts index bcab74f9853b..6f2ea755585f 100644 --- a/src/media/local-media-access.ts +++ b/src/media/local-media-access.ts @@ -32,7 +32,7 @@ export class LocalMediaAccessError extends Error { } /** Returns the default root allowlist for local media reads. */ -export function getDefaultLocalRoots(): readonly string[] { +export function getDefaultLocalRootsCore(): readonly string[] { return getDefaultMediaLocalRoots(); } @@ -49,7 +49,7 @@ async function resolveCanonicalBoundaryPath(root: string): Promise { export async function resolveLocalMediaRoots( localRoots?: readonly string[], ): Promise { - const roots = localRoots ?? getDefaultLocalRoots(); + const roots = localRoots ?? getDefaultLocalRootsCore(); return await Promise.all( roots.map(async (root) => { const resolvedRoot = await resolveCanonicalBoundaryPath(root); @@ -142,7 +142,7 @@ async function resolveLocalMediaBoundary( roots: [resolvedRoot], }; } - const roots = localRoots ?? getDefaultLocalRoots(); + const roots = localRoots ?? getDefaultLocalRootsCore(); const resolved = await resolveLocalMediaPathForContainment(mediaPath); if (localRoots === undefined) { diff --git a/src/media/web-media.ts b/src/media/web-media.ts index c1694f320d3b..d74f5679e60d 100644 --- a/src/media/web-media.ts +++ b/src/media/web-media.ts @@ -40,7 +40,7 @@ import { readRemoteMediaBuffer } from "./fetch.js"; import type { OutboundMediaReadFile } from "./load-options.js"; import { assertLocalMediaAllowed, - getDefaultLocalRoots, + getDefaultLocalRootsCore, LocalMediaAccessError, readLocalMediaFile, type LocalMediaAccessErrorCode, @@ -53,7 +53,7 @@ import { } from "./media-services.js"; import { extractOriginalFilename, getMediaDir } from "./store.js"; -export { getDefaultLocalRoots, LocalMediaAccessError }; +export { getDefaultLocalRootsCore, LocalMediaAccessError }; export type { LocalMediaAccessErrorCode }; /** Loaded media bytes plus resolved MIME kind and filename metadata for outbound/plugin callers. */ diff --git a/src/plugin-sdk/agent-harness-tool-runtime.ts b/src/plugin-sdk/agent-harness-tool-runtime.ts index a94ea315246d..06fa8e695525 100644 --- a/src/plugin-sdk/agent-harness-tool-runtime.ts +++ b/src/plugin-sdk/agent-harness-tool-runtime.ts @@ -5,7 +5,7 @@ * lifecycle facade used during plugin startup. */ import { - createAgentHarnessToolSurfaceRuntime as createCoreAgentHarnessToolSurfaceRuntime, + createAgentHarnessToolSurfaceRuntimeCore, type AgentHarnessToolSurfaceRuntime as CoreAgentHarnessToolSurfaceRuntime, } from "../agents/harness/tool-surface-bridge.js"; @@ -22,7 +22,7 @@ export type AgentHarnessToolSurfaceRuntime = Omit< }; export type AgentHarnessToolSurfaceRuntimeParams = Omit< - Parameters[0], + Parameters[0], "executeTool" > & { executeTool: NonNullable; @@ -31,5 +31,5 @@ export type AgentHarnessToolSurfaceRuntimeParams = Omit< export function createAgentHarnessToolSurfaceRuntime( params: AgentHarnessToolSurfaceRuntimeParams, ): AgentHarnessToolSurfaceRuntime { - return createCoreAgentHarnessToolSurfaceRuntime(params); + return createAgentHarnessToolSurfaceRuntimeCore(params); } diff --git a/src/plugin-sdk/allow-from.ts b/src/plugin-sdk/allow-from.ts index 3a6f77bebd9e..81145e7b0115 100644 --- a/src/plugin-sdk/allow-from.ts +++ b/src/plugin-sdk/allow-from.ts @@ -4,7 +4,7 @@ import { normalizeStringEntries, uniqueStrings, } from "../../packages/normalization-core/src/string-normalization.js"; -import { isAllowedParsedChatSender as isAllowedParsedChatSenderShared } from "../channels/plugins/chat-target-prefixes.js"; +export { isAllowedParsedChatSender } from "../channels/plugins/chat-target-prefixes.js"; export type { AllowlistMatch, @@ -126,34 +126,6 @@ export function isNormalizedSenderAllowed(params: { return sender ? normalizedAllow.includes(sender) : false; } -type ParsedChatAllowTarget = - | { kind: "chat_id"; chatId: number } - | { kind: "chat_guid"; chatGuid: string } - | { kind: "chat_identifier"; chatIdentifier: string } - | { kind: "handle"; handle: string }; - -/** Match allowlist entries against senders, with conversation targets requiring explicit opt-in. */ -export function isAllowedParsedChatSender(params: { - /** Raw allowlist entries, including handles, wildcard, or parsed chat targets. */ - allowFrom: Array; - /** Sender handle/id from the inbound message. */ - sender: string; - /** Optional numeric conversation id for channel-specific chat target entries. */ - chatId?: number | null; - /** Optional stable conversation guid for channel-specific chat target entries. */ - chatGuid?: string | null; - /** Optional human/channel conversation identifier for chat target entries. */ - chatIdentifier?: string | null; - /** Enables matching conversation targets in addition to sender handles. */ - allowConversationTargets?: boolean | null; - /** Channel-specific sender normalization hook. */ - normalizeSender: (sender: string) => string; - /** Channel-specific allowlist parser for handles and conversation targets. */ - parseAllowTarget: (entry: string) => ParsedChatAllowTarget; -}): boolean { - return isAllowedParsedChatSenderShared(params); -} - /** Serializable allowlist resolution record used by setup/status UI surfaces. */ export type BasicAllowlistResolutionEntry = { /** Original allowlist input. */ diff --git a/src/plugin-sdk/channel-core.ts b/src/plugin-sdk/channel-core.ts index 53a95805d3fa..beb4c9f883d3 100644 --- a/src/plugin-sdk/channel-core.ts +++ b/src/plugin-sdk/channel-core.ts @@ -11,11 +11,7 @@ export type { ChannelOutboundSessionRouteParams, } from "./core.js"; -import { createChannelPluginBase as createChannelPluginBaseFromCore } from "./core.js"; - -/** Creates a channel plugin base while keeping the public import on this SDK subpath. */ -export const createChannelPluginBase: typeof createChannelPluginBaseFromCore = (params) => - createChannelPluginBaseFromCore(params); +export { createChannelPluginBase } from "./core.js"; export { buildChannelConfigSchema, diff --git a/src/plugin-sdk/extension-shared.ts b/src/plugin-sdk/extension-shared.ts index 382c7466c528..81d08aeaeea3 100644 --- a/src/plugin-sdk/extension-shared.ts +++ b/src/plugin-sdk/extension-shared.ts @@ -5,7 +5,7 @@ import type { z } from "zod"; import type { OpenClawConfig } from "../config/config.js"; import { resolveActiveManagedProxyTlsOptions } from "../infra/net/proxy/managed-proxy-undici.js"; import { resolveDefaultSecretProviderAlias } from "../secrets/ref-contract.js"; -import { createDeferred as createSharedDeferred } from "../shared/deferred.js"; +import { createDeferredCore } from "../shared/deferred.js"; import { runPassiveAccountLifecycle } from "./channel-lifecycle.core.js"; import { createLoggerBackedRuntime } from "./runtime-logger.internal.js"; export { safeParseJsonWithSchema, safeParseWithSchema } from "../utils/zod-parse.js"; @@ -151,7 +151,7 @@ export function coerceStatusIssueAccountId(value: unknown): string | undefined { /** Creates a promise with externally controlled resolve/reject hooks for async handoff code. */ export function createDeferred() { - return createSharedDeferred(); + return createDeferredCore(); } const DEFAULT_PACKAGE_JSON_VERSION_CANDIDATES = [ diff --git a/src/plugin-sdk/memory-host-core.ts b/src/plugin-sdk/memory-host-core.ts index a2ed888f803f..e93377470654 100644 --- a/src/plugin-sdk/memory-host-core.ts +++ b/src/plugin-sdk/memory-host-core.ts @@ -7,6 +7,7 @@ import type { OpenClawConfig } from "../config/config.js"; import { resolveStateDir } from "../config/paths.js"; import { sha256Hex, sha256HexPrefix } 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"; import { sameFileIdentity, type FileIdentityStat } from "../infra/fs-safe-advanced.js"; import { FsSafeError, root as createFsSafeRoot } from "../infra/fs-safe.js"; @@ -20,7 +21,6 @@ import { KeyedAsyncQueue } from "./keyed-async-queue.js"; import { resolveMemoryDreamingWorkspaces } from "./memory-core-host-status.js"; import { isMemoryHostEventArtifactAtIdentity, - isMissingPathError, isRejectedWorkspaceArtifactPath, memoryHostEventExportOwnerContent, publishMemoryHostEventArtifact, diff --git a/src/plugin-sdk/memory-host-event-export.ts b/src/plugin-sdk/memory-host-event-export.ts index 142f4cc23900..6961c67c6ed7 100644 --- a/src/plugin-sdk/memory-host-event-export.ts +++ b/src/plugin-sdk/memory-host-event-export.ts @@ -1,7 +1,7 @@ import type { FileHandle } from "node:fs/promises"; import path from "node:path"; import { syncDirectoryIfSupported } from "../infra/directory-durability.js"; -import { isMissingPathError as isCanonicalMissingPathError } from "../infra/errors.js"; +import { isMissingPathError } from "../infra/errors.js"; import { sameFileIdentity, type FileIdentityStat } from "../infra/fs-safe-advanced.js"; import { FsSafeError, root as createFsSafeRoot } from "../infra/fs-safe.js"; @@ -16,10 +16,6 @@ export type MemoryHostEventExportOwner = { type MemoryHostWorkspaceRoot = Awaited>; -export function isMissingPathError(error: unknown): boolean { - return isCanonicalMissingPathError(error); -} - export function isRejectedWorkspaceArtifactPath(error: unknown): boolean { if (!(error instanceof FsSafeError)) { return false; diff --git a/src/plugin-sdk/web-media.ts b/src/plugin-sdk/web-media.ts index e53209d702de..8645d230d421 100644 --- a/src/plugin-sdk/web-media.ts +++ b/src/plugin-sdk/web-media.ts @@ -2,7 +2,7 @@ * Public SDK subpath for loading and optimizing local or remote web media. */ export { - getDefaultLocalRoots, + getDefaultLocalRootsCore as getDefaultLocalRoots, LocalMediaAccessError, loadWebMedia, loadWebMediaRaw, diff --git a/src/plugins/bundled-channel-config-metadata.ts b/src/plugins/bundled-channel-config-metadata.ts index 91b6d6be661c..13e1fc9ec546 100644 --- a/src/plugins/bundled-channel-config-metadata.ts +++ b/src/plugins/bundled-channel-config-metadata.ts @@ -144,7 +144,7 @@ function resolvePackageChannelMeta( return channelMeta?.id?.trim() === channelId ? channelMeta : undefined; } -export function collectBundledChannelConfigs(params: { +export function collectBundledChannelConfigsCore(params: { pluginDir: string; manifest: PluginManifest; packageManifest?: OpenClawPackageManifest; diff --git a/src/plugins/bundled-plugin-metadata.test.ts b/src/plugins/bundled-plugin-metadata.test.ts index 0bc083ed8c23..674fb991da6b 100644 --- a/src/plugins/bundled-plugin-metadata.test.ts +++ b/src/plugins/bundled-plugin-metadata.test.ts @@ -5,7 +5,7 @@ import path from "node:path"; import { beforeAll, describe, expect, it } from "vitest"; import { expectNoReaddirSyncDuring } from "../test-utils/fs-scan-assertions.js"; import { listGitTrackedFiles, toRepoRelativePath } from "../test-utils/repo-files.js"; -import { collectBundledChannelConfigs } from "./bundled-channel-config-metadata.js"; +import { collectBundledChannelConfigsCore } from "./bundled-channel-config-metadata.js"; import { listBundledPluginMetadata, resolveBundledPluginGeneratedPath, @@ -143,7 +143,7 @@ let repoBundledPluginManifestsCache: | undefined; const repoBundledChannelConfigsCache = new Map< string, - ReturnType + ReturnType >(); function listRepoBundledPluginMetadata(): readonly BundledPluginMetadata[] { @@ -292,7 +292,7 @@ function collectRepoBundledChannelConfigsForTest(dirName: string) { if (!manifest.ok) { throw toLintErrorObject(manifest.error, "Non-Error thrown"); } - const configs = collectBundledChannelConfigs({ + const configs = collectBundledChannelConfigsCore({ pluginDir, manifest: manifest.manifest, packageManifest: getPackageManifestMetadata(readPackageManifest(pluginDir)), diff --git a/src/plugins/bundled-plugin-metadata.ts b/src/plugins/bundled-plugin-metadata.ts index 90011528aced..ca912756c73a 100644 --- a/src/plugins/bundled-plugin-metadata.ts +++ b/src/plugins/bundled-plugin-metadata.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import { uniqueStrings } from "@openclaw/normalization-core/string-normalization"; import { tryReadJsonSync } from "../infra/json-files.js"; -import { collectBundledChannelConfigs } from "./bundled-channel-config-metadata.js"; +import { collectBundledChannelConfigsCore } from "./bundled-channel-config-metadata.js"; import { collectBundledPluginPublicSurfaceArtifacts, collectBundledPluginRuntimeSidecarArtifacts, @@ -121,7 +121,7 @@ function collectBundledPluginMetadata( collectBundledPluginRuntimeSidecarArtifacts(publicSurfaceArtifacts); const channelConfigs = includeChannelConfigs && includeSyntheticChannelConfigs - ? collectBundledChannelConfigs({ + ? collectBundledChannelConfigsCore({ pluginDir, manifest: manifestResult.manifest, packageManifest, diff --git a/src/plugins/config-normalization-shared.ts b/src/plugins/config-normalization-shared.ts index 3ef75e4d8bef..b452901da442 100644 --- a/src/plugins/config-normalization-shared.ts +++ b/src/plugins/config-normalization-shared.ts @@ -255,31 +255,6 @@ export function normalizePluginsConfigWithResolver( }; } -export function hasExplicitPluginConfig(plugins?: OpenClawConfig["plugins"]): boolean { - if (!plugins) { - return false; - } - if (typeof plugins.enabled === "boolean") { - return true; - } - if (Array.isArray(plugins.allow) && plugins.allow.length > 0) { - return true; - } - if (Array.isArray(plugins.deny) && plugins.deny.length > 0) { - return true; - } - if (plugins.load?.paths && Array.isArray(plugins.load.paths) && plugins.load.paths.length > 0) { - return true; - } - if (plugins.slots && Object.keys(plugins.slots).length > 0) { - return true; - } - if (plugins.entries && Object.keys(plugins.entries).length > 0) { - return true; - } - return false; -} - export function isBundledChannelEnabledByChannelConfig( cfg: OpenClawConfig | undefined, pluginId: string, diff --git a/src/plugins/config-state.ts b/src/plugins/config-state.ts index efbf9afca791..7debf5fc87a4 100644 --- a/src/plugins/config-state.ts +++ b/src/plugins/config-state.ts @@ -16,7 +16,6 @@ import { type PluginActivationStateLike, } from "./config-activation-shared.js"; import { - hasExplicitPluginConfig as hasExplicitPluginConfigShared, isBundledChannelEnabledByChannelConfig as isBundledChannelEnabledByChannelConfigShared, normalizePluginsConfigWithResolver, type NormalizePluginId, @@ -156,8 +155,30 @@ const hasExplicitMemorySlot = (plugins?: OpenClawConfig["plugins"]) => const hasExplicitMemoryEntry = (plugins?: OpenClawConfig["plugins"]) => Boolean(plugins?.entries && Object.hasOwn(plugins.entries, defaultSlotIdForKey("memory"))); -export const hasExplicitPluginConfig = (plugins?: OpenClawConfig["plugins"]) => - hasExplicitPluginConfigShared(plugins); +export function hasExplicitPluginConfig(plugins?: OpenClawConfig["plugins"]): boolean { + if (!plugins) { + return false; + } + if (typeof plugins.enabled === "boolean") { + return true; + } + if (Array.isArray(plugins.allow) && plugins.allow.length > 0) { + return true; + } + if (Array.isArray(plugins.deny) && plugins.deny.length > 0) { + return true; + } + if (plugins.load?.paths && Array.isArray(plugins.load.paths) && plugins.load.paths.length > 0) { + return true; + } + if (plugins.slots && Object.keys(plugins.slots).length > 0) { + return true; + } + if (plugins.entries && Object.keys(plugins.entries).length > 0) { + return true; + } + return false; +} export function applyTestPluginDefaults( cfg: OpenClawConfig, @@ -230,7 +251,7 @@ export function resolvePluginActivationState(params: { plugins: params.config, }), allowBundledChannelExplicitBypassesAllowlist: true, - isBundledChannelEnabledByChannelConfig, + isBundledChannelEnabledByChannelConfig: isBundledChannelEnabledByChannelConfigShared, }), ); } @@ -240,8 +261,6 @@ export const resolveEnableState = createPluginEnableStateResolver< PluginOrigin >(resolvePluginActivationState); -export const isBundledChannelEnabledByChannelConfig = isBundledChannelEnabledByChannelConfigShared; - type EffectiveActivationParams = { id: string; origin: PluginOrigin; diff --git a/src/plugins/manifest-model-id-normalization.ts b/src/plugins/manifest-model-id-normalization.ts index 8af217f7e265..c0a057553e9d 100644 --- a/src/plugins/manifest-model-id-normalization.ts +++ b/src/plugins/manifest-model-id-normalization.ts @@ -13,7 +13,7 @@ import { getCurrentPluginMetadataSnapshotRuntime, resolvePluginMetadataSnapshotRuntime, } from "./plugin-metadata-snapshot.runtime.js"; -import { getActivePluginRegistryWorkspaceDirFromState } from "./runtime-workspace-state.js"; +import { getActivePluginRegistryWorkspaceDirFromStateCore } from "./runtime-workspace-state.js"; type ManifestModelIdNormalizationLookupParams = { config?: OpenClawConfig; @@ -37,7 +37,7 @@ function resolveMetadataSnapshotForPolicies( cacheable: boolean; } { const env = params.env ?? process.env; - const workspaceDir = params.workspaceDir ?? getActivePluginRegistryWorkspaceDirFromState(); + const workspaceDir = params.workspaceDir ?? getActivePluginRegistryWorkspaceDirFromStateCore(); if (params.config === undefined) { const currentSnapshot = getCurrentPluginMetadataSnapshotRuntime({ env, diff --git a/src/plugins/manifest-registry.test.ts b/src/plugins/manifest-registry.test.ts index 24613b7bd378..04de2691d9a9 100644 --- a/src/plugins/manifest-registry.test.ts +++ b/src/plugins/manifest-registry.test.ts @@ -2,9 +2,9 @@ import fs from "node:fs"; import path from "node:path"; import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; -import { collectChannelSchemaMetadata } from "../config/channel-config-metadata.js"; +import { collectChannelSchemaMetadataCore } from "../config/channel-config-metadata.js"; import type { PluginInstallRecord } from "../config/types.plugins.js"; -import { collectBundledChannelConfigs } from "./bundled-channel-config-metadata.js"; +import { collectBundledChannelConfigsCore } from "./bundled-channel-config-metadata.js"; import type { PluginCandidate } from "./discovery.js"; import { loadPluginManifestRegistry } from "./manifest-registry.js"; import type { OpenClawPackageManifest } from "./manifest.js"; @@ -1664,7 +1664,7 @@ describe("loadPluginManifestRegistry", () => { }); const registry = loadPluginManifestRegistry({ - bundledChannelConfigCollector: collectBundledChannelConfigs, + bundledChannelConfigCollector: collectBundledChannelConfigsCore, candidates: [candidate], }); @@ -1683,7 +1683,7 @@ describe("loadPluginManifestRegistry", () => { manifestOnly: { help: "manifest hint" }, }, }); - expect(collectChannelSchemaMetadata(registry)).toEqual([ + expect(collectChannelSchemaMetadataCore(registry)).toEqual([ { id: "alpha", label: "Alpha", diff --git a/src/plugins/runtime-state.ts b/src/plugins/runtime-state.ts index be9e89303285..eff32827b5c8 100644 --- a/src/plugins/runtime-state.ts +++ b/src/plugins/runtime-state.ts @@ -1,6 +1,6 @@ import { PLUGIN_REGISTRY_STATE } from "./runtime-state-key.js"; // Stores plugin runtime registry state for the current process lifecycle. -import { getActivePluginRegistryWorkspaceDirFromState as getWorkspaceDirFromState } from "./runtime-workspace-state.js"; +import { getActivePluginRegistryWorkspaceDirFromStateCore } from "./runtime-workspace-state.js"; export { PLUGIN_REGISTRY_STATE }; @@ -27,5 +27,5 @@ export function getPluginRegistryState(): RegistryState | undefined { return (globalThis as GlobalRegistryState)[PLUGIN_REGISTRY_STATE]; } export function getActivePluginRegistryWorkspaceDirFromState(): string | undefined { - return getWorkspaceDirFromState(); + return getActivePluginRegistryWorkspaceDirFromStateCore(); } diff --git a/src/plugins/runtime-workspace-state.test.ts b/src/plugins/runtime-workspace-state.test.ts index 0e7a6b90f510..714970852c43 100644 --- a/src/plugins/runtime-workspace-state.test.ts +++ b/src/plugins/runtime-workspace-state.test.ts @@ -83,7 +83,7 @@ describe("runtime workspace state pin", () => { new URL("./runtime-workspace-state.ts?workspace-pin-reload", import.meta.url).href ); - expect(reloaded.getActivePluginRegistryWorkspaceDirFromState()).toBe("/workspace/a"); + expect(reloaded.getActivePluginRegistryWorkspaceDirFromStateCore()).toBe("/workspace/a"); }); }); diff --git a/src/plugins/runtime-workspace-state.ts b/src/plugins/runtime-workspace-state.ts index 2d32196d9a97..64362f380d64 100644 --- a/src/plugins/runtime-workspace-state.ts +++ b/src/plugins/runtime-workspace-state.ts @@ -19,7 +19,7 @@ const pinnedWorkspaceDirStorage = resolveGlobalSingleton< /** Reads the active plugin registry workspace directory from global runtime state, * respecting any pinned workspace from the current async context. */ -export function getActivePluginRegistryWorkspaceDirFromState(): string | undefined { +export function getActivePluginRegistryWorkspaceDirFromStateCore(): string | undefined { const pinned = pinnedWorkspaceDirStorage.getStore(); if (pinned) { return pinned.workspaceDir; @@ -31,7 +31,7 @@ export function getActivePluginRegistryWorkspaceDirFromState(): string | undefin /** * Pin the active plugin-registry workspace dir for the duration of `fn`. - * While pinned, calls to `getActivePluginRegistryWorkspaceDirFromState()` return + * While pinned, calls to `getActivePluginRegistryWorkspaceDirFromStateCore()` return * the snapshot taken at pin time, ignoring concurrent mutations from other * agent turns or crons. This prevents per-row memo-busting in operations that * iterate over many rows (e.g. sessions.list). diff --git a/src/plugins/runtime/index.test.ts b/src/plugins/runtime/index.test.ts index 77cd25f46394..3b9eaf771ee6 100644 --- a/src/plugins/runtime/index.test.ts +++ b/src/plugins/runtime/index.test.ts @@ -405,7 +405,7 @@ describe("plugin runtime command execution", () => { // The wrappers should not forward agentDir or store from plugin callers. // We verify this by checking the wrapper functions exist and are not the // raw implementations (they are wrapped, not direct references). - const { getApiKeyForModel: rawGetApiKey } = await import("../../agents/model-auth.js"); + const { getApiKeyForModelCore: rawGetApiKey } = await import("../../agents/model-auth.js"); const runtime = createPluginRuntime(); // Wrappers should NOT be the same reference as the raw functions expect(runtime.modelAuth.getApiKeyForModel).not.toBe(rawGetApiKey); diff --git a/src/plugins/runtime/runtime-model-auth.runtime.test.ts b/src/plugins/runtime/runtime-model-auth.runtime.test.ts index dfa6232bc43e..47bfbe976b40 100644 --- a/src/plugins/runtime/runtime-model-auth.runtime.test.ts +++ b/src/plugins/runtime/runtime-model-auth.runtime.test.ts @@ -8,7 +8,7 @@ const hoisted = vi.hoisted(() => ({ })); vi.mock("../../agents/model-auth.js", () => ({ - getApiKeyForModel: hoisted.getApiKeyForModel, + getApiKeyForModelCore: hoisted.getApiKeyForModel, resolveApiKeyForProvider: hoisted.resolveApiKeyForProvider, })); diff --git a/src/plugins/runtime/runtime-model-auth.runtime.ts b/src/plugins/runtime/runtime-model-auth.runtime.ts index 310b5ee27726..b7c8217b89d6 100644 --- a/src/plugins/runtime/runtime-model-auth.runtime.ts +++ b/src/plugins/runtime/runtime-model-auth.runtime.ts @@ -1,6 +1,6 @@ // Runtime model auth helpers expose provider auth resolution to plugin runtimes. import { - getApiKeyForModel as resolveModelApiKey, + getApiKeyForModelCore, resolveApiKeyForProvider as resolveProviderApiKey, } from "../../agents/model-auth.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; @@ -9,9 +9,9 @@ import { prepareProviderRuntimeAuth } from "../provider-runtime.runtime.js"; import type { ResolvedProviderRuntimeAuth } from "./model-auth-types.js"; export async function getApiKeyForModel( - params: Parameters[0], -): Promise>> { - return resolveModelApiKey(params); + params: Parameters[0], +): Promise>> { + return getApiKeyForModelCore(params); } export async function resolveApiKeyForProvider( @@ -29,7 +29,7 @@ export async function getRuntimeAuthForModelCore(params: { cfg?: OpenClawConfig; workspaceDir?: string; }): Promise { - const resolvedAuth = await resolveModelApiKey({ + const resolvedAuth = await getApiKeyForModelCore({ model: params.model, cfg: params.cfg, workspaceDir: params.workspaceDir, diff --git a/src/plugins/runtime/runtime-web-channel-plugin.ts b/src/plugins/runtime/runtime-web-channel-plugin.ts index c707ad367f84..10562739cecd 100644 --- a/src/plugins/runtime/runtime-web-channel-plugin.ts +++ b/src/plugins/runtime/runtime-web-channel-plugin.ts @@ -1,6 +1,6 @@ // Runtime web-channel plugin helpers expose web-channel tools through activated plugin runtimes. import { - getDefaultLocalRoots as getDefaultLocalRootsImpl, + getDefaultLocalRootsCore, loadWebMedia as loadWebMediaImpl, loadWebMediaRaw as loadWebMediaRawImpl, optimizeImageToJpeg as optimizeImageToJpegImpl, @@ -291,7 +291,7 @@ export const extractText = (...args: Parameters -): ReturnType { - return getDefaultLocalRootsImpl(...args); + ...args: Parameters +): ReturnType { + return getDefaultLocalRootsCore(...args); } diff --git a/src/secrets/apply.ts b/src/secrets/apply.ts index 7482f12054c8..71f126fe18f3 100644 --- a/src/secrets/apply.ts +++ b/src/secrets/apply.ts @@ -30,6 +30,7 @@ import { normalizePluginConfigId } from "../plugins/plugin-config-trust.js"; import { normalizeAgentId } from "../routing/session-key.js"; import { resolveUserPath } from "../utils.js"; import { iterateAuthProfileCredentials } from "./auth-profiles-scan.js"; +import { listAuthProfileStoreAgentDirs } from "./auth-store-paths.js"; import { createSecretsConfigIO } from "./config-io.js"; import { getSkippedExecRefStaticError } from "./exec-resolution-policy.js"; import { deletePathStrict, getPath, setPathCreateStrict } from "./path-utils.js"; @@ -44,11 +45,7 @@ import { resolveSecretRefValue } from "./resolve.js"; import { prepareSecretsRuntimeSnapshot } from "./runtime.js"; import { assertExpectedResolvedSecretValue } from "./secret-value.js"; import { isNonEmptyString, isRecord, writeTextFileAtomic } from "./shared.js"; -import { - listAuthProfileStoreAgentDirs, - listSecretsDotEnvPaths, - parseEnvAssignmentValue, -} from "./storage-scan.js"; +import { listSecretsDotEnvPaths, parseEnvAssignmentValue } from "./storage-scan.js"; type FileSnapshot = { existed: boolean; diff --git a/src/secrets/audit.ts b/src/secrets/audit.ts index 1c1d522e8b12..698b84da1679 100644 --- a/src/secrets/audit.ts +++ b/src/secrets/audit.ts @@ -23,6 +23,7 @@ import { runTasksWithConcurrency } from "../utils/run-with-concurrency.js"; import { findSecretStorePlaintextResidueFindings } from "./audit-store.js"; import type { PlaintextAssignment } from "./audit-store.js"; import { iterateAuthProfileCredentials } from "./auth-profiles-scan.js"; +import { listAuthProfileStoreAgentDirs } from "./auth-store-paths.js"; import { createSecretsConfigIO } from "./config-io.js"; import { getSkippedExecRefStaticError, selectRefsForExecPolicy } from "./exec-resolution-policy.js"; import { isLikelySensitiveModelProviderHeaderName } from "./model-provider-header-policy.js"; @@ -41,7 +42,6 @@ import { import { isNonEmptyString, isRecord } from "./shared.js"; import { listAgentModelsJsonPaths, - listAuthProfileStoreAgentDirs, listSecretsDotEnvPaths, parseEnvAssignmentValue, readJsonObjectIfExists, diff --git a/src/secrets/runtime-command-secrets.ts b/src/secrets/runtime-command-secrets.ts index 06c53374c5e1..0e3d489d7cd5 100644 --- a/src/secrets/runtime-command-secrets.ts +++ b/src/secrets/runtime-command-secrets.ts @@ -11,7 +11,10 @@ import { import { setPathExistingStrict } from "./path-utils.js"; import { resolveSecretRefValue } from "./resolve.js"; import { createResolverContext } from "./runtime-shared.js"; -import { getActiveSecretsRuntimeEnv, getActiveSecretsRuntimeSnapshot } from "./runtime-state.js"; +import { + getActiveSecretsRuntimeEnvState, + getActiveSecretsRuntimeSnapshotState, +} from "./runtime-state.js"; import { resolveRuntimeWebTools } from "./runtime-web-tools.js"; import { assertExpectedResolvedSecretValue } from "./secret-value.js"; import { discoverConfigSecretTargetsByIds } from "./target-registry.js"; @@ -204,7 +207,7 @@ async function resolveForcedActiveCommandSecretTargets(params: { } const context = createResolverContext({ sourceConfig: params.sourceConfig, - env: getActiveSecretsRuntimeEnv(), + env: getActiveSecretsRuntimeEnvState(), }); const defaults = params.sourceConfig.secrets?.defaults; for (const target of discoverConfigSecretTargetsByIds(params.sourceConfig, params.targetIds)) { @@ -265,7 +268,7 @@ export function resolveCommandSecretsFromActiveRuntimeSnapshot(params: { diagnostics: string[]; inactiveRefPaths: string[]; }> { - const activeSnapshot = getActiveSecretsRuntimeSnapshot(); + const activeSnapshot = getActiveSecretsRuntimeSnapshotState(); if (!activeSnapshot) { throw new Error("Secrets runtime snapshot is not active."); } @@ -284,7 +287,7 @@ export function resolveCommandSecretsFromActiveRuntimeSnapshot(params: { } async function resolveCommandSecretsFromSnapshot(params: { - activeSnapshot: NonNullable>; + activeSnapshot: NonNullable>; commandName: string; targetIds: ReadonlySet; allowedPaths?: ReadonlySet; @@ -308,7 +311,7 @@ async function resolveCommandSecretsFromSnapshot(params: { const context = hasOverrides ? createResolverContext({ sourceConfig, - env: getActiveSecretsRuntimeEnv(), + env: getActiveSecretsRuntimeEnvState(), }) : undefined; if (context) { diff --git a/src/secrets/runtime-owner-assignments.ts b/src/secrets/runtime-owner-assignments.ts index 148d9406d8e6..aada6b9ec678 100644 --- a/src/secrets/runtime-owner-assignments.ts +++ b/src/secrets/runtime-owner-assignments.ts @@ -31,7 +31,7 @@ import { type SecretAssignment, } from "./runtime-shared.js"; import { - getActiveSecretsRuntimeSnapshot, + getActiveSecretsRuntimeSnapshotState, hasSameSecretProviderDefinition, } from "./runtime-state.js"; @@ -49,7 +49,7 @@ export function classifySecretOwnerDegradationState(params: { if (params.refs.some((ref) => params.forceColdRefKeys?.has(secretRefKey(ref)))) { return "cold"; } - const active = getActiveSecretsRuntimeSnapshot(); + const active = getActiveSecretsRuntimeSnapshotState(); if ( !active || active.degradedOwners?.some( @@ -277,7 +277,7 @@ function associateAssignmentFailureOwners(params: { owners.map((owner) => `${owner.source}\0${owner.ownerKind}\0${owner.ownerId}`), ); const collectedOwnerKeys = new Set(params.assignments.map(assignmentOwnerKey)); - const activeSnapshot = getActiveSecretsRuntimeSnapshot(); + const activeSnapshot = getActiveSecretsRuntimeSnapshotState(); const activeAuthOwnerIds = new Set( (activeSnapshot?.authStores ?? []).flatMap(({ agentDir, store }) => Object.keys(store.profiles).map((profileId) => @@ -542,7 +542,7 @@ export async function resolveAndApplySecretAssignments(params: { }); const activeOwner = degradationState === "stale" - ? getActiveSecretsRuntimeSnapshot()?.secretOwners?.find( + ? getActiveSecretsRuntimeSnapshotState()?.secretOwners?.find( (entry) => entry.ownerKind === owner.ownerKind && entry.ownerId === owner.ownerId, ) : undefined; diff --git a/src/secrets/runtime-provider-failure-fanout.test.ts b/src/secrets/runtime-provider-failure-fanout.test.ts index 91739839c4a2..81e33aac580b 100644 --- a/src/secrets/runtime-provider-failure-fanout.test.ts +++ b/src/secrets/runtime-provider-failure-fanout.test.ts @@ -3,7 +3,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 { clearSecretsRuntimeSnapshot } from "./runtime-state.js"; +import { clearSecretsRuntimeSnapshotState } from "./runtime-state.js"; import { asConfig, setupSecretsRuntimeSnapshotTestHooks } from "./runtime.test-support.js"; const EMPTY_LOADABLE_PLUGIN_ORIGINS = new Map(); @@ -22,7 +22,7 @@ function execFixtureProvider(command: string) { } afterEach(() => { - clearSecretsRuntimeSnapshot(); + clearSecretsRuntimeSnapshotState(); }); describe("provider-scoped SecretRef failure fan-out", () => { diff --git a/src/secrets/runtime-state.test.ts b/src/secrets/runtime-state.test.ts index 9fa2b996bdac..7eb50bf18800 100644 --- a/src/secrets/runtime-state.test.ts +++ b/src/secrets/runtime-state.test.ts @@ -6,7 +6,7 @@ import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; import { clearRuntimeAuthProfileStoreSnapshots, getRuntimeAuthProfileStoreCredentialsRevision, - getRuntimeAuthProfileStoreSnapshot, + getRuntimeAuthProfileStoreSnapshotCore, noteRuntimeAuthProfileStorePersistedMutation, setRuntimeAuthProfileStoreSnapshot, } from "../agents/auth-profiles/runtime-snapshots.js"; @@ -28,11 +28,11 @@ import { captureEnv } from "../test-utils/env.js"; import { activateSecretsRuntimeSnapshotState, activateSecretsRuntimeSnapshotStateIfCurrent, - clearSecretsRuntimeSnapshot, + clearSecretsRuntimeSnapshotState, collectSecretStoreRefKeysInConfig, getActiveSecretsRuntimeConfigSnapshot, - getActiveSecretsRuntimeSnapshot, - getActiveSecretsRuntimeSnapshotRevision, + getActiveSecretsRuntimeSnapshotState, + getActiveSecretsRuntimeSnapshotRevisionState, hasSameSecretReloadContract, restoreSecretsRuntimeSourceSnapshotIfLineageCurrent, restoreSecretsRuntimeSnapshotStateIfCurrent, @@ -129,7 +129,7 @@ function activateSnapshotIfCurrent( ): boolean { return activateSecretsRuntimeSnapshotStateIfCurrent({ snapshot, - expectedRevision: options.expectedRevision ?? getActiveSecretsRuntimeSnapshotRevision(), + expectedRevision: options.expectedRevision ?? getActiveSecretsRuntimeSnapshotRevisionState(), refreshContext: null, refreshHandler: null, ...options, @@ -149,7 +149,7 @@ function restoreSnapshotIfCurrent( return restoreSecretsRuntimeSnapshotStateIfCurrent({ snapshot, ownedSnapshot, - expectedRevision: options.expectedRevision ?? getActiveSecretsRuntimeSnapshotRevision(), + expectedRevision: options.expectedRevision ?? getActiveSecretsRuntimeSnapshotRevisionState(), refreshContext: null, refreshHandler: null, ...options, @@ -165,7 +165,7 @@ describe("secrets runtime state", () => { }); afterEach(() => { - clearSecretsRuntimeSnapshot(); + clearSecretsRuntimeSnapshotState(); runtimeSnapshotsTesting.resetPersistedMutationLineage(); envSnapshot.restore(); }); @@ -207,7 +207,7 @@ describe("secrets runtime state", () => { activateSnapshot(snapshot); const configSnapshot = getActiveSecretsRuntimeConfigSnapshot(); - const fullSnapshot = getActiveSecretsRuntimeSnapshot(); + const fullSnapshot = getActiveSecretsRuntimeSnapshotState(); expect(configSnapshot?.config).not.toBe(fullSnapshot?.config); expect(configSnapshot?.sourceConfig).not.toBe(fullSnapshot?.sourceConfig); @@ -243,8 +243,8 @@ describe("secrets runtime state", () => { ).toBe(true); expect(getRuntimeConfigSourceSnapshot()).toEqual(rawSourceConfig); - expect(getActiveSecretsRuntimeSnapshot()?.sourceConfig).toEqual(secretsSourceConfig); - expect(getActiveSecretsRuntimeSnapshot()?.config).toEqual(snapshot.config); + expect(getActiveSecretsRuntimeSnapshotState()?.sourceConfig).toEqual(secretsSourceConfig); + expect(getActiveSecretsRuntimeSnapshotState()?.config).toEqual(snapshot.config); }); it("rejects a source-only secrets write after runtime config ownership changes", () => { @@ -265,14 +265,14 @@ describe("secrets runtime state", () => { expect( setSecretsRuntimeSourceSnapshotIfCurrent({ - expectedSecretsRevision: getActiveSecretsRuntimeSnapshotRevision(), + expectedSecretsRevision: getActiveSecretsRuntimeSnapshotRevisionState(), expectedRuntimeConfigRevision: staleMetadata.revision, runtimeSourceConfig: initialConfig, secretsSourceConfig: initialConfig, }), ).toBe(false); expect(getRuntimeConfigSourceSnapshot()).toEqual(concurrentConfig); - expect(getActiveSecretsRuntimeSnapshot()?.sourceConfig).toEqual(initialConfig); + expect(getActiveSecretsRuntimeSnapshotState()?.sourceConfig).toEqual(initialConfig); }); it("restores source-only ownership through a scoped descendant", () => { @@ -299,14 +299,14 @@ describe("secrets runtime state", () => { } expect( setSecretsRuntimeSourceSnapshotIfCurrent({ - expectedSecretsRevision: getActiveSecretsRuntimeSnapshotRevision(), + expectedSecretsRevision: getActiveSecretsRuntimeSnapshotRevisionState(), expectedRuntimeConfigRevision: runtimeMetadata.revision, runtimeSourceConfig: nextSource, secretsSourceConfig: nextSource, }), ).toBe(true); - const committedRevision = getActiveSecretsRuntimeSnapshotRevision(); - const active = getActiveSecretsRuntimeSnapshot()!; + const committedRevision = getActiveSecretsRuntimeSnapshotRevisionState(); + const active = getActiveSecretsRuntimeSnapshotState()!; const descendant = structuredClone(active); descendant.config.models!.providers!.openai!.baseUrl = "https://refreshed.example.invalid/v1"; expect( @@ -325,8 +325,8 @@ describe("secrets runtime state", () => { }), ).toBe(true); expect(getRuntimeConfigSourceSnapshot()).toEqual(initialSource); - expect(getActiveSecretsRuntimeSnapshot()?.sourceConfig).toEqual(initialSource); - expect(getActiveSecretsRuntimeSnapshot()?.config.models?.providers?.openai?.baseUrl).toBe( + expect(getActiveSecretsRuntimeSnapshotState()?.sourceConfig).toEqual(initialSource); + expect(getActiveSecretsRuntimeSnapshotState()?.config.models?.providers?.openai?.baseUrl).toBe( "https://refreshed.example.invalid/v1", ); }); @@ -373,7 +373,7 @@ describe("secrets runtime state", () => { activateSnapshot(snapshot); expect( - getRuntimeAuthProfileStoreSnapshot(agentDir)?.usageStats?.["openai:default"], + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.usageStats?.["openai:default"], ).toMatchObject({ lastUsed: 2, cooldownUntil: expect.any(Number) }); }); @@ -387,8 +387,8 @@ describe("secrets runtime state", () => { }, }); activateSnapshot(snapshot("sk-old", 19_001)); - const previous = getActiveSecretsRuntimeSnapshot(); - const previousRevision = getActiveSecretsRuntimeSnapshotRevision(); + const previous = getActiveSecretsRuntimeSnapshotState(); + const previousRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const candidate = snapshot("sk-old", 19_002); candidate.authStores[0]!.store.profiles["anthropic:candidate"] = { type: "api_key", @@ -397,16 +397,18 @@ describe("secrets runtime state", () => { }; expect(previous).not.toBeNull(); expect(activateSnapshotIfCurrent(candidate, { expectedRevision: previousRevision })).toBe(true); - const candidateRevision = getActiveSecretsRuntimeSnapshotRevision(); + const candidateRevision = getActiveSecretsRuntimeSnapshotRevisionState(); expect( restoreSnapshotIfCurrent(previous!, candidate, { expectedRevision: candidateRevision }), ).toBe(true); - expect(getActiveSecretsRuntimeSnapshot()?.config.gateway?.port).toBe(19_001); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:default"]).toMatchObject({ + expect(getActiveSecretsRuntimeSnapshotState()?.config.gateway?.port).toBe(19_001); + expect( + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:default"], + ).toMatchObject({ key: "sk-old", }); expect( - getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["anthropic:candidate"], + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["anthropic:candidate"], ).toBeUndefined(); }); @@ -436,8 +438,8 @@ describe("secrets runtime state", () => { usageStats: { "provider-b:default": { lastUsed: 1 } }, }; activateSnapshot(snapshot(predecessorProfiles, 19_001, predecessorState)); - const previous = getActiveSecretsRuntimeSnapshot()!; - const previousRevision = getActiveSecretsRuntimeSnapshotRevision(); + const previous = getActiveSecretsRuntimeSnapshotState()!; + const previousRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const activationProfiles = { ...predecessorProfiles, "provider-b:default": profile("provider-b", "b-external"), @@ -469,7 +471,7 @@ describe("secrets runtime state", () => { preparedState, ); expect(activateSnapshotIfCurrent(candidate, { expectedRevision: previousRevision })).toBe(true); - const liveAfterActivation = getRuntimeAuthProfileStoreSnapshot(agentDir)!; + const liveAfterActivation = getRuntimeAuthProfileStoreSnapshotCore(agentDir)!; liveAfterActivation.order = { provider: ["provider-q:login", "provider-b:default"] }; liveAfterActivation.lastGood = { provider: "provider-q:login" }; liveAfterActivation.usageStats = { @@ -478,12 +480,12 @@ describe("secrets runtime state", () => { setRuntimeAuthProfileStoreSnapshot(liveAfterActivation, agentDir); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - const restored = getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles; + const restored = getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles; expect(restored?.["provider-a:default"]).toMatchObject({ key: "a-old" }); expect(restored?.["provider-b:default"]).toMatchObject({ key: "b-external" }); expect(restored?.["provider-q:login"]).toMatchObject({ key: "q-external" }); expect(restored?.["provider-x:candidate"]).toBeUndefined(); - const restoredStore = getRuntimeAuthProfileStoreSnapshot(agentDir); + const restoredStore = getRuntimeAuthProfileStoreSnapshotCore(agentDir); expect(restoredStore?.order?.provider).toEqual(["provider-q:login", "provider-b:default"]); expect(restoredStore?.lastGood?.provider).toBe("provider-q:login"); expect(restoredStore?.usageStats?.["provider-b:default"]).toMatchObject({ @@ -503,8 +505,8 @@ describe("secrets runtime state", () => { }, }); activateSnapshot(snapshot("sk-old", 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; - const previousRevision = getActiveSecretsRuntimeSnapshotRevision(); + const previous = getActiveSecretsRuntimeSnapshotState()!; + const previousRevision = getActiveSecretsRuntimeSnapshotRevisionState(); setRuntimeAuthProfileStoreSnapshot( snapshot("sk-candidate", 19_002).authStores[0]!.store, agentDir, @@ -517,12 +519,14 @@ describe("secrets runtime state", () => { }; expect(activateSnapshotIfCurrent(candidate, { expectedRevision: previousRevision })).toBe(true); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getActiveSecretsRuntimeSnapshot()?.config.gateway?.port).toBe(19_001); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:default"]).toMatchObject({ + expect(getActiveSecretsRuntimeSnapshotState()?.config.gateway?.port).toBe(19_001); + expect( + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:default"], + ).toMatchObject({ key: finalKey, }); expect( - getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["anthropic:candidate"], + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["anthropic:candidate"], ).toBeUndefined(); }); @@ -586,7 +590,7 @@ describe("secrets runtime state", () => { runtimeExternalProfileIds: aExternal ? ["provider-a:default"] : undefined, }); activateSnapshot(snapshot(baselineAKey, "b-old", 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot(candidateAKey, "b-old", 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); setRuntimeAuthProfileStoreSnapshot( @@ -600,7 +604,7 @@ describe("secrets runtime state", () => { }); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - const restored = getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles; + const restored = getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles; if (expectedAKey === null) { expect(restored?.["provider-a:default"]).toBeUndefined(); } else { @@ -608,9 +612,9 @@ describe("secrets runtime state", () => { } expect(restored?.["provider-b:default"]).toMatchObject({ key: "b-external" }); if (currentAExternal) { - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)?.runtimeExternalProfileIds).toContain( - "provider-a:default", - ); + expect( + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.runtimeExternalProfileIds, + ).toContain("provider-a:default"); } }, ); @@ -629,7 +633,7 @@ describe("secrets runtime state", () => { runtimeLocalProfileIds, }); activateSnapshot(snapshot("sk-old", 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot("sk-candidate", 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); noteRuntimeAuthProfileStorePersistedMutation(undefined, { @@ -640,7 +644,9 @@ describe("secrets runtime state", () => { setRuntimeAuthProfileStoreSnapshot(candidate.authStores[0]!.store, agentDir); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:default"]).toMatchObject({ + expect( + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:default"], + ).toMatchObject({ key: expected, }); }); @@ -670,7 +676,7 @@ describe("secrets runtime state", () => { activateSnapshot( snapshot({ "openai:x": profileX, "openai:y": profileY }, ["openai:x", "openai:y"], 19_001), ); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot({ "openai:y": profileY }, ["openai:y"], 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); noteRuntimeAuthProfileStorePersistedMutation(undefined, { @@ -681,7 +687,7 @@ describe("secrets runtime state", () => { setRuntimeAuthProfileStoreSnapshot(candidate.authStores[0]!.store, agentDir); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); }); it.each([ @@ -703,7 +709,7 @@ describe("secrets runtime state", () => { runtimeLocalProfileIds: owner === "local" ? ["openai:x"] : [], }); activateSnapshot(snapshot("sk-external-old", "external", 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot("sk-candidate", candidateOwner, 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); if (mutateCandidateOwner) { @@ -724,9 +730,9 @@ describe("secrets runtime state", () => { expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); if (mutateCandidateOwner) { - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); } else { - const restored = getRuntimeAuthProfileStoreSnapshot(agentDir); + const restored = getRuntimeAuthProfileStoreSnapshotCore(agentDir); expect(restored?.profiles["openai:x"]).toMatchObject({ key: "sk-external-old" }); expect(restored?.runtimeExternalProfileIds).toContain("openai:x"); } @@ -764,7 +770,7 @@ describe("secrets runtime state", () => { 19_001, ), ); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot("sk-external", "external", 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); noteRuntimeAuthProfileStorePersistedMutation( @@ -778,7 +784,7 @@ describe("secrets runtime state", () => { setRuntimeAuthProfileStoreSnapshot(candidate.authStores[0]!.store, agentDir); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); }, ); @@ -812,7 +818,7 @@ describe("secrets runtime state", () => { 19_001, ); activateSnapshot(baseline); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot("sk-external", "external", 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); setRuntimeAuthProfileStoreSnapshot( @@ -821,7 +827,7 @@ describe("secrets runtime state", () => { ); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - const restored = getRuntimeAuthProfileStoreSnapshot(agentDir); + const restored = getRuntimeAuthProfileStoreSnapshotCore(agentDir); if (baselineOwner === "absent") { expect(restored?.profiles["openai:x"]).toBeUndefined(); } else { @@ -848,7 +854,7 @@ describe("secrets runtime state", () => { runtimeLocalProfileIds: owner === "local" ? ["openai:x"] : [], }); activateSnapshot(snapshot("sk-old", candidateOwner, 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot("sk-candidate", candidateOwner, 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); setRuntimeAuthProfileStoreSnapshot( @@ -857,7 +863,7 @@ describe("secrets runtime state", () => { ); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - const restored = getRuntimeAuthProfileStoreSnapshot(agentDir); + const restored = getRuntimeAuthProfileStoreSnapshotCore(agentDir); expect(restored?.profiles["openai:x"]).toMatchObject({ key: "sk-candidate" }); if (currentOwner === "local") { expect(restored?.runtimeLocalProfileIds).toContain("openai:x"); @@ -879,12 +885,12 @@ describe("secrets runtime state", () => { runtimeExternalProfileIdsAuthoritative: authoritative ? true : undefined, }); activateSnapshot(snapshot(true, 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot(false, 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toMatchObject({ + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toMatchObject({ runtimeExternalProfileIds: [], runtimeExternalProfileIdsAuthoritative: true, }); @@ -903,7 +909,7 @@ describe("secrets runtime state", () => { runtimeExternalProfileIdsAuthoritative: authoritative ? true : undefined, }); activateSnapshot(snapshot("sk-old", false, 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot("sk-old", true, 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); setRuntimeAuthProfileStoreSnapshot( @@ -912,7 +918,7 @@ describe("secrets runtime state", () => { ); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - const restored = getRuntimeAuthProfileStoreSnapshot(agentDir); + const restored = getRuntimeAuthProfileStoreSnapshotCore(agentDir); expect(restored?.profiles["openai:x"]).toMatchObject({ key: "sk-current" }); expect(restored?.runtimeExternalProfileIdsAuthoritative).toBeUndefined(); }); @@ -931,7 +937,7 @@ describe("secrets runtime state", () => { runtimeExternalProfileIds: ["openai:external"], }); activateSnapshot(snapshot("sk-old", 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot("sk-candidate", 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); noteRuntimeAuthProfileStorePersistedMutation(undefined, { @@ -942,11 +948,11 @@ describe("secrets runtime state", () => { setRuntimeAuthProfileStoreSnapshot(snapshot(current, 19_002).authStores[0]!.store, agentDir); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:external"]).toMatchObject( - { - key: expected, - }, - ); + expect( + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:external"], + ).toMatchObject({ + key: expected, + }); }); it("removes a rejected candidate credential when its bounded lineage was evicted", () => { @@ -965,7 +971,7 @@ describe("secrets runtime state", () => { runtimeLocalProfileIds: ["anthropic:stable", "openai:default"], }); activateSnapshot(snapshot("sk-old", 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot("sk-candidate", 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); for (let index = 0; index < 300; index += 1) { @@ -977,7 +983,7 @@ describe("secrets runtime state", () => { } expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); }); it.each(["owner", "profile"] as const)( @@ -1006,7 +1012,7 @@ describe("secrets runtime state", () => { agentDir, ); activateSnapshot(snapshot("sk-old", previousRef, 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot("sk-candidate", candidateRef, 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); setRuntimeAuthProfileStoreSnapshot( @@ -1025,12 +1031,12 @@ describe("secrets runtime state", () => { } expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); expect( ensureAuthProfileStoreWithoutExternalProfiles(agentDir).profiles["openai:default"], ).toMatchObject({ keyRef: previousRef }); } finally { - clearSecretsRuntimeSnapshot(); + clearSecretsRuntimeSnapshotState(); closeOpenClawAgentDatabasesForTest(); fs.rmSync(root, { recursive: true, force: true }); } @@ -1137,7 +1143,7 @@ describe("secrets runtime state", () => { : [], }); activateSnapshot(snapshot(true, 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot(false, 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); if (mutationOwner !== "none") { @@ -1153,10 +1159,10 @@ describe("secrets runtime state", () => { expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); if (expectMissing) { - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); } else { expect( - getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:default"], + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:default"], ).toMatchObject({ key: "sk-old" }); } }, @@ -1187,7 +1193,7 @@ describe("secrets runtime state", () => { : [], }); activateSnapshot(snapshot(true, 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot(false, 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); noteRuntimeAuthProfileStorePersistedMutation(undefined, { @@ -1198,7 +1204,7 @@ describe("secrets runtime state", () => { }); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); }); it("does not resurrect an auth store cleared after candidate activation", () => { @@ -1211,13 +1217,13 @@ describe("secrets runtime state", () => { }, }); activateSnapshot(snapshot("sk-old", 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot("sk-candidate", 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); clearRuntimeAuthProfileStoreSnapshots(); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); }); it.each([ @@ -1239,10 +1245,10 @@ describe("secrets runtime state", () => { }, }); activateSnapshot(snapshot("sk-old", previousRef, 19_001)); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot("sk-candidate", candidateRef, 19_002); expect(activateSnapshotIfCurrent(candidate)).toBe(true); - const candidateRevision = getActiveSecretsRuntimeSnapshotRevision(); + const candidateRevision = getActiveSecretsRuntimeSnapshotRevisionState(); expect( activateSnapshotIfCurrent(snapshot("sk-refreshed", candidateRef, 19_002), { expectedRevision: candidateRevision, @@ -1253,7 +1259,9 @@ describe("secrets runtime state", () => { expect( restoreSnapshotIfCurrent(previous, candidate, { expectedRevision: candidateRevision }), ).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:default"]).toMatchObject({ + expect( + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:default"], + ).toMatchObject({ key: changedRef ? "sk-old" : "sk-refreshed", keyRef: changedRef ? previousRef : candidateRef, }); @@ -1278,15 +1286,17 @@ describe("secrets runtime state", () => { }, agentDir, ); - const previous = getActiveSecretsRuntimeSnapshot(); - const previousRevision = getActiveSecretsRuntimeSnapshotRevision(); + const previous = getActiveSecretsRuntimeSnapshotState(); + const previousRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const candidate = snapshot("sk-live", 19_012); expect(previous).not.toBeNull(); expect(activateSnapshotIfCurrent(candidate, { expectedRevision: previousRevision })).toBe(true); expect(restoreSnapshotIfCurrent(previous!, candidate)).toBe(true); - expect(getActiveSecretsRuntimeSnapshot()?.config.gateway?.port).toBe(19_011); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:default"]).toMatchObject({ + expect(getActiveSecretsRuntimeSnapshotState()?.config.gateway?.port).toBe(19_011); + expect( + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:default"], + ).toMatchObject({ key: "sk-live", }); }); @@ -1359,7 +1369,7 @@ describe("secrets runtime state", () => { keyRef: previousKeyInput, }), ); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot({ sourcePort: 19_022, runtimePort: 19_022, @@ -1367,7 +1377,7 @@ describe("secrets runtime state", () => { keyRef: candidateKeyInput, }); expect(activateSnapshotIfCurrent(candidate)).toBe(true); - const candidateRevision = getActiveSecretsRuntimeSnapshotRevision(); + const candidateRevision = getActiveSecretsRuntimeSnapshotRevisionState(); const providerRefresh = snapshot({ sourcePort: 19_022, runtimePort: 19_022, @@ -1384,8 +1394,8 @@ describe("secrets runtime state", () => { expect( restoreSnapshotIfCurrent(previous, candidate, { expectedRevision: candidateRevision }), ).toBe(true); - expect(getActiveSecretsRuntimeSnapshot()?.config.gateway?.port).toBe(19_021); - expect(getActiveSecretsRuntimeSnapshot()?.config.models?.providers?.openai?.apiKey).toBe( + expect(getActiveSecretsRuntimeSnapshotState()?.config.gateway?.port).toBe(19_021); + expect(getActiveSecretsRuntimeSnapshotState()?.config.models?.providers?.openai?.apiKey).toBe( expectedKey, ); }); @@ -1491,7 +1501,7 @@ describe("secrets runtime state", () => { activateSnapshot( snapshot({ sourceConfig: previousSourceConfig, apiKey: "sk-old", port: 19_031 }), ); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot({ sourceConfig: candidateSourceConfig, apiKey: "sk-candidate", @@ -1507,7 +1517,7 @@ describe("secrets runtime state", () => { }); } } - const candidateRevision = getActiveSecretsRuntimeSnapshotRevision(); + const candidateRevision = getActiveSecretsRuntimeSnapshotRevisionState(); expect( activateSnapshotIfCurrent( snapshot({ @@ -1525,14 +1535,14 @@ describe("secrets runtime state", () => { expect( restoreSnapshotIfCurrent(previous, candidate, { expectedRevision: candidateRevision }), ).toBe(true); - const restored = getActiveSecretsRuntimeSnapshot(); + const restored = getActiveSecretsRuntimeSnapshotState(); expect(restored?.sourceConfig).toMatchObject(previousSourceConfig); expect(restored?.config.models?.providers?.openai?.apiKey).toBe("sk-old"); if (evictLineage) { - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); } else { expect( - getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:default"], + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:default"], ).toMatchObject({ key: "sk-old", keyRef, @@ -1594,7 +1604,7 @@ describe("secrets runtime state", () => { port: 19_041, }), ); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot({ key: "sk-candidate", owner: capturedOwner, @@ -1618,7 +1628,7 @@ describe("secrets runtime state", () => { ); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); }, ); @@ -1690,7 +1700,7 @@ describe("secrets runtime state", () => { sourceConfig: previousSourceConfig, }), ); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot({ key: "sk-candidate", keyRef: previousRef, @@ -1715,10 +1725,10 @@ describe("secrets runtime state", () => { expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); if (affectedProvider) { - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); } else { expect( - getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:default"], + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:default"], ).toMatchObject({ key: "sk-durable", keyRef: currentRef }); } }, @@ -1780,7 +1790,7 @@ describe("secrets runtime state", () => { port: 19_061, }), ); - const previous = getActiveSecretsRuntimeSnapshot()!; + const previous = getActiveSecretsRuntimeSnapshotState()!; const candidate = snapshot({ includeProfile: false, providerPath: "/tmp/rejected-secrets.json", @@ -1805,7 +1815,7 @@ describe("secrets runtime state", () => { ); expect(restoreSnapshotIfCurrent(previous, candidate)).toBe(true); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)).toBeUndefined(); + expect(getRuntimeAuthProfileStoreSnapshotCore(agentDir)).toBeUndefined(); }, ); }); diff --git a/src/secrets/runtime-state.ts b/src/secrets/runtime-state.ts index 8cabd06d188d..0830a631c7bc 100644 --- a/src/secrets/runtime-state.ts +++ b/src/secrets/runtime-state.ts @@ -2,7 +2,7 @@ import { isDeepStrictEqual } from "node:util"; import { clearRuntimeAuthProfileStoreSnapshots, - getRuntimeAuthProfileStoreSnapshot, + getRuntimeAuthProfileStoreSnapshotCore, getRuntimeAuthProfileStoreCredentialMutationToken, getRuntimeAuthProfileStoreCredentialsRevision, getRuntimeAuthProfileStoreProfileSetMutationToken, @@ -247,7 +247,7 @@ function mergeLiveAuthStoreBookkeeping( authStores: PreparedSecretsRuntimeSnapshot["authStores"], ): PreparedSecretsRuntimeSnapshot["authStores"] { return authStores.map((entry) => { - const live = getRuntimeAuthProfileStoreSnapshot(entry.agentDir); + const live = getRuntimeAuthProfileStoreSnapshotCore(entry.agentDir); if (!live) { return entry; } @@ -871,7 +871,7 @@ export function graftActiveSecretsRuntimeAuthState(snapshot: PreparedSecretsRunt /** * Returns the env used by the active runtime snapshot, falling back to process env. */ -export function getActiveSecretsRuntimeEnv(): NodeJS.ProcessEnv { +export function getActiveSecretsRuntimeEnvState(): NodeJS.ProcessEnv { return { ...(activeRefreshContext?.env ?? process.env), } as NodeJS.ProcessEnv; @@ -1037,7 +1037,7 @@ export function restoreSecretsRuntimeSnapshotStateIfCurrent( /** * Returns a cloned active secrets runtime snapshot for callers that need mutable data. */ -export function getActiveSecretsRuntimeSnapshot(): PreparedSecretsRuntimeSnapshot | null { +export function getActiveSecretsRuntimeSnapshotState(): PreparedSecretsRuntimeSnapshot | null { if (!activeSnapshot) { return null; } @@ -1054,7 +1054,7 @@ export function getActiveSecretsRuntimeSnapshot(): PreparedSecretsRuntimeSnapsho } /** Stable token for compare-and-activate ownership across cloned snapshot reads. */ -export function getActiveSecretsRuntimeSnapshotRevision(): number { +export function getActiveSecretsRuntimeSnapshotRevisionState(): number { return activeSnapshotRevision; } @@ -1154,7 +1154,7 @@ export function getLiveSecretsRuntimeAuthStores(): PreparedSecretsRuntimeSnapsho return []; } return activeSnapshot.authStores.flatMap((entry) => { - const store = getRuntimeAuthProfileStoreSnapshot(entry.agentDir); + const store = getRuntimeAuthProfileStoreSnapshotCore(entry.agentDir); return store ? [{ agentDir: entry.agentDir, store }] : []; }); } @@ -1162,7 +1162,7 @@ export function getLiveSecretsRuntimeAuthStores(): PreparedSecretsRuntimeSnapsho /** * Clears active secrets runtime state and all linked config/auth/web-tool snapshots. */ -export function clearSecretsRuntimeSnapshot(): void { +export function clearSecretsRuntimeSnapshotState(): void { activeSnapshotRevision += 1; activeSnapshotLineageStartRevision = 0; activeSnapshotLineageAuthStores = []; diff --git a/src/secrets/runtime-web-tools-state.test.ts b/src/secrets/runtime-web-tools-state.test.ts index 277da301c17a..00788373e5e1 100644 --- a/src/secrets/runtime-web-tools-state.test.ts +++ b/src/secrets/runtime-web-tools-state.test.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it } from "vitest"; import { clearActiveRuntimeWebToolsMetadata, - getActiveRuntimeWebToolsMetadata, + getActiveRuntimeWebToolsMetadataFromState, setActiveRuntimeWebToolsMetadata, } from "./runtime-web-tools-state.js"; @@ -27,7 +27,7 @@ describe("runtime web tools state", () => { diagnostics: [], }); - const first = getActiveRuntimeWebToolsMetadata(); + const first = getActiveRuntimeWebToolsMetadataFromState(); if (!first) { throw new Error("missing runtime web tools metadata"); } @@ -37,7 +37,7 @@ describe("runtime web tools state", () => { first.search.providerConfigured = "brave"; first.search.selectedProvider = "brave"; - const second = getActiveRuntimeWebToolsMetadata(); + const second = getActiveRuntimeWebToolsMetadataFromState(); if (!second) { throw new Error("missing cloned runtime web tools metadata"); } diff --git a/src/secrets/runtime-web-tools-state.ts b/src/secrets/runtime-web-tools-state.ts index d80626102cdb..67886140c4f2 100644 --- a/src/secrets/runtime-web-tools-state.ts +++ b/src/secrets/runtime-web-tools-state.ts @@ -20,7 +20,7 @@ export function setActiveRuntimeWebToolsMetadata(metadata: RuntimeWebToolsMetada /** * Returns active web-tool metadata without exposing mutable runtime state. */ -export function getActiveRuntimeWebToolsMetadata(): RuntimeWebToolsMetadata | null { +export function getActiveRuntimeWebToolsMetadataFromState(): RuntimeWebToolsMetadata | null { if (!activeRuntimeWebToolsMetadata) { return null; } diff --git a/src/secrets/runtime-web-tools.test.ts b/src/secrets/runtime-web-tools.test.ts index 349cc9419f08..131b81d995e1 100644 --- a/src/secrets/runtime-web-tools.test.ts +++ b/src/secrets/runtime-web-tools.test.ts @@ -9,7 +9,7 @@ import type { import { listSecretResolutionErrorOwners } from "./runtime-degraded-state.js"; import { activateSecretsRuntimeSnapshotState, - clearSecretsRuntimeSnapshot, + clearSecretsRuntimeSnapshotState, } from "./runtime-state.js"; type ProviderUnderTest = "brave" | "gemini" | "grok" | "kimi" | "perplexity" | "duckduckgo"; @@ -417,7 +417,7 @@ describe("runtime web tools resolution", () => { afterEach(() => { restoreResolveSecretRefValuesSpy?.(); restoreResolveSecretRefValuesSpy = undefined; - clearSecretsRuntimeSnapshot(); + clearSecretsRuntimeSnapshotState(); }); it("keeps web search inactive when only web fetch is configured", async () => { diff --git a/src/secrets/runtime-web-tools.ts b/src/secrets/runtime-web-tools.ts index e75bcbe32001..dc826283d353 100644 --- a/src/secrets/runtime-web-tools.ts +++ b/src/secrets/runtime-web-tools.ts @@ -36,7 +36,7 @@ import { } from "./runtime-owner-assignments.js"; import { hasCredentialBearingObjectValue } from "./runtime-secret-scan.js"; import type { ResolverContext, SecretDefaults } from "./runtime-shared.js"; -import { getActiveSecretsRuntimeSnapshot } from "./runtime-state.js"; +import { getActiveSecretsRuntimeSnapshotState } from "./runtime-state.js"; import { runtimeWebSecretOwnerId } from "./runtime-web-secret-owner.js"; import { hasConfiguredSecretRef, @@ -153,7 +153,7 @@ function collectUnavailableWebProviders(params: { forceColdRefKeys: params.forceColdRefKeys, }); if (degradationState === "stale") { - const active = getActiveSecretsRuntimeSnapshot(); + const active = getActiveSecretsRuntimeSnapshotState(); const activeOwner = active?.secretOwners?.find( (entry) => entry.ownerKind === "capability" && @@ -240,7 +240,7 @@ function associateWebProviderResolutionError(params: { }; }); const ownerIds = new Set(owners.map((owner) => owner.ownerId)); - const activeCoOwners = (getActiveSecretsRuntimeSnapshot()?.secretOwners ?? []).flatMap( + const activeCoOwners = (getActiveSecretsRuntimeSnapshotState()?.secretOwners ?? []).flatMap( (owner) => { if ( owner.ownerKind !== "capability" || diff --git a/src/secrets/runtime.fast-path.test.ts b/src/secrets/runtime.fast-path.test.ts index 8df1d9a7f696..a500e9cdcf5e 100644 --- a/src/secrets/runtime.fast-path.test.ts +++ b/src/secrets/runtime.fast-path.test.ts @@ -278,7 +278,7 @@ describe("secrets runtime fast path", () => { it("refreshes startup-only fast-path snapshots from persisted auth stores after startup", async () => { const { prepareSecretsRuntimeFastPathSnapshot } = await import("./runtime-fast-path.js"); - const { activateSecretsRuntimeSnapshotState, getActiveSecretsRuntimeSnapshot } = + const { activateSecretsRuntimeSnapshotState, getActiveSecretsRuntimeSnapshotState } = await import("./runtime-state.js"); const { refreshActiveProviderAuthRuntimeSnapshot } = await import("./runtime.js"); const root = mkdtempSync(path.join(tmpdir(), "openclaw-runtime-fast-path-refresh-")); @@ -308,7 +308,7 @@ describe("secrets runtime fast path", () => { writeAuthProfileStore(agentDir); await expect(refreshActiveProviderAuthRuntimeSnapshot()).resolves.toBe(true); - const active = getActiveSecretsRuntimeSnapshot(); + const active = getActiveSecretsRuntimeSnapshotState(); expect(active?.authStores[0]?.agentDir).toBe(agentDir); expect(active?.authStores[0]?.store.profiles["openai:default"]).toMatchObject({ type: "api_key", @@ -361,7 +361,7 @@ describe("secrets runtime fast path", () => { }); it("does not let an active refresh overwrite auth stores mutated during preparation", async () => { - const { getRuntimeAuthProfileStoreSnapshot, setRuntimeAuthProfileStoreSnapshot } = + const { getRuntimeAuthProfileStoreSnapshotCore, setRuntimeAuthProfileStoreSnapshot } = await import("../agents/auth-profiles/runtime-snapshots.js"); const { activateSecretsRuntimeSnapshot, @@ -389,7 +389,7 @@ describe("secrets runtime fast path", () => { setRuntimeAuthProfileStoreSnapshot(newStore, agentDir); return oldStore; } - return getRuntimeAuthProfileStoreSnapshot(agentDir) ?? oldStore; + return getRuntimeAuthProfileStoreSnapshotCore(agentDir) ?? oldStore; }; const initial = await prepareSecretsRuntimeSnapshot({ config: asConfig({ @@ -406,7 +406,9 @@ describe("secrets runtime fast path", () => { expect( getActiveSecretsRuntimeSnapshot()?.authStores[0]?.store.profiles["openai:default"], ).toMatchObject({ key: "sk-new" }); - expect(getRuntimeAuthProfileStoreSnapshot(agentDir)?.profiles["openai:default"]).toMatchObject({ + expect( + getRuntimeAuthProfileStoreSnapshotCore(agentDir)?.profiles["openai:default"], + ).toMatchObject({ key: "sk-new", }); }); diff --git a/src/secrets/runtime.test.ts b/src/secrets/runtime.test.ts index 9815ff8a9b41..c9f724722f0c 100644 --- a/src/secrets/runtime.test.ts +++ b/src/secrets/runtime.test.ts @@ -8,7 +8,7 @@ import { resetSecretRedactionRegistryForTest } from "../logging/secret-redaction import { assertSecretOwnerAvailable } from "./runtime-degraded-state.js"; import { activateSecretsRuntimeSnapshotState, - clearSecretsRuntimeSnapshot, + clearSecretsRuntimeSnapshotState, } from "./runtime-state.js"; import { asConfig, setupSecretsRuntimeSnapshotTestHooks } from "./runtime.test-support.ts"; @@ -30,7 +30,7 @@ const CODEX_APP_SERVER_TOKEN_REF = { afterEach(() => { resetSecretRedactionRegistryForTest(); - clearSecretsRuntimeSnapshot(); + clearSecretsRuntimeSnapshotState(); }); const TTS_REF = { diff --git a/src/secrets/runtime.ts b/src/secrets/runtime.ts index 87c1c02f4f28..52f0ad4ca8cc 100644 --- a/src/secrets/runtime.ts +++ b/src/secrets/runtime.ts @@ -44,11 +44,11 @@ import { mergeProviderAuthRuntimeWarnings } from "./runtime-provider-auth-warnin import { activateSecretsRuntimeSnapshotState, activateSecretsRuntimeSnapshotStateIfCurrent, - clearSecretsRuntimeSnapshot as clearSecretsRuntimeSnapshotState, - getActiveSecretsRuntimeEnv as getActiveSecretsRuntimeEnvState, + clearSecretsRuntimeSnapshotState, + getActiveSecretsRuntimeEnvState, getActiveSecretsRuntimeRefreshContext, - getActiveSecretsRuntimeSnapshot as getActiveSecretsRuntimeSnapshotState, - getActiveSecretsRuntimeSnapshotRevision as getActiveSecretsRuntimeSnapshotRevisionState, + getActiveSecretsRuntimeSnapshotState, + getActiveSecretsRuntimeSnapshotRevisionState, getLiveSecretsRuntimeAuthStores, getPreparedSecretsRuntimeSnapshotRefreshContext, registerSecretsRuntimeStateClearHook, @@ -57,7 +57,7 @@ import { type PreparedSecretsRuntimeSnapshot, type SecretsRuntimeRefreshContext, } from "./runtime-state.js"; -import { getActiveRuntimeWebToolsMetadata as getActiveRuntimeWebToolsMetadataFromState } from "./runtime-web-tools-state.js"; +import { getActiveRuntimeWebToolsMetadataFromState } from "./runtime-web-tools-state.js"; import type { RuntimeWebToolsMetadata } from "./runtime-web-tools.types.js"; export type { SecretResolverWarning } from "./runtime-shared.js"; diff --git a/src/secrets/storage-scan.ts b/src/secrets/storage-scan.ts index 068169969040..e18e96987a28 100644 --- a/src/secrets/storage-scan.ts +++ b/src/secrets/storage-scan.ts @@ -6,7 +6,6 @@ import { listAgentIds, resolveAgentDir } from "../agents/agent-scope.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { formatErrorMessage } from "../infra/errors.js"; import { resolveUserPath } from "../utils.js"; -import { listAuthProfileStoreAgentDirs as listAuthProfileStoreAgentDirsFromAuthStorePaths } from "./auth-store-paths.js"; import { parseEnvValue } from "./shared.js"; /** Parses one .env assignment value using the shared shell-ish env parser. */ @@ -14,11 +13,6 @@ export function parseEnvAssignmentValue(raw: string): string { return parseEnvValue(raw); } -/** Lists agent directories that own canonical auth-profile stores. */ -export function listAuthProfileStoreAgentDirs(config: OpenClawConfig, stateDir: string): string[] { - return listAuthProfileStoreAgentDirsFromAuthStorePaths(config, stateDir); -} - /** Lists global dotenv files that can supply secrets for the selected config and state roots. */ export function listSecretsDotEnvPaths(params: { configPath: string; stateDir: string }): string[] { const candidates = [ diff --git a/src/security/audit-channel-account-metadata.test.ts b/src/security/audit-channel-account-metadata.test.ts index a347a5f2564c..32cfc34a00f3 100644 --- a/src/security/audit-channel-account-metadata.test.ts +++ b/src/security/audit-channel-account-metadata.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import type { ChannelPlugin } from "../channels/plugins/types.public.js"; import type { OpenClawConfig } from "../config/config.js"; -import { collectChannelSecurityFindings } from "./audit-channel.js"; +import { collectChannelSecurityFindingsCore } from "./audit-channel.js"; function stubChannelPlugin(): ChannelPlugin { return { @@ -39,7 +39,7 @@ function stubChannelPlugin(): ChannelPlugin { } function requireDangerousMatchingFinding( - findings: Awaited>, + findings: Awaited>, ) { const finding = findings.find( (entry) => entry.checkId === "channels.discord.allowFrom.dangerous_name_matching_enabled", @@ -64,7 +64,7 @@ describe("security audit channel account metadata", () => { }, }; - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg, plugins: [stubChannelPlugin()], }); diff --git a/src/security/audit-channel-dm-policy.test.ts b/src/security/audit-channel-dm-policy.test.ts index d1e9acfec7b7..9e3807b44c07 100644 --- a/src/security/audit-channel-dm-policy.test.ts +++ b/src/security/audit-channel-dm-policy.test.ts @@ -2,9 +2,11 @@ import { describe, expect, it, vi } from "vitest"; import type { ChannelPlugin } from "../channels/plugins/types.public.js"; import type { OpenClawConfig } from "../config/config.js"; -import { collectChannelSecurityFindings } from "./audit-channel.js"; +import { collectChannelSecurityFindingsCore } from "./audit-channel.js"; -type ChannelSecurityFinding = Awaited>[number]; +type ChannelSecurityFinding = Awaited< + ReturnType +>[number]; function requireFinding( findings: ChannelSecurityFinding[], @@ -151,7 +153,7 @@ describe("security audit channel dm policy", () => { expectedCollisions: 0, }, ])("$name", async ({ cfg, expectedCollisions, remediation }) => { - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg, plugins: [createDmPlugin()], }); @@ -164,7 +166,7 @@ describe("security audit channel dm policy", () => { }); it("detects cross-account collisions with one admitted sender per account", async () => { - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: { session: { dmScope: "main" } }, plugins: [ createDmPlugin({ @@ -185,7 +187,7 @@ describe("security audit channel dm policy", () => { }); it("keeps same-named accounts attributed across channels", async () => { - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: { session: { dmScope: "main" } }, plugins: [ createDmPlugin({ accounts: { default: { allowFrom: ["user-a"] } } }), @@ -210,8 +212,8 @@ describe("security audit channel dm policy", () => { }; const plugin = createDmPlugin(); - const linkedFindings = await collectChannelSecurityFindings({ cfg, plugins: [plugin] }); - const distinctFindings = await collectChannelSecurityFindings({ + const linkedFindings = await collectChannelSecurityFindingsCore({ cfg, plugins: [plugin] }); + const distinctFindings = await collectChannelSecurityFindingsCore({ cfg: { session: { dmScope: "main" } }, plugins: [plugin], }); @@ -221,7 +223,7 @@ describe("security audit channel dm policy", () => { }); it("keeps separate collision topologies distinct", async () => { - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: { agents: { list: [{ id: "alpha", default: true }, { id: "beta" }] }, session: { dmScope: "main" }, @@ -251,7 +253,7 @@ describe("security audit channel dm policy", () => { }); it("uses the channel-owned DM route for wildcard senders", async () => { - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: { session: { dmScope: "main" } }, plugins: [ createDmPlugin({ @@ -304,7 +306,7 @@ describe("security audit channel dm policy", () => { expectedCollisions: 0, }, ])("models wildcard namespace: $name", async ({ dmScope, plugins, expectedCollisions }) => { - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: { session: { dmScope } }, plugins: plugins(), }); @@ -375,7 +377,7 @@ describe("security audit channel dm policy", () => { expectedCollisions: 1, }, ])("intersects wildcard namespace: $name", async ({ cfg, plugins, expectedCollisions }) => { - const findings = await collectChannelSecurityFindings({ cfg, plugins }); + const findings = await collectChannelSecurityFindingsCore({ cfg, plugins }); const collisions = collisionFindings(findings); expect(collisions).toHaveLength(expectedCollisions); @@ -386,7 +388,7 @@ describe("security audit channel dm policy", () => { }); it("does not let exact bindings on finite probe strings hide an open-DM collision", async () => { - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: { session: { dmScope: "main" }, bindings: [ @@ -419,7 +421,7 @@ describe("security audit channel dm policy", () => { }); it("audits exact DM bindings admitted by wildcard policy", async () => { - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: { session: { dmScope: "per-account-channel-peer" }, bindings: [ @@ -466,7 +468,7 @@ describe("security audit channel dm policy", () => { } return { sessionKey: route.sessionKey }; }); - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: { session: { dmScope: "main" } }, plugins: [ createDmPlugin({ @@ -482,7 +484,7 @@ describe("security audit channel dm policy", () => { }); it("warns when custom finite routing omits an unknown-principal policy", async () => { - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: { session: { dmScope: "main" } }, plugins: [ createDmPlugin({ @@ -503,7 +505,7 @@ describe("security audit channel dm policy", () => { }); it("flags public DMs and shared session ownership together", async () => { - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: { session: { dmScope: "main" } }, plugins: [ createDmPlugin({ diff --git a/src/security/audit-channel-readonly-resolution.test.ts b/src/security/audit-channel-readonly-resolution.test.ts index 6806b91d76b7..5dc991c0594b 100644 --- a/src/security/audit-channel-readonly-resolution.test.ts +++ b/src/security/audit-channel-readonly-resolution.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import type { ChannelPlugin } from "../channels/plugins/types.public.js"; import type { OpenClawConfig } from "../config/config.js"; -import { collectChannelSecurityFindings } from "./audit-channel.js"; +import { collectChannelSecurityFindingsCore } from "./audit-channel.js"; function stubChannelPlugin(params: { id: "zalouser"; @@ -33,7 +33,7 @@ function stubChannelPlugin(params: { } function requireReadOnlyResolutionFinding( - findings: Awaited>, + findings: Awaited>, ) { const finding = findings.find( (entry) => entry.checkId === "channels.zalouser.account.read_only_resolution", @@ -62,7 +62,7 @@ describe("security audit channel read-only resolution", () => { }, }; - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg, plugins: [plugin], }); diff --git a/src/security/audit-channel-source-config-discord.test.ts b/src/security/audit-channel-source-config-discord.test.ts index 1d144875794d..ccdf3ec36fe2 100644 --- a/src/security/audit-channel-source-config-discord.test.ts +++ b/src/security/audit-channel-source-config-discord.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { stubAuditChannelPlugin } from "./audit-channel-test-helpers.js"; -import { collectChannelSecurityFindings } from "./audit-channel.js"; +import { collectChannelSecurityFindingsCore } from "./audit-channel.js"; function stubDiscordPlugin(params: { resolveAccount: (cfg: OpenClawConfig, accountId: string | null | undefined) => unknown; @@ -74,7 +74,7 @@ describe("security audit channel source-config fallback discord", () => { }, }; - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: resolvedConfig, sourceConfig, plugins: [ diff --git a/src/security/audit-channel-source-config-slack.test.ts b/src/security/audit-channel-source-config-slack.test.ts index 040144fba75a..5bf4ced5fdec 100644 --- a/src/security/audit-channel-source-config-slack.test.ts +++ b/src/security/audit-channel-source-config-slack.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { stubAuditChannelPlugin } from "./audit-channel-test-helpers.js"; -import { collectChannelSecurityFindings } from "./audit-channel.js"; +import { collectChannelSecurityFindingsCore } from "./audit-channel.js"; function stubSlackPlugin(params: { resolveAccount: (cfg: OpenClawConfig, accountId: string | null | undefined) => unknown; @@ -123,7 +123,7 @@ describe("security audit channel source-config fallback slack", () => { ] as const; for (const testCase of cases) { - const findings = await collectChannelSecurityFindings({ + const findings = await collectChannelSecurityFindingsCore({ cfg: testCase.resolvedConfig, sourceConfig: testCase.sourceConfig, plugins: [testCase.plugin(testCase.sourceConfig)], diff --git a/src/security/audit-channel.collect.runtime.ts b/src/security/audit-channel.collect.runtime.ts index 9759c5b9f0e8..2befeae2433d 100644 --- a/src/security/audit-channel.collect.runtime.ts +++ b/src/security/audit-channel.collect.runtime.ts @@ -1,12 +1,12 @@ // Runtime boundary for collecting channel security audit findings. -import { collectChannelSecurityFindings as collectChannelSecurityFindingsImpl } from "./audit-channel.js"; +import { collectChannelSecurityFindingsCore } from "./audit-channel.js"; type CollectChannelSecurityFindings = - typeof import("./audit-channel.js").collectChannelSecurityFindings; + typeof import("./audit-channel.js").collectChannelSecurityFindingsCore; /** Runtime facade for channel security collection, kept mockable for audit tests. */ export function collectChannelSecurityFindings( ...args: Parameters ): ReturnType { - return collectChannelSecurityFindingsImpl(...args); + return collectChannelSecurityFindingsCore(...args); } diff --git a/src/security/audit-channel.ts b/src/security/audit-channel.ts index 2ba91fc7a37d..124f91a07f4a 100644 --- a/src/security/audit-channel.ts +++ b/src/security/audit-channel.ts @@ -95,7 +95,7 @@ function formatChannelAccountNote(params: { } /** Collect channel-specific security findings across active channel plugins/accounts. */ -export async function collectChannelSecurityFindings(params: { +export async function collectChannelSecurityFindingsCore(params: { cfg: OpenClawConfig; sourceConfig?: OpenClawConfig; plugins: ChannelPlugin[]; diff --git a/src/shared/deferred.test.ts b/src/shared/deferred.test.ts index ecd47b7b57fa..b65352ccad9a 100644 --- a/src/shared/deferred.test.ts +++ b/src/shared/deferred.test.ts @@ -1,9 +1,9 @@ import { describe, expect, it } from "vitest"; -import { createDeferred } from "./deferred.js"; +import { createDeferredCore } from "./deferred.js"; -describe("createDeferred", () => { +describe("createDeferredCore", () => { it("adopts promise-like resolution values", async () => { - const deferred = createDeferred(); + const deferred = createDeferredCore(); deferred.resolve(Promise.resolve(42)); @@ -11,7 +11,7 @@ describe("createDeferred", () => { }); it("keeps the first settlement", async () => { - const deferred = createDeferred(); + const deferred = createDeferredCore(); deferred.resolve("first"); deferred.reject(new Error("late rejection")); diff --git a/src/shared/deferred.ts b/src/shared/deferred.ts index e107c9a58df1..1d2a71bd5733 100644 --- a/src/shared/deferred.ts +++ b/src/shared/deferred.ts @@ -10,6 +10,6 @@ type PromiseConstructorWithResolvers = PromiseConstructor & { const promiseWithResolvers = Promise as PromiseConstructorWithResolvers; -export function createDeferred(): Deferred { +export function createDeferredCore(): Deferred { return promiseWithResolvers.withResolvers(); } diff --git a/src/shared/pending-request-registry.ts b/src/shared/pending-request-registry.ts index 57c64b95d1a1..070ea2b00329 100644 --- a/src/shared/pending-request-registry.ts +++ b/src/shared/pending-request-registry.ts @@ -1,4 +1,4 @@ -import { createDeferred, type Deferred } from "./deferred.js"; +import { createDeferredCore, type Deferred } from "./deferred.js"; export type PendingRequestEntry = Deferred & { value: TValue }; @@ -34,7 +34,7 @@ export function createPendingRequestRegistry() { return undefined; } const entry: StoredPendingRequest = { - ...createDeferred(), + ...createDeferredCore(), value: options.value, dispose: options.dispose, }; diff --git a/src/skills/loading/skills.test.ts b/src/skills/loading/skills.test.ts index c9c5e643f26f..56f0e5a70945 100644 --- a/src/skills/loading/skills.test.ts +++ b/src/skills/loading/skills.test.ts @@ -16,7 +16,7 @@ import { buildWorkspaceSkillCommandSpecs } from "../discovery/command-specs.js"; import { applySkillEnvOverrides, applySkillEnvOverridesFromSnapshot, - getActiveSkillEnvKeys, + getActiveSkillEnvKeysCore, } from "../runtime/env-overrides.js"; import { writeSkill } from "../test-support/e2e-test-helpers.js"; import { @@ -645,11 +645,11 @@ describe("applySkillEnvOverrides", () => { try { expect(process.env.ENV_KEY).toBe("injected"); - expect(getActiveSkillEnvKeys().has("ENV_KEY")).toBe(true); + expect(getActiveSkillEnvKeysCore().has("ENV_KEY")).toBe(true); } finally { restore(); expect(process.env.ENV_KEY).toBeUndefined(); - expect(getActiveSkillEnvKeys().has("ENV_KEY")).toBe(false); + expect(getActiveSkillEnvKeysCore().has("ENV_KEY")).toBe(false); } }); }); @@ -667,15 +667,15 @@ describe("applySkillEnvOverrides", () => { try { expect(process.env.ENV_KEY).toBe("injected"); - expect(getActiveSkillEnvKeys().has("ENV_KEY")).toBe(true); + expect(getActiveSkillEnvKeysCore().has("ENV_KEY")).toBe(true); restoreFirst(); expect(process.env.ENV_KEY).toBe("injected"); - expect(getActiveSkillEnvKeys().has("ENV_KEY")).toBe(true); + expect(getActiveSkillEnvKeysCore().has("ENV_KEY")).toBe(true); } finally { restoreSecond(); expect(process.env.ENV_KEY).toBeUndefined(); - expect(getActiveSkillEnvKeys().has("ENV_KEY")).toBe(false); + expect(getActiveSkillEnvKeysCore().has("ENV_KEY")).toBe(false); } }); }); diff --git a/src/skills/runtime/env-overrides.runtime.ts b/src/skills/runtime/env-overrides.runtime.ts index 74475e00d392..ae3efb4c86c8 100644 --- a/src/skills/runtime/env-overrides.runtime.ts +++ b/src/skills/runtime/env-overrides.runtime.ts @@ -1,11 +1,11 @@ // Runtime env override facade keeps env override loading behind a lazy boundary. -import { getActiveSkillEnvKeys as getActiveSkillEnvKeysImpl } from "./env-overrides.js"; +import { getActiveSkillEnvKeysCore } from "./env-overrides.js"; -type GetActiveSkillEnvKeys = typeof import("./env-overrides.js").getActiveSkillEnvKeys; +type GetActiveSkillEnvKeys = typeof import("./env-overrides.js").getActiveSkillEnvKeysCore; /** Runtime facade for active skill env override discovery. */ export function getActiveSkillEnvKeys( ...args: Parameters ): ReturnType { - return getActiveSkillEnvKeysImpl(...args); + return getActiveSkillEnvKeysCore(...args); } diff --git a/src/skills/runtime/env-overrides.ts b/src/skills/runtime/env-overrides.ts index 3fb1a792cb45..baa00f005bfe 100644 --- a/src/skills/runtime/env-overrides.ts +++ b/src/skills/runtime/env-overrides.ts @@ -31,7 +31,7 @@ type ActiveSkillEnvEntry = { const activeSkillEnvEntries = new Map(); /** Returns a snapshot of env var keys currently injected by skill overrides. */ -export function getActiveSkillEnvKeys(): ReadonlySet { +export function getActiveSkillEnvKeysCore(): ReadonlySet { return new Set(activeSkillEnvEntries.keys()); } diff --git a/src/system-agent/operations-execute.ts b/src/system-agent/operations-execute.ts index b316a3906192..0bd3fc15752b 100644 --- a/src/system-agent/operations-execute.ts +++ b/src/system-agent/operations-execute.ts @@ -142,8 +142,8 @@ export async function executeSystemAgentOperation( return { applied: false }; } case "config-schema": { - const { buildConfigSchema, lookupConfigSchema } = await import("../config/schema.js"); - const response = buildConfigSchema(); + const { buildConfigSchemaCore, lookupConfigSchema } = await import("../config/schema.js"); + const response = buildConfigSchemaCore(); const path = operation.path ?? "."; const result = lookupConfigSchema(response, path); if (!result) { diff --git a/src/web-fetch/runtime.ts b/src/web-fetch/runtime.ts index f8576bd24b0f..d004af64aba8 100644 --- a/src/web-fetch/runtime.ts +++ b/src/web-fetch/runtime.ts @@ -13,7 +13,7 @@ import { resolveRuntimeWebFetchProviders, } from "../plugins/web-fetch-providers.runtime.js"; import { sortWebFetchProvidersForAutoDetect } from "../plugins/web-fetch-providers.shared.js"; -import { getActiveRuntimeWebToolsMetadata } from "../secrets/runtime-web-tools-state.js"; +import { getActiveRuntimeWebToolsMetadataFromState } from "../secrets/runtime-web-tools-state.js"; import type { RuntimeWebFetchMetadata } from "../secrets/runtime-web-tools.types.js"; import { hasWebProviderEntryCredential, @@ -278,7 +278,8 @@ function resolveWebFetchDefinitionUncached( if (!resolveWebFetchEnabled({ fetch, sandboxed: options?.sandboxed })) { return null; } - const runtimeWebFetch = options?.runtimeWebFetch ?? getActiveRuntimeWebToolsMetadata()?.fetch; + const runtimeWebFetch = + options?.runtimeWebFetch ?? getActiveRuntimeWebToolsMetadataFromState()?.fetch; const providers = resolveWebFetchProvidersForOptions(options); return resolveWebProviderDefinition({ config: options?.config, diff --git a/src/web-search/runtime.ts b/src/web-search/runtime.ts index ff18a9bfe2de..04425afeb239 100644 --- a/src/web-search/runtime.ts +++ b/src/web-search/runtime.ts @@ -20,7 +20,7 @@ import { resolveRuntimeWebSearchProviders, } from "../plugins/web-search-providers.runtime.js"; import { sortWebSearchProvidersForAutoDetect } from "../plugins/web-search-providers.shared.js"; -import { getActiveRuntimeWebToolsMetadata } from "../secrets/runtime-web-tools-state.js"; +import { getActiveRuntimeWebToolsMetadataFromState } from "../secrets/runtime-web-tools-state.js"; import type { RuntimeWebSearchMetadata } from "../secrets/runtime-web-tools.types.js"; import { hasWebProviderEntryCredential, @@ -313,7 +313,8 @@ function resolveWebSearchRequestContext( return { config, search: resolveSearchConfig(config), - runtimeWebSearch: options?.runtimeWebSearch ?? getActiveRuntimeWebToolsMetadata()?.search, + runtimeWebSearch: + options?.runtimeWebSearch ?? getActiveRuntimeWebToolsMetadataFromState()?.search, }; } @@ -453,7 +454,8 @@ export async function runWebSearch(params: RunWebSearchParams): Promise(); + const deferred = createDeferredCore(); this.answerDeferred.set(step.id, { deferred, text: step.type === "text", validate }); return await deferred.promise; } diff --git a/test/helpers/promise.ts b/test/helpers/promise.ts index ab0a73a82d0a..5568092c4b4f 100644 --- a/test/helpers/promise.ts +++ b/test/helpers/promise.ts @@ -1,4 +1,4 @@ -export { createDeferred } from "../../src/shared/deferred.js"; +export { createDeferredCore as createDeferred } from "../../src/shared/deferred.js"; export async function withTestTimeout( promise: PromiseLike, diff --git a/test/scripts/control-ui-performance.test.ts b/test/scripts/control-ui-performance.test.ts index 81f9470c003a..290087b95087 100644 --- a/test/scripts/control-ui-performance.test.ts +++ b/test/scripts/control-ui-performance.test.ts @@ -4,6 +4,7 @@ import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { + CONTROL_UI_STARTUP_JS_GZIP_IDENTITY_VARIANCE_BYTES, collectControlUiPerformanceMetrics, evaluateControlUiPerformanceBudgets, extractControlUiStartupAssetPaths, @@ -233,13 +234,25 @@ describe("Control UI performance budgets", () => { expect( evaluateControlUiPerformanceBudgets( - createMetrics(10_001), + createMetrics(10_000 + CONTROL_UI_STARTUP_JS_GZIP_IDENTITY_VARIANCE_BYTES + 1), budgets, startupBaseline(1_000_000), ).map((entry) => entry.metric), ).toEqual(["startup JS gzip"]); }); + it("absorbs bounded build-identity gzip variance at the fixed ceiling", () => { + const budgets = { ...looseBudgets, startupJsGzipBytes: 10_000 }; + + expect( + evaluateControlUiPerformanceBudgets( + createMetrics(10_000 + CONTROL_UI_STARTUP_JS_GZIP_IDENTITY_VARIANCE_BYTES), + budgets, + startupBaseline(10_000), + ), + ).toEqual([]); + }); + it("suggests lowering a baseline after a meaningful size reduction", () => { expect( formatControlUiPerformanceReport(