deadbyte-mcp 0.15.0 → 0.15.2
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 +109 -64
- package/PROVENANCE.json +11 -0
- package/R45-PARENT.json +18 -0
- package/README.txt +36 -20
- package/RELEASE.json +53 -0
- package/SBOM.cdx.json +46 -0
- package/TEST-MATRIX.json +31 -0
- package/bin/deadbyte-hardware-observer-r45.exe +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/controller/README.TXT +2 -2
- package/controller/deadbyte-controller.ps1 +51 -3
- package/controller/deadbyte-process-policy.json +63 -1
- package/docs/ARCHITECTURE.md +3 -4
- package/native/deadbyte-hardware-observer.cpp +308 -0
- package/package.json +14 -7
- package/scripts/autonomous-tests.ps1 +2 -2
- package/scripts/build-hardware-r45.ps1 +71 -0
- package/scripts/deferred-slot-operation-r45.mjs +126 -0
- package/scripts/deferred-slot-operation-r46.mjs +126 -0
- package/scripts/derive-r45-parent.mjs +24 -0
- package/scripts/derive-r45-release.mjs +72 -0
- package/scripts/derive-r45-tests.mjs +20 -0
- package/scripts/final-closure-r45.mjs +121 -0
- package/scripts/final-closure-r46.mjs +98 -0
- package/scripts/final-closure-verify-r45.mjs +220 -0
- package/scripts/final-closure-verify-r46.mjs +119 -0
- package/scripts/finalize-r45.mjs +219 -0
- package/scripts/full-regression-r45.mjs +20 -0
- package/scripts/full-regression-r46.mjs +20 -0
- package/scripts/full-regression-summary-r45.mjs +14 -0
- package/scripts/gate-windows-r45.ps1 +44 -0
- package/scripts/gate-windows-r46.ps1 +56 -0
- package/scripts/init-host-policy.ps1 +39 -1
- package/scripts/inspect-active-release-r45.mjs +20 -0
- package/scripts/refresh-process-policy.mjs +64 -7
- package/scripts/release-manifest.mjs +3 -1
- package/scripts/release-parity-r45.mjs +253 -0
- package/scripts/release-parity-r46.mjs +187 -0
- package/scripts/release-parity-tests-r45.ps1 +8 -0
- package/scripts/verify-r45-parent.mjs +142 -0
- package/scripts/windows-gate-evidence-r45.mjs +60 -0
- package/scripts/windows-gate-evidence-r46.mjs +110 -0
- package/src/autonomous-runtime.mjs +12 -7
- package/src/capability-ledger.mjs +2 -0
- package/src/compact-gateway-mcp-tools.mjs +11 -7
- package/src/deadbyte-cli.mjs +24 -12
- package/src/doctor-r45.mjs +65 -0
- package/src/host-plane.mjs +133 -85
- package/src/machine-fs-mcp-tools.mjs +3 -3
- package/src/machine-fs-output-contracts.mjs +1 -1
- package/src/machine-fs-read-worker.mjs +46 -0
- package/src/machine-fs.mjs +63 -10
- package/src/mcp-server.mjs +68 -23
- package/src/mcp-smoke-client.mjs +4 -1
- package/src/native-diff-v2.mjs +9 -4
- package/src/native-diff.mjs +29 -7
- package/src/observation-mcp-tools.mjs +5 -1
- package/src/observation-output-contracts.mjs +8 -0
- package/src/observation-runtime.mjs +57 -5
- package/src/observation-smoke-client.mjs +6 -2
- package/src/output-contracts.mjs +27 -11
- package/src/process-mcp-tools.mjs +18 -5
- package/src/process-output-contracts.mjs +20 -3
- package/src/process-policy.mjs +3 -1
- package/src/process-runtime.mjs +99 -19
- package/src/process-smoke-client.mjs +67 -22
- package/src/r45-compact-disarmed-probe.mjs +74 -0
- package/src/r45-full-surface-probe.mjs +34 -0
- package/src/release-generation.mjs +23 -3
- package/src/remote-device-r44.mjs +53 -15
- package/src/remote-mcp-r45-parity-client.mjs +206 -0
- package/src/remote-mcp-smoke-client.mjs +1 -1
- package/src/runtime-update.mjs +36 -8
- package/src/stdio-console-guard.mjs +24 -0
- package/src/version.mjs +1 -1
- package/test/agent-runtime.test.mjs +1 -0
- package/test/autonomous-loop.test.mjs +24 -0
- package/test/autonomous-runtime.test.mjs +1 -0
- package/test/cli-entrypoint.test.mjs +4 -4
- package/test/coding-plane.test.mjs +1 -0
- package/test/compact-gateway-r36.test.mjs +28 -0
- package/test/compact-max-catalog-r45.test.mjs +99 -0
- package/test/contained.test.mjs +23 -11
- package/test/controller.test.mjs +5 -1
- package/test/core.test.mjs +1 -1
- package/test/full-max-catalog-r45.test.mjs +91 -0
- package/test/full-max-catalog-r46.test.mjs +92 -0
- package/test/hardware-observation-r45.test.mjs +44 -0
- package/test/host-inline-r45.test.mjs +67 -0
- package/test/machine-fs-mcp-tools.test.mjs +4 -1
- package/test/machine-fs-runtime.test.mjs +18 -2
- package/test/machine-mcp-integration.test.mjs +1 -0
- package/test/observation-mcp-tools.test.mjs +4 -3
- package/test/observation-runtime.test.mjs +29 -2
- package/test/output-schema.test.mjs +1 -0
- package/test/package-boundary.test.mjs +1 -1
- package/test/process-mcp-tools.test.mjs +23 -5
- package/test/process-policy-refresh.test.mjs +87 -10
- package/test/process-runtime.test.mjs +111 -1
- package/test/process-supervision.test.mjs +4 -2
- package/test/production-docs.test.mjs +3 -3
- package/test/r33-closeout-regression.test.mjs +20 -19
- package/test/r33-finalization.test.mjs +3 -3
- package/test/r45-autonomous-quick-test.test.mjs +10 -0
- package/test/r45-finalization.test.mjs +84 -0
- package/test/release-generation.test.mjs +26 -11
- package/test/release-version.test.mjs +112 -46
- package/test/remote-device-r44.test.mjs +75 -0
- package/test/remote-r24-catalog.test.mjs +2 -2
- package/test/runtime-host-bootstrap-r45.test.mjs +46 -0
package/src/machine-fs.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createHash, randomBytes, sign as cryptoSign, verify as cryptoVerify } from 'node:crypto';
|
|
2
2
|
import { createReadStream } from 'node:fs';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
3
4
|
import { lstat, mkdir as fsMkdir, open, readFile, readdir, realpath, rename, rm, rmdir } from 'node:fs/promises';
|
|
4
5
|
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
5
7
|
import { canonicalJson } from './canonical-json.mjs';
|
|
6
8
|
import { assertCodingArmed, recheckCodingAuthority } from './coding-plane.mjs';
|
|
7
9
|
import { isInside, validatePortableRelativePath } from './path-policy.mjs';
|
|
@@ -29,6 +31,55 @@ async function sha256File(file) {
|
|
|
29
31
|
return hash.digest('hex');
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
const machineFsReadWorkerPath = fileURLToPath(new URL('./machine-fs-read-worker.mjs', import.meta.url));
|
|
35
|
+
async function boundedReadWorker({ file, offset, length, hashFile, timeoutMs, workerPath = machineFsReadWorkerPath }) {
|
|
36
|
+
return await new Promise((resolve, reject) => {
|
|
37
|
+
const child = spawn(process.execPath, [workerPath, file, String(offset), String(length), hashFile ? '1' : '0'], {
|
|
38
|
+
windowsHide: true, shell: false, stdio: ['ignore','pipe','pipe']
|
|
39
|
+
});
|
|
40
|
+
const stdout = [];
|
|
41
|
+
let stdoutBytes = 0;
|
|
42
|
+
let stderr = '';
|
|
43
|
+
let settled = false;
|
|
44
|
+
let timedOut = false;
|
|
45
|
+
const finish = (fn, value) => {
|
|
46
|
+
if (settled) return;
|
|
47
|
+
settled = true;
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
fn(value);
|
|
50
|
+
};
|
|
51
|
+
child.stdout.on('data', chunk => {
|
|
52
|
+
stdoutBytes += chunk.length;
|
|
53
|
+
if (stdoutBytes > 8 * 1024 * 1024) {
|
|
54
|
+
child.kill();
|
|
55
|
+
finish(reject, new Error('machine fs read worker output exceeded bound'));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
stdout.push(Buffer.from(chunk));
|
|
59
|
+
});
|
|
60
|
+
child.stderr.on('data', chunk => { stderr = (stderr + chunk.toString('utf8')).slice(-16384); });
|
|
61
|
+
child.once('error', error => finish(reject, error));
|
|
62
|
+
child.once('close', code => {
|
|
63
|
+
if (settled) return;
|
|
64
|
+
if (timedOut) return finish(reject, new Error(`machine fs read timed out after ${timeoutMs}ms`));
|
|
65
|
+
if (code !== 0) return finish(reject, new Error(`machine fs read worker failed exit=${String(code)}: ${stderr}`));
|
|
66
|
+
let value;
|
|
67
|
+
try { value = JSON.parse(Buffer.concat(stdout).toString('utf8')); }
|
|
68
|
+
catch { return finish(reject, new Error('machine fs read worker returned invalid JSON')); }
|
|
69
|
+
if (value?.schema !== 'deadbyte.machine-fs-read-worker.v1' || !Number.isInteger(value.bytes_read) ||
|
|
70
|
+
typeof value.data_base64 !== 'string' || (value.file_sha256 !== null && !SHA256_RE.test(value.file_sha256 ?? ''))) {
|
|
71
|
+
return finish(reject, new Error('machine fs read worker response invalid'));
|
|
72
|
+
}
|
|
73
|
+
finish(resolve, value);
|
|
74
|
+
});
|
|
75
|
+
const timer = setTimeout(() => {
|
|
76
|
+
timedOut = true;
|
|
77
|
+
child.kill();
|
|
78
|
+
}, timeoutMs);
|
|
79
|
+
timer.unref?.();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
32
83
|
function getRoot(policy, rootId, permission) {
|
|
33
84
|
const root = policy.roots[rootId];
|
|
34
85
|
if (!root) throw new Error(`machine fs root '${rootId}' is not allowed`);
|
|
@@ -245,7 +296,7 @@ async function atomicWrite(target, bytes, expectedSha256, authorityCheck = null)
|
|
|
245
296
|
return { before, after };
|
|
246
297
|
}
|
|
247
298
|
|
|
248
|
-
export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath }) {
|
|
299
|
+
export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath, readWorkerPath = machineFsReadWorkerPath }) {
|
|
249
300
|
if (!policy) throw new Error('machine fs runtime requires coding policy');
|
|
250
301
|
if (!signingKeyPath || !verifyKeyPath) throw new Error('machine fs runtime requires trust key paths');
|
|
251
302
|
|
|
@@ -292,25 +343,27 @@ export function createMachineFsRuntime({ policy, signingKeyPath, verifyKeyPath }
|
|
|
292
343
|
const nextCursor = filtered.length > selected.length && selected.length ? encodeCursor(selected.at(-1).name) : null;
|
|
293
344
|
return { status: 'ok', root_id: rootId, path: found.relative, entries, next_cursor: nextCursor };
|
|
294
345
|
}
|
|
295
|
-
async function readRange({ rootId, relativePath, offset = 0, maxBytes = 65536, hashFile = false }) {
|
|
346
|
+
async function readRange({ rootId, relativePath, offset = 0, maxBytes = 65536, hashFile = false, timeoutMs = 30000 }) {
|
|
296
347
|
if (!Number.isInteger(offset) || offset < 0) throw new Error('machine fs read offset must be nonnegative integer');
|
|
297
348
|
if (!Number.isInteger(maxBytes) || maxBytes < 1 || maxBytes > policy.max_read_bytes) throw new Error(`machine fs max_bytes must be in [1, ${policy.max_read_bytes}]`);
|
|
349
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 180000) throw new Error('machine fs read timeout_ms must be in [100, 180000]');
|
|
298
350
|
const grant = await assertCodingArmed(policy, rootId);
|
|
299
351
|
const found = await resolveExisting(policy, grant, rootId, relativePath, 'read', false);
|
|
300
352
|
const info = await lstat(found.resolved);
|
|
301
353
|
if (!info.isFile()) throw new Error('machine fs read target must be a regular file');
|
|
302
354
|
if (offset > info.size) throw new Error('machine fs read offset exceeds file size');
|
|
303
|
-
const handle = await open(found.resolved, 'r');
|
|
304
355
|
const length = Math.min(maxBytes, info.size - offset);
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
356
|
+
await recheckCodingAuthority(policy, grant, rootId);
|
|
357
|
+
const worker = await boundedReadWorker({ file: found.resolved, offset, length, hashFile, timeoutMs, workerPath: readWorkerPath });
|
|
358
|
+
const buffer = Buffer.from(worker.data_base64, 'base64');
|
|
359
|
+
if (buffer.length !== worker.bytes_read || buffer.length > length) throw new Error('machine fs read worker byte count mismatch');
|
|
360
|
+
await recheckCodingAuthority(policy, grant, rootId);
|
|
309
361
|
return {
|
|
310
362
|
status: 'ok', root_id: rootId, path: found.relative,
|
|
311
|
-
offset, next_offset: offset + length, file_size: info.size,
|
|
312
|
-
eof: offset + length >= info.size, data_base64: buffer.toString('base64'),
|
|
313
|
-
data_sha256: sha256Bytes(buffer), file_sha256:
|
|
363
|
+
offset, next_offset: offset + buffer.length, file_size: info.size,
|
|
364
|
+
eof: offset + buffer.length >= info.size, data_base64: buffer.toString('base64'),
|
|
365
|
+
data_sha256: sha256Bytes(buffer), file_sha256: worker.file_sha256,
|
|
366
|
+
timeout_ms: timeoutMs
|
|
314
367
|
};
|
|
315
368
|
}
|
|
316
369
|
|
package/src/mcp-server.mjs
CHANGED
|
@@ -14,8 +14,9 @@ import { defaultPrivateKeyPath, defaultPublicKeyPath, loadPrivateSigningKey, loa
|
|
|
14
14
|
import { DEADBYTE_VERSION } from './version.mjs';
|
|
15
15
|
import { currentIngressAdapterMode, TUNNEL_STDIO_PROBE_COMPAT_MODE } from './ingress-mode.mjs';
|
|
16
16
|
import { installTunnelStdioProbeCompat } from './tunnel-stdio-compat.mjs';
|
|
17
|
+
import { installStdioConsoleGuard } from './stdio-console-guard.mjs';
|
|
17
18
|
import { decodeUtf8Strict } from './text-policy.mjs';
|
|
18
|
-
import { assertHostArmed, hostExec, hostFileRead, hostFileWrite, loadHostPolicy } from './host-plane.mjs';
|
|
19
|
+
import { assertHostArmed, hostExec, hostExecInline, hostExecNative, hostFileRead, hostFileWrite, loadHostPolicy } from './host-plane.mjs';
|
|
19
20
|
import { createMachineRuntime } from './machine-identity.mjs';
|
|
20
21
|
import { registerMachineTools } from './machine-mcp-tools.mjs';
|
|
21
22
|
import { createAgentRuntime } from './agent-runtime.mjs';
|
|
@@ -75,8 +76,9 @@ if (!['0','1'].includes(auditRuntimeMode)) throw new Error('DEADBYTE_AUDIT_RUNTI
|
|
|
75
76
|
const runtimeStateRoot = path.resolve(process.env.DEADBYTE_STATE_ROOT || path.join(os.homedir(),'.deadbyte-mcp'));
|
|
76
77
|
const surfaceProfile = capabilityProfileFromEnv(process.env);
|
|
77
78
|
const localPrivilegedPolicyPath = path.join(packageRoot, 'controller', 'deadbyte-host-policy.json');
|
|
79
|
+
const privilegedPolicyExplicit = Object.hasOwn(process.env, 'DEADBYTE_PRIVILEGED_POLICY');
|
|
78
80
|
let privilegedPolicyPath = process.env.DEADBYTE_PRIVILEGED_POLICY ?? '';
|
|
79
|
-
if (!
|
|
81
|
+
if (!privilegedPolicyExplicit) {
|
|
80
82
|
try {
|
|
81
83
|
await access(localPrivilegedPolicyPath);
|
|
82
84
|
privilegedPolicyPath = localPrivilegedPolicyPath;
|
|
@@ -230,6 +232,9 @@ const skillRoot=path.join(runtimeStateRoot,'skills');
|
|
|
230
232
|
await mkdir(skillRoot,{recursive:true});
|
|
231
233
|
const skillRuntime=await createSkillPlane({root:skillRoot,verifyKeyPath});
|
|
232
234
|
|
|
235
|
+
installStdioConsoleGuard();
|
|
236
|
+
if (process.env.DEADBYTE_STDIO_CONSOLE_GUARD_PROBE === '1') console.log('DEADBYTE_STDIO_CONSOLE_GUARD_PROBE');
|
|
237
|
+
|
|
233
238
|
const ingressAdapterMode = currentIngressAdapterMode();
|
|
234
239
|
installTunnelStdioProbeCompat({
|
|
235
240
|
enabled: ingressAdapterMode === TUNNEL_STDIO_PROBE_COMPAT_MODE,
|
|
@@ -505,31 +510,71 @@ serveStdio(() => {
|
|
|
505
510
|
toolServer.registerTool(
|
|
506
511
|
'host_exec',
|
|
507
512
|
{
|
|
508
|
-
description: 'Privileged
|
|
509
|
-
inputSchema: z.
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
513
|
+
description: 'Privileged Host execution with three bounded modes: legacy hash-pinned script, policy-approved inline argv memory, or absolute hash-pinned native executable argv. shell=false; live Host revocation is enforced.',
|
|
514
|
+
inputSchema: z.union([
|
|
515
|
+
z.strictObject({
|
|
516
|
+
mode: z.literal('script').optional().default('script'),
|
|
517
|
+
command_id: z.string().min(1).max(64),
|
|
518
|
+
root_id: z.string().min(1).max(64),
|
|
519
|
+
script_path: z.string().min(1).max(512),
|
|
520
|
+
script_sha256: z.string().regex(/^[0-9a-f]{64}$/),
|
|
521
|
+
args: z.array(z.string().max(2048)).max(64).default([]),
|
|
522
|
+
cwd: z.string().min(1).max(512).default('.'),
|
|
523
|
+
timeout_ms: z.number().int().min(100).max(300000).nullable().default(null)
|
|
524
|
+
}),
|
|
525
|
+
z.strictObject({
|
|
526
|
+
mode: z.literal('inline'),
|
|
527
|
+
command_id: z.string().min(1).max(64),
|
|
528
|
+
root_id: z.string().min(1).max(64),
|
|
529
|
+
command: z.string().min(1).max(262144),
|
|
530
|
+
cwd: z.string().min(1).max(512).default('.'),
|
|
531
|
+
timeout_ms: z.number().int().min(100).max(300000).nullable().default(null)
|
|
532
|
+
}),
|
|
533
|
+
z.strictObject({
|
|
534
|
+
mode: z.literal('native'),
|
|
535
|
+
root_id: z.string().min(1).max(64),
|
|
536
|
+
executable: z.string().min(1).max(2048),
|
|
537
|
+
executable_sha256: z.string().regex(/^[0-9a-f]{64}$/),
|
|
538
|
+
args: z.array(z.string().max(32768)).max(64).default([]),
|
|
539
|
+
cwd: z.string().min(1).max(512).default('.'),
|
|
540
|
+
timeout_ms: z.number().int().min(100).max(300000).nullable().default(null)
|
|
541
|
+
})
|
|
542
|
+
]),
|
|
518
543
|
outputSchema: hostExecOutputSchema
|
|
519
544
|
},
|
|
520
|
-
async
|
|
545
|
+
async args => {
|
|
521
546
|
try {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
547
|
+
let result;
|
|
548
|
+
if (args.mode === 'inline') {
|
|
549
|
+
result = await hostExecInline(hostPolicy, {
|
|
550
|
+
commandId: args.command_id,
|
|
551
|
+
rootId: args.root_id,
|
|
552
|
+
command: args.command,
|
|
553
|
+
cwd: args.cwd,
|
|
554
|
+
timeoutMs: args.timeout_ms
|
|
555
|
+
});
|
|
556
|
+
} else if (args.mode === 'native') {
|
|
557
|
+
result = await hostExecNative(hostPolicy, {
|
|
558
|
+
rootId: args.root_id,
|
|
559
|
+
executable: args.executable,
|
|
560
|
+
executableSha256: args.executable_sha256,
|
|
561
|
+
args: args.args,
|
|
562
|
+
cwd: args.cwd,
|
|
563
|
+
timeoutMs: args.timeout_ms
|
|
564
|
+
});
|
|
565
|
+
} else {
|
|
566
|
+
result = await hostExec(hostPolicy, {
|
|
567
|
+
commandId: args.command_id,
|
|
568
|
+
rootId: args.root_id,
|
|
569
|
+
scriptPath: args.script_path,
|
|
570
|
+
scriptSha256: args.script_sha256,
|
|
571
|
+
args: args.args,
|
|
572
|
+
cwd: args.cwd,
|
|
573
|
+
timeoutMs: args.timeout_ms
|
|
574
|
+
});
|
|
575
|
+
}
|
|
531
576
|
const failed = result.timed_out || result.exit_code !== 0;
|
|
532
|
-
return textResult(hostExecOutputSchema, { status: failed ? 'failed' : 'passed', root_id, ...result }, failed);
|
|
577
|
+
return textResult(hostExecOutputSchema, { status: failed ? 'failed' : 'passed', root_id: args.root_id, ...result }, failed);
|
|
533
578
|
} catch (error) {
|
|
534
579
|
return textResult(hostExecOutputSchema, { status: 'denied', error: String(error) }, true);
|
|
535
580
|
}
|
package/src/mcp-smoke-client.mjs
CHANGED
|
@@ -38,7 +38,8 @@ const transport = new StdioClientTransport({
|
|
|
38
38
|
...getDefaultEnvironment(),
|
|
39
39
|
DEADBYTE_WORKSPACE_ROOT: workspaceRoot,
|
|
40
40
|
DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
|
|
41
|
-
DEADBYTE_VERIFY_KEY: keys.publicKeyPath
|
|
41
|
+
DEADBYTE_VERIFY_KEY: keys.publicKeyPath,
|
|
42
|
+
DEADBYTE_STDIO_CONSOLE_GUARD_PROBE: '1'
|
|
42
43
|
},
|
|
43
44
|
stderr: 'pipe'
|
|
44
45
|
});
|
|
@@ -64,6 +65,8 @@ try {
|
|
|
64
65
|
|
|
65
66
|
const listed = await client.listTools();
|
|
66
67
|
const names = listed.tools.map(tool => tool.name).sort();
|
|
68
|
+
const guardStderr = Buffer.concat(serverStderr).toString('utf8');
|
|
69
|
+
if (!guardStderr.includes('DEADBYTE MCP LOG DEADBYTE_STDIO_CONSOLE_GUARD_PROBE')) throw new Error('stdio console guard probe did not reach stderr');
|
|
67
70
|
const baseTools = ['contained_transform', 'evidence_verify', 'file_read', 'proof_execute', 'skill_catalog', 'workspace_open'];
|
|
68
71
|
const privilegedTools = ['host_exec', 'host_file_read', 'host_file_write'];
|
|
69
72
|
const allowedCatalogs = [
|
package/src/native-diff-v2.mjs
CHANGED
|
@@ -321,17 +321,22 @@ export function createNativeDiffV2Runtime({policy,codingRuntime,machineFsRuntime
|
|
|
321
321
|
const receipt=await createApplyReceipt(ctx,diffId,identity,preimage,events);return {status:'committed',diff_id:diffId,receipt_sha256:receipt.receipt_sha256,operation_count:identity.operations.length,operation_receipts:receipt.operation_receipts.map(x=>({index:x.index,kind:x.kind,receipt_class:x.receipt_class,receipt_id:x.receipt_id,receipt_sha256:x.receipt_sha256}))};
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
-
async function
|
|
324
|
+
async function verifyEvidence({diffId}){
|
|
325
325
|
try{
|
|
326
326
|
const {ctx,identity}=await loadDiff(diffId);const verifyKey=await loadPublicVerifyKey(verifyKeyPath);const preimage=await readEnvelope(ctx.preimagePath,verifyKey,PREIMAGE_DOMAIN,'native diff V2 preimage');const events=await readEvents(ctx);const receipt=await readEnvelope(ctx.receiptPath,verifyKey,RECEIPT_DOMAIN,'native diff V2 receipt');const {attestation:_a,receipt_sha256:claimed,...body}=receipt;
|
|
327
327
|
if(body.schema!==NATIVE_DIFF_V2_RECEIPT_SCHEMA||body.diff_id!==diffId||body.policy_sha256!==policy.policy_sha256||body.coding_policy_sha256!==policy.coding_policy_sha256||!SHA256_RE.test(claimed??'')||sha256Object(body)!==claimed)return {ok:false,reason:'native diff V2 receipt identity/hash mismatch'};
|
|
328
328
|
const completion=events.at(-1);if(completion?.type!=='transaction_completed'||completion.prev_sha256!==receipt.event_head_sha256||completion.payload?.receipt_sha256!==claimed||receipt.preimage_sha256!==preimage.preimage_sha256)return {ok:false,reason:'native diff V2 receipt/event/preimage binding mismatch'};
|
|
329
329
|
if(!Array.isArray(receipt.operation_receipts)||receipt.operation_receipts.length!==identity.operations.length)return {ok:false,reason:'native diff V2 operation receipt count mismatch'};
|
|
330
330
|
let verified=0,codingVerified=0;for(let index=0;index<identity.operations.length;index+=1){const op=identity.operations[index];const bound=receipt.operation_receipts.find(x=>x.index===index);if(!bound||bound.kind!==op.kind||bound.receipt_class!==receiptClass(op))return {ok:false,reason:`native diff V2 receipt binding mismatch index ${index}`};const checked=await verifyOperationReceipt(op,bound.receipt_id);if(!checked.ok||checked.receipt_sha256!==bound.receipt_sha256||checked.request_sha256!==bound.request_sha256)return {ok:false,reason:`native diff V2 operation receipt invalid index ${index}`};verified+=1;if(bound.receipt_class==='coding')codingVerified+=1;}
|
|
331
|
-
|
|
332
|
-
return {ok:true,diff_id:diffId,receipt_sha256:claimed,signature_valid:true,root_id:identity.root_id,operation_count:identity.operations.length,coding_receipts_verified:codingVerified,operation_receipts_verified:verified,final_states:receipt.final_states,current_state_matches:currentMatches,journal_head_sha256:completion.event_sha256};
|
|
331
|
+
return {ok:true,diff_id:diffId,receipt_sha256:claimed,signature_valid:true,root_id:identity.root_id,operation_count:identity.operations.length,coding_receipts_verified:codingVerified,operation_receipts_verified:verified,final_states:receipt.final_states,journal_head_sha256:completion.event_sha256};
|
|
333
332
|
}catch(error){return {ok:false,reason:String(error).slice(0,2048)};}
|
|
334
333
|
}
|
|
335
334
|
|
|
336
|
-
|
|
335
|
+
async function verify({diffId}){
|
|
336
|
+
const checked=await verifyEvidence({diffId});if(!checked.ok)return checked;
|
|
337
|
+
try{const {identity}=await loadDiff(diffId);let currentMatches=true;for(const op of identity.operations)if(!(await terminalStateMatches(identity.root_id,op)))currentMatches=false;return {...checked,current_state_matches:currentMatches};}
|
|
338
|
+
catch(error){return {ok:false,reason:String(error).slice(0,2048)};}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return Object.freeze({preview,apply,verify,verifyEvidence,policy});
|
|
337
342
|
}
|
package/src/native-diff.mjs
CHANGED
|
@@ -361,7 +361,7 @@ function createNativeDiffRuntimeV1({ policy, codingRuntime, signingKeyPath, veri
|
|
|
361
361
|
coding_receipts:receipt.coding_receipts.map(item => ({ index:item.index, path:item.path, receipt_id:item.receipt_id, receipt_sha256:item.receipt_sha256 })) };
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
-
async function
|
|
364
|
+
async function verifyEvidence({ diffId }) {
|
|
365
365
|
try {
|
|
366
366
|
const { ctx, identity } = await loadDiff(diffId);
|
|
367
367
|
const verifyKey = await loadPublicVerifyKey(verifyKeyPath);
|
|
@@ -390,21 +390,32 @@ function createNativeDiffRuntimeV1({ policy, codingRuntime, signingKeyPath, veri
|
|
|
390
390
|
}
|
|
391
391
|
verifiedReceipts += 1;
|
|
392
392
|
}
|
|
393
|
+
return { ok:true, diff_id:diffId, receipt_sha256:claimed, signature_valid:true,
|
|
394
|
+
root_id:identity.root_id, operation_count:identity.operations.length,
|
|
395
|
+
coding_receipts_verified:verifiedReceipts, final_states:receipt.final_states,
|
|
396
|
+
journal_head_sha256:completion.event_sha256 };
|
|
397
|
+
} catch (error) {
|
|
398
|
+
return { ok:false, reason:String(error).slice(0,2048) };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async function verify({ diffId }) {
|
|
403
|
+
const checked = await verifyEvidence({ diffId });
|
|
404
|
+
if (!checked.ok) return checked;
|
|
405
|
+
try {
|
|
406
|
+
const { identity } = await loadDiff(diffId);
|
|
393
407
|
let currentMatches = true;
|
|
394
408
|
for (const op of identity.operations) {
|
|
395
409
|
const current = await codingRuntime.fileState({ rootId:identity.root_id, relativePath:op.path });
|
|
396
410
|
if (!stateMatches(current, op.after_sha256)) currentMatches = false;
|
|
397
411
|
}
|
|
398
|
-
return {
|
|
399
|
-
root_id:identity.root_id, operation_count:identity.operations.length,
|
|
400
|
-
coding_receipts_verified:verifiedReceipts, final_states:receipt.final_states,
|
|
401
|
-
current_state_matches:currentMatches, journal_head_sha256:completion.event_sha256 };
|
|
412
|
+
return { ...checked, current_state_matches:currentMatches };
|
|
402
413
|
} catch (error) {
|
|
403
414
|
return { ok:false, reason:String(error).slice(0,2048) };
|
|
404
415
|
}
|
|
405
416
|
}
|
|
406
417
|
|
|
407
|
-
return Object.freeze({ preview, apply, verify, policy });
|
|
418
|
+
return Object.freeze({ preview, apply, verify, verifyEvidence, policy });
|
|
408
419
|
}
|
|
409
420
|
|
|
410
421
|
export { NATIVE_DIFF_V2_SCHEMA };
|
|
@@ -448,5 +459,16 @@ export function createNativeDiffRuntime(options) {
|
|
|
448
459
|
return {ok:false,reason:String(error).slice(0,2048)};
|
|
449
460
|
}
|
|
450
461
|
}
|
|
451
|
-
|
|
462
|
+
async function verifyEvidence(args){
|
|
463
|
+
try {
|
|
464
|
+
const schema=await schemaFor(args?.diffId);
|
|
465
|
+
if(schema===NATIVE_DIFF_SCHEMA) return v1.verifyEvidence(args);
|
|
466
|
+
if(schema===NATIVE_DIFF_V2_SCHEMA){
|
|
467
|
+
if(!v2) return {ok:false,reason:'native diff V2 topology requires machine fs runtime'};
|
|
468
|
+
return v2.verifyEvidence(args);
|
|
469
|
+
}
|
|
470
|
+
return {ok:false,reason:`unsupported native diff schema: ${String(schema)}`};
|
|
471
|
+
}catch(error){return {ok:false,reason:String(error).slice(0,2048)};}
|
|
472
|
+
}
|
|
473
|
+
return Object.freeze({preview,apply,verify,verifyEvidence,policy:options.policy});
|
|
452
474
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as z from 'zod/v4';
|
|
2
2
|
import {
|
|
3
|
-
observationProcessListOutputSchema,observationSearchCancelOutputSchema,observationSearchPageOutputSchema,
|
|
3
|
+
observationHardwareQueryOutputSchema,observationProcessListOutputSchema,observationSearchCancelOutputSchema,observationSearchPageOutputSchema,
|
|
4
4
|
observationSearchStartOutputSchema,observationSearchStatusOutputSchema,observationStructuredResult,observationSystemStatusOutputSchema
|
|
5
5
|
} from './observation-output-contracts.mjs';
|
|
6
6
|
|
|
@@ -41,4 +41,8 @@ export function registerObservationTools(server,{runtime}){
|
|
|
41
41
|
description:'Read bounded timestamped system status with signed observation evidence. This tool cannot mutate or arm any authority.',
|
|
42
42
|
inputSchema:z.strictObject({}),outputSchema:observationSystemStatusOutputSchema
|
|
43
43
|
},async()=>{try{return ok(observationSystemStatusOutputSchema,await runtime.systemStatus());}catch(error){return fail(observationSystemStatusOutputSchema,error);}});
|
|
44
|
+
server.registerTool('machine_hardware_query',{
|
|
45
|
+
description:'Read one typed native hardware snapshot (CPU, GPU, memory, storage, network, or PCI) with signed observation evidence. No shell or Host authority is used.',
|
|
46
|
+
inputSchema:z.strictObject({query:z.enum(['cpu','gpu','memory','storage','network','pci'])}),outputSchema:observationHardwareQueryOutputSchema
|
|
47
|
+
},async args=>{try{return ok(observationHardwareQueryOutputSchema,await runtime.hardwareQuery({query:args.query}));}catch(error){return fail(observationHardwareQueryOutputSchema,error);}});
|
|
44
48
|
}
|
|
@@ -45,6 +45,14 @@ export const observationSystemStatusOutputSchema=statusContract({
|
|
|
45
45
|
...Object.fromEntries(Object.entries(evidenceShape).map(([k,v])=>[k,v.optional()]))
|
|
46
46
|
},['observed_at_utc','hostname','platform','architecture','uptime_seconds','total_memory_bytes','free_memory_bytes','cpu_count','load_average','request_sha256','result_head_sha256','receipt_id','receipt_sha256']);
|
|
47
47
|
|
|
48
|
+
export const observationHardwareQueryOutputSchema=statusContract({
|
|
49
|
+
observed_at_utc:z.string().min(1).optional(),
|
|
50
|
+
query:z.enum(['cpu','gpu','memory','storage','network','pci']).optional(),
|
|
51
|
+
data:z.record(z.string(),z.json()).optional(),
|
|
52
|
+
observer_sha256:hex64.optional(),
|
|
53
|
+
...Object.fromEntries(Object.entries(evidenceShape).map(([k,v])=>[k,v.optional()]))
|
|
54
|
+
},['observed_at_utc','query','data','request_sha256','result_head_sha256','receipt_id','receipt_sha256']);
|
|
55
|
+
|
|
48
56
|
export function observationStructuredResult(schema,value,isError=false){
|
|
49
57
|
return structuredToolResult(schema,value,isError);
|
|
50
58
|
}
|
|
@@ -3,6 +3,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
3
3
|
import { lstat, mkdir, open, readFile, readdir, realpath } from 'node:fs/promises';
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
6
7
|
import { canonicalJson } from './canonical-json.mjs';
|
|
7
8
|
import { decodeUtf8Strict } from './text-policy.mjs';
|
|
8
9
|
import { isInside, validatePortableRelativePath } from './path-policy.mjs';
|
|
@@ -13,6 +14,7 @@ const RECEIPT_DOMAIN=Buffer.from('DEADBYTE-OBSERVATION-RECEIPT-V1\0','utf8');
|
|
|
13
14
|
const ZERO='0'.repeat(64);
|
|
14
15
|
const HEX64=/^[0-9a-f]{64}$/;
|
|
15
16
|
const ID=/^[A-Za-z0-9._-]{1,64}$/;
|
|
17
|
+
const HARDWARE_QUERIES=new Set(['cpu','gpu','memory','storage','network','pci']);
|
|
16
18
|
const sha256Bytes=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
17
19
|
const sha256Object=value=>sha256Bytes(Buffer.from(canonicalJson(value),'utf8'));
|
|
18
20
|
const nowUtc=()=>new Date().toISOString();
|
|
@@ -110,19 +112,53 @@ function defaultSystemSnapshotProvider(){
|
|
|
110
112
|
return {hostname:os.hostname(),platform:process.platform,architecture:process.arch,uptime_seconds:Math.floor(os.uptime()),
|
|
111
113
|
total_memory_bytes:os.totalmem(),free_memory_bytes:os.freemem(),cpu_count:os.cpus().length,load_average:os.loadavg().map(Number)};
|
|
112
114
|
}
|
|
115
|
+
async function defaultHardwareSnapshotProvider(query){
|
|
116
|
+
if(process.platform!=='win32') throw new Error('native hardware observation is supported on win32 only');
|
|
117
|
+
if(!HARDWARE_QUERIES.has(query)) throw new Error('unsupported hardware query');
|
|
118
|
+
const executable=fileURLToPath(new URL('../bin/deadbyte-hardware-observer-r45.exe',import.meta.url));
|
|
119
|
+
const executableSha256=sha256Bytes(await readFile(executable));
|
|
120
|
+
const result=spawnSync(executable,[query],{encoding:'utf8',windowsHide:true,shell:false,timeout:15000,maxBuffer:16*1024*1024});
|
|
121
|
+
if(result.error) throw result.error;
|
|
122
|
+
if(result.status!==0) throw new Error(`hardware observation failed query=${query}: ${String(result.stderr??result.stdout??'').trim()}`);
|
|
123
|
+
let parsed;
|
|
124
|
+
try{parsed=JSON.parse(String(result.stdout??'').trim());}
|
|
125
|
+
catch{throw new Error(`hardware observation returned invalid JSON query=${query}`);}
|
|
126
|
+
if(parsed?.schema!=='deadbyte.hardware-query.v1'||parsed?.query!==query||!parsed.data||typeof parsed.data!=='object'||Array.isArray(parsed.data)){
|
|
127
|
+
throw new Error(`hardware observation contract mismatch query=${query}`);
|
|
128
|
+
}
|
|
129
|
+
return {...parsed,observer_sha256:executableSha256};
|
|
130
|
+
}
|
|
113
131
|
function requestSha(policy,operation,args){
|
|
114
132
|
return sha256Object({schema:'deadbyte.observation-request.v1',policy_sha256:policy.policy_sha256,operation,args});
|
|
115
133
|
}
|
|
116
134
|
export function createObservationRuntime({policy,signingKeyPath,verifyKeyPath,processRuntime=null,stateRoot=path.join(os.homedir(),'.deadbyte-mcp'),
|
|
117
135
|
processSnapshotProvider=defaultProcessSnapshotProvider,systemSnapshotProvider=defaultSystemSnapshotProvider,
|
|
118
|
-
ownedRuntimePidProvider=defaultOwnedRuntimePidProvider
|
|
136
|
+
ownedRuntimePidProvider=defaultOwnedRuntimePidProvider,hardwareSnapshotProvider=defaultHardwareSnapshotProvider,
|
|
137
|
+
searchSessionTtlMs=5*60*1000,maxSearchSessions=128,clockMs=()=>Date.now()}={}){
|
|
119
138
|
if(!policy||!signingKeyPath||!verifyKeyPath) throw new Error('observation runtime requires policy and trust keys');
|
|
139
|
+
if(!Number.isInteger(searchSessionTtlMs)||searchSessionTtlMs<10||searchSessionTtlMs>24*60*60*1000) throw new Error('searchSessionTtlMs out of range');
|
|
140
|
+
if(!Number.isInteger(maxSearchSessions)||maxSearchSessions<1||maxSearchSessions>4096) throw new Error('maxSearchSessions out of range');
|
|
141
|
+
if(typeof clockMs!=='function') throw new Error('clockMs must be a function');
|
|
120
142
|
const searches=new Map(),snapshots=new Map();
|
|
121
143
|
let signingPromise=null;
|
|
122
144
|
const runtime={policy,signing:()=>signingPromise??=(loadPrivateSigningKey(signingKeyPath))};
|
|
145
|
+
const terminalSearchState=state=>['completed','cancelled','failed'].includes(state);
|
|
146
|
+
function pruneSearches(){
|
|
147
|
+
const now=Number(clockMs());
|
|
148
|
+
if(!Number.isFinite(now)) throw new Error('search clock invalid');
|
|
149
|
+
for(const [id,session] of searches){
|
|
150
|
+
if(terminalSearchState(session.state)&&Number.isFinite(session.completed_at_ms)&&now-session.completed_at_ms>=searchSessionTtlMs) searches.delete(id);
|
|
151
|
+
}
|
|
152
|
+
if(searches.size>maxSearchSessions){
|
|
153
|
+
const terminal=[...searches.values()].filter(s=>terminalSearchState(s.state))
|
|
154
|
+
.sort((a,b)=>(a.completed_at_ms??Infinity)-(b.completed_at_ms??Infinity)||a.search_id.localeCompare(b.search_id));
|
|
155
|
+
for(const session of terminal){ if(searches.size<=maxSearchSessions) break; searches.delete(session.search_id); }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
123
158
|
function searchFor(searchId){
|
|
124
159
|
if(!HEX64.test(searchId??'')) throw new Error('invalid search id');
|
|
125
|
-
|
|
160
|
+
pruneSearches();
|
|
161
|
+
const session=searches.get(searchId); if(!session) throw new Error(`unknown or expired search '${searchId}'`); return session;
|
|
126
162
|
}
|
|
127
163
|
async function finalizeSearch(session,state,error=null){
|
|
128
164
|
session.error=error?String(error):null;
|
|
@@ -131,7 +167,8 @@ export function createObservationRuntime({policy,signingKeyPath,verifyKeyPath,pr
|
|
|
131
167
|
bytes_scanned:session.bytes_scanned,truncated:session.truncated,error:session.error
|
|
132
168
|
});
|
|
133
169
|
session.receipt_id=receipt.receipt_id; session.receipt_sha256=receipt.receipt_sha256;
|
|
134
|
-
session.state=state;
|
|
170
|
+
session.state=state; session.completed_at_ms=Number(clockMs());
|
|
171
|
+
pruneSearches();
|
|
135
172
|
}
|
|
136
173
|
async function executeSearch(session){
|
|
137
174
|
if(session.cancel_requested){ await finalizeSearch(session,'cancelled'); return; }
|
|
@@ -182,11 +219,13 @@ export function createObservationRuntime({policy,signingKeyPath,verifyKeyPath,pr
|
|
|
182
219
|
if(!Number.isInteger(maxBytes)||maxBytes<1||maxBytes>64*1024*1024) throw new Error('max_bytes out of range');
|
|
183
220
|
if(!Number.isInteger(maxFileBytes)||maxFileBytes<1||maxFileBytes>4*1024*1024) throw new Error('max_file_bytes out of range');
|
|
184
221
|
rootFor(policy,rootId); if(relativePath!=='.') validatePortableRelativePath(relativePath);
|
|
222
|
+
pruneSearches();
|
|
223
|
+
if(searches.size>=maxSearchSessions) throw new Error('observation search session limit reached');
|
|
185
224
|
const args={root_id:rootId,path:relativePath,pattern,case_sensitive:caseSensitive,max_results:maxResults,max_files:maxFiles,max_bytes:maxBytes,max_file_bytes:maxFileBytes};
|
|
186
225
|
const session={search_id:randomBytes(32).toString('hex'),root_id:rootId,path:relativePath,pattern,case_sensitive:caseSensitive,
|
|
187
226
|
max_results:maxResults,max_files:maxFiles,max_bytes:maxBytes,max_file_bytes:maxFileBytes,state:'pending',cancel_requested:false,
|
|
188
227
|
results:[],files_scanned:0,bytes_scanned:0,truncated:false,result_head_sha256:ZERO,error:null,receipt_id:null,receipt_sha256:null,
|
|
189
|
-
request_sha256:requestSha(policy,'machine_search_start',args)};
|
|
228
|
+
created_at_ms:Number(clockMs()),completed_at_ms:null,request_sha256:requestSha(policy,'machine_search_start',args)};
|
|
190
229
|
searches.set(session.search_id,session); setImmediate(()=>{ void executeSearch(session); });
|
|
191
230
|
return {status:'accepted',search_id:session.search_id,state:session.state,request_sha256:session.request_sha256};
|
|
192
231
|
}
|
|
@@ -253,5 +292,18 @@ export function createObservationRuntime({policy,signingKeyPath,verifyKeyPath,pr
|
|
|
253
292
|
const receipt=await writeReceipt(runtime,'machine_system_status',req,head,{observed_at_utc:observedAt});
|
|
254
293
|
return {...result,request_sha256:req,result_head_sha256:head,...receipt};
|
|
255
294
|
}
|
|
256
|
-
|
|
295
|
+
async function hardwareQuery({query}={}){
|
|
296
|
+
if(typeof query!=='string'||!HARDWARE_QUERIES.has(query)) throw new Error('hardware query must be one of cpu,gpu,memory,storage,network,pci');
|
|
297
|
+
const observedAt=nowUtc();
|
|
298
|
+
const snapshot=await hardwareSnapshotProvider(query);
|
|
299
|
+
if(!snapshot||snapshot.schema!=='deadbyte.hardware-query.v1'||snapshot.query!==query||!snapshot.data||typeof snapshot.data!=='object'||Array.isArray(snapshot.data)){
|
|
300
|
+
throw new Error('hardware query provider contract mismatch');
|
|
301
|
+
}
|
|
302
|
+
const result={status:'ok',observed_at_utc:observedAt,query,data:snapshot.data};
|
|
303
|
+
if(typeof snapshot.observer_sha256==='string'&&HEX64.test(snapshot.observer_sha256)) result.observer_sha256=snapshot.observer_sha256;
|
|
304
|
+
const req=requestSha(policy,'machine_hardware_query',{query}); const head=sha256Object(result);
|
|
305
|
+
const receipt=await writeReceipt(runtime,'machine_hardware_query',req,head,{observed_at_utc:observedAt,query,observer_sha256:result.observer_sha256??null});
|
|
306
|
+
return {...result,request_sha256:req,result_head_sha256:head,...receipt};
|
|
307
|
+
}
|
|
308
|
+
return Object.freeze({policy,searchStart,searchPage,searchStatus,searchCancel,processList,systemStatus,hardwareQuery});
|
|
257
309
|
}
|
|
@@ -57,7 +57,7 @@ const coding=['coding_benchmark_run','coding_command_run','coding_file_read','co
|
|
|
57
57
|
'coding_session_status','coding_stat','coding_symbol_definition','coding_symbol_diagnostics','coding_symbol_implementations','coding_symbol_outline','coding_symbol_references','coding_symbol_rename_preview','coding_symbol_replace',
|
|
58
58
|
'coding_task_approve','coding_task_cancel','coding_task_events','coding_task_run','coding_task_status',
|
|
59
59
|
'coding_task_submit','coding_task_verify','coding_text_replace','coding_tree'];
|
|
60
|
-
const observation=['machine_process_list','machine_search_cancel','machine_search_page','machine_search_start','machine_search_status','machine_system_status'];
|
|
60
|
+
const observation=['machine_hardware_query','machine_process_list','machine_search_cancel','machine_search_page','machine_search_start','machine_search_status','machine_system_status'];
|
|
61
61
|
const expected=[...base,...machine,...coding,...observation].sort();
|
|
62
62
|
try{
|
|
63
63
|
await client.connect(transport);
|
|
@@ -70,6 +70,10 @@ try{
|
|
|
70
70
|
if(system.status!=='ok'||!/^[0-9a-f]{24}$/.test(system.receipt_id??'')) throw new Error('system observation missing signed receipt');
|
|
71
71
|
const systemVerify=await verifyObservationReceiptFile({policy,verifyKeyPath:keys.publicKeyPath,receiptId:system.receipt_id});
|
|
72
72
|
if(!systemVerify.ok||systemVerify.signature_valid!==true||systemVerify.request_sha256!==system.request_sha256) throw new Error('system observation receipt verification failed');
|
|
73
|
+
const hardware=structured(await client.callTool({name:'machine_hardware_query',arguments:{query:'cpu'}}),'machine_hardware_query');
|
|
74
|
+
if(hardware.status!=='ok'||hardware.query!=='cpu'||!/^[0-9a-f]{24}$/.test(hardware.receipt_id??'')||!/^[0-9a-f]{64}$/.test(hardware.observer_sha256??'')) throw new Error('hardware observation missing signed native evidence');
|
|
75
|
+
const hardwareVerify=await verifyObservationReceiptFile({policy,verifyKeyPath:keys.publicKeyPath,receiptId:hardware.receipt_id});
|
|
76
|
+
if(!hardwareVerify.ok||hardwareVerify.signature_valid!==true||hardwareVerify.request_sha256!==hardware.request_sha256) throw new Error('hardware observation receipt verification failed');
|
|
73
77
|
const processes=structured(await client.callTool({name:'machine_process_list',arguments:{limit:16,include_external:false,cursor:null}}),'machine_process_list');
|
|
74
78
|
if(processes.status!=='ok'||!/^[0-9a-f]{24}$/.test(processes.receipt_id??'')) throw new Error('process observation missing signed receipt');
|
|
75
79
|
const started=structured(await client.callTool({name:'machine_search_start',arguments:{root_id:'core',path:'.',pattern:'needle',case_sensitive:true,
|
|
@@ -87,7 +91,7 @@ try{
|
|
|
87
91
|
if(!searchVerify.ok||searchVerify.signature_valid!==true||searchVerify.result_head_sha256!==status.result_head_sha256) throw new Error('search receipt verification failed');
|
|
88
92
|
console.log(JSON.stringify({status:'passed',protocol_revision:'2026-07-28',tool_count:names.length,
|
|
89
93
|
coding_armed:false,search_results:page.results.length,process_snapshot_id:processes.snapshot_id,
|
|
90
|
-
system_receipt_id:system.receipt_id,search_receipt_id:status.receipt_id,strict_output_schemas:true,
|
|
94
|
+
system_receipt_id:system.receipt_id,hardware_receipt_id:hardware.receipt_id,hardware_observer_sha256:hardware.observer_sha256,search_receipt_id:status.receipt_id,strict_output_schemas:true,
|
|
91
95
|
signature_valid:true,policy_sha256:policy.policy_sha256},null,2));
|
|
92
96
|
}catch(error){
|
|
93
97
|
const detail=Buffer.concat(stderr).toString('utf8').trim(); if(detail) console.error(`server stderr:\n${detail}`);
|
package/src/output-contracts.mjs
CHANGED
|
@@ -226,17 +226,33 @@ export const hostFileWriteOutputSchema = statusContract({
|
|
|
226
226
|
successStatuses: ['committed'], successRequired: ['root_id', 'path', 'previous_sha256', 'sha256', 'bytes', 'created'], errorStatuses: ['denied']
|
|
227
227
|
});
|
|
228
228
|
|
|
229
|
-
export const hostExecOutputSchema =
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
229
|
+
export const hostExecOutputSchema = z.strictObject({
|
|
230
|
+
status: z.enum(['passed', 'failed', 'denied']),
|
|
231
|
+
error: z.string().optional(),
|
|
232
|
+
root_id: z.string().min(1).max(64).optional(),
|
|
233
|
+
mode: z.enum(['script','inline','native']).optional(),
|
|
234
|
+
command_id: z.string().min(1).max(64).optional(),
|
|
235
|
+
executable_sha256: hex64.optional(),
|
|
236
|
+
script_sha256: hex64.optional(),
|
|
237
|
+
command_sha256: hex64.optional(),
|
|
238
|
+
argv_sha256: hex64.optional(),
|
|
239
|
+
stdout_sha256: hex64.optional(),
|
|
240
|
+
stderr_sha256: hex64.optional(),
|
|
241
|
+
exit_code: z.number().int().nullable().optional(),
|
|
242
|
+
signal: z.string().nullable().optional(),
|
|
243
|
+
timed_out: z.boolean().optional(),
|
|
244
|
+
timeout_ms: z.number().int().min(100).optional(),
|
|
245
|
+
stdout: z.string().optional(),
|
|
246
|
+
stderr: z.string().optional()
|
|
247
|
+
}).superRefine((value,ctx)=>{
|
|
248
|
+
if(value.status==='denied'){
|
|
249
|
+
if(!value.error) ctx.addIssue({code:'custom',path:['error'],message:'error required'});
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if(value.error!==undefined) ctx.addIssue({code:'custom',path:['error'],message:'error invalid for executed result'});
|
|
253
|
+
requireFields(value,ctx,['root_id','mode','command_id','executable_sha256','argv_sha256','stdout_sha256','stderr_sha256','exit_code','signal','timed_out','timeout_ms','stdout','stderr']);
|
|
254
|
+
if(value.mode==='script'&&value.script_sha256===undefined) ctx.addIssue({code:'custom',path:['script_sha256'],message:'script_sha256 required for script mode'});
|
|
255
|
+
if(value.mode==='inline'&&value.command_sha256===undefined) ctx.addIssue({code:'custom',path:['command_sha256'],message:'command_sha256 required for inline mode'});
|
|
240
256
|
});
|
|
241
257
|
|
|
242
258
|
export function structuredToolResult(schema, object, isError = false) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import * as z from 'zod/v4';
|
|
2
2
|
import {
|
|
3
3
|
processActionOutputSchema,
|
|
4
4
|
processListOutputSchema,
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
processStartOutputSchema,
|
|
7
7
|
processStatusOutputSchema,
|
|
8
8
|
processTerminateOutputSchema,
|
|
9
|
+
processWriteOutputSchema,
|
|
9
10
|
processStructuredResult
|
|
10
11
|
} from './process-output-contracts.mjs';
|
|
11
12
|
|
|
@@ -30,14 +31,26 @@ export function registerProcessTools(server, { runtime }) {
|
|
|
30
31
|
server.registerTool('process_session_read', {
|
|
31
32
|
description: 'Read a bounded byte range from one DEADBYTE-owned process session using ordered stream offsets.',
|
|
32
33
|
inputSchema: z.strictObject({
|
|
33
|
-
session_id: hex64, offset: z.number().int().
|
|
34
|
-
max_bytes: z.number().int().min(1).max(262144).default(65536)
|
|
34
|
+
session_id: hex64, offset: z.number().int().min(-(16 * 1024 * 1024)).default(0),
|
|
35
|
+
max_bytes: z.number().int().min(1).max(262144).default(65536),
|
|
36
|
+
wait_ms: z.number().int().min(0).max(30000).default(0),
|
|
37
|
+
wait_for: z.string().min(1).max(256).nullable().default(null)
|
|
35
38
|
}), outputSchema: processReadOutputSchema
|
|
36
|
-
}, async ({ session_id, offset, max_bytes }) => {
|
|
37
|
-
try { return ok(processReadOutputSchema, { status: 'ok', ...(await runtime.read({ sessionId: session_id, offset, maxBytes: max_bytes })) }); }
|
|
39
|
+
}, async ({ session_id, offset, max_bytes, wait_ms, wait_for }) => {
|
|
40
|
+
try { return ok(processReadOutputSchema, { status: 'ok', ...(await runtime.read({ sessionId: session_id, offset, maxBytes: max_bytes, waitMs: wait_ms, waitFor: wait_for })) }); }
|
|
38
41
|
catch (error) { return fail(processReadOutputSchema, 'denied', error); }
|
|
39
42
|
});
|
|
40
43
|
|
|
44
|
+
server.registerTool('process_session_write', {
|
|
45
|
+
description: 'Write one bounded UTF-8 input payload to a running DEADBYTE-owned pipe session only when its local profile explicitly enables raw input. Input text is never stored in the signed journal; only byte count and SHA-256 identity are recorded.',
|
|
46
|
+
inputSchema: z.strictObject({
|
|
47
|
+
session_id: hex64, input: z.string().min(1).max(1024 * 1024), append_newline: z.boolean().default(true)
|
|
48
|
+
}), outputSchema: processWriteOutputSchema
|
|
49
|
+
}, async ({ session_id, input, append_newline }) => {
|
|
50
|
+
try { return ok(processWriteOutputSchema, { status: 'ok', ...(await runtime.write({ sessionId: session_id, input, appendNewline: append_newline })) }); }
|
|
51
|
+
catch (error) { return fail(processWriteOutputSchema, 'denied', error); }
|
|
52
|
+
});
|
|
53
|
+
|
|
41
54
|
server.registerTool('process_session_status', {
|
|
42
55
|
description: 'Read the verified state of one DEADBYTE-owned process session by session identity.',
|
|
43
56
|
inputSchema: z.strictObject({ session_id: hex64 }), outputSchema: processStatusOutputSchema
|