@agentshouse/mdruntime-adapter 0.1.0-cd0511bd2d033 → 0.1.0-cd24cf2286fd6

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/read.js CHANGED
@@ -1,6 +1,5 @@
1
- import { paced, patching, ROW_WEIGHT, } from '@agentshouse/mdruntime';
2
- import { gunzipped } from './part.js';
3
- import { readPosition, roomVersion } from './version.js';
1
+ import { patching, ROW_WEIGHT, } from '@agentshouse/mdruntime';
2
+ import { roomVersion } from './version.js';
4
3
  const REVISION_FILE = /^fr_([0-9a-z]+)\.[0-9a-z]+\.[0-9a-z]$/u;
5
4
  function fileOf(revision) {
6
5
  return REVISION_FILE.exec(revision)?.[1];
@@ -144,7 +143,10 @@ function binaried(binary) {
144
143
  export function carriedFacts(held) {
145
144
  return held.facts === undefined ? {} : { facts: held.facts };
146
145
  }
147
- function parseFile(value, mode) {
146
+ function carriedSpace(held) {
147
+ return typeof held.space === 'string' ? { space: held.space } : {};
148
+ }
149
+ export function parseFile(value, mode) {
148
150
  if (typeof value !== 'object' || value === null)
149
151
  return null;
150
152
  const file = value;
@@ -176,150 +178,18 @@ function parseFile(value, mode) {
176
178
  ...headed(head),
177
179
  ...binaried(binary),
178
180
  ...carriedFacts(file),
179
- };
180
- }
181
- function parseModels(value, mode) {
182
- if (!Array.isArray(value))
183
- return null;
184
- const files = [];
185
- const contents = [];
186
- for (const entry of value) {
187
- const file = parseFile(entry, mode);
188
- const { content } = entry;
189
- if (file === null || typeof content !== 'string')
190
- return null;
191
- files.push(file);
192
- contents.push({ path: file.path, content });
193
- }
194
- return { files, contents };
195
- }
196
- function parseRoomHead(value, mode) {
197
- if (typeof value !== 'object' || value === null)
198
- return null;
199
- const held = value;
200
- if (typeof held.manifest !== 'object' || held.manifest === null)
201
- return null;
202
- const models = parseModels(held.models, mode);
203
- if (models === null)
204
- return null;
205
- const manifest = held.manifest;
206
- if (manifest.scope !== 'room')
207
- return null;
208
- const at = readPosition(manifest);
209
- if (typeof manifest.anchor !== 'string' || manifest.anchor.length === 0 || at === null)
210
- return null;
211
- if (typeof manifest.room_handle !== 'string' || manifest.room_handle.length === 0)
212
- return null;
213
- return { anchor: manifest.anchor, ...at, models };
214
- }
215
- export async function roomHead(payload, mode) {
216
- let text = '';
217
- try {
218
- for await (const chunk of gunzipped(payload))
219
- text += chunk;
220
- return text.endsWith('\n') && text.indexOf('\n') === text.length - 1
221
- ? parseRoomHead(JSON.parse(text), mode)
222
- : null;
223
- }
224
- catch {
225
- return null;
226
- }
227
- }
228
- export function manifested(room, pageToken, models) {
229
- return {
230
- kind: 'bootstrap',
231
- roomVersion: roomVersion(room),
232
- documents: {},
233
- facts: {},
234
- revisions: {},
235
- changes: {},
236
- binaryFiles: {},
237
- pageToken,
238
- ...(models === undefined ? {} : { models }),
181
+ ...carriedSpace(file),
239
182
  };
240
183
  }
241
184
  export function modelScope(room) {
242
- return { ...emptyScope(room), files: room.models.files, contents: room.models.contents };
243
- }
244
- export function emptyScope(room) {
245
185
  return {
246
186
  anchor: room.anchor,
247
187
  position: room.position,
248
188
  logEpoch: room.logEpoch,
249
- files: [],
250
- contents: [],
189
+ files: room.models.files,
190
+ contents: room.models.contents,
251
191
  };
252
192
  }
253
- function partLine(line, mode, read) {
254
- if (read.ended)
255
- return false;
256
- let entry;
257
- try {
258
- entry = JSON.parse(line);
259
- }
260
- catch {
261
- return false;
262
- }
263
- if (endLine(entry)) {
264
- read.ended = true;
265
- return true;
266
- }
267
- if (folderLine(entry)) {
268
- read.folders.push(entry.folder);
269
- return true;
270
- }
271
- const file = parseFile(entry, mode);
272
- if (file === null)
273
- return false;
274
- read.files.push(file);
275
- const { content } = entry;
276
- if (typeof content === 'string')
277
- read.contents.push({ path: file.path, content });
278
- else if (content !== undefined)
279
- return false;
280
- return true;
281
- }
282
- async function partRead(chunks, mode) {
283
- const read = { files: [], contents: [], folders: [], ended: false };
284
- const pace = paced();
285
- const unfinished = [];
286
- for (let chunk = await chunks.read(); chunk.done !== true; chunk = await chunks.read()) {
287
- const text = chunk.value;
288
- let start = 0;
289
- for (let end = text.indexOf('\n'); end !== -1; end = text.indexOf('\n', start)) {
290
- const piece = text.slice(start, end);
291
- const line = unfinished.length === 0 ? piece : unfinished.splice(0).join('') + piece;
292
- if (!partLine(line, mode, read)) {
293
- await chunks.cancel();
294
- return null;
295
- }
296
- start = end + 1;
297
- const due = pace(line.length + ROW_WEIGHT);
298
- if (due instanceof Promise)
299
- await due;
300
- }
301
- if (start < text.length)
302
- unfinished.push(text.slice(start));
303
- }
304
- return unfinished.length === 0 ? read : null;
305
- }
306
- function endLine(entry) {
307
- if (typeof entry !== 'object' || entry === null || !('end' in entry))
308
- return false;
309
- return entry.end === true && Object.keys(entry).length === 1;
310
- }
311
- function folderLine(entry) {
312
- if (typeof entry !== 'object' || entry === null || !('folder' in entry))
313
- return false;
314
- return typeof entry.folder === 'string' && Object.keys(entry).length === 1;
315
- }
316
- export async function housePart(room, payload, mode) {
317
- const read = await partRead(gunzipped(payload).getReader(), mode).catch(() => null);
318
- if (read === null)
319
- return { kind: 'transport-uncertainty' };
320
- const { ended, ...scope } = read;
321
- return { scope: { ...emptyScope(room), ...scope }, ended };
322
- }
323
193
  export function* bootstrapping(scope) {
324
194
  const contents = new Map();
325
195
  for (const entry of scope.contents) {
@@ -329,6 +199,8 @@ export function* bootstrapping(scope) {
329
199
  const answer = answering();
330
200
  for (const file of scope.files) {
331
201
  yield ROW_WEIGHT;
202
+ if (file.space !== undefined)
203
+ continue;
332
204
  if (file.head !== undefined)
333
205
  answer.changes.set(file.path, file.head);
334
206
  const content = contents.get(file.path);
@@ -353,5 +225,10 @@ export function schemaOf(scope, schema) {
353
225
  const content = scope.contents.find((entry) => entry.path === schema);
354
226
  return content === undefined
355
227
  ? null
356
- : { schema: content.content, version: file.version, revision: file.revision ?? null };
228
+ : {
229
+ schema: content.content,
230
+ version: file.version,
231
+ revision: file.revision ?? null,
232
+ ...(file.space === undefined ? {} : { space: file.space }),
233
+ };
357
234
  }
@@ -1 +1 @@
1
- {"version":3,"file":"room-types.d.ts","sourceRoot":"","sources":["../src/room-types.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,YAC9B,cAAc,EACd,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,WAAW,EACX,OAAO,EACP,QAAQ,CACA,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,eAAO,MAAM,SAAS,SAAS,CAAC;AAEhC,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD,eAAO,MAAM,yBAAyB,UAAiC,CAAC;AAyBxE,eAAO,MAAM,gBAAgB,QA6FjB,CAAC"}
1
+ {"version":3,"file":"room-types.d.ts","sourceRoot":"","sources":["../src/room-types.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,YAC9B,cAAc,EACd,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,WAAW,EACX,OAAO,EACP,QAAQ,CACA,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,eAAO,MAAM,SAAS,SAAS,CAAC;AAEhC,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD,eAAO,MAAM,yBAAyB,UAAiC,CAAC;AAyBxE,eAAO,MAAM,gBAAgB,QA2FjB,CAAC"}
@@ -58,8 +58,6 @@ export const HOUSE_ROOM_TYPES = [
58
58
  ` username: ${IDENTITY}`,
59
59
  ' sent_at: { type: string, format: date-time }',
60
60
  ` channel_message: ${IDENTITY}`,
61
- ' state: { enum: [sending, sent, failed] }',
62
- ' reason: { type: string }',
63
61
  ' labelled: { const: true }',
64
62
  ' reactions:',
65
63
  ' type: array',
@@ -1 +1 @@
1
- {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAQR,MAAM,wBAAwB,CAAC;AAgBhC,OAAO,EAKL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAuBxB,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAuK1F"}
1
+ {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAQR,MAAM,wBAAwB,CAAC;AAehC,OAAO,EAKL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAuBxB,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAiK1F"}
package/dist/site.js CHANGED
@@ -1,14 +1,13 @@
1
- import { stepped } from '@agentshouse/mdruntime';
2
1
  import { actionOutcome } from './actions.js';
2
+ import { chunkedHead, chunkPart, siteChunkList } from './chunks.js';
3
3
  import { mapDiscovery, parseSiteDiscovery, siteCapabilities, siteTracked, } from './discovery.js';
4
- import { bootstrapping } from './read.js';
5
- import { roomStreams } from './room-stream.js';
6
- import { classifyDoor, discoveryFailure } from './refusal.js';
4
+ import { spaceSchemas } from './space-schema.js';
5
+ import { discoveryFailure } from './refusal.js';
7
6
  import { HOUSE_TYPED_PATHS } from './house-model.js';
8
7
  import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
9
8
  import { houseNotifications } from './stream.js';
10
9
  import { houseSiteRoute, SITE_DOOR_SUBMIT, SITE_FILES, SITE_SUBMISSION, } from './transport.js';
11
- import { followsVersion, logPosition, presented, reachedVersion, roomVersion } from './version.js';
10
+ import { followsVersion, reachedVersion, roomVersion } from './version.js';
12
11
  const UNCERTAIN = { kind: 'transport-uncertainty' };
13
12
  function unfound() {
14
13
  return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
@@ -19,50 +18,47 @@ function siteRoom(tracked) {
19
18
  locator: tracked.locator,
20
19
  handle: tracked.handle,
21
20
  roomVersion: roomVersion(tracked),
22
- operations: { write: [], delete: [] },
21
+ operations: { write: [], erase: [] },
23
22
  replica: { selection: tracked.locator, admission: 'admitted', offline: 'open' },
24
23
  providerOwnedPaths: [],
25
24
  };
26
25
  }
27
26
  export function createSiteAdapter(transport, snapshot = false) {
27
+ const spaces = spaceSchemas();
28
28
  const notifications = houseNotifications(transport, 'site', {
29
29
  opened: async () => {
30
30
  const found = await discovered();
31
31
  return found.kind === 'discovery' ? found : null;
32
32
  },
33
33
  changed: () => null,
34
- });
34
+ }, spaces);
35
35
  const tracked = new Map();
36
- const read = new Map();
37
- const rooms = roomStreams('site');
36
+ const chunked = new Map();
38
37
  function fileRoute(request) {
39
38
  const entry = tracked.get(request.locator);
40
39
  if (entry === undefined)
41
40
  return { kind: 'room-not-readable', locator: request.locator };
42
41
  return `${SITE_FILES}${entry.authority}/${encodeURIComponent(request.revision)}/${encodeURIComponent(request.path)}`;
43
42
  }
44
- async function openedRoom(entry, arriving, signal) {
45
- const cut = read.get(entry.locator);
46
- const answered = await transport.streamed(`${houseSiteRoute('bootstrap')}/${entry.authority}${cut === undefined ? '' : `?expired=${cut}`}`, signal ?? new AbortController().signal);
47
- if (answered.room === null)
48
- return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN;
49
- const head = await rooms.head(entry, answered.room, arriving, signal);
50
- const at = head.kind === 'bootstrap' ? logPosition(head.roomVersion) : null;
51
- if (at !== null)
52
- read.set(entry.locator, at.position);
43
+ async function listedRoom(entry) {
44
+ const past = chunked.get(entry.locator)?.position;
45
+ const listed = siteChunkList(await transport.read(`${houseSiteRoute('chunks')}/${entry.authority}${past === undefined ? '' : `?expired=${past}`}`), entry.locator);
46
+ if ('kind' in listed)
47
+ return listed;
48
+ const head = await chunkedHead(listed, entry, spaces);
49
+ if (head.kind === 'bootstrap')
50
+ chunked.set(entry.locator, listed);
53
51
  return head;
54
52
  }
55
- async function streamedPage(entry, token) {
56
- const part = await rooms.part(entry, token);
57
- if ('kind' in part)
58
- return part;
59
- const { folders: _folders, ...page } = await stepped(bootstrapping(part.scope));
60
- return {
61
- ...page,
62
- ...(part.models === undefined ? {} : { models: part.models }),
63
- ...(part.pageToken === undefined ? {} : { pageToken: part.pageToken }),
64
- bytes: part.bytes,
65
- };
53
+ async function chunkPage(entry, id, arriving) {
54
+ const room = chunked.get(entry.locator);
55
+ const address = room?.chunks.get(id);
56
+ if (room === undefined || address === undefined)
57
+ return UNCERTAIN;
58
+ const read = await transport.download(address.url);
59
+ if (read.status !== 200)
60
+ return UNCERTAIN;
61
+ return chunkPart(read.body, room, id, arriving);
66
62
  }
67
63
  async function discovered() {
68
64
  const answer = await transport.read(houseSiteRoute('discover'));
@@ -92,8 +88,8 @@ export function createSiteAdapter(transport, snapshot = false) {
92
88
  if (entry === undefined)
93
89
  return { kind: 'room-not-readable', locator: request.locator };
94
90
  return request.pageToken === undefined
95
- ? openedRoom(entry, request.arriving, request.signal)
96
- : streamedPage(entry, request.pageToken);
91
+ ? listedRoom(entry)
92
+ : chunkPage(entry, request.pageToken, request.arriving);
97
93
  },
98
94
  ...(snapshot
99
95
  ? {
@@ -162,13 +158,7 @@ export function createSiteAdapter(transport, snapshot = false) {
162
158
  roomStream: snapshot
163
159
  ? undefined
164
160
  : {
165
- hold(rooms) {
166
- notifications.send(rooms.flatMap((room) => {
167
- const entry = tracked.get(room.locator);
168
- const at = logPosition(room.roomVersion);
169
- return entry === undefined || at === null ? [] : [presented(entry.authority, at)];
170
- }));
171
- },
161
+ hold: notifications.send,
172
162
  observe: notifications.observe,
173
163
  },
174
164
  serviceMode: notifications.serviceMode,
@@ -0,0 +1,7 @@
1
+ export type SpaceSchemas = {
2
+ held(space: string, revision: string): string | undefined;
3
+ keep(space: string, revision: string, text: string): void;
4
+ };
5
+ export declare function spaceSchemas(): SpaceSchemas;
6
+ export declare function spaceIdentity(space: string): string;
7
+ //# sourceMappingURL=space-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"space-schema.d.ts","sourceRoot":"","sources":["../src/space-schema.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1D,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3D,CAAC;AAEF,wBAAgB,YAAY,IAAI,YAAY,CAY3C;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD"}
@@ -0,0 +1,18 @@
1
+ const HELD_TEXTS = 16;
2
+ export function spaceSchemas() {
3
+ const texts = new Map();
4
+ const key = (space, revision) => `${space} ${revision}`;
5
+ return {
6
+ held: (space, revision) => texts.get(key(space, revision)),
7
+ keep(space, revision, text) {
8
+ const named = key(space, revision);
9
+ texts.delete(named);
10
+ texts.set(named, text);
11
+ if (texts.size > HELD_TEXTS)
12
+ texts.delete(texts.keys().next().value);
13
+ },
14
+ };
15
+ }
16
+ export function spaceIdentity(space) {
17
+ return `space:${space}`;
18
+ }
package/dist/stream.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { type AuthorityChange, type Discovery, type ConversationStream, type RoomStreamEvent, type ServiceModeSignal } from '@agentshouse/mdruntime';
1
+ import { type AuthorityChange, type Discovery, type ConversationStream, type HeldRoom, type RoomStreamEvent, type ServiceModeSignal } from '@agentshouse/mdruntime';
2
2
  import type { TrackedAuthority } from './discovery.js';
3
- import { type HouseDoor, type HouseHeldRoom, type HouseMode, type HouseStreamFrame } from './transport.js';
3
+ import type { SpaceSchemas } from './space-schema.js';
4
+ import { type HouseDoor, type HouseMode, type HouseStreamFrame } from './transport.js';
4
5
  export type HouseOpeningFrame = Extract<HouseStreamFrame, {
5
6
  type: 'opening';
6
7
  }>;
@@ -10,14 +11,15 @@ export type HouseAuthorityChangedFrame = Extract<HouseStreamFrame, {
10
11
  export type HouseDiscovering = {
11
12
  opened(frame: HouseOpeningFrame | null): Promise<Discovery | null>;
12
13
  changed(frame: HouseAuthorityChangedFrame): AuthorityChange | null;
14
+ expired?(authorities: readonly string[]): void;
13
15
  };
14
16
  export type HouseNotifications = {
15
17
  runtime: string;
16
18
  observe(notify: (event: RoomStreamEvent) => void): () => void;
17
- send(rooms: readonly HouseHeldRoom[]): void;
19
+ send(rooms: readonly HeldRoom[]): void;
18
20
  track(authorities: readonly TrackedAuthority[]): void;
19
21
  conversations: ConversationStream;
20
22
  serviceMode: ServiceModeSignal;
21
23
  };
22
- export declare function houseNotifications(transport: HouseDoor, mode: HouseMode, discovering: HouseDiscovering): HouseNotifications;
24
+ export declare function houseNotifications(transport: HouseDoor, mode: HouseMode, discovering: HouseDiscovering, spaces: SpaceSchemas): HouseNotifications;
23
25
  //# sourceMappingURL=stream.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EAEpB,KAAK,SAAS,EACd,KAAK,kBAAkB,EAIvB,KAAK,eAAe,EAEpB,KAAK,iBAAiB,EAEvB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAWvD,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,aAAa,EAElB,KAAK,SAAS,EAGd,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AAaxB,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAE/E,MAAM,MAAM,0BAA0B,GAAG,OAAO,CAAC,gBAAgB,EAAE;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAAC;AAElG,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACnE,OAAO,CAAC,KAAK,EAAE,0BAA0B,GAAG,eAAe,GAAG,IAAI,CAAC;CACpE,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC9D,IAAI,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAAC;IACtD,aAAa,EAAE,kBAAkB,CAAC;IAClC,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AAqFF,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,SAAS,EACf,WAAW,EAAE,gBAAgB,GAC5B,kBAAkB,CA6TpB"}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EAEpB,KAAK,SAAS,EACd,KAAK,kBAAkB,EAEvB,KAAK,QAAQ,EAGb,KAAK,eAAe,EAEpB,KAAK,iBAAiB,EAEvB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAUtD,OAAO,EAEL,KAAK,SAAS,EAEd,KAAK,SAAS,EAGd,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AAaxB,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAE/E,MAAM,MAAM,0BAA0B,GAAG,OAAO,CAAC,gBAAgB,EAAE;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAAC;AAElG,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACnE,OAAO,CAAC,KAAK,EAAE,0BAA0B,GAAG,eAAe,GAAG,IAAI,CAAC;IACnE,OAAO,CAAC,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC9D,IAAI,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI,CAAC;IACvC,KAAK,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAAC;IACtD,aAAa,EAAE,kBAAkB,CAAC;IAClC,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AA2FF,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,SAAS,EACf,WAAW,EAAE,gBAAgB,EAC7B,MAAM,EAAE,YAAY,GACnB,kBAAkB,CA8XpB"}
package/dist/stream.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { ROW_WEIGHT, stepped, } from '@agentshouse/mdruntime';
2
2
  import { acceptedMessage, cursorPosition, houseHistory } from './history.js';
3
- import { streamedModels } from './model.js';
3
+ import { keptSpaceSchemas, streamedModels } from './model.js';
4
4
  import { answered, answering, binaryFileOf, folded, forgotten, headChange, revised, } from './read.js';
5
5
  import { houseDoorRoute, } from './transport.js';
6
- import { DECIMAL, roomVersion } from './version.js';
6
+ import { DECIMAL, logPosition, presented, roomVersion } from './version.js';
7
7
  function turnOperation(frame) {
8
8
  const { type: _type, conversation_id: _conversation, turn_id: _turn, ...operation } = frame;
9
9
  return structuredClone(operation);
@@ -51,6 +51,10 @@ function* changed(tracked, entries, version, schemas) {
51
51
  if (entry.path === tracked.schema)
52
52
  schemas.set(entry.path, entry);
53
53
  forgotten(answer, entry.path);
54
+ if (entry.space !== undefined) {
55
+ answer.removals.add(entry.path);
56
+ continue;
57
+ }
54
58
  if (entry.kind === 'removed') {
55
59
  answer.removals.add(entry.path);
56
60
  continue;
@@ -71,29 +75,37 @@ function* changed(tracked, entries, version, schemas) {
71
75
  }
72
76
  return { kind: 'net-advancement', roomVersion: version, ...(yield* answered(answer)) };
73
77
  }
74
- async function changePage(tracked, entries, version, mode) {
78
+ async function changePage(tracked, entries, version, mode, spaces, whole) {
75
79
  const schemas = new Map();
76
80
  const page = await stepped(changed(tracked, entries, version, schemas));
77
- const models = await streamedModels(schemas, tracked, mode);
81
+ const models = await streamedModels(schemas, tracked, mode, spaces, whole);
78
82
  return models === undefined ? page : { ...page, models };
79
83
  }
80
- export function houseNotifications(transport, mode, discovering) {
84
+ export function houseNotifications(transport, mode, discovering, spaces) {
81
85
  const stream = transport.stream;
82
86
  const staged = new Map();
83
87
  let socket = null;
84
88
  const tracking = new Map();
85
89
  const observers = new Set();
90
+ const replaying = new Map();
86
91
  const conversationObservers = new Set();
87
92
  const modeObservers = new Set();
88
93
  let replicated = [];
89
94
  let listedCursor = null;
95
+ const unanswered = [];
96
+ const heldAtZero = new Set();
90
97
  const live = new Set();
91
98
  let opening = null;
92
99
  let announced = null;
93
100
  const runtime = crypto.randomUUID();
94
101
  function emit(event) {
95
- for (const observer of observers)
96
- observer(event);
102
+ for (const observer of observers) {
103
+ const held = replaying.get(observer);
104
+ if (held === undefined)
105
+ observer(event);
106
+ else
107
+ held.push(event);
108
+ }
97
109
  }
98
110
  function emitConversation(event) {
99
111
  if (event.kind === 'turn' ||
@@ -115,6 +127,8 @@ export function houseNotifications(transport, mode, discovering) {
115
127
  announced = null;
116
128
  socket = next;
117
129
  staged.clear();
130
+ unanswered.length = 0;
131
+ heldAtZero.clear();
118
132
  const gapped = new Set([...replicated, ...live]);
119
133
  live.clear();
120
134
  for (const conversation of gapped)
@@ -216,6 +230,7 @@ export function houseNotifications(transport, mode, discovering) {
216
230
  }
217
231
  }
218
232
  const next = held ?? { authority: frame.authority, parts: 0, entries: [] };
233
+ keptSpaceSchemas(frame.entries, spaces);
219
234
  next.entries.push(...frame.entries);
220
235
  next.parts += 1;
221
236
  staged.set(frame.carry, next);
@@ -243,9 +258,13 @@ export function houseNotifications(transport, mode, discovering) {
243
258
  return;
244
259
  }
245
260
  if (frame.type === 'caught_up') {
261
+ discovering.expired?.(frame.expired);
246
262
  emit({
247
263
  kind: 'caught-up',
248
- expired: frame.expired.flatMap((authority) => (tracking.get(authority) ?? []).map((tracked) => tracked.locator)),
264
+ expired: [
265
+ ...frame.expired.flatMap((authority) => (tracking.get(authority) ?? []).map((tracked) => tracked.locator)),
266
+ ...(unanswered.shift() ?? []),
267
+ ],
249
268
  });
250
269
  return;
251
270
  }
@@ -296,7 +315,9 @@ export function houseNotifications(transport, mode, discovering) {
296
315
  const after = position(frame.after);
297
316
  if (at === null || after === null || after >= at)
298
317
  return;
299
- const from = roomVersion({ position: frame.after, logEpoch: frame.log_epoch });
318
+ const from = after === 0n && !heldAtZero.has(frame.authority)
319
+ ? null
320
+ : roomVersion({ position: frame.after, logEpoch: frame.log_epoch });
300
321
  const version = roomVersion({ position: frame.position, logEpoch: frame.log_epoch });
301
322
  for (const tracked of tracking.get(frame.authority) ?? []) {
302
323
  emit({
@@ -304,7 +325,7 @@ export function houseNotifications(transport, mode, discovering) {
304
325
  locator: tracked.locator,
305
326
  from,
306
327
  roomVersion: version,
307
- read: () => changePage(tracked, entries, version, mode),
328
+ read: () => changePage(tracked, entries, version, mode, spaces, from === null),
308
329
  });
309
330
  }
310
331
  }
@@ -315,6 +336,8 @@ export function houseNotifications(transport, mode, discovering) {
315
336
  announced = null;
316
337
  socket = null;
317
338
  staged.clear();
339
+ unanswered.length = 0;
340
+ heldAtZero.clear();
318
341
  emit({ kind: 'closed', revoked, lost, authorityChanged });
319
342
  });
320
343
  return {
@@ -349,11 +372,15 @@ export function houseNotifications(transport, mode, discovering) {
349
372
  },
350
373
  observe(notify) {
351
374
  observers.add(notify);
352
- const replayed = opening === null ? [] : [{ kind: 'opened' }, announced];
353
- if (replayed.length > 0) {
375
+ if (opening !== null) {
376
+ const replayed = [{ kind: 'opened' }];
377
+ if (announced !== null)
378
+ replayed.push(announced);
379
+ replaying.set(notify, replayed);
354
380
  queueMicrotask(() => {
381
+ replaying.delete(notify);
355
382
  for (const event of replayed)
356
- if (event !== null && observers.has(notify))
383
+ if (observers.has(notify))
357
384
  notify(event);
358
385
  });
359
386
  }
@@ -364,7 +391,47 @@ export function houseNotifications(transport, mode, discovering) {
364
391
  send(rooms) {
365
392
  opening = null;
366
393
  announced = null;
367
- socket?.hold(rooms, runtime, listedCursor);
394
+ if (socket === null)
395
+ return;
396
+ const entries = new Map([...tracking.values()].flat().map((tracked) => [tracked.locator, tracked]));
397
+ const named = rooms.flatMap((room) => {
398
+ const entry = entries.get(room.locator);
399
+ const at = room.roomVersion === null ? null : logPosition(room.roomVersion);
400
+ return entry === undefined || at === null ? [] : [{ entry, at }];
401
+ });
402
+ const unheld = rooms.flatMap((room) => {
403
+ const entry = entries.get(room.locator);
404
+ return entry === undefined || room.roomVersion !== null ? [] : [entry];
405
+ });
406
+ for (const { entry, at } of named) {
407
+ if (at.position === '0')
408
+ heldAtZero.add(entry.authority);
409
+ else
410
+ heldAtZero.delete(entry.authority);
411
+ }
412
+ for (const entry of unheld)
413
+ heldAtZero.delete(entry.authority);
414
+ const site = mode === 'site';
415
+ unanswered.push(site ? unheld.map((entry) => entry.locator) : []);
416
+ socket.hold([
417
+ ...named.map(({ entry, at }) => presented(entry.authority, at)),
418
+ ...(site ? [] : unheld).map((entry) => presented(entry.authority, { position: '0', logEpoch: entry.logEpoch })),
419
+ ], runtime, listedCursor);
420
+ if (site)
421
+ return;
422
+ const made = unheld.filter((entry) => entry.position === '0');
423
+ queueMicrotask(() => {
424
+ for (const entry of made) {
425
+ const version = roomVersion(entry);
426
+ emit({
427
+ kind: 'room-change',
428
+ locator: entry.locator,
429
+ from: null,
430
+ roomVersion: version,
431
+ read: () => changePage(entry, [], version, mode, spaces, true),
432
+ });
433
+ }
434
+ });
368
435
  },
369
436
  track(entries) {
370
437
  tracking.clear();
@@ -1,12 +1,12 @@
1
1
  import type { ConversationEvent, SavedFacts, TurnOperation } from '@agentshouse/mdruntime';
2
2
  export declare const HOUSE_RUNTIME_READS: {
3
- readonly acts: readonly ['account/push', 'agents/read', 'apps/features/set', 'apps/source/download', 'apps/source/top', 'batch', 'batch-upload', 'bootstrap', 'conversations/history', 'discover', 'files/attach', 'files/finish', 'files/get', 'revisions', 'search', 'sites/screenshot/read'];
3
+ readonly acts: readonly ['account/push', 'agents/read', 'apps/features/set', 'apps/source/download', 'apps/source/top', 'batch', 'batch-upload', 'conversations/history', 'discover', 'files/attach', 'files/finish', 'files/get', 'revisions', 'room-chunks/list', 'search', 'sites/screenshot/read'];
4
4
  readonly frames: readonly ['app-message', 'authority_changed', 'carried', 'caught_up', 'commands', 'conversation', 'conversation_deleted', 'conversations', 'deliveries', 'deployment', 'held', 'interactions', 'link', 'message', 'opening', 'options', 'part', 'process', 'received', 'signal', 'site', 'starting', 'subagents', 'tab-icon', 'turn'];
5
5
  };
6
6
  export type HouseRuntimeAct = (typeof HOUSE_RUNTIME_READS.acts)[number];
7
7
  export type HouseRuntimeFrame = (typeof HOUSE_RUNTIME_READS.frames)[number];
8
8
  export declare function houseDoorRoute(act: HouseRuntimeAct): string;
9
- export declare function houseSiteRoute(act: Extract<HouseRuntimeAct, 'bootstrap' | 'discover'>): string;
9
+ export declare function houseSiteRoute(act: 'chunks' | 'discover'): string;
10
10
  export declare const SITE_SUBMISSION = "site/submission";
11
11
  export declare const SITE_DOOR_SUBMIT = "/_house/door/submit";
12
12
  export type HouseMode = 'credential' | 'site';
@@ -44,6 +44,7 @@ export type HouseStreamEntry = {
44
44
  };
45
45
  created_at?: string;
46
46
  facts?: SavedFacts;
47
+ space?: string;
47
48
  };
48
49
  type LiveFact<K extends ConversationEvent['kind']> = Extract<ConversationEvent, {
49
50
  kind: K;
@@ -172,15 +173,11 @@ export type HouseStream = {
172
173
  opened(notify: (opening: HouseOpening) => void): () => void;
173
174
  closed(notify: (closing: HouseClosing) => void): () => void;
174
175
  };
175
- export type HouseStreamed = HouseDoorAnswer & {
176
- room: ReadableStream<Uint8Array> | null;
177
- };
178
176
  export type HouseDoor = {
179
177
  door(route: string, body: unknown): Promise<HouseDoorAnswer>;
180
178
  stream: HouseStream;
181
179
  };
182
180
  export type HouseTransport = HouseDoor & {
183
- streamed(route: string, body: unknown, signal: AbortSignal): Promise<HouseStreamed>;
184
181
  transfer(request: HouseByteTransfer): Promise<HouseDoorAnswer>;
185
182
  put(url: string, body: Blob, headers: Readonly<Record<string, string>>): Promise<number>;
186
183
  download(url: string): Promise<HouseBytesAnswer>;
@@ -188,7 +185,6 @@ export type HouseTransport = HouseDoor & {
188
185
  export type HouseSiteTransport = HouseDoor & {
189
186
  online(): boolean;
190
187
  read(route: string): Promise<HouseDoorAnswer>;
191
- streamed(route: string, signal: AbortSignal): Promise<HouseStreamed>;
192
188
  download(route: string): Promise<HouseBytesAnswer>;
193
189
  };
194
190
  export {};