Gateway: activity line, thread history paging, a project's threads, and a thread's subagents
This commit is contained in:
1 parent
6bc9cac6ca
commit
52210d0223
11 files changed
+474
-19
No files matched your search
@@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test";
|
||||
|
||||
import type { BoundedThread, ThreadShell } from "../src/t3/types.ts";
|
||||
import type { T3Host } from "../src/t3/host.ts";
|
||||
import { agentProviders, buildFeed, buildSummary, routeKind, summarize, SummaryCache, trimText } from "../src/views.ts";
|
||||
import { agentProviders, buildFeed, currentActivity, loadHistory, projectThreads, buildSummary, routeKind, summarize, SummaryCache, trimText } from "../src/views.ts";
|
||||
|
||||
const thread = (overrides: Partial<ThreadShell> = {}): ThreadShell => ({
|
||||
id: "thread-1",
|
||||
@@ -231,3 +231,58 @@ describe("agentProviders", () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("currentActivity", () => {
|
||||
const item = (type: string, status: string, extra: object = {}) => ({ type, status, startedAt: "2026-11-01T09:00:00.000Z", ...extra });
|
||||
|
||||
it("says the newest step still in progress, leaving out monitors", () => {
|
||||
expect(
|
||||
currentActivity([
|
||||
item("file_change", "running", { fileName: "gateway/src/host.ts" }),
|
||||
item("command_execution", "running", { input: "bun test" }),
|
||||
item("command_execution", "running", { input: "tail -f log", monitor: true }),
|
||||
item("assistant_message", "running"),
|
||||
]),
|
||||
).toEqual({ kind: "command", text: "bun test", startedAt: "2026-11-01T09:00:00.000Z" });
|
||||
expect(currentActivity([item("file_change", "running", { fileName: "gateway/src/host.ts" })])?.text).toBe("host.ts");
|
||||
});
|
||||
|
||||
it("is null once every step finished", () => {
|
||||
expect(currentActivity([item("command_execution", "completed", { input: "bun test" })])).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("thread history", () => {
|
||||
const messages = Array.from({ length: 45 }, (_, i) => message(i % 2 ? "assistant" : "user", `m${i}`));
|
||||
const window = { snapshotSequence: 1, historyCursor: "t3-cursor", hasMoreHistory: true, projection: { thread: thread(), messages, turnItems: [] } };
|
||||
const host = { bounded: () => Promise.resolve(window) } as unknown as T3Host;
|
||||
|
||||
it("pages back through the bounded window, then hands over to T3's cursor", async () => {
|
||||
expect(summarize(window as BoundedThread, thread()).earlierCursor).toBe("w25");
|
||||
const first = await loadHistory(host, thread(), "w25");
|
||||
expect([first.messages[0]?.text, first.messages.at(-1)?.text, first.earlierCursor]).toEqual(["m5", "m24", "w5"]);
|
||||
const second = await loadHistory(host, thread(), "w5");
|
||||
expect([second.messages.length, second.earlierCursor]).toEqual([5, "ht3-cursor"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("projectThreads", () => {
|
||||
it("lists a project's threads newest first, settled ones too, without subagents", () => {
|
||||
const host = {
|
||||
id: "Desktop",
|
||||
threads: new Map(
|
||||
[
|
||||
thread({ id: "old", updatedAt: "2026-11-01T08:00:00.000Z", settledOverride: "settled", status: "completed" }),
|
||||
thread({ id: "new", updatedAt: "2026-11-01T10:00:00.000Z" }),
|
||||
thread({ id: "sub", lineage: { relationshipToParent: "subagent", parentThreadId: "new" } }),
|
||||
thread({ id: "gone", archivedAt: "2026-11-01T09:30:00.000Z" }),
|
||||
thread({ id: "elsewhere", projectId: "project-2" }),
|
||||
].map((t) => [t.id, t]),
|
||||
),
|
||||
} as unknown as T3Host;
|
||||
expect(projectThreads(host, "project-1").map((row) => [row.threadId, row.phase, row.settled])).toEqual([
|
||||
["new", "running", false],
|
||||
["old", "completed", true],
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user