@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.
Files changed (95) hide show
  1. package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
  2. package/dist/aria-connector/src/connectors/claude-code.js +115 -20
  3. package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
  4. package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
  5. package/dist/aria-connector/src/connectors/codex.js +551 -11
  6. package/dist/aria-connector/src/connectors/codex.js.map +1 -1
  7. package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts +7 -0
  8. package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts.map +1 -0
  9. package/dist/aria-connector/src/connectors/doctrine-trigger-map.js +87 -0
  10. package/dist/aria-connector/src/connectors/doctrine-trigger-map.js.map +1 -0
  11. package/dist/aria-connector/src/connectors/must-read.d.ts +4 -0
  12. package/dist/aria-connector/src/connectors/must-read.d.ts.map +1 -0
  13. package/dist/aria-connector/src/connectors/must-read.js +115 -0
  14. package/dist/aria-connector/src/connectors/must-read.js.map +1 -0
  15. package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
  16. package/dist/aria-connector/src/connectors/opencode.js +27 -9
  17. package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
  18. package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -1
  19. package/dist/aria-connector/src/connectors/runtime.js +231 -19
  20. package/dist/aria-connector/src/connectors/runtime.js.map +1 -1
  21. package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -1
  22. package/dist/aria-connector/src/connectors/shell.js +76 -3
  23. package/dist/aria-connector/src/connectors/shell.js.map +1 -1
  24. package/dist/assets/hooks/aria-agent-handoff.mjs +23 -0
  25. package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +121 -28
  26. package/dist/assets/hooks/aria-harness-via-sdk.mjs +126 -12
  27. package/dist/assets/hooks/aria-pre-emit-dryrun.mjs +35 -0
  28. package/dist/assets/hooks/aria-pre-tool-gate.mjs +383 -93
  29. package/dist/assets/hooks/aria-preprompt-consult.mjs +28 -2
  30. package/dist/assets/hooks/aria-preturn-memory-gate.mjs +93 -16
  31. package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +33 -1
  32. package/dist/assets/hooks/aria-stop-gate.mjs +346 -81
  33. package/dist/assets/hooks/doctrine_trigger_map.json +55 -0
  34. package/dist/assets/hooks/lib/canonical-lenses.mjs +6 -5
  35. package/dist/assets/hooks/lib/gate-loop-state.mjs +50 -0
  36. package/dist/assets/hooks/lib/hook-message-window.mjs +121 -0
  37. package/dist/assets/hooks/test-tier-lens-labeling.mjs +26 -58
  38. package/dist/assets/opencode-plugins/harness-gate/index.js +40 -5
  39. package/dist/assets/opencode-plugins/harness-stop/index.js +133 -10
  40. package/dist/runtime/auth-middleware.mjs +251 -0
  41. package/dist/runtime/codex-bridge.mjs +644 -0
  42. package/dist/runtime/discipline/CLAUDE.md +28 -0
  43. package/dist/runtime/discipline/doctrine_trigger_map.json +534 -0
  44. package/dist/runtime/doctrine_trigger_map.json +534 -0
  45. package/dist/runtime/fleet-engine.mjs +231 -0
  46. package/dist/runtime/harness-daemon.mjs +460 -0
  47. package/dist/runtime/manifest.json +1 -1
  48. package/dist/runtime/metering.mjs +100 -0
  49. package/dist/runtime/onboarding-engine.mjs +89 -0
  50. package/dist/runtime/plugin-engine.mjs +196 -0
  51. package/dist/runtime/sdk/BUNDLED.json +1 -1
  52. package/dist/runtime/sdk/index.d.ts +12 -0
  53. package/dist/runtime/sdk/index.js +120 -14
  54. package/dist/runtime/sdk/index.js.map +1 -1
  55. package/dist/runtime/service.mjs +1140 -48
  56. package/dist/runtime/workflow-engine.mjs +322 -0
  57. package/dist/sdk/BUNDLED.json +1 -1
  58. package/dist/sdk/index.d.ts +12 -0
  59. package/dist/sdk/index.js +120 -14
  60. package/dist/sdk/index.js.map +1 -1
  61. package/hooks/aria-agent-handoff.mjs +23 -0
  62. package/hooks/aria-cognition-substrate-binding.mjs +121 -28
  63. package/hooks/aria-harness-via-sdk.mjs +126 -12
  64. package/hooks/aria-pre-emit-dryrun.mjs +35 -0
  65. package/hooks/aria-pre-tool-gate.mjs +383 -93
  66. package/hooks/aria-preprompt-consult.mjs +28 -2
  67. package/hooks/aria-preturn-memory-gate.mjs +93 -16
  68. package/hooks/aria-repo-doctrine-gate.mjs +33 -1
  69. package/hooks/aria-stop-gate.mjs +346 -81
  70. package/hooks/doctrine_trigger_map.json +55 -0
  71. package/hooks/lib/canonical-lenses.mjs +6 -5
  72. package/hooks/lib/gate-loop-state.mjs +50 -0
  73. package/hooks/lib/hook-message-window.mjs +121 -0
  74. package/hooks/test-tier-lens-labeling.mjs +26 -58
  75. package/opencode-plugins/harness-gate/index.js +40 -5
  76. package/opencode-plugins/harness-stop/index.js +133 -10
  77. package/package.json +1 -1
  78. package/runtime-src/auth-middleware.mjs +251 -0
  79. package/runtime-src/codex-bridge.mjs +644 -0
  80. package/runtime-src/fleet-engine.mjs +231 -0
  81. package/runtime-src/harness-daemon.mjs +460 -0
  82. package/runtime-src/metering.mjs +100 -0
  83. package/runtime-src/onboarding-engine.mjs +89 -0
  84. package/runtime-src/plugin-engine.mjs +196 -0
  85. package/runtime-src/service.mjs +1140 -48
  86. package/runtime-src/workflow-engine.mjs +322 -0
  87. package/scripts/bundle-sdk.mjs +5 -0
  88. package/src/connectors/claude-code.ts +126 -20
  89. package/src/connectors/codex.ts +559 -10
  90. package/src/connectors/doctrine-trigger-map.ts +112 -0
  91. package/src/connectors/must-read.ts +117 -0
  92. package/src/connectors/opencode.ts +28 -9
  93. package/src/connectors/runtime.ts +241 -21
  94. package/src/connectors/shell.ts +78 -3
  95. package/dist/cli-0.2.0.tgz +0 -0
@@ -0,0 +1,112 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ statSync,
6
+ writeFileSync,
7
+ } from 'fs';
8
+ import { homedir } from 'os';
9
+ import * as path from 'path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ export interface DoctrineMapSyncResult {
13
+ sourcePath: string | null;
14
+ targetCount: number;
15
+ updatedCount: number;
16
+ }
17
+
18
+ interface DoctrineMapCandidate {
19
+ path: string;
20
+ mtimeMs: number;
21
+ data: Record<string, unknown>;
22
+ }
23
+
24
+ function bundledDoctrineTriggerMapCandidates(): string[] {
25
+ const here = path.dirname(fileURLToPath(import.meta.url));
26
+ return [
27
+ path.resolve(here, '..', '..', '..', '..', 'hooks', 'doctrine_trigger_map.json'),
28
+ path.resolve(here, '..', '..', '..', 'assets', 'hooks', 'doctrine_trigger_map.json'),
29
+ ];
30
+ }
31
+
32
+ function installedDoctrineTriggerMapTargets(): string[] {
33
+ const home = homedir();
34
+ return [
35
+ path.join(home, '.aria', 'runtime', 'discipline', 'doctrine_trigger_map.json'),
36
+ path.join(home, '.aria', 'runtime', 'doctrine_trigger_map.json'),
37
+ path.join(home, '.claude', 'hooks', 'doctrine_trigger_map.json'),
38
+ path.join(home, '.claude', 'projects', '-home-hamzaibrahim1', 'memory', 'doctrine_trigger_map.json'),
39
+ path.join(home, '.codex', 'doctrine_trigger_map.json'),
40
+ path.join(home, '.opencode', 'doctrine_trigger_map.json'),
41
+ ];
42
+ }
43
+
44
+ function readCandidate(targetPath: string): DoctrineMapCandidate | null {
45
+ if (!existsSync(targetPath)) return null;
46
+ try {
47
+ const data = JSON.parse(readFileSync(targetPath, 'utf8')) as Record<string, unknown>;
48
+ if (!Array.isArray(data.triggers)) return null;
49
+ return {
50
+ path: targetPath,
51
+ mtimeMs: statSync(targetPath).mtimeMs,
52
+ data,
53
+ };
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ function normalizeDoctrineMap(data: Record<string, unknown>): string {
60
+ return JSON.stringify(data, null, 2) + '\n';
61
+ }
62
+
63
+ export function syncDoctrineTriggerMap(logs: string[] = []): DoctrineMapSyncResult {
64
+ const targets = installedDoctrineTriggerMapTargets();
65
+ const readableCandidates = [
66
+ ...bundledDoctrineTriggerMapCandidates(),
67
+ ...targets,
68
+ ];
69
+
70
+ const candidates = readableCandidates
71
+ .map((targetPath) => readCandidate(targetPath))
72
+ .filter((candidate): candidate is DoctrineMapCandidate => candidate !== null)
73
+ .sort((a, b) => b.mtimeMs - a.mtimeMs);
74
+
75
+ if (candidates.length === 0) {
76
+ logs.push('⚠ doctrine trigger map missing from bundle and all installed targets');
77
+ return {
78
+ sourcePath: null,
79
+ targetCount: targets.length,
80
+ updatedCount: 0,
81
+ };
82
+ }
83
+
84
+ const source = candidates[0];
85
+ const normalized = normalizeDoctrineMap(source.data);
86
+ let updatedCount = 0;
87
+
88
+ for (const targetPath of targets) {
89
+ try {
90
+ const dirPath = path.dirname(targetPath);
91
+ if (!existsSync(dirPath)) {
92
+ mkdirSync(dirPath, { recursive: true, mode: 0o755 });
93
+ }
94
+ const current = existsSync(targetPath) ? readFileSync(targetPath, 'utf8') : null;
95
+ if (current === normalized) continue;
96
+ writeFileSync(targetPath, normalized, { mode: 0o644 });
97
+ updatedCount++;
98
+ } catch (error) {
99
+ logs.push(`⚠ doctrine trigger map sync failed at ${targetPath}: ${error instanceof Error ? error.message : String(error)}`);
100
+ }
101
+ }
102
+
103
+ logs.push(
104
+ `Synced doctrine trigger map from ${source.path} to ${targets.length} installed target(s)`,
105
+ );
106
+
107
+ return {
108
+ sourcePath: source.path,
109
+ targetCount: targets.length,
110
+ updatedCount,
111
+ };
112
+ }
@@ -0,0 +1,117 @@
1
+ export type MustReadSurface = 'claude' | 'codex' | 'opencode';
2
+
3
+ function surfaceRoot(surface: MustReadSurface): string {
4
+ if (surface === 'claude') return '~/.claude';
5
+ if (surface === 'codex') return '~/.codex';
6
+ return '~/.aria';
7
+ }
8
+
9
+ function doctrineMapPath(surface: MustReadSurface): string {
10
+ if (surface === 'claude') return '~/.claude/hooks/doctrine_trigger_map.json';
11
+ if (surface === 'codex') return '~/.codex/doctrine_trigger_map.json';
12
+ return '~/.opencode/doctrine_trigger_map.json';
13
+ }
14
+
15
+ function surfaceSpecificFiles(surface: MustReadSurface): string[] {
16
+ if (surface === 'claude') {
17
+ return [
18
+ '~/.claude/hooks/aria-harness-via-sdk.mjs',
19
+ '~/.claude/hooks/aria-preprompt-consult.mjs',
20
+ '~/.claude/hooks/aria-pre-tool-gate.mjs',
21
+ '~/.claude/hooks/aria-preturn-memory-gate.mjs',
22
+ '~/.claude/hooks/aria-cognition-substrate-binding.mjs',
23
+ '~/.claude/hooks/aria-stop-gate.mjs',
24
+ '~/.claude/hooks/aria-pre-text-gate.mjs',
25
+ ];
26
+ }
27
+ if (surface === 'codex') {
28
+ return [
29
+ '~/.codex/AGENTS.md',
30
+ '~/.codex/config.toml',
31
+ '~/.aria/wrappers/codex',
32
+ '~/.aria/runtime/service.mjs',
33
+ '~/.codex/skills/aria-http-harness-client/SKILL.md',
34
+ '~/.codex/skills/aria-cognition/aria-repo-doctrine/SKILL.md',
35
+ '~/.codex/skills/aria-cognition/ghazali-8lens/SKILL.md',
36
+ '~/.codex/skills/aria-cognition/mizan/SKILL.md',
37
+ ];
38
+ }
39
+ return [
40
+ '~/.aria/AGENTS.md',
41
+ '~/.aria/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md',
42
+ '~/.aria/runtime/discipline/skills/aria-cognition/aria-repo-doctrine/SKILL.md',
43
+ '~/.aria/runtime/discipline/skills/aria-cognition/ghazali-8lens/SKILL.md',
44
+ '~/.aria/runtime/discipline/skills/aria-cognition/mizan/SKILL.md',
45
+ ];
46
+ }
47
+
48
+ export function buildMustReadGuide(surface: MustReadSurface): string {
49
+ const root = surfaceRoot(surface);
50
+ const coreFiles = [
51
+ `${root}/ARIA_MUST_READ.md`,
52
+ '~/.aria/runtime/discipline/CLAUDE.md',
53
+ '~/.aria/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md',
54
+ '~/.aria/runtime/discipline/skills/aria-harness/aria-harness-substrate-binding/SKILL.md',
55
+ doctrineMapPath(surface),
56
+ ];
57
+ const orderedFiles = [...coreFiles, ...surfaceSpecificFiles(surface)];
58
+
59
+ return `# Aria Agent Must-Read Order
60
+
61
+ This file exists because weaker models and long sessions drift unless the reading order is explicit.
62
+ Read these files in order before the first non-trivial action, and re-open the relevant gate file whenever a block fires.
63
+
64
+ ## Required Read Order
65
+
66
+ ${orderedFiles.map((filePath, index) => `${index + 1}. \`${filePath}\``).join('\n')}
67
+
68
+ ## What Each File Teaches
69
+
70
+ 1. \`${root}/ARIA_MUST_READ.md\`
71
+ The cross-surface reading order, recovery rules, and what to re-open after compaction or a block.
72
+
73
+ 2. \`~/.aria/runtime/discipline/CLAUDE.md\`
74
+ The full harness doctrine: L1/L2/L3, cognition requirements, deploy verify contract, drift triggers, and anti-loop rules.
75
+
76
+ 3. \`~/.aria/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md\`
77
+ The harness model in practical terms: packet, bind, and live gates.
78
+
79
+ 4. \`~/.aria/runtime/discipline/skills/aria-harness/aria-harness-substrate-binding/SKILL.md\`
80
+ The cognition contract and anchor discipline. Re-open this whenever cognition or stop-gate fails.
81
+
82
+ 5. \`${doctrineMapPath(surface)}\`
83
+ The live doctrine trigger map. This is what the runtime and stop gates actually match against.
84
+
85
+ ## Surface-Specific Re-Open Rules
86
+
87
+ - If a pre-tool or write block fires:
88
+ Re-open the pre-tool surface file for this client plus \`aria-harness-substrate-binding\`.
89
+
90
+ - If an output or stop-gate block fires:
91
+ Re-open \`~/.aria/runtime/discipline/CLAUDE.md\`, the doctrine trigger map, and the substrate-binding skill before re-authoring.
92
+
93
+ - If transcript compaction or ledger deadlock happens:
94
+ Re-open items 1 through 4 first, then inspect the exact current hook or runtime file causing the block. Do not guess from stale memory.
95
+
96
+ - If the model starts repeating itself:
97
+ Stop, re-open this file, and repair from the gate evidence instead of restating intent.
98
+
99
+ ## Non-Negotiables
100
+
101
+ - Do not rename canonical cognition meaning into generic aliases.
102
+ - Do not treat cognition as a gate password; use it to change the tool input, edit shape, review finding, answer structure, or evidence threshold.
103
+ - Do not emit non-trivial output without \`<applied_cognition>\` containing \`decision_delta\`, \`dominant_domain\`, \`binds_to\`, \`expected_predicate\`, and \`artifact_change\`.
104
+ - Do not let \`decision_delta\` say cognition changed nothing; that is performative cognition.
105
+ - Do not cite substrate anchors that were not loaded in the current harness packet.
106
+ - Do not say "good enough", "should work", or make unverified completion claims.
107
+ - Do not treat skills as optional flavor text; they are the reading order and repair guide.
108
+ - Do not proceed after a gate block without re-opening the file that owns that gate.
109
+ `;
110
+ }
111
+
112
+ export function mustReadIntro(surface: MustReadSurface): string {
113
+ const root = surfaceRoot(surface);
114
+ return `## Must Read Order
115
+ Read \`${root}/ARIA_MUST_READ.md\` before the first non-trivial action and re-open it after any gate block, compaction, or repeated drift.
116
+ `;
117
+ }
@@ -16,6 +16,8 @@ import * as path from 'path';
16
16
  import { fileURLToPath } from 'node:url';
17
17
  import type { AriaConfig } from '../config.js';
18
18
  import { connectShell } from './shell.js';
19
+ import { syncDoctrineTriggerMap } from './doctrine-trigger-map.js';
20
+ import { buildMustReadGuide, mustReadIntro } from './must-read.js';
19
21
 
20
22
  // ── Bundled OpenCode plugins ────────────────────────────────────────────────
21
23
  //
@@ -139,6 +141,7 @@ export async function connectOpenCode(config: AriaConfig): Promise<string[]> {
139
141
  copyPluginDir(srcDir, dstDir, logs);
140
142
  installedPaths.push(dstDir);
141
143
  }
144
+ syncDoctrineTriggerMap(logs);
142
145
 
143
146
  // ── Wire ~/.opencode/config.json ──────────────────────────────────────────
144
147
  const configPath = path.join(opencodeDir, 'config.json');
@@ -337,14 +340,30 @@ ${schemaText || '(no schema images yet — run \`aria repo scan\`)'}
337
340
  - If a fix only works by weakening the contract, the mechanism is still broken. Debug the mechanism.
338
341
 
339
342
  ## 8-Lens Cognition
340
- Apply multi-perspective analysis for every decision:
341
- - Truth lens: Is this factually correct?
342
- - Harm lens: Could this cause damage?
343
- - Trust lens: Does this maintain sacred trust?
344
- - Power lens: Am I using capability responsibly?
345
- - Reflection lens: Have I thought deeply enough?
346
- - Context lens: Do I have full context?
347
- - Impact lens: What are second-order effects?
348
- - Beauty lens: Is this elegant and sustainable?
343
+ Use Aria cognition as a work method, not a checklist:
344
+ - Perceive the real substrate: user intent, repo/runtime state, loaded harness packet, memories, prior tool results, and uncertainty.
345
+ - Infer constraints: what must be true, what must not be harmed, what prior doctrine changes, and what evidence is missing.
346
+ - Shape the next action: choose the smallest useful tool call, edit, question, or answer that satisfies those constraints.
347
+ - Improve the artifact: make the tool input, code change, review finding, or prose more specific because of the cognition.
348
+ - Predict the observable result: define what would prove the action or answer succeeded before claiming it.
349
+ - Report evidence: distinguish observed fact, bounded inference, unresolved risk, and next real action.
350
+
351
+ Lens roles for decisions:
352
+ - Truth lens: binds claims to observed evidence and missing evidence.
353
+ - Harm lens: removes or changes actions that could damage state, trust, data, or runtime health.
354
+ - Trust lens: preserves user intent, prior directives, and secret/infra boundaries.
355
+ - Power lens: uses capability to serve the task, not convenience or control.
356
+ - Reflection lens: catches mechanism failure, shortcut pressure, and repeated loops.
357
+ - Context lens: integrates repo patterns, runtime state, and loaded substrate.
358
+ - Impact lens: predicts downstream effects and verification predicates.
359
+ - Beauty lens: prefers the simplest durable artifact that preserves the contract.
360
+
361
+ ## Structural Cognition Contract
362
+ - Cognition is not a ceremony. It must change the next action, tool call, or output claim.
363
+ - Each lens must affect work selection or artifact shape; do not write lenses after the decision is already made.
364
+ - For every non-trivial output, include an \`<applied_cognition>\` block with \`decision_delta\`, \`dominant_domain\`, \`binds_to\`, \`expected_predicate\`, and \`artifact_change\`.
365
+ - Tool-bound cognition must name the exact tool/action it constrains and the measurable predicate that proves the action succeeded.
366
+ - Deploy or destructive actions still require \`<verify>\` and \`<expected>\` blocks before execution.
367
+ - If cognition did not change anything, stop and re-think; \`decision_delta: none\` is treated as performative.
349
368
  `;
350
369
  }
@@ -7,12 +7,14 @@ import {
7
7
  chmodSync,
8
8
  rmSync,
9
9
  writeFileSync,
10
+ readFileSync,
10
11
  } from 'fs';
11
12
  import { execFileSync } from 'child_process';
12
13
  import { homedir } from 'os';
13
14
  import * as path from 'path';
14
15
  import { fileURLToPath } from 'node:url';
15
16
  import { loadConfig, saveConfig } from '../config.js';
17
+ import { syncDoctrineTriggerMap } from './doctrine-trigger-map.js';
16
18
 
17
19
  function packageRuntimeDir(): string {
18
20
  const here = path.dirname(fileURLToPath(import.meta.url));
@@ -50,6 +52,39 @@ function writeWrapper(binPath: string, targetScript: string): void {
50
52
  try { chmodSync(binPath, 0o755); } catch {}
51
53
  }
52
54
 
55
+ function trimUrl(value: string): string {
56
+ return value.trim().replace(/\/+$/, '');
57
+ }
58
+
59
+ function isLocalHarnessDaemonUrl(value: string): boolean {
60
+ const normalized = trimUrl(value);
61
+ return normalized === 'http://127.0.0.1:8790' || normalized === 'http://localhost:8790';
62
+ }
63
+
64
+ function canonicalUpstreamHarnessUrl(): string {
65
+ const localSoulCandidates = existsSync('/etc/rancher/k3s/k3s.yaml')
66
+ ? ['http://127.0.0.1:30080', 'http://192.168.4.25:30080']
67
+ : [];
68
+ const candidates = [
69
+ process.env.ARIA_UPSTREAM_HARNESS_URL || '',
70
+ process.env.ARIA_HIVE_UPSTREAM_URL || '',
71
+ ...localSoulCandidates,
72
+ process.env.ARIA_SOUL_URL || '',
73
+ process.env.ARIAS_SOUL_URL || '',
74
+ process.env.ARIA_SOUL_BASE_URL || '',
75
+ ...(process.env.ARIA_HARNESS_FALLBACK_URLS || '').split(','),
76
+ 'https://harness.ariasos.com',
77
+ ]
78
+ .map((entry) => trimUrl(String(entry || '')))
79
+ .filter(Boolean);
80
+
81
+ for (const candidate of candidates) {
82
+ if (!isLocalHarnessDaemonUrl(candidate)) return candidate;
83
+ }
84
+
85
+ return 'https://harness.ariasos.com';
86
+ }
87
+
53
88
  function writeRuntimeEnv(runtimeDst: string): string {
54
89
  const envPath = path.join(runtimeDst, 'runtime.env');
55
90
  const config = loadConfig();
@@ -59,24 +94,46 @@ function writeRuntimeEnv(runtimeDst: string): string {
59
94
  const deepModel = runtimeProfiles.deepModel || 'deepseek-v4-pro';
60
95
  const xaiFallbackModel = runtimeProfiles.xaiFallbackModel || 'grok-4-2-reasoning';
61
96
  const nimFallbackModel = runtimeProfiles.nimFallbackModel || 'qwen/qwen3.5-122b-a10b';
62
- const harnessUrl =
63
- process.env.ARIA_HIVE_RUNTIME_URL ||
64
- process.env.ARIA_HARNESS_BASE_URL ||
65
- process.env.ARIA_HARNESS_URL ||
66
- 'https://harness.ariasos.com';
67
- const forgeServiceUrl =
68
- process.env.ARIA_FORGE_SERVICE_URL ||
69
- process.env.FORGE_SERVICE_URL ||
70
- `${harnessUrl.replace(/\/$/, '')}/api/forge/psi`;
97
+ const localHarnessUrl = 'http://127.0.0.1:8790';
98
+ const upstreamHarnessUrl = canonicalUpstreamHarnessUrl();
99
+ const harnessFallbackUrls = [
100
+ process.env.ARIA_HARNESS_FALLBACK_URLS || '',
101
+ process.env.ARIA_SOUL_URL || '',
102
+ process.env.ARIAS_SOUL_URL || '',
103
+ process.env.ARIA_SOUL_BASE_URL || '',
104
+ upstreamHarnessUrl,
105
+ 'http://127.0.0.1:30080',
106
+ 'http://192.168.4.25:30080',
107
+ 'https://arias-soul-6zp3gtk2ca-uc.a.run.app',
108
+ 'https://harness.ariasos.com',
109
+ ]
110
+ .flatMap((entry) => String(entry || '').split(','))
111
+ .map((entry) => trimUrl(String(entry || '')))
112
+ .filter((entry) => entry && !isLocalHarnessDaemonUrl(entry));
113
+ const forgeServiceUrl = `${localHarnessUrl}/api/forge/psi`;
114
+ const upstreamForgeServiceUrl =
115
+ process.env.ARIA_UPSTREAM_FORGE_SERVICE_URL ||
116
+ process.env.ARIA_FORGE_UPSTREAM_URL ||
117
+ `${upstreamHarnessUrl}/api/forge/psi`;
71
118
  writeFileSync(
72
119
  envPath,
73
120
  [
74
121
  'ARIA_RUNTIME_HOST=127.0.0.1',
75
122
  'ARIA_RUNTIME_PORT=4319',
76
123
  'ARIA_RUNTIME_URL=http://127.0.0.1:4319',
77
- `ARIA_HIVE_RUNTIME_URL=${harnessUrl}`,
78
- `ARIA_HARNESS_URL=${harnessUrl}`,
124
+ 'ARIA_HARNESS_DAEMON_HOST=127.0.0.1',
125
+ 'ARIA_HARNESS_DAEMON_PORT=8790',
126
+ `ARIA_HARNESS_DAEMON_URL=${localHarnessUrl}`,
127
+ 'ARIA_CODEX_BRIDGE_HOST=127.0.0.1',
128
+ 'ARIA_CODEX_BRIDGE_PORT=4320',
129
+ 'ARIA_CODEX_DOWNSTREAM_PORT=4321',
130
+ `ARIA_HIVE_RUNTIME_URL=${localHarnessUrl}`,
131
+ `ARIA_HARNESS_BASE_URL=${localHarnessUrl}`,
132
+ `ARIA_HARNESS_URL=${localHarnessUrl}`,
133
+ `ARIA_UPSTREAM_HARNESS_URL=${upstreamHarnessUrl}`,
134
+ `ARIA_HARNESS_FALLBACK_URLS=${Array.from(new Set(harnessFallbackUrls)).join(',')}`,
79
135
  `ARIA_FORGE_SERVICE_URL=${forgeServiceUrl}`,
136
+ `ARIA_UPSTREAM_FORGE_SERVICE_URL=${upstreamForgeServiceUrl}`,
80
137
  'ARIA_QDRANT_URL=http://127.0.0.1:6333',
81
138
  'ARIA_QDRANT_COLLECTION=aria_garden_memory',
82
139
  `ARIA_RUNTIME_DEFAULT_PROVIDER=${defaultProvider}`,
@@ -230,7 +287,8 @@ function installSystemdUserService(ariaDir: string, logs: string[]): void {
230
287
  const unit = [
231
288
  '[Unit]',
232
289
  'Description=Aria Mounted Runtime',
233
- 'After=network.target',
290
+ 'After=network.target aria-harnessd.service',
291
+ 'Requires=aria-harnessd.service',
234
292
  '',
235
293
  '[Service]',
236
294
  'Type=simple',
@@ -256,13 +314,154 @@ function installSystemdUserService(ariaDir: string, logs: string[]): void {
256
314
  }
257
315
  }
258
316
 
317
+ function installHarnessDaemonSystemdUserService(ariaDir: string, logs: string[]): void {
318
+ const systemctlPath = '/bin/systemctl';
319
+ if (process.platform !== 'linux' || !existsSync(systemctlPath)) {
320
+ return;
321
+ }
322
+
323
+ const serviceDir = path.join(homedir(), '.config', 'systemd', 'user');
324
+ const servicePath = path.join(serviceDir, 'aria-harnessd.service');
325
+ mkdirSync(serviceDir, { recursive: true, mode: 0o755 });
326
+
327
+ const unit = [
328
+ '[Unit]',
329
+ 'Description=Aria Local Harness Daemon',
330
+ 'After=network.target',
331
+ '',
332
+ '[Service]',
333
+ 'Type=simple',
334
+ `EnvironmentFile=-${path.join(ariaDir, 'runtime', 'runtime.env')}`,
335
+ `ExecStart=${path.join(ariaDir, 'bin', 'aria-harnessd')}`,
336
+ 'Restart=always',
337
+ 'RestartSec=2',
338
+ '',
339
+ '[Install]',
340
+ 'WantedBy=default.target',
341
+ '',
342
+ ].join('\n');
343
+
344
+ writeFileSync(servicePath, unit, { mode: 0o644 });
345
+ logs.push(`Installed systemd user unit → ${servicePath}`);
346
+
347
+ try {
348
+ execFileSync(systemctlPath, ['--user', 'daemon-reload'], { stdio: 'ignore' });
349
+ execFileSync(systemctlPath, ['--user', 'enable', '--now', 'aria-harnessd.service'], { stdio: 'ignore' });
350
+ logs.push('Enabled and started systemd user service: aria-harnessd.service');
351
+ } catch (error) {
352
+ logs.push(`⚠ local harness daemon installed but not activated: ${error instanceof Error ? error.message : String(error)}`);
353
+ }
354
+ }
355
+
356
+ function retireLegacySoulPortForwardServices(logs: string[]): void {
357
+ const systemctlPath = '/bin/systemctl';
358
+ if (process.platform !== 'linux' || !existsSync(systemctlPath)) {
359
+ return;
360
+ }
361
+
362
+ const serviceDir = path.join(homedir(), '.config', 'systemd', 'user');
363
+ const legacyUnits = ['aria-soul-portforward.service', 'aria-soul-forward.service'];
364
+ const dependencyNeedles = new Set(legacyUnits);
365
+
366
+ if (existsSync(serviceDir)) {
367
+ for (const name of readdirSync(serviceDir)) {
368
+ if (!name.endsWith('.service')) continue;
369
+ const unitPath = path.join(serviceDir, name);
370
+ if (!statSync(unitPath).isFile()) continue;
371
+ let current = '';
372
+ try {
373
+ current = readFileSync(unitPath, 'utf8');
374
+ } catch {
375
+ continue;
376
+ }
377
+ let next = current;
378
+ for (const needle of dependencyNeedles) {
379
+ next = next.replaceAll(needle, 'aria-harnessd.service');
380
+ }
381
+ if (next !== current) {
382
+ writeFileSync(unitPath, next, { mode: 0o644 });
383
+ logs.push(`Rebound legacy 8790 dependency → ${unitPath}`);
384
+ }
385
+ }
386
+ }
387
+
388
+ for (const unit of legacyUnits) {
389
+ const unitPath = path.join(serviceDir, unit);
390
+ const backupPath = `${unitPath}.legacy-disabled.bak`;
391
+ const disabledPath = `${unitPath}.disabled`;
392
+ try {
393
+ if (existsSync(unitPath) && statSync(unitPath).isFile() && !existsSync(backupPath)) {
394
+ copyFileSync(unitPath, backupPath);
395
+ logs.push(`Backed up legacy 8790 unit → ${backupPath}`);
396
+ }
397
+ } catch {}
398
+ try {
399
+ if (existsSync(unitPath) && statSync(unitPath).isFile()) {
400
+ rmSync(disabledPath, { force: true });
401
+ copyFileSync(unitPath, disabledPath);
402
+ rmSync(unitPath, { force: true });
403
+ logs.push(`Retired legacy 8790 unit file → ${disabledPath}`);
404
+ }
405
+ } catch {}
406
+ try {
407
+ execFileSync(systemctlPath, ['--user', 'disable', '--now', unit], { stdio: 'ignore' });
408
+ logs.push(`Disabled legacy 8790 unit: ${unit}`);
409
+ } catch {}
410
+ try {
411
+ execFileSync(systemctlPath, ['--user', 'mask', '--force', unit], { stdio: 'ignore' });
412
+ logs.push(`Masked legacy 8790 unit: ${unit}`);
413
+ } catch {}
414
+ }
415
+
416
+ try {
417
+ execFileSync(systemctlPath, ['--user', 'daemon-reload'], { stdio: 'ignore' });
418
+ } catch {}
419
+ }
420
+
421
+ function installCodexBridgeSystemdUserService(ariaDir: string, logs: string[]): void {
422
+ const systemctlPath = '/bin/systemctl';
423
+ if (process.platform !== 'linux' || !existsSync(systemctlPath)) {
424
+ return;
425
+ }
426
+
427
+ const serviceDir = path.join(homedir(), '.config', 'systemd', 'user');
428
+ const servicePath = path.join(serviceDir, 'aria-codex-bridge.service');
429
+ mkdirSync(serviceDir, { recursive: true, mode: 0o755 });
430
+
431
+ const unit = [
432
+ '[Unit]',
433
+ 'Description=Aria Codex Enforcement Bridge',
434
+ 'After=network.target aria-mounted-runtime.service',
435
+ 'Requires=aria-mounted-runtime.service',
436
+ '',
437
+ '[Service]',
438
+ 'Type=simple',
439
+ `EnvironmentFile=-${path.join(ariaDir, 'runtime', 'runtime.env')}`,
440
+ `ExecStart=${path.join(ariaDir, 'bin', 'aria-codex-bridge')}`,
441
+ 'Restart=always',
442
+ 'RestartSec=2',
443
+ '',
444
+ '[Install]',
445
+ 'WantedBy=default.target',
446
+ '',
447
+ ].join('\n');
448
+
449
+ writeFileSync(servicePath, unit, { mode: 0o644 });
450
+ logs.push(`Installed systemd user unit → ${servicePath}`);
451
+
452
+ try {
453
+ execFileSync(systemctlPath, ['--user', 'daemon-reload'], { stdio: 'ignore' });
454
+ execFileSync(systemctlPath, ['--user', 'enable', '--now', 'aria-codex-bridge.service'], { stdio: 'ignore' });
455
+ logs.push('Enabled and started systemd user service: aria-codex-bridge.service');
456
+ } catch (error) {
457
+ logs.push(`⚠ codex bridge service installed but not activated: ${error instanceof Error ? error.message : String(error)}`);
458
+ }
459
+ }
460
+
259
461
  function installLaunchAgent(ariaDir: string, logs: string[]): void {
260
462
  if (process.platform !== 'darwin') return;
261
- const harnessUrl =
262
- process.env.ARIA_HIVE_RUNTIME_URL ||
263
- process.env.ARIA_HARNESS_BASE_URL ||
264
- process.env.ARIA_HARNESS_URL ||
265
- 'https://harness.ariasos.com';
463
+ const localHarnessUrl = 'http://127.0.0.1:8790';
464
+ const upstreamHarnessUrl = canonicalUpstreamHarnessUrl();
266
465
 
267
466
  const launchAgentDir = path.join(homedir(), 'Library', 'LaunchAgents');
268
467
  const plistPath = path.join(launchAgentDir, 'com.aria.mounted-runtime.plist');
@@ -286,10 +485,20 @@ function installLaunchAgent(ariaDir: string, logs: string[]): void {
286
485
  <string>4319</string>
287
486
  <key>ARIA_RUNTIME_URL</key>
288
487
  <string>http://127.0.0.1:4319</string>
289
- <key>ARIA_HIVE_RUNTIME_URL</key>
290
- <string>${harnessUrl}</string>
291
- <key>ARIA_HARNESS_URL</key>
292
- <string>${harnessUrl}</string>
488
+ <key>ARIA_HARNESS_DAEMON_URL</key>
489
+ <string>${localHarnessUrl}</string>
490
+ <key>ARIA_HIVE_RUNTIME_URL</key>
491
+ <string>${localHarnessUrl}</string>
492
+ <key>ARIA_HARNESS_BASE_URL</key>
493
+ <string>${localHarnessUrl}</string>
494
+ <key>ARIA_HARNESS_URL</key>
495
+ <string>${localHarnessUrl}</string>
496
+ <key>ARIA_UPSTREAM_HARNESS_URL</key>
497
+ <string>${upstreamHarnessUrl}</string>
498
+ <key>ARIA_FORGE_SERVICE_URL</key>
499
+ <string>${localHarnessUrl}/api/forge/psi</string>
500
+ <key>ARIA_UPSTREAM_FORGE_SERVICE_URL</key>
501
+ <string>${upstreamHarnessUrl}/api/forge/psi</string>
293
502
  </dict>
294
503
  <key>RunAtLoad</key>
295
504
  <true/>
@@ -351,20 +560,31 @@ export async function installSharedRuntime(): Promise<string[]> {
351
560
  }
352
561
 
353
562
  const startWrapper = path.join(binDir, 'aria-runtime');
563
+ const harnessDaemonWrapper = path.join(binDir, 'aria-harnessd');
564
+ const codexBridgeWrapper = path.join(binDir, 'aria-codex-bridge');
354
565
  const doctorWrapper = path.join(binDir, 'aria-runtime-doctor');
355
566
  writeWrapper(startWrapper, path.join(runtimeDst, 'service.mjs'));
567
+ writeWrapper(harnessDaemonWrapper, path.join(runtimeDst, 'harness-daemon.mjs'));
568
+ writeWrapper(codexBridgeWrapper, path.join(runtimeDst, 'codex-bridge.mjs'));
356
569
  writeWrapper(doctorWrapper, path.join(runtimeDst, 'doctor.mjs'));
357
570
  const envPath = writeRuntimeEnv(runtimeDst);
358
571
  installDisciplinePack(runtimeDst, logs);
572
+ syncDoctrineTriggerMap(logs);
359
573
  installLocalQdrantService(ariaDir, logs);
574
+ retireLegacySoulPortForwardServices(logs);
575
+ installHarnessDaemonSystemdUserService(ariaDir, logs);
360
576
  installSystemdUserService(ariaDir, logs);
577
+ installCodexBridgeSystemdUserService(ariaDir, logs);
361
578
  installLaunchAgent(ariaDir, logs);
362
579
 
363
580
  logs.push(`Installed shared Aria runtime → ${runtimeDst}`);
364
581
  logs.push(`Installed runtime wrapper → ${startWrapper}`);
582
+ logs.push(`Installed harness daemon wrapper → ${harnessDaemonWrapper}`);
583
+ logs.push(`Installed Codex bridge wrapper → ${codexBridgeWrapper}`);
365
584
  logs.push(`Installed runtime doctor → ${doctorWrapper}`);
366
585
  logs.push(`Installed runtime env → ${envPath}`);
367
586
  logs.push('Mount any client against ARIA_RUNTIME_URL=http://127.0.0.1:4319');
587
+ logs.push('Harness packet and local validation are served by aria-harnessd on http://127.0.0.1:8790.');
368
588
  logs.push('Local Qdrant memory is mounted at http://127.0.0.1:6333 for garden pulse persistence.');
369
589
  logs.push('Runtime lease is encrypted at rest and revalidated against /api/license/heartbeat');
370
590
  logs.push('Forge synthesis is relayed through the canonical Aria endpoint unless ARIA_FORGE_SERVICE_URL overrides it.');