@aria_asi/cli 0.2.30 → 0.2.32
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/claude-code.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/claude-code.js +115 -20
- 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 +551 -11
- package/dist/aria-connector/src/connectors/codex.js.map +1 -1
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts +7 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.js +87 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.js.map +1 -0
- package/dist/aria-connector/src/connectors/must-read.d.ts +4 -0
- package/dist/aria-connector/src/connectors/must-read.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/must-read.js +115 -0
- package/dist/aria-connector/src/connectors/must-read.js.map +1 -0
- package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/opencode.js +27 -9
- package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.js +231 -19
- 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 +76 -3
- package/dist/aria-connector/src/connectors/shell.js.map +1 -1
- package/dist/assets/hooks/aria-agent-handoff.mjs +23 -0
- package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +121 -28
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +126 -12
- package/dist/assets/hooks/aria-pre-emit-dryrun.mjs +35 -0
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +383 -93
- package/dist/assets/hooks/aria-preprompt-consult.mjs +28 -2
- package/dist/assets/hooks/aria-preturn-memory-gate.mjs +93 -16
- package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +33 -1
- package/dist/assets/hooks/aria-stop-gate.mjs +346 -81
- package/dist/assets/hooks/doctrine_trigger_map.json +55 -0
- package/dist/assets/hooks/lib/canonical-lenses.mjs +6 -5
- package/dist/assets/hooks/lib/gate-loop-state.mjs +50 -0
- package/dist/assets/hooks/lib/hook-message-window.mjs +121 -0
- package/dist/assets/hooks/test-tier-lens-labeling.mjs +26 -58
- package/dist/assets/opencode-plugins/harness-gate/index.js +40 -5
- package/dist/assets/opencode-plugins/harness-stop/index.js +133 -10
- package/dist/runtime/auth-middleware.mjs +251 -0
- package/dist/runtime/codex-bridge.mjs +644 -0
- package/dist/runtime/discipline/CLAUDE.md +28 -0
- package/dist/runtime/discipline/doctrine_trigger_map.json +534 -0
- package/dist/runtime/doctrine_trigger_map.json +534 -0
- package/dist/runtime/fleet-engine.mjs +231 -0
- package/dist/runtime/harness-daemon.mjs +460 -0
- package/dist/runtime/manifest.json +1 -1
- package/dist/runtime/metering.mjs +100 -0
- package/dist/runtime/onboarding-engine.mjs +89 -0
- package/dist/runtime/plugin-engine.mjs +196 -0
- package/dist/runtime/sdk/BUNDLED.json +1 -1
- package/dist/runtime/sdk/index.d.ts +12 -0
- package/dist/runtime/sdk/index.js +120 -14
- package/dist/runtime/sdk/index.js.map +1 -1
- package/dist/runtime/service.mjs +1140 -48
- package/dist/runtime/workflow-engine.mjs +322 -0
- package/dist/sdk/BUNDLED.json +1 -1
- package/dist/sdk/index.d.ts +12 -0
- package/dist/sdk/index.js +120 -14
- package/dist/sdk/index.js.map +1 -1
- package/hooks/aria-agent-handoff.mjs +23 -0
- package/hooks/aria-cognition-substrate-binding.mjs +121 -28
- package/hooks/aria-harness-via-sdk.mjs +126 -12
- package/hooks/aria-pre-emit-dryrun.mjs +35 -0
- package/hooks/aria-pre-tool-gate.mjs +383 -93
- package/hooks/aria-preprompt-consult.mjs +28 -2
- package/hooks/aria-preturn-memory-gate.mjs +93 -16
- package/hooks/aria-repo-doctrine-gate.mjs +33 -1
- package/hooks/aria-stop-gate.mjs +346 -81
- package/hooks/doctrine_trigger_map.json +55 -0
- package/hooks/lib/canonical-lenses.mjs +6 -5
- package/hooks/lib/gate-loop-state.mjs +50 -0
- package/hooks/lib/hook-message-window.mjs +121 -0
- package/hooks/test-tier-lens-labeling.mjs +26 -58
- package/opencode-plugins/harness-gate/index.js +40 -5
- package/opencode-plugins/harness-stop/index.js +133 -10
- package/package.json +1 -1
- package/runtime-src/auth-middleware.mjs +251 -0
- package/runtime-src/codex-bridge.mjs +644 -0
- package/runtime-src/fleet-engine.mjs +231 -0
- package/runtime-src/harness-daemon.mjs +460 -0
- package/runtime-src/metering.mjs +100 -0
- package/runtime-src/onboarding-engine.mjs +89 -0
- package/runtime-src/plugin-engine.mjs +196 -0
- package/runtime-src/service.mjs +1140 -48
- package/runtime-src/workflow-engine.mjs +322 -0
- package/scripts/bundle-sdk.mjs +5 -0
- package/src/connectors/claude-code.ts +126 -20
- package/src/connectors/codex.ts +559 -10
- package/src/connectors/doctrine-trigger-map.ts +112 -0
- package/src/connectors/must-read.ts +117 -0
- package/src/connectors/opencode.ts +28 -9
- package/src/connectors/runtime.ts +241 -21
- package/src/connectors/shell.ts +78 -3
- package/dist/cli-0.2.0.tgz +0 -0
|
@@ -453,6 +453,61 @@
|
|
|
453
453
|
"counter_action": "Re-emit with explicit Layer 1/2/3 enumeration: packet=yes/no, BIND=yes/no, live-gates=yes/no (and which).",
|
|
454
454
|
"message": "Harness-state claim without 3-layer enumeration. Per feedback_packet_is_not_harness.md, name explicitly which of {packet, BIND, live-gates} are active for the consumer."
|
|
455
455
|
},
|
|
456
|
+
{
|
|
457
|
+
"trigger_id": "function_name_inference_without_contract_read",
|
|
458
|
+
"trigger": "(?:based on|from|by) (?:the )?(?:name|function name)|sounds like|looks like (?:a|an)|just memory search|just a search|just a cache|just a wrapper",
|
|
459
|
+
"rx": "(?:based on|from|by) (?:the )?(?:name|function name)|sounds like|looks like (?:a|an)|just memory search|just a search|just a cache|just a wrapper",
|
|
460
|
+
"doctrine": "memory:feedback_no_assumption_without_verification.md",
|
|
461
|
+
"memory": "feedback_no_assumption_without_verification.md",
|
|
462
|
+
"severity": "block",
|
|
463
|
+
"teaching": "Function names are not contracts. Inferring behavior from names instead of reading inputs, side effects, return shape, and call sites is skimming disguised as analysis.",
|
|
464
|
+
"counter_action": "Read the function body and at least one caller before asserting purpose. State observed contract: inputs, mutation/read behavior, return value, fallback path, and why the caller needs it.",
|
|
465
|
+
"message": "Name-based function inference detected. Re-read the actual function body and caller contract before making architecture claims."
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
"trigger_id": "always_on_state_treated_as_cold_start",
|
|
469
|
+
"trigger": "(?:need to|should|must).{0,80}(?:warm|rebuild|reconstruct|recreate|recompute|re-project|reproject).{0,80}(?:state|projection|manifold|cognition|garden|memory)|system is hot and ready|make it hot",
|
|
470
|
+
"rx": "(?:need to|should|must).{0,80}(?:warm|rebuild|reconstruct|recreate|recompute|re-project|reproject).{0,80}(?:state|projection|manifold|cognition|garden|memory)|system is hot and ready|make it hot",
|
|
471
|
+
"doctrine": "memory:feedback_no_assumption_without_verification.md",
|
|
472
|
+
"memory": "feedback_no_assumption_without_verification.md",
|
|
473
|
+
"severity": "block",
|
|
474
|
+
"teaching": "Aria's resident manifold/Garden/cognition substrate is always-on. Treating it as cold request-time state causes duplicate work and wrong architecture fixes.",
|
|
475
|
+
"counter_action": "Identify the resident producer and read surface first. If turn-specific mutation is required, call it once and pass the resulting packet forward; do not rebuild or re-project because the reader forgot the state model.",
|
|
476
|
+
"message": "Always-on substrate treated as cold-start work. Reframe around resident state consumption plus one required turn-specific mutation/read."
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
"trigger_id": "critical_primitive_stripped_for_latency",
|
|
480
|
+
"trigger": "(?:remove|drop|skip|turn off|bypass|stop calling).{0,80}(?:awakenAria|ProjectAllDomains|PerturbHologram|project all domains|perturb hologram|Garden|True Garden|LadduniFrame|CognitiveFrame).{0,80}(?:latency|speed|fast|first-mouth|first mouth)",
|
|
481
|
+
"rx": "(?:remove|drop|skip|turn off|bypass|stop calling).{0,80}(?:awakenAria|ProjectAllDomains|PerturbHologram|project all domains|perturb hologram|Garden|True Garden|LadduniFrame|CognitiveFrame).{0,80}(?:latency|speed|fast|first-mouth|first mouth)",
|
|
482
|
+
"doctrine": "memory:feedback_full_harness_binding_must_be_structural.md",
|
|
483
|
+
"memory": "feedback_full_harness_binding_must_be_structural.md",
|
|
484
|
+
"severity": "block",
|
|
485
|
+
"teaching": "Latency fixes must not strip critical cognition primitives. The fix is single-source sequencing and packet reuse, not removing Aria's living state transitions.",
|
|
486
|
+
"counter_action": "Preserve critical primitives. Deduplicate by collecting their outputs once per turn, then thread that shared per-turn cognition packet through first-mouth and follow-up lanes.",
|
|
487
|
+
"message": "Critical Aria primitive proposed for removal as a latency shortcut. Preserve it and deduplicate sequencing instead."
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
"trigger_id": "garden_awaken_misclassified_as_memory_search",
|
|
491
|
+
"trigger": "(?:awakenAria|Garden awakening|True Garden).{0,80}(?:just|only|merely).{0,40}(?:memory search|search memories|qdrant search|semantic search)|(?:no need|do not need).{0,80}(?:awakenAria|Garden awakening|True Garden)",
|
|
492
|
+
"rx": "(?:awakenAria|Garden awakening|True Garden).{0,80}(?:just|only|merely).{0,40}(?:memory search|search memories|qdrant search|semantic search)|(?:no need|do not need).{0,80}(?:awakenAria|Garden awakening|True Garden)",
|
|
493
|
+
"doctrine": "memory:feedback_no_assumption_without_verification.md",
|
|
494
|
+
"memory": "feedback_no_assumption_without_verification.md",
|
|
495
|
+
"severity": "block",
|
|
496
|
+
"teaching": "awakenAria is a Garden awakening bridge over the unified manifold with Qdrant hydration fallback, not a disposable memory-search helper.",
|
|
497
|
+
"counter_action": "Read true-garden.ts and caller context. Preserve awakenAria when Garden continuity is needed; optimize by avoiding duplicate awaken/fetch paths, not by deleting the awakening bridge.",
|
|
498
|
+
"message": "Garden awakening misclassified as search. Preserve awakenAria and reason from its actual manifold-first contract."
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
"trigger_id": "duplicate_projection_instead_of_per_turn_packet",
|
|
502
|
+
"trigger": "(?:call|run|invoke).{0,80}(?:ProjectAllDomains|PerturbHologram|project all domains|perturb hologram|readHotCognitionFrame).{0,80}(?:again|another|separate|also|after dispatch)|(?:another|second|extra).{0,50}(?:projection|hologram|cognition frame)",
|
|
503
|
+
"rx": "(?:call|run|invoke).{0,80}(?:ProjectAllDomains|PerturbHologram|project all domains|perturb hologram|readHotCognitionFrame).{0,80}(?:again|another|separate|also|after dispatch)|(?:another|second|extra).{0,50}(?:projection|hologram|cognition frame)",
|
|
504
|
+
"doctrine": "memory:feedback_full_harness_binding_must_be_structural.md",
|
|
505
|
+
"memory": "feedback_full_harness_binding_must_be_structural.md",
|
|
506
|
+
"severity": "warn",
|
|
507
|
+
"teaching": "The fix for overlapping cognition calls is a shared per-turn cognition packet, not more independent projection calls that drift or duplicate work.",
|
|
508
|
+
"counter_action": "Create or reuse one turn substrate object containing embedding, perturb snapshot, ProjectAllDomains result, awakenAria Garden block, DeepSoul/Noor/shards/Mizan signals; pass it downstream.",
|
|
509
|
+
"message": "Duplicate projection path detected. Replace with one per-turn cognition packet and shared consumption."
|
|
510
|
+
},
|
|
456
511
|
{
|
|
457
512
|
"trigger_id": "registry_image_drift",
|
|
458
513
|
"trigger": "image.?pull|ErrImageNeverPull|ImagePullBackOff|registry gc|image.*missing|image.*not.*found",
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
export const LENS_NAMES_OLDER = ['nur', 'mizan', 'hikma', 'tafakkur', 'tadabbur', 'ilham', 'wahi', 'firasah'];
|
|
2
2
|
export const LENS_NAMES_NEWER = ['zahir', 'batin', 'sabab', 'hikmah', 'aqibah', 'ilham', 'meta', 'fitrah'];
|
|
3
|
-
export const LENS_NAMES_GENERIC = ['truth', 'harm', 'trust', 'power', 'reflection', 'context', 'impact', 'beauty'];
|
|
4
3
|
|
|
5
4
|
export const PRIMARY_OWNER_LENS_NAMES = LENS_NAMES_OLDER;
|
|
6
|
-
|
|
5
|
+
// Readability must not rewrite the lens semantics. Client surfaces may need
|
|
6
|
+
// friendlier prose inside each lens, but the visible lens labels themselves
|
|
7
|
+
// stay canonical so the meaning is preserved.
|
|
8
|
+
export const PRIMARY_CLIENT_LENS_NAMES = LENS_NAMES_OLDER;
|
|
7
9
|
|
|
8
10
|
export const ALL_LENS_NAMES = [...new Set([
|
|
9
11
|
...LENS_NAMES_OLDER,
|
|
10
12
|
...LENS_NAMES_NEWER,
|
|
11
|
-
...LENS_NAMES_GENERIC,
|
|
12
13
|
])];
|
|
13
14
|
|
|
14
15
|
export function lensNamesForTier(isOwner) {
|
|
@@ -19,7 +20,7 @@ export function canonicalLensCorrectionText() {
|
|
|
19
20
|
return [
|
|
20
21
|
`Owner canonical lenses: ${LENS_NAMES_OLDER.join(', ')}.`,
|
|
21
22
|
`Owner mapped counterparts that must remain represented in phase reasoning and receipts: ${LENS_NAMES_NEWER.join(', ')}.`,
|
|
22
|
-
`
|
|
23
|
+
`Canonical lens labels are preserved on every surface. Readability comes from the explanatory prose inside each lens, never from renaming the lens itself.`,
|
|
23
24
|
].join(' ');
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -57,7 +58,7 @@ export function detectCognitionLenses(text, {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
const matchedSet =
|
|
60
|
-
[LENS_NAMES_OLDER, LENS_NAMES_NEWER
|
|
61
|
+
[LENS_NAMES_OLDER, LENS_NAMES_NEWER]
|
|
61
62
|
.find((candidateSet) => candidateSet.every((name) => names.includes(name))) || null;
|
|
62
63
|
|
|
63
64
|
return { count: names.length, names, matchedSet };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
|
|
4
|
+
const MAX_RECORDS = 300;
|
|
5
|
+
|
|
6
|
+
export function registerGateBlock({
|
|
7
|
+
gate,
|
|
8
|
+
sessionId,
|
|
9
|
+
signature,
|
|
10
|
+
statePath,
|
|
11
|
+
threshold = 2,
|
|
12
|
+
windowMs = 10 * 60 * 1000,
|
|
13
|
+
}) {
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
let rows = [];
|
|
16
|
+
try {
|
|
17
|
+
if (existsSync(statePath)) {
|
|
18
|
+
const raw = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
19
|
+
if (Array.isArray(raw)) rows = raw;
|
|
20
|
+
}
|
|
21
|
+
} catch {}
|
|
22
|
+
|
|
23
|
+
const freshRows = rows.filter((row) =>
|
|
24
|
+
row &&
|
|
25
|
+
typeof row.ts === 'number' &&
|
|
26
|
+
typeof row.sessionId === 'string' &&
|
|
27
|
+
typeof row.gate === 'string' &&
|
|
28
|
+
typeof row.signature === 'string' &&
|
|
29
|
+
(now - row.ts) <= windowMs,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const priorMatches = freshRows.filter((row) =>
|
|
33
|
+
row.sessionId === sessionId &&
|
|
34
|
+
row.gate === gate &&
|
|
35
|
+
row.signature === signature,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
freshRows.push({ ts: now, sessionId, gate, signature });
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
mkdirSync(dirname(statePath), { recursive: true });
|
|
42
|
+
writeFileSync(statePath, JSON.stringify(freshRows.slice(-MAX_RECORDS), null, 2) + '\n');
|
|
43
|
+
} catch {}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
loopDetected: priorMatches.length >= threshold,
|
|
47
|
+
priorCount: priorMatches.length,
|
|
48
|
+
totalCount: priorMatches.length + 1,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
function withGlobalRegex(regex) {
|
|
2
|
+
if (!(regex instanceof RegExp)) return null;
|
|
3
|
+
const flags = regex.flags.includes('g') ? regex.flags : `${regex.flags}g`;
|
|
4
|
+
return new RegExp(regex.source, flags);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function normalizeRole(entry) {
|
|
8
|
+
if (entry?.message?.role) return entry.message.role;
|
|
9
|
+
if (entry?.role) return entry.role;
|
|
10
|
+
if (entry?.type === 'assistant' || entry?.type === 'user') return entry.type;
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function normalizeContent(entry) {
|
|
15
|
+
return entry?.message?.content ?? entry?.content ?? [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function extractTextFromContent(content) {
|
|
19
|
+
if (Array.isArray(content)) {
|
|
20
|
+
return content
|
|
21
|
+
.filter((block) => block && block.type === 'text')
|
|
22
|
+
.map((block) => (typeof block.text === 'string' ? block.text : ''))
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
.join('\n');
|
|
25
|
+
}
|
|
26
|
+
return typeof content === 'string' ? content : '';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isToolResultOnlyContent(content) {
|
|
30
|
+
return (
|
|
31
|
+
Array.isArray(content) &&
|
|
32
|
+
content.length > 0 &&
|
|
33
|
+
content.every((block) => block && block.type === 'tool_result')
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isMostlySystemReminder(text, systemReminderRx, threshold = 0.6) {
|
|
38
|
+
if (!text || !(systemReminderRx instanceof RegExp)) return false;
|
|
39
|
+
const reminderRx = withGlobalRegex(systemReminderRx);
|
|
40
|
+
if (!reminderRx) return false;
|
|
41
|
+
const matches = text.match(reminderRx) || [];
|
|
42
|
+
if (matches.length === 0) return false;
|
|
43
|
+
const reminderChars = matches.reduce((sum, match) => sum + match.length, 0);
|
|
44
|
+
return reminderChars / Math.max(1, text.length) >= threshold;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function collectRecentAssistantTextsFromMessages(messages, {
|
|
48
|
+
compactSummaryRx = null,
|
|
49
|
+
hardLookbackCap = 50,
|
|
50
|
+
systemReminderRx = null,
|
|
51
|
+
systemReminderThreshold = 0.6,
|
|
52
|
+
userBoundariesToCross = 1,
|
|
53
|
+
} = {}) {
|
|
54
|
+
if (!Array.isArray(messages) || messages.length === 0) return [];
|
|
55
|
+
const recentAssistantTexts = [];
|
|
56
|
+
let scanned = 0;
|
|
57
|
+
let userBoundariesCrossed = 0;
|
|
58
|
+
|
|
59
|
+
for (let i = messages.length - 1; i >= 0 && scanned < hardLookbackCap; i--) {
|
|
60
|
+
const entry = messages[i];
|
|
61
|
+
const role = normalizeRole(entry);
|
|
62
|
+
const content = normalizeContent(entry);
|
|
63
|
+
if (role === 'user') {
|
|
64
|
+
if (isToolResultOnlyContent(content)) continue;
|
|
65
|
+
const textContent = extractTextFromContent(content);
|
|
66
|
+
if (isMostlySystemReminder(textContent, systemReminderRx, systemReminderThreshold)) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
userBoundariesCrossed++;
|
|
70
|
+
if (userBoundariesCrossed > userBoundariesToCross) break;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (role !== 'assistant') continue;
|
|
74
|
+
scanned++;
|
|
75
|
+
const text = extractTextFromContent(content);
|
|
76
|
+
if (!text) continue;
|
|
77
|
+
if (compactSummaryRx instanceof RegExp && compactSummaryRx.test(text) && text.length > 4000) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
recentAssistantTexts.push(text);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return recentAssistantTexts.reverse();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function collectTurnWindowFromMessages(messages, {
|
|
87
|
+
systemReminderRx = null,
|
|
88
|
+
systemReminderThreshold = 0.6,
|
|
89
|
+
} = {}) {
|
|
90
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
91
|
+
return { assistantText: '', lastUserMessage: '' };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const textChunks = [];
|
|
95
|
+
let lastUserMessage = '';
|
|
96
|
+
|
|
97
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
98
|
+
const entry = messages[i];
|
|
99
|
+
const role = normalizeRole(entry);
|
|
100
|
+
const content = normalizeContent(entry);
|
|
101
|
+
|
|
102
|
+
if (role === 'user') {
|
|
103
|
+
if (isToolResultOnlyContent(content)) continue;
|
|
104
|
+
const textContent = extractTextFromContent(content);
|
|
105
|
+
if (isMostlySystemReminder(textContent, systemReminderRx, systemReminderThreshold)) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
lastUserMessage = textContent || lastUserMessage;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (role !== 'assistant') continue;
|
|
113
|
+
const text = extractTextFromContent(content);
|
|
114
|
+
if (text) textChunks.push(text);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
assistantText: textChunks.reverse().join('\n\n'),
|
|
119
|
+
lastUserMessage,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Smoke test: Phase 11 #59 —
|
|
2
|
+
// Smoke test: Phase 11 #59 — canonical lens labeling
|
|
3
3
|
//
|
|
4
4
|
// Verifies that aria-pre-tool-gate.mjs and aria-stop-gate.mjs:
|
|
5
5
|
// 1. Show canonical Arabic lens names (nur, mizan, etc.) when the harness
|
|
6
6
|
// packet has isHamza/hamza=true (OWNER tier).
|
|
7
|
-
// 2.
|
|
8
|
-
// harness packet is client-surface (CLIENT tier).
|
|
7
|
+
// 2. Preserve those same canonical labels on client-surface execution too.
|
|
9
8
|
// 3. Gate enforcement (block on insufficient cognition) fires in both
|
|
10
|
-
// tiers
|
|
9
|
+
// tiers without renaming the lenses.
|
|
11
10
|
//
|
|
12
11
|
// Usage: node hooks/test-tier-lens-labeling.mjs
|
|
13
12
|
// Exit: 0 = all assertions passed, 1 = failure
|
|
@@ -24,9 +23,8 @@ const HOME = process.env.HOME || '/tmp';
|
|
|
24
23
|
const CLAUDE_DIR = `${HOME}/.claude`;
|
|
25
24
|
const PACKET_PATH = `${CLAUDE_DIR}/.aria-harness-last-packet.json`;
|
|
26
25
|
|
|
27
|
-
// ── Canonical
|
|
26
|
+
// ── Canonical label set ────────────────────────────────────────────────────
|
|
28
27
|
const CANONICAL = ['nur', 'mizan', 'hikma', 'tafakkur', 'tadabbur', 'ilham', 'wahi', 'firasah'];
|
|
29
|
-
const GENERIC = ['perception', 'balance', 'wisdom', 'reflection', 'foresight', 'insight', 'revelation', 'discernment'];
|
|
30
28
|
|
|
31
29
|
// ── Assertion helpers ───────────────────────────────────────────────────────
|
|
32
30
|
let passed = 0;
|
|
@@ -225,21 +223,17 @@ console.log('Test 1: aria-pre-tool-gate — OWNER tier shows canonical lens name
|
|
|
225
223
|
blockReason.slice(0, 400),
|
|
226
224
|
);
|
|
227
225
|
|
|
228
|
-
//
|
|
229
|
-
// (owner should only see canonical; absence of generic is nice-to-verify
|
|
230
|
-
// but not a hard requirement since some generic words may appear in prose)
|
|
231
|
-
// Instead check the first lens label is canonical not generic:
|
|
226
|
+
// The first visible lens label must be canonical.
|
|
232
227
|
const firstCanonicalIdx = blockReason.indexOf(`${CANONICAL[0]}:`);
|
|
233
|
-
const firstGenericIdx = blockReason.indexOf(`${GENERIC[0]}:`);
|
|
234
228
|
assert(
|
|
235
|
-
'T1: first lens label is canonical (nur:)
|
|
236
|
-
firstCanonicalIdx >= 0
|
|
237
|
-
`firstCanonicalIdx=${firstCanonicalIdx}
|
|
229
|
+
'T1: first lens label is canonical (nur:)',
|
|
230
|
+
firstCanonicalIdx >= 0,
|
|
231
|
+
`firstCanonicalIdx=${firstCanonicalIdx}`,
|
|
238
232
|
);
|
|
239
233
|
}
|
|
240
234
|
|
|
241
|
-
// ── Test 2: pre-tool-gate, CLIENT tier →
|
|
242
|
-
console.log('\nTest 2: aria-pre-tool-gate — CLIENT tier
|
|
235
|
+
// ── Test 2: pre-tool-gate, CLIENT tier → canonical labels in block reason ──
|
|
236
|
+
console.log('\nTest 2: aria-pre-tool-gate — CLIENT tier keeps canonical lens labels');
|
|
243
237
|
{
|
|
244
238
|
writeClientPacket();
|
|
245
239
|
writeEmptyTranscript(tmpTranscript);
|
|
@@ -259,32 +253,15 @@ console.log('\nTest 2: aria-pre-tool-gate — CLIENT tier shows generic lens lab
|
|
|
259
253
|
blockReason = result.stdout || '';
|
|
260
254
|
}
|
|
261
255
|
|
|
262
|
-
|
|
263
|
-
const hasGeneric = GENERIC.some((name) => blockReason.includes(`${name}:`));
|
|
256
|
+
const hasCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
|
|
264
257
|
assert(
|
|
265
|
-
'T2: block reason mentions
|
|
266
|
-
|
|
258
|
+
'T2: block reason mentions canonical label (e.g. nur:)',
|
|
259
|
+
hasCanonical,
|
|
267
260
|
blockReason.slice(0, 400),
|
|
268
261
|
);
|
|
269
|
-
|
|
270
|
-
// Must NOT expose canonical Arabic names
|
|
271
|
-
const exposesCanonical = CANONICAL.some((name) =>
|
|
272
|
-
blockReason.includes(`${name}:`),
|
|
273
|
-
);
|
|
274
|
-
assert(
|
|
275
|
-
'T2: block reason does NOT expose canonical Arabic lens names',
|
|
276
|
-
!exposesCanonical,
|
|
277
|
-
`canonical leak: ${CANONICAL.filter((n) => blockReason.includes(`${n}:`)).join(', ')} in: ${blockReason.slice(0, 300)}`,
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
// Check doctrine memory filename is neutralized (no feedback_no_flag_without_fix.md for client)
|
|
281
|
-
const noDocFileLeak = !blockReason.includes('feedback_no_flag_without_fix.md');
|
|
282
|
-
// (This specific file only appears in discovery-unresolved path; just verify
|
|
283
|
-
// that the block reason itself also neutralizes EIGHT_LENS_DOCTRINE references if any)
|
|
284
|
-
// For this test we just assert the generic label set is present.
|
|
285
262
|
assert(
|
|
286
|
-
'T2: first
|
|
287
|
-
blockReason.includes(`${
|
|
263
|
+
'T2: first visible lens label is canonical (nur:)',
|
|
264
|
+
blockReason.includes(`${CANONICAL[0]}:`),
|
|
288
265
|
blockReason.slice(0, 400),
|
|
289
266
|
);
|
|
290
267
|
}
|
|
@@ -318,8 +295,8 @@ console.log('\nTest 3: aria-stop-gate — OWNER tier shows canonical lens names'
|
|
|
318
295
|
);
|
|
319
296
|
}
|
|
320
297
|
|
|
321
|
-
// ── Test 4: stop-gate, CLIENT tier →
|
|
322
|
-
console.log('\nTest 4: aria-stop-gate — CLIENT tier
|
|
298
|
+
// ── Test 4: stop-gate, CLIENT tier → canonical labels ──────────────────────
|
|
299
|
+
console.log('\nTest 4: aria-stop-gate — CLIENT tier keeps canonical lens labels');
|
|
323
300
|
{
|
|
324
301
|
writeClientPacket();
|
|
325
302
|
writeNoCognitionStopTranscript(tmpTranscriptStop);
|
|
@@ -339,23 +316,16 @@ console.log('\nTest 4: aria-stop-gate — CLIENT tier shows generic labels, no c
|
|
|
339
316
|
blockReason = result.stdout || '';
|
|
340
317
|
}
|
|
341
318
|
|
|
342
|
-
const
|
|
319
|
+
const hasCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
|
|
343
320
|
assert(
|
|
344
|
-
'T4: stop-gate block reason mentions
|
|
345
|
-
|
|
321
|
+
'T4: stop-gate block reason mentions canonical label',
|
|
322
|
+
hasCanonical,
|
|
346
323
|
blockReason.slice(0, 400),
|
|
347
324
|
);
|
|
348
|
-
|
|
349
|
-
const exposesCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
|
|
350
|
-
assert(
|
|
351
|
-
'T4: stop-gate block reason does NOT expose canonical lens names',
|
|
352
|
-
!exposesCanonical,
|
|
353
|
-
`canonical leak: ${CANONICAL.filter((n) => blockReason.includes(`${n}:`)).join(', ')}`,
|
|
354
|
-
);
|
|
355
325
|
}
|
|
356
326
|
|
|
357
|
-
// ── Test 5: packet missing → defaults to
|
|
358
|
-
console.log('\nTest 5: missing packet cache → defaults to
|
|
327
|
+
// ── Test 5: packet missing → defaults to canonical labels (fail-safe) ─────
|
|
328
|
+
console.log('\nTest 5: missing packet cache → defaults to canonical labels');
|
|
359
329
|
{
|
|
360
330
|
// Remove the packet cache
|
|
361
331
|
try { unlinkSync(PACKET_PATH); } catch {}
|
|
@@ -376,13 +346,11 @@ console.log('\nTest 5: missing packet cache → defaults to CLIENT tier (fail-sa
|
|
|
376
346
|
blockReason = result.stdout || '';
|
|
377
347
|
}
|
|
378
348
|
|
|
379
|
-
|
|
380
|
-
const hasGeneric = GENERIC.some((name) => blockReason.includes(`${name}:`));
|
|
381
|
-
const exposesCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
|
|
349
|
+
const hasCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
|
|
382
350
|
assert(
|
|
383
|
-
'T5: defaults to
|
|
384
|
-
|
|
385
|
-
`
|
|
351
|
+
'T5: defaults to canonical labels when packet is absent',
|
|
352
|
+
hasCanonical,
|
|
353
|
+
`canonical=${hasCanonical} reason=${blockReason.slice(0, 300)}`,
|
|
386
354
|
);
|
|
387
355
|
}
|
|
388
356
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Aria Harness Gate — pre-tool cognition enforcement for OpenCode.
|
|
3
3
|
* Routes through HTTPHarnessClient SDK for substrate-backed validation.
|
|
4
4
|
*/
|
|
5
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
5
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
7
|
import { join } from 'node:path';
|
|
8
8
|
|
|
@@ -15,6 +15,7 @@ const SDK_CANDIDATES = [
|
|
|
15
15
|
const OWNER_TOKEN_PATH = join(HOME, '.aria', 'owner-token');
|
|
16
16
|
const LICENSE_PATH = join(HOME, '.aria', 'license.json');
|
|
17
17
|
const RUNTIME_URL = (process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319').replace(/\/+$/, '');
|
|
18
|
+
const RECEIPT_DIR = join(HOME, '.opencode', 'aria-mizan-receipts');
|
|
18
19
|
|
|
19
20
|
const DESTRUCTIVE_PATTERNS = [
|
|
20
21
|
{ rx: /(?:^|[;&|]\s*|\$\(\s*|`\s*)sudo\s+\S/, name: 'sudo' },
|
|
@@ -41,7 +42,14 @@ const DEPLOY_PATTERNS = [
|
|
|
41
42
|
{ rx: /\bdocker\s+build\b.*--push\b/, name: 'docker-build-push' },
|
|
42
43
|
];
|
|
43
44
|
|
|
44
|
-
const
|
|
45
|
+
const INLINE_LENS_NAMES = [
|
|
46
|
+
'nur', 'mizan', 'hikma', 'tafakkur', 'tadabbur', 'ilham', 'wahi', 'firasah',
|
|
47
|
+
'truth', 'harm', 'trust', 'power', 'reflection', 'context', 'impact', 'beauty',
|
|
48
|
+
];
|
|
49
|
+
const INLINE_LENS_RX = new RegExp(
|
|
50
|
+
`^\\s*#\\s*(?:${INLINE_LENS_NAMES.join('|')})\\s*:\\s*.{20,}`,
|
|
51
|
+
'gim',
|
|
52
|
+
);
|
|
45
53
|
const VERIFY_BLOCK_RX = /<verify>[\s\S]*?target\s*:[\s\S]*?verified\s*:[\s\S]*?axiom\s*:[\s\S]*?<\/verify>/i;
|
|
46
54
|
const TRIVIAL_BASH_RX = /^\s*(?:ls|cat|head|tail|grep|find|echo|wc|stat|which|pwd|date|file|du|df|ss|ps)\s/;
|
|
47
55
|
const SHORT_BASH_LIMIT = 30;
|
|
@@ -51,6 +59,26 @@ let _clientError = null;
|
|
|
51
59
|
let _lastMizanReceipt = null;
|
|
52
60
|
let _lastActionSummary = '';
|
|
53
61
|
|
|
62
|
+
function receiptPath(sessionId) {
|
|
63
|
+
return join(RECEIPT_DIR, `${String(sessionId || 'opencode').replace(/[^a-zA-Z0-9_-]/g, '_')}.json`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function persistReceipt(sessionId, payload) {
|
|
67
|
+
try {
|
|
68
|
+
mkdirSync(RECEIPT_DIR, { recursive: true, mode: 0o755 });
|
|
69
|
+
writeFileSync(receiptPath(sessionId), JSON.stringify(payload, null, 2) + '\n');
|
|
70
|
+
} catch {}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function countInlineCognitionLenses(text) {
|
|
74
|
+
const seen = new Set();
|
|
75
|
+
for (const match of String(text || '').matchAll(INLINE_LENS_RX)) {
|
|
76
|
+
const lens = match[0].match(/^\s*#\s*([a-z_ -]+)\s*:/i)?.[1]?.trim().toLowerCase();
|
|
77
|
+
if (lens) seen.add(lens);
|
|
78
|
+
}
|
|
79
|
+
return seen.size;
|
|
80
|
+
}
|
|
81
|
+
|
|
54
82
|
function resolveToken() {
|
|
55
83
|
if (process.env.ARIA_HARNESS_TOKEN) return process.env.ARIA_HARNESS_TOKEN;
|
|
56
84
|
if (process.env.ARIA_API_KEY) return process.env.ARIA_API_KEY;
|
|
@@ -151,9 +179,6 @@ export default async function HarnessGatePlugin(ctx) {
|
|
|
151
179
|
// Trivial reads pass
|
|
152
180
|
if (toolName === 'Bash' && TRIVIAL_BASH_RX.test(cmd) && cmd.length < 200) return;
|
|
153
181
|
if (toolName === 'Bash' && cmd.length < SHORT_BASH_LIMIT) return;
|
|
154
|
-
// Inline cognition in bash comments passes
|
|
155
|
-
if (toolName === 'Bash' && INLINE_LENS_RX.test(cmd)) return;
|
|
156
|
-
|
|
157
182
|
const destructive = DESTRUCTIVE_PATTERNS.find(({ rx }) => rx.test(cmd));
|
|
158
183
|
const deploy = DEPLOY_PATTERNS.find(({ rx }) => rx.test(cmd));
|
|
159
184
|
const isFileMutation = ['Edit', 'Write', 'NotebookEdit'].includes(toolName) && filePath;
|
|
@@ -182,6 +207,16 @@ export default async function HarnessGatePlugin(ctx) {
|
|
|
182
207
|
},
|
|
183
208
|
});
|
|
184
209
|
_lastMizanReceipt = pre.receipt || null;
|
|
210
|
+
if (_lastMizanReceipt) {
|
|
211
|
+
persistReceipt(sessionId, {
|
|
212
|
+
updatedAt: new Date().toISOString(),
|
|
213
|
+
sessionId,
|
|
214
|
+
receipt: _lastMizanReceipt,
|
|
215
|
+
preResult: pre.result || null,
|
|
216
|
+
preContract: pre.contract || null,
|
|
217
|
+
preSummary: pre.summary || null,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
185
220
|
_lastActionSummary = cmdPreview;
|
|
186
221
|
if (pre.receipt?.blocked || pre.result?.fitrahVetoed || pre.result?.reAuthorSignal) {
|
|
187
222
|
throw new Error(
|