@agentshouse/mdruntime-adapter 0.1.0-alpha.8 → 0.1.0-c6229cc476473

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 (60) hide show
  1. package/AGENTS.md +157 -26
  2. package/README.md +32 -0
  3. package/dist/actions.d.ts +4 -12
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +239 -332
  6. package/dist/adapter.d.ts.map +1 -1
  7. package/dist/adapter.js +259 -194
  8. package/dist/digest.js +1 -5
  9. package/dist/discovery.d.ts +20 -18
  10. package/dist/discovery.d.ts.map +1 -1
  11. package/dist/discovery.js +182 -210
  12. package/dist/gather.d.ts +3 -0
  13. package/dist/gather.d.ts.map +1 -0
  14. package/dist/gather.js +40 -0
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +0 -2
  18. package/dist/links.d.ts +12 -0
  19. package/dist/links.d.ts.map +1 -0
  20. package/dist/links.js +66 -0
  21. package/dist/map.d.ts +2 -2
  22. package/dist/map.d.ts.map +1 -1
  23. package/dist/map.js +6 -7
  24. package/dist/model.d.ts +2 -2
  25. package/dist/model.d.ts.map +1 -1
  26. package/dist/model.js +21 -39
  27. package/dist/read.d.ts +29 -23
  28. package/dist/read.d.ts.map +1 -1
  29. package/dist/read.js +222 -195
  30. package/dist/refusal.d.ts.map +1 -1
  31. package/dist/refusal.js +30 -51
  32. package/dist/site.d.ts.map +1 -1
  33. package/dist/site.js +110 -124
  34. package/dist/stream.d.ts +11 -5
  35. package/dist/stream.d.ts.map +1 -1
  36. package/dist/stream.js +211 -213
  37. package/dist/transport.d.ts +46 -28
  38. package/dist/transport.d.ts.map +1 -1
  39. package/dist/transport.js +3 -3
  40. package/dist/version.d.ts +1 -0
  41. package/dist/version.d.ts.map +1 -1
  42. package/dist/version.js +23 -18
  43. package/dist/write.d.ts +9 -3
  44. package/dist/write.d.ts.map +1 -1
  45. package/dist/write.js +123 -196
  46. package/package.json +4 -12
  47. package/dist/actions.js.map +0 -1
  48. package/dist/adapter.js.map +0 -1
  49. package/dist/digest.js.map +0 -1
  50. package/dist/discovery.js.map +0 -1
  51. package/dist/index.js.map +0 -1
  52. package/dist/map.js.map +0 -1
  53. package/dist/model.js.map +0 -1
  54. package/dist/read.js.map +0 -1
  55. package/dist/refusal.js.map +0 -1
  56. package/dist/site.js.map +0 -1
  57. package/dist/stream.js.map +0 -1
  58. package/dist/transport.js.map +0 -1
  59. package/dist/version.js.map +0 -1
  60. package/dist/write.js.map +0 -1
package/dist/actions.js CHANGED
@@ -1,121 +1,59 @@
1
- import { VERSION } from '@agentshouse/mdmodel';
2
- import { changeBatchIdentity } from '@agentshouse/mdruntime';
1
+ import { changeBatchIdentity, } from '@agentshouse/mdruntime';
3
2
  import { doorRefusal } from './refusal.js';
4
- import { PRIVATE_AUTHORITY } from './transport.js';
5
- export const CONVERSATION_ACTIONS = [
6
- 'conversation.create',
7
- 'conversation.send',
8
- 'conversation.answer',
9
- 'conversation.interrupt',
10
- 'conversation.kill',
11
- 'conversation.cancel',
12
- 'conversation.delete',
13
- 'conversation.history'
14
- ];
15
- export const HOUSE_ACTS = [
16
- 'conversations/interaction/read',
17
- 'conversations/originals/read',
18
- 'apps/read',
19
- 'apps/customize',
20
- 'rooms/read',
21
- 'rooms/access/read',
22
- 'rooms/access/invitations',
23
- 'rooms/access/entries',
24
- 'rooms/access/invite',
25
- 'rooms/access/invitation-cancel',
26
- 'rooms/access/revoke',
27
- 'rooms/access/leave',
28
- 'rooms/roles/define',
29
- 'rooms/roles/remove',
30
- 'rooms/roles/assign',
31
- 'rooms/roles/unassign',
32
- 'rooms/settings/processing/read',
33
- 'rooms/settings/processing/write',
34
- 'profiles/create',
35
- 'profiles/read',
36
- 'profiles/edit',
37
- 'profiles/duplicate',
38
- 'profiles/delegate',
39
- 'agents/create',
40
- 'agents/read',
41
- 'routines/requests',
42
- 'routines/access',
43
- 'routines/owned',
44
- 'routines/granted',
45
- 'routines/grant',
46
- 'routines/send',
47
- 'routines/cancel',
48
- 'routines/start',
49
- 'routines/schedules',
50
- 'routines/schedules/create',
51
- 'routines/schedules/enabled',
52
- 'account/security/read',
53
- 'storage/inventory',
54
- 'storage/usage',
55
- 'usage/statement',
56
- 'storage/blob-store',
57
- 'storage/blob-store/select',
58
- 'storage/connections',
59
- 'storage/archive',
60
- 'storage/archive/status',
61
- 'environments/read',
62
- 'environments/create',
63
- 'environments/start',
64
- 'environments/stop',
65
- 'environments/delete',
66
- 'environments/renewals/read',
67
- 'environments/work/read',
68
- 'nodes/read'
69
- ];
70
- export const HOUSE_PROVIDER_ACTIONS = [
71
- ...CONVERSATION_ACTIONS,
72
- ...HOUSE_ACTS
73
- ];
74
- const DECLARED = new Set(HOUSE_PROVIDER_ACTIONS);
75
- export const HISTORY_PAGE = 100;
3
+ import { PRIVATE_LOCATOR } from './transport.js';
4
+ const HISTORY_PAGE = 100;
5
+ const HISTORY_CONVERSATIONS = 64;
76
6
  const OCTET_STREAM = 'application/octet-stream';
77
- const CONVERSATION_ROUTES = {
78
- 'conversation.create': '/door/conversations/create',
79
- 'conversation.send': '/door/conversations/send',
80
- 'conversation.answer': '/door/conversations/interaction/answer',
81
- 'conversation.interrupt': '/door/conversations/interrupt',
82
- 'conversation.kill': '/door/conversations/kill',
83
- 'conversation.cancel': '/door/conversations/cancel',
84
- 'conversation.delete': '/door/conversations/delete',
85
- 'conversation.history': '/door/conversations/history',
86
- 'conversations/interaction/read': '/door/conversations/interaction/read',
87
- 'conversations/originals/read': '/door/conversations/originals/read'
88
- };
7
+ const CONVERSATION_ROUTES = new Map([
8
+ ['conversation.create', '/door/conversations/create'],
9
+ ['conversation.send', '/door/conversations/send'],
10
+ ['conversation.answer', '/door/conversations/interaction/answer'],
11
+ ['conversation.interrupt', '/door/conversations/interrupt'],
12
+ ['conversation.kill', '/door/conversations/kill'],
13
+ ['conversation.delete', '/door/conversations/delete'],
14
+ ['conversation.history', '/door/conversations/history'],
15
+ ]);
89
16
  export const HOUSE_CONVERSATION_CARDS = {
90
- locator: PRIVATE_AUTHORITY,
91
- type: 'conversation',
17
+ locator: PRIVATE_LOCATOR,
18
+ type: 'house-conversation',
92
19
  identity: 'conversation',
93
- agent: 'agent'
20
+ agent: 'agent',
94
21
  };
95
- export function declaredActions() {
96
- return HOUSE_PROVIDER_ACTIONS.map((name)=>({
97
- name,
98
- inputShape: {},
99
- resultKind: 'house'
100
- }));
22
+ export function declaredActions(routes) {
23
+ const mapped = new Map([...CONVERSATION_ROUTES].map(([name, route]) => [route.slice('/door/'.length), name]));
24
+ return routes.map((route) => ({
25
+ name: mapped.get(route) ?? route,
26
+ inputShape: {},
27
+ resultKind: 'house',
28
+ }));
101
29
  }
102
30
  export function houseAction(transport, request) {
103
- if (!DECLARED.has(request.name)) {
104
- return Promise.resolve({
105
- kind: 'rejected',
106
- code: 'action-unimplemented',
107
- fields: {
108
- name: request.name,
109
- mdModel: VERSION
110
- }
111
- });
112
- }
113
- if (request.name === 'conversation.send') return sendAction(transport, request.input);
114
- if (request.name === 'conversation.history') return historyAction(transport, request.input);
115
- return carry(transport, request.name, request.input);
31
+ if (request.name === 'conversation.send')
32
+ return sendAction(transport, request.input);
33
+ if (request.name === 'conversation.history')
34
+ return historyAction(transport, request.input);
35
+ if (request.name === 'attachments/upload')
36
+ return attachAction(transport, request.input);
37
+ if (request.name === 'attachments/get')
38
+ return openAction(transport, request.input);
39
+ const route = routeOf(request.name);
40
+ if (route === null)
41
+ return Promise.resolve(malformed());
42
+ return carry(transport, route, request.name, request.input);
116
43
  }
117
44
  function routeOf(name) {
118
- return CONVERSATION_ROUTES[name] ?? `/door/${name}`;
45
+ const mapped = CONVERSATION_ROUTES.get(name);
46
+ if (mapped !== undefined)
47
+ return mapped;
48
+ const parts = name.split('/');
49
+ if (parts.some((part) => part === '.' || part === '..'))
50
+ return null;
51
+ try {
52
+ return `/door/${parts.map(encodeURIComponent).join('/')}`;
53
+ }
54
+ catch {
55
+ return null;
56
+ }
119
57
  }
120
58
  function record(value) {
121
59
  return typeof value === 'object' && value !== null ? value : {};
@@ -128,31 +66,16 @@ function operationId(input) {
128
66
  return text(input, 'operation_id') ?? changeBatchIdentity();
129
67
  }
130
68
  function malformed() {
131
- return {
132
- kind: 'rejected',
133
- code: 'invalid_request',
134
- fields: {}
135
- };
69
+ return { kind: 'rejected', code: 'invalid_request', fields: {} };
136
70
  }
137
71
  function bodyOf(name, input) {
138
72
  if (name === 'conversation.create') {
139
- return {
140
- agent_id: text(input, 'agent_id'),
141
- operation_id: operationId(input)
142
- };
143
- }
144
- if (name === 'conversation.interrupt' || name === 'conversation.kill' || name === 'conversation.delete') {
145
- return {
146
- conversation_id: text(input, 'conversation'),
147
- operation_id: operationId(input)
148
- };
73
+ return { agent_id: text(input, 'agent_id'), operation_id: operationId(input) };
149
74
  }
150
- if (name === 'conversation.cancel') {
151
- return {
152
- conversation_id: text(input, 'conversation'),
153
- message_id: text(input, 'turn'),
154
- operation_id: operationId(input)
155
- };
75
+ if (name === 'conversation.interrupt' ||
76
+ name === 'conversation.kill' ||
77
+ name === 'conversation.delete') {
78
+ return { conversation_id: text(input, 'conversation'), operation_id: operationId(input) };
156
79
  }
157
80
  if (name === 'conversation.answer') {
158
81
  const nested = record(record(input).answer);
@@ -160,8 +83,7 @@ function bodyOf(name, input) {
160
83
  conversation_id: text(input, 'conversation'),
161
84
  operation_id: operationId(input),
162
85
  interaction_id: text(nested, 'interaction_id'),
163
- revision: nested.revision,
164
- response: nested.response
86
+ response: nested.response,
165
87
  };
166
88
  }
167
89
  return input;
@@ -169,37 +91,25 @@ function bodyOf(name, input) {
169
91
  function withTurn(body) {
170
92
  const held = record(body);
171
93
  const turn = text(held, 'message_id');
172
- return turn === undefined ? body : {
173
- ...held,
174
- turn
175
- };
94
+ return turn === undefined ? body : { ...held, turn };
176
95
  }
177
96
  export function actionOutcome(answer, name) {
178
- if (answer.status === 401) return {
179
- kind: 'revoked-authorization'
180
- };
97
+ if (answer.status === 401)
98
+ return { kind: 'revoked-authorization' };
181
99
  const refusal = doorRefusal(answer.body);
182
100
  if (refusal !== null) {
183
- return {
184
- kind: 'rejected',
185
- code: refusal.code,
186
- fields: refusal.fields
187
- };
101
+ return { kind: 'rejected', code: refusal.code, fields: refusal.fields };
188
102
  }
189
- if (answer.status === 0 || answer.status >= 400) return {
190
- kind: 'transport-uncertainty'
191
- };
192
- if (name === 'conversation.send') return {
193
- kind: 'action',
194
- result: withTurn(answer.body)
195
- };
196
- return {
197
- kind: 'action',
198
- result: answer.body
199
- };
103
+ if (answer.status === 404)
104
+ return { kind: 'rejected', code: 'action-unimplemented', fields: { name } };
105
+ if (answer.status === 0 || answer.status >= 400)
106
+ return { kind: 'transport-uncertainty' };
107
+ if (name === 'conversation.send')
108
+ return { kind: 'action', result: withTurn(answer.body) };
109
+ return { kind: 'action', result: answer.body };
200
110
  }
201
- async function carry(transport, name, input) {
202
- const answer = await transport.door(routeOf(name), bodyOf(name, input));
111
+ async function carry(transport, route, name, input) {
112
+ const answer = await transport.door(route, bodyOf(name, input));
203
113
  return actionOutcome(answer, name);
204
114
  }
205
115
  function sendFile(entry) {
@@ -207,7 +117,8 @@ function sendFile(entry) {
207
117
  const name = text(file, 'name');
208
118
  const bytes = file.byteLength;
209
119
  const sha256 = text(file, 'contentHash');
210
- if (name === undefined || typeof bytes !== 'number' || sha256 === undefined) return null;
120
+ if (name === undefined || typeof bytes !== 'number' || sha256 === undefined)
121
+ return null;
211
122
  const mediaType = text(file, 'mediaType');
212
123
  const width = file.width;
213
124
  const height = file.height;
@@ -216,239 +127,235 @@ function sendFile(entry) {
216
127
  media_type: mediaType !== undefined && mediaType.length > 0 ? mediaType : OCTET_STREAM,
217
128
  bytes,
218
129
  sha256,
219
- ...typeof width === 'number' && typeof height === 'number' ? {
220
- dimensions: {
221
- width,
222
- height
223
- }
224
- } : {}
130
+ ...(typeof width === 'number' && typeof height === 'number'
131
+ ? { dimensions: { width, height } }
132
+ : {}),
225
133
  };
226
134
  }
227
135
  function sendFiles(input) {
228
136
  const files = record(input).files;
229
- if (files === undefined) return [];
230
- if (!Array.isArray(files)) return null;
137
+ if (files === undefined)
138
+ return [];
139
+ if (!Array.isArray(files))
140
+ return null;
231
141
  const declared = [];
232
- for (const entry of files){
142
+ for (const entry of files) {
233
143
  const file = sendFile(entry);
234
- if (file === null) return null;
144
+ if (file === null)
145
+ return null;
235
146
  declared.push(file);
236
147
  }
237
148
  return declared;
238
149
  }
239
150
  function sendBody(input) {
240
151
  const files = sendFiles(input);
241
- if (files === null) return null;
152
+ if (files === null)
153
+ return null;
242
154
  const textValue = text(input, 'text');
243
155
  return {
244
156
  conversation_id: text(input, 'conversation'),
245
157
  operation_id: operationId(input),
246
- ...textValue !== undefined && textValue.length > 0 ? {
247
- text: textValue
248
- } : {},
249
- ...files.length === 0 ? {} : {
250
- files
251
- }
158
+ ...(textValue !== undefined && textValue.length > 0 ? { text: textValue } : {}),
159
+ ...(files.length === 0 ? {} : { files }),
252
160
  };
253
161
  }
254
162
  async function fileBytes(file) {
255
163
  const blob = record(file).blob;
256
- if (blob === undefined || typeof blob !== 'object' || blob === null) return null;
257
- if (!('arrayBuffer' in blob) || typeof blob.arrayBuffer !== 'function') return null;
164
+ if (blob === undefined || typeof blob !== 'object' || blob === null)
165
+ return null;
166
+ if (!('arrayBuffer' in blob) || typeof blob.arrayBuffer !== 'function')
167
+ return null;
258
168
  return new Uint8Array(await blob.arrayBuffer());
259
169
  }
170
+ function delivered(answer, name) {
171
+ return answer.status === 200 ? null : actionOutcome(answer, name);
172
+ }
260
173
  function uploadGrant(entry) {
261
- if (!('upload' in record(entry))) return 'absent';
174
+ if (!('upload' in record(entry)))
175
+ return 'absent';
262
176
  const upload = record(record(entry).upload);
263
- if (upload.method !== 'POST') return null;
264
- if (typeof upload.operation !== 'string' || typeof upload.url !== 'string') return null;
265
- return {
266
- method: 'POST',
267
- operation: upload.operation,
268
- url: upload.url
269
- };
177
+ if (upload.method !== 'POST')
178
+ return null;
179
+ if (typeof upload.operation !== 'string' || typeof upload.url !== 'string')
180
+ return null;
181
+ return { method: 'POST', operation: upload.operation, url: upload.url };
270
182
  }
271
183
  async function uploadOriginals(transport, input, result) {
272
184
  const originals = record(result).originals;
273
- if (!Array.isArray(originals) || originals.length === 0) return null;
185
+ if (!Array.isArray(originals) || originals.length === 0)
186
+ return null;
274
187
  const files = Array.isArray(record(input).files) ? record(input).files : [];
275
- for (const [index, entry] of originals.entries()){
188
+ for (const [index, entry] of originals.entries()) {
276
189
  const grant = uploadGrant(entry);
277
- if (grant === 'absent') continue;
278
- if (grant === null) return malformed();
190
+ if (grant === 'absent')
191
+ continue;
192
+ if (grant === null)
193
+ return malformed();
279
194
  const bytes = index < files.length ? await fileBytes(files[index]) : null;
280
195
  if (bytes === null) {
281
- return {
282
- kind: 'rejected',
283
- code: 'conversation_original_descriptor_required',
284
- fields: {}
285
- };
196
+ return { kind: 'rejected', code: 'conversation_original_descriptor_required', fields: {} };
286
197
  }
287
- const delivered = await transport.transfer({
198
+ const failed = delivered(await transport.transfer({
288
199
  act: 'conversation_original_upload',
289
200
  method: 'POST',
290
201
  url: grant.url,
202
+ answer: 'json',
291
203
  body: bytes,
292
- operation: grant.operation
293
- });
294
- if (delivered.status === 200) continue;
295
- return actionOutcome(delivered, 'conversation.send');
204
+ operation: grant.operation,
205
+ }), 'conversation.send');
206
+ if (failed !== null)
207
+ return failed;
296
208
  }
297
209
  return null;
298
210
  }
211
+ async function attachAction(transport, input) {
212
+ const { blob, ...descriptor } = record(input);
213
+ const bytes = await fileBytes({ blob });
214
+ if (bytes === null)
215
+ return malformed();
216
+ const answer = await transport.door('/door/attachments/upload', descriptor);
217
+ const outcome = actionOutcome(answer, 'attachments/upload');
218
+ if (outcome.kind !== 'action')
219
+ return outcome;
220
+ const grant = uploadGrant(outcome.result);
221
+ if (grant === 'absent')
222
+ return outcome;
223
+ if (grant === null)
224
+ return malformed();
225
+ const stored = await transport.transfer({
226
+ act: 'original_upload',
227
+ method: 'POST',
228
+ url: grant.url,
229
+ answer: 'json',
230
+ body: bytes,
231
+ operation: grant.operation,
232
+ });
233
+ return delivered(stored, 'attachments/upload') ?? { kind: 'action', result: stored.body };
234
+ }
235
+ async function openAction(transport, input) {
236
+ const answer = await transport.door('/door/attachments/get', input);
237
+ const outcome = actionOutcome(answer, 'attachments/get');
238
+ if (outcome.kind !== 'action')
239
+ return outcome;
240
+ const { download, ...opened } = record(outcome.result);
241
+ if (download === undefined)
242
+ return outcome;
243
+ const grant = record(download);
244
+ if (grant.method !== 'GET' || typeof grant.url !== 'string')
245
+ return malformed();
246
+ const read = await transport.transfer({
247
+ act: 'original_download',
248
+ method: 'GET',
249
+ url: grant.url,
250
+ answer: 'bytes',
251
+ });
252
+ const failed = delivered(read, 'attachments/get');
253
+ if (failed !== null)
254
+ return failed;
255
+ if (!(read.body instanceof Blob))
256
+ return { kind: 'transport-uncertainty' };
257
+ return { kind: 'action', result: { ...opened, blob: read.body } };
258
+ }
299
259
  async function sendAction(transport, input) {
300
260
  const body = sendBody(input);
301
- if (body === null) return malformed();
261
+ if (body === null)
262
+ return malformed();
302
263
  const answer = await transport.door('/door/conversations/send', body);
303
264
  const outcome = actionOutcome(answer, 'conversation.send');
304
- if (outcome.kind !== 'action') return outcome;
265
+ if (outcome.kind !== 'action')
266
+ return outcome;
305
267
  const uploaded = await uploadOriginals(transport, input, outcome.result);
306
- if (uploaded !== null) return uploaded;
268
+ if (uploaded !== null)
269
+ return uploaded;
307
270
  return outcome;
308
271
  }
309
272
  function historyCursor(value) {
310
- let parsed;
273
+ const acceptedAt = text(value, 'accepted_at');
274
+ const messageId = text(value, 'message_id');
275
+ if (acceptedAt === undefined || messageId === undefined)
276
+ return null;
277
+ return { accepted_at: acceptedAt, message_id: messageId };
278
+ }
279
+ function position(cursor) {
280
+ return JSON.stringify({ accepted_at: cursor.accepted_at, message_id: cursor.message_id });
281
+ }
282
+ function positionCursor(value) {
311
283
  try {
312
- parsed = JSON.parse(value);
313
- } catch {
284
+ return historyCursor(JSON.parse(value));
285
+ }
286
+ catch {
314
287
  return null;
315
288
  }
316
- const acceptedAt = text(parsed, 'accepted_at');
317
- const messageId = text(parsed, 'message_id');
318
- if (acceptedAt === undefined || messageId === undefined) return null;
319
- return {
320
- accepted_at: acceptedAt,
321
- message_id: messageId
322
- };
323
289
  }
324
- function turned(entry) {
325
- const turn = text(entry, 'message_id');
326
- if (turn === undefined) return null;
290
+ export function acceptedMessage(entry) {
291
+ const cursor = historyCursor(entry);
292
+ if (cursor === null)
293
+ return null;
327
294
  return {
328
- turn,
329
- message: structuredClone(entry)
295
+ identity: cursor.message_id,
296
+ turn: text(entry, 'turn_id') ?? cursor.message_id,
297
+ position: position(cursor),
298
+ text: text(entry, 'text') ?? '',
299
+ message: structuredClone(entry),
330
300
  };
331
301
  }
332
- function olderCursor(entry) {
333
- const acceptedAt = text(entry.message, 'accepted_at');
334
- const messageId = text(entry.message, 'message_id');
335
- if (acceptedAt === undefined || messageId === undefined) return null;
336
- return JSON.stringify({
337
- accepted_at: acceptedAt,
338
- message_id: messageId
339
- });
340
- }
341
- export async function readConversationHistory(transport, conversationId) {
302
+ function historyPage(entry) {
303
+ const held = record(entry);
304
+ const conversation = text(held, 'conversation_id');
305
+ if (conversation === undefined || !Array.isArray(held.messages))
306
+ return null;
342
307
  const messages = [];
343
- let after;
344
- for(;;){
345
- const answer = await transport.door('/door/conversations/history', {
346
- conversation_id: conversationId,
347
- ...after === undefined ? {} : {
348
- after
349
- },
350
- limit: HISTORY_PAGE
351
- });
352
- const outcome = actionOutcome(answer, 'conversation.history');
353
- if (outcome.kind !== 'action') return {
354
- outcome
355
- };
356
- const held = record(outcome.result);
357
- if (!Array.isArray(held.messages)) return {
358
- outcome: {
359
- kind: 'transport-uncertainty'
360
- }
361
- };
362
- for (const entry of held.messages){
363
- if (text(entry, 'message_id') === undefined) {
364
- return {
365
- outcome: {
366
- kind: 'transport-uncertainty'
367
- }
368
- };
369
- }
370
- messages.push(entry);
371
- }
372
- if (held.next === null || held.next === undefined) return {
373
- messages
374
- };
375
- const next = record(held.next);
376
- const acceptedAt = text(next, 'accepted_at');
377
- const messageId = text(next, 'message_id');
378
- if (acceptedAt === undefined || messageId === undefined) {
379
- return {
380
- outcome: {
381
- kind: 'transport-uncertainty'
382
- }
383
- };
384
- }
385
- after = {
386
- accepted_at: acceptedAt,
387
- message_id: messageId
388
- };
308
+ for (const one of held.messages) {
309
+ const message = acceptedMessage(one);
310
+ if (message === null)
311
+ return null;
312
+ messages.push(message);
389
313
  }
314
+ const next = held.next === null || held.next === undefined ? undefined : historyCursor(held.next);
315
+ if (next === null)
316
+ return null;
317
+ return { conversation, messages, ...(next === undefined ? {} : { next: position(next) }) };
390
318
  }
391
- function historyPage(conversationId, messages, until) {
392
- const from = Math.max(0, until - HISTORY_PAGE);
393
- const page = messages.slice(from, until);
394
- const first = page[0];
395
- const cursor = from === 0 || first === undefined ? null : olderCursor(first);
396
- if (from > 0 && cursor === null) return {
397
- kind: 'transport-uncertainty'
398
- };
399
- return {
400
- kind: 'action',
401
- result: {
402
- conversation_id: conversationId,
403
- messages: page,
404
- ...cursor === null ? {} : {
405
- cursor
406
- }
407
- }
408
- };
319
+ function historyAsks(input) {
320
+ const asked = record(input).conversations;
321
+ if (!Array.isArray(asked) || asked.length === 0)
322
+ return null;
323
+ const asks = [];
324
+ for (const entry of asked) {
325
+ const conversationId = text(entry, 'conversation');
326
+ if (conversationId === undefined)
327
+ return null;
328
+ const afterValue = text(entry, 'after');
329
+ const after = afterValue === undefined ? undefined : positionCursor(afterValue);
330
+ if (after === null)
331
+ return null;
332
+ asks.push({ conversation_id: conversationId, ...(after === undefined ? {} : { after }) });
333
+ }
334
+ return asks;
409
335
  }
410
336
  async function historyAction(transport, input) {
411
- const conversationId = text(input, 'conversation');
412
- if (conversationId === undefined) return malformed();
413
- const cursorValue = text(input, 'cursor');
414
- let olderThan;
415
- if (cursorValue !== undefined) {
416
- const parsed = historyCursor(cursorValue);
417
- if (parsed === null) {
418
- return {
419
- kind: 'rejected',
420
- code: 'unknown-cursor',
421
- fields: {
422
- cursor: cursorValue
423
- }
424
- };
337
+ const asks = historyAsks(input);
338
+ if (asks === null)
339
+ return malformed();
340
+ const histories = [];
341
+ for (let at = 0; at < asks.length; at += HISTORY_CONVERSATIONS) {
342
+ const answer = await transport.door('/door/conversations/history', {
343
+ conversations: asks.slice(at, at + HISTORY_CONVERSATIONS),
344
+ limit: HISTORY_PAGE,
345
+ });
346
+ const outcome = actionOutcome(answer, 'conversation.history');
347
+ if (outcome.kind !== 'action')
348
+ return outcome;
349
+ const answered = record(outcome.result).histories;
350
+ if (!Array.isArray(answered))
351
+ return { kind: 'transport-uncertainty' };
352
+ for (const entry of answered) {
353
+ const page = historyPage(entry);
354
+ if (page === null)
355
+ return { kind: 'transport-uncertainty' };
356
+ histories.push(page);
425
357
  }
426
- olderThan = parsed;
427
- }
428
- const held = await readConversationHistory(transport, conversationId);
429
- if ('outcome' in held) return held.outcome;
430
- const messages = [];
431
- for (const entry of held.messages){
432
- const mapped = turned(entry);
433
- if (mapped === null) return {
434
- kind: 'transport-uncertainty'
435
- };
436
- messages.push(mapped);
437
- }
438
- if (olderThan === undefined) {
439
- return historyPage(conversationId, messages, messages.length);
440
- }
441
- const at = messages.findIndex((entry)=>text(entry.message, 'accepted_at') === olderThan.accepted_at && text(entry.message, 'message_id') === olderThan.message_id);
442
- if (at === -1) {
443
- return {
444
- kind: 'rejected',
445
- code: 'unknown-cursor',
446
- fields: {
447
- cursor: cursorValue
448
- }
449
- };
450
358
  }
451
- return historyPage(conversationId, messages, at);
359
+ const result = { histories };
360
+ return { kind: 'action', result };
452
361
  }
453
-
454
- //# sourceMappingURL=actions.js.map