@aria_asi/cli 0.2.32 → 0.2.34
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/dist/aria-connector/src/connectors/codebase-awareness.d.ts +8 -1
- package/dist/aria-connector/src/connectors/codebase-awareness.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/codebase-awareness.js +126 -71
- package/dist/aria-connector/src/connectors/codebase-awareness.js.map +1 -1
- package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/codex.js +98 -0
- package/dist/aria-connector/src/connectors/codex.js.map +1 -1
- package/dist/aria-connector/src/setup-wizard.d.ts.map +1 -1
- package/dist/aria-connector/src/setup-wizard.js +91 -24
- package/dist/aria-connector/src/setup-wizard.js.map +1 -1
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +26 -8
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +60 -1
- package/dist/assets/hooks/aria-stop-gate.mjs +69 -3
- package/dist/assets/hooks/doctrine_trigger_map.json +43 -0
- package/dist/assets/hooks/lib/domain-output-quality.mjs +103 -0
- package/dist/assets/hooks/lib/skill-autoload-gate.mjs +14 -0
- package/dist/assets/opencode-plugins/harness-context/index.js +1 -1
- package/dist/assets/opencode-plugins/harness-gate/index.js +114 -10
- package/dist/assets/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +14 -0
- package/dist/assets/opencode-plugins/harness-outcome/index.js +39 -0
- package/dist/assets/opencode-plugins/harness-stop/index.js +234 -139
- package/dist/assets/opencode-plugins/harness-stop/lib/domain-output-quality.js +103 -0
- package/dist/assets/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +14 -0
- package/dist/runtime/codex-bridge.mjs +71 -8
- package/dist/runtime/discipline/CLAUDE.md +2 -2
- package/dist/runtime/discipline/doctrine_trigger_map.json +43 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md +3 -3
- package/dist/runtime/doctrine_trigger_map.json +43 -0
- package/dist/runtime/harness-daemon.mjs +50 -2
- package/dist/runtime/hooks/aria-agent-handoff.mjs +247 -0
- package/dist/runtime/hooks/aria-agent-ledger-merge.mjs +164 -0
- package/dist/runtime/hooks/aria-architect-fallback.mjs +267 -0
- package/dist/runtime/hooks/aria-cognition-substrate-binding.mjs +761 -0
- package/dist/runtime/hooks/aria-discovery-record.mjs +101 -0
- package/dist/runtime/hooks/aria-harness-via-sdk.mjs +544 -0
- package/dist/runtime/hooks/aria-import-resolution-gate.mjs +330 -0
- package/dist/runtime/hooks/aria-outcome-record.mjs +84 -0
- package/dist/runtime/hooks/aria-pre-emit-dryrun.mjs +329 -0
- package/dist/runtime/hooks/aria-pre-text-gate.mjs +112 -0
- package/dist/runtime/hooks/aria-pre-tool-gate.mjs +2482 -0
- package/dist/runtime/hooks/aria-preprompt-consult.mjs +464 -0
- package/dist/runtime/hooks/aria-preturn-memory-gate.mjs +647 -0
- package/dist/runtime/hooks/aria-repo-doctrine-gate.mjs +429 -0
- package/dist/runtime/hooks/aria-stop-gate.mjs +1882 -0
- package/dist/runtime/hooks/aria-trigger-autolearn.mjs +229 -0
- package/dist/runtime/hooks/aria-userprompt-abandon-detect.mjs +192 -0
- package/dist/runtime/hooks/doctrine_trigger_map.json +577 -0
- package/dist/runtime/hooks/lib/canonical-lenses.mjs +65 -0
- package/dist/runtime/hooks/lib/domain-output-quality.mjs +103 -0
- package/dist/runtime/hooks/lib/gate-audit.mjs +43 -0
- package/dist/runtime/hooks/lib/gate-loop-state.mjs +50 -0
- package/dist/runtime/hooks/lib/hook-message-window.mjs +121 -0
- package/dist/runtime/hooks/lib/skill-autoload-gate.mjs +14 -0
- package/dist/runtime/hooks/test-aria-preturn-memory-gate.mjs +245 -0
- package/dist/runtime/hooks/test-tier-lens-labeling.mjs +367 -0
- package/dist/runtime/manifest.json +2 -2
- package/dist/runtime/sdk/BUNDLED.json +2 -2
- package/dist/runtime/sdk/index.d.ts +48 -0
- package/dist/runtime/sdk/index.js +140 -1
- package/dist/runtime/sdk/index.js.map +1 -1
- package/dist/runtime/sdk/runWithGovernance.d.ts +16 -0
- package/dist/runtime/sdk/runWithGovernance.js +54 -0
- package/dist/runtime/sdk/runWithGovernance.js.map +1 -0
- package/dist/runtime/service.mjs +339 -10
- package/dist/sdk/BUNDLED.json +2 -2
- package/dist/sdk/index.d.ts +48 -0
- package/dist/sdk/index.js +140 -1
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/runWithGovernance.d.ts +16 -0
- package/dist/sdk/runWithGovernance.js +54 -0
- package/dist/sdk/runWithGovernance.js.map +1 -0
- package/hooks/aria-harness-via-sdk.mjs +26 -8
- package/hooks/aria-pre-tool-gate.mjs +60 -1
- package/hooks/aria-stop-gate.mjs +69 -3
- package/hooks/doctrine_trigger_map.json +43 -0
- package/hooks/lib/domain-output-quality.mjs +103 -0
- package/hooks/lib/skill-autoload-gate.mjs +14 -0
- package/opencode-plugins/harness-context/index.js +1 -1
- package/opencode-plugins/harness-gate/index.js +114 -10
- package/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +14 -0
- package/opencode-plugins/harness-outcome/index.js +39 -0
- package/opencode-plugins/harness-stop/index.js +234 -139
- package/opencode-plugins/harness-stop/lib/domain-output-quality.js +103 -0
- package/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +14 -0
- package/package.json +12 -5
- package/runtime-src/codex-bridge.mjs +71 -8
- package/runtime-src/harness-daemon.mjs +50 -2
- package/runtime-src/service.mjs +339 -10
- package/scripts/bundle-sdk.mjs +2 -0
- package/scripts/self-test-harness-gates.mjs +79 -0
- package/src/connectors/codebase-awareness.ts +141 -77
- package/src/connectors/codex.ts +98 -0
- package/src/setup-wizard.ts +105 -25
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
const RECEIPT_ROOT = process.env.ARIA_SKILL_RECEIPT_DIR || join(tmpdir(), 'aria-skill-receipts');
|
|
5
|
+
const ALIASES = new Map([['deploy', 'aria-harness-deploy'], ['output', 'aria-harness-output-discipline'], ['repo', 'aria-repo-doctrine'], ['forge', 'aria-forge-guardrails']]);
|
|
6
|
+
const RX = { deploy: /deploy-service\.sh|kubectl\s+(?:apply|set|rollout|delete|scale)|helm\s+upgrade|terraform\s+apply|docker\s+push/i, mutationTool: /^(?:edit|write|notebookedit|patch|apply_patch)$/i, mutation: /apply_patch|write file|edit file|modify|delete file|migration|handler|route|runtime|hook|plugin|\btest\b|smoke script/i, strip: /remove|delete|strip|drop|omit|disable|bypass|skip|stub|mock|fake|placeholder|temporary|quick scaffold|band-aid/i, readiness: /production-ready|ready for production|works in general|general readiness|client packages?|npm packages?|SDKs?|runtimes?|harnesses?|release-ready|ship-ready/i, narrow: /single flow|one flow|narrow e2e|covered flow|specific path|widget flow/i, completion: /done|complete|completed|ready|verified|fixed|shipped|implemented|production-ready/i, badProof: /but|except|caveat|remaining|not yet|still|separate|later|blocked|skipped|unresolved|follow-up|failed|failing|error|red|not run|could not verify|untested|no proof|missing proof|without proof/i, advisory: /non-blocking|warning only|warn only|advisory|fall through|falls through|fail open|soft fail|logged and continue|quality gate warning/i, success: /(?:verified|passed|success|successful|green|ok)\s*[:=\-].{0,120}(?:npm|node|playwright|jest|vitest|build|test|lint|typecheck|curl|kubectl|self-test|e2e|probe|smoke)|(?:npm|node|playwright|jest|vitest|build|test|lint|typecheck|curl|kubectl).{0,120}(?:passed|success|successful|green|exit\s*0)/i, resubmit: /re-?submission|resubmit/i, rewrite: /re-?write|rewrite|fix/i, retest: /re-?test|retest|rerun/i, aria: /ARIA console|Aria console|\/chat|aria-pipeline-mcp|aria_chat|escalat(?:e|ion).{0,80}ARIA/i };
|
|
7
|
+
function normalizeSkillName(skill) { return ALIASES.get(String(skill || '').trim()) || String(skill || '').trim(); }
|
|
8
|
+
function sessionDir(sessionId) { return join(RECEIPT_ROOT, encodeURIComponent(String(sessionId || 'unknown'))); }
|
|
9
|
+
function readReceiptSkills(sessionId) { const dir = sessionDir(sessionId); if (!existsSync(dir)) return new Set(); const skills = new Set(); for (const name of readdirSync(dir)) { if (!name.endsWith('.json')) continue; try { const receipt = JSON.parse(readFileSync(join(dir, name), 'utf8')); if (receipt?.skill) skills.add(normalizeSkillName(receipt.skill)); } catch {} } return skills; }
|
|
10
|
+
function readInlineSkills(text) { const skills = new Set(); const value = String(text || ''); for (const match of value.matchAll(/<skill_content\s+name=["']([^"']+)["']/gi)) skills.add(normalizeSkillName(match[1])); return skills; }
|
|
11
|
+
export function recordSkillLoaded({ sessionId, skill, surface = 'unknown', metadata = {} } = {}) { const normalized = normalizeSkillName(skill); if (!normalized) throw new Error('recordSkillLoaded requires a skill name'); const dir = sessionDir(sessionId); mkdirSync(dir, { recursive: true }); const receipt = { skill: normalized, surface, metadata, recordedAt: new Date().toISOString() }; writeFileSync(join(dir, `${encodeURIComponent(normalized)}.json`), `${JSON.stringify(receipt, null, 2)}\n`); return receipt; }
|
|
12
|
+
export function classifyRequiredSkills({ text = '', action = '', toolName = '', filePath = '', isDeploy = false, isMutation = false, isOutputCloseout = false } = {}) { const combined = [text, action, toolName, filePath].filter(Boolean).join('\n'); const required = new Set(); const reasons = []; const recoveryMissing = []; if (isDeploy || RX.deploy.test(combined)) { required.add('aria-harness-deploy'); required.add('aria-forge-guardrails'); reasons.push('deploy/shared-infrastructure action requires fail-closed deploy and forge guardrails'); } if (isMutation || RX.mutationTool.test(toolName)) { required.add('aria-repo-doctrine'); reasons.push('repository/runtime mutation requires repo doctrine'); } if (RX.strip.test(combined)) { required.add('aria-harness-no-stripping'); reasons.push('strip/remove/bypass language requires no-stripping gate'); } if (isOutputCloseout && RX.completion.test(combined)) { required.add('aria-harness-output-discipline'); reasons.push('owner-facing completion/readiness claim requires output discipline'); if (!RX.success.test(combined)) recoveryMissing.push('successful proof from a concrete command/probe'); } if (RX.readiness.test(combined)) { required.add('architecture-decision'); required.add('testing-strategy'); required.add('aria-forge-guardrails'); required.add('aria-harness-output-discipline'); reasons.push('broad production/package/SDK/runtime readiness claim requires architecture, testing, and forge guardrails'); } if (RX.readiness.test(combined) && RX.narrow.test(combined)) { required.add('testing-strategy'); required.add('aria-forge-guardrails'); reasons.push('narrow e2e proof cannot support broad readiness claim without readiness-matrix discipline'); } if (RX.completion.test(combined) && RX.badProof.test(combined)) { required.add('aria-harness-output-discipline'); required.add('aria-forge-guardrails'); reasons.push('completion claim with unresolved or failed proof requires recovery cycle'); if (!RX.resubmit.test(combined)) recoveryMissing.push('re-submission'); if (!RX.rewrite.test(combined)) recoveryMissing.push('re-write'); if (!RX.retest.test(combined)) recoveryMissing.push('re-test'); if (!RX.aria.test(combined)) recoveryMissing.push('ARIA console escalation'); } if (RX.advisory.test(combined)) { required.add('aria-forge-guardrails'); required.add('aria-harness-output-discipline'); reasons.push('advisory/fail-open gate language requires fail-closed hardening discipline'); } return { requiredSkills: [...required].sort(), reasons, recoveryMissing }; }
|
|
13
|
+
export function evaluateSkillGate(options = {}) { const classified = classifyRequiredSkills(options); const text = [options.text, options.action].filter(Boolean).join('\n'); const loaded = new Set([...readReceiptSkills(options.sessionId), ...readInlineSkills(text)]); const missingSkills = classified.requiredSkills.filter((skill) => !loaded.has(skill)); const recoveryMissing = classified.recoveryMissing || []; return { ok: missingSkills.length === 0 && recoveryMissing.length === 0, surface: options.surface || 'unknown', sessionId: options.sessionId || 'unknown', requiredSkills: classified.requiredSkills, loadedSkills: [...loaded].sort(), missingSkills, recoveryMissing, reasons: classified.reasons, autoLoadAvailable: options.autoLoadAvailable === true }; }
|
|
14
|
+
export function formatSkillGateBlock(result = {}) { const missing = Array.isArray(result.missingSkills) ? result.missingSkills : []; const recovery = Array.isArray(result.recoveryMissing) ? result.recoveryMissing : []; const reasons = Array.isArray(result.reasons) ? result.reasons : []; return ['=== ARIA SKILL AUTOLOAD GATE BLOCK ===', `surface: ${result.surface || 'unknown'}`, `missing_skills: ${missing.length ? missing.join(', ') : '(none)'}`, `missing_recovery_cycle: ${recovery.length ? recovery.join(', ') : '(none)'}`, `required_skills: ${(result.requiredSkills || []).join(', ') || '(none)'}`, reasons.length ? `reasons: ${reasons.join(' | ')}` : 'reasons: no classifier reason recorded', 'counter_action: re-submit, re-write, re-test, and escalate through ARIA console until successful proof exists. Do not downgrade this to an advisory warning.'].join('\n'); }
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aria_asi/cli",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Aria Smart CLI
|
|
3
|
+
"version": "0.2.34",
|
|
4
|
+
"description": "Aria Smart CLI — the world's first harness-powered terminal companion",
|
|
5
5
|
"bin": {
|
|
6
|
-
"aria": "
|
|
6
|
+
"aria": "bin/aria.js"
|
|
7
7
|
},
|
|
8
8
|
"main": "./dist/aria-connector/src/index.js",
|
|
9
9
|
"types": "./dist/aria-connector/src/index.d.ts",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsc && node scripts/bundle-sdk.mjs",
|
|
21
|
-
"check:hooks": "node scripts/validate-hook-contracts.mjs",
|
|
21
|
+
"check:hooks": "node scripts/validate-hook-contracts.mjs && node scripts/self-test-harness-gates.mjs",
|
|
22
|
+
"self-test:gates": "node scripts/self-test-harness-gates.mjs",
|
|
22
23
|
"check:skills": "node scripts/validate-skill-prompts.mjs",
|
|
23
24
|
"prepare": "npm run build",
|
|
24
25
|
"dev": "tsc --watch",
|
|
@@ -50,5 +51,11 @@
|
|
|
50
51
|
"node": ">=20.0.0"
|
|
51
52
|
},
|
|
52
53
|
"license": "UNLICENSED",
|
|
53
|
-
"private": false
|
|
54
|
+
"private": false,
|
|
55
|
+
"keywords": [],
|
|
56
|
+
"author": "",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/REI-Nationwide/cowork-sandbox/issues"
|
|
59
|
+
},
|
|
60
|
+
"homepage": "https://github.com/REI-Nationwide/cowork-sandbox#readme"
|
|
54
61
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
5
|
import { homedir } from 'node:os';
|
|
5
6
|
import { delimiter, dirname, resolve } from 'node:path';
|
|
6
7
|
import { spawn, spawnSync } from 'node:child_process';
|
|
7
8
|
import { createRequire } from 'node:module';
|
|
9
|
+
import { evaluateSkillGate, formatSkillGateBlock } from './hooks/lib/skill-autoload-gate.mjs';
|
|
8
10
|
|
|
9
11
|
const require = createRequire(import.meta.url);
|
|
10
12
|
const { WebSocketServer, WebSocket } = require('ws');
|
|
@@ -75,6 +77,24 @@ function sleep(ms) {
|
|
|
75
77
|
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
function stableEvidenceString(value) {
|
|
81
|
+
if (typeof value === 'string') return value;
|
|
82
|
+
try { return JSON.stringify(value); } catch { return String(value); }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function makeEvidenceRef(kind, value, metadata = {}) {
|
|
86
|
+
const raw = stableEvidenceString(value);
|
|
87
|
+
const sha256 = createHash('sha256').update(raw).digest('hex');
|
|
88
|
+
return {
|
|
89
|
+
evidenceId: `ev_${sha256.slice(0, 16)}`,
|
|
90
|
+
kind,
|
|
91
|
+
at: new Date().toISOString(),
|
|
92
|
+
sha256,
|
|
93
|
+
preview: raw.slice(0, 500),
|
|
94
|
+
metadata,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
78
98
|
function readRuntimeToken() {
|
|
79
99
|
const envToken = process.env.ARIA_HARNESS_TOKEN || process.env.ARIA_API_KEY || process.env.OPENAI_API_KEY || process.env.ARIA_MASTER_TOKEN;
|
|
80
100
|
if (envToken) return envToken;
|
|
@@ -123,9 +143,10 @@ function ensureTurnState(threadId, turnId) {
|
|
|
123
143
|
userText: '',
|
|
124
144
|
preReceiptId: null,
|
|
125
145
|
agentText: '',
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
146
|
+
bufferedAgentNotifications: [],
|
|
147
|
+
firstAgentItemId: null,
|
|
148
|
+
traceId: `trace_${randomUUID()}`,
|
|
149
|
+
};
|
|
129
150
|
turnState.set(key, state);
|
|
130
151
|
}
|
|
131
152
|
return state;
|
|
@@ -186,6 +207,16 @@ async function validateTurnText(threadId, turnId) {
|
|
|
186
207
|
if (!text) {
|
|
187
208
|
return { ok: false, reason: 'No assistant text exists for this turn yet. Codex must emit readable cognition before action.' };
|
|
188
209
|
}
|
|
210
|
+
const skillGate = evaluateSkillGate({
|
|
211
|
+
sessionId: `codex:${threadId}:${turnId}`,
|
|
212
|
+
surface: 'codex-bridge-output',
|
|
213
|
+
text: [state.userText, text].join('\n'),
|
|
214
|
+
isOutputCloseout: true,
|
|
215
|
+
autoLoadAvailable: false,
|
|
216
|
+
});
|
|
217
|
+
if (!skillGate.ok) {
|
|
218
|
+
return { ok: false, reason: formatSkillGateBlock(skillGate), result: { skillGate } };
|
|
219
|
+
}
|
|
189
220
|
const result = await postRuntime('/validate-output', {
|
|
190
221
|
text,
|
|
191
222
|
sessionId: `codex:${threadId}:${turnId}`,
|
|
@@ -198,6 +229,7 @@ async function validateTurnText(threadId, turnId) {
|
|
|
198
229
|
stage: 'codex-turn',
|
|
199
230
|
actor: 'codex-bridge',
|
|
200
231
|
system: 'codex-bridge',
|
|
232
|
+
traceId: state.traceId,
|
|
201
233
|
},
|
|
202
234
|
});
|
|
203
235
|
const pass = result?.pass === true && result?.validation?.passed !== false;
|
|
@@ -210,11 +242,29 @@ async function validateTurnText(threadId, turnId) {
|
|
|
210
242
|
};
|
|
211
243
|
}
|
|
212
244
|
|
|
213
|
-
async function checkActionAgainstRuntime(action, target, threadId, turnId) {
|
|
245
|
+
async function checkActionAgainstRuntime(action, target, threadId, turnId, metadata = {}) {
|
|
246
|
+
const state = ensureTurnState(threadId, turnId);
|
|
247
|
+
const skillGate = evaluateSkillGate({
|
|
248
|
+
sessionId: `codex:${threadId}:${turnId}`,
|
|
249
|
+
surface: 'codex-bridge-action',
|
|
250
|
+
text: target,
|
|
251
|
+
action: target,
|
|
252
|
+
toolName: action,
|
|
253
|
+
isDeploy: action === 'deploy',
|
|
254
|
+
isMutation: action === 'write',
|
|
255
|
+
autoLoadAvailable: false,
|
|
256
|
+
});
|
|
257
|
+
if (!skillGate.ok) {
|
|
258
|
+
return { ok: false, reason: formatSkillGateBlock(skillGate), result: { skillGate } };
|
|
259
|
+
}
|
|
214
260
|
const result = await postRuntime('/check-action', {
|
|
215
261
|
action,
|
|
216
262
|
target,
|
|
217
263
|
sessionId: `codex:${threadId}:${turnId}`,
|
|
264
|
+
actor: 'codex',
|
|
265
|
+
roleProfile: process.env.CODEX_ARIA_ROLE_PROFILE || process.env.ARIA_ROLE_PROFILE || null,
|
|
266
|
+
verifyText: state.agentText || '',
|
|
267
|
+
...metadata,
|
|
218
268
|
});
|
|
219
269
|
if (result?.allowed === false) {
|
|
220
270
|
return {
|
|
@@ -244,6 +294,8 @@ async function recordMizanPre(threadId, turnId) {
|
|
|
244
294
|
surface: 'codex-bridge',
|
|
245
295
|
platform: 'codex',
|
|
246
296
|
userText: state.userText,
|
|
297
|
+
traceId: state.traceId,
|
|
298
|
+
evidenceRefs: [makeEvidenceRef('user_input', state.userText, { threadId, turnId, traceId: state.traceId })],
|
|
247
299
|
},
|
|
248
300
|
});
|
|
249
301
|
state.preReceiptId = result?.receipt?.receiptId || null;
|
|
@@ -262,6 +314,8 @@ async function recordMizanPost(threadId, turnId, pass, summary) {
|
|
|
262
314
|
evidence: {
|
|
263
315
|
validated_output: pass,
|
|
264
316
|
bridge: 'codex',
|
|
317
|
+
trace_id: state.traceId,
|
|
318
|
+
output_ref: makeEvidenceRef('assistant_output', state.agentText || summary, { threadId, turnId, traceId: state.traceId }),
|
|
265
319
|
},
|
|
266
320
|
context: {
|
|
267
321
|
sessionId: `codex:${threadId}:${turnId}`,
|
|
@@ -269,6 +323,7 @@ async function recordMizanPost(threadId, turnId, pass, summary) {
|
|
|
269
323
|
platform: 'codex',
|
|
270
324
|
userText: state.userText,
|
|
271
325
|
summary,
|
|
326
|
+
traceId: state.traceId,
|
|
272
327
|
},
|
|
273
328
|
});
|
|
274
329
|
} catch (error) {
|
|
@@ -285,6 +340,8 @@ async function recordMizanPost(threadId, turnId, pass, summary) {
|
|
|
285
340
|
details: {
|
|
286
341
|
threadId,
|
|
287
342
|
turnId,
|
|
343
|
+
traceId: state.traceId,
|
|
344
|
+
outputRef: makeEvidenceRef('assistant_output', state.agentText || summary, { threadId, turnId, traceId: state.traceId }),
|
|
288
345
|
},
|
|
289
346
|
});
|
|
290
347
|
} catch (error) {
|
|
@@ -336,7 +393,9 @@ async function handleApprovalRequest(upstream, downstream, message) {
|
|
|
336
393
|
cwd: params.cwd || null,
|
|
337
394
|
commandActions: params.commandActions || params.parsedCmd || null,
|
|
338
395
|
}).slice(0, 1500);
|
|
339
|
-
const actionCheck = await checkActionAgainstRuntime(action, target, threadId, turnId
|
|
396
|
+
const actionCheck = await checkActionAgainstRuntime(action, target, threadId, turnId, {
|
|
397
|
+
requireVerify: action === 'delete' || action === 'deploy',
|
|
398
|
+
});
|
|
340
399
|
if (!actionCheck.ok) {
|
|
341
400
|
const reason = `Aria runtime denied ${action}: ${actionCheck.reason}`;
|
|
342
401
|
upstream.send(JSON.stringify(makeGuardianWarning(threadId, reason)));
|
|
@@ -360,8 +419,9 @@ async function handleApprovalRequest(upstream, downstream, message) {
|
|
|
360
419
|
}
|
|
361
420
|
|
|
362
421
|
if (method === 'item/fileChange/requestApproval' || method === 'applyPatchApproval') {
|
|
363
|
-
const target = params.grantRoot || params.reason || params.itemId || 'file-change';
|
|
364
|
-
const
|
|
422
|
+
const target = params.grantRoot || params.path || params.filePath || params.reason || params.itemId || 'file-change';
|
|
423
|
+
const files = [params.path, params.filePath, params.grantRoot].filter((value) => typeof value === 'string' && value.trim());
|
|
424
|
+
const actionCheck = await checkActionAgainstRuntime('write', String(target), threadId, turnId, { files });
|
|
365
425
|
if (!actionCheck.ok) {
|
|
366
426
|
const reason = `Aria runtime denied file change: ${actionCheck.reason}`;
|
|
367
427
|
upstream.send(JSON.stringify(makeGuardianWarning(threadId, reason)));
|
|
@@ -390,7 +450,10 @@ async function handleApprovalRequest(upstream, downstream, message) {
|
|
|
390
450
|
permissions: params.permissions || null,
|
|
391
451
|
reason: params.reason || null,
|
|
392
452
|
}).slice(0, 1500);
|
|
393
|
-
const
|
|
453
|
+
const files = Array.isArray(params.permissions?.fileSystem?.entries)
|
|
454
|
+
? params.permissions.fileSystem.entries.filter((value) => typeof value === 'string' && value.trim())
|
|
455
|
+
: [];
|
|
456
|
+
const actionCheck = await checkActionAgainstRuntime('write', target, threadId, turnId, { files });
|
|
394
457
|
if (!actionCheck.ok) {
|
|
395
458
|
const reason = `Aria runtime denied permissions request: ${actionCheck.reason}`;
|
|
396
459
|
upstream.send(JSON.stringify(makeGuardianWarning(threadId, reason)));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { createServer } from 'node:http';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
4
5
|
import { createRequire } from 'node:module';
|
|
5
6
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
7
|
import { fileURLToPath } from 'node:url';
|
|
@@ -35,6 +36,7 @@ const LOCAL_FIRST_PRINCIPLE = 'Truth over deception. No harm. Sacred trust. Powe
|
|
|
35
36
|
|
|
36
37
|
let cachedPacketEnvelope = loadCachedPacketEnvelope();
|
|
37
38
|
let refreshInFlight = null;
|
|
39
|
+
let refreshInFlightKey = '';
|
|
38
40
|
let lastRefreshError = null;
|
|
39
41
|
let lastRefreshStartedAt = null;
|
|
40
42
|
let lastRefreshCompletedAt = cachedPacketEnvelope?.timestamp || null;
|
|
@@ -99,6 +101,46 @@ function sanitizePacketEnvelope(raw) {
|
|
|
99
101
|
};
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
function stableIdentityValue(value) {
|
|
105
|
+
return String(value || '').trim().toLowerCase();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function requestCacheKey(body = {}, apiKey = '') {
|
|
109
|
+
const identity = {
|
|
110
|
+
stage: stableIdentityValue(body.stage || body.packetRequest?.stage),
|
|
111
|
+
actor: stableIdentityValue(body.actor || body.packetRequest?.actor),
|
|
112
|
+
system: stableIdentityValue(body.system || body.packetRequest?.system),
|
|
113
|
+
platform: stableIdentityValue(body.platform || body.packetRequest?.platform),
|
|
114
|
+
roleProfile: stableIdentityValue(body.roleProfile || body.role_profile || body.packetRequest?.roleProfile || body.packetRequest?.role_profile),
|
|
115
|
+
token: apiKey ? createHash('sha256').update(apiKey).digest('hex').slice(0, 16) : '',
|
|
116
|
+
};
|
|
117
|
+
return createHash('sha256').update(JSON.stringify(identity)).digest('hex');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function extractPacketField(packet, field) {
|
|
121
|
+
if (!packet || typeof packet !== 'object') return '';
|
|
122
|
+
const direct = packet[field];
|
|
123
|
+
if (typeof direct === 'string' && direct.trim()) return stableIdentityValue(direct);
|
|
124
|
+
for (const source of [packet.adapter, packet.harness]) {
|
|
125
|
+
if (typeof source !== 'string') continue;
|
|
126
|
+
const match = source.match(new RegExp(`(?:^|\\n)\\s*${field}\\s*=\\s*([^\\n\\s]+)`, 'i'));
|
|
127
|
+
if (match?.[1]) return stableIdentityValue(match[1]);
|
|
128
|
+
}
|
|
129
|
+
return '';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function cachedPacketMatchesRequest(envelope, body = {}) {
|
|
133
|
+
const packet = envelope?.packet;
|
|
134
|
+
if (!packet || typeof packet !== 'object') return false;
|
|
135
|
+
for (const field of ['stage', 'actor', 'system']) {
|
|
136
|
+
const expected = stableIdentityValue(body[field] || body.packetRequest?.[field]);
|
|
137
|
+
if (!expected) continue;
|
|
138
|
+
const actual = extractPacketField(packet, field);
|
|
139
|
+
if (actual && actual !== expected) return false;
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
102
144
|
function loadCachedPacketEnvelope() {
|
|
103
145
|
const candidates = [LOCAL_PACKET_CACHE_PATH, ...LEGACY_PACKET_CACHE_CANDIDATES];
|
|
104
146
|
for (const candidate of candidates) {
|
|
@@ -220,10 +262,15 @@ async function fetchJsonWithRetry(url, init, attempts = 3) {
|
|
|
220
262
|
}
|
|
221
263
|
|
|
222
264
|
async function refreshPacket(body = {}, apiKey = '') {
|
|
223
|
-
|
|
265
|
+
const cacheKey = requestCacheKey(body, apiKey);
|
|
266
|
+
if (refreshInFlight && refreshInFlightKey === cacheKey) return refreshInFlight;
|
|
267
|
+
if (refreshInFlight) {
|
|
268
|
+
await refreshInFlight.catch(() => {});
|
|
269
|
+
}
|
|
224
270
|
if (isLoopedUpstream(UPSTREAM_HARNESS_URL)) {
|
|
225
271
|
throw new Error(`upstream harness URL loops back to local daemon: ${UPSTREAM_HARNESS_URL}`);
|
|
226
272
|
}
|
|
273
|
+
refreshInFlightKey = cacheKey;
|
|
227
274
|
lastRefreshStartedAt = new Date().toISOString();
|
|
228
275
|
lastRefreshError = null;
|
|
229
276
|
refreshInFlight = (async () => {
|
|
@@ -258,6 +305,7 @@ async function refreshPacket(body = {}, apiKey = '') {
|
|
|
258
305
|
throw error;
|
|
259
306
|
} finally {
|
|
260
307
|
refreshInFlight = null;
|
|
308
|
+
refreshInFlightKey = '';
|
|
261
309
|
}
|
|
262
310
|
}
|
|
263
311
|
|
|
@@ -268,7 +316,7 @@ function queuePacketRefresh(body, apiKey) {
|
|
|
268
316
|
}
|
|
269
317
|
|
|
270
318
|
async function resolvePacketEnvelope(packetRequest = {}, apiKey = '', message = '') {
|
|
271
|
-
if (cachedPacketEnvelope) {
|
|
319
|
+
if (cachedPacketEnvelope && cachedPacketMatchesRequest(cachedPacketEnvelope, packetRequest)) {
|
|
272
320
|
queuePacketRefresh(packetRequest, apiKey);
|
|
273
321
|
return cachedPacketEnvelope;
|
|
274
322
|
}
|