@agentshouse/mdruntime-adapter 0.1.0-cd673d02b6e50 → 0.1.0-cd75dfaf57319
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.
- package/dist/actions.d.ts +2 -47
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +7 -225
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +69 -214
- package/dist/discovery.d.ts +18 -5
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +60 -26
- package/dist/history.d.ts +12 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +92 -0
- package/dist/house-model.d.ts +718 -1
- package/dist/house-model.d.ts.map +1 -1
- package/dist/house-model.js +393 -486
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/model.d.ts +5 -4
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +27 -22
- package/dist/read.d.ts +11 -44
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +25 -174
- package/dist/refusal.d.ts +0 -3
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +0 -5
- package/dist/room-stream.d.ts +2 -1
- package/dist/room-stream.d.ts.map +1 -1
- package/dist/room-stream.js +2 -2
- package/dist/room-types.d.ts +2 -2
- package/dist/room-types.d.ts.map +1 -1
- package/dist/room-types.js +7 -9
- package/dist/search.d.ts +5 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +47 -0
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +50 -92
- package/dist/space-schema.d.ts +7 -0
- package/dist/space-schema.d.ts.map +1 -0
- package/dist/space-schema.js +18 -0
- package/dist/stream.d.ts +14 -14
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +169 -424
- package/dist/transport.d.ts +44 -35
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +54 -12
- package/dist/upload.d.ts.map +1 -1
- package/dist/upload.js +2 -2
- package/dist/write.d.ts +2 -1
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +8 -23
- package/package.json +2 -2
- package/dist/gather.d.ts +0 -3
- package/dist/gather.d.ts.map +0 -1
- package/dist/gather.js +0 -35
package/dist/adapter.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { stepped } from '@agentshouse/mdruntime';
|
|
2
|
-
import {
|
|
3
|
-
import { streamedModels, viewModels } from './model.js';
|
|
4
|
-
import { gathered, selections } from './gather.js';
|
|
2
|
+
import { capabilities, discoveredRoom, mapDiscovery, parseAuthorityChange, parseDiscovery, trackedFrom, } from './discovery.js';
|
|
5
3
|
import { createSiteAdapter } from './site.js';
|
|
6
|
-
import { authorityRoot, bootstrapping,
|
|
4
|
+
import { authorityRoot, bootstrapping, parseFileAddress, parseGrant, } from './read.js';
|
|
7
5
|
import { roomStreams } from './room-stream.js';
|
|
6
|
+
import { spaceSchemas } from './space-schema.js';
|
|
8
7
|
import { sha256Hex } from './digest.js';
|
|
9
8
|
import { finishedUpload, houseUpload } from './upload.js';
|
|
10
9
|
import { houseAction } from './actions.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
10
|
+
import { houseSearch } from './search.js';
|
|
11
|
+
import { classifyDoor, doorRefusal } from './refusal.js';
|
|
12
|
+
import { houseNotifications, } from './stream.js';
|
|
13
|
+
import { houseDoorRoute, } from './transport.js';
|
|
14
14
|
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, houseMove, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
15
|
+
import { HOUSE_TYPED_PATHS } from './house-model.js';
|
|
15
16
|
import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
|
|
16
|
-
import { followsVersion, logPosition, presented, reachedVersion, roomVersion
|
|
17
|
+
import { followsVersion, logPosition, presented, reachedVersion, roomVersion } from './version.js';
|
|
17
18
|
const UNCERTAIN = { kind: 'transport-uncertainty' };
|
|
18
19
|
const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
|
|
19
20
|
const OCTET_STREAM = 'application/octet-stream';
|
|
@@ -51,18 +52,50 @@ export function createAgentsHouseAdapter(options) {
|
|
|
51
52
|
if ('site' in options)
|
|
52
53
|
return createSiteAdapter(options.site, options.snapshot === true);
|
|
53
54
|
const transport = options.transport;
|
|
54
|
-
const
|
|
55
|
+
const spaces = spaceSchemas();
|
|
56
|
+
const notifications = houseNotifications(transport, 'credential', {
|
|
57
|
+
opened: async (frame) => (frame === null ? null : opened(frame)),
|
|
58
|
+
changed,
|
|
59
|
+
}, spaces);
|
|
55
60
|
const tracked = new Map();
|
|
56
|
-
const
|
|
57
|
-
const rooms = roomStreams('credential');
|
|
61
|
+
const rooms = roomStreams('credential', spaces);
|
|
58
62
|
const unreused = new Set();
|
|
59
|
-
let heldRevisions;
|
|
60
63
|
let bounds;
|
|
61
64
|
function declared() {
|
|
62
65
|
if (bounds === undefined)
|
|
63
66
|
throw new Error('no Discovery declared the bounds');
|
|
64
67
|
return bounds;
|
|
65
68
|
}
|
|
69
|
+
function opened(frame) {
|
|
70
|
+
const document = parseDiscovery(frame);
|
|
71
|
+
if (document === null)
|
|
72
|
+
return null;
|
|
73
|
+
bounds = document.bounds;
|
|
74
|
+
tracked.clear();
|
|
75
|
+
for (const entry of trackedFrom(document))
|
|
76
|
+
tracked.set(entry.locator, entry);
|
|
77
|
+
notifications.track([...tracked.values()]);
|
|
78
|
+
return {
|
|
79
|
+
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions)),
|
|
80
|
+
member: document.member,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function changed(frame) {
|
|
84
|
+
const change = parseAuthorityChange(frame);
|
|
85
|
+
if (change === null)
|
|
86
|
+
return null;
|
|
87
|
+
for (const locator of change.left)
|
|
88
|
+
tracked.delete(locator);
|
|
89
|
+
for (const entry of trackedFrom(change))
|
|
90
|
+
tracked.set(entry.locator, entry);
|
|
91
|
+
notifications.track([...tracked.values()]);
|
|
92
|
+
return {
|
|
93
|
+
kind: 'authority-changed',
|
|
94
|
+
rooms: change.authorities.map((authority) => discoveredRoom(authority)),
|
|
95
|
+
left: change.left,
|
|
96
|
+
member: change.member,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
66
99
|
async function streamedPage(entry, token, held) {
|
|
67
100
|
const part = await rooms.part(entry, token);
|
|
68
101
|
if ('kind' in part)
|
|
@@ -72,11 +105,6 @@ export function createAgentsHouseAdapter(options) {
|
|
|
72
105
|
unreused.add(entry.locator);
|
|
73
106
|
return UNCERTAIN_READ;
|
|
74
107
|
}
|
|
75
|
-
if (part.first && part.ended)
|
|
76
|
-
handed.set(entry.locator, roomVersion(scope));
|
|
77
|
-
else
|
|
78
|
-
handed.delete(entry.locator);
|
|
79
|
-
notifications.reach(entry.authority, scope);
|
|
80
108
|
return {
|
|
81
109
|
...(await stepped(bootstrapping(scope))),
|
|
82
110
|
...(part.models === undefined ? {} : { models: part.models }),
|
|
@@ -89,7 +117,8 @@ export function createAgentsHouseAdapter(options) {
|
|
|
89
117
|
const files = reusing === undefined
|
|
90
118
|
? null
|
|
91
119
|
: await heldFiles(entry.authority, reusing, declared().max_held_files);
|
|
92
|
-
const answered = await transport.streamed(
|
|
120
|
+
const answered = await transport.streamed(houseDoorRoute('bootstrap'), {
|
|
121
|
+
room_ref: entry.authority,
|
|
93
122
|
prefixes: [authorityRoot(entry.handle)],
|
|
94
123
|
...(files === null ? {} : { held: [files] }),
|
|
95
124
|
}, request.signal ?? new AbortController().signal);
|
|
@@ -97,84 +126,12 @@ export function createAgentsHouseAdapter(options) {
|
|
|
97
126
|
return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN_READ;
|
|
98
127
|
return rooms.head(entry, answered.room, request.arriving, request.signal);
|
|
99
128
|
}
|
|
100
|
-
async function enumeratedPage(ask, page) {
|
|
101
|
-
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
102
|
-
prefixes: [authorityRoot(ask.entry.handle)],
|
|
103
|
-
positions: [
|
|
104
|
-
{
|
|
105
|
-
...presented(ask.entry.authority, { position: page.after, logEpoch: ask.from.logEpoch }),
|
|
106
|
-
page: { through: page.through, read: page.read },
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
});
|
|
110
|
-
const failure = classifyRead(answer.status, answer.body, ask.entry.locator);
|
|
111
|
-
if (failure !== null)
|
|
112
|
-
return failure;
|
|
113
|
-
const enumeration = parseEnumeration(answer.body, 'credential');
|
|
114
|
-
if (enumeration === null)
|
|
115
|
-
return UNCERTAIN_READ;
|
|
116
|
-
return (viewOf(enumeration, ask.entry) ?? { kind: 'room-not-readable', locator: ask.entry.locator });
|
|
117
|
-
}
|
|
118
|
-
async function enumeratedRoom(enumeration, ask) {
|
|
119
|
-
const first = viewOf(enumeration, ask.entry);
|
|
120
|
-
if (first === undefined)
|
|
121
|
-
return { kind: 'room-not-readable', locator: ask.entry.locator };
|
|
122
|
-
const view = await wholeView(first, (page) => enumeratedPage(ask, page));
|
|
123
|
-
if ('kind' in view)
|
|
124
|
-
return view;
|
|
125
|
-
notifications.reach(ask.entry.authority, view);
|
|
126
|
-
const answer = await refreshFrom(view, ask.fromRoomVersion);
|
|
127
|
-
if (answer.kind !== 'net-advancement')
|
|
128
|
-
return answer;
|
|
129
|
-
handed.set(ask.entry.locator, roomVersion(view));
|
|
130
|
-
const models = await viewModels(view, ask.entry);
|
|
131
|
-
return models === undefined ? answer : { ...answer, models };
|
|
132
|
-
}
|
|
133
|
-
async function streamed(entry, request, from) {
|
|
134
|
-
if (handed.get(entry.locator) !== request.fromRoomVersion)
|
|
135
|
-
return null;
|
|
136
|
-
const asked = notifications.edited(entry, from);
|
|
137
|
-
const kept = asked.length === 0 || heldRevisions === undefined
|
|
138
|
-
? new Map()
|
|
139
|
-
: await heldRevisions(entry.locator, asked);
|
|
140
|
-
const advanced = await stepped(notifications.advance(entry, from, (path, revision) => kept.get(path) === revision));
|
|
141
|
-
if (advanced === null)
|
|
142
|
-
return null;
|
|
143
|
-
handed.set(entry.locator, advanced.page.roomVersion);
|
|
144
|
-
const models = await streamedModels(advanced.schemas, entry, 'credential');
|
|
145
|
-
return models === undefined ? advanced.page : { ...advanced.page, models };
|
|
146
|
-
}
|
|
147
129
|
function held(rooms) {
|
|
148
|
-
|
|
130
|
+
notifications.send(rooms.flatMap((room) => {
|
|
149
131
|
const entry = tracked.get(room.locator);
|
|
150
132
|
const at = logPosition(room.roomVersion);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
handed.set(room.locator, room.roomVersion);
|
|
154
|
-
notifications.reach(entry.authority, at);
|
|
155
|
-
return [presented(entry.authority, at)];
|
|
156
|
-
});
|
|
157
|
-
notifications.send(kept);
|
|
158
|
-
}
|
|
159
|
-
async function adoptedMove(entry, into, outcome) {
|
|
160
|
-
if (outcome.kind === 'accepted' && outcome.destination !== undefined) {
|
|
161
|
-
await adopted(into, { kind: 'accepted', ...outcome.destination });
|
|
162
|
-
}
|
|
163
|
-
return adopted(entry, outcome);
|
|
164
|
-
}
|
|
165
|
-
async function adopted(entry, outcome) {
|
|
166
|
-
if (outcome.kind !== 'accepted')
|
|
167
|
-
return outcome;
|
|
168
|
-
const held = handed.get(entry.locator);
|
|
169
|
-
if (held === undefined)
|
|
170
|
-
return outcome;
|
|
171
|
-
const follows = followsVersion(held, outcome.roomVersion);
|
|
172
|
-
if (follows)
|
|
173
|
-
handed.set(entry.locator, outcome.roomVersion);
|
|
174
|
-
const at = logPosition(outcome.roomVersion);
|
|
175
|
-
if (follows && at !== null)
|
|
176
|
-
notifications.reach(entry.authority, at);
|
|
177
|
-
return outcome;
|
|
133
|
+
return entry === undefined || at === null ? [] : [presented(entry.authority, at)];
|
|
134
|
+
}));
|
|
178
135
|
}
|
|
179
136
|
function authorityFor(locator) {
|
|
180
137
|
return tracked.get(locator);
|
|
@@ -193,60 +150,18 @@ export function createAgentsHouseAdapter(options) {
|
|
|
193
150
|
return result;
|
|
194
151
|
return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
|
|
195
152
|
}
|
|
196
|
-
function layered(asks) {
|
|
197
|
-
const depths = new Map();
|
|
198
|
-
const layers = [];
|
|
199
|
-
for (const ask of asks) {
|
|
200
|
-
const depth = depths.get(ask.ask.entry.authority) ?? 0;
|
|
201
|
-
depths.set(ask.ask.entry.authority, depth + 1);
|
|
202
|
-
(layers[depth] ??= []).push(ask);
|
|
203
|
-
}
|
|
204
|
-
return layers;
|
|
205
|
-
}
|
|
206
|
-
async function enumerated(asks) {
|
|
207
|
-
const answers = [];
|
|
208
|
-
const parts = layered(asks.map((ask, index) => ({ ask, index }))).flatMap((layer) => selections(layer, declared().max_prefixes));
|
|
209
|
-
for (const part of parts) {
|
|
210
|
-
let open = [...part];
|
|
211
|
-
while (open.length > 0) {
|
|
212
|
-
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
213
|
-
prefixes: open.map(({ ask }) => authorityRoot(ask.entry.handle)),
|
|
214
|
-
positions: open.map(({ ask }) => presented(ask.entry.authority, ask.from)),
|
|
215
|
-
});
|
|
216
|
-
const classified = open.map(({ ask }) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
217
|
-
if (classified[0]?.kind === 'beyond-horizon') {
|
|
218
|
-
const named = doorRefusal(answer.body)?.fields.authority;
|
|
219
|
-
const expired = open.filter(({ ask }) => ask.entry.authority === named);
|
|
220
|
-
const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
|
|
221
|
-
const gone = expired.length === 0 ? open : expired;
|
|
222
|
-
for (const ask of gone)
|
|
223
|
-
answers[ask.index] = answered;
|
|
224
|
-
open = open.filter((ask) => !gone.includes(ask));
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
|
|
228
|
-
for (const [answerIndex, { ask, index }] of open.entries()) {
|
|
229
|
-
const failure = classified[answerIndex];
|
|
230
|
-
if (failure !== null && failure !== undefined) {
|
|
231
|
-
answers[index] = failure;
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
answers[index] =
|
|
235
|
-
enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask);
|
|
236
|
-
}
|
|
237
|
-
open = [];
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return answers;
|
|
241
|
-
}
|
|
242
|
-
const enumerateTogether = gathered(enumerated);
|
|
243
153
|
async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
|
|
244
154
|
const paths = batchPaths(changes);
|
|
245
155
|
const sha256 = await deltaDigest(delta);
|
|
246
156
|
if (handed !== undefined && handed.sha256 === sha256) {
|
|
247
157
|
return { body: { ...head, paths, upload: handed, runtime: notifications.runtime } };
|
|
248
158
|
}
|
|
249
|
-
const minted = await transport.door(
|
|
159
|
+
const minted = await transport.door(houseDoorRoute('batch-upload'), {
|
|
160
|
+
...head,
|
|
161
|
+
paths,
|
|
162
|
+
bytes,
|
|
163
|
+
sha256,
|
|
164
|
+
});
|
|
250
165
|
if (minted.status !== 200) {
|
|
251
166
|
return {
|
|
252
167
|
outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
|
|
@@ -294,11 +209,11 @@ export function createAgentsHouseAdapter(options) {
|
|
|
294
209
|
: { body: inline };
|
|
295
210
|
if ('outcome' in prepared)
|
|
296
211
|
return prepared.outcome;
|
|
297
|
-
const answer = await transport.door(
|
|
212
|
+
const answer = await transport.door(houseDoorRoute('batch'), prepared.body);
|
|
298
213
|
const stagedFiles = await staged(answer, request);
|
|
299
214
|
if (stagedFiles === false)
|
|
300
215
|
return UNCERTAIN;
|
|
301
|
-
const settled = stagedFiles === null ? answer : await transport.door(
|
|
216
|
+
const settled = stagedFiles === null ? answer : await transport.door(houseDoorRoute('batch'), prepared.body);
|
|
302
217
|
if (stagedFiles === true && (await staged(settled, request)) !== null)
|
|
303
218
|
return UNCERTAIN;
|
|
304
219
|
return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
|
|
@@ -366,7 +281,7 @@ export function createAgentsHouseAdapter(options) {
|
|
|
366
281
|
const entry = authorityFor(request.locator);
|
|
367
282
|
if (entry === undefined)
|
|
368
283
|
return null;
|
|
369
|
-
return () => transport.door(
|
|
284
|
+
return () => transport.door(houseDoorRoute('files/get'), {
|
|
370
285
|
room_ref: entry.authority,
|
|
371
286
|
path: request.path,
|
|
372
287
|
revision: request.revision,
|
|
@@ -375,49 +290,10 @@ export function createAgentsHouseAdapter(options) {
|
|
|
375
290
|
return {
|
|
376
291
|
roomTypes: HOUSE_ROOM_TYPES,
|
|
377
292
|
nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
|
|
293
|
+
typedPaths: HOUSE_TYPED_PATHS,
|
|
378
294
|
async authorize() {
|
|
379
295
|
return options.authorize();
|
|
380
296
|
},
|
|
381
|
-
async discover(_authorization, scope) {
|
|
382
|
-
const answer = await transport.door(DOOR_DISCOVER, {
|
|
383
|
-
prefixes: [...DISCOVER_PREFIXES],
|
|
384
|
-
...(scope === undefined ? {} : { rooms: scope.authorities }),
|
|
385
|
-
});
|
|
386
|
-
const failed = discoveryFailure(answer.status, answer.body);
|
|
387
|
-
if (failed !== null)
|
|
388
|
-
return failed;
|
|
389
|
-
const document = parseDiscovery(answer.body);
|
|
390
|
-
if (document === null)
|
|
391
|
-
return { kind: 'transport-uncertainty' };
|
|
392
|
-
bounds = document.bounds;
|
|
393
|
-
const entries = trackedFrom(document);
|
|
394
|
-
const covered = scope === undefined
|
|
395
|
-
? null
|
|
396
|
-
: [
|
|
397
|
-
...new Set([
|
|
398
|
-
...[...tracked.values()]
|
|
399
|
-
.filter((entry) => scope.authorities.includes(entry.authority))
|
|
400
|
-
.map((entry) => entry.locator),
|
|
401
|
-
...entries.map((entry) => entry.locator),
|
|
402
|
-
]),
|
|
403
|
-
];
|
|
404
|
-
if (covered === null)
|
|
405
|
-
tracked.clear();
|
|
406
|
-
else
|
|
407
|
-
for (const locator of covered)
|
|
408
|
-
tracked.delete(locator);
|
|
409
|
-
for (const entry of entries)
|
|
410
|
-
tracked.set(entry.locator, entry);
|
|
411
|
-
for (const locator of handed.keys())
|
|
412
|
-
if (!tracked.has(locator))
|
|
413
|
-
handed.delete(locator);
|
|
414
|
-
notifications.track([...tracked.values()]);
|
|
415
|
-
return {
|
|
416
|
-
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions, [...tracked.values()])),
|
|
417
|
-
member: document.member,
|
|
418
|
-
...(covered === null ? {} : { covered }),
|
|
419
|
-
};
|
|
420
|
-
},
|
|
421
297
|
async bootstrap(_authorization, request) {
|
|
422
298
|
const entry = authorityFor(request.locator);
|
|
423
299
|
if (entry === undefined)
|
|
@@ -426,20 +302,6 @@ export function createAgentsHouseAdapter(options) {
|
|
|
426
302
|
? openedRoom(entry, request)
|
|
427
303
|
: streamedPage(entry, request.pageToken, request.held);
|
|
428
304
|
},
|
|
429
|
-
async refresh(_authorization, request) {
|
|
430
|
-
const entry = authorityFor(request.locator);
|
|
431
|
-
if (entry === undefined)
|
|
432
|
-
return { kind: 'room-not-readable', locator: request.locator };
|
|
433
|
-
if (request.pageToken !== undefined)
|
|
434
|
-
return { kind: 'transport-uncertainty' };
|
|
435
|
-
const from = logPosition(request.fromRoomVersion);
|
|
436
|
-
if (from === null)
|
|
437
|
-
return { kind: 'beyond-horizon' };
|
|
438
|
-
const answered = await streamed(entry, request, from);
|
|
439
|
-
if (answered !== null)
|
|
440
|
-
return answered;
|
|
441
|
-
return enumerateTogether({ entry, from, fromRoomVersion: request.fromRoomVersion });
|
|
442
|
-
},
|
|
443
305
|
async mutate(_authorization, request, keep) {
|
|
444
306
|
const entry = authorityFor(request.locator);
|
|
445
307
|
if (entry === undefined)
|
|
@@ -455,22 +317,21 @@ export function createAgentsHouseAdapter(options) {
|
|
|
455
317
|
: houseMove(batch, entry.handle, into.handle);
|
|
456
318
|
if (mapped.kind === 'unmapped')
|
|
457
319
|
return rejectedMapping(mapped);
|
|
458
|
-
const head = operationHead(batch);
|
|
320
|
+
const head = operationHead(batch, entry.authority);
|
|
459
321
|
const delta = deltaBody(mapped.changes);
|
|
460
322
|
const bytes = deltaBytes(delta);
|
|
461
323
|
const handed = request.upload;
|
|
462
324
|
const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
|
|
463
|
-
const settle = (outcome) => into === undefined ? adopted(entry, outcome) : adoptedMove(entry, into, outcome);
|
|
464
325
|
const outcome = await answer(handed);
|
|
465
326
|
if (handed === undefined || !lapsedGrant(outcome))
|
|
466
|
-
return
|
|
467
|
-
return
|
|
327
|
+
return outcome;
|
|
328
|
+
return answer(undefined);
|
|
468
329
|
},
|
|
469
330
|
async history(_authorization, request) {
|
|
470
331
|
const entry = authorityFor(request.locator);
|
|
471
332
|
if (entry === undefined)
|
|
472
333
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
473
|
-
const answer = await transport.door(
|
|
334
|
+
const answer = await transport.door(houseDoorRoute('revisions'), revisionBody({
|
|
474
335
|
path: request.path,
|
|
475
336
|
handle: entry.handle,
|
|
476
337
|
...(request.revision === undefined ? {} : { revision: request.revision }),
|
|
@@ -497,7 +358,7 @@ export function createAgentsHouseAdapter(options) {
|
|
|
497
358
|
const entry = authorityFor(request.locator);
|
|
498
359
|
if (entry === undefined)
|
|
499
360
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
500
|
-
const declared = await transport.door(
|
|
361
|
+
const declared = await transport.door(houseDoorRoute('files/attach'), {
|
|
501
362
|
room_ref: entry.authority,
|
|
502
363
|
name: request.name,
|
|
503
364
|
bytes: request.byteLength,
|
|
@@ -531,18 +392,12 @@ export function createAgentsHouseAdapter(options) {
|
|
|
531
392
|
async action(_authorization, request) {
|
|
532
393
|
return withAt(await houseAction(transport, request));
|
|
533
394
|
},
|
|
395
|
+
search(_authorization, request) {
|
|
396
|
+
return houseSearch(transport, [...tracked.values()], request);
|
|
397
|
+
},
|
|
534
398
|
roomStream: {
|
|
535
399
|
hold: held,
|
|
536
|
-
|
|
537
|
-
observe(notify, receipts, revisions) {
|
|
538
|
-
heldRevisions = revisions;
|
|
539
|
-
return notifications.observe(notify, receipts);
|
|
540
|
-
},
|
|
541
|
-
published(locator, roomVersion) {
|
|
542
|
-
const at = logPosition(roomVersion);
|
|
543
|
-
if (at !== null)
|
|
544
|
-
notifications.published(locator, at);
|
|
545
|
-
},
|
|
400
|
+
observe: notifications.observe,
|
|
546
401
|
},
|
|
547
402
|
conversations: notifications.conversations,
|
|
548
403
|
serviceMode: notifications.serviceMode,
|
package/dist/discovery.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Capability, DiscoveredRoom, Discovery, Member, ProviderLimit, Retention } from '@agentshouse/mdruntime';
|
|
2
2
|
import { type LogPosition } from './version.js';
|
|
3
3
|
export declare const PROVIDER_ACTIONS = "provider-actions";
|
|
4
|
-
export declare const DISCOVER_PREFIXES: readonly string[];
|
|
5
4
|
export type HouseBounds = {
|
|
6
5
|
max_authored_file_bytes: number;
|
|
7
6
|
max_batch_operations: number;
|
|
@@ -15,12 +14,11 @@ export type HouseBounds = {
|
|
|
15
14
|
max_prefixes: number;
|
|
16
15
|
max_held_files: number;
|
|
17
16
|
change_retention_seconds: number;
|
|
18
|
-
operation_retention_seconds: number;
|
|
19
17
|
max_move_documents?: number;
|
|
20
18
|
};
|
|
21
19
|
export type HouseOperations = {
|
|
22
20
|
write: readonly string[];
|
|
23
|
-
|
|
21
|
+
erase: readonly string[];
|
|
24
22
|
};
|
|
25
23
|
export type HouseAccessLevel = 'search' | 'open' | 'keep';
|
|
26
24
|
export type HouseAuthority = LogPosition & {
|
|
@@ -40,6 +38,11 @@ export type HouseDiscoveryDocument = {
|
|
|
40
38
|
authorities: readonly HouseAuthority[];
|
|
41
39
|
actions: readonly string[];
|
|
42
40
|
};
|
|
41
|
+
export type HouseAuthorityChange = {
|
|
42
|
+
member: Member;
|
|
43
|
+
authorities: readonly HouseAuthority[];
|
|
44
|
+
left: readonly string[];
|
|
45
|
+
};
|
|
43
46
|
export type TrackedAuthority = LogPosition & {
|
|
44
47
|
locator: string;
|
|
45
48
|
authority: string;
|
|
@@ -56,14 +59,24 @@ export type HouseSiteDiscovery = {
|
|
|
56
59
|
bounds: HouseBounds;
|
|
57
60
|
authorities: readonly HouseSiteAuthority[];
|
|
58
61
|
};
|
|
62
|
+
type RoomOwn = 'room_ref' | 'room_handle' | 'name' | 'position';
|
|
63
|
+
type Folded<A extends Record<RoomOwn, unknown>> = {
|
|
64
|
+
readonly shapes: readonly Omit<A, RoomOwn>[];
|
|
65
|
+
readonly rooms: readonly (Pick<A, RoomOwn> & {
|
|
66
|
+
readonly shape: number;
|
|
67
|
+
})[];
|
|
68
|
+
};
|
|
69
|
+
export declare function foldedAuthorities<A extends Record<RoomOwn, unknown>>(authorities: readonly A[]): Folded<A>;
|
|
59
70
|
export declare function parseDiscovery(body: unknown): HouseDiscoveryDocument | null;
|
|
71
|
+
export declare function parseAuthorityChange(body: unknown): HouseAuthorityChange | null;
|
|
60
72
|
export declare function parseSiteDiscovery(body: unknown): HouseSiteDiscovery | null;
|
|
61
73
|
export declare function siteTracked(document: HouseSiteDiscovery): TrackedAuthority[];
|
|
62
|
-
export declare function trackedFrom(document: HouseDiscoveryDocument): TrackedAuthority[];
|
|
74
|
+
export declare function trackedFrom(document: Pick<HouseDiscoveryDocument, 'authorities'>): TrackedAuthority[];
|
|
63
75
|
export declare function providerLimits(bounds: HouseBounds): readonly ProviderLimit[];
|
|
64
76
|
export declare function declaredRetention(bounds: HouseBounds): Retention;
|
|
65
|
-
export declare function capabilities(actions: HouseDiscoveryDocument['actions']
|
|
77
|
+
export declare function capabilities(actions: HouseDiscoveryDocument['actions']): readonly Capability[];
|
|
66
78
|
export declare function siteCapabilities(): readonly Capability[];
|
|
67
79
|
export declare function discoveredRoom(authority: HouseAuthority): DiscoveredRoom;
|
|
68
80
|
export declare function mapDiscovery(document: Pick<HouseDiscoveryDocument, 'bounds'>, rooms: readonly DiscoveredRoom[], declared: readonly Capability[]): Discovery;
|
|
81
|
+
export {};
|
|
69
82
|
//# sourceMappingURL=discovery.d.ts.map
|
package/dist/discovery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,SAAS,EACT,MAAM,EACN,aAAa,EACb,SAAS,EACV,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,SAAS,EACT,MAAM,EACN,aAAa,EACb,SAAS,EACV,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3E,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAEnD,MAAM,MAAM,WAAW,GAAG;IACxB,uBAAuB,EAAE,MAAM,CAAC;IAChC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,wBAAwB,EAAE,MAAM,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACzC,kBAAkB,EAAE,UAAU,GAAG,aAAa,CAAC;IAC/C,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,KAAK,EAAE,gBAAgB,CAAC;IACxB,UAAU,EAAE,eAAe,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,SAAS,cAAc,EAAE,CAAC;IACvC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,SAAS,cAAc,EAAE,CAAC;IACvC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,SAAS,kBAAkB,EAAE,CAAC;CAC5C,CAAC;AAoHF,KAAK,OAAO,GAAG,UAAU,GAAG,aAAa,GAAG,MAAM,GAAG,UAAU,CAAC;AAEhE,KAAK,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI;IAChD,QAAQ,CAAC,MAAM,EAAE,SAAS,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;CAC5E,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,EAClE,WAAW,EAAE,SAAS,CAAC,EAAE,GACxB,MAAM,CAAC,CAAC,CAAC,CAaX;AAeD,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,sBAAsB,GAAG,IAAI,CAgB3E;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,oBAAoB,GAAG,IAAI,CAQ/E;AAmBD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,kBAAkB,GAAG,IAAI,CAa3E;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,kBAAkB,GAAG,gBAAgB,EAAE,CAU5E;AAED,wBAAgB,WAAW,CACzB,QAAQ,EAAE,IAAI,CAAC,sBAAsB,EAAE,aAAa,CAAC,GACpD,gBAAgB,EAAE,CAUpB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,aAAa,EAAE,CAa5E;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAKhE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,sBAAsB,CAAC,SAAS,CAAC,GAAG,SAAS,UAAU,EAAE,CAE9F;AAED,wBAAgB,gBAAgB,IAAI,SAAS,UAAU,EAAE,CAOxD;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,cAAc,GAAG,cAAc,CAqBxE;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,IAAI,CAAC,sBAAsB,EAAE,QAAQ,CAAC,EAChD,KAAK,EAAE,SAAS,cAAc,EAAE,EAChC,QAAQ,EAAE,SAAS,UAAU,EAAE,GAC9B,SAAS,CAQX"}
|
package/dist/discovery.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { declaredActions
|
|
1
|
+
import { declaredActions } from './actions.js';
|
|
2
|
+
import { SITE_SUBMISSION } from './transport.js';
|
|
2
3
|
import { readPosition, roomVersion } from './version.js';
|
|
3
4
|
export const PROVIDER_ACTIONS = 'provider-actions';
|
|
4
|
-
export const DISCOVER_PREFIXES = ['/'];
|
|
5
|
-
const PRIVATE_HANDLE = 'private';
|
|
6
5
|
const BOUND_KEYS = [
|
|
7
6
|
'max_authored_file_bytes',
|
|
8
7
|
'max_batch_operations',
|
|
@@ -16,9 +15,8 @@ const BOUND_KEYS = [
|
|
|
16
15
|
'max_prefixes',
|
|
17
16
|
'max_held_files',
|
|
18
17
|
'change_retention_seconds',
|
|
19
|
-
'operation_retention_seconds',
|
|
20
18
|
];
|
|
21
|
-
const OPERATION_KEYS = ['write', '
|
|
19
|
+
const OPERATION_KEYS = ['write', 'erase'];
|
|
22
20
|
const ACCESS_LEVELS = ['search', 'open', 'keep'];
|
|
23
21
|
function stringList(value) {
|
|
24
22
|
if (!Array.isArray(value))
|
|
@@ -111,6 +109,47 @@ function parseAuthority(value) {
|
|
|
111
109
|
...at,
|
|
112
110
|
};
|
|
113
111
|
}
|
|
112
|
+
function parseAuthorities(value) {
|
|
113
|
+
if (!Array.isArray(value))
|
|
114
|
+
return null;
|
|
115
|
+
const authorities = [];
|
|
116
|
+
for (const entry of value) {
|
|
117
|
+
const authority = parseAuthority(entry);
|
|
118
|
+
if (authority === null)
|
|
119
|
+
return null;
|
|
120
|
+
authorities.push(authority);
|
|
121
|
+
}
|
|
122
|
+
return authorities;
|
|
123
|
+
}
|
|
124
|
+
export function foldedAuthorities(authorities) {
|
|
125
|
+
const shapes = [];
|
|
126
|
+
const known = new Map();
|
|
127
|
+
const rooms = authorities.map(({ room_ref, room_handle, name, position, ...shape }) => {
|
|
128
|
+
const key = JSON.stringify(shape);
|
|
129
|
+
let held = known.get(key);
|
|
130
|
+
if (held === undefined) {
|
|
131
|
+
held = shapes.push(shape) - 1;
|
|
132
|
+
known.set(key, held);
|
|
133
|
+
}
|
|
134
|
+
return { room_ref, room_handle, name, position, shape: held };
|
|
135
|
+
});
|
|
136
|
+
return { shapes, rooms };
|
|
137
|
+
}
|
|
138
|
+
function unfolded(shapes, rooms) {
|
|
139
|
+
if (!Array.isArray(shapes) || !Array.isArray(rooms))
|
|
140
|
+
return null;
|
|
141
|
+
const authorities = [];
|
|
142
|
+
for (const room of rooms) {
|
|
143
|
+
if (typeof room !== 'object' || room === null)
|
|
144
|
+
return null;
|
|
145
|
+
const { shape, ...own } = room;
|
|
146
|
+
const held = typeof shape === 'number' ? shapes[shape] : undefined;
|
|
147
|
+
if (typeof held !== 'object' || held === null)
|
|
148
|
+
return null;
|
|
149
|
+
authorities.push({ ...held, ...own });
|
|
150
|
+
}
|
|
151
|
+
return authorities;
|
|
152
|
+
}
|
|
114
153
|
export function parseDiscovery(body) {
|
|
115
154
|
if (typeof body !== 'object' || body === null)
|
|
116
155
|
return null;
|
|
@@ -126,16 +165,19 @@ export function parseDiscovery(body) {
|
|
|
126
165
|
new Set(actions).size !== actions.length ||
|
|
127
166
|
actions.some((name) => name.length === 0))
|
|
128
167
|
return null;
|
|
129
|
-
|
|
168
|
+
const authorities = parseAuthorities(unfolded(held.shapes, held.rooms));
|
|
169
|
+
return authorities === null ? null : { member, bounds, authorities, actions };
|
|
170
|
+
}
|
|
171
|
+
export function parseAuthorityChange(body) {
|
|
172
|
+
if (typeof body !== 'object' || body === null)
|
|
130
173
|
return null;
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
return { member, bounds, authorities, actions };
|
|
174
|
+
const held = body;
|
|
175
|
+
const member = parseMember(held.member);
|
|
176
|
+
const authorities = parseAuthorities(held.authorities);
|
|
177
|
+
const left = stringList(held.left);
|
|
178
|
+
if (member === null || authorities === null || left === null)
|
|
179
|
+
return null;
|
|
180
|
+
return { member, authorities, left };
|
|
139
181
|
}
|
|
140
182
|
function parseSiteAuthority(value) {
|
|
141
183
|
if (typeof value !== 'object' || value === null)
|
|
@@ -216,25 +258,17 @@ export function providerLimits(bounds) {
|
|
|
216
258
|
export function declaredRetention(bounds) {
|
|
217
259
|
return {
|
|
218
260
|
history: { kind: 'no-declared-time-bound' },
|
|
219
|
-
outcome: bounds.operation_retention_seconds,
|
|
220
261
|
refreshHorizon: bounds.change_retention_seconds,
|
|
221
262
|
};
|
|
222
263
|
}
|
|
223
|
-
export function capabilities(actions
|
|
224
|
-
|
|
225
|
-
return [
|
|
226
|
-
{
|
|
227
|
-
name: PROVIDER_ACTIONS,
|
|
228
|
-
actions: declaredActions(actions),
|
|
229
|
-
...(own === undefined ? {} : { conversationAgents: houseConversationAgents(own.authority) }),
|
|
230
|
-
},
|
|
231
|
-
];
|
|
264
|
+
export function capabilities(actions) {
|
|
265
|
+
return [{ name: PROVIDER_ACTIONS, actions: declaredActions(actions) }];
|
|
232
266
|
}
|
|
233
267
|
export function siteCapabilities() {
|
|
234
268
|
return [
|
|
235
269
|
{
|
|
236
270
|
name: PROVIDER_ACTIONS,
|
|
237
|
-
actions: [{ name:
|
|
271
|
+
actions: [{ name: SITE_SUBMISSION, inputShape: {}, resultKind: 'house' }],
|
|
238
272
|
},
|
|
239
273
|
];
|
|
240
274
|
}
|
|
@@ -247,7 +281,7 @@ export function discoveredRoom(authority) {
|
|
|
247
281
|
roomVersion: roomVersion(authority),
|
|
248
282
|
operations: {
|
|
249
283
|
write: [...authority.operations.write],
|
|
250
|
-
|
|
284
|
+
erase: [...authority.operations.erase],
|
|
251
285
|
},
|
|
252
286
|
replica: {
|
|
253
287
|
selection: locator,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AcceptedMessage, ActionResult, ConversationHistoryRequest } from '@agentshouse/mdruntime';
|
|
2
|
+
import { type HouseDoor } from './transport.js';
|
|
3
|
+
export declare const HISTORY_PAGE = 100;
|
|
4
|
+
type HistoryCursor = {
|
|
5
|
+
accepted_at: string;
|
|
6
|
+
message_id: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function cursorPosition(cursor: HistoryCursor): string;
|
|
9
|
+
export declare function acceptedMessage(entry: unknown): AcceptedMessage | null;
|
|
10
|
+
export declare function houseHistory(transport: HouseDoor, request: ConversationHistoryRequest): Promise<ActionResult>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EAGZ,0BAA0B,EAC3B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhE,eAAO,MAAM,YAAY,MAAM,CAAC;AAIhC,KAAK,aAAa,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAkBjE,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAE5D;AAUD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAUtE;AA8BD,wBAAsB,YAAY,CAChC,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,YAAY,CAAC,CAqBvB"}
|