negotium 0.18.0 → 0.18.2
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 +7082 -7062
- package/dist/agent-helpers.js.map +10 -10
- package/dist/{chunk-th3a0ew7.js → chunk-3d1pjgfw.js} +633 -195
- package/dist/{chunk-th3a0ew7.js.map → chunk-3d1pjgfw.js.map} +9 -6
- package/dist/{chunk-kgwgwc3r.js → chunk-44ajn328.js} +3 -3
- package/dist/{chunk-kgwgwc3r.js.map → chunk-44ajn328.js.map} +2 -2
- package/dist/{chunk-axa09pgx.js → chunk-q2s5f70x.js} +1 -1
- package/dist/hosted-agent.js +634 -196
- package/dist/hosted-agent.js.map +10 -7
- package/dist/main.js +5942 -5908
- package/dist/main.js.map +13 -13
- package/dist/mcp-factories.js +3418 -3393
- package/dist/mcp-factories.js.map +11 -11
- package/dist/prompts.js +18 -14
- package/dist/prompts.js.map +3 -3
- package/dist/registry.js +3 -3
- package/dist/rollout.js +2 -2
- package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +43 -16
- package/dist/runtime/src/mcp/factories/decision.ts +11 -3
- package/dist/runtime/src/mcp/session-comm/server.ts +1 -1
- package/dist/runtime/src/platform/mcp-config.ts +79 -58
- package/dist/runtime/src/prompts/builders.ts +34 -17
- package/dist/runtime/src/prompts/sessions/surfaces/otium-manager.md +18 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/types/packages/core/src/agents/mcp-tools/spawn-subagent.d.ts +8 -4
- package/dist/types/packages/core/src/mcp/factories/decision.d.ts +2 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +1 -1
- /package/dist/{chunk-axa09pgx.js.map → chunk-q2s5f70x.js.map} +0 -0
|
@@ -1025,6 +1025,624 @@ function getRegisteredCronSession(topicId, agent) {
|
|
|
1025
1025
|
}
|
|
1026
1026
|
}
|
|
1027
1027
|
|
|
1028
|
+
// ../../packages/core/src/platform/constants.ts
|
|
1029
|
+
var FROM_AUTO_CONTINUE = "auto-continue";
|
|
1030
|
+
var FROM_SELF_SCHEDULE = "self-schedule";
|
|
1031
|
+
var RESERVED_TOPIC_NAMES = new Set([
|
|
1032
|
+
FROM_AUTO_CONTINUE,
|
|
1033
|
+
FROM_SELF_SCHEDULE,
|
|
1034
|
+
"general"
|
|
1035
|
+
]);
|
|
1036
|
+
var GENERAL_TOPIC_ID = "general";
|
|
1037
|
+
|
|
1038
|
+
// ../../packages/core/src/storage/storage-host.ts
|
|
1039
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
1040
|
+
import { homedir as homedir2 } from "os";
|
|
1041
|
+
import { dirname as dirname3, join as join2, resolve as resolve5 } from "path";
|
|
1042
|
+
|
|
1043
|
+
// ../../packages/core/src/storage/sqlite.ts
|
|
1044
|
+
var isBun = typeof process.versions.bun === "string";
|
|
1045
|
+
var Database;
|
|
1046
|
+
if (isBun) {
|
|
1047
|
+
({ Database } = await import("bun:sqlite"));
|
|
1048
|
+
} else {
|
|
1049
|
+
const nodeSqliteSpecifier = ["node", "sqlite"].join(":");
|
|
1050
|
+
const { DatabaseSync } = await import(nodeSqliteSpecifier);
|
|
1051
|
+
|
|
1052
|
+
class NodeDatabase {
|
|
1053
|
+
#db;
|
|
1054
|
+
constructor(path, options = {}) {
|
|
1055
|
+
this.#db = options.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path);
|
|
1056
|
+
}
|
|
1057
|
+
query(sql) {
|
|
1058
|
+
return this.#db.prepare(sql);
|
|
1059
|
+
}
|
|
1060
|
+
prepare(sql) {
|
|
1061
|
+
return this.#db.prepare(sql);
|
|
1062
|
+
}
|
|
1063
|
+
exec(sql) {
|
|
1064
|
+
this.#db.exec(sql);
|
|
1065
|
+
}
|
|
1066
|
+
run(sql, ...params) {
|
|
1067
|
+
return this.#db.prepare(sql).run(...params);
|
|
1068
|
+
}
|
|
1069
|
+
transaction(fn) {
|
|
1070
|
+
const run = (begin) => (...args) => {
|
|
1071
|
+
this.#db.exec(begin);
|
|
1072
|
+
try {
|
|
1073
|
+
const result = fn(...args);
|
|
1074
|
+
this.#db.exec("COMMIT");
|
|
1075
|
+
return result;
|
|
1076
|
+
} catch (err) {
|
|
1077
|
+
this.#db.exec("ROLLBACK");
|
|
1078
|
+
throw err;
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
const tx = run("BEGIN");
|
|
1082
|
+
tx.deferred = run("BEGIN DEFERRED");
|
|
1083
|
+
tx.immediate = run("BEGIN IMMEDIATE");
|
|
1084
|
+
tx.exclusive = run("BEGIN EXCLUSIVE");
|
|
1085
|
+
return tx;
|
|
1086
|
+
}
|
|
1087
|
+
close() {
|
|
1088
|
+
this.#db.close();
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
Database = NodeDatabase;
|
|
1092
|
+
}
|
|
1093
|
+
function closeDatabase(database) {
|
|
1094
|
+
try {
|
|
1095
|
+
database.close(true);
|
|
1096
|
+
} catch {
|
|
1097
|
+
try {
|
|
1098
|
+
database.close();
|
|
1099
|
+
} catch {}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// ../../packages/core/src/storage/storage-host.ts
|
|
1104
|
+
var STORAGE_HOST_STATE = Symbol.for("negotium.storage-host.state.v1");
|
|
1105
|
+
function storageState() {
|
|
1106
|
+
const holder = globalThis;
|
|
1107
|
+
const existing = holder[STORAGE_HOST_STATE];
|
|
1108
|
+
if (existing)
|
|
1109
|
+
return existing;
|
|
1110
|
+
const created = {
|
|
1111
|
+
configuredHost: {},
|
|
1112
|
+
fallbackDatabase: null,
|
|
1113
|
+
fallbackDatabasePath: null,
|
|
1114
|
+
frames: [],
|
|
1115
|
+
schemaInitializers: [],
|
|
1116
|
+
initializedSchemas: new WeakMap,
|
|
1117
|
+
initializingDatabases: new WeakSet
|
|
1118
|
+
};
|
|
1119
|
+
Object.defineProperty(holder, STORAGE_HOST_STATE, {
|
|
1120
|
+
value: created,
|
|
1121
|
+
enumerable: false,
|
|
1122
|
+
writable: false,
|
|
1123
|
+
configurable: false
|
|
1124
|
+
});
|
|
1125
|
+
return created;
|
|
1126
|
+
}
|
|
1127
|
+
function envPath(name, fallback) {
|
|
1128
|
+
const value = process.env[name]?.trim();
|
|
1129
|
+
return resolve5(value || fallback);
|
|
1130
|
+
}
|
|
1131
|
+
function defaultStateDir() {
|
|
1132
|
+
return envPath("NEGOTIUM_STATE_DIR", join2(homedir2(), ".negotium"));
|
|
1133
|
+
}
|
|
1134
|
+
function defaultDataDir() {
|
|
1135
|
+
return envPath("NEGOTIUM_DATA_DIR", join2(defaultStateDir(), "data"));
|
|
1136
|
+
}
|
|
1137
|
+
function defaultSessionsDatabasePath() {
|
|
1138
|
+
return envPath("SESSIONS_DB_PATH", join2(resolveStorageDataDir(), "sessions.db"));
|
|
1139
|
+
}
|
|
1140
|
+
var SQLITE_INIT_RETRY_MS = 25;
|
|
1141
|
+
var SQLITE_INIT_TIMEOUT_MS = 5000;
|
|
1142
|
+
var SQLITE_INIT_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
1143
|
+
function isSqliteBusy(error) {
|
|
1144
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1145
|
+
return /database is (?:locked|busy)|SQLITE_(?:BUSY|LOCKED)/i.test(message);
|
|
1146
|
+
}
|
|
1147
|
+
function execWithBusyRetry(database, sql, timeoutMs = SQLITE_INIT_TIMEOUT_MS) {
|
|
1148
|
+
const deadline = Date.now() + timeoutMs;
|
|
1149
|
+
while (true) {
|
|
1150
|
+
try {
|
|
1151
|
+
database.exec(sql);
|
|
1152
|
+
return;
|
|
1153
|
+
} catch (error) {
|
|
1154
|
+
if (!isSqliteBusy(error) || Date.now() >= deadline)
|
|
1155
|
+
throw error;
|
|
1156
|
+
Atomics.wait(SQLITE_INIT_SLEEP, 0, 0, Math.min(SQLITE_INIT_RETRY_MS, deadline - Date.now()));
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
function initializeDatabase(database) {
|
|
1161
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
1162
|
+
execWithBusyRetry(database, "PRAGMA journal_mode = WAL");
|
|
1163
|
+
database.exec("PRAGMA foreign_keys = ON");
|
|
1164
|
+
database.exec("PRAGMA wal_autocheckpoint = 1000");
|
|
1165
|
+
try {
|
|
1166
|
+
database.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
1167
|
+
} catch {}
|
|
1168
|
+
}
|
|
1169
|
+
function defaultDatabase() {
|
|
1170
|
+
const path = defaultSessionsDatabasePath();
|
|
1171
|
+
const state = storageState();
|
|
1172
|
+
if (state.fallbackDatabase && state.fallbackDatabasePath === path)
|
|
1173
|
+
return state.fallbackDatabase;
|
|
1174
|
+
if (state.fallbackDatabase)
|
|
1175
|
+
closeDatabase(state.fallbackDatabase);
|
|
1176
|
+
mkdirSync3(dirname3(path), { recursive: true });
|
|
1177
|
+
state.fallbackDatabase = new Database(path, { create: true });
|
|
1178
|
+
state.fallbackDatabasePath = path;
|
|
1179
|
+
initializeDatabase(state.fallbackDatabase);
|
|
1180
|
+
return state.fallbackDatabase;
|
|
1181
|
+
}
|
|
1182
|
+
function resolveStorageDatabase() {
|
|
1183
|
+
return storageState().configuredHost.database ?? defaultDatabase();
|
|
1184
|
+
}
|
|
1185
|
+
function resolveStorageDataDir() {
|
|
1186
|
+
return storageState().configuredHost.dataDir ?? defaultDataDir();
|
|
1187
|
+
}
|
|
1188
|
+
function registerStorageSchemaInitializer(initialize, priority = 100) {
|
|
1189
|
+
const initializers = storageState().schemaInitializers;
|
|
1190
|
+
initializers.push({ initialize, priority });
|
|
1191
|
+
initializers.sort((a, b) => a.priority - b.priority);
|
|
1192
|
+
}
|
|
1193
|
+
function ensureStorageSchemas(database = resolveStorageDatabase()) {
|
|
1194
|
+
const state = storageState();
|
|
1195
|
+
if (state.initializingDatabases.has(database))
|
|
1196
|
+
return;
|
|
1197
|
+
let initialized = state.initializedSchemas.get(database);
|
|
1198
|
+
if (!initialized) {
|
|
1199
|
+
initialized = new Set;
|
|
1200
|
+
state.initializedSchemas.set(database, initialized);
|
|
1201
|
+
}
|
|
1202
|
+
state.initializingDatabases.add(database);
|
|
1203
|
+
try {
|
|
1204
|
+
for (const entry of state.schemaInitializers) {
|
|
1205
|
+
if (initialized.has(entry.initialize))
|
|
1206
|
+
continue;
|
|
1207
|
+
initialized.add(entry.initialize);
|
|
1208
|
+
try {
|
|
1209
|
+
entry.initialize(database);
|
|
1210
|
+
} catch (error) {
|
|
1211
|
+
initialized.delete(entry.initialize);
|
|
1212
|
+
throw error;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
} finally {
|
|
1216
|
+
state.initializingDatabases.delete(database);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
var internalStorageDatabase = new Proxy({}, {
|
|
1220
|
+
get(_target, property) {
|
|
1221
|
+
const database = resolveStorageDatabase();
|
|
1222
|
+
ensureStorageSchemas(database);
|
|
1223
|
+
const value = Reflect.get(database, property, database);
|
|
1224
|
+
return typeof value === "function" ? value.bind(database) : value;
|
|
1225
|
+
},
|
|
1226
|
+
set(_target, property, value) {
|
|
1227
|
+
const database = resolveStorageDatabase();
|
|
1228
|
+
ensureStorageSchemas(database);
|
|
1229
|
+
return Reflect.set(database, property, value, database);
|
|
1230
|
+
}
|
|
1231
|
+
});
|
|
1232
|
+
|
|
1233
|
+
// ../../packages/core/src/storage/forum-db.ts
|
|
1234
|
+
var db = internalStorageDatabase;
|
|
1235
|
+
|
|
1236
|
+
// ../../packages/core/src/storage/api-topics.ts
|
|
1237
|
+
function defaultTopicSurface() {
|
|
1238
|
+
return normalizeTopicSurface(process.env.NEGOTIUM_DEFAULT_SURFACE);
|
|
1239
|
+
}
|
|
1240
|
+
function normalizeTopicSurface(value) {
|
|
1241
|
+
return value === "telegram" || value === "otium" || value === "terminal" ? value : "terminal";
|
|
1242
|
+
}
|
|
1243
|
+
function normalizeSurfaceScope(value) {
|
|
1244
|
+
if (typeof value !== "string")
|
|
1245
|
+
return null;
|
|
1246
|
+
const trimmed = value.trim();
|
|
1247
|
+
return trimmed ? trimmed : null;
|
|
1248
|
+
}
|
|
1249
|
+
function tableColumns(table) {
|
|
1250
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
1251
|
+
return new Set(rows.map((row) => row.name));
|
|
1252
|
+
}
|
|
1253
|
+
function initializeApiTopicsSchema() {
|
|
1254
|
+
db.exec(`
|
|
1255
|
+
CREATE TABLE IF NOT EXISTS api_topics (
|
|
1256
|
+
id TEXT PRIMARY KEY,
|
|
1257
|
+
title TEXT NOT NULL,
|
|
1258
|
+
kind TEXT NOT NULL DEFAULT 'channel',
|
|
1259
|
+
description TEXT,
|
|
1260
|
+
agent TEXT,
|
|
1261
|
+
default_model TEXT,
|
|
1262
|
+
default_effort TEXT,
|
|
1263
|
+
participants TEXT,
|
|
1264
|
+
created_at TEXT NOT NULL,
|
|
1265
|
+
last_message_at TEXT,
|
|
1266
|
+
is_archived INTEGER NOT NULL DEFAULT 0,
|
|
1267
|
+
ai_mention INTEGER NOT NULL DEFAULT 0,
|
|
1268
|
+
ai_mode TEXT
|
|
1269
|
+
)
|
|
1270
|
+
`);
|
|
1271
|
+
const initialTopicColumns = tableColumns("api_topics");
|
|
1272
|
+
const legacyTopicSchema = !initialTopicColumns.has("response_policy");
|
|
1273
|
+
const needsCanonicalTopicRebuild = legacyTopicSchema || !initialTopicColumns.has("agent");
|
|
1274
|
+
if (legacyTopicSchema) {
|
|
1275
|
+
try {
|
|
1276
|
+
db.exec("ALTER TABLE api_topics RENAME COLUMN default_agent TO agent");
|
|
1277
|
+
} catch {}
|
|
1278
|
+
try {
|
|
1279
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN ai_mention INTEGER NOT NULL DEFAULT 0");
|
|
1280
|
+
} catch {}
|
|
1281
|
+
try {
|
|
1282
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN parent_topic_id TEXT");
|
|
1283
|
+
} catch {}
|
|
1284
|
+
try {
|
|
1285
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN is_fork INTEGER NOT NULL DEFAULT 0");
|
|
1286
|
+
} catch {}
|
|
1287
|
+
try {
|
|
1288
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN is_subagent INTEGER NOT NULL DEFAULT 0");
|
|
1289
|
+
} catch {}
|
|
1290
|
+
try {
|
|
1291
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN session_id TEXT");
|
|
1292
|
+
} catch {}
|
|
1293
|
+
try {
|
|
1294
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN kind TEXT NOT NULL DEFAULT 'channel'");
|
|
1295
|
+
} catch {}
|
|
1296
|
+
try {
|
|
1297
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN ai_mode TEXT");
|
|
1298
|
+
} catch {}
|
|
1299
|
+
db.exec(`
|
|
1300
|
+
CREATE TABLE IF NOT EXISTS api_schema_migrations (
|
|
1301
|
+
key TEXT PRIMARY KEY,
|
|
1302
|
+
applied_at TEXT NOT NULL
|
|
1303
|
+
)
|
|
1304
|
+
`);
|
|
1305
|
+
const ALWAYS_RESPOND_MIGRATION = "api_topics_ai_invited_default_always_respond_20260623";
|
|
1306
|
+
const alwaysRespondMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(ALWAYS_RESPOND_MIGRATION);
|
|
1307
|
+
if (!alwaysRespondMigration) {
|
|
1308
|
+
db.transaction(() => {
|
|
1309
|
+
db.query("UPDATE api_topics SET ai_mention = 0 WHERE agent IS NOT NULL").run();
|
|
1310
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(ALWAYS_RESPOND_MIGRATION, new Date().toISOString());
|
|
1311
|
+
})();
|
|
1312
|
+
}
|
|
1313
|
+
const GENERAL_AGENT_KIND_MIGRATION = "api_topics_general_agent_kind_20260704";
|
|
1314
|
+
const generalAgentKindMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(GENERAL_AGENT_KIND_MIGRATION);
|
|
1315
|
+
if (!generalAgentKindMigration) {
|
|
1316
|
+
db.transaction(() => {
|
|
1317
|
+
db.query("UPDATE api_topics SET kind = 'agent', ai_mention = 0 WHERE id = ?").run(GENERAL_TOPIC_ID);
|
|
1318
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(GENERAL_AGENT_KIND_MIGRATION, new Date().toISOString());
|
|
1319
|
+
})();
|
|
1320
|
+
}
|
|
1321
|
+
const AI_MODE_MIGRATION = "api_topics_ai_mode_20260704";
|
|
1322
|
+
const aiModeMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(AI_MODE_MIGRATION);
|
|
1323
|
+
if (!aiModeMigration) {
|
|
1324
|
+
db.transaction(() => {
|
|
1325
|
+
db.query(`UPDATE api_topics
|
|
1326
|
+
SET
|
|
1327
|
+
kind = CASE
|
|
1328
|
+
WHEN id = ? THEN 'agent'
|
|
1329
|
+
WHEN kind = 'channel' AND agent IS NOT NULL AND ai_mention = 0 THEN 'agent'
|
|
1330
|
+
ELSE kind
|
|
1331
|
+
END,
|
|
1332
|
+
ai_mode = CASE
|
|
1333
|
+
WHEN id = ? THEN 'always'
|
|
1334
|
+
WHEN agent IS NULL THEN 'off'
|
|
1335
|
+
WHEN kind = 'agent' THEN 'always'
|
|
1336
|
+
WHEN kind = 'channel' AND ai_mention != 0 THEN 'mention'
|
|
1337
|
+
WHEN kind = 'channel' AND ai_mention = 0 THEN 'always'
|
|
1338
|
+
WHEN ai_mention != 0 THEN 'mention'
|
|
1339
|
+
ELSE 'always'
|
|
1340
|
+
END`).run(GENERAL_TOPIC_ID, GENERAL_TOPIC_ID);
|
|
1341
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(AI_MODE_MIGRATION, new Date().toISOString());
|
|
1342
|
+
})();
|
|
1343
|
+
}
|
|
1344
|
+
const GENERAL_MANAGER_KIND_MIGRATION = "api_topics_general_manager_kind_20260704";
|
|
1345
|
+
const generalManagerKindMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(GENERAL_MANAGER_KIND_MIGRATION);
|
|
1346
|
+
if (!generalManagerKindMigration) {
|
|
1347
|
+
db.transaction(() => {
|
|
1348
|
+
db.query("UPDATE api_topics SET kind = 'manager', ai_mention = 0, ai_mode = 'always' WHERE id = ?").run(GENERAL_TOPIC_ID);
|
|
1349
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(GENERAL_MANAGER_KIND_MIGRATION, new Date().toISOString());
|
|
1350
|
+
})();
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
function createCanonicalTopicsTable(name) {
|
|
1354
|
+
db.exec(`
|
|
1355
|
+
CREATE TABLE IF NOT EXISTS ${name} (
|
|
1356
|
+
id TEXT PRIMARY KEY,
|
|
1357
|
+
title TEXT NOT NULL,
|
|
1358
|
+
kind TEXT NOT NULL CHECK (kind IN ('channel','agent','manager')),
|
|
1359
|
+
description TEXT,
|
|
1360
|
+
agent TEXT CHECK (agent IS NULL OR agent IN ('maestro','claude','codex')),
|
|
1361
|
+
base_model TEXT,
|
|
1362
|
+
base_effort TEXT CHECK (base_effort IS NULL OR base_effort IN ('low','medium','high','xhigh','max')),
|
|
1363
|
+
response_policy TEXT NOT NULL CHECK (response_policy IN ('off','mention','always')),
|
|
1364
|
+
created_at TEXT NOT NULL,
|
|
1365
|
+
last_message_at TEXT,
|
|
1366
|
+
parent_topic_id TEXT,
|
|
1367
|
+
memory_topic_id TEXT,
|
|
1368
|
+
memory_key TEXT,
|
|
1369
|
+
is_fork INTEGER NOT NULL DEFAULT 0 CHECK (is_fork IN (0,1)),
|
|
1370
|
+
is_subagent INTEGER NOT NULL DEFAULT 0 CHECK (is_subagent IN (0,1)),
|
|
1371
|
+
visibility TEXT NOT NULL DEFAULT 'visible' CHECK (visibility IN ('visible','hidden')),
|
|
1372
|
+
surface TEXT NOT NULL DEFAULT 'terminal' CHECK (surface IN ('terminal','telegram','otium')),
|
|
1373
|
+
surface_scope TEXT,
|
|
1374
|
+
browser_profile TEXT NOT NULL DEFAULT 'default',
|
|
1375
|
+
browser_profile_owner TEXT,
|
|
1376
|
+
session_id TEXT,
|
|
1377
|
+
CHECK (
|
|
1378
|
+
(kind = 'channel' AND response_policy = 'off' AND agent IS NULL) OR
|
|
1379
|
+
(kind = 'channel' AND response_policy = 'mention' AND agent IS NOT NULL) OR
|
|
1380
|
+
(kind IN ('agent','manager') AND response_policy = 'always' AND agent IS NOT NULL)
|
|
1381
|
+
)
|
|
1382
|
+
)
|
|
1383
|
+
`);
|
|
1384
|
+
}
|
|
1385
|
+
function createTopicMembersTable() {
|
|
1386
|
+
db.exec(`
|
|
1387
|
+
CREATE TABLE IF NOT EXISTS topic_members (
|
|
1388
|
+
topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
|
|
1389
|
+
user_id TEXT NOT NULL,
|
|
1390
|
+
role TEXT NOT NULL CHECK (role IN ('owner','member')),
|
|
1391
|
+
PRIMARY KEY (topic_id, user_id)
|
|
1392
|
+
)
|
|
1393
|
+
`);
|
|
1394
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_topic_members_user ON topic_members(user_id)");
|
|
1395
|
+
}
|
|
1396
|
+
if (needsCanonicalTopicRebuild) {
|
|
1397
|
+
const legacyRows = db.query("SELECT * FROM api_topics").all();
|
|
1398
|
+
const existingMemberRows = tableColumns("topic_members").has("topic_id") ? db.query("SELECT topic_id, user_id, role FROM topic_members").all() : [];
|
|
1399
|
+
const configColumns = tableColumns("api_topic_config");
|
|
1400
|
+
const legacyAgentOverrides = configColumns.has("agent") ? new Map(db.query("SELECT topic_id, agent FROM api_topic_config WHERE agent IS NOT NULL").all().map((row) => [row.topic_id, row.agent])) : new Map;
|
|
1401
|
+
const previousForeignKeys = db.query("PRAGMA foreign_keys").get()?.foreign_keys;
|
|
1402
|
+
db.exec("PRAGMA foreign_keys = OFF");
|
|
1403
|
+
try {
|
|
1404
|
+
db.transaction(() => {
|
|
1405
|
+
createCanonicalTopicsTable("api_topics_next");
|
|
1406
|
+
for (const row of legacyRows) {
|
|
1407
|
+
const selectedAgent = legacyAgentOverrides.get(String(row.id)) ?? row.agent ?? row.runtime_agent ?? row.default_agent ?? undefined;
|
|
1408
|
+
const normalized = normalizeTopicState({
|
|
1409
|
+
id: String(row.id),
|
|
1410
|
+
kind: normalizeTopicKind(row.kind),
|
|
1411
|
+
agent: selectedAgent,
|
|
1412
|
+
aiMode: normalizeAiMode(row.response_policy ?? row.ai_mode),
|
|
1413
|
+
aiMention: Number(row.ai_mention ?? 0) !== 0
|
|
1414
|
+
});
|
|
1415
|
+
const legacyBaseModel = row.base_model ?? row.default_model;
|
|
1416
|
+
const legacyBaseEffort = row.base_effort ?? row.default_effort;
|
|
1417
|
+
db.query(`INSERT INTO api_topics_next
|
|
1418
|
+
(id,title,kind,description,agent,base_model,base_effort,response_policy,
|
|
1419
|
+
created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,surface,surface_scope,session_id)
|
|
1420
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(String(row.id), String(row.title), normalized.kind, typeof row.description === "string" ? row.description : null, normalized.agent ?? null, typeof legacyBaseModel === "string" ? legacyBaseModel : null, typeof legacyBaseEffort === "string" ? legacyBaseEffort : null, normalized.aiMode, String(row.created_at), typeof row.last_message_at === "string" ? row.last_message_at : null, typeof row.parent_topic_id === "string" ? row.parent_topic_id : null, typeof row.memory_topic_id === "string" ? row.memory_topic_id : null, typeof row.memory_key === "string" ? row.memory_key : null, Number(row.is_fork ?? 0) !== 0 ? 1 : 0, Number(row.is_subagent ?? 0) !== 0 ? 1 : 0, row.visibility === "hidden" ? "hidden" : "visible", row.surface === undefined ? defaultTopicSurface() : normalizeTopicSurface(row.surface), normalizeSurfaceScope(row.surface_scope), typeof row.session_id === "string" ? row.session_id : null);
|
|
1421
|
+
}
|
|
1422
|
+
db.exec("DROP TABLE IF EXISTS topic_members");
|
|
1423
|
+
db.exec("DROP TABLE api_topics");
|
|
1424
|
+
db.exec("ALTER TABLE api_topics_next RENAME TO api_topics");
|
|
1425
|
+
createTopicMembersTable();
|
|
1426
|
+
if (existingMemberRows.length > 0) {
|
|
1427
|
+
for (const member of existingMemberRows) {
|
|
1428
|
+
db.query("INSERT OR REPLACE INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(member.topic_id, member.user_id, member.role === "owner" ? "owner" : "member");
|
|
1429
|
+
}
|
|
1430
|
+
} else {
|
|
1431
|
+
for (const row of legacyRows) {
|
|
1432
|
+
let participants = [];
|
|
1433
|
+
try {
|
|
1434
|
+
const parsed = JSON.parse(String(row.participants ?? "[]"));
|
|
1435
|
+
if (Array.isArray(parsed))
|
|
1436
|
+
participants = parsed;
|
|
1437
|
+
} catch {
|
|
1438
|
+
participants = [];
|
|
1439
|
+
}
|
|
1440
|
+
for (const participant of participants) {
|
|
1441
|
+
if (!participant?.userId)
|
|
1442
|
+
continue;
|
|
1443
|
+
db.query("INSERT OR REPLACE INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(String(row.id), participant.userId, participant.role === "owner" ? "owner" : "member");
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
})();
|
|
1448
|
+
} finally {
|
|
1449
|
+
db.exec(`PRAGMA foreign_keys = ${previousForeignKeys ? "ON" : "OFF"}`);
|
|
1450
|
+
}
|
|
1451
|
+
} else {
|
|
1452
|
+
createCanonicalTopicsTable("api_topics");
|
|
1453
|
+
createTopicMembersTable();
|
|
1454
|
+
}
|
|
1455
|
+
db.exec(`
|
|
1456
|
+
CREATE TABLE IF NOT EXISTS subagent_tell_grants (
|
|
1457
|
+
subagent_topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
|
|
1458
|
+
target_topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
|
|
1459
|
+
granted_by_topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
|
|
1460
|
+
created_at TEXT NOT NULL,
|
|
1461
|
+
PRIMARY KEY (subagent_topic_id, target_topic_id)
|
|
1462
|
+
)
|
|
1463
|
+
`);
|
|
1464
|
+
db.exec(`
|
|
1465
|
+
DELETE FROM subagent_tell_grants
|
|
1466
|
+
WHERE subagent_topic_id NOT IN (SELECT id FROM api_topics)
|
|
1467
|
+
OR target_topic_id NOT IN (SELECT id FROM api_topics)
|
|
1468
|
+
OR granted_by_topic_id NOT IN (SELECT id FROM api_topics)
|
|
1469
|
+
`);
|
|
1470
|
+
db.exec("DELETE FROM topic_members WHERE topic_id NOT IN (SELECT id FROM api_topics)");
|
|
1471
|
+
if (!tableColumns("api_topics").has("visibility")) {
|
|
1472
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN visibility TEXT NOT NULL DEFAULT 'visible'");
|
|
1473
|
+
}
|
|
1474
|
+
if (!tableColumns("api_topics").has("surface")) {
|
|
1475
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN surface TEXT NOT NULL DEFAULT 'terminal'");
|
|
1476
|
+
}
|
|
1477
|
+
if (!tableColumns("api_topics").has("surface_scope")) {
|
|
1478
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN surface_scope TEXT");
|
|
1479
|
+
}
|
|
1480
|
+
if (tableColumns("api_topics").has("access_mode")) {
|
|
1481
|
+
try {
|
|
1482
|
+
db.exec("ALTER TABLE api_topics DROP COLUMN access_mode");
|
|
1483
|
+
} catch (err) {
|
|
1484
|
+
logger.warn({ err }, "api_topics: could not drop the retired access_mode column");
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
if (!tableColumns("api_topics").has("browser_profile")) {
|
|
1488
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile TEXT NOT NULL DEFAULT 'default'");
|
|
1489
|
+
}
|
|
1490
|
+
if (!tableColumns("api_topics").has("browser_profile_owner")) {
|
|
1491
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile_owner TEXT");
|
|
1492
|
+
}
|
|
1493
|
+
if (!tableColumns("api_topics").has("subagent_report_mode")) {
|
|
1494
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN subagent_report_mode TEXT NOT NULL DEFAULT 'auto'");
|
|
1495
|
+
}
|
|
1496
|
+
if (!tableColumns("api_topics").has("memory_topic_id")) {
|
|
1497
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN memory_topic_id TEXT");
|
|
1498
|
+
}
|
|
1499
|
+
if (!tableColumns("api_topics").has("memory_key")) {
|
|
1500
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN memory_key TEXT");
|
|
1501
|
+
}
|
|
1502
|
+
db.exec(`
|
|
1503
|
+
UPDATE api_topics
|
|
1504
|
+
SET browser_profile_owner = (
|
|
1505
|
+
SELECT m.user_id FROM topic_members m
|
|
1506
|
+
WHERE m.topic_id = api_topics.id AND m.role = 'owner'
|
|
1507
|
+
ORDER BY m.rowid
|
|
1508
|
+
LIMIT 1
|
|
1509
|
+
)
|
|
1510
|
+
WHERE browser_profile_owner IS NULL
|
|
1511
|
+
`);
|
|
1512
|
+
backfillTopicSurfaces();
|
|
1513
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_last_message ON api_topics(last_message_at DESC)");
|
|
1514
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_surface ON api_topics(surface)");
|
|
1515
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_surface_scope ON api_topics(surface, surface_scope)");
|
|
1516
|
+
}
|
|
1517
|
+
var SURFACE_BACKFILL_MIGRATION = "api_topics_surface_backfill_20260808";
|
|
1518
|
+
function backfillTopicSurfaces() {
|
|
1519
|
+
db.exec(`
|
|
1520
|
+
CREATE TABLE IF NOT EXISTS api_schema_migrations (
|
|
1521
|
+
key TEXT PRIMARY KEY,
|
|
1522
|
+
applied_at TEXT NOT NULL
|
|
1523
|
+
)
|
|
1524
|
+
`);
|
|
1525
|
+
const applied = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(SURFACE_BACKFILL_MIGRATION);
|
|
1526
|
+
if (applied)
|
|
1527
|
+
return;
|
|
1528
|
+
const surface = defaultTopicSurface();
|
|
1529
|
+
db.transaction(() => {
|
|
1530
|
+
db.query("UPDATE api_topics SET surface = ?").run(surface);
|
|
1531
|
+
renameSurfaceTitleCollisions();
|
|
1532
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(SURFACE_BACKFILL_MIGRATION, new Date().toISOString());
|
|
1533
|
+
})();
|
|
1534
|
+
logger.info({ surface }, "api_topics: surface backfilled");
|
|
1535
|
+
}
|
|
1536
|
+
function renameSurfaceTitleCollisions() {
|
|
1537
|
+
const rows = db.query("SELECT id, title, kind, surface FROM api_topics WHERE kind != 'manager' ORDER BY created_at ASC, rowid ASC").all();
|
|
1538
|
+
const keyOf = (surface, kind, title) => [surface, kind, normalizedTitle(title)].join("\x00");
|
|
1539
|
+
const reserved = new Set(rows.map((row) => keyOf(row.surface, row.kind, row.title)));
|
|
1540
|
+
const used = new Set;
|
|
1541
|
+
const update = db.query("UPDATE api_topics SET title = ? WHERE id = ?");
|
|
1542
|
+
for (const row of rows) {
|
|
1543
|
+
const key = (title) => keyOf(row.surface, row.kind, title);
|
|
1544
|
+
if (!used.has(key(row.title))) {
|
|
1545
|
+
used.add(key(row.title));
|
|
1546
|
+
continue;
|
|
1547
|
+
}
|
|
1548
|
+
let suffix = 2;
|
|
1549
|
+
let candidate = `${row.title} (${suffix})`;
|
|
1550
|
+
while (reserved.has(key(candidate)) || used.has(key(candidate))) {
|
|
1551
|
+
suffix += 1;
|
|
1552
|
+
candidate = `${row.title} (${suffix})`;
|
|
1553
|
+
}
|
|
1554
|
+
used.add(key(candidate));
|
|
1555
|
+
update.run(candidate, row.id);
|
|
1556
|
+
logger.warn({ topicId: row.id, surface: row.surface, from: row.title, to: candidate }, "api_topics: renamed a duplicate title for surface-scoped uniqueness");
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
registerStorageSchemaInitializer(initializeApiTopicsSchema, 20);
|
|
1560
|
+
function getTopicParticipants(topicId) {
|
|
1561
|
+
return db.query("SELECT user_id, role FROM topic_members WHERE topic_id = ? ORDER BY rowid").all(topicId).map((row) => ({ userId: row.user_id, role: row.role }));
|
|
1562
|
+
}
|
|
1563
|
+
function rowToDto(r, participants = getTopicParticipants(r.id), tellTargets) {
|
|
1564
|
+
const normalized = normalizeTopicState({
|
|
1565
|
+
id: r.id,
|
|
1566
|
+
kind: normalizeTopicKind(r.kind),
|
|
1567
|
+
agent: r.agent ?? undefined,
|
|
1568
|
+
aiMode: normalizeAiMode(r.response_policy)
|
|
1569
|
+
});
|
|
1570
|
+
return {
|
|
1571
|
+
id: r.id,
|
|
1572
|
+
title: r.title,
|
|
1573
|
+
kind: normalized.kind,
|
|
1574
|
+
description: r.description ?? undefined,
|
|
1575
|
+
agent: normalized.agent,
|
|
1576
|
+
defaultModel: r.base_model ?? "deepseek-pro",
|
|
1577
|
+
defaultEffort: r.base_effort ?? undefined,
|
|
1578
|
+
aiMode: normalized.aiMode,
|
|
1579
|
+
aiMention: aiMentionFromMode(normalized.aiMode),
|
|
1580
|
+
participants,
|
|
1581
|
+
createdAt: r.created_at,
|
|
1582
|
+
lastMessageAt: r.last_message_at ?? new Date().toISOString(),
|
|
1583
|
+
parentTopicId: r.parent_topic_id ?? undefined,
|
|
1584
|
+
memoryTopicId: r.memory_topic_id ?? undefined,
|
|
1585
|
+
memoryKey: r.memory_key ?? undefined,
|
|
1586
|
+
isFork: r.is_fork !== 0,
|
|
1587
|
+
...r.is_subagent !== 0 ? { isSubagent: true } : {},
|
|
1588
|
+
...r.is_subagent !== 0 ? {
|
|
1589
|
+
subagentTellTargetIds: tellTargets ? tellTargets.get(r.id) ?? [] : listSubagentTellTargetIds(r.id)
|
|
1590
|
+
} : {},
|
|
1591
|
+
...r.is_subagent !== 0 ? {
|
|
1592
|
+
subagentReportMode: r.subagent_report_mode === "tell" || r.subagent_report_mode === "status-only" ? r.subagent_report_mode : "auto"
|
|
1593
|
+
} : {},
|
|
1594
|
+
visibility: normalizeTopicVisibility(r.visibility),
|
|
1595
|
+
surface: normalizeTopicSurface(r.surface),
|
|
1596
|
+
surfaceScope: normalizeSurfaceScope(r.surface_scope)
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
function normalizeTopicVisibility(value) {
|
|
1600
|
+
return value === "hidden" ? "hidden" : "visible";
|
|
1601
|
+
}
|
|
1602
|
+
function normalizeTopicKind(value) {
|
|
1603
|
+
return value === "channel" || value === "agent" || value === "manager" ? value : null;
|
|
1604
|
+
}
|
|
1605
|
+
function normalizeAiMode(value) {
|
|
1606
|
+
return value === "off" || value === "mention" || value === "always" ? value : null;
|
|
1607
|
+
}
|
|
1608
|
+
function aiMentionFromMode(mode) {
|
|
1609
|
+
return mode === "mention";
|
|
1610
|
+
}
|
|
1611
|
+
function normalizeTopicState(input) {
|
|
1612
|
+
if (input.kind === "manager" || input.id === GENERAL_TOPIC_ID) {
|
|
1613
|
+
return {
|
|
1614
|
+
kind: "manager",
|
|
1615
|
+
aiMode: "always",
|
|
1616
|
+
agent: input.agent ?? resolveFallbackAgent()
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
const requestedKind = input.kind;
|
|
1620
|
+
const kind = requestedKind ?? (input.aiMode === "always" ? "agent" : input.aiMode === "mention" || input.aiMode === "off" ? "channel" : input.agent && input.aiMention !== true ? "agent" : "channel");
|
|
1621
|
+
if (kind === "agent") {
|
|
1622
|
+
return {
|
|
1623
|
+
kind: "agent",
|
|
1624
|
+
aiMode: "always",
|
|
1625
|
+
agent: input.agent ?? resolveFallbackAgent()
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
const agent = input.aiMode === "off" ? undefined : input.agent ?? undefined;
|
|
1629
|
+
return {
|
|
1630
|
+
kind: "channel",
|
|
1631
|
+
aiMode: agent ? "mention" : "off",
|
|
1632
|
+
agent
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
function normalizedTitle(title) {
|
|
1636
|
+
return title.trim().toLowerCase();
|
|
1637
|
+
}
|
|
1638
|
+
function getTopic(id) {
|
|
1639
|
+
const r = db.query("SELECT * FROM api_topics WHERE id = ?").get(id);
|
|
1640
|
+
return r ? rowToDto(r) : null;
|
|
1641
|
+
}
|
|
1642
|
+
function listSubagentTellTargetIds(subagentTopicId) {
|
|
1643
|
+
return db.query("SELECT target_topic_id FROM subagent_tell_grants WHERE subagent_topic_id = ? ORDER BY created_at").all(subagentTopicId).map((row) => row.target_topic_id);
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1028
1646
|
// ../../packages/core/src/platform/mcp-config.ts
|
|
1029
1647
|
function buildStdioMcpServer(agent, serverFile, serverArgs, env) {
|
|
1030
1648
|
if (agent === "codex") {
|
|
@@ -1531,7 +2149,7 @@ function getMcpServersForQuery(opts) {
|
|
|
1531
2149
|
if (opts.sessionType === "cron") {
|
|
1532
2150
|
if (!opts.topicId)
|
|
1533
2151
|
throw new Error("getMcpServersForQuery: cron sessionType requires topicId");
|
|
1534
|
-
return getCronMcpServers({
|
|
2152
|
+
return hideProviderIntrospectionForOtium(getCronMcpServers({
|
|
1535
2153
|
userId: opts.userId || "local",
|
|
1536
2154
|
session: opts.session || "cron",
|
|
1537
2155
|
topicId: opts.topicId,
|
|
@@ -1545,7 +2163,7 @@ function getMcpServersForQuery(opts) {
|
|
|
1545
2163
|
playwrightCapability: opts.playwrightCapability,
|
|
1546
2164
|
visualTools: opts.visualTools,
|
|
1547
2165
|
fileDeliveryTools: opts.fileDeliveryTools
|
|
1548
|
-
});
|
|
2166
|
+
}), opts.topicId);
|
|
1549
2167
|
}
|
|
1550
2168
|
if (opts.sessionType === "dm" || opts.sessionType === "ephemeral") {
|
|
1551
2169
|
return getDmMcpServers({
|
|
@@ -1558,7 +2176,7 @@ function getMcpServersForQuery(opts) {
|
|
|
1558
2176
|
});
|
|
1559
2177
|
}
|
|
1560
2178
|
if (opts.sessionType === "manager") {
|
|
1561
|
-
return mergeHostMcpServers(getManagerMcpServers({
|
|
2179
|
+
return hideProviderIntrospectionForOtium(mergeHostMcpServers(getManagerMcpServers({
|
|
1562
2180
|
userId: opts.userId || "local",
|
|
1563
2181
|
session: opts.session,
|
|
1564
2182
|
topicId: opts.topicId,
|
|
@@ -1573,9 +2191,9 @@ function getMcpServersForQuery(opts) {
|
|
|
1573
2191
|
autoContinue: opts.autoContinue,
|
|
1574
2192
|
visualTools: opts.visualTools,
|
|
1575
2193
|
fileDeliveryTools: opts.fileDeliveryTools
|
|
1576
|
-
}), opts.hostMcpServers);
|
|
2194
|
+
}), opts.hostMcpServers), opts.topicId);
|
|
1577
2195
|
}
|
|
1578
|
-
return getForumMcpServers({
|
|
2196
|
+
return hideProviderIntrospectionForOtium(getForumMcpServers({
|
|
1579
2197
|
userId: opts.userId || "local",
|
|
1580
2198
|
actorUserId: opts.actorUserId,
|
|
1581
2199
|
vaultUserId: opts.vaultUserId,
|
|
@@ -1600,201 +2218,21 @@ function getMcpServersForQuery(opts) {
|
|
|
1600
2218
|
extra: opts.mcpExtra,
|
|
1601
2219
|
silent: opts.silent,
|
|
1602
2220
|
peerBridge: opts.peerBridge
|
|
1603
|
-
});
|
|
2221
|
+
}), opts.topicId);
|
|
2222
|
+
}
|
|
2223
|
+
function hideProviderIntrospectionForOtium(servers, topicId) {
|
|
2224
|
+
if (!topicId || getTopic(topicId)?.surface !== "otium")
|
|
2225
|
+
return servers;
|
|
2226
|
+
const filtered = { ...servers };
|
|
2227
|
+
delete filtered["agent-health"];
|
|
2228
|
+
delete filtered["token-stats"];
|
|
2229
|
+
return filtered;
|
|
1604
2230
|
}
|
|
1605
2231
|
|
|
1606
2232
|
// ../../packages/core/src/storage/vault.ts
|
|
1607
2233
|
import { chmodSync as chmodSync2, mkdirSync as mkdirSync4 } from "fs";
|
|
1608
2234
|
import { join as join3 } from "path";
|
|
1609
2235
|
|
|
1610
|
-
// ../../packages/core/src/storage/sqlite.ts
|
|
1611
|
-
var isBun = typeof process.versions.bun === "string";
|
|
1612
|
-
var Database;
|
|
1613
|
-
if (isBun) {
|
|
1614
|
-
({ Database } = await import("bun:sqlite"));
|
|
1615
|
-
} else {
|
|
1616
|
-
const nodeSqliteSpecifier = ["node", "sqlite"].join(":");
|
|
1617
|
-
const { DatabaseSync } = await import(nodeSqliteSpecifier);
|
|
1618
|
-
|
|
1619
|
-
class NodeDatabase {
|
|
1620
|
-
#db;
|
|
1621
|
-
constructor(path, options = {}) {
|
|
1622
|
-
this.#db = options.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path);
|
|
1623
|
-
}
|
|
1624
|
-
query(sql) {
|
|
1625
|
-
return this.#db.prepare(sql);
|
|
1626
|
-
}
|
|
1627
|
-
prepare(sql) {
|
|
1628
|
-
return this.#db.prepare(sql);
|
|
1629
|
-
}
|
|
1630
|
-
exec(sql) {
|
|
1631
|
-
this.#db.exec(sql);
|
|
1632
|
-
}
|
|
1633
|
-
run(sql, ...params) {
|
|
1634
|
-
return this.#db.prepare(sql).run(...params);
|
|
1635
|
-
}
|
|
1636
|
-
transaction(fn) {
|
|
1637
|
-
const run = (begin) => (...args) => {
|
|
1638
|
-
this.#db.exec(begin);
|
|
1639
|
-
try {
|
|
1640
|
-
const result = fn(...args);
|
|
1641
|
-
this.#db.exec("COMMIT");
|
|
1642
|
-
return result;
|
|
1643
|
-
} catch (err) {
|
|
1644
|
-
this.#db.exec("ROLLBACK");
|
|
1645
|
-
throw err;
|
|
1646
|
-
}
|
|
1647
|
-
};
|
|
1648
|
-
const tx = run("BEGIN");
|
|
1649
|
-
tx.deferred = run("BEGIN DEFERRED");
|
|
1650
|
-
tx.immediate = run("BEGIN IMMEDIATE");
|
|
1651
|
-
tx.exclusive = run("BEGIN EXCLUSIVE");
|
|
1652
|
-
return tx;
|
|
1653
|
-
}
|
|
1654
|
-
close() {
|
|
1655
|
-
this.#db.close();
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
Database = NodeDatabase;
|
|
1659
|
-
}
|
|
1660
|
-
function closeDatabase(database) {
|
|
1661
|
-
try {
|
|
1662
|
-
database.close(true);
|
|
1663
|
-
} catch {
|
|
1664
|
-
try {
|
|
1665
|
-
database.close();
|
|
1666
|
-
} catch {}
|
|
1667
|
-
}
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
// ../../packages/core/src/storage/storage-host.ts
|
|
1671
|
-
import { mkdirSync as mkdirSync3 } from "fs";
|
|
1672
|
-
import { homedir as homedir2 } from "os";
|
|
1673
|
-
import { dirname as dirname3, join as join2, resolve as resolve5 } from "path";
|
|
1674
|
-
var STORAGE_HOST_STATE = Symbol.for("negotium.storage-host.state.v1");
|
|
1675
|
-
function storageState() {
|
|
1676
|
-
const holder = globalThis;
|
|
1677
|
-
const existing = holder[STORAGE_HOST_STATE];
|
|
1678
|
-
if (existing)
|
|
1679
|
-
return existing;
|
|
1680
|
-
const created = {
|
|
1681
|
-
configuredHost: {},
|
|
1682
|
-
fallbackDatabase: null,
|
|
1683
|
-
fallbackDatabasePath: null,
|
|
1684
|
-
frames: [],
|
|
1685
|
-
schemaInitializers: [],
|
|
1686
|
-
initializedSchemas: new WeakMap,
|
|
1687
|
-
initializingDatabases: new WeakSet
|
|
1688
|
-
};
|
|
1689
|
-
Object.defineProperty(holder, STORAGE_HOST_STATE, {
|
|
1690
|
-
value: created,
|
|
1691
|
-
enumerable: false,
|
|
1692
|
-
writable: false,
|
|
1693
|
-
configurable: false
|
|
1694
|
-
});
|
|
1695
|
-
return created;
|
|
1696
|
-
}
|
|
1697
|
-
function envPath(name, fallback) {
|
|
1698
|
-
const value = process.env[name]?.trim();
|
|
1699
|
-
return resolve5(value || fallback);
|
|
1700
|
-
}
|
|
1701
|
-
function defaultStateDir() {
|
|
1702
|
-
return envPath("NEGOTIUM_STATE_DIR", join2(homedir2(), ".negotium"));
|
|
1703
|
-
}
|
|
1704
|
-
function defaultDataDir() {
|
|
1705
|
-
return envPath("NEGOTIUM_DATA_DIR", join2(defaultStateDir(), "data"));
|
|
1706
|
-
}
|
|
1707
|
-
function defaultSessionsDatabasePath() {
|
|
1708
|
-
return envPath("SESSIONS_DB_PATH", join2(resolveStorageDataDir(), "sessions.db"));
|
|
1709
|
-
}
|
|
1710
|
-
var SQLITE_INIT_RETRY_MS = 25;
|
|
1711
|
-
var SQLITE_INIT_TIMEOUT_MS = 5000;
|
|
1712
|
-
var SQLITE_INIT_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
1713
|
-
function isSqliteBusy(error) {
|
|
1714
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1715
|
-
return /database is (?:locked|busy)|SQLITE_(?:BUSY|LOCKED)/i.test(message);
|
|
1716
|
-
}
|
|
1717
|
-
function execWithBusyRetry(database, sql, timeoutMs = SQLITE_INIT_TIMEOUT_MS) {
|
|
1718
|
-
const deadline = Date.now() + timeoutMs;
|
|
1719
|
-
while (true) {
|
|
1720
|
-
try {
|
|
1721
|
-
database.exec(sql);
|
|
1722
|
-
return;
|
|
1723
|
-
} catch (error) {
|
|
1724
|
-
if (!isSqliteBusy(error) || Date.now() >= deadline)
|
|
1725
|
-
throw error;
|
|
1726
|
-
Atomics.wait(SQLITE_INIT_SLEEP, 0, 0, Math.min(SQLITE_INIT_RETRY_MS, deadline - Date.now()));
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
function initializeDatabase(database) {
|
|
1731
|
-
database.exec("PRAGMA busy_timeout = 5000");
|
|
1732
|
-
execWithBusyRetry(database, "PRAGMA journal_mode = WAL");
|
|
1733
|
-
database.exec("PRAGMA foreign_keys = ON");
|
|
1734
|
-
database.exec("PRAGMA wal_autocheckpoint = 1000");
|
|
1735
|
-
try {
|
|
1736
|
-
database.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
1737
|
-
} catch {}
|
|
1738
|
-
}
|
|
1739
|
-
function defaultDatabase() {
|
|
1740
|
-
const path = defaultSessionsDatabasePath();
|
|
1741
|
-
const state = storageState();
|
|
1742
|
-
if (state.fallbackDatabase && state.fallbackDatabasePath === path)
|
|
1743
|
-
return state.fallbackDatabase;
|
|
1744
|
-
if (state.fallbackDatabase)
|
|
1745
|
-
closeDatabase(state.fallbackDatabase);
|
|
1746
|
-
mkdirSync3(dirname3(path), { recursive: true });
|
|
1747
|
-
state.fallbackDatabase = new Database(path, { create: true });
|
|
1748
|
-
state.fallbackDatabasePath = path;
|
|
1749
|
-
initializeDatabase(state.fallbackDatabase);
|
|
1750
|
-
return state.fallbackDatabase;
|
|
1751
|
-
}
|
|
1752
|
-
function resolveStorageDatabase() {
|
|
1753
|
-
return storageState().configuredHost.database ?? defaultDatabase();
|
|
1754
|
-
}
|
|
1755
|
-
function resolveStorageDataDir() {
|
|
1756
|
-
return storageState().configuredHost.dataDir ?? defaultDataDir();
|
|
1757
|
-
}
|
|
1758
|
-
function ensureStorageSchemas(database = resolveStorageDatabase()) {
|
|
1759
|
-
const state = storageState();
|
|
1760
|
-
if (state.initializingDatabases.has(database))
|
|
1761
|
-
return;
|
|
1762
|
-
let initialized = state.initializedSchemas.get(database);
|
|
1763
|
-
if (!initialized) {
|
|
1764
|
-
initialized = new Set;
|
|
1765
|
-
state.initializedSchemas.set(database, initialized);
|
|
1766
|
-
}
|
|
1767
|
-
state.initializingDatabases.add(database);
|
|
1768
|
-
try {
|
|
1769
|
-
for (const entry of state.schemaInitializers) {
|
|
1770
|
-
if (initialized.has(entry.initialize))
|
|
1771
|
-
continue;
|
|
1772
|
-
initialized.add(entry.initialize);
|
|
1773
|
-
try {
|
|
1774
|
-
entry.initialize(database);
|
|
1775
|
-
} catch (error) {
|
|
1776
|
-
initialized.delete(entry.initialize);
|
|
1777
|
-
throw error;
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1780
|
-
} finally {
|
|
1781
|
-
state.initializingDatabases.delete(database);
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
var internalStorageDatabase = new Proxy({}, {
|
|
1785
|
-
get(_target, property) {
|
|
1786
|
-
const database = resolveStorageDatabase();
|
|
1787
|
-
ensureStorageSchemas(database);
|
|
1788
|
-
const value = Reflect.get(database, property, database);
|
|
1789
|
-
return typeof value === "function" ? value.bind(database) : value;
|
|
1790
|
-
},
|
|
1791
|
-
set(_target, property, value) {
|
|
1792
|
-
const database = resolveStorageDatabase();
|
|
1793
|
-
ensureStorageSchemas(database);
|
|
1794
|
-
return Reflect.set(database, property, value, database);
|
|
1795
|
-
}
|
|
1796
|
-
});
|
|
1797
|
-
|
|
1798
2236
|
// ../../packages/core/src/storage/vault-crypto-core.ts
|
|
1799
2237
|
import { createCipheriv, createDecipheriv, createHash as createHash2, randomBytes as randomBytes3 } from "crypto";
|
|
1800
2238
|
var ENVELOPE_PREFIX = "otium-vault:v1:";
|
|
@@ -2586,4 +3024,4 @@ function formatDateBucket(d) {
|
|
|
2586
3024
|
|
|
2587
3025
|
export { __require, CLAUDE_EFFORT_VALUES, CODEX_EFFORT_VALUES, MAESTRO_EFFORT_VALUES, logger, MODEL_SONNET, MODEL_OPUS, MODEL_FABLE, configureRolloutHost, FIXTURES_DIR, clone, assertUuidLike, ensureCwdExists, extractChatPairs, parseJsonlText, writeJsonlFile, hostedCodexHomePath, extractLatestCodexPatchPreview, extractCodexPatchCallIds, readCodexPatchCallIds, readLatestCodexPatchPreview, extractLatestCodexContextUsage, readLatestCodexContextUsage, migrateCodexRolloutNativeMultiAgentMetadata, latestCodexRolloutPath, writeCodexRollout, decodeUuidV7Timestamp };
|
|
2588
3026
|
|
|
2589
|
-
//# debugId=
|
|
3027
|
+
//# debugId=54820657F6417AF864756E2164756E21
|