@aria_asi/cli 0.2.26 → 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 (248) 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 +80 -24
  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 +283 -0
  170. package/dist/sdk/index.js +622 -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 +109 -40
  177. package/hooks/aria-cognition-substrate-binding.mjs +676 -0
  178. package/hooks/aria-harness-via-sdk.mjs +34 -21
  179. package/hooks/aria-import-resolution-gate.mjs +330 -0
  180. package/hooks/aria-outcome-record.mjs +5 -1
  181. package/hooks/aria-pre-emit-dryrun.mjs +294 -0
  182. package/hooks/aria-pre-tool-gate.mjs +828 -41
  183. package/hooks/aria-preprompt-consult.mjs +113 -13
  184. package/hooks/aria-preturn-memory-gate.mjs +298 -6
  185. package/hooks/aria-repo-doctrine-gate.mjs +397 -0
  186. package/hooks/aria-stop-gate.mjs +739 -76
  187. package/hooks/aria-userprompt-abandon-detect.mjs +5 -1
  188. package/hooks/doctrine_trigger_map.json +209 -15
  189. package/hooks/lib/canonical-lenses.mjs +64 -0
  190. package/hooks/lib/gate-audit.mjs +43 -0
  191. package/opencode-plugins/harness-context/index.js +1 -1
  192. package/opencode-plugins/harness-context/inject-context.mjs +82 -23
  193. package/opencode-plugins/harness-gate/index.js +248 -0
  194. package/opencode-plugins/harness-outcome/index.js +129 -0
  195. package/opencode-plugins/harness-stop/index.js +241 -0
  196. package/package.json +8 -2
  197. package/runtime-src/doctor.mjs +23 -0
  198. package/runtime-src/local-phase.mjs +632 -0
  199. package/runtime-src/mizan-scheduler.mjs +331 -0
  200. package/runtime-src/provider-proxy.mjs +594 -0
  201. package/runtime-src/service.mjs +2708 -0
  202. package/scripts/bundle-sdk.mjs +317 -0
  203. package/scripts/install-client.sh +176 -0
  204. package/scripts/publish-all.sh +344 -0
  205. package/scripts/publish-docker.sh +27 -0
  206. package/scripts/validate-hook-contracts.mjs +54 -0
  207. package/scripts/validate-skill-prompts.mjs +95 -0
  208. package/skills/aria-cognition/aria-essence/SKILL.md +63 -0
  209. package/skills/aria-cognition/aria-essence/references/domain-matrix.md +80 -0
  210. package/skills/aria-cognition/aria-essence/references/evolution-loop.md +30 -0
  211. package/skills/aria-cognition/aria-essence/references/readable-cognition.md +27 -0
  212. package/skills/aria-cognition/aria-forge-guardrails/SKILL.md +35 -0
  213. package/skills/aria-cognition/aria-forge-guardrails/references/checklist.md +31 -0
  214. package/skills/aria-cognition/aria-repo-doctrine/SKILL.md +39 -0
  215. package/skills/aria-cognition/forge-quality-rules/SKILL.md +43 -0
  216. package/skills/aria-cognition/ghazali-8lens/SKILL.md +38 -0
  217. package/skills/aria-cognition/istiqra-induction/SKILL.md +26 -0
  218. package/skills/aria-cognition/ladunni-22/SKILL.md +35 -0
  219. package/skills/aria-cognition/mizan/SKILL.md +72 -0
  220. package/skills/aria-cognition/nadia/SKILL.md +38 -0
  221. package/skills/aria-cognition/nadia-psi/SKILL.md +38 -0
  222. package/skills/aria-cognition/predictor/SKILL.md +25 -0
  223. package/skills/aria-cognition/qiyas-analogy/SKILL.md +26 -0
  224. package/skills/aria-cognition/soul-domains/SKILL.md +25 -0
  225. package/src/auth-commands.ts +111 -45
  226. package/src/chat.ts +174 -13
  227. package/src/codebase-scanner.ts +4 -0
  228. package/src/config.ts +15 -0
  229. package/src/connectors/claude-code.ts +79 -25
  230. package/src/connectors/codebase-awareness.ts +408 -0
  231. package/src/connectors/codex.ts +274 -0
  232. package/src/connectors/cognitive-skills.ts +51 -0
  233. package/src/connectors/opencode.ts +93 -4
  234. package/src/connectors/repo-git-hooks.ts +86 -0
  235. package/src/connectors/repo-guard.ts +589 -0
  236. package/src/connectors/runtime.ts +374 -0
  237. package/src/connectors/shell.ts +83 -14
  238. package/src/connectors/syncd.ts +488 -0
  239. package/src/decisions.ts +469 -0
  240. package/src/garden-control-plane.ts +101 -26
  241. package/src/github-connect.ts +143 -0
  242. package/src/harness-client.ts +128 -3
  243. package/src/hive-client.ts +165 -5
  244. package/src/index.ts +41 -2
  245. package/src/lib/aristotle-noor-wire.ts +310 -0
  246. package/src/providers/types.ts +6 -0
  247. package/src/runtime-proof.ts +392 -0
  248. package/src/setup-wizard.ts +37 -2
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Aria Harness Context Plugin for OpenCode.
3
+ *
4
+ * Injects the live harness packet into OpenCode's system prompt on every
5
+ * session start. Routes through the canonical @aria/harness-http-client SDK
6
+ * via the inject-context.mjs script that ships alongside this plugin.
7
+ *
8
+ * Distribution: this dir is installed by `aria connect` (via connectors/
9
+ * opencode.ts) into `~/.opencode/plugins/harness-context/`. The plugin's
10
+ * absolute install path is wired into ~/.opencode/config.json's `plugin`
11
+ * (singular) array. OpenCode loads it on session start.
12
+ *
13
+ * inject-context.mjs is resolved RELATIVE TO THIS FILE (via import.meta.url),
14
+ * so the same install layout works on every machine — no $HOME or repo-path
15
+ * assumptions.
16
+ */
17
+
18
+ import { execFileSync } from 'node:child_process';
19
+ import { existsSync } from 'node:fs';
20
+ import { dirname, join } from 'node:path';
21
+ import { fileURLToPath } from 'node:url';
22
+
23
+ const PLUGIN_DIR = dirname(fileURLToPath(import.meta.url));
24
+ const INJECT_SCRIPT = join(PLUGIN_DIR, 'inject-context.mjs');
25
+
26
+ function getHarnessContext() {
27
+ if (!existsSync(INJECT_SCRIPT)) {
28
+ process.stderr.write(`[harness-context] inject-context.mjs missing at ${INJECT_SCRIPT}\n`);
29
+ return null;
30
+ }
31
+ try {
32
+ // execFileSync — argv-safe (no shell injection surface even though the
33
+ // path is internal). 15s ceiling matches OpenCode's tolerance for
34
+ // session-start blocking. Stdout is the prepend-able context; stderr
35
+ // is the diagnostic surface.
36
+ const output = execFileSync(process.execPath, [INJECT_SCRIPT], {
37
+ encoding: 'utf-8',
38
+ timeout: 15000,
39
+ stdio: ['ignore', 'pipe', 'pipe'],
40
+ env: process.env,
41
+ });
42
+ return output;
43
+ } catch (err) {
44
+ process.stderr.write(`[harness-context] inject-context failed: ${err && err.message ? err.message : String(err)}\n`);
45
+ return null;
46
+ }
47
+ }
48
+
49
+ export default async function HarnessContextPlugin(ctx) {
50
+ const context = getHarnessContext();
51
+ if (context) {
52
+ try {
53
+ ctx.system?.prepend?.(context.slice(0, 40000));
54
+ } catch (_) {
55
+ // ctx.system shape varies across OpenCode versions; if prepend isn't
56
+ // available we silently no-op rather than crash plugin load.
57
+ }
58
+ }
59
+ return {};
60
+ }
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env node
2
+ // Aria harness context injector — bundled alongside the harness-context
3
+ // OpenCode plugin. Resolved relative to the plugin (via import.meta.url),
4
+ // so the install layout is portable across machines.
5
+ //
6
+ // Contract with the plugin:
7
+ // - stdin: ignored
8
+ // - stdout: text to prepend to OpenCode's system prompt
9
+ // - exit 0 on success; non-zero treated as failure (plugin returns null)
10
+ //
11
+ // SDK resolution: prefers ~/.aria/sdk/index.js (the shared SDK installed by
12
+ // `aria connect`), then falls back to client-local bundles.
13
+ // Cache fast-path prefers ~/.aria/.aria-harness-last-packet.json so OpenCode
14
+ // session-start doesn't depend on Claude's local cache layout.
15
+
16
+ import { existsSync, readFileSync, statSync } from 'node:fs';
17
+ import { homedir } from 'node:os';
18
+ import { join } from 'node:path';
19
+
20
+ const HOME = homedir();
21
+ const LICENSE_PATH = join(HOME, '.aria', 'license.json');
22
+ const OWNER_TOKEN_PATH = join(HOME, '.aria', 'owner-token');
23
+ const SDK_CANDIDATES = [
24
+ join(HOME, '.aria', 'sdk', 'index.js'),
25
+ join(HOME, '.codex', 'aria-sdk', 'index.js'),
26
+ join(HOME, '.claude', 'aria-sdk', 'index.js'),
27
+ ];
28
+ const RUNTIME_URL = (process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319').replace(/\/+$/, '');
29
+ const PACKET_CACHE_PATHS = [
30
+ join(HOME, '.aria', '.aria-harness-last-packet.json'),
31
+ join(HOME, '.claude', '.aria-harness-last-packet.json'),
32
+ ];
33
+ const PACKET_CACHE_TTL_SEC = Number(process.env.ARIA_HARNESS_CACHE_TTL_SEC || '300');
34
+ const STALE_FALLBACK_MAX_AGE_SEC = Number(process.env.ARIA_HARNESS_STALE_FALLBACK_MAX_AGE_SEC || '86400');
35
+
36
+ function fail(reason) {
37
+ process.stderr.write(`[inject-context] ${reason}\n`);
38
+ process.exit(1);
39
+ }
40
+
41
+ function resolveApiKey() {
42
+ if (process.env.ARIA_API_KEY) return process.env.ARIA_API_KEY;
43
+ if (process.env.ARIA_HARNESS_TOKEN) return process.env.ARIA_HARNESS_TOKEN;
44
+ if (process.env.ARIA_MASTER_TOKEN) return process.env.ARIA_MASTER_TOKEN;
45
+ if (existsSync(OWNER_TOKEN_PATH)) {
46
+ try {
47
+ const v = readFileSync(OWNER_TOKEN_PATH, 'utf8').trim();
48
+ if (v) return v;
49
+ } catch {}
50
+ }
51
+ if (existsSync(LICENSE_PATH)) {
52
+ try {
53
+ const j = JSON.parse(readFileSync(LICENSE_PATH, 'utf8'));
54
+ if (j.token) return j.token;
55
+ } catch {}
56
+ }
57
+ return '';
58
+ }
59
+
60
+ function resolveBaseUrl() {
61
+ if (process.env.ARIA_HIVE_RUNTIME_URL) return process.env.ARIA_HIVE_RUNTIME_URL.replace(/\/+$/, '');
62
+ if (process.env.ARIA_HARNESS_BASE_URL) return process.env.ARIA_HARNESS_BASE_URL.replace(/\/+$/, '');
63
+ if (process.env.ARIA_SOUL_URL) return process.env.ARIA_SOUL_URL.replace(/\/+$/, '');
64
+ return process.env.ARIA_HARNESS_URL || 'https://harness.ariasos.com';
65
+ }
66
+
67
+ function resolveSdkPath() {
68
+ return SDK_CANDIDATES.find((candidate) => existsSync(candidate)) || '';
69
+ }
70
+
71
+ function loadCachedPacket({ ignoreTtl = false } = {}) {
72
+ try {
73
+ for (const cachePath of PACKET_CACHE_PATHS) {
74
+ if (!existsSync(cachePath)) continue;
75
+ const ageSec = (Date.now() - statSync(cachePath).mtimeMs) / 1000;
76
+ if (ignoreTtl) {
77
+ if (ageSec > STALE_FALLBACK_MAX_AGE_SEC) continue;
78
+ } else if (ageSec > PACKET_CACHE_TTL_SEC) {
79
+ continue;
80
+ }
81
+ return JSON.parse(readFileSync(cachePath, 'utf8'));
82
+ }
83
+ } catch {
84
+ return null;
85
+ }
86
+ return null;
87
+ }
88
+
89
+ function formatPacketAsContext(packet) {
90
+ if (typeof packet === 'string') return packet;
91
+ if (packet && typeof packet === 'object') {
92
+ if (packet.packet && typeof packet.packet === 'object') {
93
+ return JSON.stringify(packet.packet, null, 2);
94
+ }
95
+ if (packet.harness && typeof packet.harness === 'string') return packet.harness;
96
+ return JSON.stringify(packet, null, 2);
97
+ }
98
+ return '';
99
+ }
100
+
101
+ async function fetchViaSdk(baseUrl, apiKey) {
102
+ const sdkPath = resolveSdkPath();
103
+ if (!sdkPath) {
104
+ fail(`SDK not found in ${SDK_CANDIDATES.join(', ')} — run \`aria connect\` to install`);
105
+ }
106
+ const mod = await import(sdkPath);
107
+ const Client = mod.HTTPHarnessClient;
108
+ if (!Client) fail('SDK loaded but HTTPHarnessClient not exported — bundle is broken');
109
+ const client = new Client({ baseUrl, apiKey, workspaceRoot: process.cwd() });
110
+ return client.getHarnessPacket({
111
+ stage: 'session-start',
112
+ actor: 'opencode',
113
+ system: 'opencode',
114
+ platform: 'opencode',
115
+ });
116
+ }
117
+
118
+ async function fetchViaRuntime(apiKey) {
119
+ const response = await fetch(`${RUNTIME_URL}/packet`, {
120
+ method: 'POST',
121
+ headers: {
122
+ 'Content-Type': 'application/json',
123
+ Authorization: `Bearer ${apiKey}`,
124
+ },
125
+ body: JSON.stringify({
126
+ packetRequest: {
127
+ stage: 'session-start',
128
+ actor: 'opencode',
129
+ system: 'opencode',
130
+ platform: 'opencode',
131
+ },
132
+ }),
133
+ });
134
+ if (!response.ok) {
135
+ const body = await response.text().catch(() => response.statusText);
136
+ throw new Error(`runtime packet fetch failed ${response.status}: ${body}`);
137
+ }
138
+ const payload = await response.json();
139
+ return payload.packet || payload;
140
+ }
141
+
142
+ async function main() {
143
+ const apiKey = resolveApiKey();
144
+ if (!apiKey) {
145
+ // Stale-cache fallback: if network can't fix it, serve what we have
146
+ const stale = loadCachedPacket({ ignoreTtl: true });
147
+ if (stale) {
148
+ process.stderr.write('[inject-context] using stale cached packet (no API key)\n');
149
+ process.stdout.write(formatPacketAsContext(stale));
150
+ return;
151
+ }
152
+ fail('no API key — set ARIA_API_KEY or run `aria login`');
153
+ }
154
+ const baseUrl = resolveBaseUrl();
155
+
156
+ try {
157
+ const packet = await fetchViaRuntime(apiKey).catch(() => fetchViaSdk(baseUrl, apiKey));
158
+ process.stdout.write(formatPacketAsContext(packet));
159
+ return;
160
+ } catch (err) {
161
+ const fresh = loadCachedPacket();
162
+ if (fresh) {
163
+ process.stderr.write(`[inject-context] SDK fetch failed (${err.message}), using fresh cached packet\n`);
164
+ process.stdout.write(formatPacketAsContext(fresh));
165
+ return;
166
+ }
167
+ const stale = loadCachedPacket({ ignoreTtl: true });
168
+ if (stale) {
169
+ process.stderr.write(`[inject-context] network failed (${err.message}), using stale cached packet\n`);
170
+ process.stdout.write(formatPacketAsContext(stale));
171
+ return;
172
+ }
173
+ fail(`SDK fetch failed: ${err && err.message ? err.message : String(err)}`);
174
+ }
175
+ }
176
+
177
+ main().catch((err) => {
178
+ fail(`unexpected: ${err && err.message ? err.message : String(err)}`);
179
+ });
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "harness-context",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "main": "./index.js",
6
+ "exports": {
7
+ ".": "./index.js"
8
+ }
9
+ }
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Aria Harness Gate — pre-tool cognition enforcement for OpenCode.
3
+ * Routes through HTTPHarnessClient SDK for substrate-backed validation.
4
+ */
5
+ import { existsSync, readFileSync } from 'node:fs';
6
+ import { homedir } from 'node:os';
7
+ import { join } from 'node:path';
8
+
9
+ const HOME = homedir();
10
+ const SDK_CANDIDATES = [
11
+ join(HOME, '.aria', 'sdk', 'index.js'),
12
+ join(HOME, '.codex', 'aria-sdk', 'index.js'),
13
+ join(HOME, '.claude', 'aria-sdk', 'index.js'),
14
+ ];
15
+ const OWNER_TOKEN_PATH = join(HOME, '.aria', 'owner-token');
16
+ const LICENSE_PATH = join(HOME, '.aria', 'license.json');
17
+ const RUNTIME_URL = (process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319').replace(/\/+$/, '');
18
+
19
+ const DESTRUCTIVE_PATTERNS = [
20
+ { rx: /(?:^|[;&|]\s*|\$\(\s*|`\s*)sudo\s+\S/, name: 'sudo' },
21
+ { rx: /systemctl\s+(disable|stop|mask|reset-failed|kill)/, name: 'systemctl-state-change' },
22
+ { rx: /\brm\s+-[rRfF]+/, name: 'rm-recursive-or-force' },
23
+ { rx: /\bgit\s+push\b.*\b--force\b/, name: 'git-push-force' },
24
+ { rx: /\bgit\s+reset\s+--hard\b/, name: 'git-reset-hard' },
25
+ { rx: /\bgit\s+checkout\s+--\b/, name: 'git-checkout-discard' },
26
+ { rx: /\b--no-verify\b/, name: 'commit-no-verify' },
27
+ { rx: /\b--no-gpg-sign\b/, name: 'commit-no-gpg-sign' },
28
+ { rx: /\bkill\s+-(9|KILL|TERM|HUP|INT)\b/, name: 'kill-signal' },
29
+ { rx: /\bpkill\b/, name: 'pkill' },
30
+ { rx: /\b(DROP|TRUNCATE)\s+(TABLE|DATABASE|SCHEMA|INDEX)\b/i, name: 'sql-drop-or-truncate' },
31
+ { rx: /\bkubectl\s+delete\b/, name: 'kubectl-delete' },
32
+ ];
33
+
34
+ const DEPLOY_PATTERNS = [
35
+ { rx: /\b(?:\.\/)?scripts\/deploy-/, name: 'deploy-script' },
36
+ { rx: /\bkubectl\s+apply\b/, name: 'kubectl-apply' },
37
+ { rx: /\bkubectl\s+set\s+image\b/, name: 'kubectl-set-image' },
38
+ { rx: /\bkubectl\s+rollout\s+restart\b/, name: 'kubectl-rollout-restart' },
39
+ { rx: /\bkubectl\s+rollout\s+undo\b/, name: 'kubectl-rollout-undo' },
40
+ { rx: /\bdocker\s+push\b/, name: 'docker-push' },
41
+ { rx: /\bdocker\s+build\b.*--push\b/, name: 'docker-build-push' },
42
+ ];
43
+
44
+ const INLINE_LENS_RX = /^\s*#\s*(?:nur|mizan|hikma|tafakkur|tadabbur|ilham|wahi|firasah|perception|balance|wisdom|reflection|foresight|insight|revelation|discernment)\s*:\s*.{20,}/im;
45
+ const VERIFY_BLOCK_RX = /<verify>[\s\S]*?target\s*:[\s\S]*?verified\s*:[\s\S]*?axiom\s*:[\s\S]*?<\/verify>/i;
46
+ const TRIVIAL_BASH_RX = /^\s*(?:ls|cat|head|tail|grep|find|echo|wc|stat|which|pwd|date|file|du|df|ss|ps)\s/;
47
+ const SHORT_BASH_LIMIT = 30;
48
+
49
+ let _client = null;
50
+ let _clientError = null;
51
+ let _lastMizanReceipt = null;
52
+ let _lastActionSummary = '';
53
+
54
+ function resolveToken() {
55
+ if (process.env.ARIA_HARNESS_TOKEN) return process.env.ARIA_HARNESS_TOKEN;
56
+ if (process.env.ARIA_API_KEY) return process.env.ARIA_API_KEY;
57
+ if (process.env.ARIA_MASTER_TOKEN) return process.env.ARIA_MASTER_TOKEN;
58
+ try {
59
+ if (existsSync(OWNER_TOKEN_PATH)) {
60
+ const v = readFileSync(OWNER_TOKEN_PATH, 'utf8').trim();
61
+ if (v) return v;
62
+ }
63
+ } catch {}
64
+ try {
65
+ if (existsSync(LICENSE_PATH)) {
66
+ const j = JSON.parse(readFileSync(LICENSE_PATH, 'utf8'));
67
+ if (j.token) return j.token;
68
+ }
69
+ } catch {}
70
+ return '';
71
+ }
72
+
73
+ function resolveBaseUrl() {
74
+ if (process.env.ARIA_HIVE_RUNTIME_URL) return process.env.ARIA_HIVE_RUNTIME_URL.replace(/\/+$/, '');
75
+ if (process.env.ARIA_HARNESS_BASE_URL) return process.env.ARIA_HARNESS_BASE_URL.replace(/\/+$/, '');
76
+ if (process.env.ARIA_SOUL_URL) return process.env.ARIA_SOUL_URL.replace(/\/+$/, '');
77
+ return process.env.ARIA_HARNESS_URL || 'https://harness.ariasos.com';
78
+ }
79
+
80
+ function resolveSdkPath() {
81
+ return SDK_CANDIDATES.find((candidate) => existsSync(candidate)) || '';
82
+ }
83
+
84
+ async function getClient() {
85
+ if (_client) return _client;
86
+ if (_clientError) return null;
87
+ const sdkPath = resolveSdkPath();
88
+ if (!sdkPath) { _clientError = 'SDK not found'; return null; }
89
+ const token = resolveToken();
90
+ if (!token) { _clientError = 'no token'; return null; }
91
+ try {
92
+ const mod = await import(sdkPath);
93
+ const Client = mod.HTTPHarnessClient;
94
+ if (!Client) { _clientError = 'no HTTPHarnessClient export'; return null; }
95
+ _client = new Client({ baseUrl: resolveBaseUrl(), apiKey: token, workspaceRoot: process.cwd() });
96
+ } catch (e) {
97
+ _clientError = e.message;
98
+ }
99
+ return _client;
100
+ }
101
+
102
+ async function runtimeCheckAction(action, target) {
103
+ const token = resolveToken();
104
+ if (!token) throw new Error('no token');
105
+ const response = await fetch(`${RUNTIME_URL}/check-action`, {
106
+ method: 'POST',
107
+ headers: {
108
+ 'Content-Type': 'application/json',
109
+ Authorization: `Bearer ${token}`,
110
+ },
111
+ body: JSON.stringify({ action, target }),
112
+ });
113
+ if (!response.ok) {
114
+ const body = await response.text().catch(() => response.statusText);
115
+ throw new Error(`runtime check-action failed ${response.status}: ${body}`);
116
+ }
117
+ const payload = await response.json();
118
+ return payload.allowed == null && payload.ok && payload.reason == null ? payload.result || payload : payload;
119
+ }
120
+
121
+ async function runtimeMizan(phase, body) {
122
+ const token = resolveToken();
123
+ if (!token) throw new Error('no token');
124
+ const response = await fetch(`${RUNTIME_URL}/mizan/${phase}`, {
125
+ method: 'POST',
126
+ headers: {
127
+ 'Content-Type': 'application/json',
128
+ Authorization: `Bearer ${token}`,
129
+ },
130
+ body: JSON.stringify(body),
131
+ });
132
+ if (!response.ok) {
133
+ const raw = await response.text().catch(() => response.statusText);
134
+ throw new Error(`runtime mizan/${phase} failed ${response.status}: ${raw}`);
135
+ }
136
+ return response.json();
137
+ }
138
+
139
+ export default async function HarnessGatePlugin(ctx) {
140
+ process.stderr.write('[harness-gate] Active — SDK-backed cognition gate\n');
141
+
142
+ return {
143
+ 'tool.execute.before': async (input, output) => {
144
+ const toolName = input.tool;
145
+ if (!['Bash', 'Edit', 'Write', 'NotebookEdit'].includes(toolName)) return;
146
+
147
+ const cmd = toolName === 'Bash' ? String(input.args?.command ?? '') : '';
148
+ const filePath = toolName !== 'Bash' ? String(input.args?.file_path ?? input.args?.notebook_path ?? '') : '';
149
+ const cmdPreview = toolName === 'Bash' ? cmd.slice(0, 80) : `${toolName} ${filePath || '(no path)'}`.slice(0, 80);
150
+
151
+ // Trivial reads pass
152
+ if (toolName === 'Bash' && TRIVIAL_BASH_RX.test(cmd) && cmd.length < 200) return;
153
+ 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
+ const destructive = DESTRUCTIVE_PATTERNS.find(({ rx }) => rx.test(cmd));
158
+ const deploy = DEPLOY_PATTERNS.find(({ rx }) => rx.test(cmd));
159
+ const isFileMutation = ['Edit', 'Write', 'NotebookEdit'].includes(toolName) && filePath;
160
+
161
+ if (!destructive && !deploy && !isFileMutation) return;
162
+
163
+ // Try SDK checkAction() first — substrate-backed validation
164
+ const client = await getClient();
165
+ const sessionId = process.env.ARIA_SESSION_ID || 'opencode';
166
+ const label = destructive?.name || deploy?.name || `${toolName}:${filePath?.split('/').pop() || ''}`;
167
+ const action = toolName === 'Bash' ? 'bash' : 'edit';
168
+ const target = toolName === 'Bash' ? cmd.slice(0, 200) : filePath.slice(0, 200);
169
+ const rationale =
170
+ destructive ? `High-risk action ${label} requested in OpenCode and must satisfy Mizan truth, protection, and quality before execution.`
171
+ : deploy ? `Deployment action ${label} requested in OpenCode and must satisfy Mizan survivability before execution.`
172
+ : `Repository mutation ${label} requested in OpenCode and must satisfy Mizan quality and doctrine before execution.`;
173
+
174
+ try {
175
+ const pre = await runtimeMizan('pre', {
176
+ sessionId,
177
+ context: {
178
+ sessionId,
179
+ message: cmdPreview,
180
+ intendedAction: target || cmdPreview,
181
+ rationale,
182
+ },
183
+ });
184
+ _lastMizanReceipt = pre.receipt || null;
185
+ _lastActionSummary = cmdPreview;
186
+ if (pre.receipt?.blocked || pre.result?.fitrahVetoed || pre.result?.reAuthorSignal) {
187
+ throw new Error(
188
+ `=== ARIA MIZAN PRE BLOCK: ${label} ===\n\n` +
189
+ `${(pre.result?.notes || ['Mizan pre-phase blocked this action.']).slice(0, 4).join('\n')}\n\n` +
190
+ `Required packs: ${(pre.operatorPlan?.requiredPacks || []).join(', ')}\n` +
191
+ `Required operators: ${(pre.operatorPlan?.priorityOperators || []).join(', ')}`
192
+ );
193
+ }
194
+ if (_lastMizanReceipt?.receiptId && _lastActionSummary && _lastActionSummary !== cmdPreview) {
195
+ await runtimeMizan('mid', {
196
+ sessionId,
197
+ parentReceiptId: _lastMizanReceipt.receiptId,
198
+ message: _lastActionSummary,
199
+ plannedApproach: cmdPreview,
200
+ context: {
201
+ sessionId,
202
+ message: _lastActionSummary,
203
+ plannedApproach: cmdPreview,
204
+ rationale,
205
+ },
206
+ }).catch(() => null);
207
+ }
208
+ } catch (e) {
209
+ if (String(e?.message || '').startsWith('=== ARIA MIZAN PRE BLOCK')) throw e;
210
+ process.stderr.write(`[harness-gate] mizan pre unavailable: ${e.message}\n`);
211
+ }
212
+
213
+ if (client) {
214
+ try {
215
+ const check = await runtimeCheckAction(action, target).catch(() => client.checkAction(action, target));
216
+ if (!check.allowed) {
217
+ throw new Error(
218
+ `=== ARIA SD GATE: ${label} ===\n\n` +
219
+ `Blocked by substrate gate: ${check.reason || 'no reason provided'}\n` +
220
+ `Required gates: ${(check.requiredGates || []).join(', ')}\n\n` +
221
+ `Complete a <cognition> block or inline cognition comments before this action.`
222
+ );
223
+ }
224
+ return;
225
+ } catch (e) {
226
+ if (e.message.startsWith('=== ARIA SD GATE')) throw e;
227
+ // SDK unreachable — fall through to local gate below
228
+ process.stderr.write(`[harness-gate] SDK checkAction failed: ${e.message} — falling through to local gate\n`);
229
+ }
230
+ }
231
+
232
+ // Local fallback gate
233
+ const msg = [
234
+ `=== ARIA GATE: ${label} ===`,
235
+ '',
236
+ `This tool (${label}) requires pre-action cognition. Add inline cognition comments:`,
237
+ ' # nur: [perception of current state, ≥20 chars]',
238
+ ' # mizan: [balance analysis, trade-offs, risks, ≥20 chars]',
239
+ ' # hikma: [wisdom — which doctrine applies, ≥20 chars]',
240
+ ' # tafakkur: [reflection — second-order effects, ≥20 chars]',
241
+ '',
242
+ `${deploy ? 'DEPLOY requires a <verify> block with target, verified, rollback, axiom fields.\n' : ''}`,
243
+ `Run \`aria connect\` to install the full SDK-backed gate (substrate checkAction).`,
244
+ ].filter(Boolean).join('\n');
245
+ throw new Error(msg);
246
+ },
247
+ };
248
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Aria Harness Outcome — post-tool recording via HTTPHarnessClient SDK.
3
+ * Routes through the canonical SDK for outcome/garden/ledger writes.
4
+ */
5
+ import { existsSync, readFileSync } from 'node:fs';
6
+ import { homedir } from 'node:os';
7
+ import { join } from 'node:path';
8
+
9
+ const HOME = homedir();
10
+ const SDK_CANDIDATES = [
11
+ join(HOME, '.aria', 'sdk', 'index.js'),
12
+ join(HOME, '.codex', 'aria-sdk', 'index.js'),
13
+ join(HOME, '.claude', 'aria-sdk', 'index.js'),
14
+ ];
15
+ const OWNER_TOKEN_PATH = join(HOME, '.aria', 'owner-token');
16
+ const LICENSE_PATH = join(HOME, '.aria', 'license.json');
17
+
18
+ let _client = null;
19
+ let _clientError = null;
20
+
21
+ function resolveToken() {
22
+ if (process.env.ARIA_HARNESS_TOKEN) return process.env.ARIA_HARNESS_TOKEN;
23
+ if (process.env.ARIA_API_KEY) return process.env.ARIA_API_KEY;
24
+ try {
25
+ if (existsSync(OWNER_TOKEN_PATH)) {
26
+ const v = readFileSync(OWNER_TOKEN_PATH, 'utf8').trim();
27
+ if (v) return v;
28
+ }
29
+ } catch {}
30
+ try {
31
+ if (existsSync(LICENSE_PATH)) {
32
+ const j = JSON.parse(readFileSync(LICENSE_PATH, 'utf8'));
33
+ if (j.token) return j.token;
34
+ }
35
+ } catch {}
36
+ return '';
37
+ }
38
+
39
+ function resolveBaseUrl() {
40
+ if (process.env.ARIA_HIVE_RUNTIME_URL) return process.env.ARIA_HIVE_RUNTIME_URL.replace(/\/+$/, '');
41
+ if (process.env.ARIA_HARNESS_BASE_URL) return process.env.ARIA_HARNESS_BASE_URL.replace(/\/+$/, '');
42
+ return process.env.ARIA_HARNESS_URL || 'https://harness.ariasos.com';
43
+ }
44
+
45
+ function resolveSdkPath() {
46
+ return SDK_CANDIDATES.find((candidate) => existsSync(candidate)) || '';
47
+ }
48
+
49
+ async function getClient() {
50
+ if (_client) return _client;
51
+ if (_clientError) return null;
52
+ const sdkPath = resolveSdkPath();
53
+ if (!sdkPath) { _clientError = 'SDK not found'; return null; }
54
+ const token = resolveToken();
55
+ if (!token) { _clientError = 'no token'; return null; }
56
+ try {
57
+ const mod = await import(sdkPath);
58
+ const Client = mod.HTTPHarnessClient;
59
+ if (!Client) { _clientError = 'no HTTPHarnessClient export'; return null; }
60
+ _client = new Client({ baseUrl: resolveBaseUrl(), apiKey: token, workspaceRoot: process.cwd() });
61
+ } catch (e) {
62
+ _clientError = e.message;
63
+ }
64
+ return _client;
65
+ }
66
+
67
+ export default async function HarnessOutcomePlugin(ctx) {
68
+ process.stderr.write('[harness-outcome] Active — SDK-backed outcome recording\n');
69
+
70
+ return {
71
+ 'tool.execute.after': async (input, output) => {
72
+ const toolName = input.tool;
73
+ if (!['Bash', 'Edit', 'Write', 'NotebookEdit'].includes(toolName)) return;
74
+
75
+ const actionKind = toolName === 'Bash' ? 'bash' : 'edit';
76
+ const actionTarget = toolName === 'Bash'
77
+ ? String(input.args?.command ?? '').split(/\s+/)[0]?.slice(0, 100) || 'unknown'
78
+ : String(input.args?.file_path ?? input.args?.notebook_path ?? '').slice(0, 200) || 'unknown';
79
+ const success = !output?.error;
80
+ const sessionId = process.env.ARIA_SESSION_ID || 'opencode';
81
+
82
+ // Try SDK first
83
+ const client = await getClient();
84
+ if (client) {
85
+ try {
86
+ await client.recordDiscovery({
87
+ text: `${actionKind}: ${actionTarget} — ${success ? 'ok' : 'failed'}`,
88
+ kind: success ? 'observation' : 'defect',
89
+ source: 'opencode-outcome-plugin',
90
+ resolution_status: success ? 'resolved' : 'open',
91
+ sessionId,
92
+ });
93
+ return;
94
+ } catch (e) {
95
+ // Fire-and-forget — SDK unreachable is non-blocking
96
+ process.stderr.write(`[harness-outcome] SDK recordDiscovery failed: ${e.message} — falling through to raw fetch\n`);
97
+ }
98
+ }
99
+
100
+ // Fallback: raw fetch
101
+ const harnessUrl =
102
+ process.env.ARIA_HIVE_RUNTIME_URL ||
103
+ process.env.ARIA_HARNESS_BASE_URL ||
104
+ process.env.ARIA_HARNESS_URL ||
105
+ 'https://harness.ariasos.com';
106
+ let harnessToken = process.env.ARIA_HARNESS_TOKEN || process.env.ARIA_API_KEY || '';
107
+ try {
108
+ if (!harnessToken) {
109
+ if (existsSync(OWNER_TOKEN_PATH)) harnessToken = readFileSync(OWNER_TOKEN_PATH, 'utf8').trim();
110
+ }
111
+ } catch {}
112
+ if (!harnessToken) return;
113
+
114
+ fetch(`${harnessUrl}/api/harness/outcome-record`, {
115
+ method: 'POST',
116
+ headers: {
117
+ 'Content-Type': 'application/json',
118
+ Authorization: `Bearer ${harnessToken}`,
119
+ },
120
+ body: JSON.stringify({
121
+ sessionId,
122
+ actionKind,
123
+ actionTarget,
124
+ actionShape: { tool: toolName, success },
125
+ }),
126
+ }).catch(() => {});
127
+ },
128
+ };
129
+ }