@aria_asi/cli 0.2.33 → 0.2.35
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/codex.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/codex.js +60 -5
- package/dist/aria-connector/src/connectors/codex.js.map +1 -1
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +16 -3
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +41 -1
- package/dist/assets/hooks/aria-stop-gate.mjs +42 -1
- package/dist/assets/hooks/doctrine_trigger_map.json +43 -0
- package/dist/assets/hooks/lib/skill-autoload-gate.mjs +14 -1
- package/dist/assets/opencode-plugins/harness-context/index.js +1 -1
- package/dist/assets/opencode-plugins/harness-gate/index.js +49 -9
- package/dist/assets/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +14 -1
- package/dist/assets/opencode-plugins/harness-stop/index.js +201 -166
- package/dist/assets/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +14 -1
- package/dist/runtime/codex-bridge.mjs +1 -1
- 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/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 +39 -0
- package/dist/runtime/sdk/index.js +117 -0
- 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/sdk/BUNDLED.json +2 -2
- package/dist/sdk/index.d.ts +39 -0
- package/dist/sdk/index.js +117 -0
- 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 +16 -3
- package/hooks/aria-pre-tool-gate.mjs +41 -1
- package/hooks/aria-stop-gate.mjs +42 -1
- package/hooks/doctrine_trigger_map.json +43 -0
- package/hooks/lib/skill-autoload-gate.mjs +14 -1
- package/opencode-plugins/harness-context/index.js +1 -1
- package/opencode-plugins/harness-gate/index.js +49 -9
- package/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +14 -1
- package/opencode-plugins/harness-stop/index.js +201 -166
- package/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +14 -1
- package/package.json +12 -5
- package/runtime-src/codex-bridge.mjs +1 -1
- package/scripts/bundle-sdk.mjs +2 -0
- package/scripts/self-test-harness-gates.mjs +79 -0
- package/src/connectors/codex.ts +60 -5
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
7
8
|
import { homedir } from 'node:os';
|
|
8
9
|
import { join } from 'node:path';
|
|
9
10
|
import { evaluateSkillGate, formatSkillGateBlock } from './lib/skill-autoload-gate.js';
|
|
@@ -16,6 +17,7 @@ const SDK_CANDIDATES = [
|
|
|
16
17
|
];
|
|
17
18
|
const OWNER_TOKEN_PATH = join(HOME, '.aria', 'owner-token');
|
|
18
19
|
const LICENSE_PATH = join(HOME, '.aria', 'license.json');
|
|
20
|
+
const GOVERNANCE_GATE_PATH = join(HOME, '.aria', 'bin', 'aria-governance-gate');
|
|
19
21
|
const RUNTIME_URL = (process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319').replace(/\/+$/, '');
|
|
20
22
|
const RECEIPT_DIR = join(HOME, '.opencode', 'aria-mizan-receipts');
|
|
21
23
|
|
|
@@ -94,6 +96,25 @@ function countInlineCognitionLenses(text) {
|
|
|
94
96
|
return seen.size;
|
|
95
97
|
}
|
|
96
98
|
|
|
99
|
+
function runUniversalGovernanceGate(payload) {
|
|
100
|
+
if (!existsSync(GOVERNANCE_GATE_PATH)) return null;
|
|
101
|
+
const child = spawnSync(GOVERNANCE_GATE_PATH, {
|
|
102
|
+
input: `${JSON.stringify(payload)}\n`,
|
|
103
|
+
encoding: 'utf8',
|
|
104
|
+
maxBuffer: 1024 * 1024,
|
|
105
|
+
});
|
|
106
|
+
const stdout = String(child.stdout || '').trim();
|
|
107
|
+
let result = null;
|
|
108
|
+
try { result = stdout ? JSON.parse(stdout) : null; } catch {}
|
|
109
|
+
if (child.status !== 0 || result?.ok === false || result?.decision === 'block') {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`=== ARIA UNIVERSAL GOVERNANCE GATE BLOCK ===\n\n` +
|
|
112
|
+
`${stdout || child.stderr || 'aria-governance-gate blocked this action.'}`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
97
118
|
function resolveToken() {
|
|
98
119
|
if (process.env.ARIA_HARNESS_TOKEN) return process.env.ARIA_HARNESS_TOKEN;
|
|
99
120
|
if (process.env.ARIA_API_KEY) return process.env.ARIA_API_KEY;
|
|
@@ -184,28 +205,48 @@ export default async function HarnessGatePlugin(ctx) {
|
|
|
184
205
|
|
|
185
206
|
return {
|
|
186
207
|
'tool.execute.before': async (input, output) => {
|
|
187
|
-
const
|
|
208
|
+
const args = output?.args ?? input.args ?? {};
|
|
209
|
+
const rawToolName = String(input.tool || input.name || input.type || '');
|
|
210
|
+
const normalizedToolName = rawToolName.toLowerCase();
|
|
211
|
+
const toolName = normalizedToolName.includes('bash') ? 'Bash'
|
|
212
|
+
: normalizedToolName.includes('edit') ? 'Edit'
|
|
213
|
+
: normalizedToolName.includes('write') ? 'Write'
|
|
214
|
+
: rawToolName;
|
|
188
215
|
if (!['Bash', 'Edit', 'Write', 'NotebookEdit'].includes(toolName)) return;
|
|
189
216
|
|
|
190
|
-
const cmd = toolName === 'Bash' ? String(
|
|
191
|
-
const filePath = toolName !== 'Bash' ? String(
|
|
217
|
+
const cmd = toolName === 'Bash' ? String(args?.command ?? '') : '';
|
|
218
|
+
const filePath = toolName !== 'Bash' ? String(args?.file_path ?? args?.filePath ?? args?.notebook_path ?? args?.notebookPath ?? '') : '';
|
|
192
219
|
const cmdPreview = toolName === 'Bash' ? cmd.slice(0, 80) : `${toolName} ${filePath || '(no path)'}`.slice(0, 80);
|
|
193
220
|
|
|
194
|
-
// Trivial reads pass
|
|
195
|
-
if (toolName === 'Bash' && TRIVIAL_BASH_RX.test(cmd) && cmd.length < 200) return;
|
|
196
|
-
if (toolName === 'Bash' && cmd.length < SHORT_BASH_LIMIT) return;
|
|
197
221
|
const destructive = DESTRUCTIVE_PATTERNS.find(({ rx }) => rx.test(cmd));
|
|
198
222
|
const deploy = DEPLOY_PATTERNS.find(({ rx }) => rx.test(cmd));
|
|
199
223
|
const isFileMutation = ['Edit', 'Write', 'NotebookEdit'].includes(toolName) && filePath;
|
|
200
224
|
|
|
225
|
+
// Trivial reads pass only after high-risk patterns are classified.
|
|
226
|
+
if (!destructive && !deploy && toolName === 'Bash' && TRIVIAL_BASH_RX.test(cmd) && cmd.length < 200) return;
|
|
227
|
+
if (!destructive && !deploy && toolName === 'Bash' && cmd.length < SHORT_BASH_LIMIT) return;
|
|
228
|
+
|
|
201
229
|
if (!destructive && !deploy && !isFileMutation) return;
|
|
202
230
|
|
|
203
231
|
// Try SDK checkAction() first — substrate-backed validation
|
|
204
232
|
const client = await getClient();
|
|
205
|
-
const sessionId = process.env.ARIA_SESSION_ID || 'opencode';
|
|
233
|
+
const sessionId = input.sessionID || process.env.ARIA_SESSION_ID || process.env.OPENCODE_SESSION_ID || 'opencode';
|
|
206
234
|
const label = destructive?.name || deploy?.name || `${toolName}:${filePath?.split('/').pop() || ''}`;
|
|
207
235
|
const action = toolName === 'Bash' ? 'bash' : 'edit';
|
|
208
236
|
const target = toolName === 'Bash' ? cmd.slice(0, 200) : filePath.slice(0, 200);
|
|
237
|
+
const actionRef = makeEvidenceRef('opencode_tool_request', { toolName, action, target }, { sessionId, label });
|
|
238
|
+
runUniversalGovernanceGate({
|
|
239
|
+
sessionId,
|
|
240
|
+
sourceRuntime: 'opencode',
|
|
241
|
+
surface: 'opencode-harness-gate',
|
|
242
|
+
text: JSON.stringify({ toolName, action, target, args }).slice(0, 8000),
|
|
243
|
+
action: cmd,
|
|
244
|
+
toolName,
|
|
245
|
+
filePath,
|
|
246
|
+
isDeploy: Boolean(deploy),
|
|
247
|
+
isMutation: Boolean(isFileMutation),
|
|
248
|
+
evidence: actionRef,
|
|
249
|
+
});
|
|
209
250
|
const skillGate = evaluateSkillGate({
|
|
210
251
|
sessionId,
|
|
211
252
|
surface: 'opencode-harness-gate',
|
|
@@ -224,7 +265,6 @@ export default async function HarnessGatePlugin(ctx) {
|
|
|
224
265
|
`Required next step: call the skill loader for ${skillGate.missingSkills.join(', ')} before retrying this tool.`
|
|
225
266
|
);
|
|
226
267
|
}
|
|
227
|
-
const actionRef = makeEvidenceRef('opencode_tool_request', { toolName, action, target }, { sessionId, label });
|
|
228
268
|
const rationale =
|
|
229
269
|
destructive ? `High-risk action ${label} requested in OpenCode and must satisfy Mizan truth, protection, and quality before execution.`
|
|
230
270
|
: deploy ? `Deployment action ${label} requested in OpenCode and must satisfy Mizan survivability before execution.`
|
|
@@ -299,7 +339,7 @@ export default async function HarnessGatePlugin(ctx) {
|
|
|
299
339
|
roleProfile: process.env.OPENCODE_ARIA_ROLE_PROFILE || process.env.ARIA_ROLE_PROFILE || null,
|
|
300
340
|
files: filePath ? [filePath] : [],
|
|
301
341
|
requireVerify: Boolean(destructive || deploy),
|
|
302
|
-
verifyText: String(
|
|
342
|
+
verifyText: String(args?.verifyText || args?.verifyBlock || args?.verify || ''),
|
|
303
343
|
}).catch(() => client.checkAction(action, target));
|
|
304
344
|
if (!check.allowed) {
|
|
305
345
|
if (check.queued) {
|
|
@@ -1 +1,14 @@
|
|
|
1
|
-
|
|
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'); }
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
7
8
|
import { homedir } from 'node:os';
|
|
8
9
|
import { join } from 'node:path';
|
|
9
10
|
import { analyzeDomainOutputQuality, extractCodeBlocks } from './lib/domain-output-quality.js';
|
|
@@ -17,6 +18,7 @@ const SDK_CANDIDATES = [
|
|
|
17
18
|
];
|
|
18
19
|
const OWNER_TOKEN_PATH = join(HOME, '.aria', 'owner-token');
|
|
19
20
|
const LICENSE_PATH = join(HOME, '.aria', 'license.json');
|
|
21
|
+
const GOVERNANCE_GATE_PATH = join(HOME, '.aria', 'bin', 'aria-governance-gate');
|
|
20
22
|
const RUNTIME_URL = (process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319').replace(/\/+$/, '');
|
|
21
23
|
const RECEIPT_DIR = join(HOME, '.opencode', 'aria-mizan-receipts');
|
|
22
24
|
|
|
@@ -49,6 +51,25 @@ function isGateBlock(error) {
|
|
|
49
51
|
return BLOCK_PREFIX_RX.test(String(error?.message || error || ''));
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
function runUniversalGovernanceGate(payload) {
|
|
55
|
+
if (!existsSync(GOVERNANCE_GATE_PATH)) return null;
|
|
56
|
+
const child = spawnSync(GOVERNANCE_GATE_PATH, {
|
|
57
|
+
input: `${JSON.stringify(payload)}\n`,
|
|
58
|
+
encoding: 'utf8',
|
|
59
|
+
maxBuffer: 1024 * 1024,
|
|
60
|
+
});
|
|
61
|
+
const stdout = String(child.stdout || '').trim();
|
|
62
|
+
let result = null;
|
|
63
|
+
try { result = stdout ? JSON.parse(stdout) : null; } catch {}
|
|
64
|
+
if (child.status !== 0 || result?.ok === false || result?.decision === 'block') {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`=== ARIA UNIVERSAL GOVERNANCE GATE BLOCK ===\n\n` +
|
|
67
|
+
`${stdout || child.stderr || 'aria-governance-gate blocked this output.'}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
52
73
|
let _client = null;
|
|
53
74
|
let _clientError = null;
|
|
54
75
|
let _lastMizanReceipt = null;
|
|
@@ -233,192 +254,206 @@ function detectCognitionLenses(text) {
|
|
|
233
254
|
export default async function HarnessStopPlugin(ctx) {
|
|
234
255
|
process.stderr.write('[harness-stop] Active — SDK-backed text-emission gate\n');
|
|
235
256
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
},
|
|
240
|
-
|
|
241
|
-
event: async ({ event }) => {
|
|
242
|
-
if (event.type !== 'message.updated') return;
|
|
243
|
-
const parts = event.properties?.parts || [];
|
|
244
|
-
const text = parts
|
|
245
|
-
.filter(p => p.type === 'text')
|
|
246
|
-
.map(p => p.text || '')
|
|
247
|
-
.join('\n');
|
|
257
|
+
async function validateText(text, eventContext = {}) {
|
|
258
|
+
if (!text || text.length < NON_TRIVIAL_MIN_CHARS) return;
|
|
259
|
+
if (TRIVIAL_ACK_RX.test(text.trim())) return;
|
|
248
260
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
261
|
+
const cog = detectCognitionLenses(text);
|
|
262
|
+
const sessionId = eventContext.sessionID || process.env.ARIA_SESSION_ID || process.env.OPENCODE_SESSION_ID || 'opencode';
|
|
263
|
+
runUniversalGovernanceGate({
|
|
264
|
+
sessionId,
|
|
265
|
+
sourceRuntime: 'opencode',
|
|
266
|
+
surface: 'opencode-harness-stop',
|
|
267
|
+
text,
|
|
268
|
+
isOutputCloseout: true,
|
|
269
|
+
evidence: makeEvidenceRef('opencode_assistant_output_candidate', text.slice(0, 8000), { sessionId }),
|
|
270
|
+
});
|
|
271
|
+
const skillGate = evaluateSkillGate({
|
|
272
|
+
sessionId,
|
|
273
|
+
surface: 'opencode-harness-stop',
|
|
274
|
+
text: JSON.stringify(eventContext || {}) + '\n' + text,
|
|
275
|
+
isOutputCloseout: true,
|
|
276
|
+
autoLoadAvailable: false,
|
|
277
|
+
});
|
|
278
|
+
if (!skillGate.ok) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
formatBlockReason(
|
|
281
|
+
'=== ARIA SKILL AUTOLOAD GATE BLOCK ===',
|
|
282
|
+
`${formatSkillGateBlock(skillGate)}\nRequired next step: load ${skillGate.missingSkills.join(', ')} before re-emitting this output.`,
|
|
283
|
+
)
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
const outputRef = makeEvidenceRef('opencode_assistant_output', text.slice(0, 8000), { sessionId });
|
|
287
|
+
try {
|
|
288
|
+
const mizan = await runtimeMizanPost(text.slice(0, 8000), sessionId, {
|
|
289
|
+
message: text.slice(0, 1000),
|
|
290
|
+
plannedApproach: 'OpenCode stop gate output review',
|
|
291
|
+
outputRef,
|
|
292
|
+
}, {
|
|
293
|
+
output_ref: outputRef,
|
|
294
|
+
cognition_lens_count: cog.count,
|
|
260
295
|
});
|
|
261
|
-
|
|
296
|
+
_lastMizanReceipt = mizan.receipt || _lastMizanReceipt;
|
|
297
|
+
if (_lastMizanReceipt) {
|
|
298
|
+
const existing = loadReceiptState(sessionId) || {};
|
|
299
|
+
persistReceiptState(sessionId, {
|
|
300
|
+
...existing,
|
|
301
|
+
updatedAt: new Date().toISOString(),
|
|
302
|
+
sessionId,
|
|
303
|
+
postReceipt: _lastMizanReceipt,
|
|
304
|
+
postResult: mizan.result || null,
|
|
305
|
+
postContract: mizan.contract || null,
|
|
306
|
+
postSummary: mizan.summary || null,
|
|
307
|
+
outputRef,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
if (mizan.receipt?.blocked || mizan.result?.fitrahVetoed || mizan.result?.reAuthorSignal) {
|
|
311
|
+
const details = (mizan.result?.notes || ['post-phase blocked']).slice(0, 4).join(' | ');
|
|
262
312
|
throw new Error(
|
|
263
|
-
formatBlockReason(
|
|
264
|
-
'=== ARIA SKILL AUTOLOAD GATE BLOCK ===',
|
|
265
|
-
`${formatSkillGateBlock(skillGate)}\nRequired next step: load ${skillGate.missingSkills.join(', ')} before re-emitting this output.`,
|
|
266
|
-
)
|
|
313
|
+
formatBlockReason('=== ARIA MIZAN POST BLOCK ===', details)
|
|
267
314
|
);
|
|
268
315
|
}
|
|
269
|
-
|
|
316
|
+
} catch (e) {
|
|
317
|
+
if (isGateBlock(e)) throw e;
|
|
318
|
+
process.stderr.write(`[harness-stop] mizan/post unavailable: ${e.message}\n`);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const client = await getClient();
|
|
322
|
+
if (client) {
|
|
270
323
|
try {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
_lastMizanReceipt = mizan.receipt || _lastMizanReceipt;
|
|
280
|
-
if (_lastMizanReceipt) {
|
|
281
|
-
const existing = loadReceiptState(sessionId) || {};
|
|
282
|
-
persistReceiptState(sessionId, {
|
|
283
|
-
...existing,
|
|
284
|
-
updatedAt: new Date().toISOString(),
|
|
285
|
-
sessionId,
|
|
286
|
-
postReceipt: _lastMizanReceipt,
|
|
287
|
-
postResult: mizan.result || null,
|
|
288
|
-
postContract: mizan.contract || null,
|
|
289
|
-
postSummary: mizan.summary || null,
|
|
290
|
-
outputRef,
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
if (mizan.receipt?.blocked || mizan.result?.fitrahVetoed || mizan.result?.reAuthorSignal) {
|
|
294
|
-
const details = (mizan.result?.notes || ['post-phase blocked']).slice(0, 4).join(' | ');
|
|
324
|
+
const result = await runtimeValidateOutput(
|
|
325
|
+
text.slice(0, 8000),
|
|
326
|
+
sessionId,
|
|
327
|
+
).catch(() => client.validateOutput(
|
|
328
|
+
text.slice(0, 8000),
|
|
329
|
+
sessionId,
|
|
330
|
+
));
|
|
331
|
+
if (result.severity === 'block') {
|
|
295
332
|
throw new Error(
|
|
296
|
-
formatBlockReason(
|
|
333
|
+
formatBlockReason(
|
|
334
|
+
'=== ARIA OUTPUT GATE BLOCK ===',
|
|
335
|
+
`${result.violations.length} violations: ${result.violations.join('; ').slice(0, 500)}`,
|
|
336
|
+
)
|
|
337
|
+
);
|
|
338
|
+
} else if (result.severity === 'warn') {
|
|
339
|
+
throw new Error(
|
|
340
|
+
formatBlockReason(
|
|
341
|
+
'=== ARIA OUTPUT GATE BLOCK ===',
|
|
342
|
+
`SDK returned warn for ${result.violations.length} violation(s): ${(result.violations || []).join('; ').slice(0, 500)}. Warnings are fail-closed on owner-facing output.`,
|
|
343
|
+
)
|
|
297
344
|
);
|
|
298
345
|
}
|
|
346
|
+
if (result.gateTriggers?.length) {
|
|
347
|
+
process.stderr.write(`[harness-stop] SDK triggers: ${result.gateTriggers.join(', ')}\n`);
|
|
348
|
+
}
|
|
349
|
+
return;
|
|
299
350
|
} catch (e) {
|
|
300
351
|
if (isGateBlock(e)) throw e;
|
|
301
|
-
process.stderr.write(`[harness-stop]
|
|
352
|
+
process.stderr.write(`[harness-stop] SDK validateOutput failed: ${e.message} — falling through to local gate\n`);
|
|
302
353
|
}
|
|
354
|
+
}
|
|
303
355
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
)
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
// Log gate triggers
|
|
328
|
-
if (result.gateTriggers?.length) {
|
|
329
|
-
process.stderr.write(`[harness-stop] SDK triggers: ${result.gateTriggers.join(', ')}\n`);
|
|
330
|
-
}
|
|
331
|
-
return;
|
|
332
|
-
} catch (e) {
|
|
333
|
-
if (isGateBlock(e)) throw e;
|
|
334
|
-
process.stderr.write(`[harness-stop] SDK validateOutput failed: ${e.message} — falling through to local gate\n`);
|
|
356
|
+
const triggerMapPaths = [
|
|
357
|
+
`${HOME}/.aria/runtime/discipline/doctrine_trigger_map.json`,
|
|
358
|
+
`${HOME}/.aria/runtime/doctrine_trigger_map.json`,
|
|
359
|
+
`${HOME}/.opencode/doctrine_trigger_map.json`,
|
|
360
|
+
`${HOME}/.codex/doctrine_trigger_map.json`,
|
|
361
|
+
`${HOME}/.claude/hooks/doctrine_trigger_map.json`,
|
|
362
|
+
`${HOME}/.claude/projects/-home-hamzaibrahim1/memory/doctrine_trigger_map.json`,
|
|
363
|
+
];
|
|
364
|
+
let driftHits = [];
|
|
365
|
+
try {
|
|
366
|
+
const triggerMapPath = triggerMapPaths.find((candidate) => existsSync(candidate));
|
|
367
|
+
if (triggerMapPath) {
|
|
368
|
+
const triggerMap = JSON.parse(readFileSync(triggerMapPath, 'utf8'));
|
|
369
|
+
const lower = text.toLowerCase();
|
|
370
|
+
for (const t of triggerMap.triggers || []) {
|
|
371
|
+
try {
|
|
372
|
+
const rx = new RegExp(t.trigger, 'i');
|
|
373
|
+
if (rx.test(lower)) {
|
|
374
|
+
const memCited = t.memory && lower.includes(t.memory.replace(/\.md$/, '').toLowerCase());
|
|
375
|
+
if (!memCited) driftHits.push(t.trigger);
|
|
376
|
+
}
|
|
377
|
+
} catch {}
|
|
335
378
|
}
|
|
336
379
|
}
|
|
380
|
+
} catch {}
|
|
337
381
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
`${HOME}/.claude/projects/-home-hamzaibrahim1/memory/doctrine_trigger_map.json`,
|
|
347
|
-
];
|
|
348
|
-
let driftHits = [];
|
|
349
|
-
try {
|
|
350
|
-
const triggerMapPath = triggerMapPaths.find((candidate) => existsSync(candidate));
|
|
351
|
-
if (triggerMapPath) {
|
|
352
|
-
const triggerMap = JSON.parse(readFileSync(triggerMapPath, 'utf8'));
|
|
353
|
-
const lower = text.toLowerCase();
|
|
354
|
-
for (const t of triggerMap.triggers || []) {
|
|
355
|
-
try {
|
|
356
|
-
const rx = new RegExp(t.trigger, 'i');
|
|
357
|
-
if (rx.test(lower)) {
|
|
358
|
-
const memCited = t.memory && lower.includes(t.memory.replace(/\.md$/, '').toLowerCase());
|
|
359
|
-
if (!memCited) driftHits.push(t.trigger);
|
|
360
|
-
}
|
|
361
|
-
} catch {}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
} catch {}
|
|
382
|
+
if (cog.count < REQUIRED_LENSES || driftHits.length >= 2) {
|
|
383
|
+
throw new Error(
|
|
384
|
+
formatBlockReason(
|
|
385
|
+
'=== ARIA LOCAL OUTPUT BLOCK ===',
|
|
386
|
+
`cognition=${cog.count}/${REQUIRED_LENSES}; drift=${driftHits.length}`,
|
|
387
|
+
)
|
|
388
|
+
);
|
|
389
|
+
}
|
|
365
390
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
391
|
+
if (DECISION_SIGNAL_RX.test(text) && !APPLIED_COGNITION_RX.test(text)) {
|
|
392
|
+
throw new Error(
|
|
393
|
+
formatBlockReason(
|
|
394
|
+
'=== ARIA LOCAL OUTPUT BLOCK ===',
|
|
395
|
+
'decision-bearing output lacks required applied_cognition binding fields',
|
|
396
|
+
)
|
|
397
|
+
);
|
|
398
|
+
}
|
|
374
399
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
)
|
|
381
|
-
)
|
|
382
|
-
|
|
400
|
+
const domainQuality = analyzeDomainOutputQuality(text, { codeBlocks: extractCodeBlocks(text) });
|
|
401
|
+
if (domainQuality.blockers.length > 0) {
|
|
402
|
+
throw new Error(
|
|
403
|
+
formatBlockReason(
|
|
404
|
+
'=== ARIA LOCAL OUTPUT BLOCK ===',
|
|
405
|
+
`domain output QA (${domainQuality.domains.join(', ') || 'general'}): ${domainQuality.blockers.join('; ')}`,
|
|
406
|
+
)
|
|
407
|
+
);
|
|
408
|
+
}
|
|
383
409
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
410
|
+
try {
|
|
411
|
+
const existing = loadReceiptState(sessionId);
|
|
412
|
+
await runtimeDecisionLog({
|
|
413
|
+
decision_type: 'turn_action',
|
|
414
|
+
category: 'agentic_execution',
|
|
415
|
+
context: `opencode stop-gate turn (chars=${text.length})`,
|
|
416
|
+
decision: 'turn completed',
|
|
417
|
+
reasoning: cog.count > 0
|
|
418
|
+
? `Cognition lenses applied: ${cog.names.join(', ')}.`
|
|
419
|
+
: 'No explicit cognition block in turn.',
|
|
420
|
+
outcome: 'pending',
|
|
421
|
+
outcome_details: {
|
|
422
|
+
expected: null,
|
|
423
|
+
immediate_actual: null,
|
|
424
|
+
anchors: [],
|
|
425
|
+
},
|
|
426
|
+
expected_outcome: null,
|
|
427
|
+
metadata: {
|
|
428
|
+
pre_receipt_id: existing?.receipt?.receiptId || null,
|
|
429
|
+
post_receipt_id: _lastMizanReceipt?.receiptId || null,
|
|
430
|
+
output_ref: outputRef,
|
|
431
|
+
},
|
|
432
|
+
source: 'opencode-stop-gate-runtime',
|
|
433
|
+
model_used: process.env.OPENCODE_MODEL || 'opencode',
|
|
434
|
+
});
|
|
435
|
+
} catch (e) {
|
|
436
|
+
process.stderr.write(`[harness-stop] decision/log unavailable: ${e.message}\n`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
393
439
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
metadata: {
|
|
412
|
-
pre_receipt_id: existing?.receipt?.receiptId || null,
|
|
413
|
-
post_receipt_id: _lastMizanReceipt?.receiptId || null,
|
|
414
|
-
output_ref: outputRef,
|
|
415
|
-
},
|
|
416
|
-
source: 'opencode-stop-gate-runtime',
|
|
417
|
-
model_used: process.env.OPENCODE_MODEL || 'opencode',
|
|
418
|
-
});
|
|
419
|
-
} catch (e) {
|
|
420
|
-
process.stderr.write(`[harness-stop] decision/log unavailable: ${e.message}\n`);
|
|
421
|
-
}
|
|
440
|
+
return {
|
|
441
|
+
'session.idle': async (event) => {
|
|
442
|
+
process.stderr.write('[harness-stop] session idle heartbeat — text-emission gate registered\n');
|
|
443
|
+
},
|
|
444
|
+
|
|
445
|
+
event: async ({ event }) => {
|
|
446
|
+
if (event.type !== 'message.updated') return;
|
|
447
|
+
const parts = event.properties?.parts || [];
|
|
448
|
+
const text = parts
|
|
449
|
+
.filter(p => p.type === 'text')
|
|
450
|
+
.map(p => p.text || '')
|
|
451
|
+
.join('\n');
|
|
452
|
+
await validateText(text, { event });
|
|
453
|
+
},
|
|
454
|
+
|
|
455
|
+
'experimental.text.complete': async (input, output) => {
|
|
456
|
+
await validateText(String(output?.text || ''), input || {});
|
|
422
457
|
},
|
|
423
458
|
};
|
|
424
459
|
}
|
|
@@ -1 +1,14 @@
|
|
|
1
|
-
|
|
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.35",
|
|
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
|
}
|
|
@@ -6,7 +6,7 @@ import { homedir } from 'node:os';
|
|
|
6
6
|
import { delimiter, dirname, resolve } from 'node:path';
|
|
7
7
|
import { spawn, spawnSync } from 'node:child_process';
|
|
8
8
|
import { createRequire } from 'node:module';
|
|
9
|
-
import { evaluateSkillGate, formatSkillGateBlock } from '
|
|
9
|
+
import { evaluateSkillGate, formatSkillGateBlock } from './hooks/lib/skill-autoload-gate.mjs';
|
|
10
10
|
|
|
11
11
|
const require = createRequire(import.meta.url);
|
|
12
12
|
const { WebSocketServer, WebSocket } = require('ws');
|