@aria_asi/cli 0.2.29 → 0.2.31

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 (98) 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 +88 -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 +526 -2
  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 +111 -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 +2 -0
  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/aria-connector/src/self-update.d.ts +2 -1
  25. package/dist/aria-connector/src/self-update.d.ts.map +1 -1
  26. package/dist/aria-connector/src/self-update.js +84 -8
  27. package/dist/aria-connector/src/self-update.js.map +1 -1
  28. package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +53 -34
  29. package/dist/assets/hooks/aria-harness-via-sdk.mjs +126 -12
  30. package/dist/assets/hooks/aria-pre-tool-gate.mjs +185 -76
  31. package/dist/assets/hooks/aria-preturn-memory-gate.mjs +63 -14
  32. package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +2 -0
  33. package/dist/assets/hooks/aria-stop-gate.mjs +225 -52
  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 +24 -2
  39. package/dist/assets/opencode-plugins/harness-stop/index.js +94 -5
  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 +12 -0
  43. package/dist/runtime/discipline/doctrine_trigger_map.json +479 -0
  44. package/dist/runtime/doctrine_trigger_map.json +479 -0
  45. package/dist/runtime/fleet-engine.mjs +231 -0
  46. package/dist/runtime/harness-daemon.mjs +433 -0
  47. package/dist/runtime/local-phase.mjs +18 -0
  48. package/dist/runtime/manifest.json +1 -1
  49. package/dist/runtime/metering.mjs +100 -0
  50. package/dist/runtime/onboarding-engine.mjs +89 -0
  51. package/dist/runtime/plugin-engine.mjs +196 -0
  52. package/dist/runtime/sdk/BUNDLED.json +1 -1
  53. package/dist/runtime/sdk/index.d.ts +7 -0
  54. package/dist/runtime/sdk/index.js +120 -14
  55. package/dist/runtime/sdk/index.js.map +1 -1
  56. package/dist/runtime/service.mjs +1464 -67
  57. package/dist/runtime/vendor/aria-gate-runtime/index.d.ts +1 -1
  58. package/dist/runtime/vendor/aria-gate-runtime/index.d.ts.map +1 -1
  59. package/dist/runtime/vendor/aria-gate-runtime/index.js +16 -1
  60. package/dist/runtime/vendor/aria-gate-runtime/index.js.map +1 -1
  61. package/dist/runtime/workflow-engine.mjs +322 -0
  62. package/dist/sdk/BUNDLED.json +1 -1
  63. package/dist/sdk/index.d.ts +7 -0
  64. package/dist/sdk/index.js +120 -14
  65. package/dist/sdk/index.js.map +1 -1
  66. package/hooks/aria-cognition-substrate-binding.mjs +53 -34
  67. package/hooks/aria-harness-via-sdk.mjs +126 -12
  68. package/hooks/aria-pre-tool-gate.mjs +185 -76
  69. package/hooks/aria-preturn-memory-gate.mjs +63 -14
  70. package/hooks/aria-repo-doctrine-gate.mjs +2 -0
  71. package/hooks/aria-stop-gate.mjs +225 -52
  72. package/hooks/lib/canonical-lenses.mjs +6 -5
  73. package/hooks/lib/gate-loop-state.mjs +50 -0
  74. package/hooks/lib/hook-message-window.mjs +121 -0
  75. package/hooks/test-tier-lens-labeling.mjs +26 -58
  76. package/opencode-plugins/harness-gate/index.js +24 -2
  77. package/opencode-plugins/harness-stop/index.js +94 -5
  78. package/package.json +2 -2
  79. package/runtime-src/auth-middleware.mjs +251 -0
  80. package/runtime-src/codex-bridge.mjs +644 -0
  81. package/runtime-src/fleet-engine.mjs +231 -0
  82. package/runtime-src/harness-daemon.mjs +433 -0
  83. package/runtime-src/local-phase.mjs +18 -0
  84. package/runtime-src/metering.mjs +100 -0
  85. package/runtime-src/onboarding-engine.mjs +89 -0
  86. package/runtime-src/plugin-engine.mjs +196 -0
  87. package/runtime-src/service.mjs +1464 -67
  88. package/runtime-src/workflow-engine.mjs +322 -0
  89. package/scripts/bundle-sdk.mjs +5 -0
  90. package/src/connectors/claude-code.ts +98 -20
  91. package/src/connectors/codex.ts +534 -1
  92. package/src/connectors/doctrine-trigger-map.ts +112 -0
  93. package/src/connectors/must-read.ts +113 -0
  94. package/src/connectors/opencode.ts +3 -0
  95. package/src/connectors/runtime.ts +241 -21
  96. package/src/connectors/shell.ts +78 -3
  97. package/src/self-update.ts +89 -8
  98. package/dist/cli-0.2.0.tgz +0 -0
@@ -0,0 +1,113 @@
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 say "good enough", "should work", or make unverified completion claims.
103
+ - Do not treat skills as optional flavor text; they are the reading order and repair guide.
104
+ - Do not proceed after a gate block without re-opening the file that owns that gate.
105
+ `;
106
+ }
107
+
108
+ export function mustReadIntro(surface: MustReadSurface): string {
109
+ const root = surfaceRoot(surface);
110
+ return `## Must Read Order
111
+ Read \`${root}/ARIA_MUST_READ.md\` before the first non-trivial action and re-open it after any gate block, compaction, or repeated drift.
112
+ `;
113
+ }
@@ -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');
@@ -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.');
@@ -12,15 +12,32 @@ const SHELL_RC_FILES = ['.bashrc', '.bash_profile', '.profile', '.zshrc', '.zpro
12
12
  const SHELL_HOOK_LINE = '[ -f "$HOME/.aria/shell/runtime-client-env.sh" ] && . "$HOME/.aria/shell/runtime-client-env.sh"';
13
13
 
14
14
  function resolveToolPath(toolName: string): string | null {
15
+ const sanitizedPath = (process.env.PATH || '')
16
+ .split(path.delimiter)
17
+ .filter((entry) => entry && path.resolve(entry) !== path.resolve(WRAPPER_DIR))
18
+ .join(path.delimiter);
15
19
  try {
16
- const lines = execSync(`which -a ${toolName}`, { encoding: 'utf-8' })
20
+ const lines = execSync(`which -a ${toolName}`, {
21
+ encoding: 'utf-8',
22
+ env: {
23
+ ...process.env,
24
+ PATH: sanitizedPath,
25
+ },
26
+ })
17
27
  .split('\n')
18
28
  .map((line) => line.trim())
19
29
  .filter(Boolean);
20
30
  const preferred = lines.find((candidate) => !candidate.startsWith(`${WRAPPER_DIR}/`));
21
31
  return preferred || lines[0] || null;
22
32
  } catch {
23
- return null;
33
+ const fallbackCandidates = [
34
+ path.join(homedir(), '.npm-global', 'bin', toolName),
35
+ path.join(homedir(), '.local', 'bin', toolName),
36
+ path.join('/usr/local/bin', toolName),
37
+ path.join('/usr/bin', toolName),
38
+ ];
39
+ const fallback = fallbackCandidates.find((candidate) => existsSync(candidate));
40
+ return fallback || null;
24
41
  }
25
42
  }
26
43
 
@@ -72,9 +89,66 @@ if [ -z "\${ARIA_API_KEY:-}" ]; then
72
89
  fi
73
90
  fi
74
91
  fi
92
+ if [ -n "\${ARIA_API_KEY:-}" ] && [ -z "\${OPENAI_API_KEY:-}" ]; then
93
+ export OPENAI_API_KEY="$ARIA_API_KEY"
94
+ fi
75
95
  `;
76
96
  }
77
97
 
98
+ function buildToolExecBlock(toolName: string, toolPath: string): string {
99
+ if (toolName !== 'codex') {
100
+ return `exec "${toolPath}" "$@"`;
101
+ }
102
+
103
+ return `for arg in "$@"; do
104
+ if [ "$arg" = "--remote" ]; then
105
+ exec "${toolPath}" "$@"
106
+ fi
107
+ done
108
+
109
+ case "\${1:-}" in
110
+ app-server|exec-server|exec|review)
111
+ exec "${toolPath}" "$@"
112
+ ;;
113
+ esac
114
+
115
+ if [ "\${ARIA_CODEX_BYPASS_BRIDGE:-0}" = "1" ]; then
116
+ exec "${toolPath}" "$@"
117
+ fi
118
+
119
+ export ARIA_CODEX_REAL_BIN="\${ARIA_CODEX_REAL_BIN:-${toolPath}}"
120
+ export ARIA_CODEX_BRIDGE_HOST="\${ARIA_CODEX_BRIDGE_HOST:-127.0.0.1}"
121
+ export ARIA_CODEX_BRIDGE_PORT="\${ARIA_CODEX_BRIDGE_PORT:-4320}"
122
+ export ARIA_CODEX_DOWNSTREAM_PORT="\${ARIA_CODEX_DOWNSTREAM_PORT:-4321}"
123
+
124
+ if command -v systemctl >/dev/null 2>&1; then
125
+ systemctl --user import-environment ARIA_CODEX_REAL_BIN ARIA_CODEX_BRIDGE_HOST ARIA_CODEX_BRIDGE_PORT ARIA_CODEX_DOWNSTREAM_PORT >/dev/null 2>&1 || true
126
+ systemctl --user start aria-codex-bridge.service >/dev/null 2>&1 || true
127
+ fi
128
+
129
+ if ! (exec 9<>"/dev/tcp/$ARIA_CODEX_BRIDGE_HOST/$ARIA_CODEX_BRIDGE_PORT") >/dev/null 2>&1; then
130
+ nohup "$HOME/.aria/bin/aria-codex-bridge" >/dev/null 2>&1 &
131
+ fi
132
+
133
+ bridge_ready=0
134
+ for _aria_try in $(seq 1 50); do
135
+ if (exec 9<>"/dev/tcp/$ARIA_CODEX_BRIDGE_HOST/$ARIA_CODEX_BRIDGE_PORT") >/dev/null 2>&1; then
136
+ exec 9>&-
137
+ exec 9<&-
138
+ bridge_ready=1
139
+ break
140
+ fi
141
+ sleep 0.1
142
+ done
143
+
144
+ if [ "$bridge_ready" != "1" ]; then
145
+ echo "Aria Codex bridge did not become ready at ws://$ARIA_CODEX_BRIDGE_HOST:$ARIA_CODEX_BRIDGE_PORT" >&2
146
+ exit 1
147
+ fi
148
+
149
+ exec "${toolPath}" --remote "ws://$ARIA_CODEX_BRIDGE_HOST:$ARIA_CODEX_BRIDGE_PORT" "$@"`;
150
+ }
151
+
78
152
  export async function connectShell(
79
153
  toolName: string,
80
154
  config: AriaConfig,
@@ -93,13 +167,14 @@ export async function connectShell(
93
167
 
94
168
  const ariaBlock = buildShellWrapperBlock(config, toolName);
95
169
  const runtimeBlock = buildRuntimeInjectionBlock();
170
+ const execBlock = buildToolExecBlock(toolName, toolPath);
96
171
  const wrapperPath = path.join(WRAPPER_DIR, toolName);
97
172
  const script = `#!/usr/bin/env bash
98
173
  # ARIA HARNESS WRAPPER — generated by @aria/connector
99
174
  # Wraps ${toolName} with Aria's cognitive harness
100
175
  ${ariaBlock}
101
176
  ${runtimeBlock}
102
- exec "${toolPath}" "$@"
177
+ ${execBlock}
103
178
  `;
104
179
 
105
180
  writeFileSync(wrapperPath, script);