@monoes/monomindcli 2.9.19 → 2.9.20

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.
@@ -5298,71 +5298,38 @@ export async function startServer({
5298
5298
 
5299
5299
  // ── Gap-fill ordering (ADR Issue 7): rebuild activeOrgRuns BEFORE the server
5300
5300
  // starts accepting connections so the first incoming event already has runId context.
5301
- // Uses SQLite when available; falls back to JSONL scan.
5302
5301
  await _initRunDb(MONOMIND_HOME);
5302
+ // #155 follow-up: the original gap-fill scanned run_events (SQLite) / a
5303
+ // run's bus.jsonl for a terminal event — but run_events is populated by
5304
+ // LIVE forwarding while a dashboard is connected, not backfilled from a
5305
+ // run's actual history. A dashboard that starts after a run has already
5306
+ // stopped never saw most (or any) of that run's events, including its
5307
+ // terminal one, so there was nothing for the corrected #155 query to
5308
+ // match against even once the query itself was fixed. runtime.json's own
5309
+ // top-level `status` field is authoritative regardless of dashboard
5310
+ // uptime — it's the exact thing `monomind org status` reads — so check
5311
+ // that directly instead of reconstructing "done" from an event log that
5312
+ // may simply never have recorded it.
5303
5313
  try {
5304
- if (_runDb) {
5305
- // SQLite gap-fill: for each org, find latest run_id and check if it has run:complete
5306
- const _gfOrgsStmt = _runDb.prepare('SELECT DISTINCT org FROM run_events');
5307
- while (_gfOrgsStmt.step()) {
5308
- const _gfOrg = _gfOrgsStmt.getAsObject().org;
5309
- if (!_gfOrg || !/^[a-z0-9][a-z0-9_-]*$/i.test(_gfOrg)) continue;
5310
- // Resolve the latest run_id for this org, then check if it has a terminal event
5311
- const _gfLatRunStmt = _runDb.prepare(
5312
- 'SELECT run_id FROM run_events WHERE org=? ORDER BY id DESC LIMIT 1',
5313
- );
5314
- _gfLatRunStmt.bind([_gfOrg]);
5315
- let _gfLatestRun = null;
5316
- if (_gfLatRunStmt.step()) _gfLatestRun = _gfLatRunStmt.getAsObject().run_id;
5317
- _gfLatRunStmt.free();
5318
- let _gfDone = false;
5319
- if (_gfLatestRun) {
5320
- // #155: daemon.ts never emits type IN ('run:complete','org:complete',
5321
- // 'org:stop') — the real terminal signals are type:'status' events
5322
- // with msg:'org stopped' (stopOrg) or reason:'org-complete'
5323
- // (org_complete tool). Both land in the JSON-stringified `raw`
5324
- // column, not a dedicated type string, so match on that instead.
5325
- const _gfRunStmt = _runDb.prepare(
5326
- "SELECT type FROM run_events WHERE org=? AND run_id=? AND type='status' AND (raw LIKE '%\"msg\":\"org stopped\"%' OR raw LIKE '%\"reason\":\"org-complete\"%') LIMIT 1",
5327
- );
5328
- _gfRunStmt.bind([_gfOrg, _gfLatestRun]);
5329
- if (_gfRunStmt.step()) _gfDone = true;
5330
- _gfRunStmt.free();
5331
- }
5332
- if (_gfLatestRun && !_gfDone) activeOrgRuns.set(_gfOrg, _gfLatestRun);
5333
- }
5334
- _gfOrgsStmt.free();
5335
- } else {
5336
- // JSONL fallback (sql.js unavailable). #155: Org Runtime v2 never
5337
- // writes <org>/runs/*.jsonl — the actual event log per run is
5338
- // <org>/<runId>/bus.jsonl (OrgBus, bus.ts). runtime.json's `run` field
5339
- // names the current/latest run id, matching the #138 fix already
5340
- // shipped for statusline.cjs's getActiveOrgs().
5341
- const _gfOrgsDir = path.join(MONOMIND_HOME, '.monomind', 'orgs');
5342
- if (fs.existsSync(_gfOrgsDir)) {
5343
- for (const _gfOrg of fs.readdirSync(_gfOrgsDir)) {
5344
- if (!_gfOrg || _gfOrg.startsWith('.') || !/^[a-z0-9][a-z0-9_-]*$/i.test(_gfOrg)) continue;
5345
- try {
5346
- const _gfRuntimePath = path.join(_gfOrgsDir, _gfOrg, 'runtime.json');
5347
- if (!fs.existsSync(_gfRuntimePath)) continue;
5348
- const _gfRt = JSON.parse(fs.readFileSync(_gfRuntimePath, 'utf8'));
5349
- const _gfId = typeof _gfRt?.run === 'string' ? _gfRt.run : null;
5350
- if (!_gfId) continue;
5351
- const _gfBusPath = path.join(_gfOrgsDir, _gfOrg, _gfId, 'bus.jsonl');
5352
- if (!fs.existsSync(_gfBusPath)) continue;
5353
- const _gfContent = fs.readFileSync(_gfBusPath, 'utf8');
5354
- const _gfLast = _gfContent.trim().split('\n').filter(Boolean).slice(-10);
5355
- const _gfDone = _gfLast.some((l) => {
5356
- try {
5357
- const e = JSON.parse(l);
5358
- return e.type === 'status' && (e.msg === 'org stopped' || e.reason === 'org-complete');
5359
- } catch {
5360
- return false;
5361
- }
5362
- });
5363
- if (!_gfDone) activeOrgRuns.set(_gfOrg, _gfId);
5364
- } catch (_) {}
5365
- }
5314
+ const _gfOrgsDir = path.join(MONOMIND_HOME, '.monomind', 'orgs');
5315
+ if (fs.existsSync(_gfOrgsDir)) {
5316
+ for (const _gfOrg of fs.readdirSync(_gfOrgsDir)) {
5317
+ if (!_gfOrg || _gfOrg.startsWith('.') || !/^[a-z0-9][a-z0-9_-]*$/i.test(_gfOrg)) continue;
5318
+ try {
5319
+ const _gfRuntimePath = path.join(_gfOrgsDir, _gfOrg, 'runtime.json');
5320
+ if (!fs.existsSync(_gfRuntimePath)) continue;
5321
+ const _gfRt = JSON.parse(fs.readFileSync(_gfRuntimePath, 'utf8'));
5322
+ const _gfId = typeof _gfRt?.run === 'string' ? _gfRt.run : null;
5323
+ if (!_gfId) continue;
5324
+ // Mirrors org.ts's statusAction: a "running" record whose pid is
5325
+ // gone means the daemon died without its stopOrg cleanup — treat
5326
+ // that as not-active too, not just a literal status !== 'running'.
5327
+ let _gfActive = _gfRt.status === 'running';
5328
+ if (_gfActive && typeof _gfRt.pid === 'number') {
5329
+ try { process.kill(_gfRt.pid, 0); } catch { _gfActive = false; }
5330
+ }
5331
+ if (_gfActive) activeOrgRuns.set(_gfOrg, _gfId);
5332
+ } catch (_) {}
5366
5333
  }
5367
5334
  }
5368
5335
  } catch (_) {}