CI: fix command-reply payload typing
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
import type { AgentMeta, AgentSpec } from "./types.js";
|
import type { AgentParseResult, AgentSpec } from "./types.js";
|
||||||
|
|
||||||
const GEMINI_BIN = "gemini";
|
const GEMINI_BIN = "gemini";
|
||||||
export const GEMINI_IDENTITY_PREFIX =
|
export const GEMINI_IDENTITY_PREFIX =
|
||||||
@@ -8,10 +8,13 @@ export const GEMINI_IDENTITY_PREFIX =
|
|||||||
|
|
||||||
// Gemini CLI currently prints plain text; --output json is flaky across versions, so we
|
// Gemini CLI currently prints plain text; --output json is flaky across versions, so we
|
||||||
// keep parsing minimal and let MEDIA token stripping happen later in the pipeline.
|
// keep parsing minimal and let MEDIA token stripping happen later in the pipeline.
|
||||||
function parseGeminiOutput(raw: string): { text?: string; meta?: AgentMeta } {
|
function parseGeminiOutput(raw: string): AgentParseResult {
|
||||||
const trimmed = raw.trim();
|
const trimmed = raw.trim();
|
||||||
const text = trimmed || undefined;
|
const text = trimmed || undefined;
|
||||||
return { texts: text ? [text] : undefined, meta: undefined };
|
return {
|
||||||
|
texts: text ? [text] : undefined,
|
||||||
|
meta: undefined,
|
||||||
|
} satisfies AgentParseResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const geminiSpec: AgentSpec = {
|
export const geminiSpec: AgentSpec = {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ describe("runCommandReply", () => {
|
|||||||
it("injects claude flags and identity prefix", async () => {
|
it("injects claude flags and identity prefix", async () => {
|
||||||
const captures: ReplyPayload[] = [];
|
const captures: ReplyPayload[] = [];
|
||||||
const runner = makeRunner({ stdout: "ok" }, captures);
|
const runner = makeRunner({ stdout: "ok" }, captures);
|
||||||
const { payload } = await runCommandReply({
|
const { payloads } = await runCommandReply({
|
||||||
reply: {
|
reply: {
|
||||||
mode: "command",
|
mode: "command",
|
||||||
command: ["claude", "{{Body}}"],
|
command: ["claude", "{{Body}}"],
|
||||||
@@ -83,6 +83,7 @@ describe("runCommandReply", () => {
|
|||||||
enqueue: enqueueImmediate,
|
enqueue: enqueueImmediate,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const payload = payloads?.[0];
|
||||||
expect(payload?.text).toBe("ok");
|
expect(payload?.text).toBe("ok");
|
||||||
const finalArgv = captures[0].argv as string[];
|
const finalArgv = captures[0].argv as string[];
|
||||||
expect(finalArgv).toContain("--output-format");
|
expect(finalArgv).toContain("--output-format");
|
||||||
@@ -192,7 +193,7 @@ describe("runCommandReply", () => {
|
|||||||
const runner = vi.fn(async () => {
|
const runner = vi.fn(async () => {
|
||||||
throw { stdout: "partial output here", killed: true, signal: "SIGKILL" };
|
throw { stdout: "partial output here", killed: true, signal: "SIGKILL" };
|
||||||
});
|
});
|
||||||
const { payload, meta } = await runCommandReply({
|
const { payloads, meta } = await runCommandReply({
|
||||||
reply: {
|
reply: {
|
||||||
mode: "command",
|
mode: "command",
|
||||||
command: ["echo", "hi"],
|
command: ["echo", "hi"],
|
||||||
@@ -208,6 +209,7 @@ describe("runCommandReply", () => {
|
|||||||
commandRunner: runner,
|
commandRunner: runner,
|
||||||
enqueue: enqueueImmediate,
|
enqueue: enqueueImmediate,
|
||||||
});
|
});
|
||||||
|
const payload = payloads?.[0];
|
||||||
expect(payload?.text).toContain("Command timed out after 1s");
|
expect(payload?.text).toContain("Command timed out after 1s");
|
||||||
expect(payload?.text).toContain("partial output");
|
expect(payload?.text).toContain("partial output");
|
||||||
expect(meta.killed).toBe(true);
|
expect(meta.killed).toBe(true);
|
||||||
@@ -217,7 +219,7 @@ describe("runCommandReply", () => {
|
|||||||
const runner = vi.fn(async () => {
|
const runner = vi.fn(async () => {
|
||||||
throw { stdout: "", killed: true, signal: "SIGKILL" };
|
throw { stdout: "", killed: true, signal: "SIGKILL" };
|
||||||
});
|
});
|
||||||
const { payload } = await runCommandReply({
|
const { payloads } = await runCommandReply({
|
||||||
reply: {
|
reply: {
|
||||||
mode: "command",
|
mode: "command",
|
||||||
command: ["echo", "hi"],
|
command: ["echo", "hi"],
|
||||||
@@ -234,6 +236,7 @@ describe("runCommandReply", () => {
|
|||||||
commandRunner: runner,
|
commandRunner: runner,
|
||||||
enqueue: enqueueImmediate,
|
enqueue: enqueueImmediate,
|
||||||
});
|
});
|
||||||
|
const payload = payloads?.[0];
|
||||||
expect(payload?.text).toContain("(cwd: /tmp/work)");
|
expect(payload?.text).toContain("(cwd: /tmp/work)");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -244,7 +247,7 @@ describe("runCommandReply", () => {
|
|||||||
const runner = makeRunner({
|
const runner = makeRunner({
|
||||||
stdout: `hi\nMEDIA:${tmp}\nMEDIA:https://example.com/img.jpg`,
|
stdout: `hi\nMEDIA:${tmp}\nMEDIA:https://example.com/img.jpg`,
|
||||||
});
|
});
|
||||||
const { payload } = await runCommandReply({
|
const { payloads } = await runCommandReply({
|
||||||
reply: {
|
reply: {
|
||||||
mode: "command",
|
mode: "command",
|
||||||
command: ["echo", "hi"],
|
command: ["echo", "hi"],
|
||||||
@@ -261,6 +264,7 @@ describe("runCommandReply", () => {
|
|||||||
commandRunner: runner,
|
commandRunner: runner,
|
||||||
enqueue: enqueueImmediate,
|
enqueue: enqueueImmediate,
|
||||||
});
|
});
|
||||||
|
const payload = payloads?.[0];
|
||||||
expect(payload?.mediaUrls).toEqual(["https://example.com/img.jpg"]);
|
expect(payload?.mediaUrls).toEqual(["https://example.com/img.jpg"]);
|
||||||
await fs.unlink(tmp);
|
await fs.unlink(tmp);
|
||||||
});
|
});
|
||||||
@@ -318,7 +322,7 @@ describe("runCommandReply", () => {
|
|||||||
const runner = makeRunner({
|
const runner = makeRunner({
|
||||||
stdout: '{"result":"","duration_ms":50,"total_cost_usd":0.001}',
|
stdout: '{"result":"","duration_ms":50,"total_cost_usd":0.001}',
|
||||||
});
|
});
|
||||||
const { payload } = await runCommandReply({
|
const { payloads } = await runCommandReply({
|
||||||
reply: {
|
reply: {
|
||||||
mode: "command",
|
mode: "command",
|
||||||
command: ["claude", "{{Body}}"],
|
command: ["claude", "{{Body}}"],
|
||||||
@@ -335,6 +339,7 @@ describe("runCommandReply", () => {
|
|||||||
enqueue: enqueueImmediate,
|
enqueue: enqueueImmediate,
|
||||||
});
|
});
|
||||||
// Should NOT contain raw JSON - empty result should produce fallback message
|
// Should NOT contain raw JSON - empty result should produce fallback message
|
||||||
|
const payload = payloads?.[0];
|
||||||
expect(payload?.text).not.toContain('{"result"');
|
expect(payload?.text).not.toContain('{"result"');
|
||||||
expect(payload?.text).toContain("command produced no output");
|
expect(payload?.text).toContain("command produced no output");
|
||||||
});
|
});
|
||||||
@@ -343,7 +348,7 @@ describe("runCommandReply", () => {
|
|||||||
const runner = makeRunner({
|
const runner = makeRunner({
|
||||||
stdout: '{"text":"","duration_ms":50}',
|
stdout: '{"text":"","duration_ms":50}',
|
||||||
});
|
});
|
||||||
const { payload } = await runCommandReply({
|
const { payloads } = await runCommandReply({
|
||||||
reply: {
|
reply: {
|
||||||
mode: "command",
|
mode: "command",
|
||||||
command: ["claude", "{{Body}}"],
|
command: ["claude", "{{Body}}"],
|
||||||
@@ -360,6 +365,7 @@ describe("runCommandReply", () => {
|
|||||||
enqueue: enqueueImmediate,
|
enqueue: enqueueImmediate,
|
||||||
});
|
});
|
||||||
// Empty text should produce fallback message, not raw JSON
|
// Empty text should produce fallback message, not raw JSON
|
||||||
|
const payload = payloads?.[0];
|
||||||
expect(payload?.text).not.toContain('{"text"');
|
expect(payload?.text).not.toContain('{"text"');
|
||||||
expect(payload?.text).toContain("command produced no output");
|
expect(payload?.text).toContain("command produced no output");
|
||||||
});
|
});
|
||||||
@@ -368,7 +374,7 @@ describe("runCommandReply", () => {
|
|||||||
const runner = makeRunner({
|
const runner = makeRunner({
|
||||||
stdout: '{"result":"hello world","duration_ms":50}',
|
stdout: '{"result":"hello world","duration_ms":50}',
|
||||||
});
|
});
|
||||||
const { payload } = await runCommandReply({
|
const { payloads } = await runCommandReply({
|
||||||
reply: {
|
reply: {
|
||||||
mode: "command",
|
mode: "command",
|
||||||
command: ["claude", "{{Body}}"],
|
command: ["claude", "{{Body}}"],
|
||||||
@@ -384,6 +390,7 @@ describe("runCommandReply", () => {
|
|||||||
commandRunner: runner,
|
commandRunner: runner,
|
||||||
enqueue: enqueueImmediate,
|
enqueue: enqueueImmediate,
|
||||||
});
|
});
|
||||||
|
const payload = payloads?.[0];
|
||||||
expect(payload?.text).toBe("hello world");
|
expect(payload?.text).toBe("hello world");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -401,7 +401,7 @@ export async function runCommandReply(
|
|||||||
}
|
}
|
||||||
|
|
||||||
verboseLog(`Command auto-reply meta: ${JSON.stringify(meta)}`);
|
verboseLog(`Command auto-reply meta: ${JSON.stringify(meta)}`);
|
||||||
return { payloads, payload: payloads[0], meta };
|
return { payloads, meta };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const elapsed = Date.now() - started;
|
const elapsed = Date.now() - started;
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -430,7 +430,7 @@ export async function runCommandReply(
|
|||||||
? `${baseMsg}\n\nPartial output before timeout:\n${partialSnippet}`
|
? `${baseMsg}\n\nPartial output before timeout:\n${partialSnippet}`
|
||||||
: baseMsg;
|
: baseMsg;
|
||||||
return {
|
return {
|
||||||
payload: { text },
|
payloads: [{ text }],
|
||||||
meta: {
|
meta: {
|
||||||
durationMs: elapsed,
|
durationMs: elapsed,
|
||||||
queuedMs,
|
queuedMs,
|
||||||
|
|||||||
@@ -329,8 +329,7 @@ export async function getReplyFromConfig(
|
|||||||
timeoutSeconds,
|
timeoutSeconds,
|
||||||
commandRunner,
|
commandRunner,
|
||||||
});
|
});
|
||||||
const payloadArray =
|
const payloadArray = runResult.payloads ?? [];
|
||||||
runResult.payloads ?? (runResult.payload ? [runResult.payload] : []);
|
|
||||||
const meta = runResult.meta;
|
const meta = runResult.meta;
|
||||||
const normalizedPayloads =
|
const normalizedPayloads =
|
||||||
payloadArray.length === 1 ? payloadArray[0] : payloadArray;
|
payloadArray.length === 1 ? payloadArray[0] : payloadArray;
|
||||||
|
|||||||
Reference in New Issue
Block a user