docs: align documentation with current surfaces

This commit is contained in:
Peter Steinberger
2026-04-23 07:21:39 +01:00
parent 25451c9639
commit 834fdc4832
34 changed files with 162 additions and 73 deletions

View File

@@ -39,7 +39,12 @@
"details",
"summary",
"p",
"div",
"strong",
"span",
"iframe",
"h2",
"h3",
"picture",
"source",
"Tooltip",

View File

@@ -383,6 +383,22 @@
"source": "Testing",
"target": "测试"
},
{
"source": "Async Exec Duplicate Completion Investigation",
"target": "Async Exec Duplicate Completion Investigation"
},
{
"source": "QA Refactor",
"target": "QA 重构"
},
{
"source": "Rich Output Protocol",
"target": "富输出协议"
},
{
"source": "Tencent Cloud (TokenHub)",
"target": "腾讯云TokenHub"
},
{
"source": "/gateway/configuration#strict-validation",
"target": "/gateway/configuration#strict-validation"

View File

@@ -5,8 +5,8 @@ This directory owns docs authoring, Mintlify link rules, and docs i18n policy.
## Mintlify Rules
- Docs are hosted on Mintlify (`https://docs.openclaw.ai`).
- Internal doc links in `docs/**/*.md` must stay root-relative with no `.md` or `.mdx` suffix (example: `[Config](/configuration)`).
- Section cross-references should use anchors on root-relative paths (example: `[Hooks](/configuration#hooks)`).
- Internal doc links in `docs/**/*.md` must stay root-relative with no `.md` or `.mdx` suffix (example: `[Config](/gateway/configuration)`).
- Section cross-references should use anchors on root-relative paths (example: `[Hooks](/gateway/configuration-reference#hooks)`).
- Doc headings should avoid em dashes and apostrophes because Mintlify anchor generation is brittle there.
- README and other GitHub-rendered docs should keep absolute docs URLs so links work outside Mintlify.
- Docs content must stay generic: no personal device names, hostnames, or local paths; use placeholders like `user@gateway-host`.

View File

@@ -1,5 +1,5 @@
---
summary: "Redirect to /tools/message"
summary: "Redirect to /cli/message"
title: "Polls"
---

View File

@@ -393,6 +393,8 @@ Use full IDs for durable automations and storage:
See [Configuration](/gateway/configuration) for template variables.
<a id="coalescing-split-send-dms-command--url-in-one-composition"></a>
## Coalescing split-send DMs (command + URL in one composition)
When a user types a command and a URL together in iMessage — e.g. `Dump https://example.com/article` — Apple splits the send into **two separate webhook deliveries**:

View File

@@ -135,6 +135,8 @@ Default: `allowlist`
---
<a id="get-groupuser-ids"></a>
## Get group/user IDs
### Group IDs (`chat_id`, format: `oc_xxx`)

View File

@@ -9,7 +9,7 @@ read_when:
# IRC
Use IRC when you want OpenClaw in classic channels (`#room`) and direct messages.
IRC ships as an extension plugin, but it is configured in the main config under `channels.irc`.
IRC ships as a bundled plugin, but it is configured in the main config under `channels.irc`.
## Quick start

View File

@@ -190,6 +190,8 @@ Before configuring OpenClaw, you need to create an Azure Bot resource.
2. Click **Microsoft Teams** → Configure → Save
3. Accept the Terms of Service
<a id="federated-authentication-certificate--managed-identity"></a>
## Federated Authentication (Certificate + Managed Identity)
> Added in 2026.3.24

View File

@@ -172,7 +172,7 @@ For local paths and archives, OpenClaw auto-detects:
component layout)
- Cursor-compatible bundles (`.cursor-plugin/plugin.json`)
Compatible bundles install into the normal extensions root and participate in
Compatible bundles install into the normal plugin root and participate in
the same list/info/enable/disable flow. Today, bundle skills, Claude
command-skills, Claude `settings.json` defaults, Claude `.lsp.json` /
manifest-declared `lspServers` defaults, Cursor command-skills, and compatible

View File

@@ -31,7 +31,7 @@ It also emits `security.trust_model.multi_user_heuristic` when config suggests l
For intentional shared-user setups, the audit guidance is to sandbox all sessions, keep filesystem access workspace-scoped, and keep personal/private identities or credentials off that runtime.
It also warns when small models (`<=300B`) are used without sandboxing and with web/browser tools enabled.
For webhook ingress, it warns when `hooks.token` reuses the Gateway token, when `hooks.token` is short, when `hooks.path="/"`, when `hooks.defaultSessionKey` is unset, when `hooks.allowedAgentIds` is unrestricted, when request `sessionKey` overrides are enabled, and when overrides are enabled without `hooks.allowedSessionKeyPrefixes`.
It also warns when sandbox Docker settings are configured while sandbox mode is off, when `gateway.nodes.denyCommands` uses ineffective pattern-like/unknown entries (exact node command-name matching only, not shell-text filtering), when `gateway.nodes.allowCommands` explicitly enables dangerous node commands, when global `tools.profile="minimal"` is overridden by agent tool profiles, when open groups expose runtime/filesystem tools without sandbox/workspace guards, and when installed extension plugin tools may be reachable under permissive tool policy.
It also warns when sandbox Docker settings are configured while sandbox mode is off, when `gateway.nodes.denyCommands` uses ineffective pattern-like/unknown entries (exact node command-name matching only, not shell-text filtering), when `gateway.nodes.allowCommands` explicitly enables dangerous node commands, when global `tools.profile="minimal"` is overridden by agent tool profiles, when open groups expose runtime/filesystem tools without sandbox/workspace guards, and when installed plugin tools may be reachable under permissive tool policy.
It also flags `gateway.allowRealIpFallback=true` (header-spoofing risk if proxies are misconfigured) and `discovery.mdns.mode="full"` (metadata leakage via mDNS TXT records).
It also warns when sandbox browser uses Docker `bridge` network without `sandbox.browser.cdpSourceRange`.
It also flags dangerous sandbox Docker network modes (including `host` and `container:*` namespace joins).

View File

@@ -238,7 +238,7 @@ The managed dreaming cron rides the default agent's heartbeat. If heartbeat is n
Two common causes:
- Another agent declares an explicit `heartbeat:` block. When any entry in `agents.list` has its own `heartbeat` block, only those agents heartbeat — the defaults stop applying to everyone else, so the default agent can go silent. Move the heartbeat settings to `agents.defaults.heartbeat`, or add an explicit `heartbeat` block on the default agent. See [Scope and precedence](/gateway/heartbeat#scope-and-precedence).
- `heartbeat.every` is `0`, empty, or unparseable. The cron has no interval to schedule against, so the heartbeat is effectively disabled. Set `every` to a positive duration such as `30m`. See [Defaults](/gateway/heartbeat#defaults).
- `heartbeat.every` is `0`, empty, or unparsable. The cron has no interval to schedule against, so the heartbeat is effectively disabled. Set `every` to a positive duration such as `30m`. See [Defaults](/gateway/heartbeat#defaults).
## Related

View File

@@ -2493,6 +2493,8 @@ Notes:
- File permissions are `0700` for directories and `0600` for files.
- Cleanup follows the `cleanup` policy: `delete` always removes attachments; `keep` retains them only when `retainOnSessionKeep: true`.
<a id="toolsexperimental"></a>
### `tools.experimental`
Experimental built-in tool flags. Default off unless a strict-agentic GPT-5 auto-enable rule applies.
@@ -2891,7 +2893,7 @@ See [Local Models](/gateway/local-models). TL;DR: run a large local model via LM
allow: ["voice-call"],
deny: [],
load: {
paths: ["~/Projects/oss/voice-call-extension"],
paths: ["~/Projects/oss/voice-call-plugin"],
},
entries: {
"voice-call": {

View File

@@ -124,15 +124,15 @@ and setup-time config writes through `openclaw-gateway` with
The setup script accepts these optional environment variables:
| Variable | Purpose |
| ------------------------------ | ---------------------------------------------------------------- |
| `OPENCLAW_IMAGE` | Use a remote image instead of building locally |
| `OPENCLAW_DOCKER_APT_PACKAGES` | Install extra apt packages during build (space-separated) |
| `OPENCLAW_EXTENSIONS` | Pre-install extension deps at build time (space-separated names) |
| `OPENCLAW_EXTRA_MOUNTS` | Extra host bind mounts (comma-separated `source:target[:opts]`) |
| `OPENCLAW_HOME_VOLUME` | Persist `/home/node` in a named Docker volume |
| `OPENCLAW_SANDBOX` | Opt in to sandbox bootstrap (`1`, `true`, `yes`, `on`) |
| `OPENCLAW_DOCKER_SOCKET` | Override Docker socket path |
| Variable | Purpose |
| ------------------------------ | --------------------------------------------------------------- |
| `OPENCLAW_IMAGE` | Use a remote image instead of building locally |
| `OPENCLAW_DOCKER_APT_PACKAGES` | Install extra apt packages during build (space-separated) |
| `OPENCLAW_EXTENSIONS` | Pre-install plugin deps at build time (space-separated names) |
| `OPENCLAW_EXTRA_MOUNTS` | Extra host bind mounts (comma-separated `source:target[:opts]`) |
| `OPENCLAW_HOME_VOLUME` | Persist `/home/node` in a named Docker volume |
| `OPENCLAW_SANDBOX` | Opt in to sandbox bootstrap (`1`, `true`, `yes`, `on`) |
| `OPENCLAW_DOCKER_SOCKET` | Override Docker socket path |
### Health checks

View File

@@ -91,4 +91,4 @@ Send "Hi" to your assistant on the channel you connected. OpenClaw will reply an
## Next steps
- [Channels](/channels) -- connect Telegram, WhatsApp, Discord, and more
- [Gateway configuration](/gateway/configuration) -- all config optionss
- [Gateway configuration](/gateway/configuration) -- all config options

View File

@@ -64,7 +64,7 @@ Optional build/setup env vars:
- `OPENCLAW_IMAGE` or `OPENCLAW_PODMAN_IMAGE` -- use an existing/pulled image instead of building `openclaw:local`
- `OPENCLAW_DOCKER_APT_PACKAGES` -- install extra apt packages during image build
- `OPENCLAW_EXTENSIONS` -- pre-install extension dependencies at build time
- `OPENCLAW_EXTENSIONS` -- pre-install plugin dependencies at build time
Container start:

View File

@@ -26,7 +26,8 @@ Anthropic's current public docs:
- [Claude Agent SDK overview](https://platform.claude.com/docs/en/agent-sdk/overview)
- [Using Claude Code with your Pro or Max plan](https://support.claude.com/en/articles/11145838-using-claude-code-with-your-pro-or-max-plan)
- [Using Claude Code with your Team or Enterprise plan](https://support.anthropic.com/en/articles/11845131-using-claude-code-with-your-team-or-enterprise-plan/)
</Warning>
</Warning>
## Getting started

View File

@@ -171,10 +171,10 @@ Twilio media frames can be forwarded directly.
## Related
<CardGroup cols={2}>
<Card title="Media tools" href="/tools/media" icon="photo-film">
<Card title="Media tools" href="/tools/media-overview" icon="photo-film">
Audio, image, and video processing pipeline overview.
</Card>
<Card title="Configuration" href="/configuration" icon="gear">
<Card title="Configuration" href="/gateway/configuration" icon="gear">
Full config reference including media tool settings.
</Card>
<Card title="Troubleshooting" href="/help/troubleshooting" icon="wrench">

View File

@@ -64,6 +64,7 @@ Looking for chat channel docs (WhatsApp/Telegram/Discord/Slack/Mattermost (plugi
- [SGLang (local models)](/providers/sglang)
- [StepFun](/providers/stepfun)
- [Synthetic](/providers/synthetic)
- [Tencent Cloud (TokenHub)](/providers/tencent)
- [Together AI](/providers/together)
- [Venice (Venice AI, privacy-focused)](/providers/venice)
- [Vercel AI Gateway](/providers/vercel-ai-gateway)

View File

@@ -18,7 +18,8 @@ read_when:
- **Virtual keys** — Create keys with spend limits for OpenClaw
- **Logging** — Full request/response logs for debugging
- **Fallbacks** — Automatic failover if your primary provider is down
</Tip>
</Tip>
## Quick start

View File

@@ -172,7 +172,7 @@ matching `sampleRate` only if your upstream stream is already raw PCM.
<Card title="Model selection" href="/concepts/model-providers" icon="layers">
Choosing providers, model refs, and failover behavior.
</Card>
<Card title="Media understanding" href="/tools/media-understanding" icon="microphone">
<Card title="Media understanding" href="/nodes/media-understanding" icon="microphone">
Audio transcription setup and provider selection.
</Card>
</CardGroup>

View File

@@ -383,7 +383,7 @@ Config lives under `plugins.entries.moonshot.config.webSearch`:
<Card title="Model selection" href="/concepts/model-providers" icon="layers">
Choosing providers, model refs, and failover behavior.
</Card>
<Card title="Web search" href="/tools/web-search" icon="magnifying-glass">
<Card title="Web search" href="/tools/web" icon="magnifying-glass">
Configuring web search providers including Kimi.
</Card>
<Card title="Configuration reference" href="/gateway/configuration-reference" icon="gear">

View File

@@ -445,6 +445,8 @@ Legacy `plugins.entries.openai.config.personality` is still read as a compatibil
</Accordion>
<a id="openai-fast-mode"></a>
<Accordion title="Fast mode">
OpenClaw exposes a shared fast-mode toggle for both `openai/*` and `openai-codex/*`:

View File

@@ -55,6 +55,6 @@ is available to that process (for example, in `~/.openclaw/.env` or via
## Related documentation
- [OpenClaw Configuration](/configuration)
- [OpenClaw Configuration](/gateway/configuration)
- [Model Providers](/concepts/model-providers)
- [Tencent TokenHub](https://cloud.tencent.com/document/product/1823/130050)

View File

@@ -131,7 +131,7 @@ interactive shell are not automatically inherited. See the daemon note above.
<Card title="Model selection" href="/concepts/model-providers" icon="layers">
Choosing providers, model refs, and failover behavior.
</Card>
<Card title="Configuration" href="/configuration" icon="gear">
<Card title="Configuration" href="/gateway/configuration" icon="gear">
Full config reference for agents, models, and providers.
</Card>
<Card title="Troubleshooting" href="/help/troubleshooting" icon="wrench">

View File

@@ -1,3 +1,11 @@
---
summary: "Investigation notes for duplicate async exec completion injection"
read_when:
- Debugging repeated node exec completion events
- Working on heartbeat/system-event dedupe
title: "Async Exec Duplicate Completion Investigation"
---
# Async Exec Duplicate Completion Investigation
## Scope

View File

@@ -1,3 +1,11 @@
---
summary: "QA refactor plan for scenario catalog and harness consolidation"
read_when:
- Refactoring QA scenario definitions or qa-lab harness code
- Moving QA behavior between markdown scenarios and TypeScript harness logic
title: "QA Refactor"
---
# QA Refactor
Status: foundational migration landed.

View File

@@ -1,3 +1,11 @@
---
summary: "Rich output shortcode protocol for embeds, media, audio hints, and replies"
read_when:
- Changing assistant output rendering in the Control UI
- Debugging `[embed ...]`, `MEDIA:`, reply, or audio presentation directives
title: "Rich Output Protocol"
---
# Rich Output Protocol
Assistant output can carry a small set of delivery/render directives:

View File

@@ -7,8 +7,6 @@ read_when:
- Updating community project highlights
---
<!-- markdownlint-disable MD033 -->
# Showcase
<div className="showcase-hero">
@@ -54,7 +52,7 @@ read_when:
<a href="#submit-your-project">Submit a project</a>
</div>
<h2 id="videos">Videos</h2>
## Videos
<p className="showcase-section-intro">
Start here if you want the shortest path from “what is this?” to “okay, I get it.”
@@ -107,7 +105,7 @@ read_when:
</div>
</div>
<h2 id="fresh-from-discord">Fresh from Discord</h2>
## Fresh from Discord
<p className="showcase-section-intro">
Recent standouts across coding, devtools, mobile, and chat-native product building.
@@ -234,7 +232,9 @@ Read, send, and archive messages via Beeper Desktop. Uses Beeper local MCP API s
</CardGroup>
<h2 id="automation-workflows">Automation &amp; Workflows</h2>
<a id="automation-workflows"></a>
## Automation & Workflows
<p className="showcase-section-intro">
Scheduling, browser control, support loops, and the “just do the task for me” side of the product.
@@ -316,7 +316,9 @@ Watches company Slack channel, responds helpfully, and forwards notifications to
</CardGroup>
<h2 id="knowledge-memory">Knowledge &amp; Memory</h2>
<a id="knowledge-memory"></a>
## Knowledge & Memory
<p className="showcase-section-intro">
Systems that index, search, remember, and reason over personal or team knowledge.
@@ -352,7 +354,9 @@ Watches company Slack channel, responds helpfully, and forwards notifications to
</CardGroup>
<h2 id="voice-phone">Voice &amp; Phone</h2>
<a id="voice-phone"></a>
## Voice & Phone
<p className="showcase-section-intro">
Speech-first entry points, phone bridges, and transcription-heavy workflows.
@@ -374,7 +378,9 @@ Multi-lingual audio transcription via OpenRouter (Gemini, etc). Available on Cla
</CardGroup>
<h2 id="infrastructure-deployment">Infrastructure &amp; Deployment</h2>
<a id="infrastructure-deployment"></a>
## Infrastructure & Deployment
<p className="showcase-section-intro">
Packaging, deployment, and integrations that make OpenClaw easier to run and extend.
@@ -408,7 +414,9 @@ Multi-lingual audio transcription via OpenRouter (Gemini, etc). Available on Cla
</CardGroup>
<h2 id="home-hardware">Home &amp; Hardware</h2>
<a id="home-hardware"></a>
## Home & Hardware
<p className="showcase-section-intro">
The physical-world side of OpenClaw: homes, sensors, cameras, vacuums, and other devices.
@@ -434,7 +442,7 @@ Multi-lingual audio transcription via OpenRouter (Gemini, etc). Available on Cla
</CardGroup>
<h2 id="community-projects">Community Projects</h2>
## Community Projects
<p className="showcase-section-intro">
Things that grew beyond a single workflow into broader products or ecosystems.
@@ -452,7 +460,7 @@ Multi-lingual audio transcription via OpenRouter (Gemini, etc). Available on Cla
---
<h2 id="submit-your-project">Submit Your Project</h2>
## Submit Your Project
<p className="showcase-section-intro">
If you are building something interesting with OpenClaw, send it over. Strong screenshots and concrete outcomes help.

View File

@@ -53,24 +53,24 @@ OpenClaw has three layers that work together:
These tools ship with OpenClaw and are available without installing any plugins:
| Tool | What it does | Page |
| ------------------------------------------ | --------------------------------------------------------------------- | ------------------------------------------- |
| `exec` / `process` | Run shell commands, manage background processes | [Exec](/tools/exec) |
| `code_execution` | Run sandboxed remote Python analysis | [Code Execution](/tools/code-execution) |
| `browser` | Control a Chromium browser (navigate, click, screenshot) | [Browser](/tools/browser) |
| `web_search` / `x_search` / `web_fetch` | Search the web, search X posts, fetch page content | [Web](/tools/web) |
| `read` / `write` / `edit` | File I/O in the workspace | |
| `apply_patch` | Multi-hunk file patches | [Apply Patch](/tools/apply-patch) |
| `message` | Send messages across all channels | [Agent Send](/tools/agent-send) |
| `canvas` | Drive node Canvas (present, eval, snapshot) | |
| `nodes` | Discover and target paired devices | |
| `cron` / `gateway` | Manage scheduled jobs; inspect, patch, restart, or update the gateway | |
| `image` / `image_generate` | Analyze or generate images | [Image Generation](/tools/image-generation) |
| `music_generate` | Generate music tracks | [Music Generation](/tools/music-generation) |
| `video_generate` | Generate videos | [Video Generation](/tools/video-generation) |
| `tts` | One-shot text-to-speech conversion | [TTS](/tools/tts) |
| `sessions_*` / `subagents` / `agents_list` | Session management, status, and sub-agent orchestration | [Sub-agents](/tools/subagents) |
| `session_status` | Lightweight `/status`-style readback and session model override | [Session Tools](/concepts/session-tool) |
| Tool | What it does | Page |
| ------------------------------------------ | --------------------------------------------------------------------- | ------------------------------------------------------------ |
| `exec` / `process` | Run shell commands, manage background processes | [Exec](/tools/exec), [Exec Approvals](/tools/exec-approvals) |
| `code_execution` | Run sandboxed remote Python analysis | [Code Execution](/tools/code-execution) |
| `browser` | Control a Chromium browser (navigate, click, screenshot) | [Browser](/tools/browser) |
| `web_search` / `x_search` / `web_fetch` | Search the web, search X posts, fetch page content | [Web](/tools/web), [Web Fetch](/tools/web-fetch) |
| `read` / `write` / `edit` | File I/O in the workspace | |
| `apply_patch` | Multi-hunk file patches | [Apply Patch](/tools/apply-patch) |
| `message` | Send messages across all channels | [Agent Send](/tools/agent-send) |
| `canvas` | Drive node Canvas (present, eval, snapshot) | |
| `nodes` | Discover and target paired devices | |
| `cron` / `gateway` | Manage scheduled jobs; inspect, patch, restart, or update the gateway | |
| `image` / `image_generate` | Analyze or generate images | [Image Generation](/tools/image-generation) |
| `music_generate` | Generate music tracks | [Music Generation](/tools/music-generation) |
| `video_generate` | Generate videos | [Video Generation](/tools/video-generation) |
| `tts` | One-shot text-to-speech conversion | [TTS](/tools/tts) |
| `sessions_*` / `subagents` / `agents_list` | Session management, status, and sub-agent orchestration | [Sub-agents](/tools/subagents) |
| `session_status` | Lightweight `/status`-style readback and session model override | [Session Tools](/concepts/session-tool) |
For image work, use `image` for analysis and `image_generate` for generation or editing. If you target `openai/*`, `google/*`, `fal/*`, or another non-default image provider, configure that provider's auth/API key first.

View File

@@ -133,7 +133,7 @@ Looking for third-party plugins? See [Community Plugins](/plugins/community).
enabled: true,
allow: ["voice-call"],
deny: ["untrusted-plugin"],
load: { paths: ["~/Projects/oss/voice-call-extension"] },
load: { paths: ["~/Projects/oss/voice-call-plugin"] },
entries: {
"voice-call": { enabled: true, config: { provider: "twilio" } },
},

View File

@@ -1,5 +1,6 @@
---
title: "Text-to-Speech"
summary: "Redirect to /tools/tts"
redirect: /tools/tts
---

View File

@@ -21,6 +21,12 @@ const runtimeMocks = vi.hoisted(() => ({
refreshOpenAICodexToken: vi.fn(),
}));
type OpenAIRefreshDelegateGlobal = typeof globalThis & {
__OPENCLAW_TEST_REFRESH_OPENAI_CODEX_TOKEN__?: (...args: unknown[]) => unknown;
};
const openAIRefreshDelegateGlobal = () => globalThis as OpenAIRefreshDelegateGlobal;
vi.mock("openclaw/plugin-sdk/runtime-env", async () => {
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/runtime-env")>(
"openclaw/plugin-sdk/runtime-env",
@@ -31,15 +37,14 @@ vi.mock("openclaw/plugin-sdk/runtime-env", async () => {
};
});
vi.mock("@mariozechner/pi-ai/oauth", async () => {
const actual = await vi.importActual<typeof import("@mariozechner/pi-ai/oauth")>(
"@mariozechner/pi-ai/oauth",
);
return {
...actual,
refreshOpenAICodexToken: runtimeMocks.refreshOpenAICodexToken,
};
});
vi.mock("@mariozechner/pi-ai/oauth", () => ({
getOAuthApiKey: vi.fn(),
getOAuthProviders: () => [],
loginOpenAICodex: vi.fn(),
refreshOpenAICodexToken: vi.fn((...args: unknown[]) =>
openAIRefreshDelegateGlobal().__OPENCLAW_TEST_REFRESH_OPENAI_CODEX_TOKEN__?.(...args),
),
}));
import { refreshOpenAICodexToken } from "./openai-codex-provider.runtime.js";
@@ -282,14 +287,19 @@ describe("openai plugin", () => {
expires: Date.now() + 60_000,
};
runtimeMocks.refreshOpenAICodexToken.mockResolvedValue(refreshed);
openAIRefreshDelegateGlobal().__OPENCLAW_TEST_REFRESH_OPENAI_CODEX_TOKEN__ =
runtimeMocks.refreshOpenAICodexToken;
try {
await expect(refreshOpenAICodexToken("refresh-token")).resolves.toBe(refreshed);
await expect(refreshOpenAICodexToken("refresh-token")).resolves.toBe(refreshed);
expect(runtimeMocks.ensureGlobalUndiciEnvProxyDispatcher).toHaveBeenCalledOnce();
expect(runtimeMocks.refreshOpenAICodexToken).toHaveBeenCalledOnce();
expect(
runtimeMocks.ensureGlobalUndiciEnvProxyDispatcher.mock.invocationCallOrder[0],
).toBeLessThan(runtimeMocks.refreshOpenAICodexToken.mock.invocationCallOrder[0]);
expect(runtimeMocks.ensureGlobalUndiciEnvProxyDispatcher).toHaveBeenCalledOnce();
expect(runtimeMocks.refreshOpenAICodexToken).toHaveBeenCalledOnce();
expect(
runtimeMocks.ensureGlobalUndiciEnvProxyDispatcher.mock.invocationCallOrder[0],
).toBeLessThan(runtimeMocks.refreshOpenAICodexToken.mock.invocationCallOrder[0]);
} finally {
delete openAIRefreshDelegateGlobal().__OPENCLAW_TEST_REFRESH_OPENAI_CODEX_TOKEN__;
}
});
it("registers provider-owned OpenAI tool compat hooks for openai and codex", async () => {

View File

@@ -7,3 +7,7 @@ OptionA
CAF
overlayed
re-use
AKS
Ballance
optIn
wit

View File

@@ -1,9 +1,17 @@
import { vi } from "vitest";
declare global {
// Optional per-test delegate for the shared OAuth mock.
var __OPENCLAW_TEST_REFRESH_OPENAI_CODEX_TOKEN__: ((...args: unknown[]) => unknown) | undefined;
}
vi.mock("@mariozechner/pi-ai/oauth", () => ({
getOAuthApiKey: () => undefined,
getOAuthProviders: () => [],
loginOpenAICodex: vi.fn(),
refreshOpenAICodexToken: vi.fn((...args: unknown[]) =>
globalThis.__OPENCLAW_TEST_REFRESH_OPENAI_CODEX_TOKEN__?.(...args),
),
}));
vi.mock("@mariozechner/clipboard", () => ({