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
|
@@ -30,7 +30,7 @@ const agentTools = [
|
|
|
30
30
|
const codingTools = [
|
|
31
31
|
'coding_benchmark_run', 'coding_command_run', 'coding_file_read', 'coding_file_write', 'coding_files_read',
|
|
32
32
|
'coding_receipt_verify', 'coding_search', 'coding_session_status', 'coding_stat',
|
|
33
|
-
'coding_symbol_definition', 'coding_symbol_outline', 'coding_symbol_references', 'coding_symbol_replace',
|
|
33
|
+
'coding_symbol_definition', 'coding_symbol_diagnostics', 'coding_symbol_implementations', 'coding_symbol_outline', 'coding_symbol_references', 'coding_symbol_rename_preview', 'coding_symbol_replace',
|
|
34
34
|
'coding_task_approve', 'coding_task_cancel', 'coding_task_events', 'coding_task_run',
|
|
35
35
|
'coding_task_status', 'coding_task_submit', 'coding_task_verify', 'coding_text_replace', 'coding_tree'
|
|
36
36
|
];
|
|
@@ -39,6 +39,18 @@ const autonomousTools = [
|
|
|
39
39
|
'autonomous_goal_status', 'autonomous_goal_submit', 'autonomous_goal_verify', 'autonomous_release_gate',
|
|
40
40
|
'autonomous_session_status', 'native_diff_apply', 'native_diff_preview', 'native_diff_verify'
|
|
41
41
|
];
|
|
42
|
+
const r36CompactTools = [
|
|
43
|
+
'autonomous_goal_approve','autonomous_goal_cancel','autonomous_goal_events','autonomous_goal_run','autonomous_goal_status','autonomous_goal_submit','autonomous_goal_verify','autonomous_release_gate','autonomous_session_status',
|
|
44
|
+
'capability_manifest',
|
|
45
|
+
'coding_benchmark_run','coding_command_run','coding_file_read','coding_file_write','coding_inspect','coding_receipt_verify','coding_session_status','coding_symbol_replace',
|
|
46
|
+
'coding_task_approve','coding_task_cancel','coding_task_events','coding_task_run','coding_task_status','coding_task_submit','coding_task_verify','coding_text_replace','coding_tree',
|
|
47
|
+
'evidence_verify',
|
|
48
|
+
'host_exec','host_file_read','host_file_write',
|
|
49
|
+
'machine_fs','machine_list','machine_observe','machine_ping','machine_revoke','machine_status',
|
|
50
|
+
'native_diff_apply','native_diff_preview','native_diff_verify',
|
|
51
|
+
'process_session'
|
|
52
|
+
].sort();
|
|
53
|
+
if (r36CompactTools.length !== 41) throw new Error(`R36 compact catalog constant drift: ${r36CompactTools.length}`);
|
|
42
54
|
const allowedCatalogs = [];
|
|
43
55
|
for (const machine of [false, true]) for (const host of [false, true]) for (const agent of [false, true]) {
|
|
44
56
|
for (const processPlane of [false, true]) for (const machineFs of [false, true]) for (const observation of [false, true]) for (const codingMode of ['none', 'coding', 'autonomous']) {
|
|
@@ -68,13 +80,21 @@ function structured(result, label) {
|
|
|
68
80
|
if (!result.structuredContent || typeof result.structuredContent !== 'object') throw new Error(`${label} returned no structured object`);
|
|
69
81
|
return result.structuredContent;
|
|
70
82
|
}
|
|
83
|
+
function gateway(result, label) {
|
|
84
|
+
const value = structured(result, label);
|
|
85
|
+
if (value.status !== 'ok' || !value.result || typeof value.result !== 'object') {
|
|
86
|
+
throw new Error(`${label} gateway status=${String(value.status)} error=${String(value.error ?? '')}`);
|
|
87
|
+
}
|
|
88
|
+
return value.result;
|
|
89
|
+
}
|
|
71
90
|
|
|
72
91
|
try {
|
|
73
92
|
await client.connect(transport);
|
|
74
93
|
if (client.getProtocolEra() !== 'modern') throw new Error(`expected modern MCP era, got ${String(client.getProtocolEra())}`);
|
|
75
94
|
const listed = await client.listTools();
|
|
76
95
|
const names = listed.tools.map(t => t.name).sort();
|
|
77
|
-
const
|
|
96
|
+
const compactProfile = JSON.stringify(names) === JSON.stringify(r36CompactTools);
|
|
97
|
+
const catalogOk = compactProfile || allowedCatalogs.some(expected => JSON.stringify(names) === JSON.stringify(expected));
|
|
78
98
|
if (!catalogOk) throw new Error(`tool catalog mismatch: ${JSON.stringify(names)}`);
|
|
79
99
|
for (const tool of listed.tools) {
|
|
80
100
|
if (tool.outputSchema?.type !== 'object' || tool.outputSchema?.additionalProperties !== false || !tool.outputSchema?.properties) {
|
|
@@ -90,72 +110,108 @@ try {
|
|
|
90
110
|
const codingRuntime = codingTools.every(name => names.includes(name));
|
|
91
111
|
const autonomousRuntime = autonomousTools.every(name => names.includes(name));
|
|
92
112
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const proofOutput = `remote-proof.${randomBytes(8).toString('hex')}.txt`;
|
|
97
|
-
const proof = structured(await client.callTool({
|
|
98
|
-
name: 'proof_execute', arguments: { workspace_id: 'demo', input: 'input.txt', output: proofOutput }
|
|
99
|
-
}), 'proof_execute');
|
|
100
|
-
if (proof.status !== 'passed' || !/^[0-9a-f]{64}$/.test(proof.request_sha256)) throw new Error('remote proof request identity missing');
|
|
101
|
-
const proofVerify = structured(await client.callTool({
|
|
102
|
-
name: 'evidence_verify', arguments: { workspace_id: 'demo', receipt_path: proof.receipt_path }
|
|
103
|
-
}), 'evidence_verify proof');
|
|
104
|
-
if (proofVerify.ok !== true || proofVerify.signature_valid !== true || proofVerify.receipt_schema !== 'deadbyte.receipt.v2' ||
|
|
105
|
-
proofVerify.request_sha256 !== proof.request_sha256) throw new Error(`remote proof verify failed: ${JSON.stringify(proofVerify)}`);
|
|
106
|
-
|
|
113
|
+
let compact = null;
|
|
114
|
+
let proofOutput = null;
|
|
115
|
+
let proof = null;
|
|
107
116
|
let contained = null;
|
|
108
|
-
if (mode === 'full') {
|
|
109
|
-
const output = `remote-contained.${randomBytes(8).toString('hex')}.txt`;
|
|
110
|
-
const exec = structured(await client.callTool({
|
|
111
|
-
name: 'contained_transform',
|
|
112
|
-
arguments: { workspace_id: 'demo', input: 'input.txt', output, operation: 'uppercase_ascii' }
|
|
113
|
-
}), 'contained_transform');
|
|
114
|
-
if (exec.status !== 'passed' || exec.output_sha256 !== '3e740f6fcf2c6659bc5f99e0706b09f3c127407bbabe74d05285d2e3276946c2' ||
|
|
115
|
-
!/^[0-9a-f]{64}$/.test(exec.request_sha256)) throw new Error(`remote contained result mismatch: ${JSON.stringify(exec)}`);
|
|
116
|
-
const verify = structured(await client.callTool({
|
|
117
|
-
name: 'evidence_verify', arguments: { workspace_id: 'demo', receipt_path: exec.receipt_path }
|
|
118
|
-
}), 'evidence_verify contained');
|
|
119
|
-
if (verify.ok !== true || verify.signature_valid !== true || verify.containment_verified !== true ||
|
|
120
|
-
verify.active_network_denial_verified !== true || verify.receipt_schema !== 'deadbyte.receipt.v2' ||
|
|
121
|
-
verify.request_sha256 !== exec.request_sha256) throw new Error(`remote contained verification failed: ${JSON.stringify(verify)}`);
|
|
122
|
-
contained = { output, receipt_sha256: exec.receipt_sha256, request_sha256: exec.request_sha256, output_sha256: exec.output_sha256 };
|
|
123
|
-
}
|
|
124
117
|
let agent = null;
|
|
125
|
-
if (
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
118
|
+
if (compactProfile) {
|
|
119
|
+
const manifest = gateway(await client.callTool({ name: 'capability_manifest', arguments: {} }), 'capability_manifest');
|
|
120
|
+
if (manifest.schema !== 'deadbyte.capability-manifest.v1' || manifest.profile !== 'compact' ||
|
|
121
|
+
!Array.isArray(manifest.capabilities) || !/^[0-9a-f]{64}$/.test(String(manifest.ledger_sha256 ?? ''))) {
|
|
122
|
+
throw new Error('R36 compact capability manifest malformed');
|
|
123
|
+
}
|
|
124
|
+
const required = manifest.capabilities.filter(item => item?.production === true && item?.host_only !== true);
|
|
125
|
+
for (const item of required) {
|
|
126
|
+
if (typeof item.compact_tool !== 'string' || !names.includes(item.compact_tool)) {
|
|
127
|
+
throw new Error(`R36 compact capability route missing: ${String(item.id)}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const machine = gateway(await client.callTool({ name: 'machine_observe', arguments: { op: 'machine_status' } }), 'machine_status');
|
|
131
|
+
const nonce = `remote-smoke-${randomBytes(16).toString('hex')}`;
|
|
132
|
+
const ping = gateway(await client.callTool({ name: 'machine_observe', arguments: { op: 'machine_ping', nonce } }), 'machine_ping');
|
|
133
|
+
if (ping.nonce !== nonce || ping.machine_id !== machine.machine_id ||
|
|
134
|
+
ping.release_id !== machine.release_id || ping.release_manifest_sha256 !== machine.release_manifest_sha256) {
|
|
135
|
+
throw new Error('R36 compact machine ping binding mismatch');
|
|
136
|
+
}
|
|
137
|
+
compact = {
|
|
138
|
+
profile: 'compact',
|
|
139
|
+
ledger_sha256: manifest.ledger_sha256,
|
|
140
|
+
capability_count: manifest.capabilities.length,
|
|
141
|
+
production_non_host_capabilities: required.length,
|
|
142
|
+
capability_closure: true,
|
|
143
|
+
machine_id: machine.machine_id,
|
|
144
|
+
release_id: machine.release_id,
|
|
145
|
+
release_manifest_sha256: machine.release_manifest_sha256,
|
|
146
|
+
ping_signature_valid: ping.signature_valid === true
|
|
147
|
+
};
|
|
148
|
+
} else {
|
|
149
|
+
const opened = structured(await client.callTool({ name: 'workspace_open', arguments: { workspace_id: 'demo' } }), 'workspace_open');
|
|
150
|
+
if (opened.status !== 'ok') throw new Error(`workspace_open status=${String(opened.status)}`);
|
|
151
|
+
|
|
152
|
+
proofOutput = `remote-proof.${randomBytes(8).toString('hex')}.txt`;
|
|
153
|
+
proof = structured(await client.callTool({
|
|
154
|
+
name: 'proof_execute', arguments: { workspace_id: 'demo', input: 'input.txt', output: proofOutput }
|
|
155
|
+
}), 'proof_execute');
|
|
156
|
+
if (proof.status !== 'passed' || !/^[0-9a-f]{64}$/.test(proof.request_sha256)) throw new Error('remote proof request identity missing');
|
|
157
|
+
const proofVerify = structured(await client.callTool({
|
|
158
|
+
name: 'evidence_verify', arguments: { workspace_id: 'demo', receipt_path: proof.receipt_path }
|
|
159
|
+
}), 'evidence_verify proof');
|
|
160
|
+
if (proofVerify.ok !== true || proofVerify.signature_valid !== true || proofVerify.receipt_schema !== 'deadbyte.receipt.v2' ||
|
|
161
|
+
proofVerify.request_sha256 !== proof.request_sha256) throw new Error(`remote proof verify failed: ${JSON.stringify(proofVerify)}`);
|
|
162
|
+
|
|
163
|
+
if (mode === 'full') {
|
|
164
|
+
const output = `remote-contained.${randomBytes(8).toString('hex')}.txt`;
|
|
165
|
+
const exec = structured(await client.callTool({
|
|
166
|
+
name: 'contained_transform',
|
|
167
|
+
arguments: { workspace_id: 'demo', input: 'input.txt', output, operation: 'uppercase_ascii' }
|
|
168
|
+
}), 'contained_transform');
|
|
169
|
+
if (exec.status !== 'passed' || exec.output_sha256 !== '3e740f6fcf2c6659bc5f99e0706b09f3c127407bbabe74d05285d2e3276946c2' ||
|
|
170
|
+
!/^[0-9a-f]{64}$/.test(exec.request_sha256)) throw new Error(`remote contained result mismatch: ${JSON.stringify(exec)}`);
|
|
171
|
+
const verify = structured(await client.callTool({
|
|
172
|
+
name: 'evidence_verify', arguments: { workspace_id: 'demo', receipt_path: exec.receipt_path }
|
|
173
|
+
}), 'evidence_verify contained');
|
|
174
|
+
if (verify.ok !== true || verify.signature_valid !== true || verify.containment_verified !== true ||
|
|
175
|
+
verify.active_network_denial_verified !== true || verify.receipt_schema !== 'deadbyte.receipt.v2' ||
|
|
176
|
+
verify.request_sha256 !== exec.request_sha256) throw new Error(`remote contained verification failed: ${JSON.stringify(verify)}`);
|
|
177
|
+
contained = { output, receipt_sha256: exec.receipt_sha256, request_sha256: exec.request_sha256, output_sha256: exec.output_sha256 };
|
|
178
|
+
}
|
|
179
|
+
if (mode === 'full' && agentRuntime) {
|
|
180
|
+
const submissionId = `remote-gate-${randomBytes(8).toString('hex')}`;
|
|
181
|
+
const submitted = structured(await client.callTool({
|
|
182
|
+
name: 'agent_task_submit', arguments: {
|
|
183
|
+
workspace_id: 'demo', submission_id: submissionId, title: 'remote R22 gate',
|
|
184
|
+
steps: [
|
|
185
|
+
{ step_id: 'proof', kind: 'proof_execute', input: { kind: 'workspace', path: 'input.txt' }, retry_limit: 0 },
|
|
186
|
+
{ step_id: 'contained', kind: 'contained_transform', operation: 'uppercase_ascii', input: { kind: 'step_output', step_id: 'proof' }, retry_limit: 0 }
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
}), 'agent_task_submit');
|
|
190
|
+
const run = structured(await client.callTool({
|
|
191
|
+
name: 'agent_task_run', arguments: { workspace_id: 'demo', task_id: submitted.task_id, max_steps: 8 }
|
|
192
|
+
}), 'agent_task_run');
|
|
193
|
+
if (run.state !== 'succeeded' || run.completed_steps !== 2) throw new Error(`agent task run failed: ${JSON.stringify(run)}`);
|
|
194
|
+
const verified = structured(await client.callTool({
|
|
195
|
+
name: 'agent_task_verify', arguments: { workspace_id: 'demo', task_id: submitted.task_id }
|
|
196
|
+
}), 'agent_task_verify');
|
|
197
|
+
if (verified.ok !== true || verified.signature_valid !== true || verified.step_receipts_verified !== 2) {
|
|
198
|
+
throw new Error(`agent task verify failed: ${JSON.stringify(verified)}`);
|
|
134
199
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
name: 'agent_task_run', arguments: { workspace_id: 'demo', task_id: submitted.task_id, max_steps: 8 }
|
|
138
|
-
}), 'agent_task_run');
|
|
139
|
-
if (run.state !== 'succeeded' || run.completed_steps !== 2) throw new Error(`agent task run failed: ${JSON.stringify(run)}`);
|
|
140
|
-
const verified = structured(await client.callTool({
|
|
141
|
-
name: 'agent_task_verify', arguments: { workspace_id: 'demo', task_id: submitted.task_id }
|
|
142
|
-
}), 'agent_task_verify');
|
|
143
|
-
if (verified.ok !== true || verified.signature_valid !== true || verified.step_receipts_verified !== 2) {
|
|
144
|
-
throw new Error(`agent task verify failed: ${JSON.stringify(verified)}`);
|
|
200
|
+
agent = { task_id: submitted.task_id, plan_sha256: submitted.plan_sha256,
|
|
201
|
+
task_receipt_sha256: verified.task_receipt_sha256, step_receipts_verified: verified.step_receipts_verified };
|
|
145
202
|
}
|
|
146
|
-
agent = { task_id: submitted.task_id, plan_sha256: submitted.plan_sha256,
|
|
147
|
-
task_receipt_sha256: verified.task_receipt_sha256, step_receipts_verified: verified.step_receipts_verified };
|
|
148
203
|
}
|
|
149
204
|
console.log(JSON.stringify({
|
|
150
|
-
status: 'passed', mode,
|
|
205
|
+
status: 'passed', mode, profile: compactProfile ? 'compact' : 'legacy-full',
|
|
151
206
|
protocol_revision: '2026-07-28', protocol_era: 'modern', tools: names,
|
|
152
207
|
output_schemas_strict: true,
|
|
153
208
|
machine_runtime: machineRuntime, privileged_host_plane: privilegedHostPlane,
|
|
154
209
|
agent_runtime: agentRuntime, process_runtime: processRuntime,
|
|
155
210
|
machine_fs_runtime: machineFsRuntime, observation_runtime: observationRuntime,
|
|
156
211
|
coding_runtime: codingRuntime, autonomous_runtime: autonomousRuntime,
|
|
157
|
-
|
|
158
|
-
|
|
212
|
+
compact,
|
|
213
|
+
proof: proof ? { output: proofOutput, receipt_sha256: proof.receipt_sha256,
|
|
214
|
+
request_sha256: proof.request_sha256, signature_valid: true } : null,
|
|
159
215
|
contained, agent
|
|
160
216
|
}, null, 2));
|
|
161
217
|
} finally {
|
package/src/runtime-update.mjs
CHANGED
|
@@ -497,6 +497,34 @@ export function runControllerStartNoCapture(ctx,timeoutMs=45000){
|
|
|
497
497
|
const call=controllerInvocation(ctx,'start');
|
|
498
498
|
return controlResult(call.exe,call.args,{cwd:call.cwd,timeoutMs,stdio:'ignore'});
|
|
499
499
|
}
|
|
500
|
+
export async function readControllerStartFailure(ctx){
|
|
501
|
+
const file=path.join(ctx.runtime_root,'controller-start-failure.json');
|
|
502
|
+
let bytes;
|
|
503
|
+
try { bytes=await readFile(file); }
|
|
504
|
+
catch(error){ if(error?.code==='ENOENT') return null; throw error; }
|
|
505
|
+
const text=bytes.toString('utf8').replace(/^\uFEFF/,'');
|
|
506
|
+
let value;
|
|
507
|
+
try { value=JSON.parse(text); } catch(error){ throw new Error(`controller start failure evidence JSON invalid: ${error.message}`); }
|
|
508
|
+
if(value?.schema!=='deadbyte.controller-start-failure.v1'||!['start','resume','restart'].includes(String(value?.command??''))||
|
|
509
|
+
typeof value?.message!=='string'||value.message.length<1||typeof value?.failed_at_utc!=='string') {
|
|
510
|
+
throw new Error('controller start failure evidence schema invalid');
|
|
511
|
+
}
|
|
512
|
+
return {path:file,sha256:sha256Bytes(bytes),...value};
|
|
513
|
+
}
|
|
514
|
+
export async function preserveControllerStartFailure(ctx,evidenceDir){
|
|
515
|
+
const evidence=await readControllerStartFailure(ctx);
|
|
516
|
+
if(!evidence) return null;
|
|
517
|
+
const source=path.join(ctx.runtime_root,'controller-start-failure.json');
|
|
518
|
+
const bytes=await readFile(source);
|
|
519
|
+
const sourceSha256=sha256Bytes(bytes);
|
|
520
|
+
if(sourceSha256!==evidence.sha256) throw new Error('controller start failure evidence changed before preservation');
|
|
521
|
+
const destination=path.join(path.resolve(evidenceDir),'controller-start-failure.json');
|
|
522
|
+
await atomicBytes(destination,bytes);
|
|
523
|
+
const persistedSha256=sha256Bytes(await readFile(destination));
|
|
524
|
+
if(persistedSha256!==sourceSha256) throw new Error('preserved controller start failure evidence SHA-256 mismatch');
|
|
525
|
+
return {path:destination,sha256:persistedSha256,schema:evidence.schema,command:evidence.command,
|
|
526
|
+
failed_at_utc:evidence.failed_at_utc,message:evidence.message};
|
|
527
|
+
}
|
|
500
528
|
async function readLedger(ctx,name){
|
|
501
529
|
const file=path.join(ctx.runtime_root,`${name}.instance.json`);
|
|
502
530
|
const value=JSON.parse((await readFile(file,'utf8')).replace(/^\uFEFF/,''));
|
|
@@ -554,12 +582,20 @@ async function waitCoreGone(runtime,timeoutMs=10000){
|
|
|
554
582
|
}
|
|
555
583
|
async function startAndVerify(ctx,oldRuntime){
|
|
556
584
|
let start=null;
|
|
585
|
+
await rm(path.join(ctx.runtime_root,'controller-start-failure.json'),{force:true});
|
|
557
586
|
for(let attempt=1;attempt<=3;attempt+=1){
|
|
558
587
|
start=runControllerStartNoCapture(ctx,45000);
|
|
559
588
|
if(!start.error&&start.status===0) break;
|
|
560
589
|
if(attempt<3) await sleep(1500);
|
|
561
590
|
}
|
|
562
|
-
if(start.error||start.status!==0)
|
|
591
|
+
if(start.error||start.status!==0){
|
|
592
|
+
let detail='';
|
|
593
|
+
try {
|
|
594
|
+
const evidence=await readControllerStartFailure(ctx);
|
|
595
|
+
if(evidence) detail=` controller=${evidence.message} evidence_sha256=${evidence.sha256}`;
|
|
596
|
+
} catch(error) { detail=` controller_evidence_error=${error.message}`; }
|
|
597
|
+
throw new Error(`controller start transport failed status=${start.status} error=${start.error?.message??'none'}${detail}`);
|
|
598
|
+
}
|
|
563
599
|
let statusText=''; let status=null;
|
|
564
600
|
const started=Date.now();
|
|
565
601
|
while(Date.now()-started<45000){
|
|
@@ -879,6 +915,18 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
|
|
|
879
915
|
return {ok:true,update_id:updateId,state_path:statePath,receipt_path:receiptPath,receipt_sha256:receipt.receipt_sha256,
|
|
880
916
|
pointer:newPointer,new_runtime:newRuntime,final_authority:finalAuthority,workspace_root:workspace.target};
|
|
881
917
|
} catch(error){
|
|
918
|
+
let controllerStartFailure=null,controllerStartFailurePreservationError=null;
|
|
919
|
+
if(pointerCommitted&&newCtx){
|
|
920
|
+
try {
|
|
921
|
+
controllerStartFailure=await preserveControllerStartFailure(newCtx,evidenceDir);
|
|
922
|
+
const claimed=/evidence_sha256=([0-9a-f]{64})/.exec(String(error?.message??error));
|
|
923
|
+
if(claimed&&(!controllerStartFailure||controllerStartFailure.sha256!==claimed[1])) {
|
|
924
|
+
controllerStartFailurePreservationError=`controller start failure evidence claim mismatch expected=${claimed[1]} preserved=${controllerStartFailure?.sha256??'missing'}`;
|
|
925
|
+
}
|
|
926
|
+
} catch(preserveError) {
|
|
927
|
+
controllerStartFailurePreservationError=String(preserveError?.message??preserveError);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
882
930
|
const rollback={attempted:false,new_runtime_stopped:false,pointer_restored:false,launchers_restored:false,workspace_restored:false,
|
|
883
931
|
mcp_url_restored:false,old_runtime_restarted:false,authority_restored:false,errors:[]};
|
|
884
932
|
try {
|
|
@@ -916,6 +964,8 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
|
|
|
916
964
|
} catch(rollbackError){ rollback.errors.push(`rollback:${rollbackError.message}`); }
|
|
917
965
|
const failure={schema:FAILURE_SCHEMA,update_id:updateId,release_version:expectedVersion,candidate_manifest_sha256:expectedManifest,
|
|
918
966
|
failed_at_utc:new Date().toISOString(),phase:journal.phase??'unknown',error:{name:error?.name??'Error',code:error?.code??null,message:String(error?.message??error)},
|
|
967
|
+
controller_start_failure:controllerStartFailure,
|
|
968
|
+
controller_start_failure_preservation_error:controllerStartFailurePreservationError,
|
|
919
969
|
predecessor_mode:predecessor?.mode??null,prior_pointer:publicPointer(pointerInfo?.pointer??null),attempted_pointer:publicPointer(newPointer),old_runtime:generationTuple(oldRuntime),rollback};
|
|
920
970
|
await writeState(statePath,journal,'failed',{failure,rollback}).catch(()=>{});
|
|
921
971
|
let failureReceipt=null;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { sign as cryptoSign, verify as cryptoVerify } from 'node:crypto';
|
|
2
|
+
import { canonicalJson } from './canonical-json.mjs';
|
|
3
|
+
import { loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
|
|
4
|
+
|
|
5
|
+
const DOMAIN=Buffer.from('DEADBYTE-SEARCH-CURSOR-V1\0','utf8');
|
|
6
|
+
const HEX64=/^[0-9a-f]{64}$/;
|
|
7
|
+
const SCHEMA='deadbyte.search-cursor.v1';
|
|
8
|
+
|
|
9
|
+
function validateBody(body){
|
|
10
|
+
if(!body||typeof body!=='object'||Array.isArray(body)||body.schema!==SCHEMA) throw new Error('search cursor body invalid');
|
|
11
|
+
if(!HEX64.test(body.query_sha256??'')) throw new Error('search cursor query identity invalid');
|
|
12
|
+
if(!HEX64.test(body.snapshot_sha256??'')) throw new Error('search cursor snapshot identity invalid');
|
|
13
|
+
if(!Number.isInteger(body.offset)||body.offset<0||body.offset>1_000_000) throw new Error('search cursor offset invalid');
|
|
14
|
+
return {schema:SCHEMA,query_sha256:body.query_sha256,snapshot_sha256:body.snapshot_sha256,offset:body.offset};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createSearchCursorCodec({signingKeyPath,verifyKeyPath}){
|
|
18
|
+
if(!signingKeyPath||!verifyKeyPath) throw new Error('search cursor codec requires signing and verify keys');
|
|
19
|
+
return Object.freeze({
|
|
20
|
+
async encode(input){
|
|
21
|
+
const body=validateBody({...input,schema:SCHEMA});
|
|
22
|
+
const signing=await loadPrivateSigningKey(signingKeyPath);
|
|
23
|
+
const bytes=Buffer.from(canonicalJson(body),'utf8');
|
|
24
|
+
const signature=cryptoSign(null,Buffer.concat([DOMAIN,bytes]),signing.privateKey).toString('base64');
|
|
25
|
+
const envelope={body,attestation:{algorithm:'ed25519',key_id:signing.keyId,signature_base64:signature}};
|
|
26
|
+
return Buffer.from(canonicalJson(envelope),'utf8').toString('base64url');
|
|
27
|
+
},
|
|
28
|
+
async decode(cursor){
|
|
29
|
+
if(typeof cursor!=='string'||cursor.length<1||cursor.length>8192) throw new Error('search cursor invalid');
|
|
30
|
+
let envelope;
|
|
31
|
+
try{envelope=JSON.parse(Buffer.from(cursor,'base64url').toString('utf8'));}catch{throw new Error('search cursor invalid');}
|
|
32
|
+
const body=validateBody(envelope?.body);
|
|
33
|
+
const verifyKey=await loadPublicVerifyKey(verifyKeyPath);
|
|
34
|
+
const att=envelope?.attestation;
|
|
35
|
+
if(att?.algorithm!=='ed25519'||att?.key_id!==verifyKey.keyId||typeof att?.signature_base64!=='string') throw new Error('search cursor attestation invalid');
|
|
36
|
+
const signature=Buffer.from(att.signature_base64,'base64');
|
|
37
|
+
if(signature.length!==64||signature.toString('base64')!==att.signature_base64) throw new Error('search cursor signature encoding invalid');
|
|
38
|
+
const bytes=Buffer.from(canonicalJson(body),'utf8');
|
|
39
|
+
if(!cryptoVerify(null,Buffer.concat([DOMAIN,bytes]),verifyKey.publicKey,signature)) throw new Error('search cursor signature invalid');
|
|
40
|
+
return body;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const SEARCH_CURSOR_SCHEMA=SCHEMA;
|
package/src/semantic-code.mjs
CHANGED
|
@@ -149,7 +149,7 @@ export function createTypeScriptSemanticBackend({codingRuntime}){
|
|
|
149
149
|
}
|
|
150
150
|
async function diagnostics({rootId,relativePath,maxResults=200}){
|
|
151
151
|
if(!Number.isInteger(maxResults)||maxResults<1||maxResults>500) throw new Error('max_results must be in [1, 500]');
|
|
152
|
-
await codingRuntime.fileRead({rootId,relativePath,encoding:'utf8'});
|
|
152
|
+
const file=await codingRuntime.fileRead({rootId,relativePath,encoding:'utf8'});
|
|
153
153
|
const project=await buildProject(codingRuntime,rootId); const target=targetFile(project,relativePath);
|
|
154
154
|
const all=[...project.service.getSyntacticDiagnostics(target.absolute),...project.service.getSemanticDiagnostics(target.absolute)];
|
|
155
155
|
const diagnosticsOut=all.slice(0,maxResults).map(item=>{
|
|
@@ -157,23 +157,90 @@ export function createTypeScriptSemanticBackend({codingRuntime}){
|
|
|
157
157
|
const loc=locationFromSpan(project,target.absolute,span,'','diagnostic');
|
|
158
158
|
return {message:ts.flattenDiagnosticMessageText(item.messageText,'\n'),code:String(item.code),severity:diagnosticCategory(item.category),...(loc?{location:loc}:{})};
|
|
159
159
|
});
|
|
160
|
-
|
|
160
|
+
const stable={root_id:rootId,path:relativePath,file_sha256:file.sha256,engine:`typescript-${ts.version}`,diagnostics:diagnosticsOut,truncated:all.length>maxResults};
|
|
161
|
+
return {status:'ok',...stable,result_sha256:digest({schema:'deadbyte.semantic-diagnostics.v1',...stable})};
|
|
162
|
+
}
|
|
163
|
+
async function implementations({rootId,relativePath,line,column,maxResults=200}){
|
|
164
|
+
if(!Number.isInteger(maxResults)||maxResults<1||maxResults>500) throw new Error('max_results must be in [1, 500]');
|
|
165
|
+
const file=await codingRuntime.fileRead({rootId,relativePath,encoding:'utf8'});
|
|
166
|
+
const project=await buildProject(codingRuntime,rootId); const target=targetFile(project,relativePath); const pos=offsetAt(target.source,line,column);
|
|
167
|
+
const raw=project.service.getImplementationAtPosition(target.absolute,pos)??[];
|
|
168
|
+
const locations=raw.map(item=>locationFromSpan(project,item.fileName,item.textSpan,item.name??'',item.kind??'implementation')).filter(Boolean).slice(0,maxResults);
|
|
169
|
+
const stable={root_id:rootId,path:relativePath,line,column,file_sha256:file.sha256,engine:`typescript-${ts.version}`,locations,truncated:raw.length>maxResults};
|
|
170
|
+
return {status:'ok',...stable,result_sha256:digest({schema:'deadbyte.semantic-implementations.v1',...stable})};
|
|
161
171
|
}
|
|
162
|
-
|
|
172
|
+
async function renameEdits({rootId,relativePath,line,column,newName}){
|
|
173
|
+
const file=await codingRuntime.fileRead({rootId,relativePath,encoding:'utf8'});
|
|
174
|
+
const project=await buildProject(codingRuntime,rootId); const target=targetFile(project,relativePath); const pos=offsetAt(target.source,line,column);
|
|
175
|
+
const info=project.service.getRenameInfo(target.absolute,pos,{allowRenameOfImportPath:false});
|
|
176
|
+
if(!info?.canRename) throw new Error(`semantic rename unavailable: ${info?.localizedErrorMessage??'symbol cannot be renamed'}`);
|
|
177
|
+
const raw=project.service.findRenameLocations(target.absolute,pos,false,false,true)??[];
|
|
178
|
+
if(raw.length<1) throw new Error('semantic rename produced no locations');
|
|
179
|
+
const edits=[];
|
|
180
|
+
for(const item of raw){
|
|
181
|
+
const loc=locationFromSpan(project,item.fileName,item.textSpan,'','rename');
|
|
182
|
+
if(!loc) continue;
|
|
183
|
+
edits.push({path:loc.path,start:item.textSpan.start,length:item.textSpan.length,new_text:`${item.prefixText??''}${newName}${item.suffixText??''}`});
|
|
184
|
+
}
|
|
185
|
+
if(edits.length<1) throw new Error('semantic rename produced no allowed edits');
|
|
186
|
+
edits.sort((a,b)=>a.path.localeCompare(b.path,'en')||a.start-b.start||a.length-b.length);
|
|
187
|
+
return {status:'ok',root_id:rootId,path:relativePath,engine:`typescript-${ts.version}`,target_file_sha256:file.sha256,edits};
|
|
188
|
+
}
|
|
189
|
+
return Object.freeze({id:'typescript',extensions:new Set(SOURCE_EXTENSIONS),outline,definition,references,replaceSymbol,diagnostics,implementations,renameEdits});
|
|
163
190
|
}
|
|
164
191
|
|
|
165
|
-
export function createSemanticCodeRuntime({codingRuntime,backends=[]}){
|
|
192
|
+
export function createSemanticCodeRuntime({codingRuntime,backends=[],nativeDiffRuntime=null}){
|
|
166
193
|
if(!codingRuntime) throw new Error('semantic runtime requires coding runtime');
|
|
167
194
|
if(!Array.isArray(backends)) throw new Error('semantic runtime backends must be an array');
|
|
168
195
|
const typescript=createTypeScriptSemanticBackend({codingRuntime});
|
|
169
196
|
const registry=createSemanticBackendRegistry({backends:[typescript,...backends]});
|
|
170
|
-
const
|
|
197
|
+
const backendFor=args=>registry.backendForPath(args.relativePath);
|
|
198
|
+
const route=(method,args)=>{
|
|
199
|
+
const backend=backendFor(args);
|
|
200
|
+
if(typeof backend[method]!=='function') return {status:'unsupported',capability:method,engine:backend.id};
|
|
201
|
+
return backend[method](args);
|
|
202
|
+
};
|
|
203
|
+
async function renamePreview({rootId,relativePath,line,column,newName}){
|
|
204
|
+
if(typeof newName!=='string'||!/^[A-Za-z_$][A-Za-z0-9_$]{0,127}$/.test(newName)) throw new Error('semantic rename new name must be a valid bounded identifier');
|
|
205
|
+
if(!nativeDiffRuntime||typeof nativeDiffRuntime.preview!=='function') throw new Error('semantic rename preview requires Native Diff runtime');
|
|
206
|
+
const backend=backendFor({relativePath});
|
|
207
|
+
if(typeof backend.renameEdits!=='function') return {status:'unsupported',capability:'rename_preview',engine:backend.id};
|
|
208
|
+
const plan=await backend.renameEdits({rootId,relativePath,line,column,newName});
|
|
209
|
+
if(plan?.status==='unsupported') return plan;
|
|
210
|
+
if(!plan||plan.status!=='ok'||!Array.isArray(plan.edits)||plan.edits.length<1) throw new Error('semantic rename backend returned no normalized edits');
|
|
211
|
+
const groups=new Map();
|
|
212
|
+
for(const edit of plan.edits){
|
|
213
|
+
if(!edit||typeof edit.path!=='string'||!Number.isInteger(edit.start)||edit.start<0||!Number.isInteger(edit.length)||edit.length<0||typeof edit.new_text!=='string') throw new Error('semantic rename edit malformed');
|
|
214
|
+
const list=groups.get(edit.path)??[]; list.push(edit); groups.set(edit.path,list);
|
|
215
|
+
}
|
|
216
|
+
const operations=[]; const preimages=[]; let editCount=0;
|
|
217
|
+
for(const filePath of [...groups.keys()].sort((a,b)=>a.localeCompare(b,'en'))){
|
|
218
|
+
const file=await codingRuntime.fileRead({rootId,relativePath:filePath,encoding:'utf8'});
|
|
219
|
+
const edits=[...groups.get(filePath)].sort((a,b)=>a.start-b.start||a.length-b.length);
|
|
220
|
+
let priorEnd=-1;
|
|
221
|
+
for(const edit of edits){
|
|
222
|
+
if(edit.start<priorEnd) throw new Error(`semantic rename overlapping edits in ${filePath}`);
|
|
223
|
+
if(edit.start+edit.length>file.content.length) throw new Error(`semantic rename edit outside ${filePath}`);
|
|
224
|
+
priorEnd=edit.start+edit.length;
|
|
225
|
+
}
|
|
226
|
+
let next=file.content;
|
|
227
|
+
for(const edit of [...edits].sort((a,b)=>b.start-a.start)) next=next.slice(0,edit.start)+edit.new_text+next.slice(edit.start+edit.length);
|
|
228
|
+
operations.push({kind:'write',path:filePath,content:next,encoding:'utf8',expected_sha256:file.sha256});
|
|
229
|
+
preimages.push({path:filePath,sha256:file.sha256});
|
|
230
|
+
editCount+=edits.length;
|
|
231
|
+
}
|
|
232
|
+
const diff=await nativeDiffRuntime.preview({rootId,title:`semantic rename ${relativePath} -> ${newName}`,operations});
|
|
233
|
+
return {status:'ready',diff_id:diff.diff_id,affected_files:operations.length,edit_count:editCount,
|
|
234
|
+
backend:plan.engine??backend.id,preimages};
|
|
235
|
+
}
|
|
171
236
|
return Object.freeze({
|
|
172
237
|
outline:async args=>route('outline',args),
|
|
173
238
|
definition:async args=>route('definition',args),
|
|
174
239
|
references:async args=>route('references',args),
|
|
175
240
|
replaceSymbol:async args=>route('replaceSymbol',args),
|
|
176
241
|
diagnostics:async args=>route('diagnostics',args),
|
|
242
|
+
implementations:async args=>route('implementations',args),
|
|
243
|
+
renamePreview,
|
|
177
244
|
languages:()=>registry.languages()
|
|
178
245
|
});
|
|
179
246
|
}
|
|
@@ -119,7 +119,7 @@ export function createSemanticLspBackend({id,extensions,codingRuntime,server}){
|
|
|
119
119
|
const open=async ctx=>{
|
|
120
120
|
const client=createLspStdioClient({executablePath:server.executable_path,expectedSha256:server.executable_sha256,args:server.args??[],cwd:ctx.root.real_path,env:server.env??{},dependencies:server.dependencies??[],requestTimeoutMs:server.request_timeout_ms??10000,maxMessageBytes:server.max_message_bytes??4*1024*1024});
|
|
121
121
|
try{
|
|
122
|
-
await client.initialize({processId:null,rootPath:ctx.root.real_path,rootUri:ctx.rootUri,workspaceFolders:[{uri:ctx.rootUri,name:path.basename(ctx.root.real_path)}],capabilities:{workspace:{workspaceFolders:true},textDocument:{documentSymbol:{hierarchicalDocumentSymbolSupport:true},definition:{},references:{},diagnostic:{}}},initializationOptions:server.initialization_options??{}});
|
|
122
|
+
await client.initialize({processId:null,rootPath:ctx.root.real_path,rootUri:ctx.rootUri,workspaceFolders:[{uri:ctx.rootUri,name:path.basename(ctx.root.real_path)}],capabilities:{workspace:{workspaceFolders:true},textDocument:{documentSymbol:{hierarchicalDocumentSymbolSupport:true},definition:{},references:{},implementation:{},rename:{prepareSupport:false},diagnostic:{}}},initializationOptions:server.initialization_options??{}});
|
|
123
123
|
for(const document of await workspaceDocuments(ctx)) client.notify('textDocument/didOpen',{textDocument:{uri:document.uri,languageId,version:1,text:document.text}});
|
|
124
124
|
if(startupSettleMs>0) await new Promise(resolve=>setTimeout(resolve,startupSettleMs));
|
|
125
125
|
return client;
|
|
@@ -163,7 +163,43 @@ export function createSemanticLspBackend({id,extensions,codingRuntime,server}){
|
|
|
163
163
|
const raw=await withClient(ctx,client=>requestRead(client,'textDocument/diagnostic',{textDocument:{uri:ctx.uri}}));
|
|
164
164
|
const items=Array.isArray(raw?.items)?raw.items:[];
|
|
165
165
|
const diagnosticsOut=items.slice(0,maxResults).filter(item=>validRange(item?.range)).map(item=>({message:String(item.message??''),code:String(item.code??''),severity:SEVERITY[Number(item.severity)]??'unknown',location:{path:ctx.relativePath,name:'',kind:'diagnostic',start_line:item.range.start.line,start_column:item.range.start.character,end_line:item.range.end.line,end_column:item.range.end.character}}));
|
|
166
|
-
|
|
166
|
+
const stable={root_id:rootId,path:ctx.relativePath,file_sha256:ctx.file.sha256,engine:id,diagnostics:diagnosticsOut,truncated:items.length>maxResults};
|
|
167
|
+
return {status:'ok',...stable,result_sha256:digest({schema:'deadbyte.semantic-diagnostics.v1',...stable})};
|
|
168
|
+
}
|
|
169
|
+
async function implementations({rootId,relativePath,line,column,maxResults=200}){
|
|
170
|
+
if(!Number.isInteger(maxResults)||maxResults<1||maxResults>500) throw new Error('max_results must be in [1, 500]');
|
|
171
|
+
const ctx=await rootContext(codingRuntime,rootId,relativePath);
|
|
172
|
+
let raw;
|
|
173
|
+
try { raw=await withClient(ctx,client=>requestRead(client,'textDocument/implementation',{textDocument:{uri:ctx.uri},position:{line,character:column}})); }
|
|
174
|
+
catch(error){ if(/-32601|method not found/i.test(String(error))) return {status:'unsupported',capability:'implementations',engine:id}; throw error; }
|
|
175
|
+
const normalized=normalizeLocations(raw,ctx,maxResults);
|
|
176
|
+
const stable={root_id:rootId,path:ctx.relativePath,line,column,file_sha256:ctx.file.sha256,engine:id,...normalized};
|
|
177
|
+
return {status:'ok',...stable,result_sha256:digest({schema:'deadbyte.semantic-implementations.v1',...stable})};
|
|
178
|
+
}
|
|
179
|
+
async function renameEdits({rootId,relativePath,line,column,newName}){
|
|
180
|
+
const ctx=await rootContext(codingRuntime,rootId,relativePath);
|
|
181
|
+
let raw;
|
|
182
|
+
try { raw=await withClient(ctx,client=>requestRead(client,'textDocument/rename',{textDocument:{uri:ctx.uri},position:{line,character:column},newName})); }
|
|
183
|
+
catch(error){ if(/-32601|method not found/i.test(String(error))) return {status:'unsupported',capability:'rename_preview',engine:id}; throw error; }
|
|
184
|
+
const byUri=[];
|
|
185
|
+
if(raw?.changes&&typeof raw.changes==='object') for(const [uri,edits] of Object.entries(raw.changes)) byUri.push({uri,edits});
|
|
186
|
+
for(const change of raw?.documentChanges??[]){ if(change?.textDocument?.uri&&Array.isArray(change.edits)) byUri.push({uri:change.textDocument.uri,edits:change.edits}); }
|
|
187
|
+
const normalized=[];
|
|
188
|
+
for(const group of byUri){
|
|
189
|
+
let absolute; try{absolute=path.resolve(fileURLToPath(group.uri));}catch{continue;}
|
|
190
|
+
if(!inside(ctx.root.real_path,absolute)||denied(ctx.root,absolute)) continue;
|
|
191
|
+
const rel=relNormalized(path.relative(ctx.root.real_path,absolute));
|
|
192
|
+
const file=await codingRuntime.fileRead({rootId,relativePath:rel,encoding:'utf8'});
|
|
193
|
+
for(const edit of group.edits??[]){
|
|
194
|
+
if(!validRange(edit?.range)||typeof edit?.newText!=='string') throw new Error('semantic LSP rename edit malformed');
|
|
195
|
+
const start=positionToOffset(file.content,edit.range.start); const end=positionToOffset(file.content,edit.range.end);
|
|
196
|
+
if(end<start) throw new Error('semantic LSP rename range invalid');
|
|
197
|
+
normalized.push({path:rel,start,length:end-start,new_text:edit.newText});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if(normalized.length<1) throw new Error('semantic LSP rename produced no allowed edits');
|
|
201
|
+
normalized.sort((a,b)=>a.path.localeCompare(b.path,'en')||a.start-b.start||a.length-b.length);
|
|
202
|
+
return {status:'ok',root_id:rootId,path:ctx.relativePath,engine:id,target_file_sha256:ctx.file.sha256,edits:normalized};
|
|
167
203
|
}
|
|
168
204
|
async function replaceSymbol({rootId,relativePath,symbolId,newBody,expectedSha256}){
|
|
169
205
|
if(typeof newBody!=='string'||newBody.length>524288) throw new Error('new_body exceeds 524288 characters');
|
|
@@ -179,5 +215,5 @@ export function createSemanticLspBackend({id,extensions,codingRuntime,server}){
|
|
|
179
215
|
const result=await codingRuntime.textReplace({rootId,relativePath:ctx.relativePath,oldString:oldBody,newString:newBody,expectedSha256,expectedReplacements:1});
|
|
180
216
|
return {...result,symbol_id:symbolId,symbol_name:symbol.name,engine:id};
|
|
181
217
|
}
|
|
182
|
-
return Object.freeze({id,extensions:extensionSet,outline,definition,references,replaceSymbol,diagnostics});
|
|
218
|
+
return Object.freeze({id,extensions:extensionSet,outline,definition,references,replaceSymbol,diagnostics,implementations,renameEdits});
|
|
183
219
|
}
|
package/src/version.mjs
CHANGED