@jungjaehoon/mama-os 0.62.0 → 0.64.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 (106) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +1 -1
  3. package/dist/api/attachment-actions.d.ts.map +1 -1
  4. package/dist/api/attachment-actions.js +6 -2
  5. package/dist/api/attachment-actions.js.map +1 -1
  6. package/dist/api/drive-actions.d.ts +2 -1
  7. package/dist/api/drive-actions.d.ts.map +1 -1
  8. package/dist/api/drive-actions.js +1 -0
  9. package/dist/api/drive-actions.js.map +1 -1
  10. package/dist/api/drive-delivery.d.ts +16 -0
  11. package/dist/api/drive-delivery.d.ts.map +1 -0
  12. package/dist/api/drive-delivery.js +190 -0
  13. package/dist/api/drive-delivery.js.map +1 -0
  14. package/dist/api/file-delivery.d.ts +2 -2
  15. package/dist/api/file-delivery.d.ts.map +1 -1
  16. package/dist/api/file-delivery.js +5 -5
  17. package/dist/api/file-delivery.js.map +1 -1
  18. package/dist/api/security-events.d.ts +5 -4
  19. package/dist/api/security-events.d.ts.map +1 -1
  20. package/dist/api/security-events.js +25 -3
  21. package/dist/api/security-events.js.map +1 -1
  22. package/dist/api/viewer-data.d.ts +82 -1
  23. package/dist/api/viewer-data.d.ts.map +1 -1
  24. package/dist/api/viewer-data.js +116 -0
  25. package/dist/api/viewer-data.js.map +1 -1
  26. package/dist/api/viewer-server.d.ts +2 -2
  27. package/dist/api/viewer-server.d.ts.map +1 -1
  28. package/dist/api/viewer-server.js +154 -5
  29. package/dist/api/viewer-server.js.map +1 -1
  30. package/dist/api/wiki-actions.d.ts.map +1 -1
  31. package/dist/api/wiki-actions.js +44 -0
  32. package/dist/api/wiki-actions.js.map +1 -1
  33. package/dist/api/work-actions.d.ts +2 -0
  34. package/dist/api/work-actions.d.ts.map +1 -1
  35. package/dist/api/work-actions.js +4 -5
  36. package/dist/api/work-actions.js.map +1 -1
  37. package/dist/cli/commands/daemon.d.ts.map +1 -1
  38. package/dist/cli/commands/daemon.js +14 -7
  39. package/dist/cli/commands/daemon.js.map +1 -1
  40. package/dist/gateways/discord.d.ts.map +1 -1
  41. package/dist/gateways/discord.js +1 -1
  42. package/dist/gateways/discord.js.map +1 -1
  43. package/dist/gateways/slack.d.ts.map +1 -1
  44. package/dist/gateways/slack.js +1 -1
  45. package/dist/gateways/slack.js.map +1 -1
  46. package/dist/gateways/telegram.d.ts.map +1 -1
  47. package/dist/gateways/telegram.js +1 -1
  48. package/dist/gateways/telegram.js.map +1 -1
  49. package/dist/replay/replay-source-catalog.js +1 -1
  50. package/dist/replay/replay-source-catalog.js.map +1 -1
  51. package/dist/runtime/action-surface.d.ts +4 -1
  52. package/dist/runtime/action-surface.d.ts.map +1 -1
  53. package/dist/runtime/action-surface.js +19 -1
  54. package/dist/runtime/action-surface.js.map +1 -1
  55. package/dist/runtime/config.d.ts +20 -0
  56. package/dist/runtime/config.d.ts.map +1 -1
  57. package/dist/runtime/config.js +48 -3
  58. package/dist/runtime/config.js.map +1 -1
  59. package/dist/runtime/outbound-attempts.d.ts +4 -2
  60. package/dist/runtime/outbound-attempts.d.ts.map +1 -1
  61. package/dist/runtime/outbound-attempts.js +17 -1
  62. package/dist/runtime/outbound-attempts.js.map +1 -1
  63. package/dist/runtime/owner-runtime.d.ts +6 -1
  64. package/dist/runtime/owner-runtime.d.ts.map +1 -1
  65. package/dist/runtime/owner-runtime.js +4 -1
  66. package/dist/runtime/owner-runtime.js.map +1 -1
  67. package/dist/runtime/owner-system-prompt.d.ts +1 -1
  68. package/dist/runtime/owner-system-prompt.d.ts.map +1 -1
  69. package/dist/runtime/owner-system-prompt.js +10 -4
  70. package/dist/runtime/owner-system-prompt.js.map +1 -1
  71. package/dist/runtime/report-scheduler.d.ts +2 -1
  72. package/dist/runtime/report-scheduler.d.ts.map +1 -1
  73. package/dist/runtime/report-scheduler.js +1 -1
  74. package/dist/runtime/report-scheduler.js.map +1 -1
  75. package/dist/runtime/stimulus-delivery.js +1 -1
  76. package/dist/runtime/stimulus-delivery.js.map +1 -1
  77. package/dist/runtime/turn-orders.d.ts.map +1 -1
  78. package/dist/runtime/turn-orders.js +7 -3
  79. package/dist/runtime/turn-orders.js.map +1 -1
  80. package/dist/wiki/obsidian-writer.d.ts +0 -3
  81. package/dist/wiki/obsidian-writer.d.ts.map +1 -1
  82. package/dist/wiki/obsidian-writer.js +8 -108
  83. package/dist/wiki/obsidian-writer.js.map +1 -1
  84. package/dist/wiki/path-safety.d.ts +1 -1
  85. package/dist/wiki/path-safety.d.ts.map +1 -1
  86. package/dist/wiki/path-safety.js +2 -2
  87. package/dist/wiki/path-safety.js.map +1 -1
  88. package/dist/wiki/wiki-move.d.ts +12 -0
  89. package/dist/wiki/wiki-move.d.ts.map +1 -0
  90. package/dist/wiki/wiki-move.js +95 -0
  91. package/dist/wiki/wiki-move.js.map +1 -0
  92. package/dist/wiki/wiki-read.d.ts +7 -0
  93. package/dist/wiki/wiki-read.d.ts.map +1 -1
  94. package/dist/wiki/wiki-read.js +38 -17
  95. package/dist/wiki/wiki-read.js.map +1 -1
  96. package/package.json +2 -2
  97. package/public/viewer/js/modules/graph.js +266 -992
  98. package/public/viewer/js/modules/memory-timeline.js +216 -0
  99. package/public/viewer/js/modules/wiki.js +4 -4
  100. package/public/viewer/js/utils/api.js +7 -26
  101. package/public/viewer/src/modules/graph.ts +307 -1185
  102. package/public/viewer/src/modules/memory-timeline.ts +280 -0
  103. package/public/viewer/src/modules/wiki.ts +4 -4
  104. package/public/viewer/src/utils/api.ts +48 -47
  105. package/public/viewer/viewer.css +257 -7
  106. package/public/viewer/viewer.html +68 -292
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Memory Timeline Module - what was saved, and when
3
+ * @module modules/memory-timeline
4
+ *
5
+ * The memory view's list: a period (today, 7 days, 30 days or one day), then by day, then by
6
+ * kind, then by work item and its updates. Every filter is a server read, so the list and the
7
+ * counts on the kind chips always describe the same records.
8
+ */
9
+ /* eslint-env browser */
10
+ import { escapeAttr, escapeHtml, debounce, getElementByIdOrNull } from '../utils/dom.js';
11
+ import { API, } from '../utils/api.js';
12
+ /** The groups the server names first, in its order, with the words and colours the list shows. */
13
+ const GROUPS = {
14
+ owner_rule: {
15
+ label: 'Owner rules',
16
+ hint: 'Rules the owner set in their own chat',
17
+ color: '#FB7185',
18
+ },
19
+ learned: {
20
+ label: 'Learned rules',
21
+ hint: 'Lessons, preferences, constraints and workflows learned in other turns',
22
+ color: '#FBBF24',
23
+ },
24
+ decision: { label: 'Decisions', hint: 'Decisions recorded', color: '#60A5FA' },
25
+ fact: { label: 'Facts', hint: 'Facts recorded', color: '#2DD4BF' },
26
+ work: { label: 'Work updates', hint: 'Work items opened or updated', color: '#FB923C' },
27
+ };
28
+ const VIA_LABELS = {
29
+ owner_chat: 'owner chat',
30
+ source_delta: 'source update',
31
+ report: 'report',
32
+ };
33
+ const OPERATION_LABELS = {
34
+ create: 'opened',
35
+ revise: 'updated',
36
+ withdraw: 'withdrawn',
37
+ };
38
+ export function groupInfo(group) {
39
+ return GROUPS[group] ?? { label: group, hint: `Records of kind ${group}`, color: '#CBD5E1' };
40
+ }
41
+ function dayLabel(day) {
42
+ return new Intl.DateTimeFormat(undefined, {
43
+ weekday: 'short',
44
+ year: 'numeric',
45
+ month: 'short',
46
+ day: 'numeric',
47
+ timeZone: 'UTC',
48
+ }).format(new Date(`${day}T00:00:00Z`));
49
+ }
50
+ export class MemoryTimelineModule {
51
+ hooks;
52
+ period = '7d';
53
+ day = null;
54
+ query = '';
55
+ groups = new Set();
56
+ selectedId = null;
57
+ request = 0;
58
+ bound = false;
59
+ debouncedLoad = debounce(() => void this.load(), 300);
60
+ constructor(hooks) {
61
+ this.hooks = hooks;
62
+ }
63
+ /** Wire the controls once, then read the current period. */
64
+ async init() {
65
+ if (!this.bound) {
66
+ this.bind();
67
+ this.bound = true;
68
+ }
69
+ await this.load();
70
+ }
71
+ bind() {
72
+ document.querySelectorAll('[data-memory-period]').forEach((button) => {
73
+ button.addEventListener('click', () => {
74
+ this.period = button.dataset.memoryPeriod ?? '7d';
75
+ this.day = null;
76
+ const dayInput = getElementByIdOrNull('memory-day');
77
+ if (dayInput)
78
+ dayInput.value = '';
79
+ void this.load();
80
+ });
81
+ });
82
+ getElementByIdOrNull('memory-day')?.addEventListener('change', (event) => {
83
+ const value = event.target.value;
84
+ this.day = value === '' ? null : value;
85
+ void this.load();
86
+ });
87
+ getElementByIdOrNull('memory-timeline-search')?.addEventListener('input', (event) => {
88
+ this.query = event.target.value.trim();
89
+ this.debouncedLoad();
90
+ });
91
+ getElementByIdOrNull('memory-group-chips')?.addEventListener('click', (event) => {
92
+ const chip = event.target.closest('[data-group]');
93
+ if (!chip?.dataset.group)
94
+ return;
95
+ const group = chip.dataset.group;
96
+ if (this.groups.has(group))
97
+ this.groups.delete(group);
98
+ else
99
+ this.groups.add(group);
100
+ void this.load();
101
+ });
102
+ getElementByIdOrNull('memory-timeline')?.addEventListener('click', (event) => {
103
+ const row = event.target.closest('[data-record-id]');
104
+ if (!row?.dataset.recordId)
105
+ return;
106
+ this.select(row.dataset.recordId);
107
+ });
108
+ }
109
+ select(id) {
110
+ this.selectedId = id;
111
+ document.querySelectorAll('#memory-timeline .mt-row.selected').forEach((row) => {
112
+ row.classList.remove('selected');
113
+ });
114
+ document
115
+ .querySelector(`#memory-timeline [data-record-id="${CSS.escape(id)}"]`)
116
+ ?.classList.add('selected');
117
+ this.hooks.onSelect(id);
118
+ }
119
+ params() {
120
+ return {
121
+ ...(this.day === null ? { period: this.period } : { from: this.day, to: this.day }),
122
+ ...(this.query === '' ? {} : { q: this.query }),
123
+ ...(this.groups.size === 0 ? {} : { groups: [...this.groups].join(',') }),
124
+ };
125
+ }
126
+ async load() {
127
+ const list = getElementByIdOrNull('memory-timeline');
128
+ if (!list)
129
+ return;
130
+ const request = ++this.request;
131
+ list.setAttribute('aria-busy', 'true');
132
+ try {
133
+ const data = await API.getMemoryTimeline(this.params());
134
+ // A slower earlier read must not overwrite the answer to the latest filter.
135
+ if (request !== this.request)
136
+ return;
137
+ this.render(data);
138
+ this.hooks.onRender(data);
139
+ }
140
+ catch (error) {
141
+ if (request !== this.request)
142
+ return;
143
+ list.innerHTML = `<div class="mt-empty mt-error">Could not read what was saved: ${escapeHtml(error instanceof Error ? error.message : String(error))}</div>`;
144
+ }
145
+ finally {
146
+ if (request === this.request)
147
+ list.removeAttribute('aria-busy');
148
+ }
149
+ }
150
+ render(data) {
151
+ document.querySelectorAll('[data-memory-period]').forEach((button) => {
152
+ button.setAttribute('aria-pressed', String(this.day === null && button.dataset.memoryPeriod === this.period));
153
+ });
154
+ const range = getElementByIdOrNull('memory-timeline-range');
155
+ if (range) {
156
+ range.textContent =
157
+ data.from === data.to
158
+ ? dayLabel(data.from)
159
+ : `${dayLabel(data.from)} – ${dayLabel(data.to)}`;
160
+ }
161
+ const count = getElementByIdOrNull('memory-timeline-count');
162
+ if (count)
163
+ count.textContent = `${data.total} saved`;
164
+ this.renderChips(data.counts);
165
+ const list = getElementByIdOrNull('memory-timeline');
166
+ if (!list)
167
+ return;
168
+ if (data.days.length === 0) {
169
+ list.innerHTML = `<div class="mt-empty">Nothing saved${this.query === '' ? '' : ` matching “${escapeHtml(this.query)}”`} in this period.</div>`;
170
+ return;
171
+ }
172
+ list.innerHTML = data.days
173
+ .map((day) => `<section class="mt-day"><h3 class="mt-day-head"><span>${escapeHtml(dayLabel(day.day))}</span><span class="mt-count">${day.total}</span></h3>${day.groups
174
+ .map((group) => this.renderGroup(group))
175
+ .join('')}</section>`)
176
+ .join('');
177
+ if (this.selectedId !== null) {
178
+ document
179
+ .querySelector(`#memory-timeline [data-record-id="${CSS.escape(this.selectedId)}"]`)
180
+ ?.classList.add('selected');
181
+ }
182
+ }
183
+ renderChips(counts) {
184
+ const container = getElementByIdOrNull('memory-group-chips');
185
+ if (!container)
186
+ return;
187
+ container.innerHTML = Object.entries(counts)
188
+ .filter(([group, count]) => count > 0 || this.groups.has(group))
189
+ .map(([group, count]) => {
190
+ const info = groupInfo(group);
191
+ return `<button type="button" class="memory-chip" data-group="${escapeAttr(group)}" aria-pressed="${this.groups.has(group)}" title="${escapeAttr(info.hint)}"><span class="mt-dot" style="background:${info.color}"></span>${escapeHtml(info.label)} <span class="mt-count">${count}</span></button>`;
192
+ })
193
+ .join('');
194
+ }
195
+ renderGroup(group) {
196
+ const info = groupInfo(group.group);
197
+ const body = group.items !== undefined
198
+ ? group.items.map((item) => this.renderItem(item)).join('')
199
+ : (group.records ?? []).map((record) => this.renderRecord(record)).join('');
200
+ return `<details class="mt-group" open><summary class="mt-group-head" title="${escapeAttr(info.hint)}"><span class="mt-dot" style="background:${info.color}"></span>${escapeHtml(info.label)}<span class="mt-count">${group.count}</span></summary>${body}</details>`;
201
+ }
202
+ renderRecord(record) {
203
+ const via = record.via === null ? '' : (VIA_LABELS[record.via] ?? record.via);
204
+ const status = record.status === null || record.status === 'active'
205
+ ? ''
206
+ : `<span class="mt-badge">${escapeHtml(record.status)}</span>`;
207
+ return `<button type="button" class="mt-row" data-record-id="${escapeAttr(record.id)}"><span class="mt-time">${escapeHtml(record.time)}</span><span class="mt-body"><span class="mt-topic">${escapeHtml(record.topic)}${status}</span><span class="mt-summary">${escapeHtml(record.summary)}</span>${via === '' ? '' : `<span class="mt-via">from ${escapeHtml(via)}</span>`}</span></button>`;
208
+ }
209
+ renderItem(item) {
210
+ const latest = item.revisions[0];
211
+ const updates = item.revisions.length;
212
+ return `<details class="mt-item"><summary class="mt-item-head"><span class="mt-time">${escapeHtml(latest?.time ?? '')}</span><span class="mt-body"><span class="mt-topic">${escapeHtml(item.title ?? item.topic)}</span><span class="mt-summary">${escapeHtml(latest?.summary ?? '')}</span></span><span class="mt-count">${updates} ${updates === 1 ? 'update' : 'updates'}</span></summary>${item.revisions
213
+ .map((revision) => `<button type="button" class="mt-row mt-revision" data-record-id="${escapeAttr(revision.id)}"><span class="mt-time">${escapeHtml(revision.time)}</span><span class="mt-body"><span class="mt-op">${escapeHtml(OPERATION_LABELS[revision.operation ?? ''] ?? revision.operation ?? '')}${revision.revision === null ? '' : ` · revision ${revision.revision}`}</span><span class="mt-summary">${escapeHtml(revision.summary)}</span></span></button>`)
214
+ .join('')}</details>`;
215
+ }
216
+ }
@@ -154,11 +154,11 @@ export class WikiModule {
154
154
  });
155
155
  });
156
156
  document.getElementById('wiki-new-btn')?.addEventListener('click', () => this.promptNewPage());
157
- // Auto-open index page only on initial load (no page selected yet)
157
+ // Auto-open the agent's table of contents only on initial load (no page selected yet)
158
158
  if (!this.currentPath) {
159
- const indexNode = tree.find((n) => n.name === 'index.md');
160
- if (!mobile && indexNode) {
161
- this.openPage(indexNode.path);
159
+ const homeNode = tree.find((n) => n.name === 'Home.md');
160
+ if (!mobile && homeNode) {
161
+ this.openPage(homeNode.path);
162
162
  }
163
163
  }
164
164
  }
@@ -124,36 +124,17 @@ export class API {
124
124
  // =============================================
125
125
  // Graph API
126
126
  // =============================================
127
- /**
128
- * Get graph data
129
- * @param {Object} params - Query parameters
130
- * @returns {Promise<Object>} Graph data
131
- */
132
- static async getGraph(params = {}) {
133
- // cluster: false by default to avoid slow embedding calculations
134
- return this.get('/graph', { cluster: 'false', ...params });
127
+ /** Every link between records, with each end's kind, label and work item. */
128
+ static async getMemoryLinks() {
129
+ return this.get('/api/memory/links');
130
+ }
131
+ /** What was saved in a period, by day, kind and item. */
132
+ static async getMemoryTimeline(params) {
133
+ return this.get('/api/memory/timeline', params);
135
134
  }
136
135
  static async getGraphDetail(nodeId) {
137
136
  return this.get('/graph/detail', { id: nodeId });
138
137
  }
139
- /**
140
- * Get similar decisions for a node
141
- * @param {string} nodeId - Node ID
142
- * @returns {Promise<Object>} Similar decisions
143
- */
144
- static async getSimilarDecisions(nodeId) {
145
- return this.get('/graph/similar', { id: nodeId });
146
- }
147
- /**
148
- * Update decision outcome
149
- * @param {string} id - Decision ID
150
- * @param {string} outcome - Outcome value
151
- * @param {string} reason - Optional reason
152
- * @returns {Promise<Object>} Update result
153
- */
154
- static async updateOutcome(id, outcome, reason = null) {
155
- return this.post('/graph/update', { id, outcome, reason });
156
- }
157
138
  // =============================================
158
139
  // Checkpoint API
159
140
  // =============================================