acp: add regression coverage and smoke-test docs

This commit is contained in:
Mariano Belinky
2026-03-09 21:32:55 +01:00
committed by mbelinky
parent 4aebff78bc
commit d06138ac3d
3 changed files with 86 additions and 0 deletions

View File

@@ -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.

View File

@@ -127,6 +127,40 @@ 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();

View File

@@ -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",