deadbyte-mcp 0.9.0 → 0.10.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 +119 -91
- package/README.txt +16 -11
- package/bench/fixtures/corpus.json +32 -13
- package/bench/fixtures/r36/case-map.json +8 -0
- package/bin/WORKER-REGISTRY.txt +3 -3
- package/bin/appcontainer-stage.exe +0 -0
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.exe +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.exe +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.exe +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.exe +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.exe +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.exe +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.exe +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.exe +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.exe +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-exec.exe +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-process-host.exe +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.exe +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/README.TXT +8 -6
- package/controller/deadbyte-controller.ps1 +48 -2
- package/controller/deadbyte-process-policy.json +5 -5
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/R36-CAPABILITY-EVIDENCE.md +43 -0
- package/docs/superpowers/plans/2026-09-18-r36-zero-loss-autonomy.md +739 -0
- package/docs/superpowers/specs/2026-09-18-r36-zero-loss-autonomy-design.md +640 -0
- package/package.json +1 -1
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/deferred-slot-operation.mjs +53 -15
- package/scripts/final-closure-verify.mjs +88 -39
- package/scripts/final-closure.mjs +63 -56
- package/scripts/gate-windows.ps1 +3 -3
- package/scripts/release-parity-tests.ps1 +9 -0
- package/scripts/release-parity.mjs +149 -139
- package/scripts/windows-gate-evidence.mjs +5 -5
- package/src/autonomous-context-engine.mjs +2 -1
- package/src/autonomous-mcp-tools.mjs +5 -1
- package/src/autonomous-output-contracts.mjs +4 -4
- package/src/autonomous-planner-contracts.mjs +36 -1
- package/src/autonomous-planner.mjs +25 -3
- package/src/autonomous-runtime.mjs +191 -21
- package/src/capability-benchmark-runner.mjs +86 -1
- package/src/capability-benchmark.mjs +45 -0
- package/src/capability-ledger.mjs +158 -0
- package/src/coding-mcp-tools.mjs +31 -17
- package/src/coding-output-contracts.mjs +48 -10
- package/src/coding-plane.mjs +45 -9
- package/src/coding-search.mjs +130 -47
- package/src/compact-gateway-mcp-tools.mjs +128 -0
- package/src/compact-gateway-output-contracts.mjs +16 -0
- package/src/deadbyte-cli.mjs +51 -28
- package/src/machine-fs-smoke-client.mjs +1 -1
- package/src/machine-fs.mjs +51 -8
- package/src/mcp-server.mjs +32 -9
- package/src/native-diff-v2.mjs +337 -0
- package/src/native-diff.mjs +50 -2
- package/src/observation-smoke-client.mjs +1 -1
- package/src/operator-surface.mjs +7 -1
- package/src/process-runtime.mjs +98 -11
- package/src/process-smoke-client.mjs +11 -3
- package/src/r36-compact-disarmed-probe.mjs +50 -0
- package/src/r36-full-surface-probe.mjs +56 -0
- package/src/remote-live-log.mjs +76 -0
- package/src/remote-mcp-autonomous-smoke-client.mjs +7 -3
- package/src/remote-mcp-coding-smoke-client.mjs +1 -1
- package/src/remote-mcp-r36-parity-client.mjs +175 -0
- package/src/remote-mcp-smoke-client.mjs +112 -56
- package/src/runtime-update.mjs +51 -1
- package/src/search-cursor.mjs +45 -0
- package/src/semantic-code.mjs +72 -5
- package/src/semantic-lsp-backend.mjs +39 -3
- package/src/version.mjs +1 -1
- package/test/autonomous-capability-r36.test.mjs +151 -0
- package/test/autonomous-runtime.test.mjs +2 -2
- package/test/capability-benchmark-r36.test.mjs +107 -0
- package/test/capability-benchmark-semantic-r36.test.mjs +52 -0
- package/test/capability-claims-r36.test.mjs +23 -0
- package/test/capability-ledger-r36.test.mjs +102 -0
- package/test/coding-catalog-consistency.test.mjs +2 -2
- package/test/coding-plane.test.mjs +2 -2
- package/test/coding-runtime.test.mjs +16 -0
- package/test/coding-search-r36.test.mjs +90 -0
- package/test/compact-catalog-r36.test.mjs +37 -0
- package/test/compact-gateway-r36.test.mjs +66 -0
- package/test/contained.test.mjs +1 -1
- package/test/core.test.mjs +1 -1
- package/test/deferred-slot-operation.test.mjs +9 -0
- package/test/fixtures/lsp-framed-server.mjs +2 -0
- package/test/helpers/autonomous-r34-fixture.mjs +5 -3
- package/test/multi-file-read.test.mjs +3 -0
- package/test/native-diff-topology-r36.test.mjs +105 -0
- package/test/package-boundary.test.mjs +6 -0
- package/test/process-longrun-r36.test.mjs +82 -0
- package/test/process-release-gate.test.mjs +9 -0
- package/test/production-docs.test.mjs +3 -3
- package/test/prompts-resources.test.mjs +7 -1
- package/test/r33-closeout-regression.test.mjs +4 -3
- package/test/r33-finalization.test.mjs +14 -9
- package/test/r36-finalization.test.mjs +104 -0
- package/test/r36-release-identity.test.mjs +52 -0
- package/test/release-version.test.mjs +18 -15
- package/test/remote-live-log-r36.test.mjs +37 -0
- package/test/remote-live-log-recovery-r36.test.mjs +41 -0
- package/test/remote-r24-catalog.test.mjs +13 -0
- package/test/remote-session-cli.test.mjs +40 -30
- package/test/runtime-self-update.test.mjs +58 -1
- package/test/semantic-lsp-backend.test.mjs +9 -0
- package/test/semantic-r36-rename.test.mjs +82 -0
- package/deadbyte-mcp-0.8.9.tgz +0 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export const CAPABILITY_LEDGER_SCHEMA='deadbyte.capability-ledger.v1';
|
|
4
|
+
const AUTHORITIES=new Set(['none','process','coding','autonomous','host']);
|
|
5
|
+
const PROFILE_ENV='DEADBYTE_CAPABILITY_PROFILE';
|
|
6
|
+
const COMPACT_TOOL_LIMIT=42;
|
|
7
|
+
|
|
8
|
+
function sha256(text){return createHash('sha256').update(text,'utf8').digest('hex');}
|
|
9
|
+
function canonicalJson(value){
|
|
10
|
+
if(value===null||typeof value!=='object') return JSON.stringify(value);
|
|
11
|
+
if(Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;
|
|
12
|
+
return `{${Object.keys(value).sort((a,b)=>a.localeCompare(b,'en')).map(key=>`${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(',')}}`;
|
|
13
|
+
}
|
|
14
|
+
function nonEmpty(value,label){if(typeof value!=='string'||value.length<1) throw new Error(`${label} must be non-empty`); return value;}
|
|
15
|
+
function nullableRoute(value,label){if(value===null) return null; return nonEmpty(value,label);}
|
|
16
|
+
function normalizedEntry(raw){
|
|
17
|
+
if(!raw||typeof raw!=='object'||Array.isArray(raw)) throw new Error('capability entry must be an object');
|
|
18
|
+
const authority=nonEmpty(raw.authority,'capability authority');
|
|
19
|
+
if(!AUTHORITIES.has(authority)) throw new Error(`unknown capability authority: ${authority}`);
|
|
20
|
+
const entry={
|
|
21
|
+
id:nonEmpty(raw.id,'capability id'),
|
|
22
|
+
full_tool:nonEmpty(raw.full_tool,'capability full_tool'),
|
|
23
|
+
compact_tool:nullableRoute(raw.compact_tool??null,'capability compact_tool'),
|
|
24
|
+
compact_op:nullableRoute(raw.compact_op??null,'capability compact_op'),
|
|
25
|
+
runtime_flag:raw.runtime_flag===null?null:nonEmpty(raw.runtime_flag,'capability runtime_flag'),
|
|
26
|
+
authority,
|
|
27
|
+
mutating:raw.mutating===true,
|
|
28
|
+
receipt_class:raw.receipt_class===null?null:nonEmpty(raw.receipt_class,'capability receipt_class'),
|
|
29
|
+
production:raw.production===true,
|
|
30
|
+
host_only:raw.host_only===true
|
|
31
|
+
};
|
|
32
|
+
if((entry.compact_tool===null)!==(entry.compact_op===null)) throw new Error(`capability ${entry.id} compact route requires both tool and op`);
|
|
33
|
+
if(entry.host_only&&entry.authority!=='host') throw new Error(`capability ${entry.id} host_only requires host authority`);
|
|
34
|
+
return Object.freeze(entry);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function createCapabilityLedger(entries){
|
|
38
|
+
if(!Array.isArray(entries)) throw new Error('capability entries must be an array');
|
|
39
|
+
const seen=new Set();
|
|
40
|
+
const normalized=entries.map(normalizedEntry);
|
|
41
|
+
for(const item of normalized){
|
|
42
|
+
if(seen.has(item.id)) throw new Error(`duplicate capability id: ${item.id}`);
|
|
43
|
+
seen.add(item.id);
|
|
44
|
+
}
|
|
45
|
+
normalized.sort((a,b)=>a.id.localeCompare(b.id,'en'));
|
|
46
|
+
const frozenEntries=Object.freeze(normalized.slice());
|
|
47
|
+
const body={schema:CAPABILITY_LEDGER_SCHEMA,entries:frozenEntries};
|
|
48
|
+
return Object.freeze({...body,ledger_sha256:sha256(canonicalJson(body))});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function capabilityProfileFromEnv(env=process.env){
|
|
52
|
+
const value=env?.[PROFILE_ENV]??'full';
|
|
53
|
+
if(value!=='full'&&value!=='compact') throw new Error(`${PROFILE_ENV} must be full or compact`);
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function toolNames(snapshot,label){
|
|
58
|
+
if(!snapshot||typeof snapshot!=='object'||!Array.isArray(snapshot.tools)) throw new Error(`${label} tool surface snapshot invalid`);
|
|
59
|
+
const names=snapshot.tools.map(tool=>tool?.name);
|
|
60
|
+
if(names.some(name=>typeof name!=='string'||name.length<1)) throw new Error(`${label} tool surface contains invalid name`);
|
|
61
|
+
return new Set(names);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function assertCapabilityClosure({ledger,fullToolSnapshot,compactToolSnapshot}){
|
|
65
|
+
if(!ledger||ledger.schema!==CAPABILITY_LEDGER_SCHEMA||!Array.isArray(ledger.entries)) throw new Error('capability ledger invalid');
|
|
66
|
+
const full=toolNames(fullToolSnapshot,'full');
|
|
67
|
+
const compact=toolNames(compactToolSnapshot,'compact');
|
|
68
|
+
if(compact.size>COMPACT_TOOL_LIMIT) throw new Error(`compact surface exceeds ${COMPACT_TOOL_LIMIT}-tool limit: ${compact.size}`);
|
|
69
|
+
for(const capability of ledger.entries){
|
|
70
|
+
if(!full.has(capability.full_tool)) throw new Error(`capability ${capability.id} full tool missing: ${capability.full_tool}`);
|
|
71
|
+
if(capability.production&&!capability.host_only){
|
|
72
|
+
if(!capability.compact_tool||!capability.compact_op) throw new Error(`capability ${capability.id} compact route missing`);
|
|
73
|
+
if(!compact.has(capability.compact_tool)) throw new Error(`capability ${capability.id} compact tool missing: ${capability.compact_tool}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const direct=(id,tool,runtime_flag,authority,{mutating=false,receipt_class='direct',production=true,host_only=false}={})=>({id,full_tool:tool,compact_tool:tool,compact_op:'direct',runtime_flag,authority,mutating,receipt_class,production,host_only});
|
|
80
|
+
const gateway=(id,full_tool,compact_tool,compact_op,runtime_flag,authority,{mutating=false,receipt_class='direct',production=true}={})=>({id,full_tool,compact_tool,compact_op,runtime_flag,authority,mutating,receipt_class,production,host_only:false});
|
|
81
|
+
|
|
82
|
+
export const R36_CAPABILITY_ENTRIES=Object.freeze([
|
|
83
|
+
direct('evidence.verify','evidence_verify',null,'none',{receipt_class:'verification'}),
|
|
84
|
+
gateway('machine.list','machine_list','machine_observe','machine_list','DEADBYTE_MACHINE_RUNTIME','none',{receipt_class:'machine'}),
|
|
85
|
+
gateway('machine.status','machine_status','machine_observe','machine_status','DEADBYTE_MACHINE_RUNTIME','none',{receipt_class:'machine'}),
|
|
86
|
+
gateway('machine.ping','machine_ping','machine_observe','machine_ping','DEADBYTE_MACHINE_RUNTIME','none',{receipt_class:'machine'}),
|
|
87
|
+
direct('machine.revoke','machine_revoke','DEADBYTE_MACHINE_RUNTIME','host',{mutating:true,receipt_class:'machine',production:false,host_only:true}),
|
|
88
|
+
|
|
89
|
+
gateway('process.start','process_session_start','process_session','start','DEADBYTE_PROCESS_RUNTIME','process',{mutating:true,receipt_class:'process'}),
|
|
90
|
+
gateway('process.read','process_session_read','process_session','read','DEADBYTE_PROCESS_RUNTIME','process',{receipt_class:'process'}),
|
|
91
|
+
gateway('process.status','process_session_status','process_session','status','DEADBYTE_PROCESS_RUNTIME','process',{receipt_class:'process'}),
|
|
92
|
+
gateway('process.list','process_session_list','process_session','list','DEADBYTE_PROCESS_RUNTIME','process',{receipt_class:'process'}),
|
|
93
|
+
gateway('process.action','process_session_action','process_session','action','DEADBYTE_PROCESS_RUNTIME','process',{mutating:true,receipt_class:'process'}),
|
|
94
|
+
gateway('process.terminate','process_session_terminate','process_session','terminate','DEADBYTE_PROCESS_RUNTIME','process',{mutating:true,receipt_class:'process'}),
|
|
95
|
+
|
|
96
|
+
direct('coding.session_status','coding_session_status','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'coding'}),
|
|
97
|
+
direct('coding.tree','coding_tree','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'coding'}),
|
|
98
|
+
gateway('coding.stat','coding_stat','coding_inspect','stat','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'coding'}),
|
|
99
|
+
gateway('coding.search','coding_search','coding_inspect','search','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'coding'}),
|
|
100
|
+
gateway('coding.files_read','coding_files_read','coding_inspect','files_read','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'coding'}),
|
|
101
|
+
gateway('semantic.outline','coding_symbol_outline','coding_inspect','symbol_outline','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'semantic'}),
|
|
102
|
+
gateway('semantic.definition','coding_symbol_definition','coding_inspect','symbol_definition','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'semantic'}),
|
|
103
|
+
gateway('semantic.references','coding_symbol_references','coding_inspect','symbol_references','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'semantic'}),
|
|
104
|
+
gateway('semantic.diagnostics','coding_symbol_diagnostics','coding_inspect','symbol_diagnostics','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'semantic'}),
|
|
105
|
+
gateway('semantic.implementations','coding_symbol_implementations','coding_inspect','symbol_implementations','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'semantic'}),
|
|
106
|
+
gateway('semantic.rename_preview','coding_symbol_rename_preview','coding_inspect','symbol_rename_preview','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{receipt_class:'native_diff'}),
|
|
107
|
+
direct('semantic.replace','coding_symbol_replace','DEADBYTE_CODING_RUNTIME','coding',{mutating:true,receipt_class:'coding'}),
|
|
108
|
+
direct('coding.file_read','coding_file_read','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'coding'}),
|
|
109
|
+
direct('coding.file_write','coding_file_write','DEADBYTE_CODING_RUNTIME','coding',{mutating:true,receipt_class:'coding'}),
|
|
110
|
+
direct('coding.text_replace','coding_text_replace','DEADBYTE_CODING_RUNTIME','coding',{mutating:true,receipt_class:'coding'}),
|
|
111
|
+
direct('coding.command_run','coding_command_run','DEADBYTE_CODING_RUNTIME','coding',{mutating:true,receipt_class:'coding'}),
|
|
112
|
+
direct('coding.benchmark_run','coding_benchmark_run','DEADBYTE_CODING_RUNTIME','coding',{mutating:true,receipt_class:'coding'}),
|
|
113
|
+
direct('coding.receipt_verify','coding_receipt_verify','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'verification'}),
|
|
114
|
+
direct('coding.task_submit','coding_task_submit','DEADBYTE_CODING_RUNTIME','coding',{mutating:true,receipt_class:'task'}),
|
|
115
|
+
direct('coding.task_run','coding_task_run','DEADBYTE_CODING_RUNTIME','coding',{mutating:true,receipt_class:'task'}),
|
|
116
|
+
direct('coding.task_status','coding_task_status','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'task'}),
|
|
117
|
+
direct('coding.task_events','coding_task_events','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'task'}),
|
|
118
|
+
direct('coding.task_cancel','coding_task_cancel','DEADBYTE_CODING_RUNTIME','coding',{mutating:true,receipt_class:'task'}),
|
|
119
|
+
direct('coding.task_approve','coding_task_approve','DEADBYTE_CODING_RUNTIME','coding',{mutating:true,receipt_class:'task'}),
|
|
120
|
+
direct('coding.task_verify','coding_task_verify','DEADBYTE_CODING_RUNTIME','coding',{receipt_class:'verification'}),
|
|
121
|
+
|
|
122
|
+
gateway('machine_fs.stat','machine_fs_stat','machine_fs','stat','DEADBYTE_MACHINE_FS_RUNTIME','coding',{receipt_class:'machine_fs'}),
|
|
123
|
+
gateway('machine_fs.list','machine_fs_list','machine_fs','list','DEADBYTE_MACHINE_FS_RUNTIME','coding',{receipt_class:'machine_fs'}),
|
|
124
|
+
gateway('machine_fs.read','machine_fs_read','machine_fs','read','DEADBYTE_MACHINE_FS_RUNTIME','coding',{receipt_class:'machine_fs'}),
|
|
125
|
+
gateway('machine_fs.write','machine_fs_write','machine_fs','write','DEADBYTE_MACHINE_FS_RUNTIME','coding',{mutating:true,receipt_class:'machine_fs'}),
|
|
126
|
+
gateway('machine_fs.edit_exact','machine_fs_edit_exact','machine_fs','edit_exact','DEADBYTE_MACHINE_FS_RUNTIME','coding',{mutating:true,receipt_class:'machine_fs'}),
|
|
127
|
+
gateway('machine_fs.mkdir','machine_fs_mkdir','machine_fs','mkdir','DEADBYTE_MACHINE_FS_RUNTIME','coding',{mutating:true,receipt_class:'machine_fs'}),
|
|
128
|
+
gateway('machine_fs.move','machine_fs_move','machine_fs','move','DEADBYTE_MACHINE_FS_RUNTIME','coding',{mutating:true,receipt_class:'machine_fs'}),
|
|
129
|
+
gateway('machine_fs.delete','machine_fs_delete','machine_fs','delete','DEADBYTE_MACHINE_FS_RUNTIME','coding',{mutating:true,receipt_class:'machine_fs'}),
|
|
130
|
+
|
|
131
|
+
gateway('observation.search_start','machine_search_start','machine_observe','search_start','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
132
|
+
gateway('observation.search_page','machine_search_page','machine_observe','search_page','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
133
|
+
gateway('observation.search_status','machine_search_status','machine_observe','search_status','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
134
|
+
gateway('observation.search_cancel','machine_search_cancel','machine_observe','search_cancel','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
135
|
+
gateway('observation.process_list','machine_process_list','machine_observe','process_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
136
|
+
gateway('observation.system_status','machine_system_status','machine_observe','system_status','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
|
|
137
|
+
|
|
138
|
+
direct('autonomous.session_status','autonomous_session_status','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{receipt_class:'autonomous'}),
|
|
139
|
+
direct('native_diff.preview','native_diff_preview','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{mutating:true,receipt_class:'native_diff'}),
|
|
140
|
+
direct('native_diff.apply','native_diff_apply','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{mutating:true,receipt_class:'native_diff'}),
|
|
141
|
+
direct('native_diff.verify','native_diff_verify','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{receipt_class:'verification'}),
|
|
142
|
+
direct('autonomous.goal_submit','autonomous_goal_submit','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{mutating:true,receipt_class:'autonomous'}),
|
|
143
|
+
direct('autonomous.goal_run','autonomous_goal_run','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{mutating:true,receipt_class:'autonomous'}),
|
|
144
|
+
direct('autonomous.goal_status','autonomous_goal_status','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{receipt_class:'autonomous'}),
|
|
145
|
+
direct('autonomous.goal_events','autonomous_goal_events','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{receipt_class:'autonomous'}),
|
|
146
|
+
direct('autonomous.goal_cancel','autonomous_goal_cancel','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{mutating:true,receipt_class:'autonomous'}),
|
|
147
|
+
direct('autonomous.goal_approve','autonomous_goal_approve','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{mutating:true,receipt_class:'autonomous'}),
|
|
148
|
+
direct('autonomous.goal_verify','autonomous_goal_verify','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{receipt_class:'verification'}),
|
|
149
|
+
direct('autonomous.release_gate','autonomous_release_gate','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{mutating:true,receipt_class:'release'}),
|
|
150
|
+
|
|
151
|
+
direct('host.file_read','host_file_read','DEADBYTE_PRIVILEGED_POLICY','host',{receipt_class:'host',production:false,host_only:true}),
|
|
152
|
+
direct('host.file_write','host_file_write','DEADBYTE_PRIVILEGED_POLICY','host',{mutating:true,receipt_class:'host',production:false,host_only:true}),
|
|
153
|
+
direct('host.exec','host_exec','DEADBYTE_PRIVILEGED_POLICY','host',{mutating:true,receipt_class:'host',production:false,host_only:true})
|
|
154
|
+
]);
|
|
155
|
+
export const R36_CAPABILITY_LEDGER=createCapabilityLedger(R36_CAPABILITY_ENTRIES);
|
|
156
|
+
|
|
157
|
+
export const CAPABILITY_PROFILE_ENV=PROFILE_ENV;
|
|
158
|
+
export const COMPACT_SURFACE_TOOL_LIMIT=COMPACT_TOOL_LIMIT;
|
package/src/coding-mcp-tools.mjs
CHANGED
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
codingBenchmarkRunOutputSchema, codingCommandRunOutputSchema,
|
|
5
5
|
codingFileReadOutputSchema, codingFilesReadOutputSchema, codingFileWriteOutputSchema,
|
|
6
6
|
codingReceiptVerifyOutputSchema, codingSearchOutputSchema, codingSessionStatusOutputSchema, codingStatOutputSchema,
|
|
7
|
-
codingSymbolOutlineOutputSchema, codingSymbolDefinitionOutputSchema, codingSymbolReferencesOutputSchema,
|
|
7
|
+
codingSymbolOutlineOutputSchema, codingSymbolDefinitionOutputSchema, codingSymbolReferencesOutputSchema,
|
|
8
|
+
codingSymbolDiagnosticsOutputSchema, codingSymbolImplementationsOutputSchema, codingSymbolRenamePreviewOutputSchema, codingSymbolReplaceOutputSchema,
|
|
8
9
|
codingTaskApproveOutputSchema, codingTaskCancelOutputSchema,
|
|
9
10
|
codingTaskEventsOutputSchema, codingTaskRunOutputSchema,
|
|
10
11
|
codingTaskStatusOutputSchema, codingTaskSubmitOutputSchema,
|
|
@@ -67,13 +68,16 @@ export function registerCodingTools(server, { codingRuntime, taskRuntime, semant
|
|
|
67
68
|
}, async ({ root_id, path, hash }) => { try { return result(codingStatOutputSchema, await codingRuntime.stat({ rootId: root_id, relativePath: path, hash })); } catch (error) { return fail(codingStatOutputSchema, error); } });
|
|
68
69
|
|
|
69
70
|
server.registerTool('coding_search', {
|
|
70
|
-
description: 'Deterministic
|
|
71
|
+
description: 'Deterministic bounded content/path search with literal or regex mode, signed cursor paging, and observed file identities.',
|
|
71
72
|
inputSchema: z.strictObject({ root_id: rootId, path: relPath.default('.'), pattern: z.string().min(1).max(256),
|
|
72
|
-
mode: z.
|
|
73
|
-
max_results: z.number().int().min(1).max(1000).default(200), max_file_bytes: z.number().int().min(1).max(4194304).default(262144)
|
|
73
|
+
target: z.enum(['content','path']).default('content'), mode: z.enum(['literal','regex']).default('literal'), case_sensitive: z.boolean().default(false),
|
|
74
|
+
max_results: z.number().int().min(1).max(1000).default(200), max_file_bytes: z.number().int().min(1).max(4194304).default(262144),
|
|
75
|
+
max_scan_bytes: z.number().int().min(1).max(67108864).default(4194304), page_size: z.number().int().min(1).max(500).default(100),
|
|
76
|
+
cursor: z.string().max(8192).nullable().default(null) }),
|
|
74
77
|
outputSchema: codingSearchOutputSchema
|
|
75
|
-
}, async ({ root_id, path, pattern, mode, case_sensitive, max_results, max_file_bytes }) => {
|
|
76
|
-
try { return result(codingSearchOutputSchema, await codingRuntime.search({ rootId: root_id, path, pattern, mode, caseSensitive: case_sensitive,
|
|
78
|
+
}, async ({ root_id, path, pattern, target, mode, case_sensitive, max_results, max_file_bytes, max_scan_bytes, page_size, cursor }) => {
|
|
79
|
+
try { return result(codingSearchOutputSchema, await codingRuntime.search({ rootId: root_id, path, pattern, target, mode, caseSensitive: case_sensitive,
|
|
80
|
+
maxResults: max_results, maxFileBytes: max_file_bytes, maxScanBytes: max_scan_bytes, pageSize: page_size, cursor })); }
|
|
77
81
|
catch (error) { return fail(codingSearchOutputSchema, error); }
|
|
78
82
|
});
|
|
79
83
|
|
|
@@ -95,6 +99,24 @@ export function registerCodingTools(server, { codingRuntime, taskRuntime, semant
|
|
|
95
99
|
outputSchema: codingSymbolReferencesOutputSchema
|
|
96
100
|
}, async ({ root_id, path, line, column, max_results }) => { try { return result(codingSymbolReferencesOutputSchema, await semanticRuntime.references({ rootId: root_id, relativePath: path, line, column, maxResults: max_results })); } catch (error) { return fail(codingSymbolReferencesOutputSchema, error); } });
|
|
97
101
|
|
|
102
|
+
server.registerTool('coding_symbol_diagnostics', {
|
|
103
|
+
description: 'Return bounded normalized semantic diagnostics with target and result identities.',
|
|
104
|
+
inputSchema: z.strictObject({ root_id: rootId, path: relPath, max_results: z.number().int().min(1).max(500).default(200) }),
|
|
105
|
+
outputSchema: codingSymbolDiagnosticsOutputSchema
|
|
106
|
+
}, async ({ root_id, path, max_results }) => { try { const value=await semanticRuntime.diagnostics({ rootId:root_id, relativePath:path, maxResults:max_results }); return result(codingSymbolDiagnosticsOutputSchema,value,value.status==='failed'); } catch(error){ return fail(codingSymbolDiagnosticsOutputSchema,error); } });
|
|
107
|
+
|
|
108
|
+
server.registerTool('coding_symbol_implementations', {
|
|
109
|
+
description: 'Resolve bounded semantic implementations at a zero-based line/column. Unsupported backends report an explicit typed status.',
|
|
110
|
+
inputSchema: z.strictObject({ root_id:rootId, path:relPath, line:z.number().int().min(0), column:z.number().int().min(0), max_results:z.number().int().min(1).max(500).default(200) }),
|
|
111
|
+
outputSchema: codingSymbolImplementationsOutputSchema
|
|
112
|
+
}, async ({ root_id, path, line, column, max_results }) => { try { const value=await semanticRuntime.implementations({rootId:root_id,relativePath:path,line,column,maxResults:max_results}); return result(codingSymbolImplementationsOutputSchema,value,false); } catch(error){ return fail(codingSymbolImplementationsOutputSchema,error); } });
|
|
113
|
+
|
|
114
|
+
server.registerTool('coding_symbol_rename_preview', {
|
|
115
|
+
description: 'Preview one semantic rename as a content-addressed Native Diff. This tool never mutates source bytes.',
|
|
116
|
+
inputSchema: z.strictObject({ root_id:rootId, path:relPath, line:z.number().int().min(0), column:z.number().int().min(0), new_name:z.string().regex(/^[A-Za-z_$][A-Za-z0-9_$]{0,127}$/) }),
|
|
117
|
+
outputSchema: codingSymbolRenamePreviewOutputSchema
|
|
118
|
+
}, async ({ root_id, path, line, column, new_name }) => { try { const value=await semanticRuntime.renamePreview({rootId:root_id,relativePath:path,line,column,newName:new_name}); return result(codingSymbolRenamePreviewOutputSchema,value,false); } catch(error){ return fail(codingSymbolRenamePreviewOutputSchema,error); } });
|
|
119
|
+
|
|
98
120
|
server.registerTool('coding_symbol_replace', {
|
|
99
121
|
description: 'Replace one current JS/TS symbol body by symbol_id under exact file SHA-256 precondition. The commit reuses the coding plane signed surgical-edit receipt path.',
|
|
100
122
|
inputSchema: z.strictObject({ root_id: rootId, path: relPath, symbol_id: hex64, new_body: z.string().max(524288), expected_sha256: hex64 }),
|
|
@@ -102,21 +124,13 @@ export function registerCodingTools(server, { codingRuntime, taskRuntime, semant
|
|
|
102
124
|
}, async ({ root_id, path, symbol_id, new_body, expected_sha256 }) => { try { return result(codingSymbolReplaceOutputSchema, await semanticRuntime.replaceSymbol({ rootId: root_id, relativePath: path, symbolId: symbol_id, newBody: new_body, expectedSha256: expected_sha256 })); } catch (error) { return fail(codingSymbolReplaceOutputSchema, error); } });
|
|
103
125
|
|
|
104
126
|
server.registerTool('coding_files_read', {
|
|
105
|
-
description: 'Read up to 32 bounded files
|
|
127
|
+
description: 'Read up to 32 bounded files under one coding lease and return an aggregate observation identity over ordered file metadata.',
|
|
106
128
|
inputSchema: z.strictObject({ root_id: rootId, files: z.array(z.strictObject({ path: relPath, encoding: z.enum(['utf8','base64']).default('utf8') })).min(1).max(32),
|
|
107
129
|
max_total_bytes: z.number().int().min(1).max(2097152).default(1048576) }),
|
|
108
130
|
outputSchema: codingFilesReadOutputSchema
|
|
109
131
|
}, async ({ root_id, files, max_total_bytes }) => {
|
|
110
|
-
try {
|
|
111
|
-
|
|
112
|
-
for(const item of files){
|
|
113
|
-
const read=await codingRuntime.fileRead({rootId:root_id,relativePath:item.path,encoding:item.encoding});
|
|
114
|
-
total+=read.bytes;
|
|
115
|
-
if(total>max_total_bytes) throw new Error(`coding multi-read exceeds ${max_total_bytes} byte total limit`);
|
|
116
|
-
values.push({path:item.path,encoding:item.encoding,bytes:read.bytes,sha256:read.sha256,content:read.content});
|
|
117
|
-
}
|
|
118
|
-
return result(codingFilesReadOutputSchema,{status:'ok',root_id,total_bytes:total,files:values});
|
|
119
|
-
} catch(error){ return fail(codingFilesReadOutputSchema,error); }
|
|
132
|
+
try { return result(codingFilesReadOutputSchema,await codingRuntime.filesRead({rootId:root_id,files,maxTotalBytes:max_total_bytes})); }
|
|
133
|
+
catch(error){ return fail(codingFilesReadOutputSchema,error); }
|
|
120
134
|
});
|
|
121
135
|
|
|
122
136
|
server.registerTool('coding_file_read', {
|
|
@@ -70,18 +70,25 @@ export const codingTreeOutputSchema = statusContract({
|
|
|
70
70
|
export const codingStatOutputSchema = statusContract({
|
|
71
71
|
statuses: ['ok', 'failed'],
|
|
72
72
|
shape: { root_id: z.string().min(1).max(64).optional(), path: z.string().min(1).max(512).optional(),
|
|
73
|
-
type: z.enum(['file','directory','symlink','other']).optional(),
|
|
74
|
-
mtime_utc: z.string().min(1).optional(), sha256: hex64.nullable().optional()
|
|
75
|
-
|
|
73
|
+
normalized_path: z.string().min(1).max(512).optional(), type: z.enum(['file','directory','symlink','other']).optional(),
|
|
74
|
+
bytes: z.number().int().min(0).optional(), mtime_utc: z.string().min(1).optional(), sha256: hex64.nullable().optional(),
|
|
75
|
+
reparse_state: z.enum(['none','symlink']).optional(), object_identity_sha256: hex64.optional(),
|
|
76
|
+
policy_root_id: z.string().min(1).max(64).optional(), observation_sha256: hex64.optional() },
|
|
77
|
+
successStatuses: ['ok'], successRequired: ['root_id','path','normalized_path','type','bytes','mtime_utc','sha256','reparse_state','object_identity_sha256','policy_root_id','observation_sha256']
|
|
76
78
|
});
|
|
77
79
|
export const codingSearchOutputSchema = statusContract({
|
|
78
80
|
statuses: ['ok', 'failed'],
|
|
79
81
|
shape: { root_id: z.string().min(1).max(64).optional(), path: z.string().min(1).max(512).optional(),
|
|
80
|
-
pattern: z.string().min(1).max(256).optional(),
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
pattern: z.string().min(1).max(256).optional(), target: z.enum(['content','path']).optional(),
|
|
83
|
+
mode: z.enum(['literal','regex']).optional(), case_sensitive: z.boolean().optional(),
|
|
84
|
+
matches: z.array(z.strictObject({ path: z.string().min(1).max(512), line: z.number().int().min(1).nullable(),
|
|
85
|
+
column: z.number().int().min(1).nullable(), preview: z.string().max(512), file_sha256: hex64.nullable() })).max(500).optional(),
|
|
86
|
+
files_scanned: z.number().int().min(0).optional(), bytes_scanned: z.number().int().min(0).optional(),
|
|
87
|
+
binary_files_skipped: z.number().int().min(0).optional(), truncated: z.boolean().optional(),
|
|
88
|
+
truncation_reason: z.enum(['tree_limit','scan_byte_limit','max_results']).nullable().optional(),
|
|
89
|
+
query_sha256: hex64.optional(), snapshot_sha256: hex64.optional(), page_sha256: hex64.optional(),
|
|
90
|
+
next_cursor: z.string().max(8192).nullable().optional() },
|
|
91
|
+
successStatuses: ['ok'], successRequired: ['root_id','path','pattern','target','mode','case_sensitive','matches','files_scanned','bytes_scanned','binary_files_skipped','truncated','truncation_reason','query_sha256','snapshot_sha256','page_sha256','next_cursor']
|
|
85
92
|
});
|
|
86
93
|
const semanticLocationSchema = z.strictObject({
|
|
87
94
|
path: z.string().min(1).max(512), name: z.string().max(256), kind: z.string().min(1).max(64),
|
|
@@ -113,6 +120,36 @@ export const codingSymbolReferencesOutputSchema = statusContract({
|
|
|
113
120
|
locations: z.array(semanticLocationSchema).max(500).optional(), truncated: z.boolean().optional() },
|
|
114
121
|
successStatuses: ['ok'], successRequired: ['root_id','path','line','column','engine','locations','truncated']
|
|
115
122
|
});
|
|
123
|
+
export const codingSymbolDiagnosticsOutputSchema = z.strictObject({
|
|
124
|
+
status:z.enum(['ok','unsupported','failed']), error:z.string().optional(), capability:z.string().max(64).optional(),
|
|
125
|
+
root_id:z.string().min(1).max(64).optional(), path:z.string().min(1).max(512).optional(), file_sha256:hex64.optional(),
|
|
126
|
+
engine:z.string().min(1).max(128).optional(), diagnostics:z.array(z.strictObject({message:z.string().max(4096),code:z.string().max(256),severity:z.string().min(1).max(64),location:semanticLocationSchema.optional()})).max(500).optional(),
|
|
127
|
+
truncated:z.boolean().optional(), result_sha256:hex64.optional()
|
|
128
|
+
}).superRefine((value,ctx)=>{
|
|
129
|
+
if(value.status==='failed'){requireFields(value,ctx,['error']);return;}
|
|
130
|
+
if(value.status==='unsupported'){requireFields(value,ctx,['capability','engine']);return;}
|
|
131
|
+
requireFields(value,ctx,['root_id','path','file_sha256','engine','diagnostics','truncated','result_sha256']);
|
|
132
|
+
});
|
|
133
|
+
export const codingSymbolImplementationsOutputSchema = z.strictObject({
|
|
134
|
+
status:z.enum(['ok','unsupported','failed']), error:z.string().optional(), capability:z.string().max(64).optional(),
|
|
135
|
+
root_id:z.string().min(1).max(64).optional(), path:z.string().min(1).max(512).optional(), line:z.number().int().min(0).optional(), column:z.number().int().min(0).optional(),
|
|
136
|
+
file_sha256:hex64.optional(), engine:z.string().min(1).max(128).optional(), locations:z.array(semanticLocationSchema).max(500).optional(),
|
|
137
|
+
truncated:z.boolean().optional(), result_sha256:hex64.optional()
|
|
138
|
+
}).superRefine((value,ctx)=>{
|
|
139
|
+
if(value.status==='failed'){requireFields(value,ctx,['error']);return;}
|
|
140
|
+
if(value.status==='unsupported'){requireFields(value,ctx,['capability','engine']);return;}
|
|
141
|
+
requireFields(value,ctx,['root_id','path','line','column','file_sha256','engine','locations','truncated','result_sha256']);
|
|
142
|
+
});
|
|
143
|
+
export const codingSymbolRenamePreviewOutputSchema = z.strictObject({
|
|
144
|
+
status:z.enum(['ready','unsupported','failed']), error:z.string().optional(), capability:z.string().max(64).optional(), engine:z.string().min(1).max(128).optional(),
|
|
145
|
+
diff_id:hex64.optional(), affected_files:z.number().int().min(1).max(32).optional(), edit_count:z.number().int().min(1).optional(), backend:z.string().min(1).max(128).optional(),
|
|
146
|
+
preimages:z.array(z.strictObject({path:z.string().min(1).max(512),sha256:hex64})).max(32).optional()
|
|
147
|
+
}).superRefine((value,ctx)=>{
|
|
148
|
+
if(value.status==='failed'){requireFields(value,ctx,['error']);return;}
|
|
149
|
+
if(value.status==='unsupported'){requireFields(value,ctx,['capability','engine']);return;}
|
|
150
|
+
requireFields(value,ctx,['diff_id','affected_files','edit_count','backend','preimages']);
|
|
151
|
+
});
|
|
152
|
+
|
|
116
153
|
export const codingSymbolReplaceOutputSchema = statusContract({
|
|
117
154
|
statuses: ['committed','failed'],
|
|
118
155
|
shape: { root_id: z.string().min(1).max(64).optional(), path: z.string().min(1).max(512).optional(), previous_sha256: hex64.optional(),
|
|
@@ -125,8 +162,9 @@ export const codingSymbolReplaceOutputSchema = statusContract({
|
|
|
125
162
|
export const codingFilesReadOutputSchema = statusContract({
|
|
126
163
|
statuses: ['ok','failed'],
|
|
127
164
|
shape: { root_id: z.string().min(1).max(64).optional(), total_bytes: z.number().int().min(0).optional(),
|
|
128
|
-
files: z.array(z.strictObject({ path: z.string().min(1).max(512), encoding: z.enum(['utf8','base64']), bytes: z.number().int().min(0), sha256: hex64, content: z.string() })).max(32).optional()
|
|
129
|
-
|
|
165
|
+
files: z.array(z.strictObject({ path: z.string().min(1).max(512), encoding: z.enum(['utf8','base64']), bytes: z.number().int().min(0), sha256: hex64, content: z.string() })).max(32).optional(),
|
|
166
|
+
observation_sha256: hex64.optional() },
|
|
167
|
+
successStatuses: ['ok'], successRequired: ['root_id','total_bytes','files','observation_sha256']
|
|
130
168
|
});
|
|
131
169
|
export const codingFileReadOutputSchema = statusContract({
|
|
132
170
|
statuses: ['ok', 'failed'],
|
package/src/coding-plane.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { decodeUtf8Strict } from './text-policy.mjs';
|
|
|
9
9
|
import { isInside, validatePortableRelativePath } from './path-policy.mjs';
|
|
10
10
|
import { loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
|
|
11
11
|
import { createCodingSearch } from './coding-search.mjs';
|
|
12
|
+
import { createSearchCursorCodec } from './search-cursor.mjs';
|
|
12
13
|
|
|
13
14
|
export const CODING_POLICY_SCHEMA = 'deadbyte.coding-policy.v1';
|
|
14
15
|
export const CODING_LEASE_SCHEMA = 'deadbyte.coding-lease.v1';
|
|
@@ -613,18 +614,52 @@ export function createCodingRuntime({ policy, signingKeyPath, verifyKeyPath }) {
|
|
|
613
614
|
async function statPath({ rootId, relativePath = '.', hash = false }) {
|
|
614
615
|
const lease = await assertCodingArmed(policy, rootId);
|
|
615
616
|
const root = getRoot(policy, rootId, 'read');
|
|
616
|
-
let
|
|
617
|
+
let lexical = root.real_path;
|
|
617
618
|
let reported = '.';
|
|
618
619
|
if (relativePath !== '.') {
|
|
619
|
-
|
|
620
|
-
|
|
620
|
+
validatePortableRelativePath(relativePath);
|
|
621
|
+
if (!lease.root_ids.includes(rootId)) throw new Error(`coding root '${rootId}' is outside active lease`);
|
|
622
|
+
assertNotDenied(root, relativePath);
|
|
623
|
+
lexical = path.resolve(root.real_path, relativePath);
|
|
624
|
+
if (!isInside(root.real_path, lexical)) throw new Error('coding stat path escapes allowed root');
|
|
621
625
|
reported = relativePath.replaceAll('\\', '/');
|
|
622
626
|
}
|
|
623
|
-
const info = await lstat(
|
|
627
|
+
const info = await lstat(lexical);
|
|
624
628
|
const type = info.isFile() ? 'file' : info.isDirectory() ? 'directory' : info.isSymbolicLink() ? 'symlink' : 'other';
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
629
|
+
const reparseState = info.isSymbolicLink() ? 'symlink' : 'none';
|
|
630
|
+
if (type !== 'symlink') {
|
|
631
|
+
const resolved = await realpath(lexical);
|
|
632
|
+
if (!isInside(root.real_path, resolved) && resolved !== root.real_path) throw new Error('coding stat path resolves outside allowed root');
|
|
633
|
+
}
|
|
634
|
+
const contentSha = type === 'file' ? await sha256File(lexical) : null;
|
|
635
|
+
const objectIdentitySha = sha256Object({
|
|
636
|
+
schema:'deadbyte.coding-object-identity.v1',type,bytes:type==='file'?info.size:0,
|
|
637
|
+
content_sha256:contentSha,device:Number(info.dev),inode:Number(info.ino),reparse_state:reparseState
|
|
638
|
+
});
|
|
639
|
+
const result = {
|
|
640
|
+
status:'ok',root_id:rootId,path:reported,normalized_path:reported,type,
|
|
641
|
+
bytes:type==='file'?info.size:0,mtime_utc:info.mtime.toISOString(),
|
|
642
|
+
sha256:hash&&type==='file'?contentSha:null,reparse_state:reparseState,
|
|
643
|
+
object_identity_sha256:objectIdentitySha,policy_root_id:rootId
|
|
644
|
+
};
|
|
645
|
+
return {...result,observation_sha256:sha256Object({schema:'deadbyte.coding-stat-observation.v1',...result})};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
async function filesRead({ rootId, files, maxTotalBytes = 1048576 }) {
|
|
649
|
+
if (!Array.isArray(files) || files.length < 1 || files.length > 32) throw new Error('coding multi-read requires 1-32 files');
|
|
650
|
+
if (!Number.isInteger(maxTotalBytes) || maxTotalBytes < 1 || maxTotalBytes > 2097152) throw new Error('coding multi-read max_total_bytes must be in [1, 2097152]');
|
|
651
|
+
const values=[]; let total=0;
|
|
652
|
+
for(const item of files){
|
|
653
|
+
if(!item||typeof item.path!=='string'||!['utf8','base64'].includes(item.encoding??'utf8')) throw new Error('coding multi-read file entry invalid');
|
|
654
|
+
const encoding=item.encoding??'utf8';
|
|
655
|
+
const read=await fileRead({rootId,relativePath:item.path,encoding});
|
|
656
|
+
total+=read.bytes;
|
|
657
|
+
if(total>maxTotalBytes) throw new Error(`coding multi-read exceeds ${maxTotalBytes} byte total limit`);
|
|
658
|
+
values.push({path:item.path,encoding,bytes:read.bytes,sha256:read.sha256,content:read.content});
|
|
659
|
+
}
|
|
660
|
+
const identity=values.map(item=>({path:item.path,encoding:item.encoding,bytes:item.bytes,sha256:item.sha256}));
|
|
661
|
+
return {status:'ok',root_id:rootId,total_bytes:total,files:values,
|
|
662
|
+
observation_sha256:sha256Object({schema:'deadbyte.coding-files-read-observation.v1',root_id:rootId,files:identity})};
|
|
628
663
|
}
|
|
629
664
|
|
|
630
665
|
async function fileState({ rootId, relativePath }) {
|
|
@@ -651,7 +686,8 @@ export function createCodingRuntime({ policy, signingKeyPath, verifyKeyPath }) {
|
|
|
651
686
|
return verifyCodingReceiptFile(policy, verifyKeyPath, receiptId);
|
|
652
687
|
}
|
|
653
688
|
|
|
654
|
-
const
|
|
655
|
-
|
|
689
|
+
const cursorCodec = createSearchCursorCodec({ signingKeyPath, verifyKeyPath });
|
|
690
|
+
const search = createCodingSearch({ tree, fileRead, cursorCodec });
|
|
691
|
+
return Object.freeze({ policy, sessionStatus, tree, stat: statPath, search, filesRead, fileRead, fileWrite, fileDelete, textReplace, commandRun, benchmarkRun, receiptVerify,
|
|
656
692
|
fileState, verifyReceiptById: receiptVerify, verifyReceiptDetailed, requestSha });
|
|
657
693
|
}
|
package/src/coding-search.mjs
CHANGED
|
@@ -1,55 +1,138 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { canonicalJson } from './canonical-json.mjs';
|
|
3
|
+
import { decodeUtf8Strict } from './text-policy.mjs';
|
|
4
|
+
|
|
5
|
+
const sha256Object=value=>createHash('sha256').update(canonicalJson(value),'utf8').digest('hex');
|
|
6
|
+
const sha256Bytes=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
7
|
+
|
|
8
|
+
function validateInt(value,name,min,max){
|
|
9
|
+
if(!Number.isInteger(value)||value<min||value>max) throw new Error(`${name} must be in [${min}, ${max}]`);
|
|
10
|
+
}
|
|
11
|
+
function compileMatcher({pattern,mode,caseSensitive}){
|
|
12
|
+
if(typeof pattern!=='string'||pattern.length<1||pattern.length>256) throw new Error('coding search pattern must be 1-256 characters');
|
|
13
|
+
if(mode==='literal'){
|
|
14
|
+
const needle=caseSensitive?pattern:pattern.toLowerCase();
|
|
15
|
+
return {
|
|
16
|
+
all(text){
|
|
17
|
+
const haystack=caseSensitive?text:text.toLowerCase();
|
|
18
|
+
const out=[]; let from=0;
|
|
19
|
+
while(from<=haystack.length){
|
|
20
|
+
const index=haystack.indexOf(needle,from);
|
|
21
|
+
if(index<0) break;
|
|
22
|
+
out.push({index,length:Math.max(1,pattern.length)});
|
|
23
|
+
from=index+Math.max(1,pattern.length);
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
},
|
|
27
|
+
path(value){return (caseSensitive?value:value.toLowerCase()).includes(needle);}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if(mode!=='regex') throw new Error('coding search mode must be literal or regex');
|
|
31
|
+
let regex;
|
|
32
|
+
try{regex=new RegExp(pattern,caseSensitive?'gu':'giu');}
|
|
33
|
+
catch(error){throw new Error(`coding search regex invalid: ${error.message}`);}
|
|
34
|
+
return {
|
|
35
|
+
all(text){
|
|
36
|
+
const out=[]; regex.lastIndex=0; let match;
|
|
37
|
+
while((match=regex.exec(text))!==null){
|
|
38
|
+
out.push({index:match.index,length:Math.max(1,match[0].length)});
|
|
39
|
+
if(match[0].length===0) regex.lastIndex+=1;
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
},
|
|
43
|
+
path(value){regex.lastIndex=0;return regex.test(value);}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function lineColumn(text,index){
|
|
47
|
+
let line=1,column=1;
|
|
48
|
+
for(let i=0;i<index;i+=1){if(text.charCodeAt(i)===10){line+=1;column=1;}else column+=1;}
|
|
49
|
+
return {line,column};
|
|
50
|
+
}
|
|
51
|
+
function preview(text,index,length){
|
|
52
|
+
const start=Math.max(0,index-48); const end=Math.min(text.length,index+Math.max(length,1)+112);
|
|
53
|
+
return text.slice(start,end);
|
|
54
|
+
}
|
|
55
|
+
function normalizeQuery({rootId,path,target,mode,pattern,caseSensitive,maxResults,maxFileBytes,maxScanBytes,pageSize}){
|
|
56
|
+
return {
|
|
57
|
+
schema:'deadbyte.coding-search-query.v2',root_id:rootId,path,target,mode,pattern,
|
|
58
|
+
case_sensitive:caseSensitive,max_results:maxResults,max_file_bytes:maxFileBytes,
|
|
59
|
+
max_scan_bytes:maxScanBytes,page_size:pageSize
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createCodingSearch({tree,fileRead,cursorCodec}){
|
|
64
|
+
if(typeof tree!=='function'||typeof fileRead!=='function'||!cursorCodec||typeof cursorCodec.encode!=='function'||typeof cursorCodec.decode!=='function'){
|
|
65
|
+
throw new Error('coding search requires tree, fileRead, and cursorCodec');
|
|
4
66
|
}
|
|
5
67
|
return async function search({
|
|
6
|
-
rootId,
|
|
7
|
-
maxResults
|
|
8
|
-
})
|
|
9
|
-
if
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
68
|
+
rootId,path='.',pattern,target='content',mode='literal',caseSensitive=false,
|
|
69
|
+
maxResults=200,maxFileBytes=262144,maxScanBytes=4*1024*1024,pageSize=100,cursor=null
|
|
70
|
+
}){
|
|
71
|
+
if(!['content','path'].includes(target)) throw new Error('coding search target must be content or path');
|
|
72
|
+
validateInt(maxResults,'max_results',1,1000);
|
|
73
|
+
validateInt(maxFileBytes,'max_file_bytes',1,4*1024*1024);
|
|
74
|
+
validateInt(maxScanBytes,'max_scan_bytes',1,64*1024*1024);
|
|
75
|
+
validateInt(pageSize,'page_size',1,500);
|
|
76
|
+
const matcher=compileMatcher({pattern,mode,caseSensitive});
|
|
77
|
+
const query=normalizeQuery({rootId,path,target,mode,pattern,caseSensitive,maxResults,maxFileBytes,maxScanBytes,pageSize});
|
|
78
|
+
const querySha=sha256Object(query);
|
|
79
|
+
let decodedCursor=null;
|
|
80
|
+
if(cursor!==null){
|
|
81
|
+
decodedCursor=await cursorCodec.decode(cursor);
|
|
82
|
+
if(decodedCursor.query_sha256!==querySha) throw new Error('coding search cursor query mismatch');
|
|
18
83
|
}
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
84
|
+
|
|
85
|
+
const listed=await tree({rootId,path,maxDepth:12,maxEntries:2000});
|
|
86
|
+
const entries=listed.entries.filter(entry=>entry.type==='file').sort((a,b)=>a.path.localeCompare(b.path,'en'));
|
|
87
|
+
const matches=[]; const snapshotEntries=[];
|
|
88
|
+
let filesScanned=0,bytesScanned=0,binaryFilesSkipped=0;
|
|
89
|
+
let truncated=listed.truncated===true;
|
|
90
|
+
let truncationReason=listed.truncated===true?'tree_limit':null;
|
|
91
|
+
|
|
92
|
+
if(target==='path'){
|
|
93
|
+
for(const entry of entries){
|
|
94
|
+
snapshotEntries.push({path:entry.path,bytes:entry.bytes,type:'file'});
|
|
95
|
+
if(matcher.path(entry.path)){
|
|
96
|
+
matches.push({path:entry.path,line:null,column:null,preview:entry.path,file_sha256:null});
|
|
97
|
+
if(matches.length>=maxResults){truncated=true;truncationReason='max_results';break;}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
for(const entry of entries){
|
|
102
|
+
if(entry.bytes>maxFileBytes) continue;
|
|
103
|
+
if(bytesScanned+entry.bytes>maxScanBytes){truncated=true;truncationReason='scan_byte_limit';break;}
|
|
104
|
+
let item;
|
|
105
|
+
try{item=await fileRead({rootId,relativePath:entry.path,encoding:'base64'});}catch{continue;}
|
|
106
|
+
const raw=Buffer.from(item.content,'base64');
|
|
107
|
+
filesScanned+=1; bytesScanned+=item.bytes;
|
|
108
|
+
const fileSha=item.sha256??sha256Bytes(raw);
|
|
109
|
+
snapshotEntries.push({path:entry.path,bytes:item.bytes,sha256:fileSha});
|
|
110
|
+
let text;
|
|
111
|
+
try{text=decodeUtf8Strict(raw);}catch{binaryFilesSkipped+=1;continue;}
|
|
112
|
+
const found=matcher.all(text);
|
|
113
|
+
for(const hit of found){
|
|
114
|
+
const lc=lineColumn(text,hit.index);
|
|
115
|
+
matches.push({path:entry.path,line:lc.line,column:lc.column,preview:preview(text,hit.index,hit.length),file_sha256:fileSha});
|
|
116
|
+
if(matches.length>=maxResults){truncated=true;truncationReason='max_results';break;}
|
|
117
|
+
}
|
|
118
|
+
if(matches.length>=maxResults) break;
|
|
119
|
+
}
|
|
47
120
|
}
|
|
121
|
+
|
|
122
|
+
const snapshotSha=sha256Object({schema:'deadbyte.coding-search-snapshot.v2',query_sha256:querySha,entries:snapshotEntries});
|
|
123
|
+
if(decodedCursor&&decodedCursor.snapshot_sha256!==snapshotSha) throw new Error('coding search cursor stale snapshot mismatch');
|
|
124
|
+
const offset=decodedCursor?.offset??0;
|
|
125
|
+
if(offset>matches.length) throw new Error('coding search cursor offset exceeds retained results');
|
|
126
|
+
const pageMatches=matches.slice(offset,offset+pageSize);
|
|
127
|
+
const nextOffset=offset+pageMatches.length;
|
|
128
|
+
const hasMore=nextOffset<matches.length;
|
|
129
|
+
const nextCursor=hasMore?await cursorCodec.encode({query_sha256:querySha,snapshot_sha256:snapshotSha,offset:nextOffset}):null;
|
|
130
|
+
const pageSha=sha256Object({schema:'deadbyte.coding-search-page.v2',query_sha256:querySha,snapshot_sha256:snapshotSha,offset,matches:pageMatches});
|
|
48
131
|
return {
|
|
49
|
-
status:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
132
|
+
status:'ok',root_id:rootId,path,pattern,target,mode,case_sensitive:caseSensitive,
|
|
133
|
+
matches:pageMatches,files_scanned:filesScanned,bytes_scanned:bytesScanned,binary_files_skipped:binaryFilesSkipped,
|
|
134
|
+
truncated:truncated||hasMore,truncation_reason:truncationReason,query_sha256:querySha,snapshot_sha256:snapshotSha,
|
|
135
|
+
page_sha256:pageSha,next_cursor:nextCursor
|
|
53
136
|
};
|
|
54
137
|
};
|
|
55
138
|
}
|