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/cron.js
CHANGED
|
@@ -1831,201 +1831,6 @@ var init_sensitive_path = __esm(() => {
|
|
|
1831
1831
|
];
|
|
1832
1832
|
});
|
|
1833
1833
|
|
|
1834
|
-
// ../../packages/core/src/storage/vault-crypto.ts
|
|
1835
|
-
import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes3 } from "crypto";
|
|
1836
|
-
function encryptionKey(masterKey = VAULT_MASTER_KEY) {
|
|
1837
|
-
return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
|
|
1838
|
-
}
|
|
1839
|
-
function aad(userId, key) {
|
|
1840
|
-
return Buffer.from(`${userId}\x00${key.toUpperCase()}`, "utf8");
|
|
1841
|
-
}
|
|
1842
|
-
function isEncryptedVaultValue(value) {
|
|
1843
|
-
return value.startsWith(ENVELOPE_PREFIX);
|
|
1844
|
-
}
|
|
1845
|
-
function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
|
|
1846
|
-
const iv = randomBytes3(IV_BYTES);
|
|
1847
|
-
const cipher = createCipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
1848
|
-
cipher.setAAD(aad(userId, key));
|
|
1849
|
-
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
1850
|
-
const tag = cipher.getAuthTag();
|
|
1851
|
-
return `${ENVELOPE_PREFIX}${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
|
|
1852
|
-
}
|
|
1853
|
-
function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KEY) {
|
|
1854
|
-
if (!isEncryptedVaultValue(storedValue)) {
|
|
1855
|
-
return { value: storedValue, legacyPlaintext: true };
|
|
1856
|
-
}
|
|
1857
|
-
const encoded = storedValue.slice(ENVELOPE_PREFIX.length);
|
|
1858
|
-
const [ivPart, ciphertextPart, tagPart, ...extra] = encoded.split(".");
|
|
1859
|
-
if (!ivPart || ciphertextPart === undefined || !tagPart || extra.length > 0) {
|
|
1860
|
-
throw new Error("Invalid encrypted vault value");
|
|
1861
|
-
}
|
|
1862
|
-
const iv = Buffer.from(ivPart, "base64url");
|
|
1863
|
-
const ciphertext = Buffer.from(ciphertextPart, "base64url");
|
|
1864
|
-
const tag = Buffer.from(tagPart, "base64url");
|
|
1865
|
-
if (iv.length !== IV_BYTES || tag.length !== 16) {
|
|
1866
|
-
throw new Error("Invalid encrypted vault value");
|
|
1867
|
-
}
|
|
1868
|
-
const decipher = createDecipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
1869
|
-
decipher.setAAD(aad(userId, key));
|
|
1870
|
-
decipher.setAuthTag(tag);
|
|
1871
|
-
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
1872
|
-
return { value: plaintext.toString("utf8"), legacyPlaintext: false };
|
|
1873
|
-
}
|
|
1874
|
-
var ENVELOPE_PREFIX = "otium-vault:v1:", IV_BYTES = 12, KEY_BYTES = 32;
|
|
1875
|
-
var init_vault_crypto = __esm(() => {
|
|
1876
|
-
init_config();
|
|
1877
|
-
});
|
|
1878
|
-
|
|
1879
|
-
// ../../packages/core/src/storage/vault.ts
|
|
1880
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7 } from "fs";
|
|
1881
|
-
import { join as join9 } from "path";
|
|
1882
|
-
function initializeVaultDatabase(database) {
|
|
1883
|
-
database.exec("PRAGMA journal_mode = WAL");
|
|
1884
|
-
database.exec("PRAGMA busy_timeout = 5000");
|
|
1885
|
-
database.exec(`
|
|
1886
|
-
CREATE TABLE IF NOT EXISTS vault (
|
|
1887
|
-
user_id TEXT NOT NULL,
|
|
1888
|
-
key TEXT NOT NULL,
|
|
1889
|
-
value TEXT NOT NULL,
|
|
1890
|
-
description TEXT NOT NULL DEFAULT '',
|
|
1891
|
-
PRIMARY KEY (user_id, key)
|
|
1892
|
-
)
|
|
1893
|
-
`);
|
|
1894
|
-
{
|
|
1895
|
-
const cols = database.prepare("PRAGMA table_info(vault)").all();
|
|
1896
|
-
const uid = cols.find((c) => c.name === "user_id");
|
|
1897
|
-
if (uid && uid.type.toUpperCase() === "INTEGER") {
|
|
1898
|
-
database.exec("BEGIN");
|
|
1899
|
-
database.exec(`
|
|
1900
|
-
CREATE TABLE vault_migrated (
|
|
1901
|
-
user_id TEXT NOT NULL,
|
|
1902
|
-
key TEXT NOT NULL,
|
|
1903
|
-
value TEXT NOT NULL,
|
|
1904
|
-
description TEXT NOT NULL DEFAULT '',
|
|
1905
|
-
PRIMARY KEY (user_id, key)
|
|
1906
|
-
)
|
|
1907
|
-
`);
|
|
1908
|
-
database.exec("INSERT INTO vault_migrated SELECT CAST(user_id AS TEXT), key, value, description FROM vault");
|
|
1909
|
-
database.exec("DROP TABLE vault");
|
|
1910
|
-
database.exec("ALTER TABLE vault_migrated RENAME TO vault");
|
|
1911
|
-
database.exec("COMMIT");
|
|
1912
|
-
}
|
|
1913
|
-
}
|
|
1914
|
-
}
|
|
1915
|
-
function openVaultDatabase(dataDir) {
|
|
1916
|
-
const path = join9(dataDir, "vault.db");
|
|
1917
|
-
mkdirSync7(dataDir, { recursive: true });
|
|
1918
|
-
const database = new Database(path, { create: true });
|
|
1919
|
-
chmodSync2(path, 384);
|
|
1920
|
-
initializeVaultDatabase(database);
|
|
1921
|
-
return database;
|
|
1922
|
-
}
|
|
1923
|
-
function activeVaultDatabase() {
|
|
1924
|
-
if (!vaultDb)
|
|
1925
|
-
vaultDb = openVaultDatabase(DATA_DIR);
|
|
1926
|
-
return vaultDb;
|
|
1927
|
-
}
|
|
1928
|
-
function normalizeVaultKey(key) {
|
|
1929
|
-
return key.trim().toUpperCase();
|
|
1930
|
-
}
|
|
1931
|
-
function decryptRow(userId, key, storedValue) {
|
|
1932
|
-
const database = activeVaultDatabase();
|
|
1933
|
-
const decoded = decryptVaultValue(userId, key, storedValue, vaultMasterKey);
|
|
1934
|
-
if (decoded.legacyPlaintext) {
|
|
1935
|
-
database.prepare("UPDATE vault SET value = ? WHERE user_id = ? AND key = ? AND value = ?").run(encryptVaultValue(userId, key, decoded.value, vaultMasterKey), userId, key, storedValue);
|
|
1936
|
-
}
|
|
1937
|
-
return decoded.value;
|
|
1938
|
-
}
|
|
1939
|
-
function vaultListWithValues(userId) {
|
|
1940
|
-
const rows = activeVaultDatabase().prepare("SELECT key, description, value FROM vault WHERE user_id = ? ORDER BY key").all(userId);
|
|
1941
|
-
return rows.map((row) => ({
|
|
1942
|
-
key: row.key,
|
|
1943
|
-
description: row.description,
|
|
1944
|
-
value: decryptRow(userId, row.key, row.value)
|
|
1945
|
-
}));
|
|
1946
|
-
}
|
|
1947
|
-
function vaultList(userId) {
|
|
1948
|
-
return activeVaultDatabase().prepare("SELECT key, description FROM vault WHERE user_id = ? ORDER BY key").all(userId);
|
|
1949
|
-
}
|
|
1950
|
-
function vaultSubstituteDetailed(userId, text) {
|
|
1951
|
-
const entries = new Map(vaultListWithValues(userId).map((entry) => [entry.key, entry.value]));
|
|
1952
|
-
const usedKeys = new Set;
|
|
1953
|
-
const substituted = text.replace(/\{\{([^}]+)\}\}/g, (match, rawKey) => {
|
|
1954
|
-
const key = normalizeVaultKey(rawKey);
|
|
1955
|
-
const value = entries.get(key);
|
|
1956
|
-
if (value === undefined)
|
|
1957
|
-
return match;
|
|
1958
|
-
usedKeys.add(key);
|
|
1959
|
-
return value;
|
|
1960
|
-
});
|
|
1961
|
-
return { text: substituted, usedKeys: [...usedKeys] };
|
|
1962
|
-
}
|
|
1963
|
-
function valueReferencesVaultKey(userId, value) {
|
|
1964
|
-
const keys = new Set(vaultList(userId).map((entry) => entry.key));
|
|
1965
|
-
const visit = (candidate) => {
|
|
1966
|
-
if (typeof candidate === "string") {
|
|
1967
|
-
for (const match of candidate.matchAll(/\{\{([^}]+)\}\}/g)) {
|
|
1968
|
-
if (keys.has(normalizeVaultKey(match[1] ?? "")))
|
|
1969
|
-
return true;
|
|
1970
|
-
}
|
|
1971
|
-
return false;
|
|
1972
|
-
}
|
|
1973
|
-
if (Array.isArray(candidate))
|
|
1974
|
-
return candidate.some(visit);
|
|
1975
|
-
if (candidate && typeof candidate === "object") {
|
|
1976
|
-
return Object.values(candidate).some(visit);
|
|
1977
|
-
}
|
|
1978
|
-
return false;
|
|
1979
|
-
};
|
|
1980
|
-
return visit(value);
|
|
1981
|
-
}
|
|
1982
|
-
function encodedSecretForms(value) {
|
|
1983
|
-
const forms = new Set([
|
|
1984
|
-
value,
|
|
1985
|
-
encodeURIComponent(value),
|
|
1986
|
-
Buffer.from(value, "utf8").toString("base64"),
|
|
1987
|
-
Buffer.from(value, "utf8").toString("base64url"),
|
|
1988
|
-
Buffer.from(value, "utf8").toString("hex")
|
|
1989
|
-
]);
|
|
1990
|
-
forms.delete("");
|
|
1991
|
-
return [...forms].sort((a, b) => b.length - a.length);
|
|
1992
|
-
}
|
|
1993
|
-
function redactVaultSecrets(userId, text) {
|
|
1994
|
-
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));
|
|
1995
|
-
if (candidates.length === 0)
|
|
1996
|
-
return text;
|
|
1997
|
-
const candidatesByFirstCharacter = new Map;
|
|
1998
|
-
for (const candidate of candidates) {
|
|
1999
|
-
const first = candidate.form[0];
|
|
2000
|
-
if (!first)
|
|
2001
|
-
continue;
|
|
2002
|
-
const bucket = candidatesByFirstCharacter.get(first) ?? [];
|
|
2003
|
-
bucket.push(candidate);
|
|
2004
|
-
candidatesByFirstCharacter.set(first, bucket);
|
|
2005
|
-
}
|
|
2006
|
-
let redacted = "";
|
|
2007
|
-
let offset = 0;
|
|
2008
|
-
while (offset < text.length) {
|
|
2009
|
-
const match = candidatesByFirstCharacter.get(text[offset] ?? "")?.find((candidate) => text.startsWith(candidate.form, offset));
|
|
2010
|
-
if (!match) {
|
|
2011
|
-
redacted += text[offset];
|
|
2012
|
-
offset += 1;
|
|
2013
|
-
continue;
|
|
2014
|
-
}
|
|
2015
|
-
redacted += `[REDACTED:${match.key}]`;
|
|
2016
|
-
offset += match.form.length;
|
|
2017
|
-
}
|
|
2018
|
-
return redacted;
|
|
2019
|
-
}
|
|
2020
|
-
var vaultDb, vaultMasterKey, VAULT_VALUE_MAX_BYTES;
|
|
2021
|
-
var init_vault = __esm(async () => {
|
|
2022
|
-
init_config();
|
|
2023
|
-
await init_sqlite();
|
|
2024
|
-
init_vault_crypto();
|
|
2025
|
-
vaultMasterKey = VAULT_MASTER_KEY;
|
|
2026
|
-
VAULT_VALUE_MAX_BYTES = 64 * 1024;
|
|
2027
|
-
});
|
|
2028
|
-
|
|
2029
1834
|
// ../../packages/core/src/agents/vault-tool-policy.ts
|
|
2030
1835
|
function createVaultToolPolicy(host) {
|
|
2031
1836
|
function isVaultBrokerTool(toolName) {
|
|
@@ -2046,21 +1851,23 @@ function createVaultToolPolicy(host) {
|
|
|
2046
1851
|
return false;
|
|
2047
1852
|
}
|
|
2048
1853
|
function shouldRedirectVaultTool(userId, toolName, input) {
|
|
2049
|
-
return
|
|
1854
|
+
return false;
|
|
2050
1855
|
}
|
|
2051
1856
|
return { isVaultBrokerTool, referencesRuntimeSecretStorage, shouldRedirectVaultTool };
|
|
2052
1857
|
}
|
|
2053
1858
|
var SENSITIVE_RUNTIME_NAMES, defaultVaultToolPolicy, isVaultBrokerTool, referencesRuntimeSecretStorage, shouldRedirectVaultTool;
|
|
2054
|
-
var init_vault_tool_policy = __esm(
|
|
1859
|
+
var init_vault_tool_policy = __esm(() => {
|
|
2055
1860
|
init_sensitive_path();
|
|
2056
|
-
await init_vault();
|
|
2057
1861
|
SENSITIVE_RUNTIME_NAMES = [
|
|
2058
1862
|
"vault.db",
|
|
2059
1863
|
"vault-master-key",
|
|
2060
1864
|
"runtime-mcp-secret",
|
|
2061
1865
|
"sessions.db"
|
|
2062
1866
|
];
|
|
2063
|
-
defaultVaultToolPolicy = createVaultToolPolicy({
|
|
1867
|
+
defaultVaultToolPolicy = createVaultToolPolicy({
|
|
1868
|
+
isSensitivePath,
|
|
1869
|
+
valueReferencesVaultKey: () => false
|
|
1870
|
+
});
|
|
2064
1871
|
isVaultBrokerTool = defaultVaultToolPolicy.isVaultBrokerTool;
|
|
2065
1872
|
referencesRuntimeSecretStorage = defaultVaultToolPolicy.referencesRuntimeSecretStorage;
|
|
2066
1873
|
shouldRedirectVaultTool = defaultVaultToolPolicy.shouldRedirectVaultTool;
|
|
@@ -2156,7 +1963,7 @@ var init_context = () => {};
|
|
|
2156
1963
|
|
|
2157
1964
|
// ../../packages/core/src/platform/background-bash/manager.ts
|
|
2158
1965
|
import { execFileSync as execFileSync3, spawn } from "child_process";
|
|
2159
|
-
import { randomBytes as
|
|
1966
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
2160
1967
|
function makeBgBashKey(_userId, _topic) {
|
|
2161
1968
|
return "runtime";
|
|
2162
1969
|
}
|
|
@@ -2173,8 +1980,8 @@ function createBackgroundBashManager(options = {}) {
|
|
|
2173
1980
|
const usedPorts = new Set;
|
|
2174
1981
|
const spawning = new Map;
|
|
2175
1982
|
const knownContexts = new Map;
|
|
2176
|
-
const runtimeCapability = options.capability ??
|
|
2177
|
-
const runtimeServerId = options.serverId ??
|
|
1983
|
+
const runtimeCapability = options.capability ?? randomBytes3(32).toString("hex");
|
|
1984
|
+
const runtimeServerId = options.serverId ?? randomBytes3(16).toString("hex");
|
|
2178
1985
|
const serverFile = options.serverFile ?? BACKGROUND_BASH_SERVER;
|
|
2179
1986
|
const basePort = options.basePort ?? BG_BASH_BASE_PORT;
|
|
2180
1987
|
const maxPort = options.maxPort ?? BG_BASH_MAX_PORT;
|
|
@@ -2851,7 +2658,9 @@ var init_mcp_config = __esm(() => {
|
|
|
2851
2658
|
vault: {
|
|
2852
2659
|
...commonRuntimeMcpPolicy("vault"),
|
|
2853
2660
|
build({ userId, agent }) {
|
|
2854
|
-
const args = [`--user-id=${userId}
|
|
2661
|
+
const args = [`--user-id=${userId}`];
|
|
2662
|
+
if (agent !== "codex")
|
|
2663
|
+
args.push("--list-only=true");
|
|
2855
2664
|
return buildStdioMcpServer(agent, VAULT_SERVER, args);
|
|
2856
2665
|
}
|
|
2857
2666
|
}
|
|
@@ -2864,6 +2673,179 @@ var init_mcp_config = __esm(() => {
|
|
|
2864
2673
|
nodeMcpEntries = [];
|
|
2865
2674
|
});
|
|
2866
2675
|
|
|
2676
|
+
// ../../packages/core/src/storage/vault-crypto.ts
|
|
2677
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes4 } from "crypto";
|
|
2678
|
+
function encryptionKey(masterKey = VAULT_MASTER_KEY) {
|
|
2679
|
+
return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
|
|
2680
|
+
}
|
|
2681
|
+
function aad(userId, key) {
|
|
2682
|
+
return Buffer.from(`${userId}\x00${key.toUpperCase()}`, "utf8");
|
|
2683
|
+
}
|
|
2684
|
+
function isEncryptedVaultValue(value) {
|
|
2685
|
+
return value.startsWith(ENVELOPE_PREFIX);
|
|
2686
|
+
}
|
|
2687
|
+
function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
|
|
2688
|
+
const iv = randomBytes4(IV_BYTES);
|
|
2689
|
+
const cipher = createCipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
2690
|
+
cipher.setAAD(aad(userId, key));
|
|
2691
|
+
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
2692
|
+
const tag = cipher.getAuthTag();
|
|
2693
|
+
return `${ENVELOPE_PREFIX}${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
|
|
2694
|
+
}
|
|
2695
|
+
function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KEY) {
|
|
2696
|
+
if (!isEncryptedVaultValue(storedValue)) {
|
|
2697
|
+
return { value: storedValue, legacyPlaintext: true };
|
|
2698
|
+
}
|
|
2699
|
+
const encoded = storedValue.slice(ENVELOPE_PREFIX.length);
|
|
2700
|
+
const [ivPart, ciphertextPart, tagPart, ...extra] = encoded.split(".");
|
|
2701
|
+
if (!ivPart || ciphertextPart === undefined || !tagPart || extra.length > 0) {
|
|
2702
|
+
throw new Error("Invalid encrypted vault value");
|
|
2703
|
+
}
|
|
2704
|
+
const iv = Buffer.from(ivPart, "base64url");
|
|
2705
|
+
const ciphertext = Buffer.from(ciphertextPart, "base64url");
|
|
2706
|
+
const tag = Buffer.from(tagPart, "base64url");
|
|
2707
|
+
if (iv.length !== IV_BYTES || tag.length !== 16) {
|
|
2708
|
+
throw new Error("Invalid encrypted vault value");
|
|
2709
|
+
}
|
|
2710
|
+
const decipher = createDecipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
2711
|
+
decipher.setAAD(aad(userId, key));
|
|
2712
|
+
decipher.setAuthTag(tag);
|
|
2713
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
2714
|
+
return { value: plaintext.toString("utf8"), legacyPlaintext: false };
|
|
2715
|
+
}
|
|
2716
|
+
var ENVELOPE_PREFIX = "otium-vault:v1:", IV_BYTES = 12, KEY_BYTES = 32;
|
|
2717
|
+
var init_vault_crypto = __esm(() => {
|
|
2718
|
+
init_config();
|
|
2719
|
+
});
|
|
2720
|
+
|
|
2721
|
+
// ../../packages/core/src/storage/vault.ts
|
|
2722
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7 } from "fs";
|
|
2723
|
+
import { join as join9 } from "path";
|
|
2724
|
+
function initializeVaultDatabase(database) {
|
|
2725
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
2726
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
2727
|
+
database.exec(`
|
|
2728
|
+
CREATE TABLE IF NOT EXISTS vault (
|
|
2729
|
+
user_id TEXT NOT NULL,
|
|
2730
|
+
key TEXT NOT NULL,
|
|
2731
|
+
value TEXT NOT NULL,
|
|
2732
|
+
description TEXT NOT NULL DEFAULT '',
|
|
2733
|
+
PRIMARY KEY (user_id, key)
|
|
2734
|
+
)
|
|
2735
|
+
`);
|
|
2736
|
+
{
|
|
2737
|
+
const cols = database.prepare("PRAGMA table_info(vault)").all();
|
|
2738
|
+
const uid = cols.find((c) => c.name === "user_id");
|
|
2739
|
+
if (uid && uid.type.toUpperCase() === "INTEGER") {
|
|
2740
|
+
database.exec("BEGIN");
|
|
2741
|
+
database.exec(`
|
|
2742
|
+
CREATE TABLE vault_migrated (
|
|
2743
|
+
user_id TEXT NOT NULL,
|
|
2744
|
+
key TEXT NOT NULL,
|
|
2745
|
+
value TEXT NOT NULL,
|
|
2746
|
+
description TEXT NOT NULL DEFAULT '',
|
|
2747
|
+
PRIMARY KEY (user_id, key)
|
|
2748
|
+
)
|
|
2749
|
+
`);
|
|
2750
|
+
database.exec("INSERT INTO vault_migrated SELECT CAST(user_id AS TEXT), key, value, description FROM vault");
|
|
2751
|
+
database.exec("DROP TABLE vault");
|
|
2752
|
+
database.exec("ALTER TABLE vault_migrated RENAME TO vault");
|
|
2753
|
+
database.exec("COMMIT");
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
function openVaultDatabase(dataDir) {
|
|
2758
|
+
const path = join9(dataDir, "vault.db");
|
|
2759
|
+
mkdirSync7(dataDir, { recursive: true });
|
|
2760
|
+
const database = new Database(path, { create: true });
|
|
2761
|
+
chmodSync2(path, 384);
|
|
2762
|
+
initializeVaultDatabase(database);
|
|
2763
|
+
return database;
|
|
2764
|
+
}
|
|
2765
|
+
function activeVaultDatabase() {
|
|
2766
|
+
if (!vaultDb)
|
|
2767
|
+
vaultDb = openVaultDatabase(DATA_DIR);
|
|
2768
|
+
return vaultDb;
|
|
2769
|
+
}
|
|
2770
|
+
function normalizeVaultKey(key) {
|
|
2771
|
+
return key.trim().toUpperCase();
|
|
2772
|
+
}
|
|
2773
|
+
function decryptRow(userId, key, storedValue) {
|
|
2774
|
+
const database = activeVaultDatabase();
|
|
2775
|
+
const decoded = decryptVaultValue(userId, key, storedValue, vaultMasterKey);
|
|
2776
|
+
if (decoded.legacyPlaintext) {
|
|
2777
|
+
database.prepare("UPDATE vault SET value = ? WHERE user_id = ? AND key = ? AND value = ?").run(encryptVaultValue(userId, key, decoded.value, vaultMasterKey), userId, key, storedValue);
|
|
2778
|
+
}
|
|
2779
|
+
return decoded.value;
|
|
2780
|
+
}
|
|
2781
|
+
function vaultListWithValues(userId) {
|
|
2782
|
+
const rows = activeVaultDatabase().prepare("SELECT key, description, value FROM vault WHERE user_id = ? ORDER BY key").all(userId);
|
|
2783
|
+
return rows.map((row) => ({
|
|
2784
|
+
key: row.key,
|
|
2785
|
+
description: row.description,
|
|
2786
|
+
value: decryptRow(userId, row.key, row.value)
|
|
2787
|
+
}));
|
|
2788
|
+
}
|
|
2789
|
+
function vaultSubstituteDetailed(userId, text) {
|
|
2790
|
+
const entries = new Map(vaultListWithValues(userId).map((entry) => [entry.key, entry.value]));
|
|
2791
|
+
const usedKeys = new Set;
|
|
2792
|
+
const substituted = text.replace(/\{\{([^}]+)\}\}/g, (match, rawKey) => {
|
|
2793
|
+
const key = normalizeVaultKey(rawKey);
|
|
2794
|
+
const value = entries.get(key);
|
|
2795
|
+
if (value === undefined)
|
|
2796
|
+
return match;
|
|
2797
|
+
usedKeys.add(key);
|
|
2798
|
+
return value;
|
|
2799
|
+
});
|
|
2800
|
+
return { text: substituted, usedKeys: [...usedKeys] };
|
|
2801
|
+
}
|
|
2802
|
+
function encodedSecretForms(value) {
|
|
2803
|
+
const forms = new Set([
|
|
2804
|
+
value,
|
|
2805
|
+
encodeURIComponent(value),
|
|
2806
|
+
Buffer.from(value, "utf8").toString("base64"),
|
|
2807
|
+
Buffer.from(value, "utf8").toString("base64url"),
|
|
2808
|
+
Buffer.from(value, "utf8").toString("hex")
|
|
2809
|
+
]);
|
|
2810
|
+
forms.delete("");
|
|
2811
|
+
return [...forms].sort((a, b) => b.length - a.length);
|
|
2812
|
+
}
|
|
2813
|
+
function redactVaultSecrets(userId, text) {
|
|
2814
|
+
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));
|
|
2815
|
+
if (candidates.length === 0)
|
|
2816
|
+
return text;
|
|
2817
|
+
const candidatesByFirstCharacter = new Map;
|
|
2818
|
+
for (const candidate of candidates) {
|
|
2819
|
+
const first = candidate.form[0];
|
|
2820
|
+
if (!first)
|
|
2821
|
+
continue;
|
|
2822
|
+
const bucket = candidatesByFirstCharacter.get(first) ?? [];
|
|
2823
|
+
bucket.push(candidate);
|
|
2824
|
+
candidatesByFirstCharacter.set(first, bucket);
|
|
2825
|
+
}
|
|
2826
|
+
let redacted = "";
|
|
2827
|
+
let offset = 0;
|
|
2828
|
+
while (offset < text.length) {
|
|
2829
|
+
const match = candidatesByFirstCharacter.get(text[offset] ?? "")?.find((candidate) => text.startsWith(candidate.form, offset));
|
|
2830
|
+
if (!match) {
|
|
2831
|
+
redacted += text[offset];
|
|
2832
|
+
offset += 1;
|
|
2833
|
+
continue;
|
|
2834
|
+
}
|
|
2835
|
+
redacted += `[REDACTED:${match.key}]`;
|
|
2836
|
+
offset += match.form.length;
|
|
2837
|
+
}
|
|
2838
|
+
return redacted;
|
|
2839
|
+
}
|
|
2840
|
+
var vaultDb, vaultMasterKey, VAULT_VALUE_MAX_BYTES;
|
|
2841
|
+
var init_vault = __esm(async () => {
|
|
2842
|
+
init_config();
|
|
2843
|
+
await init_sqlite();
|
|
2844
|
+
init_vault_crypto();
|
|
2845
|
+
vaultMasterKey = VAULT_MASTER_KEY;
|
|
2846
|
+
VAULT_VALUE_MAX_BYTES = 64 * 1024;
|
|
2847
|
+
});
|
|
2848
|
+
|
|
2867
2849
|
// ../../packages/core/src/agents/execution-host.ts
|
|
2868
2850
|
import { AsyncLocalStorage } from "async_hooks";
|
|
2869
2851
|
function activeHost() {
|
|
@@ -2895,7 +2877,7 @@ function hostedCodexAuthFilePath() {
|
|
|
2895
2877
|
}
|
|
2896
2878
|
var defaultHost, hostRegistrations, scopedHost;
|
|
2897
2879
|
var init_execution_host = __esm(async () => {
|
|
2898
|
-
|
|
2880
|
+
init_vault_tool_policy();
|
|
2899
2881
|
init_config();
|
|
2900
2882
|
init_mcp_config();
|
|
2901
2883
|
await init_vault();
|
|
@@ -3469,7 +3451,7 @@ var init_claude_provider = __esm(async () => {
|
|
|
3469
3451
|
});
|
|
3470
3452
|
|
|
3471
3453
|
// ../../packages/core/src/version.ts
|
|
3472
|
-
var NEGOTIUM_VERSION = "0.1.
|
|
3454
|
+
var NEGOTIUM_VERSION = "0.1.22";
|
|
3473
3455
|
|
|
3474
3456
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
3475
3457
|
import { spawn as spawn3 } from "child_process";
|
|
@@ -5448,6 +5430,10 @@ var init_ask_user = __esm(async () => {
|
|
|
5448
5430
|
});
|
|
5449
5431
|
|
|
5450
5432
|
// ../../packages/core/src/agents/model-catalog.ts
|
|
5433
|
+
function formatSelectableModel(candidate) {
|
|
5434
|
+
const tier = `${candidate.intelligenceTier[0].toUpperCase()}${candidate.intelligenceTier.slice(1)}`;
|
|
5435
|
+
return `${candidate.agent} / \`${candidate.model}\` [${tier}-level]: ${candidate.routingSummary}`;
|
|
5436
|
+
}
|
|
5451
5437
|
function selectableModel(value) {
|
|
5452
5438
|
const normalized = value.trim().toLowerCase();
|
|
5453
5439
|
return SELECTABLE_MODELS.find((candidate) => candidate.model === normalized);
|
|
@@ -5470,7 +5456,7 @@ function resolveModelForAgent(agent, requested, registry) {
|
|
|
5470
5456
|
return defaultModel;
|
|
5471
5457
|
return registry.validateModel(requested) ? requested : defaultModel;
|
|
5472
5458
|
}
|
|
5473
|
-
var MODEL_OWNER, SELECTABLE_MODELS, FALLBACK_ORDER, AGENT_DISPLAY_NAME;
|
|
5459
|
+
var MODEL_OWNER, MODEL_COST_RESEARCHED_AT = "2026-07-19", MODEL_COST_ROUTING_SUMMARY = "Cost basis (2026-07-19): Codex Pro 20x and Claude Max 20x are each $200/month; DeepSeek Pro is pay-per-token. Relative marginal token cost: DeepSeek Pro << Codex < Claude.", CODEX_PRO_20X_COST = "ChatGPT Pro 20x subscription: $200/month", 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)", CLAUDE_MAX_20X_COST = "Claude Max 20x subscription: $200/month", 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)", SELECTABLE_MODELS, FALLBACK_ORDER, AGENT_DISPLAY_NAME;
|
|
5474
5460
|
var init_model_catalog = __esm(() => {
|
|
5475
5461
|
init_config();
|
|
5476
5462
|
MODEL_OWNER = {
|
|
@@ -5490,47 +5476,72 @@ var init_model_catalog = __esm(() => {
|
|
|
5490
5476
|
{
|
|
5491
5477
|
model: "gpt-5.6-sol",
|
|
5492
5478
|
agent: "codex",
|
|
5493
|
-
description: "
|
|
5479
|
+
description: "Highest-capability Codex route for the hardest agentic coding work.",
|
|
5480
|
+
intelligenceTier: "fable",
|
|
5481
|
+
routingSummary: "hardest coding work; 5x Codex quota cost",
|
|
5482
|
+
accessCost: CODEX_PRO_20X_COST,
|
|
5483
|
+
marginalTokenCost: "Codex credits: $5/M uncached input, $0.50/M cached input, $30/M output",
|
|
5484
|
+
estimatedUsage: `Official Pro 20x range: 300\u20131,800 local messages per 5 hours; quota weight 5x Luna. ${CODEX_COMMUNITY_WEEKLY}`
|
|
5494
5485
|
},
|
|
5495
5486
|
{
|
|
5496
5487
|
model: "gpt-5.6-terra",
|
|
5497
5488
|
agent: "codex",
|
|
5498
|
-
description: "
|
|
5489
|
+
description: "High-capability Codex route for complex coding and reasoning.",
|
|
5490
|
+
intelligenceTier: "opus",
|
|
5491
|
+
routingSummary: "complex coding and reasoning; 2.5x Codex quota cost",
|
|
5492
|
+
accessCost: CODEX_PRO_20X_COST,
|
|
5493
|
+
marginalTokenCost: "Codex credits: $2.50/M uncached input, $0.25/M cached input, $15/M output",
|
|
5494
|
+
estimatedUsage: `Official Pro 20x range: 400\u20132,200 local messages per 5 hours; quota weight 2.5x Luna. ${CODEX_COMMUNITY_WEEKLY}`
|
|
5499
5495
|
},
|
|
5500
5496
|
{
|
|
5501
5497
|
model: "gpt-5.6-luna",
|
|
5502
5498
|
agent: "codex",
|
|
5503
|
-
description: "
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5499
|
+
description: "Default Codex route with strong everyday coding intelligence.",
|
|
5500
|
+
intelligenceTier: "sonnet",
|
|
5501
|
+
routingSummary: "everyday coding default; lowest Codex quota cost (1x)",
|
|
5502
|
+
accessCost: CODEX_PRO_20X_COST,
|
|
5503
|
+
marginalTokenCost: "Codex credits: $1/M uncached input, $0.10/M cached input, $6/M output",
|
|
5504
|
+
estimatedUsage: `Official Pro 20x range: 1,000\u20135,600 local messages per 5 hours; lowest Codex quota weight (1x). ${CODEX_COMMUNITY_WEEKLY}`
|
|
5509
5505
|
},
|
|
5510
5506
|
{
|
|
5511
5507
|
model: "fable",
|
|
5512
5508
|
agent: "claude",
|
|
5513
|
-
description: "
|
|
5509
|
+
description: "Highest-capability Claude route for the hardest and longest-running tasks.",
|
|
5510
|
+
intelligenceTier: "fable",
|
|
5511
|
+
routingSummary: "hardest long-running work; highest Claude cost; explicit request only",
|
|
5512
|
+
accessCost: CLAUDE_MAX_20X_COST,
|
|
5513
|
+
marginalTokenCost: "Claude API/extra usage: $10/M input, $12.50/M cache write, $1/M cache read, $50/M output",
|
|
5514
|
+
estimatedUsage: `${CLAUDE_COMMUNITY_SESSION}; Fable drains weighted quota fastest, so use only on explicit user request. No stable per-model token cap is published.`
|
|
5514
5515
|
},
|
|
5515
5516
|
{
|
|
5516
5517
|
model: "opus",
|
|
5517
5518
|
agent: "claude",
|
|
5518
|
-
description: "
|
|
5519
|
+
description: "High-capability Claude route for complex reasoning and tool-heavy work.",
|
|
5520
|
+
intelligenceTier: "opus",
|
|
5521
|
+
routingSummary: "complex reasoning and tool-heavy work; about 2.5x Sonnet marginal cost",
|
|
5522
|
+
accessCost: CLAUDE_MAX_20X_COST,
|
|
5523
|
+
marginalTokenCost: "Claude API/extra usage: $5/M input, $6.25/M cache write, $0.50/M cache read, $25/M output",
|
|
5524
|
+
estimatedUsage: `${CLAUDE_COMMUNITY_SESSION}; Opus uses the shared all-model weekly pool more quickly than Sonnet. No stable per-model token cap is published.`
|
|
5519
5525
|
},
|
|
5520
5526
|
{
|
|
5521
5527
|
model: "sonnet",
|
|
5522
5528
|
agent: "claude",
|
|
5523
|
-
description: "
|
|
5529
|
+
description: "Default Claude route for capable, efficient everyday work.",
|
|
5530
|
+
intelligenceTier: "sonnet",
|
|
5531
|
+
routingSummary: "capable everyday default; lowest Claude model cost",
|
|
5532
|
+
accessCost: CLAUDE_MAX_20X_COST,
|
|
5533
|
+
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",
|
|
5534
|
+
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.`
|
|
5524
5535
|
},
|
|
5525
5536
|
{
|
|
5526
5537
|
model: "deepseek-pro",
|
|
5527
5538
|
agent: "maestro",
|
|
5528
|
-
description: "Sonnet-level
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5539
|
+
description: "API-priced Sonnet-level route for cost-efficient everyday work.",
|
|
5540
|
+
intelligenceTier: "sonnet",
|
|
5541
|
+
routingSummary: "cost-efficient everyday work; pay-per-token and cheapest route",
|
|
5542
|
+
accessCost: "DeepSeek V4 Pro pay-as-you-go API; no monthly subscription required",
|
|
5543
|
+
marginalTokenCost: "DeepSeek API: $0.435/M uncached input, $0.003625/M cached input, $0.87/M output",
|
|
5544
|
+
estimatedUsage: "No subscription token cap; pay per token. Official account concurrency limit is 500 requests."
|
|
5534
5545
|
}
|
|
5535
5546
|
];
|
|
5536
5547
|
FALLBACK_ORDER = {
|
|
@@ -5779,6 +5790,39 @@ var init_browser_profiles = __esm(async () => {
|
|
|
5779
5790
|
`);
|
|
5780
5791
|
});
|
|
5781
5792
|
|
|
5793
|
+
// ../../packages/core/src/storage/runtime-process-leases.ts
|
|
5794
|
+
function rowToLease2(row) {
|
|
5795
|
+
return {
|
|
5796
|
+
role: row.role,
|
|
5797
|
+
ownerId: row.owner_id,
|
|
5798
|
+
pid: Number(row.pid),
|
|
5799
|
+
startedAt: Number(row.started_at),
|
|
5800
|
+
heartbeatAt: Number(row.heartbeat_at)
|
|
5801
|
+
};
|
|
5802
|
+
}
|
|
5803
|
+
function getRuntimeProcessLease(role, now = Date.now(), staleMs = PROCESS_LEASE_STALE_MS) {
|
|
5804
|
+
const row = db.query("SELECT * FROM runtime_process_leases WHERE role = ?").get(role);
|
|
5805
|
+
if (!row)
|
|
5806
|
+
return null;
|
|
5807
|
+
const lease = rowToLease2(row);
|
|
5808
|
+
return now - lease.heartbeatAt <= staleMs ? lease : null;
|
|
5809
|
+
}
|
|
5810
|
+
var PROCESS_LEASE_STALE_MS = 5000;
|
|
5811
|
+
var init_runtime_process_leases = __esm(async () => {
|
|
5812
|
+
await init_forum_db();
|
|
5813
|
+
await init_runtime_leases();
|
|
5814
|
+
db.exec(`
|
|
5815
|
+
CREATE TABLE IF NOT EXISTS runtime_process_leases (
|
|
5816
|
+
role TEXT PRIMARY KEY,
|
|
5817
|
+
owner_id TEXT NOT NULL UNIQUE,
|
|
5818
|
+
pid INTEGER NOT NULL,
|
|
5819
|
+
started_at INTEGER NOT NULL,
|
|
5820
|
+
heartbeat_at INTEGER NOT NULL
|
|
5821
|
+
)
|
|
5822
|
+
`);
|
|
5823
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_runtime_process_leases_heartbeat ON runtime_process_leases(heartbeat_at)");
|
|
5824
|
+
});
|
|
5825
|
+
|
|
5782
5826
|
// ../../packages/core/src/platform/playwright/manager.ts
|
|
5783
5827
|
import { execFileSync as execFileSync4, spawn as spawn4 } from "child_process";
|
|
5784
5828
|
import { createHash as createHash2, randomBytes as randomBytes5 } from "crypto";
|
|
@@ -6040,7 +6084,13 @@ function selectOrphanBrowserPids(procs, liveUserDataDirs, profileRoot, selfPid)
|
|
|
6040
6084
|
}
|
|
6041
6085
|
return out;
|
|
6042
6086
|
}
|
|
6087
|
+
function isBrowserJanitorOwner(leaseOwnerPid, selfPid) {
|
|
6088
|
+
return leaseOwnerPid === selfPid;
|
|
6089
|
+
}
|
|
6043
6090
|
function reapOrphanBrowsers() {
|
|
6091
|
+
const daemonLease = getRuntimeProcessLease("node-daemon", Date.now(), Number.POSITIVE_INFINITY);
|
|
6092
|
+
if (!isBrowserJanitorOwner(daemonLease?.pid ?? null, process.pid))
|
|
6093
|
+
return;
|
|
6044
6094
|
const profileRoot = resolve5(BROWSER_PROFILES_DIR);
|
|
6045
6095
|
let pids;
|
|
6046
6096
|
try {
|
|
@@ -6166,6 +6216,7 @@ async function spawnPlaywright(instanceKey, userId, reservedPort, browserBin = P
|
|
|
6166
6216
|
String(port),
|
|
6167
6217
|
"--host",
|
|
6168
6218
|
"127.0.0.1",
|
|
6219
|
+
"--headed",
|
|
6169
6220
|
"--user-data-dir",
|
|
6170
6221
|
userDataDir
|
|
6171
6222
|
];
|
|
@@ -6308,6 +6359,7 @@ var init_manager2 = __esm(async () => {
|
|
|
6308
6359
|
init_config();
|
|
6309
6360
|
init_logger();
|
|
6310
6361
|
await init_browser_profiles();
|
|
6362
|
+
await init_runtime_process_leases();
|
|
6311
6363
|
BASE_PORT = PLAYWRIGHT_BASE_PORT;
|
|
6312
6364
|
MAX_PORT = PLAYWRIGHT_MAX_PORT;
|
|
6313
6365
|
MAX_IDLE_MS = 2 * 60 * 60 * 1000;
|
|
@@ -7006,7 +7058,7 @@ function loadAgentPrompt(filename) {
|
|
|
7006
7058
|
prompt: match[2].trim()
|
|
7007
7059
|
};
|
|
7008
7060
|
}
|
|
7009
|
-
function buildRuntimeToolSection(agentKind, canSpawnSubagents = false, visualTools = false, fileDeliveryTools = false) {
|
|
7061
|
+
function buildRuntimeToolSection(agentKind, canSpawnSubagents = false, visualTools = false, fileDeliveryTools = false, currentModel, currentEffort) {
|
|
7010
7062
|
const runtimeNamespace = "mcp__runtime";
|
|
7011
7063
|
const taskNamespace = "mcp__task";
|
|
7012
7064
|
const visualToolLine = agentKind === "codex" ? `To display charts, tables, or interactive HTML results to the user, call the \`show_html\` function in the \`${runtimeNamespace}\` namespace with { html: "<complete HTML string>", title?: "optional title" }.` : `To display charts, tables, or interactive HTML results to the user, call the MCP tool "${runtimeNamespace}__show_html" with { html: "<complete HTML string>", title?: "optional title" }.`;
|
|
@@ -7064,20 +7116,26 @@ function buildRuntimeToolSection(agentKind, canSpawnSubagents = false, visualToo
|
|
|
7064
7116
|
"Do not use session communication to make another topic perform destructive changes without the user's clear intent.",
|
|
7065
7117
|
...spawnSubagentSection
|
|
7066
7118
|
];
|
|
7119
|
+
const modelCatalog = SELECTABLE_MODELS.map((candidate) => `- ${formatSelectableModel(candidate)}`);
|
|
7067
7120
|
const topicConfig = [
|
|
7068
7121
|
"",
|
|
7069
7122
|
"## Topic Configuration (model / agent / effort)",
|
|
7123
|
+
`Current execution: agent=\`${agentKind}\`, model=\`${currentModel ?? "unknown (call get_model)"}\`, effort=\`${currentEffort ?? "provider default/off"}\`.`,
|
|
7070
7124
|
"The user's configured agent/model/effort is intentional. Preserve it by default.",
|
|
7071
7125
|
`When the user explicitly asks to change the model, agent backend, or reasoning effort for THIS topic, call "${runtimeNamespace}__set_model", "${runtimeNamespace}__set_agent", or "${runtimeNamespace}__set_effort". The change applies from your NEXT turn. After calling, briefly confirm and the system will continue with the new setting.`,
|
|
7072
7126
|
"`set_effort` is available but discouraged; use it only when the user explicitly requests an effort change.",
|
|
7073
|
-
"`set_model` may be called autonomously only when the current model is clearly below the task's required capability, such as complex algorithm design, proof-level math, or broad multi-file refactoring.
|
|
7127
|
+
"`set_model` may be called autonomously only when the current model is clearly below the task's required capability, such as complex algorithm design, proof-level math, or broad multi-file refactoring. Choose the best-fit model directly from the same-agent catalog; model selection is not a mandatory one-step ladder. End the turn after changing it. Do not use vague task complexity as a trigger.",
|
|
7074
7128
|
"`set_agent` autonomous calls are forbidden. Only switch agent when the user explicitly asks to switch runtime, e.g. \u201Ccodex\uB85C \uAC00\u201D, \u201Cclaude\uB85C \uBC14\uAFD4\u201D.",
|
|
7075
7129
|
"Never use `fable` unless the user explicitly requests it; it is expensive.",
|
|
7076
7130
|
"",
|
|
7077
|
-
"
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
"
|
|
7131
|
+
"Model catalog (capability/cost routing guidance):",
|
|
7132
|
+
MODEL_COST_ROUTING_SUMMARY,
|
|
7133
|
+
...modelCatalog,
|
|
7134
|
+
"",
|
|
7135
|
+
"Accepted effort values:",
|
|
7136
|
+
"- claude: `low`, `medium`, `high`, `xhigh`, `max`.",
|
|
7137
|
+
"- codex: `low`, `medium`, `high`, `xhigh`, `max`.",
|
|
7138
|
+
"- maestro: `low`, `medium`, `high`, `xhigh`, `max`.",
|
|
7081
7139
|
"Agent guidance when the user explicitly asks to switch: `codex` for deepest reasoning and complex code/math; `claude` for tool-heavy MCP/file automation; `maestro` for inexpensive fast drafts and lighter experiments."
|
|
7082
7140
|
];
|
|
7083
7141
|
if (agentKind !== "claude") {
|
|
@@ -7100,7 +7158,7 @@ function buildTopicSystemPrompt(opts) {
|
|
|
7100
7158
|
TOPIC_TITLE: opts.topicTitle,
|
|
7101
7159
|
WORKSPACE_CWD: opts.workspaceCwd,
|
|
7102
7160
|
UPLOADS_DIR: uploadsDir
|
|
7103
|
-
}) + buildRuntimeToolSection(opts.agentKind, opts.canSpawnSubagents, opts.visualTools, opts.fileDeliveryTools);
|
|
7161
|
+
}) + buildRuntimeToolSection(opts.agentKind, opts.canSpawnSubagents, opts.visualTools, opts.fileDeliveryTools, opts.currentModel, opts.currentEffort);
|
|
7104
7162
|
if (opts.description?.trim()) {
|
|
7105
7163
|
prompt += `
|
|
7106
7164
|
|
|
@@ -7116,7 +7174,7 @@ function buildChannelSystemPrompt(opts) {
|
|
|
7116
7174
|
TOPIC_TITLE: opts.topicTitle,
|
|
7117
7175
|
WORKSPACE_CWD: opts.workspaceCwd,
|
|
7118
7176
|
UPLOADS_DIR: uploadsDir
|
|
7119
|
-
}) + buildRuntimeToolSection(opts.agentKind, false, opts.visualTools, opts.fileDeliveryTools);
|
|
7177
|
+
}) + buildRuntimeToolSection(opts.agentKind, false, opts.visualTools, opts.fileDeliveryTools, opts.currentModel, opts.currentEffort);
|
|
7120
7178
|
}
|
|
7121
7179
|
function buildManagerSystemPrompt(opts) {
|
|
7122
7180
|
return `${buildTopicSystemPrompt(opts)}
|
|
@@ -7166,6 +7224,7 @@ User-uploaded files for this Channel are copied under "{{UPLOADS_DIR}}" as attac
|
|
|
7166
7224
|
This is the shared "General" hub of the user's workspace.
|
|
7167
7225
|
Act as the workspace manager: orient the user across topics, summarize what is going on, and route focused work to the right room.`, _topicSystemPromptTemplate = null, _channelSystemPromptTemplate = null, _managerSystemPromptTemplate = null, _visualDesignGuide = null;
|
|
7168
7226
|
var init_builders = __esm(() => {
|
|
7227
|
+
init_model_catalog();
|
|
7169
7228
|
init_config();
|
|
7170
7229
|
init_logger();
|
|
7171
7230
|
PROMPTS_DIR = resolve6(PROJECT_ROOT, "src/prompts");
|
|
@@ -7278,10 +7337,6 @@ function getArchiverDef() {
|
|
|
7278
7337
|
}
|
|
7279
7338
|
function runArchiverTurn(params) {
|
|
7280
7339
|
const { userId, topicId, topicTitle, archivePath, messageCount, mode = "deleted-topic" } = params;
|
|
7281
|
-
if (mode !== "active-topic" && messageCount < MIN_ARCHIVE_MESSAGES) {
|
|
7282
|
-
logger.info({ userId, topicTitle, messageCount, min: MIN_ARCHIVE_MESSAGES }, "archiver: skipped \u2014 too few messages to distil");
|
|
7283
|
-
return false;
|
|
7284
|
-
}
|
|
7285
7340
|
let archiverDef;
|
|
7286
7341
|
try {
|
|
7287
7342
|
archiverDef = getArchiverDef();
|
|
@@ -7489,7 +7544,7 @@ ${rolled.join(`
|
|
|
7489
7544
|
logger.warn({ err, topicTitle }, "archiver: failed to post #General notification");
|
|
7490
7545
|
}
|
|
7491
7546
|
}
|
|
7492
|
-
var MAX_BRIEF_ENTRIES = 8,
|
|
7547
|
+
var MAX_BRIEF_ENTRIES = 8, MAX_SESSION_STEPS = 20, activeArchiverSessions, _archiverDef = null;
|
|
7493
7548
|
var init_archiver = __esm(async () => {
|
|
7494
7549
|
await init_agents();
|
|
7495
7550
|
await init_bus();
|
|
@@ -7504,6 +7559,25 @@ var init_archiver = __esm(async () => {
|
|
|
7504
7559
|
activeArchiverSessions = new Map;
|
|
7505
7560
|
});
|
|
7506
7561
|
|
|
7562
|
+
// ../../packages/core/src/agents/memory-archive-policy.ts
|
|
7563
|
+
function countMemoryArchiveExchanges(rows) {
|
|
7564
|
+
let waitingForAssistant = false;
|
|
7565
|
+
let completed = 0;
|
|
7566
|
+
for (const row of rows) {
|
|
7567
|
+
const assistant = row.author_id === "ai" || Boolean(row.agent_type);
|
|
7568
|
+
const conversational = row.kind == null || row.kind === "message";
|
|
7569
|
+
if (!assistant && row.author_id !== "system" && conversational) {
|
|
7570
|
+
waitingForAssistant = true;
|
|
7571
|
+
continue;
|
|
7572
|
+
}
|
|
7573
|
+
if (assistant && conversational && waitingForAssistant) {
|
|
7574
|
+
completed++;
|
|
7575
|
+
waitingForAssistant = false;
|
|
7576
|
+
}
|
|
7577
|
+
}
|
|
7578
|
+
return completed;
|
|
7579
|
+
}
|
|
7580
|
+
|
|
7507
7581
|
// ../../packages/core/src/storage/topic-transcript.ts
|
|
7508
7582
|
function parseJsonField(value) {
|
|
7509
7583
|
if (!value)
|
|
@@ -7591,8 +7665,9 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
|
|
|
7591
7665
|
counter++;
|
|
7592
7666
|
}
|
|
7593
7667
|
}
|
|
7594
|
-
|
|
7595
|
-
|
|
7668
|
+
const exchangeCount = countMemoryArchiveExchanges(rows);
|
|
7669
|
+
logger.info({ topicId, topicTitle, archive: path, messageCount: rows.length, exchangeCount, lastRowid }, "archiveTopicMessages: archived topic messages");
|
|
7670
|
+
return { path, messageCount: rows.length, exchangeCount, lastRowid };
|
|
7596
7671
|
}
|
|
7597
7672
|
var init_topic_archive = __esm(async () => {
|
|
7598
7673
|
init_logger();
|
|
@@ -7796,6 +7871,7 @@ function archiveActiveTopicForMemory(topicId, userId, options) {
|
|
|
7796
7871
|
if (!options.allowMentionOnly && topic.aiMode === "mention")
|
|
7797
7872
|
return "mention-only-channel";
|
|
7798
7873
|
let skipped = "empty";
|
|
7874
|
+
let skipMemoryTurn = false;
|
|
7799
7875
|
const claim = claimTopicArchiveJob(topicId, (afterRowid) => {
|
|
7800
7876
|
const pending = getMessagesForTopicAfterRowid(topicId, afterRowid);
|
|
7801
7877
|
const minMessages = options.minMessages;
|
|
@@ -7804,6 +7880,8 @@ function archiveActiveTopicForMemory(topicId, userId, options) {
|
|
|
7804
7880
|
logger.debug({ topicId, topicTitle: topic.title, pending: pending.length, minMessages }, "topic-memory-archiver: skipped below threshold");
|
|
7805
7881
|
return null;
|
|
7806
7882
|
}
|
|
7883
|
+
const exchangeCount = countMemoryArchiveExchanges(pending);
|
|
7884
|
+
skipMemoryTurn = options.minExchanges !== undefined && exchangeCount < options.minExchanges;
|
|
7807
7885
|
const archived = (options.archiveMessages ?? archiveTopicMessages)(topicId, topic.title, {
|
|
7808
7886
|
afterRowid,
|
|
7809
7887
|
reason: options.reason
|
|
@@ -7821,6 +7899,18 @@ function archiveActiveTopicForMemory(topicId, userId, options) {
|
|
|
7821
7899
|
if (claim.kind === "busy")
|
|
7822
7900
|
return "busy";
|
|
7823
7901
|
const { job } = claim;
|
|
7902
|
+
if (skipMemoryTurn) {
|
|
7903
|
+
settleTopicArchiveJob(topicId, job.archivePath, true);
|
|
7904
|
+
logger.info({
|
|
7905
|
+
topicId,
|
|
7906
|
+
topicTitle: topic.title,
|
|
7907
|
+
messageCount: job.messageCount,
|
|
7908
|
+
minExchanges: options.minExchanges,
|
|
7909
|
+
archive: job.archivePath,
|
|
7910
|
+
reason: options.reason
|
|
7911
|
+
}, "topic-memory-archiver: raw snapshot preserved below exchange threshold");
|
|
7912
|
+
return "below-threshold";
|
|
7913
|
+
}
|
|
7824
7914
|
const memoryTopic = getTopicMemoryOrigin(topicId) ?? topic;
|
|
7825
7915
|
const launched = (options.launchArchiver ?? runArchiverTurn)({
|
|
7826
7916
|
userId,
|
|
@@ -9723,6 +9813,7 @@ __export(exports_turn_runner, {
|
|
|
9723
9813
|
isPathInside: () => isPathInside,
|
|
9724
9814
|
isLikelyCurrentChannelTrigger: () => isLikelyCurrentChannelTrigger,
|
|
9725
9815
|
ingestAttachment: () => ingestAttachment,
|
|
9816
|
+
formatSelectableModel: () => formatSelectableModel,
|
|
9726
9817
|
formatChannelTranscriptLine: () => formatChannelTranscriptLine,
|
|
9727
9818
|
escapeRegExp: () => escapeRegExp,
|
|
9728
9819
|
escapeHtml: () => escapeHtml2,
|
|
@@ -9740,6 +9831,8 @@ __export(exports_turn_runner, {
|
|
|
9740
9831
|
SESSION_EXPIRED_MSG: () => SESSION_EXPIRED_MSG,
|
|
9741
9832
|
SELECTABLE_MODELS: () => SELECTABLE_MODELS,
|
|
9742
9833
|
MODEL_OWNER: () => MODEL_OWNER,
|
|
9834
|
+
MODEL_COST_ROUTING_SUMMARY: () => MODEL_COST_ROUTING_SUMMARY,
|
|
9835
|
+
MODEL_COST_RESEARCHED_AT: () => MODEL_COST_RESEARCHED_AT,
|
|
9743
9836
|
FALLBACK_ORDER: () => FALLBACK_ORDER,
|
|
9744
9837
|
AGENT_DISPLAY_NAME: () => AGENT_DISPLAY_NAME
|
|
9745
9838
|
});
|
|
@@ -10947,6 +11040,8 @@ function startAiTurn(params) {
|
|
|
10947
11040
|
topicTitle: topic.title,
|
|
10948
11041
|
workspaceCwd,
|
|
10949
11042
|
agentKind,
|
|
11043
|
+
currentModel: resolvedModel,
|
|
11044
|
+
currentEffort: resolvedEffort,
|
|
10950
11045
|
description: topic.description,
|
|
10951
11046
|
canSpawnSubagents: peerBridge?.canSpawnSubagents ?? (topicRecord?.kind === "agent" && !topicRecord.isSubagent),
|
|
10952
11047
|
visualTools,
|
|
@@ -12034,20 +12129,7 @@ init_mcp_config();
|
|
|
12034
12129
|
|
|
12035
12130
|
// ../../packages/core/src/platform/modules.ts
|
|
12036
12131
|
init_logger();
|
|
12037
|
-
|
|
12038
|
-
// ../../packages/core/src/storage/runtime-process-leases.ts
|
|
12039
|
-
await init_forum_db();
|
|
12040
|
-
await init_runtime_leases();
|
|
12041
|
-
db.exec(`
|
|
12042
|
-
CREATE TABLE IF NOT EXISTS runtime_process_leases (
|
|
12043
|
-
role TEXT PRIMARY KEY,
|
|
12044
|
-
owner_id TEXT NOT NULL UNIQUE,
|
|
12045
|
-
pid INTEGER NOT NULL,
|
|
12046
|
-
started_at INTEGER NOT NULL,
|
|
12047
|
-
heartbeat_at INTEGER NOT NULL
|
|
12048
|
-
)
|
|
12049
|
-
`);
|
|
12050
|
-
db.exec("CREATE INDEX IF NOT EXISTS idx_runtime_process_leases_heartbeat ON runtime_process_leases(heartbeat_at)");
|
|
12132
|
+
await init_runtime_process_leases();
|
|
12051
12133
|
|
|
12052
12134
|
// ../../packages/core/src/index.ts
|
|
12053
12135
|
await init_manager2();
|
|
@@ -12183,6 +12265,7 @@ init_session_inbox_path();
|
|
|
12183
12265
|
init_types2();
|
|
12184
12266
|
await init_api_messages();
|
|
12185
12267
|
await init_runtime_leases();
|
|
12268
|
+
await init_runtime_process_leases();
|
|
12186
12269
|
await init_self_schedules();
|
|
12187
12270
|
await init_session_asks();
|
|
12188
12271
|
var topicWorkerBusy = new Set;
|
|
@@ -12197,6 +12280,7 @@ await init_app_settings();
|
|
|
12197
12280
|
await init_forum_db();
|
|
12198
12281
|
await init_runtime_events();
|
|
12199
12282
|
await init_runtime_leases();
|
|
12283
|
+
await init_runtime_process_leases();
|
|
12200
12284
|
await init_session_asks();
|
|
12201
12285
|
await init_vault();
|
|
12202
12286
|
await init_derive();
|
|
@@ -13857,4 +13941,4 @@ export {
|
|
|
13857
13941
|
CRON_CONTEXT_RETAIN_TURNS
|
|
13858
13942
|
};
|
|
13859
13943
|
|
|
13860
|
-
//# debugId=
|
|
13944
|
+
//# debugId=B45862ECC407392D64756E2164756E21
|