@jungjaehoon/mama-os 0.48.3 → 0.49.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 (161) hide show
  1. package/dist/agent/agent-loop.d.ts +6 -7
  2. package/dist/agent/agent-loop.d.ts.map +1 -1
  3. package/dist/agent/agent-loop.js +93 -79
  4. package/dist/agent/agent-loop.js.map +1 -1
  5. package/dist/agent/code-act/host-bridge.d.ts.map +1 -1
  6. package/dist/agent/code-act/host-bridge.js +2 -10
  7. package/dist/agent/code-act/host-bridge.js.map +1 -1
  8. package/dist/agent/codex-home.d.ts.map +1 -1
  9. package/dist/agent/codex-home.js +3 -1
  10. package/dist/agent/codex-home.js.map +1 -1
  11. package/dist/agent/gateway-tool-executor.d.ts +0 -16
  12. package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
  13. package/dist/agent/gateway-tool-executor.js +0 -51
  14. package/dist/agent/gateway-tool-executor.js.map +1 -1
  15. package/dist/agent/gateway-tools.md +1 -2
  16. package/dist/agent/os-agent-capabilities.md +14 -34
  17. package/dist/agent/role-manager.d.ts.map +1 -1
  18. package/dist/agent/role-manager.js +6 -3
  19. package/dist/agent/role-manager.js.map +1 -1
  20. package/dist/agent/tool-registry.d.ts.map +1 -1
  21. package/dist/agent/tool-registry.js +2 -8
  22. package/dist/agent/tool-registry.js.map +1 -1
  23. package/dist/agent/types.d.ts +18 -6
  24. package/dist/agent/types.d.ts.map +1 -1
  25. package/dist/agent/types.js.map +1 -1
  26. package/dist/api/graph-api-types.d.ts +0 -18
  27. package/dist/api/graph-api-types.d.ts.map +1 -1
  28. package/dist/api/graph-api.d.ts.map +1 -1
  29. package/dist/api/graph-api.js +9 -81
  30. package/dist/api/graph-api.js.map +1 -1
  31. package/dist/cli/commands/start.d.ts +7 -16
  32. package/dist/cli/commands/start.d.ts.map +1 -1
  33. package/dist/cli/commands/start.js +181 -188
  34. package/dist/cli/commands/start.js.map +1 -1
  35. package/dist/cli/config/types.d.ts.map +1 -1
  36. package/dist/cli/config/types.js +11 -4
  37. package/dist/cli/config/types.js.map +1 -1
  38. package/dist/cli/runtime/agent-loop-init.d.ts.map +1 -1
  39. package/dist/cli/runtime/agent-loop-init.js +28 -11
  40. package/dist/cli/runtime/agent-loop-init.js.map +1 -1
  41. package/dist/cli/runtime/api-routes-init.d.ts +0 -2
  42. package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
  43. package/dist/cli/runtime/api-routes-init.js +9 -95
  44. package/dist/cli/runtime/api-routes-init.js.map +1 -1
  45. package/dist/cli/runtime/api-server-init.d.ts +2 -2
  46. package/dist/cli/runtime/api-server-init.d.ts.map +1 -1
  47. package/dist/cli/runtime/api-server-init.js +2 -2
  48. package/dist/cli/runtime/api-server-init.js.map +1 -1
  49. package/dist/cli/runtime/scheduler-init.d.ts +3 -4
  50. package/dist/cli/runtime/scheduler-init.d.ts.map +1 -1
  51. package/dist/cli/runtime/scheduler-init.js +6 -16
  52. package/dist/cli/runtime/scheduler-init.js.map +1 -1
  53. package/dist/cli/runtime/shutdown.d.ts +0 -1
  54. package/dist/cli/runtime/shutdown.d.ts.map +1 -1
  55. package/dist/cli/runtime/shutdown.js +1 -1
  56. package/dist/cli/runtime/shutdown.js.map +1 -1
  57. package/dist/cli/runtime/types.d.ts +0 -5
  58. package/dist/cli/runtime/types.d.ts.map +1 -1
  59. package/dist/concurrency/lane-manager.d.ts +1 -1
  60. package/dist/concurrency/lane-manager.d.ts.map +1 -1
  61. package/dist/concurrency/lane-manager.js +4 -2
  62. package/dist/concurrency/lane-manager.js.map +1 -1
  63. package/dist/concurrency/types.d.ts +4 -0
  64. package/dist/concurrency/types.d.ts.map +1 -1
  65. package/dist/concurrency/types.js.map +1 -1
  66. package/dist/envelope/types.d.ts +1 -1
  67. package/dist/envelope/types.d.ts.map +1 -1
  68. package/dist/envelope/types.js.map +1 -1
  69. package/dist/gateways/message-router.d.ts +13 -71
  70. package/dist/gateways/message-router.d.ts.map +1 -1
  71. package/dist/gateways/message-router.js +99 -489
  72. package/dist/gateways/message-router.js.map +1 -1
  73. package/dist/gateways/session-store.d.ts +3 -23
  74. package/dist/gateways/session-store.d.ts.map +1 -1
  75. package/dist/gateways/session-store.js +2 -64
  76. package/dist/gateways/session-store.js.map +1 -1
  77. package/dist/gateways/telegram-report-context-store.d.ts +4 -66
  78. package/dist/gateways/telegram-report-context-store.d.ts.map +1 -1
  79. package/dist/gateways/telegram-report-context-store.js +11 -235
  80. package/dist/gateways/telegram-report-context-store.js.map +1 -1
  81. package/dist/gateways/turn-contract.d.ts +7 -3
  82. package/dist/gateways/turn-contract.d.ts.map +1 -1
  83. package/dist/operator/operator-trigger-loop.d.ts +1 -12
  84. package/dist/operator/operator-trigger-loop.d.ts.map +1 -1
  85. package/dist/operator/operator-trigger-loop.js +6 -40
  86. package/dist/operator/operator-trigger-loop.js.map +1 -1
  87. package/dist/operator/owner-event-loop.d.ts +8 -7
  88. package/dist/operator/owner-event-loop.d.ts.map +1 -1
  89. package/dist/operator/owner-event-loop.js +7 -20
  90. package/dist/operator/owner-event-loop.js.map +1 -1
  91. package/dist/operator/owner-event-policy.d.ts.map +1 -1
  92. package/dist/operator/owner-event-policy.js +0 -2
  93. package/dist/operator/owner-event-policy.js.map +1 -1
  94. package/dist/operator/owner-event-prompt.d.ts +0 -2
  95. package/dist/operator/owner-event-prompt.d.ts.map +1 -1
  96. package/dist/operator/owner-event-prompt.js +1 -8
  97. package/dist/operator/owner-event-prompt.js.map +1 -1
  98. package/dist/operator/owner-runtime-journal.d.ts +21 -0
  99. package/dist/operator/owner-runtime-journal.d.ts.map +1 -0
  100. package/dist/operator/owner-runtime-journal.js +131 -0
  101. package/dist/operator/owner-runtime-journal.js.map +1 -0
  102. package/dist/operator/owner-runtime.d.ts +32 -0
  103. package/dist/operator/owner-runtime.d.ts.map +1 -0
  104. package/dist/operator/owner-runtime.js +31 -0
  105. package/dist/operator/owner-runtime.js.map +1 -0
  106. package/dist/operator/pending-report-store.d.ts +2 -2
  107. package/dist/operator/pending-report-store.d.ts.map +1 -1
  108. package/dist/operator/pending-report-store.js +4 -1
  109. package/dist/operator/pending-report-store.js.map +1 -1
  110. package/dist/operator/report-run.d.ts +7 -10
  111. package/dist/operator/report-run.d.ts.map +1 -1
  112. package/dist/operator/report-run.js +5 -26
  113. package/dist/operator/report-run.js.map +1 -1
  114. package/dist/operator/situation-report.d.ts +25 -6
  115. package/dist/operator/situation-report.d.ts.map +1 -1
  116. package/dist/operator/situation-report.js +25 -37
  117. package/dist/operator/situation-report.js.map +1 -1
  118. package/dist/operator/trigger-author.d.ts +0 -45
  119. package/dist/operator/trigger-author.d.ts.map +1 -1
  120. package/dist/operator/trigger-author.js +0 -122
  121. package/dist/operator/trigger-author.js.map +1 -1
  122. package/dist/operator/worker-run.d.ts +9 -23
  123. package/dist/operator/worker-run.d.ts.map +1 -1
  124. package/dist/operator/worker-run.js +18 -74
  125. package/dist/operator/worker-run.js.map +1 -1
  126. package/dist/operator/workorder-consumer.d.ts.map +1 -1
  127. package/dist/operator/workorder-consumer.js +7 -0
  128. package/dist/operator/workorder-consumer.js.map +1 -1
  129. package/dist/scheduler/cron-worker.d.ts +0 -3
  130. package/dist/scheduler/cron-worker.d.ts.map +1 -1
  131. package/dist/scheduler/cron-worker.js +2 -13
  132. package/dist/scheduler/cron-worker.js.map +1 -1
  133. package/dist/scheduler/heartbeat.d.ts +3 -1
  134. package/dist/scheduler/heartbeat.d.ts.map +1 -1
  135. package/dist/scheduler/heartbeat.js +13 -4
  136. package/dist/scheduler/heartbeat.js.map +1 -1
  137. package/dist/validation/types.d.ts +1 -1
  138. package/dist/validation/types.js +1 -1
  139. package/dist/wiki/wiki-turn-contract.d.ts.map +1 -1
  140. package/dist/wiki/wiki-turn-contract.js +1 -0
  141. package/dist/wiki/wiki-turn-contract.js.map +1 -1
  142. package/dist/wiki-artifacts/wiki-publish-adapter.d.ts.map +1 -1
  143. package/dist/wiki-artifacts/wiki-publish-adapter.js +11 -1
  144. package/dist/wiki-artifacts/wiki-publish-adapter.js.map +1 -1
  145. package/package.json +1 -1
  146. package/dist/cli/runtime/memory-agent-init.d.ts +0 -37
  147. package/dist/cli/runtime/memory-agent-init.d.ts.map +0 -1
  148. package/dist/cli/runtime/memory-agent-init.js +0 -202
  149. package/dist/cli/runtime/memory-agent-init.js.map +0 -1
  150. package/dist/gateways/owner-report-inbox.d.ts +0 -40
  151. package/dist/gateways/owner-report-inbox.d.ts.map +0 -1
  152. package/dist/gateways/owner-report-inbox.js +0 -128
  153. package/dist/gateways/owner-report-inbox.js.map +0 -1
  154. package/dist/multi-agent/memory-agent-persona.d.ts +0 -11
  155. package/dist/multi-agent/memory-agent-persona.d.ts.map +0 -1
  156. package/dist/multi-agent/memory-agent-persona.js +0 -175
  157. package/dist/multi-agent/memory-agent-persona.js.map +0 -1
  158. package/dist/operator/report-context.d.ts +0 -184
  159. package/dist/operator/report-context.d.ts.map +0 -1
  160. package/dist/operator/report-context.js +0 -1052
  161. package/dist/operator/report-context.js.map +0 -1
@@ -1,1052 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MAX_OPERATIONAL_ISSUES = void 0;
4
- exports.serializeOwnerReportContext = serializeOwnerReportContext;
5
- exports.compileOwnerReportContext = compileOwnerReportContext;
6
- exports.compileChannelPacket = compileChannelPacket;
7
- const tool_registry_js_1 = require("../agent/tool-registry.js");
8
- const MAX_CLAIMS = 30;
9
- const TASK_PAGE_SIZE = 20;
10
- const MAX_TRELLO_CARDS_PER_LIST = 100;
11
- const MAX_TRELLO_CARDS = 300;
12
- const MAX_CHANGES = 100;
13
- // One prompt's context window is the only remaining bound on packet size (~128K tokens).
14
- // 96 KB silently trimmed the task list back to a slice after the task cap was lifted, and
15
- // the removers below emptied the packet's Trello half first (review of #258, 2026-09-04).
16
- // Today's board: 367 rows ~ 140 KB. The removers stay as a last resort with this cause.
17
- // Reachability at this ceiling: only the uncapped task list can exceed it; with claims,
18
- // trello and changes capped, the largest otherwise-valid packet is ~524,225 bytes, so the
19
- // correlation and window-evidence removers below are last-resort code that does not run.
20
- const MAX_PACKET_BYTES = 512 * 1024;
21
- const REGISTERED_TOOL_NAMES = new Set(tool_registry_js_1.ToolRegistry.getValidToolNames());
22
- const SOURCE_DISPLAY_LABELS = {
23
- 'claude-code': 'claude-code',
24
- calendar: 'calendar',
25
- chatwork: 'chatwork',
26
- discord: 'discord',
27
- drive: 'drive',
28
- gmail: 'gmail',
29
- imessage: 'imessage',
30
- kagemusha: 'kagemusha',
31
- notion: 'notion',
32
- obsidian: 'obsidian',
33
- sheets: 'sheets',
34
- slack: 'slack',
35
- telegram: 'telegram',
36
- trello: 'trello',
37
- };
38
- function isRecord(value) {
39
- return typeof value === 'object' && value !== null && !Array.isArray(value);
40
- }
41
- function isFiniteDate(value) {
42
- return value.trim().length > 0 && Number.isFinite(Date.parse(value));
43
- }
44
- function isSafeCount(value) {
45
- return Number.isSafeInteger(value) && value >= 0;
46
- }
47
- function validScopeRef(value) {
48
- if (!isRecord(value) || typeof value.kind !== 'string' || typeof value.id !== 'string') {
49
- return false;
50
- }
51
- return value.id.trim().length > 0;
52
- }
53
- function assertInput(input) {
54
- const scope = input.readScope;
55
- const window = input.windowEvidence;
56
- const scopeValid = isRecord(scope) &&
57
- Array.isArray(scope.projectRefs) &&
58
- scope.projectRefs.every((ref) => isRecord(ref) && ref.kind === 'project' && typeof ref.id === 'string' && ref.id !== '') &&
59
- Array.isArray(scope.memoryScopes) &&
60
- scope.memoryScopes.every(validScopeRef) &&
61
- Array.isArray(scope.rawConnectors) &&
62
- scope.rawConnectors.every((connector) => typeof connector === 'string' && connector !== '');
63
- const windowValid = isRecord(window) &&
64
- typeof window.start === 'string' &&
65
- isFiniteDate(window.start) &&
66
- typeof window.end === 'string' &&
67
- isFiniteDate(window.end) &&
68
- isSafeCount(window.channelCount) &&
69
- isSafeCount(window.messageCount) &&
70
- Array.isArray(window.channels) &&
71
- window.channels.length <= 48 &&
72
- window.channels.every((channel) => isRecord(channel) &&
73
- typeof channel.label === 'string' &&
74
- isSafeCount(channel.count) &&
75
- Array.isArray(channel.excerpts) &&
76
- channel.excerpts.length <= 5 &&
77
- channel.excerpts.every((excerpt) => isRecord(excerpt) &&
78
- typeof excerpt.authorLabel === 'string' &&
79
- typeof excerpt.text === 'string' &&
80
- (excerpt.observedAt === null ||
81
- (typeof excerpt.observedAt === 'string' && isFiniteDate(excerpt.observedAt))))) &&
82
- Array.isArray(window.triggerActivity) &&
83
- window.triggerActivity.length <= 100 &&
84
- window.triggerActivity.every((activity) => isRecord(activity) &&
85
- typeof activity.kind === 'string' &&
86
- isSafeCount(activity.count) &&
87
- Array.isArray(activity.topics) &&
88
- activity.topics.every((topic) => typeof topic === 'string'));
89
- if (!scopeValid ||
90
- !windowValid ||
91
- typeof input.since !== 'string' ||
92
- !isFiniteDate(input.since)) {
93
- throw new Error('Invalid owner report context input');
94
- }
95
- }
96
- function detachScope(scope) {
97
- return {
98
- projectRefs: scope.projectRefs.map((ref) => ({ kind: 'project', id: ref.id })),
99
- memoryScopes: scope.memoryScopes.map((ref) => ({ kind: ref.kind, id: ref.id })),
100
- rawConnectors: [...new Set(scope.rawConnectors)].sort(),
101
- };
102
- }
103
- function isRegisteredJsonToolObject(value) {
104
- let parsed;
105
- try {
106
- parsed = JSON.parse(value);
107
- }
108
- catch {
109
- return false;
110
- }
111
- return (isRecord(parsed) &&
112
- typeof parsed.name === 'string' &&
113
- REGISTERED_TOOL_NAMES.has(parsed.name) &&
114
- (isRecord(parsed.input) || isRecord(parsed.arguments)));
115
- }
116
- function hasRegisteredImperativeTarget(value) {
117
- const matches = value.matchAll(/\b(?:call|use|invoke|run)\s+([A-Za-z][A-Za-z0-9_-]*)\b/gi);
118
- return [...matches].some((match) => REGISTERED_TOOL_NAMES.has(match[1]));
119
- }
120
- function redactText(value, maxLength) {
121
- if (/^\s*(?:system|developer|assistant)\s*:/i.test(value) ||
122
- /<\/?(?:tool_call|function_call|invoke)\b/i.test(value) ||
123
- isRegisteredJsonToolObject(value) ||
124
- /\bmcp__[a-z0-9_.-]+/i.test(value) ||
125
- /\b(?:ignore|disregard)\s+(?:all\s+)?(?:prior|previous|above)\s+(?:instructions|messages)\b/i.test(value) ||
126
- hasRegisteredImperativeTarget(value)) {
127
- return '[redacted-instruction]'.slice(0, maxLength);
128
- }
129
- return (value
130
- .replace(/```\s*(?:tool_call|json\s*tool_call)[\s\S]*?```/gi, '[redacted-instruction]')
131
- .replace(/```\s*(?:tool_call|json\s*tool_call)\b/gi, '[redacted-instruction]')
132
- .replace(/\bignore\s+(?:all\s+)?previous\s+instructions\b/gi, '[redacted-instruction]')
133
- // Real-data verification (2026-09-02): Board-written latest_event prose and
134
- // curated claim summaries carried raw event ids (`source_event_id=evt_...`),
135
- // run ids (`mr_...`) and numeric chat-room ids (`channel=chatwork:123...`) into
136
- // the model-visible packet. The packet contract forbids raw run/event/source
137
- // and chat ids; the words around them stay.
138
- .replace(/\bsource_event_id\s*=\s*\S+/gi, 'source_event_id=[redacted-id]')
139
- .replace(/\b(?:evt|mr)_[0-9a-f]{8,}\b/gi, '[redacted-id]')
140
- .replace(/\b(chatwork|telegram|slack|kakao|kagemusha|discord|line)\s*[:=]\s*\d{5,}\b/gi, '$1:[redacted-id]')
141
- .replace(/(?:~|\/(?:Users|home|private|var|tmp)\/)[^\s,;)}\]]+/g, '[redacted-path]')
142
- .replace(/\b(token|api[_-]?key|password|secret|authorization)\s*[:=]\s*[^\s,;]+/gi, '$1=[redacted-secret]')
143
- .slice(0, maxLength));
144
- }
145
- /**
146
- * Upstream instants (Trello card timestamps, effect-ledger times) are copied into the
147
- * model-visible packet. Normalising them here means connector text can never ride
148
- * along in a timestamp field and an unparseable value cannot fail packet validation
149
- * after composition.
150
- */
151
- function toIsoInstant(value) {
152
- if (typeof value !== 'string' && typeof value !== 'number')
153
- return null;
154
- const ms = typeof value === 'number' ? value : Date.parse(value);
155
- if (!Number.isFinite(ms))
156
- return null;
157
- return new Date(ms).toISOString();
158
- }
159
- function sanitizeWindow(window) {
160
- return {
161
- start: new Date(window.start).toISOString(),
162
- end: new Date(window.end).toISOString(),
163
- channelCount: window.channelCount,
164
- messageCount: window.messageCount,
165
- channels: window.channels
166
- .map((channel) => ({
167
- label: redactText(channel.label, 512),
168
- count: channel.count,
169
- excerpts: channel.excerpts.map((excerpt) => ({
170
- authorLabel: redactText(excerpt.authorLabel, 160),
171
- text: redactText(excerpt.text, 160),
172
- observedAt: excerpt.observedAt === null ? null : new Date(excerpt.observedAt).toISOString(),
173
- })),
174
- }))
175
- .sort((left, right) => right.count - left.count || left.label.localeCompare(right.label)),
176
- triggerActivity: window.triggerActivity
177
- .map((activity) => ({
178
- kind: redactText(activity.kind, 160),
179
- count: activity.count,
180
- topics: activity.topics.map((topic) => redactText(topic, 256)).sort(),
181
- }))
182
- .sort((left, right) => right.count - left.count || left.kind.localeCompare(right.kind)),
183
- };
184
- }
185
- function sourceLabel(sourceChannel, authorizedConnectors) {
186
- if (!sourceChannel)
187
- return null;
188
- const separator = sourceChannel.indexOf(':');
189
- if (separator <= 0)
190
- return null;
191
- const connector = sourceChannel.slice(0, separator);
192
- return authorizedConnectors.includes(connector)
193
- ? (SOURCE_DISPLAY_LABELS[connector] ?? null)
194
- : null;
195
- }
196
- function emptyCorrelation() {
197
- return {
198
- coverage: {
199
- total: 0,
200
- matched: 0,
201
- unmatched: 0,
202
- ambiguous: 0,
203
- historical_only: 0,
204
- not_applicable: 0,
205
- },
206
- rows: [],
207
- };
208
- }
209
- function canonicalize(value) {
210
- if (Array.isArray(value))
211
- return value.map(canonicalize);
212
- if (!isRecord(value))
213
- return value;
214
- return Object.fromEntries(Object.keys(value)
215
- .sort()
216
- .map((key) => [key, canonicalize(value[key])]));
217
- }
218
- function canonicalJson(packet) {
219
- return JSON.stringify(canonicalize(packet));
220
- }
221
- function setPacketBytes(packet) {
222
- let serialized = '';
223
- let previous = -1;
224
- for (let attempt = 0; attempt < 8; attempt += 1) {
225
- serialized = canonicalJson(packet);
226
- const bytes = Buffer.byteLength(serialized);
227
- packet.packet.bytes = bytes;
228
- if (bytes === previous)
229
- return canonicalJson(packet);
230
- previous = bytes;
231
- }
232
- throw new Error('Owner report context canonical byte count did not converge');
233
- }
234
- function markSourcePartial(packet, source, reason) {
235
- const current = packet.sources[source];
236
- packet.sources[source] = {
237
- state: 'partial',
238
- observedAt: current.observedAt,
239
- reason,
240
- };
241
- }
242
- function recomputeCorrelationCoverage(report) {
243
- const coverage = {
244
- total: report.rows.length,
245
- matched: 0,
246
- unmatched: 0,
247
- ambiguous: 0,
248
- historical_only: 0,
249
- not_applicable: 0,
250
- };
251
- for (const row of report.rows)
252
- coverage[row.outcome] += 1;
253
- report.coverage = coverage;
254
- }
255
- function enforcePacketLimit(packet) {
256
- let serialized = setPacketBytes(packet);
257
- if (Buffer.byteLength(serialized) <= MAX_PACKET_BYTES)
258
- return;
259
- packet.packet.truncated = true;
260
- if (!packet.caveats.includes('packet_size_truncated')) {
261
- packet.caveats.push('packet_size_truncated');
262
- }
263
- const removers = [
264
- () => {
265
- for (let index = packet.trello.columns.length - 1; index >= 0; index -= 1) {
266
- const column = packet.trello.columns[index];
267
- if (column.cards.length > 0) {
268
- column.cards.pop();
269
- column.returned = column.cards.length;
270
- packet.trello.truncated = true;
271
- packet.trello.complete = false;
272
- markSourcePartial(packet, 'trello', 'packet_size_limit_reached');
273
- return true;
274
- }
275
- }
276
- return false;
277
- },
278
- () => {
279
- if (packet.changes.rows.length === 0)
280
- return false;
281
- packet.changes.rows.pop();
282
- packet.changes.returned = packet.changes.rows.length;
283
- markSourcePartial(packet, 'changes', 'packet_size_limit_reached');
284
- return true;
285
- },
286
- () => {
287
- if (packet.currentClaims.length === 0)
288
- return false;
289
- packet.currentClaims.pop();
290
- markSourcePartial(packet, 'claims', 'packet_size_limit_reached');
291
- return true;
292
- },
293
- () => {
294
- if (packet.tasks.length === 0)
295
- return false;
296
- packet.tasks.pop();
297
- packet.taskCoverage.returned = packet.tasks.length;
298
- packet.taskCoverage.truncated = packet.taskCoverage.total > packet.tasks.length;
299
- markSourcePartial(packet, 'tasks', 'packet_size_limit_reached');
300
- packet.correlations.rows = packet.correlations.rows.filter((row) => packet.tasks.some((task) => task.id === row.taskId));
301
- recomputeCorrelationCoverage(packet.correlations);
302
- return true;
303
- },
304
- () => {
305
- if (packet.correlations.rows.length === 0)
306
- return false;
307
- packet.correlations.rows.pop();
308
- recomputeCorrelationCoverage(packet.correlations);
309
- return true;
310
- },
311
- () => {
312
- for (let index = packet.windowEvidence.channels.length - 1; index >= 0; index -= 1) {
313
- const channel = packet.windowEvidence.channels[index];
314
- if (channel.excerpts.length > 0) {
315
- channel.excerpts.pop();
316
- return true;
317
- }
318
- }
319
- return false;
320
- },
321
- () => {
322
- for (let index = packet.windowEvidence.triggerActivity.length - 1; index >= 0; index -= 1) {
323
- const activity = packet.windowEvidence.triggerActivity[index];
324
- if (activity.topics.length > 0) {
325
- activity.topics.pop();
326
- return true;
327
- }
328
- }
329
- return false;
330
- },
331
- () => {
332
- if (packet.windowEvidence.triggerActivity.length === 0)
333
- return false;
334
- packet.windowEvidence.triggerActivity.pop();
335
- return true;
336
- },
337
- () => {
338
- if (packet.trello.columns.length === 0)
339
- return false;
340
- packet.trello.columns.pop();
341
- packet.trello.truncated = true;
342
- packet.trello.complete = false;
343
- markSourcePartial(packet, 'trello', 'packet_size_limit_reached');
344
- return true;
345
- },
346
- () => {
347
- if (packet.trello.boards.length === 0)
348
- return false;
349
- packet.trello.boards.pop();
350
- packet.trello.truncated = true;
351
- packet.trello.complete = false;
352
- markSourcePartial(packet, 'trello', 'packet_size_limit_reached');
353
- return true;
354
- },
355
- () => {
356
- if (packet.windowEvidence.channels.length === 0)
357
- return false;
358
- packet.windowEvidence.channels.pop();
359
- return true;
360
- },
361
- ];
362
- while (Buffer.byteLength(serialized) > MAX_PACKET_BYTES) {
363
- let removed = false;
364
- for (const remove of removers) {
365
- if (remove()) {
366
- removed = true;
367
- break;
368
- }
369
- }
370
- if (!removed) {
371
- throw new Error('Owner report context cannot fit the canonical packet byte limit');
372
- }
373
- serialized = setPacketBytes(packet);
374
- }
375
- }
376
- function hasExactKeys(value, keys) {
377
- const actual = Object.keys(value);
378
- return actual.length === keys.length && actual.every((key) => keys.includes(key));
379
- }
380
- function isBoundedString(value, maxLength) {
381
- return typeof value === 'string' && value.length <= maxLength;
382
- }
383
- function isIsoString(value) {
384
- return isBoundedString(value, 64) && isFiniteDate(value);
385
- }
386
- function isSourceState(value) {
387
- if (!isRecord(value) ||
388
- (value.state !== 'complete' && value.state !== 'partial' && value.state !== 'unavailable')) {
389
- return false;
390
- }
391
- if (value.state === 'complete') {
392
- return hasExactKeys(value, ['state', 'observedAt']) && isIsoString(value.observedAt);
393
- }
394
- return (hasExactKeys(value, ['state', 'observedAt', 'reason']) &&
395
- (value.state === 'partial'
396
- ? value.observedAt === null || isIsoString(value.observedAt)
397
- : value.observedAt === null) &&
398
- isBoundedString(value.reason, 160) &&
399
- value.reason.length > 0);
400
- }
401
- function isReportWindowEvidence(value) {
402
- if (!isRecord(value) ||
403
- !hasExactKeys(value, [
404
- 'start',
405
- 'end',
406
- 'channelCount',
407
- 'messageCount',
408
- 'channels',
409
- 'triggerActivity',
410
- ]) ||
411
- !isIsoString(value.start) ||
412
- !isIsoString(value.end) ||
413
- !isSafeCount(value.channelCount) ||
414
- !isSafeCount(value.messageCount) ||
415
- !Array.isArray(value.channels) ||
416
- value.channels.length > 48 ||
417
- !Array.isArray(value.triggerActivity) ||
418
- value.triggerActivity.length > 100) {
419
- return false;
420
- }
421
- const channelsValid = value.channels.every((channel) => {
422
- if (!isRecord(channel) ||
423
- !hasExactKeys(channel, ['label', 'count', 'excerpts']) ||
424
- !isBoundedString(channel.label, 512) ||
425
- !isSafeCount(channel.count) ||
426
- !Array.isArray(channel.excerpts) ||
427
- channel.excerpts.length > 5) {
428
- return false;
429
- }
430
- return channel.excerpts.every((excerpt) => isRecord(excerpt) &&
431
- hasExactKeys(excerpt, ['authorLabel', 'text', 'observedAt']) &&
432
- isBoundedString(excerpt.authorLabel, 160) &&
433
- isBoundedString(excerpt.text, 160) &&
434
- (excerpt.observedAt === null || isIsoString(excerpt.observedAt)));
435
- });
436
- return (channelsValid &&
437
- value.triggerActivity.every((activity) => isRecord(activity) &&
438
- hasExactKeys(activity, ['kind', 'count', 'topics']) &&
439
- isBoundedString(activity.kind, 160) &&
440
- isSafeCount(activity.count) &&
441
- Array.isArray(activity.topics) &&
442
- activity.topics.every((topic) => isBoundedString(topic, 256))));
443
- }
444
- function isCurrentClaim(value) {
445
- return (isRecord(value) &&
446
- hasExactKeys(value, ['id', 'topic', 'summary', 'status', 'confidence']) &&
447
- isBoundedString(value.id, 512) &&
448
- isBoundedString(value.topic, 512) &&
449
- isBoundedString(value.summary, 4_000) &&
450
- isBoundedString(value.status, 64) &&
451
- typeof value.confidence === 'number' &&
452
- Number.isFinite(value.confidence));
453
- }
454
- function isOwnerTask(value) {
455
- return (isRecord(value) &&
456
- hasExactKeys(value, [
457
- 'id',
458
- 'revision',
459
- 'title',
460
- 'status',
461
- 'latestEvent',
462
- 'updatedAt',
463
- 'deadline',
464
- 'dueAt',
465
- 'sourceLabel',
466
- ]) &&
467
- isSafeCount(value.id) &&
468
- isSafeCount(value.revision) &&
469
- isBoundedString(value.title, 1_000) &&
470
- isBoundedString(value.status, 64) &&
471
- (value.latestEvent === null || isBoundedString(value.latestEvent, 1_000)) &&
472
- isIsoString(value.updatedAt) &&
473
- (value.deadline === null ||
474
- (typeof value.deadline === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value.deadline))) &&
475
- (value.dueAt === null || isIsoString(value.dueAt)) &&
476
- (value.sourceLabel === null ||
477
- (isBoundedString(value.sourceLabel, 64) &&
478
- SOURCE_DISPLAY_LABELS[value.sourceLabel] !== undefined)));
479
- }
480
- function isTrelloReport(value) {
481
- if (!isRecord(value) ||
482
- !hasExactKeys(value, ['observedAt', 'complete', 'truncated', 'boards', 'columns']) ||
483
- !isIsoString(value.observedAt) ||
484
- typeof value.complete !== 'boolean' ||
485
- typeof value.truncated !== 'boolean' ||
486
- !Array.isArray(value.boards) ||
487
- !Array.isArray(value.columns)) {
488
- return false;
489
- }
490
- const boardsValid = value.boards.every((board) => isRecord(board) &&
491
- hasExactKeys(board, ['board', 'status', 'rosterDegraded']) &&
492
- isBoundedString(board.board, 256) &&
493
- (board.status === 'ok' || board.status === 'failed') &&
494
- typeof board.rosterDegraded === 'boolean');
495
- let cardTotal = 0;
496
- const columnsValid = value.columns.every((column) => {
497
- if (!isRecord(column) ||
498
- !hasExactKeys(column, ['board', 'list', 'count', 'returned', 'cards']) ||
499
- !isBoundedString(column.board, 256) ||
500
- !isBoundedString(column.list, 256) ||
501
- !isSafeCount(column.count) ||
502
- !isSafeCount(column.returned) ||
503
- !Array.isArray(column.cards) ||
504
- column.cards.length > MAX_TRELLO_CARDS_PER_LIST ||
505
- column.returned !== column.cards.length ||
506
- column.count < column.returned) {
507
- return false;
508
- }
509
- cardTotal += column.cards.length;
510
- return column.cards.every((card) => isRecord(card) &&
511
- hasExactKeys(card, ['name', 'labels', 'assignees', 'due', 'lastActivity']) &&
512
- isBoundedString(card.name, 512) &&
513
- Array.isArray(card.labels) &&
514
- card.labels.length <= 20 &&
515
- card.labels.every((label) => isBoundedString(label, 128)) &&
516
- Array.isArray(card.assignees) &&
517
- card.assignees.length <= 20 &&
518
- card.assignees.every((assignee) => isBoundedString(assignee, 128)) &&
519
- (card.due === null || isBoundedString(card.due, 64)) &&
520
- isBoundedString(card.lastActivity, 64));
521
- });
522
- return boardsValid && columnsValid && cardTotal <= MAX_TRELLO_CARDS;
523
- }
524
- const CORRELATION_OUTCOMES = new Set([
525
- 'matched',
526
- 'unmatched',
527
- 'ambiguous',
528
- 'historical_only',
529
- 'not_applicable',
530
- ]);
531
- const CORRELATION_REASONS = new Set([
532
- 'no_source',
533
- 'other_connector',
534
- 'no_provenance',
535
- 'provenance_not_indexed',
536
- 'provenance_connector_mismatch',
537
- 'external_ref_unresolvable',
538
- 'provenance_conflict',
539
- 'multiple_rows_one_item',
540
- 'absent_from_live_snapshot',
541
- 'live_snapshot_incomplete',
542
- 'live_item',
543
- ]);
544
- function isCorrelationReport(value) {
545
- if (!isRecord(value) ||
546
- !hasExactKeys(value, ['coverage', 'rows']) ||
547
- !isRecord(value.coverage) ||
548
- !hasExactKeys(value.coverage, [
549
- 'total',
550
- 'matched',
551
- 'unmatched',
552
- 'ambiguous',
553
- 'historical_only',
554
- 'not_applicable',
555
- ]) ||
556
- !Array.isArray(value.rows)) {
557
- return false;
558
- }
559
- const counts = {
560
- matched: 0,
561
- unmatched: 0,
562
- ambiguous: 0,
563
- historical_only: 0,
564
- not_applicable: 0,
565
- };
566
- const coverage = value.coverage;
567
- const rowsValid = value.rows.every((row) => {
568
- if (!isRecord(row) ||
569
- !hasExactKeys(row, ['taskId', 'outcome', 'reason', 'live']) ||
570
- !isSafeCount(row.taskId) ||
571
- typeof row.outcome !== 'string' ||
572
- !CORRELATION_OUTCOMES.has(row.outcome) ||
573
- typeof row.reason !== 'string' ||
574
- !CORRELATION_REASONS.has(row.reason) ||
575
- !(row.live === null ||
576
- (isRecord(row.live) &&
577
- hasExactKeys(row.live, ['board', 'list']) &&
578
- isBoundedString(row.live.board, 256) &&
579
- isBoundedString(row.live.list, 256)))) {
580
- return false;
581
- }
582
- counts[row.outcome] += 1;
583
- return true;
584
- });
585
- return (rowsValid &&
586
- isSafeCount(coverage.total) &&
587
- coverage.total === value.rows.length &&
588
- [...CORRELATION_OUTCOMES].every((outcome) => isSafeCount(coverage[outcome]) && coverage[outcome] === counts[outcome]));
589
- }
590
- function isChangesReport(value) {
591
- return (isRecord(value) &&
592
- hasExactKeys(value, ['since', 'total', 'returned', 'coverage', 'rows']) &&
593
- isIsoString(value.since) &&
594
- isSafeCount(value.total) &&
595
- isSafeCount(value.returned) &&
596
- isRecord(value.coverage) &&
597
- hasExactKeys(value.coverage, ['attributed', 'unattributed']) &&
598
- isSafeCount(value.coverage.attributed) &&
599
- isSafeCount(value.coverage.unattributed) &&
600
- Array.isArray(value.rows) &&
601
- value.rows.length <= MAX_CHANGES &&
602
- value.returned === value.rows.length &&
603
- value.total >= value.returned &&
604
- value.rows.every((row) => isRecord(row) &&
605
- hasExactKeys(row, ['kind', 'targetType', 'causeState', 'causeKind', 'at']) &&
606
- isBoundedString(row.kind, 160) &&
607
- isBoundedString(row.targetType, 160) &&
608
- (row.causeState === 'attributed' || row.causeState === 'unattributed') &&
609
- isBoundedString(row.causeKind, 160) &&
610
- isIsoString(row.at)));
611
- }
612
- function isOwnerReportContext(value) {
613
- if (!isRecord(value) ||
614
- !hasExactKeys(value, [
615
- 'schemaVersion',
616
- 'observedAt',
617
- 'windowEvidence',
618
- 'sources',
619
- 'packet',
620
- 'taskCoverage',
621
- 'currentClaims',
622
- 'tasks',
623
- 'trello',
624
- 'correlations',
625
- 'changes',
626
- 'caveats',
627
- 'operationalIssues',
628
- ]) ||
629
- value.schemaVersion !== 'mama.owner-report-context/v1' ||
630
- !isIsoString(value.observedAt) ||
631
- !isReportWindowEvidence(value.windowEvidence) ||
632
- !isRecord(value.sources) ||
633
- !hasExactKeys(value.sources, ['claims', 'tasks', 'trello', 'changes']) ||
634
- !Object.values(value.sources).every(isSourceState) ||
635
- !isRecord(value.packet) ||
636
- !hasExactKeys(value.packet, ['bytes', 'truncated']) ||
637
- !isSafeCount(value.packet.bytes) ||
638
- typeof value.packet.truncated !== 'boolean' ||
639
- !isRecord(value.taskCoverage) ||
640
- !hasExactKeys(value.taskCoverage, ['total', 'returned', 'truncated']) ||
641
- !isSafeCount(value.taskCoverage.total) ||
642
- !isSafeCount(value.taskCoverage.returned) ||
643
- value.taskCoverage.total < value.taskCoverage.returned ||
644
- typeof value.taskCoverage.truncated !== 'boolean' ||
645
- !Array.isArray(value.currentClaims) ||
646
- value.currentClaims.length > MAX_CLAIMS ||
647
- !value.currentClaims.every(isCurrentClaim) ||
648
- !Array.isArray(value.tasks) ||
649
- !value.tasks.every(isOwnerTask) ||
650
- !isTrelloReport(value.trello) ||
651
- !isCorrelationReport(value.correlations) ||
652
- !isChangesReport(value.changes) ||
653
- !Array.isArray(value.caveats) ||
654
- !value.caveats.every((caveat) => isBoundedString(caveat, 160)) ||
655
- !Array.isArray(value.operationalIssues) ||
656
- value.operationalIssues.length > exports.MAX_OPERATIONAL_ISSUES ||
657
- !value.operationalIssues.every(isOperationalIssueSummary)) {
658
- return false;
659
- }
660
- return true;
661
- }
662
- function isOperationalIssueSummary(value) {
663
- if (typeof value !== 'object' || value === null) {
664
- return false;
665
- }
666
- const issue = value;
667
- return (isBoundedString(issue.issueId, 64) &&
668
- isBoundedString(issue.surface, 32) &&
669
- (issue.severity === 'info' || issue.severity === 'warn' || issue.severity === 'error') &&
670
- isBoundedString(issue.summary, 300) &&
671
- Number.isSafeInteger(issue.occurrences) &&
672
- issue.occurrences >= 1 &&
673
- isBoundedString(issue.firstSeenAt, 40));
674
- }
675
- function serializeOwnerReportContext(packet) {
676
- if (!isOwnerReportContext(packet)) {
677
- throw new Error('Invalid owner report context packet');
678
- }
679
- const detached = JSON.parse(JSON.stringify(packet));
680
- const serialized = canonicalJson(detached);
681
- const bytes = Buffer.byteLength(serialized);
682
- if (bytes > MAX_PACKET_BYTES || detached.packet.bytes !== bytes) {
683
- throw new Error('Invalid owner report context packet');
684
- }
685
- return serialized;
686
- }
687
- exports.MAX_OPERATIONAL_ISSUES = 20;
688
- const SEVERITY_ORDER = { error: 0, warn: 1, info: 2 };
689
- /** The system's open failures, redacted upstream, highest severity first, bounded. */
690
- function readOperationalIssuesBounded(deps) {
691
- if (!deps.readOperationalIssues) {
692
- return [];
693
- }
694
- return [...deps.readOperationalIssues()]
695
- .sort((a, b) => (SEVERITY_ORDER[a.severity] ?? 3) - (SEVERITY_ORDER[b.severity] ?? 3))
696
- .slice(0, exports.MAX_OPERATIONAL_ISSUES)
697
- .map((issue) => ({
698
- issueId: redactText(issue.issueId, 64),
699
- surface: redactText(issue.surface, 32),
700
- severity: issue.severity,
701
- summary: redactText(issue.summary, 300),
702
- occurrences: issue.occurrences,
703
- firstSeenAt: issue.firstSeenAt,
704
- }));
705
- }
706
- async function compileOwnerReportContext(input, deps) {
707
- assertInput(input);
708
- const nowMs = deps.now();
709
- if (!Number.isSafeInteger(nowMs) || nowMs < 0) {
710
- throw new Error('Invalid owner report context clock');
711
- }
712
- const observedAt = new Date(nowMs).toISOString();
713
- const readScope = detachScope(input.readScope);
714
- const caveats = [];
715
- const taskRows = [];
716
- let taskTotal = 0;
717
- let taskState = { state: 'complete', observedAt };
718
- try {
719
- let cursor;
720
- const seenCursors = new Set();
721
- do {
722
- // Real-data verification (2026-09-02): 264 open owner tasks against a 50-row
723
- // bound. Deadline-first ranking filled the packet with month-old deadline
724
- // rows and displaced every task updated in the last week, including all
725
- // in_progress work - the owner report answers "what is open NOW", so recency
726
- // ranks the bounded set. Stale deadline work stays the Board lane's job.
727
- const page = deps.listTaskPage({
728
- includeTerminal: false,
729
- order: 'updated',
730
- limit: TASK_PAGE_SIZE,
731
- ...(cursor ? { cursor } : {}),
732
- });
733
- if (!isSafeCount(page.total) || !Array.isArray(page.tasks)) {
734
- throw new Error('invalid task page');
735
- }
736
- if (taskRows.length === 0)
737
- taskTotal = page.total;
738
- taskRows.push(...page.tasks);
739
- const next = page.nextCursor ?? undefined;
740
- if (next && seenCursors.has(next))
741
- throw new Error('task cursor repeated');
742
- if (next)
743
- seenCursors.add(next);
744
- cursor = next;
745
- } while (cursor);
746
- if (taskTotal > taskRows.length) {
747
- taskState = { state: 'partial', observedAt, reason: 'task_limit_reached' };
748
- caveats.push('task_set_truncated');
749
- }
750
- }
751
- catch {
752
- taskState =
753
- taskRows.length > 0
754
- ? { state: 'partial', observedAt, reason: 'tasks_read_failed' }
755
- : { state: 'unavailable', observedAt: null, reason: 'tasks_read_failed' };
756
- caveats.push('tasks_unavailable');
757
- }
758
- let claims = [];
759
- let claimsState = { state: 'complete', observedAt };
760
- try {
761
- claims = await deps.readClaims(readScope);
762
- }
763
- catch {
764
- claimsState = { state: 'unavailable', observedAt: null, reason: 'claims_read_failed' };
765
- caveats.push('claims_unavailable');
766
- }
767
- claims.sort((left, right) => (right.updated_at ?? right.created_at ?? 0) - (left.updated_at ?? left.created_at ?? 0) ||
768
- left.memory_id.localeCompare(right.memory_id));
769
- if (claims.length > MAX_CLAIMS) {
770
- claims = claims.slice(0, MAX_CLAIMS);
771
- claimsState = { state: 'partial', observedAt, reason: 'claims_limit_reached' };
772
- caveats.push('claims_truncated');
773
- }
774
- let rawTrello = null;
775
- let trelloState;
776
- if (!readScope.rawConnectors.includes('trello')) {
777
- trelloState = { state: 'unavailable', observedAt: null, reason: 'trello_not_authorized' };
778
- caveats.push('trello_unavailable');
779
- }
780
- else {
781
- try {
782
- rawTrello = await deps.readTrello(readScope);
783
- if (rawTrello.boards.length === 0) {
784
- trelloState = {
785
- state: 'unavailable',
786
- observedAt: null,
787
- reason: 'trello_no_authorized_boards',
788
- };
789
- caveats.push('trello_unavailable');
790
- }
791
- else if (!rawTrello.complete || rawTrello.truncated) {
792
- trelloState = {
793
- state: 'partial',
794
- observedAt: rawTrello.observedAt,
795
- reason: 'trello_snapshot_incomplete',
796
- };
797
- caveats.push('trello_snapshot_incomplete');
798
- }
799
- else {
800
- trelloState = { state: 'complete', observedAt: rawTrello.observedAt };
801
- }
802
- }
803
- catch {
804
- trelloState = { state: 'unavailable', observedAt: null, reason: 'trello_read_failed' };
805
- caveats.push('trello_unavailable');
806
- }
807
- }
808
- const trello = {
809
- observedAt: rawTrello?.observedAt ?? observedAt,
810
- complete: rawTrello !== null && rawTrello.boards.length > 0 && rawTrello.complete,
811
- truncated: rawTrello?.truncated ?? false,
812
- boards: (rawTrello?.boards ?? [])
813
- .map((board) => ({
814
- board: redactText(board.board, 256),
815
- status: board.status,
816
- rosterDegraded: board.rosterDegraded,
817
- }))
818
- .sort((left, right) => left.board.localeCompare(right.board)),
819
- columns: [],
820
- };
821
- let remainingCards = MAX_TRELLO_CARDS;
822
- for (const column of [...(rawTrello?.columns ?? [])].sort((left, right) => left.board.localeCompare(right.board) || left.list.localeCompare(right.list))) {
823
- const sortedCards = [...column.cards].sort((left, right) => left.name.localeCompare(right.name) || left.lastActivity.localeCompare(right.lastActivity));
824
- const allowed = Math.max(0, Math.min(MAX_TRELLO_CARDS_PER_LIST, remainingCards));
825
- const cards = sortedCards.slice(0, allowed).map((card) => ({
826
- name: redactText(card.name, 512),
827
- labels: card.labels
828
- .slice(0, 20)
829
- .map((label) => redactText(label, 128))
830
- .sort(),
831
- assignees: card.assignees
832
- .slice(0, 20)
833
- .map((assignee) => redactText(assignee, 128))
834
- .sort(),
835
- due: toIsoInstant(card.due),
836
- lastActivity: toIsoInstant(card.lastActivity) ?? observedAt,
837
- }));
838
- remainingCards -= cards.length;
839
- if (cards.length < column.cards.length || cards.length < column.returned) {
840
- trello.truncated = true;
841
- trello.complete = false;
842
- }
843
- trello.columns.push({
844
- board: redactText(column.board, 256),
845
- list: redactText(column.list, 256),
846
- count: column.count,
847
- returned: cards.length,
848
- cards,
849
- });
850
- }
851
- if (trello.truncated && trelloState.state !== 'unavailable') {
852
- trelloState = {
853
- state: 'partial',
854
- observedAt: rawTrello?.observedAt ?? null,
855
- reason: 'trello_card_limit_reached',
856
- };
857
- if (!caveats.includes('trello_snapshot_incomplete'))
858
- caveats.push('trello_snapshot_incomplete');
859
- }
860
- let correlation = emptyCorrelation();
861
- try {
862
- const lookupProvenance = rawTrello === null ? () => null : await deps.buildProvenanceLookup();
863
- const rawResult = deps.correlate({
864
- connector: 'trello',
865
- rows: taskRows.map((row) => ({
866
- id: row.id,
867
- sourceChannel: row.sourceChannel,
868
- sourceEventId: row.sourceEventId,
869
- })),
870
- lookupProvenance,
871
- liveItems: (rawTrello?.columns ?? []).flatMap((column) => column.cards.map((card) => ({
872
- itemId: card.cardId,
873
- board: column.board,
874
- list: column.list,
875
- }))),
876
- liveSnapshotComplete: trelloState.state === 'complete',
877
- });
878
- correlation = {
879
- coverage: { ...rawResult.coverage },
880
- rows: rawResult.correlations
881
- .map((row) => ({
882
- taskId: row.taskId,
883
- outcome: row.outcome,
884
- reason: row.reason,
885
- live: row.live
886
- ? { board: redactText(row.live.board, 256), list: redactText(row.live.list, 256) }
887
- : null,
888
- }))
889
- .sort((left, right) => left.taskId - right.taskId),
890
- };
891
- }
892
- catch {
893
- caveats.push('correlation_unavailable');
894
- }
895
- let changes = {
896
- since: new Date(input.since).toISOString(),
897
- total: 0,
898
- returned: 0,
899
- coverage: { attributed: 0, unattributed: 0 },
900
- rows: [],
901
- };
902
- let changesState = { state: 'complete', observedAt };
903
- try {
904
- const rawChanges = deps.readChanges(readScope, { since: input.since, limit: MAX_CHANGES }, nowMs);
905
- if (!rawChanges.success) {
906
- changesState = { state: 'unavailable', observedAt: null, reason: 'changes_read_failed' };
907
- caveats.push('changes_unavailable');
908
- }
909
- else {
910
- const normalizedRows = rawChanges.changes.slice(0, MAX_CHANGES).map((change) => ({
911
- kind: redactText(change.kind, 160),
912
- targetType: redactText(change.target_type, 160),
913
- causeState: change.cause_state,
914
- causeKind: redactText(change.cause_kind, 160),
915
- at: toIsoInstant(change.at),
916
- }));
917
- const rows = normalizedRows.flatMap((row) => row.at === null ? [] : [{ ...row, at: row.at }]);
918
- changes = {
919
- since: rawChanges.since,
920
- total: rawChanges.total,
921
- returned: rows.length,
922
- coverage: { ...rawChanges.coverage },
923
- rows,
924
- };
925
- if (rows.length < normalizedRows.length) {
926
- // A change whose time cannot be read is not silently redated; the source is
927
- // marked partial so the report says the ledger was not fully readable.
928
- changesState = { state: 'partial', observedAt, reason: 'changes_row_time_invalid' };
929
- caveats.push('changes_rows_dropped');
930
- }
931
- else if (rawChanges.total > rows.length || rawChanges.returned > rows.length) {
932
- changesState = { state: 'partial', observedAt, reason: 'changes_limit_reached' };
933
- caveats.push('changes_truncated');
934
- }
935
- }
936
- }
937
- catch {
938
- changesState = { state: 'unavailable', observedAt: null, reason: 'changes_read_failed' };
939
- caveats.push('changes_unavailable');
940
- }
941
- const windowEvidence = sanitizeWindow(input.windowEvidence);
942
- if (windowEvidence.channels.some((channel) => channel.excerpts.some((item) => item.observedAt === null))) {
943
- caveats.push('window_evidence_timestamp_unavailable');
944
- }
945
- const packet = {
946
- schemaVersion: 'mama.owner-report-context/v1',
947
- observedAt,
948
- windowEvidence,
949
- sources: {
950
- claims: claimsState,
951
- tasks: taskState,
952
- trello: trelloState,
953
- changes: changesState,
954
- },
955
- packet: { bytes: 0, truncated: false },
956
- taskCoverage: {
957
- total: taskTotal,
958
- returned: taskRows.length,
959
- truncated: taskTotal > taskRows.length,
960
- },
961
- operationalIssues: readOperationalIssuesBounded(deps),
962
- currentClaims: claims.map((row) => ({
963
- id: redactText(row.memory_id, 512),
964
- topic: redactText(row.topic, 512),
965
- summary: redactText(row.effective_summary, 4_000),
966
- status: redactText(row.truth_status, 64),
967
- confidence: row.trust_score,
968
- })),
969
- tasks: taskRows.map((row) => ({
970
- id: row.id,
971
- revision: row.revision,
972
- title: redactText(row.title, 1_000),
973
- status: redactText(row.status, 64),
974
- latestEvent: row.latestEvent === null ? null : redactText(row.latestEvent, 1_000),
975
- updatedAt: new Date(row.updatedAt).toISOString(),
976
- deadline: row.deadlineIso,
977
- dueAt: row.dueAt === null ? null : new Date(row.dueAt).toISOString(),
978
- sourceLabel: sourceLabel(row.sourceChannel, readScope.rawConnectors),
979
- })),
980
- trello,
981
- correlations: correlation,
982
- changes,
983
- caveats: [...new Set(caveats)].sort(),
984
- };
985
- const detached = JSON.parse(JSON.stringify(packet));
986
- enforcePacketLimit(detached);
987
- setPacketBytes(detached);
988
- if (!isOwnerReportContext(detached)) {
989
- throw new Error('Owner report context failed canonical schema validation');
990
- }
991
- return detached;
992
- }
993
- /**
994
- * One MAMA (Phase 1 Task 3): the event turn starts from the same host-compiled
995
- * packet as the report, narrowed to its channel. The read scope MUST be the
996
- * owner-event scope the run's envelope holds - reading under another principal
997
- * is the mismatch that fails the envelope scope audit.
998
- */
999
- async function compileChannelPacket(input, deps) {
1000
- const separator = input.channelKey.indexOf(':');
1001
- const connector = separator > 0 ? input.channelKey.slice(0, separator) : input.channelKey;
1002
- // The packet validator only accepts sourceLabels from SOURCE_DISPLAY_LABELS, so an
1003
- // unknown connector fails OPEN: keep every row rather than filter on a label no
1004
- // row can carry.
1005
- const label = SOURCE_DISPLAY_LABELS[connector] ?? null;
1006
- const end = new Date(deps.now()).toISOString();
1007
- const full = await compileOwnerReportContext({
1008
- readScope: input.readScope,
1009
- since: input.since,
1010
- windowEvidence: {
1011
- start: input.since,
1012
- end,
1013
- channelCount: 1,
1014
- messageCount: input.eventIds.length,
1015
- channels: [{ label: label ?? connector, count: input.eventIds.length, excerpts: [] }],
1016
- triggerActivity: [],
1017
- },
1018
- }, deps);
1019
- const keep = new Set(full.tasks
1020
- .filter((row) => label === null ||
1021
- row.sourceLabel === label ||
1022
- row.status === 'in_progress' ||
1023
- row.status === 'review')
1024
- .map((row) => row.id));
1025
- const tasks = full.tasks.filter((row) => keep.has(row.id));
1026
- const rows = full.correlations.rows.filter((row) => keep.has(row.taskId));
1027
- // The recency bound is applied to the whole ledger before this channel filter, so a
1028
- // channel whose rows were not updated recently can come out empty while its tasks
1029
- // exist. Say so, or the turn will create the task a second time.
1030
- const caveats = tasks.length === 0 && full.taskCoverage.total > 0
1031
- ? [
1032
- ...full.caveats,
1033
- // Bounded to the caveat cap (160 chars) even for the longest label.
1034
- `channel_has_no_matching_task: ${(label ?? connector).slice(0, 24)} has no task among the ${full.tasks.length} board rows; task_list allowed`,
1035
- ]
1036
- : full.caveats;
1037
- const packet = {
1038
- ...full,
1039
- caveats,
1040
- tasks,
1041
- taskCoverage: {
1042
- total: full.taskCoverage.total,
1043
- returned: tasks.length,
1044
- truncated: full.taskCoverage.truncated,
1045
- },
1046
- correlations: { coverage: { ...full.correlations.coverage }, rows },
1047
- };
1048
- recomputeCorrelationCoverage(packet.correlations);
1049
- setPacketBytes(packet);
1050
- return packet;
1051
- }
1052
- //# sourceMappingURL=report-context.js.map