@aria_asi/cli 0.2.24 → 0.2.25

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.
@@ -1 +1 @@
1
- {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../../src/connectors/opencode.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA2D3E;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAoB5D"}
1
+ {"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../../src/connectors/opencode.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAiD/C,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAyH3E;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAqC5D"}
@@ -1,6 +1,52 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, readdirSync, statSync, chmodSync, unlinkSync, } from 'fs';
2
2
  import { homedir } from 'os';
3
3
  import * as path from 'path';
4
+ import { fileURLToPath } from 'node:url';
5
+ // ── Bundled OpenCode plugins ────────────────────────────────────────────────
6
+ //
7
+ // Two plugins ship with the connector:
8
+ // - harness-context: spawns the bundled inject-context.mjs on session start
9
+ // and prepends the resolved harness packet to OpenCode's
10
+ // system prompt. Routes through the canonical SDK at
11
+ // ~/.claude/aria-sdk/.
12
+ // - harness-role: injects the active role profile (default
13
+ // opencode_deepseek_engineer) — intro / continuity /
14
+ // post-action governance rules.
15
+ //
16
+ // Both live at <pkg>/opencode-plugins/<name>/. connectOpenCode copies them
17
+ // into ~/.opencode/plugins/<name>/ and wires the absolute paths into
18
+ // ~/.opencode/config.json's `plugin` (singular, OpenCode v2 schema) array.
19
+ //
20
+ // Compiled location of THIS file (claude-code path applies the same way):
21
+ // <pkg>/dist/aria-connector/src/connectors/opencode.js
22
+ // Plugins ship at <pkg>/opencode-plugins/ → 4 dirs up + 'opencode-plugins'.
23
+ const PLUGIN_NAMES = ['harness-context', 'harness-role'];
24
+ function packageOpenCodePluginsDir() {
25
+ const here = path.dirname(fileURLToPath(import.meta.url));
26
+ return path.resolve(here, '..', '..', '..', '..', 'opencode-plugins');
27
+ }
28
+ function copyPluginDir(srcDir, dstDir, logs) {
29
+ if (!existsSync(dstDir)) {
30
+ mkdirSync(dstDir, { recursive: true, mode: 0o755 });
31
+ }
32
+ for (const name of readdirSync(srcDir)) {
33
+ const src = path.join(srcDir, name);
34
+ const stat = statSync(src);
35
+ if (!stat.isFile())
36
+ continue;
37
+ const dst = path.join(dstDir, name);
38
+ copyFileSync(src, dst);
39
+ if (name.endsWith('.mjs') || name.endsWith('.js')) {
40
+ try {
41
+ chmodSync(dst, 0o755);
42
+ }
43
+ catch {
44
+ // chmod failure isn't fatal on filesystems that ignore mode bits.
45
+ }
46
+ }
47
+ }
48
+ logs.push(`Installed plugin → ${dstDir}`);
49
+ }
4
50
  export async function connectOpenCode(config) {
5
51
  const logs = [];
6
52
  const opencodeDir = path.join(homedir(), '.opencode');
@@ -8,17 +54,51 @@ export async function connectOpenCode(config) {
8
54
  logs.push('No ~/.opencode directory found — OpenCode may not be installed');
9
55
  return logs;
10
56
  }
57
+ // ── Install bundled plugins into ~/.opencode/plugins/<name>/ ──────────────
58
+ const pluginsRoot = path.join(opencodeDir, 'plugins');
59
+ if (!existsSync(pluginsRoot)) {
60
+ mkdirSync(pluginsRoot, { recursive: true, mode: 0o755 });
61
+ }
62
+ // Remove legacy single-file plugin shadows (~/.opencode/plugins/<name>.js).
63
+ // OpenCode v2 auto-discovers BOTH loose .js files and dir-shaped plugins;
64
+ // when both exist with the same name, behavior is undefined and on at least
65
+ // one machine the loose .js (carrying old hardcoded paths to
66
+ // ~/rei-ai-brain/harness/inject-context.mjs) was winning over the dir
67
+ // install. Idempotent cleanup so re-running connect heals these.
68
+ for (const pluginName of PLUGIN_NAMES) {
69
+ const shadowPath = path.join(pluginsRoot, `${pluginName}.js`);
70
+ if (existsSync(shadowPath)) {
71
+ try {
72
+ unlinkSync(shadowPath);
73
+ logs.push(`Removed legacy single-file plugin shadow: ${shadowPath}`);
74
+ }
75
+ catch {
76
+ // Permission or fs failure isn't fatal — the install proceeds.
77
+ }
78
+ }
79
+ }
80
+ const bundledDir = packageOpenCodePluginsDir();
81
+ const installedPaths = [];
82
+ for (const pluginName of PLUGIN_NAMES) {
83
+ const srcDir = path.join(bundledDir, pluginName);
84
+ if (!existsSync(srcDir)) {
85
+ logs.push(`⚠ Bundled plugin missing: ${srcDir} — connector tarball may be incomplete`);
86
+ continue;
87
+ }
88
+ const dstDir = path.join(pluginsRoot, pluginName);
89
+ copyPluginDir(srcDir, dstDir, logs);
90
+ installedPaths.push(dstDir);
91
+ }
92
+ // ── Wire ~/.opencode/config.json ──────────────────────────────────────────
11
93
  const configPath = path.join(opencodeDir, 'config.json');
12
94
  if (existsSync(configPath)) {
13
95
  try {
14
96
  const opencodeConfig = JSON.parse(readFileSync(configPath, 'utf-8'));
15
97
  let modified = false;
16
- // Remove any prior reference to '@aria/connector' from plugins.
17
- // That package name was written into older client configs but no
18
- // such npm package exists — the harness integration with OpenCode
19
- // is via agentsMdPath below, not via a plugin module. Loading a
20
- // non-existent plugin name crashes OpenCode on open. Idempotent
21
- // cleanup so re-running connect heals broken installs.
98
+ // Heal legacy '@aria/connector' references in the older `plugins`
99
+ // (plural) array. That package name was written by older versions
100
+ // of this connector but no such npm package exists — loading a
101
+ // non-existent plugin crashes OpenCode on open. Idempotent.
22
102
  if (Array.isArray(opencodeConfig.plugins)) {
23
103
  const before = opencodeConfig.plugins.length;
24
104
  opencodeConfig.plugins = opencodeConfig.plugins.filter((p) => p !== '@aria/connector');
@@ -27,6 +107,27 @@ export async function connectOpenCode(config) {
27
107
  logs.push('Removed legacy @aria/connector reference from OpenCode plugins (was crashing on open)');
28
108
  }
29
109
  }
110
+ // OpenCode v2 schema: `plugin` (singular) array of absolute paths or
111
+ // npm names. We write absolute paths to the locally-installed plugin
112
+ // dirs above, so OpenCode resolves them as Node modules without any
113
+ // network or registry dependency.
114
+ const existingPlugin = Array.isArray(opencodeConfig.plugin)
115
+ ? opencodeConfig.plugin.slice()
116
+ : [];
117
+ const stringSet = new Set(existingPlugin.filter((p) => typeof p === 'string'));
118
+ let pluginAdded = false;
119
+ for (const installPath of installedPaths) {
120
+ if (!stringSet.has(installPath)) {
121
+ existingPlugin.push(installPath);
122
+ stringSet.add(installPath);
123
+ pluginAdded = true;
124
+ }
125
+ }
126
+ if (pluginAdded) {
127
+ opencodeConfig.plugin = existingPlugin;
128
+ modified = true;
129
+ logs.push(`Wired ${installedPaths.length} Aria plugin(s) into OpenCode config.plugin`);
130
+ }
30
131
  if (!opencodeConfig.agentsMdPath) {
31
132
  opencodeConfig.agentsMdPath = path.join(homedir(), '.aria', 'AGENTS.md');
32
133
  modified = true;
@@ -43,6 +144,7 @@ export async function connectOpenCode(config) {
43
144
  logs.push('Failed to parse OpenCode config.json');
44
145
  }
45
146
  }
147
+ // ── Write the harness AGENTS.md ───────────────────────────────────────────
46
148
  const ariaDir = path.join(homedir(), '.aria');
47
149
  if (!existsSync(ariaDir)) {
48
150
  mkdirSync(ariaDir, { recursive: true });
@@ -59,11 +161,24 @@ export async function disconnectOpenCode() {
59
161
  if (existsSync(configPath)) {
60
162
  try {
61
163
  const opencodeConfig = JSON.parse(readFileSync(configPath, 'utf-8'));
164
+ // Strip any locally-installed Aria plugin paths from `plugin` (singular).
165
+ const pluginsRoot = path.join(homedir(), '.opencode', 'plugins');
166
+ if (Array.isArray(opencodeConfig.plugin)) {
167
+ const before = opencodeConfig.plugin.length;
168
+ opencodeConfig.plugin = opencodeConfig.plugin.filter((p) => {
169
+ if (typeof p !== 'string')
170
+ return true;
171
+ return !PLUGIN_NAMES.some((n) => p === path.join(pluginsRoot, n));
172
+ });
173
+ if (opencodeConfig.plugin.length !== before) {
174
+ logs.push('Removed Aria plugin paths from OpenCode config.plugin');
175
+ }
176
+ }
177
+ // Strip the legacy @aria/connector reference if present.
62
178
  if (Array.isArray(opencodeConfig.plugins)) {
63
179
  opencodeConfig.plugins = opencodeConfig.plugins.filter((p) => p !== '@aria/connector');
64
- writeFileSync(configPath, JSON.stringify(opencodeConfig, null, 2));
65
- logs.push('Removed @aria/connector from OpenCode plugins');
66
180
  }
181
+ writeFileSync(configPath, JSON.stringify(opencodeConfig, null, 2));
67
182
  }
68
183
  catch {
69
184
  logs.push('Failed to update OpenCode config');
@@ -78,7 +193,7 @@ function buildOpenCodeAgentsMd(config) {
78
193
  .join('\n\n');
79
194
  return `# Aria Harness — AGENTS.md
80
195
 
81
- Automatically injected by @aria/connector. This file provides Aria's cognitive harness
196
+ Automatically injected by @aria_asi/cli. This file provides Aria's cognitive harness
82
197
  to OpenCode sessions.
83
198
 
84
199
  ## Connected Repositories
@@ -1 +1 @@
1
- {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../../../src/connectors/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAkB;IACtD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;IAEtD,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;QAC5E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IACzD,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;YACrE,IAAI,QAAQ,GAAG,KAAK,CAAC;YAErB,gEAAgE;YAChE,iEAAiE;YACjE,kEAAkE;YAClE,gEAAgE;YAChE,gEAAgE;YAChE,uDAAuD;YACvD,IAAI,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1C,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;gBAC7C,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CACpD,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,KAAK,iBAAiB,CACxC,CAAC;gBACF,IAAI,cAAc,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;oBAC7C,QAAQ,GAAG,IAAI,CAAC;oBAChB,IAAI,CAAC,IAAI,CAAC,uFAAuF,CAAC,CAAC;gBACrG,CAAC;YACH,CAAC;YAED,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC;gBACjC,cAAc,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;gBACzE,QAAQ,GAAG,IAAI,CAAC;gBAChB,IAAI,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;YAChE,CAAC;YAED,IAAI,QAAQ,EAAE,CAAC;gBACb,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACrE,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACvD,MAAM,aAAa,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACpD,aAAa,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;IAC7C,IAAI,CAAC,IAAI,CAAC,mCAAmC,cAAc,EAAE,CAAC,CAAC;IAE/D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;IAEpE,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;YACrE,IAAI,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1C,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CACpD,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,KAAK,iBAAiB,CACvC,CAAC;gBACF,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBACnE,IAAI,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAkB;IAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtF,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;SACnD,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,KAAK,EAAE,CAAC;SAC/C,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,OAAO;;;;;;EAMP,QAAQ,IAAI,mBAAmB;;;EAG/B,UAAU,IAAI,iDAAiD;;;;;;;;;;;;;;;;;;;CAmBhE,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../../../src/connectors/opencode.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  {
2
- "bundledAt": "2026-04-28T01:07:13.445Z",
2
+ "bundledAt": "2026-04-28T01:14:50.399Z",
3
3
  "sdkSource": "/home/hamzaibrahim1/rei-ai-brain/harness/packages/harness-http-client/dist",
4
4
  "files": 3
5
5
  }
@@ -21,6 +21,7 @@
21
21
  import { readFileSync, writeFileSync, existsSync, mkdirSync, appendFileSync } from 'node:fs';
22
22
  import { dirname, join } from 'node:path';
23
23
  import { homedir } from 'node:os';
24
+ import { createRequire } from 'node:module';
24
25
 
25
26
  const HOME = homedir();
26
27
  const LOG = `${HOME}/.claude/aria-pre-tool-gate.log`;
@@ -129,6 +130,62 @@ if (isClientTier) {
129
130
  parentLedgerPath = `${HOME}/.claude/aria-discoveries-${safeSession}.jsonl`;
130
131
  }
131
132
 
133
+ // ── Harness packet fetch for sub-agent substrate binding ────────────────────
134
+ // Layer 1: after writing identity handoff, fetch the harness packet from
135
+ // /api/harness/codex so the sub-agent can load COGNITION (not just identity).
136
+ // Fail-soft: any fetch failure logs a warning and leaves the handoff identity-only.
137
+ // Never blocks the spawn — packet is substrate enrichment, not a gate.
138
+ //
139
+ // Tier-aware packet paths:
140
+ // Owner: ~/.claude/aria-agent-harness-packet.json
141
+ // Client: /var/lib/aria-licensee/{jti}/aria-agent-harness-packet.json
142
+ let packetPath = null;
143
+ if (isClientTier) {
144
+ packetPath = `/var/lib/aria-licensee/${jti}/aria-agent-harness-packet.json`;
145
+ } else {
146
+ packetPath = `${HOME}/.claude/aria-agent-harness-packet.json`;
147
+ }
148
+
149
+ async function fetchAndWriteHarnessPacket() {
150
+ if (!harnessToken || !harnessUrl) {
151
+ audit('warn: skipping packet fetch — no harnessToken or harnessUrl');
152
+ return null;
153
+ }
154
+ try {
155
+ const body = JSON.stringify({
156
+ stage: 'agent-spawn',
157
+ actor: 'harness-http-client',
158
+ system: 'claude-coding-agent',
159
+ surface: 'platform:harness-http-client',
160
+ isHamza: ownerTier.hamza,
161
+ sessionId: sessionId,
162
+ mode: 'subagent',
163
+ });
164
+ const resp = await fetch(`${harnessUrl}/api/harness/codex`, {
165
+ method: 'POST',
166
+ headers: {
167
+ 'Content-Type': 'application/json',
168
+ 'Authorization': `Bearer ${harnessToken}`,
169
+ },
170
+ body,
171
+ });
172
+ if (!resp.ok) {
173
+ audit(`warn: harness packet fetch HTTP ${resp.status} — identity-only handoff`);
174
+ return null;
175
+ }
176
+ const data = await resp.json();
177
+ mkdirSync(dirname(packetPath), { recursive: true });
178
+ writeFileSync(packetPath, JSON.stringify(data, null, 2));
179
+ audit(`wrote harness packet tier=${isClientTier ? 'client' : 'owner'} path=${packetPath}`);
180
+ return packetPath;
181
+ } catch (err) {
182
+ audit(`warn: harness packet fetch failed: ${(err?.message || err).toString().slice(0, 200)} — identity-only handoff`);
183
+ return null;
184
+ }
185
+ }
186
+
187
+ const resolvedPacketPath = await fetchAndWriteHarnessPacket();
188
+
132
189
  const handoff = {
133
190
  writtenAt: new Date().toISOString(),
134
191
  parentSessionId: sessionId,
@@ -137,6 +194,9 @@ const handoff = {
137
194
  ownerTier,
138
195
  parentLedgerPath,
139
196
  ttlMs: HANDOFF_TTL_MS,
197
+ // harnessPacketPath is set only if packet was successfully fetched;
198
+ // absent means sub-agent should fall back to calling /api/harness/codex directly.
199
+ ...(resolvedPacketPath ? { harnessPacketPath: resolvedPacketPath } : {}),
140
200
  };
141
201
 
142
202
  try {
@@ -144,7 +204,8 @@ try {
144
204
  writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
145
205
  audit(
146
206
  `wrote handoff tier=${isClientTier ? 'client' : 'owner'} jti=${jti ?? 'none'} ` +
147
- `session=${sessionId} hamza=${ownerTier.hamza} role=${ownerTier.roleProfile}`,
207
+ `session=${sessionId} hamza=${ownerTier.hamza} role=${ownerTier.roleProfile} ` +
208
+ `packetPath=${resolvedPacketPath ?? 'none'}`,
148
209
  );
149
210
  } catch (err) {
150
211
  audit(`ERROR: handoff write failed: ${(err?.message || err).toString().slice(0, 200)}`);
@@ -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, 8000));
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,120 @@
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 ~/.claude/aria-sdk/index.js (the SDK installed by
12
+ // `aria connect`'s claude-code path — clients with both connectors share it).
13
+ // Cache fast-path uses ~/.claude/.aria-harness-last-packet.json so OpenCode
14
+ // session-start doesn't block on a slow consult endpoint.
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_PATH = join(HOME, '.claude', 'aria-sdk', 'index.js');
24
+ const PACKET_CACHE_PATH = join(HOME, '.claude', '.aria-harness-last-packet.json');
25
+ const PACKET_CACHE_TTL_SEC = Number(process.env.ARIA_HARNESS_CACHE_TTL_SEC || '60');
26
+
27
+ function fail(reason) {
28
+ process.stderr.write(`[inject-context] ${reason}\n`);
29
+ process.exit(1);
30
+ }
31
+
32
+ function resolveApiKey() {
33
+ if (process.env.ARIA_API_KEY) return process.env.ARIA_API_KEY;
34
+ if (process.env.ARIA_HARNESS_TOKEN) return process.env.ARIA_HARNESS_TOKEN;
35
+ if (process.env.ARIA_MASTER_TOKEN) return process.env.ARIA_MASTER_TOKEN;
36
+ if (existsSync(OWNER_TOKEN_PATH)) {
37
+ try {
38
+ const v = readFileSync(OWNER_TOKEN_PATH, 'utf8').trim();
39
+ if (v) return v;
40
+ } catch {}
41
+ }
42
+ if (existsSync(LICENSE_PATH)) {
43
+ try {
44
+ const j = JSON.parse(readFileSync(LICENSE_PATH, 'utf8'));
45
+ if (j.token) return j.token;
46
+ } catch {}
47
+ }
48
+ return '';
49
+ }
50
+
51
+ function resolveBaseUrl() {
52
+ if (process.env.ARIA_HARNESS_BASE_URL) return process.env.ARIA_HARNESS_BASE_URL.replace(/\/+$/, '');
53
+ if (process.env.ARIA_SOUL_URL) return process.env.ARIA_SOUL_URL.replace(/\/+$/, '');
54
+ return 'http://localhost:30080';
55
+ }
56
+
57
+ function loadCachedPacket() {
58
+ try {
59
+ if (!existsSync(PACKET_CACHE_PATH)) return null;
60
+ const ageSec = (Date.now() - statSync(PACKET_CACHE_PATH).mtimeMs) / 1000;
61
+ if (ageSec > PACKET_CACHE_TTL_SEC) return null;
62
+ return JSON.parse(readFileSync(PACKET_CACHE_PATH, 'utf8'));
63
+ } catch {
64
+ return null;
65
+ }
66
+ }
67
+
68
+ function formatPacketAsContext(packet) {
69
+ if (typeof packet === 'string') return packet;
70
+ if (packet && typeof packet === 'object') {
71
+ if (packet.packet && typeof packet.packet === 'object') {
72
+ return JSON.stringify(packet.packet, null, 2);
73
+ }
74
+ if (packet.harness && typeof packet.harness === 'string') return packet.harness;
75
+ return JSON.stringify(packet, null, 2);
76
+ }
77
+ return '';
78
+ }
79
+
80
+ async function fetchViaSdk(baseUrl, apiKey) {
81
+ if (!existsSync(SDK_PATH)) {
82
+ fail(`SDK not found at ${SDK_PATH} — run \`aria connect\` to install`);
83
+ }
84
+ const mod = await import(SDK_PATH);
85
+ const Client = mod.HTTPHarnessClient;
86
+ if (!Client) fail('SDK loaded but HTTPHarnessClient not exported — bundle is broken');
87
+ const client = new Client({ baseUrl, apiKey, workspaceRoot: process.cwd() });
88
+ return client.getHarnessPacket({
89
+ stage: 'session-start',
90
+ actor: 'opencode',
91
+ system: 'opencode',
92
+ platform: 'opencode',
93
+ });
94
+ }
95
+
96
+ async function main() {
97
+ // Cache fast-path first — don't block OpenCode startup on a slow consult.
98
+ const cached = loadCachedPacket();
99
+ if (cached) {
100
+ process.stdout.write(formatPacketAsContext(cached));
101
+ return;
102
+ }
103
+
104
+ const apiKey = resolveApiKey();
105
+ if (!apiKey) {
106
+ fail('no API key — set ARIA_API_KEY or run `aria login`');
107
+ }
108
+ const baseUrl = resolveBaseUrl();
109
+
110
+ try {
111
+ const packet = await fetchViaSdk(baseUrl, apiKey);
112
+ process.stdout.write(formatPacketAsContext(packet));
113
+ } catch (err) {
114
+ fail(`SDK fetch failed: ${err && err.message ? err.message : String(err)}`);
115
+ }
116
+ }
117
+
118
+ main().catch((err) => {
119
+ fail(`unexpected: ${err && err.message ? err.message : String(err)}`);
120
+ });
@@ -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,77 @@
1
+ /**
2
+ * Aria Harness Role Plugin for OpenCode.
3
+ *
4
+ * Resolves the active role profile on session start and injects lane-specific
5
+ * governance rules (intro, continuity, post-action). Default profile is
6
+ * opencode_deepseek_engineer; override with OPENCODE_ARIA_ROLE_PROFILE env.
7
+ *
8
+ * Distribution: installed by `aria connect` (via connectors/opencode.ts) into
9
+ * `~/.opencode/plugins/harness-role/`. The plugin's absolute install path is
10
+ * wired into ~/.opencode/config.json's `plugin` array.
11
+ */
12
+
13
+ const ROLE_PROFILES = [
14
+ {
15
+ id: 'opencode_deepseek_engineer',
16
+ label: 'DeepSeek Engineer',
17
+ authority: 'full_write',
18
+ destructiveAllowed: false,
19
+ contractRequired: true,
20
+ ruleIntro: 'You are an engineering execution lane. Prefer exact files, commands, state transitions, and verification over explanation.',
21
+ ruleContinuity: 'Continue the active plan. Update issue/task ledger after meaningful state changes. Keep exact files, commands, evidence, and blockers visible.',
22
+ rulePostAction: 'After work, write outcome, verification, deploy state, residual risk, and next action.',
23
+ },
24
+ {
25
+ id: 'opencode_code_reviewer',
26
+ label: 'Code Reviewer',
27
+ authority: 'read_only',
28
+ destructiveAllowed: false,
29
+ contractRequired: true,
30
+ ruleIntro: 'You are a quality gate, not a generic explainer. Prioritize contradictions, missing evidence, regressions, and unverifiable completion claims.',
31
+ ruleContinuity: 'If output is not proven, fail it cleanly and say what evidence or fix is missing.',
32
+ rulePostAction: 'After review, record findings, severity, and specific fix recommendations.',
33
+ },
34
+ {
35
+ id: 'opencode_infra_operator',
36
+ label: 'Infra Operator',
37
+ authority: 'destructive',
38
+ destructiveAllowed: true,
39
+ contractRequired: true,
40
+ ruleIntro: 'You are an infra/execution lane. Prefer exact commands, state transitions, and verification. Never report success from intent alone.',
41
+ ruleContinuity: 'Report only what was executed, observed, changed, or still blocked. Bind every action to a tracked issue.',
42
+ rulePostAction: 'Record deploy state, rollback plan, residual risk, and verification evidence.',
43
+ },
44
+ ];
45
+
46
+ function resolveRole() {
47
+ const envRole = (process.env.OPENCODE_ARIA_ROLE_PROFILE || '').trim().toLowerCase();
48
+ if (envRole) {
49
+ const found = ROLE_PROFILES.find(p => p.id === envRole);
50
+ if (found) return found;
51
+ }
52
+ return ROLE_PROFILES.find(p => p.id === 'opencode_deepseek_engineer');
53
+ }
54
+
55
+ export default async function HarnessRolePlugin(ctx) {
56
+ const role = resolveRole();
57
+ const systemBlock = [
58
+ `=== ARIA LANE: ${role.label} ===`,
59
+ `Role: ${role.id} | Authority: ${role.authority} | Contract Required: ${role.contractRequired}`,
60
+ '',
61
+ role.ruleIntro,
62
+ '',
63
+ role.ruleContinuity,
64
+ '',
65
+ role.rulePostAction,
66
+ ].join('\n');
67
+
68
+ process.stderr.write(`[harness-role] Active role: ${role.id} (${role.label})\n`);
69
+ process.stderr.write(`[harness-role] Authority: ${role.authority} | Destructive: ${role.destructiveAllowed} | Contract: ${role.contractRequired}\n`);
70
+
71
+ try {
72
+ ctx.system?.prepend?.(systemBlock);
73
+ } catch (_) {
74
+ // Silent — different OpenCode versions expose ctx differently.
75
+ }
76
+ return {};
77
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "harness-role",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "main": "./index.js",
6
+ "exports": {
7
+ ".": "./index.js"
8
+ }
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aria_asi/cli",
3
- "version": "0.2.24",
3
+ "version": "0.2.25",
4
4
  "description": "Aria Smart CLI — the world's first harness-powered terminal companion",
5
5
  "bin": {
6
6
  "aria": "./bin/aria.js"
@@ -37,7 +37,8 @@
37
37
  "bin",
38
38
  "dist",
39
39
  "src",
40
- "hooks"
40
+ "hooks",
41
+ "opencode-plugins"
41
42
  ],
42
43
  "engines": {
43
44
  "node": ">=20.0.0"
@@ -1,8 +1,66 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ writeFileSync,
6
+ copyFileSync,
7
+ readdirSync,
8
+ statSync,
9
+ chmodSync,
10
+ unlinkSync,
11
+ } from 'fs';
2
12
  import { homedir } from 'os';
3
13
  import * as path from 'path';
14
+ import { fileURLToPath } from 'node:url';
4
15
  import type { AriaConfig } from '../config.js';
5
16
 
17
+ // ── Bundled OpenCode plugins ────────────────────────────────────────────────
18
+ //
19
+ // Two plugins ship with the connector:
20
+ // - harness-context: spawns the bundled inject-context.mjs on session start
21
+ // and prepends the resolved harness packet to OpenCode's
22
+ // system prompt. Routes through the canonical SDK at
23
+ // ~/.claude/aria-sdk/.
24
+ // - harness-role: injects the active role profile (default
25
+ // opencode_deepseek_engineer) — intro / continuity /
26
+ // post-action governance rules.
27
+ //
28
+ // Both live at <pkg>/opencode-plugins/<name>/. connectOpenCode copies them
29
+ // into ~/.opencode/plugins/<name>/ and wires the absolute paths into
30
+ // ~/.opencode/config.json's `plugin` (singular, OpenCode v2 schema) array.
31
+ //
32
+ // Compiled location of THIS file (claude-code path applies the same way):
33
+ // <pkg>/dist/aria-connector/src/connectors/opencode.js
34
+ // Plugins ship at <pkg>/opencode-plugins/ → 4 dirs up + 'opencode-plugins'.
35
+
36
+ const PLUGIN_NAMES = ['harness-context', 'harness-role'] as const;
37
+
38
+ function packageOpenCodePluginsDir(): string {
39
+ const here = path.dirname(fileURLToPath(import.meta.url));
40
+ return path.resolve(here, '..', '..', '..', '..', 'opencode-plugins');
41
+ }
42
+
43
+ function copyPluginDir(srcDir: string, dstDir: string, logs: string[]): void {
44
+ if (!existsSync(dstDir)) {
45
+ mkdirSync(dstDir, { recursive: true, mode: 0o755 });
46
+ }
47
+ for (const name of readdirSync(srcDir)) {
48
+ const src = path.join(srcDir, name);
49
+ const stat = statSync(src);
50
+ if (!stat.isFile()) continue;
51
+ const dst = path.join(dstDir, name);
52
+ copyFileSync(src, dst);
53
+ if (name.endsWith('.mjs') || name.endsWith('.js')) {
54
+ try {
55
+ chmodSync(dst, 0o755);
56
+ } catch {
57
+ // chmod failure isn't fatal on filesystems that ignore mode bits.
58
+ }
59
+ }
60
+ }
61
+ logs.push(`Installed plugin → ${dstDir}`);
62
+ }
63
+
6
64
  export async function connectOpenCode(config: AriaConfig): Promise<string[]> {
7
65
  const logs: string[] = [];
8
66
  const opencodeDir = path.join(homedir(), '.opencode');
@@ -12,29 +70,91 @@ export async function connectOpenCode(config: AriaConfig): Promise<string[]> {
12
70
  return logs;
13
71
  }
14
72
 
73
+ // ── Install bundled plugins into ~/.opencode/plugins/<name>/ ──────────────
74
+ const pluginsRoot = path.join(opencodeDir, 'plugins');
75
+ if (!existsSync(pluginsRoot)) {
76
+ mkdirSync(pluginsRoot, { recursive: true, mode: 0o755 });
77
+ }
78
+
79
+ // Remove legacy single-file plugin shadows (~/.opencode/plugins/<name>.js).
80
+ // OpenCode v2 auto-discovers BOTH loose .js files and dir-shaped plugins;
81
+ // when both exist with the same name, behavior is undefined and on at least
82
+ // one machine the loose .js (carrying old hardcoded paths to
83
+ // ~/rei-ai-brain/harness/inject-context.mjs) was winning over the dir
84
+ // install. Idempotent cleanup so re-running connect heals these.
85
+ for (const pluginName of PLUGIN_NAMES) {
86
+ const shadowPath = path.join(pluginsRoot, `${pluginName}.js`);
87
+ if (existsSync(shadowPath)) {
88
+ try {
89
+ unlinkSync(shadowPath);
90
+ logs.push(`Removed legacy single-file plugin shadow: ${shadowPath}`);
91
+ } catch {
92
+ // Permission or fs failure isn't fatal — the install proceeds.
93
+ }
94
+ }
95
+ }
96
+
97
+ const bundledDir = packageOpenCodePluginsDir();
98
+ const installedPaths: string[] = [];
99
+ for (const pluginName of PLUGIN_NAMES) {
100
+ const srcDir = path.join(bundledDir, pluginName);
101
+ if (!existsSync(srcDir)) {
102
+ logs.push(`⚠ Bundled plugin missing: ${srcDir} — connector tarball may be incomplete`);
103
+ continue;
104
+ }
105
+ const dstDir = path.join(pluginsRoot, pluginName);
106
+ copyPluginDir(srcDir, dstDir, logs);
107
+ installedPaths.push(dstDir);
108
+ }
109
+
110
+ // ── Wire ~/.opencode/config.json ──────────────────────────────────────────
15
111
  const configPath = path.join(opencodeDir, 'config.json');
16
112
  if (existsSync(configPath)) {
17
113
  try {
18
- const opencodeConfig = JSON.parse(readFileSync(configPath, 'utf-8'));
114
+ const opencodeConfig: Record<string, unknown> = JSON.parse(
115
+ readFileSync(configPath, 'utf-8'),
116
+ );
19
117
  let modified = false;
20
118
 
21
- // Remove any prior reference to '@aria/connector' from plugins.
22
- // That package name was written into older client configs but no
23
- // such npm package exists — the harness integration with OpenCode
24
- // is via agentsMdPath below, not via a plugin module. Loading a
25
- // non-existent plugin name crashes OpenCode on open. Idempotent
26
- // cleanup so re-running connect heals broken installs.
119
+ // Heal legacy '@aria/connector' references in the older `plugins`
120
+ // (plural) array. That package name was written by older versions
121
+ // of this connector but no such npm package exists — loading a
122
+ // non-existent plugin crashes OpenCode on open. Idempotent.
27
123
  if (Array.isArray(opencodeConfig.plugins)) {
28
- const before = opencodeConfig.plugins.length;
29
- opencodeConfig.plugins = opencodeConfig.plugins.filter(
30
- (p: unknown) => p !== '@aria/connector',
124
+ const before = (opencodeConfig.plugins as unknown[]).length;
125
+ opencodeConfig.plugins = (opencodeConfig.plugins as unknown[]).filter(
126
+ (p) => p !== '@aria/connector',
31
127
  );
32
- if (opencodeConfig.plugins.length !== before) {
128
+ if ((opencodeConfig.plugins as unknown[]).length !== before) {
33
129
  modified = true;
34
130
  logs.push('Removed legacy @aria/connector reference from OpenCode plugins (was crashing on open)');
35
131
  }
36
132
  }
37
133
 
134
+ // OpenCode v2 schema: `plugin` (singular) array of absolute paths or
135
+ // npm names. We write absolute paths to the locally-installed plugin
136
+ // dirs above, so OpenCode resolves them as Node modules without any
137
+ // network or registry dependency.
138
+ const existingPlugin: unknown[] = Array.isArray(opencodeConfig.plugin)
139
+ ? (opencodeConfig.plugin as unknown[]).slice()
140
+ : [];
141
+ const stringSet = new Set(
142
+ existingPlugin.filter((p): p is string => typeof p === 'string'),
143
+ );
144
+ let pluginAdded = false;
145
+ for (const installPath of installedPaths) {
146
+ if (!stringSet.has(installPath)) {
147
+ existingPlugin.push(installPath);
148
+ stringSet.add(installPath);
149
+ pluginAdded = true;
150
+ }
151
+ }
152
+ if (pluginAdded) {
153
+ opencodeConfig.plugin = existingPlugin;
154
+ modified = true;
155
+ logs.push(`Wired ${installedPaths.length} Aria plugin(s) into OpenCode config.plugin`);
156
+ }
157
+
38
158
  if (!opencodeConfig.agentsMdPath) {
39
159
  opencodeConfig.agentsMdPath = path.join(homedir(), '.aria', 'AGENTS.md');
40
160
  modified = true;
@@ -51,11 +171,11 @@ export async function connectOpenCode(config: AriaConfig): Promise<string[]> {
51
171
  }
52
172
  }
53
173
 
174
+ // ── Write the harness AGENTS.md ───────────────────────────────────────────
54
175
  const ariaDir = path.join(homedir(), '.aria');
55
176
  if (!existsSync(ariaDir)) {
56
177
  mkdirSync(ariaDir, { recursive: true });
57
178
  }
58
-
59
179
  const ariaAgentsPath = path.join(ariaDir, 'AGENTS.md');
60
180
  const agentsContent = buildOpenCodeAgentsMd(config);
61
181
  writeFileSync(ariaAgentsPath, agentsContent);
@@ -70,14 +190,31 @@ export async function disconnectOpenCode(): Promise<string[]> {
70
190
 
71
191
  if (existsSync(configPath)) {
72
192
  try {
73
- const opencodeConfig = JSON.parse(readFileSync(configPath, 'utf-8'));
193
+ const opencodeConfig: Record<string, unknown> = JSON.parse(
194
+ readFileSync(configPath, 'utf-8'),
195
+ );
196
+
197
+ // Strip any locally-installed Aria plugin paths from `plugin` (singular).
198
+ const pluginsRoot = path.join(homedir(), '.opencode', 'plugins');
199
+ if (Array.isArray(opencodeConfig.plugin)) {
200
+ const before = (opencodeConfig.plugin as unknown[]).length;
201
+ opencodeConfig.plugin = (opencodeConfig.plugin as unknown[]).filter((p) => {
202
+ if (typeof p !== 'string') return true;
203
+ return !PLUGIN_NAMES.some((n) => p === path.join(pluginsRoot, n));
204
+ });
205
+ if ((opencodeConfig.plugin as unknown[]).length !== before) {
206
+ logs.push('Removed Aria plugin paths from OpenCode config.plugin');
207
+ }
208
+ }
209
+
210
+ // Strip the legacy @aria/connector reference if present.
74
211
  if (Array.isArray(opencodeConfig.plugins)) {
75
- opencodeConfig.plugins = opencodeConfig.plugins.filter(
76
- (p: string) => p !== '@aria/connector',
212
+ opencodeConfig.plugins = (opencodeConfig.plugins as unknown[]).filter(
213
+ (p) => p !== '@aria/connector',
77
214
  );
78
- writeFileSync(configPath, JSON.stringify(opencodeConfig, null, 2));
79
- logs.push('Removed @aria/connector from OpenCode plugins');
80
215
  }
216
+
217
+ writeFileSync(configPath, JSON.stringify(opencodeConfig, null, 2));
81
218
  } catch {
82
219
  logs.push('Failed to update OpenCode config');
83
220
  }
@@ -94,7 +231,7 @@ function buildOpenCodeAgentsMd(config: AriaConfig): string {
94
231
 
95
232
  return `# Aria Harness — AGENTS.md
96
233
 
97
- Automatically injected by @aria/connector. This file provides Aria's cognitive harness
234
+ Automatically injected by @aria_asi/cli. This file provides Aria's cognitive harness
98
235
  to OpenCode sessions.
99
236
 
100
237
  ## Connected Repositories