negotium 0.1.20 → 0.1.22
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 +300 -207
- package/dist/agent-helpers.js.map +14 -12
- package/dist/cron.js +337 -253
- package/dist/cron.js.map +21 -20
- package/dist/hosted-agent.js +233 -250
- package/dist/hosted-agent.js.map +9 -9
- package/dist/main.js +494 -420
- package/dist/main.js.map +23 -22
- package/dist/prompts.js +98 -9
- package/dist/prompts.js.map +6 -5
- package/dist/runtime/scripts/browser-passkey-policy.mjs +36 -0
- package/dist/runtime/scripts/browser-webauthn-policy.mjs +37 -0
- package/dist/runtime/scripts/mcp-patchright-http.mjs +20 -5
- package/dist/runtime/src/agents/archiver.ts +0 -14
- package/dist/runtime/src/agents/idle-archiver.ts +21 -0
- package/dist/runtime/src/agents/mcp-tools/self-config.ts +1 -1
- package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +6 -1
- package/dist/runtime/src/agents/memory-archive-policy.ts +34 -0
- package/dist/runtime/src/agents/model-catalog.ts +84 -18
- package/dist/runtime/src/agents/vault-tool-policy.ts +13 -4
- package/dist/runtime/src/platform/mcp-config.ts +2 -1
- package/dist/runtime/src/platform/playwright/manager.ts +22 -2
- package/dist/runtime/src/prompts/builders.ts +36 -7
- 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/runtime/turn-runner.ts +2 -0
- package/dist/runtime/src/storage/topic-archive.ts +5 -2
- package/dist/runtime/src/topics/lifecycle.ts +2 -1
- package/dist/runtime/src/topics/session.ts +2 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/storage.js +23 -3
- package/dist/storage.js.map +5 -4
- package/dist/types/packages/core/src/agents/idle-archiver.d.ts +2 -0
- package/dist/types/packages/core/src/agents/memory-archive-policy.d.ts +14 -0
- package/dist/types/packages/core/src/agents/model-catalog.d.ts +77 -0
- package/dist/types/packages/core/src/agents/vault-tool-policy.d.ts +1 -1
- package/dist/types/packages/core/src/prompts/builders.d.ts +5 -1
- package/dist/types/packages/core/src/storage/topic-archive.d.ts +1 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +2 -2
package/dist/agent-helpers.js
CHANGED
|
@@ -1787,197 +1787,6 @@ function isSensitivePath(filePath) {
|
|
|
1787
1787
|
return false;
|
|
1788
1788
|
}
|
|
1789
1789
|
|
|
1790
|
-
// ../../packages/core/src/storage/vault.ts
|
|
1791
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7 } from "fs";
|
|
1792
|
-
import { join as join10 } from "path";
|
|
1793
|
-
|
|
1794
|
-
// ../../packages/core/src/storage/vault-crypto.ts
|
|
1795
|
-
import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes3 } from "crypto";
|
|
1796
|
-
var ENVELOPE_PREFIX = "otium-vault:v1:";
|
|
1797
|
-
var IV_BYTES = 12;
|
|
1798
|
-
var KEY_BYTES = 32;
|
|
1799
|
-
function encryptionKey(masterKey = VAULT_MASTER_KEY) {
|
|
1800
|
-
return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
|
|
1801
|
-
}
|
|
1802
|
-
function aad(userId, key) {
|
|
1803
|
-
return Buffer.from(`${userId}\x00${key.toUpperCase()}`, "utf8");
|
|
1804
|
-
}
|
|
1805
|
-
function isEncryptedVaultValue(value) {
|
|
1806
|
-
return value.startsWith(ENVELOPE_PREFIX);
|
|
1807
|
-
}
|
|
1808
|
-
function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
|
|
1809
|
-
const iv = randomBytes3(IV_BYTES);
|
|
1810
|
-
const cipher = createCipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
1811
|
-
cipher.setAAD(aad(userId, key));
|
|
1812
|
-
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
1813
|
-
const tag = cipher.getAuthTag();
|
|
1814
|
-
return `${ENVELOPE_PREFIX}${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
|
|
1815
|
-
}
|
|
1816
|
-
function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KEY) {
|
|
1817
|
-
if (!isEncryptedVaultValue(storedValue)) {
|
|
1818
|
-
return { value: storedValue, legacyPlaintext: true };
|
|
1819
|
-
}
|
|
1820
|
-
const encoded = storedValue.slice(ENVELOPE_PREFIX.length);
|
|
1821
|
-
const [ivPart, ciphertextPart, tagPart, ...extra] = encoded.split(".");
|
|
1822
|
-
if (!ivPart || ciphertextPart === undefined || !tagPart || extra.length > 0) {
|
|
1823
|
-
throw new Error("Invalid encrypted vault value");
|
|
1824
|
-
}
|
|
1825
|
-
const iv = Buffer.from(ivPart, "base64url");
|
|
1826
|
-
const ciphertext = Buffer.from(ciphertextPart, "base64url");
|
|
1827
|
-
const tag = Buffer.from(tagPart, "base64url");
|
|
1828
|
-
if (iv.length !== IV_BYTES || tag.length !== 16) {
|
|
1829
|
-
throw new Error("Invalid encrypted vault value");
|
|
1830
|
-
}
|
|
1831
|
-
const decipher = createDecipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
1832
|
-
decipher.setAAD(aad(userId, key));
|
|
1833
|
-
decipher.setAuthTag(tag);
|
|
1834
|
-
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
1835
|
-
return { value: plaintext.toString("utf8"), legacyPlaintext: false };
|
|
1836
|
-
}
|
|
1837
|
-
|
|
1838
|
-
// ../../packages/core/src/storage/vault.ts
|
|
1839
|
-
var vaultDb;
|
|
1840
|
-
var vaultMasterKey = VAULT_MASTER_KEY;
|
|
1841
|
-
function initializeVaultDatabase(database) {
|
|
1842
|
-
database.exec("PRAGMA journal_mode = WAL");
|
|
1843
|
-
database.exec("PRAGMA busy_timeout = 5000");
|
|
1844
|
-
database.exec(`
|
|
1845
|
-
CREATE TABLE IF NOT EXISTS vault (
|
|
1846
|
-
user_id TEXT NOT NULL,
|
|
1847
|
-
key TEXT NOT NULL,
|
|
1848
|
-
value TEXT NOT NULL,
|
|
1849
|
-
description TEXT NOT NULL DEFAULT '',
|
|
1850
|
-
PRIMARY KEY (user_id, key)
|
|
1851
|
-
)
|
|
1852
|
-
`);
|
|
1853
|
-
{
|
|
1854
|
-
const cols = database.prepare("PRAGMA table_info(vault)").all();
|
|
1855
|
-
const uid = cols.find((c) => c.name === "user_id");
|
|
1856
|
-
if (uid && uid.type.toUpperCase() === "INTEGER") {
|
|
1857
|
-
database.exec("BEGIN");
|
|
1858
|
-
database.exec(`
|
|
1859
|
-
CREATE TABLE vault_migrated (
|
|
1860
|
-
user_id TEXT NOT NULL,
|
|
1861
|
-
key TEXT NOT NULL,
|
|
1862
|
-
value TEXT NOT NULL,
|
|
1863
|
-
description TEXT NOT NULL DEFAULT '',
|
|
1864
|
-
PRIMARY KEY (user_id, key)
|
|
1865
|
-
)
|
|
1866
|
-
`);
|
|
1867
|
-
database.exec("INSERT INTO vault_migrated SELECT CAST(user_id AS TEXT), key, value, description FROM vault");
|
|
1868
|
-
database.exec("DROP TABLE vault");
|
|
1869
|
-
database.exec("ALTER TABLE vault_migrated RENAME TO vault");
|
|
1870
|
-
database.exec("COMMIT");
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
function openVaultDatabase(dataDir) {
|
|
1875
|
-
const path = join10(dataDir, "vault.db");
|
|
1876
|
-
mkdirSync7(dataDir, { recursive: true });
|
|
1877
|
-
const database = new Database(path, { create: true });
|
|
1878
|
-
chmodSync2(path, 384);
|
|
1879
|
-
initializeVaultDatabase(database);
|
|
1880
|
-
return database;
|
|
1881
|
-
}
|
|
1882
|
-
function activeVaultDatabase() {
|
|
1883
|
-
if (!vaultDb)
|
|
1884
|
-
vaultDb = openVaultDatabase(DATA_DIR);
|
|
1885
|
-
return vaultDb;
|
|
1886
|
-
}
|
|
1887
|
-
var VAULT_VALUE_MAX_BYTES = 64 * 1024;
|
|
1888
|
-
function normalizeVaultKey(key) {
|
|
1889
|
-
return key.trim().toUpperCase();
|
|
1890
|
-
}
|
|
1891
|
-
function decryptRow(userId, key, storedValue) {
|
|
1892
|
-
const database = activeVaultDatabase();
|
|
1893
|
-
const decoded = decryptVaultValue(userId, key, storedValue, vaultMasterKey);
|
|
1894
|
-
if (decoded.legacyPlaintext) {
|
|
1895
|
-
database.prepare("UPDATE vault SET value = ? WHERE user_id = ? AND key = ? AND value = ?").run(encryptVaultValue(userId, key, decoded.value, vaultMasterKey), userId, key, storedValue);
|
|
1896
|
-
}
|
|
1897
|
-
return decoded.value;
|
|
1898
|
-
}
|
|
1899
|
-
function vaultListWithValues(userId) {
|
|
1900
|
-
const rows = activeVaultDatabase().prepare("SELECT key, description, value FROM vault WHERE user_id = ? ORDER BY key").all(userId);
|
|
1901
|
-
return rows.map((row) => ({
|
|
1902
|
-
key: row.key,
|
|
1903
|
-
description: row.description,
|
|
1904
|
-
value: decryptRow(userId, row.key, row.value)
|
|
1905
|
-
}));
|
|
1906
|
-
}
|
|
1907
|
-
function vaultList(userId) {
|
|
1908
|
-
return activeVaultDatabase().prepare("SELECT key, description FROM vault WHERE user_id = ? ORDER BY key").all(userId);
|
|
1909
|
-
}
|
|
1910
|
-
function vaultSubstituteDetailed(userId, text) {
|
|
1911
|
-
const entries = new Map(vaultListWithValues(userId).map((entry) => [entry.key, entry.value]));
|
|
1912
|
-
const usedKeys = new Set;
|
|
1913
|
-
const substituted = text.replace(/\{\{([^}]+)\}\}/g, (match, rawKey) => {
|
|
1914
|
-
const key = normalizeVaultKey(rawKey);
|
|
1915
|
-
const value = entries.get(key);
|
|
1916
|
-
if (value === undefined)
|
|
1917
|
-
return match;
|
|
1918
|
-
usedKeys.add(key);
|
|
1919
|
-
return value;
|
|
1920
|
-
});
|
|
1921
|
-
return { text: substituted, usedKeys: [...usedKeys] };
|
|
1922
|
-
}
|
|
1923
|
-
function valueReferencesVaultKey(userId, value) {
|
|
1924
|
-
const keys = new Set(vaultList(userId).map((entry) => entry.key));
|
|
1925
|
-
const visit = (candidate) => {
|
|
1926
|
-
if (typeof candidate === "string") {
|
|
1927
|
-
for (const match of candidate.matchAll(/\{\{([^}]+)\}\}/g)) {
|
|
1928
|
-
if (keys.has(normalizeVaultKey(match[1] ?? "")))
|
|
1929
|
-
return true;
|
|
1930
|
-
}
|
|
1931
|
-
return false;
|
|
1932
|
-
}
|
|
1933
|
-
if (Array.isArray(candidate))
|
|
1934
|
-
return candidate.some(visit);
|
|
1935
|
-
if (candidate && typeof candidate === "object") {
|
|
1936
|
-
return Object.values(candidate).some(visit);
|
|
1937
|
-
}
|
|
1938
|
-
return false;
|
|
1939
|
-
};
|
|
1940
|
-
return visit(value);
|
|
1941
|
-
}
|
|
1942
|
-
function encodedSecretForms(value) {
|
|
1943
|
-
const forms = new Set([
|
|
1944
|
-
value,
|
|
1945
|
-
encodeURIComponent(value),
|
|
1946
|
-
Buffer.from(value, "utf8").toString("base64"),
|
|
1947
|
-
Buffer.from(value, "utf8").toString("base64url"),
|
|
1948
|
-
Buffer.from(value, "utf8").toString("hex")
|
|
1949
|
-
]);
|
|
1950
|
-
forms.delete("");
|
|
1951
|
-
return [...forms].sort((a, b) => b.length - a.length);
|
|
1952
|
-
}
|
|
1953
|
-
function redactVaultSecrets(userId, text) {
|
|
1954
|
-
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));
|
|
1955
|
-
if (candidates.length === 0)
|
|
1956
|
-
return text;
|
|
1957
|
-
const candidatesByFirstCharacter = new Map;
|
|
1958
|
-
for (const candidate of candidates) {
|
|
1959
|
-
const first = candidate.form[0];
|
|
1960
|
-
if (!first)
|
|
1961
|
-
continue;
|
|
1962
|
-
const bucket = candidatesByFirstCharacter.get(first) ?? [];
|
|
1963
|
-
bucket.push(candidate);
|
|
1964
|
-
candidatesByFirstCharacter.set(first, bucket);
|
|
1965
|
-
}
|
|
1966
|
-
let redacted = "";
|
|
1967
|
-
let offset = 0;
|
|
1968
|
-
while (offset < text.length) {
|
|
1969
|
-
const match = candidatesByFirstCharacter.get(text[offset] ?? "")?.find((candidate) => text.startsWith(candidate.form, offset));
|
|
1970
|
-
if (!match) {
|
|
1971
|
-
redacted += text[offset];
|
|
1972
|
-
offset += 1;
|
|
1973
|
-
continue;
|
|
1974
|
-
}
|
|
1975
|
-
redacted += `[REDACTED:${match.key}]`;
|
|
1976
|
-
offset += match.form.length;
|
|
1977
|
-
}
|
|
1978
|
-
return redacted;
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
1790
|
// ../../packages/core/src/agents/vault-tool-policy.ts
|
|
1982
1791
|
var SENSITIVE_RUNTIME_NAMES = [
|
|
1983
1792
|
"vault.db",
|
|
@@ -1985,7 +1794,7 @@ var SENSITIVE_RUNTIME_NAMES = [
|
|
|
1985
1794
|
"runtime-mcp-secret",
|
|
1986
1795
|
"sessions.db"
|
|
1987
1796
|
];
|
|
1988
|
-
var VAULT_BROKER_REDIRECT_ERROR = "Vault
|
|
1797
|
+
var VAULT_BROKER_REDIRECT_ERROR = "Vault broker redirection is disabled; use {{KEY}} directly in normal tool inputs.";
|
|
1989
1798
|
function createVaultToolPolicy(host) {
|
|
1990
1799
|
function isVaultBrokerTool(toolName) {
|
|
1991
1800
|
return toolName.includes("vault_run") || toolName.includes("vault_http_request");
|
|
@@ -2005,11 +1814,14 @@ function createVaultToolPolicy(host) {
|
|
|
2005
1814
|
return false;
|
|
2006
1815
|
}
|
|
2007
1816
|
function shouldRedirectVaultTool(userId, toolName, input) {
|
|
2008
|
-
return
|
|
1817
|
+
return false;
|
|
2009
1818
|
}
|
|
2010
1819
|
return { isVaultBrokerTool, referencesRuntimeSecretStorage, shouldRedirectVaultTool };
|
|
2011
1820
|
}
|
|
2012
|
-
var defaultVaultToolPolicy = createVaultToolPolicy({
|
|
1821
|
+
var defaultVaultToolPolicy = createVaultToolPolicy({
|
|
1822
|
+
isSensitivePath,
|
|
1823
|
+
valueReferencesVaultKey: () => false
|
|
1824
|
+
});
|
|
2013
1825
|
var isVaultBrokerTool = defaultVaultToolPolicy.isVaultBrokerTool;
|
|
2014
1826
|
var referencesRuntimeSecretStorage = defaultVaultToolPolicy.referencesRuntimeSecretStorage;
|
|
2015
1827
|
var shouldRedirectVaultTool = defaultVaultToolPolicy.shouldRedirectVaultTool;
|
|
@@ -2089,7 +1901,7 @@ function peerSessionBridgeIpcEnv() {
|
|
|
2089
1901
|
|
|
2090
1902
|
// ../../packages/core/src/platform/background-bash/manager.ts
|
|
2091
1903
|
import { execFileSync as execFileSync3, spawn } from "child_process";
|
|
2092
|
-
import { randomBytes as
|
|
1904
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
2093
1905
|
|
|
2094
1906
|
// ../../packages/core/src/platform/delay.ts
|
|
2095
1907
|
var delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -2117,8 +1929,8 @@ function createBackgroundBashManager(options = {}) {
|
|
|
2117
1929
|
const usedPorts = new Set;
|
|
2118
1930
|
const spawning = new Map;
|
|
2119
1931
|
const knownContexts = new Map;
|
|
2120
|
-
const runtimeCapability = options.capability ??
|
|
2121
|
-
const runtimeServerId = options.serverId ??
|
|
1932
|
+
const runtimeCapability = options.capability ?? randomBytes3(32).toString("hex");
|
|
1933
|
+
const runtimeServerId = options.serverId ?? randomBytes3(16).toString("hex");
|
|
2122
1934
|
const serverFile = options.serverFile ?? BACKGROUND_BASH_SERVER;
|
|
2123
1935
|
const basePort = options.basePort ?? BG_BASH_BASE_PORT;
|
|
2124
1936
|
const maxPort = options.maxPort ?? BG_BASH_MAX_PORT;
|
|
@@ -2546,7 +2358,9 @@ var MCP_CATALOG = {
|
|
|
2546
2358
|
vault: {
|
|
2547
2359
|
...commonRuntimeMcpPolicy("vault"),
|
|
2548
2360
|
build({ userId, agent }) {
|
|
2549
|
-
const args = [`--user-id=${userId}
|
|
2361
|
+
const args = [`--user-id=${userId}`];
|
|
2362
|
+
if (agent !== "codex")
|
|
2363
|
+
args.push("--list-only=true");
|
|
2550
2364
|
return buildStdioMcpServer(agent, VAULT_SERVER, args);
|
|
2551
2365
|
}
|
|
2552
2366
|
}
|
|
@@ -2764,6 +2578,175 @@ function getMcpServersForQuery(opts) {
|
|
|
2764
2578
|
});
|
|
2765
2579
|
}
|
|
2766
2580
|
|
|
2581
|
+
// ../../packages/core/src/storage/vault.ts
|
|
2582
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7 } from "fs";
|
|
2583
|
+
import { join as join10 } from "path";
|
|
2584
|
+
|
|
2585
|
+
// ../../packages/core/src/storage/vault-crypto.ts
|
|
2586
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes4 } from "crypto";
|
|
2587
|
+
var ENVELOPE_PREFIX = "otium-vault:v1:";
|
|
2588
|
+
var IV_BYTES = 12;
|
|
2589
|
+
var KEY_BYTES = 32;
|
|
2590
|
+
function encryptionKey(masterKey = VAULT_MASTER_KEY) {
|
|
2591
|
+
return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
|
|
2592
|
+
}
|
|
2593
|
+
function aad(userId, key) {
|
|
2594
|
+
return Buffer.from(`${userId}\x00${key.toUpperCase()}`, "utf8");
|
|
2595
|
+
}
|
|
2596
|
+
function isEncryptedVaultValue(value) {
|
|
2597
|
+
return value.startsWith(ENVELOPE_PREFIX);
|
|
2598
|
+
}
|
|
2599
|
+
function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
|
|
2600
|
+
const iv = randomBytes4(IV_BYTES);
|
|
2601
|
+
const cipher = createCipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
2602
|
+
cipher.setAAD(aad(userId, key));
|
|
2603
|
+
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
2604
|
+
const tag = cipher.getAuthTag();
|
|
2605
|
+
return `${ENVELOPE_PREFIX}${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
|
|
2606
|
+
}
|
|
2607
|
+
function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KEY) {
|
|
2608
|
+
if (!isEncryptedVaultValue(storedValue)) {
|
|
2609
|
+
return { value: storedValue, legacyPlaintext: true };
|
|
2610
|
+
}
|
|
2611
|
+
const encoded = storedValue.slice(ENVELOPE_PREFIX.length);
|
|
2612
|
+
const [ivPart, ciphertextPart, tagPart, ...extra] = encoded.split(".");
|
|
2613
|
+
if (!ivPart || ciphertextPart === undefined || !tagPart || extra.length > 0) {
|
|
2614
|
+
throw new Error("Invalid encrypted vault value");
|
|
2615
|
+
}
|
|
2616
|
+
const iv = Buffer.from(ivPart, "base64url");
|
|
2617
|
+
const ciphertext = Buffer.from(ciphertextPart, "base64url");
|
|
2618
|
+
const tag = Buffer.from(tagPart, "base64url");
|
|
2619
|
+
if (iv.length !== IV_BYTES || tag.length !== 16) {
|
|
2620
|
+
throw new Error("Invalid encrypted vault value");
|
|
2621
|
+
}
|
|
2622
|
+
const decipher = createDecipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
2623
|
+
decipher.setAAD(aad(userId, key));
|
|
2624
|
+
decipher.setAuthTag(tag);
|
|
2625
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
2626
|
+
return { value: plaintext.toString("utf8"), legacyPlaintext: false };
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
// ../../packages/core/src/storage/vault.ts
|
|
2630
|
+
var vaultDb;
|
|
2631
|
+
var vaultMasterKey = VAULT_MASTER_KEY;
|
|
2632
|
+
function initializeVaultDatabase(database) {
|
|
2633
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
2634
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
2635
|
+
database.exec(`
|
|
2636
|
+
CREATE TABLE IF NOT EXISTS vault (
|
|
2637
|
+
user_id TEXT NOT NULL,
|
|
2638
|
+
key TEXT NOT NULL,
|
|
2639
|
+
value TEXT NOT NULL,
|
|
2640
|
+
description TEXT NOT NULL DEFAULT '',
|
|
2641
|
+
PRIMARY KEY (user_id, key)
|
|
2642
|
+
)
|
|
2643
|
+
`);
|
|
2644
|
+
{
|
|
2645
|
+
const cols = database.prepare("PRAGMA table_info(vault)").all();
|
|
2646
|
+
const uid = cols.find((c) => c.name === "user_id");
|
|
2647
|
+
if (uid && uid.type.toUpperCase() === "INTEGER") {
|
|
2648
|
+
database.exec("BEGIN");
|
|
2649
|
+
database.exec(`
|
|
2650
|
+
CREATE TABLE vault_migrated (
|
|
2651
|
+
user_id TEXT NOT NULL,
|
|
2652
|
+
key TEXT NOT NULL,
|
|
2653
|
+
value TEXT NOT NULL,
|
|
2654
|
+
description TEXT NOT NULL DEFAULT '',
|
|
2655
|
+
PRIMARY KEY (user_id, key)
|
|
2656
|
+
)
|
|
2657
|
+
`);
|
|
2658
|
+
database.exec("INSERT INTO vault_migrated SELECT CAST(user_id AS TEXT), key, value, description FROM vault");
|
|
2659
|
+
database.exec("DROP TABLE vault");
|
|
2660
|
+
database.exec("ALTER TABLE vault_migrated RENAME TO vault");
|
|
2661
|
+
database.exec("COMMIT");
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
function openVaultDatabase(dataDir) {
|
|
2666
|
+
const path = join10(dataDir, "vault.db");
|
|
2667
|
+
mkdirSync7(dataDir, { recursive: true });
|
|
2668
|
+
const database = new Database(path, { create: true });
|
|
2669
|
+
chmodSync2(path, 384);
|
|
2670
|
+
initializeVaultDatabase(database);
|
|
2671
|
+
return database;
|
|
2672
|
+
}
|
|
2673
|
+
function activeVaultDatabase() {
|
|
2674
|
+
if (!vaultDb)
|
|
2675
|
+
vaultDb = openVaultDatabase(DATA_DIR);
|
|
2676
|
+
return vaultDb;
|
|
2677
|
+
}
|
|
2678
|
+
var VAULT_VALUE_MAX_BYTES = 64 * 1024;
|
|
2679
|
+
function normalizeVaultKey(key) {
|
|
2680
|
+
return key.trim().toUpperCase();
|
|
2681
|
+
}
|
|
2682
|
+
function decryptRow(userId, key, storedValue) {
|
|
2683
|
+
const database = activeVaultDatabase();
|
|
2684
|
+
const decoded = decryptVaultValue(userId, key, storedValue, vaultMasterKey);
|
|
2685
|
+
if (decoded.legacyPlaintext) {
|
|
2686
|
+
database.prepare("UPDATE vault SET value = ? WHERE user_id = ? AND key = ? AND value = ?").run(encryptVaultValue(userId, key, decoded.value, vaultMasterKey), userId, key, storedValue);
|
|
2687
|
+
}
|
|
2688
|
+
return decoded.value;
|
|
2689
|
+
}
|
|
2690
|
+
function vaultListWithValues(userId) {
|
|
2691
|
+
const rows = activeVaultDatabase().prepare("SELECT key, description, value FROM vault WHERE user_id = ? ORDER BY key").all(userId);
|
|
2692
|
+
return rows.map((row) => ({
|
|
2693
|
+
key: row.key,
|
|
2694
|
+
description: row.description,
|
|
2695
|
+
value: decryptRow(userId, row.key, row.value)
|
|
2696
|
+
}));
|
|
2697
|
+
}
|
|
2698
|
+
function vaultSubstituteDetailed(userId, text) {
|
|
2699
|
+
const entries = new Map(vaultListWithValues(userId).map((entry) => [entry.key, entry.value]));
|
|
2700
|
+
const usedKeys = new Set;
|
|
2701
|
+
const substituted = text.replace(/\{\{([^}]+)\}\}/g, (match, rawKey) => {
|
|
2702
|
+
const key = normalizeVaultKey(rawKey);
|
|
2703
|
+
const value = entries.get(key);
|
|
2704
|
+
if (value === undefined)
|
|
2705
|
+
return match;
|
|
2706
|
+
usedKeys.add(key);
|
|
2707
|
+
return value;
|
|
2708
|
+
});
|
|
2709
|
+
return { text: substituted, usedKeys: [...usedKeys] };
|
|
2710
|
+
}
|
|
2711
|
+
function encodedSecretForms(value) {
|
|
2712
|
+
const forms = new Set([
|
|
2713
|
+
value,
|
|
2714
|
+
encodeURIComponent(value),
|
|
2715
|
+
Buffer.from(value, "utf8").toString("base64"),
|
|
2716
|
+
Buffer.from(value, "utf8").toString("base64url"),
|
|
2717
|
+
Buffer.from(value, "utf8").toString("hex")
|
|
2718
|
+
]);
|
|
2719
|
+
forms.delete("");
|
|
2720
|
+
return [...forms].sort((a, b) => b.length - a.length);
|
|
2721
|
+
}
|
|
2722
|
+
function redactVaultSecrets(userId, text) {
|
|
2723
|
+
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));
|
|
2724
|
+
if (candidates.length === 0)
|
|
2725
|
+
return text;
|
|
2726
|
+
const candidatesByFirstCharacter = new Map;
|
|
2727
|
+
for (const candidate of candidates) {
|
|
2728
|
+
const first = candidate.form[0];
|
|
2729
|
+
if (!first)
|
|
2730
|
+
continue;
|
|
2731
|
+
const bucket = candidatesByFirstCharacter.get(first) ?? [];
|
|
2732
|
+
bucket.push(candidate);
|
|
2733
|
+
candidatesByFirstCharacter.set(first, bucket);
|
|
2734
|
+
}
|
|
2735
|
+
let redacted = "";
|
|
2736
|
+
let offset = 0;
|
|
2737
|
+
while (offset < text.length) {
|
|
2738
|
+
const match = candidatesByFirstCharacter.get(text[offset] ?? "")?.find((candidate) => text.startsWith(candidate.form, offset));
|
|
2739
|
+
if (!match) {
|
|
2740
|
+
redacted += text[offset];
|
|
2741
|
+
offset += 1;
|
|
2742
|
+
continue;
|
|
2743
|
+
}
|
|
2744
|
+
redacted += `[REDACTED:${match.key}]`;
|
|
2745
|
+
offset += match.form.length;
|
|
2746
|
+
}
|
|
2747
|
+
return redacted;
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2767
2750
|
// ../../packages/core/src/agents/execution-host.ts
|
|
2768
2751
|
var defaultHost = {
|
|
2769
2752
|
getMcpServersForQuery,
|
|
@@ -3321,7 +3304,7 @@ import { createRequire } from "module";
|
|
|
3321
3304
|
import { dirname as dirname6, join as join11 } from "path";
|
|
3322
3305
|
|
|
3323
3306
|
// ../../packages/core/src/version.ts
|
|
3324
|
-
var NEGOTIUM_VERSION = "0.1.
|
|
3307
|
+
var NEGOTIUM_VERSION = "0.1.22";
|
|
3325
3308
|
|
|
3326
3309
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
3327
3310
|
var NEGOTIUM_MODEL_CATALOG = "negotium-model-catalog.json";
|
|
@@ -4418,6 +4401,86 @@ var WsHub = {
|
|
|
4418
4401
|
// ../../packages/core/src/prompts/builders.ts
|
|
4419
4402
|
import { readFileSync as readFileSync9 } from "fs";
|
|
4420
4403
|
import { resolve as resolve5 } from "path";
|
|
4404
|
+
|
|
4405
|
+
// ../../packages/core/src/agents/model-catalog.ts
|
|
4406
|
+
var CODEX_PRO_20X_COST = "ChatGPT Pro 20x subscription: $200/month";
|
|
4407
|
+
var CODEX_COMMUNITY_WEEKLY = "Community plan-level observation: roughly 2\u20134B raw/cached tokens per week; fresh-input equivalent is much lower and unstable (low confidence)";
|
|
4408
|
+
var CLAUDE_MAX_20X_COST = "Claude Max 20x subscription: $200/month";
|
|
4409
|
+
var CLAUDE_COMMUNITY_SESSION = "Community observations vary from roughly 220\u2013250K locally displayed tokens per 5-hour session to billions of cache-heavy raw tokens per week; calibrated reports value a full weekly allowance around $680\u2013$1,900 at API rates. Recent heavy-model reports reach the weekly cap after about 4\u20135 full sessions (low confidence; not a token cap)";
|
|
4410
|
+
var SELECTABLE_MODELS = [
|
|
4411
|
+
{
|
|
4412
|
+
model: "gpt-5.6-sol",
|
|
4413
|
+
agent: "codex",
|
|
4414
|
+
description: "Highest-capability Codex route for the hardest agentic coding work.",
|
|
4415
|
+
intelligenceTier: "fable",
|
|
4416
|
+
routingSummary: "hardest coding work; 5x Codex quota cost",
|
|
4417
|
+
accessCost: CODEX_PRO_20X_COST,
|
|
4418
|
+
marginalTokenCost: "Codex credits: $5/M uncached input, $0.50/M cached input, $30/M output",
|
|
4419
|
+
estimatedUsage: `Official Pro 20x range: 300\u20131,800 local messages per 5 hours; quota weight 5x Luna. ${CODEX_COMMUNITY_WEEKLY}`
|
|
4420
|
+
},
|
|
4421
|
+
{
|
|
4422
|
+
model: "gpt-5.6-terra",
|
|
4423
|
+
agent: "codex",
|
|
4424
|
+
description: "High-capability Codex route for complex coding and reasoning.",
|
|
4425
|
+
intelligenceTier: "opus",
|
|
4426
|
+
routingSummary: "complex coding and reasoning; 2.5x Codex quota cost",
|
|
4427
|
+
accessCost: CODEX_PRO_20X_COST,
|
|
4428
|
+
marginalTokenCost: "Codex credits: $2.50/M uncached input, $0.25/M cached input, $15/M output",
|
|
4429
|
+
estimatedUsage: `Official Pro 20x range: 400\u20132,200 local messages per 5 hours; quota weight 2.5x Luna. ${CODEX_COMMUNITY_WEEKLY}`
|
|
4430
|
+
},
|
|
4431
|
+
{
|
|
4432
|
+
model: "gpt-5.6-luna",
|
|
4433
|
+
agent: "codex",
|
|
4434
|
+
description: "Default Codex route with strong everyday coding intelligence.",
|
|
4435
|
+
intelligenceTier: "sonnet",
|
|
4436
|
+
routingSummary: "everyday coding default; lowest Codex quota cost (1x)",
|
|
4437
|
+
accessCost: CODEX_PRO_20X_COST,
|
|
4438
|
+
marginalTokenCost: "Codex credits: $1/M uncached input, $0.10/M cached input, $6/M output",
|
|
4439
|
+
estimatedUsage: `Official Pro 20x range: 1,000\u20135,600 local messages per 5 hours; lowest Codex quota weight (1x). ${CODEX_COMMUNITY_WEEKLY}`
|
|
4440
|
+
},
|
|
4441
|
+
{
|
|
4442
|
+
model: "fable",
|
|
4443
|
+
agent: "claude",
|
|
4444
|
+
description: "Highest-capability Claude route for the hardest and longest-running tasks.",
|
|
4445
|
+
intelligenceTier: "fable",
|
|
4446
|
+
routingSummary: "hardest long-running work; highest Claude cost; explicit request only",
|
|
4447
|
+
accessCost: CLAUDE_MAX_20X_COST,
|
|
4448
|
+
marginalTokenCost: "Claude API/extra usage: $10/M input, $12.50/M cache write, $1/M cache read, $50/M output",
|
|
4449
|
+
estimatedUsage: `${CLAUDE_COMMUNITY_SESSION}; Fable drains weighted quota fastest, so use only on explicit user request. No stable per-model token cap is published.`
|
|
4450
|
+
},
|
|
4451
|
+
{
|
|
4452
|
+
model: "opus",
|
|
4453
|
+
agent: "claude",
|
|
4454
|
+
description: "High-capability Claude route for complex reasoning and tool-heavy work.",
|
|
4455
|
+
intelligenceTier: "opus",
|
|
4456
|
+
routingSummary: "complex reasoning and tool-heavy work; about 2.5x Sonnet marginal cost",
|
|
4457
|
+
accessCost: CLAUDE_MAX_20X_COST,
|
|
4458
|
+
marginalTokenCost: "Claude API/extra usage: $5/M input, $6.25/M cache write, $0.50/M cache read, $25/M output",
|
|
4459
|
+
estimatedUsage: `${CLAUDE_COMMUNITY_SESSION}; Opus uses the shared all-model weekly pool more quickly than Sonnet. No stable per-model token cap is published.`
|
|
4460
|
+
},
|
|
4461
|
+
{
|
|
4462
|
+
model: "sonnet",
|
|
4463
|
+
agent: "claude",
|
|
4464
|
+
description: "Default Claude route for capable, efficient everyday work.",
|
|
4465
|
+
intelligenceTier: "sonnet",
|
|
4466
|
+
routingSummary: "capable everyday default; lowest Claude model cost",
|
|
4467
|
+
accessCost: CLAUDE_MAX_20X_COST,
|
|
4468
|
+
marginalTokenCost: "Claude API/extra usage introductory rate: $2/M input, $2.50/M cache write, $0.20/M cache read, $10/M output through 2026-08-31; then $3/M input and $15/M output",
|
|
4469
|
+
estimatedUsage: `${CLAUDE_COMMUNITY_SESSION}; Sonnet also has a separate weekly allowance and normally provides the highest Claude throughput. No stable weekly token cap is published.`
|
|
4470
|
+
},
|
|
4471
|
+
{
|
|
4472
|
+
model: "deepseek-pro",
|
|
4473
|
+
agent: "maestro",
|
|
4474
|
+
description: "API-priced Sonnet-level route for cost-efficient everyday work.",
|
|
4475
|
+
intelligenceTier: "sonnet",
|
|
4476
|
+
routingSummary: "cost-efficient everyday work; pay-per-token and cheapest route",
|
|
4477
|
+
accessCost: "DeepSeek V4 Pro pay-as-you-go API; no monthly subscription required",
|
|
4478
|
+
marginalTokenCost: "DeepSeek API: $0.435/M uncached input, $0.003625/M cached input, $0.87/M output",
|
|
4479
|
+
estimatedUsage: "No subscription token cap; pay per token. Official account concurrency limit is 500 requests."
|
|
4480
|
+
}
|
|
4481
|
+
];
|
|
4482
|
+
|
|
4483
|
+
// ../../packages/core/src/prompts/builders.ts
|
|
4421
4484
|
var PROMPTS_DIR = resolve5(PROJECT_ROOT, "src/prompts");
|
|
4422
4485
|
var SESSIONS_DIR = resolve5(PROMPTS_DIR, "sessions");
|
|
4423
4486
|
function loadAgentPrompt(filename) {
|
|
@@ -5151,7 +5214,6 @@ function ensurePersonalGeneral(userId) {
|
|
|
5151
5214
|
|
|
5152
5215
|
// ../../packages/core/src/agents/archiver.ts
|
|
5153
5216
|
var MAX_BRIEF_ENTRIES = 8;
|
|
5154
|
-
var MIN_ARCHIVE_MESSAGES = 4;
|
|
5155
5217
|
var MAX_SESSION_STEPS = 20;
|
|
5156
5218
|
var activeArchiverSessions = new Map;
|
|
5157
5219
|
function boundedSessionText(value) {
|
|
@@ -5179,10 +5241,6 @@ function getArchiverDef() {
|
|
|
5179
5241
|
}
|
|
5180
5242
|
function runArchiverTurn(params) {
|
|
5181
5243
|
const { userId, topicId, topicTitle, archivePath, messageCount, mode = "deleted-topic" } = params;
|
|
5182
|
-
if (mode !== "active-topic" && messageCount < MIN_ARCHIVE_MESSAGES) {
|
|
5183
|
-
logger.info({ userId, topicTitle, messageCount, min: MIN_ARCHIVE_MESSAGES }, "archiver: skipped \u2014 too few messages to distil");
|
|
5184
|
-
return false;
|
|
5185
|
-
}
|
|
5186
5244
|
let archiverDef;
|
|
5187
5245
|
try {
|
|
5188
5246
|
archiverDef = getArchiverDef();
|
|
@@ -5391,6 +5449,25 @@ ${rolled.join(`
|
|
|
5391
5449
|
}
|
|
5392
5450
|
}
|
|
5393
5451
|
|
|
5452
|
+
// ../../packages/core/src/agents/memory-archive-policy.ts
|
|
5453
|
+
function countMemoryArchiveExchanges(rows) {
|
|
5454
|
+
let waitingForAssistant = false;
|
|
5455
|
+
let completed = 0;
|
|
5456
|
+
for (const row of rows) {
|
|
5457
|
+
const assistant = row.author_id === "ai" || Boolean(row.agent_type);
|
|
5458
|
+
const conversational = row.kind == null || row.kind === "message";
|
|
5459
|
+
if (!assistant && row.author_id !== "system" && conversational) {
|
|
5460
|
+
waitingForAssistant = true;
|
|
5461
|
+
continue;
|
|
5462
|
+
}
|
|
5463
|
+
if (assistant && conversational && waitingForAssistant) {
|
|
5464
|
+
completed++;
|
|
5465
|
+
waitingForAssistant = false;
|
|
5466
|
+
}
|
|
5467
|
+
}
|
|
5468
|
+
return completed;
|
|
5469
|
+
}
|
|
5470
|
+
|
|
5394
5471
|
// ../../packages/core/src/storage/runtime-leases.ts
|
|
5395
5472
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
5396
5473
|
|
|
@@ -5880,8 +5957,9 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
|
|
|
5880
5957
|
counter++;
|
|
5881
5958
|
}
|
|
5882
5959
|
}
|
|
5883
|
-
|
|
5884
|
-
|
|
5960
|
+
const exchangeCount = countMemoryArchiveExchanges(rows);
|
|
5961
|
+
logger.info({ topicId, topicTitle, archive: path, messageCount: rows.length, exchangeCount, lastRowid }, "archiveTopicMessages: archived topic messages");
|
|
5962
|
+
return { path, messageCount: rows.length, exchangeCount, lastRowid };
|
|
5885
5963
|
}
|
|
5886
5964
|
|
|
5887
5965
|
// ../../packages/core/src/storage/topic-archive-state.ts
|
|
@@ -6078,6 +6156,7 @@ function archiveActiveTopicForMemory(topicId, userId, options) {
|
|
|
6078
6156
|
if (!options.allowMentionOnly && topic.aiMode === "mention")
|
|
6079
6157
|
return "mention-only-channel";
|
|
6080
6158
|
let skipped = "empty";
|
|
6159
|
+
let skipMemoryTurn = false;
|
|
6081
6160
|
const claim = claimTopicArchiveJob(topicId, (afterRowid) => {
|
|
6082
6161
|
const pending = getMessagesForTopicAfterRowid(topicId, afterRowid);
|
|
6083
6162
|
const minMessages = options.minMessages;
|
|
@@ -6086,6 +6165,8 @@ function archiveActiveTopicForMemory(topicId, userId, options) {
|
|
|
6086
6165
|
logger.debug({ topicId, topicTitle: topic.title, pending: pending.length, minMessages }, "topic-memory-archiver: skipped below threshold");
|
|
6087
6166
|
return null;
|
|
6088
6167
|
}
|
|
6168
|
+
const exchangeCount = countMemoryArchiveExchanges(pending);
|
|
6169
|
+
skipMemoryTurn = options.minExchanges !== undefined && exchangeCount < options.minExchanges;
|
|
6089
6170
|
const archived = (options.archiveMessages ?? archiveTopicMessages)(topicId, topic.title, {
|
|
6090
6171
|
afterRowid,
|
|
6091
6172
|
reason: options.reason
|
|
@@ -6103,6 +6184,18 @@ function archiveActiveTopicForMemory(topicId, userId, options) {
|
|
|
6103
6184
|
if (claim.kind === "busy")
|
|
6104
6185
|
return "busy";
|
|
6105
6186
|
const { job } = claim;
|
|
6187
|
+
if (skipMemoryTurn) {
|
|
6188
|
+
settleTopicArchiveJob(topicId, job.archivePath, true);
|
|
6189
|
+
logger.info({
|
|
6190
|
+
topicId,
|
|
6191
|
+
topicTitle: topic.title,
|
|
6192
|
+
messageCount: job.messageCount,
|
|
6193
|
+
minExchanges: options.minExchanges,
|
|
6194
|
+
archive: job.archivePath,
|
|
6195
|
+
reason: options.reason
|
|
6196
|
+
}, "topic-memory-archiver: raw snapshot preserved below exchange threshold");
|
|
6197
|
+
return "below-threshold";
|
|
6198
|
+
}
|
|
6106
6199
|
const memoryTopic = getTopicMemoryOrigin(topicId) ?? topic;
|
|
6107
6200
|
const launched = (options.launchArchiver ?? runArchiverTurn)({
|
|
6108
6201
|
userId,
|
|
@@ -6255,4 +6348,4 @@ export {
|
|
|
6255
6348
|
VAULT_BROKER_REDIRECT_ERROR
|
|
6256
6349
|
};
|
|
6257
6350
|
|
|
6258
|
-
//# debugId=
|
|
6351
|
+
//# debugId=386290251F1B5ABD64756E2164756E21
|