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
package/dist/hosted-agent.js
CHANGED
|
@@ -948,6 +948,624 @@ function getRegisteredCronSession(topicId, agent) {
|
|
|
948
948
|
}
|
|
949
949
|
}
|
|
950
950
|
|
|
951
|
+
// ../../packages/core/src/platform/constants.ts
|
|
952
|
+
var FROM_AUTO_CONTINUE = "auto-continue";
|
|
953
|
+
var FROM_SELF_SCHEDULE = "self-schedule";
|
|
954
|
+
var RESERVED_TOPIC_NAMES = new Set([
|
|
955
|
+
FROM_AUTO_CONTINUE,
|
|
956
|
+
FROM_SELF_SCHEDULE,
|
|
957
|
+
"general"
|
|
958
|
+
]);
|
|
959
|
+
var GENERAL_TOPIC_ID = "general";
|
|
960
|
+
|
|
961
|
+
// ../../packages/core/src/storage/storage-host.ts
|
|
962
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
963
|
+
import { homedir as homedir2 } from "os";
|
|
964
|
+
import { dirname as dirname4, join as join2, resolve as resolve5 } from "path";
|
|
965
|
+
|
|
966
|
+
// ../../packages/core/src/storage/sqlite.ts
|
|
967
|
+
var isBun = typeof process.versions.bun === "string";
|
|
968
|
+
var Database;
|
|
969
|
+
if (isBun) {
|
|
970
|
+
({ Database } = await import("bun:sqlite"));
|
|
971
|
+
} else {
|
|
972
|
+
const nodeSqliteSpecifier = ["node", "sqlite"].join(":");
|
|
973
|
+
const { DatabaseSync } = await import(nodeSqliteSpecifier);
|
|
974
|
+
|
|
975
|
+
class NodeDatabase {
|
|
976
|
+
#db;
|
|
977
|
+
constructor(path, options = {}) {
|
|
978
|
+
this.#db = options.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path);
|
|
979
|
+
}
|
|
980
|
+
query(sql) {
|
|
981
|
+
return this.#db.prepare(sql);
|
|
982
|
+
}
|
|
983
|
+
prepare(sql) {
|
|
984
|
+
return this.#db.prepare(sql);
|
|
985
|
+
}
|
|
986
|
+
exec(sql) {
|
|
987
|
+
this.#db.exec(sql);
|
|
988
|
+
}
|
|
989
|
+
run(sql, ...params) {
|
|
990
|
+
return this.#db.prepare(sql).run(...params);
|
|
991
|
+
}
|
|
992
|
+
transaction(fn) {
|
|
993
|
+
const run = (begin) => (...args) => {
|
|
994
|
+
this.#db.exec(begin);
|
|
995
|
+
try {
|
|
996
|
+
const result = fn(...args);
|
|
997
|
+
this.#db.exec("COMMIT");
|
|
998
|
+
return result;
|
|
999
|
+
} catch (err) {
|
|
1000
|
+
this.#db.exec("ROLLBACK");
|
|
1001
|
+
throw err;
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
const tx = run("BEGIN");
|
|
1005
|
+
tx.deferred = run("BEGIN DEFERRED");
|
|
1006
|
+
tx.immediate = run("BEGIN IMMEDIATE");
|
|
1007
|
+
tx.exclusive = run("BEGIN EXCLUSIVE");
|
|
1008
|
+
return tx;
|
|
1009
|
+
}
|
|
1010
|
+
close() {
|
|
1011
|
+
this.#db.close();
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
Database = NodeDatabase;
|
|
1015
|
+
}
|
|
1016
|
+
function closeDatabase(database) {
|
|
1017
|
+
try {
|
|
1018
|
+
database.close(true);
|
|
1019
|
+
} catch {
|
|
1020
|
+
try {
|
|
1021
|
+
database.close();
|
|
1022
|
+
} catch {}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// ../../packages/core/src/storage/storage-host.ts
|
|
1027
|
+
var STORAGE_HOST_STATE = Symbol.for("negotium.storage-host.state.v1");
|
|
1028
|
+
function storageState() {
|
|
1029
|
+
const holder = globalThis;
|
|
1030
|
+
const existing = holder[STORAGE_HOST_STATE];
|
|
1031
|
+
if (existing)
|
|
1032
|
+
return existing;
|
|
1033
|
+
const created = {
|
|
1034
|
+
configuredHost: {},
|
|
1035
|
+
fallbackDatabase: null,
|
|
1036
|
+
fallbackDatabasePath: null,
|
|
1037
|
+
frames: [],
|
|
1038
|
+
schemaInitializers: [],
|
|
1039
|
+
initializedSchemas: new WeakMap,
|
|
1040
|
+
initializingDatabases: new WeakSet
|
|
1041
|
+
};
|
|
1042
|
+
Object.defineProperty(holder, STORAGE_HOST_STATE, {
|
|
1043
|
+
value: created,
|
|
1044
|
+
enumerable: false,
|
|
1045
|
+
writable: false,
|
|
1046
|
+
configurable: false
|
|
1047
|
+
});
|
|
1048
|
+
return created;
|
|
1049
|
+
}
|
|
1050
|
+
function envPath(name, fallback) {
|
|
1051
|
+
const value = process.env[name]?.trim();
|
|
1052
|
+
return resolve5(value || fallback);
|
|
1053
|
+
}
|
|
1054
|
+
function defaultStateDir() {
|
|
1055
|
+
return envPath("NEGOTIUM_STATE_DIR", join2(homedir2(), ".negotium"));
|
|
1056
|
+
}
|
|
1057
|
+
function defaultDataDir() {
|
|
1058
|
+
return envPath("NEGOTIUM_DATA_DIR", join2(defaultStateDir(), "data"));
|
|
1059
|
+
}
|
|
1060
|
+
function defaultSessionsDatabasePath() {
|
|
1061
|
+
return envPath("SESSIONS_DB_PATH", join2(resolveStorageDataDir(), "sessions.db"));
|
|
1062
|
+
}
|
|
1063
|
+
var SQLITE_INIT_RETRY_MS = 25;
|
|
1064
|
+
var SQLITE_INIT_TIMEOUT_MS = 5000;
|
|
1065
|
+
var SQLITE_INIT_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
1066
|
+
function isSqliteBusy(error) {
|
|
1067
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1068
|
+
return /database is (?:locked|busy)|SQLITE_(?:BUSY|LOCKED)/i.test(message);
|
|
1069
|
+
}
|
|
1070
|
+
function execWithBusyRetry(database, sql, timeoutMs = SQLITE_INIT_TIMEOUT_MS) {
|
|
1071
|
+
const deadline = Date.now() + timeoutMs;
|
|
1072
|
+
while (true) {
|
|
1073
|
+
try {
|
|
1074
|
+
database.exec(sql);
|
|
1075
|
+
return;
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
if (!isSqliteBusy(error) || Date.now() >= deadline)
|
|
1078
|
+
throw error;
|
|
1079
|
+
Atomics.wait(SQLITE_INIT_SLEEP, 0, 0, Math.min(SQLITE_INIT_RETRY_MS, deadline - Date.now()));
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
function initializeDatabase(database) {
|
|
1084
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
1085
|
+
execWithBusyRetry(database, "PRAGMA journal_mode = WAL");
|
|
1086
|
+
database.exec("PRAGMA foreign_keys = ON");
|
|
1087
|
+
database.exec("PRAGMA wal_autocheckpoint = 1000");
|
|
1088
|
+
try {
|
|
1089
|
+
database.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
1090
|
+
} catch {}
|
|
1091
|
+
}
|
|
1092
|
+
function defaultDatabase() {
|
|
1093
|
+
const path = defaultSessionsDatabasePath();
|
|
1094
|
+
const state = storageState();
|
|
1095
|
+
if (state.fallbackDatabase && state.fallbackDatabasePath === path)
|
|
1096
|
+
return state.fallbackDatabase;
|
|
1097
|
+
if (state.fallbackDatabase)
|
|
1098
|
+
closeDatabase(state.fallbackDatabase);
|
|
1099
|
+
mkdirSync3(dirname4(path), { recursive: true });
|
|
1100
|
+
state.fallbackDatabase = new Database(path, { create: true });
|
|
1101
|
+
state.fallbackDatabasePath = path;
|
|
1102
|
+
initializeDatabase(state.fallbackDatabase);
|
|
1103
|
+
return state.fallbackDatabase;
|
|
1104
|
+
}
|
|
1105
|
+
function resolveStorageDatabase() {
|
|
1106
|
+
return storageState().configuredHost.database ?? defaultDatabase();
|
|
1107
|
+
}
|
|
1108
|
+
function resolveStorageDataDir() {
|
|
1109
|
+
return storageState().configuredHost.dataDir ?? defaultDataDir();
|
|
1110
|
+
}
|
|
1111
|
+
function registerStorageSchemaInitializer(initialize, priority = 100) {
|
|
1112
|
+
const initializers = storageState().schemaInitializers;
|
|
1113
|
+
initializers.push({ initialize, priority });
|
|
1114
|
+
initializers.sort((a, b) => a.priority - b.priority);
|
|
1115
|
+
}
|
|
1116
|
+
function ensureStorageSchemas(database = resolveStorageDatabase()) {
|
|
1117
|
+
const state = storageState();
|
|
1118
|
+
if (state.initializingDatabases.has(database))
|
|
1119
|
+
return;
|
|
1120
|
+
let initialized = state.initializedSchemas.get(database);
|
|
1121
|
+
if (!initialized) {
|
|
1122
|
+
initialized = new Set;
|
|
1123
|
+
state.initializedSchemas.set(database, initialized);
|
|
1124
|
+
}
|
|
1125
|
+
state.initializingDatabases.add(database);
|
|
1126
|
+
try {
|
|
1127
|
+
for (const entry of state.schemaInitializers) {
|
|
1128
|
+
if (initialized.has(entry.initialize))
|
|
1129
|
+
continue;
|
|
1130
|
+
initialized.add(entry.initialize);
|
|
1131
|
+
try {
|
|
1132
|
+
entry.initialize(database);
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
initialized.delete(entry.initialize);
|
|
1135
|
+
throw error;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
} finally {
|
|
1139
|
+
state.initializingDatabases.delete(database);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
var internalStorageDatabase = new Proxy({}, {
|
|
1143
|
+
get(_target, property) {
|
|
1144
|
+
const database = resolveStorageDatabase();
|
|
1145
|
+
ensureStorageSchemas(database);
|
|
1146
|
+
const value = Reflect.get(database, property, database);
|
|
1147
|
+
return typeof value === "function" ? value.bind(database) : value;
|
|
1148
|
+
},
|
|
1149
|
+
set(_target, property, value) {
|
|
1150
|
+
const database = resolveStorageDatabase();
|
|
1151
|
+
ensureStorageSchemas(database);
|
|
1152
|
+
return Reflect.set(database, property, value, database);
|
|
1153
|
+
}
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
// ../../packages/core/src/storage/forum-db.ts
|
|
1157
|
+
var db = internalStorageDatabase;
|
|
1158
|
+
|
|
1159
|
+
// ../../packages/core/src/storage/api-topics.ts
|
|
1160
|
+
function defaultTopicSurface() {
|
|
1161
|
+
return normalizeTopicSurface(process.env.NEGOTIUM_DEFAULT_SURFACE);
|
|
1162
|
+
}
|
|
1163
|
+
function normalizeTopicSurface(value) {
|
|
1164
|
+
return value === "telegram" || value === "otium" || value === "terminal" ? value : "terminal";
|
|
1165
|
+
}
|
|
1166
|
+
function normalizeSurfaceScope(value) {
|
|
1167
|
+
if (typeof value !== "string")
|
|
1168
|
+
return null;
|
|
1169
|
+
const trimmed = value.trim();
|
|
1170
|
+
return trimmed ? trimmed : null;
|
|
1171
|
+
}
|
|
1172
|
+
function tableColumns(table) {
|
|
1173
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
1174
|
+
return new Set(rows.map((row) => row.name));
|
|
1175
|
+
}
|
|
1176
|
+
function initializeApiTopicsSchema() {
|
|
1177
|
+
db.exec(`
|
|
1178
|
+
CREATE TABLE IF NOT EXISTS api_topics (
|
|
1179
|
+
id TEXT PRIMARY KEY,
|
|
1180
|
+
title TEXT NOT NULL,
|
|
1181
|
+
kind TEXT NOT NULL DEFAULT 'channel',
|
|
1182
|
+
description TEXT,
|
|
1183
|
+
agent TEXT,
|
|
1184
|
+
default_model TEXT,
|
|
1185
|
+
default_effort TEXT,
|
|
1186
|
+
participants TEXT,
|
|
1187
|
+
created_at TEXT NOT NULL,
|
|
1188
|
+
last_message_at TEXT,
|
|
1189
|
+
is_archived INTEGER NOT NULL DEFAULT 0,
|
|
1190
|
+
ai_mention INTEGER NOT NULL DEFAULT 0,
|
|
1191
|
+
ai_mode TEXT
|
|
1192
|
+
)
|
|
1193
|
+
`);
|
|
1194
|
+
const initialTopicColumns = tableColumns("api_topics");
|
|
1195
|
+
const legacyTopicSchema = !initialTopicColumns.has("response_policy");
|
|
1196
|
+
const needsCanonicalTopicRebuild = legacyTopicSchema || !initialTopicColumns.has("agent");
|
|
1197
|
+
if (legacyTopicSchema) {
|
|
1198
|
+
try {
|
|
1199
|
+
db.exec("ALTER TABLE api_topics RENAME COLUMN default_agent TO agent");
|
|
1200
|
+
} catch {}
|
|
1201
|
+
try {
|
|
1202
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN ai_mention INTEGER NOT NULL DEFAULT 0");
|
|
1203
|
+
} catch {}
|
|
1204
|
+
try {
|
|
1205
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN parent_topic_id TEXT");
|
|
1206
|
+
} catch {}
|
|
1207
|
+
try {
|
|
1208
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN is_fork INTEGER NOT NULL DEFAULT 0");
|
|
1209
|
+
} catch {}
|
|
1210
|
+
try {
|
|
1211
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN is_subagent INTEGER NOT NULL DEFAULT 0");
|
|
1212
|
+
} catch {}
|
|
1213
|
+
try {
|
|
1214
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN session_id TEXT");
|
|
1215
|
+
} catch {}
|
|
1216
|
+
try {
|
|
1217
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN kind TEXT NOT NULL DEFAULT 'channel'");
|
|
1218
|
+
} catch {}
|
|
1219
|
+
try {
|
|
1220
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN ai_mode TEXT");
|
|
1221
|
+
} catch {}
|
|
1222
|
+
db.exec(`
|
|
1223
|
+
CREATE TABLE IF NOT EXISTS api_schema_migrations (
|
|
1224
|
+
key TEXT PRIMARY KEY,
|
|
1225
|
+
applied_at TEXT NOT NULL
|
|
1226
|
+
)
|
|
1227
|
+
`);
|
|
1228
|
+
const ALWAYS_RESPOND_MIGRATION = "api_topics_ai_invited_default_always_respond_20260623";
|
|
1229
|
+
const alwaysRespondMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(ALWAYS_RESPOND_MIGRATION);
|
|
1230
|
+
if (!alwaysRespondMigration) {
|
|
1231
|
+
db.transaction(() => {
|
|
1232
|
+
db.query("UPDATE api_topics SET ai_mention = 0 WHERE agent IS NOT NULL").run();
|
|
1233
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(ALWAYS_RESPOND_MIGRATION, new Date().toISOString());
|
|
1234
|
+
})();
|
|
1235
|
+
}
|
|
1236
|
+
const GENERAL_AGENT_KIND_MIGRATION = "api_topics_general_agent_kind_20260704";
|
|
1237
|
+
const generalAgentKindMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(GENERAL_AGENT_KIND_MIGRATION);
|
|
1238
|
+
if (!generalAgentKindMigration) {
|
|
1239
|
+
db.transaction(() => {
|
|
1240
|
+
db.query("UPDATE api_topics SET kind = 'agent', ai_mention = 0 WHERE id = ?").run(GENERAL_TOPIC_ID);
|
|
1241
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(GENERAL_AGENT_KIND_MIGRATION, new Date().toISOString());
|
|
1242
|
+
})();
|
|
1243
|
+
}
|
|
1244
|
+
const AI_MODE_MIGRATION = "api_topics_ai_mode_20260704";
|
|
1245
|
+
const aiModeMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(AI_MODE_MIGRATION);
|
|
1246
|
+
if (!aiModeMigration) {
|
|
1247
|
+
db.transaction(() => {
|
|
1248
|
+
db.query(`UPDATE api_topics
|
|
1249
|
+
SET
|
|
1250
|
+
kind = CASE
|
|
1251
|
+
WHEN id = ? THEN 'agent'
|
|
1252
|
+
WHEN kind = 'channel' AND agent IS NOT NULL AND ai_mention = 0 THEN 'agent'
|
|
1253
|
+
ELSE kind
|
|
1254
|
+
END,
|
|
1255
|
+
ai_mode = CASE
|
|
1256
|
+
WHEN id = ? THEN 'always'
|
|
1257
|
+
WHEN agent IS NULL THEN 'off'
|
|
1258
|
+
WHEN kind = 'agent' THEN 'always'
|
|
1259
|
+
WHEN kind = 'channel' AND ai_mention != 0 THEN 'mention'
|
|
1260
|
+
WHEN kind = 'channel' AND ai_mention = 0 THEN 'always'
|
|
1261
|
+
WHEN ai_mention != 0 THEN 'mention'
|
|
1262
|
+
ELSE 'always'
|
|
1263
|
+
END`).run(GENERAL_TOPIC_ID, GENERAL_TOPIC_ID);
|
|
1264
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(AI_MODE_MIGRATION, new Date().toISOString());
|
|
1265
|
+
})();
|
|
1266
|
+
}
|
|
1267
|
+
const GENERAL_MANAGER_KIND_MIGRATION = "api_topics_general_manager_kind_20260704";
|
|
1268
|
+
const generalManagerKindMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(GENERAL_MANAGER_KIND_MIGRATION);
|
|
1269
|
+
if (!generalManagerKindMigration) {
|
|
1270
|
+
db.transaction(() => {
|
|
1271
|
+
db.query("UPDATE api_topics SET kind = 'manager', ai_mention = 0, ai_mode = 'always' WHERE id = ?").run(GENERAL_TOPIC_ID);
|
|
1272
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(GENERAL_MANAGER_KIND_MIGRATION, new Date().toISOString());
|
|
1273
|
+
})();
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
function createCanonicalTopicsTable(name) {
|
|
1277
|
+
db.exec(`
|
|
1278
|
+
CREATE TABLE IF NOT EXISTS ${name} (
|
|
1279
|
+
id TEXT PRIMARY KEY,
|
|
1280
|
+
title TEXT NOT NULL,
|
|
1281
|
+
kind TEXT NOT NULL CHECK (kind IN ('channel','agent','manager')),
|
|
1282
|
+
description TEXT,
|
|
1283
|
+
agent TEXT CHECK (agent IS NULL OR agent IN ('maestro','claude','codex')),
|
|
1284
|
+
base_model TEXT,
|
|
1285
|
+
base_effort TEXT CHECK (base_effort IS NULL OR base_effort IN ('low','medium','high','xhigh','max')),
|
|
1286
|
+
response_policy TEXT NOT NULL CHECK (response_policy IN ('off','mention','always')),
|
|
1287
|
+
created_at TEXT NOT NULL,
|
|
1288
|
+
last_message_at TEXT,
|
|
1289
|
+
parent_topic_id TEXT,
|
|
1290
|
+
memory_topic_id TEXT,
|
|
1291
|
+
memory_key TEXT,
|
|
1292
|
+
is_fork INTEGER NOT NULL DEFAULT 0 CHECK (is_fork IN (0,1)),
|
|
1293
|
+
is_subagent INTEGER NOT NULL DEFAULT 0 CHECK (is_subagent IN (0,1)),
|
|
1294
|
+
visibility TEXT NOT NULL DEFAULT 'visible' CHECK (visibility IN ('visible','hidden')),
|
|
1295
|
+
surface TEXT NOT NULL DEFAULT 'terminal' CHECK (surface IN ('terminal','telegram','otium')),
|
|
1296
|
+
surface_scope TEXT,
|
|
1297
|
+
browser_profile TEXT NOT NULL DEFAULT 'default',
|
|
1298
|
+
browser_profile_owner TEXT,
|
|
1299
|
+
session_id TEXT,
|
|
1300
|
+
CHECK (
|
|
1301
|
+
(kind = 'channel' AND response_policy = 'off' AND agent IS NULL) OR
|
|
1302
|
+
(kind = 'channel' AND response_policy = 'mention' AND agent IS NOT NULL) OR
|
|
1303
|
+
(kind IN ('agent','manager') AND response_policy = 'always' AND agent IS NOT NULL)
|
|
1304
|
+
)
|
|
1305
|
+
)
|
|
1306
|
+
`);
|
|
1307
|
+
}
|
|
1308
|
+
function createTopicMembersTable() {
|
|
1309
|
+
db.exec(`
|
|
1310
|
+
CREATE TABLE IF NOT EXISTS topic_members (
|
|
1311
|
+
topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
|
|
1312
|
+
user_id TEXT NOT NULL,
|
|
1313
|
+
role TEXT NOT NULL CHECK (role IN ('owner','member')),
|
|
1314
|
+
PRIMARY KEY (topic_id, user_id)
|
|
1315
|
+
)
|
|
1316
|
+
`);
|
|
1317
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_topic_members_user ON topic_members(user_id)");
|
|
1318
|
+
}
|
|
1319
|
+
if (needsCanonicalTopicRebuild) {
|
|
1320
|
+
const legacyRows = db.query("SELECT * FROM api_topics").all();
|
|
1321
|
+
const existingMemberRows = tableColumns("topic_members").has("topic_id") ? db.query("SELECT topic_id, user_id, role FROM topic_members").all() : [];
|
|
1322
|
+
const configColumns = tableColumns("api_topic_config");
|
|
1323
|
+
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;
|
|
1324
|
+
const previousForeignKeys = db.query("PRAGMA foreign_keys").get()?.foreign_keys;
|
|
1325
|
+
db.exec("PRAGMA foreign_keys = OFF");
|
|
1326
|
+
try {
|
|
1327
|
+
db.transaction(() => {
|
|
1328
|
+
createCanonicalTopicsTable("api_topics_next");
|
|
1329
|
+
for (const row of legacyRows) {
|
|
1330
|
+
const selectedAgent = legacyAgentOverrides.get(String(row.id)) ?? row.agent ?? row.runtime_agent ?? row.default_agent ?? undefined;
|
|
1331
|
+
const normalized = normalizeTopicState({
|
|
1332
|
+
id: String(row.id),
|
|
1333
|
+
kind: normalizeTopicKind(row.kind),
|
|
1334
|
+
agent: selectedAgent,
|
|
1335
|
+
aiMode: normalizeAiMode(row.response_policy ?? row.ai_mode),
|
|
1336
|
+
aiMention: Number(row.ai_mention ?? 0) !== 0
|
|
1337
|
+
});
|
|
1338
|
+
const legacyBaseModel = row.base_model ?? row.default_model;
|
|
1339
|
+
const legacyBaseEffort = row.base_effort ?? row.default_effort;
|
|
1340
|
+
db.query(`INSERT INTO api_topics_next
|
|
1341
|
+
(id,title,kind,description,agent,base_model,base_effort,response_policy,
|
|
1342
|
+
created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,surface,surface_scope,session_id)
|
|
1343
|
+
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);
|
|
1344
|
+
}
|
|
1345
|
+
db.exec("DROP TABLE IF EXISTS topic_members");
|
|
1346
|
+
db.exec("DROP TABLE api_topics");
|
|
1347
|
+
db.exec("ALTER TABLE api_topics_next RENAME TO api_topics");
|
|
1348
|
+
createTopicMembersTable();
|
|
1349
|
+
if (existingMemberRows.length > 0) {
|
|
1350
|
+
for (const member of existingMemberRows) {
|
|
1351
|
+
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");
|
|
1352
|
+
}
|
|
1353
|
+
} else {
|
|
1354
|
+
for (const row of legacyRows) {
|
|
1355
|
+
let participants = [];
|
|
1356
|
+
try {
|
|
1357
|
+
const parsed = JSON.parse(String(row.participants ?? "[]"));
|
|
1358
|
+
if (Array.isArray(parsed))
|
|
1359
|
+
participants = parsed;
|
|
1360
|
+
} catch {
|
|
1361
|
+
participants = [];
|
|
1362
|
+
}
|
|
1363
|
+
for (const participant of participants) {
|
|
1364
|
+
if (!participant?.userId)
|
|
1365
|
+
continue;
|
|
1366
|
+
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");
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
})();
|
|
1371
|
+
} finally {
|
|
1372
|
+
db.exec(`PRAGMA foreign_keys = ${previousForeignKeys ? "ON" : "OFF"}`);
|
|
1373
|
+
}
|
|
1374
|
+
} else {
|
|
1375
|
+
createCanonicalTopicsTable("api_topics");
|
|
1376
|
+
createTopicMembersTable();
|
|
1377
|
+
}
|
|
1378
|
+
db.exec(`
|
|
1379
|
+
CREATE TABLE IF NOT EXISTS subagent_tell_grants (
|
|
1380
|
+
subagent_topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
|
|
1381
|
+
target_topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
|
|
1382
|
+
granted_by_topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
|
|
1383
|
+
created_at TEXT NOT NULL,
|
|
1384
|
+
PRIMARY KEY (subagent_topic_id, target_topic_id)
|
|
1385
|
+
)
|
|
1386
|
+
`);
|
|
1387
|
+
db.exec(`
|
|
1388
|
+
DELETE FROM subagent_tell_grants
|
|
1389
|
+
WHERE subagent_topic_id NOT IN (SELECT id FROM api_topics)
|
|
1390
|
+
OR target_topic_id NOT IN (SELECT id FROM api_topics)
|
|
1391
|
+
OR granted_by_topic_id NOT IN (SELECT id FROM api_topics)
|
|
1392
|
+
`);
|
|
1393
|
+
db.exec("DELETE FROM topic_members WHERE topic_id NOT IN (SELECT id FROM api_topics)");
|
|
1394
|
+
if (!tableColumns("api_topics").has("visibility")) {
|
|
1395
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN visibility TEXT NOT NULL DEFAULT 'visible'");
|
|
1396
|
+
}
|
|
1397
|
+
if (!tableColumns("api_topics").has("surface")) {
|
|
1398
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN surface TEXT NOT NULL DEFAULT 'terminal'");
|
|
1399
|
+
}
|
|
1400
|
+
if (!tableColumns("api_topics").has("surface_scope")) {
|
|
1401
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN surface_scope TEXT");
|
|
1402
|
+
}
|
|
1403
|
+
if (tableColumns("api_topics").has("access_mode")) {
|
|
1404
|
+
try {
|
|
1405
|
+
db.exec("ALTER TABLE api_topics DROP COLUMN access_mode");
|
|
1406
|
+
} catch (err) {
|
|
1407
|
+
logger.warn({ err }, "api_topics: could not drop the retired access_mode column");
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
if (!tableColumns("api_topics").has("browser_profile")) {
|
|
1411
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile TEXT NOT NULL DEFAULT 'default'");
|
|
1412
|
+
}
|
|
1413
|
+
if (!tableColumns("api_topics").has("browser_profile_owner")) {
|
|
1414
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile_owner TEXT");
|
|
1415
|
+
}
|
|
1416
|
+
if (!tableColumns("api_topics").has("subagent_report_mode")) {
|
|
1417
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN subagent_report_mode TEXT NOT NULL DEFAULT 'auto'");
|
|
1418
|
+
}
|
|
1419
|
+
if (!tableColumns("api_topics").has("memory_topic_id")) {
|
|
1420
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN memory_topic_id TEXT");
|
|
1421
|
+
}
|
|
1422
|
+
if (!tableColumns("api_topics").has("memory_key")) {
|
|
1423
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN memory_key TEXT");
|
|
1424
|
+
}
|
|
1425
|
+
db.exec(`
|
|
1426
|
+
UPDATE api_topics
|
|
1427
|
+
SET browser_profile_owner = (
|
|
1428
|
+
SELECT m.user_id FROM topic_members m
|
|
1429
|
+
WHERE m.topic_id = api_topics.id AND m.role = 'owner'
|
|
1430
|
+
ORDER BY m.rowid
|
|
1431
|
+
LIMIT 1
|
|
1432
|
+
)
|
|
1433
|
+
WHERE browser_profile_owner IS NULL
|
|
1434
|
+
`);
|
|
1435
|
+
backfillTopicSurfaces();
|
|
1436
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_last_message ON api_topics(last_message_at DESC)");
|
|
1437
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_surface ON api_topics(surface)");
|
|
1438
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_surface_scope ON api_topics(surface, surface_scope)");
|
|
1439
|
+
}
|
|
1440
|
+
var SURFACE_BACKFILL_MIGRATION = "api_topics_surface_backfill_20260808";
|
|
1441
|
+
function backfillTopicSurfaces() {
|
|
1442
|
+
db.exec(`
|
|
1443
|
+
CREATE TABLE IF NOT EXISTS api_schema_migrations (
|
|
1444
|
+
key TEXT PRIMARY KEY,
|
|
1445
|
+
applied_at TEXT NOT NULL
|
|
1446
|
+
)
|
|
1447
|
+
`);
|
|
1448
|
+
const applied = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(SURFACE_BACKFILL_MIGRATION);
|
|
1449
|
+
if (applied)
|
|
1450
|
+
return;
|
|
1451
|
+
const surface = defaultTopicSurface();
|
|
1452
|
+
db.transaction(() => {
|
|
1453
|
+
db.query("UPDATE api_topics SET surface = ?").run(surface);
|
|
1454
|
+
renameSurfaceTitleCollisions();
|
|
1455
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(SURFACE_BACKFILL_MIGRATION, new Date().toISOString());
|
|
1456
|
+
})();
|
|
1457
|
+
logger.info({ surface }, "api_topics: surface backfilled");
|
|
1458
|
+
}
|
|
1459
|
+
function renameSurfaceTitleCollisions() {
|
|
1460
|
+
const rows = db.query("SELECT id, title, kind, surface FROM api_topics WHERE kind != 'manager' ORDER BY created_at ASC, rowid ASC").all();
|
|
1461
|
+
const keyOf = (surface, kind, title) => [surface, kind, normalizedTitle(title)].join("\x00");
|
|
1462
|
+
const reserved = new Set(rows.map((row) => keyOf(row.surface, row.kind, row.title)));
|
|
1463
|
+
const used = new Set;
|
|
1464
|
+
const update = db.query("UPDATE api_topics SET title = ? WHERE id = ?");
|
|
1465
|
+
for (const row of rows) {
|
|
1466
|
+
const key = (title) => keyOf(row.surface, row.kind, title);
|
|
1467
|
+
if (!used.has(key(row.title))) {
|
|
1468
|
+
used.add(key(row.title));
|
|
1469
|
+
continue;
|
|
1470
|
+
}
|
|
1471
|
+
let suffix = 2;
|
|
1472
|
+
let candidate = `${row.title} (${suffix})`;
|
|
1473
|
+
while (reserved.has(key(candidate)) || used.has(key(candidate))) {
|
|
1474
|
+
suffix += 1;
|
|
1475
|
+
candidate = `${row.title} (${suffix})`;
|
|
1476
|
+
}
|
|
1477
|
+
used.add(key(candidate));
|
|
1478
|
+
update.run(candidate, row.id);
|
|
1479
|
+
logger.warn({ topicId: row.id, surface: row.surface, from: row.title, to: candidate }, "api_topics: renamed a duplicate title for surface-scoped uniqueness");
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
registerStorageSchemaInitializer(initializeApiTopicsSchema, 20);
|
|
1483
|
+
function getTopicParticipants(topicId) {
|
|
1484
|
+
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 }));
|
|
1485
|
+
}
|
|
1486
|
+
function rowToDto(r, participants = getTopicParticipants(r.id), tellTargets) {
|
|
1487
|
+
const normalized = normalizeTopicState({
|
|
1488
|
+
id: r.id,
|
|
1489
|
+
kind: normalizeTopicKind(r.kind),
|
|
1490
|
+
agent: r.agent ?? undefined,
|
|
1491
|
+
aiMode: normalizeAiMode(r.response_policy)
|
|
1492
|
+
});
|
|
1493
|
+
return {
|
|
1494
|
+
id: r.id,
|
|
1495
|
+
title: r.title,
|
|
1496
|
+
kind: normalized.kind,
|
|
1497
|
+
description: r.description ?? undefined,
|
|
1498
|
+
agent: normalized.agent,
|
|
1499
|
+
defaultModel: r.base_model ?? "deepseek-pro",
|
|
1500
|
+
defaultEffort: r.base_effort ?? undefined,
|
|
1501
|
+
aiMode: normalized.aiMode,
|
|
1502
|
+
aiMention: aiMentionFromMode(normalized.aiMode),
|
|
1503
|
+
participants,
|
|
1504
|
+
createdAt: r.created_at,
|
|
1505
|
+
lastMessageAt: r.last_message_at ?? new Date().toISOString(),
|
|
1506
|
+
parentTopicId: r.parent_topic_id ?? undefined,
|
|
1507
|
+
memoryTopicId: r.memory_topic_id ?? undefined,
|
|
1508
|
+
memoryKey: r.memory_key ?? undefined,
|
|
1509
|
+
isFork: r.is_fork !== 0,
|
|
1510
|
+
...r.is_subagent !== 0 ? { isSubagent: true } : {},
|
|
1511
|
+
...r.is_subagent !== 0 ? {
|
|
1512
|
+
subagentTellTargetIds: tellTargets ? tellTargets.get(r.id) ?? [] : listSubagentTellTargetIds(r.id)
|
|
1513
|
+
} : {},
|
|
1514
|
+
...r.is_subagent !== 0 ? {
|
|
1515
|
+
subagentReportMode: r.subagent_report_mode === "tell" || r.subagent_report_mode === "status-only" ? r.subagent_report_mode : "auto"
|
|
1516
|
+
} : {},
|
|
1517
|
+
visibility: normalizeTopicVisibility(r.visibility),
|
|
1518
|
+
surface: normalizeTopicSurface(r.surface),
|
|
1519
|
+
surfaceScope: normalizeSurfaceScope(r.surface_scope)
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
function normalizeTopicVisibility(value) {
|
|
1523
|
+
return value === "hidden" ? "hidden" : "visible";
|
|
1524
|
+
}
|
|
1525
|
+
function normalizeTopicKind(value) {
|
|
1526
|
+
return value === "channel" || value === "agent" || value === "manager" ? value : null;
|
|
1527
|
+
}
|
|
1528
|
+
function normalizeAiMode(value) {
|
|
1529
|
+
return value === "off" || value === "mention" || value === "always" ? value : null;
|
|
1530
|
+
}
|
|
1531
|
+
function aiMentionFromMode(mode) {
|
|
1532
|
+
return mode === "mention";
|
|
1533
|
+
}
|
|
1534
|
+
function normalizeTopicState(input) {
|
|
1535
|
+
if (input.kind === "manager" || input.id === GENERAL_TOPIC_ID) {
|
|
1536
|
+
return {
|
|
1537
|
+
kind: "manager",
|
|
1538
|
+
aiMode: "always",
|
|
1539
|
+
agent: input.agent ?? resolveFallbackAgent()
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
const requestedKind = input.kind;
|
|
1543
|
+
const kind = requestedKind ?? (input.aiMode === "always" ? "agent" : input.aiMode === "mention" || input.aiMode === "off" ? "channel" : input.agent && input.aiMention !== true ? "agent" : "channel");
|
|
1544
|
+
if (kind === "agent") {
|
|
1545
|
+
return {
|
|
1546
|
+
kind: "agent",
|
|
1547
|
+
aiMode: "always",
|
|
1548
|
+
agent: input.agent ?? resolveFallbackAgent()
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
const agent = input.aiMode === "off" ? undefined : input.agent ?? undefined;
|
|
1552
|
+
return {
|
|
1553
|
+
kind: "channel",
|
|
1554
|
+
aiMode: agent ? "mention" : "off",
|
|
1555
|
+
agent
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
function normalizedTitle(title) {
|
|
1559
|
+
return title.trim().toLowerCase();
|
|
1560
|
+
}
|
|
1561
|
+
function getTopic(id) {
|
|
1562
|
+
const r = db.query("SELECT * FROM api_topics WHERE id = ?").get(id);
|
|
1563
|
+
return r ? rowToDto(r) : null;
|
|
1564
|
+
}
|
|
1565
|
+
function listSubagentTellTargetIds(subagentTopicId) {
|
|
1566
|
+
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);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
951
1569
|
// ../../packages/core/src/platform/mcp-config.ts
|
|
952
1570
|
function buildStdioMcpServer(agent, serverFile, serverArgs, env) {
|
|
953
1571
|
if (agent === "codex") {
|
|
@@ -1454,7 +2072,7 @@ function getMcpServersForQuery(opts) {
|
|
|
1454
2072
|
if (opts.sessionType === "cron") {
|
|
1455
2073
|
if (!opts.topicId)
|
|
1456
2074
|
throw new Error("getMcpServersForQuery: cron sessionType requires topicId");
|
|
1457
|
-
return getCronMcpServers({
|
|
2075
|
+
return hideProviderIntrospectionForOtium(getCronMcpServers({
|
|
1458
2076
|
userId: opts.userId || "local",
|
|
1459
2077
|
session: opts.session || "cron",
|
|
1460
2078
|
topicId: opts.topicId,
|
|
@@ -1468,7 +2086,7 @@ function getMcpServersForQuery(opts) {
|
|
|
1468
2086
|
playwrightCapability: opts.playwrightCapability,
|
|
1469
2087
|
visualTools: opts.visualTools,
|
|
1470
2088
|
fileDeliveryTools: opts.fileDeliveryTools
|
|
1471
|
-
});
|
|
2089
|
+
}), opts.topicId);
|
|
1472
2090
|
}
|
|
1473
2091
|
if (opts.sessionType === "dm" || opts.sessionType === "ephemeral") {
|
|
1474
2092
|
return getDmMcpServers({
|
|
@@ -1481,7 +2099,7 @@ function getMcpServersForQuery(opts) {
|
|
|
1481
2099
|
});
|
|
1482
2100
|
}
|
|
1483
2101
|
if (opts.sessionType === "manager") {
|
|
1484
|
-
return mergeHostMcpServers(getManagerMcpServers({
|
|
2102
|
+
return hideProviderIntrospectionForOtium(mergeHostMcpServers(getManagerMcpServers({
|
|
1485
2103
|
userId: opts.userId || "local",
|
|
1486
2104
|
session: opts.session,
|
|
1487
2105
|
topicId: opts.topicId,
|
|
@@ -1496,9 +2114,9 @@ function getMcpServersForQuery(opts) {
|
|
|
1496
2114
|
autoContinue: opts.autoContinue,
|
|
1497
2115
|
visualTools: opts.visualTools,
|
|
1498
2116
|
fileDeliveryTools: opts.fileDeliveryTools
|
|
1499
|
-
}), opts.hostMcpServers);
|
|
2117
|
+
}), opts.hostMcpServers), opts.topicId);
|
|
1500
2118
|
}
|
|
1501
|
-
return getForumMcpServers({
|
|
2119
|
+
return hideProviderIntrospectionForOtium(getForumMcpServers({
|
|
1502
2120
|
userId: opts.userId || "local",
|
|
1503
2121
|
actorUserId: opts.actorUserId,
|
|
1504
2122
|
vaultUserId: opts.vaultUserId,
|
|
@@ -1523,201 +2141,21 @@ function getMcpServersForQuery(opts) {
|
|
|
1523
2141
|
extra: opts.mcpExtra,
|
|
1524
2142
|
silent: opts.silent,
|
|
1525
2143
|
peerBridge: opts.peerBridge
|
|
1526
|
-
});
|
|
2144
|
+
}), opts.topicId);
|
|
2145
|
+
}
|
|
2146
|
+
function hideProviderIntrospectionForOtium(servers, topicId) {
|
|
2147
|
+
if (!topicId || getTopic(topicId)?.surface !== "otium")
|
|
2148
|
+
return servers;
|
|
2149
|
+
const filtered = { ...servers };
|
|
2150
|
+
delete filtered["agent-health"];
|
|
2151
|
+
delete filtered["token-stats"];
|
|
2152
|
+
return filtered;
|
|
1527
2153
|
}
|
|
1528
2154
|
|
|
1529
2155
|
// ../../packages/core/src/storage/vault.ts
|
|
1530
2156
|
import { chmodSync as chmodSync2, mkdirSync as mkdirSync4 } from "fs";
|
|
1531
2157
|
import { join as join3 } from "path";
|
|
1532
2158
|
|
|
1533
|
-
// ../../packages/core/src/storage/sqlite.ts
|
|
1534
|
-
var isBun = typeof process.versions.bun === "string";
|
|
1535
|
-
var Database;
|
|
1536
|
-
if (isBun) {
|
|
1537
|
-
({ Database } = await import("bun:sqlite"));
|
|
1538
|
-
} else {
|
|
1539
|
-
const nodeSqliteSpecifier = ["node", "sqlite"].join(":");
|
|
1540
|
-
const { DatabaseSync } = await import(nodeSqliteSpecifier);
|
|
1541
|
-
|
|
1542
|
-
class NodeDatabase {
|
|
1543
|
-
#db;
|
|
1544
|
-
constructor(path, options = {}) {
|
|
1545
|
-
this.#db = options.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path);
|
|
1546
|
-
}
|
|
1547
|
-
query(sql) {
|
|
1548
|
-
return this.#db.prepare(sql);
|
|
1549
|
-
}
|
|
1550
|
-
prepare(sql) {
|
|
1551
|
-
return this.#db.prepare(sql);
|
|
1552
|
-
}
|
|
1553
|
-
exec(sql) {
|
|
1554
|
-
this.#db.exec(sql);
|
|
1555
|
-
}
|
|
1556
|
-
run(sql, ...params) {
|
|
1557
|
-
return this.#db.prepare(sql).run(...params);
|
|
1558
|
-
}
|
|
1559
|
-
transaction(fn) {
|
|
1560
|
-
const run = (begin) => (...args) => {
|
|
1561
|
-
this.#db.exec(begin);
|
|
1562
|
-
try {
|
|
1563
|
-
const result = fn(...args);
|
|
1564
|
-
this.#db.exec("COMMIT");
|
|
1565
|
-
return result;
|
|
1566
|
-
} catch (err) {
|
|
1567
|
-
this.#db.exec("ROLLBACK");
|
|
1568
|
-
throw err;
|
|
1569
|
-
}
|
|
1570
|
-
};
|
|
1571
|
-
const tx = run("BEGIN");
|
|
1572
|
-
tx.deferred = run("BEGIN DEFERRED");
|
|
1573
|
-
tx.immediate = run("BEGIN IMMEDIATE");
|
|
1574
|
-
tx.exclusive = run("BEGIN EXCLUSIVE");
|
|
1575
|
-
return tx;
|
|
1576
|
-
}
|
|
1577
|
-
close() {
|
|
1578
|
-
this.#db.close();
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
Database = NodeDatabase;
|
|
1582
|
-
}
|
|
1583
|
-
function closeDatabase(database) {
|
|
1584
|
-
try {
|
|
1585
|
-
database.close(true);
|
|
1586
|
-
} catch {
|
|
1587
|
-
try {
|
|
1588
|
-
database.close();
|
|
1589
|
-
} catch {}
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
// ../../packages/core/src/storage/storage-host.ts
|
|
1594
|
-
import { mkdirSync as mkdirSync3 } from "fs";
|
|
1595
|
-
import { homedir as homedir2 } from "os";
|
|
1596
|
-
import { dirname as dirname4, join as join2, resolve as resolve5 } from "path";
|
|
1597
|
-
var STORAGE_HOST_STATE = Symbol.for("negotium.storage-host.state.v1");
|
|
1598
|
-
function storageState() {
|
|
1599
|
-
const holder = globalThis;
|
|
1600
|
-
const existing = holder[STORAGE_HOST_STATE];
|
|
1601
|
-
if (existing)
|
|
1602
|
-
return existing;
|
|
1603
|
-
const created = {
|
|
1604
|
-
configuredHost: {},
|
|
1605
|
-
fallbackDatabase: null,
|
|
1606
|
-
fallbackDatabasePath: null,
|
|
1607
|
-
frames: [],
|
|
1608
|
-
schemaInitializers: [],
|
|
1609
|
-
initializedSchemas: new WeakMap,
|
|
1610
|
-
initializingDatabases: new WeakSet
|
|
1611
|
-
};
|
|
1612
|
-
Object.defineProperty(holder, STORAGE_HOST_STATE, {
|
|
1613
|
-
value: created,
|
|
1614
|
-
enumerable: false,
|
|
1615
|
-
writable: false,
|
|
1616
|
-
configurable: false
|
|
1617
|
-
});
|
|
1618
|
-
return created;
|
|
1619
|
-
}
|
|
1620
|
-
function envPath(name, fallback) {
|
|
1621
|
-
const value = process.env[name]?.trim();
|
|
1622
|
-
return resolve5(value || fallback);
|
|
1623
|
-
}
|
|
1624
|
-
function defaultStateDir() {
|
|
1625
|
-
return envPath("NEGOTIUM_STATE_DIR", join2(homedir2(), ".negotium"));
|
|
1626
|
-
}
|
|
1627
|
-
function defaultDataDir() {
|
|
1628
|
-
return envPath("NEGOTIUM_DATA_DIR", join2(defaultStateDir(), "data"));
|
|
1629
|
-
}
|
|
1630
|
-
function defaultSessionsDatabasePath() {
|
|
1631
|
-
return envPath("SESSIONS_DB_PATH", join2(resolveStorageDataDir(), "sessions.db"));
|
|
1632
|
-
}
|
|
1633
|
-
var SQLITE_INIT_RETRY_MS = 25;
|
|
1634
|
-
var SQLITE_INIT_TIMEOUT_MS = 5000;
|
|
1635
|
-
var SQLITE_INIT_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
1636
|
-
function isSqliteBusy(error) {
|
|
1637
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1638
|
-
return /database is (?:locked|busy)|SQLITE_(?:BUSY|LOCKED)/i.test(message);
|
|
1639
|
-
}
|
|
1640
|
-
function execWithBusyRetry(database, sql, timeoutMs = SQLITE_INIT_TIMEOUT_MS) {
|
|
1641
|
-
const deadline = Date.now() + timeoutMs;
|
|
1642
|
-
while (true) {
|
|
1643
|
-
try {
|
|
1644
|
-
database.exec(sql);
|
|
1645
|
-
return;
|
|
1646
|
-
} catch (error) {
|
|
1647
|
-
if (!isSqliteBusy(error) || Date.now() >= deadline)
|
|
1648
|
-
throw error;
|
|
1649
|
-
Atomics.wait(SQLITE_INIT_SLEEP, 0, 0, Math.min(SQLITE_INIT_RETRY_MS, deadline - Date.now()));
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
function initializeDatabase(database) {
|
|
1654
|
-
database.exec("PRAGMA busy_timeout = 5000");
|
|
1655
|
-
execWithBusyRetry(database, "PRAGMA journal_mode = WAL");
|
|
1656
|
-
database.exec("PRAGMA foreign_keys = ON");
|
|
1657
|
-
database.exec("PRAGMA wal_autocheckpoint = 1000");
|
|
1658
|
-
try {
|
|
1659
|
-
database.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
1660
|
-
} catch {}
|
|
1661
|
-
}
|
|
1662
|
-
function defaultDatabase() {
|
|
1663
|
-
const path = defaultSessionsDatabasePath();
|
|
1664
|
-
const state = storageState();
|
|
1665
|
-
if (state.fallbackDatabase && state.fallbackDatabasePath === path)
|
|
1666
|
-
return state.fallbackDatabase;
|
|
1667
|
-
if (state.fallbackDatabase)
|
|
1668
|
-
closeDatabase(state.fallbackDatabase);
|
|
1669
|
-
mkdirSync3(dirname4(path), { recursive: true });
|
|
1670
|
-
state.fallbackDatabase = new Database(path, { create: true });
|
|
1671
|
-
state.fallbackDatabasePath = path;
|
|
1672
|
-
initializeDatabase(state.fallbackDatabase);
|
|
1673
|
-
return state.fallbackDatabase;
|
|
1674
|
-
}
|
|
1675
|
-
function resolveStorageDatabase() {
|
|
1676
|
-
return storageState().configuredHost.database ?? defaultDatabase();
|
|
1677
|
-
}
|
|
1678
|
-
function resolveStorageDataDir() {
|
|
1679
|
-
return storageState().configuredHost.dataDir ?? defaultDataDir();
|
|
1680
|
-
}
|
|
1681
|
-
function ensureStorageSchemas(database = resolveStorageDatabase()) {
|
|
1682
|
-
const state = storageState();
|
|
1683
|
-
if (state.initializingDatabases.has(database))
|
|
1684
|
-
return;
|
|
1685
|
-
let initialized = state.initializedSchemas.get(database);
|
|
1686
|
-
if (!initialized) {
|
|
1687
|
-
initialized = new Set;
|
|
1688
|
-
state.initializedSchemas.set(database, initialized);
|
|
1689
|
-
}
|
|
1690
|
-
state.initializingDatabases.add(database);
|
|
1691
|
-
try {
|
|
1692
|
-
for (const entry of state.schemaInitializers) {
|
|
1693
|
-
if (initialized.has(entry.initialize))
|
|
1694
|
-
continue;
|
|
1695
|
-
initialized.add(entry.initialize);
|
|
1696
|
-
try {
|
|
1697
|
-
entry.initialize(database);
|
|
1698
|
-
} catch (error) {
|
|
1699
|
-
initialized.delete(entry.initialize);
|
|
1700
|
-
throw error;
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
} finally {
|
|
1704
|
-
state.initializingDatabases.delete(database);
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
var internalStorageDatabase = new Proxy({}, {
|
|
1708
|
-
get(_target, property) {
|
|
1709
|
-
const database = resolveStorageDatabase();
|
|
1710
|
-
ensureStorageSchemas(database);
|
|
1711
|
-
const value = Reflect.get(database, property, database);
|
|
1712
|
-
return typeof value === "function" ? value.bind(database) : value;
|
|
1713
|
-
},
|
|
1714
|
-
set(_target, property, value) {
|
|
1715
|
-
const database = resolveStorageDatabase();
|
|
1716
|
-
ensureStorageSchemas(database);
|
|
1717
|
-
return Reflect.set(database, property, value, database);
|
|
1718
|
-
}
|
|
1719
|
-
});
|
|
1720
|
-
|
|
1721
2159
|
// ../../packages/core/src/storage/vault-crypto-core.ts
|
|
1722
2160
|
import { createCipheriv, createDecipheriv, createHash as createHash2, randomBytes as randomBytes3 } from "crypto";
|
|
1723
2161
|
var ENVELOPE_PREFIX = "otium-vault:v1:";
|
|
@@ -2578,7 +3016,7 @@ import { tmpdir } from "os";
|
|
|
2578
3016
|
import { dirname as dirname6, join as join4 } from "path";
|
|
2579
3017
|
|
|
2580
3018
|
// ../../packages/core/src/version.ts
|
|
2581
|
-
var NEGOTIUM_VERSION = "0.18.
|
|
3019
|
+
var NEGOTIUM_VERSION = "0.18.2";
|
|
2582
3020
|
|
|
2583
3021
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
2584
3022
|
var moduleRequire = createRequire2(import.meta.url);
|
|
@@ -4452,4 +4890,4 @@ export {
|
|
|
4452
4890
|
buildClaudeDisallowedTools
|
|
4453
4891
|
};
|
|
4454
4892
|
|
|
4455
|
-
//# debugId=
|
|
4893
|
+
//# debugId=CAE7C2B6A4FAB8F464756E2164756E21
|