@agentshouse/mdruntime-adapter 0.1.0-cf6a7cf003c4b → 0.1.0-cf9ab9b7cbbca

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 (46) hide show
  1. package/AGENTS.md +261 -231
  2. package/dist/actions.d.ts +23 -0
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +74 -32
  5. package/dist/adapter.d.ts +1 -0
  6. package/dist/adapter.d.ts.map +1 -1
  7. package/dist/adapter.js +229 -61
  8. package/dist/discovery.d.ts +1 -0
  9. package/dist/discovery.d.ts.map +1 -1
  10. package/dist/discovery.js +6 -2
  11. package/dist/handed.d.ts +15 -0
  12. package/dist/handed.d.ts.map +1 -0
  13. package/dist/handed.js +38 -0
  14. package/dist/index.d.ts +2 -2
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/model.d.ts +0 -1
  18. package/dist/model.d.ts.map +1 -1
  19. package/dist/model.js +0 -3
  20. package/dist/read.d.ts +29 -6
  21. package/dist/read.d.ts.map +1 -1
  22. package/dist/read.js +150 -52
  23. package/dist/referrer.d.ts +7 -0
  24. package/dist/referrer.d.ts.map +1 -0
  25. package/dist/referrer.js +35 -0
  26. package/dist/refusal.d.ts.map +1 -1
  27. package/dist/refusal.js +2 -1
  28. package/dist/site.d.ts +1 -1
  29. package/dist/site.d.ts.map +1 -1
  30. package/dist/site.js +157 -47
  31. package/dist/stream.d.ts +5 -4
  32. package/dist/stream.d.ts.map +1 -1
  33. package/dist/stream.js +303 -16
  34. package/dist/transport.d.ts +52 -7
  35. package/dist/transport.d.ts.map +1 -1
  36. package/dist/transport.js +3 -0
  37. package/dist/version.d.ts +1 -0
  38. package/dist/version.d.ts.map +1 -1
  39. package/dist/version.js +1 -1
  40. package/dist/write.d.ts +2 -9
  41. package/dist/write.d.ts.map +1 -1
  42. package/dist/write.js +61 -8
  43. package/package.json +2 -3
  44. package/dist/links.d.ts +0 -20
  45. package/dist/links.d.ts.map +0 -1
  46. package/dist/links.js +0 -104
package/dist/site.js CHANGED
@@ -1,10 +1,12 @@
1
- import { actionOutcome } from './actions.js';
1
+ import { actionOutcome, SITE_SUBMIT } from './actions.js';
2
2
  import { mapDiscovery, parseSiteDiscovery, siteCapabilities, siteTracked, } from './discovery.js';
3
- import { scopeModels, streamedModels, viewModels } from './model.js';
4
- import { advancedScope, bootstrapOf, parseEnumeration, parseSiteBootstrap, refreshFrom, viewOf, } from './read.js';
3
+ import { partModels, streamedModels, viewModels } from './model.js';
4
+ import { advancedScope, bootstrapOf, parseEnumeration, parseSiteBootstrap, refreshFrom, siteBootstrapNext, siteBootstrapOffset, viewOf, wholeView, } from './read.js';
5
+ import { sha256Hex } from './digest.js';
5
6
  import { classifyDoor, classifyRead, discoveryFailure } from './refusal.js';
6
7
  import { houseNotifications } from './stream.js';
7
- import { SITE_DOOR_BOOTSTRAP, SITE_DOOR_DISCOVER, SITE_DOOR_ENUMERATE, SITE_DOOR_SUBMIT, } from './transport.js';
8
+ import { SITE_DOOR_BOOTSTRAP, SITE_DOOR_DISCOVER, SITE_DOOR_ENUMERATE, SITE_DOOR_SUBMIT, SITE_FILE_RECORD, SITE_FILES, } from './transport.js';
9
+ import { houseReferrer } from './referrer.js';
8
10
  import { followsVersion, logPosition, presented, reachedVersion, roomVersion, } from './version.js';
9
11
  const UNCERTAIN = { kind: 'transport-uncertainty' };
10
12
  function siteRoom(tracked) {
@@ -21,35 +23,97 @@ function siteRoom(tracked) {
21
23
  function settled(refused) {
22
24
  return refused.kind === 'beyond-horizon' ? UNCERTAIN : refused;
23
25
  }
24
- export function createSiteAdapter(transport) {
26
+ export function createSiteAdapter(transport, snapshot = false) {
25
27
  const notifications = houseNotifications(transport, 'site');
26
28
  const tracked = new Map();
27
- async function enumeration(entry, from) {
29
+ function published(referrer) {
30
+ const named = houseReferrer(referrer, tracked);
31
+ return named === null || !tracked.has(named.locator) ? null : named;
32
+ }
33
+ async function enumerated(entry, from, page) {
28
34
  const answer = await transport.door(SITE_DOOR_ENUMERATE, {
29
- positions: [presented(entry.authority, from)],
35
+ positions: [
36
+ page === undefined
37
+ ? presented(entry.authority, from)
38
+ : {
39
+ ...presented(entry.authority, { position: page.after, logEpoch: from.logEpoch }),
40
+ page: { through: page.through, read: page.read },
41
+ },
42
+ ],
30
43
  });
31
44
  const classified = classifyRead(answer.status, answer.body, entry.locator);
32
45
  if (classified !== null)
33
46
  return classified;
34
- return parseEnumeration(answer.body, 'site') ?? UNCERTAIN;
47
+ const found = parseEnumeration(answer.body, 'site');
48
+ if (found === null)
49
+ return UNCERTAIN;
50
+ return viewOf(found, entry) ?? { kind: 'room-not-readable', locator: entry.locator };
51
+ }
52
+ async function enumeration(entry, from) {
53
+ const first = await enumerated(entry, from);
54
+ return 'kind' in first ? first : wholeView(first, (page) => enumerated(entry, from, page));
35
55
  }
36
- async function caughtUp(entry) {
37
- const answer = await transport.read(`${SITE_DOOR_BOOTSTRAP}${entry.authority}`);
56
+ async function bootstrapPart(entry, after, offset) {
57
+ const query = [
58
+ ...(after === null ? [] : [`after=${encodeURIComponent(after)}`]),
59
+ ...(offset === null ? [] : [`offset=${offset}`]),
60
+ ];
61
+ const answer = await transport.read(`${SITE_DOOR_BOOTSTRAP}${entry.authority}${query.length === 0 ? '' : `?${query.join('&')}`}`);
38
62
  const classified = classifyDoor(answer.status, answer.body, entry.locator);
39
63
  if (classified !== null)
40
64
  return { failure: classified };
41
65
  const scope = parseSiteBootstrap(answer.body);
42
- if (scope === null)
43
- return { failure: UNCERTAIN };
44
- if (roomVersion(scope) === roomVersion(entry))
45
- return { scope };
46
- const found = await enumeration(entry, scope);
47
- if ('kind' in found)
48
- return { failure: settled(found) };
49
- const view = viewOf(found, entry);
50
- if (view === undefined)
51
- return { failure: { kind: 'room-not-readable', locator: entry.locator } };
52
- return { scope: advancedScope(scope, view) };
66
+ return scope === null ? { failure: UNCERTAIN } : { body: answer.body, scope };
67
+ }
68
+ async function assembled(entry, scope, path, offset) {
69
+ const opened = scope.contents.at(-1);
70
+ if (opened === undefined || opened.path !== path || opened.content.length !== offset) {
71
+ return UNCERTAIN;
72
+ }
73
+ let text = opened.content;
74
+ for (;;) {
75
+ const part = await bootstrapPart(entry, path, text.length);
76
+ if ('failure' in part)
77
+ return part.failure;
78
+ const [piece] = part.scope.contents;
79
+ if (piece === undefined ||
80
+ part.scope.contents.length !== 1 ||
81
+ piece.path !== path ||
82
+ piece.content.length === 0) {
83
+ return UNCERTAIN;
84
+ }
85
+ text += piece.content;
86
+ const more = siteBootstrapOffset(part.body);
87
+ if (more === null)
88
+ break;
89
+ if (more !== text.length)
90
+ return UNCERTAIN;
91
+ }
92
+ const file = scope.files.find((held) => held.path === path);
93
+ if (file === undefined || (await sha256Hex(text)) !== file.version)
94
+ return UNCERTAIN;
95
+ return { ...scope, contents: [...scope.contents.slice(0, -1), { path, content: text }] };
96
+ }
97
+ async function caughtUp(entry, after) {
98
+ const part = await bootstrapPart(entry, after, null);
99
+ if ('failure' in part)
100
+ return part;
101
+ const next = siteBootstrapNext(part.body);
102
+ const offset = siteBootstrapOffset(part.body);
103
+ if (offset !== null) {
104
+ if (next === null)
105
+ return { failure: UNCERTAIN };
106
+ const scope = await assembled(entry, part.scope, next, offset);
107
+ return 'kind' in scope ? { failure: scope } : { scope, next };
108
+ }
109
+ const { scope } = part;
110
+ if (after !== null || next !== null || roomVersion(scope) === roomVersion(entry)) {
111
+ return { scope, next };
112
+ }
113
+ const view = await enumeration(entry, scope);
114
+ if ('kind' in view)
115
+ return { failure: settled(view) };
116
+ return { scope: advancedScope(scope, view), next: null };
53
117
  }
54
118
  async function refreshed(entry, fromRoomVersion) {
55
119
  const from = logPosition(fromRoomVersion);
@@ -60,12 +124,9 @@ export function createSiteAdapter(transport) {
60
124
  const models = await streamedModels(streamed.schemas, entry, 'site');
61
125
  return models === undefined ? streamed.page : { ...streamed.page, models };
62
126
  }
63
- const found = await enumeration(entry, from);
64
- if ('kind' in found)
65
- return found;
66
- const view = viewOf(found, entry);
67
- if (view === undefined)
68
- return { kind: 'room-not-readable', locator: entry.locator };
127
+ const view = await enumeration(entry, from);
128
+ if ('kind' in view)
129
+ return view;
69
130
  notifications.reach(entry.authority, view);
70
131
  const answer = refreshFrom(view, fromRoomVersion);
71
132
  if (answer.kind !== 'net-advancement')
@@ -75,6 +136,7 @@ export function createSiteAdapter(transport) {
75
136
  }
76
137
  return {
77
138
  localSaves: true,
139
+ exhaustiveDiscovery: true,
78
140
  async authorize() {
79
141
  return {};
80
142
  },
@@ -97,14 +159,24 @@ export function createSiteAdapter(transport) {
97
159
  const entry = tracked.get(request.locator);
98
160
  if (entry === undefined)
99
161
  return { kind: 'room-not-readable', locator: request.locator };
100
- if (request.pageToken !== undefined)
101
- return UNCERTAIN;
102
- const held = await caughtUp(entry);
162
+ const continued = request.pageToken === undefined
163
+ ? null
164
+ : JSON.parse(request.pageToken);
165
+ const held = await caughtUp(entry, continued?.after ?? null);
103
166
  if ('failure' in held)
104
167
  return held.failure;
105
168
  notifications.reach(entry.authority, held.scope);
106
- const models = await scopeModels(held.scope, entry);
107
- return { ...bootstrapOf(held.scope), ...(models === undefined ? {} : { models }) };
169
+ const seen = [
170
+ ...(continued?.seen ?? []),
171
+ ...entry.schemas.filter((schema) => held.scope.files.some((file) => file.path === schema)),
172
+ ];
173
+ const models = await partModels(held.scope, entry, new Set(seen), held.next === null);
174
+ const continuation = held.next === null ? null : { after: held.next, seen };
175
+ return {
176
+ ...bootstrapOf(held.scope),
177
+ ...(models === undefined ? {} : { models }),
178
+ ...(continuation === null ? {} : { pageToken: JSON.stringify(continuation) }),
179
+ };
108
180
  },
109
181
  async refresh(_authorization, request) {
110
182
  const entry = tracked.get(request.locator);
@@ -112,6 +184,11 @@ export function createSiteAdapter(transport) {
112
184
  return { kind: 'room-not-readable', locator: request.locator };
113
185
  if (request.pageToken !== undefined)
114
186
  return UNCERTAIN;
187
+ if (snapshot) {
188
+ return request.fromRoomVersion === roomVersion(entry)
189
+ ? { kind: 'still-current' }
190
+ : { kind: 'beyond-horizon' };
191
+ }
115
192
  return refreshed(entry, request.fromRoomVersion);
116
193
  },
117
194
  async mutate() {
@@ -120,6 +197,32 @@ export function createSiteAdapter(transport) {
120
197
  async history() {
121
198
  return { kind: 'no-history' };
122
199
  },
200
+ async binary(_authorization, request) {
201
+ const named = published(request.kind === 'path' ? { room: request.locator, path: request.path } : request.referrer);
202
+ if (named === null) {
203
+ return { kind: 'rejected', code: 'referrer_unknown', fields: {}, retryable: false };
204
+ }
205
+ if (request.fileRef === undefined) {
206
+ return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
207
+ }
208
+ const { room_ref, path, record } = named.referrer;
209
+ const read = await transport.download(`${SITE_FILES}${room_ref}/${encodeURIComponent(request.fileRef)}/${path === undefined
210
+ ? `${SITE_FILE_RECORD}/${encodeURIComponent(record)}`
211
+ : encodeURIComponent(path)}`);
212
+ if (read.status === 404) {
213
+ return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
214
+ }
215
+ return read.status === 200 && read.blob !== null
216
+ ? { kind: 'binary', blob: read.blob }
217
+ : UNCERTAIN;
218
+ },
219
+ referrerScope(referrer) {
220
+ const named = published(referrer);
221
+ if (named === null)
222
+ return null;
223
+ const { path } = named.referrer;
224
+ return { kind: 'room', locator: named.locator, ...(path === undefined ? {} : { path }) };
225
+ },
123
226
  reached(locator, version, target) {
124
227
  return reachedVersion(version, target, tracked.get(locator)?.logEpoch);
125
228
  },
@@ -127,7 +230,7 @@ export function createSiteAdapter(transport) {
127
230
  return followsVersion(version, next);
128
231
  },
129
232
  async action(_authorization, request) {
130
- if (request.name !== 'submit') {
233
+ if (request.name !== SITE_SUBMIT) {
131
234
  return {
132
235
  kind: 'rejected',
133
236
  code: 'action-unimplemented',
@@ -137,22 +240,29 @@ export function createSiteAdapter(transport) {
137
240
  }
138
241
  if (!transport.online())
139
242
  return UNCERTAIN;
140
- return actionOutcome(await transport.door(SITE_DOOR_SUBMIT, request.input), 'submit');
243
+ return actionOutcome(await transport.door(SITE_DOOR_SUBMIT, request.input), SITE_SUBMIT);
141
244
  },
142
- roomStream: {
143
- hold(rooms) {
144
- notifications.send(rooms.flatMap((room) => {
145
- const entry = tracked.get(room.locator);
146
- const at = logPosition(room.roomVersion);
147
- if (entry === undefined || at === null)
148
- return [];
149
- notifications.reach(entry.authority, at);
150
- return [presented(entry.authority, at)];
151
- }));
245
+ roomStream: snapshot
246
+ ? undefined
247
+ : {
248
+ hold(rooms) {
249
+ notifications.send(rooms.flatMap((room) => {
250
+ const entry = tracked.get(room.locator);
251
+ const at = logPosition(room.roomVersion);
252
+ if (entry === undefined || at === null)
253
+ return [];
254
+ notifications.reach(entry.authority, at);
255
+ return [presented(entry.authority, at)];
256
+ }), null);
257
+ },
258
+ reopen: notifications.reopen,
259
+ observe: notifications.observe,
260
+ published(locator, roomVersion) {
261
+ const at = logPosition(roomVersion);
262
+ if (at !== null)
263
+ notifications.published(locator, at);
264
+ },
152
265
  },
153
- reopen: notifications.reopen,
154
- observe: notifications.observe,
155
- },
156
266
  serviceMode: notifications.serviceMode,
157
267
  };
158
268
  }
package/dist/stream.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { ConversationStream, RefreshPage, RoomStreamEvent, ServiceModeSignal } from '@agentshouse/mdruntime';
1
+ import type { ConversationStream, RefreshPage, RoomStreamEvent, ServiceModeSignal, StreamReceipts } from '@agentshouse/mdruntime';
2
2
  import type { TrackedAuthority } from './discovery.js';
3
- import type { HouseDoor, HouseHeldRoom, HouseMode, HouseStreamEntry } from './transport.js';
3
+ import type { HouseDemand, HouseDoor, HouseHeldRoom, HouseMode, HouseStreamEntry } from './transport.js';
4
4
  import { type LogPosition } from './version.js';
5
5
  export type HeldRevision = (path: string, revision: string) => boolean;
6
6
  export type StreamedAdvancement = {
@@ -10,8 +10,9 @@ export type StreamedAdvancement = {
10
10
  };
11
11
  export type HouseNotifications = {
12
12
  runtime: string;
13
- observe(notify: (event: RoomStreamEvent) => void): () => void;
14
- send(rooms: readonly HouseHeldRoom[]): void;
13
+ observe(notify: (event: RoomStreamEvent) => void, receipts: StreamReceipts): () => void;
14
+ send(rooms: readonly HouseHeldRoom[], demand: HouseDemand | null): void;
15
+ published(locator: string, at: LogPosition): void;
15
16
  reopen(): void;
16
17
  track(authorities: readonly TrackedAuthority[]): void;
17
18
  reach(authority: string, at: LogPosition): void;
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EAElB,WAAW,EACX,eAAe,EAEf,iBAAiB,EAElB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EAEb,SAAS,EAET,gBAAgB,EAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAgB7D,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;AAEvE,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACrC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,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,aAAa,EAAE,GAAG,IAAI,CAAC;IAC5C,MAAM,IAAI,IAAI,CAAC;IACf,KAAK,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD,OAAO,CACL,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,WAAW,EACjB,KAAK,CAAC,EAAE,YAAY,GACnB,mBAAmB,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,kBAAkB,CAAC;IAClC,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AAuBF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,kBAAkB,CAyS5F"}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,kBAAkB,EAElB,WAAW,EACX,eAAe,EAEf,iBAAiB,EACjB,cAAc,EAEf,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EACT,aAAa,EAEb,SAAS,EAET,gBAAgB,EAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AA0C7D,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;AAEvE,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACrC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,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,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI,CAAC;IACxF,IAAI,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;IACxE,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IAClD,MAAM,IAAI,IAAI,CAAC;IACf,KAAK,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD,OAAO,CACL,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,WAAW,EACjB,KAAK,CAAC,EAAE,YAAY,GACnB,mBAAmB,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,kBAAkB,CAAC;IAClC,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AAkFF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,kBAAkB,CAohB5F"}