negotium 0.1.12 → 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.
Files changed (137) hide show
  1. package/dist/canonical-mcp-bridge.js +2 -3
  2. package/dist/canonical-mcp-bridge.js.map +1 -1
  3. package/dist/chunk-a9y2rwxf.js.map +1 -1
  4. package/dist/{chunk-j0kjscb7.js → chunk-s2gez3wg.js} +20 -2
  5. package/dist/{chunk-j0kjscb7.js.map → chunk-s2gez3wg.js.map} +2 -2
  6. package/dist/cron.js +49 -25
  7. package/dist/cron.js.map +15 -14
  8. package/dist/hosted-agent.js +3012 -17
  9. package/dist/hosted-agent.js.map +30 -3
  10. package/dist/main.js +31323 -36
  11. package/dist/main.js.map +198 -3
  12. package/dist/prompts.js +11 -8
  13. package/dist/prompts.js.map +4 -4
  14. package/dist/runtime/src/application/submit-user-message.ts +3 -0
  15. package/dist/runtime/src/index.ts +7 -0
  16. package/dist/runtime/src/mcp/runtime-spec.ts +3 -0
  17. package/dist/runtime/src/mcp/session-comm/server.ts +1 -1
  18. package/dist/runtime/src/platform/mcp-config.ts +13 -0
  19. package/dist/runtime/src/prompts/builders.ts +20 -6
  20. package/dist/runtime/src/query/active-rooms.ts +2 -0
  21. package/dist/runtime/src/runtime/delivery-ack.ts +112 -0
  22. package/dist/runtime/src/runtime/errors.ts +12 -12
  23. package/dist/runtime/src/runtime/turn-runner.ts +12 -0
  24. package/dist/runtime/src/storage/runtime-turn-requests.ts +1 -0
  25. package/dist/runtime/src/types/api.ts +2 -0
  26. package/dist/runtime/src/types.ts +2 -0
  27. package/dist/runtime/src/version.ts +1 -1
  28. package/dist/runtime-helpers.js.map +1 -1
  29. package/dist/storage.js.map +1 -1
  30. package/dist/types/packages/core/src/mcp/runtime-spec.d.ts +2 -0
  31. package/dist/types/packages/core/src/platform/mcp-config.d.ts +4 -0
  32. package/dist/types/packages/core/src/prompts/builders.d.ts +2 -0
  33. package/dist/types/packages/core/src/types/api.d.ts +2 -0
  34. package/dist/types/packages/core/src/types.d.ts +2 -0
  35. package/dist/types/packages/core/src/version.d.ts +1 -1
  36. package/dist/vault.js.map +1 -1
  37. package/package.json +1 -1
  38. package/dist/chunk-00q25k7p.js +0 -2387
  39. package/dist/chunk-00q25k7p.js.map +0 -26
  40. package/dist/chunk-03kv7mvj.js +0 -2152
  41. package/dist/chunk-03kv7mvj.js.map +0 -15
  42. package/dist/chunk-05pm51dh.js +0 -589
  43. package/dist/chunk-05pm51dh.js.map +0 -11
  44. package/dist/chunk-0eh16bk1.js +0 -2936
  45. package/dist/chunk-0eh16bk1.js.map +0 -20
  46. package/dist/chunk-0yfqyv88.js +0 -108
  47. package/dist/chunk-0yfqyv88.js.map +0 -10
  48. package/dist/chunk-1k7t7bnx.js +0 -97
  49. package/dist/chunk-1k7t7bnx.js.map +0 -10
  50. package/dist/chunk-3gkfr9vb.js +0 -431
  51. package/dist/chunk-3gkfr9vb.js.map +0 -11
  52. package/dist/chunk-5akd1gbj.js +0 -281
  53. package/dist/chunk-5akd1gbj.js.map +0 -10
  54. package/dist/chunk-7k19pcg8.js +0 -577
  55. package/dist/chunk-7k19pcg8.js.map +0 -14
  56. package/dist/chunk-81tbfbmt.js +0 -10
  57. package/dist/chunk-81tbfbmt.js.map +0 -10
  58. package/dist/chunk-82qhv1mq.js +0 -512
  59. package/dist/chunk-82qhv1mq.js.map +0 -13
  60. package/dist/chunk-8dy7e2a9.js +0 -103
  61. package/dist/chunk-8dy7e2a9.js.map +0 -10
  62. package/dist/chunk-8f2sqmrj.js +0 -1712
  63. package/dist/chunk-8f2sqmrj.js.map +0 -18
  64. package/dist/chunk-9hjeemfz.js +0 -372
  65. package/dist/chunk-9hjeemfz.js.map +0 -10
  66. package/dist/chunk-a4aa2ccv.js +0 -141
  67. package/dist/chunk-a4aa2ccv.js.map +0 -10
  68. package/dist/chunk-bsg215q3.js +0 -3536
  69. package/dist/chunk-bsg215q3.js.map +0 -20
  70. package/dist/chunk-bxnfsc7w.js +0 -58
  71. package/dist/chunk-bxnfsc7w.js.map +0 -10
  72. package/dist/chunk-c500xe9z.js +0 -33
  73. package/dist/chunk-c500xe9z.js.map +0 -10
  74. package/dist/chunk-cngrf7ab.js +0 -149
  75. package/dist/chunk-cngrf7ab.js.map +0 -10
  76. package/dist/chunk-d7tprf06.js +0 -53
  77. package/dist/chunk-d7tprf06.js.map +0 -10
  78. package/dist/chunk-ddnrmkrv.js +0 -61
  79. package/dist/chunk-ddnrmkrv.js.map +0 -10
  80. package/dist/chunk-edb07sjp.js +0 -567
  81. package/dist/chunk-edb07sjp.js.map +0 -14
  82. package/dist/chunk-ejcm6zx8.js +0 -591
  83. package/dist/chunk-ejcm6zx8.js.map +0 -11
  84. package/dist/chunk-ezs352qs.js +0 -237
  85. package/dist/chunk-ezs352qs.js.map +0 -11
  86. package/dist/chunk-f5xgprg1.js +0 -59
  87. package/dist/chunk-f5xgprg1.js.map +0 -10
  88. package/dist/chunk-fbtb3v0s.js +0 -1339
  89. package/dist/chunk-fbtb3v0s.js.map +0 -16
  90. package/dist/chunk-ffefbd3r.js +0 -17
  91. package/dist/chunk-ffefbd3r.js.map +0 -10
  92. package/dist/chunk-gaknyw04.js +0 -708
  93. package/dist/chunk-gaknyw04.js.map +0 -14
  94. package/dist/chunk-gxmny6tq.js +0 -1637
  95. package/dist/chunk-gxmny6tq.js.map +0 -15
  96. package/dist/chunk-h7bj5zwb.js +0 -3069
  97. package/dist/chunk-h7bj5zwb.js.map +0 -32
  98. package/dist/chunk-hp4g7wx7.js +0 -46
  99. package/dist/chunk-hp4g7wx7.js.map +0 -10
  100. package/dist/chunk-hwy3n31w.js +0 -21
  101. package/dist/chunk-hwy3n31w.js.map +0 -9
  102. package/dist/chunk-jzz91jwh.js +0 -70
  103. package/dist/chunk-jzz91jwh.js.map +0 -10
  104. package/dist/chunk-k778y16p.js +0 -153
  105. package/dist/chunk-k778y16p.js.map +0 -11
  106. package/dist/chunk-mh5mexsf.js +0 -147
  107. package/dist/chunk-mh5mexsf.js.map +0 -11
  108. package/dist/chunk-mnwaext5.js +0 -318
  109. package/dist/chunk-mnwaext5.js.map +0 -10
  110. package/dist/chunk-nkgs40ch.js +0 -23
  111. package/dist/chunk-nkgs40ch.js.map +0 -10
  112. package/dist/chunk-pz57vwsm.js +0 -250
  113. package/dist/chunk-pz57vwsm.js.map +0 -11
  114. package/dist/chunk-r6kdp7gn.js +0 -252
  115. package/dist/chunk-r6kdp7gn.js.map +0 -10
  116. package/dist/chunk-rkvrkcgx.js +0 -117
  117. package/dist/chunk-rkvrkcgx.js.map +0 -10
  118. package/dist/chunk-s74rdyqe.js +0 -117
  119. package/dist/chunk-s74rdyqe.js.map +0 -12
  120. package/dist/chunk-sa874j5c.js +0 -9
  121. package/dist/chunk-sa874j5c.js.map +0 -10
  122. package/dist/chunk-sffqdkvq.js +0 -122
  123. package/dist/chunk-sffqdkvq.js.map +0 -10
  124. package/dist/chunk-tcs9qjps.js +0 -64
  125. package/dist/chunk-tcs9qjps.js.map +0 -10
  126. package/dist/chunk-wx83ep8e.js +0 -131
  127. package/dist/chunk-wx83ep8e.js.map +0 -10
  128. package/dist/chunk-xag86ng3.js +0 -122
  129. package/dist/chunk-xag86ng3.js.map +0 -10
  130. package/dist/chunk-xfdkep71.js +0 -371
  131. package/dist/chunk-xfdkep71.js.map +0 -11
  132. package/dist/chunk-xhm1rfpt.js +0 -2907
  133. package/dist/chunk-xhm1rfpt.js.map +0 -24
  134. package/dist/chunk-xpcps28p.js +0 -2138
  135. package/dist/chunk-xpcps28p.js.map +0 -29
  136. package/dist/chunk-yb4rbev9.js +0 -214
  137. package/dist/chunk-yb4rbev9.js.map +0 -10
@@ -1,577 +0,0 @@
1
- // @bun
2
- import {
3
- cancelAskCallbacksForTopic
4
- } from "./chunk-8dy7e2a9.js";
5
- import {
6
- deletePendingAsksForTopic
7
- } from "./chunk-9hjeemfz.js";
8
- import"./chunk-1k7t7bnx.js";
9
- import {
10
- archiveTopicMessages,
11
- cancelIdleArchiveForTopic,
12
- cancelRuntimeUserTurnRequests,
13
- cancelRuntimeUserTurnRequestsBeforeEpoch,
14
- cancelSubagentWatchForDeletedTopic,
15
- clearQueryState,
16
- clearQueryUsageAlert,
17
- deleteFilesForTopic,
18
- deleteTopicArchiveState,
19
- deleteTopicBrief,
20
- deleteTopicVisuals,
21
- killBgBash,
22
- runArchiverTurn
23
- } from "./chunk-xpcps28p.js";
24
- import {
25
- WsHub,
26
- closeBrowserOwnerTabs,
27
- delay,
28
- deleteMessagesForTopic,
29
- deleteTopicProfileDir,
30
- getBrowserProfileOwner,
31
- getTopicBrowserProfile
32
- } from "./chunk-fbtb3v0s.js";
33
- import {
34
- GENERAL_TOPIC_ID,
35
- deleteTopic,
36
- getTopicMemoryOrigin,
37
- getTopicSessionId,
38
- listTopics,
39
- reparentTopicChildren
40
- } from "./chunk-ejcm6zx8.js";
41
- import {
42
- getRegistry
43
- } from "./chunk-s74rdyqe.js";
44
- import {
45
- getConversationPath,
46
- readConversation
47
- } from "./chunk-k778y16p.js";
48
- import {
49
- deleteApiTopicConfig
50
- } from "./chunk-wx83ep8e.js";
51
- import"./chunk-00q25k7p.js";
52
- import"./chunk-gaknyw04.js";
53
- import"./chunk-mh5mexsf.js";
54
- import {
55
- SESSION_INBOX_DIR,
56
- SUPPORTED_AGENTS,
57
- resolveTopicWorkspaceDir
58
- } from "./chunk-ezs352qs.js";
59
- import"./chunk-j0kjscb7.js";
60
- import {
61
- TOPIC_MAINTENANCE_STALE_MS,
62
- TURN_LEASE_STALE_MS,
63
- abortRoom,
64
- beginRuntimeTopicMaintenance,
65
- getRoomQuery,
66
- getRuntimeTurnLease,
67
- interSessionQueue
68
- } from "./chunk-82qhv1mq.js";
69
- import {
70
- logger
71
- } from "./chunk-ffefbd3r.js";
72
- import {
73
- db
74
- } from "./chunk-81tbfbmt.js";
75
- import"./chunk-sffqdkvq.js";
76
- import"./chunk-f5xgprg1.js";
77
- import"./chunk-hwy3n31w.js";
78
-
79
- // ../../packages/core/src/topics/lifecycle.ts
80
- import { rmSync } from "fs";
81
-
82
- // ../../packages/core/src/agents/topic-cleanup.ts
83
- import { mkdirSync, renameSync, unlinkSync, writeFileSync } from "fs";
84
- import { dirname } from "path";
85
- function collectSessionIdsByAgent(entries, extraSessions = []) {
86
- const out = new Map;
87
- for (const e of entries) {
88
- if (e.event.type !== "session")
89
- continue;
90
- const set = out.get(e.agent) ?? new Set;
91
- set.add(e.event.sessionId);
92
- out.set(e.agent, set);
93
- }
94
- for (const { agent, sessionId } of extraSessions) {
95
- if (!sessionId)
96
- continue;
97
- const set = out.get(agent) ?? new Set;
98
- set.add(sessionId);
99
- out.set(agent, set);
100
- }
101
- return out;
102
- }
103
- async function cleanupSessionRollouts(opts, entries) {
104
- const { userId, topicName, cwd, extraSessions = [] } = opts;
105
- const idsByAgent = collectSessionIdsByAgent(entries, extraSessions);
106
- let cleanupFailed = false;
107
- await Promise.all(SUPPORTED_AGENTS.map(async (agent) => {
108
- const ids = idsByAgent.get(agent);
109
- if (!ids || ids.size === 0)
110
- return;
111
- try {
112
- await getRegistry(agent).cleanupRollouts({
113
- cwd,
114
- sessionIds: Array.from(ids)
115
- });
116
- } catch (e) {
117
- logger.warn({ err: e, userId, topicName, agent, count: ids.size }, "topic logs: agent cleanup failed");
118
- cleanupFailed = true;
119
- }
120
- }));
121
- return !cleanupFailed;
122
- }
123
- async function cleanupTopicRollouts(opts) {
124
- return cleanupSessionRollouts(opts, readConversation(opts.userId, opts.topicName));
125
- }
126
- async function rotateTopicLogs(opts) {
127
- const retainTurns = Math.max(0, Math.floor(opts.retainTurns));
128
- const entries = readConversation(opts.userId, opts.topicName);
129
- const userEntryIndexes = entries.flatMap((entry, index) => entry.event.type === "user_message" ? [index] : []);
130
- const totalTurns = userEntryIndexes.length;
131
- const firstRetainedIndex = retainTurns === 0 ? entries.length : userEntryIndexes[Math.max(0, totalTurns - retainTurns)] ?? 0;
132
- const retained = entries.slice(firstRetainedIndex).filter((entry) => entry.event.type !== "session");
133
- if (!await cleanupSessionRollouts(opts, entries)) {
134
- logger.warn({ userId: opts.userId, topicName: opts.topicName }, "rotateTopicLogs: keeping current context because rollout cleanup failed");
135
- return {
136
- rotated: false,
137
- totalTurns,
138
- retainedTurns: Math.min(totalTurns, retainTurns),
139
- retainedEntries: retained.length
140
- };
141
- }
142
- const path = getConversationPath(opts.userId, opts.topicName);
143
- const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
144
- try {
145
- mkdirSync(dirname(path), { recursive: true });
146
- writeFileSync(tempPath, retained.length > 0 ? `${retained.map((entry) => JSON.stringify(entry)).join(`
147
- `)}
148
- ` : "", { flag: "wx" });
149
- renameSync(tempPath, path);
150
- } catch (e) {
151
- try {
152
- unlinkSync(tempPath);
153
- } catch {}
154
- logger.warn({ err: e, path }, "rotateTopicLogs: conversation replacement failed");
155
- return {
156
- rotated: false,
157
- totalTurns,
158
- retainedTurns: Math.min(totalTurns, retainTurns),
159
- retainedEntries: retained.length
160
- };
161
- }
162
- return {
163
- rotated: true,
164
- totalTurns,
165
- retainedTurns: Math.min(totalTurns, retainTurns),
166
- retainedEntries: retained.length
167
- };
168
- }
169
- async function purgeTopicLogs(opts) {
170
- const { userId, topicName } = opts;
171
- const entries = readConversation(userId, topicName);
172
- if (!await cleanupSessionRollouts(opts, entries)) {
173
- logger.warn({ userId, topicName }, "purgeTopicLogs: keeping unified log because one or more rollout cleanups failed");
174
- return;
175
- }
176
- const path = getConversationPath(userId, topicName);
177
- try {
178
- unlinkSync(path);
179
- } catch (e) {
180
- if (e?.code !== "ENOENT") {
181
- logger.warn({ err: e, path }, "purgeTopicLogs: unified log unlink failed");
182
- }
183
- }
184
- }
185
-
186
- // ../../packages/core/src/query/session-inbox-cleanup.ts
187
- import { unlinkSync as unlinkSync2 } from "fs";
188
- import { basename, join as join2 } from "path";
189
-
190
- // ../../packages/core/src/query/session-inbox-path.ts
191
- import { join } from "path";
192
- var TOPIC_ID_FILE_PREFIX = "topic-id-";
193
- var JSONL_SUFFIX = ".jsonl";
194
- var SCHEDULE_SUFFIX = ".schedule";
195
- function sessionInboxPath(userId, topicId) {
196
- const key = Buffer.from(topicId, "utf8").toString("base64url");
197
- return join(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${JSONL_SUFFIX}`);
198
- }
199
- function scheduledSessionInboxPath(userId, topicId) {
200
- const key = Buffer.from(topicId, "utf8").toString("base64url");
201
- return join(SESSION_INBOX_DIR, userId, `${TOPIC_ID_FILE_PREFIX}${key}${SCHEDULE_SUFFIX}`);
202
- }
203
- function decodeTopicIdFileName(fileName, suffix) {
204
- if (!fileName.startsWith(TOPIC_ID_FILE_PREFIX) || !fileName.endsWith(suffix))
205
- return null;
206
- const encoded = fileName.slice(TOPIC_ID_FILE_PREFIX.length, -suffix.length);
207
- if (!encoded || !/^[A-Za-z0-9_-]+$/.test(encoded))
208
- return null;
209
- try {
210
- return Buffer.from(encoded, "base64url").toString("utf8") || null;
211
- } catch {
212
- return null;
213
- }
214
- }
215
- function topicIdFromSessionInboxFileName(fileName) {
216
- return decodeTopicIdFileName(fileName, JSONL_SUFFIX);
217
- }
218
- function topicIdFromScheduledSessionInboxFileName(fileName) {
219
- const base = fileName.endsWith(".processing") ? fileName.slice(0, -".processing".length) : fileName;
220
- return decodeTopicIdFileName(base, SCHEDULE_SUFFIX);
221
- }
222
-
223
- // ../../packages/core/src/query/session-inbox-cleanup.ts
224
- function cleanupSessionInboxFiles(userId, topicId, legacyTopicTitle) {
225
- const live = sessionInboxPath(userId, topicId);
226
- const scheduled = scheduledSessionInboxPath(userId, topicId);
227
- const candidates = new Set([live, `${live}.processing`, scheduled, `${scheduled}.processing`]);
228
- if (legacyTopicTitle && legacyTopicTitle !== "." && legacyTopicTitle !== ".." && basename(legacyTopicTitle) === legacyTopicTitle) {
229
- const legacyBase = join2(SESSION_INBOX_DIR, userId, legacyTopicTitle);
230
- for (const suffix of [".jsonl", ".jsonl.processing", ".schedule", ".schedule.processing"]) {
231
- candidates.add(`${legacyBase}${suffix}`);
232
- }
233
- }
234
- let deleted = 0;
235
- for (const path of candidates) {
236
- try {
237
- unlinkSync2(path);
238
- deleted++;
239
- } catch (err) {
240
- if (err?.code !== "ENOENT") {
241
- logger.warn({ err, path, topicId, userId }, "topic cleanup: inbox unlink failed");
242
- }
243
- }
244
- }
245
- return deleted;
246
- }
247
-
248
- // ../../packages/core/src/storage/self-schedules.ts
249
- import { randomUUID } from "crypto";
250
- db.exec(`
251
- CREATE TABLE IF NOT EXISTS runtime_self_schedules (
252
- id TEXT PRIMARY KEY,
253
- topic_id TEXT NOT NULL,
254
- user_id TEXT NOT NULL,
255
- message TEXT NOT NULL,
256
- deliver_at INTEGER NOT NULL,
257
- created_at INTEGER NOT NULL,
258
- updated_at INTEGER NOT NULL,
259
- status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'running')),
260
- claimed_by TEXT,
261
- claimed_at INTEGER,
262
- running_query_id TEXT
263
- )
264
- `);
265
- db.exec(`
266
- CREATE UNIQUE INDEX IF NOT EXISTS idx_runtime_self_schedules_one_pending
267
- ON runtime_self_schedules(topic_id)
268
- WHERE status = 'pending'
269
- `);
270
- db.exec(`
271
- CREATE INDEX IF NOT EXISTS idx_runtime_self_schedules_due
272
- ON runtime_self_schedules(status, deliver_at)
273
- `);
274
- function rowToSchedule(row) {
275
- return {
276
- id: row.id,
277
- topicId: row.topic_id,
278
- userId: row.user_id,
279
- message: row.message,
280
- deliverAt: Number(row.deliver_at),
281
- createdAt: Number(row.created_at),
282
- updatedAt: Number(row.updated_at),
283
- status: row.status === "running" ? "running" : "pending",
284
- claimedBy: row.claimed_by ?? undefined,
285
- claimedAt: row.claimed_at === null ? undefined : Number(row.claimed_at),
286
- runningQueryId: row.running_query_id ?? undefined
287
- };
288
- }
289
- function createPendingSelfSchedule(input) {
290
- return db.transaction(() => {
291
- const existing = getPendingSelfSchedule(input.topicId);
292
- if (existing)
293
- return { ok: false, existing };
294
- const now = input.now ?? Date.now();
295
- const schedule = {
296
- id: `scheduled-${randomUUID()}`,
297
- topicId: input.topicId,
298
- userId: input.userId,
299
- message: input.message,
300
- deliverAt: input.deliverAt,
301
- createdAt: now,
302
- updatedAt: now,
303
- status: "pending"
304
- };
305
- db.query(`INSERT INTO runtime_self_schedules
306
- (id, topic_id, user_id, message, deliver_at, created_at, updated_at, status)
307
- VALUES (?, ?, ?, ?, ?, ?, ?, 'pending')`).run(schedule.id, schedule.topicId, schedule.userId, schedule.message, schedule.deliverAt, schedule.createdAt, schedule.updatedAt);
308
- return { ok: true, schedule };
309
- }).immediate();
310
- }
311
- function getPendingSelfSchedule(topicId) {
312
- const row = db.query("SELECT * FROM runtime_self_schedules WHERE topic_id = ? AND status = 'pending'").get(topicId);
313
- return row ? rowToSchedule(row) : null;
314
- }
315
- function updatePendingSelfSchedule(input) {
316
- const existing = getPendingSelfSchedule(input.topicId);
317
- if (!existing || existing.id !== input.scheduleId)
318
- return null;
319
- const result = db.query(`UPDATE runtime_self_schedules
320
- SET message = ?, deliver_at = ?, updated_at = ?
321
- WHERE id = ? AND topic_id = ? AND status = 'pending'`).run(input.message ?? existing.message, input.deliverAt ?? existing.deliverAt, input.now ?? Date.now(), input.scheduleId, input.topicId);
322
- return Number(result.changes ?? 0) > 0 ? getPendingSelfSchedule(input.topicId) : null;
323
- }
324
- function cancelPendingSelfSchedule(topicId, scheduleId) {
325
- const result = db.query("DELETE FROM runtime_self_schedules WHERE id = ? AND topic_id = ? AND status = 'pending'").run(scheduleId, topicId);
326
- return Number(result.changes ?? 0) > 0;
327
- }
328
- function claimNextDueSelfSchedule(ownerId, now = Date.now()) {
329
- const staleBefore = now - TURN_LEASE_STALE_MS;
330
- return db.transaction(() => {
331
- const row = db.query(`SELECT s.*
332
- FROM runtime_self_schedules s
333
- LEFT JOIN runtime_turn_leases l ON l.topic_id = s.topic_id
334
- LEFT JOIN runtime_topic_state t ON t.topic_id = s.topic_id
335
- WHERE (l.topic_id IS NULL OR l.heartbeat_at < ?)
336
- AND (t.topic_id IS NULL OR t.maintenance = 0 OR t.heartbeat_at < ?)
337
- AND (
338
- (s.status = 'pending' AND s.deliver_at <= ?)
339
- OR (s.status = 'running' AND (s.claimed_at IS NULL OR s.claimed_at < ?))
340
- )
341
- ORDER BY CASE s.status WHEN 'running' THEN 0 ELSE 1 END, s.deliver_at ASC
342
- LIMIT 1`).get(staleBefore, now - TOPIC_MAINTENANCE_STALE_MS, now, staleBefore);
343
- if (!row)
344
- return null;
345
- const result = db.query(`UPDATE runtime_self_schedules
346
- SET status = 'running', claimed_by = ?, claimed_at = ?, running_query_id = NULL
347
- WHERE id = ? AND (
348
- (status = 'pending' AND deliver_at <= ?)
349
- OR (status = 'running' AND (claimed_at IS NULL OR claimed_at < ?))
350
- )`).run(ownerId, now, row.id, now, staleBefore);
351
- if (Number(result.changes ?? 0) === 0)
352
- return null;
353
- return rowToSchedule({
354
- ...row,
355
- status: "running",
356
- claimed_by: ownerId,
357
- claimed_at: now,
358
- running_query_id: null
359
- });
360
- }).immediate();
361
- }
362
- function markSelfScheduleRunning(scheduleId, ownerId, queryId) {
363
- const result = db.query(`UPDATE runtime_self_schedules
364
- SET running_query_id = ?, claimed_at = ?
365
- WHERE id = ? AND status = 'running' AND claimed_by = ?`).run(queryId, Date.now(), scheduleId, ownerId);
366
- return Number(result.changes ?? 0) > 0;
367
- }
368
- function heartbeatSelfScheduleClaim(scheduleId, ownerId, now = Date.now()) {
369
- const result = db.query(`UPDATE runtime_self_schedules
370
- SET claimed_at = ?
371
- WHERE id = ? AND status = 'running' AND claimed_by = ?`).run(now, scheduleId, ownerId);
372
- return Number(result.changes ?? 0) > 0;
373
- }
374
- function releaseSelfScheduleClaim(scheduleId, ownerId) {
375
- return db.transaction(() => {
376
- const row = db.query("SELECT * FROM runtime_self_schedules WHERE id = ? AND status = 'running' AND claimed_by = ?").get(scheduleId, ownerId);
377
- if (!row)
378
- return "dropped";
379
- const newer = getPendingSelfSchedule(row.topic_id);
380
- if (newer) {
381
- db.query("DELETE FROM runtime_self_schedules WHERE id = ?").run(scheduleId);
382
- return "dropped";
383
- }
384
- db.query(`UPDATE runtime_self_schedules
385
- SET status = 'pending', claimed_by = NULL, claimed_at = NULL,
386
- running_query_id = NULL, updated_at = ?
387
- WHERE id = ? AND claimed_by = ?`).run(Date.now(), scheduleId, ownerId);
388
- return "released";
389
- }).immediate();
390
- }
391
- function completeSelfSchedule(scheduleId, ownerId) {
392
- const result = ownerId ? db.query("DELETE FROM runtime_self_schedules WHERE id = ? AND claimed_by = ?").run(scheduleId, ownerId) : db.query("DELETE FROM runtime_self_schedules WHERE id = ?").run(scheduleId);
393
- return Number(result.changes ?? 0) > 0;
394
- }
395
- function deleteSelfSchedulesForTopic(topicId) {
396
- const result = db.query("DELETE FROM runtime_self_schedules WHERE topic_id = ?").run(topicId);
397
- return Number(result.changes ?? 0);
398
- }
399
-
400
- // ../../packages/core/src/topics/lifecycle.ts
401
- var DELETE_TURN_WAIT_MS = 5000;
402
- async function abortAndWaitForTopic(topicId) {
403
- interSessionQueue.drop(topicId);
404
- cancelIdleArchiveForTopic(topicId);
405
- const aborted = abortRoom(topicId);
406
- if (!aborted)
407
- return true;
408
- logger.info({ topicId }, "deleteTopicCascade: aborted in-flight turn");
409
- const deadline = Date.now() + DELETE_TURN_WAIT_MS;
410
- while ((getRoomQuery(topicId) || getRuntimeTurnLease(topicId)) && Date.now() < deadline) {
411
- await delay(50);
412
- }
413
- if (getRoomQuery(topicId) || getRuntimeTurnLease(topicId)) {
414
- logger.warn({ topicId, timeoutMs: DELETE_TURN_WAIT_MS }, "deleteTopicCascade: timed out waiting for in-flight turn cleanup");
415
- return false;
416
- }
417
- return true;
418
- }
419
- function currentSessionRef(topic, sessionId) {
420
- return topic.agent && sessionId ? [{ agent: topic.agent, sessionId }] : [];
421
- }
422
- async function cleanupParticipantResources(topic, userIds, sessionId, cwd) {
423
- for (const [index, participantUserId] of userIds.entries()) {
424
- try {
425
- await purgeTopicLogs({
426
- userId: participantUserId,
427
- topicName: topic.title,
428
- cwd,
429
- extraSessions: index === 0 ? currentSessionRef(topic, sessionId) : []
430
- });
431
- } catch (err) {
432
- logger.warn({ err, topicId: topic.id, userId: participantUserId }, "deleteTopicCascade: participant rollout cleanup failed");
433
- }
434
- cleanupSessionInboxFiles(participantUserId, topic.id, topic.title);
435
- clearQueryState(participantUserId, topic.title);
436
- clearQueryUsageAlert(participantUserId, topic.id);
437
- deletePendingAsksForTopic({ userId: participantUserId, topicName: topic.title });
438
- }
439
- }
440
-
441
- class TopicArchiveRequiredError extends Error {
442
- code = "TOPIC_ARCHIVE_FAILED";
443
- topicId;
444
- constructor(topicId, cause) {
445
- super("Topic archive failed; deletion was blocked to avoid losing conversation history.");
446
- this.name = "TopicArchiveRequiredError";
447
- this.topicId = topicId;
448
- this.cause = cause;
449
- }
450
- }
451
-
452
- class TopicTurnStillActiveError extends Error {
453
- code = "TOPIC_TURN_STILL_ACTIVE";
454
- topicId;
455
- constructor(topicId) {
456
- super("The active turn did not stop in time; deletion was blocked. Use force-delete explicitly.");
457
- this.name = "TopicTurnStillActiveError";
458
- this.topicId = topicId;
459
- }
460
- }
461
- async function deleteTopicCascade(topic, userId, options = {}) {
462
- await deleteTopicCascadeImpl(topic, userId, options, new Set);
463
- }
464
- async function deleteTopicCascadeImpl(topic, userId, options, deletingAncestorIds) {
465
- const topicId = topic.id;
466
- if (topicId === GENERAL_TOPIC_ID || topic.kind === "manager" && !options.allowManager) {
467
- logger.warn({ topicId }, "deleteTopicCascade: refused to delete essential topic");
468
- return;
469
- }
470
- const deletingTopicIds = new Set(deletingAncestorIds);
471
- deletingTopicIds.add(topicId);
472
- const force = options.force === true;
473
- const maintenance = beginRuntimeTopicMaintenance(topicId);
474
- if (!maintenance)
475
- throw new Error("Topic maintenance is already in progress.");
476
- let deleted = false;
477
- const cancelledQueryIds = cancelRuntimeUserTurnRequestsBeforeEpoch(topicId, maintenance.epoch);
478
- for (const queryId of cancelledQueryIds) {
479
- WsHub.get().broadcastAborted(topicId, queryId, "stopped");
480
- }
481
- try {
482
- const turnStopped = await abortAndWaitForTopic(topicId);
483
- if (!turnStopped && !force)
484
- throw new TopicTurnStillActiveError(topicId);
485
- if (!maintenance.isOwned())
486
- throw new Error("Topic maintenance ownership was lost.");
487
- let archived = null;
488
- if (!options.skipArchive) {
489
- try {
490
- archived = archiveTopicMessages(topicId, topic.title);
491
- } catch (err) {
492
- if (!force) {
493
- logger.warn({ err, topicId }, "deleteTopicCascade: archive failed - blocking delete unless force=true");
494
- throw new TopicArchiveRequiredError(topicId, err);
495
- }
496
- logger.warn({ err, topicId }, "deleteTopicCascade: archive failed but force=true - continuing with hard delete");
497
- }
498
- }
499
- if (archived) {
500
- try {
501
- const memoryTopic = getTopicMemoryOrigin(topicId) ?? topic;
502
- runArchiverTurn({
503
- userId,
504
- ...memoryTopic.id !== topicId && !deletingTopicIds.has(memoryTopic.id) ? { topicId: memoryTopic.id } : {},
505
- topicTitle: memoryTopic.title,
506
- archivePath: archived.path,
507
- messageCount: archived.messageCount
508
- });
509
- } catch (err) {
510
- logger.warn({ err, topicId, archive: archived.path }, "deleteTopicCascade: background archiver launch failed - raw archive preserved");
511
- }
512
- }
513
- cancelSubagentWatchForDeletedTopic(topicId);
514
- const spawnedChildren = listTopics().filter((candidate) => candidate.parentTopicId === topicId && candidate.isSubagent && !deletingTopicIds.has(candidate.id));
515
- for (const child of spawnedChildren) {
516
- await deleteTopicCascadeImpl(child, userId, options, deletingTopicIds);
517
- }
518
- const sessionId = getTopicSessionId(topicId);
519
- const cwd = resolveTopicWorkspaceDir(topicId);
520
- const participantUserIds = Array.from(new Set([userId, ...topic.participants.map((participant) => participant.userId)]));
521
- killBgBash(userId, topicId);
522
- const browserProfileOwner = getBrowserProfileOwner(topicId, userId);
523
- const browserProfile = getTopicBrowserProfile(topicId);
524
- try {
525
- await closeBrowserOwnerTabs(browserProfileOwner, browserProfile, `topic:${topicId}`);
526
- } catch (err) {
527
- logger.warn({ err, topicId }, "deleteTopicCascade: browser owner cleanup failed");
528
- }
529
- deleteTopicProfileDir(userId, topicId);
530
- cancelAskCallbacksForTopic(topicId);
531
- deleteSelfSchedulesForTopic(topicId);
532
- await cleanupParticipantResources(topic, participantUserIds, sessionId, cwd);
533
- try {
534
- await deleteFilesForTopic(topicId);
535
- } catch (err) {
536
- logger.warn({ err, topicId }, "deleteTopicCascade: host file cleanup failed");
537
- }
538
- try {
539
- rmSync(cwd, { recursive: true, force: true });
540
- } catch (err) {
541
- logger.warn({ err, topicId, cwd }, "deleteTopicCascade: workspace cleanup failed");
542
- }
543
- deleteTopicVisuals(topicId);
544
- deleteTopicArchiveState(topicId);
545
- deleteMessagesForTopic(topicId);
546
- deleteApiTopicConfig(topicId);
547
- deleteTopicBrief(topicId);
548
- const reparentedChildIds = reparentTopicChildren(topicId, topic.parentTopicId ?? null);
549
- const rowDeleted = deleteTopic(topicId, { allowManager: options.allowManager });
550
- if (!rowDeleted) {
551
- logger.warn({ topicId }, "deleteTopicCascade: topic row was not deleted");
552
- return;
553
- }
554
- deleted = true;
555
- WsHub.get().broadcastTopicDeleted(topicId);
556
- for (const childId of reparentedChildIds)
557
- WsHub.get().broadcastTopicUpdated(childId);
558
- } finally {
559
- if (deleted) {
560
- for (const queryId of cancelRuntimeUserTurnRequests(topicId)) {
561
- WsHub.get().broadcastAborted(topicId, queryId, "stopped");
562
- }
563
- maintenance.finish({ deleteState: true });
564
- } else {
565
- maintenance.finish();
566
- }
567
- }
568
- }
569
- export {
570
- deleteTopicCascade,
571
- TopicTurnStillActiveError,
572
- TopicArchiveRequiredError
573
- };
574
-
575
- export { createPendingSelfSchedule, getPendingSelfSchedule, updatePendingSelfSchedule, cancelPendingSelfSchedule, claimNextDueSelfSchedule, markSelfScheduleRunning, heartbeatSelfScheduleClaim, releaseSelfScheduleClaim, completeSelfSchedule, cleanupTopicRollouts, rotateTopicLogs, purgeTopicLogs, sessionInboxPath, topicIdFromSessionInboxFileName, topicIdFromScheduledSessionInboxFileName, TopicArchiveRequiredError, TopicTurnStillActiveError, deleteTopicCascade };
576
-
577
- //# debugId=9E5ABA7869142D0564756E2164756E21