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%
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