@addai/node 0.4.0

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 (133) hide show
  1. package/README.md +96 -0
  2. package/dist/agent-auth.d.ts +9 -0
  3. package/dist/agent-auth.js +96 -0
  4. package/dist/attachments.d.ts +20 -0
  5. package/dist/attachments.js +164 -0
  6. package/dist/capabilities.d.ts +64 -0
  7. package/dist/capabilities.js +406 -0
  8. package/dist/claude-binary.d.ts +16 -0
  9. package/dist/claude-binary.js +145 -0
  10. package/dist/claude-config.d.ts +11 -0
  11. package/dist/claude-config.js +144 -0
  12. package/dist/claude-print.d.ts +35 -0
  13. package/dist/claude-print.js +312 -0
  14. package/dist/claude-spawn.d.ts +40 -0
  15. package/dist/claude-spawn.js +220 -0
  16. package/dist/cli.d.ts +2 -0
  17. package/dist/cli.js +137 -0
  18. package/dist/codex-binary.d.ts +4 -0
  19. package/dist/codex-binary.js +70 -0
  20. package/dist/codex-spawn.d.ts +47 -0
  21. package/dist/codex-spawn.js +320 -0
  22. package/dist/command-runner.d.ts +9 -0
  23. package/dist/command-runner.js +592 -0
  24. package/dist/config.d.ts +7 -0
  25. package/dist/config.js +38 -0
  26. package/dist/control-server.d.ts +5 -0
  27. package/dist/control-server.js +292 -0
  28. package/dist/diskguard.d.ts +96 -0
  29. package/dist/diskguard.js +435 -0
  30. package/dist/error-classify.d.ts +20 -0
  31. package/dist/error-classify.js +159 -0
  32. package/dist/events.d.ts +26 -0
  33. package/dist/events.js +97 -0
  34. package/dist/gemini-binary.d.ts +4 -0
  35. package/dist/gemini-binary.js +64 -0
  36. package/dist/gemini-spawn.d.ts +52 -0
  37. package/dist/gemini-spawn.js +369 -0
  38. package/dist/git-identity.d.ts +18 -0
  39. package/dist/git-identity.js +104 -0
  40. package/dist/grok-binary.d.ts +4 -0
  41. package/dist/grok-binary.js +70 -0
  42. package/dist/grok-spawn.d.ts +41 -0
  43. package/dist/grok-spawn.js +495 -0
  44. package/dist/guardrails.d.ts +32 -0
  45. package/dist/guardrails.js +125 -0
  46. package/dist/hang-watchdog.d.ts +17 -0
  47. package/dist/hang-watchdog.js +48 -0
  48. package/dist/harness-registry.d.ts +55 -0
  49. package/dist/harness-registry.js +180 -0
  50. package/dist/heartbeat.d.ts +13 -0
  51. package/dist/heartbeat.js +95 -0
  52. package/dist/index.d.ts +12 -0
  53. package/dist/index.js +440 -0
  54. package/dist/install.d.ts +63 -0
  55. package/dist/install.js +467 -0
  56. package/dist/jsonl-tail.d.ts +20 -0
  57. package/dist/jsonl-tail.js +192 -0
  58. package/dist/kimi-binary.d.ts +4 -0
  59. package/dist/kimi-binary.js +68 -0
  60. package/dist/kimi-spawn.d.ts +47 -0
  61. package/dist/kimi-spawn.js +319 -0
  62. package/dist/lockfile.d.ts +30 -0
  63. package/dist/lockfile.js +169 -0
  64. package/dist/mcp-config.d.ts +23 -0
  65. package/dist/mcp-config.js +133 -0
  66. package/dist/memory-capture.d.ts +20 -0
  67. package/dist/memory-capture.js +45 -0
  68. package/dist/memory-pack.d.ts +34 -0
  69. package/dist/memory-pack.js +171 -0
  70. package/dist/model-filter.d.ts +4 -0
  71. package/dist/model-filter.js +34 -0
  72. package/dist/node-health.d.ts +18 -0
  73. package/dist/node-health.js +74 -0
  74. package/dist/pairing.d.ts +20 -0
  75. package/dist/pairing.js +129 -0
  76. package/dist/paths.d.ts +9 -0
  77. package/dist/paths.js +48 -0
  78. package/dist/precompact-hook.d.ts +27 -0
  79. package/dist/precompact-hook.js +136 -0
  80. package/dist/projects.d.ts +25 -0
  81. package/dist/projects.js +255 -0
  82. package/dist/prompt-keystrokes.d.ts +3 -0
  83. package/dist/prompt-keystrokes.js +56 -0
  84. package/dist/pty-helper.d.ts +26 -0
  85. package/dist/pty-helper.js +156 -0
  86. package/dist/request-pump.d.ts +12 -0
  87. package/dist/request-pump.js +249 -0
  88. package/dist/run-retry.d.ts +28 -0
  89. package/dist/run-retry.js +81 -0
  90. package/dist/self-update.d.ts +75 -0
  91. package/dist/self-update.js +192 -0
  92. package/dist/session-runner.d.ts +130 -0
  93. package/dist/session-runner.js +2271 -0
  94. package/dist/sleep-detector.d.ts +11 -0
  95. package/dist/sleep-detector.js +32 -0
  96. package/dist/state-migrate.d.ts +8 -0
  97. package/dist/state-migrate.js +91 -0
  98. package/dist/store.d.ts +5 -0
  99. package/dist/store.js +82 -0
  100. package/dist/supabase-client.d.ts +15 -0
  101. package/dist/supabase-client.js +119 -0
  102. package/dist/time-context.d.ts +10 -0
  103. package/dist/time-context.js +71 -0
  104. package/dist/tokens.d.ts +4 -0
  105. package/dist/tokens.js +33 -0
  106. package/dist/tui/app.d.ts +35 -0
  107. package/dist/tui/app.js +82 -0
  108. package/dist/tui/console-capture.d.ts +13 -0
  109. package/dist/tui/console-capture.js +62 -0
  110. package/dist/tui/dashboard.d.ts +42 -0
  111. package/dist/tui/dashboard.js +205 -0
  112. package/dist/tui/data.d.ts +57 -0
  113. package/dist/tui/data.js +62 -0
  114. package/dist/tui/harnesses.d.ts +16 -0
  115. package/dist/tui/harnesses.js +354 -0
  116. package/dist/tui/render.d.ts +21 -0
  117. package/dist/tui/render.js +116 -0
  118. package/dist/tui/requests.d.ts +18 -0
  119. package/dist/tui/requests.js +127 -0
  120. package/dist/tui/run.d.ts +13 -0
  121. package/dist/tui/run.js +178 -0
  122. package/dist/tui/transcript.d.ts +39 -0
  123. package/dist/tui/transcript.js +234 -0
  124. package/dist/tui.d.ts +1 -0
  125. package/dist/tui.js +314 -0
  126. package/dist/types.d.ts +65 -0
  127. package/dist/types.js +3 -0
  128. package/dist/win.d.ts +67 -0
  129. package/dist/win.js +317 -0
  130. package/package.json +60 -0
  131. package/scripts/fix-pty-helper.js +28 -0
  132. package/scripts/precompact-capture.js +292 -0
  133. package/scripts/smoke-test.sh +74 -0
@@ -0,0 +1,320 @@
1
+ "use strict";
2
+ // codex exec --json adapter.
3
+ //
4
+ // Codex's CLI prints a JSONL stream similar in spirit to claude
5
+ // --output-format stream-json, with different shapes. We normalise what
6
+ // we can recognise (thread.started, turn.started/completed/failed,
7
+ // assistant text deltas, errors) and pass everything else through as
8
+ // claude:codex.<type> events.
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.codexEffort = codexEffort;
44
+ exports.spawnCodex = spawnCodex;
45
+ const child_process_1 = require("child_process");
46
+ const fs = __importStar(require("fs"));
47
+ const os = __importStar(require("os"));
48
+ const path = __importStar(require("path"));
49
+ const codex_binary_1 = require("./codex-binary");
50
+ const diskguard_1 = require("./diskguard");
51
+ const win_1 = require("./win");
52
+ const events_1 = require("./events");
53
+ /** Quote a string as a TOML basic string (double-quoted, escape backslash + quote). */
54
+ function tomlString(s) {
55
+ return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"';
56
+ }
57
+ /** Build a config.toml that declares only the supplied MCP servers and
58
+ * return the directory path to use as CODEX_HOME. Caller is responsible
59
+ * for cleanup (we drop it under os.tmpdir() so it gets reaped). */
60
+ function writeCodexHome(servers, workingDirectory) {
61
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'entities-codex-'));
62
+ let toml = '# Generated by ainode — sandboxes codex against host config\n\n';
63
+ for (const s of servers) {
64
+ if (!s.slug || !s.command)
65
+ continue;
66
+ // On native Windows, registry commands like `npx` are .cmd shims that
67
+ // codex can't CreateProcess directly — wrap with `cmd /c` (no-op on POSIX).
68
+ const wrapped = (0, win_1.wrapMcpCommandForPlatform)(s.command, Array.isArray(s.args) ? s.args : []);
69
+ toml += `[mcp_servers.${tomlString(s.slug).slice(1, -1)}]\n`;
70
+ toml += `command = ${tomlString(wrapped.command)}\n`;
71
+ if (wrapped.args.length > 0) {
72
+ toml += `args = [${wrapped.args.map(tomlString).join(', ')}]\n`;
73
+ }
74
+ if (s.env && Object.keys(s.env).length > 0) {
75
+ toml += `[mcp_servers.${tomlString(s.slug).slice(1, -1)}.env]\n`;
76
+ for (const [k, v] of Object.entries(s.env)) {
77
+ toml += `${k} = ${tomlString(String(v))}\n`;
78
+ }
79
+ }
80
+ toml += '\n';
81
+ }
82
+ fs.writeFileSync(path.join(dir, 'config.toml'), toml, { mode: 0o600 });
83
+ // Symlink the host's auth artifacts into the sandbox so codex can find
84
+ // the user's logged-in credentials. Without this codex sees CODEX_HOME
85
+ // pointing at an empty dir and 401s against the OpenAI API.
86
+ const hostHome = path.join(os.homedir(), '.codex');
87
+ if (fs.existsSync(hostHome)) {
88
+ for (const file of ['auth.json', 'credentials.json', 'config.json']) {
89
+ const real = path.join(hostHome, file);
90
+ if (fs.existsSync(real)) {
91
+ // symlink where allowed; copy on Windows (file symlinks need
92
+ // admin/Developer Mode there). A copied auth.json means a token
93
+ // refreshed mid-run doesn't propagate back to the host — acceptable:
94
+ // the next run re-copies the current host file.
95
+ (0, win_1.linkOrCopyFile)(real, path.join(dir, file));
96
+ }
97
+ }
98
+ // Also link the sessions dir so codex can resume across runs.
99
+ // 'junction' works unprivileged on Windows; ignored on POSIX.
100
+ const sessionsDir = path.join(hostHome, 'sessions');
101
+ if (fs.existsSync(sessionsDir)) {
102
+ (0, win_1.linkDir)(sessionsDir, path.join(dir, 'sessions'));
103
+ }
104
+ }
105
+ // The registry installer writes selected skills to .claude/skills for
106
+ // Claude. Codex discovers skills from CODEX_HOME/skills, so bridge the
107
+ // session-local install into the isolated home.
108
+ const sessionSkillsDir = path.join(workingDirectory, '.claude', 'skills');
109
+ if (fs.existsSync(sessionSkillsDir)) {
110
+ (0, win_1.linkDir)(sessionSkillsDir, path.join(dir, 'skills'));
111
+ }
112
+ return dir;
113
+ }
114
+ function lineToEvents(line) {
115
+ const type = typeof line.type === 'string' ? line.type : '';
116
+ // Errors come as { type: 'error', message: string } — surface them.
117
+ if (type === 'error') {
118
+ return [{ type: 'error', code: 'codex_stream_error', message: String(line.message ?? 'unknown') }];
119
+ }
120
+ // Failure of the whole turn.
121
+ if (type === 'turn.failed') {
122
+ const err = line.error?.message ?? 'turn failed';
123
+ return [
124
+ { type: 'error', code: 'codex_turn_failed', message: err },
125
+ { type: 'turn_complete', stopReason: 'failed' },
126
+ ];
127
+ }
128
+ // Successful completion of a turn.
129
+ if (type === 'turn.completed') {
130
+ return [{ type: 'turn_complete', usage: line.usage ?? undefined }];
131
+ }
132
+ // Agent text response (codex uses slightly different keys depending
133
+ // on version — try a few likely shapes).
134
+ if (type === 'agent_message' || type === 'agent.response.delta' || type === 'message') {
135
+ const text = (typeof line.text === 'string' && line.text) ||
136
+ (typeof line.message === 'string' && line.message) ||
137
+ (line.delta?.text) ||
138
+ undefined;
139
+ if (text)
140
+ return [{ type: 'assistant_text', delta: text }];
141
+ }
142
+ // Newer codex versions wrap assistant text in 'item.completed' events
143
+ // as { item: { type: 'agent_message', text } }. Keep this strict so
144
+ // command/reasoning/tool items with textual payloads don't become answers.
145
+ if (type === 'item.completed') {
146
+ const item = line.item;
147
+ if (item?.type === 'agent_message' && typeof item.text === 'string' && item.text) {
148
+ return [{ type: 'assistant_text', delta: item.text }];
149
+ }
150
+ }
151
+ // Anything else: opaque passthrough. Reusing the claude:<type> namespace
152
+ // is intentional — vault renders it generically; we'd otherwise need a
153
+ // new "codex:" namespace which adds zero value.
154
+ return [{ type: `claude:codex.${type || 'unknown'}`, raw: line }];
155
+ }
156
+ /** Clamp our effort ladder to a value codex's CLI accepts. codex only knows
157
+ * `minimal|low|medium|high` and FAIL-FASTS (exit 2, dead-letter) on anything
158
+ * else — but RuntimeEffortLevel also carries `xhigh`/`max`, and a ladder
159
+ * failover (e.g. claude(max) → codex) forwards req.effort across agents
160
+ * unchanged. Map the extra-high tiers down to `high`; drop anything
161
+ * unrecognised so we emit no `-c reasoning.effort` at all rather than a bad
162
+ * one. Mirrors grokEffort() in grok-spawn.ts. */
163
+ function codexEffort(level) {
164
+ switch (level) {
165
+ case 'low': return 'low';
166
+ case 'medium': return 'medium';
167
+ case 'high':
168
+ case 'xhigh':
169
+ case 'max': return 'high';
170
+ default: return null;
171
+ }
172
+ }
173
+ function spawnCodex(input) {
174
+ // codex 0.132's `exec resume` subcommand has a restricted option set —
175
+ // --cd, --json, --skip-git-repo-check, --model all belong on the BASE
176
+ // `exec` command, before the `resume` subcommand. Putting them after
177
+ // resume returns "unexpected argument" and exits 2.
178
+ const baseFlags = ['--cd', input.workingDirectory, '--json'];
179
+ if (input.skipGitRepoCheck)
180
+ baseFlags.push('--skip-git-repo-check');
181
+ if (input.bypassPermissions)
182
+ baseFlags.push('--dangerously-bypass-approvals-and-sandbox');
183
+ else
184
+ baseFlags.push('--sandbox', input.sandboxMode ?? 'workspace-write');
185
+ if (input.model)
186
+ baseFlags.push('--model', input.model);
187
+ const effort = codexEffort(input.effortLevel);
188
+ if (effort) {
189
+ baseFlags.push('-c', `reasoning.effort=${JSON.stringify(effort)}`);
190
+ }
191
+ const args = input.resumeThreadId
192
+ ? ['exec', ...baseFlags, 'resume', input.resumeThreadId, input.prompt]
193
+ : ['exec', ...baseFlags, input.prompt];
194
+ // Sandbox codex against an isolated CODEX_HOME so the host's
195
+ // ~/.codex/config.toml mcp_servers don't leak in. TERM=dumb keeps
196
+ // codex's TTY heuristics happy (no fancy terminal output).
197
+ const codexHome = writeCodexHome(input.mcpServers ?? [], input.workingDirectory);
198
+ // Register the scratch dir with DiskGuard: marks it active (so a
199
+ // concurrent sweep won't evict a live run) and writes a PID lock so a
200
+ // different runtime process can tell it's still owned. dispose(true)
201
+ // removes the dir — this is the fix for the historical leak where every
202
+ // run's CODEX_HOME (50-350 MB) was abandoned under os.tmpdir() forever.
203
+ const guardSession = (0, diskguard_1.getDiskGuard)().beginSession(codexHome);
204
+ const childEnv = {
205
+ ...process.env,
206
+ TERM: 'dumb',
207
+ CODEX_HOME: codexHome,
208
+ };
209
+ // Resolve binary path before spawn. Previously called spawn('codex', ...)
210
+ // bare, which threw ENOENT under launchd / desktop-app PATH inheritance
211
+ // and rejected `done` before any session_end event fired → request
212
+ // stuck at 'running' on the DB.
213
+ const bin = (0, codex_binary_1.findCodexBinary)();
214
+ const listeners = [];
215
+ const emit = (e) => { for (const l of listeners)
216
+ try {
217
+ l(e);
218
+ }
219
+ catch { } };
220
+ const activityListeners = [];
221
+ const touchActivity = () => { for (const l of activityListeners)
222
+ try {
223
+ l();
224
+ }
225
+ catch { } };
226
+ let threadId = null;
227
+ let buffer = '';
228
+ if (!bin) {
229
+ // Synthesise a synchronous-looking failure: emit error + turn_complete
230
+ // so the caller's onExit logic gets the same shape as a normal failed
231
+ // run. bufferedEvents delivers even though the runner attaches its
232
+ // listener after an await (setImmediate used to fire first → dropped).
233
+ void guardSession.dispose(true);
234
+ const events = (0, events_1.bufferedEvents)([
235
+ { type: 'error', code: 'codex_not_found', message: 'codex binary not on PATH. Install with `npm i -g @openai/codex-cli`.' },
236
+ { type: 'turn_complete', stopReason: 'failed' },
237
+ ]);
238
+ return {
239
+ pid: undefined,
240
+ get threadId() { return null; },
241
+ onEvent: events.onEvent,
242
+ onActivity: () => { },
243
+ done: Promise.resolve(-1),
244
+ kill() { },
245
+ };
246
+ }
247
+ let proc;
248
+ try {
249
+ // npm .cmd shim → node script (win32); identity on POSIX. Throws on an
250
+ // unresolvable shim — caught below alongside spawn's own sync errors.
251
+ const inv = (0, win_1.resolveCliInvocation)(bin, args);
252
+ proc = (0, child_process_1.spawn)(inv.file, inv.args, {
253
+ cwd: input.workingDirectory,
254
+ env: childEnv,
255
+ stdio: ['ignore', 'pipe', 'pipe'],
256
+ });
257
+ }
258
+ catch (err) {
259
+ const msg = err.message || String(err);
260
+ void guardSession.dispose(true);
261
+ const events = (0, events_1.bufferedEvents)([
262
+ { type: 'error', code: 'codex_spawn_failed', message: msg },
263
+ { type: 'turn_complete', stopReason: 'failed' },
264
+ ]);
265
+ return {
266
+ pid: undefined,
267
+ get threadId() { return null; },
268
+ onEvent: events.onEvent,
269
+ onActivity: () => { },
270
+ done: Promise.resolve(-1),
271
+ kill() { },
272
+ };
273
+ }
274
+ proc.stdout?.on('data', (chunk) => {
275
+ touchActivity();
276
+ buffer += chunk.toString('utf8');
277
+ let nl;
278
+ while ((nl = buffer.indexOf('\n')) !== -1) {
279
+ const raw = buffer.slice(0, nl).trim();
280
+ buffer = buffer.slice(nl + 1);
281
+ if (!raw)
282
+ continue;
283
+ let parsed;
284
+ try {
285
+ parsed = JSON.parse(raw);
286
+ }
287
+ catch {
288
+ continue;
289
+ }
290
+ if (parsed.type === 'thread.started' && typeof parsed.thread_id === 'string') {
291
+ threadId = parsed.thread_id;
292
+ }
293
+ for (const ev of lineToEvents(parsed))
294
+ emit(ev);
295
+ }
296
+ });
297
+ proc.stderr?.on('data', (chunk) => {
298
+ touchActivity();
299
+ const txt = chunk.toString('utf8').trim();
300
+ if (txt === 'Reading additional input from stdin...')
301
+ return;
302
+ if (txt)
303
+ emit({ type: 'error', code: 'codex_stderr', message: txt.slice(0, 4096) });
304
+ });
305
+ const done = new Promise((resolve, reject) => {
306
+ // Remove the scratch CODEX_HOME on any terminal outcome (clean exit,
307
+ // SIGINT from kill()/timeout, or async spawn error). DiskGuard is the
308
+ // backstop for the paths this can't cover — SIGKILL and crashes.
309
+ proc.once('error', (err) => { void guardSession.dispose(true); reject(err); });
310
+ proc.once('exit', (code) => { void guardSession.dispose(true); resolve(code ?? -1); });
311
+ });
312
+ return {
313
+ pid: proc.pid,
314
+ get threadId() { return threadId; },
315
+ kill: () => { (0, win_1.killProcessTree)(proc); },
316
+ onEvent: (cb) => { listeners.push(cb); },
317
+ onActivity: (cb) => { activityListeners.push(cb); },
318
+ done,
319
+ };
320
+ }
@@ -0,0 +1,9 @@
1
+ export type RestartHook = (opts: {
2
+ commandId: string;
3
+ version: string;
4
+ }) => Promise<void>;
5
+ export declare function setRestartHook(fn: RestartHook): void;
6
+ /** Called from index.ts when a heartbeat reports pending_commands > 0.
7
+ * Drains the queue serially; re-entrant calls while draining are no-ops
8
+ * (the drain loop re-picks until empty). */
9
+ export declare function wake(): void;