negotium 0.1.11 → 0.1.13
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/README.md +11 -2
- package/dist/canonical-mcp-bridge.js +2 -3
- package/dist/canonical-mcp-bridge.js.map +1 -1
- package/dist/{chunk-gz12qcf0.js → chunk-a9y2rwxf.js} +2 -2
- package/dist/{chunk-gz12qcf0.js.map → chunk-a9y2rwxf.js.map} +3 -3
- package/dist/{chunk-j0kjscb7.js → chunk-s2gez3wg.js} +20 -2
- package/dist/{chunk-j0kjscb7.js.map → chunk-s2gez3wg.js.map} +2 -2
- package/dist/cron.js +292 -256
- package/dist/cron.js.map +23 -22
- package/dist/hosted-agent.js +3012 -17
- package/dist/hosted-agent.js.map +30 -3
- package/dist/main.js +31323 -36
- package/dist/main.js.map +198 -3
- package/dist/prompts.js +12 -9
- package/dist/prompts.js.map +5 -5
- package/dist/registry.js +10 -6
- package/dist/registry.js.map +3 -3
- package/dist/rollout.js +1 -1
- package/dist/runtime/cron/store.ts +24 -1
- package/dist/runtime/src/application/submit-user-message.ts +3 -0
- package/dist/runtime/src/index.ts +7 -0
- package/dist/runtime/src/mcp/runtime-spec.ts +3 -0
- package/dist/runtime/src/mcp/session-comm/server.ts +1 -1
- package/dist/runtime/src/platform/config.ts +2 -2
- package/dist/runtime/src/platform/mcp-config.ts +13 -0
- package/dist/runtime/src/prompts/builders.ts +20 -6
- package/dist/runtime/src/query/active-rooms.ts +2 -0
- package/dist/runtime/src/runtime/delivery-ack.ts +112 -0
- package/dist/runtime/src/runtime/errors.ts +12 -12
- package/dist/runtime/src/runtime/turn-runner.ts +12 -0
- package/dist/runtime/src/storage/api-messages.ts +7 -5
- package/dist/runtime/src/storage/api-topics.ts +5 -2
- package/dist/runtime/src/storage/forum-db.ts +4 -1
- package/dist/runtime/src/storage/runtime-turn-requests.ts +1 -0
- package/dist/runtime/src/storage/session-asks.ts +13 -5
- package/dist/runtime/src/storage/storage-contract.ts +54 -0
- package/dist/runtime/src/storage/storage-host.ts +89 -36
- package/dist/runtime/src/storage/storage-public-host.ts +16 -0
- package/dist/runtime/src/storage/storage-public.ts +21 -5
- package/dist/runtime/src/storage/token-stats.ts +1 -1
- package/dist/runtime/src/types/api.ts +2 -0
- package/dist/runtime/src/types.ts +2 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +29 -18
- package/dist/runtime-helpers.js.map +8 -7
- package/dist/storage.js +260 -202
- package/dist/storage.js.map +11 -9
- package/dist/types/packages/core/src/mcp/runtime-spec.d.ts +2 -0
- package/dist/types/packages/core/src/platform/mcp-config.d.ts +4 -0
- package/dist/types/packages/core/src/prompts/builders.d.ts +2 -0
- package/dist/types/packages/core/src/storage/api-messages.d.ts +6 -6
- package/dist/types/packages/core/src/storage/api-topics.d.ts +20 -2
- package/dist/types/packages/core/src/storage/forum-db.d.ts +2 -1
- package/dist/types/packages/core/src/storage/session-asks.d.ts +3 -2
- package/dist/types/packages/core/src/storage/storage-contract.d.ts +46 -0
- package/dist/types/packages/core/src/storage/storage-host.d.ts +12 -19
- package/dist/types/packages/core/src/storage/storage-public-host.d.ts +7 -0
- package/dist/types/packages/core/src/storage/storage-public.d.ts +4 -2
- package/dist/types/packages/core/src/types/api.d.ts +2 -0
- package/dist/types/packages/core/src/types.d.ts +2 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/types/packages/module-cron/src/store.d.ts +22 -2
- package/dist/vault.js +2 -2
- package/dist/vault.js.map +3 -3
- package/package.json +1 -1
- package/dist/chunk-01xcc856.js +0 -149
- package/dist/chunk-01xcc856.js.map +0 -10
- package/dist/chunk-1a6kv9vt.js +0 -118
- package/dist/chunk-1a6kv9vt.js.map +0 -10
- package/dist/chunk-1b62d730.js +0 -117
- package/dist/chunk-1b62d730.js.map +0 -10
- package/dist/chunk-1k7t7bnx.js +0 -97
- package/dist/chunk-1k7t7bnx.js.map +0 -10
- package/dist/chunk-28apmz4r.js +0 -431
- package/dist/chunk-28apmz4r.js.map +0 -11
- package/dist/chunk-2fvydfkx.js +0 -141
- package/dist/chunk-2fvydfkx.js.map +0 -10
- package/dist/chunk-41ygvg5k.js +0 -2387
- package/dist/chunk-41ygvg5k.js.map +0 -26
- package/dist/chunk-4q966ttx.js +0 -371
- package/dist/chunk-4q966ttx.js.map +0 -11
- package/dist/chunk-51rcsavw.js +0 -1712
- package/dist/chunk-51rcsavw.js.map +0 -18
- package/dist/chunk-59d0shyj.js +0 -103
- package/dist/chunk-59d0shyj.js.map +0 -10
- package/dist/chunk-5fzgvn79.js +0 -3536
- package/dist/chunk-5fzgvn79.js.map +0 -20
- package/dist/chunk-61px0717.js +0 -153
- package/dist/chunk-61px0717.js.map +0 -11
- package/dist/chunk-66y0hr8c.js +0 -2136
- package/dist/chunk-66y0hr8c.js.map +0 -29
- package/dist/chunk-69k45zg8.js +0 -250
- package/dist/chunk-69k45zg8.js.map +0 -11
- package/dist/chunk-6ah5farg.js +0 -130
- package/dist/chunk-6ah5farg.js.map +0 -10
- package/dist/chunk-6d4fmea5.js +0 -117
- package/dist/chunk-6d4fmea5.js.map +0 -12
- package/dist/chunk-8cahx7xc.js +0 -46
- package/dist/chunk-8cahx7xc.js.map +0 -10
- package/dist/chunk-8f20fk4n.js +0 -252
- package/dist/chunk-8f20fk4n.js.map +0 -10
- package/dist/chunk-91ckrvk8.js +0 -318
- package/dist/chunk-91ckrvk8.js.map +0 -10
- package/dist/chunk-93krf6ng.js +0 -3069
- package/dist/chunk-93krf6ng.js.map +0 -32
- package/dist/chunk-9qx1t77a.js +0 -214
- package/dist/chunk-9qx1t77a.js.map +0 -10
- package/dist/chunk-beqhdr9w.js +0 -589
- package/dist/chunk-beqhdr9w.js.map +0 -11
- package/dist/chunk-bftftsgq.js +0 -1637
- package/dist/chunk-bftftsgq.js.map +0 -15
- package/dist/chunk-c500xe9z.js +0 -33
- package/dist/chunk-c500xe9z.js.map +0 -10
- package/dist/chunk-cjsnkvq2.js +0 -23
- package/dist/chunk-cjsnkvq2.js.map +0 -10
- package/dist/chunk-cxa7yghg.js +0 -577
- package/dist/chunk-cxa7yghg.js.map +0 -14
- package/dist/chunk-edb07sjp.js +0 -567
- package/dist/chunk-edb07sjp.js.map +0 -14
- package/dist/chunk-f361dwba.js +0 -589
- package/dist/chunk-f361dwba.js.map +0 -11
- package/dist/chunk-f5xgprg1.js +0 -59
- package/dist/chunk-f5xgprg1.js.map +0 -10
- package/dist/chunk-ffefbd3r.js +0 -17
- package/dist/chunk-ffefbd3r.js.map +0 -10
- package/dist/chunk-fswhzdf5.js +0 -64
- package/dist/chunk-fswhzdf5.js.map +0 -10
- package/dist/chunk-g144wdrs.js +0 -2907
- package/dist/chunk-g144wdrs.js.map +0 -24
- package/dist/chunk-h980hb2n.js +0 -512
- package/dist/chunk-h980hb2n.js.map +0 -13
- package/dist/chunk-h9vbhhgr.js +0 -1337
- package/dist/chunk-h9vbhhgr.js.map +0 -16
- package/dist/chunk-hwy3n31w.js +0 -21
- package/dist/chunk-hwy3n31w.js.map +0 -9
- package/dist/chunk-jtrk0khr.js +0 -108
- package/dist/chunk-jtrk0khr.js.map +0 -10
- package/dist/chunk-jzz91jwh.js +0 -70
- package/dist/chunk-jzz91jwh.js.map +0 -10
- package/dist/chunk-m9e3tbr8.js +0 -708
- package/dist/chunk-m9e3tbr8.js.map +0 -14
- package/dist/chunk-mh5mexsf.js +0 -147
- package/dist/chunk-mh5mexsf.js.map +0 -11
- package/dist/chunk-npd8n03v.js +0 -2152
- package/dist/chunk-npd8n03v.js.map +0 -15
- package/dist/chunk-pg1azh6n.js +0 -2
- package/dist/chunk-pg1azh6n.js.map +0 -9
- package/dist/chunk-qwy57177.js +0 -237
- package/dist/chunk-qwy57177.js.map +0 -11
- package/dist/chunk-r31q21h5.js +0 -53
- package/dist/chunk-r31q21h5.js.map +0 -10
- package/dist/chunk-rbvctv1n.js +0 -366
- package/dist/chunk-rbvctv1n.js.map +0 -10
- package/dist/chunk-sa874j5c.js +0 -9
- package/dist/chunk-sa874j5c.js.map +0 -10
- package/dist/chunk-sctcs5zb.js +0 -281
- package/dist/chunk-sctcs5zb.js.map +0 -10
- package/dist/chunk-tsstrcfc.js +0 -122
- package/dist/chunk-tsstrcfc.js.map +0 -10
- package/dist/chunk-wk2qf31s.js +0 -58
- package/dist/chunk-wk2qf31s.js.map +0 -10
- package/dist/chunk-wx7ag4dd.js +0 -2936
- package/dist/chunk-wx7ag4dd.js.map +0 -20
- package/dist/chunk-znx1fpfd.js +0 -61
- package/dist/chunk-znx1fpfd.js.map +0 -10
package/dist/cron.js
CHANGED
|
@@ -273,7 +273,7 @@ var init_config = __esm(() => {
|
|
|
273
273
|
TESSERACT_BIN = envText("TESSERACT_BIN") ?? "tesseract";
|
|
274
274
|
PDFTOTEXT_BIN = envText("PDFTOTEXT_BIN") ?? "pdftotext";
|
|
275
275
|
_envMaxTellDepth = Number.parseInt(process.env.MAX_TELL_DEPTH ?? "", 10);
|
|
276
|
-
MAX_TELL_DEPTH = Number.isInteger(_envMaxTellDepth) && _envMaxTellDepth > 0 ? _envMaxTellDepth :
|
|
276
|
+
MAX_TELL_DEPTH = Number.isInteger(_envMaxTellDepth) && _envMaxTellDepth > 0 ? _envMaxTellDepth : 20;
|
|
277
277
|
});
|
|
278
278
|
|
|
279
279
|
// ../../packages/core/src/agents/auth-check.ts
|
|
@@ -1385,7 +1385,7 @@ function defaultSessionAsksDir() {
|
|
|
1385
1385
|
return join6(runDir, "session-asks");
|
|
1386
1386
|
}
|
|
1387
1387
|
function defaultSessionsDatabasePath() {
|
|
1388
|
-
return envPath("SESSIONS_DB_PATH", join6(
|
|
1388
|
+
return envPath("SESSIONS_DB_PATH", join6(resolveStorageDataDir(), "sessions.db"));
|
|
1389
1389
|
}
|
|
1390
1390
|
function initializeDatabase(database) {
|
|
1391
1391
|
database.exec("PRAGMA journal_mode = WAL");
|
|
@@ -1397,11 +1397,14 @@ function initializeDatabase(database) {
|
|
|
1397
1397
|
} catch {}
|
|
1398
1398
|
}
|
|
1399
1399
|
function defaultDatabase() {
|
|
1400
|
-
if (fallbackDatabase)
|
|
1401
|
-
return fallbackDatabase;
|
|
1402
1400
|
const path = defaultSessionsDatabasePath();
|
|
1401
|
+
if (fallbackDatabase && fallbackDatabasePath === path)
|
|
1402
|
+
return fallbackDatabase;
|
|
1403
|
+
if (fallbackDatabase)
|
|
1404
|
+
fallbackDatabase.close();
|
|
1403
1405
|
mkdirSync5(dirname4(path), { recursive: true });
|
|
1404
1406
|
fallbackDatabase = new Database(path, { create: true });
|
|
1407
|
+
fallbackDatabasePath = path;
|
|
1405
1408
|
initializeDatabase(fallbackDatabase);
|
|
1406
1409
|
return fallbackDatabase;
|
|
1407
1410
|
}
|
|
@@ -1421,7 +1424,7 @@ function resolveStorageWorkspaceDir() {
|
|
|
1421
1424
|
return configuredHost.workspaceDir ?? defaultWorkspaceDir();
|
|
1422
1425
|
}
|
|
1423
1426
|
function resolveStorageSharedWikiDir() {
|
|
1424
|
-
return join6(resolveStorageWorkspaceDir(), "wiki");
|
|
1427
|
+
return configuredHost.sharedWikiDir ?? join6(resolveStorageWorkspaceDir(), "wiki");
|
|
1425
1428
|
}
|
|
1426
1429
|
function registerStorageSchemaInitializer(initialize, priority = 100) {
|
|
1427
1430
|
schemaInitializers.push({ initialize, priority });
|
|
@@ -1452,14 +1455,14 @@ function ensureStorageSchemas(database = resolveStorageDatabase()) {
|
|
|
1452
1455
|
initializingDatabases.delete(database);
|
|
1453
1456
|
}
|
|
1454
1457
|
}
|
|
1455
|
-
var configuredHost, fallbackDatabase = null, schemaInitializers, initializedSchemas, initializingDatabases,
|
|
1458
|
+
var configuredHost, fallbackDatabase = null, fallbackDatabasePath = null, schemaInitializers, initializedSchemas, initializingDatabases, internalStorageDatabase;
|
|
1456
1459
|
var init_storage_host = __esm(async () => {
|
|
1457
1460
|
await init_sqlite();
|
|
1458
1461
|
configuredHost = {};
|
|
1459
1462
|
schemaInitializers = [];
|
|
1460
1463
|
initializedSchemas = new WeakMap;
|
|
1461
1464
|
initializingDatabases = new WeakSet;
|
|
1462
|
-
|
|
1465
|
+
internalStorageDatabase = new Proxy({}, {
|
|
1463
1466
|
get(_target, property) {
|
|
1464
1467
|
const database = resolveStorageDatabase();
|
|
1465
1468
|
ensureStorageSchemas(database);
|
|
@@ -2179,7 +2182,8 @@ function getManagerMcpServers(opts) {
|
|
|
2179
2182
|
playwrightPort: opts.playwrightPort,
|
|
2180
2183
|
playwrightCapability: opts.playwrightCapability,
|
|
2181
2184
|
autoContinue: opts.autoContinue,
|
|
2182
|
-
visualTools: opts.visualTools
|
|
2185
|
+
visualTools: opts.visualTools,
|
|
2186
|
+
fileDeliveryTools: opts.fileDeliveryTools
|
|
2183
2187
|
});
|
|
2184
2188
|
}
|
|
2185
2189
|
function getForumMcpServers(opts) {
|
|
@@ -2202,6 +2206,7 @@ function getForumMcpServers(opts) {
|
|
|
2202
2206
|
bgBashPort,
|
|
2203
2207
|
autoContinue,
|
|
2204
2208
|
visualTools,
|
|
2209
|
+
fileDeliveryTools,
|
|
2205
2210
|
peerBridge
|
|
2206
2211
|
} = opts;
|
|
2207
2212
|
const filter = (name) => {
|
|
@@ -2227,6 +2232,7 @@ function getForumMcpServers(opts) {
|
|
|
2227
2232
|
bgBashPort,
|
|
2228
2233
|
autoContinue,
|
|
2229
2234
|
visualTools,
|
|
2235
|
+
fileDeliveryTools,
|
|
2230
2236
|
silent,
|
|
2231
2237
|
peerBridge
|
|
2232
2238
|
}, filter);
|
|
@@ -2246,7 +2252,8 @@ function getCronMcpServers(opts) {
|
|
|
2246
2252
|
playwrightPort: opts.playwrightPort,
|
|
2247
2253
|
playwrightCapability: opts.playwrightCapability,
|
|
2248
2254
|
autoContinue: false,
|
|
2249
|
-
visualTools: opts.visualTools
|
|
2255
|
+
visualTools: opts.visualTools,
|
|
2256
|
+
fileDeliveryTools: opts.fileDeliveryTools
|
|
2250
2257
|
});
|
|
2251
2258
|
}
|
|
2252
2259
|
function getMcpServersForQuery(opts) {
|
|
@@ -2265,7 +2272,8 @@ function getMcpServersForQuery(opts) {
|
|
|
2265
2272
|
currentUserPrompt: opts.prompt,
|
|
2266
2273
|
playwrightPort: opts.playwrightPort,
|
|
2267
2274
|
playwrightCapability: opts.playwrightCapability,
|
|
2268
|
-
visualTools: opts.visualTools
|
|
2275
|
+
visualTools: opts.visualTools,
|
|
2276
|
+
fileDeliveryTools: opts.fileDeliveryTools
|
|
2269
2277
|
});
|
|
2270
2278
|
}
|
|
2271
2279
|
if (opts.sessionType === "dm" || opts.sessionType === "ephemeral") {
|
|
@@ -2290,7 +2298,8 @@ function getMcpServersForQuery(opts) {
|
|
|
2290
2298
|
playwrightPort: opts.playwrightPort,
|
|
2291
2299
|
playwrightCapability: opts.playwrightCapability,
|
|
2292
2300
|
autoContinue: opts.autoContinue,
|
|
2293
|
-
visualTools: opts.visualTools
|
|
2301
|
+
visualTools: opts.visualTools,
|
|
2302
|
+
fileDeliveryTools: opts.fileDeliveryTools
|
|
2294
2303
|
});
|
|
2295
2304
|
}
|
|
2296
2305
|
return getForumMcpServers({
|
|
@@ -2308,6 +2317,7 @@ function getMcpServersForQuery(opts) {
|
|
|
2308
2317
|
bgBashPort: opts.bgBashPort,
|
|
2309
2318
|
autoContinue: opts.autoContinue,
|
|
2310
2319
|
visualTools: opts.visualTools,
|
|
2320
|
+
fileDeliveryTools: opts.fileDeliveryTools,
|
|
2311
2321
|
depth: opts.depth,
|
|
2312
2322
|
enabled: opts.mcpEnabled,
|
|
2313
2323
|
extra: opts.mcpExtra,
|
|
@@ -2357,6 +2367,7 @@ var init_mcp_config = __esm(() => {
|
|
|
2357
2367
|
currentUserPrompt,
|
|
2358
2368
|
autoContinue,
|
|
2359
2369
|
visualTools,
|
|
2370
|
+
fileDeliveryTools,
|
|
2360
2371
|
peerBridge
|
|
2361
2372
|
}) {
|
|
2362
2373
|
if (!topicId || !agent)
|
|
@@ -2372,6 +2383,7 @@ var init_mcp_config = __esm(() => {
|
|
|
2372
2383
|
currentUserPrompt,
|
|
2373
2384
|
autoContinue,
|
|
2374
2385
|
visualTools,
|
|
2386
|
+
fileDeliveryTools,
|
|
2375
2387
|
peerBridge
|
|
2376
2388
|
});
|
|
2377
2389
|
}
|
|
@@ -3042,7 +3054,7 @@ var init_claude_provider = __esm(async () => {
|
|
|
3042
3054
|
});
|
|
3043
3055
|
|
|
3044
3056
|
// ../../packages/core/src/version.ts
|
|
3045
|
-
var NEGOTIUM_VERSION = "0.1.
|
|
3057
|
+
var NEGOTIUM_VERSION = "0.1.13";
|
|
3046
3058
|
|
|
3047
3059
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
3048
3060
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -3973,8 +3985,10 @@ function errorResult(text) {
|
|
|
3973
3985
|
}
|
|
3974
3986
|
|
|
3975
3987
|
// ../../packages/core/src/storage/forum-db.ts
|
|
3988
|
+
var db;
|
|
3976
3989
|
var init_forum_db = __esm(async () => {
|
|
3977
3990
|
await init_storage_host();
|
|
3991
|
+
db = internalStorageDatabase;
|
|
3978
3992
|
});
|
|
3979
3993
|
|
|
3980
3994
|
// ../../packages/core/src/storage/runtime-events.ts
|
|
@@ -3995,19 +4009,19 @@ function rowToEvent(row) {
|
|
|
3995
4009
|
}
|
|
3996
4010
|
}
|
|
3997
4011
|
function latestRuntimeEventSeq() {
|
|
3998
|
-
const row =
|
|
4012
|
+
const row = db.query("SELECT MAX(seq) AS seq FROM runtime_events").get();
|
|
3999
4013
|
return Number(row?.seq ?? 0);
|
|
4000
4014
|
}
|
|
4001
4015
|
function appendRuntimeEvent(sourceId, event) {
|
|
4002
4016
|
const createdAt = new Date().toISOString();
|
|
4003
|
-
const result =
|
|
4017
|
+
const result = db.query(`INSERT INTO runtime_events
|
|
4004
4018
|
(source_id, event_type, topic_id, payload_json, created_at)
|
|
4005
4019
|
VALUES (?, ?, ?, ?, ?)`).run(sourceId, event.type, event.topicId, JSON.stringify(event.payload ?? null), createdAt);
|
|
4006
4020
|
const seq = Number(result.lastInsertRowid);
|
|
4007
4021
|
return { seq, sourceId, ...event, createdAt };
|
|
4008
4022
|
}
|
|
4009
4023
|
function listRuntimeEventsAfter(seq, limit = 500) {
|
|
4010
|
-
const rows =
|
|
4024
|
+
const rows = db.query(`SELECT seq, source_id, event_type, topic_id, payload_json, created_at
|
|
4011
4025
|
FROM runtime_events
|
|
4012
4026
|
WHERE seq > ?
|
|
4013
4027
|
ORDER BY seq ASC
|
|
@@ -4015,7 +4029,7 @@ function listRuntimeEventsAfter(seq, limit = 500) {
|
|
|
4015
4029
|
return rows.map(rowToEvent).filter((event) => event !== null);
|
|
4016
4030
|
}
|
|
4017
4031
|
function listRecentRuntimeEventsForTopic(topicId, limit = 300) {
|
|
4018
|
-
const rows =
|
|
4032
|
+
const rows = db.query(`SELECT seq, source_id, event_type, topic_id, payload_json, created_at
|
|
4019
4033
|
FROM (
|
|
4020
4034
|
SELECT seq, source_id, event_type, topic_id, payload_json, created_at
|
|
4021
4035
|
FROM runtime_events
|
|
@@ -4037,7 +4051,7 @@ var init_runtime_events = __esm(async () => {
|
|
|
4037
4051
|
"topic-updated",
|
|
4038
4052
|
"topic-deleted"
|
|
4039
4053
|
];
|
|
4040
|
-
|
|
4054
|
+
db.exec(`
|
|
4041
4055
|
CREATE TABLE IF NOT EXISTS runtime_events (
|
|
4042
4056
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
4043
4057
|
source_id TEXT NOT NULL,
|
|
@@ -4056,7 +4070,7 @@ var init_runtime_events = __esm(async () => {
|
|
|
4056
4070
|
created_at TEXT NOT NULL
|
|
4057
4071
|
)
|
|
4058
4072
|
`);
|
|
4059
|
-
|
|
4073
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_runtime_events_topic_seq ON runtime_events(topic_id, seq)");
|
|
4060
4074
|
types = new Set(RUNTIME_EVENT_TYPES);
|
|
4061
4075
|
});
|
|
4062
4076
|
|
|
@@ -4226,7 +4240,7 @@ var init_bus = __esm(async () => {
|
|
|
4226
4240
|
|
|
4227
4241
|
// ../../packages/core/src/storage/api-messages.ts
|
|
4228
4242
|
function initializeApiMessagesSchema() {
|
|
4229
|
-
|
|
4243
|
+
db.exec(`
|
|
4230
4244
|
CREATE TABLE IF NOT EXISTS api_messages (
|
|
4231
4245
|
id TEXT PRIMARY KEY,
|
|
4232
4246
|
topic_id TEXT NOT NULL,
|
|
@@ -4250,34 +4264,34 @@ function initializeApiMessagesSchema() {
|
|
|
4250
4264
|
)
|
|
4251
4265
|
`);
|
|
4252
4266
|
try {
|
|
4253
|
-
|
|
4267
|
+
db.exec("ALTER TABLE api_messages ADD COLUMN source_adapter TEXT");
|
|
4254
4268
|
} catch {}
|
|
4255
4269
|
try {
|
|
4256
|
-
|
|
4270
|
+
db.exec("ALTER TABLE api_messages ADD COLUMN deleted INTEGER NOT NULL DEFAULT 0");
|
|
4257
4271
|
} catch {}
|
|
4258
4272
|
try {
|
|
4259
|
-
|
|
4273
|
+
db.exec("ALTER TABLE api_messages ADD COLUMN edited_at TEXT");
|
|
4260
4274
|
} catch {}
|
|
4261
4275
|
try {
|
|
4262
|
-
|
|
4276
|
+
db.exec("ALTER TABLE api_messages ADD COLUMN reactions TEXT");
|
|
4263
4277
|
} catch {}
|
|
4264
4278
|
try {
|
|
4265
|
-
|
|
4279
|
+
db.exec("ALTER TABLE api_messages ADD COLUMN kind TEXT");
|
|
4266
4280
|
} catch {}
|
|
4267
4281
|
try {
|
|
4268
|
-
|
|
4282
|
+
db.exec("ALTER TABLE api_messages ADD COLUMN ask_user_question TEXT");
|
|
4269
4283
|
} catch {}
|
|
4270
4284
|
try {
|
|
4271
|
-
|
|
4285
|
+
db.exec("ALTER TABLE api_messages ADD COLUMN mentions TEXT");
|
|
4272
4286
|
} catch {}
|
|
4273
4287
|
try {
|
|
4274
|
-
|
|
4288
|
+
db.exec("ALTER TABLE api_messages ADD COLUMN thread_root_id TEXT");
|
|
4275
4289
|
} catch {}
|
|
4276
4290
|
try {
|
|
4277
|
-
|
|
4291
|
+
db.exec("ALTER TABLE api_messages ADD COLUMN subagent_card TEXT");
|
|
4278
4292
|
} catch {}
|
|
4279
|
-
|
|
4280
|
-
|
|
4293
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_api_messages_topic ON api_messages(topic_id)");
|
|
4294
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_api_messages_thread_root ON api_messages(thread_root_id)");
|
|
4281
4295
|
}
|
|
4282
4296
|
function emitApiMessageAppended(msg) {
|
|
4283
4297
|
for (const hook of appendHooks) {
|
|
@@ -4328,14 +4342,14 @@ function appendApiMessage(msg, options = {}) {
|
|
|
4328
4342
|
const notify = options.notify ?? true;
|
|
4329
4343
|
const updateTopicLastMessageAt = options.updateTopicLastMessageAt ?? true;
|
|
4330
4344
|
let inserted = false;
|
|
4331
|
-
|
|
4332
|
-
const result =
|
|
4345
|
+
db.transaction(() => {
|
|
4346
|
+
const result = db.query(`INSERT INTO api_messages
|
|
4333
4347
|
(id, topic_id, parent_id, author_id, source_adapter, text, query_id, agent_type, model, attachments, usage, deleted, edited_at, reactions, kind, ask_user_question, subagent_card, mentions, thread_root_id, created_at)
|
|
4334
4348
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
4335
4349
|
ON CONFLICT(id) DO NOTHING`).run(msg.id, msg.topicId, msg.parentId ?? null, msg.authorId, msg.sourceAdapter ?? null, msg.text, msg.queryId ?? null, msg.agentType ?? null, msg.model ?? null, msg.attachments ? JSON.stringify(msg.attachments) : null, msg.usage ? JSON.stringify(msg.usage) : null, msg.deleted ? 1 : 0, msg.editedAt ?? null, msg.reactions?.length ? JSON.stringify(msg.reactions) : null, msg.kind ?? null, msg.askUserQuestion ? JSON.stringify(msg.askUserQuestion) : null, msg.subagentCard ? JSON.stringify(msg.subagentCard) : null, msg.mentions?.length ? JSON.stringify(msg.mentions) : null, msg.threadRootId ?? null, msg.createdAt);
|
|
4336
4350
|
inserted = Number(result.changes ?? 0) > 0;
|
|
4337
4351
|
if (inserted && updateTopicLastMessageAt && !msg.deleted) {
|
|
4338
|
-
|
|
4352
|
+
db.query(`UPDATE api_topics
|
|
4339
4353
|
SET last_message_at = CASE
|
|
4340
4354
|
WHEN last_message_at IS NULL OR last_message_at < ? THEN ?
|
|
4341
4355
|
ELSE last_message_at
|
|
@@ -4347,15 +4361,15 @@ function appendApiMessage(msg, options = {}) {
|
|
|
4347
4361
|
emitApiMessageAppended(msg);
|
|
4348
4362
|
}
|
|
4349
4363
|
function getApiMessage(topicId, messageId) {
|
|
4350
|
-
const row =
|
|
4364
|
+
const row = db.query("SELECT * FROM api_messages WHERE topic_id = ? AND id = ?").get(topicId, messageId);
|
|
4351
4365
|
return row ? rowToDto(row) : null;
|
|
4352
4366
|
}
|
|
4353
4367
|
function topicHasAttachmentFileId(topicId, fileId) {
|
|
4354
|
-
const rows =
|
|
4368
|
+
const rows = db.query("SELECT attachments FROM api_messages WHERE topic_id = ? AND deleted = 0 AND attachments IS NOT NULL").all(topicId);
|
|
4355
4369
|
return rows.some((row) => attachmentListHasFileId(row.attachments, fileId));
|
|
4356
4370
|
}
|
|
4357
4371
|
function updateApiMessageText(topicId, messageId, text, editedAt = new Date().toISOString()) {
|
|
4358
|
-
const res =
|
|
4372
|
+
const res = db.query(`UPDATE api_messages
|
|
4359
4373
|
SET text = ?, edited_at = ?
|
|
4360
4374
|
WHERE topic_id = ? AND id = ? AND deleted = 0`).run(text, editedAt, topicId, messageId);
|
|
4361
4375
|
if (Number(res.changes ?? 0) === 0)
|
|
@@ -4363,7 +4377,7 @@ function updateApiMessageText(topicId, messageId, text, editedAt = new Date().to
|
|
|
4363
4377
|
return getApiMessage(topicId, messageId);
|
|
4364
4378
|
}
|
|
4365
4379
|
function updateApiMessageUsage(topicId, messageId, usage) {
|
|
4366
|
-
const res =
|
|
4380
|
+
const res = db.query(`UPDATE api_messages
|
|
4367
4381
|
SET usage = ?
|
|
4368
4382
|
WHERE topic_id = ? AND id = ? AND deleted = 0`).run(JSON.stringify(usage), topicId, messageId);
|
|
4369
4383
|
if (Number(res.changes ?? 0) === 0)
|
|
@@ -4371,7 +4385,7 @@ function updateApiMessageUsage(topicId, messageId, usage) {
|
|
|
4371
4385
|
return getApiMessage(topicId, messageId);
|
|
4372
4386
|
}
|
|
4373
4387
|
function updateApiMessageAskUserQuestion(topicId, messageId, askUserQuestion, editedAt = new Date().toISOString()) {
|
|
4374
|
-
const res =
|
|
4388
|
+
const res = db.query(`UPDATE api_messages
|
|
4375
4389
|
SET ask_user_question = ?, edited_at = ?
|
|
4376
4390
|
WHERE topic_id = ? AND id = ? AND deleted = 0 AND kind = 'ask_user_question'`).run(JSON.stringify(askUserQuestion), editedAt, topicId, messageId);
|
|
4377
4391
|
if (Number(res.changes ?? 0) === 0)
|
|
@@ -4379,7 +4393,7 @@ function updateApiMessageAskUserQuestion(topicId, messageId, askUserQuestion, ed
|
|
|
4379
4393
|
return getApiMessage(topicId, messageId);
|
|
4380
4394
|
}
|
|
4381
4395
|
function updateApiMessageSubagentCard(topicId, messageId, subagentCard, editedAt = new Date().toISOString()) {
|
|
4382
|
-
const res =
|
|
4396
|
+
const res = db.query(`UPDATE api_messages
|
|
4383
4397
|
SET subagent_card = ?, edited_at = ?
|
|
4384
4398
|
WHERE topic_id = ? AND id = ? AND deleted = 0 AND kind = 'subagent'`).run(JSON.stringify(subagentCard), editedAt, topicId, messageId);
|
|
4385
4399
|
if (Number(res.changes ?? 0) === 0)
|
|
@@ -4387,11 +4401,11 @@ function updateApiMessageSubagentCard(topicId, messageId, subagentCard, editedAt
|
|
|
4387
4401
|
return getApiMessage(topicId, messageId);
|
|
4388
4402
|
}
|
|
4389
4403
|
function listApiMessagesByKind(kind) {
|
|
4390
|
-
const rows =
|
|
4404
|
+
const rows = db.query("SELECT * FROM api_messages WHERE kind = ? AND deleted = 0").all(kind);
|
|
4391
4405
|
return rows.map(rowToDto);
|
|
4392
4406
|
}
|
|
4393
4407
|
function softDeleteApiMessage(topicId, messageId) {
|
|
4394
|
-
const res =
|
|
4408
|
+
const res = db.query(`UPDATE api_messages
|
|
4395
4409
|
SET deleted = 1, text = ''
|
|
4396
4410
|
WHERE topic_id = ? AND id = ? AND deleted = 0`).run(topicId, messageId);
|
|
4397
4411
|
if (Number(res.changes ?? 0) === 0)
|
|
@@ -4399,10 +4413,10 @@ function softDeleteApiMessage(topicId, messageId) {
|
|
|
4399
4413
|
return getApiMessage(topicId, messageId);
|
|
4400
4414
|
}
|
|
4401
4415
|
function getAllMessagesForTopic(topicId) {
|
|
4402
|
-
return
|
|
4416
|
+
return db.query("SELECT *, rowid AS rowid FROM api_messages WHERE topic_id = ? AND deleted = 0 ORDER BY rowid ASC").all(topicId);
|
|
4403
4417
|
}
|
|
4404
4418
|
function getMessagesForTopicAfterRowid(topicId, afterRowid) {
|
|
4405
|
-
return
|
|
4419
|
+
return db.query("SELECT *, rowid AS rowid FROM api_messages WHERE topic_id = ? AND rowid > ? AND deleted = 0 ORDER BY rowid ASC").all(topicId, afterRowid);
|
|
4406
4420
|
}
|
|
4407
4421
|
var appendHooks;
|
|
4408
4422
|
var init_api_messages = __esm(async () => {
|
|
@@ -4414,11 +4428,11 @@ var init_api_messages = __esm(async () => {
|
|
|
4414
4428
|
|
|
4415
4429
|
// ../../packages/core/src/storage/api-topic-config.ts
|
|
4416
4430
|
function tableColumns(table) {
|
|
4417
|
-
const rows =
|
|
4431
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
4418
4432
|
return new Set(rows.map((row) => row.name));
|
|
4419
4433
|
}
|
|
4420
4434
|
function createCanonicalConfigTable(name = "api_topic_config") {
|
|
4421
|
-
|
|
4435
|
+
db.exec(`
|
|
4422
4436
|
CREATE TABLE IF NOT EXISTS ${name} (
|
|
4423
4437
|
topic_id TEXT PRIMARY KEY REFERENCES api_topics(id) ON DELETE CASCADE,
|
|
4424
4438
|
model TEXT,
|
|
@@ -4438,7 +4452,7 @@ function initializeApiTopicConfigSchema() {
|
|
|
4438
4452
|
const topicColumns = tableColumns("api_topics");
|
|
4439
4453
|
const agentColumn = topicColumns.has("agent") ? "agent" : topicColumns.has("runtime_agent") ? "runtime_agent" : null;
|
|
4440
4454
|
if (agentColumn && existingConfigColumns.has("agent")) {
|
|
4441
|
-
|
|
4455
|
+
db.exec(`UPDATE api_topics
|
|
4442
4456
|
SET ${agentColumn} = (
|
|
4443
4457
|
SELECT c.agent FROM api_topic_config c WHERE c.topic_id = api_topics.id
|
|
4444
4458
|
)
|
|
@@ -4448,15 +4462,15 @@ function initializeApiTopicConfigSchema() {
|
|
|
4448
4462
|
WHERE c.topic_id = api_topics.id AND c.agent IS NOT NULL
|
|
4449
4463
|
)`);
|
|
4450
4464
|
}
|
|
4451
|
-
const previousForeignKeys =
|
|
4452
|
-
|
|
4465
|
+
const previousForeignKeys = db.query("PRAGMA foreign_keys").get()?.foreign_keys;
|
|
4466
|
+
db.exec("PRAGMA foreign_keys = OFF");
|
|
4453
4467
|
try {
|
|
4454
4468
|
const agentLockSource = existingConfigColumns.has("runtime_locked") ? "runtime_locked" : existingConfigColumns.has("agent_pinned") ? "agent_pinned" : "0";
|
|
4455
4469
|
const modelLockSource = existingConfigColumns.has("model_locked") ? "model_locked" : existingConfigColumns.has("model_pinned") ? "model_pinned" : "0";
|
|
4456
4470
|
const effortLockSource = existingConfigColumns.has("effort_locked") ? "effort_locked" : existingConfigColumns.has("effort_pinned") ? "effort_pinned" : "0";
|
|
4457
|
-
|
|
4471
|
+
db.transaction(() => {
|
|
4458
4472
|
createCanonicalConfigTable("api_topic_config_next");
|
|
4459
|
-
|
|
4473
|
+
db.exec(`
|
|
4460
4474
|
INSERT INTO api_topic_config_next
|
|
4461
4475
|
(topic_id, model, effort, mcp, agent_locked, model_locked, effort_locked)
|
|
4462
4476
|
SELECT
|
|
@@ -4470,11 +4484,11 @@ function initializeApiTopicConfigSchema() {
|
|
|
4470
4484
|
FROM api_topic_config
|
|
4471
4485
|
WHERE EXISTS (SELECT 1 FROM api_topics t WHERE t.id = api_topic_config.topic_id)
|
|
4472
4486
|
`);
|
|
4473
|
-
|
|
4474
|
-
|
|
4487
|
+
db.exec("DROP TABLE api_topic_config");
|
|
4488
|
+
db.exec("ALTER TABLE api_topic_config_next RENAME TO api_topic_config");
|
|
4475
4489
|
})();
|
|
4476
4490
|
} finally {
|
|
4477
|
-
|
|
4491
|
+
db.exec(`PRAGMA foreign_keys = ${previousForeignKeys ? "ON" : "OFF"}`);
|
|
4478
4492
|
}
|
|
4479
4493
|
} else {
|
|
4480
4494
|
createCanonicalConfigTable();
|
|
@@ -4502,14 +4516,14 @@ function rowToConfig(r) {
|
|
|
4502
4516
|
return cfg;
|
|
4503
4517
|
}
|
|
4504
4518
|
function getApiTopicConfig(topicId) {
|
|
4505
|
-
const row =
|
|
4519
|
+
const row = db.query("SELECT topic_id, model, effort, mcp, agent_locked, model_locked, effort_locked FROM api_topic_config WHERE topic_id = ?").get(topicId);
|
|
4506
4520
|
if (!row)
|
|
4507
4521
|
return;
|
|
4508
4522
|
const config = rowToConfig(row);
|
|
4509
4523
|
return Object.keys(config).length > 0 ? config : undefined;
|
|
4510
4524
|
}
|
|
4511
4525
|
function setApiTopicConfig(topicId, config) {
|
|
4512
|
-
|
|
4526
|
+
db.query(`INSERT INTO api_topic_config
|
|
4513
4527
|
(topic_id, model, effort, mcp, agent_locked, model_locked, effort_locked)
|
|
4514
4528
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
4515
4529
|
ON CONFLICT(topic_id) DO UPDATE SET
|
|
@@ -4538,11 +4552,11 @@ var init_constants = __esm(() => {
|
|
|
4538
4552
|
|
|
4539
4553
|
// ../../packages/core/src/storage/api-topics.ts
|
|
4540
4554
|
function tableColumns2(table) {
|
|
4541
|
-
const rows =
|
|
4555
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
4542
4556
|
return new Set(rows.map((row) => row.name));
|
|
4543
4557
|
}
|
|
4544
4558
|
function initializeApiTopicsSchema() {
|
|
4545
|
-
|
|
4559
|
+
db.exec(`
|
|
4546
4560
|
CREATE TABLE IF NOT EXISTS api_topics (
|
|
4547
4561
|
id TEXT PRIMARY KEY,
|
|
4548
4562
|
title TEXT NOT NULL,
|
|
@@ -4564,56 +4578,56 @@ function initializeApiTopicsSchema() {
|
|
|
4564
4578
|
const needsCanonicalTopicRebuild = legacyTopicSchema || !initialTopicColumns.has("agent");
|
|
4565
4579
|
if (legacyTopicSchema) {
|
|
4566
4580
|
try {
|
|
4567
|
-
|
|
4581
|
+
db.exec("ALTER TABLE api_topics RENAME COLUMN default_agent TO agent");
|
|
4568
4582
|
} catch {}
|
|
4569
4583
|
try {
|
|
4570
|
-
|
|
4584
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN ai_mention INTEGER NOT NULL DEFAULT 0");
|
|
4571
4585
|
} catch {}
|
|
4572
4586
|
try {
|
|
4573
|
-
|
|
4587
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN parent_topic_id TEXT");
|
|
4574
4588
|
} catch {}
|
|
4575
4589
|
try {
|
|
4576
|
-
|
|
4590
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN is_fork INTEGER NOT NULL DEFAULT 0");
|
|
4577
4591
|
} catch {}
|
|
4578
4592
|
try {
|
|
4579
|
-
|
|
4593
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN is_subagent INTEGER NOT NULL DEFAULT 0");
|
|
4580
4594
|
} catch {}
|
|
4581
4595
|
try {
|
|
4582
|
-
|
|
4596
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN session_id TEXT");
|
|
4583
4597
|
} catch {}
|
|
4584
4598
|
try {
|
|
4585
|
-
|
|
4599
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN kind TEXT NOT NULL DEFAULT 'channel'");
|
|
4586
4600
|
} catch {}
|
|
4587
4601
|
try {
|
|
4588
|
-
|
|
4602
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN ai_mode TEXT");
|
|
4589
4603
|
} catch {}
|
|
4590
|
-
|
|
4604
|
+
db.exec(`
|
|
4591
4605
|
CREATE TABLE IF NOT EXISTS api_schema_migrations (
|
|
4592
4606
|
key TEXT PRIMARY KEY,
|
|
4593
4607
|
applied_at TEXT NOT NULL
|
|
4594
4608
|
)
|
|
4595
4609
|
`);
|
|
4596
4610
|
const ALWAYS_RESPOND_MIGRATION = "api_topics_ai_invited_default_always_respond_20260623";
|
|
4597
|
-
const alwaysRespondMigration =
|
|
4611
|
+
const alwaysRespondMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(ALWAYS_RESPOND_MIGRATION);
|
|
4598
4612
|
if (!alwaysRespondMigration) {
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4613
|
+
db.transaction(() => {
|
|
4614
|
+
db.query("UPDATE api_topics SET ai_mention = 0 WHERE agent IS NOT NULL").run();
|
|
4615
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(ALWAYS_RESPOND_MIGRATION, new Date().toISOString());
|
|
4602
4616
|
})();
|
|
4603
4617
|
}
|
|
4604
4618
|
const GENERAL_AGENT_KIND_MIGRATION = "api_topics_general_agent_kind_20260704";
|
|
4605
|
-
const generalAgentKindMigration =
|
|
4619
|
+
const generalAgentKindMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(GENERAL_AGENT_KIND_MIGRATION);
|
|
4606
4620
|
if (!generalAgentKindMigration) {
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4621
|
+
db.transaction(() => {
|
|
4622
|
+
db.query("UPDATE api_topics SET kind = 'agent', ai_mention = 0 WHERE id = ?").run(GENERAL_TOPIC_ID);
|
|
4623
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(GENERAL_AGENT_KIND_MIGRATION, new Date().toISOString());
|
|
4610
4624
|
})();
|
|
4611
4625
|
}
|
|
4612
4626
|
const AI_MODE_MIGRATION = "api_topics_ai_mode_20260704";
|
|
4613
|
-
const aiModeMigration =
|
|
4627
|
+
const aiModeMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(AI_MODE_MIGRATION);
|
|
4614
4628
|
if (!aiModeMigration) {
|
|
4615
|
-
|
|
4616
|
-
|
|
4629
|
+
db.transaction(() => {
|
|
4630
|
+
db.query(`UPDATE api_topics
|
|
4617
4631
|
SET
|
|
4618
4632
|
kind = CASE
|
|
4619
4633
|
WHEN id = ? THEN 'agent'
|
|
@@ -4629,20 +4643,20 @@ function initializeApiTopicsSchema() {
|
|
|
4629
4643
|
WHEN ai_mention != 0 THEN 'mention'
|
|
4630
4644
|
ELSE 'always'
|
|
4631
4645
|
END`).run(GENERAL_TOPIC_ID, GENERAL_TOPIC_ID);
|
|
4632
|
-
|
|
4646
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(AI_MODE_MIGRATION, new Date().toISOString());
|
|
4633
4647
|
})();
|
|
4634
4648
|
}
|
|
4635
4649
|
const GENERAL_MANAGER_KIND_MIGRATION = "api_topics_general_manager_kind_20260704";
|
|
4636
|
-
const generalManagerKindMigration =
|
|
4650
|
+
const generalManagerKindMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(GENERAL_MANAGER_KIND_MIGRATION);
|
|
4637
4651
|
if (!generalManagerKindMigration) {
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4652
|
+
db.transaction(() => {
|
|
4653
|
+
db.query("UPDATE api_topics SET kind = 'manager', ai_mention = 0, ai_mode = 'always' WHERE id = ?").run(GENERAL_TOPIC_ID);
|
|
4654
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(GENERAL_MANAGER_KIND_MIGRATION, new Date().toISOString());
|
|
4641
4655
|
})();
|
|
4642
4656
|
}
|
|
4643
4657
|
}
|
|
4644
4658
|
function createCanonicalTopicsTable(name) {
|
|
4645
|
-
|
|
4659
|
+
db.exec(`
|
|
4646
4660
|
CREATE TABLE IF NOT EXISTS ${name} (
|
|
4647
4661
|
id TEXT PRIMARY KEY,
|
|
4648
4662
|
title TEXT NOT NULL,
|
|
@@ -4671,7 +4685,7 @@ function initializeApiTopicsSchema() {
|
|
|
4671
4685
|
`);
|
|
4672
4686
|
}
|
|
4673
4687
|
function createTopicMembersTable() {
|
|
4674
|
-
|
|
4688
|
+
db.exec(`
|
|
4675
4689
|
CREATE TABLE IF NOT EXISTS topic_members (
|
|
4676
4690
|
topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
|
|
4677
4691
|
user_id TEXT NOT NULL,
|
|
@@ -4679,17 +4693,17 @@ function initializeApiTopicsSchema() {
|
|
|
4679
4693
|
PRIMARY KEY (topic_id, user_id)
|
|
4680
4694
|
)
|
|
4681
4695
|
`);
|
|
4682
|
-
|
|
4696
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_topic_members_user ON topic_members(user_id)");
|
|
4683
4697
|
}
|
|
4684
4698
|
if (needsCanonicalTopicRebuild) {
|
|
4685
|
-
const legacyRows =
|
|
4686
|
-
const existingMemberRows = tableColumns2("topic_members").has("topic_id") ?
|
|
4699
|
+
const legacyRows = db.query("SELECT * FROM api_topics").all();
|
|
4700
|
+
const existingMemberRows = tableColumns2("topic_members").has("topic_id") ? db.query("SELECT topic_id, user_id, role FROM topic_members").all() : [];
|
|
4687
4701
|
const configColumns = tableColumns2("api_topic_config");
|
|
4688
|
-
const legacyAgentOverrides = configColumns.has("agent") ? new Map(
|
|
4689
|
-
const previousForeignKeys =
|
|
4690
|
-
|
|
4702
|
+
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;
|
|
4703
|
+
const previousForeignKeys = db.query("PRAGMA foreign_keys").get()?.foreign_keys;
|
|
4704
|
+
db.exec("PRAGMA foreign_keys = OFF");
|
|
4691
4705
|
try {
|
|
4692
|
-
|
|
4706
|
+
db.transaction(() => {
|
|
4693
4707
|
createCanonicalTopicsTable("api_topics_next");
|
|
4694
4708
|
for (const row of legacyRows) {
|
|
4695
4709
|
const selectedAgent = legacyAgentOverrides.get(String(row.id)) ?? row.agent ?? row.runtime_agent ?? row.default_agent ?? undefined;
|
|
@@ -4702,18 +4716,18 @@ function initializeApiTopicsSchema() {
|
|
|
4702
4716
|
});
|
|
4703
4717
|
const legacyBaseModel = row.base_model ?? row.default_model;
|
|
4704
4718
|
const legacyBaseEffort = row.base_effort ?? row.default_effort;
|
|
4705
|
-
|
|
4719
|
+
db.query(`INSERT INTO api_topics_next
|
|
4706
4720
|
(id,title,kind,description,agent,base_model,base_effort,response_policy,
|
|
4707
4721
|
created_at,last_message_at,parent_topic_id,is_fork,is_subagent,visibility,access_mode,session_id)
|
|
4708
4722
|
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, Number(row.is_fork ?? 0) !== 0 ? 1 : 0, Number(row.is_subagent ?? 0) !== 0 ? 1 : 0, row.visibility === "hidden" ? "hidden" : "visible", row.access_mode === "shared" ? "shared" : "private", typeof row.session_id === "string" ? row.session_id : null);
|
|
4709
4723
|
}
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4724
|
+
db.exec("DROP TABLE IF EXISTS topic_members");
|
|
4725
|
+
db.exec("DROP TABLE api_topics");
|
|
4726
|
+
db.exec("ALTER TABLE api_topics_next RENAME TO api_topics");
|
|
4713
4727
|
createTopicMembersTable();
|
|
4714
4728
|
if (existingMemberRows.length > 0) {
|
|
4715
4729
|
for (const member of existingMemberRows) {
|
|
4716
|
-
|
|
4730
|
+
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");
|
|
4717
4731
|
}
|
|
4718
4732
|
} else {
|
|
4719
4733
|
for (const row of legacyRows) {
|
|
@@ -4728,31 +4742,31 @@ function initializeApiTopicsSchema() {
|
|
|
4728
4742
|
for (const participant of participants) {
|
|
4729
4743
|
if (!participant?.userId)
|
|
4730
4744
|
continue;
|
|
4731
|
-
|
|
4745
|
+
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");
|
|
4732
4746
|
}
|
|
4733
4747
|
}
|
|
4734
4748
|
}
|
|
4735
4749
|
})();
|
|
4736
4750
|
} finally {
|
|
4737
|
-
|
|
4751
|
+
db.exec(`PRAGMA foreign_keys = ${previousForeignKeys ? "ON" : "OFF"}`);
|
|
4738
4752
|
}
|
|
4739
4753
|
} else {
|
|
4740
4754
|
createCanonicalTopicsTable("api_topics");
|
|
4741
4755
|
createTopicMembersTable();
|
|
4742
4756
|
}
|
|
4743
4757
|
if (!tableColumns2("api_topics").has("visibility")) {
|
|
4744
|
-
|
|
4758
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN visibility TEXT NOT NULL DEFAULT 'visible'");
|
|
4745
4759
|
}
|
|
4746
4760
|
if (!tableColumns2("api_topics").has("access_mode")) {
|
|
4747
|
-
|
|
4761
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN access_mode TEXT NOT NULL DEFAULT 'private'");
|
|
4748
4762
|
}
|
|
4749
4763
|
if (!tableColumns2("api_topics").has("browser_profile")) {
|
|
4750
|
-
|
|
4764
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile TEXT NOT NULL DEFAULT 'default'");
|
|
4751
4765
|
}
|
|
4752
4766
|
if (!tableColumns2("api_topics").has("browser_profile_owner")) {
|
|
4753
|
-
|
|
4767
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile_owner TEXT");
|
|
4754
4768
|
}
|
|
4755
|
-
|
|
4769
|
+
db.exec(`
|
|
4756
4770
|
UPDATE api_topics
|
|
4757
4771
|
SET browser_profile_owner = (
|
|
4758
4772
|
SELECT m.user_id FROM topic_members m
|
|
@@ -4762,16 +4776,17 @@ function initializeApiTopicsSchema() {
|
|
|
4762
4776
|
)
|
|
4763
4777
|
WHERE browser_profile_owner IS NULL
|
|
4764
4778
|
`);
|
|
4779
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_api_topics_last_message ON api_topics(last_message_at DESC)");
|
|
4765
4780
|
}
|
|
4766
4781
|
function shortSessionId(sessionId) {
|
|
4767
4782
|
return sessionId ? sessionId.slice(0, 8) : null;
|
|
4768
4783
|
}
|
|
4769
4784
|
function getTopicParticipants(topicId) {
|
|
4770
|
-
return
|
|
4785
|
+
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 }));
|
|
4771
4786
|
}
|
|
4772
4787
|
function getAllTopicParticipants() {
|
|
4773
4788
|
const grouped = new Map;
|
|
4774
|
-
const rows =
|
|
4789
|
+
const rows = db.query("SELECT topic_id, user_id, role FROM topic_members ORDER BY rowid").all();
|
|
4775
4790
|
for (const row of rows) {
|
|
4776
4791
|
const participants = grouped.get(row.topic_id) ?? [];
|
|
4777
4792
|
participants.push({ userId: row.user_id, role: row.role });
|
|
@@ -4853,8 +4868,8 @@ function upsertTopic(t) {
|
|
|
4853
4868
|
aiMode: normalizeAiMode(t.aiMode),
|
|
4854
4869
|
aiMention: t.aiMention
|
|
4855
4870
|
});
|
|
4856
|
-
|
|
4857
|
-
|
|
4871
|
+
db.transaction(() => {
|
|
4872
|
+
db.query(`INSERT INTO api_topics
|
|
4858
4873
|
(id,title,kind,description,agent,base_model,base_effort,response_policy,
|
|
4859
4874
|
created_at,last_message_at,parent_topic_id,is_fork,is_subagent,visibility,access_mode)
|
|
4860
4875
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
@@ -4873,29 +4888,29 @@ function upsertTopic(t) {
|
|
|
4873
4888
|
is_subagent = excluded.is_subagent,
|
|
4874
4889
|
visibility = excluded.visibility,
|
|
4875
4890
|
access_mode = excluded.access_mode`).run(t.id, t.title, normalized.kind, t.description ?? null, normalized.agent ?? null, t.defaultModel ?? null, t.defaultEffort ?? null, normalized.aiMode, t.createdAt, t.lastMessageAt ?? null, t.parentTopicId ?? null, t.isFork ? 1 : 0, t.isSubagent ? 1 : 0, normalizeTopicVisibility(t.visibility), normalizeTopicAccessMode(t.accessMode));
|
|
4876
|
-
|
|
4891
|
+
db.query("DELETE FROM topic_members WHERE topic_id = ?").run(t.id);
|
|
4877
4892
|
for (const participant of t.participants) {
|
|
4878
|
-
|
|
4893
|
+
db.query("INSERT INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(t.id, participant.userId, participant.role);
|
|
4879
4894
|
}
|
|
4880
4895
|
const initialBrowserProfileOwner = t.participants.find((participant) => participant.role === "owner")?.userId;
|
|
4881
4896
|
if (initialBrowserProfileOwner) {
|
|
4882
|
-
|
|
4897
|
+
db.query(`UPDATE api_topics
|
|
4883
4898
|
SET browser_profile_owner = COALESCE(browser_profile_owner, ?)
|
|
4884
4899
|
WHERE id = ?`).run(initialBrowserProfileOwner, t.id);
|
|
4885
4900
|
}
|
|
4886
4901
|
})();
|
|
4887
4902
|
}
|
|
4888
4903
|
function listTopics() {
|
|
4889
|
-
const rows =
|
|
4904
|
+
const rows = db.query("SELECT * FROM api_topics ORDER BY last_message_at DESC").all();
|
|
4890
4905
|
const participants = getAllTopicParticipants();
|
|
4891
4906
|
return rows.map((row) => rowToDto2(row, participants.get(row.id) ?? []));
|
|
4892
4907
|
}
|
|
4893
4908
|
function getTopic(id) {
|
|
4894
|
-
const r =
|
|
4909
|
+
const r = db.query("SELECT * FROM api_topics WHERE id = ?").get(id);
|
|
4895
4910
|
return r ? rowToDto2(r) : null;
|
|
4896
4911
|
}
|
|
4897
4912
|
function getManagerTopicForUser(userId) {
|
|
4898
|
-
const row =
|
|
4913
|
+
const row = db.query(`SELECT t.* FROM api_topics t
|
|
4899
4914
|
JOIN topic_members m ON m.topic_id = t.id
|
|
4900
4915
|
WHERE t.kind = 'manager'
|
|
4901
4916
|
AND t.id != ?
|
|
@@ -4921,7 +4936,7 @@ function getTopicMemoryOrigin(id) {
|
|
|
4921
4936
|
}
|
|
4922
4937
|
function setTopicSessionId(topicId, sessionId, context = {}) {
|
|
4923
4938
|
const previous = getTopicSessionId(topicId);
|
|
4924
|
-
const result =
|
|
4939
|
+
const result = db.query("UPDATE api_topics SET session_id = ? WHERE id = ?").run(sessionId, topicId);
|
|
4925
4940
|
const changes = Number(result.changes ?? 0);
|
|
4926
4941
|
const logContext = {
|
|
4927
4942
|
topicId,
|
|
@@ -4941,7 +4956,7 @@ function setTopicSessionId(topicId, sessionId, context = {}) {
|
|
|
4941
4956
|
}
|
|
4942
4957
|
function clearTopicSessionId(topicId, reason = "unspecified") {
|
|
4943
4958
|
const previous = getTopicSessionId(topicId);
|
|
4944
|
-
const result =
|
|
4959
|
+
const result = db.query("UPDATE api_topics SET session_id = NULL WHERE id = ?").run(topicId);
|
|
4945
4960
|
const changes = Number(result.changes ?? 0);
|
|
4946
4961
|
const logContext = { topicId, previousSessionId: shortSessionId(previous), reason };
|
|
4947
4962
|
if (changes === 0) {
|
|
@@ -4953,7 +4968,7 @@ function clearTopicSessionId(topicId, reason = "unspecified") {
|
|
|
4953
4968
|
}
|
|
4954
4969
|
}
|
|
4955
4970
|
function getTopicSessionId(topicId) {
|
|
4956
|
-
const r =
|
|
4971
|
+
const r = db.query("SELECT session_id FROM api_topics WHERE id = ?").get(topicId);
|
|
4957
4972
|
return r?.session_id ?? null;
|
|
4958
4973
|
}
|
|
4959
4974
|
var DEFAULT_AGENT_ROOM_AGENT = "maestro";
|
|
@@ -5121,7 +5136,7 @@ function rowToState(row, now = Date.now()) {
|
|
|
5121
5136
|
};
|
|
5122
5137
|
}
|
|
5123
5138
|
function getRuntimeTopicState(topicId, now = Date.now()) {
|
|
5124
|
-
const row =
|
|
5139
|
+
const row = db.query("SELECT * FROM runtime_topic_state WHERE topic_id = ?").get(topicId);
|
|
5125
5140
|
return row ? rowToState(row, now) : { topicId, epoch: 0, maintenance: false };
|
|
5126
5141
|
}
|
|
5127
5142
|
function getRuntimeTopicEpoch(topicId) {
|
|
@@ -5133,7 +5148,7 @@ function isRuntimeTopicMaintenance(topicId, now = Date.now()) {
|
|
|
5133
5148
|
var TOPIC_MAINTENANCE_STALE_MS = 30000;
|
|
5134
5149
|
var init_runtime_topic_state = __esm(async () => {
|
|
5135
5150
|
await init_forum_db();
|
|
5136
|
-
|
|
5151
|
+
db.exec(`
|
|
5137
5152
|
CREATE TABLE IF NOT EXISTS runtime_topic_state (
|
|
5138
5153
|
topic_id TEXT PRIMARY KEY,
|
|
5139
5154
|
epoch INTEGER NOT NULL DEFAULT 0,
|
|
@@ -5160,18 +5175,18 @@ function rowToLease(row) {
|
|
|
5160
5175
|
};
|
|
5161
5176
|
}
|
|
5162
5177
|
function getRuntimeTurnLease(topicId, now = Date.now()) {
|
|
5163
|
-
const row =
|
|
5178
|
+
const row = db.query("SELECT * FROM runtime_turn_leases WHERE topic_id = ?").get(topicId);
|
|
5164
5179
|
if (!row)
|
|
5165
5180
|
return null;
|
|
5166
5181
|
const lease = rowToLease(row);
|
|
5167
5182
|
return now - lease.heartbeatAt <= TURN_LEASE_STALE_MS ? lease : null;
|
|
5168
5183
|
}
|
|
5169
5184
|
function listRuntimeTurnLeases(now = Date.now()) {
|
|
5170
|
-
return
|
|
5185
|
+
return db.query("SELECT * FROM runtime_turn_leases ORDER BY started_at ASC").all().map(rowToLease).filter((lease) => now - lease.heartbeatAt <= TURN_LEASE_STALE_MS);
|
|
5171
5186
|
}
|
|
5172
5187
|
function claimRuntimeTurnLease(input, now = Date.now()) {
|
|
5173
5188
|
const ownerId = input.ownerId ?? RUNTIME_INSTANCE_ID;
|
|
5174
|
-
const result =
|
|
5189
|
+
const result = db.query(`INSERT INTO runtime_turn_leases
|
|
5175
5190
|
(topic_id, query_id, owner_id, origin, started_at, heartbeat_at, abort_requested, abort_reason)
|
|
5176
5191
|
SELECT ?, ?, ?, ?, ?, ?, 0, NULL
|
|
5177
5192
|
WHERE NOT EXISTS (
|
|
@@ -5195,12 +5210,12 @@ function claimRuntimeTurnLease(input, now = Date.now()) {
|
|
|
5195
5210
|
return Number(result.changes ?? 0) > 0;
|
|
5196
5211
|
}
|
|
5197
5212
|
function heartbeatRuntimeTurnLease(topicId, queryId, ownerId = RUNTIME_INSTANCE_ID, now = Date.now()) {
|
|
5198
|
-
const updated =
|
|
5213
|
+
const updated = db.query(`UPDATE runtime_turn_leases
|
|
5199
5214
|
SET heartbeat_at = ?
|
|
5200
5215
|
WHERE topic_id = ? AND query_id = ? AND owner_id = ?`).run(now, topicId, queryId, ownerId);
|
|
5201
5216
|
if (Number(updated.changes ?? 0) === 0)
|
|
5202
5217
|
return { owned: false, abortRequested: false };
|
|
5203
|
-
const row =
|
|
5218
|
+
const row = db.query(`SELECT abort_requested, abort_reason
|
|
5204
5219
|
FROM runtime_turn_leases
|
|
5205
5220
|
WHERE topic_id = ? AND query_id = ? AND owner_id = ?`).get(topicId, queryId, ownerId);
|
|
5206
5221
|
const abortReason = row?.abort_reason === "internal" || row?.abort_reason === "external" ? row.abort_reason : undefined;
|
|
@@ -5211,7 +5226,7 @@ function heartbeatRuntimeTurnLease(topicId, queryId, ownerId = RUNTIME_INSTANCE_
|
|
|
5211
5226
|
};
|
|
5212
5227
|
}
|
|
5213
5228
|
function requestRuntimeTurnAbort(topicId, reason) {
|
|
5214
|
-
const result =
|
|
5229
|
+
const result = db.query(`UPDATE runtime_turn_leases
|
|
5215
5230
|
SET abort_requested = 1,
|
|
5216
5231
|
abort_reason = CASE
|
|
5217
5232
|
WHEN abort_reason = 'external' THEN abort_reason
|
|
@@ -5221,7 +5236,7 @@ function requestRuntimeTurnAbort(topicId, reason) {
|
|
|
5221
5236
|
return Number(result.changes ?? 0) > 0;
|
|
5222
5237
|
}
|
|
5223
5238
|
function releaseRuntimeTurnLease(topicId, queryId, ownerId = RUNTIME_INSTANCE_ID) {
|
|
5224
|
-
const result =
|
|
5239
|
+
const result = db.query(`DELETE FROM runtime_turn_leases
|
|
5225
5240
|
WHERE topic_id = ? AND query_id = ? AND owner_id = ?`).run(topicId, queryId, ownerId);
|
|
5226
5241
|
return Number(result.changes ?? 0) > 0;
|
|
5227
5242
|
}
|
|
@@ -5230,7 +5245,7 @@ var init_runtime_leases = __esm(async () => {
|
|
|
5230
5245
|
await init_forum_db();
|
|
5231
5246
|
await init_runtime_topic_state();
|
|
5232
5247
|
RUNTIME_INSTANCE_ID = `${process.pid}-${randomUUID3()}`;
|
|
5233
|
-
|
|
5248
|
+
db.exec(`
|
|
5234
5249
|
CREATE TABLE IF NOT EXISTS runtime_turn_leases (
|
|
5235
5250
|
topic_id TEXT PRIMARY KEY,
|
|
5236
5251
|
query_id TEXT NOT NULL UNIQUE,
|
|
@@ -5242,7 +5257,7 @@ var init_runtime_leases = __esm(async () => {
|
|
|
5242
5257
|
abort_reason TEXT CHECK (abort_reason IS NULL OR abort_reason IN ('internal', 'external'))
|
|
5243
5258
|
)
|
|
5244
5259
|
`);
|
|
5245
|
-
|
|
5260
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_runtime_turn_leases_heartbeat ON runtime_turn_leases(heartbeat_at)");
|
|
5246
5261
|
});
|
|
5247
5262
|
|
|
5248
5263
|
// ../../packages/core/src/storage/self-schedules.ts
|
|
@@ -5250,7 +5265,7 @@ var init_self_schedules = __esm(async () => {
|
|
|
5250
5265
|
await init_forum_db();
|
|
5251
5266
|
await init_runtime_leases();
|
|
5252
5267
|
await init_runtime_topic_state();
|
|
5253
|
-
|
|
5268
|
+
db.exec(`
|
|
5254
5269
|
CREATE TABLE IF NOT EXISTS runtime_self_schedules (
|
|
5255
5270
|
id TEXT PRIMARY KEY,
|
|
5256
5271
|
topic_id TEXT NOT NULL,
|
|
@@ -5265,12 +5280,12 @@ var init_self_schedules = __esm(async () => {
|
|
|
5265
5280
|
running_query_id TEXT
|
|
5266
5281
|
)
|
|
5267
5282
|
`);
|
|
5268
|
-
|
|
5283
|
+
db.exec(`
|
|
5269
5284
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_runtime_self_schedules_one_pending
|
|
5270
5285
|
ON runtime_self_schedules(topic_id)
|
|
5271
5286
|
WHERE status = 'pending'
|
|
5272
5287
|
`);
|
|
5273
|
-
|
|
5288
|
+
db.exec(`
|
|
5274
5289
|
CREATE INDEX IF NOT EXISTS idx_runtime_self_schedules_due
|
|
5275
5290
|
ON runtime_self_schedules(status, deliver_at)
|
|
5276
5291
|
`);
|
|
@@ -5288,23 +5303,23 @@ function normalizeBrowserProfileName(name) {
|
|
|
5288
5303
|
return value;
|
|
5289
5304
|
}
|
|
5290
5305
|
function getBrowserProfileOwner(topicId, fallbackUserId) {
|
|
5291
|
-
return
|
|
5306
|
+
return db.query("SELECT browser_profile_owner FROM api_topics WHERE id = ?").get(topicId)?.browser_profile_owner ?? fallbackUserId;
|
|
5292
5307
|
}
|
|
5293
5308
|
function isTopicBrowserProfileOwner(topicId, userId) {
|
|
5294
5309
|
return getBrowserProfileOwner(topicId, "") === userId;
|
|
5295
5310
|
}
|
|
5296
5311
|
function getTopicBrowserProfile(topicId) {
|
|
5297
|
-
const value =
|
|
5312
|
+
const value = db.query("SELECT browser_profile FROM api_topics WHERE id = ?").get(topicId)?.browser_profile;
|
|
5298
5313
|
return value || DEFAULT_BROWSER_PROFILE;
|
|
5299
5314
|
}
|
|
5300
5315
|
function hasBrowserProfileTopic(topicId) {
|
|
5301
|
-
return Boolean(
|
|
5316
|
+
return Boolean(db.query("SELECT id FROM api_topics WHERE id = ?").get(topicId));
|
|
5302
5317
|
}
|
|
5303
5318
|
function createBrowserProfile(ownerId, rawName) {
|
|
5304
5319
|
const name = normalizeBrowserProfileName(rawName);
|
|
5305
5320
|
if (name === DEFAULT_BROWSER_PROFILE)
|
|
5306
5321
|
return name;
|
|
5307
|
-
|
|
5322
|
+
db.query("INSERT OR IGNORE INTO browser_profiles (owner_id, name, created_at) VALUES (?, ?, ?)").run(ownerId, name, new Date().toISOString());
|
|
5308
5323
|
return name;
|
|
5309
5324
|
}
|
|
5310
5325
|
function assignTopicBrowserProfile(opts) {
|
|
@@ -5318,7 +5333,7 @@ function assignTopicBrowserProfile(opts) {
|
|
|
5318
5333
|
if (profile !== DEFAULT_BROWSER_PROFILE)
|
|
5319
5334
|
createBrowserProfile(actualOwner, profile);
|
|
5320
5335
|
const previous = getTopicBrowserProfile(opts.topicId);
|
|
5321
|
-
const result =
|
|
5336
|
+
const result = db.query("UPDATE api_topics SET browser_profile = ? WHERE id = ?").run(profile, opts.topicId);
|
|
5322
5337
|
if (Number(result.changes ?? 0) !== 1)
|
|
5323
5338
|
throw new Error(`Topic "${opts.topicId}" not found.`);
|
|
5324
5339
|
return { previous, profile };
|
|
@@ -5326,7 +5341,7 @@ function assignTopicBrowserProfile(opts) {
|
|
|
5326
5341
|
var DEFAULT_BROWSER_PROFILE = "default";
|
|
5327
5342
|
var init_browser_profiles = __esm(async () => {
|
|
5328
5343
|
await init_forum_db();
|
|
5329
|
-
|
|
5344
|
+
db.exec(`
|
|
5330
5345
|
CREATE TABLE IF NOT EXISTS browser_profiles (
|
|
5331
5346
|
owner_id TEXT NOT NULL,
|
|
5332
5347
|
name TEXT NOT NULL,
|
|
@@ -6304,7 +6319,7 @@ function enqueueRuntimeUserTurnRequest(input) {
|
|
|
6304
6319
|
const requestId = input.requestId ?? randomUUID5();
|
|
6305
6320
|
const now = Date.now();
|
|
6306
6321
|
const topicEpoch = input.topicEpoch ?? getRuntimeTopicEpoch(input.topicId);
|
|
6307
|
-
|
|
6322
|
+
db.query(`INSERT INTO runtime_user_turn_requests
|
|
6308
6323
|
(topic_id, request_id, user_id, prompt, attachments_json,
|
|
6309
6324
|
allow_auto_continue, execution_json, topic_epoch, created_at,
|
|
6310
6325
|
status, claimed_by, claimed_at, running_query_id)
|
|
@@ -6325,8 +6340,8 @@ function enqueueRuntimeUserTurnRequest(input) {
|
|
|
6325
6340
|
return requestId;
|
|
6326
6341
|
}
|
|
6327
6342
|
function claimNextRuntimeUserTurnRequest(ownerId, now = Date.now()) {
|
|
6328
|
-
return
|
|
6329
|
-
const row =
|
|
6343
|
+
return db.transaction(() => {
|
|
6344
|
+
const row = db.query(`SELECT r.*
|
|
6330
6345
|
FROM runtime_user_turn_requests r
|
|
6331
6346
|
LEFT JOIN runtime_turn_leases l ON l.topic_id = r.topic_id
|
|
6332
6347
|
LEFT JOIN runtime_topic_state s ON s.topic_id = r.topic_id
|
|
@@ -6341,7 +6356,7 @@ function claimNextRuntimeUserTurnRequest(ownerId, now = Date.now()) {
|
|
|
6341
6356
|
LIMIT 1`).get(now - TURN_LEASE_STALE_MS, now - TOPIC_MAINTENANCE_STALE_MS, now - REQUEST_CLAIM_STALE_MS, now - REQUEST_CLAIM_STALE_MS);
|
|
6342
6357
|
if (!row)
|
|
6343
6358
|
return null;
|
|
6344
|
-
const updated =
|
|
6359
|
+
const updated = db.query(`UPDATE runtime_user_turn_requests
|
|
6345
6360
|
SET claimed_by = ?, claimed_at = ?
|
|
6346
6361
|
WHERE topic_id = ? AND request_id = ?
|
|
6347
6362
|
AND (
|
|
@@ -6354,23 +6369,23 @@ function claimNextRuntimeUserTurnRequest(ownerId, now = Date.now()) {
|
|
|
6354
6369
|
}).immediate();
|
|
6355
6370
|
}
|
|
6356
6371
|
function markRuntimeUserTurnRunning(topicId, requestId, ownerId, queryId) {
|
|
6357
|
-
const result =
|
|
6372
|
+
const result = db.query(`UPDATE runtime_user_turn_requests
|
|
6358
6373
|
SET status = 'running', running_query_id = ?, claimed_at = ?
|
|
6359
6374
|
WHERE topic_id = ? AND request_id = ? AND claimed_by = ?`).run(queryId, Date.now(), topicId, requestId, ownerId);
|
|
6360
6375
|
return Number(result.changes ?? 0) > 0;
|
|
6361
6376
|
}
|
|
6362
6377
|
function releaseRuntimeUserTurnClaim(topicId, requestId, ownerId) {
|
|
6363
|
-
const result =
|
|
6378
|
+
const result = db.query(`UPDATE runtime_user_turn_requests
|
|
6364
6379
|
SET status = 'pending', claimed_by = NULL, claimed_at = NULL, running_query_id = NULL
|
|
6365
6380
|
WHERE topic_id = ? AND request_id = ? AND claimed_by = ?`).run(topicId, requestId, ownerId);
|
|
6366
6381
|
return Number(result.changes ?? 0) > 0;
|
|
6367
6382
|
}
|
|
6368
6383
|
function completeRuntimeUserTurnRequest(topicId, requestId) {
|
|
6369
|
-
const result =
|
|
6384
|
+
const result = db.query("DELETE FROM runtime_user_turn_requests WHERE topic_id = ? AND request_id = ?").run(topicId, requestId);
|
|
6370
6385
|
return Number(result.changes ?? 0) > 0;
|
|
6371
6386
|
}
|
|
6372
6387
|
function getRuntimeUserTurnRequest(topicId) {
|
|
6373
|
-
const row =
|
|
6388
|
+
const row = db.query("SELECT * FROM runtime_user_turn_requests WHERE topic_id = ?").get(topicId);
|
|
6374
6389
|
return row ? rowToRequest(row) : null;
|
|
6375
6390
|
}
|
|
6376
6391
|
var REQUEST_CLAIM_STALE_MS;
|
|
@@ -6379,7 +6394,7 @@ var init_runtime_turn_requests = __esm(async () => {
|
|
|
6379
6394
|
await init_runtime_leases();
|
|
6380
6395
|
await init_runtime_topic_state();
|
|
6381
6396
|
REQUEST_CLAIM_STALE_MS = TURN_LEASE_STALE_MS;
|
|
6382
|
-
|
|
6397
|
+
db.exec(`
|
|
6383
6398
|
CREATE TABLE IF NOT EXISTS runtime_user_turn_requests (
|
|
6384
6399
|
topic_id TEXT PRIMARY KEY,
|
|
6385
6400
|
request_id TEXT NOT NULL UNIQUE,
|
|
@@ -6397,12 +6412,12 @@ var init_runtime_turn_requests = __esm(async () => {
|
|
|
6397
6412
|
)
|
|
6398
6413
|
`);
|
|
6399
6414
|
try {
|
|
6400
|
-
|
|
6415
|
+
db.exec("ALTER TABLE runtime_user_turn_requests ADD COLUMN execution_json TEXT");
|
|
6401
6416
|
} catch {}
|
|
6402
6417
|
try {
|
|
6403
|
-
|
|
6418
|
+
db.exec("ALTER TABLE runtime_user_turn_requests ADD COLUMN topic_epoch INTEGER NOT NULL DEFAULT 0");
|
|
6404
6419
|
} catch {}
|
|
6405
|
-
|
|
6420
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_runtime_user_turn_requests_ready ON runtime_user_turn_requests(status, created_at)");
|
|
6406
6421
|
});
|
|
6407
6422
|
|
|
6408
6423
|
// ../../packages/core/src/prompts/builders.ts
|
|
@@ -6483,7 +6498,7 @@ function loadAgentPrompt(filename) {
|
|
|
6483
6498
|
prompt: match[2].trim()
|
|
6484
6499
|
};
|
|
6485
6500
|
}
|
|
6486
|
-
function buildRuntimeToolSection(agentKind, canSpawnSubagents = false, visualTools = false) {
|
|
6501
|
+
function buildRuntimeToolSection(agentKind, canSpawnSubagents = false, visualTools = false, fileDeliveryTools = false) {
|
|
6487
6502
|
const runtimeNamespace = "mcp__runtime";
|
|
6488
6503
|
const taskNamespace = "mcp__task";
|
|
6489
6504
|
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" }.`;
|
|
@@ -6510,6 +6525,12 @@ function buildRuntimeToolSection(agentKind, canSpawnSubagents = false, visualToo
|
|
|
6510
6525
|
'Do not call a bare "show_html"; use the exposed visuals MCP tool. A successful call means the card was shown in the user chat.',
|
|
6511
6526
|
"Visual HTML runs in a sandbox. Use inline CSS/JS only; local buttons, tabs, filters, forms with preventDefault, canvas, and SVG interactions are supported. External navigation, scripts, network fetches, form posts, popups, and parent-window access are blocked."
|
|
6512
6527
|
] : [];
|
|
6528
|
+
const fileDeliverySection = fileDeliveryTools ? [
|
|
6529
|
+
"",
|
|
6530
|
+
"## File Delivery",
|
|
6531
|
+
`To send a file to the user, save it under your working directory and call the ${sendFileTool} with { file_path: "<absolute path>" }.`,
|
|
6532
|
+
"It appears as a downloadable attachment in the chat and returns success. Never claim file delivery is unavailable after a successful call."
|
|
6533
|
+
] : [];
|
|
6513
6534
|
const shared = [
|
|
6514
6535
|
"",
|
|
6515
6536
|
"",
|
|
@@ -6524,10 +6545,7 @@ function buildRuntimeToolSection(agentKind, canSpawnSubagents = false, visualToo
|
|
|
6524
6545
|
taskToolLine,
|
|
6525
6546
|
"Use this shared task store for plans, task progress, and checklist updates. It is visible across claude/codex/maestro turns and drives the live task panel.",
|
|
6526
6547
|
nativeTaskPolicyLine,
|
|
6527
|
-
|
|
6528
|
-
"## File Delivery",
|
|
6529
|
-
`To send a file to the user, save it under your working directory and call the ${sendFileTool} with { file_path: "<absolute path>" }.`,
|
|
6530
|
-
"It appears as a downloadable attachment in the chat and returns success. Never claim file delivery is unavailable after a successful call.",
|
|
6548
|
+
...fileDeliverySection,
|
|
6531
6549
|
"",
|
|
6532
6550
|
"## Session Communication",
|
|
6533
6551
|
"The session-comm MCP server is the only cross-topic messaging surface. Its canonical tools are `list_sessions`, `peek_session`, `tell_session`, `ask_session`, and `abort_session`.",
|
|
@@ -6574,7 +6592,7 @@ function buildTopicSystemPrompt(opts) {
|
|
|
6574
6592
|
TOPIC_TITLE: opts.topicTitle,
|
|
6575
6593
|
WORKSPACE_CWD: opts.workspaceCwd,
|
|
6576
6594
|
UPLOADS_DIR: uploadsDir
|
|
6577
|
-
}) + buildRuntimeToolSection(opts.agentKind, opts.canSpawnSubagents, opts.visualTools);
|
|
6595
|
+
}) + buildRuntimeToolSection(opts.agentKind, opts.canSpawnSubagents, opts.visualTools, opts.fileDeliveryTools);
|
|
6578
6596
|
if (opts.description?.trim()) {
|
|
6579
6597
|
prompt += `
|
|
6580
6598
|
|
|
@@ -6590,7 +6608,7 @@ function buildChannelSystemPrompt(opts) {
|
|
|
6590
6608
|
TOPIC_TITLE: opts.topicTitle,
|
|
6591
6609
|
WORKSPACE_CWD: opts.workspaceCwd,
|
|
6592
6610
|
UPLOADS_DIR: uploadsDir
|
|
6593
|
-
}) + buildRuntimeToolSection(opts.agentKind, false, opts.visualTools);
|
|
6611
|
+
}) + buildRuntimeToolSection(opts.agentKind, false, opts.visualTools, opts.fileDeliveryTools);
|
|
6594
6612
|
}
|
|
6595
6613
|
function buildManagerSystemPrompt(opts) {
|
|
6596
6614
|
return `${buildTopicSystemPrompt(opts)}
|
|
@@ -6657,7 +6675,7 @@ function rowToBrief(r) {
|
|
|
6657
6675
|
};
|
|
6658
6676
|
}
|
|
6659
6677
|
function getTopicBrief(topicId) {
|
|
6660
|
-
const row =
|
|
6678
|
+
const row = db.query("SELECT topic_id, brief_md, latest_summary_md, summary_date, updated_at FROM api_topic_brief WHERE topic_id = ?").get(topicId);
|
|
6661
6679
|
if (!row)
|
|
6662
6680
|
return null;
|
|
6663
6681
|
return rowToBrief(row);
|
|
@@ -6674,7 +6692,7 @@ function setTopicBrief(topicId, fields) {
|
|
|
6674
6692
|
const briefMd = fields.briefMd !== undefined ? fields.briefMd : existing?.briefMd ?? "";
|
|
6675
6693
|
const latestSummaryMd = fields.latestSummaryMd !== undefined ? fields.latestSummaryMd : existing?.latestSummaryMd ?? null;
|
|
6676
6694
|
const summaryDate = fields.summaryDate !== undefined ? fields.summaryDate : existing?.summaryDate ?? null;
|
|
6677
|
-
|
|
6695
|
+
db.query(`INSERT INTO api_topic_brief
|
|
6678
6696
|
(topic_id, brief_md, latest_summary_md, summary_date, updated_at)
|
|
6679
6697
|
VALUES (?, ?, ?, ?, datetime('now'))
|
|
6680
6698
|
ON CONFLICT(topic_id) DO UPDATE SET
|
|
@@ -7063,7 +7081,7 @@ var init_topic_archive = __esm(async () => {
|
|
|
7063
7081
|
|
|
7064
7082
|
// ../../packages/core/src/storage/topic-archive-state.ts
|
|
7065
7083
|
function getTopicArchiveState(topicId) {
|
|
7066
|
-
const row =
|
|
7084
|
+
const row = db.query("SELECT * FROM api_topic_archive_state WHERE topic_id = ?").get(topicId);
|
|
7067
7085
|
if (!row)
|
|
7068
7086
|
return null;
|
|
7069
7087
|
return {
|
|
@@ -7074,7 +7092,7 @@ function getTopicArchiveState(topicId) {
|
|
|
7074
7092
|
};
|
|
7075
7093
|
}
|
|
7076
7094
|
function setTopicArchiveState(topicId, lastArchivedRowid, lastArchivePath) {
|
|
7077
|
-
|
|
7095
|
+
db.query(`INSERT INTO api_topic_archive_state (topic_id, last_archived_rowid, last_archive_path, updated_at)
|
|
7078
7096
|
VALUES (?, ?, ?, ?)
|
|
7079
7097
|
ON CONFLICT(topic_id) DO UPDATE SET
|
|
7080
7098
|
last_archived_rowid = excluded.last_archived_rowid,
|
|
@@ -7958,11 +7976,11 @@ function stringifyError(err) {
|
|
|
7958
7976
|
function authRecoveryHint(agent) {
|
|
7959
7977
|
switch (agent) {
|
|
7960
7978
|
case "claude":
|
|
7961
|
-
return "Claude Code
|
|
7979
|
+
return "Please refresh your Claude Code login";
|
|
7962
7980
|
case "codex":
|
|
7963
|
-
return "`codex login`
|
|
7981
|
+
return "Please log in again with `codex login` (~/.codex/auth.json)";
|
|
7964
7982
|
case "maestro":
|
|
7965
|
-
return "DEEPSEEK_API_KEY
|
|
7983
|
+
return "Please check the DEEPSEEK_API_KEY environment variable";
|
|
7966
7984
|
}
|
|
7967
7985
|
}
|
|
7968
7986
|
function classifyAgentError(err, agent) {
|
|
@@ -7970,25 +7988,25 @@ function classifyAgentError(err, agent) {
|
|
|
7970
7988
|
const hint = authRecoveryHint(agent);
|
|
7971
7989
|
const ctor = typeof err === "object" && err !== null ? err.constructor?.name : undefined;
|
|
7972
7990
|
if (ctor === "AuthenticationError")
|
|
7973
|
-
return `${name}
|
|
7991
|
+
return `${name} authentication expired. ${hint}. (401)`;
|
|
7974
7992
|
if (ctor === "RateLimitError")
|
|
7975
|
-
return `${name}
|
|
7993
|
+
return `${name} request limit exceeded. Please try again in a moment. (429)`;
|
|
7976
7994
|
if (ctor === "InternalServerError") {
|
|
7977
7995
|
const status = err.status;
|
|
7978
|
-
return status === 529 ? `${name}
|
|
7996
|
+
return status === 529 ? `${name} server is overloaded. Please try again in a moment. (529)` : `${name} server error occurred. Please try again in a moment. (500)`;
|
|
7979
7997
|
}
|
|
7980
7998
|
const s = stringifyError(err);
|
|
7981
7999
|
if (/401|authentication.error|invalid.*api.key|not logged|ANTHROPIC_API_KEY|DEEPSEEK_API_KEY/i.test(s)) {
|
|
7982
|
-
return `${name}
|
|
8000
|
+
return `${name} authentication expired. ${hint}. (401)`;
|
|
7983
8001
|
}
|
|
7984
8002
|
if (/429|rate.limit/i.test(s))
|
|
7985
|
-
return `${name}
|
|
8003
|
+
return `${name} request limit exceeded. Please try again in a moment. (429)`;
|
|
7986
8004
|
if (/529|overloaded/i.test(s))
|
|
7987
|
-
return `${name}
|
|
8005
|
+
return `${name} server is overloaded. Please try again in a moment. (529)`;
|
|
7988
8006
|
if (/500|internal.server/i.test(s))
|
|
7989
|
-
return `${name}
|
|
8007
|
+
return `${name} server error occurred. Please try again in a moment. (500)`;
|
|
7990
8008
|
const snippet = s.length > 200 ? `${s.slice(0, 200)}...` : s;
|
|
7991
|
-
return `${name}
|
|
8009
|
+
return `${name} error: ${snippet}`;
|
|
7992
8010
|
}
|
|
7993
8011
|
function isSessionExpiredError(message) {
|
|
7994
8012
|
return message.includes(SESSION_EXPIRED_MSG) || /session was recorded with model .+ but is resuming with/i.test(message) || /session.*not found|session.*expired|unknown conversation/i.test(message);
|
|
@@ -8152,13 +8170,13 @@ function normalizeVisualKind(value) {
|
|
|
8152
8170
|
return VISUAL_KINDS.includes(value) ? value : "html";
|
|
8153
8171
|
}
|
|
8154
8172
|
function insertVisual(input) {
|
|
8155
|
-
const result =
|
|
8173
|
+
const result = db.query(`INSERT INTO api_topic_visuals
|
|
8156
8174
|
(topic_id, html, title, created_at, kind, source, file_id, mime_type, media_token)
|
|
8157
8175
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8158
8176
|
RETURNING id`).get(input.topicId, input.html, input.title, Date.now(), input.kind ?? "html", input.source ?? null, input.fileId ?? null, input.mimeType ?? null, input.mediaToken ?? null);
|
|
8159
8177
|
if (!result)
|
|
8160
8178
|
throw new Error("Failed to insert visual");
|
|
8161
|
-
|
|
8179
|
+
db.query(`DELETE FROM api_topic_visual_views
|
|
8162
8180
|
WHERE visual_id IN (
|
|
8163
8181
|
SELECT id FROM api_topic_visuals
|
|
8164
8182
|
WHERE topic_id = ? AND id NOT IN (
|
|
@@ -8166,7 +8184,7 @@ function insertVisual(input) {
|
|
|
8166
8184
|
ORDER BY created_at DESC LIMIT ?
|
|
8167
8185
|
)
|
|
8168
8186
|
)`).run(input.topicId, input.topicId, MAX_HISTORY);
|
|
8169
|
-
|
|
8187
|
+
db.query(`DELETE FROM api_topic_visuals
|
|
8170
8188
|
WHERE topic_id = ? AND id NOT IN (
|
|
8171
8189
|
SELECT id FROM api_topic_visuals WHERE topic_id = ?
|
|
8172
8190
|
ORDER BY created_at DESC LIMIT ?
|
|
@@ -8174,22 +8192,22 @@ function insertVisual(input) {
|
|
|
8174
8192
|
return result.id;
|
|
8175
8193
|
}
|
|
8176
8194
|
function listVisuals(topicId) {
|
|
8177
|
-
return
|
|
8195
|
+
return db.query("SELECT id, title, created_at, kind, mime_type FROM api_topic_visuals WHERE topic_id = ? ORDER BY created_at DESC").all(topicId).map((row) => ({ ...row, kind: normalizeVisualKind(row.kind) }));
|
|
8178
8196
|
}
|
|
8179
8197
|
function getVisualById(id) {
|
|
8180
|
-
const row =
|
|
8198
|
+
const row = db.query("SELECT * FROM api_topic_visuals WHERE id = ?").get(id);
|
|
8181
8199
|
return row ? { ...row, kind: normalizeVisualKind(row.kind) } : null;
|
|
8182
8200
|
}
|
|
8183
8201
|
function topicHasVisualFileId(topicId, fileId) {
|
|
8184
|
-
const row =
|
|
8202
|
+
const row = db.query("SELECT id FROM api_topic_visuals WHERE topic_id = ? AND file_id = ? LIMIT 1").get(topicId, fileId);
|
|
8185
8203
|
return Boolean(row);
|
|
8186
8204
|
}
|
|
8187
8205
|
function getUserActiveVisualId(topicId, userId) {
|
|
8188
|
-
const row =
|
|
8206
|
+
const row = db.query("SELECT visual_id FROM api_topic_visual_views WHERE topic_id = ? AND user_id = ?").get(topicId, userId);
|
|
8189
8207
|
return row?.visual_id ?? null;
|
|
8190
8208
|
}
|
|
8191
8209
|
function setUserActiveVisualId(topicId, userId, visualId) {
|
|
8192
|
-
|
|
8210
|
+
db.query(`INSERT INTO api_topic_visual_views (topic_id, user_id, visual_id) VALUES (?, ?, ?)
|
|
8193
8211
|
ON CONFLICT(topic_id, user_id) DO UPDATE SET visual_id = excluded.visual_id`).run(topicId, userId, visualId);
|
|
8194
8212
|
}
|
|
8195
8213
|
function withVisualBaseStyle(html) {
|
|
@@ -8313,7 +8331,7 @@ if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",
|
|
|
8313
8331
|
</script>`;
|
|
8314
8332
|
var init_visual_store = __esm(async () => {
|
|
8315
8333
|
await init_forum_db();
|
|
8316
|
-
|
|
8334
|
+
db.exec(`
|
|
8317
8335
|
CREATE TABLE IF NOT EXISTS api_topic_visuals (
|
|
8318
8336
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
8319
8337
|
topic_id TEXT NOT NULL,
|
|
@@ -8344,7 +8362,7 @@ var init_visual_store = __esm(async () => {
|
|
|
8344
8362
|
"ALTER TABLE api_topic_visuals ADD COLUMN media_token TEXT"
|
|
8345
8363
|
]) {
|
|
8346
8364
|
try {
|
|
8347
|
-
|
|
8365
|
+
db.exec(statement);
|
|
8348
8366
|
} catch {}
|
|
8349
8367
|
}
|
|
8350
8368
|
VISUAL_KINDS = ["html", "mermaid", "image", "video"];
|
|
@@ -8644,7 +8662,7 @@ import { mkdirSync as mkdirSync14 } from "fs";
|
|
|
8644
8662
|
import { join as join20 } from "path";
|
|
8645
8663
|
function tokenStatsFileId(userId) {
|
|
8646
8664
|
const rawUserId = String(userId);
|
|
8647
|
-
return /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) ? rawUserId : `sha256-${createHash3("sha256").update(rawUserId).digest("hex")}`;
|
|
8665
|
+
return /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash3("sha256").update(rawUserId).digest("hex")}`;
|
|
8648
8666
|
}
|
|
8649
8667
|
function queriesPath(userId) {
|
|
8650
8668
|
const fileId = tokenStatsFileId(userId);
|
|
@@ -8789,7 +8807,7 @@ import {
|
|
|
8789
8807
|
import { dirname as dirname11, join as join21 } from "path";
|
|
8790
8808
|
function pendingAskDir(userId) {
|
|
8791
8809
|
const rawUserId = String(userId);
|
|
8792
|
-
const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) ? rawUserId : `sha256-${createHash4("sha256").update(rawUserId).digest("hex")}`;
|
|
8810
|
+
const safeUserId = /^[A-Za-z0-9][A-Za-z0-9_.@-]{0,255}$/.test(rawUserId) && !rawUserId.includes("..") ? rawUserId : `sha256-${createHash4("sha256").update(rawUserId).digest("hex")}`;
|
|
8793
8811
|
return join21(resolveStorageSessionAsksDir(), safeUserId);
|
|
8794
8812
|
}
|
|
8795
8813
|
function encodeAskKey(key) {
|
|
@@ -8848,6 +8866,12 @@ function readPendingAskFile(path, fallback) {
|
|
|
8848
8866
|
const parsed = JSON.parse(raw);
|
|
8849
8867
|
if (!parsed.requestId || !parsed.from || !parsed.to)
|
|
8850
8868
|
return null;
|
|
8869
|
+
if (parsed.userId !== undefined && String(parsed.userId) !== String(fallback.userId))
|
|
8870
|
+
return null;
|
|
8871
|
+
if (fallback.from && parsed.from !== fallback.from)
|
|
8872
|
+
return null;
|
|
8873
|
+
if (fallback.to && parsed.to !== fallback.to)
|
|
8874
|
+
return null;
|
|
8851
8875
|
const now = new Date().toISOString();
|
|
8852
8876
|
return {
|
|
8853
8877
|
userId: fallback.userId,
|
|
@@ -9133,7 +9157,7 @@ __export(exports_ask_callbacks, {
|
|
|
9133
9157
|
function registerAskCallback(entry) {
|
|
9134
9158
|
pendingAsks2.set(entry.targetQueryId, entry);
|
|
9135
9159
|
if (entry.remoteReply) {
|
|
9136
|
-
|
|
9160
|
+
db.query(`INSERT INTO remote_ask_callbacks
|
|
9137
9161
|
(target_query_id, request_id, node_name, node_cell_id, topic_id, user_id, created_at)
|
|
9138
9162
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
9139
9163
|
ON CONFLICT(target_query_id) DO UPDATE SET
|
|
@@ -9153,7 +9177,7 @@ function resolveAskCallback(queryId) {
|
|
|
9153
9177
|
entry.timedOut = true;
|
|
9154
9178
|
}
|
|
9155
9179
|
pendingAsks2.delete(queryId);
|
|
9156
|
-
|
|
9180
|
+
db.query("DELETE FROM remote_ask_callbacks WHERE target_query_id = ?").run(queryId);
|
|
9157
9181
|
return entry;
|
|
9158
9182
|
}
|
|
9159
9183
|
function purgeStaleAsks(now = Date.now()) {
|
|
@@ -9161,7 +9185,7 @@ function purgeStaleAsks(now = Date.now()) {
|
|
|
9161
9185
|
if (now - entry.createdAt > MAX_ASK_AGE_MS)
|
|
9162
9186
|
pendingAsks2.delete(id);
|
|
9163
9187
|
}
|
|
9164
|
-
|
|
9188
|
+
db.query("DELETE FROM remote_ask_callbacks WHERE created_at < ?").run(now - MAX_ASK_AGE_MS);
|
|
9165
9189
|
}
|
|
9166
9190
|
function cancelAskCallbacksForTopic(topicId) {
|
|
9167
9191
|
let cancelled = 0;
|
|
@@ -9169,13 +9193,13 @@ function cancelAskCallbacksForTopic(topicId) {
|
|
|
9169
9193
|
if (entry.callerTopicId !== topicId)
|
|
9170
9194
|
continue;
|
|
9171
9195
|
pendingAsks2.delete(queryId);
|
|
9172
|
-
|
|
9196
|
+
db.query("DELETE FROM remote_ask_callbacks WHERE target_query_id = ?").run(queryId);
|
|
9173
9197
|
cancelled++;
|
|
9174
9198
|
}
|
|
9175
9199
|
return cancelled;
|
|
9176
9200
|
}
|
|
9177
9201
|
async function failInterruptedRemoteAskCallbacks() {
|
|
9178
|
-
const rows =
|
|
9202
|
+
const rows = db.query(`SELECT target_query_id, request_id, node_name, node_cell_id, topic_id, user_id
|
|
9179
9203
|
FROM remote_ask_callbacks
|
|
9180
9204
|
ORDER BY created_at ASC`).all();
|
|
9181
9205
|
let failed = 0;
|
|
@@ -9189,7 +9213,7 @@ async function failInterruptedRemoteAskCallbacks() {
|
|
|
9189
9213
|
}, "peer", "The remote worker restarted before this ask completed. Please retry the request.", "error");
|
|
9190
9214
|
if (!delivered)
|
|
9191
9215
|
continue;
|
|
9192
|
-
|
|
9216
|
+
db.query("DELETE FROM remote_ask_callbacks WHERE target_query_id = ?").run(row.target_query_id);
|
|
9193
9217
|
failed++;
|
|
9194
9218
|
}
|
|
9195
9219
|
return failed;
|
|
@@ -9198,7 +9222,7 @@ var pendingAsks2, MAX_ASK_AGE_MS;
|
|
|
9198
9222
|
var init_ask_callbacks = __esm(async () => {
|
|
9199
9223
|
await init_forum_db();
|
|
9200
9224
|
await init_session_asks();
|
|
9201
|
-
|
|
9225
|
+
db.exec(`
|
|
9202
9226
|
CREATE TABLE IF NOT EXISTS remote_ask_callbacks (
|
|
9203
9227
|
target_query_id TEXT PRIMARY KEY,
|
|
9204
9228
|
request_id TEXT NOT NULL,
|
|
@@ -9859,6 +9883,7 @@ function redispatchInject(inject) {
|
|
|
9859
9883
|
sessionName: inject.sessionName,
|
|
9860
9884
|
sessionType: inject.sessionType,
|
|
9861
9885
|
visualTools: inject.visualTools,
|
|
9886
|
+
fileDeliveryTools: inject.fileDeliveryTools,
|
|
9862
9887
|
onSessionId: inject.onSessionId,
|
|
9863
9888
|
onSessionReset: inject.onSessionReset,
|
|
9864
9889
|
bridgeSessionFromHistory: inject.bridgeSessionFromHistory,
|
|
@@ -10040,6 +10065,7 @@ function serializableUserTurnExecution(params) {
|
|
|
10040
10065
|
sessionName: params.sessionName,
|
|
10041
10066
|
sessionType: params.sessionType,
|
|
10042
10067
|
visualTools: params.visualTools,
|
|
10068
|
+
fileDeliveryTools: params.fileDeliveryTools,
|
|
10043
10069
|
bridgeSessionFromHistory: params.bridgeSessionFromHistory,
|
|
10044
10070
|
peerBridge: params.peerBridge,
|
|
10045
10071
|
from: params.from
|
|
@@ -10119,6 +10145,7 @@ async function drainOneDurableUserTurn() {
|
|
|
10119
10145
|
sessionName: execution?.sessionName,
|
|
10120
10146
|
sessionType: execution?.sessionType,
|
|
10121
10147
|
visualTools: execution?.visualTools,
|
|
10148
|
+
fileDeliveryTools: execution?.fileDeliveryTools,
|
|
10122
10149
|
bridgeSessionFromHistory: execution?.bridgeSessionFromHistory,
|
|
10123
10150
|
peerBridge: execution?.peerBridge,
|
|
10124
10151
|
from: execution?.from,
|
|
@@ -10204,6 +10231,7 @@ function startAiTurn(params) {
|
|
|
10204
10231
|
const sessionName = params.sessionName ?? topic.title;
|
|
10205
10232
|
const sessionType = params.sessionType;
|
|
10206
10233
|
const visualTools = params.visualTools === true;
|
|
10234
|
+
const fileDeliveryTools = params.fileDeliveryTools === true;
|
|
10207
10235
|
const onSessionId = params.onSessionId;
|
|
10208
10236
|
const onSessionReset = params.onSessionReset;
|
|
10209
10237
|
const bridgeSessionFromHistory = params.bridgeSessionFromHistory === true;
|
|
@@ -10252,6 +10280,7 @@ function startAiTurn(params) {
|
|
|
10252
10280
|
sessionName,
|
|
10253
10281
|
sessionType,
|
|
10254
10282
|
visualTools,
|
|
10283
|
+
fileDeliveryTools,
|
|
10255
10284
|
onSessionId,
|
|
10256
10285
|
onSessionReset,
|
|
10257
10286
|
bridgeSessionFromHistory,
|
|
@@ -10359,6 +10388,7 @@ function startAiTurn(params) {
|
|
|
10359
10388
|
sessionName,
|
|
10360
10389
|
sessionType,
|
|
10361
10390
|
visualTools,
|
|
10391
|
+
fileDeliveryTools,
|
|
10362
10392
|
onSessionId,
|
|
10363
10393
|
onSessionReset,
|
|
10364
10394
|
bridgeSessionFromHistory,
|
|
@@ -10460,7 +10490,8 @@ function startAiTurn(params) {
|
|
|
10460
10490
|
agentKind,
|
|
10461
10491
|
description: topic.description,
|
|
10462
10492
|
canSpawnSubagents: peerBridge?.canSpawnSubagents ?? (topicRecord?.kind === "agent" && !topicRecord.isSubagent),
|
|
10463
|
-
visualTools
|
|
10493
|
+
visualTools,
|
|
10494
|
+
fileDeliveryTools
|
|
10464
10495
|
};
|
|
10465
10496
|
const isManager = topicRecord?.kind === "manager";
|
|
10466
10497
|
const isMentionOnlyChannel = topic.aiMode === "mention" && !isManager;
|
|
@@ -10610,6 +10641,7 @@ ${playwrightNote}`;
|
|
|
10610
10641
|
wikiTopicId: memoryTopic.id,
|
|
10611
10642
|
autoContinue: allowAutoContinue && !silent,
|
|
10612
10643
|
visualTools,
|
|
10644
|
+
fileDeliveryTools,
|
|
10613
10645
|
peerBridge
|
|
10614
10646
|
});
|
|
10615
10647
|
} finally {
|
|
@@ -10674,6 +10706,7 @@ ${playwrightNote}`;
|
|
|
10674
10706
|
sessionName,
|
|
10675
10707
|
sessionType,
|
|
10676
10708
|
visualTools,
|
|
10709
|
+
fileDeliveryTools,
|
|
10677
10710
|
onSessionId,
|
|
10678
10711
|
onSessionReset,
|
|
10679
10712
|
bridgeSessionFromHistory,
|
|
@@ -10800,6 +10833,7 @@ function triggerTopicAiTurn(topicId, userId, prompt, agentType, opts) {
|
|
|
10800
10833
|
sessionName: opts?.sessionName,
|
|
10801
10834
|
sessionType: opts?.sessionType,
|
|
10802
10835
|
visualTools: opts?.visualTools,
|
|
10836
|
+
fileDeliveryTools: opts?.fileDeliveryTools,
|
|
10803
10837
|
onSessionId: opts?.onSessionId,
|
|
10804
10838
|
onSessionReset: opts?.onSessionReset,
|
|
10805
10839
|
bridgeSessionFromHistory: opts?.bridgeSessionFromHistory,
|
|
@@ -11418,7 +11452,7 @@ init_logger();
|
|
|
11418
11452
|
// ../../packages/core/src/storage/runtime-process-leases.ts
|
|
11419
11453
|
await init_forum_db();
|
|
11420
11454
|
await init_runtime_leases();
|
|
11421
|
-
|
|
11455
|
+
db.exec(`
|
|
11422
11456
|
CREATE TABLE IF NOT EXISTS runtime_process_leases (
|
|
11423
11457
|
role TEXT PRIMARY KEY,
|
|
11424
11458
|
owner_id TEXT NOT NULL UNIQUE,
|
|
@@ -11427,7 +11461,7 @@ storageDatabase.exec(`
|
|
|
11427
11461
|
heartbeat_at INTEGER NOT NULL
|
|
11428
11462
|
)
|
|
11429
11463
|
`);
|
|
11430
|
-
|
|
11464
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_runtime_process_leases_heartbeat ON runtime_process_leases(heartbeat_at)");
|
|
11431
11465
|
|
|
11432
11466
|
// ../../packages/core/src/index.ts
|
|
11433
11467
|
await init_manager();
|
|
@@ -11484,6 +11518,8 @@ function backgroundSessionProgress(topicId, queryId) {
|
|
|
11484
11518
|
}
|
|
11485
11519
|
return { status, steps: steps.slice(-MAX_STEPS) };
|
|
11486
11520
|
}
|
|
11521
|
+
// ../../packages/core/src/runtime/delivery-ack.ts
|
|
11522
|
+
await init_bus();
|
|
11487
11523
|
|
|
11488
11524
|
// ../../packages/core/src/index.ts
|
|
11489
11525
|
init_file_hooks();
|
|
@@ -11839,24 +11875,24 @@ async function runCronPromptScript(options) {
|
|
|
11839
11875
|
|
|
11840
11876
|
// ../../packages/module-cron/src/store.ts
|
|
11841
11877
|
var schemaReady = false;
|
|
11842
|
-
var
|
|
11878
|
+
var db2 = db;
|
|
11843
11879
|
function configureCronDatabase(database) {
|
|
11844
|
-
const previous =
|
|
11845
|
-
|
|
11880
|
+
const previous = db2;
|
|
11881
|
+
db2 = database;
|
|
11846
11882
|
schemaReady = false;
|
|
11847
11883
|
let disposed = false;
|
|
11848
11884
|
return () => {
|
|
11849
11885
|
if (disposed)
|
|
11850
11886
|
return;
|
|
11851
11887
|
disposed = true;
|
|
11852
|
-
|
|
11888
|
+
db2 = previous;
|
|
11853
11889
|
schemaReady = false;
|
|
11854
11890
|
};
|
|
11855
11891
|
}
|
|
11856
11892
|
function ensureCronSchema() {
|
|
11857
11893
|
if (schemaReady)
|
|
11858
11894
|
return;
|
|
11859
|
-
|
|
11895
|
+
db2.exec(`
|
|
11860
11896
|
CREATE TABLE IF NOT EXISTS negotium_cron_jobs (
|
|
11861
11897
|
id TEXT PRIMARY KEY,
|
|
11862
11898
|
name TEXT NOT NULL,
|
|
@@ -11925,28 +11961,28 @@ function ensureCronSchema() {
|
|
|
11925
11961
|
updated_at TEXT NOT NULL
|
|
11926
11962
|
);
|
|
11927
11963
|
`);
|
|
11928
|
-
const jobColumns = new Set(
|
|
11964
|
+
const jobColumns = new Set(db2.query("PRAGMA table_info(negotium_cron_jobs)").all().map((column) => column.name));
|
|
11929
11965
|
if (!jobColumns.has("script")) {
|
|
11930
|
-
|
|
11966
|
+
db2.exec("ALTER TABLE negotium_cron_jobs ADD COLUMN script TEXT");
|
|
11931
11967
|
}
|
|
11932
11968
|
if (!jobColumns.has("summary")) {
|
|
11933
|
-
|
|
11969
|
+
db2.exec("ALTER TABLE negotium_cron_jobs ADD COLUMN summary TEXT");
|
|
11934
11970
|
}
|
|
11935
|
-
const runColumns = new Set(
|
|
11971
|
+
const runColumns = new Set(db2.query("PRAGMA table_info(negotium_cron_runs)").all().map((column) => column.name));
|
|
11936
11972
|
if (!runColumns.has("exit_code")) {
|
|
11937
|
-
|
|
11973
|
+
db2.exec("ALTER TABLE negotium_cron_runs ADD COLUMN exit_code INTEGER");
|
|
11938
11974
|
}
|
|
11939
11975
|
if (!runColumns.has("topic_id")) {
|
|
11940
|
-
|
|
11976
|
+
db2.exec("ALTER TABLE negotium_cron_runs ADD COLUMN topic_id TEXT");
|
|
11941
11977
|
}
|
|
11942
|
-
const legacySessions =
|
|
11978
|
+
const legacySessions = db2.query(`SELECT j.topic_id, COALESCE(j.agent, t.agent) AS agent,
|
|
11943
11979
|
j.owner_user_id, j.session_id, j.created_at, j.updated_at
|
|
11944
11980
|
FROM negotium_cron_jobs j
|
|
11945
11981
|
LEFT JOIN api_topics t ON t.id = j.topic_id
|
|
11946
11982
|
WHERE j.session_id IS NOT NULL AND COALESCE(j.agent, t.agent) IS NOT NULL
|
|
11947
11983
|
ORDER BY j.updated_at ASC`).all();
|
|
11948
11984
|
for (const legacy of legacySessions) {
|
|
11949
|
-
|
|
11985
|
+
db2.query(`INSERT INTO negotium_cron_topic_sessions
|
|
11950
11986
|
(topic_id,agent,owner_user_id,session_id,created_at,updated_at)
|
|
11951
11987
|
VALUES (?,?,?,?,?,?)
|
|
11952
11988
|
ON CONFLICT(topic_id,agent) DO UPDATE SET
|
|
@@ -11955,13 +11991,13 @@ function ensureCronSchema() {
|
|
|
11955
11991
|
updated_at = excluded.updated_at`).run(legacy.topic_id, legacy.agent, legacy.owner_user_id, legacy.session_id, legacy.created_at, legacy.updated_at);
|
|
11956
11992
|
}
|
|
11957
11993
|
if (legacySessions.length > 0) {
|
|
11958
|
-
|
|
11994
|
+
db2.query("UPDATE negotium_cron_jobs SET session_id = NULL WHERE session_id IS NOT NULL").run();
|
|
11959
11995
|
}
|
|
11960
11996
|
schemaReady = true;
|
|
11961
11997
|
}
|
|
11962
11998
|
function toJob(row) {
|
|
11963
|
-
const topicAgent = row.agent ? row.agent :
|
|
11964
|
-
const topicSession = topicAgent ?
|
|
11999
|
+
const topicAgent = row.agent ? row.agent : db2.query("SELECT agent FROM api_topics WHERE id = ?").get(row.topic_id)?.agent;
|
|
12000
|
+
const topicSession = topicAgent ? db2.query("SELECT session_id FROM negotium_cron_topic_sessions WHERE topic_id = ? AND agent = ?").get(row.topic_id, topicAgent) : undefined;
|
|
11965
12001
|
return {
|
|
11966
12002
|
id: row.id,
|
|
11967
12003
|
name: row.name,
|
|
@@ -12031,7 +12067,7 @@ function createCronJob(input) {
|
|
|
12031
12067
|
if (!valid.ok)
|
|
12032
12068
|
throw new Error(valid.error);
|
|
12033
12069
|
}
|
|
12034
|
-
const otherOwner =
|
|
12070
|
+
const otherOwner = db2.query(`SELECT owner_user_id
|
|
12035
12071
|
FROM negotium_cron_jobs
|
|
12036
12072
|
WHERE topic_id = ? AND owner_user_id <> ?
|
|
12037
12073
|
LIMIT 1`).get(input.topicId, input.ownerUserId);
|
|
@@ -12062,36 +12098,36 @@ function createCronJob(input) {
|
|
|
12062
12098
|
createdAt: nowIso,
|
|
12063
12099
|
updatedAt: nowIso
|
|
12064
12100
|
};
|
|
12065
|
-
|
|
12101
|
+
db2.query(`INSERT INTO negotium_cron_jobs
|
|
12066
12102
|
(id,name,owner_user_id,topic_id,prompt,script,summary,schedule,timezone,enabled,agent,model,effort,session_id,next_run_at,created_at,updated_at)
|
|
12067
12103
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(job.id, job.name, job.ownerUserId, job.topicId, job.prompt ?? "", job.script ?? null, job.summary ?? null, job.schedule, job.timezone ?? null, 1, job.agent ?? null, job.model ?? null, job.effort ?? null, null, job.nextRunAt, job.createdAt, job.updatedAt);
|
|
12068
12104
|
return job;
|
|
12069
12105
|
}
|
|
12070
12106
|
function listCronJobs(ownerUserId) {
|
|
12071
12107
|
ensureCronSchema();
|
|
12072
|
-
const rows = ownerUserId ?
|
|
12108
|
+
const rows = ownerUserId ? db2.query("SELECT * FROM negotium_cron_jobs WHERE owner_user_id = ? ORDER BY created_at DESC").all(ownerUserId) : db2.query("SELECT * FROM negotium_cron_jobs ORDER BY created_at DESC").all();
|
|
12073
12109
|
return rows.map(toJob);
|
|
12074
12110
|
}
|
|
12075
12111
|
function listEnabledCronJobs() {
|
|
12076
12112
|
ensureCronSchema();
|
|
12077
|
-
return
|
|
12113
|
+
return db2.query("SELECT * FROM negotium_cron_jobs WHERE enabled = 1 ORDER BY created_at DESC").all().map(toJob);
|
|
12078
12114
|
}
|
|
12079
12115
|
function listCronJobsForTopic(topicId) {
|
|
12080
12116
|
ensureCronSchema();
|
|
12081
|
-
return
|
|
12117
|
+
return db2.query("SELECT * FROM negotium_cron_jobs WHERE topic_id = ? ORDER BY created_at DESC").all(topicId).map(toJob);
|
|
12082
12118
|
}
|
|
12083
12119
|
function listCronJobsForTopicOwner(topicId, ownerUserId) {
|
|
12084
12120
|
ensureCronSchema();
|
|
12085
|
-
return
|
|
12121
|
+
return db2.query("SELECT * FROM negotium_cron_jobs WHERE topic_id = ? AND owner_user_id = ? ORDER BY created_at DESC").all(topicId, ownerUserId).map(toJob);
|
|
12086
12122
|
}
|
|
12087
12123
|
function getCronJob(id) {
|
|
12088
12124
|
ensureCronSchema();
|
|
12089
|
-
const row =
|
|
12125
|
+
const row = db2.query("SELECT * FROM negotium_cron_jobs WHERE id = ?").get(id);
|
|
12090
12126
|
return row ? toJob(row) : null;
|
|
12091
12127
|
}
|
|
12092
12128
|
function getCronJobByOwnerAndName(ownerUserId, name) {
|
|
12093
12129
|
ensureCronSchema();
|
|
12094
|
-
const row =
|
|
12130
|
+
const row = db2.query("SELECT * FROM negotium_cron_jobs WHERE owner_user_id = ? AND name = ?").get(ownerUserId, name);
|
|
12095
12131
|
return row ? toJob(row) : null;
|
|
12096
12132
|
}
|
|
12097
12133
|
function cronJobPatchChangesContext(job, patch) {
|
|
@@ -12125,7 +12161,7 @@ function updateCronJob(id, patch, now = new Date) {
|
|
|
12125
12161
|
if (rawTimezone && !nextTimezone)
|
|
12126
12162
|
throw new Error(`invalid timezone: ${rawTimezone}`);
|
|
12127
12163
|
const nextTopicId = patch.topicId?.trim() || job.topicId;
|
|
12128
|
-
const otherOwner =
|
|
12164
|
+
const otherOwner = db2.query(`SELECT owner_user_id FROM negotium_cron_jobs
|
|
12129
12165
|
WHERE topic_id = ? AND owner_user_id <> ? AND id <> ? LIMIT 1`).get(nextTopicId, job.ownerUserId, id);
|
|
12130
12166
|
if (otherOwner) {
|
|
12131
12167
|
throw new Error(`topic cron context is already owned by ${otherOwner.owner_user_id}; ` + "all cron jobs in one topic must share one owner");
|
|
@@ -12136,13 +12172,13 @@ function updateCronJob(id, patch, now = new Date) {
|
|
|
12136
12172
|
const timestamp = now.toISOString();
|
|
12137
12173
|
const sessionSensitiveChanged = cronJobPatchChangesContext(job, patch);
|
|
12138
12174
|
if (sessionSensitiveChanged) {
|
|
12139
|
-
const active =
|
|
12175
|
+
const active = db2.query("SELECT 1 FROM negotium_cron_runs WHERE job_id = ? AND status IN ('pending','running') LIMIT 1").get(id);
|
|
12140
12176
|
if (active) {
|
|
12141
12177
|
throw new Error("cannot change Cron context while a run is active; cancel it first");
|
|
12142
12178
|
}
|
|
12143
12179
|
}
|
|
12144
|
-
|
|
12145
|
-
|
|
12180
|
+
db2.transaction(() => {
|
|
12181
|
+
db2.query(`UPDATE negotium_cron_jobs SET
|
|
12146
12182
|
name = ?, topic_id = ?, prompt = ?, script = ?, summary = ?, schedule = ?, timezone = ?,
|
|
12147
12183
|
enabled = ?, agent = ?, model = ?, effort = ?, next_run_at = ?, updated_at = ?
|
|
12148
12184
|
WHERE id = ?`).run(nextName, nextTopicId, nextPrompt ?? "", nextScript ?? null, patch.summary !== undefined ? patch.summary?.trim() || null : job.summary ?? null, nextSchedule, nextTimezone ?? null, enabled ? 1 : 0, patch.agent !== undefined ? patch.agent : job.agent ?? null, patch.model !== undefined ? patch.model?.trim() || null : job.model ?? null, patch.effort !== undefined ? patch.effort : job.effort ?? null, nextRunAt, timestamp, id);
|
|
@@ -12151,7 +12187,7 @@ function updateCronJob(id, patch, now = new Date) {
|
|
|
12151
12187
|
}
|
|
12152
12188
|
function updateCronJobSummaryIfPromptMatches(id, expectedPrompt, summary) {
|
|
12153
12189
|
ensureCronSchema();
|
|
12154
|
-
const result =
|
|
12190
|
+
const result = db2.query("UPDATE negotium_cron_jobs SET summary = ? WHERE id = ? AND prompt = ?").run(summary.trim(), id, expectedPrompt.trim());
|
|
12155
12191
|
return Number(result.changes) > 0;
|
|
12156
12192
|
}
|
|
12157
12193
|
function setCronJobEnabled(id, enabled, now = new Date) {
|
|
@@ -12160,14 +12196,14 @@ function setCronJobEnabled(id, enabled, now = new Date) {
|
|
|
12160
12196
|
if (!job)
|
|
12161
12197
|
return null;
|
|
12162
12198
|
const nextRunAt = enabled ? computeNextCronRun(job.schedule, now, job.timezone).toISOString() : job.nextRunAt;
|
|
12163
|
-
|
|
12199
|
+
db2.query("UPDATE negotium_cron_jobs SET enabled = ?, next_run_at = ?, updated_at = ? WHERE id = ?").run(enabled ? 1 : 0, nextRunAt, now.toISOString(), id);
|
|
12164
12200
|
return getCronJob(id);
|
|
12165
12201
|
}
|
|
12166
12202
|
function setCronJobSessionId(id, sessionId) {
|
|
12167
12203
|
const job = getCronJob(id);
|
|
12168
12204
|
if (!job)
|
|
12169
12205
|
return;
|
|
12170
|
-
const topic =
|
|
12206
|
+
const topic = db2.query("SELECT agent FROM api_topics WHERE id = ?").get(job.topicId);
|
|
12171
12207
|
const agent = job.agent ?? topic?.agent;
|
|
12172
12208
|
if (!agent)
|
|
12173
12209
|
return;
|
|
@@ -12178,17 +12214,17 @@ function setCronJobSessionId(id, sessionId) {
|
|
|
12178
12214
|
}
|
|
12179
12215
|
function getCronTopicSession(topicId, agent) {
|
|
12180
12216
|
ensureCronSchema();
|
|
12181
|
-
const row =
|
|
12217
|
+
const row = db2.query("SELECT * FROM negotium_cron_topic_sessions WHERE topic_id = ? AND agent = ?").get(topicId, agent);
|
|
12182
12218
|
return row ? toTopicSession(row) : null;
|
|
12183
12219
|
}
|
|
12184
12220
|
function listCronTopicSessions(topicId) {
|
|
12185
12221
|
ensureCronSchema();
|
|
12186
|
-
return
|
|
12222
|
+
return db2.query("SELECT * FROM negotium_cron_topic_sessions WHERE topic_id = ? ORDER BY agent").all(topicId).map(toTopicSession);
|
|
12187
12223
|
}
|
|
12188
12224
|
function setCronTopicSession(topicId, agent, ownerUserId, sessionId, now = new Date) {
|
|
12189
12225
|
ensureCronSchema();
|
|
12190
12226
|
const timestamp = now.toISOString();
|
|
12191
|
-
|
|
12227
|
+
db2.query(`INSERT INTO negotium_cron_topic_sessions
|
|
12192
12228
|
(topic_id,agent,owner_user_id,session_id,created_at,updated_at)
|
|
12193
12229
|
VALUES (?,?,?,?,?,?)
|
|
12194
12230
|
ON CONFLICT(topic_id,agent) DO UPDATE SET
|
|
@@ -12200,8 +12236,8 @@ function setCronTopicSession(topicId, agent, ownerUserId, sessionId, now = new D
|
|
|
12200
12236
|
function setCronTopicSessionIfJobUpdatedAt(jobId, expectedJobUpdatedAt, topicId, agent, ownerUserId, sessionId, now = new Date) {
|
|
12201
12237
|
ensureCronSchema();
|
|
12202
12238
|
let updated = false;
|
|
12203
|
-
|
|
12204
|
-
const current3 =
|
|
12239
|
+
db2.transaction(() => {
|
|
12240
|
+
const current3 = db2.query("SELECT topic_id, updated_at FROM negotium_cron_jobs WHERE id = ?").get(jobId);
|
|
12205
12241
|
if (!current3 || current3.topic_id !== topicId || current3.updated_at !== expectedJobUpdatedAt)
|
|
12206
12242
|
return;
|
|
12207
12243
|
setCronTopicSession(topicId, agent, ownerUserId, sessionId, now);
|
|
@@ -12211,23 +12247,23 @@ function setCronTopicSessionIfJobUpdatedAt(jobId, expectedJobUpdatedAt, topicId,
|
|
|
12211
12247
|
}
|
|
12212
12248
|
function clearCronTopicSession(topicId, agent) {
|
|
12213
12249
|
ensureCronSchema();
|
|
12214
|
-
const result =
|
|
12250
|
+
const result = db2.query("DELETE FROM negotium_cron_topic_sessions WHERE topic_id = ? AND agent = ?").run(topicId, agent);
|
|
12215
12251
|
return Number(result.changes) > 0;
|
|
12216
12252
|
}
|
|
12217
12253
|
function resetCronTopicSessions(topicId) {
|
|
12218
12254
|
ensureCronSchema();
|
|
12219
12255
|
const sessions = listCronTopicSessions(topicId);
|
|
12220
|
-
|
|
12256
|
+
db2.query("DELETE FROM negotium_cron_topic_sessions WHERE topic_id = ?").run(topicId);
|
|
12221
12257
|
return sessions;
|
|
12222
12258
|
}
|
|
12223
12259
|
function getCronTopicContext(topicId) {
|
|
12224
12260
|
ensureCronSchema();
|
|
12225
|
-
const row =
|
|
12261
|
+
const row = db2.query("SELECT * FROM negotium_cron_topic_context WHERE topic_id = ?").get(topicId);
|
|
12226
12262
|
return row ? toTopicContext(row) : null;
|
|
12227
12263
|
}
|
|
12228
12264
|
function incrementCronTopicSuccess(topicId, now) {
|
|
12229
12265
|
const timestamp = now.toISOString();
|
|
12230
|
-
|
|
12266
|
+
db2.query(`INSERT INTO negotium_cron_topic_context
|
|
12231
12267
|
(topic_id,successful_runs_since_rotation,last_rotated_at,updated_at)
|
|
12232
12268
|
VALUES (?,1,NULL,?)
|
|
12233
12269
|
ON CONFLICT(topic_id) DO UPDATE SET
|
|
@@ -12238,7 +12274,7 @@ function incrementCronTopicSuccess(topicId, now) {
|
|
|
12238
12274
|
function markCronTopicContextRotated(topicId, now = new Date) {
|
|
12239
12275
|
ensureCronSchema();
|
|
12240
12276
|
const timestamp = now.toISOString();
|
|
12241
|
-
|
|
12277
|
+
db2.query(`INSERT INTO negotium_cron_topic_context
|
|
12242
12278
|
(topic_id,successful_runs_since_rotation,last_rotated_at,updated_at)
|
|
12243
12279
|
VALUES (?,0,?,?)
|
|
12244
12280
|
ON CONFLICT(topic_id) DO UPDATE SET
|
|
@@ -12248,41 +12284,41 @@ function markCronTopicContextRotated(topicId, now = new Date) {
|
|
|
12248
12284
|
}
|
|
12249
12285
|
function resetCronTopicContextState(topicId) {
|
|
12250
12286
|
ensureCronSchema();
|
|
12251
|
-
const result =
|
|
12287
|
+
const result = db2.query("DELETE FROM negotium_cron_topic_context WHERE topic_id = ?").run(topicId);
|
|
12252
12288
|
return Number(result.changes) > 0;
|
|
12253
12289
|
}
|
|
12254
12290
|
function listOrphanedCronTopicSessions() {
|
|
12255
12291
|
ensureCronSchema();
|
|
12256
|
-
return
|
|
12292
|
+
return db2.query(`SELECT s.* FROM negotium_cron_topic_sessions s
|
|
12257
12293
|
LEFT JOIN api_topics t ON t.id = s.topic_id
|
|
12258
12294
|
WHERE t.id IS NULL
|
|
12259
12295
|
ORDER BY s.topic_id, s.agent`).all().map(toTopicSession);
|
|
12260
12296
|
}
|
|
12261
12297
|
function deleteCronJob(id) {
|
|
12262
12298
|
ensureCronSchema();
|
|
12263
|
-
const result =
|
|
12299
|
+
const result = db2.query("DELETE FROM negotium_cron_jobs WHERE id = ?").run(id);
|
|
12264
12300
|
return Number(result.changes) > 0;
|
|
12265
12301
|
}
|
|
12266
12302
|
function requestCronRun(jobId, now = new Date) {
|
|
12267
12303
|
ensureCronSchema();
|
|
12268
12304
|
const id = randomUUID10();
|
|
12269
|
-
|
|
12305
|
+
db2.query("INSERT INTO negotium_cron_requests (id,job_id,requested_at) VALUES (?,?,?)").run(id, jobId, now.toISOString());
|
|
12270
12306
|
return id;
|
|
12271
12307
|
}
|
|
12272
12308
|
function requestCronCancel(jobId, now = new Date) {
|
|
12273
12309
|
ensureCronSchema();
|
|
12274
12310
|
const id = randomUUID10();
|
|
12275
|
-
|
|
12311
|
+
db2.query("INSERT INTO negotium_cron_cancellations (id,job_id,requested_at) VALUES (?,?,?)").run(id, jobId, now.toISOString());
|
|
12276
12312
|
return id;
|
|
12277
12313
|
}
|
|
12278
12314
|
function claimCronCancellations(limit = 100) {
|
|
12279
12315
|
ensureCronSchema();
|
|
12280
12316
|
const jobIds = [];
|
|
12281
|
-
|
|
12282
|
-
const rows =
|
|
12317
|
+
db2.transaction(() => {
|
|
12318
|
+
const rows = db2.query(`SELECT id, job_id FROM negotium_cron_cancellations
|
|
12283
12319
|
ORDER BY requested_at LIMIT ?`).all(limit);
|
|
12284
12320
|
for (const row of rows) {
|
|
12285
|
-
|
|
12321
|
+
db2.query("DELETE FROM negotium_cron_cancellations WHERE id = ?").run(row.id);
|
|
12286
12322
|
if (!jobIds.includes(row.job_id))
|
|
12287
12323
|
jobIds.push(row.job_id);
|
|
12288
12324
|
}
|
|
@@ -12297,18 +12333,18 @@ function insertRun(jobId, topicId, source, scheduledAt) {
|
|
|
12297
12333
|
scheduledAt,
|
|
12298
12334
|
status: "pending"
|
|
12299
12335
|
};
|
|
12300
|
-
|
|
12336
|
+
db2.query("INSERT INTO negotium_cron_runs (id,job_id,topic_id,source,scheduled_at,status) VALUES (?,?,?,?,?,?)").run(run.id, run.jobId, topicId, run.source, run.scheduledAt, run.status);
|
|
12301
12337
|
return run;
|
|
12302
12338
|
}
|
|
12303
12339
|
function claimCronRuns(now = new Date, limit = 20) {
|
|
12304
12340
|
ensureCronSchema();
|
|
12305
12341
|
const claimed = [];
|
|
12306
|
-
|
|
12307
|
-
const requests =
|
|
12342
|
+
db2.transaction(() => {
|
|
12343
|
+
const requests = db2.query(`SELECT r.id AS request_id, r.requested_at, j.*
|
|
12308
12344
|
FROM negotium_cron_requests r JOIN negotium_cron_jobs j ON j.id = r.job_id
|
|
12309
12345
|
ORDER BY r.requested_at LIMIT ?`).all(limit);
|
|
12310
12346
|
for (const row of requests) {
|
|
12311
|
-
|
|
12347
|
+
db2.query("DELETE FROM negotium_cron_requests WHERE id = ?").run(row.request_id);
|
|
12312
12348
|
claimed.push({
|
|
12313
12349
|
job: toJob(row),
|
|
12314
12350
|
run: insertRun(row.id, row.topic_id, "manual", row.requested_at)
|
|
@@ -12317,13 +12353,13 @@ function claimCronRuns(now = new Date, limit = 20) {
|
|
|
12317
12353
|
const remaining = Math.max(0, limit - claimed.length);
|
|
12318
12354
|
if (remaining === 0)
|
|
12319
12355
|
return;
|
|
12320
|
-
const due =
|
|
12356
|
+
const due = db2.query(`SELECT * FROM negotium_cron_jobs
|
|
12321
12357
|
WHERE enabled = 1 AND next_run_at <= ? ORDER BY next_run_at LIMIT ?`).all(now.toISOString(), remaining);
|
|
12322
12358
|
for (const row of due) {
|
|
12323
12359
|
const job = toJob(row);
|
|
12324
12360
|
const run = insertRun(job.id, job.topicId, "schedule", job.nextRunAt);
|
|
12325
12361
|
const nextRunAt = computeNextCronRun(job.schedule, now, job.timezone).toISOString();
|
|
12326
|
-
|
|
12362
|
+
db2.query("UPDATE negotium_cron_jobs SET next_run_at = ? WHERE id = ?").run(nextRunAt, job.id);
|
|
12327
12363
|
claimed.push({ job: { ...job, nextRunAt }, run });
|
|
12328
12364
|
}
|
|
12329
12365
|
})();
|
|
@@ -12331,13 +12367,13 @@ function claimCronRuns(now = new Date, limit = 20) {
|
|
|
12331
12367
|
}
|
|
12332
12368
|
function markCronRunStarted(runId, queryId, now = new Date) {
|
|
12333
12369
|
ensureCronSchema();
|
|
12334
|
-
|
|
12370
|
+
db2.query(`UPDATE negotium_cron_runs
|
|
12335
12371
|
SET status = 'running', query_id = ?, started_at = COALESCE(started_at, ?)
|
|
12336
12372
|
WHERE id = ?`).run(queryId, now.toISOString(), runId);
|
|
12337
12373
|
}
|
|
12338
12374
|
function finishCronRun(runId, result, now = new Date) {
|
|
12339
12375
|
ensureCronSchema();
|
|
12340
|
-
const row =
|
|
12376
|
+
const row = db2.query(`SELECT r.started_at, r.status, COALESCE(r.topic_id, j.topic_id) AS topic_id
|
|
12341
12377
|
FROM negotium_cron_runs r
|
|
12342
12378
|
JOIN negotium_cron_jobs j ON j.id = r.job_id
|
|
12343
12379
|
WHERE r.id = ?`).get(runId);
|
|
@@ -12345,9 +12381,9 @@ function finishCronRun(runId, result, now = new Date) {
|
|
|
12345
12381
|
return null;
|
|
12346
12382
|
const durationMs = row?.started_at ? Math.max(0, now.getTime() - Date.parse(row.started_at)) : null;
|
|
12347
12383
|
let successfulRunsSinceRotation = null;
|
|
12348
|
-
|
|
12384
|
+
db2.transaction(() => {
|
|
12349
12385
|
const exitCode = result.exitCode ?? { succeeded: 0, skipped: 0, failed: 1, aborted: 130, interrupted: 137 }[result.status];
|
|
12350
|
-
const updated =
|
|
12386
|
+
const updated = db2.query(`UPDATE negotium_cron_runs
|
|
12351
12387
|
SET status = ?, finished_at = ?, duration_ms = ?, output_preview = ?, error = ?, exit_code = ?
|
|
12352
12388
|
WHERE id = ? AND status IN ('pending','running')`).run(result.status, now.toISOString(), durationMs, result.outputPreview?.slice(0, 500) ?? null, result.error ?? null, exitCode, runId);
|
|
12353
12389
|
if (Number(updated.changes) > 0 && result.status === "succeeded") {
|
|
@@ -12358,19 +12394,19 @@ function finishCronRun(runId, result, now = new Date) {
|
|
|
12358
12394
|
}
|
|
12359
12395
|
function listCronRuns(jobId, limit = 20) {
|
|
12360
12396
|
ensureCronSchema();
|
|
12361
|
-
return
|
|
12397
|
+
return db2.query("SELECT * FROM negotium_cron_runs WHERE job_id = ? ORDER BY scheduled_at DESC LIMIT ?").all(jobId, limit).map(toRun);
|
|
12362
12398
|
}
|
|
12363
12399
|
function getLastCronRun(jobId) {
|
|
12364
12400
|
return listCronRuns(jobId, 1)[0] ?? null;
|
|
12365
12401
|
}
|
|
12366
12402
|
function countCronRuns(jobId) {
|
|
12367
12403
|
ensureCronSchema();
|
|
12368
|
-
const row =
|
|
12404
|
+
const row = db2.query("SELECT COUNT(*) AS count FROM negotium_cron_runs WHERE job_id = ?").get(jobId);
|
|
12369
12405
|
return row?.count ?? 0;
|
|
12370
12406
|
}
|
|
12371
12407
|
function recoverPendingCronRuns(limit = 100) {
|
|
12372
12408
|
ensureCronSchema();
|
|
12373
|
-
const rows =
|
|
12409
|
+
const rows = db2.query(`SELECT r.id AS run_id, r.job_id AS run_job_id, r.source AS run_source,
|
|
12374
12410
|
r.scheduled_at AS run_scheduled_at, r.started_at AS run_started_at,
|
|
12375
12411
|
r.finished_at AS run_finished_at, r.status AS run_status,
|
|
12376
12412
|
r.query_id AS run_query_id, r.duration_ms AS run_duration_ms,
|
|
@@ -12404,7 +12440,7 @@ function recoverPendingCronRuns(limit = 100) {
|
|
|
12404
12440
|
}
|
|
12405
12441
|
function finalizeOrphanedCronRuns(now = new Date) {
|
|
12406
12442
|
ensureCronSchema();
|
|
12407
|
-
const result =
|
|
12443
|
+
const result = db2.query(`UPDATE negotium_cron_runs
|
|
12408
12444
|
SET status = 'interrupted', finished_at = ?, exit_code = 137,
|
|
12409
12445
|
error = 'node restarted after dispatch; final outcome is unknown'
|
|
12410
12446
|
WHERE status = 'running'`).run(now.toISOString());
|
|
@@ -13181,4 +13217,4 @@ export {
|
|
|
13181
13217
|
CRON_CONTEXT_RETAIN_TURNS
|
|
13182
13218
|
};
|
|
13183
13219
|
|
|
13184
|
-
//# debugId=
|
|
13220
|
+
//# debugId=4523810F2A196B5864756E2164756E21
|