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.
Files changed (165) hide show
  1. package/README.md +11 -2
  2. package/dist/canonical-mcp-bridge.js +2 -3
  3. package/dist/canonical-mcp-bridge.js.map +1 -1
  4. package/dist/{chunk-gz12qcf0.js → chunk-a9y2rwxf.js} +2 -2
  5. package/dist/{chunk-gz12qcf0.js.map → chunk-a9y2rwxf.js.map} +3 -3
  6. package/dist/{chunk-j0kjscb7.js → chunk-s2gez3wg.js} +20 -2
  7. package/dist/{chunk-j0kjscb7.js.map → chunk-s2gez3wg.js.map} +2 -2
  8. package/dist/cron.js +292 -256
  9. package/dist/cron.js.map +23 -22
  10. package/dist/hosted-agent.js +3012 -17
  11. package/dist/hosted-agent.js.map +30 -3
  12. package/dist/main.js +31323 -36
  13. package/dist/main.js.map +198 -3
  14. package/dist/prompts.js +12 -9
  15. package/dist/prompts.js.map +5 -5
  16. package/dist/registry.js +10 -6
  17. package/dist/registry.js.map +3 -3
  18. package/dist/rollout.js +1 -1
  19. package/dist/runtime/cron/store.ts +24 -1
  20. package/dist/runtime/src/application/submit-user-message.ts +3 -0
  21. package/dist/runtime/src/index.ts +7 -0
  22. package/dist/runtime/src/mcp/runtime-spec.ts +3 -0
  23. package/dist/runtime/src/mcp/session-comm/server.ts +1 -1
  24. package/dist/runtime/src/platform/config.ts +2 -2
  25. package/dist/runtime/src/platform/mcp-config.ts +13 -0
  26. package/dist/runtime/src/prompts/builders.ts +20 -6
  27. package/dist/runtime/src/query/active-rooms.ts +2 -0
  28. package/dist/runtime/src/runtime/delivery-ack.ts +112 -0
  29. package/dist/runtime/src/runtime/errors.ts +12 -12
  30. package/dist/runtime/src/runtime/turn-runner.ts +12 -0
  31. package/dist/runtime/src/storage/api-messages.ts +7 -5
  32. package/dist/runtime/src/storage/api-topics.ts +5 -2
  33. package/dist/runtime/src/storage/forum-db.ts +4 -1
  34. package/dist/runtime/src/storage/runtime-turn-requests.ts +1 -0
  35. package/dist/runtime/src/storage/session-asks.ts +13 -5
  36. package/dist/runtime/src/storage/storage-contract.ts +54 -0
  37. package/dist/runtime/src/storage/storage-host.ts +89 -36
  38. package/dist/runtime/src/storage/storage-public-host.ts +16 -0
  39. package/dist/runtime/src/storage/storage-public.ts +21 -5
  40. package/dist/runtime/src/storage/token-stats.ts +1 -1
  41. package/dist/runtime/src/types/api.ts +2 -0
  42. package/dist/runtime/src/types.ts +2 -0
  43. package/dist/runtime/src/version.ts +1 -1
  44. package/dist/runtime-helpers.js +29 -18
  45. package/dist/runtime-helpers.js.map +8 -7
  46. package/dist/storage.js +260 -202
  47. package/dist/storage.js.map +11 -9
  48. package/dist/types/packages/core/src/mcp/runtime-spec.d.ts +2 -0
  49. package/dist/types/packages/core/src/platform/mcp-config.d.ts +4 -0
  50. package/dist/types/packages/core/src/prompts/builders.d.ts +2 -0
  51. package/dist/types/packages/core/src/storage/api-messages.d.ts +6 -6
  52. package/dist/types/packages/core/src/storage/api-topics.d.ts +20 -2
  53. package/dist/types/packages/core/src/storage/forum-db.d.ts +2 -1
  54. package/dist/types/packages/core/src/storage/session-asks.d.ts +3 -2
  55. package/dist/types/packages/core/src/storage/storage-contract.d.ts +46 -0
  56. package/dist/types/packages/core/src/storage/storage-host.d.ts +12 -19
  57. package/dist/types/packages/core/src/storage/storage-public-host.d.ts +7 -0
  58. package/dist/types/packages/core/src/storage/storage-public.d.ts +4 -2
  59. package/dist/types/packages/core/src/types/api.d.ts +2 -0
  60. package/dist/types/packages/core/src/types.d.ts +2 -0
  61. package/dist/types/packages/core/src/version.d.ts +1 -1
  62. package/dist/types/packages/module-cron/src/store.d.ts +22 -2
  63. package/dist/vault.js +2 -2
  64. package/dist/vault.js.map +3 -3
  65. package/package.json +1 -1
  66. package/dist/chunk-01xcc856.js +0 -149
  67. package/dist/chunk-01xcc856.js.map +0 -10
  68. package/dist/chunk-1a6kv9vt.js +0 -118
  69. package/dist/chunk-1a6kv9vt.js.map +0 -10
  70. package/dist/chunk-1b62d730.js +0 -117
  71. package/dist/chunk-1b62d730.js.map +0 -10
  72. package/dist/chunk-1k7t7bnx.js +0 -97
  73. package/dist/chunk-1k7t7bnx.js.map +0 -10
  74. package/dist/chunk-28apmz4r.js +0 -431
  75. package/dist/chunk-28apmz4r.js.map +0 -11
  76. package/dist/chunk-2fvydfkx.js +0 -141
  77. package/dist/chunk-2fvydfkx.js.map +0 -10
  78. package/dist/chunk-41ygvg5k.js +0 -2387
  79. package/dist/chunk-41ygvg5k.js.map +0 -26
  80. package/dist/chunk-4q966ttx.js +0 -371
  81. package/dist/chunk-4q966ttx.js.map +0 -11
  82. package/dist/chunk-51rcsavw.js +0 -1712
  83. package/dist/chunk-51rcsavw.js.map +0 -18
  84. package/dist/chunk-59d0shyj.js +0 -103
  85. package/dist/chunk-59d0shyj.js.map +0 -10
  86. package/dist/chunk-5fzgvn79.js +0 -3536
  87. package/dist/chunk-5fzgvn79.js.map +0 -20
  88. package/dist/chunk-61px0717.js +0 -153
  89. package/dist/chunk-61px0717.js.map +0 -11
  90. package/dist/chunk-66y0hr8c.js +0 -2136
  91. package/dist/chunk-66y0hr8c.js.map +0 -29
  92. package/dist/chunk-69k45zg8.js +0 -250
  93. package/dist/chunk-69k45zg8.js.map +0 -11
  94. package/dist/chunk-6ah5farg.js +0 -130
  95. package/dist/chunk-6ah5farg.js.map +0 -10
  96. package/dist/chunk-6d4fmea5.js +0 -117
  97. package/dist/chunk-6d4fmea5.js.map +0 -12
  98. package/dist/chunk-8cahx7xc.js +0 -46
  99. package/dist/chunk-8cahx7xc.js.map +0 -10
  100. package/dist/chunk-8f20fk4n.js +0 -252
  101. package/dist/chunk-8f20fk4n.js.map +0 -10
  102. package/dist/chunk-91ckrvk8.js +0 -318
  103. package/dist/chunk-91ckrvk8.js.map +0 -10
  104. package/dist/chunk-93krf6ng.js +0 -3069
  105. package/dist/chunk-93krf6ng.js.map +0 -32
  106. package/dist/chunk-9qx1t77a.js +0 -214
  107. package/dist/chunk-9qx1t77a.js.map +0 -10
  108. package/dist/chunk-beqhdr9w.js +0 -589
  109. package/dist/chunk-beqhdr9w.js.map +0 -11
  110. package/dist/chunk-bftftsgq.js +0 -1637
  111. package/dist/chunk-bftftsgq.js.map +0 -15
  112. package/dist/chunk-c500xe9z.js +0 -33
  113. package/dist/chunk-c500xe9z.js.map +0 -10
  114. package/dist/chunk-cjsnkvq2.js +0 -23
  115. package/dist/chunk-cjsnkvq2.js.map +0 -10
  116. package/dist/chunk-cxa7yghg.js +0 -577
  117. package/dist/chunk-cxa7yghg.js.map +0 -14
  118. package/dist/chunk-edb07sjp.js +0 -567
  119. package/dist/chunk-edb07sjp.js.map +0 -14
  120. package/dist/chunk-f361dwba.js +0 -589
  121. package/dist/chunk-f361dwba.js.map +0 -11
  122. package/dist/chunk-f5xgprg1.js +0 -59
  123. package/dist/chunk-f5xgprg1.js.map +0 -10
  124. package/dist/chunk-ffefbd3r.js +0 -17
  125. package/dist/chunk-ffefbd3r.js.map +0 -10
  126. package/dist/chunk-fswhzdf5.js +0 -64
  127. package/dist/chunk-fswhzdf5.js.map +0 -10
  128. package/dist/chunk-g144wdrs.js +0 -2907
  129. package/dist/chunk-g144wdrs.js.map +0 -24
  130. package/dist/chunk-h980hb2n.js +0 -512
  131. package/dist/chunk-h980hb2n.js.map +0 -13
  132. package/dist/chunk-h9vbhhgr.js +0 -1337
  133. package/dist/chunk-h9vbhhgr.js.map +0 -16
  134. package/dist/chunk-hwy3n31w.js +0 -21
  135. package/dist/chunk-hwy3n31w.js.map +0 -9
  136. package/dist/chunk-jtrk0khr.js +0 -108
  137. package/dist/chunk-jtrk0khr.js.map +0 -10
  138. package/dist/chunk-jzz91jwh.js +0 -70
  139. package/dist/chunk-jzz91jwh.js.map +0 -10
  140. package/dist/chunk-m9e3tbr8.js +0 -708
  141. package/dist/chunk-m9e3tbr8.js.map +0 -14
  142. package/dist/chunk-mh5mexsf.js +0 -147
  143. package/dist/chunk-mh5mexsf.js.map +0 -11
  144. package/dist/chunk-npd8n03v.js +0 -2152
  145. package/dist/chunk-npd8n03v.js.map +0 -15
  146. package/dist/chunk-pg1azh6n.js +0 -2
  147. package/dist/chunk-pg1azh6n.js.map +0 -9
  148. package/dist/chunk-qwy57177.js +0 -237
  149. package/dist/chunk-qwy57177.js.map +0 -11
  150. package/dist/chunk-r31q21h5.js +0 -53
  151. package/dist/chunk-r31q21h5.js.map +0 -10
  152. package/dist/chunk-rbvctv1n.js +0 -366
  153. package/dist/chunk-rbvctv1n.js.map +0 -10
  154. package/dist/chunk-sa874j5c.js +0 -9
  155. package/dist/chunk-sa874j5c.js.map +0 -10
  156. package/dist/chunk-sctcs5zb.js +0 -281
  157. package/dist/chunk-sctcs5zb.js.map +0 -10
  158. package/dist/chunk-tsstrcfc.js +0 -122
  159. package/dist/chunk-tsstrcfc.js.map +0 -10
  160. package/dist/chunk-wk2qf31s.js +0 -58
  161. package/dist/chunk-wk2qf31s.js.map +0 -10
  162. package/dist/chunk-wx7ag4dd.js +0 -2936
  163. package/dist/chunk-wx7ag4dd.js.map +0 -20
  164. package/dist/chunk-znx1fpfd.js +0 -61
  165. package/dist/chunk-znx1fpfd.js.map +0 -10
@@ -1,577 +0,0 @@
1
- // @bun
2
- import {
3
- cancelAskCallbacksForTopic
4
- } from "./chunk-59d0shyj.js";
5
- import {
6
- deletePendingAsksForTopic
7
- } from "./chunk-rbvctv1n.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-66y0hr8c.js";
24
- import {
25
- WsHub,
26
- closeBrowserOwnerTabs,
27
- delay,
28
- deleteMessagesForTopic,
29
- deleteTopicProfileDir,
30
- getBrowserProfileOwner,
31
- getTopicBrowserProfile
32
- } from "./chunk-h9vbhhgr.js";
33
- import {
34
- GENERAL_TOPIC_ID,
35
- deleteTopic,
36
- getTopicMemoryOrigin,
37
- getTopicSessionId,
38
- listTopics,
39
- reparentTopicChildren
40
- } from "./chunk-f361dwba.js";
41
- import {
42
- getRegistry
43
- } from "./chunk-6d4fmea5.js";
44
- import {
45
- getConversationPath,
46
- readConversation
47
- } from "./chunk-61px0717.js";
48
- import {
49
- deleteApiTopicConfig
50
- } from "./chunk-6ah5farg.js";
51
- import"./chunk-41ygvg5k.js";
52
- import"./chunk-m9e3tbr8.js";
53
- import"./chunk-mh5mexsf.js";
54
- import {
55
- SESSION_INBOX_DIR,
56
- SUPPORTED_AGENTS,
57
- resolveTopicWorkspaceDir
58
- } from "./chunk-qwy57177.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-h980hb2n.js";
69
- import {
70
- logger
71
- } from "./chunk-ffefbd3r.js";
72
- import"./chunk-pg1azh6n.js";
73
- import {
74
- storageDatabase
75
- } from "./chunk-1a6kv9vt.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
- storageDatabase.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
- storageDatabase.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
- storageDatabase.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 storageDatabase.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
- storageDatabase.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 = storageDatabase.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 = storageDatabase.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 = storageDatabase.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 storageDatabase.transaction(() => {
331
- const row = storageDatabase.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 = storageDatabase.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 = storageDatabase.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 = storageDatabase.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 storageDatabase.transaction(() => {
376
- const row = storageDatabase.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
- storageDatabase.query("DELETE FROM runtime_self_schedules WHERE id = ?").run(scheduleId);
382
- return "dropped";
383
- }
384
- storageDatabase.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 ? storageDatabase.query("DELETE FROM runtime_self_schedules WHERE id = ? AND claimed_by = ?").run(scheduleId, ownerId) : storageDatabase.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 = storageDatabase.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=A77902FD7A938BC964756E2164756E21