@jungjaehoon/mama-os 0.32.3 → 0.34.0

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 (105) hide show
  1. package/README.md +15 -17
  2. package/dist/agent/os-agent-capabilities.md +2 -2
  3. package/dist/api/graph-api.d.ts +4 -4
  4. package/dist/api/graph-api.d.ts.map +1 -1
  5. package/dist/api/graph-api.js +47 -78
  6. package/dist/api/graph-api.js.map +1 -1
  7. package/dist/api/index.d.ts +10 -0
  8. package/dist/api/index.d.ts.map +1 -1
  9. package/dist/api/index.js +9 -2
  10. package/dist/api/index.js.map +1 -1
  11. package/dist/api/operator-handler.d.ts +1 -1
  12. package/dist/api/operator-handler.js +1 -1
  13. package/dist/api/runtime-status-handler.d.ts +41 -0
  14. package/dist/api/runtime-status-handler.d.ts.map +1 -0
  15. package/dist/api/runtime-status-handler.js +55 -0
  16. package/dist/api/runtime-status-handler.js.map +1 -0
  17. package/dist/cli/commands/start.d.ts.map +1 -1
  18. package/dist/cli/commands/start.js +58 -23
  19. package/dist/cli/commands/start.js.map +1 -1
  20. package/dist/cli/runtime/api-routes-init.d.ts +2 -0
  21. package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
  22. package/dist/cli/runtime/api-routes-init.js +62 -1
  23. package/dist/cli/runtime/api-routes-init.js.map +1 -1
  24. package/dist/cli/runtime/api-server-init.d.ts +12 -1
  25. package/dist/cli/runtime/api-server-init.d.ts.map +1 -1
  26. package/dist/cli/runtime/api-server-init.js +54 -0
  27. package/dist/cli/runtime/api-server-init.js.map +1 -1
  28. package/dist/cli/runtime/utilities.d.ts +1 -1
  29. package/dist/cli/runtime/utilities.d.ts.map +1 -1
  30. package/dist/cli/runtime/utilities.js +3 -1
  31. package/dist/cli/runtime/utilities.js.map +1 -1
  32. package/dist/gateways/message-router.d.ts +4 -0
  33. package/dist/gateways/message-router.d.ts.map +1 -1
  34. package/dist/gateways/message-router.js +62 -13
  35. package/dist/gateways/message-router.js.map +1 -1
  36. package/dist/gateways/owner-report-inbox.d.ts +40 -0
  37. package/dist/gateways/owner-report-inbox.d.ts.map +1 -0
  38. package/dist/gateways/owner-report-inbox.js +128 -0
  39. package/dist/gateways/owner-report-inbox.js.map +1 -0
  40. package/dist/gateways/report-projection-v1.d.ts +41 -0
  41. package/dist/gateways/report-projection-v1.d.ts.map +1 -0
  42. package/dist/gateways/report-projection-v1.js +160 -0
  43. package/dist/gateways/report-projection-v1.js.map +1 -0
  44. package/dist/gateways/session-store.d.ts +22 -0
  45. package/dist/gateways/session-store.d.ts.map +1 -1
  46. package/dist/gateways/session-store.js +76 -3
  47. package/dist/gateways/session-store.js.map +1 -1
  48. package/dist/gateways/telegram-message-ledger.d.ts +10 -0
  49. package/dist/gateways/telegram-message-ledger.d.ts.map +1 -1
  50. package/dist/gateways/telegram-message-ledger.js +23 -2
  51. package/dist/gateways/telegram-message-ledger.js.map +1 -1
  52. package/dist/gateways/telegram-report-context-store.d.ts +193 -0
  53. package/dist/gateways/telegram-report-context-store.d.ts.map +1 -0
  54. package/dist/gateways/telegram-report-context-store.js +612 -0
  55. package/dist/gateways/telegram-report-context-store.js.map +1 -0
  56. package/dist/gateways/telegram.d.ts +9 -0
  57. package/dist/gateways/telegram.d.ts.map +1 -1
  58. package/dist/gateways/telegram.js +100 -0
  59. package/dist/gateways/telegram.js.map +1 -1
  60. package/dist/gateways/types.d.ts +12 -0
  61. package/dist/gateways/types.d.ts.map +1 -1
  62. package/dist/multi-agent/dashboard-agent-persona.js +2 -2
  63. package/dist/operator/board-slot-instructions.d.ts +1 -1
  64. package/dist/operator/board-slot-instructions.js +1 -1
  65. package/dist/operator/operator-trigger-loop.d.ts +17 -1
  66. package/dist/operator/operator-trigger-loop.d.ts.map +1 -1
  67. package/dist/operator/operator-trigger-loop.js +87 -23
  68. package/dist/operator/operator-trigger-loop.js.map +1 -1
  69. package/dist/operator/report-delivery-coordinator.d.ts +82 -0
  70. package/dist/operator/report-delivery-coordinator.d.ts.map +1 -0
  71. package/dist/operator/report-delivery-coordinator.js +123 -0
  72. package/dist/operator/report-delivery-coordinator.js.map +1 -0
  73. package/dist/operator/situation-report.d.ts +7 -0
  74. package/dist/operator/situation-report.d.ts.map +1 -1
  75. package/dist/operator/situation-report.js +12 -0
  76. package/dist/operator/situation-report.js.map +1 -1
  77. package/package.json +1 -2
  78. package/public/viewer/js/modules/system.js +183 -0
  79. package/public/viewer/js/utils/ui-commands.js +4 -9
  80. package/public/viewer/log-viewer.html +2 -192
  81. package/public/viewer/operator/operator.css +1 -0
  82. package/public/viewer/operator/operator.js +14830 -0
  83. package/public/viewer/src/modules/system.ts +242 -0
  84. package/public/viewer/src/utils/ui-commands.ts +4 -8
  85. package/public/viewer/sw.js +44 -10
  86. package/public/viewer/viewer.css +338 -331
  87. package/public/viewer/viewer.html +388 -1472
  88. package/public/ui/assets/index-BDXDGEV6.js +0 -62
  89. package/public/ui/assets/index-po2tN0xq.css +0 -1
  90. package/public/ui/index.html +0 -14
  91. package/public/ui/theme-init.js +0 -20
  92. package/public/viewer/js/modules/agents.js +0 -1153
  93. package/public/viewer/js/modules/chat.js +0 -1993
  94. package/public/viewer/js/modules/connector-feed.js +0 -352
  95. package/public/viewer/js/modules/dashboard.js +0 -456
  96. package/public/viewer/js/modules/entity-audit.js +0 -106
  97. package/public/viewer/js/modules/entity-review.js +0 -66
  98. package/public/viewer/js/modules/settings.js +0 -1257
  99. package/public/viewer/src/modules/agents.ts +0 -1302
  100. package/public/viewer/src/modules/chat.ts +0 -2426
  101. package/public/viewer/src/modules/connector-feed.ts +0 -391
  102. package/public/viewer/src/modules/dashboard.ts +0 -498
  103. package/public/viewer/src/modules/entity-audit.ts +0 -154
  104. package/public/viewer/src/modules/entity-review.ts +0 -175
  105. package/public/viewer/src/modules/settings.ts +0 -1547
@@ -0,0 +1,242 @@
1
+ /**
2
+ * System views: Runtime and Connectors.
3
+ * @module modules/system
4
+ *
5
+ * Read-only truth, one source. Both views render `GET /api/runtime/status` and
6
+ * nothing else: no config read, no client-side model registry, no credential
7
+ * ever reaching the DOM. Editing daemon configuration from the browser is not a
8
+ * capability of this surface; setup is a CLI action.
9
+ */
10
+
11
+ /* eslint-env browser */
12
+
13
+ const RUNTIME_STATUS_ENDPOINT = '/api/runtime/status';
14
+
15
+ /** Shown when the daemon booted without a health service. Never a fake score. */
16
+ const HEALTH_UNAVAILABLE = 'unavailable';
17
+
18
+ export type RuntimeBackend = 'claude' | 'codex' | 'cline';
19
+ export type RuntimeConnectorState = 'connected' | 'disconnected' | 'unknown';
20
+
21
+ export interface RuntimeConnectorStatus {
22
+ name: string;
23
+ enabled: boolean;
24
+ state: RuntimeConnectorState;
25
+ }
26
+
27
+ export interface RuntimeStatusSnapshot {
28
+ running: boolean;
29
+ backend: RuntimeBackend;
30
+ model: string;
31
+ startedAt: number;
32
+ health: { score: number; status: string } | null;
33
+ connectors: RuntimeConnectorStatus[];
34
+ }
35
+
36
+ export interface SystemViewOptions {
37
+ /** Test seam; defaults to fetching the authoritative endpoint. */
38
+ fetchStatus?: () => Promise<RuntimeStatusSnapshot>;
39
+ }
40
+
41
+ export function escapeHtml(value: unknown): string {
42
+ return String(value ?? '')
43
+ .replace(/&/g, '&amp;')
44
+ .replace(/</g, '&lt;')
45
+ .replace(/>/g, '&gt;')
46
+ .replace(/"/g, '&quot;')
47
+ .replace(/'/g, '&#39;');
48
+ }
49
+
50
+ async function defaultFetchStatus(): Promise<RuntimeStatusSnapshot> {
51
+ const response = await fetch(RUNTIME_STATUS_ENDPOINT);
52
+ if (!response.ok) {
53
+ throw new Error('HTTP ' + response.status);
54
+ }
55
+ return (await response.json()) as RuntimeStatusSnapshot;
56
+ }
57
+
58
+ function skeleton(rows: number): string {
59
+ let html = '<div class="mama-system-skeleton" aria-busy="true" aria-live="polite">';
60
+ for (let i = 0; i < rows; i++) {
61
+ html += '<div class="mama-system-skeleton-row"></div>';
62
+ }
63
+ html += '</div>';
64
+ return html;
65
+ }
66
+
67
+ /**
68
+ * Failure names the endpoint that failed and offers a retry. A System view that
69
+ * silently shows nothing is indistinguishable from a healthy empty runtime.
70
+ */
71
+ function errorCard(error: unknown): string {
72
+ const detail = error instanceof Error ? error.message : String(error ?? 'Unknown error');
73
+ return (
74
+ '<div class="mama-system-error" role="alert">' +
75
+ '<p class="mama-system-error-title">Could not read ' +
76
+ escapeHtml(RUNTIME_STATUS_ENDPOINT) +
77
+ '</p>' +
78
+ '<p class="mama-system-error-detail">' +
79
+ escapeHtml(detail) +
80
+ '</p>' +
81
+ '<button type="button" class="mama-system-retry" data-retry>Retry</button>' +
82
+ '</div>'
83
+ );
84
+ }
85
+
86
+ function formatStartedAt(startedAt: number): string {
87
+ if (!Number.isFinite(startedAt) || startedAt <= 0) return 'unknown';
88
+ return new Date(startedAt).toLocaleString();
89
+ }
90
+
91
+ function formatUptime(startedAt: number): string {
92
+ if (!Number.isFinite(startedAt) || startedAt <= 0) return 'unknown';
93
+ const seconds = Math.max(0, Math.floor((Date.now() - startedAt) / 1000));
94
+ const days = Math.floor(seconds / 86400);
95
+ const hours = Math.floor((seconds % 86400) / 3600);
96
+ const minutes = Math.floor((seconds % 3600) / 60);
97
+ if (days > 0) return days + 'd ' + hours + 'h';
98
+ if (hours > 0) return hours + 'h ' + minutes + 'm';
99
+ return minutes + 'm';
100
+ }
101
+
102
+ function statTile(label: string, value: string, tone = 'neutral'): string {
103
+ return (
104
+ '<div class="mama-system-tile" data-tone="' +
105
+ escapeHtml(tone) +
106
+ '">' +
107
+ '<span class="mama-system-tile-label">' +
108
+ escapeHtml(label) +
109
+ '</span>' +
110
+ '<span class="mama-system-tile-value">' +
111
+ escapeHtml(value) +
112
+ '</span>' +
113
+ '</div>'
114
+ );
115
+ }
116
+
117
+ export function renderRuntimeSnapshot(snapshot: RuntimeStatusSnapshot): string {
118
+ const health = snapshot.health;
119
+ const healthValue = health ? health.status + ' - ' + health.score : HEALTH_UNAVAILABLE;
120
+ const healthTone = health
121
+ ? health.status === 'healthy'
122
+ ? 'ok'
123
+ : health.status === 'degraded'
124
+ ? 'warn'
125
+ : 'bad'
126
+ : 'muted';
127
+
128
+ return (
129
+ '<section class="mama-system-panel">' +
130
+ '<h2 class="mama-system-heading">Runtime</h2>' +
131
+ '<p class="mama-system-note">Reported by the running daemon; some fields reflect boot-time state. Configuration is edited via the CLI or the config file.</p>' +
132
+ '<div class="mama-system-tiles">' +
133
+ statTile('Daemon', snapshot.running ? 'running' : 'stopped', snapshot.running ? 'ok' : 'bad') +
134
+ statTile('Backend', snapshot.backend) +
135
+ statTile('Model', snapshot.model) +
136
+ statTile('Health', healthValue, healthTone) +
137
+ statTile('Uptime', formatUptime(snapshot.startedAt)) +
138
+ statTile('Started', formatStartedAt(snapshot.startedAt)) +
139
+ '</div>' +
140
+ '</section>'
141
+ );
142
+ }
143
+
144
+ const CONNECTOR_STATE_TONE: Record<RuntimeConnectorState, string> = {
145
+ connected: 'ok',
146
+ disconnected: 'muted',
147
+ unknown: 'warn',
148
+ };
149
+
150
+ /**
151
+ * What a connector state actually means. 'connected' is not a live probe: it
152
+ * says the connector registered when the daemon booted, so the chip carries the
153
+ * narrower claim rather than letting the word oversell itself.
154
+ */
155
+ const CONNECTOR_STATE_TITLE: Record<RuntimeConnectorState, string> = {
156
+ connected: 'Registered at daemon boot',
157
+ disconnected: 'Disabled in config',
158
+ unknown: 'Enabled in config but not registered at boot',
159
+ };
160
+
161
+ export function renderConnectorsSnapshot(snapshot: RuntimeStatusSnapshot): string {
162
+ const connectors = snapshot.connectors ?? [];
163
+ if (connectors.length === 0) {
164
+ return (
165
+ '<section class="mama-system-panel">' +
166
+ '<h2 class="mama-system-heading">Connectors</h2>' +
167
+ '<div class="mama-system-empty">' +
168
+ '<p class="mama-system-empty-title">No connectors configured</p>' +
169
+ '<p class="mama-system-empty-detail">Add one from a terminal:</p>' +
170
+ '<code class="mama-system-code">mama connector add &lt;name&gt;</code>' +
171
+ '</div>' +
172
+ '</section>'
173
+ );
174
+ }
175
+
176
+ let rows = '';
177
+ for (const connector of connectors) {
178
+ rows +=
179
+ '<li class="mama-system-row" data-tone="' +
180
+ escapeHtml(CONNECTOR_STATE_TONE[connector.state] ?? 'muted') +
181
+ '">' +
182
+ '<span class="mama-system-dot"></span>' +
183
+ '<span class="mama-system-row-name">' +
184
+ escapeHtml(connector.name) +
185
+ '</span>' +
186
+ '<span class="mama-system-row-state" title="' +
187
+ escapeHtml(CONNECTOR_STATE_TITLE[connector.state] ?? 'State reported at daemon boot') +
188
+ '">' +
189
+ escapeHtml(connector.state) +
190
+ '</span>' +
191
+ (connector.enabled ? '' : '<span class="mama-system-badge">off</span>') +
192
+ '</li>';
193
+ }
194
+
195
+ return (
196
+ '<section class="mama-system-panel">' +
197
+ '<h2 class="mama-system-heading">Connectors</h2>' +
198
+ '<p class="mama-system-note">Read-only. Add or remove connectors with <code>mama connector add</code>.</p>' +
199
+ '<ul class="mama-system-list">' +
200
+ rows +
201
+ '</ul>' +
202
+ '</section>'
203
+ );
204
+ }
205
+
206
+ async function renderView(
207
+ container: HTMLElement | null,
208
+ options: SystemViewOptions,
209
+ toHtml: (snapshot: RuntimeStatusSnapshot) => string,
210
+ skeletonRows: number
211
+ ): Promise<void> {
212
+ if (!container) return;
213
+ const fetchStatus = options.fetchStatus ?? defaultFetchStatus;
214
+
215
+ container.innerHTML = skeleton(skeletonRows);
216
+ try {
217
+ const snapshot = await fetchStatus();
218
+ container.innerHTML = toHtml(snapshot);
219
+ } catch (error) {
220
+ container.innerHTML = errorCard(error);
221
+ const retry = container.querySelector('[data-retry]');
222
+ if (retry) {
223
+ retry.addEventListener('click', () => {
224
+ void renderView(container, options, toHtml, skeletonRows);
225
+ });
226
+ }
227
+ }
228
+ }
229
+
230
+ export function renderRuntimeView(
231
+ container: HTMLElement | null,
232
+ options: SystemViewOptions = {}
233
+ ): Promise<void> {
234
+ return renderView(container, options, renderRuntimeSnapshot, 3);
235
+ }
236
+
237
+ export function renderConnectorsView(
238
+ container: HTMLElement | null,
239
+ options: SystemViewOptions = {}
240
+ ): Promise<void> {
241
+ return renderView(container, options, renderConnectorsSnapshot, 5);
242
+ }
@@ -26,15 +26,11 @@ type StoredPageContext = {
26
26
  let lastReportedPageContext: StoredPageContext | null = null;
27
27
  let lastPageContextPublishAt = 0;
28
28
 
29
+ // The Viewer always reports into the one frontdoor channel. The per-chat
30
+ // session override this used to read is gone with the Chat tab that wrote it:
31
+ // nothing writes `mama_chat_session_id` any more, so reading it could only ever
32
+ // return a stale value.
29
33
  function getViewerChannelId(): string {
30
- try {
31
- const sessionId = window.localStorage.getItem('mama_chat_session_id');
32
- if (sessionId && !sessionId.startsWith('session_')) {
33
- return sessionId;
34
- }
35
- } catch {
36
- /* ignore */
37
- }
38
34
  return VIEWER_FRONTDOOR_CHANNEL_ID;
39
35
  }
40
36
 
@@ -1,25 +1,32 @@
1
1
  /**
2
- * @fileoverview Service Worker for MAMA Mobile PWA
3
- * @version 1.5.0
2
+ * @fileoverview Service Worker for the MAMA Viewer PWA
3
+ * @version 2.0.0
4
4
  *
5
5
  * Provides offline caching for static assets using cache-first strategy.
6
6
  */
7
7
 
8
8
  /* eslint-env serviceworker */
9
9
 
10
- const CACHE_NAME = 'mama-mobile-v1.6.1';
10
+ // Bumped for the unified Viewer: the retired chat/feed/agents/dashboard/settings
11
+ // modules no longer exist, so a client holding the old cache would keep serving
12
+ // 404-shaped entries for them. A new cache name is what evicts them.
13
+ const CACHE_NAME = 'mama-viewer-v2';
11
14
  const STATIC_ASSETS = [
12
15
  '/viewer',
13
16
  '/viewer/viewer.css',
14
17
  '/viewer/manifest.json',
15
18
  '/viewer/js/modules/graph.js',
16
- '/viewer/js/modules/chat.js',
17
19
  '/viewer/js/modules/memory.js',
18
- '/viewer/js/modules/dashboard.js',
19
- '/viewer/js/modules/settings.js',
20
+ '/viewer/js/modules/wiki.js',
21
+ '/viewer/js/modules/system.js',
22
+ '/viewer/js/utils/debug-logger.js',
20
23
  '/viewer/js/utils/dom.js',
21
24
  '/viewer/js/utils/format.js',
22
25
  '/viewer/js/utils/api.js',
26
+ '/viewer/js/utils/markdown.js',
27
+ '/viewer/js/utils/ui-commands.js',
28
+ '/viewer/operator/operator.js',
29
+ '/viewer/operator/operator.css',
23
30
  '/viewer/icons/icon-192.png',
24
31
  '/viewer/icons/icon-512.png',
25
32
  '/viewer/icons/mama-icon.svg',
@@ -92,6 +99,32 @@ self.addEventListener('fetch', (event) => {
92
99
  return;
93
100
  }
94
101
 
102
+ // Network-first for the operator bundle. It ships under stable filenames
103
+ // (/viewer/operator/operator.js), so a cache-first hit would pin one build
104
+ // forever until CACHE_NAME changes. The cache stays the offline fallback.
105
+ // Must be checked before the generic '/viewer' prefix match below.
106
+ if (url.pathname.startsWith('/viewer/operator/')) {
107
+ event.respondWith(
108
+ fetch(request)
109
+ .then((response) => {
110
+ if (response.ok) {
111
+ const responseClone = response.clone();
112
+ // The write is held open by the event, not by respondWith: the
113
+ // response still returns immediately, but the worker may not be
114
+ // killed mid-put, which would leave a truncated cache entry.
115
+ event.waitUntil(
116
+ caches.open(CACHE_NAME).then((cache) => cache.put(request, responseClone))
117
+ );
118
+ }
119
+ return response;
120
+ })
121
+ .catch(() =>
122
+ caches.match(request).then((cachedResponse) => cachedResponse || caches.match('/viewer'))
123
+ )
124
+ );
125
+ return;
126
+ }
127
+
95
128
  // Cache-first for static assets
96
129
  if (STATIC_ASSETS.some((asset) => url.pathname.startsWith(asset.split('?')[0]))) {
97
130
  event.respondWith(
@@ -100,12 +133,13 @@ self.addEventListener('fetch', (event) => {
100
133
  return cachedResponse;
101
134
  }
102
135
  return fetch(request).then((response) => {
103
- // Cache successful responses
136
+ // Cache successful responses. Same detached-put hazard as the
137
+ // operator branch above, so it is held open the same way.
104
138
  if (response.ok) {
105
139
  const responseClone = response.clone();
106
- caches.open(CACHE_NAME).then((cache) => {
107
- cache.put(request, responseClone);
108
- });
140
+ event.waitUntil(
141
+ caches.open(CACHE_NAME).then((cache) => cache.put(request, responseClone))
142
+ );
109
143
  }
110
144
  return response;
111
145
  });