SPB Git

spb/khaelor Public

KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.

TypeScript 82.9% HTML 14.9% CSS 1.1% JavaScript 0.7%
9.7 KB · 342 lines typescript
Raw Blame History
1/**2 * KHAELOR3 * File: tests/session/fixtures.ts4 * Description: Test fixtures — one realistic sample for every durable and ephemeral event type.5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910import { EVENT_SCHEMA_VERSION } from "../../src/session/events.js";11import type {12  DurableEvent,13  DurableEventInput,14  EphemeralEvent,15  EphemeralEventInput,16} from "../../src/session/events.js";17import { ulid } from "../../src/shared/index.js";1819export const SESSION_ID = "01TESTSESSION0000000000000";2021export function envelope<T extends DurableEventInput>(seq: number, input: T): DurableEvent {22  return {23    v: EVENT_SCHEMA_VERSION,24    id: ulid(),25    sessionId: SESSION_ID,26    seq,27    ts: 1_700_000_000_000 + seq,28    type: input.type,29    payload: input.payload,30  } as DurableEvent;31}3233export function ephemeralEnvelope<T extends EphemeralEventInput>(input: T): EphemeralEvent {34  return {35    id: ulid(),36    sessionId: SESSION_ID,37    ts: 1_700_000_000_000,38    type: input.type,39    payload: input.payload,40  } as EphemeralEvent;41}4243/** One input per durable event type — 32 total, matching DURABLE_EVENT_TYPES. */44export function sampleDurableInputs(): DurableEventInput[] {45  return [46    {47      type: "session.started",48      payload: {49        title: "Fix the session store",50        projectHash: "abc123",51        workingDirectory: "/home/user/project",52        gitBranch: "main",53        model: "claude-sonnet-4-5",54        auxModel: "claude-haiku-4-5",55        khaelorVersion: "0.1.0",56      },57    },58    {59      type: "session.resumed",60      payload: {61        khaelorVersion: "0.1.0",62        replayedSeq: 42,63        model: "claude-sonnet-4-5",64        toolNames: ["read", "write", "edit", "grep", "glob", "bash", "process"],65      },66    },67    { type: "session.renamed", payload: { title: "New title" } },68    {69      type: "session.model-changed",70      payload: { from: "claude-sonnet-4-5", to: "claude-opus-4-6", reason: "user" },71    },72    {73      type: "git.baseline-recorded",74      payload: {75        when: "session-start",76        baseline: {77          branch: "main",78          dirtyFiles: ["src/a.ts"],79          untrackedFiles: ["notes.txt"],80          diffHash: "deadbeef",81        },82      },83    },84    {85      type: "user.message-created",86      payload: {87        text: "Fix the bug in @src/store.ts",88        mentions: [{ path: "src/store.ts", range: { start: 10, end: 20 } }],89      },90    },91    { type: "user.steering-queued", payload: { text: "Also add tests" } },92    {93      type: "user.steering-injected",94      payload: { queuedEventId: "01QUEUED000000000000000000", seam: "post-tool-batch", afterSeq: 7 },95    },96    { type: "user.interrupted", payload: { scope: "turn", pendingToolUseIds: ["toolu_1"] } },97    {98      type: "model.request-started",99      payload: {100        requestId: "req_1",101        model: "claude-sonnet-4-5",102        purpose: "main",103        contextStats: {104          estimatedInputTokens: 1234,105          sections: [{ name: "system", estimatedTokens: 400 }],106        },107      },108    },109    {110      type: "model.text-block-completed",111      payload: { requestId: "req_1", blockIndex: 1, text: "I found the bug." },112    },113    {114      type: "model.thinking-block-completed",115      payload: { requestId: "req_1", blockIndex: 0, thinking: "Let me check…", signature: "sig_abc" },116    },117    {118      type: "tool.requested",119      payload: {120        requestId: "req_1",121        blockIndex: 2,122        toolUseId: "toolu_1",123        toolName: "read",124        input: { path: "src/store.ts" },125      },126    },127    {128      type: "model.response-completed",129      payload: {130        requestId: "req_1",131        stopReason: "tool_use",132        usage: { inputTokens: 100, outputTokens: 50, cacheReadTokens: 80, cacheWriteTokens: 20 },133        durationMs: 1500,134      },135    },136    {137      type: "model.request-failed",138      payload: {139        requestId: "req_2",140        kind: "retryable",141        message: "overloaded",142        status: 529,143        retriesExhausted: false,144      },145    },146    {147      type: "permission.requested",148      payload: {149        permissionRequestId: "perm_1",150        toolUseId: "toolu_2",151        capability: "process.execute",152        descriptor: "Run `npm install` in ~/project",153        suggestion: { capability: "process.execute", pattern: "npm install *" },154      },155    },156    { type: "permission.granted", payload: { permissionRequestId: "perm_1", scope: "once" } },157    {158      type: "permission.denied",159      payload: { permissionRequestId: "perm_2", source: "user", feedback: "Do not run that." },160    },161    { type: "tool.approved", payload: { toolUseId: "toolu_1", via: "policy-allow" } },162    { type: "tool.started", payload: { toolUseId: "toolu_1", toolName: "read" } },163    {164      type: "tool.completed",165      payload: {166        toolUseId: "toolu_1",167        modelText: "1  const x = 1;",168        spillFile: "/home/user/.khaelor/spill/toolu_1.txt",169        durationMs: 12,170        ui: { kind: "read", summary: "Read src/store.ts", matchCount: 0 },171      },172    },173    {174      type: "tool.failed",175      payload: {176        toolUseId: "toolu_2",177        modelText: "File not found: src/missing.ts",178        errorKind: "not-found",179        durationMs: 3,180      },181    },182    {183      type: "tool.cancelled",184      payload: {185        toolUseId: "toolu_3",186        reason: "interrupted",187        modelText: "[Tool execution cancelled by user]",188      },189    },190    {191      type: "file.read",192      payload: {193        path: "src/store.ts",194        range: { start: 1, end: 50 },195        bytes: 2048,196        mtimeMs: 1_700_000_000_000,197        toolUseId: "toolu_1",198      },199    },200    {201      type: "file.modified",202      payload: {203        path: "src/store.ts",204        operation: "edit",205        diffStats: { added: 3, removed: 1 },206        diff: "--- a/src/store.ts\n+++ b/src/store.ts\n",207        toolUseId: "toolu_4",208      },209    },210    {211      type: "process.started",212      payload: {213        processId: "proc_1",214        pid: 4242,215        command: "npm run dev",216        cwd: "/home/user/project",217        name: "dev-server",218        toolUseId: "toolu_5",219      },220    },221    {222      type: "process.exited",223      payload: { processId: "proc_1", exitCode: 0, cause: "exited", durationMs: 60000 },224    },225    {226      type: "context.pruned",227      payload: {228        toolUseIds: ["toolu_1"],229        placeholder: "[Old tool result removed to save context]",230        tokensReclaimedEstimate: 900,231      },232    },233    {234      type: "context.compacted",235      payload: {236        checkpointYaml: "objective: fix the bug\ncompleted: []\n",237        cut: { fromSeq: 2, toSeq: 9 },238        trigger: "proactive-token-budget",239        tokensBefore: 150000,240        summaryModel: "claude-haiku-4-5",241      },242    },243    {244      type: "task.verification-requested",245      payload: { attempt: 1, detectedChecks: ["npm test"], withheldCandidateSeq: 30 },246    },247    {248      type: "task.completed",249      payload: {250        evidence: {251          objective: "Fix the bug",252          changedFiles: ["src/store.ts"],253          checks: [{ command: "npm test", exitCode: 0, summary: "12 passed", durationMs: 2100 }],254          unresolvedIssues: [],255        },256      },257    },258    {259      type: "task.failed",260      payload: { reason: "iteration-budget-exhausted", detail: "gave up after 50 iterations" },261    },262    { type: "phase.entered", payload: { phase: "design", via: "design-submitted" } },263    {264      type: "phase.artifact",265      payload: {266        artifactId: "art_1",267        artifact: {268          goal: "fix the renderer leak",269          filesTouched: ["src/tui/render.ts"],270          approach: "ring buffer instead of growing array",271          risks: ["scrollback regression"],272          verification: "vitest run tests/tui",273          outOfScope: ["theming"],274        },275      },276    },277    {278      type: "phase.approved",279      payload: { phase: "design", approvedBy: "auto-policy", artifactId: "art_1" },280    },281    { type: "phase.rejected", payload: { phase: "design", reason: "too broad", artifactId: "art_1" } },282    {283      type: "verify.result",284      payload: {285        check: "typecheck",286        command: "npx tsc --noEmit",287        ok: false,288        exitCode: 2,289        output: "src/a.ts(42): TS2345",290        durationMs: 4200,291      },292    },293    {294      type: "memory.written",295      payload: {296        section: "conventions",297        entry: "errors flow through Result<T, KError>",298        confidence: "high",299        toolUseId: "toolu_9",300      },301    },302    {303      type: "subtask.created",304      payload: {305        taskId: "t1",306        description: "add rate limiting",307        childSessionId: "01CHILD",308        worktreePath: "/repo/.khaelor/worktrees/t1",309        branch: "khaelor/t1",310      },311    },312    {313      type: "subtask.completed",314      payload: {315        taskId: "t1",316        outcome: "done",317        diffStats: { added: 42, removed: 13 },318        verifyOk: true,319        detail: "rate limiting added",320      },321    },322  ];323}324325/** One input per ephemeral event type — 6 total, matching EPHEMERAL_EVENT_TYPES. */326export function sampleEphemeralInputs(): EphemeralEventInput[] {327  return [328    { type: "model.text-delta", payload: { requestId: "req_1", blockIndex: 1, text: "Hel" } },329    { type: "model.thinking-delta", payload: { requestId: "req_1", blockIndex: 0, text: "hmm" } },330    {331      type: "model.tool-call-started",332      payload: { requestId: "req_1", blockIndex: 2, toolUseId: "toolu_1", toolName: "read" },333    },334    {335      type: "model.tool-input-delta",336      payload: { requestId: "req_1", blockIndex: 2, toolUseId: "toolu_1", partialJson: '{"pa' },337    },338    { type: "tool.output", payload: { toolUseId: "toolu_1", chunk: "line 1\n" } },339    { type: "process.output", payload: { processId: "proc_1", stream: "stdout", chunk: "ready\n" } },340  ];341}342