Merge branch 'chore/510-open-source-readiness'
This commit is contained in:
@@ -25,4 +25,4 @@ jobs:
|
|||||||
run: bunx biome check .
|
run: bunx biome check .
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: bun run --filter './packages/*' test
|
run: bun run test:ci
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"typecheck": "bunx tsc --build",
|
"typecheck": "bunx tsc --build",
|
||||||
"format": "biome format --write .",
|
"format": "biome format --write .",
|
||||||
"test": "bun run --filter './packages/*' test",
|
"test": "bun run --filter './packages/*' test",
|
||||||
|
"test:ci": "bun run --filter './packages/*' test:ci",
|
||||||
"changeset": "bunx changeset",
|
"changeset": "bunx changeset",
|
||||||
"version": "bunx changeset version",
|
"version": "bunx changeset version",
|
||||||
"release": "bun run build && bun test && node scripts/publish-all.mjs"
|
"release": "bun run build && bun test && node scripts/publish-all.mjs"
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
"yaml": "^2.8.4"
|
"yaml": "^2.8.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest run"
|
"test": "vitest run",
|
||||||
|
"test:ci": "vitest run"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "bun test"
|
"test": "bun test",
|
||||||
|
"test:ci": "bun test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uncaged/json-cas": "^0.5.2",
|
"@uncaged/json-cas": "^0.5.2",
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "bun test"
|
"test": "bun test",
|
||||||
|
"test:ci": "bun test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uncaged/json-cas": "^0.5.2",
|
"@uncaged/json-cas": "^0.5.2",
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|||||||
|
|
||||||
import { HermesAcpClient } from "../src/acp-client.js";
|
import { HermesAcpClient } from "../src/acp-client.js";
|
||||||
|
|
||||||
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
||||||
|
|
||||||
describe("handleSessionUpdate — helper extraction", () => {
|
describe("handleSessionUpdate — helper extraction", () => {
|
||||||
let client: HermesAcpClient;
|
let client: HermesAcpClient;
|
||||||
|
|
||||||
@@ -94,75 +92,4 @@ describe("handleSessionUpdate — helper extraction", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("HermesAcpClient", () => {
|
|
||||||
let client: HermesAcpClient;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
client = new HermesAcpClient();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await client.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(
|
|
||||||
"connect() returns a UUID sessionId",
|
|
||||||
async () => {
|
|
||||||
const sessionId = await client.connect(process.cwd());
|
|
||||||
expect(typeof sessionId).toBe("string");
|
|
||||||
expect(sessionId).toMatch(UUID_RE);
|
|
||||||
},
|
|
||||||
{ timeout: 2 * 60 * 1000 },
|
|
||||||
);
|
|
||||||
|
|
||||||
it(
|
|
||||||
"prompt() returns a non-empty text response",
|
|
||||||
async () => {
|
|
||||||
await client.connect(process.cwd());
|
|
||||||
const result = await client.prompt("Reply with exactly the word: PONG");
|
|
||||||
expect(typeof result.text).toBe("string");
|
|
||||||
expect(result.text.length).toBeGreaterThan(0);
|
|
||||||
expect(typeof result.sessionId).toBe("string");
|
|
||||||
expect(result.sessionId).toMatch(UUID_RE);
|
|
||||||
},
|
|
||||||
{ timeout: 2 * 60 * 1000 },
|
|
||||||
);
|
|
||||||
|
|
||||||
it(
|
|
||||||
"prompt() can be called twice on the same session (resume)",
|
|
||||||
async () => {
|
|
||||||
await client.connect(process.cwd());
|
|
||||||
|
|
||||||
const first = await client.prompt("Say the word ALPHA and nothing else.");
|
|
||||||
expect(first.text.length).toBeGreaterThan(0);
|
|
||||||
|
|
||||||
const second = await client.prompt("Now say the word BETA and nothing else.");
|
|
||||||
expect(second.text.length).toBeGreaterThan(0);
|
|
||||||
|
|
||||||
expect(first.sessionId).toBe(second.sessionId);
|
|
||||||
},
|
|
||||||
{ timeout: 2 * 60 * 1000 },
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO(#435): flaky — depends on live LLM; mock or move to integration suite
|
|
||||||
it.skip(
|
|
||||||
"prompt() collects structured messages including tool calls",
|
|
||||||
async () => {
|
|
||||||
await client.connect(process.cwd());
|
|
||||||
const result = await client.prompt("Run this command: echo TOOL_DETAIL_TEST");
|
|
||||||
expect(result.messages.length).toBeGreaterThan(0);
|
|
||||||
// Should have at least one tool message (the echo command)
|
|
||||||
const toolMessages = result.messages.filter((m) => m.role === "tool");
|
|
||||||
expect(toolMessages.length).toBeGreaterThan(0);
|
|
||||||
// Tool message should contain the output
|
|
||||||
const toolContent = toolMessages[0]?.content ?? "";
|
|
||||||
expect(toolContent).toContain("TOOL_DETAIL_TEST");
|
|
||||||
// Should have assistant messages with tool_calls
|
|
||||||
const assistantWithTools = result.messages.filter(
|
|
||||||
(m) => m.role === "assistant" && m.tool_calls !== null,
|
|
||||||
);
|
|
||||||
expect(assistantWithTools.length).toBeGreaterThan(0);
|
|
||||||
},
|
|
||||||
{ timeout: 2 * 60 * 1000 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
||||||
|
|
||||||
|
import { HermesAcpClient } from "../../src/acp-client.js";
|
||||||
|
|
||||||
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||||
|
|
||||||
|
describe("HermesAcpClient", () => {
|
||||||
|
let client: HermesAcpClient;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
client = new HermesAcpClient();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await client.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(
|
||||||
|
"connect() returns a UUID sessionId",
|
||||||
|
async () => {
|
||||||
|
const sessionId = await client.connect(process.cwd());
|
||||||
|
expect(typeof sessionId).toBe("string");
|
||||||
|
expect(sessionId).toMatch(UUID_RE);
|
||||||
|
},
|
||||||
|
{ timeout: 2 * 60 * 1000 },
|
||||||
|
);
|
||||||
|
|
||||||
|
it(
|
||||||
|
"prompt() returns a non-empty text response",
|
||||||
|
async () => {
|
||||||
|
await client.connect(process.cwd());
|
||||||
|
const result = await client.prompt("Reply with exactly the word: PONG");
|
||||||
|
expect(typeof result.text).toBe("string");
|
||||||
|
expect(result.text.length).toBeGreaterThan(0);
|
||||||
|
expect(typeof result.sessionId).toBe("string");
|
||||||
|
expect(result.sessionId).toMatch(UUID_RE);
|
||||||
|
},
|
||||||
|
{ timeout: 2 * 60 * 1000 },
|
||||||
|
);
|
||||||
|
|
||||||
|
it(
|
||||||
|
"prompt() can be called twice on the same session (resume)",
|
||||||
|
async () => {
|
||||||
|
await client.connect(process.cwd());
|
||||||
|
|
||||||
|
const first = await client.prompt("Say the word ALPHA and nothing else.");
|
||||||
|
expect(first.text.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
const second = await client.prompt("Now say the word BETA and nothing else.");
|
||||||
|
expect(second.text.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
expect(first.sessionId).toBe(second.sessionId);
|
||||||
|
},
|
||||||
|
{ timeout: 2 * 60 * 1000 },
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO(#435): flaky — depends on live LLM; mock or move to integration suite
|
||||||
|
it.skip(
|
||||||
|
"prompt() collects structured messages including tool calls",
|
||||||
|
async () => {
|
||||||
|
await client.connect(process.cwd());
|
||||||
|
const result = await client.prompt("Run this command: echo TOOL_DETAIL_TEST");
|
||||||
|
expect(result.messages.length).toBeGreaterThan(0);
|
||||||
|
const toolMessages = result.messages.filter((m) => m.role === "tool");
|
||||||
|
expect(toolMessages.length).toBeGreaterThan(0);
|
||||||
|
const toolContent = toolMessages[0]?.content ?? "";
|
||||||
|
expect(toolContent).toContain("TOOL_DETAIL_TEST");
|
||||||
|
const assistantWithTools = result.messages.filter(
|
||||||
|
(m) => m.role === "assistant" && m.tool_calls !== null,
|
||||||
|
);
|
||||||
|
expect(assistantWithTools.length).toBeGreaterThan(0);
|
||||||
|
},
|
||||||
|
{ timeout: 2 * 60 * 1000 },
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -18,7 +18,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "bun test"
|
"test": "bun test",
|
||||||
|
"test:ci": "bun test __tests__/*.test.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uncaged/json-cas": "^0.5.2",
|
"@uncaged/json-cas": "^0.5.2",
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "bun test"
|
"test": "bun test",
|
||||||
|
"test:ci": "bun test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uncaged/json-cas": "^0.5.2",
|
"@uncaged/json-cas": "^0.5.2",
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "bun test"
|
"test": "bun test",
|
||||||
|
"test:ci": "bun test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uncaged/workflow-protocol": "workspace:^",
|
"@uncaged/workflow-protocol": "workspace:^",
|
||||||
|
|||||||
Reference in New Issue
Block a user