mirror of
https://github.com/harivansh-afk/clanker-agent.git
synced 2026-04-17 02:04:09 +00:00
move pi-mono into companion-cloud as apps/companion-os
- Copy all pi-mono source into apps/companion-os/ - Update Dockerfile to COPY pre-built binary instead of downloading from GitHub Releases - Update deploy-staging.yml to build pi from source (bun compile) before Docker build - Add apps/companion-os/** to path triggers - No more cross-repo dispatch needed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
0250f72976
579 changed files with 206942 additions and 0 deletions
198
packages/coding-agent/test/vercel-ai-stream.test.ts
Normal file
198
packages/coding-agent/test/vercel-ai-stream.test.ts
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import type { AgentSessionEvent } from "../src/core/agent-session.js";
|
||||
import {
|
||||
createVercelStreamListener,
|
||||
extractUserText,
|
||||
} from "../src/core/vercel-ai-stream.js";
|
||||
|
||||
describe("extractUserText", () => {
|
||||
it("extracts text from useChat v5+ format with parts", () => {
|
||||
const body = {
|
||||
messages: [
|
||||
{ role: "user", parts: [{ type: "text", text: "hello world" }] },
|
||||
],
|
||||
};
|
||||
expect(extractUserText(body)).toBe("hello world");
|
||||
});
|
||||
|
||||
it("extracts text from useChat v4 format with content string", () => {
|
||||
const body = {
|
||||
messages: [{ role: "user", content: "hello world" }],
|
||||
};
|
||||
expect(extractUserText(body)).toBe("hello world");
|
||||
});
|
||||
|
||||
it("extracts last user message when multiple messages present", () => {
|
||||
const body = {
|
||||
messages: [
|
||||
{ role: "user", parts: [{ type: "text", text: "first" }] },
|
||||
{ role: "assistant", parts: [{ type: "text", text: "response" }] },
|
||||
{ role: "user", parts: [{ type: "text", text: "second" }] },
|
||||
],
|
||||
};
|
||||
expect(extractUserText(body)).toBe("second");
|
||||
});
|
||||
|
||||
it("extracts text from simple gateway format", () => {
|
||||
expect(extractUserText({ text: "hello" })).toBe("hello");
|
||||
});
|
||||
|
||||
it("extracts text from prompt format", () => {
|
||||
expect(extractUserText({ prompt: "hello" })).toBe("hello");
|
||||
});
|
||||
|
||||
it("returns null for empty body", () => {
|
||||
expect(extractUserText({})).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null for empty messages array", () => {
|
||||
expect(extractUserText({ messages: [] })).toBeNull();
|
||||
});
|
||||
|
||||
it("prefers text field over messages", () => {
|
||||
const body = {
|
||||
text: "direct",
|
||||
messages: [
|
||||
{ role: "user", parts: [{ type: "text", text: "from messages" }] },
|
||||
],
|
||||
};
|
||||
expect(extractUserText(body)).toBe("direct");
|
||||
});
|
||||
});
|
||||
|
||||
describe("createVercelStreamListener", () => {
|
||||
function createMockResponse() {
|
||||
const chunks: string[] = [];
|
||||
let ended = false;
|
||||
return {
|
||||
writableEnded: false,
|
||||
write(data: string) {
|
||||
chunks.push(data);
|
||||
return true;
|
||||
},
|
||||
end() {
|
||||
ended = true;
|
||||
this.writableEnded = true;
|
||||
},
|
||||
chunks,
|
||||
get ended() {
|
||||
return ended;
|
||||
},
|
||||
} as any;
|
||||
}
|
||||
|
||||
function parseChunks(chunks: string[]): Array<object | string> {
|
||||
return chunks
|
||||
.filter((c) => c.startsWith("data: "))
|
||||
.map((c) => {
|
||||
const payload = c.replace(/^data: /, "").replace(/\n\n$/, "");
|
||||
try {
|
||||
return JSON.parse(payload);
|
||||
} catch {
|
||||
return payload;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
it("translates text streaming events", () => {
|
||||
const response = createMockResponse();
|
||||
const listener = createVercelStreamListener(response, "test-msg-id");
|
||||
|
||||
listener({ type: "agent_start" } as AgentSessionEvent);
|
||||
listener({
|
||||
type: "turn_start",
|
||||
turnIndex: 0,
|
||||
timestamp: Date.now(),
|
||||
} as AgentSessionEvent);
|
||||
listener({
|
||||
type: "message_update",
|
||||
message: {} as any,
|
||||
assistantMessageEvent: {
|
||||
type: "text_start",
|
||||
contentIndex: 0,
|
||||
partial: {} as any,
|
||||
},
|
||||
} as AgentSessionEvent);
|
||||
listener({
|
||||
type: "message_update",
|
||||
message: {} as any,
|
||||
assistantMessageEvent: {
|
||||
type: "text_delta",
|
||||
contentIndex: 0,
|
||||
delta: "hello",
|
||||
partial: {} as any,
|
||||
},
|
||||
} as AgentSessionEvent);
|
||||
listener({
|
||||
type: "message_update",
|
||||
message: {} as any,
|
||||
assistantMessageEvent: {
|
||||
type: "text_end",
|
||||
contentIndex: 0,
|
||||
content: "hello",
|
||||
partial: {} as any,
|
||||
},
|
||||
} as AgentSessionEvent);
|
||||
listener({
|
||||
type: "turn_end",
|
||||
turnIndex: 0,
|
||||
message: {} as any,
|
||||
toolResults: [],
|
||||
} as AgentSessionEvent);
|
||||
|
||||
const parsed = parseChunks(response.chunks);
|
||||
expect(parsed).toEqual([
|
||||
{ type: "start", messageId: "test-msg-id" },
|
||||
{ type: "start-step" },
|
||||
{ type: "text-start", id: "text_0" },
|
||||
{ type: "text-delta", id: "text_0", delta: "hello" },
|
||||
{ type: "text-end", id: "text_0" },
|
||||
{ type: "finish-step" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not write after response has ended", () => {
|
||||
const response = createMockResponse();
|
||||
const listener = createVercelStreamListener(response, "test-msg-id");
|
||||
|
||||
listener({ type: "agent_start" } as AgentSessionEvent);
|
||||
response.end();
|
||||
listener({
|
||||
type: "turn_start",
|
||||
turnIndex: 0,
|
||||
timestamp: Date.now(),
|
||||
} as AgentSessionEvent);
|
||||
|
||||
const parsed = parseChunks(response.chunks);
|
||||
expect(parsed).toEqual([{ type: "start", messageId: "test-msg-id" }]);
|
||||
});
|
||||
|
||||
it("ignores events outside the active prompt lifecycle", () => {
|
||||
const response = createMockResponse();
|
||||
const listener = createVercelStreamListener(response, "test-msg-id");
|
||||
|
||||
listener({
|
||||
type: "turn_start",
|
||||
turnIndex: 0,
|
||||
timestamp: Date.now(),
|
||||
} as AgentSessionEvent);
|
||||
listener({ type: "agent_start" } as AgentSessionEvent);
|
||||
listener({
|
||||
type: "turn_start",
|
||||
turnIndex: 0,
|
||||
timestamp: Date.now(),
|
||||
} as AgentSessionEvent);
|
||||
listener({ type: "agent_end", messages: [] } as AgentSessionEvent);
|
||||
listener({
|
||||
type: "turn_start",
|
||||
turnIndex: 1,
|
||||
timestamp: Date.now(),
|
||||
} as AgentSessionEvent);
|
||||
|
||||
const parsed = parseChunks(response.chunks);
|
||||
expect(parsed).toEqual([
|
||||
{ type: "start", messageId: "test-msg-id" },
|
||||
{ type: "start-step" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue