deadbyte-mcp 0.11.3 → 0.13.0
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 +126 -54
- package/R41-PARENT.json +15 -0
- package/README.txt +5 -4
- package/acceptance/r40/desktop-live-drift.mjs +106 -0
- package/acceptance/r40/desktop-live-ground.mjs +110 -0
- package/acceptance/r40/desktop-live-input.mjs +128 -0
- package/acceptance/r40/desktop-live-observe.mjs +100 -0
- package/acceptance/r40/desktop-live-uia.mjs +121 -0
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-desktop-input-r40.exe +0 -0
- package/bin/deadbyte-desktop-input-r40.obj +0 -0
- package/bin/deadbyte-desktop-observer-r40.exe +0 -0
- package/bin/deadbyte-desktop-observer-r40.obj +0 -0
- package/bin/deadbyte-desktop-uia-r40.exe +0 -0
- package/bin/deadbyte-desktop-uia-r40.obj +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/README.TXT +7 -5
- package/controller/deadbyte-controller.config.psd1.example +3 -0
- package/controller/deadbyte-controller.ps1 +149 -4
- package/controller/deadbyte-desktop-policy.json +41 -0
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/ARCHITECTURE.md +6 -1
- package/docs/superpowers/specs/2026-09-19-r40-desktop-computer-use-design.md +214 -0
- package/docs/superpowers/specs/2026-09-20-r41-deterministic-memory-design.md +132 -0
- package/native/deadbyte-desktop-input.cpp +455 -0
- package/native/deadbyte-desktop-observer.cpp +253 -0
- package/native/deadbyte-desktop-uia.cpp +307 -0
- package/package.json +12 -8
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/build-containment.ps1 +24 -14
- package/scripts/build-desktop-r40.ps1 +112 -0
- package/scripts/deferred-slot-operation-r40.mjs +126 -0
- package/scripts/deferred-slot-operation-r41.mjs +127 -0
- package/scripts/final-closure-r40.mjs +114 -0
- package/scripts/final-closure-r41.mjs +116 -0
- package/scripts/final-closure-verify-r40.mjs +212 -0
- package/scripts/final-closure-verify-r41.mjs +215 -0
- package/scripts/gate-windows-r40.ps1 +43 -0
- package/scripts/gate-windows-r41.ps1 +48 -0
- package/scripts/init-desktop-policy-r40.ps1 +85 -0
- package/scripts/r40-compact-ledger-probe.mjs +4 -0
- package/scripts/release-parity-r40.mjs +212 -0
- package/scripts/release-parity-r41.mjs +221 -0
- package/scripts/release-parity-tests-r40.ps1 +9 -0
- package/scripts/release-parity-tests-r41.ps1 +9 -0
- package/scripts/verify-memory-r41.mjs +88 -0
- package/scripts/verify-r41-parent.mjs +61 -0
- package/scripts/windows-gate-evidence-r40.mjs +52 -0
- package/scripts/windows-gate-evidence-r41.mjs +53 -0
- package/src/autonomous-context-engine.mjs +3 -1
- package/src/autonomous-planner-contracts.mjs +19 -1
- package/src/autonomous-planner.mjs +24 -0
- package/src/autonomous-runtime.mjs +164 -25
- package/src/capability-ledger.mjs +6 -1
- package/src/compact-gateway-mcp-tools.mjs +14 -2
- package/src/deadbyte-cli.mjs +72 -44
- package/src/desktop-contract.mjs +263 -0
- package/src/desktop-input-r40.mjs +148 -0
- package/src/desktop-mcp-tools-r40.mjs +126 -0
- package/src/desktop-memory-r40.mjs +272 -0
- package/src/desktop-observer-r40.mjs +231 -0
- package/src/desktop-policy-r40.mjs +110 -0
- package/src/desktop-runtime-r40.mjs +318 -0
- package/src/desktop-uia-r40.mjs +123 -0
- package/src/mcp-server.mjs +87 -19
- package/src/memory-r41.mjs +692 -0
- package/src/r40-compact-disarmed-probe.mjs +71 -0
- package/src/r40-full-surface-probe.mjs +34 -0
- package/src/release-generation.mjs +38 -5
- package/src/remote-console.mjs +41 -0
- package/src/remote-live-log.mjs +208 -45
- package/src/remote-mcp-desktop-r40-smoke-client.mjs +70 -0
- package/src/remote-mcp-r40-parity-client.mjs +200 -0
- package/src/remote-mcp-smoke-client.mjs +20 -6
- package/src/remote-result-journal.mjs +10 -2
- package/src/runtime-update.mjs +55 -17
- package/src/tool-audit.mjs +29 -2
- package/src/version.mjs +1 -1
- package/test/autonomous-capability-r36.test.mjs +43 -1
- package/test/autonomous-desktop-r40.test.mjs +188 -0
- package/test/capability-ledger-r36.test.mjs +1 -0
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/compact-catalog-r36.test.mjs +2 -2
- package/test/compact-gateway-r36.test.mjs +56 -4
- package/test/compact-max-catalog-r40.test.mjs +95 -0
- package/test/controller-desktop-r40.test.mjs +86 -0
- package/test/core.test.mjs +1 -1
- package/test/desktop-adversarial-r40.test.mjs +62 -0
- package/test/desktop-contract-r40.test.mjs +200 -0
- package/test/desktop-input-r40.test.mjs +63 -0
- package/test/desktop-mcp-tools-r40.test.mjs +61 -0
- package/test/desktop-memory-r40.test.mjs +141 -0
- package/test/desktop-observer-r40.test.mjs +119 -0
- package/test/desktop-policy-r40.test.mjs +111 -0
- package/test/desktop-runtime-r40.test.mjs +278 -0
- package/test/desktop-uia-r40.test.mjs +42 -0
- package/test/full-max-catalog-r40.test.mjs +91 -0
- package/test/helpers/autonomous-r34-fixture.mjs +3 -3
- package/test/memory-autonomous-integration-r41.test.mjs +31 -0
- package/test/memory-history-r41.test.mjs +126 -0
- package/test/memory-projections-r41.test.mjs +92 -0
- package/test/memory-retrieval-r41.test.mjs +124 -0
- package/test/memory-snapshot-r41.test.mjs +106 -0
- package/test/r33-closeout-regression.test.mjs +7 -7
- package/test/r33-finalization.test.mjs +15 -15
- package/test/r37-remote-tool-call-timeline.test.mjs +6 -5
- package/test/r40-finalization.test.mjs +118 -0
- package/test/r41-finalization.test.mjs +38 -0
- package/test/release-generation.test.mjs +41 -9
- package/test/release-version.test.mjs +20 -61
- package/test/remote-console-r40.test.mjs +38 -0
- package/test/remote-live-log-r37.test.mjs +89 -32
- package/test/remote-r24-catalog.test.mjs +12 -3
- package/test/remote-result-journal.test.mjs +15 -0
- package/test/remote-session-cli.test.mjs +69 -46
- package/test/runtime-self-update.test.mjs +44 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
|
|
7
|
+
import { R40_COMPACT_GATEWAYS,R40_COMPACT_TOOL_LIMIT } from './remote-mcp-r40-parity-client.mjs';
|
|
8
|
+
import { loadDesktopPolicy } from './desktop-policy-r40.mjs';
|
|
9
|
+
import { verifyDesktopReceiptObject } from './desktop-runtime-r40.mjs';
|
|
10
|
+
import { loadPublicVerifyKey } from './trust.mjs';
|
|
11
|
+
|
|
12
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
13
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
14
|
+
const verifyKeyPath=path.join(stateRoot,'trust','ed25519-public.pem');
|
|
15
|
+
function structured(result,label){
|
|
16
|
+
if(result.isError) throw new Error(label+' returned error: '+JSON.stringify(result.structuredContent??result.content));
|
|
17
|
+
if(!result.structuredContent||typeof result.structuredContent!=='object') throw new Error(label+' missing structured output');
|
|
18
|
+
return result.structuredContent;
|
|
19
|
+
}
|
|
20
|
+
function gateway(result,label){
|
|
21
|
+
const value=structured(result,label);
|
|
22
|
+
if(value.status!=='ok'||!value.result||typeof value.result!=='object') throw new Error(label+' gateway status='+String(value.status));
|
|
23
|
+
return value.result;
|
|
24
|
+
}
|
|
25
|
+
async function verifyDesktop(policy,verifyKey,id,sha,kind){
|
|
26
|
+
const receipt=JSON.parse(await readFile(path.join(policy.evidence_dir,id+'.json'),'utf8'));
|
|
27
|
+
const proof=verifyDesktopReceiptObject(receipt,{policySha256:policy.policy_sha256,publicKey:verifyKey.publicKey,keyId:verifyKey.keyId});
|
|
28
|
+
if(!proof.ok||proof.receipt_sha256!==sha||proof.kind!==kind) throw new Error('R40 desktop '+kind+' receipt verification failed');
|
|
29
|
+
}
|
|
30
|
+
export async function probeR40CompactDisarmed(endpointText){
|
|
31
|
+
const endpoint=new URL(endpointText);if(endpoint.protocol!=='https:')throw new Error('R40 compact disarmed probe requires HTTPS');
|
|
32
|
+
const pointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
|
|
33
|
+
const desktopPolicy=await loadDesktopPolicy(path.join(pointer.generation_root,'desktop-policy.json'));
|
|
34
|
+
const verifyKey=await loadPublicVerifyKey(verifyKeyPath);
|
|
35
|
+
const client=new Client({name:'deadbyte-r40-compact-disarmed-probe',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
36
|
+
const transport=new StreamableHTTPClientTransport(endpoint);
|
|
37
|
+
try{
|
|
38
|
+
await client.connect(transport);
|
|
39
|
+
const listed=await client.listTools(),names=listed.tools.map(t=>t.name).sort();
|
|
40
|
+
if(names.length!==R40_COMPACT_TOOL_LIMIT)throw new Error('R40 compact disarmed catalog must be exactly '+R40_COMPACT_TOOL_LIMIT+', got '+names.length);
|
|
41
|
+
for(const name of R40_COMPACT_GATEWAYS)if(!names.includes(name))throw new Error('R40 compact gateway missing '+name);
|
|
42
|
+
for(const tool of listed.tools)if(tool.outputSchema?.type!=='object'||tool.outputSchema?.additionalProperties!==false||!tool.outputSchema?.properties)throw new Error('tool '+tool.name+' missing strict output schema');
|
|
43
|
+
const manifest=gateway(await client.callTool({name:'capability_manifest',arguments:{}}),'capability_manifest');
|
|
44
|
+
if(manifest.schema!=='deadbyte.capability-manifest.v2'||manifest.profile!=='compact'||manifest.tool_count!==names.length||
|
|
45
|
+
JSON.stringify(manifest.tool_catalog)!==JSON.stringify(names))throw new Error('R40 compact manifest malformed');
|
|
46
|
+
const required=manifest.capabilities.filter(x=>x?.production===true&&x?.host_only!==true);
|
|
47
|
+
for(const item of required)if(typeof item.compact_tool!=='string'||!names.includes(item.compact_tool))throw new Error('compact route missing '+String(item.id));
|
|
48
|
+
const coding=structured(await client.callTool({name:'coding_session_status',arguments:{}}),'coding status');
|
|
49
|
+
const autonomous=structured(await client.callTool({name:'autonomous_session_status',arguments:{}}),'autonomous status');
|
|
50
|
+
const desktop=gateway(await client.callTool({name:'desktop_control',arguments:{op:'status'}}),'desktop status');
|
|
51
|
+
if(coding.armed!==false||autonomous.coding_armed!==false||autonomous.autonomous_armed!==false||desktop.armed!==false)throw new Error('R40 closure requires Coding/Autonomous/Desktop DISARMED');
|
|
52
|
+
const observed=gateway(await client.callTool({name:'desktop_control',arguments:{op:'observe',preview:false}}),'desktop observe');
|
|
53
|
+
await verifyDesktop(desktopPolicy,verifyKey,observed.receipt_id,observed.receipt_sha256,'desktop_observation');
|
|
54
|
+
const grounded=gateway(await client.callTool({name:'desktop_control',arguments:{op:'target',frame_id:observed.frame.frame_id,uia_query:{kind:'root',value:'',max_results:1,ordinal:0}}}),'desktop target');
|
|
55
|
+
await verifyDesktop(desktopPolicy,verifyKey,grounded.grounding.receipt_id,grounded.grounding.receipt_sha256,'desktop_grounding');
|
|
56
|
+
const machine=gateway(await client.callTool({name:'machine_observe',arguments:{op:'machine_status'}}),'machine status');
|
|
57
|
+
const nonce='r40-final-'+randomBytes(16).toString('hex');
|
|
58
|
+
const ping=gateway(await client.callTool({name:'machine_observe',arguments:{op:'machine_ping',nonce}}),'machine ping');
|
|
59
|
+
if(ping.nonce!==nonce||ping.release_id!==machine.release_id||ping.release_manifest_sha256!==machine.release_manifest_sha256)throw new Error('R40 machine ping binding mismatch');
|
|
60
|
+
return {status:'passed',profile:'compact',tool_count:names.length,tools:names,catalog_sha256:sha256(Buffer.from(JSON.stringify(names),'utf8')),
|
|
61
|
+
strict_output_schemas:true,ledger_sha256:manifest.ledger_sha256,capability_count:manifest.capabilities.length,
|
|
62
|
+
production_non_host_capabilities:required.length,capability_closure:true,coding_armed:false,autonomous_armed:false,desktop_armed:false,
|
|
63
|
+
desktop_observation_receipt_verified:true,desktop_grounding_receipt_verified:true,
|
|
64
|
+
desktop_frame_id:observed.frame.frame_id,desktop_target_id:grounded.target.target_id,
|
|
65
|
+
release_id:machine.release_id,release_manifest_sha256:machine.release_manifest_sha256,machine_status:machine,machine_ping:ping,nonce,
|
|
66
|
+
protocol_revision:client.getNegotiatedProtocolVersion()};
|
|
67
|
+
}finally{await client.close().catch(()=>{});}
|
|
68
|
+
}
|
|
69
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
70
|
+
if(invoked){const endpoint=process.argv[2];if(!endpoint)throw new Error('usage: node src/r40-compact-disarmed-probe.mjs https://host/mcp/path');
|
|
71
|
+
probeR40CompactDisarmed(endpoint).then(v=>console.log(JSON.stringify(v,null,2))).catch(e=>{console.error(e instanceof Error?e.stack:String(e));process.exitCode=1;});}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { Client } from '@modelcontextprotocol/client';
|
|
6
|
+
import { StdioClientTransport,getDefaultEnvironment } from '@modelcontextprotocol/client/stdio';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { R40_EXPECTED_TOOLS } from './remote-mcp-r40-parity-client.mjs';
|
|
9
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
10
|
+
export async function probeR40FullSurface({packageRoot,generationRoot,stateRoot=path.join(os.homedir(),'.deadbyte-mcp')}={}){
|
|
11
|
+
const pkgRoot=packageRoot?path.resolve(packageRoot):path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
12
|
+
const gen=path.resolve(generationRoot),semanticPath=path.join(gen,'semantic-lsp-config.json');
|
|
13
|
+
const semanticSha=sha256(await readFile(semanticPath));
|
|
14
|
+
const env={...getDefaultEnvironment(),DEADBYTE_STATE_ROOT:path.resolve(stateRoot),DEADBYTE_CORE_ROOT:pkgRoot,
|
|
15
|
+
DEADBYTE_WORKSPACE_ROOT:path.join(pkgRoot,'workspaces'),DEADBYTE_CAPABILITY_PROFILE:'full',DEADBYTE_AUDIT_RUNTIME:'0',
|
|
16
|
+
DEADBYTE_MACHINE_RUNTIME:'1',DEADBYTE_AGENT_RUNTIME:'1',DEADBYTE_PROCESS_RUNTIME:'1',DEADBYTE_PROCESS_POLICY:path.join(gen,'process-policy.json'),
|
|
17
|
+
DEADBYTE_CODING_RUNTIME:'1',DEADBYTE_MACHINE_FS_RUNTIME:'1',DEADBYTE_OBSERVATION_RUNTIME:'1',
|
|
18
|
+
DEADBYTE_DESKTOP_RUNTIME:'1',DEADBYTE_DESKTOP_POLICY:path.join(gen,'desktop-policy.json'),
|
|
19
|
+
DEADBYTE_CODING_POLICY:path.join(gen,'coding-policy.json'),DEADBYTE_SEMANTIC_LSP_CONFIG:semanticPath,DEADBYTE_SEMANTIC_LSP_CONFIG_SHA256:semanticSha,
|
|
20
|
+
DEADBYTE_AUTONOMOUS_RUNTIME:'1',DEADBYTE_AUTONOMOUS_POLICY:path.join(gen,'autonomous-policy.json'),
|
|
21
|
+
DEADBYTE_AUTONOMOUS_SUPERVISOR_MODE:'external',DEADBYTE_PRIVILEGED_POLICY:path.join(gen,'host-policy.json')};
|
|
22
|
+
const client=new Client({name:'deadbyte-r40-full-surface-probe',version:'0.12.0'},{versionNegotiation:{mode:{pin:'2026-07-28'}}});
|
|
23
|
+
const transport=new StdioClientTransport({command:process.execPath,args:[path.join(pkgRoot,'src','mcp-server.mjs')],cwd:pkgRoot,env,stderr:'pipe'});
|
|
24
|
+
try{
|
|
25
|
+
await client.connect(transport);const listed=await client.listTools(),names=listed.tools.map(t=>t.name).sort();
|
|
26
|
+
if(JSON.stringify(names)!==JSON.stringify(R40_EXPECTED_TOOLS))throw new Error('R40 full catalog mismatch count='+names.length);
|
|
27
|
+
for(const tool of listed.tools)if(tool.outputSchema?.type!=='object'||tool.outputSchema?.additionalProperties!==false||!tool.outputSchema?.properties)throw new Error('R40 full tool '+tool.name+' missing strict output schema');
|
|
28
|
+
return {status:'passed',profile:'full',tool_count:names.length,tools:names,catalog_sha256:sha256(Buffer.from(JSON.stringify(names),'utf8')),
|
|
29
|
+
strict_output_schemas:true,desktop_tools:['desktop_status','desktop_observe','desktop_target','desktop_action'],protocol_revision:client.getNegotiatedProtocolVersion()};
|
|
30
|
+
}finally{await client.close().catch(()=>{});}
|
|
31
|
+
}
|
|
32
|
+
if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){const [packageRoot,generationRoot]=process.argv.slice(2);
|
|
33
|
+
if(!packageRoot||!generationRoot)throw new Error('usage: node src/r40-full-surface-probe.mjs <package-root> <generation-root>');
|
|
34
|
+
probeR40FullSurface({packageRoot,generationRoot}).then(v=>console.log(JSON.stringify(v,null,2))).catch(e=>{console.error(e instanceof Error?e.stack:String(e));process.exitCode=1;});}
|
|
@@ -118,7 +118,31 @@ async function rebindProcessPolicy(raw,{developmentRoot,stateRoot,releaseRoot}){
|
|
|
118
118
|
return policy;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
function
|
|
121
|
+
async function rebindDesktopPolicy(raw,{stateRoot,releaseRoot}){
|
|
122
|
+
if(!raw) return null;
|
|
123
|
+
const policy=clone(raw);
|
|
124
|
+
if(policy.schema!=='deadbyte.desktop-policy.v1'||policy.enabled!==true) throw new Error('desktop policy schema/enabled mismatch');
|
|
125
|
+
policy.grant_file=path.join(stateRoot,'runtime','desktop-grant.json');
|
|
126
|
+
policy.evidence_dir=path.join(stateRoot,'runtime','desktop-evidence');
|
|
127
|
+
const providers={
|
|
128
|
+
observer:'deadbyte-desktop-observer-r40.exe',
|
|
129
|
+
input:'deadbyte-desktop-input-r40.exe',
|
|
130
|
+
grounder:'deadbyte-desktop-uia-r40.exe'
|
|
131
|
+
};
|
|
132
|
+
for(const [name,file] of Object.entries(providers)){
|
|
133
|
+
const provider=policy[name];
|
|
134
|
+
if(!provider||typeof provider.sha256!=='string'||!HEX64.test(provider.sha256)) throw new Error(`desktop ${name} policy pin invalid`);
|
|
135
|
+
const providerPath=path.join(releaseRoot,'bin',file);
|
|
136
|
+
const stat=await lstat(providerPath);
|
|
137
|
+
if(!stat.isFile()||stat.isSymbolicLink()) throw new Error(`desktop ${name} helper invalid in release slot`);
|
|
138
|
+
const actual=sha256(await readFile(providerPath));
|
|
139
|
+
if(actual!==provider.sha256) throw new Error(`desktop ${name} helper SHA-256 mismatch: policy=${provider.sha256} release=${actual}`);
|
|
140
|
+
provider.executable=providerPath;
|
|
141
|
+
}
|
|
142
|
+
return policy;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function renderControllerConfig({currentConfig,coreRoot,workspaceRoot,processPolicyPath,desktopPolicyPath,codingPolicyPath,semanticConfigPath,semanticConfigSha256,autonomousPolicyPath,hostPolicyPath}){
|
|
122
146
|
const supervisor=currentConfig.Supervisor??{},bridge=currentConfig.Bridge??{},cloud=currentConfig.Cloud??{};
|
|
123
147
|
return [
|
|
124
148
|
'@{'," Schema = 'deadbyte.controller.v1'",` CoreRoot = ${psQuote(coreRoot)}`,
|
|
@@ -130,6 +154,8 @@ function renderControllerConfig({currentConfig,coreRoot,workspaceRoot,processPol
|
|
|
130
154
|
` CodingRuntimeEnabled = ${psBool(currentConfig.CodingRuntimeEnabled)}`,
|
|
131
155
|
` MachineFsRuntimeEnabled = ${psBool(currentConfig.MachineFsRuntimeEnabled)}`,
|
|
132
156
|
` ObservationRuntimeEnabled = ${psBool(currentConfig.ObservationRuntimeEnabled)}`,
|
|
157
|
+
` DesktopRuntimeEnabled = ${psBool(currentConfig.DesktopRuntimeEnabled)}`,
|
|
158
|
+
` DesktopPolicyFile = ${psQuote(desktopPolicyPath??'')}`,
|
|
133
159
|
` CodingPolicyFile = ${psQuote(codingPolicyPath)}`,
|
|
134
160
|
` SemanticLspConfigFile = ${psQuote(semanticConfigPath??'')}`,
|
|
135
161
|
` SemanticLspConfigSha256 = ${psQuote(semanticConfigSha256??'')}`,
|
|
@@ -172,7 +198,7 @@ function renderControllerConfig({currentConfig,coreRoot,workspaceRoot,processPol
|
|
|
172
198
|
'}',''
|
|
173
199
|
].join('\r\n');
|
|
174
200
|
}
|
|
175
|
-
export async function createGenerationBundle({stateRoot,releaseRoot,developmentRoot,workspaceRoot,releaseId,currentConfig,processPolicy,codingPolicy,autonomousPolicy,hostPolicy}){
|
|
201
|
+
export async function createGenerationBundle({stateRoot,releaseRoot,developmentRoot,workspaceRoot,releaseId,currentConfig,processPolicy,desktopPolicy,codingPolicy,autonomousPolicy,hostPolicy}){
|
|
176
202
|
const state=path.resolve(stateRoot),release=path.resolve(releaseRoot),development=path.resolve(developmentRoot),workspace=path.resolve(workspaceRoot);
|
|
177
203
|
if(typeof releaseId!=='string'||!/^[A-Za-z0-9._-]{1,96}$/.test(releaseId)) throw new Error('generation release id invalid');
|
|
178
204
|
if(typeof currentConfig?.Bridge?.FileSha256!=='string'||!HEX64.test(currentConfig.Bridge.FileSha256)) throw new Error('generation Bridge FileSha256 invalid');
|
|
@@ -230,6 +256,9 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
230
256
|
const hostBytes=host?jsonBytes(host):null;
|
|
231
257
|
const processBound=await rebindProcessPolicy(processPolicy,{developmentRoot:development,stateRoot:state,releaseRoot:release});
|
|
232
258
|
const processBytes=processBound?jsonBytes(processBound):null;
|
|
259
|
+
const desktopBound=await rebindDesktopPolicy(desktopPolicy,{stateRoot:state,releaseRoot:release});
|
|
260
|
+
const desktopBytes=desktopBound?jsonBytes(desktopBound):null;
|
|
261
|
+
if(currentConfig.DesktopRuntimeEnabled===true&&!desktopBytes) throw new Error('DesktopRuntimeEnabled requires desktop policy');
|
|
233
262
|
const seedBody=canonical({
|
|
234
263
|
release_id:releaseId,core_root:release,development_root:development,workspace_root:workspace,
|
|
235
264
|
machine_runtime_enabled:currentConfig.MachineRuntimeEnabled!==false,
|
|
@@ -238,6 +267,7 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
238
267
|
coding_runtime_enabled:currentConfig.CodingRuntimeEnabled===true,
|
|
239
268
|
machine_fs_runtime_enabled:currentConfig.MachineFsRuntimeEnabled===true,
|
|
240
269
|
observation_runtime_enabled:currentConfig.ObservationRuntimeEnabled===true,
|
|
270
|
+
desktop_runtime_enabled:currentConfig.DesktopRuntimeEnabled===true,
|
|
241
271
|
autonomous_runtime_enabled:currentConfig.AutonomousRuntimeEnabled===true,
|
|
242
272
|
autonomous_supervisor_enabled:currentConfig.AutonomousSupervisorEnabled===true,
|
|
243
273
|
supervisor:currentConfig.AutonomousSupervisorEnabled===true?(currentConfig.Supervisor??{}):null,
|
|
@@ -247,6 +277,7 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
247
277
|
start_timeout_seconds:Number(currentConfig.StartTimeoutSeconds??30),probe_timeout_seconds:Number(currentConfig.ProbeTimeoutSeconds??3),
|
|
248
278
|
semantic_lsp_config_sha256:semanticConfigSha256,
|
|
249
279
|
process_policy_sha256:processBytes?sha256(processBytes):null,
|
|
280
|
+
desktop_policy_sha256:desktopBytes?sha256(desktopBytes):null,
|
|
250
281
|
coding_policy_sha256:sha256(codingBytes),autonomous_policy_sha256:sha256(autonomousBytes),host_policy_sha256:hostBytes?sha256(hostBytes):null
|
|
251
282
|
});
|
|
252
283
|
const generationSeed=sha256(Buffer.from(JSON.stringify(seedBody),'utf8'));
|
|
@@ -254,6 +285,7 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
254
285
|
const generationsRoot=path.join(state,'generations');
|
|
255
286
|
const generationRoot=path.join(generationsRoot,generationId);
|
|
256
287
|
const processPath=processBytes?path.join(generationRoot,'process-policy.json'):null;
|
|
288
|
+
const desktopPath=desktopBytes?path.join(generationRoot,'desktop-policy.json'):null;
|
|
257
289
|
const codingPath=path.join(generationRoot,'coding-policy.json');
|
|
258
290
|
const semanticPath=semanticBytes?path.join(generationRoot,'semantic-lsp-config.json'):null;
|
|
259
291
|
const autonomousPath=path.join(generationRoot,'autonomous-policy.json');
|
|
@@ -262,15 +294,16 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
262
294
|
const files=new Map([
|
|
263
295
|
['coding-policy.json',codingBytes],
|
|
264
296
|
['autonomous-policy.json',autonomousBytes],
|
|
265
|
-
['controller-config.psd1',Buffer.from(renderControllerConfig({currentConfig,coreRoot:release,workspaceRoot:workspace,processPolicyPath:processPath,codingPolicyPath:codingPath,semanticConfigPath:semanticPath,semanticConfigSha256,autonomousPolicyPath:autonomousPath,hostPolicyPath:hostPath}),'utf8')]
|
|
297
|
+
['controller-config.psd1',Buffer.from(renderControllerConfig({currentConfig,coreRoot:release,workspaceRoot:workspace,processPolicyPath:processPath,desktopPolicyPath:desktopPath,codingPolicyPath:codingPath,semanticConfigPath:semanticPath,semanticConfigSha256,autonomousPolicyPath:autonomousPath,hostPolicyPath:hostPath}),'utf8')]
|
|
266
298
|
]);
|
|
267
299
|
if(semanticBytes) files.set('semantic-lsp-config.json',semanticBytes);
|
|
268
300
|
if(processBytes) files.set('process-policy.json',processBytes);
|
|
301
|
+
if(desktopBytes) files.set('desktop-policy.json',desktopBytes);
|
|
269
302
|
if(hostBytes) files.set('host-policy.json',hostBytes);
|
|
270
303
|
const manifestBytes=generationManifest(files),manifestSha256=sha256(manifestBytes);
|
|
271
304
|
if(existsSync(generationRoot)){
|
|
272
305
|
const verified=await verifyGenerationBundle(generationRoot,manifestSha256);
|
|
273
|
-
return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:true,...verified};
|
|
306
|
+
return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,desktop_policy_path:desktopPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:true,...verified};
|
|
274
307
|
}
|
|
275
308
|
await mkdir(generationsRoot,{recursive:true});
|
|
276
309
|
const staging=path.join(generationsRoot,`.staging-${generationId}-${process.pid}-${randomBytes(4).toString('hex')}`);
|
|
@@ -285,5 +318,5 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
285
318
|
throw error;
|
|
286
319
|
}
|
|
287
320
|
const verified=await verifyGenerationBundle(generationRoot,manifestSha256);
|
|
288
|
-
return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:false,...verified};
|
|
321
|
+
return {generation_id:generationId,generation_seed_sha256:generationSeed,generation_root:generationRoot,generation_manifest_sha256:manifestSha256,config_path:configPath,process_policy_path:processPath,desktop_policy_path:desktopPath,coding_policy_path:codingPath,semantic_config_path:semanticPath,autonomous_policy_path:autonomousPath,host_policy_path:hostPath,adopted:false,...verified};
|
|
289
322
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
|
|
4
|
+
export const REMOTE_DISPLAY_MODES=Object.freeze(['unicode','ascii']);
|
|
5
|
+
|
|
6
|
+
function checkedMode(mode){
|
|
7
|
+
if(!REMOTE_DISPLAY_MODES.includes(mode)) throw new Error(`remote display mode invalid: ${String(mode)}`);
|
|
8
|
+
return mode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function remoteDisplayText(value,mode='unicode'){
|
|
12
|
+
checkedMode(mode);
|
|
13
|
+
const text=String(value??'');
|
|
14
|
+
if(mode==='unicode') return text;
|
|
15
|
+
let out='';
|
|
16
|
+
for(const ch of text){
|
|
17
|
+
const cp=ch.codePointAt(0);
|
|
18
|
+
if(cp===9||cp===10||cp===13||(cp>=0x20&&cp<=0x7e)) out+=ch;
|
|
19
|
+
else out+=`\\u{${cp.toString(16).toUpperCase()}}`;
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function prepareRemoteConsole({
|
|
25
|
+
platform=process.platform,
|
|
26
|
+
isTTY=Boolean(process.stdout?.isTTY),
|
|
27
|
+
env=process.env,
|
|
28
|
+
spawnSyncFn=spawnSync
|
|
29
|
+
}={}){
|
|
30
|
+
if(String(env?.DEADBYTE_REMOTE_ASCII??'')==='1') return Object.freeze({mode:'ascii',code_page:null,reason:'forced_ascii'});
|
|
31
|
+
if(platform!=='win32') return Object.freeze({mode:'unicode',code_page:null,reason:'non_windows'});
|
|
32
|
+
if(!isTTY) return Object.freeze({mode:'ascii',code_page:null,reason:'stdout_not_tty'});
|
|
33
|
+
const systemRoot=env?.SystemRoot||env?.WINDIR;
|
|
34
|
+
if(typeof systemRoot!=='string'||!systemRoot) return Object.freeze({mode:'ascii',code_page:null,reason:'system_root_missing'});
|
|
35
|
+
const chcp=path.win32.join(systemRoot,'System32','chcp.com');
|
|
36
|
+
let result;
|
|
37
|
+
try{ result=spawnSyncFn(chcp,['65001'],{stdio:'ignore',windowsHide:true,shell:false}); }
|
|
38
|
+
catch{ return Object.freeze({mode:'ascii',code_page:null,reason:'codepage_probe_threw'}); }
|
|
39
|
+
if(result?.error||result?.status!==0) return Object.freeze({mode:'ascii',code_page:null,reason:'codepage_65001_unavailable'});
|
|
40
|
+
return Object.freeze({mode:'unicode',code_page:65001,reason:'codepage_65001'});
|
|
41
|
+
}
|
package/src/remote-live-log.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { canonicalJson } from './canonical-json.mjs';
|
|
2
2
|
import { sanitizeAuditArgs } from './tool-audit.mjs';
|
|
3
|
+
import { remoteDisplayText, REMOTE_DISPLAY_MODES } from './remote-console.mjs';
|
|
3
4
|
|
|
4
5
|
const SAFE_ARG_KEYS=Object.freeze(['root_id','path','pattern','command_id','profile_id','session_id','goal_id','task_id','step_id','mode']);
|
|
5
6
|
const LIFECYCLE=Object.freeze({
|
|
@@ -11,6 +12,15 @@ const LIFECYCLE=Object.freeze({
|
|
|
11
12
|
ready:'✅ [READY] DEADBYTE MCP online',
|
|
12
13
|
cleanup:'🔐 [AUTH] Revoking session authority...'
|
|
13
14
|
});
|
|
15
|
+
const LIFECYCLE_ASCII=Object.freeze({
|
|
16
|
+
device_start:'[DEVICE] Starting DEADBYTE MCP...',
|
|
17
|
+
bridge_connected:'[BRIDGE] Local bridge connected',
|
|
18
|
+
cloud_connected:'[CLOUD] Remote MCP connected',
|
|
19
|
+
session_new:'[SESSION] New foreground session',
|
|
20
|
+
session_restored:'[SESSION] Previous result stream replayed',
|
|
21
|
+
ready:'[READY] DEADBYTE MCP online',
|
|
22
|
+
cleanup:'[AUTH] Revoking session authority...'
|
|
23
|
+
});
|
|
14
24
|
|
|
15
25
|
function safeAtom(value){
|
|
16
26
|
if(typeof value==='string'){
|
|
@@ -36,27 +46,97 @@ function summarizeArgs(args){
|
|
|
36
46
|
const TOOL_COLUMN_WIDTH=32;
|
|
37
47
|
const DISPLAY_UTC_OFFSET_MINUTES=7*60;
|
|
38
48
|
const DISPLAY_UTC_OFFSET_LABEL='+07:00';
|
|
49
|
+
const HEARTBEAT_TOOLS=new Set(['machine_ping','machine_status','machine_system_status','machine_list']);
|
|
50
|
+
const TOOL_ICONS=Object.freeze({read:'📖',write:'📝',edit:'✏️',run:'🏃',verify:'🧾',search:'🔍',heartbeat:'📡',desktop:'🖥️',autonomous:'🤖',process:'⚙️',generic:'•'});
|
|
51
|
+
const TOOL_ICONS_ASCII=Object.freeze({read:'R',write:'W',edit:'E',run:'>',verify:'V',search:'S',heartbeat:'H',desktop:'D',autonomous:'A',process:'P',generic:'*'});
|
|
52
|
+
const START_VISIBLE_PATTERNS=[/(?:^|_)file_write$/,/text_replace$/,/symbol_replace$/,/native_diff_(?:apply|preview)$/,
|
|
53
|
+
/(?:command|benchmark)_run$/,/task_run$/,/goal_run$/,/goal_submit$/,/goal_approve$/,/process_session_(?:start|action|terminate)$/,/^desktop_action$/];
|
|
54
|
+
|
|
55
|
+
export function truncateUtf8(value,maxBytes,{ellipsis='…'}={}){
|
|
56
|
+
if(typeof value!=='string'||!Number.isInteger(maxBytes)||maxBytes<1) return '';
|
|
57
|
+
const bytes=new TextEncoder().encode(value);
|
|
58
|
+
if(bytes.length<=maxBytes) return value;
|
|
59
|
+
const suffix=new TextEncoder().encode(ellipsis);
|
|
60
|
+
const budget=Math.max(0,maxBytes-suffix.length);
|
|
61
|
+
let end=Math.min(budget,bytes.length);
|
|
62
|
+
while(end>0&&(bytes[end]&0xc0)===0x80) end--;
|
|
63
|
+
return new TextDecoder('utf-8',{fatal:false}).decode(bytes.subarray(0,end))+(suffix.length<=maxBytes?ellipsis:'');
|
|
64
|
+
}
|
|
65
|
+
function clipped(value,max=72){
|
|
66
|
+
if(typeof value!=='string') return '';
|
|
67
|
+
const one=value.replace(/[\r\n\t]+/g,' ').replace(/\s{2,}/g,' ').trim();
|
|
68
|
+
return truncateUtf8(one,max);
|
|
69
|
+
}
|
|
70
|
+
function shortValue(value,max=12){
|
|
71
|
+
if(typeof value!=='string'||!value) return '';
|
|
72
|
+
return value.length<=max?value:value.slice(0,max)+'…';
|
|
73
|
+
}
|
|
74
|
+
function payloadByteCount(value){
|
|
75
|
+
return value&&typeof value==='object'&&value.redacted===true&&Number.isFinite(Number(value.byte_count))?Number(value.byte_count):null;
|
|
76
|
+
}
|
|
77
|
+
function rootPath(args){
|
|
78
|
+
const root=safeAtom(args?.root_id);const p=safeAtom(args?.path);
|
|
79
|
+
if(root&&p) return root+':'+p;
|
|
80
|
+
return root||p||'';
|
|
81
|
+
}
|
|
82
|
+
export function summarizeLiveToolArgs(tool,rawArgs){
|
|
83
|
+
const args=sanitizeAuditArgs(rawArgs??{});const where=rootPath(args);
|
|
84
|
+
if(/(?:file_read|files_read)$/.test(tool)) return where;
|
|
85
|
+
if(/file_write$/.test(tool)){const bytes=payloadByteCount(args.content);return [where,bytes!==null?'('+bytes+'B)':''].filter(Boolean).join(' ');}
|
|
86
|
+
if(/text_replace$|symbol_replace$/.test(tool)){const bytes=payloadByteCount(args.new_string);return [where,bytes!==null?'('+bytes+'B new)':''].filter(Boolean).join(' ');}
|
|
87
|
+
if(/coding_tree$/.test(tool)) return where;
|
|
88
|
+
if(/coding_(?:search|inspect)$/.test(tool)) return [where,args.pattern?'\"'+clipped(String(args.pattern),36)+'\"':'',args.op?String(args.op):''].filter(Boolean).join(' ');
|
|
89
|
+
if(/(?:command|benchmark)_run$/.test(tool)) return [safeAtom(args.root_id),safeAtom(args.command_id),safeAtom(args.script_path)].filter(Boolean).join(' ');
|
|
90
|
+
if(/^machine_(?:ping|status|system_status|list)$/.test(tool)) return safeAtom(args.nonce)?'nonce='+shortValue(String(args.nonce),24):'';
|
|
91
|
+
if(/^process_session/.test(tool)) return [safeAtom(args.profile_id),shortValue(safeAtom(args.session_id)||'',12),safeAtom(args.op)].filter(Boolean).join(' ');
|
|
92
|
+
if(/^desktop_/.test(tool)) return [shortValue(safeAtom(args.frame_id)||'',10),shortValue(safeAtom(args.target_id)||'',10),safeAtom(args.op)].filter(Boolean).join(' ');
|
|
93
|
+
if(/^autonomous_/.test(tool)) return [shortValue(safeAtom(args.goal_id)||'',12),safeAtom(args.step_id)].filter(Boolean).join(' ');
|
|
94
|
+
return summarizeArgs(args).trim();
|
|
95
|
+
}
|
|
96
|
+
function categoryForTool(tool){
|
|
97
|
+
if(/^desktop_/.test(tool)) return 'desktop';
|
|
98
|
+
if(/^autonomous_/.test(tool)) return 'autonomous';
|
|
99
|
+
if(HEARTBEAT_TOOLS.has(tool)) return 'heartbeat';
|
|
100
|
+
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)|_stat$|_tree$)/.test(tool)) return 'search';
|
|
102
|
+
if(/(?:file_read|files_read|machine_fs_read|process_session_read)/.test(tool)) return 'read';
|
|
103
|
+
if(/(?:file_write|machine_fs_write|mkdir|move|delete)/.test(tool)) return 'write';
|
|
104
|
+
if(/(?:replace|edit_exact|native_diff_apply)/.test(tool)) return 'edit';
|
|
105
|
+
if(/(?:command_run|benchmark_run|task_run|goal_run|process_session_(?:start|action|terminate))/.test(tool)) return 'run';
|
|
106
|
+
if(/^process_/.test(tool)) return 'process';
|
|
107
|
+
return 'generic';
|
|
108
|
+
}
|
|
109
|
+
function iconForTool(tool,displayMode='unicode'){
|
|
110
|
+
const icons=displayMode==='ascii'?TOOL_ICONS_ASCII:TOOL_ICONS;
|
|
111
|
+
return icons[categoryForTool(tool)]??icons.generic;
|
|
112
|
+
}
|
|
113
|
+
function visibleStart(tool){return START_VISIBLE_PATTERNS.some(pattern=>pattern.test(tool));}
|
|
39
114
|
|
|
40
115
|
function displayTimestamp(ms){
|
|
41
116
|
const shifted=new Date(ms+DISPLAY_UTC_OFFSET_MINUTES*60*1000).toISOString();
|
|
42
117
|
return `${shifted.slice(0,-1)}${DISPLAY_UTC_OFFSET_LABEL}`;
|
|
43
118
|
}
|
|
44
119
|
|
|
45
|
-
function
|
|
46
|
-
for(const value of [event?.created_at_utc,event?.observed_at_utc]){
|
|
120
|
+
function eventMs(event,clock){
|
|
121
|
+
for(const value of [event?.ts,event?.created_at_utc,event?.observed_at_utc]){
|
|
47
122
|
const parsed=Date.parse(value);
|
|
48
|
-
if(typeof value==='string'&&Number.isFinite(parsed)) return
|
|
123
|
+
if(typeof value==='string'&&Number.isFinite(parsed)) return parsed;
|
|
49
124
|
}
|
|
50
125
|
const value=Number(clock());
|
|
51
|
-
return
|
|
126
|
+
return Number.isFinite(value)?value:Date.now();
|
|
127
|
+
}
|
|
128
|
+
function eventTimestamp(event,clock){return displayTimestamp(eventMs(event,clock));}
|
|
129
|
+
function displayClock(ms){
|
|
130
|
+
const shifted=new Date(ms+DISPLAY_UTC_OFFSET_MINUTES*60*1000).toISOString();
|
|
131
|
+
return shifted.slice(11,23);
|
|
52
132
|
}
|
|
53
133
|
|
|
54
|
-
export function formatRemoteToolCall(event,{clock=()=>Date.now()}={}){
|
|
134
|
+
export function formatRemoteToolCall(event,{clock=()=>Date.now(),displayMode='unicode'}={}){
|
|
55
135
|
if(!event||typeof event!=='object'||Array.isArray(event)||event.event_type!=='tool.request'||
|
|
56
136
|
typeof event.tool!=='string'||!event.tool) return null;
|
|
57
137
|
if(typeof clock!=='function') throw new Error('remote tool-call clock must be a function');
|
|
58
138
|
const args=sanitizeAuditArgs(event.args??{});
|
|
59
|
-
return `${eventTimestamp(event,clock)} | ${event.tool.padEnd(TOOL_COLUMN_WIDTH)} | Arguments: ${canonicalJson(args)}
|
|
139
|
+
return remoteDisplayText(`${eventTimestamp(event,clock)} | ${event.tool.padEnd(TOOL_COLUMN_WIDTH)} | Arguments: ${canonicalJson(args)}`,displayMode);
|
|
60
140
|
}
|
|
61
141
|
|
|
62
142
|
function formatDuration(ms){
|
|
@@ -77,9 +157,10 @@ export function formatCompactAuditEvent(event){
|
|
|
77
157
|
return null;
|
|
78
158
|
}
|
|
79
159
|
|
|
80
|
-
export function formatRemoteLifecycle(kind){
|
|
160
|
+
export function formatRemoteLifecycle(kind,{displayMode='unicode'}={}){
|
|
81
161
|
if(typeof kind!=='string'||!Object.hasOwn(LIFECYCLE,kind)) throw new Error(`unknown remote lifecycle '${String(kind)}'`);
|
|
82
|
-
|
|
162
|
+
if(!REMOTE_DISPLAY_MODES.includes(displayMode)) throw new Error('remote display mode invalid');
|
|
163
|
+
return displayMode==='ascii'?LIFECYCLE_ASCII[kind]:LIFECYCLE[kind];
|
|
83
164
|
}
|
|
84
165
|
|
|
85
166
|
export function createLiveActivityTracker(){
|
|
@@ -109,56 +190,138 @@ function shortSpanId(requestId){
|
|
|
109
190
|
return typeof requestId==='string'&&requestId.length?requestId.slice(0,8):'????????';
|
|
110
191
|
}
|
|
111
192
|
|
|
112
|
-
export function createLiveTimelineTracker({clock=()=>Date.now()}={}){
|
|
193
|
+
export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,heartbeatBurstLimit=10,heartbeatWindowMs=3000,displayMode='unicode'}={}){
|
|
113
194
|
if(typeof clock!=='function') throw new Error('live timeline clock must be a function');
|
|
195
|
+
if(!REMOTE_DISPLAY_MODES.includes(displayMode)) throw new Error('remote display mode invalid');
|
|
196
|
+
if(!Number.isInteger(heartbeatBurstLimit)||heartbeatBurstLimit<2||heartbeatBurstLimit>100) throw new Error('heartbeatBurstLimit invalid');
|
|
197
|
+
if(!Number.isInteger(heartbeatWindowMs)||heartbeatWindowMs<250||heartbeatWindowMs>60000) throw new Error('heartbeatWindowMs invalid');
|
|
114
198
|
const pending=new Map();
|
|
115
199
|
const completed=new Set();
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
200
|
+
const toolCounts=new Map();
|
|
201
|
+
let resultCount=0,errorCount=0,totalDurationMs=0,burst=null,foldedHeartbeatLines=0;
|
|
202
|
+
|
|
203
|
+
function eventPrefix(event,sequence){
|
|
204
|
+
const seq=Number.isSafeInteger(sequence)&&sequence>0?'#'+sequence+' ':'';
|
|
205
|
+
return iconForTool(event.tool,displayMode)+' '+displayClock(eventMs(event,clock))+' '+seq+'['+shortSpanId(event.request_id)+'] '+event.tool;
|
|
206
|
+
}
|
|
207
|
+
function resultLine(event,span,sequence){
|
|
208
|
+
const error=event.outcome?.is_error===true;
|
|
209
|
+
const unknown=event.outcome?.side_effect_state==='unknown';
|
|
210
|
+
const duration=Number.isFinite(Number(event.outcome?.duration_ms))
|
|
211
|
+
?formatDuration(event.outcome.duration_ms)
|
|
212
|
+
:span?formatDuration(Math.max(0,clock()-span.started_at_ms)):'';
|
|
213
|
+
const summary=span?.summary||'';
|
|
214
|
+
const rawReason=typeof event.outcome?.reason==='string'&&event.outcome.reason?event.outcome.reason:
|
|
215
|
+
(unknown&&typeof event.outcome?.recovery?.reason==='string'?event.outcome.recovery.reason:'');
|
|
216
|
+
const reason=rawReason?clipped(rawReason,180):(unknown?'terminal result unavailable; inspect state before retry':'');
|
|
217
|
+
const marker=unknown?'OUTCOME_UNKNOWN':error?(displayMode==='ascii'?'ERR':'✗'):(displayMode==='ascii'?'OK':'✓');
|
|
218
|
+
return remoteDisplayText(eventPrefix(event,sequence)+(summary?' '+summary:'')+(duration?' '+duration:'')+' '+marker+(reason?' — '+reason:''),displayMode);
|
|
219
|
+
}
|
|
220
|
+
function burstLine(value){
|
|
221
|
+
if(!value) return null;
|
|
222
|
+
const avg=value.count?value.total_ms/value.count:0;
|
|
223
|
+
const seq=Number.isSafeInteger(value.last_sequence)&&value.last_sequence>0?'#'+value.last_sequence+' ':'';
|
|
224
|
+
const startClock=displayClock(value.first_ms);
|
|
225
|
+
const endClock=displayClock(value.last_ms);
|
|
226
|
+
const range=value.first_ms===value.last_ms?endClock:startClock.slice(0,8)+'–'+endClock.slice(0,8);
|
|
227
|
+
if(value.count>1) foldedHeartbeatLines+=value.count-1;
|
|
228
|
+
const marker=displayMode==='ascii'?'OK':'✓';
|
|
229
|
+
return remoteDisplayText(iconForTool(value.tool,displayMode)+' '+range+' '+seq+value.tool+' ×'+value.count+' avg '+formatDuration(avg)+' '+marker,displayMode);
|
|
230
|
+
}
|
|
231
|
+
function flushBurst(){
|
|
232
|
+
const line=burstLine(burst);
|
|
233
|
+
burst=null;
|
|
234
|
+
return line;
|
|
235
|
+
}
|
|
236
|
+
function addStats(event){
|
|
237
|
+
resultCount++;
|
|
238
|
+
toolCounts.set(event.tool,(toolCounts.get(event.tool)??0)+1);
|
|
239
|
+
const duration=Number(event.outcome?.duration_ms);
|
|
240
|
+
if(Number.isFinite(duration)&&duration>=0) totalDurationMs+=duration;
|
|
241
|
+
if(event.outcome?.is_error===true) errorCount++;
|
|
242
|
+
}
|
|
243
|
+
function observe(event,{sequence=null}={}){
|
|
244
|
+
if(!event||typeof event!=='object'||Array.isArray(event)) return null;
|
|
245
|
+
const requestId=typeof event.request_id==='string'&&event.request_id?event.request_id:null;
|
|
246
|
+
const tool=typeof event.tool==='string'&&event.tool?event.tool:null;
|
|
247
|
+
if(event.event_type==='tool.request'&&requestId&&tool){
|
|
248
|
+
if(pending.has(requestId)||completed.has(requestId)) return null;
|
|
249
|
+
const summary=summarizeLiveToolArgs(tool,event.args??{});
|
|
250
|
+
pending.set(requestId,{request_id:requestId,tool,summary,started_at_ms:clock()});
|
|
251
|
+
if(verbose) return formatRemoteToolCall(event,{clock,displayMode});
|
|
252
|
+
if(visibleStart(tool)) return eventPrefix(event,sequence)+(summary?' '+summary:'')+' …';
|
|
139
253
|
return null;
|
|
140
|
-
},
|
|
141
|
-
pending(){return [...pending.values()].map(({request_id,tool})=>({request_id,tool}));},
|
|
142
|
-
reconcileInterrupted(reason='session_end'){
|
|
143
|
-
const suffix=reason==='recovery'?'recovery ended before terminal result':
|
|
144
|
-
reason==='session_end'?'session ended before terminal result':'stream ended before terminal result';
|
|
145
|
-
const lines=[...pending.values()].map(item=>`⚠ [${shortSpanId(item.request_id)}] INTERRUPTED ${item.tool} — ${suffix}`);
|
|
146
|
-
pending.clear();
|
|
147
|
-
return lines;
|
|
148
254
|
}
|
|
255
|
+
if(event.event_type==='tool.result'&&requestId&&tool){
|
|
256
|
+
if(completed.has(requestId)) return null;
|
|
257
|
+
const span=pending.get(requestId)??null;
|
|
258
|
+
pending.delete(requestId);
|
|
259
|
+
completed.add(requestId);
|
|
260
|
+
addStats(event);
|
|
261
|
+
const error=event.outcome?.is_error===true;
|
|
262
|
+
const nowMs=eventMs(event,clock);
|
|
263
|
+
if(HEARTBEAT_TOOLS.has(tool)&&!error){
|
|
264
|
+
const duration=Number(event.outcome?.duration_ms);
|
|
265
|
+
if(burst&&burst.tool===tool&&nowMs-burst.last_ms<=heartbeatWindowMs){
|
|
266
|
+
burst.count++;
|
|
267
|
+
burst.last_ms=nowMs;
|
|
268
|
+
burst.last_sequence=sequence;
|
|
269
|
+
if(Number.isFinite(duration)&&duration>=0) burst.total_ms+=duration;
|
|
270
|
+
}else{
|
|
271
|
+
const prior=flushBurst();
|
|
272
|
+
burst={tool,count:1,total_ms:Number.isFinite(duration)&&duration>=0?duration:0,
|
|
273
|
+
first_ms:nowMs,last_ms:nowMs,first_sequence:sequence,last_sequence:sequence};
|
|
274
|
+
return prior;
|
|
275
|
+
}
|
|
276
|
+
if(burst.count>=heartbeatBurstLimit) return flushBurst();
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
const lines=[];
|
|
280
|
+
const prior=flushBurst();
|
|
281
|
+
if(prior) lines.push(prior);
|
|
282
|
+
lines.push(resultLine(event,span,sequence));
|
|
283
|
+
return lines.length===1?lines[0]:lines;
|
|
284
|
+
}
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
function reconcileInterrupted(reason='session_end'){
|
|
288
|
+
const suffix=reason==='recovery'?'recovery ended before terminal result':
|
|
289
|
+
reason==='session_end'?'session ended before terminal result':'stream ended before terminal result';
|
|
290
|
+
const lines=[];
|
|
291
|
+
const folded=flushBurst();
|
|
292
|
+
if(folded) lines.push(folded);
|
|
293
|
+
for(const item of pending.values()) lines.push(remoteDisplayText((displayMode==='ascii'?'[WARN]':'⚠')+' ['+shortSpanId(item.request_id)+'] OUTCOME_UNKNOWN '+item.tool+' — '+suffix+'; inspect state before retry',displayMode));
|
|
294
|
+
pending.clear();
|
|
295
|
+
return lines;
|
|
296
|
+
}
|
|
297
|
+
function summary(){
|
|
298
|
+
const top=[...toolCounts.entries()].sort((a,b)=>b[1]-a[1]||a[0].localeCompare(b[0])).slice(0,3)
|
|
299
|
+
.map(([tool,count])=>tool+'×'+count).join(', ');
|
|
300
|
+
return remoteDisplayText((displayMode==='ascii'?'[SUMMARY]':'📊 [SUMMARY]')+' '+resultCount+' calls | '+errorCount+' error'+(errorCount===1?'':'s')+
|
|
301
|
+
' | tool time '+(formatDuration(totalDurationMs)||'0ms')+
|
|
302
|
+
(foldedHeartbeatLines?' | folded '+foldedHeartbeatLines+' heartbeat lines':'')+
|
|
303
|
+
(top?' | top: '+top:''),displayMode);
|
|
304
|
+
}
|
|
305
|
+
return Object.freeze({
|
|
306
|
+
observe,
|
|
307
|
+
pending(){return [...pending.values()].map(({request_id,tool})=>({request_id,tool}));},
|
|
308
|
+
reconcileInterrupted,
|
|
309
|
+
summary
|
|
149
310
|
});
|
|
150
311
|
}
|
|
151
312
|
|
|
152
|
-
|
|
153
|
-
export function formatRuntimeDiagnosticLine(file,line){
|
|
313
|
+
export function formatRuntimeDiagnosticLine(file,line,{verbose=false,displayMode='unicode'}={}){
|
|
154
314
|
if(typeof file!=='string'||typeof line!=='string'||!line) return null;
|
|
155
315
|
const label=file.split(/[\\/]/).at(-1).replace(/\.log$/i,'').toUpperCase();
|
|
156
316
|
const input=line.match(/\[TOOL IN \]\s+\S+\s+#\d+\s+([A-Za-z0-9_.-]+)/);
|
|
157
|
-
if(input) return
|
|
317
|
+
if(input) return verbose?remoteDisplayText('['+label+'.DEBUG] IN '+input[1],displayMode):null;
|
|
158
318
|
const output=line.match(/\[TOOL OUT\]\s+\S+\s+#\d+\s+([A-Za-z0-9_.-]+)/);
|
|
159
319
|
if(output){
|
|
160
|
-
if(
|
|
161
|
-
return
|
|
320
|
+
if(!verbose) return null;
|
|
321
|
+
if(/"is_error"\s*:\s*true/.test(line)) return remoteDisplayText('['+label+'.DEBUG] ERR '+output[1],displayMode);
|
|
322
|
+
return remoteDisplayText('['+label+'.DEBUG] OUT '+output[1],displayMode);
|
|
162
323
|
}
|
|
324
|
+
const compact=line.match(/^\[TOOL\]\s+(\S+)\s+#(\d+)\s+(✓|✗|OK|ERR)\s+([A-Za-z0-9_.-]+)\s+([^\s]+)(?:\s+—\s+(.+))?$/u);
|
|
325
|
+
if(compact) return verbose?remoteDisplayText('['+label+'.DEBUG] #'+compact[2]+' '+compact[3]+' '+compact[4]+' '+compact[5]+(compact[6]?' — '+compact[6]:''),displayMode):null;
|
|
163
326
|
return null;
|
|
164
327
|
}
|