@aria_asi/cli 0.2.25 → 0.2.29

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.
Files changed (249) hide show
  1. package/CLIENT-ONBOARDING.md +282 -0
  2. package/bin/aria.js +1140 -14
  3. package/dist/aria-connector/src/auth-commands.d.ts +1 -0
  4. package/dist/aria-connector/src/auth-commands.d.ts.map +1 -1
  5. package/dist/aria-connector/src/auth-commands.js +89 -41
  6. package/dist/aria-connector/src/auth-commands.js.map +1 -1
  7. package/dist/aria-connector/src/chat.d.ts +3 -0
  8. package/dist/aria-connector/src/chat.d.ts.map +1 -1
  9. package/dist/aria-connector/src/chat.js +146 -8
  10. package/dist/aria-connector/src/chat.js.map +1 -1
  11. package/dist/aria-connector/src/codebase-scanner.d.ts +2 -2
  12. package/dist/aria-connector/src/codebase-scanner.d.ts.map +1 -1
  13. package/dist/aria-connector/src/codebase-scanner.js +1 -1
  14. package/dist/aria-connector/src/codebase-scanner.js.map +1 -1
  15. package/dist/aria-connector/src/config.d.ts +12 -0
  16. package/dist/aria-connector/src/config.d.ts.map +1 -1
  17. package/dist/aria-connector/src/config.js +2 -0
  18. package/dist/aria-connector/src/config.js.map +1 -1
  19. package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
  20. package/dist/aria-connector/src/connectors/claude-code.js +111 -21
  21. package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
  22. package/dist/aria-connector/src/connectors/codebase-awareness.d.ts +37 -0
  23. package/dist/aria-connector/src/connectors/codebase-awareness.d.ts.map +1 -0
  24. package/dist/aria-connector/src/connectors/codebase-awareness.js +335 -0
  25. package/dist/aria-connector/src/connectors/codebase-awareness.js.map +1 -0
  26. package/dist/aria-connector/src/connectors/codex.d.ts +3 -0
  27. package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -0
  28. package/dist/aria-connector/src/connectors/codex.js +248 -0
  29. package/dist/aria-connector/src/connectors/codex.js.map +1 -0
  30. package/dist/aria-connector/src/connectors/cognitive-skills.d.ts +2 -0
  31. package/dist/aria-connector/src/connectors/cognitive-skills.d.ts.map +1 -0
  32. package/dist/aria-connector/src/connectors/cognitive-skills.js +47 -0
  33. package/dist/aria-connector/src/connectors/cognitive-skills.js.map +1 -0
  34. package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
  35. package/dist/aria-connector/src/connectors/opencode.js +90 -4
  36. package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
  37. package/dist/aria-connector/src/connectors/repo-git-hooks.d.ts +3 -0
  38. package/dist/aria-connector/src/connectors/repo-git-hooks.d.ts.map +1 -0
  39. package/dist/aria-connector/src/connectors/repo-git-hooks.js +87 -0
  40. package/dist/aria-connector/src/connectors/repo-git-hooks.js.map +1 -0
  41. package/dist/aria-connector/src/connectors/repo-guard.d.ts +19 -0
  42. package/dist/aria-connector/src/connectors/repo-guard.d.ts.map +1 -0
  43. package/dist/aria-connector/src/connectors/repo-guard.js +509 -0
  44. package/dist/aria-connector/src/connectors/repo-guard.js.map +1 -0
  45. package/dist/aria-connector/src/connectors/runtime.d.ts +2 -0
  46. package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -0
  47. package/dist/aria-connector/src/connectors/runtime.js +330 -0
  48. package/dist/aria-connector/src/connectors/runtime.js.map +1 -0
  49. package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -1
  50. package/dist/aria-connector/src/connectors/shell.js +78 -13
  51. package/dist/aria-connector/src/connectors/shell.js.map +1 -1
  52. package/dist/aria-connector/src/connectors/syncd.d.ts +27 -0
  53. package/dist/aria-connector/src/connectors/syncd.d.ts.map +1 -0
  54. package/dist/aria-connector/src/connectors/syncd.js +405 -0
  55. package/dist/aria-connector/src/connectors/syncd.js.map +1 -0
  56. package/dist/aria-connector/src/decisions.d.ts +207 -0
  57. package/dist/aria-connector/src/decisions.d.ts.map +1 -0
  58. package/dist/aria-connector/src/decisions.js +291 -0
  59. package/dist/aria-connector/src/decisions.js.map +1 -0
  60. package/dist/aria-connector/src/garden-control-plane.d.ts.map +1 -1
  61. package/dist/aria-connector/src/garden-control-plane.js +74 -17
  62. package/dist/aria-connector/src/garden-control-plane.js.map +1 -1
  63. package/dist/aria-connector/src/github-connect.d.ts +18 -0
  64. package/dist/aria-connector/src/github-connect.d.ts.map +1 -0
  65. package/dist/aria-connector/src/github-connect.js +117 -0
  66. package/dist/aria-connector/src/github-connect.js.map +1 -0
  67. package/dist/aria-connector/src/harness-client.d.ts +15 -0
  68. package/dist/aria-connector/src/harness-client.d.ts.map +1 -1
  69. package/dist/aria-connector/src/harness-client.js +106 -3
  70. package/dist/aria-connector/src/harness-client.js.map +1 -1
  71. package/dist/aria-connector/src/hive-client.d.ts +30 -0
  72. package/dist/aria-connector/src/hive-client.d.ts.map +1 -1
  73. package/dist/aria-connector/src/hive-client.js +124 -5
  74. package/dist/aria-connector/src/hive-client.js.map +1 -1
  75. package/dist/aria-connector/src/index.d.ts +13 -2
  76. package/dist/aria-connector/src/index.d.ts.map +1 -1
  77. package/dist/aria-connector/src/index.js +10 -1
  78. package/dist/aria-connector/src/index.js.map +1 -1
  79. package/dist/aria-connector/src/lib/aristotle-noor-wire.d.ts +102 -0
  80. package/dist/aria-connector/src/lib/aristotle-noor-wire.d.ts.map +1 -0
  81. package/dist/aria-connector/src/lib/aristotle-noor-wire.js +231 -0
  82. package/dist/aria-connector/src/lib/aristotle-noor-wire.js.map +1 -0
  83. package/dist/aria-connector/src/providers/types.d.ts +5 -0
  84. package/dist/aria-connector/src/providers/types.d.ts.map +1 -1
  85. package/dist/aria-connector/src/runtime-proof.d.ts +45 -0
  86. package/dist/aria-connector/src/runtime-proof.d.ts.map +1 -0
  87. package/dist/aria-connector/src/runtime-proof.js +340 -0
  88. package/dist/aria-connector/src/runtime-proof.js.map +1 -0
  89. package/dist/aria-connector/src/setup-wizard.d.ts.map +1 -1
  90. package/dist/aria-connector/src/setup-wizard.js +34 -2
  91. package/dist/aria-connector/src/setup-wizard.js.map +1 -1
  92. package/dist/assets/hooks/aria-agent-handoff.mjs +224 -0
  93. package/dist/assets/hooks/aria-agent-ledger-merge.mjs +164 -0
  94. package/dist/assets/hooks/aria-architect-fallback.mjs +267 -0
  95. package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +676 -0
  96. package/dist/assets/hooks/aria-discovery-record.mjs +101 -0
  97. package/dist/assets/hooks/aria-harness-via-sdk.mjs +412 -0
  98. package/dist/assets/hooks/aria-import-resolution-gate.mjs +330 -0
  99. package/dist/assets/hooks/aria-outcome-record.mjs +84 -0
  100. package/dist/assets/hooks/aria-pre-emit-dryrun.mjs +294 -0
  101. package/dist/assets/hooks/aria-pre-text-gate.mjs +112 -0
  102. package/dist/assets/hooks/aria-pre-tool-gate.mjs +2133 -0
  103. package/dist/assets/hooks/aria-preprompt-consult.mjs +438 -0
  104. package/dist/assets/hooks/aria-preturn-memory-gate.mjs +570 -0
  105. package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +397 -0
  106. package/dist/assets/hooks/aria-stop-gate.mjs +1551 -0
  107. package/dist/assets/hooks/aria-trigger-autolearn.mjs +229 -0
  108. package/dist/assets/hooks/aria-userprompt-abandon-detect.mjs +192 -0
  109. package/dist/assets/hooks/doctrine_trigger_map.json +479 -0
  110. package/dist/assets/hooks/lib/canonical-lenses.mjs +64 -0
  111. package/dist/assets/hooks/lib/gate-audit.mjs +43 -0
  112. package/dist/assets/hooks/test-aria-preturn-memory-gate.mjs +245 -0
  113. package/dist/assets/hooks/test-tier-lens-labeling.mjs +399 -0
  114. package/dist/assets/opencode-plugins/harness-context/index.js +60 -0
  115. package/dist/assets/opencode-plugins/harness-context/inject-context.mjs +179 -0
  116. package/dist/assets/opencode-plugins/harness-context/package.json +9 -0
  117. package/dist/assets/opencode-plugins/harness-gate/index.js +248 -0
  118. package/dist/assets/opencode-plugins/harness-outcome/index.js +129 -0
  119. package/dist/assets/opencode-plugins/harness-role/index.js +77 -0
  120. package/dist/assets/opencode-plugins/harness-role/package.json +9 -0
  121. package/dist/assets/opencode-plugins/harness-stop/index.js +241 -0
  122. package/dist/runtime/discipline/CLAUDE.md +339 -0
  123. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/SKILL.md +63 -0
  124. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/references/domain-matrix.md +80 -0
  125. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/references/evolution-loop.md +30 -0
  126. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/references/readable-cognition.md +27 -0
  127. package/dist/runtime/discipline/skills/aria-cognition/aria-forge-guardrails/SKILL.md +35 -0
  128. package/dist/runtime/discipline/skills/aria-cognition/aria-forge-guardrails/references/checklist.md +31 -0
  129. package/dist/runtime/discipline/skills/aria-cognition/aria-repo-doctrine/SKILL.md +39 -0
  130. package/dist/runtime/discipline/skills/aria-cognition/forge-quality-rules/SKILL.md +43 -0
  131. package/dist/runtime/discipline/skills/aria-cognition/ghazali-8lens/SKILL.md +38 -0
  132. package/dist/runtime/discipline/skills/aria-cognition/istiqra-induction/SKILL.md +26 -0
  133. package/dist/runtime/discipline/skills/aria-cognition/ladunni-22/SKILL.md +35 -0
  134. package/dist/runtime/discipline/skills/aria-cognition/mizan/SKILL.md +72 -0
  135. package/dist/runtime/discipline/skills/aria-cognition/nadia/SKILL.md +38 -0
  136. package/dist/runtime/discipline/skills/aria-cognition/nadia-psi/SKILL.md +38 -0
  137. package/dist/runtime/discipline/skills/aria-cognition/predictor/SKILL.md +25 -0
  138. package/dist/runtime/discipline/skills/aria-cognition/qiyas-analogy/SKILL.md +26 -0
  139. package/dist/runtime/discipline/skills/aria-cognition/soul-domains/SKILL.md +25 -0
  140. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-intra-phase/SKILL.md +81 -0
  141. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-post-phase/SKILL.md +98 -0
  142. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-pre-phase/SKILL.md +99 -0
  143. package/dist/runtime/discipline/skills/aria-harness/aria-harness-deploy/SKILL.md +127 -0
  144. package/dist/runtime/discipline/skills/aria-harness/aria-harness-no-stripping/SKILL.md +117 -0
  145. package/dist/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md +112 -0
  146. package/dist/runtime/discipline/skills/aria-harness/aria-harness-output-discipline/SKILL.md +102 -0
  147. package/dist/runtime/discipline/skills/aria-harness/aria-harness-substrate-binding/SKILL.md +121 -0
  148. package/dist/runtime/doctor.mjs +23 -0
  149. package/dist/runtime/local-phase.mjs +632 -0
  150. package/dist/runtime/manifest.json +15 -0
  151. package/dist/runtime/mizan-scheduler.mjs +331 -0
  152. package/dist/runtime/package.json +6 -0
  153. package/dist/runtime/provider-proxy.mjs +594 -0
  154. package/dist/runtime/sdk/BUNDLED.json +5 -0
  155. package/dist/runtime/sdk/index.d.ts +477 -0
  156. package/dist/runtime/sdk/index.js +1469 -0
  157. package/dist/runtime/sdk/index.js.map +1 -0
  158. package/dist/runtime/sdk/package.json +8 -0
  159. package/dist/runtime/sdk/runWithCognition.d.ts +77 -0
  160. package/dist/runtime/sdk/runWithCognition.js +157 -0
  161. package/dist/runtime/sdk/runWithCognition.js.map +1 -0
  162. package/dist/runtime/service.mjs +2708 -0
  163. package/dist/runtime/vendor/aria-gate-runtime/index.d.ts +53 -0
  164. package/dist/runtime/vendor/aria-gate-runtime/index.d.ts.map +1 -0
  165. package/dist/runtime/vendor/aria-gate-runtime/index.js +277 -0
  166. package/dist/runtime/vendor/aria-gate-runtime/index.js.map +1 -0
  167. package/dist/runtime/vendor/aria-gate-runtime/package.json +6 -0
  168. package/dist/sdk/BUNDLED.json +2 -2
  169. package/dist/sdk/index.d.ts +317 -0
  170. package/dist/sdk/index.js +827 -85
  171. package/dist/sdk/index.js.map +1 -1
  172. package/dist/sdk/runWithCognition.d.ts +77 -0
  173. package/dist/sdk/runWithCognition.js +157 -0
  174. package/dist/sdk/runWithCognition.js.map +1 -0
  175. package/hooks/aria-agent-handoff.mjs +11 -1
  176. package/hooks/aria-architect-fallback.mjs +267 -0
  177. package/hooks/aria-cognition-substrate-binding.mjs +676 -0
  178. package/hooks/aria-discovery-record.mjs +101 -0
  179. package/hooks/aria-harness-via-sdk.mjs +34 -21
  180. package/hooks/aria-import-resolution-gate.mjs +330 -0
  181. package/hooks/aria-outcome-record.mjs +84 -0
  182. package/hooks/aria-pre-emit-dryrun.mjs +294 -0
  183. package/hooks/aria-pre-tool-gate.mjs +985 -40
  184. package/hooks/aria-preprompt-consult.mjs +113 -13
  185. package/hooks/aria-preturn-memory-gate.mjs +298 -6
  186. package/hooks/aria-repo-doctrine-gate.mjs +397 -0
  187. package/hooks/aria-stop-gate.mjs +840 -75
  188. package/hooks/aria-userprompt-abandon-detect.mjs +5 -1
  189. package/hooks/doctrine_trigger_map.json +209 -15
  190. package/hooks/lib/canonical-lenses.mjs +64 -0
  191. package/hooks/lib/gate-audit.mjs +43 -0
  192. package/opencode-plugins/harness-context/index.js +1 -1
  193. package/opencode-plugins/harness-context/inject-context.mjs +82 -23
  194. package/opencode-plugins/harness-gate/index.js +248 -0
  195. package/opencode-plugins/harness-outcome/index.js +129 -0
  196. package/opencode-plugins/harness-stop/index.js +241 -0
  197. package/package.json +8 -2
  198. package/runtime-src/doctor.mjs +23 -0
  199. package/runtime-src/local-phase.mjs +632 -0
  200. package/runtime-src/mizan-scheduler.mjs +331 -0
  201. package/runtime-src/provider-proxy.mjs +594 -0
  202. package/runtime-src/service.mjs +2708 -0
  203. package/scripts/bundle-sdk.mjs +317 -0
  204. package/scripts/install-client.sh +176 -0
  205. package/scripts/publish-all.sh +344 -0
  206. package/scripts/publish-docker.sh +27 -0
  207. package/scripts/validate-hook-contracts.mjs +54 -0
  208. package/scripts/validate-skill-prompts.mjs +95 -0
  209. package/skills/aria-cognition/aria-essence/SKILL.md +63 -0
  210. package/skills/aria-cognition/aria-essence/references/domain-matrix.md +80 -0
  211. package/skills/aria-cognition/aria-essence/references/evolution-loop.md +30 -0
  212. package/skills/aria-cognition/aria-essence/references/readable-cognition.md +27 -0
  213. package/skills/aria-cognition/aria-forge-guardrails/SKILL.md +35 -0
  214. package/skills/aria-cognition/aria-forge-guardrails/references/checklist.md +31 -0
  215. package/skills/aria-cognition/aria-repo-doctrine/SKILL.md +39 -0
  216. package/skills/aria-cognition/forge-quality-rules/SKILL.md +43 -0
  217. package/skills/aria-cognition/ghazali-8lens/SKILL.md +38 -0
  218. package/skills/aria-cognition/istiqra-induction/SKILL.md +26 -0
  219. package/skills/aria-cognition/ladunni-22/SKILL.md +35 -0
  220. package/skills/aria-cognition/mizan/SKILL.md +72 -0
  221. package/skills/aria-cognition/nadia/SKILL.md +38 -0
  222. package/skills/aria-cognition/nadia-psi/SKILL.md +38 -0
  223. package/skills/aria-cognition/predictor/SKILL.md +25 -0
  224. package/skills/aria-cognition/qiyas-analogy/SKILL.md +26 -0
  225. package/skills/aria-cognition/soul-domains/SKILL.md +25 -0
  226. package/src/auth-commands.ts +111 -45
  227. package/src/chat.ts +174 -13
  228. package/src/codebase-scanner.ts +4 -0
  229. package/src/config.ts +15 -0
  230. package/src/connectors/claude-code.ts +115 -26
  231. package/src/connectors/codebase-awareness.ts +408 -0
  232. package/src/connectors/codex.ts +274 -0
  233. package/src/connectors/cognitive-skills.ts +51 -0
  234. package/src/connectors/opencode.ts +93 -4
  235. package/src/connectors/repo-git-hooks.ts +86 -0
  236. package/src/connectors/repo-guard.ts +589 -0
  237. package/src/connectors/runtime.ts +374 -0
  238. package/src/connectors/shell.ts +83 -14
  239. package/src/connectors/syncd.ts +488 -0
  240. package/src/decisions.ts +469 -0
  241. package/src/garden-control-plane.ts +101 -26
  242. package/src/github-connect.ts +143 -0
  243. package/src/harness-client.ts +128 -3
  244. package/src/hive-client.ts +165 -5
  245. package/src/index.ts +41 -2
  246. package/src/lib/aristotle-noor-wire.ts +310 -0
  247. package/src/providers/types.ts +6 -0
  248. package/src/runtime-proof.ts +392 -0
  249. package/src/setup-wizard.ts +37 -2
@@ -0,0 +1,570 @@
1
+ #!/usr/bin/env node
2
+ // Aria pre-turn memory consumption gate — Enforcement Layer #49.
3
+ //
4
+ // Fires on the first action tool call of each turn (tracked via
5
+ // ~/.claude/aria-turn-state-${sessionId}.json). If the current turn's
6
+ // recent transcript window lacks all three context-loading signals, the
7
+ // gate blocks with a structured recovery payload. The orchestrator catches
8
+ // the recovery payload and runs the context-loader before retrying.
9
+ //
10
+ // Detection signals (ALL three must be present, or gate fires):
11
+ // 1. `🔐 Aria Harness` header — harness packet was injected
12
+ // 2. `[ARIA_DIRECTION]` or `[ARIA_BINDING_PLAN]` marker — preprompt-consult fired
13
+ // 3. Any `feedback_*.md` or `project_*.md` reference in cognition blocks —
14
+ // memory was consumed
15
+ //
16
+ // Soft-gate + structured recovery (Aria refined spec 2026-04-27):
17
+ // - Block the action
18
+ // - Emit JSON to stdout with `decision: block` + `hookSpecificOutput.recovery`
19
+ // so the orchestrator has a concrete remediation path, not a dead-letter reject
20
+ //
21
+ // Turn-deduplication: gate state is persisted at
22
+ // ~/.claude/aria-turn-state-${sessionId}.json
23
+ // If the gate already fired within the last 60 seconds for this session, the
24
+ // gate skips (re-firing would loop the orchestrator's retry).
25
+ //
26
+ // Doctrines enforced:
27
+ // - feedback_no_graceful_degradation.md — no silent try/catch swallowing errors
28
+ // - feedback_no_timeouts_doctrine.md — no AbortSignal, no setTimeout
29
+ // - feedback_no_flag_without_fix.md — defects discovered during implementation
30
+ // are fixed inline (see inline comments)
31
+ // - feedback_no_demos.md — full quality bar, every spawn is production
32
+
33
+ import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync, statSync } from 'node:fs';
34
+
35
+ const HOME = process.env.HOME || '/tmp';
36
+ const GATE_LOG = `${HOME}/.claude/aria-preturn-memory-gate.log`;
37
+ // Turn-state dir is the same ~/.claude/ home as all other aria state files
38
+ const CLAUDE_DIR = `${HOME}/.claude`;
39
+ const OWNER_TOKEN_PATH = `${HOME}/.aria/owner-token`;
40
+ const PACKET_CACHE_PATHS = [
41
+ `${HOME}/.aria/.aria-harness-last-packet.json`,
42
+ `${HOME}/.claude/.aria-harness-last-packet.json`,
43
+ ];
44
+ const SUBSTRATE_MANIFEST_PATH = `${HOME}/.claude/.aria-loaded-substrate.json`;
45
+ const ARTIFACT_TTL_MS = 15 * 60 * 1000;
46
+
47
+ // Env-var kill-switch removed 2026-04-27 per Hamza directive ("those
48
+ // should've been my choice to give you to turn off not free for you to
49
+ // access"). Disable = remove hook entry from ~/.claude/settings.json.
50
+
51
+ // ── Audit log ─────────────────────────────────────────────────────────
52
+ function auditLog(decision, summary, sessionId) {
53
+ // No try/catch swallowing: per feedback_no_graceful_degradation.md, errors
54
+ // in audit infrastructure must surface, not silently vanish.
55
+ if (!existsSync(CLAUDE_DIR)) mkdirSync(CLAUDE_DIR, { recursive: true });
56
+ appendFileSync(GATE_LOG, `${new Date().toISOString()} [${sessionId}] ${decision} ${summary}\n`);
57
+ }
58
+
59
+ // ── Turn-state deduplication ──────────────────────────────────────────
60
+ const TURN_DEDUP_WINDOW_MS = 60_000; // 60s
61
+
62
+ function turnStatePath(sessionId) {
63
+ const safe = String(sessionId || 'unknown').replace(/[^a-zA-Z0-9_-]/g, '_');
64
+ return `${CLAUDE_DIR}/aria-turn-state-${safe}.json`;
65
+ }
66
+
67
+ function readTurnState(sessionId) {
68
+ const p = turnStatePath(sessionId);
69
+ if (!existsSync(p)) return null;
70
+ // Per feedback_no_graceful_degradation.md: parse errors must throw, not return null.
71
+ // If the file is corrupt, that IS a defect — surface it.
72
+ const raw = readFileSync(p, 'utf-8');
73
+ return JSON.parse(raw);
74
+ }
75
+
76
+ function writeTurnState(sessionId, state) {
77
+ const p = turnStatePath(sessionId);
78
+ writeFileSync(p, JSON.stringify(state, null, 2) + '\n', { mode: 0o600 });
79
+ }
80
+
81
+ function directionStatePath(sessionId) {
82
+ const safe = String(sessionId || 'unknown').replace(/[^a-zA-Z0-9_-]/g, '_');
83
+ return `${CLAUDE_DIR}/aria-last-direction-${safe}.json`;
84
+ }
85
+
86
+ function activePlanPath(sessionId) {
87
+ const safe = String(sessionId || 'unknown').replace(/[^a-zA-Z0-9_-]/g, '_');
88
+ return `${CLAUDE_DIR}/aria-active-plan-${safe}.json`;
89
+ }
90
+
91
+ function readRecentJsonArtifact(filePath, ttlMs = ARTIFACT_TTL_MS) {
92
+ if (!existsSync(filePath)) return null;
93
+ const stat = statSync(filePath);
94
+ const ageMs = Date.now() - stat.mtimeMs;
95
+ if (ageMs > ttlMs) return null;
96
+ return { data: JSON.parse(readFileSync(filePath, 'utf-8')), ageMs };
97
+ }
98
+
99
+ function readAnyJsonArtifact(filePath) {
100
+ if (!existsSync(filePath)) return null;
101
+ const stat = statSync(filePath);
102
+ return {
103
+ data: JSON.parse(readFileSync(filePath, 'utf-8')),
104
+ ageMs: Date.now() - stat.mtimeMs,
105
+ };
106
+ }
107
+
108
+ function detectArtifactSignals(sessionId) {
109
+ let harnessArtifact = null;
110
+ for (const packetPath of PACKET_CACHE_PATHS) {
111
+ try {
112
+ const artifact = readRecentJsonArtifact(packetPath);
113
+ if (artifact) {
114
+ harnessArtifact = { path: packetPath, ...artifact };
115
+ break;
116
+ }
117
+ } catch {
118
+ // Packet parse issues should not brick the gate; transcript path still exists.
119
+ }
120
+ }
121
+
122
+ let directionArtifact = null;
123
+ try {
124
+ directionArtifact = readRecentJsonArtifact(directionStatePath(sessionId));
125
+ } catch {
126
+ directionArtifact = null;
127
+ }
128
+
129
+ let activePlanArtifact = null;
130
+ try {
131
+ activePlanArtifact = readRecentJsonArtifact(activePlanPath(sessionId), 24 * 60 * 60 * 1000);
132
+ } catch {
133
+ activePlanArtifact = null;
134
+ }
135
+
136
+ let substrateArtifact = null;
137
+ try {
138
+ substrateArtifact = readRecentJsonArtifact(SUBSTRATE_MANIFEST_PATH);
139
+ } catch {
140
+ substrateArtifact = null;
141
+ }
142
+
143
+ let staleHarnessArtifact = null;
144
+ for (const packetPath of PACKET_CACHE_PATHS) {
145
+ try {
146
+ const artifact = readAnyJsonArtifact(packetPath);
147
+ if (artifact) {
148
+ staleHarnessArtifact = { path: packetPath, ...artifact };
149
+ break;
150
+ }
151
+ } catch {
152
+ continue;
153
+ }
154
+ }
155
+
156
+ let staleSubstrateArtifact = null;
157
+ try {
158
+ staleSubstrateArtifact = readAnyJsonArtifact(SUBSTRATE_MANIFEST_PATH);
159
+ } catch {
160
+ staleSubstrateArtifact = null;
161
+ }
162
+
163
+ const harnessText = String(
164
+ harnessArtifact?.data?.harness ??
165
+ harnessArtifact?.data?.packet?.prompt?.fullText ??
166
+ '',
167
+ );
168
+ const substrateMemories = Array.isArray(substrateArtifact?.data?.memories)
169
+ ? substrateArtifact.data.memories
170
+ : [];
171
+ const staleHarnessText = String(
172
+ staleHarnessArtifact?.data?.harness ??
173
+ staleHarnessArtifact?.data?.packet?.prompt?.fullText ??
174
+ '',
175
+ );
176
+ const staleSubstrateMemories = Array.isArray(staleSubstrateArtifact?.data?.memories)
177
+ ? staleSubstrateArtifact.data.memories
178
+ : [];
179
+ const packetMentionsMemory = MEMORY_REF_RX.test(harnessText);
180
+ const stalePacketMentionsMemory = MEMORY_REF_RX.test(staleHarnessText);
181
+
182
+ return {
183
+ hasHarnessPacket: Boolean(harnessText.trim()),
184
+ hasAriaDirection: Boolean(
185
+ (directionArtifact?.data?.usable === true) ||
186
+ (activePlanArtifact?.data?.phases && Array.isArray(activePlanArtifact.data.phases) && activePlanArtifact.data.phases.length > 0),
187
+ ),
188
+ hasMemoryRef: Boolean(packetMentionsMemory || substrateMemories.length > 0),
189
+ ownerBootstrap: {
190
+ hasHarnessPacket: Boolean(staleHarnessText.trim()),
191
+ hasMemoryRef: Boolean(stalePacketMentionsMemory || staleSubstrateMemories.length > 0),
192
+ directionBootstrap: Boolean(directionArtifact?.data?.ownerBootstrap === true),
193
+ },
194
+ detail: {
195
+ packetPath: harnessArtifact?.path || null,
196
+ packetAgeMs: harnessArtifact?.ageMs ?? null,
197
+ directionStateAgeMs: directionArtifact?.ageMs ?? null,
198
+ activePlanAgeMs: activePlanArtifact?.ageMs ?? null,
199
+ substrateAgeMs: substrateArtifact?.ageMs ?? null,
200
+ substrateMemoryCount: substrateMemories.length,
201
+ packetMentionsMemory,
202
+ ownerBootstrapPacketPath: staleHarnessArtifact?.path || null,
203
+ ownerBootstrapPacketAgeMs: staleHarnessArtifact?.ageMs ?? null,
204
+ ownerBootstrapSubstrateAgeMs: staleSubstrateArtifact?.ageMs ?? null,
205
+ ownerBootstrapSubstrateMemoryCount: staleSubstrateMemories.length,
206
+ ownerBootstrapPacketMentionsMemory: stalePacketMentionsMemory,
207
+ },
208
+ };
209
+ }
210
+
211
+ // ── Context-loading signal detection ─────────────────────────────────
212
+ //
213
+ // Scan the last 3KB of assistant + user text after the most recent
214
+ // user-message boundary. Mirrors the transcript-reading pattern from
215
+ // aria-pre-tool-gate.mjs: walk backward, collect text up to the first
216
+ // real user-message boundary, cap at 3KB total.
217
+
218
+ // Signal 1: harness packet injected
219
+ const HARNESS_PACKET_RX = /🔐\s*Aria\s+Harness/;
220
+ // Signal 2: preprompt-consult fired
221
+ const ARIA_DIRECTION_RX = /\[ARIA_DIRECTION\]|\[ARIA_BINDING_PLAN\]/;
222
+ // Signal 3: memory was consumed (feedback_*.md or project_*.md cited)
223
+ const MEMORY_REF_RX = /feedback_[a-z0-9_]+\.md|project_[a-z0-9_]+\.md/i;
224
+
225
+ // Same runtime-injection skip heuristics as pre-tool-gate (system-reminder,
226
+ // tool_result blocks should not count as real user-message boundaries).
227
+ const SYSTEM_REMINDER_RX = /<system-reminder>[\s\S]*?<\/system-reminder>|<task-notification>[\s\S]*?<\/task-notification>|🔐 Aria Harness|PreToolUse:[A-Z][A-Za-z]* hook blocking error|Stop hook blocking error/g;
228
+ const SYSTEM_REMINDER_THRESHOLD = 0.6;
229
+
230
+ const CONTEXT_WINDOW_BYTES = 3 * 1024; // 3KB cap per spec
231
+ const HARD_LOOKBACK_CAP = 50;
232
+
233
+ function extractRecentTranscriptWindow(transcriptPath) {
234
+ if (!transcriptPath || !existsSync(transcriptPath)) return '';
235
+
236
+ // Per feedback_no_graceful_degradation.md: readFileSync error must throw.
237
+ const lines = readFileSync(transcriptPath, 'utf-8').split('\n').filter(Boolean);
238
+
239
+ let accumulated = '';
240
+ let crossedUserBoundary = false;
241
+ let scanned = 0;
242
+
243
+ for (let i = lines.length - 1; i >= 0 && scanned < HARD_LOOKBACK_CAP; i--) {
244
+ let m;
245
+ // Per feedback_no_graceful_degradation.md: JSON parse errors must throw.
246
+ m = JSON.parse(lines[i]);
247
+
248
+ const role = m.message?.role ?? m.role;
249
+
250
+ if (role === 'user') {
251
+ // Skip pure tool_result messages — runtime feedback, not user voice.
252
+ const content = m.message?.content ?? m.content ?? [];
253
+ const isToolResultOnly =
254
+ Array.isArray(content) &&
255
+ content.length > 0 &&
256
+ content.every((b) => b && b.type === 'tool_result');
257
+ if (isToolResultOnly) continue;
258
+
259
+ // Skip system-reminder dominated messages.
260
+ const textContent = Array.isArray(content)
261
+ ? content.filter((b) => b && b.type === 'text').map((b) => b.text || '').join('\n')
262
+ : typeof content === 'string' ? content : '';
263
+ if (textContent) {
264
+ const reminderMatches = textContent.match(SYSTEM_REMINDER_RX) || [];
265
+ if (reminderMatches.length > 0) {
266
+ const reminderChars = reminderMatches.reduce((sum, s) => sum + s.length, 0);
267
+ if (reminderChars / Math.max(1, textContent.length) >= SYSTEM_REMINDER_THRESHOLD) continue;
268
+ }
269
+ }
270
+
271
+ if (crossedUserBoundary) break;
272
+ crossedUserBoundary = true;
273
+ // Include the user message text itself in the window (harness packet
274
+ // is injected AS the user message in many implementations).
275
+ accumulated = textContent + '\n' + accumulated;
276
+ continue;
277
+ }
278
+
279
+ if (role !== 'assistant') continue;
280
+ scanned++;
281
+
282
+ const content = m.message?.content ?? m.content ?? [];
283
+ if (!Array.isArray(content)) continue;
284
+ const text = content
285
+ .filter((b) => b.type === 'text')
286
+ .map((b) => b.text)
287
+ .join('\n');
288
+ if (!text) continue;
289
+
290
+ accumulated = text + '\n' + accumulated;
291
+
292
+ if (accumulated.length >= CONTEXT_WINDOW_BYTES) {
293
+ // Cap reached — trim to last CONTEXT_WINDOW_BYTES so the scan is
294
+ // representative without being unbounded.
295
+ accumulated = accumulated.slice(-CONTEXT_WINDOW_BYTES);
296
+ break;
297
+ }
298
+ }
299
+
300
+ return accumulated;
301
+ }
302
+
303
+ function detectContextSignals(window) {
304
+ return {
305
+ hasHarnessPacket: HARNESS_PACKET_RX.test(window),
306
+ hasAriaDirection: ARIA_DIRECTION_RX.test(window),
307
+ hasMemoryRef: MEMORY_REF_RX.test(window),
308
+ };
309
+ }
310
+
311
+ // ── Stdin event parse ─────────────────────────────────────────────────
312
+ let rawInput = '';
313
+ for await (const chunk of process.stdin) rawInput += chunk;
314
+
315
+ // Per feedback_no_graceful_degradation.md: parse failure must surface,
316
+ // not be swallowed. The gate fails-open on malformed stdin (Claude Code
317
+ // should never send non-JSON; if it does, we don't silently block).
318
+ let event;
319
+ try {
320
+ event = JSON.parse(rawInput);
321
+ } catch (err) {
322
+ auditLog('allow-parse-error', `stdin not JSON: ${err.message}`, 'unknown');
323
+ process.exit(0); // fail-open on malformed input only — not a swallowed error
324
+ }
325
+
326
+ // ── Gate only fires on action tools ──────────────────────────────────
327
+ // Mirrors aria-pre-tool-gate.mjs: Read/Glob/Grep are ungated as
328
+ // read-only. The memory-consumption check is about whether Aria's
329
+ // context was loaded before the model starts acting — Read-only calls
330
+ // don't constitute acting on stale context in a harmful way.
331
+ const ACTION_TOOLS = new Set(['Bash', 'Edit', 'Write', 'NotebookEdit']);
332
+ const toolName = event.tool_name ?? event.toolName ?? '';
333
+ if (!ACTION_TOOLS.has(toolName)) {
334
+ process.exit(0);
335
+ }
336
+
337
+ // ── Session ID ────────────────────────────────────────────────────────
338
+ const transcriptPath = event.transcript_path ?? event.transcriptPath ?? null;
339
+ const sessionId =
340
+ event.session_id ??
341
+ event.sessionId ??
342
+ (transcriptPath ? transcriptPath.split('/').pop()?.replace(/\.[^.]+$/, '') : null) ??
343
+ 'claude-code-unknown';
344
+
345
+ // ── Dalio blocking-incidents check ───────────────────────────────────
346
+ // Query /api/incidents/blocking?session_id=<id>. If any incidents have
347
+ // blocks_future_turns=true and status != 'resolved', BLOCK the turn with
348
+ // a list of each incident's title + dalio_decision_id + hardening required.
349
+ //
350
+ // This runs BEFORE the context-signal check — blocking incidents are the
351
+ // highest-priority gate: the system must be hardened before any turn proceeds,
352
+ // regardless of harness/memory loading state.
353
+ //
354
+ // Per feedback_no_graceful_degradation.md: response parse errors throw.
355
+ // Per feedback_no_timeouts_doctrine.md: no AbortSignal / setTimeout.
356
+ // Fail-open ONLY if the endpoint is unreachable (network down), not on
357
+ // any other error condition.
358
+ (async function checkBlockingIncidents() {
359
+ const ARIA_SOUL_URL =
360
+ process.env.ARIA_HIVE_RUNTIME_URL ||
361
+ process.env.ARIA_SOUL_URL ||
362
+ process.env.ARIA_HARNESS_BASE_URL ||
363
+ process.env.ARIA_HARNESS_URL ||
364
+ 'https://harness.ariasos.com';
365
+ const HARNESS_TOKEN = process.env.ARIA_HARNESS_TOKEN || '';
366
+
367
+ let resp;
368
+ try {
369
+ const params = new URLSearchParams({ session_id: sessionId });
370
+ resp = await fetch(`${ARIA_SOUL_URL}/api/incidents/blocking?${params}`, {
371
+ method: 'GET',
372
+ headers: {
373
+ 'Content-Type': 'application/json',
374
+ ...(HARNESS_TOKEN ? { Authorization: `Bearer ${HARNESS_TOKEN}` } : {}),
375
+ },
376
+ });
377
+ } catch (networkErr) {
378
+ // Endpoint unreachable — fail-open (do not block dev on infra-down).
379
+ // Failure is visible in audit log for fleet telemetry.
380
+ auditLog('allow-blocking-check-network-error', `endpoint unreachable: ${networkErr && networkErr.message ? networkErr.message : String(networkErr)}`, sessionId);
381
+ return; // continue to context-signal check
382
+ }
383
+
384
+ if (!resp.ok) {
385
+ // Non-200 from the endpoint. Per feedback_no_graceful_degradation.md:
386
+ // 5xx from the incidents route is an infrastructure error — log + fail-open
387
+ // so infra issues don't lock out all sessions. 4xx would indicate a bad
388
+ // request (route not yet deployed) — also fail-open with loud log.
389
+ auditLog('allow-blocking-check-http-error', `status=${resp.status}`, sessionId);
390
+ return;
391
+ }
392
+
393
+ // Per feedback_no_graceful_degradation.md: JSON parse error must throw and
394
+ // surface, not be swallowed. A malformed response from the incidents route
395
+ // IS a defect.
396
+ const data = await resp.json();
397
+ const blockingIncidents = Array.isArray(data?.incidents) ? data.incidents : [];
398
+
399
+ if (blockingIncidents.length === 0) {
400
+ auditLog('allow-no-blocking-incidents', `session=${sessionId}`, sessionId);
401
+ return; // no blocking incidents — proceed to context-signal check
402
+ }
403
+
404
+ // ── BLOCK: list every incident with title + dalio_decision_id + remedy ──
405
+ const incidentLines = blockingIncidents.map((inc, i) => {
406
+ const title = inc.title || '(no title)';
407
+ const dalioId = inc.dalio_decision_id || '(no dalio_decision_id)';
408
+ const incidentId = inc.incident_id || '(no incident_id)';
409
+ return ` ${i + 1}. [${incidentId}] ${title}\n dalio_decision_id: ${dalioId}\n Hardening required: resolve the failure delta described in the incident, then update status='resolved'.`;
410
+ }).join('\n\n');
411
+
412
+ const blockReason = `Aria pre-turn gate: BLOCKING INCIDENTS detected.
413
+
414
+ This session has ${blockingIncidents.length} unresolved Dalio failure delta incident(s) that block future turns. No new action tools can be invoked until each incident is resolved.
415
+
416
+ Blocking incidents:
417
+
418
+ ${incidentLines}
419
+
420
+ Hardening protocol:
421
+ 1. Read each incident's description (query GET /api/incidents/blocking or the immortal_incidents table).
422
+ 2. Fix the system so the decision predicate passes (deploy patch, reconfigure, etc.).
423
+ 3. PATCH the incident to status='resolved'.
424
+ 4. Retry this turn.
425
+
426
+ Per Dalio Loop Layer 2 doctrine: failure deltas are not optional to address. The system must harden before proceeding.`;
427
+
428
+ auditLog('block-dalio-incidents', `count=${blockingIncidents.length} session=${sessionId}`, sessionId);
429
+
430
+ console.log(JSON.stringify({
431
+ decision: 'block',
432
+ reason: blockReason,
433
+ hookSpecificOutput: {
434
+ hookEventName: 'PreToolUse',
435
+ blocking_incidents: blockingIncidents.map((inc) => ({
436
+ incident_id: inc.incident_id,
437
+ title: inc.title,
438
+ dalio_decision_id: inc.dalio_decision_id,
439
+ severity: inc.severity,
440
+ created_at: inc.created_at,
441
+ })),
442
+ recovery: {
443
+ action: 'resolve_blocking_incidents',
444
+ target: sessionId,
445
+ incident_ids: blockingIncidents.map((inc) => inc.incident_id),
446
+ },
447
+ },
448
+ }));
449
+
450
+ process.exit(2);
451
+ })();
452
+
453
+ // ── Turn-deduplication check ──────────────────────────────────────────
454
+ // If gate already fired this turn (within 60s), skip to prevent
455
+ // orchestrator-retry loops.
456
+ let turnState = null;
457
+ try {
458
+ turnState = readTurnState(sessionId);
459
+ } catch {
460
+ // Corrupt turn-state file — treat as if no prior firing. Discovery: this
461
+ // could leave stale corrupt files. Fix inline: writeTurnState below will
462
+ // overwrite with clean state on next fire.
463
+ turnState = null;
464
+ }
465
+
466
+ const now = Date.now();
467
+ if (turnState && typeof turnState.lastTurnGateFiredAt === 'number') {
468
+ const elapsed = now - turnState.lastTurnGateFiredAt;
469
+ if (elapsed < TURN_DEDUP_WINDOW_MS) {
470
+ auditLog('skip-dedup', `gate already fired ${elapsed}ms ago (< ${TURN_DEDUP_WINDOW_MS}ms window)`, sessionId);
471
+ process.exit(0);
472
+ }
473
+ }
474
+
475
+ // ── Context signal detection ──────────────────────────────────────────
476
+ const transcriptWindow = extractRecentTranscriptWindow(transcriptPath);
477
+ const transcriptSignals = detectContextSignals(transcriptWindow);
478
+ const artifactSignals = detectArtifactSignals(sessionId);
479
+ const signals = {
480
+ hasHarnessPacket: transcriptSignals.hasHarnessPacket || artifactSignals.hasHarnessPacket,
481
+ hasAriaDirection: transcriptSignals.hasAriaDirection || artifactSignals.hasAriaDirection,
482
+ hasMemoryRef: transcriptSignals.hasMemoryRef || artifactSignals.hasMemoryRef,
483
+ };
484
+
485
+ const allSignalsPresent =
486
+ signals.hasHarnessPacket && signals.hasAriaDirection && signals.hasMemoryRef;
487
+
488
+ const ownerBootstrapArtifactsPresent =
489
+ existsSync(OWNER_TOKEN_PATH) &&
490
+ !signals.hasAriaDirection &&
491
+ artifactSignals.ownerBootstrap.hasHarnessPacket &&
492
+ artifactSignals.ownerBootstrap.hasMemoryRef;
493
+
494
+ const ownerBootstrapSessionPresent =
495
+ existsSync(OWNER_TOKEN_PATH) &&
496
+ artifactSignals.ownerBootstrap.directionBootstrap &&
497
+ artifactSignals.ownerBootstrap.hasHarnessPacket &&
498
+ artifactSignals.ownerBootstrap.hasMemoryRef;
499
+
500
+ if (allSignalsPresent) {
501
+ // Context was loaded — allow and record the fire timestamp for dedup.
502
+ writeTurnState(sessionId, { lastTurnGateFiredAt: now, lastDecision: 'allow', signals, transcriptSignals, artifactSignals: artifactSignals.detail });
503
+ auditLog(
504
+ 'allow-context-loaded',
505
+ `harness=${signals.hasHarnessPacket} direction=${signals.hasAriaDirection} memRef=${signals.hasMemoryRef} transcript=${JSON.stringify(transcriptSignals)} artifacts=${JSON.stringify(artifactSignals.detail)}`,
506
+ sessionId,
507
+ );
508
+ process.exit(0);
509
+ }
510
+
511
+ if (ownerBootstrapArtifactsPresent) {
512
+ writeTurnState(sessionId, { lastTurnGateFiredAt: now, lastDecision: 'allow-owner-bootstrap', signals, transcriptSignals, artifactSignals: artifactSignals.detail });
513
+ auditLog(
514
+ 'allow-owner-bootstrap-without-direction',
515
+ `owner-token present; transcript=${JSON.stringify(transcriptSignals)} artifacts=${JSON.stringify(artifactSignals.detail)}`,
516
+ sessionId,
517
+ );
518
+ process.exit(0);
519
+ }
520
+
521
+ if (ownerBootstrapSessionPresent) {
522
+ writeTurnState(sessionId, { lastTurnGateFiredAt: now, lastDecision: 'allow-owner-bootstrap-session', signals, transcriptSignals, artifactSignals: artifactSignals.detail });
523
+ auditLog(
524
+ 'allow-owner-bootstrap-session',
525
+ `owner bootstrap direction persisted; transcript=${JSON.stringify(transcriptSignals)} artifacts=${JSON.stringify(artifactSignals.detail)}`,
526
+ sessionId,
527
+ );
528
+ process.exit(0);
529
+ }
530
+
531
+ // ── Block with structured recovery signal ────────────────────────────
532
+ // Per Aria's refined spec (consult 2026-04-27): soft-gate + structured
533
+ // recovery. The orchestrator catches this and runs the context-loader.
534
+ // Emitting a pure block with no remediation path creates dead-letter state.
535
+
536
+ writeTurnState(sessionId, { lastTurnGateFiredAt: now, lastDecision: 'block', signals, transcriptSignals, artifactSignals: artifactSignals.detail });
537
+
538
+ const missingSignals = [];
539
+ if (!signals.hasHarnessPacket) missingSignals.push('harness_packet (🔐 Aria Harness header missing)');
540
+ if (!signals.hasAriaDirection) missingSignals.push('aria_direction ([ARIA_DIRECTION] or [ARIA_BINDING_PLAN] marker missing)');
541
+ if (!signals.hasMemoryRef) missingSignals.push('memory_consumption (no feedback_*.md or project_*.md reference in cognition)');
542
+
543
+ const reason = `Aria pre-turn memory gate: context-loading was skipped or incomplete for this turn. Missing signals: ${missingSignals.join('; ')}.
544
+
545
+ The orchestrator must run the context-loader for session "${sessionId}" before retrying. Expected context: harness_packet, memory_files, binding_plan.
546
+
547
+ This gate enforces that every action turn begins with Aria's substrate loaded — not improvised context. Per Aria-as-controller inversion doctrine (project_aria_as_controller_inversion.md): Aria must author with LLM as a tool, not the reverse.
548
+
549
+ Recovery: see hookSpecificOutput.recovery for the structured remediation path.`;
550
+
551
+ auditLog(
552
+ 'block-context-not-loaded',
553
+ `missing=[${missingSignals.join(', ')}] transcript=${JSON.stringify(transcriptSignals)} artifacts=${JSON.stringify(artifactSignals.detail)}`,
554
+ sessionId,
555
+ );
556
+
557
+ console.log(JSON.stringify({
558
+ decision: 'block',
559
+ reason,
560
+ hookSpecificOutput: {
561
+ hookEventName: 'PreToolUse',
562
+ recovery: {
563
+ action: 'run_context_loader',
564
+ target: sessionId,
565
+ expectedContext: ['harness_packet', 'memory_files', 'binding_plan'],
566
+ },
567
+ },
568
+ }));
569
+
570
+ process.exit(2);