Skip to content

Commit a86cedc

Browse files
committed
Auto-merge upstream openclaw/openclaw
2 parents 0a645c0 + af428d9 commit a86cedc

822 files changed

Lines changed: 31873 additions & 2044 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/openclaw-parallels-smoke/SKILL.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
2929
## npm install then update
3030

3131
- Preferred entrypoint: `pnpm test:parallels:npm-update`
32-
- Flow: fresh snapshot -> install npm package baseline -> smoke -> install current main tgz on the same guest -> smoke again.
32+
- Required coverage: every release/update regression run must include both lanes:
33+
- fresh snapshot -> install requested package/baseline -> smoke
34+
- same guest baseline -> run the guest's installed `openclaw update ...` command -> smoke again
35+
- The update lane must exercise OpenClaw's internal updater. Do not count a direct `npm install -g <tgz-or-spec>` or harness-side package swap as update-flow coverage; those are install smokes only.
36+
- For published targets, install the old baseline package first (for example `openclaw@2026.4.9`), then run the installed guest CLI with the intended channel/tag (for example `openclaw update --channel beta --yes --json`) and verify `openclaw --version`, `openclaw update status --json`, gateway RPC, and an agent turn after the command.
37+
- For unpublished targets, pack the candidate on the host, serve the `.tgz` over the harness HTTP server, and point the guest updater at that served package. Prefer `openclaw update --tag http://<host-ip>:<port>/openclaw-<version>.tgz --yes --json`; when channel persistence also matters, pass `--channel <stable|beta>` and set `OPENCLAW_UPDATE_PACKAGE_SPEC` to the same served URL in the guest update environment. The command under test must still be `openclaw update`, not direct npm.
38+
- For unpublished local-fix validation, remember the old baseline updater code still controls the first hop. A fix that lives only in the new updater code cannot change that already-running old process; the served candidate must either keep package/plugin metadata compatible with the baseline host or the baseline itself must include the updater fix.
3339
- For beta/stable verification, resolve the tag immediately before the run (`npm view openclaw@beta version dist.tarball` or `npm view openclaw@latest ...`). Tags can move while a long VM matrix is already running; restart the matrix when the intended prerelease appears after an earlier registry 404/tag-lag check.
3440
- Source Peter's profile in the host shell (`set -a; source "$HOME/.profile"; set +a`) before OpenAI/Anthropic lanes. Do not print profile contents or env dumps; pass provider secrets through the guest exec environment.
3541
- Same-guest update verification should set the default model explicitly to `openai/gpt-5.4` before the agent turn and use a fresh explicit `--session-id` so old session model state does not leak into the check.

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ Docs: https://docs.openclaw.ai
77
### Changes
88

99
- Tools/video_generate: allow providers and plugins to return URL-only generated video assets so agent delivery and `openclaw capability video generate --output ...` can forward or stream large videos without requiring the full file in memory first. (#61988) Thanks @xieyongliang.
10+
- Models/providers: surface how configured OpenAI-compatible endpoints are classified in embedded-agent debug logs, so local and proxy routing issues are easier to diagnose. (#64754) Thanks @ImLukeF.
1011

1112
### Fixes
1213

1314
- WhatsApp: honor the configured default account when the active listener helper is used without an explicit account id, so named default accounts do not get registered under `default`. (#53918) Thanks @yhyatt.
1415
- QA/packaging: stop packaged CLI startup and completion cache generation from reading repo-only QA scenario markdown by routing QA command registration through a narrow facade. (#64648) Thanks @obviyus.
16+
- QA/packaging: ship the bundled QA scenario pack in npm releases and keep `openclaw completion --write-state` working even if QA setup is broken, so missing QA content only degrades QA instead of breaking broader CLI startup-adjacent flows. Thanks @vincentkoc.
17+
- Codex/QA: keep Codex app-server coordination chatter out of visible replies, add a live QA leak scenario, and classify leaked harness meta text as a QA failure instead of a successful reply. Thanks @vincentkoc.
1518
- Control UI/webchat: persist agent-run TTS audio replies into webchat history before finalization so tool-generated audio reaches webchat clients again. (#63514) thanks @bittoby
1619
- macOS/Talk Mode: after granting microphone permission on first enable, continue starting Talk Mode instead of requiring a second toggle. (#62459) Thanks @ggarber.
1720
- OpenAI/Codex OAuth: stop rewriting the upstream authorize URL scopes so new Codex sign-ins do not fail with `invalid_scope` before returning an authorization code. (#64713) Thanks @fuller-stack-dev.

docs/plugins/architecture.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,10 +519,15 @@ The manifest is the control-plane source of truth. OpenClaw uses it to:
519519
- validate `plugins.entries.<id>.config`
520520
- augment Control UI labels/placeholders
521521
- show install/catalog metadata
522+
- preserve cheap activation and setup descriptors without loading plugin runtime
522523

523524
For native plugins, the runtime module is the data-plane part. It registers
524525
actual behavior such as hooks, tools, commands, or provider flows.
525526

527+
Optional manifest `activation` and `setup` blocks stay on the control plane.
528+
They are metadata-only descriptors for activation planning and setup discovery;
529+
they do not replace runtime registration, `register(...)`, or `setupEntry`.
530+
526531
### What the loader caches
527532

528533
OpenClaw keeps short in-process caches for:

docs/plugins/manifest.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ Use it for:
4747
- config validation
4848
- auth and onboarding metadata that should be available without booting plugin
4949
runtime
50+
- cheap activation hints that control-plane surfaces can inspect before runtime
51+
loads
52+
- cheap setup descriptors that setup/onboarding surfaces can inspect before
53+
runtime loads
5054
- alias and auto-enable metadata that should resolve before plugin runtime loads
5155
- shorthand model-family ownership metadata that should auto-activate the
5256
plugin before runtime loads
@@ -152,6 +156,8 @@ Those belong in your plugin code and `package.json`.
152156
| `providerAuthAliases` | No | `Record<string, string>` | Provider ids that should reuse another provider id for auth lookup, for example a coding provider that shares the base provider API key and auth profiles. |
153157
| `channelEnvVars` | No | `Record<string, string[]>` | Cheap channel env metadata that OpenClaw can inspect without loading plugin code. Use this for env-driven channel setup or auth surfaces that generic startup/config helpers should see. |
154158
| `providerAuthChoices` | No | `object[]` | Cheap auth-choice metadata for onboarding pickers, preferred-provider resolution, and simple CLI flag wiring. |
159+
| `activation` | No | `object` | Cheap activation hints for provider, command, channel, route, and capability-triggered loading. Metadata only; plugin runtime still owns actual behavior. |
160+
| `setup` | No | `object` | Cheap setup/onboarding descriptors that discovery and setup surfaces can inspect without loading plugin runtime. |
155161
| `contracts` | No | `object` | Static bundled capability snapshot for speech, realtime transcription, realtime voice, media-understanding, image-generation, music-generation, video-generation, web-fetch, web search, and tool ownership. |
156162
| `channelConfigs` | No | `Record<string, object>` | Manifest-owned channel config metadata merged into discovery and validation surfaces before runtime loads. |
157163
| `skills` | No | `string[]` | Skill directories to load, relative to the plugin root. |
@@ -208,6 +214,77 @@ uses this metadata for diagnostics without importing plugin runtime code.
208214
| `kind` | No | `"runtime-slash"` | Marks the alias as a chat slash command rather than a root CLI command. |
209215
| `cliCommand` | No | `string` | Related root CLI command to suggest for CLI operations, if one exists. |
210216

217+
## activation reference
218+
219+
Use `activation` when the plugin can cheaply declare which control-plane events
220+
should activate it later.
221+
222+
This block is metadata only. It does not register runtime behavior, and it does
223+
not replace `register(...)`, `setupEntry`, or other runtime/plugin entrypoints.
224+
225+
```json
226+
{
227+
"activation": {
228+
"onProviders": ["openai"],
229+
"onCommands": ["models"],
230+
"onChannels": ["web"],
231+
"onRoutes": ["gateway-webhook"],
232+
"onCapabilities": ["provider", "tool"]
233+
}
234+
}
235+
```
236+
237+
| Field | Required | Type | What it means |
238+
| ---------------- | -------- | ---------------------------------------------------- | ----------------------------------------------------------------- |
239+
| `onProviders` | No | `string[]` | Provider ids that should activate this plugin when requested. |
240+
| `onCommands` | No | `string[]` | Command ids that should activate this plugin. |
241+
| `onChannels` | No | `string[]` | Channel ids that should activate this plugin. |
242+
| `onRoutes` | No | `string[]` | Route kinds that should activate this plugin. |
243+
| `onCapabilities` | No | `Array<"provider" \| "channel" \| "tool" \| "hook">` | Broad capability hints used by control-plane activation planning. |
244+
245+
## setup reference
246+
247+
Use `setup` when setup and onboarding surfaces need cheap plugin-owned metadata
248+
before runtime loads.
249+
250+
```json
251+
{
252+
"setup": {
253+
"providers": [
254+
{
255+
"id": "openai",
256+
"authMethods": ["api-key"],
257+
"envVars": ["OPENAI_API_KEY"]
258+
}
259+
],
260+
"cliBackends": ["openai-cli"],
261+
"configMigrations": ["legacy-openai-auth"],
262+
"requiresRuntime": false
263+
}
264+
}
265+
```
266+
267+
Top-level `cliBackends` stays valid and continues to describe CLI inference
268+
backends. `setup.cliBackends` is the setup-specific descriptor surface for
269+
control-plane/setup flows that should stay metadata-only.
270+
271+
### setup.providers reference
272+
273+
| Field | Required | Type | What it means |
274+
| ------------- | -------- | ---------- | ---------------------------------------------------------------------------------- |
275+
| `id` | Yes | `string` | Provider id exposed during setup or onboarding. |
276+
| `authMethods` | No | `string[]` | Setup/auth method ids this provider supports without loading full runtime. |
277+
| `envVars` | No | `string[]` | Env vars that generic setup/status surfaces can check before plugin runtime loads. |
278+
279+
### setup fields
280+
281+
| Field | Required | Type | What it means |
282+
| ------------------ | -------- | ---------- | --------------------------------------------------------------------------- |
283+
| `providers` | No | `object[]` | Provider setup descriptors exposed during setup and onboarding. |
284+
| `cliBackends` | No | `string[]` | Setup-time backend ids available without full runtime activation. |
285+
| `configMigrations` | No | `string[]` | Config migration ids owned by this plugin's setup surface. |
286+
| `requiresRuntime` | No | `boolean` | Whether setup still needs plugin runtime execution after descriptor lookup. |
287+
211288
## uiHints reference
212289

213290
`uiHints` is a map from config field names to small rendering hints.

extensions/browser/src/browser/control-auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import {
33
normalizeLowercaseStringOrEmpty,
44
normalizeOptionalString,
55
} from "openclaw/plugin-sdk/text-runtime";
6-
import type { OpenClawConfig } from "../config/config.js";
76
import { loadConfig, writeConfigFile } from "../config/config.js";
7+
import type { OpenClawConfig } from "../config/types.openclaw.js";
88
import { resolveGatewayAuth } from "../gateway/auth.js";
99
import { ensureGatewayStartupAuth } from "../gateway/startup-auth.js";
1010

extensions/browser/src/doctor-browser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
readBrowserVersion,
66
resolveGoogleChromeExecutableForPlatform,
77
} from "./browser/chrome.executables.js";
8-
import type { OpenClawConfig } from "./config/config.js";
8+
import type { OpenClawConfig } from "./config/types.openclaw.js";
99
import { asRecord } from "./record-shared.js";
1010

1111
const CHROME_MCP_MIN_MAJOR = 144;

extensions/codex/src/app-server/event-projector.test.ts

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,87 @@ describe("CodexAppServerEventProjector", () => {
7979
});
8080

8181
expect(onAssistantMessageStart).toHaveBeenCalledTimes(1);
82-
expect(onPartialReply).toHaveBeenLastCalledWith({ text: "hello" });
82+
expect(onPartialReply).not.toHaveBeenCalled();
8383
expect(result.assistantTexts).toEqual(["hello"]);
8484
expect(result.messagesSnapshot.map((message) => message.role)).toEqual(["user", "assistant"]);
8585
expect(result.lastAssistant?.content).toEqual([{ type: "text", text: "hello" }]);
8686
expect(result.attemptUsage).toMatchObject({ input: 5, output: 7, cacheRead: 2, total: 12 });
8787
expect(result.replayMetadata.replaySafe).toBe(true);
8888
});
8989

90+
it("keeps intermediate agentMessage items out of the final visible reply", async () => {
91+
const onAssistantMessageStart = vi.fn();
92+
const onPartialReply = vi.fn();
93+
const params = {
94+
...createParams(),
95+
onAssistantMessageStart,
96+
onPartialReply,
97+
};
98+
const projector = new CodexAppServerEventProjector(params, "thread-1", "turn-1");
99+
100+
await projector.handleNotification({
101+
method: "item/agentMessage/delta",
102+
params: {
103+
threadId: "thread-1",
104+
turnId: "turn-1",
105+
itemId: "msg-commentary",
106+
delta: "checking thread context; then post a tight progress reply here.",
107+
},
108+
});
109+
await projector.handleNotification({
110+
method: "item/agentMessage/delta",
111+
params: {
112+
threadId: "thread-1",
113+
turnId: "turn-1",
114+
itemId: "msg-final",
115+
delta: "release fixes first. please drop affected PRs, failing checks, and blockers here.",
116+
},
117+
});
118+
await projector.handleNotification({
119+
method: "turn/completed",
120+
params: {
121+
threadId: "thread-1",
122+
turnId: "turn-1",
123+
turn: {
124+
id: "turn-1",
125+
status: "completed",
126+
items: [
127+
{
128+
type: "agentMessage",
129+
id: "msg-commentary",
130+
text: "checking thread context; then post a tight progress reply here.",
131+
},
132+
{
133+
type: "agentMessage",
134+
id: "msg-final",
135+
text: "release fixes first. please drop affected PRs, failing checks, and blockers here.",
136+
},
137+
],
138+
},
139+
},
140+
});
141+
142+
const result = projector.buildResult({
143+
didSendViaMessagingTool: false,
144+
messagingToolSentTexts: [],
145+
messagingToolSentMediaUrls: [],
146+
messagingToolSentTargets: [],
147+
});
148+
149+
expect(onAssistantMessageStart).toHaveBeenCalledTimes(1);
150+
expect(onPartialReply).not.toHaveBeenCalled();
151+
expect(result.assistantTexts).toEqual([
152+
"release fixes first. please drop affected PRs, failing checks, and blockers here.",
153+
]);
154+
expect(result.lastAssistant?.content).toEqual([
155+
{
156+
type: "text",
157+
text: "release fixes first. please drop affected PRs, failing checks, and blockers here.",
158+
},
159+
]);
160+
expect(JSON.stringify(result.messagesSnapshot)).not.toContain("checking thread context");
161+
});
162+
90163
it("ignores notifications for other turns", async () => {
91164
const params = createParams();
92165
const projector = new CodexAppServerEventProjector(params, "thread-1", "turn-1");

extensions/codex/src/app-server/event-projector.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const ZERO_USAGE: Usage = {
4444

4545
export class CodexAppServerEventProjector {
4646
private readonly assistantTextByItem = new Map<string, string>();
47+
private readonly assistantItemOrder: string[] = [];
4748
private readonly reasoningTextByItem = new Map<string, string>();
4849
private readonly planTextByItem = new Map<string, string>();
4950
private readonly activeItemIds = new Set<string>();
@@ -210,9 +211,12 @@ export class CodexAppServerEventProjector {
210211
this.assistantStarted = true;
211212
await this.params.onAssistantMessageStart?.();
212213
}
214+
this.rememberAssistantItem(itemId);
213215
const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
214216
this.assistantTextByItem.set(itemId, text);
215-
await this.params.onPartialReply?.({ text });
217+
// Codex app-server can emit multiple agentMessage items per turn, including
218+
// intermediate coordination/progress prose. Keep those deltas internal until
219+
// turn completion chooses the last assistant item as the user-visible reply.
216220
}
217221

218222
private async handleReasoningDelta(params: JsonObject): Promise<void> {
@@ -291,6 +295,7 @@ export class CodexAppServerEventProjector {
291295
this.completedItemIds.add(itemId);
292296
}
293297
if (item?.type === "agentMessage" && typeof item.text === "string" && item.text) {
298+
this.rememberAssistantItem(item.id);
294299
this.assistantTextByItem.set(item.id, item.text);
295300
}
296301
if (item?.type === "plan" && typeof item.text === "string" && item.text) {
@@ -348,6 +353,7 @@ export class CodexAppServerEventProjector {
348353
}
349354
for (const item of turn.items ?? []) {
350355
if (item.type === "agentMessage" && typeof item.text === "string" && item.text) {
356+
this.rememberAssistantItem(item.id);
351357
this.assistantTextByItem.set(item.id, item.text);
352358
}
353359
if (item.type === "plan" && typeof item.text === "string" && item.text) {
@@ -425,7 +431,29 @@ export class CodexAppServerEventProjector {
425431
}
426432

427433
private collectAssistantTexts(): string[] {
428-
return [...this.assistantTextByItem.values()].filter((text) => text.trim().length > 0);
434+
const finalText = this.resolveFinalAssistantText();
435+
return finalText ? [finalText] : [];
436+
}
437+
438+
private resolveFinalAssistantText(): string | undefined {
439+
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
440+
const itemId = this.assistantItemOrder[i];
441+
if (!itemId) {
442+
continue;
443+
}
444+
const text = this.assistantTextByItem.get(itemId)?.trim();
445+
if (text) {
446+
return text;
447+
}
448+
}
449+
return undefined;
450+
}
451+
452+
private rememberAssistantItem(itemId: string): void {
453+
if (!itemId || this.assistantItemOrder.includes(itemId)) {
454+
return;
455+
}
456+
this.assistantItemOrder.push(itemId);
429457
}
430458

431459
private createAssistantMessage(text: string): AssistantMessage {

extensions/qa-lab/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { registerQaLabCli } from "./src/cli.js";
1+
export { isQaLabCliAvailable, registerQaLabCli } from "./src/cli.js";

0 commit comments

Comments
 (0)