negotium 0.2.3 → 0.2.4

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 (68) hide show
  1. package/dist/agent-helpers.js +3069 -2865
  2. package/dist/agent-helpers.js.map +38 -37
  3. package/dist/background-bash.js.map +1 -1
  4. package/dist/browser-runtime.js +137 -21
  5. package/dist/browser-runtime.js.map +10 -9
  6. package/dist/{chunk-hsqgb2hw.js → chunk-1s9ryz8g.js} +1195 -19
  7. package/dist/chunk-1s9ryz8g.js.map +35 -0
  8. package/dist/hosted-agent.js +45 -31
  9. package/dist/hosted-agent.js.map +9 -9
  10. package/dist/main.js +2691 -2383
  11. package/dist/main.js.map +32 -30
  12. package/dist/mcp-factories.js +6210 -6002
  13. package/dist/mcp-factories.js.map +39 -38
  14. package/dist/prompts.js.map +1 -1
  15. package/dist/query-runtime.js +22 -2
  16. package/dist/query-runtime.js.map +4 -4
  17. package/dist/registry.js +169 -213
  18. package/dist/registry.js.map +8 -9
  19. package/dist/rollout.js +1 -1
  20. package/dist/runtime/src/agents/codex-app-server.ts +136 -0
  21. package/dist/runtime/src/agents/codex-native-multi-agent.ts +1 -1
  22. package/dist/runtime/src/agents/codex-provider.ts +21 -11
  23. package/dist/runtime/src/agents/codex-registry.ts +9 -17
  24. package/dist/runtime/src/agents/contracts.ts +2 -0
  25. package/dist/runtime/src/agents/execution-host.ts +6 -0
  26. package/dist/runtime/src/agents/fork.ts +24 -18
  27. package/dist/runtime/src/agents/idle-archiver.ts +7 -2
  28. package/dist/runtime/src/agents/index.ts +2 -1
  29. package/dist/runtime/src/agents/maestro-registry.ts +26 -7
  30. package/dist/runtime/src/agents/memory-archive-policy.ts +20 -0
  31. package/dist/runtime/src/agents/rollout/codex.ts +6 -5
  32. package/dist/runtime/src/mcp/factories/session-comm.ts +12 -0
  33. package/dist/runtime/src/mcp/session-comm/default-host.ts +16 -0
  34. package/dist/runtime/src/mcp/session-comm/server.ts +25 -0
  35. package/dist/runtime/src/platform/config.ts +4 -1
  36. package/dist/runtime/src/platform/playwright/manager.ts +57 -1
  37. package/dist/runtime/src/platform/playwright/profile-management.ts +33 -0
  38. package/dist/runtime/src/platform/playwright/public-runtime.ts +5 -0
  39. package/dist/runtime/src/platform/playwright/vault-broker.ts +9 -10
  40. package/dist/runtime/src/storage/browser-profiles.ts +35 -10
  41. package/dist/runtime/src/storage/storage-host.ts +31 -5
  42. package/dist/runtime/src/storage/vault.ts +30 -2
  43. package/dist/runtime/src/topics/derive.ts +38 -2
  44. package/dist/runtime/src/topics/lifecycle.ts +12 -2
  45. package/dist/runtime/src/version.ts +1 -1
  46. package/dist/runtime-helpers.js +22 -2
  47. package/dist/runtime-helpers.js.map +4 -4
  48. package/dist/storage.js +22 -2
  49. package/dist/storage.js.map +4 -4
  50. package/dist/types/packages/core/src/agents/codex-app-server.d.ts +13 -0
  51. package/dist/types/packages/core/src/agents/codex-native-multi-agent.d.ts +1 -0
  52. package/dist/types/packages/core/src/agents/contracts.d.ts +2 -0
  53. package/dist/types/packages/core/src/agents/execution-host.d.ts +2 -0
  54. package/dist/types/packages/core/src/agents/fork.d.ts +8 -7
  55. package/dist/types/packages/core/src/agents/memory-archive-policy.d.ts +10 -0
  56. package/dist/types/packages/core/src/agents/rollout/codex.d.ts +4 -2
  57. package/dist/types/packages/core/src/mcp/factories/session-comm.d.ts +1 -0
  58. package/dist/types/packages/core/src/platform/playwright/manager.d.ts +15 -0
  59. package/dist/types/packages/core/src/platform/playwright/profile-management.d.ts +8 -0
  60. package/dist/types/packages/core/src/platform/playwright/public-runtime.d.ts +2 -1
  61. package/dist/types/packages/core/src/storage/browser-profiles.d.ts +3 -1
  62. package/dist/types/packages/core/src/storage/storage-host.d.ts +1 -0
  63. package/dist/types/packages/core/src/storage/vault.d.ts +8 -0
  64. package/dist/types/packages/core/src/version.d.ts +1 -1
  65. package/dist/vault.js +19 -3
  66. package/dist/vault.js.map +4 -4
  67. package/package.json +2 -2
  68. package/dist/chunk-hsqgb2hw.js.map +0 -19
@@ -33,6 +33,10 @@ function parseRuntimePort(value, fallback) {
33
33
  const port = Number.parseInt(value, 10);
34
34
  return Number.isInteger(port) && port > 0 && port <= 65535 ? port : fallback;
35
35
  }
36
+ function safeRuntimePathSegment(value, fallback, maxLength = 160) {
37
+ const cleaned = value.trim().replace(/[^A-Za-z0-9._-]/g, "_").replace(/^_+|_+$/g, "").slice(0, maxLength);
38
+ return cleaned || fallback;
39
+ }
36
40
 
37
41
  // ../../packages/core/src/platform/logger.ts
38
42
  import pino from "pino";
@@ -181,6 +185,12 @@ var BACKGROUND_BASH_SERVER = resolve(PROJECT_ROOT, "src/mcp/background-bash-serv
181
185
  var VAULT_SERVER = resolve(PROJECT_ROOT, "src/mcp/vault-server.ts");
182
186
  var BG_BASH_BASE_PORT = parsePortEnv(process.env.BG_BASH_BASE_PORT, 9700);
183
187
  var BG_BASH_MAX_PORT = parsePortEnv(process.env.BG_BASH_MAX_PORT, 9799);
188
+ function safeWorkspaceSegment(value, fallback) {
189
+ return safeRuntimePathSegment(value, fallback);
190
+ }
191
+ function resolveTopicWorkspaceDir(topicId) {
192
+ return join(TOPIC_WORKSPACE_DIR, safeWorkspaceSegment(topicId, "topic"));
193
+ }
184
194
  function loadOrCreateLocalSecret(envKey, filename, options = {}) {
185
195
  const envValue = envText(envKey);
186
196
  const secretFile = resolve(SECRETS_DIR, filename);
@@ -283,6 +293,9 @@ var TESSERACT_BIN = envText("TESSERACT_BIN") ?? "tesseract";
283
293
  var PDFTOTEXT_BIN = envText("PDFTOTEXT_BIN") ?? "pdftotext";
284
294
  var _envMaxTellDepth = Number.parseInt(process.env.MAX_TELL_DEPTH ?? "", 10);
285
295
  var MAX_TELL_DEPTH = Number.isInteger(_envMaxTellDepth) && _envMaxTellDepth > 0 ? _envMaxTellDepth : 20;
296
+ function codexAuthFilePath() {
297
+ return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
298
+ }
286
299
 
287
300
  // ../../packages/core/src/runtime/user-turn-envelope.ts
288
301
  function renderUserPromptBatch(prompts) {
@@ -735,18 +748,1181 @@ function isPoisonedAssistantEntry(entry) {
735
748
  }
736
749
 
737
750
  // ../../packages/core/src/agents/rollout/codex.ts
751
+ import { randomBytes as randomBytes4 } from "crypto";
752
+ import { existsSync as existsSync3, readFileSync as readFileSync4, realpathSync as realpathSync2, statSync as statSync2, unlinkSync as unlinkSync3 } from "fs";
753
+ import { basename, dirname as dirname4, join as join4, resolve as resolve4 } from "path";
754
+
755
+ // ../../packages/core/src/agents/execution-host.ts
756
+ import { AsyncLocalStorage } from "async_hooks";
757
+ import { dirname as dirname3 } from "path";
758
+
759
+ // ../../packages/core/src/security/sensitive-path.ts
760
+ import { realpathSync } from "fs";
761
+ import { resolve as resolve3 } from "path";
762
+ var SENSITIVE_PATH_PATTERNS = [
763
+ /\/\.env(\.|$)/i,
764
+ /\/\.ssh\//i,
765
+ /\/\.aws\//i,
766
+ /\/\.gnupg\//i,
767
+ /\/\.netrc$/i,
768
+ /\/\.npmrc$/i,
769
+ /\/(id_rsa|id_ed25519|id_ecdsa|id_dsa)(\.pub)?$/i,
770
+ /\.(pem|key|p12|pfx|cer|crt)$/i,
771
+ /\/Library\/Keychains\//i,
772
+ /\/vault\.db(-wal|-shm|-journal)?$/i,
773
+ /\/vault-master-key$/i,
774
+ /\/runtime-mcp-secret$/i,
775
+ /\/sessions\.db(-wal|-shm|-journal)?$/i
776
+ ];
777
+ function isSensitivePath(filePath) {
778
+ const normalized = resolve3(filePath);
779
+ if (SENSITIVE_PATH_PATTERNS.some((p) => p.test(normalized)))
780
+ return true;
781
+ try {
782
+ const real = realpathSync(normalized);
783
+ if (real !== normalized)
784
+ return SENSITIVE_PATH_PATTERNS.some((p) => p.test(real));
785
+ } catch {}
786
+ return false;
787
+ }
788
+
789
+ // ../../packages/core/src/agents/vault-tool-policy.ts
790
+ var SENSITIVE_RUNTIME_NAMES = [
791
+ "vault.db",
792
+ "vault-master-key",
793
+ "runtime-mcp-secret",
794
+ "sessions.db"
795
+ ];
796
+ var DIRECT_VAULT_EXECUTION_TOOLS = new Set(["Bash", "WebFetch"]);
797
+ function createVaultToolPolicy(host) {
798
+ function isVaultBrokerTool(toolName) {
799
+ return toolName.includes("vault_run") || toolName.includes("vault_http_request");
800
+ }
801
+ function referencesRuntimeSecretStorage(value) {
802
+ if (typeof value === "string") {
803
+ const lower = value.toLowerCase();
804
+ if (SENSITIVE_RUNTIME_NAMES.some((name) => lower.includes(name)))
805
+ return true;
806
+ return value.startsWith("/") && host.isSensitivePath(value);
807
+ }
808
+ if (Array.isArray(value))
809
+ return value.some(referencesRuntimeSecretStorage);
810
+ if (value && typeof value === "object") {
811
+ return Object.values(value).some(referencesRuntimeSecretStorage);
812
+ }
813
+ return false;
814
+ }
815
+ function shouldRedirectVaultTool(userId, toolName, input) {
816
+ return false;
817
+ }
818
+ return { isVaultBrokerTool, referencesRuntimeSecretStorage, shouldRedirectVaultTool };
819
+ }
820
+ var defaultVaultToolPolicy = createVaultToolPolicy({
821
+ isSensitivePath,
822
+ valueReferencesVaultKey: () => false
823
+ });
824
+ var isVaultBrokerTool = defaultVaultToolPolicy.isVaultBrokerTool;
825
+ var referencesRuntimeSecretStorage = defaultVaultToolPolicy.referencesRuntimeSecretStorage;
826
+ var shouldRedirectVaultTool = defaultVaultToolPolicy.shouldRedirectVaultTool;
827
+
828
+ // ../../packages/core/src/mcp/canonical-bridge-config.ts
829
+ var registrations = [];
830
+ var turnLeases = new Map;
831
+ function turnKey(scope) {
832
+ return JSON.stringify([
833
+ scope.userId,
834
+ scope.topicId,
835
+ scope.queryId,
836
+ scope.peerBridge.hubCellId,
837
+ scope.peerBridge.hostTopicId,
838
+ scope.peerBridge.hostQueryId
839
+ ]);
840
+ }
841
+ function canonicalMcpBridgeEnv(scope) {
842
+ const lease = registrations.at(-1)?.provider(scope);
843
+ if (!lease)
844
+ return;
845
+ const key = turnKey(scope);
846
+ const leases = turnLeases.get(key) ?? new Set;
847
+ leases.add(lease.revoke);
848
+ turnLeases.set(key, leases);
849
+ return lease.env;
850
+ }
851
+
852
+ // ../../packages/core/src/mcp/runtime-spec.ts
853
+ import { createHmac, timingSafeEqual } from "crypto";
854
+ var RUNTIME_MCP_KEY = "runtime";
855
+ var RUNTIME_MCP_BASE_PATH = "/mcp/runtime";
856
+ var TOKEN_TTL_MS = 4 * 60 * 60 * 1000;
857
+ var CLAUDE_MCP_TOOL_TIMEOUT_MS = 600000;
858
+ var runtimePort = NEGOTIUM_PORT;
859
+ function encodeTokenPart(value) {
860
+ return Buffer.from(JSON.stringify(value), "utf-8").toString("base64url");
861
+ }
862
+ function signTokenPayload(payloadPart) {
863
+ return createHmac("sha256", RUNTIME_MCP_SECRET).update(payloadPart).digest("base64url");
864
+ }
865
+ function issueRuntimeMcpToken(ctx) {
866
+ const payloadPart = encodeTokenPart({
867
+ v: 1,
868
+ exp: Date.now() + TOKEN_TTL_MS,
869
+ ctx
870
+ });
871
+ return `${payloadPart}.${signTokenPayload(payloadPart)}`;
872
+ }
873
+ function issueHostedMcpToken(surface, ctx) {
874
+ const payloadPart = encodeTokenPart({
875
+ v: 2,
876
+ exp: Date.now() + TOKEN_TTL_MS,
877
+ aud: surface,
878
+ ctx
879
+ });
880
+ return `${payloadPart}.${signTokenPayload(payloadPart)}`;
881
+ }
882
+ function buildRuntimeMcpSpec(agent, ctx) {
883
+ const token = issueRuntimeMcpToken(ctx);
884
+ const base = `http://127.0.0.1:${runtimePort}${RUNTIME_MCP_BASE_PATH}`;
885
+ const query = `token=${encodeURIComponent(token)}`;
886
+ if (agent === "codex")
887
+ return { url: `${base}/mcp?${query}` };
888
+ return {
889
+ type: "sse",
890
+ url: `${base}/sse?${query}`,
891
+ timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS
892
+ };
893
+ }
894
+ function buildHostedMcpSpec(agent, surface, ctx) {
895
+ const token = issueHostedMcpToken(surface, ctx);
896
+ const base = `http://127.0.0.1:${runtimePort}${RUNTIME_MCP_BASE_PATH}/${surface}`;
897
+ const query = `token=${encodeURIComponent(token)}`;
898
+ if (agent === "codex")
899
+ return { url: `${base}/mcp?${query}` };
900
+ return {
901
+ type: "sse",
902
+ url: `${base}/sse?${query}`,
903
+ timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS
904
+ };
905
+ }
906
+
907
+ // ../../packages/core/src/mcp/session-comm/bridge-ipc-config.ts
908
+ var registrations2 = [];
909
+ function peerSessionBridgeIpcEnv() {
910
+ const active = registrations2.at(-1)?.config;
911
+ if (!active)
912
+ return;
913
+ return {
914
+ NEGOTIUM_PEER_SESSION_BRIDGE_URL: active.url,
915
+ NEGOTIUM_PEER_SESSION_BRIDGE_TOKEN: active.token
916
+ };
917
+ }
918
+
919
+ // ../../packages/core/src/platform/background-bash/manager.ts
920
+ import { execFileSync as execFileSync2, spawn } from "child_process";
738
921
  import { randomBytes as randomBytes2 } from "crypto";
739
- import { existsSync as existsSync3, readFileSync as readFileSync4, realpathSync, statSync as statSync2, unlinkSync as unlinkSync3 } from "fs";
740
- import { homedir as homedir3 } from "os";
741
- import { basename, dirname as dirname3, join as join3, resolve as resolve3 } from "path";
922
+
923
+ // ../../packages/core/src/platform/delay.ts
924
+ var delay = (ms) => new Promise((r) => setTimeout(r, ms));
925
+
926
+ // ../../packages/core/src/platform/background-bash/context.ts
927
+ import { createHmac as createHmac2 } from "crypto";
928
+ function deriveBgBashContextCapability(runtimeCapability, userId, topic) {
929
+ return createHmac2("sha256", runtimeCapability).update(`${userId}\x00${topic}`).digest("hex");
930
+ }
931
+
932
+ // ../../packages/core/src/platform/background-bash/manager.ts
933
+ function makeBgBashKey(_userId, _topic) {
934
+ return "runtime";
935
+ }
936
+ function defaultPortPids(port) {
937
+ try {
938
+ return execFileSync2("lsof", ["-i", `:${port}`, "-t"], { stdio: "pipe" }).toString().trim().split(`
939
+ `).map((pid) => Number.parseInt(pid, 10)).filter((pid) => !Number.isNaN(pid));
940
+ } catch {
941
+ return [];
942
+ }
943
+ }
944
+ function createBackgroundBashManager(options = {}) {
945
+ const instances = new Map;
946
+ const usedPorts = new Set;
947
+ const spawning = new Map;
948
+ const knownContexts = new Map;
949
+ const runtimeCapability = options.capability ?? randomBytes2(32).toString("hex");
950
+ const runtimeServerId = options.serverId ?? randomBytes2(16).toString("hex");
951
+ const serverFile = options.serverFile ?? BACKGROUND_BASH_SERVER;
952
+ const basePort = options.basePort ?? BG_BASH_BASE_PORT;
953
+ const maxPort = options.maxPort ?? BG_BASH_MAX_PORT;
954
+ const fetchImpl = options.fetch ?? globalThis.fetch;
955
+ const now = options.now ?? Date.now;
956
+ const wait = options.delay ?? delay;
957
+ const portPids = options.portPids ?? defaultPortPids;
958
+ const spawnImpl = options.spawn ?? ((command, args, spawnOptions) => spawn(command, [...args], spawnOptions));
959
+ function contextKey(userId, topic) {
960
+ return `${userId}\x00${topic}`;
961
+ }
962
+ function contextCapability(userId, topic) {
963
+ return deriveBgBashContextCapability(runtimeCapability, userId, topic);
964
+ }
965
+ async function allocatePort(excludedPorts = new Set) {
966
+ for (let port = basePort;port <= maxPort; port++) {
967
+ if (usedPorts.has(port) || excludedPorts.has(port))
968
+ continue;
969
+ usedPorts.add(port);
970
+ if (portPids(port).length > 0) {
971
+ usedPorts.delete(port);
972
+ continue;
973
+ }
974
+ return port;
975
+ }
976
+ throw new Error(`No available ports for background-bash (range ${basePort}-${maxPort}, ${instances.size} active)`);
977
+ }
978
+ async function isHealthy(port) {
979
+ try {
980
+ const response = await fetchImpl(`http://127.0.0.1:${port}/health`, {
981
+ signal: AbortSignal.timeout(2000)
982
+ });
983
+ return response.ok && await response.text() === runtimeServerId;
984
+ } catch {
985
+ return false;
986
+ }
987
+ }
988
+ function killRuntime() {
989
+ const key = "runtime";
990
+ const instance = instances.get(key);
991
+ if (!instance)
992
+ return;
993
+ try {
994
+ instance.process.kill("SIGTERM");
995
+ } catch {}
996
+ usedPorts.delete(instance.port);
997
+ instances.delete(key);
998
+ logger.info({ key, port: instance.port }, "background-bash server killed");
999
+ }
1000
+ async function spawnServer(key, reservedPort, excludedPorts = new Set) {
1001
+ const port = reservedPort ?? await allocatePort(excludedPorts);
1002
+ const process2 = spawnImpl("bun", ["run", serverFile, `--port=${port}`], {
1003
+ stdio: "ignore",
1004
+ detached: false,
1005
+ env: {
1006
+ ...options.env ?? globalThis.process.env,
1007
+ NEGOTIUM_BG_BASH_CAPABILITY: runtimeCapability,
1008
+ NEGOTIUM_BG_BASH_SERVER_ID: runtimeServerId
1009
+ }
1010
+ });
1011
+ process2.once("error", (error) => {
1012
+ logger.error({ err: error, key }, "background-bash server error");
1013
+ if (instances.get(key)?.process === process2) {
1014
+ usedPorts.delete(port);
1015
+ instances.delete(key);
1016
+ }
1017
+ });
1018
+ process2.once("exit", (code) => {
1019
+ logger.info({ key, code }, "background-bash server exited");
1020
+ if (instances.get(key)?.process === process2) {
1021
+ usedPorts.delete(port);
1022
+ instances.delete(key);
1023
+ }
1024
+ });
1025
+ const timestamp = now();
1026
+ instances.set(key, { process: process2, port, startedAt: timestamp, lastUsedAt: timestamp });
1027
+ const started = now();
1028
+ while (now() - started < 8000) {
1029
+ if (process2.exitCode !== null) {
1030
+ const nextExcluded = new Set(excludedPorts);
1031
+ nextExcluded.add(port);
1032
+ return spawnServer(key, undefined, nextExcluded);
1033
+ }
1034
+ if (await isHealthy(port))
1035
+ return port;
1036
+ await wait(200);
1037
+ }
1038
+ killRuntime();
1039
+ throw new Error(`background-bash server failed health check after spawn on port ${port}`);
1040
+ }
1041
+ async function ensure(userId, topic) {
1042
+ const key = makeBgBashKey(userId, topic);
1043
+ knownContexts.set(contextKey(userId, topic), { userId, topic });
1044
+ const inProgress = spawning.get(key);
1045
+ if (inProgress)
1046
+ return inProgress;
1047
+ const promise = (async () => {
1048
+ const existing = instances.get(key);
1049
+ if (existing && !existing.process.killed && existing.process.exitCode === null) {
1050
+ if (await isHealthy(existing.port)) {
1051
+ existing.lastUsedAt = now();
1052
+ return existing.port;
1053
+ }
1054
+ killRuntime();
1055
+ } else if (existing) {
1056
+ usedPorts.delete(existing.port);
1057
+ instances.delete(key);
1058
+ }
1059
+ return spawnServer(key);
1060
+ })().finally(() => spawning.delete(key));
1061
+ spawning.set(key, promise);
1062
+ return promise;
1063
+ }
1064
+ function clear(userId, topic) {
1065
+ knownContexts.delete(contextKey(userId, topic));
1066
+ const instance = instances.get("runtime");
1067
+ if (!instance)
1068
+ return;
1069
+ const query = new URLSearchParams({
1070
+ user: userId,
1071
+ topic,
1072
+ capability: contextCapability(userId, topic)
1073
+ });
1074
+ fetchImpl(`http://127.0.0.1:${instance.port}/contexts?${query}`, {
1075
+ method: "DELETE"
1076
+ }).catch(() => {});
1077
+ }
1078
+ function clearUser(userId) {
1079
+ for (const context of [...knownContexts.values()]) {
1080
+ if (context.userId === userId)
1081
+ clear(context.userId, context.topic);
1082
+ }
1083
+ }
1084
+ async function killAll() {
1085
+ const entries = [...instances.values()];
1086
+ for (const instance of entries) {
1087
+ try {
1088
+ instance.process.kill("SIGTERM");
1089
+ } catch {}
1090
+ }
1091
+ instances.clear();
1092
+ usedPorts.clear();
1093
+ knownContexts.clear();
1094
+ const deadline = now() + 3000;
1095
+ await Promise.all(entries.map((instance) => new Promise((resolve4) => {
1096
+ if (instance.process.exitCode !== null || instance.process.killed)
1097
+ return resolve4();
1098
+ instance.process.once("exit", resolve4);
1099
+ instance.process.once("error", resolve4);
1100
+ const timer = setTimeout(resolve4, Math.max(0, deadline - now()));
1101
+ timer.unref?.();
1102
+ })));
1103
+ }
1104
+ return { contextCapability, ensure, clear, clearUser, killAll };
1105
+ }
1106
+ var defaultManager = createBackgroundBashManager();
1107
+ var bgBashContextCapability = defaultManager.contextCapability;
1108
+ var ensureBgBash = defaultManager.ensure;
1109
+ var killBgBash = defaultManager.clear;
1110
+ var killBgBashForUser = defaultManager.clearUser;
1111
+ var killAllBgBash = defaultManager.killAll;
1112
+
1113
+ // ../../packages/core/src/platform/mcp-catalog-policy.ts
1114
+ var COMMON_RUNTIME_MCP_POLICY = {
1115
+ playwright: { scopes: ["dm", "forum", "fork", "cron"], forumRequired: true },
1116
+ runtime: { scopes: ["forum", "manager", "fork", "cron"], forumRequired: true },
1117
+ "token-stats": { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
1118
+ task: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
1119
+ "session-comm": { scopes: ["forum", "fork", "manager"], forumRequired: true },
1120
+ wiki: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
1121
+ skills: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
1122
+ "system-health": { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true },
1123
+ "background-bash": { scopes: ["forum"], forumRequired: true },
1124
+ "agent-health": { scopes: ["forum", "manager", "cron"], forumRequired: true },
1125
+ vault: { scopes: ["dm", "forum", "manager", "cron"], forumRequired: true }
1126
+ };
1127
+ function classifyForumMcpServers(catalog) {
1128
+ const all = Object.entries(catalog).filter(([, entry]) => entry.scopes.includes("forum")).map(([name]) => name);
1129
+ const required = Object.entries(catalog).filter(([, entry]) => entry.scopes.includes("forum") && entry.forumRequired).map(([name]) => name);
1130
+ const requiredSet = new Set(required);
1131
+ return { all, required, optional: all.filter((name) => !requiredSet.has(name)) };
1132
+ }
1133
+ function commonRuntimeMcpPolicy(name) {
1134
+ return COMMON_RUNTIME_MCP_POLICY[name];
1135
+ }
1136
+
1137
+ // ../../packages/core/src/platform/playwright/capability.ts
1138
+ import { createHmac as createHmac3 } from "crypto";
1139
+ function browserOwnerCapability(capability, owner) {
1140
+ return createHmac3("sha256", capability).update(owner).digest("hex");
1141
+ }
1142
+
1143
+ // ../../packages/core/src/platform/mcp-config.ts
1144
+ function buildStdioMcpServer(agent, serverFile, serverArgs, env) {
1145
+ if (agent === "codex") {
1146
+ return {
1147
+ command: "node",
1148
+ args: ["--import", TSX_LOADER, serverFile, ...serverArgs],
1149
+ env: { TSX_TSCONFIG_PATH: TSCONFIG_PATH, ...env }
1150
+ };
1151
+ }
1152
+ return {
1153
+ command: "bun",
1154
+ args: ["run", serverFile, ...serverArgs],
1155
+ ...env ? { env } : {}
1156
+ };
1157
+ }
1158
+ function useHostedBuiltinMcp() {
1159
+ return envText("NEGOTIUM_BUILTIN_MCP_TRANSPORT") !== "stdio";
1160
+ }
1161
+ function buildBuiltinMcpServer(surface, ctx, stdio) {
1162
+ if (!useHostedBuiltinMcp())
1163
+ return stdio();
1164
+ const agent = ctx.agent ?? FALLBACK_AGENT;
1165
+ return buildHostedMcpSpec(agent, surface, {
1166
+ userId: ctx.userId,
1167
+ topicTitle: ctx.session,
1168
+ ...ctx.topicId ? { topicId: ctx.topicId } : {},
1169
+ ...ctx.queryId ? { queryId: ctx.queryId } : {},
1170
+ ...ctx.wikiTopicId ? { wikiTopicId: ctx.wikiTopicId } : {},
1171
+ ...ctx.subagentParentTopicId ? { subagentParentTopicId: ctx.subagentParentTopicId } : {},
1172
+ cwd: ctx.cwd ?? (ctx.topicId ? resolveTopicWorkspaceDir(ctx.topicId) : process.cwd()),
1173
+ agent,
1174
+ ...ctx.model ? { model: ctx.model } : {},
1175
+ ...ctx.depth !== undefined ? { depth: ctx.depth } : {},
1176
+ ...ctx.silent !== undefined ? { silent: ctx.silent } : {},
1177
+ ...ctx.peerBridge ? { peerBridge: ctx.peerBridge } : {}
1178
+ });
1179
+ }
1180
+ var _playwrightUnavailableNotifier;
1181
+ var _playwrightUnavailableLastNotifiedAt = new Map;
1182
+ var _PLAYWRIGHT_UNAVAILABLE_COOLDOWN_MS = 5 * 60000;
1183
+ var _playwrightUnavailableThisTurn = new Set;
1184
+ function _playwrightUnavailableKey(userId, topic) {
1185
+ return `${userId}:${topic ?? ""}`;
1186
+ }
1187
+ function _markPlaywrightUnavailable(ctx) {
1188
+ _playwrightUnavailableThisTurn.add(_playwrightUnavailableKey(ctx.userId, ctx.topic));
1189
+ if (!_playwrightUnavailableNotifier)
1190
+ return;
1191
+ const key = _playwrightUnavailableKey(ctx.userId, ctx.topic);
1192
+ const now = Date.now();
1193
+ const last = _playwrightUnavailableLastNotifiedAt.get(key) ?? 0;
1194
+ if (now - last < _PLAYWRIGHT_UNAVAILABLE_COOLDOWN_MS)
1195
+ return;
1196
+ _playwrightUnavailableLastNotifiedAt.set(key, now);
1197
+ try {
1198
+ _playwrightUnavailableNotifier(ctx);
1199
+ } catch (err) {
1200
+ logger.warn({ err }, "playwright unavailable notifier threw");
1201
+ }
1202
+ }
1203
+ var CODEX_BROWSER_CAPABILITY_ENV = "NEGOTIUM_BROWSER_CAPABILITY";
1204
+ function browserOwnerForContext(ctx) {
1205
+ if (ctx.topicId)
1206
+ return `topic:${ctx.topicId}`;
1207
+ if (ctx.userId && ctx.session)
1208
+ return `user:${ctx.userId}:${ctx.session}`;
1209
+ return;
1210
+ }
1211
+ function playwrightTransport(port, owner, capability, agent) {
1212
+ const ownerCapability = browserOwnerCapability(capability, owner);
1213
+ if (agent === "codex") {
1214
+ const query2 = new URLSearchParams({ owner });
1215
+ return {
1216
+ url: `http://127.0.0.1:${port}/mcp?${query2}`,
1217
+ env_http_headers: { "X-Browser-Capability": CODEX_BROWSER_CAPABILITY_ENV }
1218
+ };
1219
+ }
1220
+ const query = new URLSearchParams({ owner });
1221
+ if (agent === "maestro") {
1222
+ return buildStdioMcpServer("maestro", BROWSER_MCP_SSE_PROXY_SERVER, [], {
1223
+ NEGOTIUM_BROWSER_SSE_URL: `http://127.0.0.1:${port}/sse?${query}`,
1224
+ NEGOTIUM_BROWSER_OWNER_CAPABILITY: ownerCapability
1225
+ });
1226
+ }
1227
+ return {
1228
+ type: "sse",
1229
+ url: `http://127.0.0.1:${port}/sse?${query}`,
1230
+ headers: { "X-Browser-Capability": ownerCapability }
1231
+ };
1232
+ }
1233
+ function longLivedHttpMcp(agent, port) {
1234
+ return agent === "codex" ? { url: `http://127.0.0.1:${port}/mcp` } : { type: "sse", url: `http://127.0.0.1:${port}/sse` };
1235
+ }
1236
+ function backgroundBashTransport(agent, port, userId, topic) {
1237
+ const capability = bgBashContextCapability(userId, topic);
1238
+ const headers = {
1239
+ "X-Background-Bash-User": userId,
1240
+ "X-Background-Bash-Topic": topic,
1241
+ "X-Background-Bash-Capability": capability
1242
+ };
1243
+ if (agent === "codex")
1244
+ return { url: `http://127.0.0.1:${port}/mcp`, http_headers: headers };
1245
+ if (agent === "maestro") {
1246
+ const query = new URLSearchParams({ user: userId, topic, capability });
1247
+ return { type: "sse", url: `http://127.0.0.1:${port}/sse?${query}` };
1248
+ }
1249
+ return { type: "sse", url: `http://127.0.0.1:${port}/sse`, headers };
1250
+ }
1251
+ var MCP_CATALOG = {
1252
+ playwright: {
1253
+ ...commonRuntimeMcpPolicy("playwright"),
1254
+ build({ userId, session, topicId, playwrightPort, playwrightCapability, agent }) {
1255
+ if (playwrightPort && playwrightCapability) {
1256
+ const owner = browserOwnerForContext({ userId, session, topicId });
1257
+ if (!owner)
1258
+ return null;
1259
+ return playwrightTransport(playwrightPort, owner, playwrightCapability, agent);
1260
+ }
1261
+ _markPlaywrightUnavailable({
1262
+ userId,
1263
+ topic: session,
1264
+ agent
1265
+ });
1266
+ return null;
1267
+ }
1268
+ },
1269
+ [RUNTIME_MCP_KEY]: {
1270
+ ...commonRuntimeMcpPolicy("runtime"),
1271
+ build({
1272
+ userId,
1273
+ session,
1274
+ topicId,
1275
+ queryId,
1276
+ agent,
1277
+ cwd,
1278
+ model,
1279
+ currentUserPrompt,
1280
+ autoContinue,
1281
+ visualTools,
1282
+ fileDeliveryTools,
1283
+ peerBridge
1284
+ }) {
1285
+ if (!topicId || !agent)
1286
+ return null;
1287
+ return buildRuntimeMcpSpec(agent, {
1288
+ userId,
1289
+ topicId,
1290
+ topicTitle: session,
1291
+ queryId,
1292
+ cwd: cwd ?? resolveTopicWorkspaceDir(topicId),
1293
+ agent,
1294
+ model,
1295
+ currentUserPrompt,
1296
+ autoContinue,
1297
+ visualTools,
1298
+ fileDeliveryTools,
1299
+ peerBridge
1300
+ });
1301
+ }
1302
+ },
1303
+ "token-stats": {
1304
+ ...commonRuntimeMcpPolicy("token-stats"),
1305
+ build(ctx) {
1306
+ return buildBuiltinMcpServer("token-stats", ctx, () => buildStdioMcpServer(ctx.agent, TOKEN_STATS_SERVER, [`--user-id=${ctx.userId}`]));
1307
+ }
1308
+ },
1309
+ task: {
1310
+ ...commonRuntimeMcpPolicy("task"),
1311
+ build(ctx) {
1312
+ const { userId, session, topicId, queryId, agent, peerBridge } = ctx;
1313
+ if (peerBridge) {
1314
+ if (!topicId || !queryId)
1315
+ return null;
1316
+ const env = canonicalMcpBridgeEnv({
1317
+ surface: "task",
1318
+ userId,
1319
+ topicId,
1320
+ queryId,
1321
+ peerBridge
1322
+ });
1323
+ return env ? buildStdioMcpServer(agent, CANONICAL_MCP_PROXY_SERVER, ["--surface=task"], env) : null;
1324
+ }
1325
+ const args = [`--user-id=${userId}`, `--topic=${session}`];
1326
+ if (topicId)
1327
+ args.push(`--topic-id=${topicId}`);
1328
+ return buildBuiltinMcpServer("task", ctx, () => buildStdioMcpServer(agent, TASK_SERVER, args));
1329
+ }
1330
+ },
1331
+ "session-comm": {
1332
+ ...commonRuntimeMcpPolicy("session-comm"),
1333
+ build(ctx) {
1334
+ const {
1335
+ userId,
1336
+ session,
1337
+ topicId,
1338
+ subagentParentTopicId,
1339
+ agent,
1340
+ depth = 0,
1341
+ silent,
1342
+ peerBridge
1343
+ } = ctx;
1344
+ const effectiveAgent = agent ?? FALLBACK_AGENT;
1345
+ const args = [
1346
+ `--user-id=${userId}`,
1347
+ `--topic=${session}`,
1348
+ ...topicId ? [`--topic-id=${topicId}`] : [],
1349
+ ...subagentParentTopicId ? [`--subagent-parent-topic-id=${subagentParentTopicId}`] : [],
1350
+ `--depth=${depth}`,
1351
+ `--agent=${effectiveAgent}`,
1352
+ ...silent ? ["--reply-only=true"] : [],
1353
+ ...peerBridge ? [`--peer-host-query-id=${peerBridge.hostQueryId}`] : []
1354
+ ];
1355
+ return buildBuiltinMcpServer("session-comm", ctx, () => buildStdioMcpServer(effectiveAgent, SESSION_COMM_SERVER, args, peerBridge ? peerSessionBridgeIpcEnv() : undefined));
1356
+ }
1357
+ },
1358
+ wiki: {
1359
+ ...commonRuntimeMcpPolicy("wiki"),
1360
+ build(ctx) {
1361
+ const { userId, session, topicId, queryId, wikiTopicId, agent, peerBridge } = ctx;
1362
+ if (peerBridge) {
1363
+ if (!topicId || !queryId)
1364
+ return null;
1365
+ const env = canonicalMcpBridgeEnv({
1366
+ surface: "wiki",
1367
+ userId,
1368
+ topicId,
1369
+ queryId,
1370
+ peerBridge
1371
+ });
1372
+ return env ? buildStdioMcpServer(agent, CANONICAL_MCP_PROXY_SERVER, ["--surface=wiki"], env) : null;
1373
+ }
1374
+ const args = [`--user-id=${userId}`];
1375
+ const resolvedWikiTopicId = wikiTopicId ?? topicId ?? (session !== "dm" ? session : undefined);
1376
+ if (resolvedWikiTopicId)
1377
+ args.push(`--topic-id=${resolvedWikiTopicId}`);
1378
+ args.push("--surface=wiki");
1379
+ return buildBuiltinMcpServer("wiki", { ...ctx, wikiTopicId: resolvedWikiTopicId }, () => buildStdioMcpServer(agent, WIKI_SERVER, args));
1380
+ }
1381
+ },
1382
+ skills: {
1383
+ ...commonRuntimeMcpPolicy("skills"),
1384
+ build(ctx) {
1385
+ const { userId, topicId, agent } = ctx;
1386
+ const args = [`--user-id=${userId}`, "--surface=skills"];
1387
+ if (topicId)
1388
+ args.push(`--topic-id=${topicId}`);
1389
+ return buildBuiltinMcpServer("skills", ctx, () => buildStdioMcpServer(agent, WIKI_SERVER, args));
1390
+ }
1391
+ },
1392
+ "system-health": {
1393
+ ...commonRuntimeMcpPolicy("system-health"),
1394
+ build(ctx) {
1395
+ return buildBuiltinMcpServer("system-health", ctx, () => buildStdioMcpServer(ctx.agent, SYSTEM_HEALTH_SERVER, []));
1396
+ }
1397
+ },
1398
+ "background-bash": {
1399
+ ...commonRuntimeMcpPolicy("background-bash"),
1400
+ build({ agent, bgBashPort, userId, topicId }) {
1401
+ if (bgBashPort === undefined || !topicId)
1402
+ return null;
1403
+ return backgroundBashTransport(agent, bgBashPort, userId, topicId);
1404
+ }
1405
+ },
1406
+ "agent-health": {
1407
+ ...commonRuntimeMcpPolicy("agent-health"),
1408
+ build(ctx) {
1409
+ const { userId, agent } = ctx;
1410
+ const args = [`--user-id=${userId}`];
1411
+ return buildBuiltinMcpServer("agent-health", ctx, () => buildStdioMcpServer(agent, AGENT_HEALTH_SERVER, args));
1412
+ }
1413
+ },
1414
+ vault: {
1415
+ ...commonRuntimeMcpPolicy("vault"),
1416
+ build(ctx) {
1417
+ const { userId, agent } = ctx;
1418
+ const args = [`--user-id=${userId}`];
1419
+ if (agent !== "codex")
1420
+ args.push("--list-only=true");
1421
+ return buildBuiltinMcpServer("vault", ctx, () => buildStdioMcpServer(agent, VAULT_SERVER, args));
1422
+ }
1423
+ }
1424
+ };
1425
+ var allForumMcpServerNames = [];
1426
+ var requiredForumMcpServers = [];
1427
+ var REQUIRED_FORUM_MCP_SERVERS = requiredForumMcpServers;
1428
+ var optionalForumMcpServers = [];
1429
+ function refreshForumCatalogViews() {
1430
+ const { all, required, optional } = classifyForumMcpServers(MCP_CATALOG);
1431
+ allForumMcpServerNames.splice(0, allForumMcpServerNames.length, ...all);
1432
+ requiredForumMcpServers.splice(0, requiredForumMcpServers.length, ...required);
1433
+ optionalForumMcpServers.splice(0, optionalForumMcpServers.length, ...optional);
1434
+ }
1435
+ refreshForumCatalogViews();
1436
+ var nodeMcpEntries = [];
1437
+ function buildNodeMcpSpecs(agent, filter) {
1438
+ const out = {};
1439
+ for (const entry of nodeMcpEntries) {
1440
+ if (!filter(entry.key))
1441
+ continue;
1442
+ out[entry.key] = entry.kind === "http" ? longLivedHttpMcp(agent, entry.port) : {
1443
+ command: entry.command,
1444
+ args: entry.args ?? [],
1445
+ ...entry.env ? { env: entry.env } : {}
1446
+ };
1447
+ }
1448
+ return out;
1449
+ }
1450
+ function buildScope(scope, ctx, filter = () => true) {
1451
+ const out = {};
1452
+ for (const [name, entry] of Object.entries(MCP_CATALOG)) {
1453
+ if (!entry.scopes.includes(scope))
1454
+ continue;
1455
+ if (!filter(name))
1456
+ continue;
1457
+ const spec = entry.build(ctx);
1458
+ if (spec === null)
1459
+ continue;
1460
+ out[name] = spec;
1461
+ }
1462
+ if (scope !== "cron") {
1463
+ Object.assign(out, buildNodeMcpSpecs(ctx.agent, filter));
1464
+ }
1465
+ return out;
1466
+ }
1467
+ function getDmMcpServers(opts) {
1468
+ return buildScope("dm", {
1469
+ userId: opts.userId,
1470
+ session: "dm",
1471
+ agent: opts.agent,
1472
+ playwrightPort: opts.playwrightPort,
1473
+ playwrightCapability: opts.playwrightCapability
1474
+ });
1475
+ }
1476
+ function getManagerMcpServers(opts) {
1477
+ if (!opts.topicId) {
1478
+ throw new Error("getManagerMcpServers: private General topicId is required");
1479
+ }
1480
+ const topicId = opts.topicId;
1481
+ return buildScope("manager", {
1482
+ userId: opts.userId,
1483
+ session: opts.session ?? "General",
1484
+ topicId,
1485
+ queryId: opts.queryId,
1486
+ wikiTopicId: opts.wikiTopicId ?? topicId,
1487
+ agent: opts.agent,
1488
+ cwd: opts.cwd,
1489
+ model: opts.model,
1490
+ currentUserPrompt: opts.currentUserPrompt,
1491
+ playwrightPort: opts.playwrightPort,
1492
+ playwrightCapability: opts.playwrightCapability,
1493
+ autoContinue: opts.autoContinue,
1494
+ visualTools: opts.visualTools,
1495
+ fileDeliveryTools: opts.fileDeliveryTools
1496
+ });
1497
+ }
1498
+ function getForumMcpServers(opts) {
1499
+ const {
1500
+ userId,
1501
+ session,
1502
+ topicId,
1503
+ subagentParentTopicId,
1504
+ queryId,
1505
+ wikiTopicId,
1506
+ agent,
1507
+ cwd,
1508
+ model,
1509
+ currentUserPrompt,
1510
+ playwrightPort,
1511
+ playwrightCapability,
1512
+ depth = 0,
1513
+ enabled = null,
1514
+ extra = {},
1515
+ silent = false,
1516
+ bgBashPort,
1517
+ autoContinue,
1518
+ visualTools,
1519
+ fileDeliveryTools,
1520
+ peerBridge
1521
+ } = opts;
1522
+ const filter = (name) => {
1523
+ if (silent && name === "task")
1524
+ return false;
1525
+ if (enabled === null)
1526
+ return true;
1527
+ return enabled.includes(name) || REQUIRED_FORUM_MCP_SERVERS.includes(name);
1528
+ };
1529
+ const base = buildScope("forum", {
1530
+ userId,
1531
+ session,
1532
+ topicId,
1533
+ subagentParentTopicId,
1534
+ queryId,
1535
+ wikiTopicId,
1536
+ agent,
1537
+ cwd,
1538
+ model,
1539
+ currentUserPrompt,
1540
+ depth,
1541
+ playwrightPort,
1542
+ playwrightCapability,
1543
+ bgBashPort,
1544
+ autoContinue,
1545
+ visualTools,
1546
+ fileDeliveryTools,
1547
+ silent,
1548
+ peerBridge
1549
+ }, filter);
1550
+ return { ...base, ...extra };
1551
+ }
1552
+ function getCronMcpServers(opts) {
1553
+ return buildScope("cron", {
1554
+ userId: opts.userId,
1555
+ session: opts.session,
1556
+ topicId: opts.topicId,
1557
+ queryId: opts.queryId,
1558
+ wikiTopicId: opts.wikiTopicId ?? opts.topicId,
1559
+ agent: opts.agent,
1560
+ cwd: opts.cwd,
1561
+ model: opts.model,
1562
+ currentUserPrompt: opts.currentUserPrompt,
1563
+ playwrightPort: opts.playwrightPort,
1564
+ playwrightCapability: opts.playwrightCapability,
1565
+ autoContinue: false,
1566
+ visualTools: opts.visualTools,
1567
+ fileDeliveryTools: opts.fileDeliveryTools
1568
+ });
1569
+ }
1570
+ function getMcpServersForQuery(opts) {
1571
+ if (opts.toolPolicy === "none")
1572
+ return {};
1573
+ if (opts.toolPolicy === "compaction-log") {
1574
+ const compactLog = opts.mcpExtra?.compact_log;
1575
+ return compactLog ? { compact_log: compactLog } : {};
1576
+ }
1577
+ if (opts.sessionType === "cron") {
1578
+ if (!opts.topicId)
1579
+ throw new Error("getMcpServersForQuery: cron sessionType requires topicId");
1580
+ return getCronMcpServers({
1581
+ userId: opts.userId || "local",
1582
+ session: opts.session || "cron",
1583
+ topicId: opts.topicId,
1584
+ queryId: opts.queryId,
1585
+ wikiTopicId: opts.wikiTopicId,
1586
+ agent: opts.agent,
1587
+ cwd: opts.cwd,
1588
+ model: opts.model,
1589
+ currentUserPrompt: opts.prompt,
1590
+ playwrightPort: opts.playwrightPort,
1591
+ playwrightCapability: opts.playwrightCapability,
1592
+ visualTools: opts.visualTools,
1593
+ fileDeliveryTools: opts.fileDeliveryTools
1594
+ });
1595
+ }
1596
+ if (opts.sessionType === "dm" || opts.sessionType === "ephemeral") {
1597
+ return getDmMcpServers({
1598
+ userId: opts.userId || "local",
1599
+ agent: opts.agent,
1600
+ playwrightPort: opts.playwrightPort,
1601
+ playwrightCapability: opts.playwrightCapability
1602
+ });
1603
+ }
1604
+ if (opts.sessionType === "manager") {
1605
+ return getManagerMcpServers({
1606
+ userId: opts.userId || "local",
1607
+ session: opts.session,
1608
+ topicId: opts.topicId,
1609
+ queryId: opts.queryId,
1610
+ wikiTopicId: opts.wikiTopicId,
1611
+ agent: opts.agent,
1612
+ cwd: opts.cwd,
1613
+ model: opts.model,
1614
+ currentUserPrompt: opts.prompt,
1615
+ playwrightPort: opts.playwrightPort,
1616
+ playwrightCapability: opts.playwrightCapability,
1617
+ autoContinue: opts.autoContinue,
1618
+ visualTools: opts.visualTools,
1619
+ fileDeliveryTools: opts.fileDeliveryTools
1620
+ });
1621
+ }
1622
+ return getForumMcpServers({
1623
+ userId: opts.userId || "local",
1624
+ session: opts.session || "default",
1625
+ topicId: opts.topicId,
1626
+ subagentParentTopicId: opts.subagentParentTopicId,
1627
+ queryId: opts.queryId,
1628
+ wikiTopicId: opts.wikiTopicId,
1629
+ agent: opts.agent,
1630
+ cwd: opts.cwd,
1631
+ model: opts.model,
1632
+ currentUserPrompt: opts.prompt,
1633
+ playwrightPort: opts.playwrightPort,
1634
+ playwrightCapability: opts.playwrightCapability,
1635
+ bgBashPort: opts.bgBashPort,
1636
+ autoContinue: opts.autoContinue,
1637
+ visualTools: opts.visualTools,
1638
+ fileDeliveryTools: opts.fileDeliveryTools,
1639
+ depth: opts.depth,
1640
+ enabled: opts.mcpEnabled,
1641
+ extra: opts.mcpExtra,
1642
+ silent: opts.silent,
1643
+ peerBridge: opts.peerBridge
1644
+ });
1645
+ }
1646
+
1647
+ // ../../packages/core/src/storage/vault.ts
1648
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync4 } from "fs";
1649
+ import { join as join3 } from "path";
1650
+
1651
+ // ../../packages/core/src/storage/sqlite.ts
1652
+ var isBun = typeof process.versions.bun === "string";
1653
+ var Database;
1654
+ if (isBun) {
1655
+ ({ Database } = await import("bun:sqlite"));
1656
+ } else {
1657
+ const nodeSqliteSpecifier = ["node", "sqlite"].join(":");
1658
+ const { DatabaseSync } = await import(nodeSqliteSpecifier);
1659
+
1660
+ class NodeDatabase {
1661
+ #db;
1662
+ constructor(path, options = {}) {
1663
+ this.#db = options.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path);
1664
+ }
1665
+ query(sql) {
1666
+ return this.#db.prepare(sql);
1667
+ }
1668
+ prepare(sql) {
1669
+ return this.#db.prepare(sql);
1670
+ }
1671
+ exec(sql) {
1672
+ this.#db.exec(sql);
1673
+ }
1674
+ run(sql, ...params) {
1675
+ return this.#db.prepare(sql).run(...params);
1676
+ }
1677
+ transaction(fn) {
1678
+ const run = (begin) => (...args) => {
1679
+ this.#db.exec(begin);
1680
+ try {
1681
+ const result = fn(...args);
1682
+ this.#db.exec("COMMIT");
1683
+ return result;
1684
+ } catch (err) {
1685
+ this.#db.exec("ROLLBACK");
1686
+ throw err;
1687
+ }
1688
+ };
1689
+ const tx = run("BEGIN");
1690
+ tx.deferred = run("BEGIN DEFERRED");
1691
+ tx.immediate = run("BEGIN IMMEDIATE");
1692
+ tx.exclusive = run("BEGIN EXCLUSIVE");
1693
+ return tx;
1694
+ }
1695
+ close() {
1696
+ this.#db.close();
1697
+ }
1698
+ }
1699
+ Database = NodeDatabase;
1700
+ }
1701
+
1702
+ // ../../packages/core/src/storage/vault-crypto-core.ts
1703
+ import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes3 } from "crypto";
1704
+ var ENVELOPE_PREFIX = "otium-vault:v1:";
1705
+ var IV_BYTES = 12;
1706
+ var KEY_BYTES = 32;
1707
+ function encryptionKey(masterKey) {
1708
+ return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
1709
+ }
1710
+ function aad(userId, key) {
1711
+ return Buffer.from(`${userId}\x00${key.toUpperCase()}`, "utf8");
1712
+ }
1713
+ function isEncryptedVaultValue(value) {
1714
+ return value.startsWith(ENVELOPE_PREFIX);
1715
+ }
1716
+ function encryptVaultValueWithKey(userId, key, value, masterKey) {
1717
+ const iv = randomBytes3(IV_BYTES);
1718
+ const cipher = createCipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
1719
+ cipher.setAAD(aad(userId, key));
1720
+ const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
1721
+ const tag = cipher.getAuthTag();
1722
+ return `${ENVELOPE_PREFIX}${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
1723
+ }
1724
+ function decryptVaultValueWithKey(userId, key, storedValue, masterKey) {
1725
+ if (!isEncryptedVaultValue(storedValue)) {
1726
+ return { value: storedValue, legacyPlaintext: true };
1727
+ }
1728
+ const encoded = storedValue.slice(ENVELOPE_PREFIX.length);
1729
+ const [ivPart, ciphertextPart, tagPart, ...extra] = encoded.split(".");
1730
+ if (!ivPart || ciphertextPart === undefined || !tagPart || extra.length > 0) {
1731
+ throw new Error("Invalid encrypted vault value");
1732
+ }
1733
+ const iv = Buffer.from(ivPart, "base64url");
1734
+ const ciphertext = Buffer.from(ciphertextPart, "base64url");
1735
+ const tag = Buffer.from(tagPart, "base64url");
1736
+ if (iv.length !== IV_BYTES || tag.length !== 16) {
1737
+ throw new Error("Invalid encrypted vault value");
1738
+ }
1739
+ const decipher = createDecipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
1740
+ decipher.setAAD(aad(userId, key));
1741
+ decipher.setAuthTag(tag);
1742
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
1743
+ return { value: plaintext.toString("utf8"), legacyPlaintext: false };
1744
+ }
1745
+
1746
+ // ../../packages/core/src/storage/vault-crypto.ts
1747
+ function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
1748
+ return encryptVaultValueWithKey(userId, key, value, masterKey);
1749
+ }
1750
+ function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KEY) {
1751
+ return decryptVaultValueWithKey(userId, key, storedValue, masterKey);
1752
+ }
1753
+
1754
+ // ../../packages/core/src/storage/vault.ts
1755
+ var vaultDb;
1756
+ var vaultMasterKey = VAULT_MASTER_KEY;
1757
+ function initializeVaultDatabase(database) {
1758
+ database.exec("PRAGMA journal_mode = WAL");
1759
+ database.exec("PRAGMA busy_timeout = 5000");
1760
+ database.exec(`
1761
+ CREATE TABLE IF NOT EXISTS vault (
1762
+ user_id TEXT NOT NULL,
1763
+ key TEXT NOT NULL,
1764
+ value TEXT NOT NULL,
1765
+ description TEXT NOT NULL DEFAULT '',
1766
+ PRIMARY KEY (user_id, key)
1767
+ )
1768
+ `);
1769
+ {
1770
+ const cols = database.prepare("PRAGMA table_info(vault)").all();
1771
+ const uid = cols.find((c) => c.name === "user_id");
1772
+ if (uid && uid.type.toUpperCase() === "INTEGER") {
1773
+ database.exec("BEGIN");
1774
+ database.exec(`
1775
+ CREATE TABLE vault_migrated (
1776
+ user_id TEXT NOT NULL,
1777
+ key TEXT NOT NULL,
1778
+ value TEXT NOT NULL,
1779
+ description TEXT NOT NULL DEFAULT '',
1780
+ PRIMARY KEY (user_id, key)
1781
+ )
1782
+ `);
1783
+ database.exec("INSERT INTO vault_migrated SELECT CAST(user_id AS TEXT), key, value, description FROM vault");
1784
+ database.exec("DROP TABLE vault");
1785
+ database.exec("ALTER TABLE vault_migrated RENAME TO vault");
1786
+ database.exec("COMMIT");
1787
+ }
1788
+ }
1789
+ }
1790
+ function openVaultDatabase(dataDir) {
1791
+ const vaultDir = join3(dataDir, "vault");
1792
+ const path = join3(vaultDir, "vault.db");
1793
+ mkdirSync4(vaultDir, { recursive: true, mode: 448 });
1794
+ const database = new Database(path, { create: true });
1795
+ chmodSync2(path, 384);
1796
+ initializeVaultDatabase(database);
1797
+ return database;
1798
+ }
1799
+ function activeVaultDatabase() {
1800
+ if (!vaultDb)
1801
+ vaultDb = openVaultDatabase(DATA_DIR);
1802
+ return vaultDb;
1803
+ }
1804
+ var VAULT_VALUE_MAX_BYTES = 64 * 1024;
1805
+ function normalizeVaultKey(key) {
1806
+ return key.trim().toUpperCase();
1807
+ }
1808
+ function decryptRow(userId, key, storedValue) {
1809
+ const database = activeVaultDatabase();
1810
+ const decoded = decryptVaultValue(userId, key, storedValue, vaultMasterKey);
1811
+ if (decoded.legacyPlaintext) {
1812
+ database.prepare("UPDATE vault SET value = ? WHERE user_id = ? AND key = ? AND value = ?").run(encryptVaultValue(userId, key, decoded.value, vaultMasterKey), userId, key, storedValue);
1813
+ }
1814
+ return decoded.value;
1815
+ }
1816
+ function vaultListDecryptableValues(userId) {
1817
+ const rows = activeVaultDatabase().prepare("SELECT key, description, value FROM vault WHERE user_id = ? ORDER BY key").all(userId);
1818
+ const entries = [];
1819
+ for (const row of rows) {
1820
+ try {
1821
+ entries.push({
1822
+ key: row.key,
1823
+ description: row.description,
1824
+ value: decryptRow(userId, row.key, row.value)
1825
+ });
1826
+ } catch {}
1827
+ }
1828
+ return entries;
1829
+ }
1830
+ function vaultGetValue(userId, key) {
1831
+ const normalizedKey = normalizeVaultKey(key);
1832
+ const row = activeVaultDatabase().prepare("SELECT key, value FROM vault WHERE user_id = ? AND key = ?").get(userId, normalizedKey);
1833
+ return row ? decryptRow(userId, row.key, row.value) : undefined;
1834
+ }
1835
+ function vaultSubstituteDetailed(userId, text) {
1836
+ const entries = new Map;
1837
+ const usedKeys = new Set;
1838
+ const substituted = text.replace(/\{\{([^}]+)\}\}/g, (match, rawKey) => {
1839
+ const key = normalizeVaultKey(rawKey);
1840
+ if (!entries.has(key))
1841
+ entries.set(key, vaultGetValue(userId, key));
1842
+ const value = entries.get(key);
1843
+ if (value === undefined)
1844
+ return match;
1845
+ usedKeys.add(key);
1846
+ return value;
1847
+ });
1848
+ return { text: substituted, usedKeys: [...usedKeys] };
1849
+ }
1850
+ function encodedSecretForms(value) {
1851
+ const forms = new Set([
1852
+ value,
1853
+ encodeURIComponent(value),
1854
+ Buffer.from(value, "utf8").toString("base64"),
1855
+ Buffer.from(value, "utf8").toString("base64url"),
1856
+ Buffer.from(value, "utf8").toString("hex")
1857
+ ]);
1858
+ forms.delete("");
1859
+ return [...forms].sort((a, b) => b.length - a.length);
1860
+ }
1861
+ function redactVaultSecrets(userId, text) {
1862
+ const candidates = vaultListDecryptableValues(userId).flatMap((entry) => encodedSecretForms(entry.value).map((form) => ({ form, key: entry.key }))).sort((a, b) => b.form.length - a.form.length || a.key.localeCompare(b.key));
1863
+ if (candidates.length === 0)
1864
+ return text;
1865
+ const candidatesByFirstCharacter = new Map;
1866
+ for (const candidate of candidates) {
1867
+ const first = candidate.form[0];
1868
+ if (!first)
1869
+ continue;
1870
+ const bucket = candidatesByFirstCharacter.get(first) ?? [];
1871
+ bucket.push(candidate);
1872
+ candidatesByFirstCharacter.set(first, bucket);
1873
+ }
1874
+ let redacted = "";
1875
+ let offset = 0;
1876
+ while (offset < text.length) {
1877
+ const match = candidatesByFirstCharacter.get(text[offset] ?? "")?.find((candidate) => text.startsWith(candidate.form, offset));
1878
+ if (!match) {
1879
+ redacted += text[offset];
1880
+ offset += 1;
1881
+ continue;
1882
+ }
1883
+ redacted += `[REDACTED:${match.key}]`;
1884
+ offset += match.form.length;
1885
+ }
1886
+ return redacted;
1887
+ }
1888
+
1889
+ // ../../packages/core/src/agents/execution-host.ts
1890
+ var defaultHost = {
1891
+ getMcpServersForQuery,
1892
+ redactVaultSecrets,
1893
+ substituteVaultSecrets: (userId, value) => vaultSubstituteDetailed(userId, value).text,
1894
+ referencesRuntimeSecretStorage,
1895
+ shouldRedirectVaultTool,
1896
+ claudeCodeExecutablePath: () => CLAUDE_EXECUTABLE,
1897
+ codexAuthFilePath
1898
+ };
1899
+ var hostRegistrations = [];
1900
+ var scopedHost = new AsyncLocalStorage;
1901
+ function activeHost() {
1902
+ const scoped = scopedHost.getStore();
1903
+ if (scoped)
1904
+ return scoped;
1905
+ const host = { ...defaultHost };
1906
+ for (const registration of hostRegistrations)
1907
+ Object.assign(host, registration.overrides);
1908
+ return host;
1909
+ }
1910
+ function hostedCodexAuthFilePath() {
1911
+ return activeHost().codexAuthFilePath();
1912
+ }
1913
+ function hostedCodexHomePath() {
1914
+ return dirname3(hostedCodexAuthFilePath());
1915
+ }
1916
+
1917
+ // ../../packages/core/src/agents/rollout/codex.ts
742
1918
  function codexSessionsDir() {
743
- return join3(process.env.CODEX_HOME || join3(homedir3(), ".codex"), "sessions");
1919
+ return join4(hostedCodexHomePath(), "sessions");
744
1920
  }
745
1921
  var _shellCache = null;
746
1922
  function loadCodexShell() {
747
1923
  if (_shellCache)
748
1924
  return _shellCache;
749
- const raw = readFileSync4(join3(FIXTURES_DIR, "codex-shell.jsonl"), "utf8");
1925
+ const raw = readFileSync4(join4(FIXTURES_DIR, "codex-shell.jsonl"), "utf8");
750
1926
  const lines = parseJsonlText(raw);
751
1927
  if (lines.length < 5) {
752
1928
  throw new Error(`loadCodexShell: expected >=5 entries in codex-shell.jsonl, got ${lines.length}`);
@@ -763,7 +1939,7 @@ function loadCodexShell() {
763
1939
  function uuidv7() {
764
1940
  const ts = Date.now();
765
1941
  const tsHex = ts.toString(16).padStart(12, "0");
766
- const rand = randomBytes2(10);
1942
+ const rand = randomBytes4(10);
767
1943
  rand[0] = rand[0] & 15 | 112;
768
1944
  rand[2] = rand[2] & 63 | 128;
769
1945
  return [
@@ -805,12 +1981,12 @@ function patchEnvContext(envContext, cwd, currentDate, timezone) {
805
1981
  }
806
1982
  }
807
1983
  function canonicalFilePath(path) {
808
- const absolute = resolve3(path);
1984
+ const absolute = resolve4(path);
809
1985
  try {
810
- return realpathSync(absolute);
1986
+ return realpathSync2(absolute);
811
1987
  } catch {
812
1988
  try {
813
- return join3(realpathSync(dirname3(absolute)), basename(absolute));
1989
+ return join4(realpathSync2(dirname4(absolute)), basename(absolute));
814
1990
  } catch {
815
1991
  return absolute;
816
1992
  }
@@ -999,19 +2175,19 @@ function latestCodexRolloutPath(threadId) {
999
2175
  try {
1000
2176
  if (buckets) {
1001
2177
  for (const bucket of buckets) {
1002
- const dir = join3(sessionsDir, bucket);
2178
+ const dir = join4(sessionsDir, bucket);
1003
2179
  if (!existsSync3(dir))
1004
2180
  continue;
1005
2181
  const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
1006
2182
  for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
1007
- candidates.push(join3(dir, rel));
2183
+ candidates.push(join4(dir, rel));
1008
2184
  }
1009
2185
  }
1010
2186
  }
1011
2187
  if (candidates.length === 0) {
1012
2188
  const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
1013
2189
  for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
1014
- candidates.push(join3(sessionsDir, rel));
2190
+ candidates.push(join4(sessionsDir, rel));
1015
2191
  }
1016
2192
  }
1017
2193
  return candidates.sort((a, b) => statSync2(b).mtimeMs - statSync2(a).mtimeMs)[0];
@@ -1120,8 +2296,8 @@ function writeCodexRollout(opts) {
1120
2296
  const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
1121
2297
  const dd = String(now.getUTCDate()).padStart(2, "0");
1122
2298
  const tsStr = tsIso.replace(/[:.]/g, "-").slice(0, 19);
1123
- const dir = join3(codexSessionsDir(), String(yyyy), mm, dd);
1124
- const path = join3(dir, `rollout-${tsStr}-${threadId}.jsonl`);
2299
+ const dir = join4(codexSessionsDir(), String(yyyy), mm, dd);
2300
+ const path = join4(dir, `rollout-${tsStr}-${threadId}.jsonl`);
1125
2301
  if (opts.threadId) {
1126
2302
  sweepPriorRolloutsForThread(opts.threadId);
1127
2303
  }
@@ -1137,13 +2313,13 @@ function sweepPriorRolloutsForThread(threadId) {
1137
2313
  return;
1138
2314
  }
1139
2315
  for (const bucket of buckets) {
1140
- const dir = join3(sessionsDir, bucket);
2316
+ const dir = join4(sessionsDir, bucket);
1141
2317
  if (!existsSync3(dir))
1142
2318
  continue;
1143
2319
  try {
1144
2320
  const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
1145
2321
  for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
1146
- const fullPath = join3(dir, rel);
2322
+ const fullPath = join4(dir, rel);
1147
2323
  try {
1148
2324
  unlinkSync3(fullPath);
1149
2325
  } catch (e) {
@@ -1161,7 +2337,7 @@ function sweepPriorRolloutsFullTree(threadId, sessionsDir) {
1161
2337
  try {
1162
2338
  const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
1163
2339
  for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
1164
- const fullPath = join3(sessionsDir, rel);
2340
+ const fullPath = join4(sessionsDir, rel);
1165
2341
  try {
1166
2342
  unlinkSync3(fullPath);
1167
2343
  } catch (e) {
@@ -1218,6 +2394,6 @@ function formatDateBucket(d) {
1218
2394
  return `${yyyy}/${mm}/${dd}`;
1219
2395
  }
1220
2396
 
1221
- export { __require, logger, CLAUDE_EFFORT_VALUES, CODEX_EFFORT_VALUES, MAESTRO_EFFORT_VALUES, MODEL_SONNET, MODEL_OPUS, MODEL_FABLE, configureRolloutHost, assertUuidLike, ensureCwdExists, extractChatPairs, encodeClaudeCwd, writeClaudeRollout, repairPoisonedRollout, extractLatestCodexPatchPreview, extractCodexPatchCallIds, readCodexPatchCallIds, readLatestCodexPatchPreview, extractLatestCodexContextUsage, readLatestCodexContextUsage, migrateCodexRolloutNativeMultiAgentMetadata, writeCodexRollout, decodeUuidV7Timestamp };
2397
+ export { __require, logger, CLAUDE_EFFORT_VALUES, CODEX_EFFORT_VALUES, MAESTRO_EFFORT_VALUES, MODEL_SONNET, MODEL_OPUS, MODEL_FABLE, configureRolloutHost, assertUuidLike, ensureCwdExists, extractChatPairs, encodeClaudeCwd, writeClaudeRollout, repairPoisonedRollout, hostedCodexHomePath, extractLatestCodexPatchPreview, extractCodexPatchCallIds, readCodexPatchCallIds, readLatestCodexPatchPreview, extractLatestCodexContextUsage, readLatestCodexContextUsage, migrateCodexRolloutNativeMultiAgentMetadata, latestCodexRolloutPath, writeCodexRollout, decodeUuidV7Timestamp };
1222
2398
 
1223
- //# debugId=D7C11CCD1FE5699464756E2164756E21
2399
+ //# debugId=889664B432D1D24364756E2164756E21