@monoes/monomindcli 2.16.12 → 2.16.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 (217) hide show
  1. package/.claude/agents/core/coder.md +1 -1
  2. package/.claude/agents/engineering/engineering-devops-automator.md +1 -1
  3. package/.claude/agents/github/repo-architect.md +1 -1
  4. package/.claude/agents/testing/production-validator.md +1 -1
  5. package/.claude/agents/testing/testing-tool-evaluator.md +1 -1
  6. package/.claude/commands/github/pr-manager.md +1 -1
  7. package/.claude/helpers/pick-rank.cjs +34 -4
  8. package/.claude/skills/monolean-audit/SKILL.md +1 -1
  9. package/dist/src/commands/cleanup-data.d.ts +30 -0
  10. package/dist/src/commands/cleanup-data.d.ts.map +1 -0
  11. package/dist/src/commands/cleanup-data.js +138 -0
  12. package/dist/src/commands/cleanup-data.js.map +1 -0
  13. package/dist/src/commands/cleanup-scratch.d.ts +43 -0
  14. package/dist/src/commands/cleanup-scratch.d.ts.map +1 -0
  15. package/dist/src/commands/cleanup-scratch.js +125 -0
  16. package/dist/src/commands/cleanup-scratch.js.map +1 -0
  17. package/dist/src/commands/cleanup.d.ts +2 -39
  18. package/dist/src/commands/cleanup.d.ts.map +1 -1
  19. package/dist/src/commands/cleanup.js +5 -247
  20. package/dist/src/commands/cleanup.js.map +1 -1
  21. package/dist/src/commands/doctor-gitignore-checks.d.ts +10 -0
  22. package/dist/src/commands/doctor-gitignore-checks.d.ts.map +1 -0
  23. package/dist/src/commands/doctor-gitignore-checks.js +141 -0
  24. package/dist/src/commands/doctor-gitignore-checks.js.map +1 -0
  25. package/dist/src/commands/doctor-helpers-checks.d.ts +10 -0
  26. package/dist/src/commands/doctor-helpers-checks.d.ts.map +1 -0
  27. package/dist/src/commands/doctor-helpers-checks.js +292 -0
  28. package/dist/src/commands/doctor-helpers-checks.js.map +1 -0
  29. package/dist/src/commands/doctor-memory-checks.d.ts +47 -0
  30. package/dist/src/commands/doctor-memory-checks.d.ts.map +1 -0
  31. package/dist/src/commands/doctor-memory-checks.js +298 -0
  32. package/dist/src/commands/doctor-memory-checks.js.map +1 -0
  33. package/dist/src/commands/doctor-monograph-checks.d.ts +9 -0
  34. package/dist/src/commands/doctor-monograph-checks.d.ts.map +1 -0
  35. package/dist/src/commands/doctor-monograph-checks.js +331 -0
  36. package/dist/src/commands/doctor-monograph-checks.js.map +1 -0
  37. package/dist/src/commands/doctor-project-checks.d.ts +7 -66
  38. package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
  39. package/dist/src/commands/doctor-project-checks.js +10 -1331
  40. package/dist/src/commands/doctor-project-checks.js.map +1 -1
  41. package/dist/src/commands/doctor-routing-checks.d.ts +10 -0
  42. package/dist/src/commands/doctor-routing-checks.d.ts.map +1 -0
  43. package/dist/src/commands/doctor-routing-checks.js +194 -0
  44. package/dist/src/commands/doctor-routing-checks.js.map +1 -0
  45. package/dist/src/commands/doctor-worker-checks.d.ts +17 -0
  46. package/dist/src/commands/doctor-worker-checks.d.ts.map +1 -0
  47. package/dist/src/commands/doctor-worker-checks.js +122 -0
  48. package/dist/src/commands/doctor-worker-checks.js.map +1 -0
  49. package/dist/src/commands/hooks-routing-commands.js +1 -1
  50. package/dist/src/commands/hooks-routing-commands.js.map +1 -1
  51. package/dist/src/commands/init.d.ts.map +1 -1
  52. package/dist/src/commands/init.js +6 -15
  53. package/dist/src/commands/init.js.map +1 -1
  54. package/dist/src/index.js +2 -2
  55. package/dist/src/index.js.map +1 -1
  56. package/dist/src/init/never-commit.d.ts +3 -3
  57. package/dist/src/init/types.d.ts +1 -1
  58. package/dist/src/init/types.js +1 -1
  59. package/dist/src/knowledge/document-store.js +1 -1
  60. package/dist/src/knowledge/document-store.js.map +1 -1
  61. package/dist/src/mcp-tools/hooks-edit-command.d.ts +10 -0
  62. package/dist/src/mcp-tools/hooks-edit-command.d.ts.map +1 -0
  63. package/dist/src/mcp-tools/hooks-edit-command.js +247 -0
  64. package/dist/src/mcp-tools/hooks-edit-command.js.map +1 -0
  65. package/dist/src/mcp-tools/hooks-learning.d.ts +9 -0
  66. package/dist/src/mcp-tools/hooks-learning.d.ts.map +1 -0
  67. package/dist/src/mcp-tools/hooks-learning.js +408 -0
  68. package/dist/src/mcp-tools/hooks-learning.js.map +1 -0
  69. package/dist/src/mcp-tools/hooks-metrics-list.d.ts +8 -0
  70. package/dist/src/mcp-tools/hooks-metrics-list.d.ts.map +1 -0
  71. package/dist/src/mcp-tools/hooks-metrics-list.js +205 -0
  72. package/dist/src/mcp-tools/hooks-metrics-list.js.map +1 -0
  73. package/dist/src/mcp-tools/hooks-route.d.ts +12 -0
  74. package/dist/src/mcp-tools/hooks-route.d.ts.map +1 -0
  75. package/dist/src/mcp-tools/hooks-route.js +251 -0
  76. package/dist/src/mcp-tools/hooks-route.js.map +1 -0
  77. package/dist/src/mcp-tools/hooks-routing.d.ts +6 -27
  78. package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -1
  79. package/dist/src/mcp-tools/hooks-routing.js +6 -1727
  80. package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
  81. package/dist/src/mcp-tools/hooks-session.d.ts +9 -0
  82. package/dist/src/mcp-tools/hooks-session.d.ts.map +1 -0
  83. package/dist/src/mcp-tools/hooks-session.js +243 -0
  84. package/dist/src/mcp-tools/hooks-session.js.map +1 -0
  85. package/dist/src/mcp-tools/hooks-task.d.ts +17 -0
  86. package/dist/src/mcp-tools/hooks-task.d.ts.map +1 -0
  87. package/dist/src/mcp-tools/hooks-task.js +427 -0
  88. package/dist/src/mcp-tools/hooks-task.js.map +1 -0
  89. package/dist/src/mcp-tools/hooks-tools.d.ts +2 -1
  90. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  91. package/dist/src/mcp-tools/hooks-tools.js +2 -1
  92. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  93. package/dist/src/mcp-tools/memory-tools.js +1 -1
  94. package/dist/src/mcp-tools/memory-tools.js.map +1 -1
  95. package/dist/src/memory/bm25-index.d.ts +2 -2
  96. package/dist/src/memory/bm25-index.js +2 -2
  97. package/dist/src/memory/embedding-operations.js +2 -2
  98. package/dist/src/memory/embedding-operations.js.map +1 -1
  99. package/dist/src/memory/entity-name-key.d.ts +1 -1
  100. package/dist/src/memory/entity-name-key.js +1 -1
  101. package/dist/src/memory/hnsw-operations.d.ts +1 -1
  102. package/dist/src/memory/hnsw-operations.js +1 -1
  103. package/dist/src/memory/memory-bridge-backend.d.ts +43 -0
  104. package/dist/src/memory/memory-bridge-backend.d.ts.map +1 -0
  105. package/dist/src/memory/memory-bridge-backend.js +363 -0
  106. package/dist/src/memory/memory-bridge-backend.js.map +1 -0
  107. package/dist/src/memory/memory-bridge-core.d.ts +42 -0
  108. package/dist/src/memory/memory-bridge-core.d.ts.map +1 -0
  109. package/dist/src/memory/memory-bridge-core.js +126 -0
  110. package/dist/src/memory/memory-bridge-core.js.map +1 -0
  111. package/dist/src/memory/memory-bridge-entries.d.ts +131 -0
  112. package/dist/src/memory/memory-bridge-entries.d.ts.map +1 -0
  113. package/dist/src/memory/memory-bridge-entries.js +227 -0
  114. package/dist/src/memory/memory-bridge-entries.js.map +1 -0
  115. package/dist/src/memory/memory-bridge-feedback.d.ts +60 -0
  116. package/dist/src/memory/memory-bridge-feedback.d.ts.map +1 -0
  117. package/dist/src/memory/memory-bridge-feedback.js +118 -0
  118. package/dist/src/memory/memory-bridge-feedback.js.map +1 -0
  119. package/dist/src/memory/memory-bridge-paths.d.ts +123 -0
  120. package/dist/src/memory/memory-bridge-paths.d.ts.map +1 -0
  121. package/dist/src/memory/memory-bridge-paths.js +335 -0
  122. package/dist/src/memory/memory-bridge-paths.js.map +1 -0
  123. package/dist/src/memory/memory-bridge-search.d.ts +48 -0
  124. package/dist/src/memory/memory-bridge-search.d.ts.map +1 -0
  125. package/dist/src/memory/memory-bridge-search.js +383 -0
  126. package/dist/src/memory/memory-bridge-search.js.map +1 -0
  127. package/dist/src/memory/memory-bridge-sessions.d.ts +38 -0
  128. package/dist/src/memory/memory-bridge-sessions.d.ts.map +1 -0
  129. package/dist/src/memory/memory-bridge-sessions.js +109 -0
  130. package/dist/src/memory/memory-bridge-sessions.js.map +1 -0
  131. package/dist/src/memory/memory-bridge-store.d.ts +58 -0
  132. package/dist/src/memory/memory-bridge-store.d.ts.map +1 -0
  133. package/dist/src/memory/memory-bridge-store.js +201 -0
  134. package/dist/src/memory/memory-bridge-store.js.map +1 -0
  135. package/dist/src/memory/memory-bridge.d.ts +10 -438
  136. package/dist/src/memory/memory-bridge.d.ts.map +1 -1
  137. package/dist/src/memory/memory-bridge.js +14 -1787
  138. package/dist/src/memory/memory-bridge.js.map +1 -1
  139. package/dist/src/memory/memory-kg-claims.d.ts +52 -0
  140. package/dist/src/memory/memory-kg-claims.d.ts.map +1 -0
  141. package/dist/src/memory/memory-kg-claims.js +99 -0
  142. package/dist/src/memory/memory-kg-claims.js.map +1 -0
  143. package/dist/src/memory/memory-kg-index.d.ts +86 -0
  144. package/dist/src/memory/memory-kg-index.d.ts.map +1 -0
  145. package/dist/src/memory/memory-kg-index.js +256 -0
  146. package/dist/src/memory/memory-kg-index.js.map +1 -0
  147. package/dist/src/memory/memory-kg-ingest.d.ts +44 -0
  148. package/dist/src/memory/memory-kg-ingest.d.ts.map +1 -0
  149. package/dist/src/memory/memory-kg-ingest.js +361 -0
  150. package/dist/src/memory/memory-kg-ingest.js.map +1 -0
  151. package/dist/src/memory/memory-kg-model.d.ts +168 -0
  152. package/dist/src/memory/memory-kg-model.d.ts.map +1 -0
  153. package/dist/src/memory/memory-kg-model.js +158 -0
  154. package/dist/src/memory/memory-kg-model.js.map +1 -0
  155. package/dist/src/memory/memory-kg-names.d.ts +64 -0
  156. package/dist/src/memory/memory-kg-names.d.ts.map +1 -0
  157. package/dist/src/memory/memory-kg-names.js +187 -0
  158. package/dist/src/memory/memory-kg-names.js.map +1 -0
  159. package/dist/src/memory/memory-kg-promote.d.ts +79 -0
  160. package/dist/src/memory/memory-kg-promote.d.ts.map +1 -0
  161. package/dist/src/memory/memory-kg-promote.js +248 -0
  162. package/dist/src/memory/memory-kg-promote.js.map +1 -0
  163. package/dist/src/memory/memory-kg-rebuild.d.ts +77 -0
  164. package/dist/src/memory/memory-kg-rebuild.d.ts.map +1 -0
  165. package/dist/src/memory/memory-kg-rebuild.js +299 -0
  166. package/dist/src/memory/memory-kg-rebuild.js.map +1 -0
  167. package/dist/src/memory/memory-kg-rollback.d.ts +47 -0
  168. package/dist/src/memory/memory-kg-rollback.d.ts.map +1 -0
  169. package/dist/src/memory/memory-kg-rollback.js +193 -0
  170. package/dist/src/memory/memory-kg-rollback.js.map +1 -0
  171. package/dist/src/memory/memory-kg-rules.d.ts +64 -0
  172. package/dist/src/memory/memory-kg-rules.d.ts.map +1 -0
  173. package/dist/src/memory/memory-kg-rules.js +251 -0
  174. package/dist/src/memory/memory-kg-rules.js.map +1 -0
  175. package/dist/src/memory/memory-kg-scan.d.ts +118 -0
  176. package/dist/src/memory/memory-kg-scan.d.ts.map +1 -0
  177. package/dist/src/memory/memory-kg-scan.js +209 -0
  178. package/dist/src/memory/memory-kg-scan.js.map +1 -0
  179. package/dist/src/memory/memory-kg-search.d.ts +80 -0
  180. package/dist/src/memory/memory-kg-search.d.ts.map +1 -0
  181. package/dist/src/memory/memory-kg-search.js +239 -0
  182. package/dist/src/memory/memory-kg-search.js.map +1 -0
  183. package/dist/src/memory/memory-kg.d.ts +19 -547
  184. package/dist/src/memory/memory-kg.d.ts.map +1 -1
  185. package/dist/src/memory/memory-kg.js +10 -2389
  186. package/dist/src/memory/memory-kg.js.map +1 -1
  187. package/dist/src/ui/org-hil.mjs +1 -1
  188. package/dist/src/ui/routes-org-agents.mjs +318 -0
  189. package/dist/src/ui/routes-org-approvals.mjs +133 -0
  190. package/dist/src/ui/routes-org-config.mjs +459 -0
  191. package/dist/src/ui/routes-org-control.mjs +300 -0
  192. package/dist/src/ui/routes-org-files.mjs +172 -0
  193. package/dist/src/ui/routes-org-helpers.mjs +84 -0
  194. package/dist/src/ui/routes-org-knowledge.mjs +262 -0
  195. package/dist/src/ui/routes-org-lifecycle.mjs +224 -0
  196. package/dist/src/ui/routes-org-live.mjs +278 -0
  197. package/dist/src/ui/routes-org-mastermind.mjs +346 -0
  198. package/dist/src/ui/routes-org-planning.mjs +398 -0
  199. package/dist/src/ui/routes-org-runs.mjs +430 -0
  200. package/dist/src/ui/routes-org-status.mjs +342 -0
  201. package/dist/src/ui/routes-org-workflows.mjs +212 -0
  202. package/dist/src/ui/routes-org.mjs +26 -3847
  203. package/dist/src/ui/server.mjs +1 -1
  204. package/dist/src/utils/native-error.d.ts +1 -1
  205. package/dist/src/utils/native-error.js +1 -1
  206. package/dist/src/utils/paths.d.ts +2 -1
  207. package/dist/src/utils/paths.d.ts.map +1 -1
  208. package/dist/src/utils/paths.js +2 -1
  209. package/dist/src/utils/paths.js.map +1 -1
  210. package/dist/tsconfig.tsbuildinfo +1 -1
  211. package/org-skills/adversarial-reviewer/SKILL.md +1 -1
  212. package/org-skills/board-deck-builder/SKILL.md +1 -1
  213. package/org-skills/code-reviewer/SKILL.md +1 -1
  214. package/org-skills/debugging-wizard/SKILL.md +1 -1
  215. package/org-skills/scientific-thinking-literature-review/SKILL.md +1 -1
  216. package/org-skills/scientific-thinking-scholar-evaluation/SKILL.md +1 -1
  217. package/package.json +8 -8
@@ -0,0 +1,430 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ // forwarder.js is compiled from orgrt/forwarder.ts (dist/src/orgrt/forwarder.js
4
+ // sits alongside this file's own compiled copy in dist/src/ui/ after
5
+ // build; under vitest the .js specifier resolves straight to the .ts source,
6
+ // same as every other cross-reference in this codebase). translate()/
7
+ // companionEvents() are pure — no filesystem or process side effects — so
8
+ // importing them here doesn't pull in attachForwarder's spawn/heal logic.
9
+ import { companionEvents, translate } from '../orgrt/forwarder.js';
10
+
11
+ // Org dashboard routes: run list and run events.
12
+ // Registered, in order, by handleOrgRoutes in routes-org.mjs.
13
+ export async function handleOrgRunRoutes(req, res, url, corsOrigin, ctx) {
14
+ // GET /api/org/:name/runs — list structured run files for an org
15
+ if (req.method === 'GET' && /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/runs(\?.*)?$/i.test(url)) {
16
+ try {
17
+ const _rQs = new URL(req.url, 'http://localhost').searchParams;
18
+ const _rOrgName = decodeURIComponent(url.split('/')[3] || '');
19
+ if (_rOrgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(_rOrgName)) {
20
+ res.writeHead(400);
21
+ res.end('{"error":"Invalid org name"}');
22
+ return true;
23
+ }
24
+ const _rExplicitDir = _rQs.get('dir');
25
+ const _rServerRoot = path.resolve(_rExplicitDir || ctx.projectDir || process.cwd());
26
+ // Search across known projects (same logic as /api/orgs) unless explicit dir given
27
+ const _rProjDirs = new Set([_rServerRoot]);
28
+ if (!_rExplicitDir) {
29
+ try {
30
+ const _rKnown = JSON.parse(
31
+ fs.readFileSync(path.join(_rServerRoot, 'data', 'known-projects.json'), 'utf8'),
32
+ );
33
+ _rKnown.forEach((p) => _rProjDirs.add(p));
34
+ } catch (_) {}
35
+ }
36
+ const _rSeenFiles = new Set();
37
+ const runs = [];
38
+ const _parseRun = (filePath, f) => {
39
+ try {
40
+ const raw = fs.readFileSync(filePath, 'utf8');
41
+ const allLines = raw.split('\n').filter(Boolean);
42
+ const parse = (l) => {
43
+ try {
44
+ return JSON.parse(l);
45
+ } catch {
46
+ return null;
47
+ }
48
+ };
49
+ // Merge .warm.jsonl (promoted pre-complete events) for accurate event count + metadata
50
+ const warmFile = filePath.replace(/\.jsonl$/, '.warm.jsonl');
51
+ let warmLines = [];
52
+ let warmEvents = [];
53
+ try {
54
+ if (fs.existsSync(warmFile)) {
55
+ warmLines = fs.readFileSync(warmFile, 'utf8').split('\n').filter(Boolean);
56
+ warmEvents = warmLines.map(parse).filter(Boolean);
57
+ }
58
+ } catch (_) {}
59
+ const combinedLines = [...warmLines, ...allLines];
60
+ const eventCount = combinedLines.length;
61
+ const headEvents = (
62
+ warmEvents.length ? warmEvents : allLines.map(parse).filter(Boolean)
63
+ ).slice(0, 10);
64
+ const _tailEvents = allLines.map(parse).filter(Boolean).slice(-5).length
65
+ ? allLines.map(parse).filter(Boolean).slice(-5)
66
+ : warmEvents.slice(-5);
67
+ const first = headEvents.find((e) => e.type === 'run:start') || headEvents[0];
68
+ const last = [...warmEvents.slice(-5), ...allLines.map(parse).filter(Boolean).slice(-3)]
69
+ .slice()
70
+ .reverse()
71
+ .find((e) => e.type === 'run:complete' || e.type === 'org:complete');
72
+ const cycles = combinedLines.filter((l) => l.includes('"org:checkpoint"')).length;
73
+ const lastEvent =
74
+ allLines.map(parse).filter(Boolean).slice(-1)[0] || warmEvents.slice(-1)[0];
75
+ const ageMs = lastEvent?.ts ? Date.now() - lastEvent.ts : Infinity;
76
+ const isStale = !last && ageMs > 30 * 60 * 1000;
77
+ const firstBossComms = headEvents.find(
78
+ (e) => e.type === 'org:comms' && (e.from === 'boss' || e.role === 'boss') && e.msg,
79
+ );
80
+ const derivedGoal = first?.goal || firstBossComms?.msg?.slice(0, 80) || '';
81
+ return {
82
+ runId: f.replace(/\.warm\.jsonl$|\.jsonl$/, ''),
83
+ startedAt: first?.ts || 0,
84
+ endedAt: last?.ts || 0,
85
+ status: last ? 'complete' : isStale ? 'stale' : 'running',
86
+ eventCount,
87
+ cycleCount: cycles,
88
+ goal: derivedGoal,
89
+ bossRole: first?.bossRole || '',
90
+ };
91
+ } catch (_) {
92
+ return null;
93
+ }
94
+ };
95
+ // Org Runtime v2 (#238): each run's raw BusEvents live under a
96
+ // per-run directory named for the run itself — orgs/<org>/<runId>/bus.jsonl
97
+ // — never the flat orgs/<org>/runs/<runId>.jsonl layout _parseRun above
98
+ // expects. Translate through the same translate()/companionEvents()
99
+ // forwarder.ts uses for the live view, so historical and live event
100
+ // shapes always match.
101
+ const _parseV2RunDir = (orgDir, runId) => {
102
+ try {
103
+ const busFile = path.join(orgDir, runId, 'bus.jsonl');
104
+ if (!fs.existsSync(busFile)) return null;
105
+ const raw = fs
106
+ .readFileSync(busFile, 'utf8')
107
+ .split('\n')
108
+ .filter(Boolean)
109
+ .map((l) => {
110
+ try {
111
+ return JSON.parse(l);
112
+ } catch {
113
+ return null;
114
+ }
115
+ })
116
+ .filter(Boolean);
117
+ if (!raw.length) return null;
118
+ const translated = raw.flatMap((e) => [...companionEvents(e), translate(e)]);
119
+ const done = translated.find(
120
+ (e) => e.type === 'session:complete' || e.type === 'org:complete',
121
+ );
122
+ const start = translated.find((e) => e.type === 'org:start');
123
+ const firstComms = translated.find((e) => e.type === 'org:comms' && e.msg);
124
+ return {
125
+ runId,
126
+ startedAt: raw[0]?.ts || 0,
127
+ endedAt: done?.ts || 0,
128
+ status: done ? 'complete' : 'running',
129
+ eventCount: raw.length,
130
+ cycleCount: translated.filter((e) => e.type === 'org:checkpoint').length,
131
+ goal: start?.goal || firstComms?.msg?.slice(0, 80) || '',
132
+ bossRole: '',
133
+ };
134
+ } catch (_) {
135
+ return null;
136
+ }
137
+ };
138
+ for (const _rpd of _rProjDirs) {
139
+ // Check both .monomind and .git/monomind locations
140
+ const _rMonoDir = ctx._getGitMonomindDir(_rpd) || path.join(_rpd, '.monomind');
141
+ const _rOrgDirs = [path.join(_rMonoDir, 'orgs', _rOrgName)];
142
+ if (_rMonoDir !== path.join(_rpd, '.monomind'))
143
+ _rOrgDirs.push(path.join(_rpd, '.monomind', 'orgs', _rOrgName));
144
+ for (const _rOrgDir of _rOrgDirs) {
145
+ if (!fs.existsSync(_rOrgDir)) continue;
146
+ const runDirs = fs
147
+ .readdirSync(_rOrgDir)
148
+ .filter(
149
+ (d) => d.startsWith('run-') && fs.statSync(path.join(_rOrgDir, d)).isDirectory(),
150
+ )
151
+ .sort()
152
+ .reverse();
153
+ for (const runId of runDirs.slice(0, 50)) {
154
+ if (_rSeenFiles.has(runId)) continue;
155
+ const r = _parseV2RunDir(_rOrgDir, runId);
156
+ if (!r) continue;
157
+ _rSeenFiles.add(runId);
158
+ runs.push(r);
159
+ }
160
+ }
161
+ const _rSearchDirs = [path.join(_rMonoDir, 'orgs', _rOrgName, 'runs')];
162
+ if (_rMonoDir !== path.join(_rpd, '.monomind'))
163
+ _rSearchDirs.push(path.join(_rpd, '.monomind', 'orgs', _rOrgName, 'runs'));
164
+ for (const _rDir of _rSearchDirs) {
165
+ if (!fs.existsSync(_rDir)) continue;
166
+ // Include .warm.jsonl — completed runs are renamed hot→warm on org:complete and
167
+ // must stay visible in the chat history dropdown.
168
+ const files = fs
169
+ .readdirSync(_rDir)
170
+ .filter(
171
+ (f) =>
172
+ f.endsWith('.jsonl') &&
173
+ !f.startsWith('._') &&
174
+ !f.endsWith('.convs.jsonl') &&
175
+ !f.endsWith('.cold.jsonl'),
176
+ )
177
+ .sort()
178
+ .reverse();
179
+ for (const f of files.slice(0, 50)) {
180
+ const _rFileId = f.replace(/\.warm\.jsonl$|\.jsonl$/, '');
181
+ if (_rSeenFiles.has(_rFileId)) continue;
182
+ _rSeenFiles.add(_rFileId);
183
+ const r = _parseRun(path.join(_rDir, f), f);
184
+ if (r) runs.push(r);
185
+ }
186
+ }
187
+ }
188
+ runs.sort((a, b) => (b.startedAt || 0) - (a.startedAt || 0));
189
+ // Threads fallback: if no run files found, synthesize a run from the org threads file
190
+ // so orgs whose boss never emitted runId-tagged events still show in the chat dropdown.
191
+ if (runs.length === 0) {
192
+ for (const _rpd of _rProjDirs) {
193
+ const _tf = path.join(_rpd, '.monomind', 'orgs', `${_rOrgName}-threads.jsonl`);
194
+ if (!fs.existsSync(_tf)) continue;
195
+ const _tLines = fs.readFileSync(_tf, 'utf8').split('\n').filter(Boolean);
196
+ if (!_tLines.length) continue;
197
+ const _tEvs = _tLines
198
+ .map((l) => {
199
+ try {
200
+ return JSON.parse(l);
201
+ } catch {
202
+ return null;
203
+ }
204
+ })
205
+ .filter(Boolean);
206
+ if (!_tEvs.length) continue;
207
+ const _firstTs = _tEvs[0].ts
208
+ ? typeof _tEvs[0].ts === 'number'
209
+ ? _tEvs[0].ts
210
+ : new Date(_tEvs[0].ts).getTime()
211
+ : Date.now();
212
+ const _lastTs = _tEvs[_tEvs.length - 1].ts
213
+ ? typeof _tEvs[_tEvs.length - 1].ts === 'number'
214
+ ? _tEvs[_tEvs.length - 1].ts
215
+ : new Date(_tEvs[_tEvs.length - 1].ts).getTime()
216
+ : _firstTs;
217
+ runs.push({
218
+ id: `threads-${_rOrgName}`,
219
+ orgName: _rOrgName,
220
+ goal: `${_rOrgName} threads`,
221
+ status: 'complete',
222
+ startedAt: _firstTs,
223
+ endedAt: _lastTs,
224
+ eventCount: _tEvs.length,
225
+ _threadsFile: _tf,
226
+ });
227
+ break;
228
+ }
229
+ }
230
+ res.writeHead(200, {
231
+ 'Content-Type': 'application/json',
232
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
233
+ 'Cache-Control': 'no-cache',
234
+ });
235
+ res.end(JSON.stringify(runs));
236
+ } catch (_) {
237
+ res.writeHead(500);
238
+ res.end('[]');
239
+ }
240
+ return true;
241
+ }
242
+
243
+ // GET /api/org/:name/runs/:runId — get all events for a specific run
244
+ if (
245
+ req.method === 'GET' &&
246
+ /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/runs\/[a-z0-9][a-z0-9_-]{0,79}(\?.*)?$/i.test(url)
247
+ ) {
248
+ try {
249
+ const _rvQs = new URL(req.url, 'http://localhost').searchParams;
250
+ const _rvParts = url.replace(/\?.*$/, '').split('/');
251
+ const _rvOrgName = decodeURIComponent(_rvParts[3] || '');
252
+ const _rvRunId = decodeURIComponent(_rvParts[5] || '');
253
+ if (
254
+ _rvOrgName.length > 64 ||
255
+ !/^[a-z0-9][a-z0-9_-]*$/i.test(_rvOrgName) ||
256
+ _rvRunId.length > 80 ||
257
+ !/^[a-z0-9][a-z0-9_-]*$/i.test(_rvRunId)
258
+ ) {
259
+ res.writeHead(400);
260
+ res.end('{"error":"Invalid org or run id"}');
261
+ return true;
262
+ }
263
+ const _rvExplicitDir = _rvQs.get('dir');
264
+ const _rvServerRoot = path.resolve(_rvExplicitDir || ctx.projectDir || process.cwd());
265
+ // Threads fallback: threads-${orgName} is a synthetic runId served from threads file
266
+ if (_rvRunId === `threads-${_rvOrgName}`) {
267
+ const _rvProjDirsT = new Set([_rvServerRoot]);
268
+ if (!_rvExplicitDir) {
269
+ try {
270
+ JSON.parse(
271
+ fs.readFileSync(path.join(_rvServerRoot, 'data', 'known-projects.json'), 'utf8'),
272
+ ).forEach((p) => _rvProjDirsT.add(p));
273
+ } catch (_) {}
274
+ }
275
+ for (const _rvpd of _rvProjDirsT) {
276
+ const _tf = path.join(_rvpd, '.monomind', 'orgs', `${_rvOrgName}-threads.jsonl`);
277
+ if (!fs.existsSync(_tf)) continue;
278
+ const _tLines = fs.readFileSync(_tf, 'utf8').split('\n').filter(Boolean);
279
+ const _tEvs = _tLines
280
+ .map((l) => {
281
+ try {
282
+ const e = JSON.parse(l);
283
+ return {
284
+ type: 'org:comms',
285
+ from: e.role || e.from || 'agent',
286
+ to: e.to || 'all',
287
+ msg: e.message || e.msg || '',
288
+ ts: e.ts
289
+ ? typeof e.ts === 'number'
290
+ ? e.ts
291
+ : new Date(e.ts).getTime()
292
+ : Date.now(),
293
+ org: _rvOrgName,
294
+ runId: _rvRunId,
295
+ };
296
+ } catch {
297
+ return null;
298
+ }
299
+ })
300
+ .filter(Boolean);
301
+ res.writeHead(200, {
302
+ 'Content-Type': 'application/json',
303
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
304
+ });
305
+ res.end(JSON.stringify(_tEvs));
306
+ return true;
307
+ }
308
+ res.writeHead(404);
309
+ res.end('{"error":"threads file not found"}');
310
+ return true;
311
+ }
312
+ // Search across known projects
313
+ const _rvProjDirs = new Set([_rvServerRoot]);
314
+ if (!_rvExplicitDir) {
315
+ try {
316
+ JSON.parse(
317
+ fs.readFileSync(path.join(_rvServerRoot, 'data', 'known-projects.json'), 'utf8'),
318
+ ).forEach((p) => _rvProjDirs.add(p));
319
+ } catch (_) {}
320
+ }
321
+ // Org Runtime v2 (#238): try the per-run-directory bus.jsonl layout
322
+ // first — orgs/<org>/<runId>/bus.jsonl — translated to the same
323
+ // dashboard vocabulary the live view gets from forwarder.ts, before
324
+ // falling back to the v1 flat orgs/<org>/runs/<runId>.jsonl lookup
325
+ // below (v1 files are already stored pre-translated).
326
+ for (const _rvpd of _rvProjDirs) {
327
+ const _rvMonoDir = ctx._getGitMonomindDir(_rvpd) || path.join(_rvpd, '.monomind');
328
+ const _v2Candidates = [path.join(_rvMonoDir, 'orgs', _rvOrgName, _rvRunId, 'bus.jsonl')];
329
+ if (_rvMonoDir !== path.join(_rvpd, '.monomind'))
330
+ _v2Candidates.push(
331
+ path.join(_rvpd, '.monomind', 'orgs', _rvOrgName, _rvRunId, 'bus.jsonl'),
332
+ );
333
+ for (const _v2File of _v2Candidates) {
334
+ if (!fs.existsSync(_v2File)) continue;
335
+ const raw = fs
336
+ .readFileSync(_v2File, 'utf8')
337
+ .split('\n')
338
+ .filter(Boolean)
339
+ .map((l) => {
340
+ try {
341
+ return JSON.parse(l);
342
+ } catch {
343
+ return null;
344
+ }
345
+ })
346
+ .filter(Boolean);
347
+ const events = raw.flatMap((e) => [...companionEvents(e), translate(e)]);
348
+ events.sort((a, b) => (a.ts || 0) - (b.ts || 0));
349
+ res.writeHead(200, {
350
+ 'Content-Type': 'application/json',
351
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
352
+ 'Cache-Control': 'no-cache',
353
+ });
354
+ res.end(JSON.stringify(events));
355
+ return true;
356
+ }
357
+ }
358
+ let _rvFile = null;
359
+ for (const _rvpd of _rvProjDirs) {
360
+ const _rvMonoDir = ctx._getGitMonomindDir(_rvpd) || path.join(_rvpd, '.monomind');
361
+ const _candidates = [
362
+ path.join(_rvMonoDir, 'orgs', _rvOrgName, 'runs', `${_rvRunId}.jsonl`),
363
+ path.join(_rvMonoDir, 'orgs', _rvOrgName, 'runs', `${_rvRunId}.warm.jsonl`),
364
+ ];
365
+ if (_rvMonoDir !== path.join(_rvpd, '.monomind')) {
366
+ _candidates.push(
367
+ path.join(_rvpd, '.monomind', 'orgs', _rvOrgName, 'runs', `${_rvRunId}.jsonl`),
368
+ );
369
+ _candidates.push(
370
+ path.join(_rvpd, '.monomind', 'orgs', _rvOrgName, 'runs', `${_rvRunId}.warm.jsonl`),
371
+ );
372
+ }
373
+ for (const c of _candidates) {
374
+ if (fs.existsSync(c)) {
375
+ _rvFile = c;
376
+ break;
377
+ }
378
+ }
379
+ if (_rvFile) break;
380
+ }
381
+ if (!_rvFile) {
382
+ res.writeHead(404);
383
+ res.end('{"error":"run not found"}');
384
+ return true;
385
+ }
386
+ const _parseLines = (p) => {
387
+ try {
388
+ return fs
389
+ .readFileSync(p, 'utf8')
390
+ .split('\n')
391
+ .filter(Boolean)
392
+ .map((l) => {
393
+ try {
394
+ return JSON.parse(l);
395
+ } catch {
396
+ return null;
397
+ }
398
+ })
399
+ .filter(Boolean);
400
+ } catch {
401
+ return [];
402
+ }
403
+ };
404
+ const events = _parseLines(_rvFile);
405
+ // Merge .warm.jsonl (pre-run:complete events, including org:comms) if it exists.
406
+ // When run:complete fires, the hot .jsonl is renamed to .warm.jsonl so all pre-complete
407
+ // events live there. The current .jsonl then only holds post-complete events (e.g. org:stop).
408
+ const _rvWarmFile = _rvFile.endsWith('.warm.jsonl')
409
+ ? _rvFile
410
+ : _rvFile.replace(/\.jsonl$/, '.warm.jsonl');
411
+ if (_rvWarmFile !== _rvFile && fs.existsSync(_rvWarmFile)) {
412
+ events.push(..._parseLines(_rvWarmFile));
413
+ }
414
+ // For in-progress runs (no .warm.jsonl), org:comms also go to .convs.jsonl (stripped form).
415
+ // They're already in .jsonl as full events, so .convs.jsonl would duplicate — skip it.
416
+ events.sort((a, b) => (a.ts || 0) - (b.ts || 0));
417
+ res.writeHead(200, {
418
+ 'Content-Type': 'application/json',
419
+ ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
420
+ 'Cache-Control': 'no-cache',
421
+ });
422
+ res.end(JSON.stringify(events));
423
+ } catch (_) {
424
+ res.writeHead(500);
425
+ res.end('[]');
426
+ }
427
+ return true;
428
+ }
429
+ return false;
430
+ }