@useorgx/openclaw-plugin 0.7.20 → 0.7.24

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/dashboard/dist/assets/B6VftyY6.js +1 -0
  2. package/dashboard/dist/assets/B6VftyY6.js.br +0 -0
  3. package/dashboard/dist/assets/B6VftyY6.js.gz +0 -0
  4. package/dashboard/dist/assets/{Dm0CfDGr.js → BANQdlC4.js} +1 -1
  5. package/dashboard/dist/assets/BANQdlC4.js.br +0 -0
  6. package/dashboard/dist/assets/BANQdlC4.js.gz +0 -0
  7. package/dashboard/dist/assets/{_zpQCpjm.js → BPL4CL3c.js} +1 -1
  8. package/dashboard/dist/assets/BPL4CL3c.js.br +0 -0
  9. package/dashboard/dist/assets/BPL4CL3c.js.gz +0 -0
  10. package/dashboard/dist/assets/{DXVs61e1.js → BZCkOZ20.js} +1 -1
  11. package/dashboard/dist/assets/BZCkOZ20.js.br +0 -0
  12. package/dashboard/dist/assets/BZCkOZ20.js.gz +0 -0
  13. package/dashboard/dist/assets/{BYb6DARX.js → B_LdOJUa.js} +1 -1
  14. package/dashboard/dist/assets/B_LdOJUa.js.br +0 -0
  15. package/dashboard/dist/assets/B_LdOJUa.js.gz +0 -0
  16. package/dashboard/dist/assets/Bfp-wdwb.css +1 -0
  17. package/dashboard/dist/assets/Bfp-wdwb.css.br +0 -0
  18. package/dashboard/dist/assets/Bfp-wdwb.css.gz +0 -0
  19. package/dashboard/dist/assets/{DibzNd0I.js → BvFcH_Iy.js} +1 -1
  20. package/dashboard/dist/assets/BvFcH_Iy.js.br +0 -0
  21. package/dashboard/dist/assets/BvFcH_Iy.js.gz +0 -0
  22. package/dashboard/dist/assets/By0MIBj_.js +1 -0
  23. package/dashboard/dist/assets/By0MIBj_.js.br +0 -0
  24. package/dashboard/dist/assets/By0MIBj_.js.gz +0 -0
  25. package/dashboard/dist/assets/C0i7ABUU.js +212 -0
  26. package/dashboard/dist/assets/C0i7ABUU.js.br +0 -0
  27. package/dashboard/dist/assets/C0i7ABUU.js.gz +0 -0
  28. package/dashboard/dist/assets/CFB0MM7j.js +1 -0
  29. package/dashboard/dist/assets/CFB0MM7j.js.br +0 -0
  30. package/dashboard/dist/assets/CFB0MM7j.js.gz +0 -0
  31. package/dashboard/dist/assets/CQSRb1yu.js +1 -0
  32. package/dashboard/dist/assets/CQSRb1yu.js.br +0 -0
  33. package/dashboard/dist/assets/CQSRb1yu.js.gz +0 -0
  34. package/dashboard/dist/assets/{wa4jJQK9.js → CUoQoSm-.js} +1 -1
  35. package/dashboard/dist/assets/CUoQoSm-.js.br +0 -0
  36. package/dashboard/dist/assets/CUoQoSm-.js.gz +0 -0
  37. package/dashboard/dist/assets/Ckd1R1iE.js +1 -0
  38. package/dashboard/dist/assets/Ckd1R1iE.js.br +0 -0
  39. package/dashboard/dist/assets/Ckd1R1iE.js.gz +0 -0
  40. package/dashboard/dist/assets/{BGY6oI8h.js → CqRNb2EL.js} +1 -1
  41. package/dashboard/dist/assets/CqRNb2EL.js.br +0 -0
  42. package/dashboard/dist/assets/CqRNb2EL.js.gz +0 -0
  43. package/dashboard/dist/assets/{DAr4MfFk.js → DClUc9rw.js} +1 -1
  44. package/dashboard/dist/assets/DClUc9rw.js.br +0 -0
  45. package/dashboard/dist/assets/DClUc9rw.js.gz +0 -0
  46. package/dashboard/dist/assets/DF2PMTwT.js +1 -0
  47. package/dashboard/dist/assets/DF2PMTwT.js.br +0 -0
  48. package/dashboard/dist/assets/DF2PMTwT.js.gz +0 -0
  49. package/dashboard/dist/assets/{B014hrCe.js → DJYl7gyA.js} +2 -2
  50. package/dashboard/dist/assets/DJYl7gyA.js.br +0 -0
  51. package/dashboard/dist/assets/DJYl7gyA.js.gz +0 -0
  52. package/dashboard/dist/assets/{BoDhb8_y.js → DZtNMX0t.js} +2 -2
  53. package/dashboard/dist/assets/DZtNMX0t.js.br +0 -0
  54. package/dashboard/dist/assets/DZtNMX0t.js.gz +0 -0
  55. package/dashboard/dist/assets/DlEa8PI0.js +1 -0
  56. package/dashboard/dist/assets/DlEa8PI0.js.br +0 -0
  57. package/dashboard/dist/assets/DlEa8PI0.js.gz +0 -0
  58. package/dashboard/dist/assets/M4QxcXjh.js +1 -0
  59. package/dashboard/dist/assets/M4QxcXjh.js.br +0 -0
  60. package/dashboard/dist/assets/M4QxcXjh.js.gz +0 -0
  61. package/dashboard/dist/assets/{CV0sWMbv.js → MrW1ixGx.js} +1 -1
  62. package/dashboard/dist/assets/MrW1ixGx.js.br +0 -0
  63. package/dashboard/dist/assets/MrW1ixGx.js.gz +0 -0
  64. package/dashboard/dist/index.html +2 -2
  65. package/dashboard/dist/index.html.br +0 -0
  66. package/dashboard/dist/index.html.gz +0 -0
  67. package/dist/activity-store.js +68 -8
  68. package/dist/agent-run-store.js +162 -24
  69. package/dist/cli/orgx.d.ts +3 -0
  70. package/dist/config/resolution.d.ts +7 -0
  71. package/dist/config/resolution.js +13 -5
  72. package/dist/contracts/onboarding-state.d.ts +2 -0
  73. package/dist/contracts/onboarding-state.js +23 -0
  74. package/dist/contracts/shared-types.d.ts +45 -0
  75. package/dist/http/helpers/auto-continue-engine.d.ts +23 -0
  76. package/dist/http/helpers/auto-continue-engine.js +468 -85
  77. package/dist/http/helpers/autopilot-runtime.js +5 -1
  78. package/dist/http/helpers/autopilot-slice-utils.js +25 -1
  79. package/dist/http/helpers/decision-mapper.d.ts +1 -0
  80. package/dist/http/helpers/decision-mapper.js +19 -2
  81. package/dist/http/helpers/dispatch-lifecycle.js +3 -0
  82. package/dist/http/helpers/mission-control.d.ts +1 -0
  83. package/dist/http/helpers/mission-control.js +5 -2
  84. package/dist/http/helpers/slice-run-projections.d.ts +27 -0
  85. package/dist/http/helpers/slice-run-projections.js +198 -10
  86. package/dist/http/helpers/triage-mapper.js +499 -6
  87. package/dist/http/helpers/value-utils.d.ts +1 -0
  88. package/dist/http/helpers/value-utils.js +17 -0
  89. package/dist/http/index.d.ts +1 -0
  90. package/dist/http/index.js +179 -46
  91. package/dist/http/router.js +64 -9
  92. package/dist/http/routes/live-legacy.d.ts +19 -2
  93. package/dist/http/routes/live-legacy.js +110 -27
  94. package/dist/http/routes/live-snapshot.d.ts +16 -2
  95. package/dist/http/routes/live-snapshot.js +169 -25
  96. package/dist/http/routes/live-triage.js +6 -1
  97. package/dist/http/routes/mission-control-actions.d.ts +9 -0
  98. package/dist/http/routes/mission-control-actions.js +185 -7
  99. package/dist/http/routes/mission-control-read.d.ts +13 -0
  100. package/dist/http/routes/mission-control-read.js +100 -219
  101. package/dist/http/routes/onboarding.d.ts +1 -0
  102. package/dist/http/routes/onboarding.js +17 -0
  103. package/dist/index.d.ts +5 -0
  104. package/dist/index.js +199 -123
  105. package/dist/outbox.d.ts +0 -2
  106. package/dist/outbox.js +259 -148
  107. package/dist/reporting/rollups.js +18 -11
  108. package/dist/runtime-instance-store.js +212 -58
  109. package/dist/stores/materialized-snapshot-store.d.ts +18 -0
  110. package/dist/stores/materialized-snapshot-store.js +91 -0
  111. package/dist/stores/sqlite-state.d.ts +6 -0
  112. package/dist/stores/sqlite-state.js +330 -0
  113. package/package.json +5 -1
  114. package/dashboard/dist/assets/B014hrCe.js.br +0 -0
  115. package/dashboard/dist/assets/B014hrCe.js.gz +0 -0
  116. package/dashboard/dist/assets/BCudUvwg.js +0 -1
  117. package/dashboard/dist/assets/BCudUvwg.js.br +0 -0
  118. package/dashboard/dist/assets/BCudUvwg.js.gz +0 -0
  119. package/dashboard/dist/assets/BGY6oI8h.js.br +0 -0
  120. package/dashboard/dist/assets/BGY6oI8h.js.gz +0 -0
  121. package/dashboard/dist/assets/BJI1Iy5v.css +0 -1
  122. package/dashboard/dist/assets/BJI1Iy5v.css.br +0 -0
  123. package/dashboard/dist/assets/BJI1Iy5v.css.gz +0 -0
  124. package/dashboard/dist/assets/BUvcp_7V.js +0 -1
  125. package/dashboard/dist/assets/BUvcp_7V.js.br +0 -0
  126. package/dashboard/dist/assets/BUvcp_7V.js.gz +0 -0
  127. package/dashboard/dist/assets/BV2Tf8S2.js +0 -212
  128. package/dashboard/dist/assets/BV2Tf8S2.js.br +0 -0
  129. package/dashboard/dist/assets/BV2Tf8S2.js.gz +0 -0
  130. package/dashboard/dist/assets/BYb6DARX.js.br +0 -0
  131. package/dashboard/dist/assets/BYb6DARX.js.gz +0 -0
  132. package/dashboard/dist/assets/BoDhb8_y.js.br +0 -0
  133. package/dashboard/dist/assets/BoDhb8_y.js.gz +0 -0
  134. package/dashboard/dist/assets/Bqk_l0k6.js +0 -1
  135. package/dashboard/dist/assets/Bqk_l0k6.js.br +0 -0
  136. package/dashboard/dist/assets/Bqk_l0k6.js.gz +0 -0
  137. package/dashboard/dist/assets/C-MOJWHs.js +0 -1
  138. package/dashboard/dist/assets/C-MOJWHs.js.br +0 -0
  139. package/dashboard/dist/assets/C-MOJWHs.js.gz +0 -0
  140. package/dashboard/dist/assets/CV0sWMbv.js.br +0 -0
  141. package/dashboard/dist/assets/CV0sWMbv.js.gz +0 -0
  142. package/dashboard/dist/assets/CaAkScfa.js +0 -1
  143. package/dashboard/dist/assets/CaAkScfa.js.br +0 -0
  144. package/dashboard/dist/assets/CaAkScfa.js.gz +0 -0
  145. package/dashboard/dist/assets/Ck5KlsPN.js +0 -1
  146. package/dashboard/dist/assets/Ck5KlsPN.js.br +0 -0
  147. package/dashboard/dist/assets/Ck5KlsPN.js.gz +0 -0
  148. package/dashboard/dist/assets/D2G51wQm.js +0 -1
  149. package/dashboard/dist/assets/D2G51wQm.js.br +0 -0
  150. package/dashboard/dist/assets/D2G51wQm.js.gz +0 -0
  151. package/dashboard/dist/assets/DAr4MfFk.js.br +0 -0
  152. package/dashboard/dist/assets/DAr4MfFk.js.gz +0 -0
  153. package/dashboard/dist/assets/DXVs61e1.js.br +0 -0
  154. package/dashboard/dist/assets/DXVs61e1.js.gz +0 -0
  155. package/dashboard/dist/assets/DibzNd0I.js.br +0 -0
  156. package/dashboard/dist/assets/DibzNd0I.js.gz +0 -0
  157. package/dashboard/dist/assets/Dm0CfDGr.js.br +0 -0
  158. package/dashboard/dist/assets/Dm0CfDGr.js.gz +0 -0
  159. package/dashboard/dist/assets/_zpQCpjm.js.br +0 -0
  160. package/dashboard/dist/assets/_zpQCpjm.js.gz +0 -0
  161. package/dashboard/dist/assets/uNGpYMSH.js +0 -1
  162. package/dashboard/dist/assets/uNGpYMSH.js.br +0 -0
  163. package/dashboard/dist/assets/uNGpYMSH.js.gz +0 -0
  164. package/dashboard/dist/assets/wa4jJQK9.js.br +0 -0
  165. package/dashboard/dist/assets/wa4jJQK9.js.gz +0 -0
@@ -1,10 +1,12 @@
1
1
  import { existsSync, readFileSync, writeFileSync, } from "node:fs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { getOrgxPluginConfigDir, getOrgxPluginConfigPath } from "./paths.js";
4
- import { backupCorruptFileSync, writeJsonFileAtomicSync } from "./fs-utils.js";
4
+ import { backupCorruptFileSync } from "./fs-utils.js";
5
5
  import { clearStoreFileSync, ensureStoreDirSync, parseJsonSafe, } from "./stores/json-store.js";
6
+ import { getStateDb, readStateMeta, writeStateMeta, } from "./stores/sqlite-state.js";
6
7
  const MAX_INSTANCES = 600;
7
8
  export const DEFAULT_RUNTIME_HEARTBEAT_TIMEOUT_MS = 90_000;
9
+ const RUNTIME_IMPORT_META_KEY = "runtime_instances_imported_v1";
8
10
  function runtimeDir() {
9
11
  return getOrgxPluginConfigDir();
10
12
  }
@@ -19,8 +21,7 @@ function ensureRuntimeDir() {
19
21
  }
20
22
  function writeHookTokenFile(token) {
21
23
  ensureRuntimeDir();
22
- const file = hookTokenFile();
23
- writeFileSync(file, `${token}\n`, { encoding: "utf8", mode: 0o600 });
24
+ writeFileSync(hookTokenFile(), `${token}\n`, { encoding: "utf8", mode: 0o600 });
24
25
  }
25
26
  function normalizeNullableString(value) {
26
27
  if (typeof value !== "string")
@@ -174,7 +175,7 @@ function normalizeRecord(input) {
174
175
  updatedAt: input.updatedAt,
175
176
  };
176
177
  }
177
- export function readRuntimeInstances() {
178
+ function legacyReadRuntimeInstances() {
178
179
  const file = runtimeFile();
179
180
  try {
180
181
  if (!existsSync(file)) {
@@ -200,24 +201,171 @@ export function readRuntimeInstances() {
200
201
  return { updatedAt: new Date().toISOString(), instances: {} };
201
202
  }
202
203
  }
203
- function pruneStore(store) {
204
- const values = Object.values(store.instances);
205
- if (values.length <= MAX_INSTANCES)
204
+ function rowToRecord(row) {
205
+ return normalizeRecord({
206
+ id: row.id,
207
+ sourceClient: normalizeSourceClient(row.source_client),
208
+ displayName: row.display_name,
209
+ providerLogo: normalizeProviderLogo(row.provider_logo, normalizeSourceClient(row.source_client)),
210
+ state: normalizeState(row.state),
211
+ event: normalizeHookEvent(row.event),
212
+ runId: row.run_id,
213
+ correlationId: row.correlation_id,
214
+ initiativeId: row.initiative_id,
215
+ workstreamId: row.workstream_id,
216
+ taskId: row.task_id,
217
+ agentId: row.agent_id,
218
+ agentName: row.agent_name,
219
+ phase: row.phase,
220
+ progressPct: typeof row.progress_pct === "number" ? row.progress_pct : null,
221
+ currentTask: row.current_task,
222
+ lastHeartbeatAt: row.last_heartbeat_at,
223
+ lastEventAt: row.last_event_at,
224
+ lastMessage: row.last_message,
225
+ metadata: typeof row.metadata_json === "string"
226
+ ? normalizeObject(parseJsonSafe(row.metadata_json))
227
+ : null,
228
+ createdAt: row.created_at,
229
+ updatedAt: row.updated_at,
230
+ });
231
+ }
232
+ function writeRuntimeRecord(record) {
233
+ const normalized = normalizeRecord(record);
234
+ getStateDb()
235
+ .prepare(`INSERT INTO runtime_instances (
236
+ id,
237
+ source_client,
238
+ display_name,
239
+ provider_logo,
240
+ state,
241
+ event,
242
+ run_id,
243
+ correlation_id,
244
+ initiative_id,
245
+ workstream_id,
246
+ task_id,
247
+ agent_id,
248
+ agent_name,
249
+ phase,
250
+ progress_pct,
251
+ current_task,
252
+ last_heartbeat_at,
253
+ last_event_at,
254
+ last_message,
255
+ metadata_json,
256
+ created_at,
257
+ updated_at
258
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
259
+ ON CONFLICT(id) DO UPDATE SET
260
+ source_client = excluded.source_client,
261
+ display_name = excluded.display_name,
262
+ provider_logo = excluded.provider_logo,
263
+ state = excluded.state,
264
+ event = excluded.event,
265
+ run_id = excluded.run_id,
266
+ correlation_id = excluded.correlation_id,
267
+ initiative_id = excluded.initiative_id,
268
+ workstream_id = excluded.workstream_id,
269
+ task_id = excluded.task_id,
270
+ agent_id = excluded.agent_id,
271
+ agent_name = excluded.agent_name,
272
+ phase = excluded.phase,
273
+ progress_pct = excluded.progress_pct,
274
+ current_task = excluded.current_task,
275
+ last_heartbeat_at = excluded.last_heartbeat_at,
276
+ last_event_at = excluded.last_event_at,
277
+ last_message = excluded.last_message,
278
+ metadata_json = excluded.metadata_json,
279
+ created_at = excluded.created_at,
280
+ updated_at = excluded.updated_at`)
281
+ .run(normalized.id, normalized.sourceClient, normalized.displayName, normalized.providerLogo, normalized.state, normalized.event, normalized.runId, normalized.correlationId, normalized.initiativeId, normalized.workstreamId, normalized.taskId, normalized.agentId, normalized.agentName, normalized.phase, normalized.progressPct, normalized.currentTask, normalized.lastHeartbeatAt, normalized.lastEventAt, normalized.lastMessage, normalized.metadata ? JSON.stringify(normalized.metadata) : null, normalized.createdAt, normalized.updatedAt);
282
+ }
283
+ function pruneRuntimeStore() {
284
+ getStateDb()
285
+ .prepare(`DELETE FROM runtime_instances
286
+ WHERE id NOT IN (
287
+ SELECT id
288
+ FROM runtime_instances
289
+ ORDER BY last_event_at DESC, updated_at DESC
290
+ LIMIT ?
291
+ )`)
292
+ .run(MAX_INSTANCES);
293
+ }
294
+ function readRuntimeRows(limit) {
295
+ ensureRuntimeStoreMigrated();
296
+ const max = Math.max(1, limit ?? MAX_INSTANCES);
297
+ return getStateDb()
298
+ .prepare(`SELECT
299
+ id,
300
+ source_client,
301
+ display_name,
302
+ provider_logo,
303
+ state,
304
+ event,
305
+ run_id,
306
+ correlation_id,
307
+ initiative_id,
308
+ workstream_id,
309
+ task_id,
310
+ agent_id,
311
+ agent_name,
312
+ phase,
313
+ progress_pct,
314
+ current_task,
315
+ last_heartbeat_at,
316
+ last_event_at,
317
+ last_message,
318
+ metadata_json,
319
+ created_at,
320
+ updated_at
321
+ FROM runtime_instances
322
+ ORDER BY last_event_at DESC, updated_at DESC
323
+ LIMIT ?`)
324
+ .all(max);
325
+ }
326
+ function ensureRuntimeStoreMigrated() {
327
+ const migrated = readStateMeta(RUNTIME_IMPORT_META_KEY);
328
+ if (migrated)
329
+ return;
330
+ const db = getStateDb();
331
+ const countRow = db
332
+ .prepare("SELECT COUNT(*) AS count FROM runtime_instances")
333
+ .get();
334
+ if ((countRow?.count ?? 0) > 0) {
335
+ writeStateMeta(RUNTIME_IMPORT_META_KEY, true);
206
336
  return;
207
- values.sort((a, b) => Date.parse(b.lastEventAt) - Date.parse(a.lastEventAt));
208
- const keep = new Set(values.slice(0, MAX_INSTANCES).map((record) => record.id));
209
- for (const key of Object.keys(store.instances)) {
210
- if (!keep.has(key)) {
211
- delete store.instances[key];
212
- }
213
337
  }
338
+ const legacy = legacyReadRuntimeInstances();
339
+ const records = Object.values(legacy.instances)
340
+ .map((record) => normalizeRecord(record))
341
+ .filter((record) => Boolean(record.id));
342
+ const transaction = db.transaction((items) => {
343
+ for (const item of items) {
344
+ writeRuntimeRecord(item);
345
+ }
346
+ pruneRuntimeStore();
347
+ writeStateMeta(RUNTIME_IMPORT_META_KEY, true);
348
+ });
349
+ transaction(records);
214
350
  }
215
- function writeRuntimeInstances(next) {
216
- ensureRuntimeDir();
217
- const file = runtimeFile();
218
- writeJsonFileAtomicSync(file, next, 0o600);
351
+ export function readRuntimeInstances() {
352
+ const rows = readRuntimeRows(MAX_INSTANCES);
353
+ const instances = {};
354
+ let updatedAt = new Date(0).toISOString();
355
+ for (const row of rows) {
356
+ const record = rowToRecord(row);
357
+ instances[record.id] = record;
358
+ if (Date.parse(record.updatedAt) > Date.parse(updatedAt)) {
359
+ updatedAt = record.updatedAt;
360
+ }
361
+ }
362
+ return {
363
+ updatedAt: rows.length > 0 ? updatedAt : new Date().toISOString(),
364
+ instances,
365
+ };
219
366
  }
220
367
  export function upsertRuntimeInstanceFromHook(payload) {
368
+ ensureRuntimeStoreMigrated();
221
369
  const sourceClient = normalizeSourceClient(payload.source_client);
222
370
  const event = normalizeHookEvent(payload.event);
223
371
  const nowIso = new Date().toISOString();
@@ -240,8 +388,34 @@ export function upsertRuntimeInstanceFromHook(payload) {
240
388
  agentId,
241
389
  initiativeId,
242
390
  });
243
- const store = readRuntimeInstances();
244
- const existing = store.instances[id] ? normalizeRecord(store.instances[id]) : null;
391
+ const existingRow = getStateDb()
392
+ .prepare(`SELECT
393
+ id,
394
+ source_client,
395
+ display_name,
396
+ provider_logo,
397
+ state,
398
+ event,
399
+ run_id,
400
+ correlation_id,
401
+ initiative_id,
402
+ workstream_id,
403
+ task_id,
404
+ agent_id,
405
+ agent_name,
406
+ phase,
407
+ progress_pct,
408
+ current_task,
409
+ last_heartbeat_at,
410
+ last_event_at,
411
+ last_message,
412
+ metadata_json,
413
+ created_at,
414
+ updated_at
415
+ FROM runtime_instances
416
+ WHERE id = ?`)
417
+ .get(id);
418
+ const existing = existingRow ? rowToRecord(existingRow) : null;
245
419
  let state = existing?.state ?? "active";
246
420
  if (event === "session_stop")
247
421
  state = "stopped";
@@ -279,56 +453,36 @@ export function upsertRuntimeInstanceFromHook(payload) {
279
453
  createdAt: existing?.createdAt ?? eventAt,
280
454
  updatedAt: nowIso,
281
455
  };
282
- store.instances[id] = record;
283
- store.updatedAt = nowIso;
284
- pruneStore(store);
285
- writeRuntimeInstances(store);
456
+ const transaction = getStateDb().transaction((item) => {
457
+ writeRuntimeRecord(item);
458
+ pruneRuntimeStore();
459
+ });
460
+ transaction(record);
286
461
  return record;
287
462
  }
288
463
  export function applyRuntimeInstanceStaleness(options) {
464
+ ensureRuntimeStoreMigrated();
289
465
  const timeoutMs = Math.max(10_000, options?.timeoutMs ?? DEFAULT_RUNTIME_HEARTBEAT_TIMEOUT_MS);
290
466
  const nowMs = options?.nowMs ?? Date.now();
291
- const store = readRuntimeInstances();
292
- let changed = false;
293
- for (const [id, rawRecord] of Object.entries(store.instances)) {
294
- const record = normalizeRecord(rawRecord);
295
- if (record.state !== "active") {
296
- if (rawRecord !== record) {
297
- store.instances[id] = record;
298
- changed = true;
299
- }
300
- continue;
301
- }
302
- const heartbeatAt = record.lastHeartbeatAt ?? record.lastEventAt;
303
- const heartbeatEpoch = Date.parse(heartbeatAt);
304
- if (!Number.isFinite(heartbeatEpoch))
305
- continue;
306
- if (nowMs - heartbeatEpoch <= timeoutMs)
307
- continue;
308
- store.instances[id] = {
309
- ...record,
310
- state: "stale",
311
- updatedAt: new Date(nowMs).toISOString(),
312
- };
313
- changed = true;
314
- }
315
- if (changed) {
316
- store.updatedAt = new Date(nowMs).toISOString();
317
- writeRuntimeInstances(store);
318
- }
319
- return store;
467
+ const cutoffIso = new Date(nowMs - timeoutMs).toISOString();
468
+ const updatedAt = new Date(nowMs).toISOString();
469
+ getStateDb()
470
+ .prepare(`UPDATE runtime_instances
471
+ SET state = 'stale',
472
+ updated_at = ?
473
+ WHERE state = 'active'
474
+ AND COALESCE(last_heartbeat_at, last_event_at) < ?`)
475
+ .run(updatedAt, cutoffIso);
476
+ return readRuntimeInstances();
320
477
  }
321
478
  export function listRuntimeInstances(options) {
322
479
  const timeoutMs = options?.timeoutMs ?? DEFAULT_RUNTIME_HEARTBEAT_TIMEOUT_MS;
323
480
  const nowMs = options?.nowMs ?? Date.now();
324
- const store = applyRuntimeInstanceStaleness({ timeoutMs, nowMs });
325
- const limit = Math.max(1, options?.limit ?? MAX_INSTANCES);
326
- return Object.values(store.instances)
327
- .map((record) => normalizeRecord(record))
328
- .sort((a, b) => Date.parse(b.lastEventAt) - Date.parse(a.lastEventAt))
329
- .slice(0, limit);
481
+ void applyRuntimeInstanceStaleness({ timeoutMs, nowMs });
482
+ return readRuntimeRows(options?.limit ?? MAX_INSTANCES).map(rowToRecord);
330
483
  }
331
484
  export function clearRuntimeInstances() {
485
+ getStateDb().prepare("DELETE FROM runtime_instances").run();
332
486
  clearStoreFileSync(runtimeFile());
333
487
  }
334
488
  export function resolveRuntimeHookToken() {
@@ -0,0 +1,18 @@
1
+ export type MaterializedSnapshotEntry = {
2
+ cacheKey: string;
3
+ generation: number;
4
+ expiresAt: number;
5
+ updatedAt: string;
6
+ payload: Record<string, unknown>;
7
+ };
8
+ export declare function readMaterializedSnapshot(cacheKey: string, input?: {
9
+ allowStale?: boolean;
10
+ generation?: number;
11
+ nowMs?: number;
12
+ }): Record<string, unknown> | null;
13
+ export declare function writeMaterializedSnapshot(cacheKey: string, payload: Record<string, unknown>, input: {
14
+ generation: number;
15
+ ttlMs: number;
16
+ nowMs?: number;
17
+ }): void;
18
+ export declare function clearMaterializedSnapshotMemory(): void;
@@ -0,0 +1,91 @@
1
+ import { parseJsonSafe } from "./json-store.js";
2
+ import { getStateDb } from "./sqlite-state.js";
3
+ const MEMORY_CACHE_MAX = 160;
4
+ const memoryCache = new Map();
5
+ function setMemoryEntry(entry) {
6
+ memoryCache.set(entry.cacheKey, entry);
7
+ if (memoryCache.size <= MEMORY_CACHE_MAX)
8
+ return;
9
+ const oldestKey = memoryCache.keys().next().value;
10
+ if (oldestKey)
11
+ memoryCache.delete(oldestKey);
12
+ }
13
+ function rowToEntry(row) {
14
+ if (!row)
15
+ return null;
16
+ const payload = parseJsonSafe(row.payload_json);
17
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
18
+ return null;
19
+ return {
20
+ cacheKey: row.cache_key,
21
+ generation: Number(row.generation) || 0,
22
+ expiresAt: Number(row.expires_at) || 0,
23
+ updatedAt: row.updated_at,
24
+ payload,
25
+ };
26
+ }
27
+ export function readMaterializedSnapshot(cacheKey, input) {
28
+ const normalizedKey = cacheKey.trim();
29
+ if (!normalizedKey)
30
+ return null;
31
+ const allowStale = Boolean(input?.allowStale);
32
+ const generation = input?.generation ?? 0;
33
+ const nowMs = input?.nowMs ?? Date.now();
34
+ const cached = memoryCache.get(normalizedKey) ?? null;
35
+ if (cached) {
36
+ if (allowStale ||
37
+ (cached.generation === generation && cached.expiresAt > nowMs)) {
38
+ return cached.payload;
39
+ }
40
+ memoryCache.delete(normalizedKey);
41
+ }
42
+ const row = getStateDb()
43
+ .prepare(`SELECT cache_key, generation, expires_at, updated_at, payload_json
44
+ FROM materialized_snapshots
45
+ WHERE cache_key = ?`)
46
+ .get(normalizedKey);
47
+ const entry = rowToEntry(row);
48
+ if (!entry)
49
+ return null;
50
+ if (!allowStale && (entry.generation !== generation || entry.expiresAt <= nowMs)) {
51
+ return null;
52
+ }
53
+ setMemoryEntry(entry);
54
+ return entry.payload;
55
+ }
56
+ export function writeMaterializedSnapshot(cacheKey, payload, input) {
57
+ const normalizedKey = cacheKey.trim();
58
+ if (!normalizedKey)
59
+ return;
60
+ const nowMs = input.nowMs ?? Date.now();
61
+ const updatedAt = new Date(nowMs).toISOString();
62
+ const expiresAt = nowMs + Math.max(250, input.ttlMs);
63
+ const entry = {
64
+ cacheKey: normalizedKey,
65
+ generation: input.generation,
66
+ expiresAt,
67
+ updatedAt,
68
+ payload,
69
+ };
70
+ getStateDb()
71
+ .prepare(`INSERT INTO materialized_snapshots (
72
+ cache_key,
73
+ generation,
74
+ expires_at,
75
+ updated_at,
76
+ payload_json
77
+ ) VALUES (?, ?, ?, ?, ?)
78
+ ON CONFLICT(cache_key) DO UPDATE SET
79
+ generation = excluded.generation,
80
+ expires_at = excluded.expires_at,
81
+ updated_at = excluded.updated_at,
82
+ payload_json = excluded.payload_json`)
83
+ .run(normalizedKey, entry.generation, entry.expiresAt, entry.updatedAt, JSON.stringify(payload));
84
+ getStateDb()
85
+ .prepare("DELETE FROM materialized_snapshots WHERE expires_at <= ?")
86
+ .run(nowMs - 1);
87
+ setMemoryEntry(entry);
88
+ }
89
+ export function clearMaterializedSnapshotMemory() {
90
+ memoryCache.clear();
91
+ }
@@ -0,0 +1,6 @@
1
+ import type Database from "better-sqlite3";
2
+ export declare function closeStateDb(): void;
3
+ export declare function getStateDb(): Database.Database;
4
+ export declare function readStateMeta<T>(key: string): T | null;
5
+ export declare function writeStateMeta(key: string, value: unknown): void;
6
+ export declare function deleteStateMeta(key: string): void;