negotium 0.1.21 → 0.1.23
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 +199 -203
- package/dist/agent-helpers.js.map +10 -10
- package/dist/cron.js +249 -223
- package/dist/cron.js.map +12 -12
- package/dist/hosted-agent.js +249 -253
- package/dist/hosted-agent.js.map +11 -11
- package/dist/main.js +409 -398
- package/dist/main.js.map +14 -14
- package/dist/runtime/scripts/browser-webauthn-policy.mjs +37 -0
- package/dist/runtime/scripts/mcp-patchright-http.mjs +8 -3
- package/dist/runtime/src/agents/claude-provider.ts +11 -5
- package/dist/runtime/src/agents/maestro-provider.ts +4 -1
- package/dist/runtime/src/agents/vault-tool-policy.ts +28 -4
- package/dist/runtime/src/platform/mcp-config.ts +2 -1
- package/dist/runtime/src/platform/playwright/manager.ts +18 -0
- package/dist/runtime/src/prompts/sessions/channel-system.md +1 -1
- package/dist/runtime/src/prompts/sessions/topic-system.md +1 -1
- package/dist/runtime/src/version.ts +1 -1
- package/dist/types/packages/core/src/agents/claude-provider.d.ts +1 -1
- package/dist/types/packages/core/src/agents/vault-tool-policy.d.ts +2 -1
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/hosted-agent.js
CHANGED
|
@@ -683,248 +683,6 @@ function isSensitivePath(filePath) {
|
|
|
683
683
|
return false;
|
|
684
684
|
}
|
|
685
685
|
|
|
686
|
-
// ../../packages/core/src/storage/vault.ts
|
|
687
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync5 } from "fs";
|
|
688
|
-
import { join as join5 } from "path";
|
|
689
|
-
|
|
690
|
-
// ../../packages/core/src/storage/sqlite.ts
|
|
691
|
-
var isBun = typeof process.versions.bun === "string";
|
|
692
|
-
var Database;
|
|
693
|
-
if (isBun) {
|
|
694
|
-
({ Database } = await import("bun:sqlite"));
|
|
695
|
-
} else {
|
|
696
|
-
const nodeSqliteSpecifier = ["node", "sqlite"].join(":");
|
|
697
|
-
const { DatabaseSync } = await import(nodeSqliteSpecifier);
|
|
698
|
-
|
|
699
|
-
class NodeDatabase {
|
|
700
|
-
#db;
|
|
701
|
-
constructor(path, options = {}) {
|
|
702
|
-
this.#db = options.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path);
|
|
703
|
-
}
|
|
704
|
-
query(sql) {
|
|
705
|
-
return this.#db.prepare(sql);
|
|
706
|
-
}
|
|
707
|
-
prepare(sql) {
|
|
708
|
-
return this.#db.prepare(sql);
|
|
709
|
-
}
|
|
710
|
-
exec(sql) {
|
|
711
|
-
this.#db.exec(sql);
|
|
712
|
-
}
|
|
713
|
-
run(sql, ...params) {
|
|
714
|
-
return this.#db.prepare(sql).run(...params);
|
|
715
|
-
}
|
|
716
|
-
transaction(fn) {
|
|
717
|
-
const run = (begin) => (...args) => {
|
|
718
|
-
this.#db.exec(begin);
|
|
719
|
-
try {
|
|
720
|
-
const result = fn(...args);
|
|
721
|
-
this.#db.exec("COMMIT");
|
|
722
|
-
return result;
|
|
723
|
-
} catch (err) {
|
|
724
|
-
this.#db.exec("ROLLBACK");
|
|
725
|
-
throw err;
|
|
726
|
-
}
|
|
727
|
-
};
|
|
728
|
-
const tx = run("BEGIN");
|
|
729
|
-
tx.deferred = run("BEGIN DEFERRED");
|
|
730
|
-
tx.immediate = run("BEGIN IMMEDIATE");
|
|
731
|
-
tx.exclusive = run("BEGIN EXCLUSIVE");
|
|
732
|
-
return tx;
|
|
733
|
-
}
|
|
734
|
-
close() {
|
|
735
|
-
this.#db.close();
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
Database = NodeDatabase;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
// ../../packages/core/src/storage/vault-crypto.ts
|
|
742
|
-
import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes2 } from "crypto";
|
|
743
|
-
var ENVELOPE_PREFIX = "otium-vault:v1:";
|
|
744
|
-
var IV_BYTES = 12;
|
|
745
|
-
var KEY_BYTES = 32;
|
|
746
|
-
function encryptionKey(masterKey = VAULT_MASTER_KEY) {
|
|
747
|
-
return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
|
|
748
|
-
}
|
|
749
|
-
function aad(userId, key) {
|
|
750
|
-
return Buffer.from(`${userId}\x00${key.toUpperCase()}`, "utf8");
|
|
751
|
-
}
|
|
752
|
-
function isEncryptedVaultValue(value) {
|
|
753
|
-
return value.startsWith(ENVELOPE_PREFIX);
|
|
754
|
-
}
|
|
755
|
-
function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
|
|
756
|
-
const iv = randomBytes2(IV_BYTES);
|
|
757
|
-
const cipher = createCipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
758
|
-
cipher.setAAD(aad(userId, key));
|
|
759
|
-
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
760
|
-
const tag = cipher.getAuthTag();
|
|
761
|
-
return `${ENVELOPE_PREFIX}${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
|
|
762
|
-
}
|
|
763
|
-
function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KEY) {
|
|
764
|
-
if (!isEncryptedVaultValue(storedValue)) {
|
|
765
|
-
return { value: storedValue, legacyPlaintext: true };
|
|
766
|
-
}
|
|
767
|
-
const encoded = storedValue.slice(ENVELOPE_PREFIX.length);
|
|
768
|
-
const [ivPart, ciphertextPart, tagPart, ...extra] = encoded.split(".");
|
|
769
|
-
if (!ivPart || ciphertextPart === undefined || !tagPart || extra.length > 0) {
|
|
770
|
-
throw new Error("Invalid encrypted vault value");
|
|
771
|
-
}
|
|
772
|
-
const iv = Buffer.from(ivPart, "base64url");
|
|
773
|
-
const ciphertext = Buffer.from(ciphertextPart, "base64url");
|
|
774
|
-
const tag = Buffer.from(tagPart, "base64url");
|
|
775
|
-
if (iv.length !== IV_BYTES || tag.length !== 16) {
|
|
776
|
-
throw new Error("Invalid encrypted vault value");
|
|
777
|
-
}
|
|
778
|
-
const decipher = createDecipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
779
|
-
decipher.setAAD(aad(userId, key));
|
|
780
|
-
decipher.setAuthTag(tag);
|
|
781
|
-
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
782
|
-
return { value: plaintext.toString("utf8"), legacyPlaintext: false };
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// ../../packages/core/src/storage/vault.ts
|
|
786
|
-
var vaultDb;
|
|
787
|
-
var vaultMasterKey = VAULT_MASTER_KEY;
|
|
788
|
-
function initializeVaultDatabase(database) {
|
|
789
|
-
database.exec("PRAGMA journal_mode = WAL");
|
|
790
|
-
database.exec("PRAGMA busy_timeout = 5000");
|
|
791
|
-
database.exec(`
|
|
792
|
-
CREATE TABLE IF NOT EXISTS vault (
|
|
793
|
-
user_id TEXT NOT NULL,
|
|
794
|
-
key TEXT NOT NULL,
|
|
795
|
-
value TEXT NOT NULL,
|
|
796
|
-
description TEXT NOT NULL DEFAULT '',
|
|
797
|
-
PRIMARY KEY (user_id, key)
|
|
798
|
-
)
|
|
799
|
-
`);
|
|
800
|
-
{
|
|
801
|
-
const cols = database.prepare("PRAGMA table_info(vault)").all();
|
|
802
|
-
const uid = cols.find((c) => c.name === "user_id");
|
|
803
|
-
if (uid && uid.type.toUpperCase() === "INTEGER") {
|
|
804
|
-
database.exec("BEGIN");
|
|
805
|
-
database.exec(`
|
|
806
|
-
CREATE TABLE vault_migrated (
|
|
807
|
-
user_id TEXT NOT NULL,
|
|
808
|
-
key TEXT NOT NULL,
|
|
809
|
-
value TEXT NOT NULL,
|
|
810
|
-
description TEXT NOT NULL DEFAULT '',
|
|
811
|
-
PRIMARY KEY (user_id, key)
|
|
812
|
-
)
|
|
813
|
-
`);
|
|
814
|
-
database.exec("INSERT INTO vault_migrated SELECT CAST(user_id AS TEXT), key, value, description FROM vault");
|
|
815
|
-
database.exec("DROP TABLE vault");
|
|
816
|
-
database.exec("ALTER TABLE vault_migrated RENAME TO vault");
|
|
817
|
-
database.exec("COMMIT");
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
function openVaultDatabase(dataDir) {
|
|
822
|
-
const path = join5(dataDir, "vault.db");
|
|
823
|
-
mkdirSync5(dataDir, { recursive: true });
|
|
824
|
-
const database = new Database(path, { create: true });
|
|
825
|
-
chmodSync2(path, 384);
|
|
826
|
-
initializeVaultDatabase(database);
|
|
827
|
-
return database;
|
|
828
|
-
}
|
|
829
|
-
function activeVaultDatabase() {
|
|
830
|
-
if (!vaultDb)
|
|
831
|
-
vaultDb = openVaultDatabase(DATA_DIR);
|
|
832
|
-
return vaultDb;
|
|
833
|
-
}
|
|
834
|
-
var VAULT_VALUE_MAX_BYTES = 64 * 1024;
|
|
835
|
-
function normalizeVaultKey(key) {
|
|
836
|
-
return key.trim().toUpperCase();
|
|
837
|
-
}
|
|
838
|
-
function decryptRow(userId, key, storedValue) {
|
|
839
|
-
const database = activeVaultDatabase();
|
|
840
|
-
const decoded = decryptVaultValue(userId, key, storedValue, vaultMasterKey);
|
|
841
|
-
if (decoded.legacyPlaintext) {
|
|
842
|
-
database.prepare("UPDATE vault SET value = ? WHERE user_id = ? AND key = ? AND value = ?").run(encryptVaultValue(userId, key, decoded.value, vaultMasterKey), userId, key, storedValue);
|
|
843
|
-
}
|
|
844
|
-
return decoded.value;
|
|
845
|
-
}
|
|
846
|
-
function vaultListWithValues(userId) {
|
|
847
|
-
const rows = activeVaultDatabase().prepare("SELECT key, description, value FROM vault WHERE user_id = ? ORDER BY key").all(userId);
|
|
848
|
-
return rows.map((row) => ({
|
|
849
|
-
key: row.key,
|
|
850
|
-
description: row.description,
|
|
851
|
-
value: decryptRow(userId, row.key, row.value)
|
|
852
|
-
}));
|
|
853
|
-
}
|
|
854
|
-
function vaultList(userId) {
|
|
855
|
-
return activeVaultDatabase().prepare("SELECT key, description FROM vault WHERE user_id = ? ORDER BY key").all(userId);
|
|
856
|
-
}
|
|
857
|
-
function vaultSubstituteDetailed(userId, text) {
|
|
858
|
-
const entries = new Map(vaultListWithValues(userId).map((entry) => [entry.key, entry.value]));
|
|
859
|
-
const usedKeys = new Set;
|
|
860
|
-
const substituted = text.replace(/\{\{([^}]+)\}\}/g, (match, rawKey) => {
|
|
861
|
-
const key = normalizeVaultKey(rawKey);
|
|
862
|
-
const value = entries.get(key);
|
|
863
|
-
if (value === undefined)
|
|
864
|
-
return match;
|
|
865
|
-
usedKeys.add(key);
|
|
866
|
-
return value;
|
|
867
|
-
});
|
|
868
|
-
return { text: substituted, usedKeys: [...usedKeys] };
|
|
869
|
-
}
|
|
870
|
-
function valueReferencesVaultKey(userId, value) {
|
|
871
|
-
const keys = new Set(vaultList(userId).map((entry) => entry.key));
|
|
872
|
-
const visit = (candidate) => {
|
|
873
|
-
if (typeof candidate === "string") {
|
|
874
|
-
for (const match of candidate.matchAll(/\{\{([^}]+)\}\}/g)) {
|
|
875
|
-
if (keys.has(normalizeVaultKey(match[1] ?? "")))
|
|
876
|
-
return true;
|
|
877
|
-
}
|
|
878
|
-
return false;
|
|
879
|
-
}
|
|
880
|
-
if (Array.isArray(candidate))
|
|
881
|
-
return candidate.some(visit);
|
|
882
|
-
if (candidate && typeof candidate === "object") {
|
|
883
|
-
return Object.values(candidate).some(visit);
|
|
884
|
-
}
|
|
885
|
-
return false;
|
|
886
|
-
};
|
|
887
|
-
return visit(value);
|
|
888
|
-
}
|
|
889
|
-
function encodedSecretForms(value) {
|
|
890
|
-
const forms = new Set([
|
|
891
|
-
value,
|
|
892
|
-
encodeURIComponent(value),
|
|
893
|
-
Buffer.from(value, "utf8").toString("base64"),
|
|
894
|
-
Buffer.from(value, "utf8").toString("base64url"),
|
|
895
|
-
Buffer.from(value, "utf8").toString("hex")
|
|
896
|
-
]);
|
|
897
|
-
forms.delete("");
|
|
898
|
-
return [...forms].sort((a, b) => b.length - a.length);
|
|
899
|
-
}
|
|
900
|
-
function redactVaultSecrets(userId, text) {
|
|
901
|
-
const candidates = vaultListWithValues(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));
|
|
902
|
-
if (candidates.length === 0)
|
|
903
|
-
return text;
|
|
904
|
-
const candidatesByFirstCharacter = new Map;
|
|
905
|
-
for (const candidate of candidates) {
|
|
906
|
-
const first = candidate.form[0];
|
|
907
|
-
if (!first)
|
|
908
|
-
continue;
|
|
909
|
-
const bucket = candidatesByFirstCharacter.get(first) ?? [];
|
|
910
|
-
bucket.push(candidate);
|
|
911
|
-
candidatesByFirstCharacter.set(first, bucket);
|
|
912
|
-
}
|
|
913
|
-
let redacted = "";
|
|
914
|
-
let offset = 0;
|
|
915
|
-
while (offset < text.length) {
|
|
916
|
-
const match = candidatesByFirstCharacter.get(text[offset] ?? "")?.find((candidate) => text.startsWith(candidate.form, offset));
|
|
917
|
-
if (!match) {
|
|
918
|
-
redacted += text[offset];
|
|
919
|
-
offset += 1;
|
|
920
|
-
continue;
|
|
921
|
-
}
|
|
922
|
-
redacted += `[REDACTED:${match.key}]`;
|
|
923
|
-
offset += match.form.length;
|
|
924
|
-
}
|
|
925
|
-
return redacted;
|
|
926
|
-
}
|
|
927
|
-
|
|
928
686
|
// ../../packages/core/src/agents/vault-tool-policy.ts
|
|
929
687
|
var SENSITIVE_RUNTIME_NAMES = [
|
|
930
688
|
"vault.db",
|
|
@@ -932,6 +690,15 @@ var SENSITIVE_RUNTIME_NAMES = [
|
|
|
932
690
|
"runtime-mcp-secret",
|
|
933
691
|
"sessions.db"
|
|
934
692
|
];
|
|
693
|
+
var DIRECT_VAULT_EXECUTION_TOOLS = new Set(["Bash", "WebFetch"]);
|
|
694
|
+
function leafToolName(toolName) {
|
|
695
|
+
const parts = toolName.split("__");
|
|
696
|
+
return parts.at(-1) ?? toolName;
|
|
697
|
+
}
|
|
698
|
+
function shouldSubstituteVaultToolInput(toolName) {
|
|
699
|
+
const leaf = leafToolName(toolName);
|
|
700
|
+
return leaf.startsWith("browser_") || DIRECT_VAULT_EXECUTION_TOOLS.has(leaf);
|
|
701
|
+
}
|
|
935
702
|
function createVaultToolPolicy(host) {
|
|
936
703
|
function isVaultBrokerTool(toolName) {
|
|
937
704
|
return toolName.includes("vault_run") || toolName.includes("vault_http_request");
|
|
@@ -951,11 +718,14 @@ function createVaultToolPolicy(host) {
|
|
|
951
718
|
return false;
|
|
952
719
|
}
|
|
953
720
|
function shouldRedirectVaultTool(userId, toolName, input) {
|
|
954
|
-
return
|
|
721
|
+
return false;
|
|
955
722
|
}
|
|
956
723
|
return { isVaultBrokerTool, referencesRuntimeSecretStorage, shouldRedirectVaultTool };
|
|
957
724
|
}
|
|
958
|
-
var defaultVaultToolPolicy = createVaultToolPolicy({
|
|
725
|
+
var defaultVaultToolPolicy = createVaultToolPolicy({
|
|
726
|
+
isSensitivePath,
|
|
727
|
+
valueReferencesVaultKey: () => false
|
|
728
|
+
});
|
|
959
729
|
var isVaultBrokerTool = defaultVaultToolPolicy.isVaultBrokerTool;
|
|
960
730
|
var referencesRuntimeSecretStorage = defaultVaultToolPolicy.referencesRuntimeSecretStorage;
|
|
961
731
|
var shouldRedirectVaultTool = defaultVaultToolPolicy.shouldRedirectVaultTool;
|
|
@@ -1011,7 +781,7 @@ function peerSessionBridgeIpcEnv() {
|
|
|
1011
781
|
|
|
1012
782
|
// ../../packages/core/src/platform/background-bash/manager.ts
|
|
1013
783
|
import { execFileSync, spawn } from "child_process";
|
|
1014
|
-
import { randomBytes as
|
|
784
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
1015
785
|
|
|
1016
786
|
// ../../packages/core/src/platform/delay.ts
|
|
1017
787
|
var delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -1039,8 +809,8 @@ function createBackgroundBashManager(options = {}) {
|
|
|
1039
809
|
const usedPorts = new Set;
|
|
1040
810
|
const spawning = new Map;
|
|
1041
811
|
const knownContexts = new Map;
|
|
1042
|
-
const runtimeCapability = options.capability ??
|
|
1043
|
-
const runtimeServerId = options.serverId ??
|
|
812
|
+
const runtimeCapability = options.capability ?? randomBytes2(32).toString("hex");
|
|
813
|
+
const runtimeServerId = options.serverId ?? randomBytes2(16).toString("hex");
|
|
1044
814
|
const serverFile = options.serverFile ?? BACKGROUND_BASH_SERVER;
|
|
1045
815
|
const basePort = options.basePort ?? BG_BASH_BASE_PORT;
|
|
1046
816
|
const maxPort = options.maxPort ?? BG_BASH_MAX_PORT;
|
|
@@ -1468,7 +1238,9 @@ var MCP_CATALOG = {
|
|
|
1468
1238
|
vault: {
|
|
1469
1239
|
...commonRuntimeMcpPolicy("vault"),
|
|
1470
1240
|
build({ userId, agent }) {
|
|
1471
|
-
const args = [`--user-id=${userId}
|
|
1241
|
+
const args = [`--user-id=${userId}`];
|
|
1242
|
+
if (agent !== "codex")
|
|
1243
|
+
args.push("--list-only=true");
|
|
1472
1244
|
return buildStdioMcpServer(agent, VAULT_SERVER, args);
|
|
1473
1245
|
}
|
|
1474
1246
|
}
|
|
@@ -1686,6 +1458,226 @@ function getMcpServersForQuery(opts) {
|
|
|
1686
1458
|
});
|
|
1687
1459
|
}
|
|
1688
1460
|
|
|
1461
|
+
// ../../packages/core/src/storage/vault.ts
|
|
1462
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync5 } from "fs";
|
|
1463
|
+
import { join as join5 } from "path";
|
|
1464
|
+
|
|
1465
|
+
// ../../packages/core/src/storage/sqlite.ts
|
|
1466
|
+
var isBun = typeof process.versions.bun === "string";
|
|
1467
|
+
var Database;
|
|
1468
|
+
if (isBun) {
|
|
1469
|
+
({ Database } = await import("bun:sqlite"));
|
|
1470
|
+
} else {
|
|
1471
|
+
const nodeSqliteSpecifier = ["node", "sqlite"].join(":");
|
|
1472
|
+
const { DatabaseSync } = await import(nodeSqliteSpecifier);
|
|
1473
|
+
|
|
1474
|
+
class NodeDatabase {
|
|
1475
|
+
#db;
|
|
1476
|
+
constructor(path, options = {}) {
|
|
1477
|
+
this.#db = options.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path);
|
|
1478
|
+
}
|
|
1479
|
+
query(sql) {
|
|
1480
|
+
return this.#db.prepare(sql);
|
|
1481
|
+
}
|
|
1482
|
+
prepare(sql) {
|
|
1483
|
+
return this.#db.prepare(sql);
|
|
1484
|
+
}
|
|
1485
|
+
exec(sql) {
|
|
1486
|
+
this.#db.exec(sql);
|
|
1487
|
+
}
|
|
1488
|
+
run(sql, ...params) {
|
|
1489
|
+
return this.#db.prepare(sql).run(...params);
|
|
1490
|
+
}
|
|
1491
|
+
transaction(fn) {
|
|
1492
|
+
const run = (begin) => (...args) => {
|
|
1493
|
+
this.#db.exec(begin);
|
|
1494
|
+
try {
|
|
1495
|
+
const result = fn(...args);
|
|
1496
|
+
this.#db.exec("COMMIT");
|
|
1497
|
+
return result;
|
|
1498
|
+
} catch (err) {
|
|
1499
|
+
this.#db.exec("ROLLBACK");
|
|
1500
|
+
throw err;
|
|
1501
|
+
}
|
|
1502
|
+
};
|
|
1503
|
+
const tx = run("BEGIN");
|
|
1504
|
+
tx.deferred = run("BEGIN DEFERRED");
|
|
1505
|
+
tx.immediate = run("BEGIN IMMEDIATE");
|
|
1506
|
+
tx.exclusive = run("BEGIN EXCLUSIVE");
|
|
1507
|
+
return tx;
|
|
1508
|
+
}
|
|
1509
|
+
close() {
|
|
1510
|
+
this.#db.close();
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
Database = NodeDatabase;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
// ../../packages/core/src/storage/vault-crypto.ts
|
|
1517
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes3 } from "crypto";
|
|
1518
|
+
var ENVELOPE_PREFIX = "otium-vault:v1:";
|
|
1519
|
+
var IV_BYTES = 12;
|
|
1520
|
+
var KEY_BYTES = 32;
|
|
1521
|
+
function encryptionKey(masterKey = VAULT_MASTER_KEY) {
|
|
1522
|
+
return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
|
|
1523
|
+
}
|
|
1524
|
+
function aad(userId, key) {
|
|
1525
|
+
return Buffer.from(`${userId}\x00${key.toUpperCase()}`, "utf8");
|
|
1526
|
+
}
|
|
1527
|
+
function isEncryptedVaultValue(value) {
|
|
1528
|
+
return value.startsWith(ENVELOPE_PREFIX);
|
|
1529
|
+
}
|
|
1530
|
+
function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
|
|
1531
|
+
const iv = randomBytes3(IV_BYTES);
|
|
1532
|
+
const cipher = createCipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
1533
|
+
cipher.setAAD(aad(userId, key));
|
|
1534
|
+
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
1535
|
+
const tag = cipher.getAuthTag();
|
|
1536
|
+
return `${ENVELOPE_PREFIX}${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
|
|
1537
|
+
}
|
|
1538
|
+
function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KEY) {
|
|
1539
|
+
if (!isEncryptedVaultValue(storedValue)) {
|
|
1540
|
+
return { value: storedValue, legacyPlaintext: true };
|
|
1541
|
+
}
|
|
1542
|
+
const encoded = storedValue.slice(ENVELOPE_PREFIX.length);
|
|
1543
|
+
const [ivPart, ciphertextPart, tagPart, ...extra] = encoded.split(".");
|
|
1544
|
+
if (!ivPart || ciphertextPart === undefined || !tagPart || extra.length > 0) {
|
|
1545
|
+
throw new Error("Invalid encrypted vault value");
|
|
1546
|
+
}
|
|
1547
|
+
const iv = Buffer.from(ivPart, "base64url");
|
|
1548
|
+
const ciphertext = Buffer.from(ciphertextPart, "base64url");
|
|
1549
|
+
const tag = Buffer.from(tagPart, "base64url");
|
|
1550
|
+
if (iv.length !== IV_BYTES || tag.length !== 16) {
|
|
1551
|
+
throw new Error("Invalid encrypted vault value");
|
|
1552
|
+
}
|
|
1553
|
+
const decipher = createDecipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
1554
|
+
decipher.setAAD(aad(userId, key));
|
|
1555
|
+
decipher.setAuthTag(tag);
|
|
1556
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
1557
|
+
return { value: plaintext.toString("utf8"), legacyPlaintext: false };
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
// ../../packages/core/src/storage/vault.ts
|
|
1561
|
+
var vaultDb;
|
|
1562
|
+
var vaultMasterKey = VAULT_MASTER_KEY;
|
|
1563
|
+
function initializeVaultDatabase(database) {
|
|
1564
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
1565
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
1566
|
+
database.exec(`
|
|
1567
|
+
CREATE TABLE IF NOT EXISTS vault (
|
|
1568
|
+
user_id TEXT NOT NULL,
|
|
1569
|
+
key TEXT NOT NULL,
|
|
1570
|
+
value TEXT NOT NULL,
|
|
1571
|
+
description TEXT NOT NULL DEFAULT '',
|
|
1572
|
+
PRIMARY KEY (user_id, key)
|
|
1573
|
+
)
|
|
1574
|
+
`);
|
|
1575
|
+
{
|
|
1576
|
+
const cols = database.prepare("PRAGMA table_info(vault)").all();
|
|
1577
|
+
const uid = cols.find((c) => c.name === "user_id");
|
|
1578
|
+
if (uid && uid.type.toUpperCase() === "INTEGER") {
|
|
1579
|
+
database.exec("BEGIN");
|
|
1580
|
+
database.exec(`
|
|
1581
|
+
CREATE TABLE vault_migrated (
|
|
1582
|
+
user_id TEXT NOT NULL,
|
|
1583
|
+
key TEXT NOT NULL,
|
|
1584
|
+
value TEXT NOT NULL,
|
|
1585
|
+
description TEXT NOT NULL DEFAULT '',
|
|
1586
|
+
PRIMARY KEY (user_id, key)
|
|
1587
|
+
)
|
|
1588
|
+
`);
|
|
1589
|
+
database.exec("INSERT INTO vault_migrated SELECT CAST(user_id AS TEXT), key, value, description FROM vault");
|
|
1590
|
+
database.exec("DROP TABLE vault");
|
|
1591
|
+
database.exec("ALTER TABLE vault_migrated RENAME TO vault");
|
|
1592
|
+
database.exec("COMMIT");
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
function openVaultDatabase(dataDir) {
|
|
1597
|
+
const path = join5(dataDir, "vault.db");
|
|
1598
|
+
mkdirSync5(dataDir, { recursive: true });
|
|
1599
|
+
const database = new Database(path, { create: true });
|
|
1600
|
+
chmodSync2(path, 384);
|
|
1601
|
+
initializeVaultDatabase(database);
|
|
1602
|
+
return database;
|
|
1603
|
+
}
|
|
1604
|
+
function activeVaultDatabase() {
|
|
1605
|
+
if (!vaultDb)
|
|
1606
|
+
vaultDb = openVaultDatabase(DATA_DIR);
|
|
1607
|
+
return vaultDb;
|
|
1608
|
+
}
|
|
1609
|
+
var VAULT_VALUE_MAX_BYTES = 64 * 1024;
|
|
1610
|
+
function normalizeVaultKey(key) {
|
|
1611
|
+
return key.trim().toUpperCase();
|
|
1612
|
+
}
|
|
1613
|
+
function decryptRow(userId, key, storedValue) {
|
|
1614
|
+
const database = activeVaultDatabase();
|
|
1615
|
+
const decoded = decryptVaultValue(userId, key, storedValue, vaultMasterKey);
|
|
1616
|
+
if (decoded.legacyPlaintext) {
|
|
1617
|
+
database.prepare("UPDATE vault SET value = ? WHERE user_id = ? AND key = ? AND value = ?").run(encryptVaultValue(userId, key, decoded.value, vaultMasterKey), userId, key, storedValue);
|
|
1618
|
+
}
|
|
1619
|
+
return decoded.value;
|
|
1620
|
+
}
|
|
1621
|
+
function vaultListWithValues(userId) {
|
|
1622
|
+
const rows = activeVaultDatabase().prepare("SELECT key, description, value FROM vault WHERE user_id = ? ORDER BY key").all(userId);
|
|
1623
|
+
return rows.map((row) => ({
|
|
1624
|
+
key: row.key,
|
|
1625
|
+
description: row.description,
|
|
1626
|
+
value: decryptRow(userId, row.key, row.value)
|
|
1627
|
+
}));
|
|
1628
|
+
}
|
|
1629
|
+
function vaultSubstituteDetailed(userId, text) {
|
|
1630
|
+
const entries = new Map(vaultListWithValues(userId).map((entry) => [entry.key, entry.value]));
|
|
1631
|
+
const usedKeys = new Set;
|
|
1632
|
+
const substituted = text.replace(/\{\{([^}]+)\}\}/g, (match, rawKey) => {
|
|
1633
|
+
const key = normalizeVaultKey(rawKey);
|
|
1634
|
+
const value = entries.get(key);
|
|
1635
|
+
if (value === undefined)
|
|
1636
|
+
return match;
|
|
1637
|
+
usedKeys.add(key);
|
|
1638
|
+
return value;
|
|
1639
|
+
});
|
|
1640
|
+
return { text: substituted, usedKeys: [...usedKeys] };
|
|
1641
|
+
}
|
|
1642
|
+
function encodedSecretForms(value) {
|
|
1643
|
+
const forms = new Set([
|
|
1644
|
+
value,
|
|
1645
|
+
encodeURIComponent(value),
|
|
1646
|
+
Buffer.from(value, "utf8").toString("base64"),
|
|
1647
|
+
Buffer.from(value, "utf8").toString("base64url"),
|
|
1648
|
+
Buffer.from(value, "utf8").toString("hex")
|
|
1649
|
+
]);
|
|
1650
|
+
forms.delete("");
|
|
1651
|
+
return [...forms].sort((a, b) => b.length - a.length);
|
|
1652
|
+
}
|
|
1653
|
+
function redactVaultSecrets(userId, text) {
|
|
1654
|
+
const candidates = vaultListWithValues(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));
|
|
1655
|
+
if (candidates.length === 0)
|
|
1656
|
+
return text;
|
|
1657
|
+
const candidatesByFirstCharacter = new Map;
|
|
1658
|
+
for (const candidate of candidates) {
|
|
1659
|
+
const first = candidate.form[0];
|
|
1660
|
+
if (!first)
|
|
1661
|
+
continue;
|
|
1662
|
+
const bucket = candidatesByFirstCharacter.get(first) ?? [];
|
|
1663
|
+
bucket.push(candidate);
|
|
1664
|
+
candidatesByFirstCharacter.set(first, bucket);
|
|
1665
|
+
}
|
|
1666
|
+
let redacted = "";
|
|
1667
|
+
let offset = 0;
|
|
1668
|
+
while (offset < text.length) {
|
|
1669
|
+
const match = candidatesByFirstCharacter.get(text[offset] ?? "")?.find((candidate) => text.startsWith(candidate.form, offset));
|
|
1670
|
+
if (!match) {
|
|
1671
|
+
redacted += text[offset];
|
|
1672
|
+
offset += 1;
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1675
|
+
redacted += `[REDACTED:${match.key}]`;
|
|
1676
|
+
offset += match.form.length;
|
|
1677
|
+
}
|
|
1678
|
+
return redacted;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1689
1681
|
// ../../packages/core/src/agents/execution-host.ts
|
|
1690
1682
|
var defaultHost = {
|
|
1691
1683
|
getMcpServersForQuery,
|
|
@@ -1792,7 +1784,9 @@ var CLAUDE_DEFAULT_DISALLOWED_TOOLS = [
|
|
|
1792
1784
|
"TaskGet"
|
|
1793
1785
|
];
|
|
1794
1786
|
var CLAUDE_NATIVE_AGENT_TOOLS = ["Task", "Agent", "TaskOutput", "TaskStop"];
|
|
1795
|
-
function substituteClaudeToolInput(userId, input) {
|
|
1787
|
+
function substituteClaudeToolInput(userId, toolName, input) {
|
|
1788
|
+
if (!shouldSubstituteVaultToolInput(toolName))
|
|
1789
|
+
return input;
|
|
1796
1790
|
return deepMapStrings(input, (value) => substituteHostedSecrets(userId, value));
|
|
1797
1791
|
}
|
|
1798
1792
|
function buildClaudeDisallowedTools(extra = undefined) {
|
|
@@ -2033,7 +2027,7 @@ async function* claudeProvider(opts) {
|
|
|
2033
2027
|
};
|
|
2034
2028
|
}
|
|
2035
2029
|
const userId = opts.userId ?? "";
|
|
2036
|
-
const withVault = substituteClaudeToolInput(userId, tool_input);
|
|
2030
|
+
const withVault = substituteClaudeToolInput(userId, tool_name, tool_input);
|
|
2037
2031
|
if (JSON.stringify(withVault) === JSON.stringify(tool_input)) {
|
|
2038
2032
|
return { continue: true };
|
|
2039
2033
|
}
|
|
@@ -2265,7 +2259,7 @@ import { createRequire } from "module";
|
|
|
2265
2259
|
import { dirname as dirname4, join as join6 } from "path";
|
|
2266
2260
|
|
|
2267
2261
|
// ../../packages/core/src/version.ts
|
|
2268
|
-
var NEGOTIUM_VERSION = "0.1.
|
|
2262
|
+
var NEGOTIUM_VERSION = "0.1.23";
|
|
2269
2263
|
|
|
2270
2264
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
2271
2265
|
var NEGOTIUM_MODEL_CATALOG = "negotium-model-catalog.json";
|
|
@@ -3273,10 +3267,12 @@ function buildMaestroDisallowedTools(callerDisallowedTools = []) {
|
|
|
3273
3267
|
function buildVaultHook(userId) {
|
|
3274
3268
|
return {
|
|
3275
3269
|
name: "vault-guard",
|
|
3276
|
-
pre({ input }) {
|
|
3270
|
+
pre({ toolName, input }) {
|
|
3277
3271
|
if (referencesHostedSecretStorage(input)) {
|
|
3278
3272
|
return { decision: "block", error: "Runtime secret storage access is not permitted" };
|
|
3279
3273
|
}
|
|
3274
|
+
if (!shouldSubstituteVaultToolInput(toolName))
|
|
3275
|
+
return { decision: "allow" };
|
|
3280
3276
|
const substituted = deepMapStrings(input, (value) => substituteHostedSecrets(userId, value));
|
|
3281
3277
|
return JSON.stringify(substituted) === JSON.stringify(input) ? { decision: "allow" } : { decision: "modify", input: substituted };
|
|
3282
3278
|
},
|
|
@@ -3415,4 +3411,4 @@ export {
|
|
|
3415
3411
|
buildClaudeDisallowedTools
|
|
3416
3412
|
};
|
|
3417
3413
|
|
|
3418
|
-
//# debugId=
|
|
3414
|
+
//# debugId=CC21E70C5AF6A3D464756E2164756E21
|