@aria_asi/cli 0.2.36 → 0.2.38
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/CLIENT-ONBOARDING.md +4 -2
- package/bin/aria.js +11 -7
- package/dist/aria-connector/src/auth.d.ts +14 -0
- package/dist/aria-connector/src/auth.d.ts.map +1 -1
- package/dist/aria-connector/src/auth.js +103 -1
- package/dist/aria-connector/src/auth.js.map +1 -1
- package/dist/aria-connector/src/chat.d.ts.map +1 -1
- package/dist/aria-connector/src/chat.js +13 -8
- package/dist/aria-connector/src/chat.js.map +1 -1
- package/dist/aria-connector/src/config.d.ts +6 -1
- package/dist/aria-connector/src/config.d.ts.map +1 -1
- package/dist/aria-connector/src/config.js.map +1 -1
- package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/claude-code.js +50 -6
- package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
- package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/codex.js +290 -32
- package/dist/aria-connector/src/connectors/codex.js.map +1 -1
- package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/opencode.js +35 -11
- package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
- package/dist/aria-connector/src/connectors/repo-guard.d.ts +10 -0
- package/dist/aria-connector/src/connectors/repo-guard.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/repo-guard.js +110 -164
- package/dist/aria-connector/src/connectors/repo-guard.js.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.js +17 -7
- package/dist/aria-connector/src/connectors/runtime.js.map +1 -1
- package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/shell.js +12 -8
- package/dist/aria-connector/src/connectors/shell.js.map +1 -1
- package/dist/aria-connector/src/harness-client.d.ts +3 -1
- package/dist/aria-connector/src/harness-client.d.ts.map +1 -1
- package/dist/aria-connector/src/harness-client.js +7 -20
- package/dist/aria-connector/src/harness-client.js.map +1 -1
- package/dist/aria-connector/src/model-context.d.ts.map +1 -1
- package/dist/aria-connector/src/model-context.js +5 -0
- package/dist/aria-connector/src/model-context.js.map +1 -1
- package/dist/aria-connector/src/providers/types.d.ts +1 -1
- package/dist/aria-connector/src/providers/types.d.ts.map +1 -1
- package/dist/aria-connector/src/providers/xai.d.ts +3 -0
- package/dist/aria-connector/src/providers/xai.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/xai.js +40 -0
- package/dist/aria-connector/src/providers/xai.js.map +1 -0
- package/dist/aria-connector/src/setup-wizard.js +1 -0
- package/dist/aria-connector/src/setup-wizard.js.map +1 -1
- package/dist/aria-connector/src/types.d.ts +2 -0
- package/dist/aria-connector/src/types.d.ts.map +1 -1
- package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +51 -9
- package/dist/assets/hooks/aria-first-class-coach.mjs +129 -0
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +33 -6
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +86 -8
- package/dist/assets/hooks/aria-pre-tool-use.mjs +75 -0
- package/dist/assets/hooks/aria-preprompt-consult.mjs +5 -6
- package/dist/assets/hooks/aria-preturn-memory-gate.mjs +5 -0
- package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +15 -0
- package/dist/assets/hooks/aria-stop-gate.mjs +125 -17
- package/dist/assets/hooks/doctrine_trigger_map.json +11 -0
- package/dist/assets/hooks/lib/emergency-gateoff-impl.mjs +39 -0
- package/dist/assets/hooks/lib/emergency-gateoff.mjs +6 -0
- package/dist/assets/hooks/lib/first-class-coach.mjs +755 -0
- package/dist/assets/hooks/lib/skill-autoload-gate-impl.mjs +103 -0
- package/dist/assets/hooks/lib/skill-autoload-gate.mjs +1 -14
- package/dist/assets/opencode-plugins/harness-context/auth-token.mjs +126 -0
- package/dist/assets/opencode-plugins/harness-context/inject-context.mjs +62 -22
- package/dist/assets/opencode-plugins/harness-context/task-project-ledger.mjs +290 -0
- package/dist/assets/opencode-plugins/harness-gate/index.js +87 -27
- package/dist/assets/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +1 -14
- package/dist/assets/opencode-plugins/harness-outcome/index.js +29 -24
- package/dist/assets/opencode-plugins/harness-stop/index.js +229 -68
- package/dist/assets/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +1 -14
- package/dist/runtime/auth-token.mjs +121 -0
- package/dist/runtime/coach-kernel.mjs +377 -0
- package/dist/runtime/codex-bridge.mjs +440 -69
- package/dist/runtime/discipline/doctrine_trigger_map.json +11 -0
- package/dist/runtime/discipline/skills/aria-cognition/aria-essence/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/aria-forge-guardrails/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/aria-repo-doctrine/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/forge-quality-rules/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/ghazali-8lens/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/istiqra-induction/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/ladunni-22/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/mizan/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/nadia/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/nadia-psi/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/predictor/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/qiyas-analogy/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-cognition/soul-domains/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-intra-phase/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-post-phase/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-pre-phase/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-deploy/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-no-stripping/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-output-discipline/SKILL.md +18 -0
- package/dist/runtime/discipline/skills/aria-harness/aria-harness-substrate-binding/SKILL.md +18 -0
- package/dist/runtime/doctrine_trigger_map.json +11 -0
- package/dist/runtime/hooks/aria-cognition-substrate-binding.mjs +51 -9
- package/dist/runtime/hooks/aria-first-class-coach.mjs +129 -0
- package/dist/runtime/hooks/aria-harness-via-sdk.mjs +33 -6
- package/dist/runtime/hooks/aria-pre-tool-gate.mjs +86 -8
- package/dist/runtime/hooks/aria-pre-tool-use.mjs +75 -0
- package/dist/runtime/hooks/aria-preprompt-consult.mjs +5 -6
- package/dist/runtime/hooks/aria-preturn-memory-gate.mjs +5 -0
- package/dist/runtime/hooks/aria-repo-doctrine-gate.mjs +15 -0
- package/dist/runtime/hooks/aria-stop-gate.mjs +125 -17
- package/dist/runtime/hooks/doctrine_trigger_map.json +11 -0
- package/dist/runtime/hooks/lib/emergency-gateoff-impl.mjs +39 -0
- package/dist/runtime/hooks/lib/emergency-gateoff.mjs +6 -0
- package/dist/runtime/hooks/lib/first-class-coach.mjs +755 -0
- package/dist/runtime/hooks/lib/skill-autoload-gate-impl.mjs +103 -0
- package/dist/runtime/hooks/lib/skill-autoload-gate.mjs +1 -14
- package/dist/runtime/local-phase.mjs +8 -0
- package/dist/runtime/manifest.json +2 -2
- package/dist/runtime/provider-proxy.mjs +136 -33
- package/dist/runtime/sdk/BUNDLED.json +2 -2
- package/dist/runtime/sdk/auth.d.ts +17 -0
- package/dist/runtime/sdk/auth.js +158 -0
- package/dist/runtime/sdk/auth.js.map +1 -0
- package/dist/runtime/sdk/index.d.ts +8 -1
- package/dist/runtime/sdk/index.js +15 -1
- package/dist/runtime/sdk/index.js.map +1 -1
- package/dist/runtime/service.mjs +1711 -74
- package/dist/runtime/task-project-ledger.mjs +290 -0
- package/dist/sdk/BUNDLED.json +2 -2
- package/dist/sdk/auth.d.ts +17 -0
- package/dist/sdk/auth.js +158 -0
- package/dist/sdk/auth.js.map +1 -0
- package/dist/sdk/index.d.ts +8 -1
- package/dist/sdk/index.js +15 -1
- package/dist/sdk/index.js.map +1 -1
- package/hooks/aria-cognition-substrate-binding.mjs +51 -9
- package/hooks/aria-first-class-coach.mjs +129 -0
- package/hooks/aria-harness-via-sdk.mjs +33 -6
- package/hooks/aria-pre-tool-gate.mjs +86 -8
- package/hooks/aria-pre-tool-use.mjs +75 -0
- package/hooks/aria-preprompt-consult.mjs +5 -6
- package/hooks/aria-preturn-memory-gate.mjs +5 -0
- package/hooks/aria-repo-doctrine-gate.mjs +15 -0
- package/hooks/aria-stop-gate.mjs +125 -17
- package/hooks/doctrine_trigger_map.json +11 -0
- package/hooks/lib/emergency-gateoff-impl.mjs +39 -0
- package/hooks/lib/emergency-gateoff.mjs +6 -0
- package/hooks/lib/first-class-coach.mjs +755 -0
- package/hooks/lib/skill-autoload-gate-impl.mjs +103 -0
- package/hooks/lib/skill-autoload-gate.mjs +1 -14
- package/opencode-plugins/harness-context/auth-token.mjs +126 -0
- package/opencode-plugins/harness-context/inject-context.mjs +62 -22
- package/opencode-plugins/harness-context/task-project-ledger.mjs +290 -0
- package/opencode-plugins/harness-gate/index.js +87 -27
- package/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +1 -14
- package/opencode-plugins/harness-outcome/index.js +29 -24
- package/opencode-plugins/harness-stop/index.js +229 -68
- package/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +1 -14
- package/package.json +8 -2
- package/runtime-src/auth-token.mjs +121 -0
- package/runtime-src/coach-kernel.mjs +377 -0
- package/runtime-src/codex-bridge.mjs +440 -69
- package/runtime-src/local-phase.mjs +8 -0
- package/runtime-src/provider-proxy.mjs +136 -33
- package/runtime-src/service.mjs +1711 -74
- package/scripts/bundle-sdk.mjs +8 -0
- package/scripts/check-client-compatibility.mjs +422 -0
- package/scripts/check-coach-kernel.mjs +204 -0
- package/scripts/check-managed-runtime-ledger.mjs +107 -0
- package/scripts/check-opencode-config-contract.mjs +78 -0
- package/scripts/check-quality-ledger.mjs +121 -0
- package/scripts/self-test-harness-gates.mjs +179 -11
- package/scripts/self-test-repo-guard.mjs +38 -0
- package/scripts/validate-skill-prompts.mjs +14 -1
- package/skills/aria-cognition/aria-essence/SKILL.md +18 -0
- package/skills/aria-cognition/aria-forge-guardrails/SKILL.md +18 -0
- package/skills/aria-cognition/aria-repo-doctrine/SKILL.md +18 -0
- package/skills/aria-cognition/forge-quality-rules/SKILL.md +18 -0
- package/skills/aria-cognition/ghazali-8lens/SKILL.md +18 -0
- package/skills/aria-cognition/istiqra-induction/SKILL.md +18 -0
- package/skills/aria-cognition/ladunni-22/SKILL.md +18 -0
- package/skills/aria-cognition/mizan/SKILL.md +18 -0
- package/skills/aria-cognition/nadia/SKILL.md +18 -0
- package/skills/aria-cognition/nadia-psi/SKILL.md +18 -0
- package/skills/aria-cognition/predictor/SKILL.md +18 -0
- package/skills/aria-cognition/qiyas-analogy/SKILL.md +18 -0
- package/skills/aria-cognition/soul-domains/SKILL.md +18 -0
- package/src/auth.ts +136 -1
- package/src/chat.ts +13 -8
- package/src/config.ts +6 -1
- package/src/connectors/claude-code.ts +62 -18
- package/src/connectors/codex.ts +288 -32
- package/src/connectors/opencode.ts +35 -12
- package/src/connectors/repo-guard.ts +117 -172
- package/src/connectors/runtime.ts +19 -7
- package/src/connectors/shell.ts +12 -8
- package/src/harness-client.ts +8 -22
- package/src/model-context.ts +6 -0
- package/src/providers/types.ts +1 -1
- package/src/providers/xai.ts +55 -0
- package/src/setup-wizard.ts +1 -0
- package/src/types.ts +2 -0
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
* Aria Harness Stop — text-emission gate via HTTPHarnessClient SDK.
|
|
3
3
|
* Routes text through Mizan validateOutput() for substrate-backed QC.
|
|
4
4
|
*/
|
|
5
|
-
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, appendFileSync } from 'node:fs';
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
7
7
|
import { spawnSync } from 'node:child_process';
|
|
8
8
|
import { homedir } from 'node:os';
|
|
9
|
-
import { join } from 'node:path';
|
|
9
|
+
import { dirname, join } from 'node:path';
|
|
10
10
|
import { analyzeDomainOutputQuality, extractCodeBlocks } from './lib/domain-output-quality.js';
|
|
11
11
|
import { evaluateSkillGate, formatSkillGateBlock } from './lib/skill-autoload-gate.js';
|
|
12
|
+
import {
|
|
13
|
+
evaluateTaskProjectClaim,
|
|
14
|
+
recordBlockedTaskProjectClaim,
|
|
15
|
+
updateTaskProjectLedger,
|
|
16
|
+
} from '../harness-context/task-project-ledger.mjs';
|
|
17
|
+
import { resolveAriaAuthToken } from '../harness-context/auth-token.mjs';
|
|
12
18
|
|
|
13
19
|
const HOME = homedir();
|
|
14
20
|
const SDK_CANDIDATES = [
|
|
@@ -16,11 +22,12 @@ const SDK_CANDIDATES = [
|
|
|
16
22
|
join(HOME, '.codex', 'aria-sdk', 'index.js'),
|
|
17
23
|
join(HOME, '.claude', 'aria-sdk', 'index.js'),
|
|
18
24
|
];
|
|
19
|
-
const OWNER_TOKEN_PATH = join(HOME, '.aria', 'owner-token');
|
|
20
|
-
const LICENSE_PATH = join(HOME, '.aria', 'license.json');
|
|
21
25
|
const GOVERNANCE_GATE_PATH = join(HOME, '.aria', 'bin', 'aria-governance-gate');
|
|
26
|
+
const CURRENT_RECOVERY_PATH = join(HOME, '.aria', 'governance-recovery-current.json');
|
|
22
27
|
const RUNTIME_URL = (process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319').replace(/\/+$/, '');
|
|
23
28
|
const RECEIPT_DIR = join(HOME, '.opencode', 'aria-mizan-receipts');
|
|
29
|
+
const RECOVERY_LOG_PATH = join(HOME, '.opencode', 'aria-governance-recovery.jsonl');
|
|
30
|
+
const RECOVERY_NOTICE_INTERVAL_MS = Number(process.env.ARIA_RECOVERY_NOTICE_INTERVAL_MS || 30000);
|
|
24
31
|
|
|
25
32
|
const LENS_NAMES = [
|
|
26
33
|
'nur', 'mizan', 'hikma', 'tafakkur', 'tadabbur', 'ilham', 'wahi', 'firasah',
|
|
@@ -34,23 +41,154 @@ const NON_TRIVIAL_MIN_CHARS = 300;
|
|
|
34
41
|
const DECISION_SIGNAL_RX = /(?:should|recommend|propose|suggest|let'?s|go with|i'd|i would|here'?s the plan|i'll|next step|action item|ship it|yes do|let me)/i;
|
|
35
42
|
const TRIVIAL_ACK_RX = /^(?:got it|on it|ok|sure|yes|no|done|ack)\b/i;
|
|
36
43
|
const PLACEHOLDER_RX = /^\s*<[^<>]+>\s*$/;
|
|
37
|
-
const BLOCK_PREFIX_RX = /^=== ARIA (?:MIZAN POST|OUTPUT GATE|LOCAL OUTPUT) BLOCK ===/;
|
|
44
|
+
const BLOCK_PREFIX_RX = /^=== ARIA (?:MIZAN POST|OUTPUT GATE|LOCAL OUTPUT|SKILL AUTOLOAD GATE|UNIVERSAL GOVERNANCE GATE) BLOCK ===/;
|
|
38
45
|
const APPLIED_COGNITION_RX = /<applied_cognition>[\s\S]*?decision_delta\s*:[\s\S]*?dominant_domain\s*:[\s\S]*?binds_to\s*:[\s\S]*?expected_predicate\s*:[\s\S]*?artifact_change\s*:[\s\S]*?<\/applied_cognition>/i;
|
|
46
|
+
const HANDOFF_MODE_RX = /\b(?:post[_ -]?compact[_ -]?continuation|handoff[_ -]?resume|continuation[_ -]?handoff|post-compact|compact(?:ion)? handoff|post\s+commission)\b/i;
|
|
47
|
+
const HANDOFF_REQUIRED_FIELDS = [
|
|
48
|
+
{ key: 'current_objective', label: 'current objective', rx: /\bcurrent\s+objective\b\s*:/i },
|
|
49
|
+
{ key: 'known_blockers', label: 'known blockers', rx: /\bknown\s+blockers\b\s*:/i },
|
|
50
|
+
{ key: 'next_executable_step', label: 'next executable step', rx: /\bnext\s+executable\s+step\b\s*:/i },
|
|
51
|
+
{ key: 'what_not_to_touch', label: 'what not to touch', rx: /\bwhat\s+not\s+to\s+touch\b\s*:/i },
|
|
52
|
+
{ key: 'verification_already_run', label: 'verification already run', rx: /\bverification\s+already\s+run\b\s*:/i },
|
|
53
|
+
];
|
|
54
|
+
const FAKE_COMPLETION_CLAIM_RX = /\b(?:everything\s+is\s+)?(?:done|fixed|ready|complete|completed|shipped|production-ready|works in general)\b/i;
|
|
55
|
+
const PROOF_SIGNAL_RX = /\b(?:passed|exit\s*=?\s*0|status\s*[:=]\s*(?:ok|healthy)|readyReplicas\s*=|\d+\s+passed|green|verified\s*[:=-].{0,80}(?:npm|jest|test|build|typecheck|kubectl|curl|smoke))\b/i;
|
|
56
|
+
const GATEOFF_MARKER_PATH = join(HOME, '.aria', 'gates-off.manual');
|
|
57
|
+
const GATEOFF_ENV_RX = /^(?:1|true|yes|on)$/i;
|
|
58
|
+
const GATEOFF_PHRASE_RX = /\bGATEOFF\b/;
|
|
59
|
+
|
|
60
|
+
function textFromPayload(payload) {
|
|
61
|
+
if (!payload) return '';
|
|
62
|
+
if (typeof payload === 'string') return payload;
|
|
63
|
+
try { return JSON.stringify(payload); } catch { return String(payload); }
|
|
64
|
+
}
|
|
39
65
|
|
|
40
|
-
function
|
|
66
|
+
function persistEmergencyGateOff(reason = 'phrase') {
|
|
67
|
+
mkdirSync(dirname(GATEOFF_MARKER_PATH), { recursive: true, mode: 0o700 });
|
|
68
|
+
writeFileSync(GATEOFF_MARKER_PATH, JSON.stringify({
|
|
69
|
+
disabled: true,
|
|
70
|
+
reason,
|
|
71
|
+
disabledAt: new Date().toISOString(),
|
|
72
|
+
reenable: 'Manual owner action only: remove this marker and unset ARIA_GATES_OFF/ARIA_HARNESS_GATES_OFF.',
|
|
73
|
+
}, null, 2) + '\n', { mode: 0o600 });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function emergencyGateOffDecision(payload = '') {
|
|
77
|
+
if (GATEOFF_ENV_RX.test(String(process.env.ARIA_GATES_OFF || process.env.ARIA_HARNESS_GATES_OFF || ''))) {
|
|
78
|
+
return { off: true, reason: 'env' };
|
|
79
|
+
}
|
|
80
|
+
if (existsSync(GATEOFF_MARKER_PATH)) return { off: true, reason: 'marker' };
|
|
81
|
+
if (GATEOFF_PHRASE_RX.test(textFromPayload(payload))) {
|
|
82
|
+
persistEmergencyGateOff('phrase');
|
|
83
|
+
return { off: true, reason: 'phrase' };
|
|
84
|
+
}
|
|
85
|
+
return { off: false, reason: null };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function recoveryTemplate(kind = 'general') {
|
|
89
|
+
const handoff = kind === 'handoff';
|
|
90
|
+
return [
|
|
91
|
+
'Recovery contract:',
|
|
92
|
+
'1. Do not retry the same blocked text.',
|
|
93
|
+
'2. Rewrite the output using this exact shape:',
|
|
94
|
+
handoff ? 'post_compact_continuation' : '<applied_cognition>',
|
|
95
|
+
handoff ? 'current objective: <one sentence of the still-active task>' : 'decision_delta: <what changed in this turn>',
|
|
96
|
+
handoff ? 'known blockers: <specific blockers, or "none verified" if truly none>' : 'dominant_domain: <code|deploy|security|testing|product|ops|writing>',
|
|
97
|
+
handoff ? 'next executable step: <single command/edit/read/action the next agent should run first>' : 'binds_to: <specific file, command result, endpoint, runtime, or user instruction>',
|
|
98
|
+
handoff ? 'what not to touch: <unrelated dirty files, secrets, protected systems, or deploys to avoid>' : 'expected_predicate: <measurable condition, e.g. exit=0, status=healthy, count=N>',
|
|
99
|
+
handoff ? 'verification already run: <commands/probes already run and exact result, or "not run">' : 'artifact_change: <exact artifact changed or "none">',
|
|
100
|
+
handoff ? '' : '</applied_cognition>',
|
|
101
|
+
'3. If the block was caused by missing proof, run the concrete command/probe first, then re-submit with the result.',
|
|
102
|
+
'4. If the block was caused by a missing skill/doctrine trigger, load the named skill, then re-write and re-test.',
|
|
103
|
+
'5. If two rewrites still block, escalate through ARIA console/MCP with the block text and stop repeating the same output.',
|
|
104
|
+
].filter(Boolean).join('\n');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function formatBlockReason(prefix, details, options = {}) {
|
|
41
108
|
return [
|
|
42
109
|
prefix,
|
|
43
110
|
'',
|
|
44
111
|
String(details || 'Aria output gate blocked this message.'),
|
|
45
112
|
'',
|
|
46
|
-
|
|
113
|
+
recoveryTemplate(options.kind || 'general'),
|
|
47
114
|
].join('\n');
|
|
48
115
|
}
|
|
49
116
|
|
|
117
|
+
function recoveryFingerprint(reason, source = 'opencode-stop') {
|
|
118
|
+
return createHash('sha256')
|
|
119
|
+
.update([source, String(reason || '').replace(/\s+/g, ' ').trim().slice(0, 1200)].join('\0'))
|
|
120
|
+
.digest('hex');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function emitRecoverableOutput(reason, { source = 'opencode-stop', sessionId = 'opencode' } = {}) {
|
|
124
|
+
const fingerprint = recoveryFingerprint(reason, source);
|
|
125
|
+
const updatedAt = new Date().toISOString();
|
|
126
|
+
const recovery = {
|
|
127
|
+
schema: 'aria.governance_recovery_current.v1',
|
|
128
|
+
updatedAt,
|
|
129
|
+
deliveryRule: 'This file is injected into the next system prompt. Execute recoveryLoop.nextStep before any completion claim.',
|
|
130
|
+
ok: true,
|
|
131
|
+
decision: 'warn',
|
|
132
|
+
source,
|
|
133
|
+
governanceMode: 'recovery-required',
|
|
134
|
+
recoveryLoop: {
|
|
135
|
+
fingerprint,
|
|
136
|
+
allowedRecoveryAttempts: 1,
|
|
137
|
+
priorRecoveryAttempts: 0,
|
|
138
|
+
remainingRecoveryAttempts: 1,
|
|
139
|
+
nextStep: 'Re-author the blocked output using the recovery contract, run a concrete verification probe when tools are available, then emit the corrected output.',
|
|
140
|
+
architectFallback: 'If Aria consult is unavailable or this same fingerprint repeats, use the strongest available client LLM under the architect harness, produce and execute the recovery plan, then verify.',
|
|
141
|
+
},
|
|
142
|
+
recoveryContract: {
|
|
143
|
+
retry: 'One recovery attempt is allowed for this exact output-gate fingerprint; repeat failure enters architect execution mode.',
|
|
144
|
+
architectMode: 'On repeat, load architecture-decision and named domain skills, define the smallest executable plan, execute the first recovery action, and verify before output.',
|
|
145
|
+
fallbackWhenAriaUnavailable: 'Do not stop or ask the owner to fix it. Bind the strongest available client LLM to the architect harness, give it this JSON contract and failed evidence, execute its recovery plan, then verify.',
|
|
146
|
+
repairRecoveryCycle: ['re-author output according to the gate reason', 'verify corrected claim or state explicit unverified boundary'],
|
|
147
|
+
retest: 'Submit the revised output through the same output gate and run the concrete verification probe named by the recovery plan.',
|
|
148
|
+
blockedReason: reason,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
mkdirSync(dirname(CURRENT_RECOVERY_PATH), { recursive: true, mode: 0o700 });
|
|
152
|
+
writeFileSync(CURRENT_RECOVERY_PATH, `${JSON.stringify(recovery, null, 2)}\n`, { mode: 0o600 });
|
|
153
|
+
mkdirSync(dirname(RECOVERY_LOG_PATH), { recursive: true, mode: 0o700 });
|
|
154
|
+
appendFileSync(RECOVERY_LOG_PATH, `${JSON.stringify(recovery)}\n`, { mode: 0o600 });
|
|
155
|
+
|
|
156
|
+
const noticeKey = `${source}:${fingerprint}`;
|
|
157
|
+
const nowMs = Date.now();
|
|
158
|
+
const lastNoticeAt = _lastRecoveryNoticeBySource.get(noticeKey) || 0;
|
|
159
|
+
if (nowMs - lastNoticeAt >= RECOVERY_NOTICE_INTERVAL_MS) {
|
|
160
|
+
_lastRecoveryNoticeBySource.set(noticeKey, nowMs);
|
|
161
|
+
const compactReason = String(reason || 'recovery required')
|
|
162
|
+
.replace(/\s+/g, ' ')
|
|
163
|
+
.replace(/^=+\s*ARIA\s*/i, '')
|
|
164
|
+
.slice(0, 160);
|
|
165
|
+
process.stderr.write(`[aria-governance] recovery-required source=${source} fingerprint=${fingerprint.slice(0, 12)} reason="${compactReason}" details=${CURRENT_RECOVERY_PATH}\n`);
|
|
166
|
+
}
|
|
167
|
+
return recovery;
|
|
168
|
+
}
|
|
169
|
+
|
|
50
170
|
function isGateBlock(error) {
|
|
51
171
|
return BLOCK_PREFIX_RX.test(String(error?.message || error || ''));
|
|
52
172
|
}
|
|
53
173
|
|
|
174
|
+
export function classifyContinuationHandoff(text) {
|
|
175
|
+
const body = String(text || '');
|
|
176
|
+
const isContinuation = HANDOFF_MODE_RX.test(body);
|
|
177
|
+
const missingFields = HANDOFF_REQUIRED_FIELDS
|
|
178
|
+
.filter((field) => !field.rx.test(body))
|
|
179
|
+
.map((field) => field.label);
|
|
180
|
+
const completionClaimLines = body
|
|
181
|
+
.split(/\r?\n/)
|
|
182
|
+
.filter((line) => /\b(?:current\s+objective|summary|status)\b\s*:/i.test(line));
|
|
183
|
+
const hasFakeCompletionClaim = completionClaimLines.some((line) => FAKE_COMPLETION_CLAIM_RX.test(line)) && !PROOF_SIGNAL_RX.test(body);
|
|
184
|
+
return {
|
|
185
|
+
isContinuation,
|
|
186
|
+
ok: isContinuation && missingFields.length === 0 && !hasFakeCompletionClaim,
|
|
187
|
+
missingFields,
|
|
188
|
+
hasFakeCompletionClaim,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
54
192
|
function runUniversalGovernanceGate(payload) {
|
|
55
193
|
if (!existsSync(GOVERNANCE_GATE_PATH)) return null;
|
|
56
194
|
const child = spawnSync(GOVERNANCE_GATE_PATH, {
|
|
@@ -63,16 +201,22 @@ function runUniversalGovernanceGate(payload) {
|
|
|
63
201
|
try { result = stdout ? JSON.parse(stdout) : null; } catch {}
|
|
64
202
|
if (child.status !== 0 || result?.ok === false || result?.decision === 'block') {
|
|
65
203
|
throw new Error(
|
|
66
|
-
|
|
67
|
-
|
|
204
|
+
formatBlockReason(
|
|
205
|
+
'=== ARIA UNIVERSAL GOVERNANCE GATE BLOCK ===',
|
|
206
|
+
stdout || child.stderr || 'aria-governance-gate blocked this output.',
|
|
207
|
+
)
|
|
68
208
|
);
|
|
69
209
|
}
|
|
210
|
+
if (result?.decision === 'warn' || result?.governanceMode === 'recovery-required' || result?.governanceMode === 'architectural-intervention-required') {
|
|
211
|
+
process.stderr.write(`[aria-governance] ${result.governanceMode || 'recovery-required'} details=${CURRENT_RECOVERY_PATH}\n`);
|
|
212
|
+
}
|
|
70
213
|
return result;
|
|
71
214
|
}
|
|
72
215
|
|
|
73
216
|
let _client = null;
|
|
74
217
|
let _clientError = null;
|
|
75
218
|
let _lastMizanReceipt = null;
|
|
219
|
+
const _lastRecoveryNoticeBySource = new Map();
|
|
76
220
|
|
|
77
221
|
function receiptPath(sessionId) {
|
|
78
222
|
return join(RECEIPT_DIR, `${String(sessionId || 'opencode').replace(/[^a-zA-Z0-9_-]/g, '_')}.json`);
|
|
@@ -109,22 +253,7 @@ function makeEvidenceRef(kind, value, metadata = {}) {
|
|
|
109
253
|
}
|
|
110
254
|
|
|
111
255
|
function resolveToken() {
|
|
112
|
-
|
|
113
|
-
if (process.env.ARIA_API_KEY) return process.env.ARIA_API_KEY;
|
|
114
|
-
if (process.env.ARIA_MASTER_TOKEN) return process.env.ARIA_MASTER_TOKEN;
|
|
115
|
-
try {
|
|
116
|
-
if (existsSync(OWNER_TOKEN_PATH)) {
|
|
117
|
-
const v = readFileSync(OWNER_TOKEN_PATH, 'utf8').trim();
|
|
118
|
-
if (v) return v;
|
|
119
|
-
}
|
|
120
|
-
} catch {}
|
|
121
|
-
try {
|
|
122
|
-
if (existsSync(LICENSE_PATH)) {
|
|
123
|
-
const j = JSON.parse(readFileSync(LICENSE_PATH, 'utf8'));
|
|
124
|
-
if (j.token) return j.token;
|
|
125
|
-
}
|
|
126
|
-
} catch {}
|
|
127
|
-
return '';
|
|
256
|
+
return resolveAriaAuthToken({ baseUrl: resolveBaseUrl() }).token;
|
|
128
257
|
}
|
|
129
258
|
|
|
130
259
|
function resolveBaseUrl() {
|
|
@@ -255,11 +384,51 @@ export default async function HarnessStopPlugin(ctx) {
|
|
|
255
384
|
process.stderr.write('[harness-stop] Active — SDK-backed text-emission gate\n');
|
|
256
385
|
|
|
257
386
|
async function validateText(text, eventContext = {}) {
|
|
387
|
+
const sessionId = eventContext.sessionID || process.env.ARIA_SESSION_ID || process.env.OPENCODE_SESSION_ID || 'opencode';
|
|
388
|
+
const handoff = classifyContinuationHandoff(text);
|
|
389
|
+
if (handoff.isContinuation) {
|
|
390
|
+
if (!handoff.ok) {
|
|
391
|
+
const reasons = [
|
|
392
|
+
handoff.missingFields.length ? `missing fields: ${handoff.missingFields.join(', ')}` : '',
|
|
393
|
+
handoff.hasFakeCompletionClaim ? 'fake completion claim without proof' : '',
|
|
394
|
+
].filter(Boolean).join('; ');
|
|
395
|
+
emitRecoverableOutput(formatBlockReason('=== ARIA LOCAL OUTPUT BLOCK ===', `post-compact continuation handoff malformed: ${reasons}`, { kind: 'handoff' }), { source: 'opencode-stop/handoff', sessionId });
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const emergencyGateOff = emergencyGateOffDecision({ text, eventContext });
|
|
401
|
+
if (emergencyGateOff.off) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const ledgerResult = updateTaskProjectLedger({
|
|
405
|
+
platform: 'opencode',
|
|
406
|
+
phase: 'stop',
|
|
407
|
+
source: 'opencode-harness-stop',
|
|
408
|
+
event: { ...eventContext, text, sessionId, cwd: process.cwd() },
|
|
409
|
+
});
|
|
410
|
+
const ledgerClaim = evaluateTaskProjectClaim({ text, ledger: ledgerResult.ledger });
|
|
411
|
+
if (!ledgerClaim.ok) {
|
|
412
|
+
recordBlockedTaskProjectClaim({
|
|
413
|
+
ledger: ledgerResult.ledger,
|
|
414
|
+
paths: ledgerResult.paths,
|
|
415
|
+
text,
|
|
416
|
+
evaluation: ledgerClaim,
|
|
417
|
+
});
|
|
418
|
+
emitRecoverableOutput(formatBlockReason(
|
|
419
|
+
'=== ARIA TASK/PROJECT LEDGER BLOCK ===',
|
|
420
|
+
[
|
|
421
|
+
`ledger_id: ${ledgerResult.ledger.ledgerId}`,
|
|
422
|
+
`missing_readiness_gates: ${ledgerClaim.missingReadinessGates.join(', ') || 'none'}`,
|
|
423
|
+
`open_blockers: ${ledgerClaim.openBlockers.length}`,
|
|
424
|
+
].join('\n')
|
|
425
|
+
), { source: 'opencode-stop/task-project-ledger', sessionId });
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
258
428
|
if (!text || text.length < NON_TRIVIAL_MIN_CHARS) return;
|
|
259
429
|
if (TRIVIAL_ACK_RX.test(text.trim())) return;
|
|
260
430
|
|
|
261
431
|
const cog = detectCognitionLenses(text);
|
|
262
|
-
const sessionId = eventContext.sessionID || process.env.ARIA_SESSION_ID || process.env.OPENCODE_SESSION_ID || 'opencode';
|
|
263
432
|
runUniversalGovernanceGate({
|
|
264
433
|
sessionId,
|
|
265
434
|
sourceRuntime: 'opencode',
|
|
@@ -275,13 +444,9 @@ export default async function HarnessStopPlugin(ctx) {
|
|
|
275
444
|
isOutputCloseout: true,
|
|
276
445
|
autoLoadAvailable: false,
|
|
277
446
|
});
|
|
278
|
-
if (!skillGate.ok) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
'=== ARIA SKILL AUTOLOAD GATE BLOCK ===',
|
|
282
|
-
`${formatSkillGateBlock(skillGate)}\nRequired next step: load ${skillGate.missingSkills.join(', ')} before re-emitting this output.`,
|
|
283
|
-
)
|
|
284
|
-
);
|
|
447
|
+
if (!skillGate.ok && !skillGate.redirectOnly) {
|
|
448
|
+
emitRecoverableOutput(formatBlockReason('=== ARIA SKILL AUTOLOAD GATE BLOCK ===', formatSkillGateBlock(skillGate)), { source: 'opencode-stop/skill-autoload', sessionId });
|
|
449
|
+
return;
|
|
285
450
|
}
|
|
286
451
|
const outputRef = makeEvidenceRef('opencode_assistant_output', text.slice(0, 8000), { sessionId });
|
|
287
452
|
try {
|
|
@@ -309,12 +474,14 @@ export default async function HarnessStopPlugin(ctx) {
|
|
|
309
474
|
}
|
|
310
475
|
if (mizan.receipt?.blocked || mizan.result?.fitrahVetoed || mizan.result?.reAuthorSignal) {
|
|
311
476
|
const details = (mizan.result?.notes || ['post-phase blocked']).slice(0, 4).join(' | ');
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
);
|
|
477
|
+
emitRecoverableOutput(formatBlockReason('=== ARIA MIZAN POST BLOCK ===', details), { source: 'opencode-stop/mizan-post', sessionId });
|
|
478
|
+
return;
|
|
315
479
|
}
|
|
316
480
|
} catch (e) {
|
|
317
|
-
if (isGateBlock(e))
|
|
481
|
+
if (isGateBlock(e)) {
|
|
482
|
+
emitRecoverableOutput(e.message, { source: 'opencode-stop/mizan-post', sessionId });
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
318
485
|
process.stderr.write(`[harness-stop] mizan/post unavailable: ${e.message}\n`);
|
|
319
486
|
}
|
|
320
487
|
|
|
@@ -329,26 +496,21 @@ export default async function HarnessStopPlugin(ctx) {
|
|
|
329
496
|
sessionId,
|
|
330
497
|
));
|
|
331
498
|
if (result.severity === 'block') {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
'=== ARIA OUTPUT GATE BLOCK ===',
|
|
335
|
-
`${result.violations.length} violations: ${result.violations.join('; ').slice(0, 500)}`,
|
|
336
|
-
)
|
|
337
|
-
);
|
|
499
|
+
emitRecoverableOutput(formatBlockReason('=== ARIA OUTPUT GATE BLOCK ===', `${result.violations.length} violations: ${result.violations.join('; ').slice(0, 500)}`), { source: 'opencode-stop/validate-output', sessionId });
|
|
500
|
+
return;
|
|
338
501
|
} else if (result.severity === 'warn') {
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
)
|
|
344
|
-
);
|
|
502
|
+
emitRecoverableOutput(formatBlockReason('=== ARIA OUTPUT GATE BLOCK ===', `SDK returned warn for ${result.violations.length} violation(s): ${(result.violations || []).join('; ').slice(0, 500)}. Warnings require forced recovery on owner-facing output.`), { source: 'opencode-stop/validate-output-warn', sessionId });
|
|
503
|
+
return;
|
|
345
504
|
}
|
|
346
505
|
if (result.gateTriggers?.length) {
|
|
347
506
|
process.stderr.write(`[harness-stop] SDK triggers: ${result.gateTriggers.join(', ')}\n`);
|
|
348
507
|
}
|
|
349
508
|
return;
|
|
350
509
|
} catch (e) {
|
|
351
|
-
if (isGateBlock(e))
|
|
510
|
+
if (isGateBlock(e)) {
|
|
511
|
+
emitRecoverableOutput(e.message, { source: 'opencode-stop/validate-output', sessionId });
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
352
514
|
process.stderr.write(`[harness-stop] SDK validateOutput failed: ${e.message} — falling through to local gate\n`);
|
|
353
515
|
}
|
|
354
516
|
}
|
|
@@ -380,31 +542,19 @@ export default async function HarnessStopPlugin(ctx) {
|
|
|
380
542
|
} catch {}
|
|
381
543
|
|
|
382
544
|
if (cog.count < REQUIRED_LENSES || driftHits.length >= 2) {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
'=== ARIA LOCAL OUTPUT BLOCK ===',
|
|
386
|
-
`cognition=${cog.count}/${REQUIRED_LENSES}; drift=${driftHits.length}`,
|
|
387
|
-
)
|
|
388
|
-
);
|
|
545
|
+
emitRecoverableOutput(formatBlockReason('=== ARIA LOCAL OUTPUT BLOCK ===', `cognition=${cog.count}/${REQUIRED_LENSES}; drift=${driftHits.length}`), { source: 'opencode-stop/local-cognition-drift', sessionId });
|
|
546
|
+
return;
|
|
389
547
|
}
|
|
390
548
|
|
|
391
549
|
if (DECISION_SIGNAL_RX.test(text) && !APPLIED_COGNITION_RX.test(text)) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
'=== ARIA LOCAL OUTPUT BLOCK ===',
|
|
395
|
-
'decision-bearing output lacks required applied_cognition binding fields',
|
|
396
|
-
)
|
|
397
|
-
);
|
|
550
|
+
emitRecoverableOutput(formatBlockReason('=== ARIA LOCAL OUTPUT BLOCK ===', 'decision-bearing output lacks required applied_cognition binding fields'), { source: 'opencode-stop/applied-cognition', sessionId });
|
|
551
|
+
return;
|
|
398
552
|
}
|
|
399
553
|
|
|
400
554
|
const domainQuality = analyzeDomainOutputQuality(text, { codeBlocks: extractCodeBlocks(text) });
|
|
401
555
|
if (domainQuality.blockers.length > 0) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
'=== ARIA LOCAL OUTPUT BLOCK ===',
|
|
405
|
-
`domain output QA (${domainQuality.domains.join(', ') || 'general'}): ${domainQuality.blockers.join('; ')}`,
|
|
406
|
-
)
|
|
407
|
-
);
|
|
556
|
+
emitRecoverableOutput(formatBlockReason('=== ARIA LOCAL OUTPUT BLOCK ===', `domain output QA (${domainQuality.domains.join(', ') || 'general'}): ${domainQuality.blockers.join('; ')}`), { source: 'opencode-stop/domain-output-quality', sessionId });
|
|
557
|
+
return;
|
|
408
558
|
}
|
|
409
559
|
|
|
410
560
|
try {
|
|
@@ -432,6 +582,17 @@ export default async function HarnessStopPlugin(ctx) {
|
|
|
432
582
|
source: 'opencode-stop-gate-runtime',
|
|
433
583
|
model_used: process.env.OPENCODE_MODEL || 'opencode',
|
|
434
584
|
});
|
|
585
|
+
updateTaskProjectLedger({
|
|
586
|
+
platform: 'opencode',
|
|
587
|
+
phase: 'post_turn',
|
|
588
|
+
source: 'opencode-harness-stop',
|
|
589
|
+
event: { ...eventContext, text, sessionId, cwd: process.cwd() },
|
|
590
|
+
evidence: {
|
|
591
|
+
post_turn: true,
|
|
592
|
+
output_ref: outputRef,
|
|
593
|
+
verification: false,
|
|
594
|
+
},
|
|
595
|
+
});
|
|
435
596
|
} catch (e) {
|
|
436
597
|
process.stderr.write(`[harness-stop] decision/log unavailable: ${e.message}\n`);
|
|
437
598
|
}
|
|
@@ -1,14 +1 @@
|
|
|
1
|
-
|
|
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'); }
|
|
1
|
+
export * from '../../../hooks/lib/skill-autoload-gate.mjs';
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const HOME = homedir();
|
|
7
|
+
const ARIA_DIR = join(HOME, '.aria');
|
|
8
|
+
const OWNER_TOKEN_PATH = join(ARIA_DIR, 'owner-token');
|
|
9
|
+
const LICENSE_PATH = join(ARIA_DIR, 'license.json');
|
|
10
|
+
const CLIENT_TOKEN_DIR = join(ARIA_DIR, 'harness-tokens');
|
|
11
|
+
|
|
12
|
+
function nonEmpty(value) {
|
|
13
|
+
return typeof value === 'string' && value.trim() ? value.trim() : '';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function readJson(path) {
|
|
17
|
+
try {
|
|
18
|
+
if (!existsSync(path)) return null;
|
|
19
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
20
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function readText(path) {
|
|
27
|
+
try {
|
|
28
|
+
if (!existsSync(path)) return '';
|
|
29
|
+
return readFileSync(path, 'utf8').trim();
|
|
30
|
+
} catch {
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function sanitizeScope(value) {
|
|
36
|
+
const clean = String(value || '').trim().replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 96);
|
|
37
|
+
return clean || 'default';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function scopeFromBaseUrl(baseUrl) {
|
|
41
|
+
try {
|
|
42
|
+
const parsed = new URL(String(baseUrl || ''));
|
|
43
|
+
return `base-${createHash('sha256').update(`${parsed.protocol}//${parsed.host}`).digest('hex').slice(0, 16)}`;
|
|
44
|
+
} catch {
|
|
45
|
+
return 'default';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function hasExplicitClientScope(options) {
|
|
50
|
+
return Boolean(nonEmpty(options.clientId) || nonEmpty(options.tokenScope));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function inferScope(options, license) {
|
|
54
|
+
const explicit = nonEmpty(options.tokenScope) || nonEmpty(options.clientId);
|
|
55
|
+
if (explicit) return sanitizeScope(explicit);
|
|
56
|
+
const envScope = nonEmpty(process.env.ARIA_HARNESS_CLIENT_ID) || nonEmpty(process.env.ARIA_CLIENT_ID) || nonEmpty(process.env.ARIA_TENANT_ID);
|
|
57
|
+
if (envScope) return sanitizeScope(envScope);
|
|
58
|
+
const licenseScope = nonEmpty(license?.jti) || nonEmpty(license?.sub);
|
|
59
|
+
if (licenseScope) return sanitizeScope(licenseScope);
|
|
60
|
+
return sanitizeScope(scopeFromBaseUrl(nonEmpty(options.baseUrl)));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function persistedPath(scope) {
|
|
64
|
+
return join(CLIENT_TOKEN_DIR, `${sanitizeScope(scope)}.json`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readPersistedToken(scope) {
|
|
68
|
+
const parsed = readJson(persistedPath(scope));
|
|
69
|
+
return nonEmpty(parsed?.token);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function persistToken(scope, token, source) {
|
|
73
|
+
if (!token) return false;
|
|
74
|
+
try {
|
|
75
|
+
mkdirSync(CLIENT_TOKEN_DIR, { recursive: true, mode: 0o700 });
|
|
76
|
+
writeFileSync(
|
|
77
|
+
persistedPath(scope),
|
|
78
|
+
`${JSON.stringify({ scope, token, source, updatedAt: new Date().toISOString() }, null, 2)}\n`,
|
|
79
|
+
{ encoding: 'utf8', mode: 0o600 },
|
|
80
|
+
);
|
|
81
|
+
return true;
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function resolveAriaAuthToken(options = {}) {
|
|
88
|
+
const license = readJson(LICENSE_PATH);
|
|
89
|
+
const scope = inferScope(options, license);
|
|
90
|
+
const persist = options.persistToken !== false;
|
|
91
|
+
|
|
92
|
+
const explicit = nonEmpty(options.explicitToken);
|
|
93
|
+
if (explicit) {
|
|
94
|
+
return { token: explicit, source: 'explicit', scope, persisted: persist ? persistToken(scope, explicit, 'explicit') : false };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const envSources = [
|
|
98
|
+
['env:ARIA_HARNESS_TOKEN', process.env.ARIA_HARNESS_TOKEN],
|
|
99
|
+
['env:ARIA_API_KEY', process.env.ARIA_API_KEY],
|
|
100
|
+
['env:ARIA_MASTER_TOKEN', process.env.ARIA_MASTER_TOKEN],
|
|
101
|
+
];
|
|
102
|
+
for (const [source, value] of envSources) {
|
|
103
|
+
const token = nonEmpty(value);
|
|
104
|
+
if (!token) continue;
|
|
105
|
+
return { token, source, scope, persisted: persist ? persistToken(scope, token, source) : false };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const stored = readPersistedToken(scope);
|
|
109
|
+
if (stored) return { token: stored, source: 'persisted-client-token', scope, persisted: true };
|
|
110
|
+
|
|
111
|
+
if (!hasExplicitClientScope(options) || process.env.ARIA_ALLOW_OWNER_TOKEN_FOR_CLIENT_SCOPE === 'true') {
|
|
112
|
+
const ownerToken = readText(OWNER_TOKEN_PATH);
|
|
113
|
+
if (ownerToken) return { token: ownerToken, source: 'owner-token-file', scope, persisted: false };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const harnessToken = nonEmpty(license?.harnessToken);
|
|
117
|
+
if (harnessToken) return { token: harnessToken, source: 'license:harnessToken', scope, persisted: false };
|
|
118
|
+
const token = nonEmpty(license?.token);
|
|
119
|
+
if (token) return { token, source: 'license:token', scope, persisted: false };
|
|
120
|
+
return { token: '', source: 'missing', scope, persisted: false };
|
|
121
|
+
}
|