negotium 0.18.2 → 0.19.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 (32) hide show
  1. package/bin/codex-sdk-window-hide.mjs +40 -0
  2. package/bin/negotium.mjs +4 -0
  3. package/dist/agent-helpers.js +432 -152
  4. package/dist/agent-helpers.js.map +7 -6
  5. package/dist/canonical-mcp-bridge.js +3 -2
  6. package/dist/canonical-mcp-bridge.js.map +1 -1
  7. package/dist/chunk-22mtr8np.js +200 -0
  8. package/dist/chunk-22mtr8np.js.map +9 -0
  9. package/dist/chunk-2mx7fq49.js +6 -0
  10. package/dist/chunk-2mx7fq49.js.map +9 -0
  11. package/dist/{chunk-44ajn328.js → chunk-4bzehg8t.js} +2 -2
  12. package/dist/chunk-4bzehg8t.js.map +12 -0
  13. package/dist/{chunk-r0xs3t81.js → chunk-f7p0h1gs.js} +2 -4
  14. package/dist/{chunk-r0xs3t81.js.map → chunk-f7p0h1gs.js.map} +2 -2
  15. package/dist/chunk-y8rtedjb.js +374 -0
  16. package/dist/chunk-y8rtedjb.js.map +13 -0
  17. package/dist/hosted-agent.js +355 -476
  18. package/dist/hosted-agent.js.map +7 -10
  19. package/dist/main.js +773 -473
  20. package/dist/main.js.map +8 -7
  21. package/dist/mcp-factories.js +407 -127
  22. package/dist/mcp-factories.js.map +7 -6
  23. package/dist/registry.js +1 -1
  24. package/dist/runtime/src/agents/codex-native-multi-agent.ts +45 -3
  25. package/dist/runtime/src/agents/codex-vault-hook-bridge.ts +36 -23
  26. package/dist/runtime/src/agents/codex-windows-wrapper.ts +174 -0
  27. package/dist/runtime/src/prompts/sessions/_shared-tools.md +19 -0
  28. package/dist/runtime/src/version.ts +1 -1
  29. package/dist/types/packages/core/src/agents/codex-windows-wrapper.d.ts +30 -0
  30. package/dist/types/packages/core/src/version.d.ts +1 -1
  31. package/package.json +1 -1
  32. package/dist/chunk-44ajn328.js.map +0 -12
package/dist/registry.js CHANGED
@@ -132,7 +132,7 @@ var codexRegistryOperations = {
132
132
  return { sessionId: threadId, rolloutPath };
133
133
  },
134
134
  async forkSession({ parentSessionId }) {
135
- const { forkCodexSession } = await import("./chunk-44ajn328.js");
135
+ const { forkCodexSession } = await import("./chunk-4bzehg8t.js");
136
136
  return await forkCodexSession(parentSessionId);
137
137
  },
138
138
  async cleanupRollouts({ sessionIds }) {
@@ -1,4 +1,4 @@
1
- import { spawn } from "node:child_process";
1
+ import { type ChildProcess, spawn } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import {
4
4
  chmodSync,
@@ -111,7 +111,7 @@ async function bootstrapCodexModelCache(codexHome: string, cachePath: string): P
111
111
  windowsHide: true,
112
112
  });
113
113
 
114
- await new Promise<void>((resolve, reject) => {
114
+ const refreshed = new Promise<void>((resolve, reject) => {
115
115
  let settled = false;
116
116
  let stdoutBuffer = "";
117
117
  let stderr = "";
@@ -192,6 +192,48 @@ async function bootstrapCodexModelCache(codexHome: string, cachePath: string): P
192
192
  },
193
193
  });
194
194
  });
195
+
196
+ try {
197
+ await refreshed;
198
+ } finally {
199
+ // `child.kill()` only signals. The caller deletes CODEX_HOME right after we
200
+ // return, and Windows cannot delete a directory the child still holds open
201
+ // (EBUSY), so wait for the process to actually exit first.
202
+ await waitForChildExit(child, CODEX_BOOTSTRAP_EXIT_WAIT_MS);
203
+ }
204
+ }
205
+
206
+ const CODEX_BOOTSTRAP_EXIT_WAIT_MS = 3_000;
207
+
208
+ function waitForChildExit(child: ChildProcess, timeoutMs: number): Promise<void> {
209
+ if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve();
210
+ return new Promise((resolve) => {
211
+ const timer = setTimeout(resolve, timeoutMs);
212
+ timer.unref?.();
213
+ child.once("exit", () => {
214
+ clearTimeout(timer);
215
+ resolve();
216
+ });
217
+ });
218
+ }
219
+
220
+ /**
221
+ * Remove the temporary CODEX_HOME. On Windows a just-exited child (or an
222
+ * antivirus scan of the files it wrote) can keep the directory busy for a
223
+ * moment, so retry, and never let cleanup of a throwaway directory fail a turn
224
+ * whose result was already read. POSIX keeps the original behaviour: unlinking
225
+ * open files works there, and a genuine failure still surfaces.
226
+ */
227
+ function removeIsolatedCodexHome(dir: string): void {
228
+ if (process.platform !== "win32") {
229
+ rmSync(dir, { recursive: true, force: true });
230
+ return;
231
+ }
232
+ try {
233
+ rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
234
+ } catch {
235
+ // Best effort: the OS temp cleaner will reclaim it.
236
+ }
195
237
  }
196
238
 
197
239
  async function bootstrapIsolatedCodexModelCache(
@@ -219,7 +261,7 @@ async function bootstrapIsolatedCodexModelCache(
219
261
  await bootstrap(isolatedHome, isolatedCachePath);
220
262
  return readCompatibleCodexModelCache(isolatedCachePath).contents;
221
263
  } finally {
222
- rmSync(isolatedHome, { recursive: true, force: true });
264
+ removeIsolatedCodexHome(isolatedHome);
223
265
  }
224
266
  }
225
267
 
@@ -1,12 +1,13 @@
1
1
  import { randomBytes } from "node:crypto";
2
2
  import { existsSync } from "node:fs";
3
- import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import { chmod, copyFile, mkdtemp, rm, writeFile } from "node:fs/promises";
4
4
  import type { Socket } from "node:net";
5
5
  import { createServer } from "node:net";
6
6
  import { tmpdir } from "node:os";
7
7
  import { dirname, join, resolve } from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { codexCliScriptPath } from "#agents/codex-native-multi-agent";
10
+ import { ensureCodexWindowsWrapperExe } from "#agents/codex-windows-wrapper";
10
11
  import { deepMapStrings } from "#agents/deep-map";
11
12
  import { referencesHostedSecretStorage, substituteHostedSecrets } from "#agents/execution-host";
12
13
  import { shouldSubstituteVaultToolInput } from "#agents/vault-tool-policy";
@@ -104,38 +105,36 @@ export interface CodexVaultHookBridge {
104
105
  }
105
106
 
106
107
  /**
107
- * Why this bridge has no Windows implementation yet.
108
+ * Why the Windows wrapper is an `.exe`, not a script.
108
109
  *
109
110
  * The hook only runs if codex is invoked with `--dangerously-bypass-hook-trust`
110
- * — an argv-only flag with no config-file or environment equivalent — and the
111
+ * -- an argv-only flag with no config-file or environment equivalent -- and the
111
112
  * SDK builds its own argv, so the flag can only be injected by a wrapper
112
113
  * standing in for the codex executable. On POSIX that wrapper is the
113
- * `#!/bin/sh` script below. Windows cannot use one: `@openai/codex-sdk` spawns
114
- * `codexPathOverride` through `child_process.spawn` with no `shell` option, and
115
- * Node refuses to spawn a `.cmd`/`.bat` that way (EINVAL), so only a real
116
- * executable would do.
114
+ * `#!/bin/sh` script above. Windows cannot use a script: `@openai/codex-sdk`
115
+ * spawns `codexPathOverride` through `child_process.spawn` with no `shell`
116
+ * option, and Node refuses to spawn a `.cmd`/`.bat` that way (EINVAL). So on
117
+ * Windows the wrapper is a small compiled executable (see
118
+ * `codex-windows-wrapper.ts`) that reads a private per-bridge config file.
117
119
  *
118
- * Failing here rather than handing back an unspawnable wrapper keeps the
119
- * security posture identical to macOS: `codexProvider` treats a bridge failure
120
- * as fatal for the turn, so a Codex turn never runs with the Vault
121
- * substitution and sensitive-storage denial silently absent. The alternative —
122
- * dropping the wrapper and letting the turn proceed — would leave those two
123
- * protections off while everything downstream assumed they were on.
120
+ * A bridge failure is still fatal for the turn (`codexProvider`), so a Codex
121
+ * turn never runs with the Vault substitution and sensitive-storage denial
122
+ * silently absent.
124
123
  */
125
- const WINDOWS_BRIDGE_UNSUPPORTED =
126
- "Codex Vault hooks are not available on Windows: the hook-trust bypass can only be passed " +
127
- "through a wrapper executable, and the Codex SDK spawns the override without a shell, so a " +
128
- ".cmd wrapper cannot run. Use the Claude or Maestro backend on this host.";
124
+ const IS_WINDOWS = process.platform === "win32";
125
+
126
+ function windowsQuote(value: string): string {
127
+ return `"${value.replaceAll('"', '\\"')}"`;
128
+ }
129
129
 
130
130
  export async function createCodexVaultHookBridge(userId: string): Promise<CodexVaultHookBridge> {
131
- if (process.platform === "win32") throw new Error(WINDOWS_BRIDGE_UNSUPPORTED);
132
131
  const root = await mkdtemp(join(tmpdir(), "negotium-codex-vault-"));
133
132
  await chmod(root, 0o700);
134
133
  // Windows listens on a named pipe instead; the digest in `ipcEndpoint` is
135
134
  // taken over this whole path, so the per-bridge mkdtemp above still supplies
136
135
  // the uniqueness that the constant basename does not.
137
136
  const socketPath = ipcEndpoint(join(root, "hook.sock"));
138
- const wrapperPath = join(root, "codex-with-hooks");
137
+ const wrapperPath = join(root, IS_WINDOWS ? "codex-with-hooks.exe" : "codex-with-hooks");
139
138
  const token = randomBytes(32).toString("hex");
140
139
  const connections = new Set<Socket>();
141
140
  const server = createServer((socket) => {
@@ -187,14 +186,28 @@ export async function createCodexVaultHookBridge(userId: string): Promise<CodexV
187
186
  });
188
187
  });
189
188
  restrictIpcEndpoint(socketPath);
190
- await writeFile(wrapperPath, privateCodexWrapper(codexCliScriptPath(), socketPath, token), {
191
- mode: 0o700,
192
- });
189
+ if (IS_WINDOWS) {
190
+ const { BINARIES_DIR } = await import("#platform/config");
191
+ const cached = await ensureCodexWindowsWrapperExe(join(BINARIES_DIR, "codex-hook-wrapper"));
192
+ await copyFile(cached, wrapperPath);
193
+ // Same private per-bridge dir as the POSIX wrapper; the exe reads this
194
+ // file (runtime, codex script, hook socket, hook token, one per line).
195
+ await writeFile(
196
+ join(root, "codex-with-hooks.cfg"),
197
+ [process.execPath, codexCliScriptPath(), socketPath, token].join("\n"),
198
+ { encoding: "utf8", mode: 0o600 },
199
+ );
200
+ } else {
201
+ await writeFile(wrapperPath, privateCodexWrapper(codexCliScriptPath(), socketPath, token), {
202
+ mode: 0o700,
203
+ });
204
+ }
193
205
 
194
206
  // Keep the capability out of the process list and reviewed hook command.
195
207
  // The private wrapper explicitly authorizes this runtime-owned hook for
196
208
  // headless Codex turns.
197
- const command = [shellQuote(process.execPath), shellQuote(hookClientPath())].join(" ");
209
+ const quote = IS_WINDOWS ? windowsQuote : shellQuote;
210
+ const command = [quote(process.execPath), quote(hookClientPath())].join(" ");
198
211
  return {
199
212
  codexPathOverride: wrapperPath,
200
213
  environment: { [HOOK_SOCKET_ENV]: socketPath, [HOOK_TOKEN_ENV]: token },
@@ -0,0 +1,174 @@
1
+ import { execFile } from "node:child_process";
2
+ import { createHash, randomUUID } from "node:crypto";
3
+ import { existsSync } from "node:fs";
4
+ import { mkdir, rename, rm, writeFile } from "node:fs/promises";
5
+ import { join } from "node:path";
6
+ import { promisify } from "node:util";
7
+
8
+ const execFileAsync = promisify(execFile);
9
+
10
+ /**
11
+ * The Windows stand-in for the POSIX `#!/bin/sh` codex wrapper.
12
+ *
13
+ * `@openai/codex-sdk` spawns `codexPathOverride` with no `shell`, and Node
14
+ * refuses to spawn a `.cmd`/`.bat` that way (EINVAL), so the wrapper has to be
15
+ * a real executable. It is a ~40-line C# program compiled once with the
16
+ * `csc.exe` that ships with every Windows (.NET Framework 4), cached by source
17
+ * hash, and copied next to a per-bridge config file for each bridge.
18
+ *
19
+ * It is a plain console exe (`Process.Start` with inherited stdio). A GUI-subsystem
20
+ * variant that hides the child via CreateProcess(CREATE_NO_WINDOW) was tried and
21
+ * rejected: antivirus flagged it as malware. The SDK spawns this without
22
+ * windowsHide, so a console window may briefly appear per Codex turn.
23
+ *
24
+ * Behaviour mirrors the sh wrapper: read the private config (runtime, codex
25
+ * script, hook socket, hook token), export the socket/token to the child, run
26
+ * `<runtime> <codex script> [exec --dangerously-bypass-hook-trust] <args...>`
27
+ * with inherited stdio, and return the child's exit code. Unlike `exec` on
28
+ * POSIX there is a second process here, so the child is placed in a job object
29
+ * with kill-on-close: when the SDK terminates the wrapper on abort the whole
30
+ * codex tree goes with it instead of being orphaned.
31
+ */
32
+ export const CODEX_WINDOWS_WRAPPER_SOURCE = String.raw`
33
+ using System;
34
+ using System.Diagnostics;
35
+ using System.IO;
36
+ using System.Runtime.InteropServices;
37
+ using System.Text;
38
+
39
+ static class CodexHookWrapper {
40
+ [StructLayout(LayoutKind.Sequential)]
41
+ struct IO_COUNTERS { public ulong a, b, c, d, e, f; }
42
+ [StructLayout(LayoutKind.Sequential)]
43
+ struct BASIC {
44
+ public long PerProcessUserTimeLimit, PerJobUserTimeLimit;
45
+ public uint LimitFlags;
46
+ public UIntPtr MinimumWorkingSetSize, MaximumWorkingSetSize;
47
+ public uint ActiveProcessLimit;
48
+ public UIntPtr Affinity;
49
+ public uint PriorityClass, SchedulingClass;
50
+ }
51
+ [StructLayout(LayoutKind.Sequential)]
52
+ struct EXTENDED {
53
+ public BASIC Basic;
54
+ public IO_COUNTERS Io;
55
+ public UIntPtr ProcessMemoryLimit, JobMemoryLimit, PeakProcessMemoryUsed, PeakJobMemoryUsed;
56
+ }
57
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
58
+ static extern IntPtr CreateJobObject(IntPtr attrs, string name);
59
+ [DllImport("kernel32.dll")]
60
+ static extern bool SetInformationJobObject(IntPtr job, int cls, IntPtr info, uint len);
61
+ [DllImport("kernel32.dll")]
62
+ static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
63
+
64
+ static string Quote(string a) {
65
+ if (a.Length > 0 && a.IndexOfAny(new[] { ' ', '\t', '\n', '\v', '"' }) < 0) return a;
66
+ var sb = new StringBuilder("\"");
67
+ int bs = 0;
68
+ foreach (char c in a) {
69
+ if (c == '\\') { bs++; }
70
+ else if (c == '"') { sb.Append('\\', bs * 2 + 1); sb.Append('"'); bs = 0; }
71
+ else { sb.Append('\\', bs); bs = 0; sb.Append(c); }
72
+ }
73
+ sb.Append('\\', bs * 2);
74
+ sb.Append('"');
75
+ return sb.ToString();
76
+ }
77
+
78
+ static IntPtr KillOnCloseJob() {
79
+ IntPtr job = CreateJobObject(IntPtr.Zero, null);
80
+ if (job == IntPtr.Zero) return IntPtr.Zero;
81
+ var info = new EXTENDED();
82
+ info.Basic.LimitFlags = 0x2000; // JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
83
+ int size = Marshal.SizeOf(typeof(EXTENDED));
84
+ IntPtr mem = Marshal.AllocHGlobal(size);
85
+ try {
86
+ Marshal.StructureToPtr(info, mem, false);
87
+ if (!SetInformationJobObject(job, 9, mem, (uint)size)) return IntPtr.Zero;
88
+ } finally { Marshal.FreeHGlobal(mem); }
89
+ return job;
90
+ }
91
+
92
+ static int Main(string[] args) {
93
+ string self = Process.GetCurrentProcess().MainModule.FileName;
94
+ string[] cfg = File.ReadAllLines(Path.ChangeExtension(self, ".cfg"), Encoding.UTF8);
95
+ if (cfg.Length < 4) { Console.Error.WriteLine("codex hook wrapper: bad config"); return 70; }
96
+ var sb = new StringBuilder(Quote(cfg[1]));
97
+ int i = 0;
98
+ if (args.Length > 0 && args[0] == "exec") {
99
+ sb.Append(" exec --dangerously-bypass-hook-trust");
100
+ i = 1;
101
+ }
102
+ for (; i < args.Length; i++) sb.Append(' ').Append(Quote(args[i]));
103
+
104
+ // No CreateNoWindow: with it the child stops inheriting our stdio handles.
105
+ var psi = new ProcessStartInfo(cfg[0], sb.ToString());
106
+ psi.UseShellExecute = false;
107
+ psi.EnvironmentVariables["NEGOTIUM_CODEX_VAULT_HOOK_SOCKET"] = cfg[2];
108
+ psi.EnvironmentVariables["NEGOTIUM_CODEX_VAULT_HOOK_TOKEN"] = cfg[3];
109
+ IntPtr job = KillOnCloseJob();
110
+ using (Process p = Process.Start(psi)) {
111
+ if (job != IntPtr.Zero) AssignProcessToJobObject(job, p.Handle);
112
+ p.WaitForExit();
113
+ return p.ExitCode;
114
+ }
115
+ }
116
+ }
117
+ `;
118
+
119
+ function cscPath(): string {
120
+ const windir = process.env.WINDIR ?? process.env.SystemRoot ?? "C:\\Windows";
121
+ for (const framework of ["Framework64", "Framework"]) {
122
+ const candidate = join(windir, "Microsoft.NET", framework, "v4.0.30319", "csc.exe");
123
+ if (existsSync(candidate)) return candidate;
124
+ }
125
+ throw new Error(
126
+ "csc.exe (.NET Framework 4) was not found, so the Codex hook wrapper cannot be built",
127
+ );
128
+ }
129
+
130
+ /**
131
+ * Path to the compiled wrapper, building it on first use. The cache key is the
132
+ * source hash, so a changed wrapper is rebuilt and old copies are just ignored.
133
+ * Compilation writes to a temp name and renames, so concurrent turns never see
134
+ * a half-written exe.
135
+ */
136
+ export async function ensureCodexWindowsWrapperExe(cacheDir: string): Promise<string> {
137
+ const digest = createHash("sha256")
138
+ .update(CODEX_WINDOWS_WRAPPER_SOURCE)
139
+ .digest("hex")
140
+ .slice(0, 12);
141
+ const exePath = join(cacheDir, `codex-hook-wrapper-${digest}.exe`);
142
+ if (existsSync(exePath)) return exePath;
143
+
144
+ await mkdir(cacheDir, { recursive: true });
145
+ const stamp = randomUUID();
146
+ const sourcePath = join(cacheDir, `wrapper-${stamp}.cs`);
147
+ const tempExe = join(cacheDir, `wrapper-${stamp}.exe`);
148
+ try {
149
+ await writeFile(sourcePath, CODEX_WINDOWS_WRAPPER_SOURCE, "utf8");
150
+ await execFileAsync(
151
+ cscPath(),
152
+ ["/nologo", "/target:exe", "/optimize+", `/out:${tempExe}`, sourcePath],
153
+ { windowsHide: true, timeout: 60_000 },
154
+ );
155
+ try {
156
+ await rename(tempExe, exePath);
157
+ } catch (error) {
158
+ // Another turn finished the same build first; its exe is equivalent.
159
+ if (!existsSync(exePath)) throw error;
160
+ }
161
+ return exePath;
162
+ } catch (error) {
163
+ const detail =
164
+ error && typeof error === "object" && "stdout" in error
165
+ ? String((error as { stdout?: unknown }).stdout ?? "").trim()
166
+ : "";
167
+ throw new Error(
168
+ `Failed to build the Codex hook wrapper: ${error instanceof Error ? error.message : String(error)}${detail ? `\n${detail}` : ""}`,
169
+ );
170
+ } finally {
171
+ await rm(sourcePath, { force: true });
172
+ await rm(tempExe, { force: true });
173
+ }
174
+ }
@@ -17,3 +17,22 @@ Use a tool only when it is actually available; otherwise say so instead of prete
17
17
  - Scheduled tasks: manage with `cron-manager` tools; scripts must already exist (`cron_list_scripts`). Jobs in one topic share a Cron conversation, so `cron_reset` clears the topic's whole Cron context, not one job.
18
18
  - Heavy work (large files, video encode, big crawls, browser automation): check `get_system_health` first and back off under resource pressure.
19
19
  - Computer Use: if unavailable, check `get_mcp_config` for optional `cua-rs`; MCP changes apply next session.
20
+
21
+ ## Browser
22
+ Reading pages:
23
+ - Escalate in this order: `browser_snapshot` (interactive elements only) → full snapshot → wait briefly and re-snapshot if the page is still changing → screenshot as a last resort. Never make a screenshot the first move.
24
+ - Never truncate a snapshot. Refs are invalidated by every new snapshot, so re-snapshot after each action, and never mix a ref into a CSS selector.
25
+
26
+ Acting:
27
+ - Click and navigation tools already wait for the page to settle, so do not add a habitual wait right after them. Wait only when a fresh snapshot shows the page is still changing.
28
+ - Treat an action as unconfirmed until a fresh snapshot shows the expected state.
29
+ - When the next step does not depend on the new page state, batch the steps into one `browser_evaluate` call.
30
+ - Do not guess URLs except for well-known destinations; follow links instead.
31
+
32
+ When something fails:
33
+ - Dismiss popups and cookie banners first. If a click fails as obscured, inspect what actually covers the target before retrying.
34
+ - If the same approach fails 2-3 times, switch to a different path rather than retrying with another selector.
35
+ - If the page state is unexpected, suspect a missed or wrong-target action before inferring site-specific rules.
36
+
37
+ Coordinate input:
38
+ - Use `browser_pointer`, `browser_wheel`, and `browser_drag` only for things the accessibility tree cannot expose (canvases, drag handles, maps), and return to ref-based actions immediately afterward.
@@ -1 +1 @@
1
- export const NEGOTIUM_VERSION = "0.18.2";
1
+ export const NEGOTIUM_VERSION = "0.19.0";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The Windows stand-in for the POSIX `#!/bin/sh` codex wrapper.
3
+ *
4
+ * `@openai/codex-sdk` spawns `codexPathOverride` with no `shell`, and Node
5
+ * refuses to spawn a `.cmd`/`.bat` that way (EINVAL), so the wrapper has to be
6
+ * a real executable. It is a ~40-line C# program compiled once with the
7
+ * `csc.exe` that ships with every Windows (.NET Framework 4), cached by source
8
+ * hash, and copied next to a per-bridge config file for each bridge.
9
+ *
10
+ * It is a plain console exe (`Process.Start` with inherited stdio). A GUI-subsystem
11
+ * variant that hides the child via CreateProcess(CREATE_NO_WINDOW) was tried and
12
+ * rejected: antivirus flagged it as malware. The SDK spawns this without
13
+ * windowsHide, so a console window may briefly appear per Codex turn.
14
+ *
15
+ * Behaviour mirrors the sh wrapper: read the private config (runtime, codex
16
+ * script, hook socket, hook token), export the socket/token to the child, run
17
+ * `<runtime> <codex script> [exec --dangerously-bypass-hook-trust] <args...>`
18
+ * with inherited stdio, and return the child's exit code. Unlike `exec` on
19
+ * POSIX there is a second process here, so the child is placed in a job object
20
+ * with kill-on-close: when the SDK terminates the wrapper on abort the whole
21
+ * codex tree goes with it instead of being orphaned.
22
+ */
23
+ export declare const CODEX_WINDOWS_WRAPPER_SOURCE: string;
24
+ /**
25
+ * Path to the compiled wrapper, building it on first use. The cache key is the
26
+ * source hash, so a changed wrapper is rebuilt and old copies are just ignored.
27
+ * Compilation writes to a temp name and renames, so concurrent turns never see
28
+ * a half-written exe.
29
+ */
30
+ export declare function ensureCodexWindowsWrapperExe(cacheDir: string): Promise<string>;
@@ -1 +1 @@
1
- export declare const NEGOTIUM_VERSION = "0.18.2";
1
+ export declare const NEGOTIUM_VERSION = "0.19.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "negotium",
3
- "version": "0.18.2",
3
+ "version": "0.19.0",
4
4
  "type": "module",
5
5
  "description": "Install the Negotium multi-agent runtime and CLI with one package",
6
6
  "license": "Apache-2.0",
@@ -1,12 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../packages/core/src/agents/codex-app-server.ts", "../../../packages/core/src/agents/codex-native-multi-agent.ts", "../../../packages/core/src/version.ts"],
4
- "sourcesContent": [
5
- "import { type ChildProcessWithoutNullStreams, spawn } from \"node:child_process\";\nimport { codexCliScriptPath } from \"#agents/codex-native-multi-agent\";\nimport { hostedCodexHomePath } from \"#agents/execution-host\";\nimport { latestCodexRolloutPath } from \"#agents/rollout/codex\";\nimport { NEGOTIUM_VERSION } from \"#version\";\n\ninterface CodexAppServerForkResult {\n forkId: string;\n rolloutPath: string;\n}\n\ninterface CodexAppServerForkHost {\n spawnServer(): ChildProcessWithoutNullStreams;\n findRolloutPath(threadId: string): string | undefined;\n timeoutMs: number;\n}\n\ntype JsonRpcResponse = {\n id?: number;\n result?: { thread?: { id?: unknown } };\n error?: { message?: unknown };\n};\n\nexport function createCodexAppServerForker(host: CodexAppServerForkHost) {\n return async (parentThreadId: string): Promise<CodexAppServerForkResult> => {\n const child = host.spawnServer();\n\n return await new Promise<CodexAppServerForkResult>((resolve, reject) => {\n let settled = false;\n let stdoutBuffer = \"\";\n let stderr = \"\";\n const timer = setTimeout(\n () => finish(new Error(\"Codex thread fork timed out\")),\n host.timeoutMs,\n );\n\n const finish = (error?: Error, result?: CodexAppServerForkResult) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n try {\n child.stdin.end();\n child.kill();\n } catch {\n // The app server may already have exited after stdin closed.\n }\n if (error) reject(error);\n else if (result) resolve(result);\n else reject(new Error(\"Codex thread fork returned no result\"));\n };\n\n const send = (message: Record<string, unknown>) => {\n child.stdin.write(`${JSON.stringify(message)}\\n`);\n };\n\n child.stderr.on(\"data\", (chunk) => {\n if (stderr.length < 8_192) stderr += String(chunk);\n });\n child.on(\"error\", (error) => finish(error));\n child.on(\"exit\", (code, signal) => {\n if (settled) return;\n const detail = signal ? `signal ${signal}` : `code ${code ?? 1}`;\n finish(\n new Error(\n `Codex app server exited with ${detail}${stderr.trim() ? `: ${stderr.trim()}` : \"\"}`,\n ),\n );\n });\n child.stdout.on(\"data\", (chunk) => {\n stdoutBuffer += String(chunk);\n for (;;) {\n const newline = stdoutBuffer.indexOf(\"\\n\");\n if (newline < 0) break;\n const line = stdoutBuffer.slice(0, newline);\n stdoutBuffer = stdoutBuffer.slice(newline + 1);\n let message: JsonRpcResponse;\n try {\n message = JSON.parse(line) as JsonRpcResponse;\n } catch {\n continue;\n }\n\n if (message.id === 1) {\n if (message.error) {\n finish(new Error(String(message.error.message || \"Codex initialization failed\")));\n return;\n }\n send({ method: \"initialized\" });\n send({ id: 2, method: \"thread/fork\", params: { threadId: parentThreadId } });\n continue;\n }\n if (message.id !== 2) continue;\n if (message.error) {\n finish(new Error(String(message.error.message || \"Codex thread fork failed\")));\n return;\n }\n const forkId = message.result?.thread?.id;\n if (typeof forkId !== \"string\" || !forkId) {\n finish(new Error(\"Codex thread fork returned no thread id\"));\n return;\n }\n const rolloutPath = host.findRolloutPath(forkId);\n if (!rolloutPath) {\n finish(new Error(`Codex thread fork rollout was not found for ${forkId}`));\n return;\n }\n finish(undefined, { forkId, rolloutPath });\n return;\n }\n });\n\n send({\n id: 1,\n method: \"initialize\",\n params: {\n clientInfo: { name: \"negotium\", version: NEGOTIUM_VERSION },\n },\n });\n });\n };\n}\n\nconst forkCodexThread = createCodexAppServerForker({\n spawnServer() {\n return spawn(process.execPath, [codexCliScriptPath(), \"app-server\", \"--stdio\"], {\n env: { ...process.env, CODEX_HOME: hostedCodexHomePath() },\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n // Background stdio server — no console window on Windows. Ignored on POSIX.\n windowsHide: true,\n });\n },\n findRolloutPath: latestCodexRolloutPath,\n timeoutMs: 15_000,\n});\n\nexport async function forkCodexSession(parentThreadId: string): Promise<CodexAppServerForkResult> {\n return await forkCodexThread(parentThreadId);\n}\n",
6
- "import { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport {\n chmodSync,\n copyFileSync,\n existsSync,\n mkdtempSync,\n readFileSync,\n renameSync,\n rmSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { NEGOTIUM_VERSION } from \"#version\";\n\ntype CodexModel = Record<string, unknown>;\ntype CodexModelCache = {\n client_version?: unknown;\n models?: unknown;\n};\n\nconst moduleRequire = createRequire(import.meta.url);\nconst codexSdkPackagePath = moduleRequire.resolve(\"@openai/codex-sdk/package.json\");\nconst codexSdkRequire = createRequire(codexSdkPackagePath);\nconst bundledCodexPackagePath = codexSdkRequire.resolve(\"@openai/codex/package.json\");\n\nfunction readPackageVersion(packageJsonPath: string): string {\n const parsed = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { version?: unknown };\n if (typeof parsed.version !== \"string\" || !parsed.version.trim()) {\n throw new Error(`Codex package has no valid version: ${packageJsonPath}`);\n }\n return parsed.version;\n}\n\nexport const BUNDLED_CODEX_VERSION = readPackageVersion(bundledCodexPackagePath);\nconst SAFE_BUNDLED_CODEX_VERSION = BUNDLED_CODEX_VERSION.replace(/[^a-zA-Z0-9._-]/g, \"_\");\nconst NEGOTIUM_MODEL_CACHE = `negotium-models-cache-${SAFE_BUNDLED_CODEX_VERSION}.json`;\nconst NEGOTIUM_MODEL_CATALOG = `negotium-model-catalog-${SAFE_BUNDLED_CODEX_VERSION}.json`;\n\nexport function codexCliScriptPath(): string {\n return join(dirname(bundledCodexPackagePath), \"bin\", \"codex.js\");\n}\n\nfunction parseCodexModelCache(contents: string, sourcePath: string): CodexModelCache {\n let parsed: CodexModelCache;\n try {\n parsed = JSON.parse(contents) as CodexModelCache;\n } catch (error) {\n throw new Error(`Codex model cache is invalid JSON: ${sourcePath}`, { cause: error });\n }\n if (!Array.isArray(parsed.models) || parsed.models.length === 0) {\n throw new Error(`Codex model cache has no models: ${sourcePath}`);\n }\n return parsed;\n}\n\nfunction readCodexModelCache(cachePath: string): {\n contents: string;\n parsed: CodexModelCache;\n} {\n const contents = readFileSync(cachePath, \"utf8\");\n return { contents, parsed: parseCodexModelCache(contents, cachePath) };\n}\n\nfunction readCompatibleCodexModelCache(cachePath: string): {\n contents: string;\n parsed: CodexModelCache;\n} {\n const cache = readCodexModelCache(cachePath);\n if (cache.parsed.client_version !== BUNDLED_CODEX_VERSION) {\n const found =\n typeof cache.parsed.client_version === \"string\"\n ? cache.parsed.client_version\n : \"missing or invalid\";\n throw new Error(\n `Codex model cache version ${found} does not match Negotium's bundled Codex ${BUNDLED_CODEX_VERSION}: ${cachePath}`,\n );\n }\n return cache;\n}\n\nfunction writePrivateFileAtomic(path: string, contents: string): void {\n if (existsSync(path) && readFileSync(path, \"utf8\") === contents) return;\n\n const tempPath = `${path}.${process.pid}.${randomUUID()}.tmp`;\n try {\n writeFileSync(tempPath, contents, { encoding: \"utf8\", mode: 0o600 });\n renameSync(tempPath, path);\n chmodSync(path, 0o600);\n } finally {\n try {\n unlinkSync(tempPath);\n } catch {\n // renameSync normally consumed the temporary file.\n }\n }\n}\n\nexport function bundledCodexModelCachePath(authFilePath: string): string {\n return join(dirname(authFilePath), NEGOTIUM_MODEL_CACHE);\n}\n\nasync function bootstrapCodexModelCache(codexHome: string, cachePath: string): Promise<void> {\n const child = spawn(process.execPath, [codexCliScriptPath(), \"app-server\", \"--stdio\"], {\n env: { ...process.env, CODEX_HOME: codexHome },\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n // Background stdio server — no console window on Windows. Ignored on POSIX.\n windowsHide: true,\n });\n\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n let stdoutBuffer = \"\";\n let stderr = \"\";\n const timer = setTimeout(\n () => finish(new Error(\"timed out while refreshing the Codex model catalog\")),\n 15_000,\n );\n\n const finish = (error?: Error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n try {\n child.stdin.end();\n child.kill();\n } catch {\n // The app server may already have exited after stdin closed.\n }\n if (error) reject(error);\n else if (!existsSync(cachePath)) reject(new Error(\"Codex did not create its model cache\"));\n else resolve();\n };\n\n const send = (message: Record<string, unknown>) => {\n child.stdin.write(`${JSON.stringify(message)}\\n`);\n };\n\n child.stderr.on(\"data\", (chunk) => {\n if (stderr.length < 4_096) stderr += String(chunk);\n });\n child.on(\"error\", (error) => finish(error));\n child.on(\"exit\", (code, signal) => {\n if (!settled) {\n finish(\n new Error(\n `Codex model catalog refresh exited with ${signal ? `signal ${signal}` : `code ${code ?? 1}`}${stderr.trim() ? `: ${stderr.trim()}` : \"\"}`,\n ),\n );\n }\n });\n child.stdout.on(\"data\", (chunk) => {\n stdoutBuffer += String(chunk);\n for (;;) {\n const newline = stdoutBuffer.indexOf(\"\\n\");\n if (newline < 0) break;\n const line = stdoutBuffer.slice(0, newline);\n stdoutBuffer = stdoutBuffer.slice(newline + 1);\n let message: { id?: number; error?: { message?: string } };\n try {\n message = JSON.parse(line) as typeof message;\n } catch {\n continue;\n }\n if (message.id === 1) {\n if (message.error) {\n finish(new Error(message.error.message || \"Codex initialization failed\"));\n return;\n }\n send({ method: \"initialized\" });\n send({ id: 2, method: \"model/list\", params: { includeHidden: true } });\n } else if (message.id === 2) {\n if (message.error) {\n finish(new Error(message.error.message || \"Codex model listing failed\"));\n } else {\n finish();\n }\n return;\n }\n }\n });\n\n send({\n id: 1,\n method: \"initialize\",\n params: {\n clientInfo: { name: \"negotium\", version: NEGOTIUM_VERSION },\n capabilities: { experimentalApi: true },\n },\n });\n });\n}\n\nasync function bootstrapIsolatedCodexModelCache(\n authFilePath: string,\n bootstrap: (codexHome: string, cachePath: string) => Promise<void>,\n): Promise<string> {\n const sourceHome = dirname(authFilePath);\n const isolatedHome = mkdtempSync(join(tmpdir(), \"negotium-codex-models-\"));\n const isolatedCachePath = join(isolatedHome, \"models_cache.json\");\n\n try {\n const isolatedAuthPath = join(isolatedHome, \"auth.json\");\n copyFileSync(authFilePath, isolatedAuthPath);\n chmodSync(isolatedAuthPath, 0o600);\n\n // Preserve custom provider configuration while keeping the bundled CLI's\n // cache write completely outside the user's shared CODEX_HOME.\n const sourceConfigPath = join(sourceHome, \"config.toml\");\n if (existsSync(sourceConfigPath)) {\n const isolatedConfigPath = join(isolatedHome, \"config.toml\");\n copyFileSync(sourceConfigPath, isolatedConfigPath);\n chmodSync(isolatedConfigPath, 0o600);\n }\n\n await bootstrap(isolatedHome, isolatedCachePath);\n return readCompatibleCodexModelCache(isolatedCachePath).contents;\n } finally {\n rmSync(isolatedHome, { recursive: true, force: true });\n }\n}\n\nexport async function ensureCodexModelCache(\n authFilePath: string,\n bootstrap: (codexHome: string, cachePath: string) => Promise<void> = bootstrapCodexModelCache,\n): Promise<string> {\n const codexHome = dirname(authFilePath);\n const configuredCachePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE;\n if (configuredCachePath) {\n if (!existsSync(configuredCachePath)) {\n throw new Error(`Configured Codex model cache does not exist: ${configuredCachePath}`);\n }\n readCompatibleCodexModelCache(configuredCachePath);\n return configuredCachePath;\n }\n\n // The global Codex CLI owns models_cache.json and may update it to a schema\n // newer than the SDK bundled by Negotium. Snapshot a cache generated for our\n // exact bundled version so later global CLI updates cannot break turns.\n const bundledCachePath = bundledCodexModelCachePath(authFilePath);\n const sharedCachePath = join(codexHome, \"models_cache.json\");\n if (existsSync(sharedCachePath)) {\n try {\n const shared = readCompatibleCodexModelCache(sharedCachePath);\n // Keep model metadata fresh while the global CLI remains compatible.\n writePrivateFileAtomic(bundledCachePath, shared.contents);\n return bundledCachePath;\n } catch {\n // A compatible private snapshot is safer than failing because another\n // process briefly exposed an incomplete shared-cache write.\n }\n }\n\n if (existsSync(bundledCachePath)) {\n try {\n readCompatibleCodexModelCache(bundledCachePath);\n return bundledCachePath;\n } catch {\n // Re-bootstrap below instead of passing a corrupt or version-mismatched\n // private snapshot into this SDK version.\n }\n }\n\n const refreshedContents = await bootstrapIsolatedCodexModelCache(authFilePath, bootstrap);\n writePrivateFileAtomic(bundledCachePath, refreshedContents);\n return bundledCachePath;\n}\n\n/**\n * Codex can resolve model metadata before `features.multi_agent=false`, so a\n * model-advertised v1/v2 value may still register native collaboration tools.\n * Feed Codex an authoritative copy of its own catalog with only that field\n * disabled. Runtime MCP delegation remains available independently.\n */\nexport function writeCodexCatalogWithNativeMultiAgentDisabled(\n authFilePath: string,\n sourcePath: string,\n): string {\n const codexHome = dirname(authFilePath);\n const outputPath = join(codexHome, NEGOTIUM_MODEL_CATALOG);\n\n const parsed = readCodexModelCache(sourcePath).parsed;\n\n const models = (parsed.models as unknown[]).map((model, index): CodexModel => {\n if (!model || typeof model !== \"object\" || Array.isArray(model)) {\n throw new Error(`Codex model cache entry ${index} is invalid: ${sourcePath}`);\n }\n return { ...(model as CodexModel), multi_agent_version: \"disabled\" };\n });\n const contents = `${JSON.stringify({ models }, null, 2)}\\n`;\n writePrivateFileAtomic(outputPath, contents);\n return outputPath;\n}\n",
7
- "export const NEGOTIUM_VERSION = \"0.18.2\";\n"
8
- ],
9
- "mappings": "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",
10
- "debugId": "BEFB3E8C582938DC64756E2164756E21",
11
- "names": []
12
- }