deadbyte-mcp 0.15.22 → 0.15.23
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 +31 -15
- package/PROVENANCE.json +10 -10
- package/README.txt +18 -7
- package/RELEASE.json +15 -9
- package/SBOM.cdx.json +3 -3
- package/TEST-MATRIX.json +14 -4
- package/package.json +3 -2
- package/scripts/activate-skill-creator-1.1.1.mjs +46 -0
- package/scripts/build-advanced-pcb-skill.mjs +208 -0
- 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/deadbyte-cli.mjs +70 -15
- package/src/remote-live-log.mjs +189 -35
- package/src/tool-audit.mjs +1 -1
- package/src/version.mjs +1 -1
- package/test/full-max-catalog-r48.test.mjs +1 -1
- package/test/r48-release-contract.test.mjs +20 -14
- 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/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
|
}
|
package/src/tool-audit.mjs
CHANGED
|
@@ -86,7 +86,7 @@ function safeOutcomeReason(value){
|
|
|
86
86
|
if(value===null||value===undefined) return null;
|
|
87
87
|
let text=typeof value==='string'?value:(value instanceof Error?String(value.message??value):null);
|
|
88
88
|
if(text===null) return null;
|
|
89
|
-
text=text.replace(/[
|
|
89
|
+
text=text.replace(/[\r\n\t]+/g,' ').replace(/\s{2,}/g,' ').trim();
|
|
90
90
|
if(!text) return null;
|
|
91
91
|
const originalBytes=Buffer.from(text,'utf8');
|
|
92
92
|
if(secretValuePattern.test(text)) return `<redacted reason bytes=${originalBytes.length} sha256=${sha256(originalBytes)}>`;
|
package/src/version.mjs
CHANGED
|
@@ -49,7 +49,7 @@ test('R48 runtime full stdio catalog exactly matches canonical registration surf
|
|
|
49
49
|
const autonomousLease=f.policy.lease_file;
|
|
50
50
|
for(const q of [processGrant,hostArm,desktopGrant,codingLease,autonomousLease]) assert.equal(await absent(q),true,'authority unexpectedly armed before start: '+q);
|
|
51
51
|
|
|
52
|
-
const client=new Client({name:'deadbyte-r48-full-catalog',version:'0.15.
|
|
52
|
+
const client=new Client({name:'deadbyte-r48-full-catalog',version:'0.15.23'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
53
53
|
const env={
|
|
54
54
|
...getDefaultEnvironment(),DEADBYTE_CAPABILITY_PROFILE:'full',DEADBYTE_AUDIT_RUNTIME:'0',
|
|
55
55
|
DEADBYTE_STATE_ROOT:path.join(f.root,'state'),DEADBYTE_WORKSPACE_ROOT:f.root,
|
|
@@ -6,27 +6,27 @@ import { DEADBYTE_VERSION } from '../src/version.mjs';
|
|
|
6
6
|
const root=new URL('../',import.meta.url);
|
|
7
7
|
const read=rel=>readFile(new URL(rel,root),'utf8');
|
|
8
8
|
|
|
9
|
-
test('R48 0.15.
|
|
9
|
+
test('R48 0.15.23 successor binds active 0.15.22 parent and livelog trace evidence',async()=>{
|
|
10
10
|
const [pkg,lock,release,provenance]=await Promise.all([
|
|
11
11
|
read('package.json').then(JSON.parse),
|
|
12
12
|
read('package-lock.json').then(JSON.parse),
|
|
13
13
|
read('RELEASE.json').then(JSON.parse),
|
|
14
14
|
read('PROVENANCE.json').then(JSON.parse)
|
|
15
15
|
]);
|
|
16
|
-
assert.equal(pkg.version,'0.15.
|
|
17
|
-
assert.equal(lock.version,'0.15.
|
|
18
|
-
assert.equal(lock.packages[''].version,'0.15.
|
|
19
|
-
assert.equal(DEADBYTE_VERSION,'0.15.
|
|
20
|
-
assert.equal(release.version,'0.15.
|
|
21
|
-
assert.equal(provenance.version,'0.15.
|
|
16
|
+
assert.equal(pkg.version,'0.15.23');
|
|
17
|
+
assert.equal(lock.version,'0.15.23');
|
|
18
|
+
assert.equal(lock.packages[''].version,'0.15.23');
|
|
19
|
+
assert.equal(DEADBYTE_VERSION,'0.15.23');
|
|
20
|
+
assert.equal(release.version,'0.15.23');
|
|
21
|
+
assert.equal(provenance.version,'0.15.23');
|
|
22
22
|
assert.equal(release.train,'R48');
|
|
23
|
-
assert.equal(release.label,'
|
|
24
|
-
assert.equal(release.parent.release_id,'v0.15.
|
|
25
|
-
assert.equal(release.parent.manifest_sha256,'
|
|
26
|
-
assert.equal(release.parent.generation_manifest_sha256,'
|
|
27
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
28
|
-
assert.equal(release.source_predecessor.tarball_sha256,'
|
|
29
|
-
assert.equal(release.source_predecessor.manifest_file_sha256,'
|
|
23
|
+
assert.equal(release.label,'HUMAN LIVELOG TRACE + VISUAL OPCODE BASELINE');
|
|
24
|
+
assert.equal(release.parent.release_id,'v0.15.22-ed178d33bf883f68');
|
|
25
|
+
assert.equal(release.parent.manifest_sha256,'ed178d33bf883f68b4f39f2e8bb805094bc8024b30114c4e6a34b80930cfcff8');
|
|
26
|
+
assert.equal(release.parent.generation_manifest_sha256,'029b177dc1838388ddec154be38cb597a4dcfa0711b2297196d5a9e7cb8dbe18');
|
|
27
|
+
assert.equal(release.source_predecessor.version,'0.15.22');
|
|
28
|
+
assert.equal(release.source_predecessor.tarball_sha256,'fda51e06fa16116179d9c48f1466432886c10e62807482326d2dc5ccb7b66eac');
|
|
29
|
+
assert.equal(release.source_predecessor.manifest_file_sha256,'ed178d33bf883f68b4f39f2e8bb805094bc8024b30114c4e6a34b80930cfcff8');
|
|
30
30
|
assert.equal(release.source_predecessor.reference_server_sha256,'dfc303bb7dd2e8d01debac50fc43bb8644d00089bffdf2887e149497cff8b305');
|
|
31
31
|
assert.equal(release.source_predecessor.reference_schemas_sha256,'f342345e34e48769dabaefe1b932f09d41e2e207b422f7794a15ee50ffa8f140');
|
|
32
32
|
assert.equal(release.capabilities.full_tool_count,116);
|
|
@@ -35,6 +35,12 @@ test('R48 0.15.22 successor binds active 0.15.21 parent and native Windows obser
|
|
|
35
35
|
assert.equal(release.capabilities.machine_fs_list_entry_fault_isolation,true);
|
|
36
36
|
assert.equal(release.capabilities.capability_manifest_authority_truth,true);
|
|
37
37
|
assert.equal(release.capabilities.capability_manifest_host_grant_truth,true);
|
|
38
|
+
assert.equal(release.capabilities.human_livelog_visual_opcode,true);
|
|
39
|
+
assert.equal(release.capabilities.human_livelog_absolute_root_paths,true);
|
|
40
|
+
assert.equal(release.capabilities.human_livelog_trace_span,true);
|
|
41
|
+
assert.equal(release.capabilities.human_livelog_presentation_filter,true);
|
|
42
|
+
assert.equal(release.capabilities.human_livelog_tty_in_place_rewrite,true);
|
|
43
|
+
assert.equal(release.capabilities.human_livelog_expanded_summary,true);
|
|
38
44
|
assert.equal(provenance.parent_release_id,release.parent.release_id);
|
|
39
45
|
assert.equal(provenance.parent_manifest_sha256,release.parent.manifest_sha256);
|
|
40
46
|
assert.equal(provenance.parent_generation_manifest_sha256,release.parent.generation_manifest_sha256);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import
|
|
3
|
+
import * as live from '../src/remote-live-log.mjs';
|
|
4
|
+
const {createLiveTimelineTracker,formatRemoteLifecycle,formatRuntimeDiagnosticLine,summarizeLiveToolArgs,truncateUtf8}=live;
|
|
4
5
|
|
|
5
6
|
test('R40 human live timeline suppresses noisy read request and emits one categorized terminal line',()=>{
|
|
6
7
|
let now=1000;
|
|
@@ -43,7 +44,7 @@ test('R40 heartbeat burst folds repeated status calls instead of printing every
|
|
|
43
44
|
else assert.match(line,/^📡 .*#39 machine_ping ×5 avg 3ms ✓$/);
|
|
44
45
|
now+=10;
|
|
45
46
|
}
|
|
46
|
-
assert.match(tracker.summary(),/5 calls \| 0 errors .*folded 4 heartbeat lines.*machine_ping×5/);
|
|
47
|
+
assert.match(tracker.summary(),/5 calls \| 5 ok \| 0 errors \| 0 unknown \| 0 mutations .*folded 4 heartbeat lines.*machine_ping×5/);
|
|
47
48
|
});
|
|
48
49
|
|
|
49
50
|
test('R40 transport IN/OUT echo is hidden by default and available only in verbose diagnostics',()=>{
|
|
@@ -64,14 +65,19 @@ test('R40 interrupted spans remain correlated and session summary is available',
|
|
|
64
65
|
args:{root_id:'core',path:'x.txt',content:'x'}},{sequence:50});
|
|
65
66
|
const lines=tracker.reconcileInterrupted('recovery');
|
|
66
67
|
assert.deepEqual(lines,['⚠ [pending-] OUTCOME_UNKNOWN coding_file_write — recovery ended before terminal result; inspect state before retry']);
|
|
67
|
-
assert.match(tracker.summary(),/^📊 \[SUMMARY\] 0 calls \| 0 errors/);
|
|
68
|
+
assert.match(tracker.summary(),/^📊 \[SUMMARY\] 0 calls \| 0 ok \| 0 errors \| 0 unknown \| 0 mutations/);
|
|
68
69
|
});
|
|
69
70
|
|
|
70
71
|
test('R40 CLI exposes explicit verbose mode while keeping default live mode compact',async()=>{
|
|
71
72
|
const {readFile}=await import('node:fs/promises');
|
|
72
73
|
const src=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
73
|
-
assert.match(src,/
|
|
74
|
-
assert.match(src,/
|
|
74
|
+
assert.match(src,/readLiveRootPaths\(ctx\.root\)/);
|
|
75
|
+
assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace\}\)/);
|
|
76
|
+
assert.match(src,/createLiveTimelineFilter\(filterExpression\)/);
|
|
77
|
+
assert.match(src,/createLiveTimelineWriter\(\{stream:process\.stdout,displayMode,rewrite:!verbose\}\)/);
|
|
78
|
+
assert.match(src,/arg==='--verbose'\|\|arg==='-v'/);
|
|
79
|
+
assert.match(src,/arg==='--trace'/);
|
|
80
|
+
assert.match(src,/arg==='--filter'/);
|
|
75
81
|
assert.match(src,/Transport IN\/OUT hidden/);
|
|
76
82
|
assert.doesNotMatch(src,/\[cursor \$\{recorded\.cursor\}\]/);
|
|
77
83
|
});
|
|
@@ -88,13 +94,13 @@ test('R40 ASCII display fallback contains no non-ASCII bytes and preserves termi
|
|
|
88
94
|
assert.equal(formatRemoteLifecycle('device_start',{displayMode:'ascii'}),'[DEVICE] Starting DEADBYTE MCP...');
|
|
89
95
|
});
|
|
90
96
|
|
|
91
|
-
test('R40 interrupted side effect is explicit
|
|
97
|
+
test('R40 interrupted side effect is explicit failed-after-unknown-effect instead of implying replay safety',()=>{
|
|
92
98
|
const tracker=createLiveTimelineTracker({clock:()=>1000});
|
|
93
99
|
tracker.observe({event_type:'tool.request',tool:'coding_file_write',request_id:'unknown-12345678',
|
|
94
100
|
args:{root_id:'core',path:'x.txt',content:'x'}},{sequence:70});
|
|
95
101
|
const line=tracker.observe({event_type:'tool.result',tool:'coding_file_write',request_id:'unknown-12345678',
|
|
96
102
|
outcome:{status:'interrupted',is_error:true,duration_ms:0,side_effect_state:'unknown',recovery:{reason:'runtime_restart'}}},{sequence:71});
|
|
97
|
-
assert.match(line,/
|
|
103
|
+
assert.match(line,/FAILED_AFTER_UNKNOWN_EFFECT — runtime_restart$/);
|
|
98
104
|
assert.doesNotMatch(line,/\s[✓✗]\s/);
|
|
99
105
|
});
|
|
100
106
|
|
|
@@ -105,3 +111,96 @@ test('R40 UTF-8 truncation never splits Thai or emoji into replacement character
|
|
|
105
111
|
assert.equal(clipped.includes('\uFFFD'),false);
|
|
106
112
|
assert.ok(clipped.endsWith('…'));
|
|
107
113
|
});
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
test('visual opcode projection resolves multiplexed machine_fs operation and exposes terminal evidence',()=>{
|
|
117
|
+
let now=1000;
|
|
118
|
+
const receipt='1'.repeat(64);
|
|
119
|
+
const tracker=createLiveTimelineTracker({clock:()=>now,rootPaths:{projects:'D:\\Projects'}});
|
|
120
|
+
const start=tracker.observe({event_type:'tool.request',tool:'machine_fs',request_id:'machinefs-12345678',
|
|
121
|
+
ts:'1970-01-01T00:00:01.000Z',args:{op:'write',root_id:'projects',path:'S3CORE01-R2/STATUS.txt',content:'x'}},{sequence:80});
|
|
122
|
+
assert.match(start,/^📝 .*machine_fs\.write /);
|
|
123
|
+
assert.ok(start.includes('D:\\Projects\\S3CORE01-R2\\STATUS.txt [root=projects]'));
|
|
124
|
+
now=1007;
|
|
125
|
+
const done=tracker.observe({event_type:'tool.result',tool:'machine_fs',request_id:'machinefs-12345678',
|
|
126
|
+
ts:'1970-01-01T00:00:01.007Z',outcome:{status:'committed',is_error:false,duration_ms:7,receipt_sha256s:[receipt]}},{sequence:81});
|
|
127
|
+
assert.match(done,/^📝 .*machine_fs\.write .*7ms ✅ SUCCEEDED 🧾 receipt=11111111…$/);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('visual opcode projection reserves warning semantics for explicitly unknown side effects',()=>{
|
|
131
|
+
const tracker=createLiveTimelineTracker({clock:()=>1000});
|
|
132
|
+
tracker.observe({event_type:'tool.request',tool:'host_exec',request_id:'unknown-host-1234',
|
|
133
|
+
args:{root_id:'core',command_id:'powershell_inline',mode:'inline'}},{sequence:90});
|
|
134
|
+
const done=tracker.observe({event_type:'tool.result',tool:'host_exec',request_id:'unknown-host-1234',
|
|
135
|
+
outcome:{status:'interrupted',is_error:true,duration_ms:4,side_effect_state:'unknown',recovery:{reason:'runtime_restart'}}},{sequence:91});
|
|
136
|
+
assert.match(done,/^⚠️ .*host_exec .*4ms FAILED_AFTER_UNKNOWN_EFFECT — runtime_restart$/);
|
|
137
|
+
assert.doesNotMatch(done,/❌|🚫/);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
test('absolute Windows root rendering shows the operator the real host path and root identity',()=>{
|
|
142
|
+
const tracker=createLiveTimelineTracker({clock:()=>1000,rootPaths:{local_c:'C:\\'}});
|
|
143
|
+
tracker.observe({event_type:'tool.request',tool:'machine_fs',request_id:'localc-123456789',
|
|
144
|
+
args:{op:'read',root_id:'local_c',path:'Users/DEADBYTE/.deadbyte-mcp/active-release.json'}},{sequence:100});
|
|
145
|
+
const line=tracker.observe({event_type:'tool.result',tool:'machine_fs',request_id:'localc-123456789',
|
|
146
|
+
outcome:{is_error:false,duration_ms:1}},{sequence:101});
|
|
147
|
+
assert.ok(line.includes('C:\\Users\\DEADBYTE\\.deadbyte-mcp\\active-release.json [root=local_c]'));
|
|
148
|
+
assert.match(line,/^📖 .*machine_fs\.read /);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
test('trace mode exposes deterministic trace/span hierarchy without payload bytes',()=>{
|
|
153
|
+
const tracker=createLiveTimelineTracker({clock:()=>1000,trace:true});
|
|
154
|
+
const start=tracker.observe({event_type:'tool.request',tool:'coding_file_write',request_id:'span-1234567890abcdef',
|
|
155
|
+
context:{parent_span_id:'parent-abcdef123456',job_id:'job-112233445566'},args:{root_id:'core',path:'x.txt',content:'SECRET'}},{sequence:110});
|
|
156
|
+
assert.match(start,/trace=job-1122… span=span-123… parent=parent-a…/);
|
|
157
|
+
assert.doesNotMatch(start,/SECRET/);
|
|
158
|
+
const done=tracker.observe({event_type:'tool.result',tool:'coding_file_write',request_id:'span-1234567890abcdef',
|
|
159
|
+
outcome:{is_error:false,duration_ms:2}},{sequence:111});
|
|
160
|
+
assert.match(done,/trace=job-1122… span=span-123… parent=parent-a…/);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('timeline filter keeps audit observation complete while selecting presentation by category status and trace',()=>{
|
|
164
|
+
const filter=live.createLiveTimelineFilter('category=write,status=error,trace=goal-1234');
|
|
165
|
+
const request={event_type:'tool.request',tool:'machine_fs',request_id:'filter-span-1',
|
|
166
|
+
args:{op:'write',root_id:'projects',path:'x.txt',goal_id:'goal-1234'}};
|
|
167
|
+
assert.equal(filter.accept(request),false,'status-constrained request start is deferred');
|
|
168
|
+
assert.equal(filter.accept({event_type:'tool.result',tool:'machine_fs',request_id:'filter-span-1',
|
|
169
|
+
outcome:{is_error:true,duration_ms:1}}),true);
|
|
170
|
+
const other={event_type:'tool.request',tool:'coding_file_read',request_id:'filter-span-2',
|
|
171
|
+
args:{root_id:'projects',path:'x.txt',goal_id:'goal-1234'}};
|
|
172
|
+
assert.equal(filter.accept(other),false);
|
|
173
|
+
assert.equal(filter.accept({event_type:'tool.result',tool:'coding_file_read',request_id:'filter-span-2',
|
|
174
|
+
outcome:{is_error:true,duration_ms:1}}),false);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('TTY writer rewrites matching visible start in place and never rewrites unrelated output',()=>{
|
|
178
|
+
const writes=[];const stream={isTTY:true,write(value){writes.push(value);return true;}};
|
|
179
|
+
const writer=live.createLiveTimelineWriter({stream,displayMode:'unicode'});
|
|
180
|
+
writer.write('RUNNING',{event_type:'tool.call',request_id:'a'});
|
|
181
|
+
writer.write('DONE',{event_type:'tool.result',request_id:'a'});
|
|
182
|
+
assert.deepEqual(writes,['RUNNING','\r\x1b[2KDONE\n']);
|
|
183
|
+
writes.length=0;
|
|
184
|
+
writer.write('RUN-A',{event_type:'tool.call',request_id:'a'});
|
|
185
|
+
writer.write('OTHER',{event_type:'tool.result',request_id:'b'});
|
|
186
|
+
assert.deepEqual(writes,['RUN-A','\n','OTHER\n']);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('expanded session summary reports outcome classes latency and slowest tool',()=>{
|
|
190
|
+
const tracker=createLiveTimelineTracker({clock:()=>1000});
|
|
191
|
+
const events=[
|
|
192
|
+
['a','coding_file_read',10,false,false],
|
|
193
|
+
['b','coding_file_write',50,false,false],
|
|
194
|
+
['c','coding_command_run',100,true,false],
|
|
195
|
+
['d','host_exec',200,true,true]
|
|
196
|
+
];
|
|
197
|
+
for(const [id,tool,duration,isError,unknown] of events){
|
|
198
|
+
tracker.observe({event_type:'tool.request',tool,request_id:id,args:{root_id:'core',path:'x'}});
|
|
199
|
+
tracker.observe({event_type:'tool.result',tool,request_id:id,outcome:{is_error:isError,duration_ms:duration,
|
|
200
|
+
...(unknown?{side_effect_state:'unknown'}:{})}});
|
|
201
|
+
}
|
|
202
|
+
const summary=tracker.summary();
|
|
203
|
+
assert.match(summary,/4 calls \| 2 ok \| 1 error \| 1 unknown \| 3 mutations/);
|
|
204
|
+
assert.match(summary,/p50 75ms \| p95 200ms/);
|
|
205
|
+
assert.match(summary,/slowest host_exec 200ms/);
|
|
206
|
+
});
|
|
@@ -466,3 +466,43 @@ winTest('bare remote surfaces bounded controller-start-failure evidence instead
|
|
|
466
466
|
assert.match(output.stderr,/controller-start-failure/i);
|
|
467
467
|
}finally{await rm(fx.dir,{recursive:true,force:true});}
|
|
468
468
|
});
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
winTest('remote --trace --filter selects presentation without dropping journaled events',async()=>{
|
|
472
|
+
const fx=await fixture();
|
|
473
|
+
let child=null;
|
|
474
|
+
try{
|
|
475
|
+
child=spawn(process.execPath,[cliPath,'remote','--trace','--filter','category=write,status=error,trace=goal-1234'],{
|
|
476
|
+
env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true
|
|
477
|
+
});
|
|
478
|
+
const output=await collect(child);
|
|
479
|
+
await waitFor(fx.trace,commands=>commands.includes('hoststatus'));
|
|
480
|
+
const marker=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-session.json'),'utf8'));
|
|
481
|
+
const auditDir=path.join(fx.state,'audit');await mkdir(auditDir,{recursive:true});
|
|
482
|
+
const audit=[
|
|
483
|
+
{event_type:'tool.request',tool:'machine_fs',request_id:'filter-write-1234',created_at_utc:'2026-09-19T01:00:00.000Z',
|
|
484
|
+
args:{op:'write',root_id:'projects',path:'x.txt',goal_id:'goal-1234',content:'SECRET'}},
|
|
485
|
+
{event_type:'tool.result',tool:'machine_fs',request_id:'filter-write-1234',created_at_utc:'2026-09-19T01:00:00.010Z',
|
|
486
|
+
outcome:{is_error:true,duration_ms:10,reason:'denied'}},
|
|
487
|
+
{event_type:'tool.request',tool:'coding_file_read',request_id:'filter-read-12345',created_at_utc:'2026-09-19T01:00:00.020Z',
|
|
488
|
+
args:{root_id:'projects',path:'x.txt',goal_id:'goal-1234'}},
|
|
489
|
+
{event_type:'tool.result',tool:'coding_file_read',request_id:'filter-read-12345',created_at_utc:'2026-09-19T01:00:00.030Z',
|
|
490
|
+
outcome:{is_error:true,duration_ms:10,reason:'missing'}}
|
|
491
|
+
];
|
|
492
|
+
await writeFile(path.join(auditDir,'segment-00000001.jsonl'),audit.map(JSON.stringify).join('\n')+'\n','utf8');
|
|
493
|
+
const deadline=Date.now()+4000;
|
|
494
|
+
while(Date.now()<deadline&&!/machine_fs\.write.*trace=goal-1234/.test(output.stdout)) await sleep(50);
|
|
495
|
+
assert.match(output.stdout,/machine_fs\.write.*trace=goal-123\.\.\. span=filter-w\.\.\..*10ms.*ERR - denied/);
|
|
496
|
+
assert.doesNotMatch(output.stdout,/coding_file_read.*missing/);
|
|
497
|
+
assert.doesNotMatch(output.stdout,/SECRET/);
|
|
498
|
+
const journal=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-results',marker.result_session_id+'.json'),'utf8'));
|
|
499
|
+
assert.equal(journal.events.length,4,'presentation filter must not remove journal evidence');
|
|
500
|
+
child.stdin.end();
|
|
501
|
+
const ended=await Promise.race([output.closed,sleep(6000).then(()=>({timeout:true}))]);
|
|
502
|
+
assert.equal(ended.timeout,undefined);
|
|
503
|
+
assert.equal(ended.code,0,output.stderr);
|
|
504
|
+
}finally{
|
|
505
|
+
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
506
|
+
await rm(fx.dir,{recursive:true,force:true});
|
|
507
|
+
}
|
|
508
|
+
});
|
package/test/tool-audit.test.mjs
CHANGED
|
@@ -132,3 +132,20 @@ test('controller enables audit only for Bridge child and restores parent audit e
|
|
|
132
132
|
assert.match(source,/if \(\$hadAuditRuntimeEnv\) \{ \$env:DEADBYTE_AUDIT_RUNTIME = \$previousAuditRuntimeEnv \}/);
|
|
133
133
|
assert.match(source,/else \{ Remove-Item Env:DEADBYTE_AUDIT_RUNTIME -ErrorAction SilentlyContinue \}/);
|
|
134
134
|
});
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
test('audit outcome reason preserves ordinary letters and Windows paths while folding actual whitespace',async()=>{
|
|
138
|
+
const root=await temp();
|
|
139
|
+
try{
|
|
140
|
+
const k=await keys(root);
|
|
141
|
+
const audit=await createToolAudit({stateRoot:root,signingKeyPath:k.privateKeyPath,contextProvider:async()=>context()});
|
|
142
|
+
const server=fakeServer();installToolAudit(server,audit);
|
|
143
|
+
server.registerTool('broken_read',{},async()=>({
|
|
144
|
+
isError:true,
|
|
145
|
+
structuredContent:{status:'error',error:"Error:\tENOENT: no such file or directory,\nrealpath 'D:\\\\Projects\\\\runtime'"}
|
|
146
|
+
}));
|
|
147
|
+
await server.handlers.get('broken_read')({path:'x'});
|
|
148
|
+
const all=await events(root);
|
|
149
|
+
assert.equal(all.at(-1).outcome.reason,"Error: ENOENT: no such file or directory, realpath 'D:\\\\Projects\\\\runtime'");
|
|
150
|
+
}finally{await rm(root,{recursive:true,force:true});}
|
|
151
|
+
});
|