deadbyte-mcp 0.12.0 → 0.14.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/.gitattributes +7 -0
- package/AGENTS.md +25 -0
- package/CONTEXT.md +3 -3
- package/MANIFEST.SHA256 +89 -45
- package/R41-PARENT.json +15 -0
- package/R42-PARENT.json +19 -0
- package/README.txt +2 -2
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/README.TXT +1 -1
- package/controller/deadbyte-controller.ps1 +1 -1
- package/controller/deadbyte-desktop-policy.json +5 -5
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/superpowers/specs/2026-09-20-r41-deterministic-memory-design.md +132 -0
- package/harness/r42-agent-evals.json +48 -0
- package/harness/task-contract.schema.json +33 -0
- package/package.json +11 -7
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/agent-eval-r42.mjs +46 -0
- package/scripts/build-containment.ps1 +24 -14
- package/scripts/build-desktop-r40.ps1 +15 -9
- package/scripts/deferred-slot-operation-r41.mjs +127 -0
- package/scripts/deferred-slot-operation-r42.mjs +126 -0
- package/scripts/final-closure-r41.mjs +116 -0
- package/scripts/final-closure-r42.mjs +116 -0
- package/scripts/final-closure-verify-r41.mjs +215 -0
- package/scripts/final-closure-verify-r42.mjs +223 -0
- package/scripts/gate-windows-r41.ps1 +48 -0
- package/scripts/gate-windows-r42.ps1 +54 -0
- package/scripts/init-desktop-policy-r40.ps1 +10 -4
- package/scripts/release-parity-r41.mjs +221 -0
- package/scripts/release-parity-r42.mjs +235 -0
- package/scripts/release-parity-tests-r41.ps1 +9 -0
- package/scripts/release-parity-tests-r42.ps1 +6 -0
- package/scripts/verify-memory-r41.mjs +88 -0
- package/scripts/verify-r41-parent.mjs +61 -0
- package/scripts/verify-r42-parent.mjs +101 -0
- package/scripts/windows-gate-evidence-r41.mjs +53 -0
- package/scripts/windows-gate-evidence-r42.mjs +64 -0
- package/src/agent-eval-r42.mjs +73 -0
- package/src/agent-eval-runner-r42.mjs +138 -0
- package/src/agent-governance-r42.mjs +142 -0
- package/src/autonomous-context-engine.mjs +4 -1
- package/src/autonomous-loop-state.mjs +10 -0
- package/src/autonomous-mcp-tools.mjs +21 -6
- package/src/autonomous-output-contracts.mjs +18 -2
- package/src/autonomous-planner-contracts.mjs +3 -0
- package/src/autonomous-planner.mjs +48 -6
- package/src/autonomous-policy.mjs +10 -2
- package/src/autonomous-runtime.mjs +144 -25
- package/src/autonomous-supervisor.mjs +1 -1
- package/src/evidence-publisher-r42.mjs +76 -0
- package/src/mcp-server.mjs +15 -2
- package/src/memory-r41.mjs +692 -0
- package/src/provider-circuit.mjs +4 -4
- package/src/version.mjs +1 -1
- package/test/agent-eval-r42.test.mjs +39 -0
- package/test/agent-eval-runner-r42.test.mjs +61 -0
- package/test/agent-governance-r42.test.mjs +77 -0
- package/test/autonomous-r34-e2e.test.mjs +2 -1
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/controller-desktop-r40.test.mjs +2 -1
- package/test/core.test.mjs +1 -1
- package/test/evidence-publisher-r42.test.mjs +53 -0
- package/test/helpers/autonomous-r34-fixture.mjs +3 -3
- package/test/human-clarification-r42.test.mjs +53 -0
- package/test/memory-autonomous-integration-r41.test.mjs +31 -0
- package/test/memory-history-r41.test.mjs +126 -0
- package/test/memory-projections-r41.test.mjs +92 -0
- package/test/memory-retrieval-r41.test.mjs +124 -0
- package/test/memory-snapshot-r41.test.mjs +106 -0
- package/test/provider-responses-r42.test.mjs +90 -0
- package/test/r33-closeout-regression.test.mjs +27 -12
- package/test/r33-finalization.test.mjs +4 -4
- package/test/r41-finalization.test.mjs +38 -0
- package/test/r42-finalization.test.mjs +38 -0
- package/test/release-version.test.mjs +14 -13
- package/test/task-contract-r42.test.mjs +41 -0
|
@@ -276,7 +276,7 @@ export function parseR34PlannerDecision(rawText, policy) {
|
|
|
276
276
|
assertR34UniqueSteps(decision.steps,'R34 replan');
|
|
277
277
|
return decision;
|
|
278
278
|
}
|
|
279
|
-
if (decision.kind === 'pause' || decision.kind === 'finish') return decision;
|
|
279
|
+
if (decision.kind === 'clarify' || decision.kind === 'pause' || decision.kind === 'finish') return decision;
|
|
280
280
|
throw new Error('R34 planner decision kind unsupported');
|
|
281
281
|
}
|
|
282
282
|
|
|
@@ -355,6 +355,7 @@ export function buildPlannerPrompt({ goal, policy, view }) {
|
|
|
355
355
|
decisions:view.recent_decisions ?? [],
|
|
356
356
|
projectMemory:view.project_memory ?? null,
|
|
357
357
|
desktopMemory:view.desktop_memory ?? null,
|
|
358
|
+
longTermMemory:view.long_term_memory ?? null,
|
|
358
359
|
capabilities:view.capabilities ?? null
|
|
359
360
|
});
|
|
360
361
|
const stateJson = canonicalJson(state);
|
|
@@ -362,7 +363,7 @@ export function buildPlannerPrompt({ goal, policy, view }) {
|
|
|
362
363
|
'You are the DEADBYTE R24 autonomous coding planner.',
|
|
363
364
|
'Return exactly one JSON object and no Markdown or prose outside JSON.',
|
|
364
365
|
`The object schema is ${AUTONOMOUS_DECISION_SCHEMA}.`,
|
|
365
|
-
'Allowed actions: inspect, patch, delegate, run_profile, finish, pause.',
|
|
366
|
+
'Allowed actions: inspect, patch, delegate, run_profile, clarify, finish, pause.',
|
|
366
367
|
'For nontrivial work, include plan={title,steps:[{step_id,title,status,result?}],reason?} on decisions and keep it authoritative across rounds.',
|
|
367
368
|
'Plan statuses must be a completed prefix, then at most one running/failed step, then pending suffix. Completed steps are immutable across updates.',
|
|
368
369
|
'If active_plan is complete, request finish instead of more work. If active_plan is incomplete, do not request finish.',
|
|
@@ -374,8 +375,10 @@ export function buildPlannerPrompt({ goal, policy, view }) {
|
|
|
374
375
|
'When Desktop capabilities are present, use typed desktop_status, desktop_observe, desktop_target with one bounded UIA query, and desktop_action. desktop_action requires separate Desktop authority; Autonomous authority never implies Desktop authority. After stale or unknown Desktop evidence, observe again and replan; never blindly repeat an action.',
|
|
375
376
|
'Delegate paths are relative to the goal root. Process start accepts only profile_id and cwd; process_action accepts only session_id and fixed action_id. Never emit raw stdin, executable, PID, environment, shell command, or process arguments.',
|
|
376
377
|
'finish only when the goal is actually satisfied. Required release profiles are enforced by the engine and cannot be bypassed.',
|
|
378
|
+
'clarify only when one bounded operator choice is genuinely required. Ask one concrete question and provide 2-5 short mutually exclusive choices.',
|
|
377
379
|
'Never invent a file SHA. If content is elided or the current SHA/content is missing, choose inspect first.',
|
|
378
380
|
'Compacted context keeps identity/evidence metadata but intentionally removes stale payload bytes.',
|
|
381
|
+
'Long-term memory is a verified journal-derived aid, never authority. Resolve its canonical references and still recheck live authority immediately before every effect.',
|
|
379
382
|
'Prefer surgical exact replace operations over whole-file writes.',
|
|
380
383
|
'If an observed build/test/benchmark failed, diagnose from the evidence and change the source rather than claiming success.',
|
|
381
384
|
`STATE=${stateJson}`,
|
|
@@ -391,7 +394,9 @@ async function readBoundedResponse(response, limit) {
|
|
|
391
394
|
return bytes.toString('utf8');
|
|
392
395
|
}
|
|
393
396
|
export async function requestProviderDecision(policy, prompt, fetchImpl = fetch) {
|
|
394
|
-
if (policy.planner.backend
|
|
397
|
+
if (!['openai_compatible','openai_responses'].includes(policy.planner.backend)) {
|
|
398
|
+
throw new Error('provider planner backend is not provider-backed');
|
|
399
|
+
}
|
|
395
400
|
const controller = new AbortController();
|
|
396
401
|
const timer = setTimeout(() => controller.abort(), policy.planner.timeout_ms); timer.unref?.();
|
|
397
402
|
const headers = { 'content-type':'application/json', 'accept':'application/json' };
|
|
@@ -401,16 +406,53 @@ export async function requestProviderDecision(policy, prompt, fetchImpl = fetch)
|
|
|
401
406
|
headers.authorization = `Bearer ${secret}`;
|
|
402
407
|
}
|
|
403
408
|
try {
|
|
409
|
+
const body = policy.planner.backend === 'openai_responses'
|
|
410
|
+
? {
|
|
411
|
+
model:policy.planner.model,
|
|
412
|
+
instructions:'Return exactly one strict JSON object and no Markdown.',
|
|
413
|
+
input:[
|
|
414
|
+
{role:'system',content:[{type:'input_text',text:'Return strict JSON only.'}]},
|
|
415
|
+
{role:'user',content:[{type:'input_text',text:prompt}]}
|
|
416
|
+
],
|
|
417
|
+
max_output_tokens:policy.planner.max_tokens,
|
|
418
|
+
text:{format:{type:'json_object'}},
|
|
419
|
+
store:false,
|
|
420
|
+
...(policy.planner.reasoning_effort?{reasoning:{effort:policy.planner.reasoning_effort}}:{})
|
|
421
|
+
}
|
|
422
|
+
: { model:policy.planner.model, temperature:0,
|
|
423
|
+
max_tokens:policy.planner.max_tokens,
|
|
424
|
+
messages:[{ role:'system', content:'Return strict JSON only.' },{ role:'user', content:prompt }] };
|
|
404
425
|
const response = await fetchImpl(policy.planner.endpoint, {
|
|
405
426
|
method:'POST', redirect:'error', signal:controller.signal, headers,
|
|
406
|
-
body:JSON.stringify(
|
|
407
|
-
max_tokens:policy.planner.max_tokens,
|
|
408
|
-
messages:[{ role:'system', content:'Return strict JSON only.' },{ role:'user', content:prompt }] })
|
|
427
|
+
body:JSON.stringify(body)
|
|
409
428
|
});
|
|
410
429
|
const rawBody = await readBoundedResponse(response, policy.planner.max_response_bytes);
|
|
411
430
|
if (!response.ok) throw new Error(`planner HTTP ${response.status}: ${rawBody.slice(0,512)}`);
|
|
412
431
|
let parsed;
|
|
413
432
|
try { parsed = JSON.parse(rawBody); } catch { throw new Error('planner HTTP response is not JSON'); }
|
|
433
|
+
if (policy.planner.backend === 'openai_responses') {
|
|
434
|
+
if (parsed?.status !== 'completed') throw new Error(`planner Responses API response not completed: ${String(parsed?.status ?? 'missing')}`);
|
|
435
|
+
const content=[];
|
|
436
|
+
let refused=false;
|
|
437
|
+
for (const item of Array.isArray(parsed?.output) ? parsed.output : []) {
|
|
438
|
+
if (item?.type !== 'message') continue;
|
|
439
|
+
for (const part of Array.isArray(item.content) ? item.content : []) {
|
|
440
|
+
if (part?.type === 'refusal') refused=true;
|
|
441
|
+
if (part?.type === 'output_text' && typeof part.text === 'string') content.push(part.text);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (refused) throw new Error('planner Responses API refusal');
|
|
445
|
+
if (content.length < 1) throw new Error('planner Responses API response missing output_text');
|
|
446
|
+
const usage=parsed?.usage && Number.isInteger(parsed.usage.input_tokens) && Number.isInteger(parsed.usage.output_tokens) && Number.isInteger(parsed.usage.total_tokens)
|
|
447
|
+
? {input_tokens:parsed.usage.input_tokens,output_tokens:parsed.usage.output_tokens,total_tokens:parsed.usage.total_tokens}
|
|
448
|
+
: null;
|
|
449
|
+
return {
|
|
450
|
+
backend:'openai_responses', model:typeof parsed.model === 'string' ? parsed.model.slice(0,128) : policy.planner.model,
|
|
451
|
+
raw_text:content.join(''),
|
|
452
|
+
response_id:typeof parsed.id === 'string' ? parsed.id.slice(0,160) : null,
|
|
453
|
+
usage
|
|
454
|
+
};
|
|
455
|
+
}
|
|
414
456
|
const content = parsed?.choices?.[0]?.message?.content;
|
|
415
457
|
if (typeof content !== 'string') throw new Error('planner HTTP response missing choices[0].message.content');
|
|
416
458
|
return { backend:'openai_compatible', model:typeof parsed.model === 'string' ? parsed.model : policy.planner.model,
|
|
@@ -33,7 +33,7 @@ function normalizeProviderCircuit(raw) {
|
|
|
33
33
|
function normalizePlanner(raw) {
|
|
34
34
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('autonomous planner missing');
|
|
35
35
|
const backend = raw.backend;
|
|
36
|
-
if (!['client_sampling', 'openai_compatible'].includes(backend)) throw new Error('autonomous planner backend invalid');
|
|
36
|
+
if (!['client_sampling', 'openai_compatible', 'openai_responses'].includes(backend)) throw new Error('autonomous planner backend invalid');
|
|
37
37
|
const common = { backend, max_tokens: integer(raw.max_tokens, 'planner.max_tokens', 256, 16384, 4096) };
|
|
38
38
|
if (backend === 'client_sampling') return common;
|
|
39
39
|
if (typeof raw.endpoint !== 'string') throw new Error('planner.endpoint required');
|
|
@@ -46,12 +46,20 @@ function normalizePlanner(raw) {
|
|
|
46
46
|
if (typeof raw.model !== 'string' || raw.model.length < 1 || raw.model.length > 128) throw new Error('planner.model invalid');
|
|
47
47
|
const apiKeyEnv = raw.api_key_env ?? null;
|
|
48
48
|
if (apiKeyEnv !== null && !ENV_RE.test(apiKeyEnv)) throw new Error('planner.api_key_env invalid');
|
|
49
|
-
|
|
49
|
+
const provider = {
|
|
50
50
|
...common, endpoint: endpoint.toString(), model: raw.model, api_key_env: apiKeyEnv,
|
|
51
51
|
timeout_ms: integer(raw.timeout_ms, 'planner.timeout_ms', 1000, 300000, 60000),
|
|
52
52
|
max_response_bytes: integer(raw.max_response_bytes, 'planner.max_response_bytes', 4096, 2 * 1024 * 1024, 512 * 1024),
|
|
53
53
|
circuit:normalizeProviderCircuit(raw.circuit)
|
|
54
54
|
};
|
|
55
|
+
if (backend === 'openai_responses') {
|
|
56
|
+
const effort = raw.reasoning_effort ?? null;
|
|
57
|
+
if (effort !== null && !['minimal','low','medium','high','xhigh'].includes(effort)) {
|
|
58
|
+
throw new Error('planner.reasoning_effort invalid');
|
|
59
|
+
}
|
|
60
|
+
return { ...provider, reasoning_effort:effort, store:false };
|
|
61
|
+
}
|
|
62
|
+
return provider;
|
|
55
63
|
}
|
|
56
64
|
function normalizeProfile(id, raw, codingPolicy) {
|
|
57
65
|
if (!ID_RE.test(id)) throw new Error(`invalid autonomous profile '${id}'`);
|
|
@@ -10,6 +10,7 @@ import { createPlan, replanPlan, completePlanStep, planIsComplete as r34PlanIsCo
|
|
|
10
10
|
import { deriveLoopState, repairFingerprint } from './autonomous-loop-state.mjs';
|
|
11
11
|
import { judgeProgress, stallDisposition } from './autonomous-progress.mjs';
|
|
12
12
|
import { classifyProviderFailure, deriveCircuit, providerCallDisposition, providerCircuitId } from './provider-circuit.mjs';
|
|
13
|
+
import { bindApprovalSubject, deriveEffectiveRigor, taskDagFromPlan, validateBoundApproval } from './agent-governance-r42.mjs';
|
|
13
14
|
import { verifyObservationReceiptFile } from './observation-runtime.mjs';
|
|
14
15
|
import { R36_CAPABILITY_LEDGER } from './capability-ledger.mjs';
|
|
15
16
|
import { deriveAgentMemory } from './agent-memory-plane.mjs';
|
|
@@ -129,6 +130,11 @@ function deriveState(goal, events) {
|
|
|
129
130
|
const binding = { ...p, event_sha256:event.event_sha256 };
|
|
130
131
|
observations.push(binding);
|
|
131
132
|
}
|
|
133
|
+
if (event.type === 'human_input_provided') {
|
|
134
|
+
observations.push({kind:'human_input',question:p.question,answer:p.answer,
|
|
135
|
+
answer_sha256:p.answer_sha256,trust_class:'operator_input',observed_at_utc:event.created_at_utc,
|
|
136
|
+
event_sha256:event.event_sha256});
|
|
137
|
+
}
|
|
132
138
|
if (event.type === 'native_diff_committed') {
|
|
133
139
|
mutationEpoch = Number(p.mutation_epoch ?? mutationEpoch + 1); mutationCount += Number(p.mutation_count ?? 1);
|
|
134
140
|
diffs.push({ ...p, event_sha256:event.event_sha256 });
|
|
@@ -325,7 +331,31 @@ function releaseReadiness(policy, state) {
|
|
|
325
331
|
});
|
|
326
332
|
return { ready:results.every(item => item.passed), mutation_epoch:state.mutation_epoch, required_profiles:results };
|
|
327
333
|
}
|
|
328
|
-
function
|
|
334
|
+
function normalizeTaskContract(raw) {
|
|
335
|
+
const value=raw ?? {schema:'deadbyte.task-contract.v1',scope:[],non_goals:[],constraints:[],
|
|
336
|
+
evidence_requirements:[],risk_level:'medium',clarification_policy:'ask_when_blocked',
|
|
337
|
+
project_default:'structured',explicit_override:null};
|
|
338
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('autonomous task contract invalid');
|
|
339
|
+
const allowed=['schema','scope','non_goals','constraints','evidence_requirements','risk_level','clarification_policy','project_default','explicit_override'];
|
|
340
|
+
const unknown=Object.keys(value).filter(key=>!allowed.includes(key));
|
|
341
|
+
if (unknown.length) throw new Error(`autonomous task contract unknown field '${unknown[0]}'`);
|
|
342
|
+
if (value.schema !== 'deadbyte.task-contract.v1') throw new Error('autonomous task contract schema invalid');
|
|
343
|
+
const list=(field,maxItems,maxLength)=>{
|
|
344
|
+
const items=value[field] ?? [];
|
|
345
|
+
if (!Array.isArray(items) || items.length > maxItems || items.some(item=>typeof item !== 'string' || item.length < 1 || item.length > maxLength || item.includes('\0'))) {
|
|
346
|
+
throw new Error(`autonomous task contract ${field} invalid`);
|
|
347
|
+
}
|
|
348
|
+
return [...items];
|
|
349
|
+
};
|
|
350
|
+
if (!['low','medium','high'].includes(value.risk_level)) throw new Error('autonomous task contract risk_level invalid');
|
|
351
|
+
if (!['ask_when_blocked','fail_closed'].includes(value.clarification_policy)) throw new Error('autonomous task contract clarification_policy invalid');
|
|
352
|
+
const rigor=deriveEffectiveRigor({project_default:value.project_default??'structured',risk_level:value.risk_level,
|
|
353
|
+
explicit_override:value.explicit_override??null});
|
|
354
|
+
return Object.freeze({schema:value.schema,scope:list('scope',32,512),non_goals:list('non_goals',24,512),
|
|
355
|
+
constraints:list('constraints',32,512),evidence_requirements:list('evidence_requirements',32,512),
|
|
356
|
+
risk_level:value.risk_level,clarification_policy:value.clarification_policy,...rigor});
|
|
357
|
+
}
|
|
358
|
+
function normalizeGoal(policy, { submissionId, rootId, title = '', goal, acceptance = '', taskContract = null, maxIterations = null }) {
|
|
329
359
|
if (!ID_RE.test(submissionId ?? '')) throw new Error('autonomous submission_id invalid');
|
|
330
360
|
if (!policy.root_ids.includes(rootId)) throw new Error(`autonomous root '${rootId}' is not allowed`);
|
|
331
361
|
if (typeof title !== 'string' || title.length > 160) throw new Error('autonomous title must be <=160 characters');
|
|
@@ -335,7 +365,7 @@ function normalizeGoal(policy, { submissionId, rootId, title = '', goal, accepta
|
|
|
335
365
|
if (!Number.isInteger(iterations) || iterations < 1 || iterations > policy.limits.max_iterations) throw new Error('autonomous max_iterations exceeds policy');
|
|
336
366
|
const body = { schema:AUTONOMOUS_GOAL_SCHEMA, policy_sha256:policy.policy_sha256,
|
|
337
367
|
coding_policy_sha256:policy.coding_policy_sha256, submission_id:submissionId, root_id:rootId,
|
|
338
|
-
title, goal, acceptance, max_iterations:iterations };
|
|
368
|
+
title, goal, acceptance, task_contract:normalizeTaskContract(taskContract),max_iterations:iterations };
|
|
339
369
|
return Object.freeze({ ...body, goal_sha256:sha256Object(body) });
|
|
340
370
|
}
|
|
341
371
|
|
|
@@ -360,20 +390,24 @@ function configuredCapabilityManifest({machineFsRuntime,observationRuntime,proce
|
|
|
360
390
|
entries:Object.freeze(entries)});
|
|
361
391
|
}
|
|
362
392
|
|
|
363
|
-
async function plannerView(goal, state, events, codingRuntime, capabilityManifest, desktopMemoryProvider=null) {
|
|
393
|
+
async function plannerView(goal, state, events, codingRuntime, capabilityManifest, desktopMemoryProvider=null,memoryProvider=null) {
|
|
364
394
|
const tree = await codingRuntime.tree({ rootId:goal.root_id, path:'.', maxDepth:4, maxEntries:600 });
|
|
365
395
|
const memory=deriveAgentMemory(events,{maxArchival:24});
|
|
366
396
|
const desktopMemory=desktopMemoryProvider?await desktopMemoryProvider.plannerContext():null;
|
|
397
|
+
const codingAuthority=memoryProvider?await codingRuntime.sessionStatus():null;
|
|
398
|
+
const longTerm=memoryProvider?await memoryProvider.refresh({goal,events,currentPlan:state.active_plan,currentFiles:[],currentSymbols:[],
|
|
399
|
+
currentAuthority:{coding:{armed:codingAuthority.armed,reason:codingAuthority.reason}}}):null;
|
|
367
400
|
return { mutation_epoch:state.mutation_epoch, planner_round:state.planner_round, tree:tree.entries,
|
|
368
401
|
contexts:state.contexts.slice(-16), observations:state.observations.slice(-24), active_plan:state.active_plan,
|
|
369
402
|
capabilities:capabilityManifest, project_memory:memory, desktop_memory:desktopMemory,
|
|
403
|
+
long_term_memory:longTerm?.planner_context??null,memory_evidence:longTerm,
|
|
370
404
|
recent_decisions:state.decisions.slice(-8).map(item => ({ planner_round:item.planner_round,
|
|
371
405
|
action:item.decision?.action, summary:item.decision?.summary })) };
|
|
372
406
|
}
|
|
373
407
|
function requestIdFor(goalId, plannerRound, journalHead) {
|
|
374
408
|
return sha256Object({ schema:'deadbyte.autonomous-planner-request.v1', goal_id:goalId, planner_round:plannerRound, journal_head_sha256:journalHead });
|
|
375
409
|
}
|
|
376
|
-
async function requestPlanner(ctx, goal, events, state, policy, codingRuntime, capabilityManifest, desktopMemoryProvider, signingKeyPath) {
|
|
410
|
+
async function requestPlanner(ctx, goal, events, state, policy, codingRuntime, capabilityManifest, desktopMemoryProvider,memoryProvider, signingKeyPath) {
|
|
377
411
|
if (state.pending_request) return state.pending_request;
|
|
378
412
|
const round = state.planner_round + 1;
|
|
379
413
|
if (round > goal.max_iterations) {
|
|
@@ -384,8 +418,9 @@ async function requestPlanner(ctx, goal, events, state, policy, codingRuntime, c
|
|
|
384
418
|
await appendEvent(ctx,events,signingKeyPath,'goal_paused',{ reason:'wall_time_limit', max_wall_ms:policy.limits.max_wall_ms });
|
|
385
419
|
return null;
|
|
386
420
|
}
|
|
387
|
-
const view = await plannerView(goal,state,events,codingRuntime,capabilityManifest,desktopMemoryProvider);
|
|
388
|
-
let prompt = buildPlannerPrompt({ goal:{ title:goal.title, goal:goal.goal, acceptance:goal.acceptance
|
|
421
|
+
const view = await plannerView(goal,state,events,codingRuntime,capabilityManifest,desktopMemoryProvider,memoryProvider);
|
|
422
|
+
let prompt = buildPlannerPrompt({ goal:{ goal_id:goal.goal_id,title:goal.title, goal:goal.goal, acceptance:goal.acceptance,
|
|
423
|
+
task_contract:goal.task_contract }, policy, view });
|
|
389
424
|
const repair = [...(state.planner_rejections ?? [])].reverse().find(item => item.planner_round === round) ?? null;
|
|
390
425
|
if (repair) {
|
|
391
426
|
const repairState = canonicalJson({ request_id:repair.request_id, response_sha256:repair.response_sha256,
|
|
@@ -397,6 +432,14 @@ Repair only the decision contract. Return one corrected strict JSON decision; do
|
|
|
397
432
|
const requestId = requestIdFor(goal.goal_id,round,state.journal_head_sha256);
|
|
398
433
|
const payload = { request_id:requestId, planner_round:round, mutation_epoch:state.mutation_epoch,
|
|
399
434
|
prompt, prompt_sha256:sha256Bytes(Buffer.from(prompt,'utf8')), backend:policy.planner.backend,
|
|
435
|
+
...(view.memory_evidence?{
|
|
436
|
+
working_memory_sha256:view.memory_evidence.working_memory_sha256,
|
|
437
|
+
memory_snapshot_sha256:view.memory_evidence.snapshot_sha256,
|
|
438
|
+
memory_index_sha256:view.memory_evidence.index_sha256,
|
|
439
|
+
historical_archive_sha256:view.memory_evidence.historical_archive_sha256,
|
|
440
|
+
historical_index_sha256:view.memory_evidence.historical_index_sha256,
|
|
441
|
+
memory_source_release_manifest:view.memory_evidence.source_release_manifest
|
|
442
|
+
}:{}),
|
|
400
443
|
...(repair ? { repair_of_request_id:repair.request_id, rejection_event_sha256:repair.event_sha256 } : {}) };
|
|
401
444
|
await appendEvent(ctx,events,signingKeyPath,'planner_requested',payload);
|
|
402
445
|
return payload;
|
|
@@ -440,7 +483,24 @@ function legacyPublicLoopPhase(phase) {
|
|
|
440
483
|
return ({PLAN:'PLANNING',INSPECT:'INSPECTING',ACT:'ACTING',VERIFY:'VERIFYING',CRITIC:'REPLANNING',
|
|
441
484
|
REPAIR:'REPLANNING',RELEASE:'VERIFYING',WAITING:'WAITING',SUCCEEDED:'COMPLETE',FAILED:'WAITING'})[phase] ?? 'WAITING';
|
|
442
485
|
}
|
|
443
|
-
function
|
|
486
|
+
function approvalScopeFor(reason){
|
|
487
|
+
if(reason==='human_input_required') return 'human_clarification';
|
|
488
|
+
if(String(reason??'').startsWith('desktop_')) return 'desktop_resume';
|
|
489
|
+
if(['ambiguous_interrupted_action','profile_execution_without_receipt'].includes(reason)) return 'interrupted_effect_resume';
|
|
490
|
+
return 'resume_goal';
|
|
491
|
+
}
|
|
492
|
+
function pendingApprovalSubject(goal,events,loop){
|
|
493
|
+
if(!loop?.pending_approval) return null;
|
|
494
|
+
const waiting=[...events].reverse().find(event=>event.type==='waiting_entered')??null;
|
|
495
|
+
if(!waiting) return null;
|
|
496
|
+
const payload=waiting.payload??{};
|
|
497
|
+
return bindApprovalSubject({
|
|
498
|
+
goal_id:goal.goal_id,reviewed_event_seq:waiting.seq,reviewed_event_sha256:waiting.event_sha256,
|
|
499
|
+
journal_head_sha256:events.at(-1)?.event_sha256??ZERO_HASH,scope:approvalScopeFor(payload.reason),
|
|
500
|
+
subject:{reason:payload.reason??loop.wait_reason??'paused',waiting_payload_sha256:sha256Object(payload)}
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
function publicLoopState(loop,goal=null,events=null) {
|
|
444
504
|
if (!loop) return null;
|
|
445
505
|
const currentStep=loop.current_step ? {
|
|
446
506
|
step_id:loop.current_step.step_id,intent:loop.current_step.intent,status:loop.current_step.status
|
|
@@ -456,15 +516,18 @@ function publicLoopState(loop) {
|
|
|
456
516
|
return {
|
|
457
517
|
schema:'deadbyte.autonomous-loop-state.v1',phase:legacyPublicLoopPhase(loop.phase),plan_id:loop.plan_id,plan_version:loop.plan_version,
|
|
458
518
|
current_step:currentStep,replan_count:loop.replan_count,progress,
|
|
459
|
-
pending_action:pendingAction,pending_approval:loop.pending_approval,wait_reason:loop.wait_reason
|
|
519
|
+
pending_action:pendingAction,pending_approval:loop.pending_approval,wait_reason:loop.wait_reason,
|
|
520
|
+
clarification:loop.clarification ?? null,
|
|
521
|
+
approval_subject:goal&&events?pendingApprovalSubject(goal,events,loop):null,
|
|
522
|
+
task_dag:goal&&loop.plan?taskDagFromPlan({run_id:goal.goal_id,goal_id:goal.goal_id,plan:loop.plan}):null
|
|
460
523
|
};
|
|
461
524
|
}
|
|
462
525
|
function withLoopResult(goal,events,result) {
|
|
463
|
-
const loop=publicLoopState(loopStateOrNull(goal,events));
|
|
526
|
+
const loop=publicLoopState(loopStateOrNull(goal,events),goal,events);
|
|
464
527
|
return loop ? {...result,loop} : result;
|
|
465
528
|
}
|
|
466
529
|
function providerCircuitSnapshot(policy,events,observedAtUtc=new Date().toISOString()) {
|
|
467
|
-
if (policy.planner.backend
|
|
530
|
+
if (!['openai_compatible','openai_responses'].includes(policy.planner.backend)) return null;
|
|
468
531
|
return deriveCircuit(events,providerCircuitId(policy.planner),policy.planner.circuit,observedAtUtc);
|
|
469
532
|
}
|
|
470
533
|
async function enterProviderCircuitWait({ctx,events,goal,policy,signingKeyPath,reason,circuit}) {
|
|
@@ -505,7 +568,9 @@ async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signin
|
|
|
505
568
|
try {
|
|
506
569
|
const response=await requestProviderDecision(policy,pending.prompt);
|
|
507
570
|
await appendEvent(ctx,events,signingKeyPath,'provider_call_succeeded',{
|
|
508
|
-
provider_id:providerId,request_id:pending.request_id,probe:disposition.probe===true
|
|
571
|
+
provider_id:providerId,request_id:pending.request_id,probe:disposition.probe===true,
|
|
572
|
+
...(response.response_id?{response_id:response.response_id}:{}),
|
|
573
|
+
...(response.usage?{usage:response.usage}:{})
|
|
509
574
|
});
|
|
510
575
|
return {response};
|
|
511
576
|
} catch (error) {
|
|
@@ -524,14 +589,14 @@ async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signin
|
|
|
524
589
|
throw error;
|
|
525
590
|
}
|
|
526
591
|
}
|
|
527
|
-
async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingRuntime, capabilityManifest, desktopMemoryProvider, signingKeyPath, plannerResponse }) {
|
|
592
|
+
async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingRuntime, capabilityManifest, desktopMemoryProvider,memoryProvider, signingKeyPath, plannerResponse }) {
|
|
528
593
|
let currentState = state;
|
|
529
|
-
let pending = currentState.pending_request ?? await requestPlanner(ctx,goal,events,currentState,policy,codingRuntime,capabilityManifest,desktopMemoryProvider,signingKeyPath);
|
|
594
|
+
let pending = currentState.pending_request ?? await requestPlanner(ctx,goal,events,currentState,policy,codingRuntime,capabilityManifest,desktopMemoryProvider,memoryProvider,signingKeyPath);
|
|
530
595
|
if (!pending) return null;
|
|
531
596
|
let suppliedResponse = plannerResponse;
|
|
532
597
|
while (pending) {
|
|
533
598
|
let response;
|
|
534
|
-
if (policy.planner.backend
|
|
599
|
+
if (['openai_compatible','openai_responses'].includes(policy.planner.backend)) {
|
|
535
600
|
const provider=await requestProviderWithCircuit({ctx,events,goal,policy,pending,signingKeyPath});
|
|
536
601
|
if (provider.waiting) return {needs_planner:false,rejected:false,paused:true,provider_waiting:true};
|
|
537
602
|
response=provider.response;
|
|
@@ -565,7 +630,7 @@ async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingR
|
|
|
565
630
|
return { needs_planner:false, rejected:true, paused:true };
|
|
566
631
|
}
|
|
567
632
|
currentState = deriveState(goal,events);
|
|
568
|
-
pending = await requestPlanner(ctx,goal,events,currentState,policy,codingRuntime,capabilityManifest,desktopMemoryProvider,signingKeyPath);
|
|
633
|
+
pending = await requestPlanner(ctx,goal,events,currentState,policy,codingRuntime,capabilityManifest,desktopMemoryProvider,memoryProvider,signingKeyPath);
|
|
569
634
|
if (!pending) return null;
|
|
570
635
|
if (policy.planner.backend === 'client_sampling') return { needs_planner:true, request:pending };
|
|
571
636
|
continue;
|
|
@@ -573,6 +638,14 @@ async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingR
|
|
|
573
638
|
const event = await appendEvent(ctx,events,signingKeyPath,'planner_decision',{
|
|
574
639
|
request_id:pending.request_id, planner_round:pending.planner_round, mutation_epoch:pending.mutation_epoch,
|
|
575
640
|
backend:response.backend, model:response.model ?? 'unknown', response_sha256:sha256Bytes(Buffer.from(response.raw_text,'utf8')),
|
|
641
|
+
...(response.response_id?{response_id:response.response_id}:{}),
|
|
642
|
+
...(response.usage?{usage:response.usage}:{}),
|
|
643
|
+
...(pending.working_memory_sha256?{
|
|
644
|
+
working_memory_sha256:pending.working_memory_sha256,memory_snapshot_sha256:pending.memory_snapshot_sha256,
|
|
645
|
+
memory_index_sha256:pending.memory_index_sha256,
|
|
646
|
+
historical_archive_sha256:pending.historical_archive_sha256,historical_index_sha256:pending.historical_index_sha256,
|
|
647
|
+
memory_source_release_manifest:pending.memory_source_release_manifest
|
|
648
|
+
}:{}),
|
|
576
649
|
decision
|
|
577
650
|
});
|
|
578
651
|
return { needs_planner:false, event, decision };
|
|
@@ -1488,6 +1561,20 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
|
|
|
1488
1561
|
return r34EnterWaiting({ctx,events,goal,signingKeyPath,reason:decision.reason,resumePhase:loop.phase});
|
|
1489
1562
|
}
|
|
1490
1563
|
|
|
1564
|
+
if (decision.kind === 'clarify') {
|
|
1565
|
+
await appendEvent(ctx,events,signingKeyPath,'human_input_requested',{
|
|
1566
|
+
decision_event_sha256:decisionEvent.event_sha256,question:decision.question,choices:decision.choices
|
|
1567
|
+
});
|
|
1568
|
+
await appendEvent(ctx,events,signingKeyPath,'waiting_entered',{
|
|
1569
|
+
reason:'human_input_required',requires_approval:true,resume_phase:'PLAN',
|
|
1570
|
+
question:decision.question,choices:decision.choices
|
|
1571
|
+
});
|
|
1572
|
+
await appendEvent(ctx,events,signingKeyPath,'goal_paused',{
|
|
1573
|
+
reason:'human_input_required',decision_event_sha256:decisionEvent.event_sha256
|
|
1574
|
+
});
|
|
1575
|
+
return {status:'paused',reason:'human_input_required'};
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1491
1578
|
if (decision.kind === 'finish') {
|
|
1492
1579
|
if (!r34PlanIsComplete(loop.plan)) {
|
|
1493
1580
|
await appendEvent(ctx,events,signingKeyPath,'plan_guard_blocked',{
|
|
@@ -1658,7 +1745,7 @@ function terminalState(state) {
|
|
|
1658
1745
|
}
|
|
1659
1746
|
function pauseState(state) { return state.state === 'paused'; }
|
|
1660
1747
|
async function resumeProviderWaitIfReady({ctx,journal,goal,state,policy,signingKeyPath}) {
|
|
1661
|
-
if (state.state!=='paused'||state.reason!=='provider_circuit_open'
|
|
1748
|
+
if (state.state!=='paused'||state.reason!=='provider_circuit_open'||!['openai_compatible','openai_responses'].includes(policy.planner.backend)) return state;
|
|
1662
1749
|
const circuit=providerCircuitSnapshot(policy,journal);
|
|
1663
1750
|
const disposition=providerCallDisposition(circuit);
|
|
1664
1751
|
if (!disposition.allowed||circuit.state!=='HALF_OPEN') return state;
|
|
@@ -1678,7 +1765,7 @@ function isAuthorityUnavailable(error) {
|
|
|
1678
1765
|
|
|
1679
1766
|
export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRuntime, machineFsRuntime = null,
|
|
1680
1767
|
observationRuntime = null, processRuntime = null, semanticRuntime = null, desktopRuntime = null,
|
|
1681
|
-
desktopMemoryProvider = null, signingKeyPath, verifyKeyPath }) {
|
|
1768
|
+
desktopMemoryProvider = null,memoryProvider=null, signingKeyPath, verifyKeyPath }) {
|
|
1682
1769
|
if (!policy || !codingRuntime || !nativeDiffRuntime) throw new Error('autonomous runtime requires policy/coding/native-diff runtimes');
|
|
1683
1770
|
if (!signingKeyPath || !verifyKeyPath) throw new Error('autonomous runtime requires trust keys');
|
|
1684
1771
|
const capabilityManifest=configuredCapabilityManifest({machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime});
|
|
@@ -1697,7 +1784,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1697
1784
|
coding_policy_sha256:policy.coding_policy_sha256 };
|
|
1698
1785
|
}
|
|
1699
1786
|
async function providerCircuitStatus({goalId}) {
|
|
1700
|
-
if (policy.planner.backend
|
|
1787
|
+
if (!['openai_compatible','openai_responses'].includes(policy.planner.backend)) return null;
|
|
1701
1788
|
const ctx=goalContext(policy,goalId);
|
|
1702
1789
|
await loadGoal(ctx,policy,verifyKeyPath);
|
|
1703
1790
|
const journal=await readEvents(ctx,verifyKeyPath);
|
|
@@ -1712,9 +1799,9 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1712
1799
|
return { autonomous, coding };
|
|
1713
1800
|
}
|
|
1714
1801
|
|
|
1715
|
-
async function submit({ submissionId, rootId, title = '', goal, acceptance = '', maxIterations = null }) {
|
|
1802
|
+
async function submit({ submissionId, rootId, title = '', goal, acceptance = '', taskContract = null, maxIterations = null }) {
|
|
1716
1803
|
await assertRunAuthority(rootId);
|
|
1717
|
-
const normalized = normalizeGoal(policy,{ submissionId,rootId,title,goal,acceptance,maxIterations });
|
|
1804
|
+
const normalized = normalizeGoal(policy,{ submissionId,rootId,title,goal,acceptance,taskContract,maxIterations });
|
|
1718
1805
|
const goalId = goalIdFor(policy.policy_sha256,submissionId);
|
|
1719
1806
|
const ctx = goalContext(policy,goalId);
|
|
1720
1807
|
if (await exists(ctx.dir)) {
|
|
@@ -1731,8 +1818,10 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1731
1818
|
await writeFile(ctx.goalPath,`${canonicalJson(envelope)}\n`,{flag:'wx'});
|
|
1732
1819
|
const journal = [];
|
|
1733
1820
|
await appendEvent(ctx,journal,signingKeyPath,'goal_created',{
|
|
1734
|
-
submission_id:submissionId, root_id:rootId, title,
|
|
1735
|
-
|
|
1821
|
+
submission_id:submissionId, root_id:rootId, title, acceptance:normalized.acceptance,
|
|
1822
|
+
task_contract:normalized.task_contract,goal_sha256:normalized.goal_sha256,
|
|
1823
|
+
max_iterations:normalized.max_iterations,
|
|
1824
|
+
...(memoryProvider?{source_release_manifest:memoryProvider.source_release_manifest}:{})
|
|
1736
1825
|
});
|
|
1737
1826
|
return { status:'accepted', goal_id:goalId, goal_sha256:normalized.goal_sha256, state:'pending' };
|
|
1738
1827
|
}
|
|
@@ -1742,7 +1831,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1742
1831
|
const goal = await loadGoal(ctx,policy,verifyKeyPath);
|
|
1743
1832
|
const journal = await readEvents(ctx,verifyKeyPath);
|
|
1744
1833
|
const derived = deriveState(goal,journal);
|
|
1745
|
-
const loop = publicLoopState(loopStateOrNull(goal,journal));
|
|
1834
|
+
const loop = publicLoopState(loopStateOrNull(goal,journal),goal,journal);
|
|
1746
1835
|
const lock=await runLockStatus(ctx);
|
|
1747
1836
|
return { status:'ok', goal_id:goalId, goal_sha256:goal.goal_sha256,
|
|
1748
1837
|
title:goal.title, root_id:goal.root_id, journal_verified:true, ...derived, ...lock,
|
|
@@ -1776,8 +1865,11 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1776
1865
|
return { status:'existing', goal_id:goalId, state:current.state };
|
|
1777
1866
|
}
|
|
1778
1867
|
}
|
|
1779
|
-
async function approve({ goalId, decision = 'approve' }) {
|
|
1868
|
+
async function approve({ goalId, decision = 'approve', answer = null, approvalSubjectSha256 = null, expiresInMs = 300000 }) {
|
|
1780
1869
|
if (!['approve','reject'].includes(decision)) throw new Error('autonomous approval decision invalid');
|
|
1870
|
+
if (answer !== null && (typeof answer !== 'string' || answer.length < 1 || answer.length > 4096 || answer.includes('\0'))) {
|
|
1871
|
+
throw new Error('autonomous approval answer invalid');
|
|
1872
|
+
}
|
|
1781
1873
|
const ctx = goalContext(policy,goalId);
|
|
1782
1874
|
await acquireLock(ctx);
|
|
1783
1875
|
try {
|
|
@@ -1792,7 +1884,34 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1792
1884
|
}
|
|
1793
1885
|
if (hasR34Loop(journal)) {
|
|
1794
1886
|
await assertRunAuthority(goal.root_id);
|
|
1887
|
+
const loopState=loopStateOrNull(goal,journal);
|
|
1888
|
+
const approvalSubject=pendingApprovalSubject(goal,journal,loopState);
|
|
1889
|
+
if(!approvalSubject) throw new Error('autonomous approval subject unavailable');
|
|
1890
|
+
if(typeof approvalSubjectSha256!=='string'||approvalSubjectSha256!==approvalSubject.approval_subject_sha256){
|
|
1891
|
+
throw new Error('autonomous approval subject mismatch');
|
|
1892
|
+
}
|
|
1893
|
+
if(!Number.isInteger(expiresInMs)||expiresInMs<1000||expiresInMs>3_600_000) throw new Error('autonomous approval lifetime invalid');
|
|
1795
1894
|
const waiting=[...journal].reverse().find(event=>event.type==='waiting_entered') ?? null;
|
|
1895
|
+
if (waiting?.payload?.reason === 'human_input_required' && answer === null) {
|
|
1896
|
+
throw new Error('autonomous clarification answer is required');
|
|
1897
|
+
}
|
|
1898
|
+
if (waiting?.payload?.reason !== 'human_input_required' && answer !== null) {
|
|
1899
|
+
throw new Error('autonomous approval answer is only valid for human clarification');
|
|
1900
|
+
}
|
|
1901
|
+
const issuedAt=new Date();
|
|
1902
|
+
const approval={schema:'deadbyte.bound-approval.v1',approval_subject_sha256:approvalSubjectSha256,
|
|
1903
|
+
reviewed_event_seq:approvalSubject.reviewed_event_seq,reviewed_event_sha256:approvalSubject.reviewed_event_sha256,
|
|
1904
|
+
journal_head_sha256:approvalSubject.journal_head_sha256,scope:approvalSubject.scope,
|
|
1905
|
+
issued_at_utc:issuedAt.toISOString(),expires_at_utc:new Date(issuedAt.getTime()+expiresInMs).toISOString(),
|
|
1906
|
+
provenance:'operator_mcp'};
|
|
1907
|
+
validateBoundApproval({approval,subject:approvalSubject,now:issuedAt});
|
|
1908
|
+
await appendEvent(ctx,journal,signingKeyPath,'approval_granted',approval);
|
|
1909
|
+
if (waiting?.payload?.reason === 'human_input_required') {
|
|
1910
|
+
await appendEvent(ctx,journal,signingKeyPath,'human_input_provided',{
|
|
1911
|
+
request_event_sha256:waiting.event_sha256,question:waiting.payload.question,
|
|
1912
|
+
answer,answer_sha256:sha256Bytes(Buffer.from(answer,'utf8'))
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1796
1915
|
const resumePhase=typeof waiting?.payload?.resume_phase === 'string' ? waiting.payload.resume_phase : 'PLAN';
|
|
1797
1916
|
await appendEvent(ctx,journal,signingKeyPath,'waiting_resumed',{
|
|
1798
1917
|
to:resumePhase,reason:waiting?.payload?.reason ?? current.reason ?? 'operator_approved',
|
|
@@ -1927,7 +2046,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1927
2046
|
continue;
|
|
1928
2047
|
}
|
|
1929
2048
|
const accepted = await acceptPlannerDecision({ ctx,events:journal,goal,state,policy,codingRuntime,capabilityManifest,
|
|
1930
|
-
desktopMemoryProvider,signingKeyPath,plannerResponse:suppliedResponse });
|
|
2049
|
+
desktopMemoryProvider,memoryProvider,signingKeyPath,plannerResponse:suppliedResponse });
|
|
1931
2050
|
suppliedResponse = null;
|
|
1932
2051
|
if (accepted?.needs_planner) {
|
|
1933
2052
|
state = deriveState(goal,journal);
|
|
@@ -24,7 +24,7 @@ async function listGoalIds(policy) {
|
|
|
24
24
|
export function startAutonomousSupervisor({ runtime, policy = runtime?.policy,
|
|
25
25
|
idleDelayMs = 750, maxCycles = 32, logger = console, continuous = true } = {}) {
|
|
26
26
|
if (!runtime || !policy) throw new Error('autonomous supervisor requires runtime + policy');
|
|
27
|
-
const providerBacked = policy.planner.backend
|
|
27
|
+
const providerBacked = ['openai_compatible','openai_responses'].includes(policy.planner.backend);
|
|
28
28
|
if (!Number.isInteger(idleDelayMs) || idleDelayMs < 25 || idleDelayMs > 60_000) {
|
|
29
29
|
throw new Error('autonomous supervisor idleDelayMs out of range');
|
|
30
30
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { link, mkdir, open, readFile, realpath, rm } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { canonicalJson } from './canonical-json.mjs';
|
|
5
|
+
|
|
6
|
+
const HEX64=/^[0-9a-f]{64}$/;
|
|
7
|
+
const ID=/^[A-Za-z0-9._-]{1,96}$/;
|
|
8
|
+
const shaBytes=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
9
|
+
const sha=value=>shaBytes(Buffer.from(canonicalJson(value),'utf8'));
|
|
10
|
+
function object(value,label){if(!value||typeof value!=='object'||Array.isArray(value))throw new Error(`${label} invalid`);return value;}
|
|
11
|
+
function keys(value,allowed,label){const extra=Object.keys(value).filter(key=>!allowed.includes(key));if(extra.length)throw new Error(`${label} unknown field '${extra[0]}'`);}
|
|
12
|
+
function id(value,label){if(typeof value!=='string'||!ID.test(value))throw new Error(`${label} invalid`);return value;}
|
|
13
|
+
function hex(value,label){if(typeof value!=='string'||!HEX64.test(value))throw new Error(`${label} invalid`);return value;}
|
|
14
|
+
function iso(value,label){if(typeof value!=='string'||!Number.isFinite(Date.parse(value)))throw new Error(`${label} invalid`);return value;}
|
|
15
|
+
|
|
16
|
+
export async function publishEvidenceAtomic({root,kind,evidenceId=null,payload}){
|
|
17
|
+
if(typeof root!=='string'||!path.isAbsolute(root)) throw new Error('evidence root must be absolute');
|
|
18
|
+
id(kind,'evidence kind');object(payload,'evidence payload');
|
|
19
|
+
const payloadSha=sha(payload);
|
|
20
|
+
const resolvedId=id(evidenceId??payloadSha,'evidence id');
|
|
21
|
+
const body={schema:'deadbyte.evidence-publication.v1',kind,evidence_id:resolvedId,payload_sha256:payloadSha,payload:structuredClone(payload)};
|
|
22
|
+
const envelope={...body,evidence_sha256:sha(body)};
|
|
23
|
+
const bytes=Buffer.from(`${canonicalJson(envelope)}\n`,'utf8');
|
|
24
|
+
const base=path.resolve(root);await mkdir(base,{recursive:true});
|
|
25
|
+
const canonicalRoot=await realpath(base);
|
|
26
|
+
const pendingDir=path.join(canonicalRoot,'.pending');const finalDir=path.join(canonicalRoot,kind);
|
|
27
|
+
await mkdir(pendingDir,{recursive:true});await mkdir(finalDir,{recursive:true});
|
|
28
|
+
const target=path.join(finalDir,`${resolvedId}.json`);
|
|
29
|
+
const temp=path.join(pendingDir,`${kind}-${resolvedId}-${randomUUID()}.tmp`);
|
|
30
|
+
let handle;
|
|
31
|
+
try{
|
|
32
|
+
handle=await open(temp,'wx',0o600);await handle.writeFile(bytes);await handle.sync();await handle.close();handle=null;
|
|
33
|
+
const staged=await readFile(temp);
|
|
34
|
+
if(shaBytes(staged)!==shaBytes(bytes)) throw new Error('staged evidence hash verification failed');
|
|
35
|
+
try{
|
|
36
|
+
await link(temp,target);
|
|
37
|
+
return Object.freeze({status:'created',kind,evidence_id:resolvedId,evidence_sha256:envelope.evidence_sha256,
|
|
38
|
+
bytes:bytes.length,path:target});
|
|
39
|
+
}catch(error){
|
|
40
|
+
if(error?.code!=='EEXIST') throw error;
|
|
41
|
+
const existing=await readFile(target);
|
|
42
|
+
if(!existing.equals(bytes)){
|
|
43
|
+
const conflict=new Error(`EVIDENCE_ID_CONFLICT: ${kind}/${resolvedId}`);conflict.code='EVIDENCE_ID_CONFLICT';throw conflict;
|
|
44
|
+
}
|
|
45
|
+
return Object.freeze({status:'existing',kind,evidence_id:resolvedId,evidence_sha256:envelope.evidence_sha256,
|
|
46
|
+
bytes:bytes.length,path:target});
|
|
47
|
+
}
|
|
48
|
+
}finally{
|
|
49
|
+
if(handle) await handle.close().catch(()=>{});
|
|
50
|
+
await rm(temp,{force:true}).catch(()=>{});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function createExecutionReceipt(raw){
|
|
55
|
+
const value=object(raw,'execution receipt');
|
|
56
|
+
keys(value,['execution_id','subject_sha256','request_sha256','observed_exit_code','started_at_utc','completed_at_utc'],'execution receipt');
|
|
57
|
+
const core={schema:'deadbyte.execution-receipt.v1',execution_id:id(value.execution_id,'execution_id'),
|
|
58
|
+
subject_sha256:hex(value.subject_sha256,'execution subject_sha256'),request_sha256:hex(value.request_sha256,'execution request_sha256'),
|
|
59
|
+
observed_exit_code:value.observed_exit_code,started_at_utc:iso(value.started_at_utc,'execution started_at_utc'),
|
|
60
|
+
completed_at_utc:iso(value.completed_at_utc,'execution completed_at_utc')};
|
|
61
|
+
if(!Number.isInteger(core.observed_exit_code)) throw new Error('execution exit code invalid');
|
|
62
|
+
if(Date.parse(core.completed_at_utc)<Date.parse(core.started_at_utc)) throw new Error('execution time order invalid');
|
|
63
|
+
return Object.freeze({...core,receipt_sha256:sha(core)});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createVerificationResult(raw){
|
|
67
|
+
const value=object(raw,'verification result');
|
|
68
|
+
keys(value,['verification_id','subject_sha256','execution_receipt_sha256','outcome_passed','oracle','evidence_refs'],'verification result');
|
|
69
|
+
if(typeof value.outcome_passed!=='boolean') throw new Error('verification outcome invalid');
|
|
70
|
+
if(!Array.isArray(value.evidence_refs)||value.evidence_refs.length<1||value.evidence_refs.length>64||value.evidence_refs.some(ref=>!HEX64.test(ref))) throw new Error('verification evidence refs invalid');
|
|
71
|
+
const core={schema:'deadbyte.verification-result.v1',verification_id:id(value.verification_id,'verification_id'),
|
|
72
|
+
subject_sha256:hex(value.subject_sha256,'verification subject_sha256'),
|
|
73
|
+
execution_receipt_sha256:hex(value.execution_receipt_sha256,'verification receipt_sha256'),
|
|
74
|
+
outcome_passed:value.outcome_passed,oracle:id(value.oracle,'verification oracle'),evidence_refs:[...value.evidence_refs]};
|
|
75
|
+
return Object.freeze({...core,verification_sha256:sha(core)});
|
|
76
|
+
}
|
package/src/mcp-server.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { access, readFile } from 'node:fs/promises';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import os from 'node:os';
|
|
@@ -37,6 +38,7 @@ import { createNativeDesktopInputProvider } from './desktop-input-r40.mjs';
|
|
|
37
38
|
import { createNativeDesktopGrounder } from './desktop-uia-r40.mjs';
|
|
38
39
|
import { createDesktopRuntime } from './desktop-runtime-r40.mjs';
|
|
39
40
|
import { createDesktopMemoryProvider } from './desktop-memory-r40.mjs';
|
|
41
|
+
import { createR41MemoryProvider } from './memory-r41.mjs';
|
|
40
42
|
import { registerDesktopTools } from './desktop-mcp-tools-r40.mjs';
|
|
41
43
|
import { loadAutonomousPolicy } from './autonomous-policy.mjs';
|
|
42
44
|
import { createNativeDiffRuntime } from './native-diff.mjs';
|
|
@@ -61,6 +63,7 @@ import {
|
|
|
61
63
|
} from './output-contracts.mjs';
|
|
62
64
|
|
|
63
65
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
66
|
+
const packageManifestSha256=createHash('sha256').update(await readFile(path.join(packageRoot,'MANIFEST.SHA256'))).digest('hex');
|
|
64
67
|
const workspaceRoot = path.resolve(process.env.DEADBYTE_WORKSPACE_ROOT ?? path.join(packageRoot, 'workspaces'));
|
|
65
68
|
const signingKeyPath = process.env.DEADBYTE_SIGNING_KEY ?? defaultPrivateKeyPath();
|
|
66
69
|
const verifyKeyPath = process.env.DEADBYTE_VERIFY_KEY ?? defaultPublicKeyPath();
|
|
@@ -187,9 +190,19 @@ if (autonomousRuntimeMode === '1' && !autonomousPolicyPath) throw new Error('DEA
|
|
|
187
190
|
const autonomousPolicy = autonomousRuntimeMode === '1' ? await loadAutonomousPolicy(autonomousPolicyPath,codingPolicy) : null;
|
|
188
191
|
const nativeDiffRuntime = autonomousPolicy ? createNativeDiffRuntime({ policy:autonomousPolicy,codingRuntime,machineFsRuntime,signingKeyPath,verifyKeyPath }) : null;
|
|
189
192
|
semanticRuntime = codingRuntime ? createSemanticCodeRuntime({ codingRuntime, backends: semanticLsp.backends, nativeDiffRuntime }) : null;
|
|
193
|
+
const autonomousMemorySigningKey=autonomousPolicy?await loadPrivateSigningKey(signingKeyPath):null;
|
|
194
|
+
const autonomousMemoryVerifyKey=autonomousPolicy?await loadPublicVerifyKey(verifyKeyPath):null;
|
|
195
|
+
if(autonomousPolicy&&autonomousMemorySigningKey.keyId!==autonomousMemoryVerifyKey.keyId) throw new Error('R41 memory signing/verify key mismatch');
|
|
196
|
+
const memoryProvider=autonomousPolicy?createR41MemoryProvider({
|
|
197
|
+
memoryRoot:path.join(autonomousPolicy.evidence_dir,'memory-r41',packageManifestSha256),sourceReleaseManifest:packageManifestSha256,
|
|
198
|
+
journalRoot:path.join(autonomousPolicy.evidence_dir,'goals'),
|
|
199
|
+
writerVersion:DEADBYTE_VERSION,privateKey:autonomousMemorySigningKey.privateKey,
|
|
200
|
+
publicKey:autonomousMemoryVerifyKey.publicKey,keyId:autonomousMemorySigningKey.keyId,
|
|
201
|
+
byteBudget:Math.min(262144,Math.max(65536,Math.floor(autonomousPolicy.limits.max_context_bytes/2))),semantic:{enabled:false}
|
|
202
|
+
}):null;
|
|
190
203
|
const autonomousRuntime = autonomousPolicy ? createAutonomousRuntime({ policy:autonomousPolicy,codingRuntime,nativeDiffRuntime,
|
|
191
|
-
machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime,desktopMemoryProvider,signingKeyPath,verifyKeyPath }) : null;
|
|
192
|
-
const autonomousSupervisor = autonomousRuntime && autonomousPolicy.planner.backend
|
|
204
|
+
machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime,desktopMemoryProvider,memoryProvider,signingKeyPath,verifyKeyPath }) : null;
|
|
205
|
+
const autonomousSupervisor = autonomousRuntime && ['openai_compatible','openai_responses'].includes(autonomousPolicy.planner.backend) &&
|
|
193
206
|
autonomousSupervisorMode === 'in_process'
|
|
194
207
|
? startAutonomousSupervisor({ runtime: autonomousRuntime, policy: autonomousPolicy })
|
|
195
208
|
: null;
|