@theihtisham/agent-shadow-brain 7.1.3 → 7.1.4

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 (254) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +125 -90
  3. package/dist/adapters/index.d.ts +1 -1
  4. package/dist/adapters/index.d.ts.map +1 -1
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/brain/activity-journal.d.ts.map +1 -1
  7. package/dist/brain/activity-journal.js +2 -1
  8. package/dist/brain/activity-journal.js.map +1 -1
  9. package/dist/brain/auto-update.js +1 -1
  10. package/dist/brain/brain-portability.js +1 -1
  11. package/dist/brain/client-config.d.ts +19 -0
  12. package/dist/brain/client-config.d.ts.map +1 -0
  13. package/dist/brain/client-config.js +40 -0
  14. package/dist/brain/client-config.js.map +1 -0
  15. package/dist/brain/client-integrations.d.ts +2 -0
  16. package/dist/brain/client-integrations.d.ts.map +1 -1
  17. package/dist/brain/client-integrations.js +8 -27
  18. package/dist/brain/client-integrations.js.map +1 -1
  19. package/dist/brain/context-assembly.d.ts +10 -2
  20. package/dist/brain/context-assembly.d.ts.map +1 -1
  21. package/dist/brain/context-assembly.js +238 -14
  22. package/dist/brain/context-assembly.js.map +1 -1
  23. package/dist/brain/context-request.d.ts +4 -0
  24. package/dist/brain/context-request.d.ts.map +1 -0
  25. package/dist/brain/context-request.js +22 -0
  26. package/dist/brain/context-request.js.map +1 -0
  27. package/dist/brain/context-retrieval.d.ts +88 -0
  28. package/dist/brain/context-retrieval.d.ts.map +1 -0
  29. package/dist/brain/context-retrieval.js +300 -0
  30. package/dist/brain/context-retrieval.js.map +1 -0
  31. package/dist/brain/feature-settings.d.ts.map +1 -1
  32. package/dist/brain/feature-settings.js +2 -1
  33. package/dist/brain/feature-settings.js.map +1 -1
  34. package/dist/brain/file-identity.d.ts +4 -0
  35. package/dist/brain/file-identity.d.ts.map +1 -0
  36. package/dist/brain/file-identity.js +27 -0
  37. package/dist/brain/file-identity.js.map +1 -0
  38. package/dist/brain/global-brain.d.ts +11 -1
  39. package/dist/brain/global-brain.d.ts.map +1 -1
  40. package/dist/brain/global-brain.js +58 -6
  41. package/dist/brain/global-brain.js.map +1 -1
  42. package/dist/brain/mcp-server.d.ts +9 -0
  43. package/dist/brain/mcp-server.d.ts.map +1 -1
  44. package/dist/brain/mcp-server.js +237 -53
  45. package/dist/brain/mcp-server.js.map +1 -1
  46. package/dist/brain/mcp-stdio.d.ts.map +1 -1
  47. package/dist/brain/mcp-stdio.js +10 -4
  48. package/dist/brain/mcp-stdio.js.map +1 -1
  49. package/dist/brain/memory-annotations.d.ts.map +1 -1
  50. package/dist/brain/memory-annotations.js +2 -1
  51. package/dist/brain/memory-annotations.js.map +1 -1
  52. package/dist/brain/memory-inspection.d.ts +72 -0
  53. package/dist/brain/memory-inspection.d.ts.map +1 -0
  54. package/dist/brain/memory-inspection.js +131 -0
  55. package/dist/brain/memory-inspection.js.map +1 -0
  56. package/dist/brain/memory-mcp-content.d.ts +44 -0
  57. package/dist/brain/memory-mcp-content.d.ts.map +1 -0
  58. package/dist/brain/memory-mcp-content.js +91 -0
  59. package/dist/brain/memory-mcp-content.js.map +1 -0
  60. package/dist/brain/orchestrator.js +1 -1
  61. package/dist/brain/project-feedback.d.ts +63 -0
  62. package/dist/brain/project-feedback.d.ts.map +1 -0
  63. package/dist/brain/project-feedback.js +289 -0
  64. package/dist/brain/project-feedback.js.map +1 -0
  65. package/dist/brain/project-hub-contract.d.ts +326 -0
  66. package/dist/brain/project-hub-contract.d.ts.map +1 -0
  67. package/dist/brain/project-hub-contract.js +56 -0
  68. package/dist/brain/project-hub-contract.js.map +1 -0
  69. package/dist/brain/project-hub-mcp.d.ts +168 -0
  70. package/dist/brain/project-hub-mcp.d.ts.map +1 -0
  71. package/dist/brain/project-hub-mcp.js +41 -0
  72. package/dist/brain/project-hub-mcp.js.map +1 -0
  73. package/dist/brain/project-hub-pdf-worker.d.ts +13 -0
  74. package/dist/brain/project-hub-pdf-worker.d.ts.map +1 -0
  75. package/dist/brain/project-hub-pdf-worker.js +84 -0
  76. package/dist/brain/project-hub-pdf-worker.js.map +1 -0
  77. package/dist/brain/project-hub-store.d.ts +38 -0
  78. package/dist/brain/project-hub-store.d.ts.map +1 -0
  79. package/dist/brain/project-hub-store.js +189 -0
  80. package/dist/brain/project-hub-store.js.map +1 -0
  81. package/dist/brain/project-hub.d.ts +127 -0
  82. package/dist/brain/project-hub.d.ts.map +1 -0
  83. package/dist/brain/project-hub.js +381 -0
  84. package/dist/brain/project-hub.js.map +1 -0
  85. package/dist/brain/project-kit-inspection.d.ts +28 -0
  86. package/dist/brain/project-kit-inspection.d.ts.map +1 -0
  87. package/dist/brain/project-kit-inspection.js +200 -0
  88. package/dist/brain/project-kit-inspection.js.map +1 -0
  89. package/dist/brain/project-kit-paths.d.ts +13 -0
  90. package/dist/brain/project-kit-paths.d.ts.map +1 -0
  91. package/dist/brain/project-kit-paths.js +141 -0
  92. package/dist/brain/project-kit-paths.js.map +1 -0
  93. package/dist/brain/project-kit.d.ts +41 -0
  94. package/dist/brain/project-kit.d.ts.map +1 -0
  95. package/dist/brain/project-kit.js +255 -0
  96. package/dist/brain/project-kit.js.map +1 -0
  97. package/dist/brain/project-memory.d.ts +38 -0
  98. package/dist/brain/project-memory.d.ts.map +1 -0
  99. package/dist/brain/project-memory.js +76 -0
  100. package/dist/brain/project-memory.js.map +1 -0
  101. package/dist/brain/subconscious.js +2 -2
  102. package/dist/brain/subconscious.js.map +1 -1
  103. package/dist/brain/workflow-skills.d.ts.map +1 -1
  104. package/dist/brain/workflow-skills.js +3 -2
  105. package/dist/brain/workflow-skills.js.map +1 -1
  106. package/dist/cli.js +22 -3
  107. package/dist/cli.js.map +1 -1
  108. package/dist/context-cli.d.ts.map +1 -1
  109. package/dist/context-cli.js +39 -5
  110. package/dist/context-cli.js.map +1 -1
  111. package/dist/dashboard/brain-hub-actions.d.ts +2 -0
  112. package/dist/dashboard/brain-hub-actions.d.ts.map +1 -0
  113. package/dist/dashboard/brain-hub-actions.js +137 -0
  114. package/dist/dashboard/brain-hub-actions.js.map +1 -0
  115. package/dist/dashboard/brain-hub-client.d.ts +2 -0
  116. package/dist/dashboard/brain-hub-client.d.ts.map +1 -0
  117. package/dist/dashboard/brain-hub-client.js +335 -0
  118. package/dist/dashboard/brain-hub-client.js.map +1 -0
  119. package/dist/dashboard/brain-hub-graph.d.ts +2 -0
  120. package/dist/dashboard/brain-hub-graph.d.ts.map +1 -0
  121. package/dist/dashboard/brain-hub-graph.js +176 -0
  122. package/dist/dashboard/brain-hub-graph.js.map +1 -0
  123. package/dist/dashboard/brain-hub-html.d.ts +2 -0
  124. package/dist/dashboard/brain-hub-html.d.ts.map +1 -0
  125. package/dist/dashboard/brain-hub-html.js +30 -0
  126. package/dist/dashboard/brain-hub-html.js.map +1 -0
  127. package/dist/dashboard/brain-hub-project-views.d.ts +2 -0
  128. package/dist/dashboard/brain-hub-project-views.d.ts.map +1 -0
  129. package/dist/dashboard/brain-hub-project-views.js +79 -0
  130. package/dist/dashboard/brain-hub-project-views.js.map +1 -0
  131. package/dist/dashboard/brain-hub-views.d.ts +2 -0
  132. package/dist/dashboard/brain-hub-views.d.ts.map +1 -0
  133. package/dist/dashboard/brain-hub-views.js +150 -0
  134. package/dist/dashboard/brain-hub-views.js.map +1 -0
  135. package/dist/dashboard/demo-workspace.d.ts +11 -0
  136. package/dist/dashboard/demo-workspace.d.ts.map +1 -0
  137. package/dist/dashboard/demo-workspace.js +178 -0
  138. package/dist/dashboard/demo-workspace.js.map +1 -0
  139. package/dist/dashboard/live-workspace-html.d.ts.map +1 -1
  140. package/dist/dashboard/live-workspace-html.js +177 -28
  141. package/dist/dashboard/live-workspace-html.js.map +1 -1
  142. package/dist/dashboard/project-feedback-html.d.ts +5 -0
  143. package/dist/dashboard/project-feedback-html.d.ts.map +1 -0
  144. package/dist/dashboard/project-feedback-html.js +216 -0
  145. package/dist/dashboard/project-feedback-html.js.map +1 -0
  146. package/dist/dashboard/project-hub-api.d.ts +5 -0
  147. package/dist/dashboard/project-hub-api.d.ts.map +1 -0
  148. package/dist/dashboard/project-hub-api.js +121 -0
  149. package/dist/dashboard/project-hub-api.js.map +1 -0
  150. package/dist/dashboard/project-kit-html.d.ts +4 -0
  151. package/dist/dashboard/project-kit-html.d.ts.map +1 -0
  152. package/dist/dashboard/project-kit-html.js +359 -0
  153. package/dist/dashboard/project-kit-html.js.map +1 -0
  154. package/dist/dashboard/project-workspace.d.ts +16 -0
  155. package/dist/dashboard/project-workspace.d.ts.map +1 -0
  156. package/dist/dashboard/project-workspace.js +106 -0
  157. package/dist/dashboard/project-workspace.js.map +1 -0
  158. package/dist/dashboard/server.d.ts +1 -0
  159. package/dist/dashboard/server.d.ts.map +1 -1
  160. package/dist/dashboard/server.js +15 -4
  161. package/dist/dashboard/server.js.map +1 -1
  162. package/dist/dashboard/v6-dashboard-html.d.ts +1 -1
  163. package/dist/dashboard/v6-dashboard-html.d.ts.map +1 -1
  164. package/dist/dashboard/v6-dashboard-html.js +27 -27
  165. package/dist/dashboard/workspace-api.d.ts +6 -0
  166. package/dist/dashboard/workspace-api.d.ts.map +1 -1
  167. package/dist/dashboard/workspace-api.js +154 -34
  168. package/dist/dashboard/workspace-api.js.map +1 -1
  169. package/dist/dashboard/workspace-html.d.ts.map +1 -1
  170. package/dist/dashboard/workspace-html.js +487 -81
  171. package/dist/dashboard/workspace-html.js.map +1 -1
  172. package/dist/dashboard/workspace-status.d.ts +50 -0
  173. package/dist/dashboard/workspace-status.d.ts.map +1 -0
  174. package/dist/dashboard/workspace-status.js +22 -0
  175. package/dist/dashboard/workspace-status.js.map +1 -0
  176. package/dist/index.d.ts +10 -0
  177. package/dist/index.d.ts.map +1 -1
  178. package/dist/index.js +7 -0
  179. package/dist/index.js.map +1 -1
  180. package/dist/integration-cli.d.ts.map +1 -1
  181. package/dist/integration-cli.js +6 -4
  182. package/dist/integration-cli.js.map +1 -1
  183. package/dist/project-cli.d.ts +3 -0
  184. package/dist/project-cli.d.ts.map +1 -0
  185. package/dist/project-cli.js +100 -0
  186. package/dist/project-cli.js.map +1 -0
  187. package/dist/types.d.ts +1 -1
  188. package/dist/types.d.ts.map +1 -1
  189. package/docs/COMPARISON.md +22 -0
  190. package/docs/COMPONENTS.md +66 -0
  191. package/docs/CONTEXT_LAB.md +79 -0
  192. package/docs/FEATURES_AND_LIMITS.md +24 -0
  193. package/docs/INTEGRATIONS.md +68 -0
  194. package/docs/LEARNING.md +46 -0
  195. package/docs/PROJECT_KIT.md +93 -0
  196. package/docs/QUICKSTART.md +44 -0
  197. package/docs/README.md +33 -0
  198. package/docs/ROADMAP-v7.md +147 -0
  199. package/docs/SHARING.md +13 -0
  200. package/docs/SHOWCASE.md +87 -0
  201. package/docs/STATS.md +21 -0
  202. package/docs/TROUBLESHOOTING.md +42 -0
  203. package/docs/UPGRADE_PLAN_v7.0.0.md +203 -0
  204. package/docs/VIRAL-PLAYBOOK.md +259 -0
  205. package/docs/VISUAL_GUIDE_v7.1.4.md +70 -0
  206. package/docs/WORKFLOW_INTEGRATION.md +19 -1
  207. package/docs/WORKSPACE.md +17 -91
  208. package/docs/assets/7.1.4-collaboration-design-preview.png +0 -0
  209. package/docs/assets/7.1.4-comparison.png +0 -0
  210. package/docs/assets/7.1.4-context-lab.png +0 -0
  211. package/docs/assets/7.1.4-control-room-dense-preview.png +0 -0
  212. package/docs/assets/7.1.4-control-room-focus-preview.png +0 -0
  213. package/docs/assets/7.1.4-features.png +0 -0
  214. package/docs/assets/7.1.4-graph.png +0 -0
  215. package/docs/assets/7.1.4-hub-control-room.png +0 -0
  216. package/docs/assets/7.1.4-hub-empty.png +0 -0
  217. package/docs/assets/7.1.4-hub-knowledge.png +0 -0
  218. package/docs/assets/7.1.4-hub-learning.png +0 -0
  219. package/docs/assets/7.1.4-hub-mobile.png +0 -0
  220. package/docs/assets/7.1.4-hub-public-docs-source.png +0 -0
  221. package/docs/assets/7.1.4-hub-public-docs.gif +0 -0
  222. package/docs/assets/7.1.4-hub-public-docs.png +0 -0
  223. package/docs/assets/7.1.4-hub-setup.png +0 -0
  224. package/docs/assets/7.1.4-hub-walkthrough.gif +0 -0
  225. package/docs/assets/7.1.4-integrations.png +0 -0
  226. package/docs/assets/7.1.4-knowledge-design-preview.png +0 -0
  227. package/docs/assets/7.1.4-learning-design-preview.png +0 -0
  228. package/docs/assets/7.1.4-learning.png +0 -0
  229. package/docs/assets/7.1.4-live.png +0 -0
  230. package/docs/assets/7.1.4-memory-review.png +0 -0
  231. package/docs/assets/7.1.4-mobile.png +0 -0
  232. package/docs/assets/7.1.4-models-design-preview.png +0 -0
  233. package/docs/assets/7.1.4-outcome-ranking.png +0 -0
  234. package/docs/assets/7.1.4-overview.png +0 -0
  235. package/docs/assets/7.1.4-project-kit.png +0 -0
  236. package/docs/assets/7.1.4-project-skill-preview.png +0 -0
  237. package/docs/assets/7.1.4-replay.png +0 -0
  238. package/docs/assets/7.1.4-setup-design-preview.png +0 -0
  239. package/docs/assets/7.1.4-usage-design-preview.png +0 -0
  240. package/docs/assets/context-v7.1.3.png +0 -0
  241. package/docs/assets/features-v7.1.3.png +0 -0
  242. package/docs/assets/graph-v7.1.3.png +0 -0
  243. package/docs/assets/integrations-v7.1.3.png +0 -0
  244. package/docs/assets/live-v7.1.4.gif +0 -0
  245. package/docs/evaluation/CONTEXT_EVALUATION_v7.1.4.json +1818 -0
  246. package/docs/evaluation/CONTEXT_EVALUATION_v7.1.4.md +36 -0
  247. package/docs/launch/COMPONENT_MAP_v7.1.4.md +307 -0
  248. package/docs/launch/PROJECT_KIT_PLAN_v7.1.4.md +110 -0
  249. package/docs/launch/PROJECT_LEARNING_PLAN_v7.1.4.md +63 -0
  250. package/docs/launch/RELEASE_NOTES_v7.1.4.md +30 -0
  251. package/docs/launch/RESEARCH_AND_PLAN_v7.1.4.md +255 -0
  252. package/package.json +25 -7
  253. package/skills/shadow-research/SKILL.md +2 -0
  254. package/skills/shadow-workflow/SKILL.md +6 -0
@@ -0,0 +1,178 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import * as http from 'node:http';
5
+ import { GlobalBrain } from '../brain/global-brain.js';
6
+ import { FeatureSettingsStore } from '../brain/feature-settings.js';
7
+ import { memoryAnnotationsFor } from '../brain/memory-annotations.js';
8
+ import { ActivityJournal } from '../brain/activity-journal.js';
9
+ import { MCPServer } from '../brain/mcp-server.js';
10
+ import { handleWorkspaceRequest } from './workspace-api.js';
11
+ import { WORKSPACE_HTML } from './workspace-html.js';
12
+ import { LIVE_WORKSPACE_HTML } from './live-workspace-html.js';
13
+ import { BRAIN_HUB_HTML } from './brain-hub-html.js';
14
+ import { WorkspaceRuntime } from './workspace-status.js';
15
+ const projectId = 'demo-checkout';
16
+ const seeds = [
17
+ ['decision', 'Retry only idempotent checkout requests.\nA stable idempotency key keeps a retry from charging twice.', 'docs/checkout/decision.md'],
18
+ ['pattern', 'retryBudgetPolicy limits reconnect attempts.\nUse capped exponential backoff with jitter; retries share a request budget.', 'src/retryBudgetPolicy.ts'],
19
+ ['verification', 'A retry test proves one charge after a connection reset.\nThe synthetic fixture records three attempts and one committed operation.', 'tests/checkout-retry.test.ts'],
20
+ ['warning', 'Do not retry validation failures.\nOnly transient connection failures enter the retry queue.', 'docs/checkout/errors.md'],
21
+ ['lesson', 'The old retry loop reused stale request bodies.', 'docs/checkout/history.md'],
22
+ ['decision', 'A reviewed retry queue preserves the original request identity.', 'docs/checkout/replacement.md'],
23
+ ['lesson', 'A team note proposes retrying all failures.', ''],
24
+ ['research', 'Latency measurements distinguish queue wait from tool execution.', 'docs/telemetry.md'],
25
+ ['pattern', 'Cancellation releases the queue slot before admitting another request.', 'src/cancel-request.ts'],
26
+ ['decision', 'Project context remains scoped unless broader retrieval is explicitly enabled.', 'docs/context-policy.md'],
27
+ ['reference', 'Import replay uses the recorded checksum to deduplicate work.', 'src/import/replay.ts'],
28
+ ['verification', 'A bounded importer rejects duplicate events without changing prior commits.', 'tests/import/replay.test.ts'],
29
+ ['release', 'Release checks include migrations, rollback and retry regression cases.', 'docs/release.md'],
30
+ ['research', 'Java worker interop uses the stable queue protocol.', 'docs/java-worker.md'],
31
+ ['pattern', 'JavaScript UI state is separate from the Java worker protocol.', 'docs/ui-state.md'],
32
+ ['lesson', 'Review the remaining retry assumption with the team.', ''],
33
+ ['lesson', 'Review the remaining retry assumption with the team.', ''],
34
+ ['warning', 'Exception boundaries must preserve the original request identifier.', 'src/errors.ts'],
35
+ ['decision', 'A separate billing worker owns settlement reconciliation.', 'docs/billing.md', 'demo-billing'],
36
+ ['pattern', 'Billing retry limits follow the settlement queue policy.', 'src/billing/retry.ts', 'demo-billing'],
37
+ ];
38
+ /** An isolated loopback tour. Never discovers clients or opens default memory/provider configuration. */
39
+ export async function startDemoWorkspace(options = {}) {
40
+ const port = options.port ?? 0;
41
+ if (!Number.isSafeInteger(port) || port < 0 || port > 65535)
42
+ throw new TypeError('Demo port must be an integer from 0 to 65535.');
43
+ const tempRoot = fs.realpathSync(os.tmpdir());
44
+ const directory = fs.mkdtempSync(path.join(tempRoot, 'shadow-brain-demo-'));
45
+ const dbPath = path.join(directory, 'global.json');
46
+ const brain = GlobalBrain.getInstance({ dbPath, syncIntervalMs: 60000 });
47
+ if (brain.dbPath !== dbPath) {
48
+ fs.rmdirSync(directory);
49
+ throw new Error('Run demo in a separate CLI process so its memory store stays isolated.');
50
+ }
51
+ const settings = new FeatureSettingsStore({ filePath: path.join(directory, 'feature-settings.json') });
52
+ const journal = new ActivityJournal({ filePath: path.join(directory, 'activity-journal.json') });
53
+ const servers = [];
54
+ const runtime = new WorkspaceRuntime('demo');
55
+ let listener;
56
+ let timer;
57
+ let task;
58
+ let closing;
59
+ let stopped = false;
60
+ const close = () => closing ?? (closing = (async () => {
61
+ stopped = true;
62
+ if (timer)
63
+ clearTimeout(timer);
64
+ if (listener?.listening) {
65
+ listener.closeAllConnections();
66
+ await new Promise((resolve, reject) => listener.close(error => error ? reject(error) : resolve()));
67
+ }
68
+ if (task)
69
+ await task.catch(() => { });
70
+ for (const server of servers)
71
+ await server.finishMemorySession();
72
+ await brain.shutdown();
73
+ const resolved = path.resolve(directory);
74
+ const relative = path.relative(tempRoot, resolved);
75
+ if (!relative.startsWith('shadow-brain-demo-') || relative.includes(path.sep) ||
76
+ fs.lstatSync(resolved).isSymbolicLink() || fs.realpathSync(resolved) !== resolved)
77
+ throw new Error('Demo cleanup path changed; temporary files were retained.');
78
+ fs.rmSync(resolved, { recursive: true });
79
+ })());
80
+ try {
81
+ await brain.init();
82
+ const ids = seeds.map(([category, content, source, project], index) => brain.writeSync({
83
+ projectId: project ?? projectId, projectName: project ? 'Billing worker · demo' : 'Checkout API · demo',
84
+ category, content, agentTool: ['codex', 'claude-code', 'opencode'][index % 3],
85
+ importance: category === 'warning' ? 0.85 : 0.7, metadata: source ? { source } : {},
86
+ }));
87
+ await brain.sync();
88
+ const annotations = memoryAnnotationsFor(brain);
89
+ for (const [from, to, type] of [[2, 0, 'supports'], [0, 1, 'related'], [3, 6, 'contradicts'], [5, 4, 'supersedes'], [10, 11, 'supports'], [1, 12, 'related']]) {
90
+ await annotations.update({ kind: 'link', projectId, fromId: ids[from], toId: ids[to], type, note: 'Synthetic demonstration relationship.' });
91
+ }
92
+ await annotations.update({ kind: 'review', projectId, memoryId: ids[6], state: 'contested', note: 'Synthetic note awaiting evidence.' });
93
+ await settings.update({ activityRecording: true, liveActivity: true });
94
+ for (let i = 0; i < 3; i++) {
95
+ const server = new MCPServer(null, { toolProfile: 'memory', authToken: '' }, { brain, settings, projectDir: directory, projectId });
96
+ servers.push(server);
97
+ await server.handleRequest({ jsonrpc: '2.0', id: 1, method: 'initialize', params: {
98
+ protocolVersion: '2025-11-25', clientInfo: { name: 'synthetic-demo-' + i, version: '1' }, capabilities: {},
99
+ } });
100
+ }
101
+ let step = 0;
102
+ const tick = async () => {
103
+ const server = servers[step % servers.length];
104
+ const phase = ['understand', 'plan', 'work', 'verify'][Math.floor(step / servers.length) % 4];
105
+ await server.handleRequest({ jsonrpc: '2.0', id: 2, method: 'tools/call', params: {
106
+ name: 'activity_report', arguments: { kind: 'phase', phase, agent: 'demo-worker-' + (step % servers.length + 1) },
107
+ } });
108
+ const calls = [
109
+ { name: 'context_pack', arguments: { projectId, query: 'Fix checkout retryBudgetPolicy', profile: 'adaptive', tokenBudget: 1800 } },
110
+ { name: 'memory_health', arguments: { projectId, limit: 5 } },
111
+ { name: 'memory_inspect', arguments: { projectId, memoryId: ids[step % 18] } },
112
+ ];
113
+ await server.handleRequest({ jsonrpc: '2.0', id: 3, method: 'tools/call', params: calls[step % calls.length] });
114
+ step++;
115
+ };
116
+ for (let i = 0; i < 9; i++)
117
+ await tick();
118
+ listener = http.createServer((req, res) => {
119
+ const actualPort = listener.address().port;
120
+ if (!['127.0.0.1:' + actualPort, 'localhost:' + actualPort].includes(req.headers.host ?? '')) {
121
+ res.writeHead(403);
122
+ res.end('Unsupported host.');
123
+ return;
124
+ }
125
+ if (req.url?.startsWith('/api/workspace/')) {
126
+ void handleWorkspaceRequest(req, res, { brain, settings, annotations, journal, demo: true, runtime });
127
+ return;
128
+ }
129
+ if (req.method !== 'GET') {
130
+ res.writeHead(405);
131
+ res.end('Method not allowed.');
132
+ return;
133
+ }
134
+ let pathname;
135
+ try {
136
+ pathname = new URL(req.url ?? '/', 'http://localhost').pathname;
137
+ }
138
+ catch {
139
+ res.writeHead(400);
140
+ res.end('Invalid request URL.');
141
+ return;
142
+ }
143
+ if (pathname === '/favicon.ico') {
144
+ res.writeHead(204);
145
+ res.end();
146
+ return;
147
+ }
148
+ const html = pathname === '/' || pathname === '/hub' ? BRAIN_HUB_HTML : pathname === '/workspace' || pathname === '/memory-workspace' ? WORKSPACE_HTML : pathname === '/live' ? LIVE_WORKSPACE_HTML : null;
149
+ if (!html) {
150
+ res.writeHead(404);
151
+ res.end('This demo serves /workspace and /live.');
152
+ return;
153
+ }
154
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'no-referrer' });
155
+ res.end(html.replace('<html ', '<html data-demo="true" '));
156
+ });
157
+ await new Promise((resolve, reject) => {
158
+ listener.once('error', reject);
159
+ listener.listen(port, '127.0.0.1', () => { listener.off('error', reject); resolve(); });
160
+ });
161
+ if (options.activity !== false) {
162
+ const schedule = () => {
163
+ if (stopped)
164
+ return;
165
+ timer = setTimeout(() => {
166
+ task = tick().catch(() => { console.error('A synthetic demo activity cycle failed; the workspace remains available.'); }).finally(schedule);
167
+ }, 1800);
168
+ };
169
+ schedule();
170
+ }
171
+ return { url: 'http://127.0.0.1:' + listener.address().port, directory, close };
172
+ }
173
+ catch (error) {
174
+ await close();
175
+ throw error;
176
+ }
177
+ }
178
+ //# sourceMappingURL=demo-workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo-workspace.js","sourceRoot":"","sources":["../../src/dashboard/demo-workspace.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"live-workspace-html.d.ts","sourceRoot":"","sources":["../../src/dashboard/live-workspace-html.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,mBAAmB,QA03BxB,CAAC"}
1
+ {"version":3,"file":"live-workspace-html.d.ts","sourceRoot":"","sources":["../../src/dashboard/live-workspace-html.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,mBAAmB,QA+gCxB,CAAC"}
@@ -150,6 +150,36 @@ export const LIVE_WORKSPACE_HTML = String.raw `<!doctype html>
150
150
  .event-link { border: 0; background: transparent; color: var(--ink); padding: 0; text-align: left; font: inherit; cursor: pointer; }
151
151
  .event-link:hover, .event-link[aria-pressed="true"] { color: var(--cyan); text-decoration: underline; }
152
152
  .event-row .tag { justify-self: end; }
153
+ h1 { font-size: 2.25rem; }
154
+ .stats { background: #172b38; }
155
+ .stat { padding: 15px 16px; }
156
+ .stat dd { font-size: 1.55rem; }
157
+ .stat:first-child dd { color: var(--cyan); }
158
+ .network-body { background: radial-gradient(ellipse at 50% 50%, #1c3c4b, #121f2d 64%); }
159
+ .network-panel { border-color: #456679; }
160
+ .topology-ring { stroke-opacity: .44; }
161
+ .network-core .core-ring { fill: none; stroke: var(--cyan); stroke-width: 1; stroke-opacity: .36; stroke-dasharray: 2 5; }
162
+ .network-edge.failed { stroke: var(--rose); }
163
+ .network-edge.active.failed { stroke: var(--rose); }
164
+ .network-edge.active.reported { stroke: var(--amber); }
165
+ .network-node.selected-event .node-surface { stroke-width: 2.6; stroke: currentColor; }
166
+ .session-row[aria-pressed="true"] { box-shadow: inset 0 0 0 1px #526e80; }
167
+ .legend-line.failed { border-color: var(--rose); }
168
+ .replay-bar { display: grid; grid-template-columns: auto minmax(150px,1fr) auto; align-items: end; gap: 11px; margin-bottom: 15px; padding: 12px 15px; background: #192d3b; }
169
+ .replay-bar h2 { color: var(--cyan); margin-bottom: 4px; }
170
+ .replay-bar .replay-caption { grid-column: 1 / -1; color: var(--muted); }
171
+ .replay-scrub { display: grid; gap: 3px; min-width: 0; }
172
+ .replay-scrub label { display: flex; justify-content: space-between; align-items: baseline; flex-wrap: wrap; gap: 4px; color: var(--muted); }
173
+ .replay-scrub input { width: 100%; min-height: 30px; margin: 0; accent-color: var(--cyan); }
174
+ .replay-buttons { display: flex; flex-wrap: wrap; gap: 5px; }
175
+ .replay-buttons .icon-button { width: 36px; padding: 0; }
176
+ .demo-notice { display: none; }
177
+ html[data-demo="true"] .demo-notice { display: flex; gap: 7px 10px; flex-wrap: wrap; padding: 8px 12px; margin-bottom: 14px; background: var(--amber-tint); color: var(--amber); border-radius: 7px; font-size: .83rem; }
178
+ .project-journal-notice { display: none; }
179
+ html[data-project-local="true"] .project-journal-notice { display: flex; gap: 4px 10px; flex-wrap: wrap; padding: 8px 12px; margin-bottom: 14px; background: var(--cyan-tint); color: var(--cyan); border-radius: 7px; font-size: .83rem; }
180
+ .project-journal-notice a { text-decoration: underline; text-underline-offset: 3px; }
181
+ .event-row[data-selected="true"] { background: var(--cyan-tint); }
182
+ .event-row[data-selected="true"] .event-link { color: var(--cyan); }
153
183
  @keyframes arrive { from { background: var(--cyan-tint); opacity: .8; } to { background: transparent; opacity: 1; } }
154
184
  html[data-motion="reduce"] *, html[data-motion="reduce"] *::before, html[data-motion="reduce"] *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
155
185
  @media (prefers-reduced-motion: reduce) {
@@ -167,7 +197,9 @@ export const LIVE_WORKSPACE_HTML = String.raw `<!doctype html>
167
197
  .main-nav { gap: 2px; }
168
198
  .top-version { display: none; }
169
199
  .head { flex-wrap: wrap; }
200
+ h1 { font-size: 1.82rem; }
170
201
  .controls { justify-content: start; }
202
+ .replay-bar { grid-template-columns: minmax(0,1fr); }
171
203
  .stats { grid-template-columns: repeat(2,minmax(0,1fr)); }
172
204
  .stat + .stat { border-left: 0; }
173
205
  .stat:nth-child(even) { border-left: 1px solid var(--line); }
@@ -191,20 +223,34 @@ export const LIVE_WORKSPACE_HTML = String.raw `<!doctype html>
191
223
  <div class="top-inner">
192
224
  <a class="brand" href="/" aria-label="Agent Shadow Brain workspace"><span class="brand-mark" data-icon="brand"></span><span>Agent Shadow Brain</span></a>
193
225
  <nav class="main-nav" aria-label="Navigation"><a href="/">Workspace</a><a href="/live" aria-current="page">Live</a></nav>
194
- <div class="top-state"><span class="small muted mono top-version">7.1.3</span><span id="demo-tag" class="tag warn" hidden>Synthetic demo</span><span id="stream-tag" class="tag"><span class="dot"></span><span id="stream-label">Connecting</span></span></div>
226
+ <div class="top-state"><span class="small muted mono top-version">7.1.4</span><span id="demo-tag" class="tag warn" hidden>Synthetic demo</span><span id="stream-tag" class="tag"><span class="dot"></span><span id="stream-label">Connecting</span></span></div>
195
227
  </div>
196
228
  </header>
197
229
  <main id="live-main" tabindex="-1">
230
+ <div class="demo-notice"><strong>Synthetic demo</strong><span>Scripted local MCP calls use sample data. Changes affect this temporary demo only.</span></div>
231
+ <div class="project-journal-notice"><strong>Project journal</strong><span>Live activity comes from this project’s local store.</span><a href="/#project-kit">Open Project Kit</a></div>
198
232
  <div class="head">
199
233
  <div><h1>Live operations</h1><p class="muted small">Follow recorded agent sessions, tool calls, and reported task phases.</p></div>
200
234
  <div class="controls" aria-label="Live view controls">
201
235
  <label for="time-window" class="label">Window<select id="time-window"><option value="1">1 minute</option><option value="5">5 minutes</option><option value="15" selected>15 minutes</option><option value="60">1 hour</option></select></label>
202
236
  <button class="button" type="button" id="motion-control" aria-pressed="false"><span data-icon="motion"></span>Reduce motion</button>
203
237
  <button class="button" type="button" id="refresh"><span data-icon="refresh"></span>Refresh</button>
238
+ <button class="button" type="button" id="replay-start" disabled><span data-icon="replay"></span>Replay loaded events</button>
204
239
  <button class="button primary" type="button" id="pause"><span data-icon="pause"></span><span id="pause-label">Pause stream</span></button>
205
240
  </div>
206
241
  </div>
207
242
  <div class="notice" id="stream-notice" role="status">Connecting to the local event journal…</div>
243
+ <section class="panel replay-bar" id="replay-bar" aria-labelledby="replay-heading" hidden>
244
+ <div><h2 id="replay-heading" tabindex="-1">Journal replay</h2><div class="replay-buttons" aria-label="Replay controls">
245
+ <button class="button icon-button" type="button" id="replay-prev" aria-label="Previous event" title="Previous event"><span data-icon="prev"></span></button>
246
+ <button class="button" type="button" id="replay-play">Play</button>
247
+ <button class="button icon-button" type="button" id="replay-next" aria-label="Next event" title="Next event"><span data-icon="next"></span></button>
248
+ </div></div>
249
+ <div class="replay-scrub"><label for="replay-range"><span>Event position</span><output for="replay-range" class="small mono" id="replay-position" aria-live="off"></output></label>
250
+ <input type="range" min="0" max="0" value="0" id="replay-range" aria-describedby="replay-caption"></div>
251
+ <button class="button primary" type="button" id="replay-exit">Return to live</button>
252
+ <p class="small replay-caption" id="replay-caption"></p>
253
+ </section>
208
254
  <dl class="stats" id="live-stats" aria-label="Tool metrics in the selected window"></dl>
209
255
  <div class="ops-grid">
210
256
  <section class="panel">
@@ -214,7 +260,7 @@ export const LIVE_WORKSPACE_HTML = String.raw `<!doctype html>
214
260
  <section class="panel network-panel">
215
261
  <div class="panel-head"><div><h2>Activity network</h2><p class="small muted" id="network-count">Recorded session activity</p></div><span id="network-status" class="tag"></span></div>
216
262
  <div class="network-body"><svg id="network-svg" viewBox="0 0 720 405" role="group" aria-label="Activity network. Use arrow keys to navigate sessions."></svg></div>
217
- <div class="network-caption"><span><i class="legend-line"></i>Measured activity</span><span><i class="legend-line reported"></i>Host-reported activity</span></div>
263
+ <div class="network-caption"><span><i class="legend-line"></i>Measured activity</span><span><i class="legend-line reported"></i>Host-reported activity</span><span><i class="legend-line failed"></i>Error or cancellation</span></div>
218
264
  </section>
219
265
  <section class="panel inspect-panel">
220
266
  <div class="panel-head"><h2>Session details</h2></div><div id="inspection" class="inspect-body"></div>
@@ -227,6 +273,7 @@ export const LIVE_WORKSPACE_HTML = String.raw `<!doctype html>
227
273
  <section class="panel">
228
274
  <div class="panel-head"><div><h2>Event timeline</h2><p class="small muted" id="timeline-scope">All sessions</p></div><div class="controls">
229
275
  <label class="label" for="event-filter">Event source<select id="event-filter"><option value="">All sources</option><option value="measured">Measured</option><option value="reported">Reported</option></select></label>
276
+ <label class="label" for="transport-filter">Transport<select id="transport-filter"><option value="">All transports</option><option value="mcp">MCP</option><option value="cli">CLI</option><option value="host">Host</option></select></label>
230
277
  <button class="button quiet" type="button" id="clear-session" hidden>Show all sessions</button></div>
231
278
  </div><div id="events" class="events"></div>
232
279
  </section>
@@ -240,6 +287,9 @@ const glyphs = {
240
287
  refresh: '<path d="M19.5 9a8 8 0 1 0 .3 5.5M20 4v5h-5"/>',
241
288
  pause: '<path d="M9 5v14m6-14v14"/>',
242
289
  play: '<path d="m8 5 11 7-11 7z"/>',
290
+ replay: '<path d="M4 11a8 8 0 1 1 2 6M4 5v6h6m2-3v5l3 2"/>',
291
+ prev: '<path d="M6 5v14m12-13-9 6 9 6z"/>',
292
+ next: '<path d="M18 5v14M6 6l9 6-9 6z"/>',
243
293
  agent: '<rect x="4.5" y="7" width="15" height="12" rx="3"/><path d="M12 7V4m-8 9H2m20 0h-2m-11-.5h.02m5.98 0H15m-5 3h4"/>',
244
294
  activity: '<path d="M3 12h4l3-7 4 14 3-7h4"/>',
245
295
  warning: '<path d="m12 3 10 17H2L12 3zm0 6v4m0 3h.01"/>'
@@ -261,7 +311,7 @@ const motionPreference = matchMedia('(prefers-reduced-motion: reduce)');
261
311
  const state = {
262
312
  events: [], journalId: null, cursor: null, retained: 0, dropped: 0, gap: false, reset: false,
263
313
  loaded: false, paused: false, pending: false, controller: null, problem: '', checkedAt: 0, recording: null, delivery: null, demo: false,
264
- selected: '', selectedEvent: '', renderKey: '', model: null, newIds: new Set()
314
+ selected: '', selectedEvent: '', renderKey: '', model: null, newIds: new Set(), replay: null
265
315
  };
266
316
 
267
317
  document.querySelectorAll('[data-icon]').forEach((element) => { element.innerHTML = icon(element.dataset.icon); });
@@ -325,7 +375,7 @@ function eventClass(event) {
325
375
  return event.source === 'reported' ? 'reported' : 'live';
326
376
  }
327
377
 
328
- function buildModel(now, minutes) {
378
+ function buildModel(now, minutes, events = state.events) {
329
379
  const start = now - minutes * 60_000;
330
380
  const measuredStarts = new Map();
331
381
  const matched = [];
@@ -333,7 +383,7 @@ function buildModel(now, minutes) {
333
383
  const ended = new Set();
334
384
  const visible = [];
335
385
  let unmatchedEnds = 0;
336
- for (const event of state.events) {
386
+ for (const event of events) {
337
387
  const timestamp = Date.parse(event.timestamp);
338
388
  if (!Number.isFinite(timestamp) || timestamp > now) continue;
339
389
  const key = sessionKey(event);
@@ -451,7 +501,7 @@ function pointPath(a, b, control = 0) {
451
501
  }
452
502
 
453
503
  function motionAllowed() {
454
- return document.documentElement.dataset.motion !== 'reduce' && !motionPreference.matches && !state.paused && !document.hidden;
504
+ return document.documentElement.dataset.motion !== 'reduce' && !motionPreference.matches && !state.paused && !state.replay && !document.hidden;
455
505
  }
456
506
 
457
507
  function pulse(path, tone, index) {
@@ -471,8 +521,8 @@ function renderNetwork(model, animate) {
471
521
  $('network-count').textContent = displayedEvents ? number(displayedEvents) + ' recent events across ' + number(mapSessions.length) + ' displayed sessions' :
472
522
  'Recorded session activity';
473
523
  const omitted = model.sessions.length - mapSessions.length;
474
- $('network-status').textContent = omitted > 0 ? number(omitted) + ' more in Sessions' : state.paused ? 'Paused' : '';
475
- $('network-status').hidden = !omitted && !state.paused;
524
+ $('network-status').textContent = omitted > 0 ? number(omitted) + ' more in Sessions' : state.replay ? 'Replay' : state.paused ? 'Paused' : '';
525
+ $('network-status').hidden = !omitted && !state.paused && !state.replay;
476
526
  const first = state.selected || nodes[0]?.session.key;
477
527
  const primary = [];
478
528
  const links = [];
@@ -482,21 +532,21 @@ function renderNetwork(model, animate) {
482
532
  for (const { session, point, satellites: events } of nodes) {
483
533
  const selected = session.key === state.selected;
484
534
  const tone = session.pending.length ? 'live' : eventClass(session.latest);
485
- const source = session.latest.source === 'reported' ? ' reported' : '';
535
+ const source = session.latest.source === 'reported' ? ' reported' : tone === 'failed' ? ' failed' : '';
486
536
  links.push('<path class="network-edge' + source + (selected ? ' active' : state.selected ? ' dimmed' : '') +
487
537
  '" d="' + pointPath(core, point, 10) + '"/>');
488
538
  for (const item of events) {
489
539
  const event = item.event;
490
540
  const eventTone = eventClass(event);
491
541
  const path = pointPath(point, item, 4);
492
- links.push('<path class="network-edge' + (eventTone === 'reported' ? ' reported' : '') +
542
+ links.push('<path class="network-edge' + (eventTone === 'reported' ? ' reported' : eventTone === 'failed' ? ' failed' : '') +
493
543
  (event.id === state.selectedEvent ? ' active' : state.selected && !selected ? ' dimmed' : '') + '" d="' + path + '"/>');
494
544
  if (animate && state.newIds.has(event.id)) {
495
545
  pulses.push(pulse(pointPath(core, point, 10), eventTone, pulses.length));
496
546
  pulses.push(pulse(path, eventTone, pulses.length));
497
547
  }
498
548
  const eventLabel = describeEvent(event) + ' · ' + labelAgent(session.agent, session.transport);
499
- satellites.push('<g class="network-node ' + eventTone + '" data-live-event="' + esc(event.id) + '" role="button" tabindex="' +
549
+ satellites.push('<g class="network-node ' + eventTone + (event.id === state.selectedEvent ? ' selected-event' : '') + '" data-live-event="' + esc(event.id) + '" role="button" tabindex="' +
500
550
  (event.id === firstEvent ? 0 : -1) + '" aria-pressed="' + (event.id === state.selectedEvent) + '" aria-label="' + esc(eventLabel) +
501
551
  '" transform="translate(' + item.x.toFixed(1) + ' ' + item.y.toFixed(1) + ')"><title>' + esc(eventLabel) +
502
552
  '</title><circle r="8" fill="transparent"/><circle class="node-surface" r="5.5"/><circle class="node-indicator" r="2.3"/></g>');
@@ -514,7 +564,7 @@ function renderNetwork(model, animate) {
514
564
  const base = '<defs><pattern id="network-grid" width="16" height="16" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r=".8" fill="var(--grid)"/></pattern></defs>' +
515
565
  '<rect width="720" height="405" fill="url(#network-grid)"/><ellipse class="topology-ring" cx="360" cy="202.5" rx="239" ry="143"/>' +
516
566
  '<ellipse class="topology-ring" cx="360" cy="202.5" rx="202" ry="113"/><ellipse class="topology-ring" cx="360" cy="202.5" rx="131" ry="79"/>';
517
- const hub = '<g class="network-core" aria-label="Local event journal"><circle cx="360" cy="202.5" r="54"/>' +
567
+ const hub = '<g class="network-core" aria-label="Local event journal"><circle class="core-ring" cx="360" cy="202.5" r="64"/><circle cx="360" cy="202.5" r="54"/>' +
518
568
  '<circle class="core-inner" cx="360" cy="202.5" r="47"/><text text-anchor="middle" x="360" y="201">Shadow Brain</text>' +
519
569
  '<text class="node-subtitle" text-anchor="middle" x="360" y="217">Event journal</text></g>';
520
570
  svg.innerHTML = base + '<g aria-hidden="true">' + links.join('') + '</g>' + hub + satellites.join('') + primary.join('') +
@@ -547,6 +597,7 @@ function renderInspector(model) {
547
597
  fact('Source', event.source === 'reported' ? 'Host report' : 'Measured') +
548
598
  fact('Recorded at', clockText(event.timestamp)) + fact('Transport', transports[event.transport]) +
549
599
  fact('Duration', Number.isFinite(event.durationMs) ? durationText(event.durationMs) : '') +
600
+ fact('Correlation ID', event.correlationId) +
550
601
  fact('Sequence', String(event.sequence)) + '</dl>' : '';
551
602
  const projectLink = session.project ? '<a class="button quiet" href="/#memories?project=' + esc(encodeURIComponent(session.project)) +
552
603
  '">Open project memories</a>' : '';
@@ -636,21 +687,22 @@ function renderChart(id, buckets) {
636
687
  function renderEvents(model) {
637
688
  const session = model.sessions.find((item) => item.key === state.selected);
638
689
  const filter = $('event-filter').value;
639
- const data = (session ? session.events : model.visible).filter((event) => !filter || event.source === filter);
690
+ const transport = $('transport-filter').value;
691
+ const data = (session ? session.events : model.visible).filter((event) => (!filter || event.source === filter) && (!transport || event.transport === transport));
640
692
  $('timeline-scope').textContent = (session ? labelAgent(session.agent, session.transport) + ' session' : 'All sessions') +
641
693
  ' · ' + number(data.length) + ' recorded events';
642
694
  $('clear-session').hidden = !session;
643
695
  const target = $('events');
644
696
  if (!data.length) {
645
- target.innerHTML = blank('activity', 'No matching events', state.loaded ? 'Choose a different window or event source.' : 'Reading retained journal events.');
697
+ target.innerHTML = blank('activity', 'No matching events', state.loaded ? 'Choose a different window, source, or transport.' : 'Reading retained journal events.');
646
698
  return;
647
699
  }
648
700
  target.innerHTML = '<ul class="list">' + [...data].reverse().slice(0, 150).map((event) => {
649
701
  const tone = eventClass(event);
650
702
  const source = event.source === 'reported' ? 'Reported' : 'Measured';
651
703
  const extra = Number.isFinite(event.durationMs) ? ' · ' + durationText(event.durationMs) : '';
652
- const meta = [event.agent ? labelAgent(event.agent, event.transport) : transports[event.transport], event.projectId].filter(Boolean).join(' · ');
653
- return '<li class="event-row' + (state.newIds.has(event.id) ? ' new' : '') + '">' + timeMarkup(event.timestamp) +
704
+ const meta = [event.agent ? labelAgent(event.agent, event.transport) : '', transports[event.transport], event.projectId].filter(Boolean).join(' · ');
705
+ return '<li class="event-row' + (state.newIds.has(event.id) ? ' new' : '') + '" data-selected="' + (event.id === state.selectedEvent) + '">' + timeMarkup(event.timestamp) +
654
706
  '<span class="event-meta small muted">' + esc(meta) + '</span><div class="event-title"><button class="event-link" type="button" data-live-event="' +
655
707
  esc(event.id) + '" aria-pressed="' + (event.id === state.selectedEvent) + '">' + esc(describeEvent(event)) +
656
708
  '</button><span class="small muted">' + esc(extra) + '</span></div>' + tag(source, tone === 'reported' ? 'warn' : tone === 'failed' ? 'bad' : 'ok') + '</li>';
@@ -659,21 +711,27 @@ function renderEvents(model) {
659
711
 
660
712
  function renderStatus(model) {
661
713
  const badge = $('stream-tag');
662
- const label = state.problem ? 'Disconnected' : !state.loaded ? 'Connecting' : state.paused ? 'Paused' : !state.delivery ? 'Delivery off' : 'Live';
663
- badge.className = 'tag ' + (state.problem ? 'bad' : state.loaded && state.delivery && !state.paused ? 'ok' : 'warn');
714
+ const label = state.replay ? 'Replay' : state.problem ? 'Disconnected' : !state.loaded ? 'Connecting' : state.paused ? 'Paused' : !state.delivery ? 'Delivery off' : 'Live';
715
+ badge.className = 'tag ' + (state.replay ? 'warn' : state.problem ? 'bad' : state.loaded && state.delivery && !state.paused ? 'ok' : 'warn');
664
716
  badge.title = state.checkedAt ? 'Last checked ' + clockText(state.checkedAt) : '';
665
717
  $('stream-label').textContent = label;
666
- $('demo-tag').hidden = !state.demo;
718
+ const demoBanner = document.documentElement.dataset.demo === 'true';
719
+ $('demo-tag').hidden = !state.demo || demoBanner;
667
720
  $('pause-label').textContent = state.paused ? 'Resume stream' : 'Pause stream';
668
721
  $('pause').querySelector('[data-icon]').innerHTML = icon(state.paused ? 'play' : 'pause');
722
+ $('pause').hidden = !!state.replay;
669
723
  $('pause').disabled = !state.loaded || state.delivery === false;
670
- $('refresh').disabled = state.pending;
724
+ $('refresh').hidden = !!state.replay;
725
+ $('refresh').disabled = state.pending || !!state.replay;
726
+ $('replay-start').hidden = !!state.replay;
727
+ $('replay-start').disabled = !state.events.length || !!state.replay || state.delivery === false;
671
728
  const messages = [];
672
- if (state.problem) messages.push(state.problem);
729
+ if (state.replay) messages.push('Replaying a frozen snapshot of ' + number(state.replay.events.length) + ' loaded events.');
730
+ else if (state.problem) messages.push(state.problem);
673
731
  else if (!state.loaded) messages.push('Connecting to the local event journal…');
674
732
  else {
675
733
  messages.push(number(model.visible.length) + ' loaded events · Last ' + model.minutes + (model.minutes === 1 ? ' minute' : ' minutes'));
676
- if (state.demo) messages.push('Scripted local activity.');
734
+ if (state.demo && !demoBanner) messages.push('Scripted local activity.');
677
735
  if (state.recording === false) messages.push('Activity recording is off.');
678
736
  if (state.delivery === false) messages.push('Live delivery is off.');
679
737
  if (state.paused) messages.push('Stream paused.');
@@ -685,13 +743,70 @@ function renderStatus(model) {
685
743
  if (model.unmatchedEnds || model.stale) messages.push('Unmatched spans are excluded from measured result metrics.');
686
744
  }
687
745
  const notice = $('stream-notice');
688
- notice.className = 'notice' + (state.problem ? ' error' : '');
746
+ notice.className = 'notice' + (state.problem && !state.replay ? ' error' : '');
689
747
  const settings = state.loaded && (state.recording === false || state.delivery === false);
690
748
  const markup = messages.map((message) => '<span>' + esc(message) + '</span>').join('') +
691
749
  (settings ? '<a class="button quiet" href="/#features">Open Features</a>' : '');
692
750
  if (notice.innerHTML !== markup) notice.innerHTML = markup;
693
751
  }
694
752
 
753
+ function renderReplay() {
754
+ const replay = state.replay;
755
+ $('replay-bar').hidden = !replay;
756
+ if (!replay) return;
757
+ const { events, index, timer, incomplete } = replay;
758
+ const event = events[index];
759
+ const slider = $('replay-range');
760
+ slider.max = String(events.length - 1);
761
+ slider.value = String(index);
762
+ slider.disabled = events.length < 2;
763
+ const position = number(index + 1) + ' / ' + number(events.length) + ' · ' + clockText(event.timestamp);
764
+ slider.setAttribute('aria-valuetext', position + ', ' + describeEvent(event));
765
+ $('replay-position').textContent = position;
766
+ $('replay-prev').disabled = index === 0;
767
+ $('replay-next').disabled = index === events.length - 1;
768
+ $('replay-play').disabled = events.length < 2;
769
+ $('replay-play').textContent = timer ? 'Pause replay' : index === events.length - 1 ? 'Play from start' : 'Play replay';
770
+ $('replay-caption').textContent = 'Loaded sequence ' + number(events[0].sequence) + '–' + number(events.at(-1).sequence) +
771
+ ' · Playback advances two events per second. The time window ends at the selected event.' +
772
+ (incomplete ? ' Earlier events may be outside this snapshot.' : '');
773
+ }
774
+
775
+ function stopReplayPlayback() {
776
+ if (!state.replay?.timer) return;
777
+ clearInterval(state.replay.timer);
778
+ state.replay.timer = null;
779
+ }
780
+
781
+ function moveReplay(index) {
782
+ const replay = state.replay;
783
+ if (!replay || !Number.isInteger(index)) return;
784
+ replay.index = Math.max(0, Math.min(replay.events.length - 1, index));
785
+ if (replay.index === replay.events.length - 1) stopReplayPlayback();
786
+ const event = replay.events[replay.index];
787
+ state.selected = sessionKey(event);
788
+ state.selectedEvent = event.id;
789
+ render(true);
790
+ }
791
+
792
+ function startReplay() {
793
+ if (!state.events.length || state.replay || state.delivery === false) return;
794
+ state.controller?.abort();
795
+ state.replay = { events: [...state.events], index: 0, timer: null,
796
+ incomplete: state.gap || state.dropped > 0 || state.retained > state.events.length || state.events[0].sequence > 1 };
797
+ moveReplay(state.replay.events.length - 1);
798
+ $('replay-heading').focus();
799
+ }
800
+
801
+ function exitReplay() {
802
+ stopReplayPlayback();
803
+ state.replay = null;
804
+ state.paused = false;
805
+ render(true);
806
+ $('replay-start').focus();
807
+ void poll();
808
+ }
809
+
695
810
  function focusKey() {
696
811
  const active = document.activeElement;
697
812
  const value = active?.dataset.session || active?.dataset.liveEvent;
@@ -701,12 +816,15 @@ function focusKey() {
701
816
  }
702
817
 
703
818
  function render(force = false, animate = false) {
704
- const now = Math.max(state.checkedAt || Date.now(), Date.parse(state.events.at(-1)?.timestamp) || 0);
819
+ const replay = state.replay;
820
+ const replayEvent = replay?.events[replay.index];
821
+ const now = replayEvent ? Date.parse(replayEvent.timestamp) : Math.max(state.checkedAt || Date.now(), Date.parse(state.events.at(-1)?.timestamp) || 0);
705
822
  const minutes = Number($('time-window').value);
706
- const model = buildModel(now, minutes);
823
+ const model = buildModel(now, minutes, replay ? replay.events.slice(0, replay.index + 1) : state.events);
707
824
  state.model = model;
708
825
  renderStatus(model);
709
- const key = [state.loaded, state.journalId, state.cursor, Math.floor(now / 15000), minutes].join('|');
826
+ renderReplay();
827
+ const key = [state.loaded, state.journalId, state.cursor, Math.floor(now / 15000), minutes, replayEvent?.sequence].join('|');
710
828
  if (!force && key === state.renderKey) return;
711
829
  state.renderKey = key;
712
830
  if (!model.sessions.some((session) => session.key === state.selected)) state.selected = '';
@@ -781,7 +899,7 @@ function acceptPage(page, animate) {
781
899
  }
782
900
 
783
901
  async function poll(manual = false) {
784
- if (state.pending || (!manual && (state.paused || state.delivery === false || document.hidden))) return;
902
+ if (state.pending || state.replay || (!manual && (state.paused || state.delivery === false || document.hidden))) return;
785
903
  state.pending = true;
786
904
  $('refresh').disabled = true;
787
905
  const controller = new AbortController();
@@ -859,8 +977,35 @@ document.addEventListener('click', (event) => {
859
977
  $('network-svg').addEventListener('keydown', keyboardNetwork);
860
978
  $('time-window').addEventListener('change', () => render(true));
861
979
  $('event-filter').addEventListener('change', () => renderEvents(state.model));
980
+ $('transport-filter').addEventListener('change', () => renderEvents(state.model));
862
981
  $('clear-session').addEventListener('click', () => chooseSession(''));
863
982
  $('refresh').addEventListener('click', () => { void poll(true); });
983
+ $('replay-start').addEventListener('click', startReplay);
984
+ $('replay-exit').addEventListener('click', exitReplay);
985
+ $('replay-range').addEventListener('input', (event) => {
986
+ stopReplayPlayback();
987
+ moveReplay(Number(event.target.value));
988
+ });
989
+ $('replay-prev').addEventListener('click', () => {
990
+ stopReplayPlayback();
991
+ moveReplay(state.replay.index - 1);
992
+ });
993
+ $('replay-next').addEventListener('click', () => {
994
+ stopReplayPlayback();
995
+ moveReplay(state.replay.index + 1);
996
+ });
997
+ $('replay-play').addEventListener('click', () => {
998
+ const replay = state.replay;
999
+ if (!replay) return;
1000
+ if (replay.timer) {
1001
+ stopReplayPlayback();
1002
+ renderReplay();
1003
+ return;
1004
+ }
1005
+ if (replay.index === replay.events.length - 1) moveReplay(0);
1006
+ replay.timer = setInterval(() => moveReplay(replay.index + 1), 500);
1007
+ renderReplay();
1008
+ });
864
1009
  $('pause').addEventListener('click', () => {
865
1010
  state.paused = !state.paused;
866
1011
  if (state.paused) {
@@ -880,7 +1025,11 @@ $('motion-control').addEventListener('click', () => {
880
1025
  });
881
1026
  motionPreference.addEventListener('change', syncMotion);
882
1027
  document.addEventListener('visibilitychange', () => {
883
- if (document.hidden) $('network-pulses')?.replaceChildren();
1028
+ if (document.hidden) {
1029
+ $('network-pulses')?.replaceChildren();
1030
+ stopReplayPlayback();
1031
+ renderReplay();
1032
+ }
884
1033
  else void poll();
885
1034
  });
886
1035
  syncMotion();
@@ -1 +1 @@
1
- {"version":3,"file":"live-workspace-html.js","sourceRoot":"","sources":["../../src/dashboard/live-workspace-html.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA03BrC,CAAC"}
1
+ {"version":3,"file":"live-workspace-html.js","sourceRoot":"","sources":["../../src/dashboard/live-workspace-html.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA+gCrC,CAAC"}