| 1 | import test from 'node:test'; |
| 2 | import assert from 'node:assert/strict'; |
| 3 | import { readFileSync } from 'node:fs'; |
| 4 | import { PetEngineTelemetry } from '../dist/core/pet-engine.js'; |
| 5 | import { PetNative } from '../dist/core/pet-native.js'; |
| 6 | import { compilePetTelemetry, encodePetJSONL } from '../dist/core/pet-telemetry.js'; |
| 7 | import { petDemoEvents } from '../dist/core/pet-demo.js'; |
| 8 | |
| 9 | const points = JSON.stringify(readFileSync(new URL('../public/whale-points.tsv', import.meta.url), 'utf8').trim().split('\n').map(row => row.trim().split(/\s+/).map(Number))); |
| 10 | test('action receipts distinguish tool names without inventing shell contents and expire', () => { |
| 11 | const e = new PetEngineTelemetry(); |
| 12 | for (const [i, name, kind] of [[0,'read_file','reading'],[1,'search_files','searching'],[2,'apply_patch','editing'],[3,'exec_command','executing'],[4,'run_tests','testing'],[5,'browser_navigate','browsing']]) { |
| 13 | e.observe({event:'turn_started'},i*1000); |
| 14 | e.observe({event:'tool_call_started',tool_call_id:'private-id',tool_name:name},i*1000); |
| 15 | const activity=e.activity(i*1000+100); |
| 16 | assert.equal(activity.kind,kind); assert.equal(activity.tool,name); |
| 17 | assert.equal(JSON.stringify(activity).includes('private-id'),false); |
| 18 | assert.equal(e.activity(i*1000+801).observed,false); |
| 19 | } |
| 20 | }); |
| 21 | test('waiting and concurrent action receipts stay bounded and clear after disconnect', () => { |
| 22 | const pet=new PetNative(points,'','[]',true); |
| 23 | pet.observeEngineBatch(JSON.stringify([{event:'tool_call_started',tool_call_id:'a',tool_name:'read_file'}, |
| 24 | ...Array.from({length:3},(_,i)=>({event:'agent_spawned',id:'private-'+i}))]),0); |
| 25 | pet.advanceEngine(200,true,false); |
| 26 | const frame=JSON.parse(pet.presentation()); |
| 27 | assert.equal(frame.activity.kind,'reading');assert.equal(frame.activity.parallel,3); |
| 28 | assert.equal(JSON.stringify(frame.activity).includes('private-'),false); |
| 29 | pet.observeEngine(JSON.stringify({event:'approval_required',id:'private'}),200); |
| 30 | pet.advanceEngine(600,true,true);assert.equal(JSON.parse(pet.presentation()).activity.kind,'waiting'); |
| 31 | const before=pet.recording(true);pet.presentation();assert.equal(pet.recording(true),before); |
| 32 | pet.disconnectEngine();assert.equal(JSON.parse(pet.presentation()).activity.observed,false); |
| 33 | }); |
| 34 | test('the incremental bucket range uses the same measured projection as full replay', () => { |
| 35 | const events = petDemoEvents(), full = compilePetTelemetry(events, 80_000); |
| 36 | for (let i = 0; i < full.length; i++) assert.deepEqual(compilePetTelemetry(events, 80_000, i)[0], full[i]); |
| 37 | }); |
| 38 | test('Engine pulses expire; a late failed completion tears at receipt time without rewriting history', () => { |
| 39 | const engine = new PetEngineTelemetry(); |
| 40 | engine.observe({ event: 'tool_call_started', tool_call_id: 'a', tool_name: 'exec_command' }, 0); |
| 41 | engine.observe({ event: 'tool_call_heartbeat' }, 300); |
| 42 | const first = engine.bucket(0); |
| 43 | assert.equal(first.channel, 'code'); assert.equal(first.activeMs[3], 300); |
| 44 | assert.equal(engine.bucket(3).observed, 0); |
| 45 | engine.observe({ event: 'tool_call_complete', tool_call_id: 'a', tool_name: 'exec_command', failed: true }, 5900); |
| 46 | assert.equal(engine.bucket(13).observed, 0); |
| 47 | assert.equal(engine.bucket(14).channel, 'error'); assert.equal(engine.bucket(14).errors, 1); |
| 48 | assert.deepEqual(engine.bucket(0), first); |
| 49 | }); |
| 50 | test('an authoritative waiting request escalates, accepted tape replays, and silence stays unknown', () => { |
| 51 | const pet = new PetNative(points, '', '[]', true); |
| 52 | pet.observeEngine(JSON.stringify({ event: 'approval_required', id: 'permission' }), 100); |
| 53 | for (let ms = 400; ms <= 30_000; ms += 400) pet.advanceEngine(ms, true, true); |
| 54 | assert.equal(JSON.parse(pet.snapshot()).needs, 'call'); |
| 55 | assert.equal(JSON.parse(pet.snapshot()).state.channel, 'human'); |
| 56 | const saved = JSON.parse(pet.recording()), replay = new PetNative(points, encodePetJSONL(saved.tape), JSON.stringify(saved.interactions)); |
| 57 | for (let i = 0; i < 900; i++) replay.step(1 / 30, true); |
| 58 | assert.deepEqual(JSON.parse(replay.snapshot()).state, JSON.parse(pet.snapshot()).state); |
| 59 | assert.equal(JSON.parse(replay.snapshot()).digest, JSON.parse(pet.snapshot()).digest); |
| 60 | pet.observeEngine(JSON.stringify({ event: 'turn_complete' }), 30_100); |
| 61 | pet.advanceEngine(32_000, true, false); |
| 62 | assert.equal(JSON.parse(pet.snapshot()).state.observed, 0); |
| 63 | }); |
| 64 | test('Engine worker boundary rejects payloads, invalid clocks and oversized active sets', () => { |
| 65 | const e = new PetEngineTelemetry(); |
| 66 | assert.throws(() => e.observe({ event: 'response_delta', index: 0, delta: 'private' }, 0)); |
| 67 | assert.throws(() => e.observe({ event: 'response_delta', index: 0, channel: 'fabricated' }, 0)); |
| 68 | e.observe({ event: 'thinking_started', index: 0 }, 100); |
| 69 | assert.throws(() => e.observe({ event: 'thinking_complete', index: 0 }, 90)); |
| 70 | for (let i = 1; i < 256; i++) e.observe({ event: 'thinking_started', index: i }, 100); |
| 71 | assert.throws(() => e.observe({ event: 'thinking_started', index: 257 }, 100)); |
| 72 | }); |
| 73 | |
| 74 | test('failed shared metadata batches accept no partial spans or private input', () => { |
| 75 | const control = new PetNative(points, '', '[]', true), pet = new PetNative(points, '', '[]', true); |
| 76 | assert.throws(() => pet.observeEngineBatch(JSON.stringify([{event:'thinking_started',index:1},{event:'response_delta',index:1,content:'PRIVATE'}]),0)); |
| 77 | pet.advanceEngine(400,true,false); control.advanceEngine(400,true,false); |
| 78 | assert.deepEqual(JSON.parse(pet.recording(true)),JSON.parse(control.recording(true))); |
| 79 | pet.observeEngineBatch(JSON.stringify([{event:'thinking_started',index:1},{event:'response_delta',index:1,channel:'reasoning'}]),400); |
| 80 | pet.advanceEngine(800,true,false); |
| 81 | assert.equal(JSON.parse(pet.snapshot()).state.channel,'reasoning'); |
| 82 | }); |
| 83 | |
| 84 | test('successive shared batches preserve active and waiting coverage exactly', () => { |
| 85 | let shared = new PetEngineTelemetry(); |
| 86 | const direct = new PetEngineTelemetry(); |
| 87 | for (const [at, event, waiting] of [ |
| 88 | [0, {event:'tool_call_started', tool_call_id:'build', tool_name:'exec_command'}, false], |
| 89 | [300, {event:'tool_call_heartbeat'}, false], |
| 90 | [600, {event:'tool_call_heartbeat'}, false], |
| 91 | [800, {event:'tool_call_complete', tool_call_id:'build'}, false], |
| 92 | [900, {event:'approval_required', id:'permission'}, true], |
| 93 | [1200, {event:'agent_spawned', id:'worker'}, true], |
| 94 | [1500, {event:'agent_progress', id:'worker', worker_status:'running'}, true], |
| 95 | ]) { |
| 96 | shared = shared.clone(); |
| 97 | for (const engine of [shared, direct]) { |
| 98 | engine.observe(event, at); |
| 99 | engine.confirmWaiting(at, waiting); |
| 100 | } |
| 101 | for (let bucket=0; bucket<=Math.floor(at/400); bucket++) |
| 102 | assert.deepEqual(shared.bucket(bucket), direct.bucket(bucket)); |
| 103 | } |
| 104 | }); |
| 105 | |
| 106 | test('shared presentation and still projections cannot change physics, clock, journal or score', () => { |
| 107 | const pet = new PetNative(points, '', '[]', true), control = new PetNative(points, '', '[]', true); |
| 108 | for(let i=1;i<=120;i++) { |
| 109 | pet.advanceEngine(i*1000/30,true,false); control.advanceEngine(i*1000/30,true,false); |
| 110 | const before=pet.recording(true), frame=JSON.parse(pet.presentation()); |
| 111 | assert.equal(frame.points.length,980);assert.equal(frame.still.points.length,980); |
| 112 | for(let view=0;view<3;view++) pet.presentation(); |
| 113 | assert.equal(pet.recording(true),before); |
| 114 | } |
| 115 | assert.deepEqual(JSON.parse(pet.recording(true)),JSON.parse(control.recording(true))); |
| 116 | }); |
| 117 |