negotium 0.2.2 → 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.
- package/dist/agent-helpers.js +3117 -2915
- package/dist/agent-helpers.js.map +38 -37
- package/dist/background-bash.js.map +1 -1
- package/dist/browser-runtime.js +137 -21
- package/dist/browser-runtime.js.map +10 -9
- package/dist/{chunk-kc5kmr95.js → chunk-1s9ryz8g.js} +1195 -21
- package/dist/chunk-1s9ryz8g.js.map +35 -0
- package/dist/hosted-agent.js +60 -48
- package/dist/hosted-agent.js.map +10 -10
- package/dist/main.js +3038 -3357
- package/dist/main.js.map +35 -35
- package/dist/mcp-factories.js +6227 -6021
- package/dist/mcp-factories.js.map +40 -39
- package/dist/prompts.js.map +1 -1
- package/dist/query-runtime.js +22 -2
- package/dist/query-runtime.js.map +4 -4
- package/dist/registry.js +169 -213
- package/dist/registry.js.map +8 -9
- package/dist/rollout.js +1 -1
- package/dist/runtime/src/agents/codex-app-server.ts +136 -0
- package/dist/runtime/src/agents/codex-native-multi-agent.ts +1 -1
- package/dist/runtime/src/agents/codex-provider.ts +21 -11
- package/dist/runtime/src/agents/codex-registry.ts +9 -17
- package/dist/runtime/src/agents/contracts.ts +2 -0
- package/dist/runtime/src/agents/execution-host.ts +6 -0
- package/dist/runtime/src/agents/fork.ts +24 -18
- package/dist/runtime/src/agents/idle-archiver.ts +7 -2
- package/dist/runtime/src/agents/index.ts +2 -1
- package/dist/runtime/src/agents/maestro-registry.ts +26 -7
- package/dist/runtime/src/agents/memory-archive-policy.ts +20 -0
- package/dist/runtime/src/agents/rollout/codex.ts +6 -5
- package/dist/runtime/src/agents/rollout/shared.ts +3 -5
- package/dist/runtime/src/mcp/factories/session-comm.ts +12 -0
- package/dist/runtime/src/mcp/session-comm/default-host.ts +16 -0
- package/dist/runtime/src/mcp/session-comm/server.ts +25 -0
- package/dist/runtime/src/platform/config.ts +4 -1
- package/dist/runtime/src/platform/playwright/manager.ts +57 -1
- package/dist/runtime/src/platform/playwright/profile-management.ts +33 -0
- package/dist/runtime/src/platform/playwright/public-runtime.ts +5 -0
- package/dist/runtime/src/platform/playwright/vault-broker.ts +9 -10
- package/dist/runtime/src/storage/browser-profiles.ts +35 -10
- package/dist/runtime/src/storage/storage-host.ts +31 -5
- package/dist/runtime/src/storage/vault.ts +30 -2
- package/dist/runtime/src/topics/derive.ts +38 -2
- package/dist/runtime/src/topics/lifecycle.ts +12 -2
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +22 -2
- package/dist/runtime-helpers.js.map +4 -4
- package/dist/storage.js +22 -2
- package/dist/storage.js.map +4 -4
- package/dist/types/packages/core/src/agents/codex-app-server.d.ts +13 -0
- package/dist/types/packages/core/src/agents/codex-native-multi-agent.d.ts +1 -0
- package/dist/types/packages/core/src/agents/contracts.d.ts +2 -0
- package/dist/types/packages/core/src/agents/execution-host.d.ts +2 -0
- package/dist/types/packages/core/src/agents/fork.d.ts +8 -7
- package/dist/types/packages/core/src/agents/memory-archive-policy.d.ts +10 -0
- package/dist/types/packages/core/src/agents/rollout/codex.d.ts +4 -2
- package/dist/types/packages/core/src/mcp/factories/session-comm.d.ts +1 -0
- package/dist/types/packages/core/src/platform/playwright/manager.d.ts +15 -0
- package/dist/types/packages/core/src/platform/playwright/profile-management.d.ts +8 -0
- package/dist/types/packages/core/src/platform/playwright/public-runtime.d.ts +2 -1
- package/dist/types/packages/core/src/storage/browser-profiles.d.ts +3 -1
- package/dist/types/packages/core/src/storage/storage-host.d.ts +1 -0
- package/dist/types/packages/core/src/storage/vault.d.ts +8 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js +19 -3
- package/dist/vault.js.map +4 -4
- package/package.json +2 -2
- package/dist/chunk-kc5kmr95.js.map +0 -19
- package/dist/runtime/src/migration/single-user.ts +0 -805
|
@@ -3,8 +3,7 @@ var __require = import.meta.require;
|
|
|
3
3
|
|
|
4
4
|
// ../../packages/core/src/agents/rollout/shared.ts
|
|
5
5
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
6
|
-
import {
|
|
7
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6
|
+
import { resolve as resolve2 } from "path";
|
|
8
7
|
|
|
9
8
|
// ../../packages/core/src/platform/config.ts
|
|
10
9
|
import { execFileSync } from "child_process";
|
|
@@ -34,6 +33,10 @@ function parseRuntimePort(value, fallback) {
|
|
|
34
33
|
const port = Number.parseInt(value, 10);
|
|
35
34
|
return Number.isInteger(port) && port > 0 && port <= 65535 ? port : fallback;
|
|
36
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
|
+
}
|
|
37
40
|
|
|
38
41
|
// ../../packages/core/src/platform/logger.ts
|
|
39
42
|
import pino from "pino";
|
|
@@ -182,6 +185,12 @@ var BACKGROUND_BASH_SERVER = resolve(PROJECT_ROOT, "src/mcp/background-bash-serv
|
|
|
182
185
|
var VAULT_SERVER = resolve(PROJECT_ROOT, "src/mcp/vault-server.ts");
|
|
183
186
|
var BG_BASH_BASE_PORT = parsePortEnv(process.env.BG_BASH_BASE_PORT, 9700);
|
|
184
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
|
+
}
|
|
185
194
|
function loadOrCreateLocalSecret(envKey, filename, options = {}) {
|
|
186
195
|
const envValue = envText(envKey);
|
|
187
196
|
const secretFile = resolve(SECRETS_DIR, filename);
|
|
@@ -284,6 +293,9 @@ var TESSERACT_BIN = envText("TESSERACT_BIN") ?? "tesseract";
|
|
|
284
293
|
var PDFTOTEXT_BIN = envText("PDFTOTEXT_BIN") ?? "pdftotext";
|
|
285
294
|
var _envMaxTellDepth = Number.parseInt(process.env.MAX_TELL_DEPTH ?? "", 10);
|
|
286
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
|
+
}
|
|
287
299
|
|
|
288
300
|
// ../../packages/core/src/runtime/user-turn-envelope.ts
|
|
289
301
|
function renderUserPromptBatch(prompts) {
|
|
@@ -298,7 +310,6 @@ function renderUserPromptBatch(prompts) {
|
|
|
298
310
|
}
|
|
299
311
|
|
|
300
312
|
// ../../packages/core/src/agents/rollout/shared.ts
|
|
301
|
-
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
302
313
|
var trustedWorkspaceRoots = [
|
|
303
314
|
WORKSPACE_DIR,
|
|
304
315
|
DM_WORKSPACE_DIR,
|
|
@@ -328,7 +339,7 @@ function configureRolloutHost(options) {
|
|
|
328
339
|
FIXTURES_DIR = previousFixturesDir;
|
|
329
340
|
};
|
|
330
341
|
}
|
|
331
|
-
var FIXTURES_DIR =
|
|
342
|
+
var FIXTURES_DIR = resolve2(PROJECT_ROOT, "src", "agents", "fixtures");
|
|
332
343
|
function clone(obj) {
|
|
333
344
|
return structuredClone(obj);
|
|
334
345
|
}
|
|
@@ -465,7 +476,7 @@ ${toolBuffer.join(`
|
|
|
465
476
|
import { randomUUID } from "crypto";
|
|
466
477
|
import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
467
478
|
import { homedir as homedir2 } from "os";
|
|
468
|
-
import { join as
|
|
479
|
+
import { join as join2 } from "path";
|
|
469
480
|
|
|
470
481
|
// ../../packages/core/src/platform/jsonl.ts
|
|
471
482
|
import {
|
|
@@ -480,7 +491,7 @@ import {
|
|
|
480
491
|
unlinkSync as unlinkSync2,
|
|
481
492
|
writeFileSync as writeFileSync2
|
|
482
493
|
} from "fs";
|
|
483
|
-
import { dirname as
|
|
494
|
+
import { dirname as dirname2 } from "path";
|
|
484
495
|
|
|
485
496
|
// ../../packages/core/src/platform/file-utils.ts
|
|
486
497
|
import { unlinkSync } from "fs";
|
|
@@ -508,7 +519,7 @@ function parseJsonlText(raw) {
|
|
|
508
519
|
}
|
|
509
520
|
var LOCK_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
510
521
|
function writeJsonlFile(filePath, entries) {
|
|
511
|
-
const dir =
|
|
522
|
+
const dir = dirname2(filePath);
|
|
512
523
|
mkdirSync3(dir, { recursive: true });
|
|
513
524
|
const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
514
525
|
const payload = `${entries.map((e) => JSON.stringify(e)).join(`
|
|
@@ -554,7 +565,7 @@ var _attachmentsCache = null;
|
|
|
554
565
|
function loadClaudeAttachments() {
|
|
555
566
|
if (_attachmentsCache)
|
|
556
567
|
return _attachmentsCache;
|
|
557
|
-
const raw = readFileSync3(
|
|
568
|
+
const raw = readFileSync3(join2(FIXTURES_DIR, "claude-attachments.jsonl"), "utf8");
|
|
558
569
|
const lines = parseJsonlText(raw);
|
|
559
570
|
if (lines.length < 2) {
|
|
560
571
|
throw new Error(`loadClaudeAttachments: expected >=2 entries in claude-attachments.jsonl, got ${lines.length}`);
|
|
@@ -658,15 +669,15 @@ function writeClaudeRollout(opts) {
|
|
|
658
669
|
});
|
|
659
670
|
lastUuid = assistantUuid;
|
|
660
671
|
}
|
|
661
|
-
const projectsDir =
|
|
662
|
-
const path =
|
|
672
|
+
const projectsDir = join2(homedir2(), ".claude", "projects", encodeClaudeCwd(opts.cwd));
|
|
673
|
+
const path = join2(projectsDir, `${sessionId}.jsonl`);
|
|
663
674
|
writeJsonlFile(path, lines);
|
|
664
675
|
logger.info({ sessionId, path, pairs: pairs.length }, "writeClaudeRollout: synthetic rollout placed");
|
|
665
676
|
return { sessionId, rolloutPath: path };
|
|
666
677
|
}
|
|
667
678
|
function repairPoisonedRollout(sessionId, cwd) {
|
|
668
679
|
try {
|
|
669
|
-
const path =
|
|
680
|
+
const path = join2(homedir2(), ".claude", "projects", encodeClaudeCwd(cwd), `${sessionId}.jsonl`);
|
|
670
681
|
if (!existsSync2(path))
|
|
671
682
|
return false;
|
|
672
683
|
const lines = readFileSync3(path, "utf8").split(`
|
|
@@ -737,12 +748,1175 @@ function isPoisonedAssistantEntry(entry) {
|
|
|
737
748
|
}
|
|
738
749
|
|
|
739
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";
|
|
740
921
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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
|
|
744
1918
|
function codexSessionsDir() {
|
|
745
|
-
return join4(
|
|
1919
|
+
return join4(hostedCodexHomePath(), "sessions");
|
|
746
1920
|
}
|
|
747
1921
|
var _shellCache = null;
|
|
748
1922
|
function loadCodexShell() {
|
|
@@ -765,7 +1939,7 @@ function loadCodexShell() {
|
|
|
765
1939
|
function uuidv7() {
|
|
766
1940
|
const ts = Date.now();
|
|
767
1941
|
const tsHex = ts.toString(16).padStart(12, "0");
|
|
768
|
-
const rand =
|
|
1942
|
+
const rand = randomBytes4(10);
|
|
769
1943
|
rand[0] = rand[0] & 15 | 112;
|
|
770
1944
|
rand[2] = rand[2] & 63 | 128;
|
|
771
1945
|
return [
|
|
@@ -807,12 +1981,12 @@ function patchEnvContext(envContext, cwd, currentDate, timezone) {
|
|
|
807
1981
|
}
|
|
808
1982
|
}
|
|
809
1983
|
function canonicalFilePath(path) {
|
|
810
|
-
const absolute =
|
|
1984
|
+
const absolute = resolve4(path);
|
|
811
1985
|
try {
|
|
812
|
-
return
|
|
1986
|
+
return realpathSync2(absolute);
|
|
813
1987
|
} catch {
|
|
814
1988
|
try {
|
|
815
|
-
return join4(
|
|
1989
|
+
return join4(realpathSync2(dirname4(absolute)), basename(absolute));
|
|
816
1990
|
} catch {
|
|
817
1991
|
return absolute;
|
|
818
1992
|
}
|
|
@@ -1220,6 +2394,6 @@ function formatDateBucket(d) {
|
|
|
1220
2394
|
return `${yyyy}/${mm}/${dd}`;
|
|
1221
2395
|
}
|
|
1222
2396
|
|
|
1223
|
-
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 };
|
|
1224
2398
|
|
|
1225
|
-
//# debugId=
|
|
2399
|
+
//# debugId=889664B432D1D24364756E2164756E21
|