mirror of https://github.com/openclaw/openclaw.git
acp: add regression coverage and smoke-test docs (#41456)
Merged via squash.
Prepared head SHA: 514d587352
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
This commit is contained in:
parent
4aebff78bc
commit
0c7f07818f
|
|
@ -31,6 +31,7 @@ Docs: https://docs.openclaw.ai
|
|||
- ACP/session UX: replay stored user and assistant text on `loadSession`, expose Gateway-backed session controls and metadata, and emit approximate session usage updates so IDE clients restore context more faithfully. (#41425) Thanks @mbelinky.
|
||||
- ACP/tool streaming: enrich `tool_call` and `tool_call_update` events with best-effort text content and file-location hints so IDE clients can follow bridge tool activity more naturally. (#41442) Thanks @mbelinky.
|
||||
- ACP/runtime attachments: forward normalized inbound image attachments into ACP runtime turns so ACPX sessions can preserve image prompt content on the runtime path. (#41427) Thanks @mbelinky.
|
||||
- ACP/regressions: add gateway RPC coverage for ACP lineage patching, ACPX runtime coverage for image prompt serialization, and an operator smoke-test procedure for live ACP spawn verification. (#41456) Thanks @mbelinky.
|
||||
|
||||
## 2026.3.8
|
||||
|
||||
|
|
|
|||
|
|
@ -246,6 +246,46 @@ Interface details:
|
|||
- `streamTo` (optional): `"parent"` streams initial ACP run progress summaries back to the requester session as system events.
|
||||
- When available, accepted responses include `streamLogPath` pointing to a session-scoped JSONL log (`<sessionId>.acp-stream.jsonl`) you can tail for full relay history.
|
||||
|
||||
### Operator smoke test
|
||||
|
||||
Use this after a gateway deploy when you want a quick live check that ACP spawn
|
||||
is actually working end-to-end, not just passing unit tests.
|
||||
|
||||
Recommended gate:
|
||||
|
||||
1. Verify the deployed gateway version/commit on the target host.
|
||||
2. Confirm the deployed source includes the ACP lineage acceptance in
|
||||
`src/gateway/sessions-patch.ts` (`subagent:* or acp:* sessions`).
|
||||
3. Open a temporary ACPX bridge session to a live agent (for example
|
||||
`razor(main)` on `jpclawhq`).
|
||||
4. Ask that agent to call `sessions_spawn` with:
|
||||
- `runtime: "acp"`
|
||||
- `agentId: "codex"`
|
||||
- `mode: "run"`
|
||||
- task: `Reply with exactly LIVE-ACP-SPAWN-OK`
|
||||
5. Verify the agent reports:
|
||||
- `accepted=yes`
|
||||
- a real `childSessionKey`
|
||||
- no validator error
|
||||
6. Clean up the temporary ACPX bridge session.
|
||||
|
||||
Example prompt to the live agent:
|
||||
|
||||
```text
|
||||
Use the sessions_spawn tool now with runtime: "acp", agentId: "codex", and mode: "run".
|
||||
Set the task to: "Reply with exactly LIVE-ACP-SPAWN-OK".
|
||||
Then report only: accepted=<yes/no>; childSessionKey=<value or none>; error=<exact text or none>.
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Keep this smoke test on `mode: "run"` unless you are intentionally testing
|
||||
thread-bound persistent ACP sessions.
|
||||
- Do not require `streamTo: "parent"` for the basic gate. That path depends on
|
||||
requester/session capabilities and is a separate integration check.
|
||||
- Treat thread-bound `mode: "session"` testing as a second, richer integration
|
||||
pass from a real Discord thread or Telegram topic.
|
||||
|
||||
## Sandbox compatibility
|
||||
|
||||
ACP sessions currently run on the host runtime, not inside the OpenClaw sandbox.
|
||||
|
|
|
|||
|
|
@ -127,6 +127,39 @@ describe("AcpxRuntime", () => {
|
|||
expect(promptArgs).toContain("--approve-all");
|
||||
});
|
||||
|
||||
it("serializes text plus image attachments into ACP prompt blocks", async () => {
|
||||
const { runtime, logPath } = await createMockRuntimeFixture();
|
||||
|
||||
const handle = await runtime.ensureSession({
|
||||
sessionKey: "agent:codex:acp:with-image",
|
||||
agent: "codex",
|
||||
mode: "persistent",
|
||||
});
|
||||
|
||||
for await (const _event of runtime.runTurn({
|
||||
handle,
|
||||
text: "describe this image",
|
||||
attachments: [{ mediaType: "image/png", data: "aW1hZ2UtYnl0ZXM=" }],
|
||||
mode: "prompt",
|
||||
requestId: "req-image",
|
||||
})) {
|
||||
// Consume stream to completion so prompt logging is finalized.
|
||||
}
|
||||
|
||||
const logs = await readMockRuntimeLogEntries(logPath);
|
||||
const prompt = logs.find(
|
||||
(entry) =>
|
||||
entry.kind === "prompt" && String(entry.sessionName ?? "") === "agent:codex:acp:with-image",
|
||||
);
|
||||
expect(prompt).toBeDefined();
|
||||
|
||||
const stdinBlocks = JSON.parse(String(prompt?.stdinText ?? ""));
|
||||
expect(stdinBlocks).toEqual([
|
||||
{ type: "text", text: "describe this image" },
|
||||
{ type: "image", mimeType: "image/png", data: "aW1hZ2UtYnl0ZXM=" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("preserves leading spaces across streamed text deltas", async () => {
|
||||
const runtime = sharedFixture?.runtime;
|
||||
expect(runtime).toBeDefined();
|
||||
|
|
|
|||
|
|
@ -463,6 +463,18 @@ describe("gateway server sessions", () => {
|
|||
expect(spawnedPatched.ok).toBe(true);
|
||||
expect(spawnedPatched.payload?.entry.spawnedBy).toBe("agent:main:main");
|
||||
|
||||
const acpPatched = await rpcReq<{
|
||||
ok: true;
|
||||
entry: { spawnedBy?: string; spawnDepth?: number };
|
||||
}>(ws, "sessions.patch", {
|
||||
key: "agent:main:acp:child",
|
||||
spawnedBy: "agent:main:main",
|
||||
spawnDepth: 1,
|
||||
});
|
||||
expect(acpPatched.ok).toBe(true);
|
||||
expect(acpPatched.payload?.entry.spawnedBy).toBe("agent:main:main");
|
||||
expect(acpPatched.payload?.entry.spawnDepth).toBe(1);
|
||||
|
||||
const spawnedPatchedInvalidKey = await rpcReq(ws, "sessions.patch", {
|
||||
key: "agent:main:main",
|
||||
spawnedBy: "agent:main:main",
|
||||
|
|
|
|||
Loading…
Reference in New Issue