deadbyte-mcp 0.15.22 → 0.15.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MANIFEST.SHA256 +58 -36
- package/PROVENANCE.json +11 -10
- package/README.txt +18 -7
- package/RELEASE.json +26 -39
- package/SBOM.cdx.json +3 -3
- package/TEST-MATRIX.json +32 -86
- package/package.json +4 -3
- package/proof/commander-closure-20260928/DIFFERENTIAL-CHECKPOINT.json +111 -0
- package/scripts/activate-skill-creator-1.1.1.mjs +46 -0
- package/scripts/build-advanced-pcb-skill.mjs +208 -0
- package/scripts/commander-real-performance-benchmark-r47.mjs +12 -3
- package/scripts/diagnose-skill-tree.mjs +8 -0
- package/scripts/npm-release-r48.mjs +252 -0
- package/scripts/pcb-skill-src-SKILL.md.txt +67 -0
- package/scripts/pcb-skill-src-bringup-evidence.md.txt +22 -0
- package/scripts/pcb-skill-src-constraint-model.md.txt +17 -0
- package/scripts/pcb-skill-src-emc-mechanical-dfm.md.txt +17 -0
- package/scripts/pcb-skill-src-high-speed-layout.md.txt +27 -0
- package/scripts/pcb-skill-src-kicad-native.md.txt +30 -0
- package/scripts/pcb-skill-src-kicad_pcb_probe.py.txt +159 -0
- package/scripts/pcb-skill-src-pcb_constraint_gate.py.txt +76 -0
- package/scripts/pcb-skill-src-power-integrity.md.txt +15 -0
- package/scripts/prepare-skill-creator-1.1.1.mjs +113 -0
- package/scripts/test-skill-creator-semver-matrix.mjs +30 -0
- package/scripts/test-skill-creator-semver.mjs +22 -0
- package/src/atomic-mutation.mjs +12 -4
- package/src/coding-mcp-tools.mjs +15 -1
- package/src/coding-output-contracts.mjs +9 -3
- package/src/commander-office-reader-r48.mjs +36 -3
- package/src/commander-pdf-r48.mjs +36 -1
- package/src/commander-wire-adapter-r47.mjs +56 -2
- package/src/deadbyte-cli.mjs +70 -15
- package/src/host-plane.mjs +10 -31
- package/src/machine-fs-mcp-tools.mjs +7 -1
- package/src/machine-fs-output-contracts.mjs +6 -0
- package/src/mcp-server.mjs +14 -2
- package/src/output-contracts.mjs +6 -4
- package/src/remote-live-log.mjs +189 -35
- package/src/stdio-protocol-guard.mjs +71 -0
- package/src/tool-audit.mjs +1 -1
- package/src/version.mjs +1 -1
- package/test/atomic-mutation.test.mjs +61 -0
- package/test/coding-mcp-unknown-effect.test.mjs +42 -0
- package/test/commander-biff-reference-r48.test.mjs +38 -0
- package/test/commander-core-bridge-r47.test.mjs +19 -0
- package/test/commander-office-reader-r48.test.mjs +18 -2
- package/test/commander-pdf-r48.test.mjs +35 -0
- package/test/commander-protocol-adversarial-r48.test.mjs +166 -0
- package/test/commander-real-parity-r47.test.mjs +37 -3
- package/test/commander-schema-differential-r48.test.mjs +1 -1
- package/test/commander-wire-contract-r47.test.mjs +32 -0
- package/test/full-max-catalog-r48.test.mjs +1 -1
- package/test/machine-fs-mcp-tools.test.mjs +12 -0
- package/test/output-schema.test.mjs +20 -1
- package/test/r48-release-contract.test.mjs +27 -21
- package/test/remote-live-log-r37.test.mjs +106 -7
- package/test/remote-session-cli.test.mjs +40 -0
- package/test/tool-audit.test.mjs +17 -0
- package/test/tunnel-stdio-compat.test.mjs +3 -1
package/src/deadbyte-cli.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import { openRemoteResultJournal, parseRemoteResultCursor } from './remote-resul
|
|
|
13
13
|
import { DEADBYTE_VERSION } from './version.mjs';
|
|
14
14
|
import { parseActivePointer } from './release-slot.mjs';
|
|
15
15
|
import { printDoctor } from './doctor-r45.mjs';
|
|
16
|
-
import { createLiveTimelineTracker, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine } from './remote-live-log.mjs';
|
|
16
|
+
import { createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine } from './remote-live-log.mjs';
|
|
17
17
|
import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
|
|
18
18
|
|
|
19
19
|
const REMOTE_COMMANDS=new Set([
|
|
@@ -46,6 +46,8 @@ function printHelp(){
|
|
|
46
46
|
` npx -y ${exact} mcp # stdio MCP server for local clients`,
|
|
47
47
|
` npx -y ${exact} remote # unified foreground device session + live timeline`,
|
|
48
48
|
` npx -y ${exact} remote --resume <cursor> # replay missed results, then reconnect`,
|
|
49
|
+
` npx -y ${exact} remote --trace # show trace/span identity without payload detail`,
|
|
50
|
+
` npx -y ${exact} remote --filter <expr> # presentation filter: tool/category/status/trace`,
|
|
49
51
|
` npx -y ${exact} remote status`,
|
|
50
52
|
` npx -y ${exact} remote restart`,
|
|
51
53
|
` npx -y ${exact} supervisor status`,
|
|
@@ -71,6 +73,21 @@ async function readActiveRuntimeBinding(root){
|
|
|
71
73
|
try{return parseActivePointer(await readFile(file,'utf8'));}
|
|
72
74
|
catch(error){if(error?.code==='ENOENT') return null;throw new Error(`active runtime pointer invalid: ${boundedSingleLine(error?.message||error,512)}`);}
|
|
73
75
|
}
|
|
76
|
+
async function readLiveRootPaths(root){
|
|
77
|
+
const active=await readActiveRuntimeBinding(root);
|
|
78
|
+
if(!active) return Object.freeze({});
|
|
79
|
+
const file=path.join(active.generation_root,'coding-policy.json');
|
|
80
|
+
let raw;
|
|
81
|
+
try{raw=JSON.parse(await readFile(file,'utf8'));}
|
|
82
|
+
catch(error){if(error?.code==='ENOENT') return Object.freeze({});throw new Error(`live root policy invalid: ${boundedSingleLine(error?.message||error,512)}`);}
|
|
83
|
+
if(raw?.schema!=='deadbyte.coding-policy.v1'||!raw.roots||typeof raw.roots!=='object'||Array.isArray(raw.roots)) throw new Error('live root policy schema invalid');
|
|
84
|
+
const roots={};
|
|
85
|
+
for(const [id,spec] of Object.entries(raw.roots)){
|
|
86
|
+
if(!/^[A-Za-z0-9._-]{1,64}$/.test(id)||!spec||typeof spec!=='object'||typeof spec.path!=='string'||!path.isAbsolute(spec.path)) continue;
|
|
87
|
+
roots[id]=path.resolve(spec.path);
|
|
88
|
+
}
|
|
89
|
+
return Object.freeze(roots);
|
|
90
|
+
}
|
|
74
91
|
async function reportVersionCoherence(root,{displayMode='ascii'}={}){
|
|
75
92
|
const active=await readActiveRuntimeBinding(root);
|
|
76
93
|
if(!active||active.version===DEADBYTE_VERSION) return active;
|
|
@@ -227,7 +244,22 @@ async function snapshotRemoteLogOffsets(root){
|
|
|
227
244
|
}
|
|
228
245
|
return offsets;
|
|
229
246
|
}
|
|
230
|
-
|
|
247
|
+
function timelineFilterEvent(timeline){
|
|
248
|
+
if(!timeline) return null;
|
|
249
|
+
if(timeline.event_type==='tool.call') return {
|
|
250
|
+
event_type:'tool.request',request_id:timeline.request_id,tool:timeline.tool,args:timeline.arguments??{}
|
|
251
|
+
};
|
|
252
|
+
return {
|
|
253
|
+
event_type:'tool.result',request_id:timeline.request_id,tool:timeline.tool,
|
|
254
|
+
outcome:{
|
|
255
|
+
is_error:timeline.status==='error',
|
|
256
|
+
...(Number.isFinite(Number(timeline.duration_ms))?{duration_ms:Number(timeline.duration_ms)}:{}),
|
|
257
|
+
...(timeline.side_effect_state?{side_effect_state:timeline.side_effect_state}:{}),
|
|
258
|
+
...(timeline.reason?{reason:timeline.reason}:{})
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTracker=null,timelineFilter=null,timelineWriter=null,verbose=false,displayMode='unicode'}={}){
|
|
231
263
|
process.stdout.write(verbose
|
|
232
264
|
? '[LOG] Verbose live timeline. Full sanitized request detail enabled; Ctrl+C disconnects and revokes authority.\n'
|
|
233
265
|
: '[LOG] Human live timeline. Transport IN/OUT hidden; full replay/audit detail remains on disk. Ctrl+C disconnects and revokes authority.\n');
|
|
@@ -244,10 +276,14 @@ async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTra
|
|
|
244
276
|
? await journal.recordCall(timeline)
|
|
245
277
|
: await journal.recordTerminal(timeline);
|
|
246
278
|
if(recorded?.duplicate) continue;
|
|
279
|
+
const accepted=timelineFilter&&timeline?timelineFilter.accept(timelineFilterEvent(timeline)):true;
|
|
247
280
|
const formatted=formatLogLine(file,line,{activityTracker,sequence:recorded?.event?.seq??null,verbose,displayMode});
|
|
248
|
-
if(!formatted) continue;
|
|
281
|
+
if(!formatted||!accepted) continue;
|
|
249
282
|
const output=Array.isArray(formatted)?formatted:[formatted];
|
|
250
|
-
for(const item of output) if(item)
|
|
283
|
+
for(const item of output) if(item){
|
|
284
|
+
if(timelineWriter) timelineWriter.write(item,{event_type:timeline?.event_type??null,request_id:timeline?.request_id??null});
|
|
285
|
+
else process.stdout.write(`${item}\n`);
|
|
286
|
+
}
|
|
251
287
|
}
|
|
252
288
|
}
|
|
253
289
|
try{await sleep(250,null,{signal});}catch(error){if(error?.name!=='AbortError') throw error;}
|
|
@@ -341,7 +377,7 @@ async function releaseRemoteSession(ctx,marker,{displayMode='unicode'}={}){
|
|
|
341
377
|
const same=current?.schema==='deadbyte.remote-session.v2'&¤t.owner_pid===marker.owner_pid&¤t.session_nonce===marker.session_nonce;
|
|
342
378
|
if(same) await rm(remoteSessionMarker(ctx.root),{force:true});
|
|
343
379
|
}
|
|
344
|
-
async function runRemoteSession({resumeCursor=null,verbose=false}={}){
|
|
380
|
+
async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,filterExpression=''}={}){
|
|
345
381
|
const display=prepareRemoteConsole();
|
|
346
382
|
const displayMode=display.mode;
|
|
347
383
|
const ctx=await remoteContext();
|
|
@@ -353,7 +389,10 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
|
|
|
353
389
|
const resultJournal=await openRemoteResultJournal({
|
|
354
390
|
filePath:remoteResultJournalPath(ctx.root,marker.result_session_id),sessionId:marker.result_session_id,maxEntries:2048
|
|
355
391
|
});
|
|
356
|
-
const
|
|
392
|
+
const rootPaths=await readLiveRootPaths(ctx.root);
|
|
393
|
+
const activityTracker=createLiveTimelineTracker({verbose,displayMode,rootPaths,trace});
|
|
394
|
+
const timelineFilter=filterExpression?createLiveTimelineFilter(filterExpression):null;
|
|
395
|
+
const timelineWriter=createLiveTimelineWriter({stream:process.stdout,displayMode,rewrite:!verbose});
|
|
357
396
|
const aborter=new AbortController();
|
|
358
397
|
const abort=()=>aborter.abort();
|
|
359
398
|
const watchStdin=!process.stdin.isTTY;
|
|
@@ -366,7 +405,7 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
|
|
|
366
405
|
process.stdout.write(`${formatRemoteLifecycle('device_start',{displayMode})}\n`);
|
|
367
406
|
process.stdout.write(`${formatRemoteLifecycle(resumeCursor!==null?'session_restored':'session_new',{displayMode})}\n`);
|
|
368
407
|
const logOffsets=await snapshotRemoteLogOffsets(ctx.root);
|
|
369
|
-
logTask=tailRemoteLogs(ctx.root,aborter.signal,{journal:resultJournal,offsets:logOffsets,activityTracker,verbose,displayMode});
|
|
408
|
+
logTask=tailRemoteLogs(ctx.root,aborter.signal,{journal:resultJournal,offsets:logOffsets,activityTracker,timelineFilter,timelineWriter,verbose,displayMode});
|
|
370
409
|
process.stdout.write('[BOOT] Starting local + remote runtime...\n');
|
|
371
410
|
const preflight=invokeController(ctx,'status',{capture:true,allowFailure:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
372
411
|
let startText=preflight.stdout||'';
|
|
@@ -415,6 +454,7 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
|
|
|
415
454
|
const wasAborted=aborter.signal.aborted;
|
|
416
455
|
aborter.abort();
|
|
417
456
|
if(logTask) await logTask.catch(()=>{});
|
|
457
|
+
timelineWriter.flush();
|
|
418
458
|
for(const line of activityTracker.reconcileInterrupted('session_end')) process.stdout.write(`${line}\n`);
|
|
419
459
|
process.stdout.write(`${activityTracker.summary()}\n`);
|
|
420
460
|
process.removeListener('SIGINT',abort);process.removeListener('SIGTERM',abort);
|
|
@@ -426,14 +466,29 @@ async function runRemoteSession({resumeCursor=null,verbose=false}={}){
|
|
|
426
466
|
}
|
|
427
467
|
}
|
|
428
468
|
async function runRemote(args){
|
|
429
|
-
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
469
|
+
let verbose=false,trace=false,filterExpression='',resumeCursor=null;
|
|
470
|
+
const positional=[];
|
|
471
|
+
for(let index=0;index<args.length;index++){
|
|
472
|
+
const arg=args[index];
|
|
473
|
+
if(arg==='--verbose'||arg==='-v'){verbose=true;continue;}
|
|
474
|
+
if(arg==='--trace'){trace=true;continue;}
|
|
475
|
+
if(arg==='--filter'){
|
|
476
|
+
if(filterExpression) throw new Error('remote --filter may be specified once');
|
|
477
|
+
if(index+1>=args.length) throw new Error('remote --filter requires an expression');
|
|
478
|
+
filterExpression=args[++index];continue;
|
|
479
|
+
}
|
|
480
|
+
if(arg==='--resume'){
|
|
481
|
+
if(resumeCursor!==null) throw new Error('remote --resume may be specified once');
|
|
482
|
+
if(index+1>=args.length) throw new Error('remote --resume requires a cursor');
|
|
483
|
+
resumeCursor=args[++index];continue;
|
|
484
|
+
}
|
|
485
|
+
positional.push(arg);
|
|
486
|
+
}
|
|
487
|
+
const presentation=verbose||trace||Boolean(filterExpression)||resumeCursor!==null;
|
|
488
|
+
if(positional.length===0){await runRemoteSession({resumeCursor,verbose,trace,filterExpression});return;}
|
|
489
|
+
if(presentation) throw new Error('remote presentation flags are valid only for foreground session');
|
|
490
|
+
if(positional.length>1) throw new Error('remote accepts one controller command');
|
|
491
|
+
const command=positional[0];if(!REMOTE_COMMANDS.has(command)) throw new Error(`unsupported remote command '${command}'`);
|
|
437
492
|
const ctx=await remoteContext();
|
|
438
493
|
await reportVersionCoherence(ctx.root,{displayMode:'ascii'});
|
|
439
494
|
const result=invokeController(ctx,command,{allowFailure:true});
|
package/src/host-plane.mjs
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
import { createHash
|
|
2
|
-
import { constants as fsConstants } from 'node:fs';
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
3
2
|
import {
|
|
4
3
|
lstat,
|
|
5
4
|
mkdir,
|
|
6
|
-
open,
|
|
7
5
|
readFile,
|
|
8
6
|
realpath,
|
|
9
|
-
rename,
|
|
10
|
-
rm,
|
|
11
7
|
stat
|
|
12
8
|
} from 'node:fs/promises';
|
|
13
9
|
import path from 'node:path';
|
|
14
10
|
import { spawn } from 'node:child_process';
|
|
15
11
|
import { decodeUtf8Strict } from './text-policy.mjs';
|
|
16
12
|
import { isInside, validatePortableRelativePath } from './path-policy.mjs';
|
|
13
|
+
import { atomicMutation } from './atomic-mutation.mjs';
|
|
17
14
|
|
|
18
15
|
export const HOST_POLICY_SCHEMA = 'deadbyte.host-policy.v1';
|
|
19
16
|
export const HOST_POLICY_SCHEMA_V2 = 'deadbyte.host-policy.v2';
|
|
@@ -315,32 +312,14 @@ export async function hostFileWrite(policy, { rootId, relativePath, content, enc
|
|
|
315
312
|
const bytes = decodeWriteContent(content, encoding);
|
|
316
313
|
if (bytes.length > policy.max_write_bytes) throw new Error(`host write exceeds ${policy.max_write_bytes} byte limit`);
|
|
317
314
|
|
|
318
|
-
const before = await
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
await handle.writeFile(bytes);
|
|
327
|
-
await handle.sync();
|
|
328
|
-
await handle.close();
|
|
329
|
-
handle = null;
|
|
330
|
-
|
|
331
|
-
const raceCheck = await currentTargetState(lexical);
|
|
332
|
-
checkExpectedState(raceCheck, expectedSha256);
|
|
333
|
-
await authorityCheck();
|
|
334
|
-
await rename(temp, lexical);
|
|
335
|
-
} catch (error) {
|
|
336
|
-
if (handle) await handle.close().catch(() => {});
|
|
337
|
-
await rm(temp, { force: true }).catch(() => {});
|
|
338
|
-
throw error;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const after = await currentTargetState(lexical);
|
|
342
|
-
const expectedAfter = sha256Bytes(bytes);
|
|
343
|
-
if (!after.exists || after.sha256 !== expectedAfter) throw new Error('host write post-commit hash mismatch');
|
|
315
|
+
const { before, after } = await atomicMutation({
|
|
316
|
+
target: lexical,
|
|
317
|
+
bytes,
|
|
318
|
+
expected: expectedSha256,
|
|
319
|
+
readState: currentTargetState,
|
|
320
|
+
assertExpected: checkExpectedState,
|
|
321
|
+
authorityCheck
|
|
322
|
+
});
|
|
344
323
|
return {
|
|
345
324
|
previous_sha256: before.sha256,
|
|
346
325
|
sha256: after.sha256,
|
|
@@ -10,7 +10,13 @@ const rel = z.string().min(1).max(2048);
|
|
|
10
10
|
const hex64 = z.string().regex(/^[0-9a-f]{64}$/);
|
|
11
11
|
const expectedFile = z.string().regex(/^(?:absent|[0-9a-f]{64})$/);
|
|
12
12
|
const ok = (schema, value) => machineFsStructuredResult(schema, value, false);
|
|
13
|
-
const fail = (schema, error) =>
|
|
13
|
+
const fail = (schema, error) => {
|
|
14
|
+
const unknown=error?.side_effect_state==='unknown'&&error?.outcome==='FAILED_AFTER_UNKNOWN_EFFECT';
|
|
15
|
+
return machineFsStructuredResult(schema, {
|
|
16
|
+
status:unknown?'failed':'denied',error:String(error),
|
|
17
|
+
...(unknown?{outcome:error.outcome,side_effect_state:'unknown',expected_after_sha256:error.expected_after_sha256}: {})
|
|
18
|
+
}, true);
|
|
19
|
+
};
|
|
14
20
|
|
|
15
21
|
export function registerMachineFsTools(server, { runtime }) {
|
|
16
22
|
if (!server || !runtime) throw new Error('machine fs tool registration requires server/runtime');
|
|
@@ -7,6 +7,11 @@ const pathText = z.string().min(1).max(2048);
|
|
|
7
7
|
const statusOk = z.enum(['ok','denied','failed']);
|
|
8
8
|
const statusCommit = z.enum(['committed','denied','failed']);
|
|
9
9
|
const error = z.string().min(1).max(4096).optional();
|
|
10
|
+
const unknownEffect = {
|
|
11
|
+
outcome: z.literal('FAILED_AFTER_UNKNOWN_EFFECT').optional(),
|
|
12
|
+
side_effect_state: z.literal('unknown').optional(),
|
|
13
|
+
expected_after_sha256: hex64.optional()
|
|
14
|
+
};
|
|
10
15
|
|
|
11
16
|
export const machineFsStatOutputSchema = z.strictObject({
|
|
12
17
|
status: statusOk, error,
|
|
@@ -38,6 +43,7 @@ export const machineFsReadOutputSchema = z.strictObject({
|
|
|
38
43
|
|
|
39
44
|
const commitBase = {
|
|
40
45
|
status: statusCommit, error,
|
|
46
|
+
...unknownEffect,
|
|
41
47
|
root_id: z.string().min(1).max(64).optional(),
|
|
42
48
|
receipt_id: receiptId.optional(), receipt_sha256: hex64.optional(), request_sha256: hex64.optional()
|
|
43
49
|
};
|
package/src/mcp-server.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { McpServer } from '@modelcontextprotocol/server';
|
|
7
7
|
import { serveStdio } from '@modelcontextprotocol/server/stdio';
|
|
8
|
+
import { DeadbyteStdioServerTransport } from './stdio-protocol-guard.mjs';
|
|
8
9
|
import * as z from 'zod/v4';
|
|
9
10
|
import { executeProof } from './executor.mjs';
|
|
10
11
|
import { CONTAINED_TRANSFORM_OPERATIONS, executeContainedTransform } from './contained-executor.mjs';
|
|
@@ -529,7 +530,18 @@ serveStdio(() => {
|
|
|
529
530
|
});
|
|
530
531
|
return textResult(hostFileWriteOutputSchema, { status: 'committed', root_id, path: relativePath, ...result });
|
|
531
532
|
} catch (error) {
|
|
532
|
-
|
|
533
|
+
const unknown = error?.outcome === 'FAILED_AFTER_UNKNOWN_EFFECT'
|
|
534
|
+
&& error?.side_effect_state === 'unknown'
|
|
535
|
+
&& /^[0-9a-f]{64}$/.test(error?.expected_after_sha256 ?? '');
|
|
536
|
+
return textResult(hostFileWriteOutputSchema, {
|
|
537
|
+
status: unknown ? 'failed' : 'denied',
|
|
538
|
+
error: String(error),
|
|
539
|
+
...(unknown ? {
|
|
540
|
+
outcome: error.outcome,
|
|
541
|
+
side_effect_state: error.side_effect_state,
|
|
542
|
+
expected_after_sha256: error.expected_after_sha256
|
|
543
|
+
} : {})
|
|
544
|
+
}, true);
|
|
533
545
|
}
|
|
534
546
|
}
|
|
535
547
|
);
|
|
@@ -618,4 +630,4 @@ serveStdio(() => {
|
|
|
618
630
|
}
|
|
619
631
|
|
|
620
632
|
return server;
|
|
621
|
-
}, { legacy: 'reject' });
|
|
633
|
+
}, { legacy: 'reject', transport: new DeadbyteStdioServerTransport() });
|
package/src/output-contracts.mjs
CHANGED
|
@@ -217,13 +217,15 @@ export const hostFileReadOutputSchema = statusContract({
|
|
|
217
217
|
});
|
|
218
218
|
|
|
219
219
|
export const hostFileWriteOutputSchema = statusContract({
|
|
220
|
-
statuses: ['committed', 'denied'],
|
|
220
|
+
statuses: ['committed', 'denied', 'failed'],
|
|
221
221
|
shape: {
|
|
222
222
|
root_id: z.string().min(1).max(64).optional(), path: z.string().min(1).max(512).optional(),
|
|
223
223
|
previous_sha256: hex64.nullable().optional(), sha256: hex64.optional(),
|
|
224
|
-
bytes: z.number().int().min(0).optional(), created: z.boolean().optional()
|
|
224
|
+
bytes: z.number().int().min(0).optional(), created: z.boolean().optional(),
|
|
225
|
+
outcome: z.literal('FAILED_AFTER_UNKNOWN_EFFECT').optional(),
|
|
226
|
+
side_effect_state: z.literal('unknown').optional(), expected_after_sha256: hex64.optional()
|
|
225
227
|
},
|
|
226
|
-
successStatuses: ['committed'], successRequired: ['root_id', 'path', 'previous_sha256', 'sha256', 'bytes', 'created'], errorStatuses: ['denied']
|
|
228
|
+
successStatuses: ['committed'], successRequired: ['root_id', 'path', 'previous_sha256', 'sha256', 'bytes', 'created'], errorStatuses: ['denied', 'failed']
|
|
227
229
|
});
|
|
228
230
|
|
|
229
231
|
export const hostExecOutputSchema = z.strictObject({
|
|
@@ -265,4 +267,4 @@ export function structuredToolResult(schema, object, isError = false) {
|
|
|
265
267
|
structuredContent: parsed.data,
|
|
266
268
|
content: [{ type: 'text', text: JSON.stringify(parsed.data) }]
|
|
267
269
|
};
|
|
268
|
-
}
|
|
270
|
+
}
|
package/src/remote-live-log.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
import { canonicalJson } from './canonical-json.mjs';
|
|
2
3
|
import { sanitizeAuditArgs } from './tool-audit.mjs';
|
|
3
4
|
import { remoteDisplayText, REMOTE_DISPLAY_MODES } from './remote-console.mjs';
|
|
4
5
|
|
|
5
|
-
const SAFE_ARG_KEYS=Object.freeze(['root_id','path','pattern','command_id','profile_id','session_id','goal_id','task_id','step_id','mode']);
|
|
6
|
+
const SAFE_ARG_KEYS=Object.freeze(['root_id','path','pattern','command_id','profile_id','session_id','goal_id','task_id','step_id','mode','op']);
|
|
6
7
|
const LIFECYCLE=Object.freeze({
|
|
7
8
|
device_start:'🚀 [DEVICE] Starting DEADBYTE MCP...',
|
|
8
9
|
bridge_connected:'🔌 [BRIDGE] Local bridge connected',
|
|
@@ -74,19 +75,47 @@ function shortValue(value,max=12){
|
|
|
74
75
|
function payloadByteCount(value){
|
|
75
76
|
return value&&typeof value==='object'&&value.redacted===true&&Number.isFinite(Number(value.byte_count))?Number(value.byte_count):null;
|
|
76
77
|
}
|
|
77
|
-
function
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
78
|
+
function safePathAtom(value){
|
|
79
|
+
if(typeof value!=='string'||value.length<1||value.length>2048||/[\r\n\0]/.test(value)) return null;
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
function rootedPath(args,key='path',rootPaths={}){
|
|
83
|
+
const root=safeAtom(args?.root_id);const p=safePathAtom(args?.[key]);
|
|
84
|
+
if(root&&p){
|
|
85
|
+
const base=safePathAtom(rootPaths?.[root]);
|
|
86
|
+
if(base){
|
|
87
|
+
const flavor=/^(?:[A-Za-z]:[\\/]|\\\\)/.test(base)?path.win32:path.posix;
|
|
88
|
+
const absolute=p==='.'?flavor.resolve(base):flavor.resolve(base,p);
|
|
89
|
+
return absolute+' [root='+root+']';
|
|
90
|
+
}
|
|
91
|
+
return root+':'+p;
|
|
92
|
+
}
|
|
93
|
+
if(root) return 'root='+root;
|
|
94
|
+
return p?'path='+p:'';
|
|
95
|
+
}
|
|
96
|
+
function displayToolName(tool,args){
|
|
97
|
+
const op=safeAtom(args?.op);
|
|
98
|
+
if(op&&['machine_fs','coding_inspect','process_session','desktop_control','machine_observe'].includes(tool)) return tool+'.'+op;
|
|
99
|
+
return tool;
|
|
100
|
+
}
|
|
101
|
+
export function summarizeLiveToolArgs(tool,rawArgs,{rootPaths={}}={}){
|
|
102
|
+
const args=sanitizeAuditArgs(rawArgs??{});const where=rootedPath(args,'path',rootPaths);
|
|
103
|
+
if(/(?:file_read|files_read)$/.test(tool)||/^machine_fs\.(?:read|stat|list)$/.test(tool)) return where;
|
|
104
|
+
if(/file_write$/.test(tool)||/^machine_fs\.write$/.test(tool)){const bytes=payloadByteCount(args.content);return [where,bytes!==null?'('+bytes+'B)':''].filter(Boolean).join(' ');}
|
|
105
|
+
if(/text_replace$|symbol_replace$/.test(tool)||/^machine_fs\.edit_exact$/.test(tool)){const bytes=payloadByteCount(args.new_string);return [where,bytes!==null?'('+bytes+'B new)':''].filter(Boolean).join(' ');}
|
|
106
|
+
if(/^machine_fs\.(?:mkdir|delete)$/.test(tool)||/coding_tree$/.test(tool)) return where;
|
|
107
|
+
if(/^machine_fs\.move$/.test(tool)) return [
|
|
108
|
+
rootedPath({...args,path:args.source_path},'path',rootPaths),
|
|
109
|
+
'->',
|
|
110
|
+
rootedPath({...args,path:args.destination_path},'path',rootPaths)
|
|
111
|
+
].filter(Boolean).join(' ');
|
|
112
|
+
if(/coding_(?:search|inspect)(?:\.|$)/.test(tool)) return [where,args.pattern?'\"'+clipped(String(args.pattern),36)+'\"':''].filter(Boolean).join(' ');
|
|
113
|
+
if(/(?:command|benchmark)_run$/.test(tool)){
|
|
114
|
+
const root=safeAtom(args.root_id);const command=safeAtom(args.command_id);const scriptPath=safePathAtom(args.script_path);
|
|
115
|
+
if(root&&safePathAtom(rootPaths?.[root])) return [rootedPath({...args,path:scriptPath},'path',rootPaths),command].filter(Boolean).join(' ');
|
|
116
|
+
return [root,command,scriptPath].filter(Boolean).join(' ');
|
|
117
|
+
}
|
|
118
|
+
if(/^host_exec$/.test(tool)) return [rootedPath(args,'path',rootPaths),safeAtom(args.command_id),safeAtom(args.mode)].filter(Boolean).join(' ');
|
|
90
119
|
if(/^machine_(?:ping|status|system_status|list)$/.test(tool)) return safeAtom(args.nonce)?'nonce='+shortValue(String(args.nonce),24):'';
|
|
91
120
|
if(/^process_session/.test(tool)) return [safeAtom(args.profile_id),shortValue(safeAtom(args.session_id)||'',12),safeAtom(args.op)].filter(Boolean).join(' ');
|
|
92
121
|
if(/^desktop_/.test(tool)) return [shortValue(safeAtom(args.frame_id)||'',10),shortValue(safeAtom(args.target_id)||'',10),safeAtom(args.op)].filter(Boolean).join(' ');
|
|
@@ -98,11 +127,11 @@ function categoryForTool(tool){
|
|
|
98
127
|
if(/^autonomous_/.test(tool)) return 'autonomous';
|
|
99
128
|
if(HEARTBEAT_TOOLS.has(tool)) return 'heartbeat';
|
|
100
129
|
if(/(?:receipt_verify|evidence_verify|goal_verify|task_verify|audit|release_gate|native_diff_verify)/.test(tool)) return 'verify';
|
|
101
|
-
if(/(?:search|inspect|symbol_(?:definition|references|implementations|outline|diagnostics)|
|
|
102
|
-
if(/(?:file_read|files_read|
|
|
103
|
-
if(/(?:file_write|
|
|
130
|
+
if(/(?:search|inspect|symbol_(?:definition|references|implementations|outline|diagnostics)|(?:^|[._])stat$|(?:^|[._])list$|_tree$)/.test(tool)) return 'search';
|
|
131
|
+
if(/(?:file_read|files_read|machine_fs[._]read|process_session[._]read|host_file_read)/.test(tool)) return 'read';
|
|
132
|
+
if(/(?:file_write|machine_fs[._]write|host_file_write|(?:^|[._])mkdir$|(?:^|[._])move$|(?:^|[._])delete$)/.test(tool)) return 'write';
|
|
104
133
|
if(/(?:replace|edit_exact|native_diff_apply)/.test(tool)) return 'edit';
|
|
105
|
-
if(/(?:command_run|benchmark_run|task_run|goal_run|
|
|
134
|
+
if(/(?:command_run|benchmark_run|task_run|goal_run|process_session[._](?:start|action|terminate)|host_exec)/.test(tool)) return 'run';
|
|
106
135
|
if(/^process_/.test(tool)) return 'process';
|
|
107
136
|
return 'generic';
|
|
108
137
|
}
|
|
@@ -110,7 +139,10 @@ function iconForTool(tool,displayMode='unicode'){
|
|
|
110
139
|
const icons=displayMode==='ascii'?TOOL_ICONS_ASCII:TOOL_ICONS;
|
|
111
140
|
return icons[categoryForTool(tool)]??icons.generic;
|
|
112
141
|
}
|
|
113
|
-
function visibleStart(tool){
|
|
142
|
+
function visibleStart(tool){
|
|
143
|
+
if(['write','edit','run'].includes(categoryForTool(tool))) return true;
|
|
144
|
+
return START_VISIBLE_PATTERNS.some(pattern=>pattern.test(tool));
|
|
145
|
+
}
|
|
114
146
|
|
|
115
147
|
function displayTimestamp(ms){
|
|
116
148
|
const shifted=new Date(ms+DISPLAY_UTC_OFFSET_MINUTES*60*1000).toISOString();
|
|
@@ -189,24 +221,121 @@ export function createLiveActivityTracker(){
|
|
|
189
221
|
function shortSpanId(requestId){
|
|
190
222
|
return typeof requestId==='string'&&requestId.length?requestId.slice(0,8):'????????';
|
|
191
223
|
}
|
|
224
|
+
function traceIdentity(event,args=event?.args??{}){
|
|
225
|
+
const context=event?.context&&typeof event.context==='object'&&!Array.isArray(event.context)?event.context:{};
|
|
226
|
+
const first=(...values)=>values.map(safeAtom).find(Boolean)||null;
|
|
227
|
+
const spanId=safeAtom(event?.request_id);
|
|
228
|
+
return {
|
|
229
|
+
trace_id:first(context.trace_id,context.job_id,context.session_id,args.goal_id,args.task_id,args.job_id,args.session_id,spanId),
|
|
230
|
+
span_id:spanId,
|
|
231
|
+
parent_span_id:first(context.parent_span_id,context.parent_request_id)
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function shortTraceValue(value,displayMode){
|
|
235
|
+
if(typeof value!=='string'||!value) return '';
|
|
236
|
+
if(value.length<=8) return value;
|
|
237
|
+
return value.slice(0,8)+(displayMode==='ascii'?'...':'…');
|
|
238
|
+
}
|
|
239
|
+
function traceText(meta,displayMode='unicode'){
|
|
240
|
+
if(!meta?.trace_id||!meta?.span_id) return '';
|
|
241
|
+
return 'trace='+shortTraceValue(meta.trace_id,displayMode)+' span='+shortTraceValue(meta.span_id,displayMode)+
|
|
242
|
+
(meta.parent_span_id?' parent='+shortTraceValue(meta.parent_span_id,displayMode):'');
|
|
243
|
+
}
|
|
244
|
+
function outcomeClass(event){
|
|
245
|
+
if(event?.outcome?.side_effect_state==='unknown') return 'unknown';
|
|
246
|
+
return event?.outcome?.is_error===true?'error':'ok';
|
|
247
|
+
}
|
|
248
|
+
function parseFilterExpression(expression){
|
|
249
|
+
if(expression===null||expression===undefined||expression==='') return Object.freeze({});
|
|
250
|
+
if(typeof expression!=='string'||expression.length>512) throw new Error('timeline filter invalid');
|
|
251
|
+
const out={};
|
|
252
|
+
for(const raw of expression.split(',')){
|
|
253
|
+
const part=raw.trim();if(!part) continue;
|
|
254
|
+
const at=part.indexOf('=');if(at<1||at===part.length-1) throw new Error('timeline filter term must be key=value');
|
|
255
|
+
const key=part.slice(0,at).trim();const value=part.slice(at+1).trim();
|
|
256
|
+
if(!['tool','category','status','trace'].includes(key)) throw new Error(`timeline filter key '${key}' unsupported`);
|
|
257
|
+
if(Object.hasOwn(out,key)) throw new Error(`timeline filter key '${key}' repeated`);
|
|
258
|
+
if(key==='status'&&!['ok','error','unknown'].includes(value)) throw new Error('timeline filter status invalid');
|
|
259
|
+
if(key==='category'&&!Object.hasOwn(TOOL_ICONS,value)) throw new Error('timeline filter category invalid');
|
|
260
|
+
out[key]=value;
|
|
261
|
+
}
|
|
262
|
+
return Object.freeze(out);
|
|
263
|
+
}
|
|
264
|
+
function toolFilterMatch(pattern,tool){
|
|
265
|
+
if(!pattern) return true;
|
|
266
|
+
return pattern.endsWith('*')?tool.startsWith(pattern.slice(0,-1)):tool===pattern;
|
|
267
|
+
}
|
|
268
|
+
export function createLiveTimelineFilter(expression=''){
|
|
269
|
+
const spec=parseFilterExpression(expression);
|
|
270
|
+
const pending=new Map();
|
|
271
|
+
const baseMatch=meta=>(!spec.tool||toolFilterMatch(spec.tool,meta.tool))&&(!spec.category||spec.category===meta.category)&&(!spec.trace||spec.trace===meta.trace_id);
|
|
272
|
+
return Object.freeze({
|
|
273
|
+
accept(event){
|
|
274
|
+
if(!event||typeof event!=='object'||Array.isArray(event)) return false;
|
|
275
|
+
const requestId=safeAtom(event.request_id);const rawTool=safeAtom(event.tool);
|
|
276
|
+
if(!requestId||!rawTool) return false;
|
|
277
|
+
if(event.event_type==='tool.request'){
|
|
278
|
+
const args=event.args??{};const tool=displayToolName(rawTool,args);const trace=traceIdentity(event,args);
|
|
279
|
+
const meta={tool,category:categoryForTool(tool),trace_id:trace.trace_id};
|
|
280
|
+
pending.set(requestId,meta);
|
|
281
|
+
return spec.status?false:baseMatch(meta);
|
|
282
|
+
}
|
|
283
|
+
if(event.event_type==='tool.result'){
|
|
284
|
+
const meta=pending.get(requestId)??{tool:rawTool,category:categoryForTool(rawTool),trace_id:traceIdentity(event).trace_id};
|
|
285
|
+
pending.delete(requestId);
|
|
286
|
+
return baseMatch(meta)&&(!spec.status||spec.status===outcomeClass(event));
|
|
287
|
+
}
|
|
288
|
+
return false;
|
|
289
|
+
},
|
|
290
|
+
spec
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
export function createLiveTimelineWriter({stream=process.stdout,displayMode='unicode',rewrite=true}={}){
|
|
294
|
+
if(!stream||typeof stream.write!=='function') throw new Error('timeline writer stream invalid');
|
|
295
|
+
if(!REMOTE_DISPLAY_MODES.includes(displayMode)) throw new Error('timeline writer display mode invalid');
|
|
296
|
+
if(typeof rewrite!=='boolean') throw new Error('timeline writer rewrite invalid');
|
|
297
|
+
let active=null;
|
|
298
|
+
function flush(){
|
|
299
|
+
if(active){stream.write('\n');active=null;}
|
|
300
|
+
}
|
|
301
|
+
function write(line,{event_type=null,request_id=null}={}){
|
|
302
|
+
if(typeof line!=='string'||!line) return;
|
|
303
|
+
const tty=stream.isTTY===true&&rewrite;
|
|
304
|
+
if(!tty){stream.write(line+'\n');return;}
|
|
305
|
+
if(active&&event_type==='tool.result'&&request_id&&request_id===active.request_id){
|
|
306
|
+
stream.write('\r\x1b[2K'+line+'\n');active=null;return;
|
|
307
|
+
}
|
|
308
|
+
if(active) flush();
|
|
309
|
+
if(event_type==='tool.call'&&request_id){stream.write(line);active={request_id};return;}
|
|
310
|
+
stream.write(line+'\n');
|
|
311
|
+
}
|
|
312
|
+
return Object.freeze({write,flush,pending(){return active?{...active}:null;}});
|
|
313
|
+
}
|
|
192
314
|
|
|
193
|
-
export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,heartbeatBurstLimit=10,heartbeatWindowMs=3000,displayMode='unicode'}={}){
|
|
315
|
+
export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,heartbeatBurstLimit=10,heartbeatWindowMs=3000,displayMode='unicode',rootPaths={},trace=false}={}){
|
|
194
316
|
if(typeof clock!=='function') throw new Error('live timeline clock must be a function');
|
|
195
317
|
if(!REMOTE_DISPLAY_MODES.includes(displayMode)) throw new Error('remote display mode invalid');
|
|
196
318
|
if(!Number.isInteger(heartbeatBurstLimit)||heartbeatBurstLimit<2||heartbeatBurstLimit>100) throw new Error('heartbeatBurstLimit invalid');
|
|
197
319
|
if(!Number.isInteger(heartbeatWindowMs)||heartbeatWindowMs<250||heartbeatWindowMs>60000) throw new Error('heartbeatWindowMs invalid');
|
|
320
|
+
if(!rootPaths||typeof rootPaths!=='object'||Array.isArray(rootPaths)) throw new Error('live timeline rootPaths invalid');
|
|
321
|
+
if(typeof trace!=='boolean') throw new Error('live timeline trace invalid');
|
|
198
322
|
const pending=new Map();
|
|
199
323
|
const completed=new Set();
|
|
200
324
|
const toolCounts=new Map();
|
|
201
|
-
|
|
325
|
+
const durationSamples=[];
|
|
326
|
+
let resultCount=0,okCount=0,errorCount=0,unknownCount=0,mutationCount=0,totalDurationMs=0,slowest=null,burst=null,foldedHeartbeatLines=0;
|
|
202
327
|
|
|
203
|
-
function eventPrefix(event,sequence){
|
|
328
|
+
function eventPrefix(event,sequence,{displayTool=null,icon=null}={}){
|
|
204
329
|
const seq=Number.isSafeInteger(sequence)&&sequence>0?'#'+sequence+' ':'';
|
|
205
|
-
|
|
330
|
+
const tool=displayTool||event.tool;
|
|
331
|
+
const visual=icon||iconForTool(tool,displayMode);
|
|
332
|
+
return visual+' '+displayClock(eventMs(event,clock))+' '+seq+'['+shortSpanId(event.request_id)+'] '+tool;
|
|
206
333
|
}
|
|
334
|
+
function traceSuffix(meta){return trace&&meta?' '+traceText(meta,displayMode):'';}
|
|
207
335
|
function resultLine(event,span,sequence){
|
|
208
336
|
const error=event.outcome?.is_error===true;
|
|
209
337
|
const unknown=event.outcome?.side_effect_state==='unknown';
|
|
338
|
+
const displayTool=span?.display_tool||event.tool;
|
|
210
339
|
const duration=Number.isFinite(Number(event.outcome?.duration_ms))
|
|
211
340
|
?formatDuration(event.outcome.duration_ms)
|
|
212
341
|
:span?formatDuration(Math.max(0,clock()-span.started_at_ms)):'';
|
|
@@ -214,8 +343,17 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
|
|
|
214
343
|
const rawReason=typeof event.outcome?.reason==='string'&&event.outcome.reason?event.outcome.reason:
|
|
215
344
|
(unknown&&typeof event.outcome?.recovery?.reason==='string'?event.outcome.recovery.reason:'');
|
|
216
345
|
const reason=rawReason?clipped(rawReason,180):(unknown?'terminal result unavailable; inspect state before retry':'');
|
|
217
|
-
const
|
|
218
|
-
|
|
346
|
+
const category=categoryForTool(displayTool);
|
|
347
|
+
const receipt=Array.isArray(event.outcome?.receipt_sha256s)&&typeof event.outcome.receipt_sha256s[0]==='string'
|
|
348
|
+
?shortValue(event.outcome.receipt_sha256s[0],8):'';
|
|
349
|
+
const marker=unknown?'FAILED_AFTER_UNKNOWN_EFFECT':
|
|
350
|
+
error?(displayMode==='ascii'?'ERR':'✗'):
|
|
351
|
+
['write','edit','run'].includes(category)?(displayMode==='ascii'?'SUCCEEDED':'✅ SUCCEEDED'):
|
|
352
|
+
(displayMode==='ascii'?'OK':'✓');
|
|
353
|
+
const prefixIcon=unknown?(displayMode==='ascii'?'WARN':'⚠️'):null;
|
|
354
|
+
const evidence=receipt?' '+(displayMode==='ascii'?'RECEIPT':'🧾 receipt')+(displayMode==='ascii'?'=':'=')+receipt:'';
|
|
355
|
+
const reasonSep=displayMode==='ascii'?' - ':' — ';
|
|
356
|
+
return remoteDisplayText(eventPrefix(event,sequence,{displayTool,icon:prefixIcon})+traceSuffix(span?.trace_meta)+(summary?' '+summary:'')+(duration?' '+duration:'')+' '+marker+evidence+(reason?reasonSep+reason:''),displayMode);
|
|
219
357
|
}
|
|
220
358
|
function burstLine(value){
|
|
221
359
|
if(!value) return null;
|
|
@@ -233,12 +371,19 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
|
|
|
233
371
|
burst=null;
|
|
234
372
|
return line;
|
|
235
373
|
}
|
|
236
|
-
function addStats(event){
|
|
374
|
+
function addStats(event,span){
|
|
237
375
|
resultCount++;
|
|
238
|
-
|
|
376
|
+
const displayTool=span?.display_tool||event.tool;
|
|
377
|
+
toolCounts.set(displayTool,(toolCounts.get(displayTool)??0)+1);
|
|
378
|
+
const outcome=outcomeClass(event);
|
|
379
|
+
if(outcome==='unknown') unknownCount++;else if(outcome==='error') errorCount++;else okCount++;
|
|
380
|
+
if(['write','edit','run'].includes(categoryForTool(displayTool))) mutationCount++;
|
|
239
381
|
const duration=Number(event.outcome?.duration_ms);
|
|
240
|
-
if(Number.isFinite(duration)&&duration>=0)
|
|
241
|
-
|
|
382
|
+
if(Number.isFinite(duration)&&duration>=0){
|
|
383
|
+
totalDurationMs+=duration;
|
|
384
|
+
if(durationSamples.length<2048) durationSamples.push(duration);
|
|
385
|
+
if(!slowest||duration>slowest.duration_ms) slowest={tool:displayTool,duration_ms:duration};
|
|
386
|
+
}
|
|
242
387
|
}
|
|
243
388
|
function observe(event,{sequence=null}={}){
|
|
244
389
|
if(!event||typeof event!=='object'||Array.isArray(event)) return null;
|
|
@@ -246,10 +391,13 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
|
|
|
246
391
|
const tool=typeof event.tool==='string'&&event.tool?event.tool:null;
|
|
247
392
|
if(event.event_type==='tool.request'&&requestId&&tool){
|
|
248
393
|
if(pending.has(requestId)||completed.has(requestId)) return null;
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
394
|
+
const args=event.args??{};
|
|
395
|
+
const displayTool=displayToolName(tool,args);
|
|
396
|
+
const summary=summarizeLiveToolArgs(displayTool,args,{rootPaths});
|
|
397
|
+
const traceMeta=traceIdentity(event,args);
|
|
398
|
+
pending.set(requestId,{request_id:requestId,tool,display_tool:displayTool,summary,trace_meta:traceMeta,started_at_ms:clock()});
|
|
399
|
+
if(verbose) return formatRemoteToolCall({...event,tool:displayTool},{clock,displayMode})+traceSuffix(traceMeta);
|
|
400
|
+
if(visibleStart(displayTool)) return eventPrefix(event,sequence,{displayTool})+traceSuffix(traceMeta)+(summary?' '+summary:'')+' …';
|
|
253
401
|
return null;
|
|
254
402
|
}
|
|
255
403
|
if(event.event_type==='tool.result'&&requestId&&tool){
|
|
@@ -257,7 +405,7 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
|
|
|
257
405
|
const span=pending.get(requestId)??null;
|
|
258
406
|
pending.delete(requestId);
|
|
259
407
|
completed.add(requestId);
|
|
260
|
-
addStats(event);
|
|
408
|
+
addStats(event,span);
|
|
261
409
|
const error=event.outcome?.is_error===true;
|
|
262
410
|
const nowMs=eventMs(event,clock);
|
|
263
411
|
if(HEARTBEAT_TOOLS.has(tool)&&!error){
|
|
@@ -297,8 +445,14 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
|
|
|
297
445
|
function summary(){
|
|
298
446
|
const top=[...toolCounts.entries()].sort((a,b)=>b[1]-a[1]||a[0].localeCompare(b[0])).slice(0,3)
|
|
299
447
|
.map(([tool,count])=>tool+'×'+count).join(', ');
|
|
300
|
-
|
|
448
|
+
const sorted=[...durationSamples].sort((a,b)=>a-b);
|
|
449
|
+
const p50=sorted.length?(sorted.length%2?sorted[(sorted.length-1)/2]:(sorted[sorted.length/2-1]+sorted[sorted.length/2])/2):null;
|
|
450
|
+
const p95=sorted.length?sorted[Math.max(0,Math.ceil(sorted.length*0.95)-1)]:null;
|
|
451
|
+
return remoteDisplayText((displayMode==='ascii'?'[SUMMARY]':'📊 [SUMMARY]')+' '+resultCount+' calls | '+okCount+' ok | '+
|
|
452
|
+
errorCount+' error'+(errorCount===1?'':'s')+' | '+unknownCount+' unknown | '+mutationCount+' mutation'+(mutationCount===1?'':'s')+
|
|
301
453
|
' | tool time '+(formatDuration(totalDurationMs)||'0ms')+
|
|
454
|
+
(p50!==null?' | p50 '+formatDuration(p50)+' | p95 '+formatDuration(p95):'')+
|
|
455
|
+
(slowest?' | slowest '+slowest.tool+' '+formatDuration(slowest.duration_ms):'')+
|
|
302
456
|
(foldedHeartbeatLines?' | folded '+foldedHeartbeatLines+' heartbeat lines':'')+
|
|
303
457
|
(top?' | top: '+top:''),displayMode);
|
|
304
458
|
}
|