-
Notifications
You must be signed in to change notification settings - Fork 32
test: session utilities — isDefaultTitle, fromRow/toRow, createObservationMask #540
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,174 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { describe, test, expect } from "bun:test" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { SessionCompaction } from "../../src/session/compaction" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { MessageV2 } from "../../src/session/message-v2" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function makeToolPart(overrides: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool?: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input?: Record<string, any> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output?: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status?: "completed" | "running" | "error" | "pending" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }): MessageV2.ToolPart { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const status = overrides.status ?? "completed" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const base = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: "part_1" as any, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sessionID: "sess_1" as any, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messageID: "msg_1" as any, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "tool" as const, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callID: "call_1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool: overrides.tool ?? "bash", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (status === "completed") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...base, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: "completed", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: overrides.input ?? {}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output: overrides.output ?? "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title: "test", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata: {}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| time: { start: 1, end: 2 }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } as MessageV2.ToolPart | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (status === "running") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...base, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: "running", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: overrides.input ?? {}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| time: { start: 1 }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } as MessageV2.ToolPart | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (status === "error") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...base, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: "error", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: overrides.input ?? {}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: "something failed", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| time: { start: 1, end: 2 }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } as MessageV2.ToolPart | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...base, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state: { status: "pending" }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } as MessageV2.ToolPart | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe("SessionCompaction.createObservationMask", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("produces correct mask for a completed tool with normal output", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const part = makeToolPart({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool: "read", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: { file_path: "/src/index.ts" }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output: "line1\nline2\nline3", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mask = SessionCompaction.createObservationMask(part) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("[Tool output cleared") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("read(") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("file_path:") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("returned 3 lines") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // "line1\nline2\nline3" = 17 bytes ASCII | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("17 B") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // First line fingerprint | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain('— "line1"') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("handles empty output gracefully", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const part = makeToolPart({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool: "bash", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: { command: "echo hello" }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output: "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mask = SessionCompaction.createObservationMask(part) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("returned 1 lines") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("0 B") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // No fingerprint for empty output | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).not.toContain('— "') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("counts lines and bytes correctly for multi-line output", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Generate enough lines to exceed 1 KB | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const lines = Array.from({ length: 200 }, (_, i) => `output line number ${i}`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const output = lines.join("\n") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const part = makeToolPart({ tool: "grep", output }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mask = SessionCompaction.createObservationMask(part) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("returned 200 lines") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const expectedBytes = Buffer.byteLength(output, "utf8") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(expectedBytes).toBeGreaterThan(1024) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toMatch(/\d+\.\d+ KB/) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain('— "output line number 0"') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("formats bytes as KB and MB for larger outputs", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ~1 MB output | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const bigOutput = "x".repeat(1024 * 1024 + 512) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const part = makeToolPart({ tool: "cat", output: bigOutput }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mask = SessionCompaction.createObservationMask(part) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toMatch(/1\.0 MB/) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("truncates long args without breaking surrogate pairs", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Create input with a value containing emoji (surrogate pairs in JS) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // U+1F600 = 😀 is represented as \uD83D\uDE00 in UTF-16 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const emoji = "😀" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Build a value string where the emoji sits right near the truncation boundary | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const padding = "a".repeat(70) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const part = makeToolPart({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool: "write", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: { content: padding + emoji + "after" }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mask = SessionCompaction.createObservationMask(part) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The mask should not contain a lone high surrogate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Verify the args portion is well-formed by checking the whole mask is valid | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toBeDefined() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Ensure no lone surrogates by round-tripping through TextEncoder | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const encoded = new TextEncoder().encode(mask) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const decoded = new TextDecoder().decode(encoded) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(decoded).toBe(mask) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+120
to
+139
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Surrogate-pair test does not prove truncation path is executed. This test currently validates UTF-8 round-trip safety, but it can still pass when no truncation occurs. Add an assertion that truncation happened (e.g., ellipsis present / suffix removed). Proposed strengthening test("truncates long args without breaking surrogate pairs", () => {
@@
- const padding = "a".repeat(70)
+ const padding = "a".repeat(120)
@@
const mask = SessionCompaction.createObservationMask(part)
@@
+ expect(mask).toContain("…")
+ expect(mask).not.toContain("after")
// The mask should not contain a lone high surrogate📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("uses empty input for pending tool parts", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const part = makeToolPart({ tool: "bash", status: "pending" }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mask = SessionCompaction.createObservationMask(part) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("bash()") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("returned 1 lines") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("0 B") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("uses input from running tool parts", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const part = makeToolPart({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool: "edit", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: { file: "main.ts" }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: "running", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mask = SessionCompaction.createObservationMask(part) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("edit(file:") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Running parts have no output | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("0 B") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test("uses input from error tool parts", () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const part = makeToolPart({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool: "bash", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| input: { command: "rm -rf /" }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: "error", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mask = SessionCompaction.createObservationMask(part) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("bash(command:") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(mask).toContain("0 B") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| import { describe, test, expect } from "bun:test" | ||
| import { Session } from "../../src/session" | ||
|
|
||
| describe("Session.isDefaultTitle", () => { | ||
| test("recognises parent session titles", () => { | ||
| expect(Session.isDefaultTitle("New session - 2024-01-15T08:30:00.000Z")).toBe(true) | ||
| }) | ||
|
|
||
| test("recognises child session titles", () => { | ||
| expect(Session.isDefaultTitle("Child session - 2024-06-01T23:59:59.999Z")).toBe(true) | ||
| }) | ||
|
|
||
| test("rejects arbitrary strings", () => { | ||
| expect(Session.isDefaultTitle("My custom title")).toBe(false) | ||
| expect(Session.isDefaultTitle("")).toBe(false) | ||
| expect(Session.isDefaultTitle("hello world")).toBe(false) | ||
| }) | ||
|
|
||
| test("rejects prefix without valid ISO timestamp", () => { | ||
| expect(Session.isDefaultTitle("New session - not-a-date")).toBe(false) | ||
| expect(Session.isDefaultTitle("New session - ")).toBe(false) | ||
| expect(Session.isDefaultTitle("New session - 2024-01-15")).toBe(false) | ||
| }) | ||
|
|
||
| test("rejects partial prefix matches", () => { | ||
| expect(Session.isDefaultTitle("New session 2024-01-15T08:30:00.000Z")).toBe(false) | ||
| expect(Session.isDefaultTitle("New session -2024-01-15T08:30:00.000Z")).toBe(false) | ||
| }) | ||
|
|
||
| test("rejects titles with extra content after timestamp", () => { | ||
| expect(Session.isDefaultTitle("New session - 2024-01-15T08:30:00.000Z extra")).toBe(false) | ||
| }) | ||
| }) | ||
|
|
||
| describe("Session.fromRow / toRow", () => { | ||
| test("roundtrip preserves fields with full summary", () => { | ||
| const info: Session.Info = { | ||
| id: "sess_123" as any, | ||
| slug: "abc-def", | ||
| projectID: "proj_456" as any, | ||
| workspaceID: "ws_789" as any, | ||
| directory: "/home/user/project", | ||
| parentID: "sess_parent" as any, | ||
| title: "Test session", | ||
| version: "0.5.13", | ||
| summary: { | ||
| additions: 10, | ||
| deletions: 5, | ||
| files: 3, | ||
| diffs: [{ file: "src/index.ts", additions: 10, deletions: 5 }] as any, | ||
| }, | ||
| share: { url: "https://example.com/share/123" }, | ||
| revert: "snapshot_abc" as any, | ||
| permission: "plan" as any, | ||
| time: { | ||
| created: 1700000000000, | ||
| updated: 1700001000000, | ||
| compacting: 1700002000000, | ||
| archived: 1700003000000, | ||
| }, | ||
| } | ||
|
|
||
| const row = Session.toRow(info) | ||
| const restored = Session.fromRow(row as any) | ||
|
|
||
| expect(restored.id).toBe(info.id) | ||
| expect(restored.slug).toBe(info.slug) | ||
| expect(restored.projectID).toBe(info.projectID) | ||
| expect(restored.workspaceID).toBe(info.workspaceID) | ||
| expect(restored.directory).toBe(info.directory) | ||
| expect(restored.parentID).toBe(info.parentID) | ||
| expect(restored.title).toBe(info.title) | ||
| expect(restored.version).toBe(info.version) | ||
| expect(restored.summary).toEqual(info.summary) | ||
| expect(restored.share).toEqual(info.share) | ||
| expect(restored.revert).toBe(info.revert) | ||
| expect(restored.permission).toBe(info.permission) | ||
| expect(restored.time.created).toBe(info.time.created) | ||
| expect(restored.time.updated).toBe(info.time.updated) | ||
| expect(restored.time.compacting).toBe(info.time.compacting) | ||
| expect(restored.time.archived).toBe(info.time.archived) | ||
| }) | ||
|
|
||
| test("fromRow produces undefined summary when all summary columns are null", () => { | ||
| const row = { | ||
| id: "sess_1", | ||
| slug: "x", | ||
| project_id: "p1", | ||
| workspace_id: null, | ||
| directory: "/tmp", | ||
| parent_id: null, | ||
| title: "t", | ||
| version: "1", | ||
| summary_additions: null, | ||
| summary_deletions: null, | ||
| summary_files: null, | ||
| summary_diffs: null, | ||
| share_url: null, | ||
| revert: null, | ||
| permission: null, | ||
| time_created: 1, | ||
| time_updated: 2, | ||
| time_compacting: null, | ||
| time_archived: null, | ||
| } | ||
| const info = Session.fromRow(row as any) | ||
| expect(info.summary).toBeUndefined() | ||
| expect(info.share).toBeUndefined() | ||
| expect(info.revert).toBeUndefined() | ||
| expect(info.parentID).toBeUndefined() | ||
| expect(info.workspaceID).toBeUndefined() | ||
| expect(info.time.compacting).toBeUndefined() | ||
| expect(info.time.archived).toBeUndefined() | ||
| }) | ||
|
|
||
| test("fromRow constructs summary when at least one summary column is non-null", () => { | ||
| const row = { | ||
| id: "sess_2", | ||
| slug: "y", | ||
| project_id: "p2", | ||
| workspace_id: null, | ||
| directory: "/tmp", | ||
| parent_id: null, | ||
| title: "t", | ||
| version: "1", | ||
| summary_additions: 5, | ||
| summary_deletions: null, | ||
| summary_files: null, | ||
| summary_diffs: null, | ||
| share_url: null, | ||
| revert: null, | ||
| permission: null, | ||
| time_created: 1, | ||
| time_updated: 2, | ||
| time_compacting: null, | ||
| time_archived: null, | ||
| } | ||
| const info = Session.fromRow(row as any) | ||
| expect(info.summary).toBeDefined() | ||
| expect(info.summary!.additions).toBe(5) | ||
| expect(info.summary!.deletions).toBe(0) | ||
| expect(info.summary!.files).toBe(0) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: AltimateAI/altimate-code
Length of output: 3210
Pending-state fixture in
makeToolPartdoes not matchToolStatePendingschema contract.The fixture at line 60 only sets
status: "pending"but omits the requiredinputandrawfields. The force-cast withas MessageV2.ToolPartbypasses type validation and can mask schema regressions.Proposed fix
return { ...base, - state: { status: "pending" }, + state: { + status: "pending", + input: overrides.input ?? {}, + raw: "", + }, } as MessageV2.ToolPart📝 Committable suggestion
🤖 Prompt for AI Agents