@agentshouse/mdruntime-adapter 0.1.0-cf22c8563f9f4 → 0.1.0-cf41d9210cb6b

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 (66) hide show
  1. package/README.md +2 -2
  2. package/dist/actions.d.ts +51 -2
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +123 -214
  5. package/dist/adapter.d.ts +1 -0
  6. package/dist/adapter.d.ts.map +1 -1
  7. package/dist/adapter.js +336 -109
  8. package/dist/app-reference.d.ts +2 -0
  9. package/dist/app-reference.d.ts.map +1 -0
  10. package/dist/app-reference.js +2 -0
  11. package/dist/crockford.d.ts +4 -0
  12. package/dist/crockford.d.ts.map +1 -0
  13. package/dist/crockford.js +3 -0
  14. package/dist/discovery.d.ts +9 -11
  15. package/dist/discovery.d.ts.map +1 -1
  16. package/dist/discovery.js +55 -36
  17. package/dist/gather.d.ts +1 -1
  18. package/dist/gather.d.ts.map +1 -1
  19. package/dist/gather.js +15 -20
  20. package/dist/house-model.d.ts +2 -0
  21. package/dist/house-model.d.ts.map +1 -0
  22. package/dist/house-model.js +489 -0
  23. package/dist/index.d.ts +8 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +7 -0
  26. package/dist/model.d.ts +6 -2
  27. package/dist/model.d.ts.map +1 -1
  28. package/dist/model.js +32 -20
  29. package/dist/part.d.ts +14 -0
  30. package/dist/part.d.ts.map +1 -0
  31. package/dist/part.js +90 -0
  32. package/dist/read.d.ts +74 -20
  33. package/dist/read.d.ts.map +1 -1
  34. package/dist/read.js +353 -168
  35. package/dist/refusal.d.ts +2 -5
  36. package/dist/refusal.d.ts.map +1 -1
  37. package/dist/refusal.js +6 -9
  38. package/dist/room-stream.d.ts +19 -0
  39. package/dist/room-stream.d.ts.map +1 -0
  40. package/dist/room-stream.js +57 -0
  41. package/dist/room-types.d.ts +7 -0
  42. package/dist/room-types.d.ts.map +1 -0
  43. package/dist/room-types.js +125 -0
  44. package/dist/site.d.ts +1 -1
  45. package/dist/site.d.ts.map +1 -1
  46. package/dist/site.js +131 -62
  47. package/dist/stream.d.ts +6 -4
  48. package/dist/stream.d.ts.map +1 -1
  49. package/dist/stream.js +366 -44
  50. package/dist/transport.d.ts +76 -11
  51. package/dist/transport.d.ts.map +1 -1
  52. package/dist/transport.js +4 -0
  53. package/dist/upload.d.ts +12 -0
  54. package/dist/upload.d.ts.map +1 -0
  55. package/dist/upload.js +63 -0
  56. package/dist/version.d.ts +1 -0
  57. package/dist/version.d.ts.map +1 -1
  58. package/dist/version.js +1 -1
  59. package/dist/write.d.ts +18 -9
  60. package/dist/write.d.ts.map +1 -1
  61. package/dist/write.js +164 -41
  62. package/package.json +9 -6
  63. package/AGENTS.md +0 -244
  64. package/dist/links.d.ts +0 -20
  65. package/dist/links.d.ts.map +0 -1
  66. package/dist/links.js +0 -104
package/dist/refusal.js CHANGED
@@ -6,11 +6,11 @@ export function doorRefusal(body) {
6
6
  const error = body.error;
7
7
  if (typeof error.code !== 'string')
8
8
  return null;
9
- const { code, ...fields } = error;
10
- return { code, fields, retryable: error.retryable === true };
9
+ const { code, retryable, ...fields } = error;
10
+ return { code, fields, retryable: retryable === true };
11
11
  }
12
12
  export function scopedRefusal(locator, scope, refusal) {
13
- return { kind: 'scoped-refusal', locator, scope, code: refusal.code, fields: refusal.fields };
13
+ return { kind: 'scoped-refusal', locator, scope, ...refusal };
14
14
  }
15
15
  function doorFailure(status, body) {
16
16
  if (status === 0)
@@ -18,8 +18,9 @@ function doorFailure(status, body) {
18
18
  const refusal = doorRefusal(body);
19
19
  if (status === 401)
20
20
  return { kind: 'revoked-authorization' };
21
- if (refusal?.retryable === true)
21
+ if (refusal?.retryable === true || refusal?.code === 'update_required') {
22
22
  return { kind: 'transport-uncertainty' };
23
+ }
23
24
  if (status === 404 || refusal?.code === 'room_not_found')
24
25
  return { kind: 'room-not-readable' };
25
26
  if (status < 400)
@@ -50,11 +51,7 @@ export function discoveryFailure(status, body) {
50
51
  if (failure.kind === 'revoked-authorization')
51
52
  return failure;
52
53
  if (failure.kind === 'refused') {
53
- return {
54
- kind: 'contract-unavailable',
55
- code: failure.refusal.code,
56
- fields: failure.refusal.fields,
57
- };
54
+ return { kind: 'contract-unavailable', ...failure.refusal };
58
55
  }
59
56
  return { kind: 'transport-uncertainty' };
60
57
  }
@@ -0,0 +1,19 @@
1
+ import type { BootstrapResult, RoomModels } from '@agentshouse/mdruntime';
2
+ import type { TrackedAuthority } from './discovery.js';
3
+ import { type HouseScope } from './read.js';
4
+ import type { HouseMode } from './transport.js';
5
+ export type StreamedPart = {
6
+ readonly scope: HouseScope;
7
+ readonly first: boolean;
8
+ readonly ended: boolean;
9
+ readonly models: RoomModels | undefined;
10
+ readonly pageToken: string | undefined;
11
+ readonly bytes: number;
12
+ };
13
+ export declare function roomStreams(mode: HouseMode): {
14
+ head: (entry: TrackedAuthority, room: ReadableStream<Uint8Array>, arriving: ((bytes: number) => void) | undefined, signal: AbortSignal | undefined) => Promise<BootstrapResult>;
15
+ part: (entry: TrackedAuthority, token: string) => Promise<StreamedPart | Exclude<BootstrapResult, {
16
+ kind: 'bootstrap';
17
+ }>>;
18
+ };
19
+ //# sourceMappingURL=room-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-stream.d.ts","sourceRoot":"","sources":["../src/room-stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvD,OAAO,EAAuD,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACjG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAYhD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS;kBAKhC,gBAAgB,QACjB,cAAc,CAAC,UAAU,CAAC,YACtB,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,UACvC,WAAW,GAAG,SAAS,KAC9B,OAAO,CAAC,eAAe,CAAC;kBAelB,gBAAgB,SAChB,MAAM,KACZ,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,eAAe,EAAE;QAAE,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC;EAkC3E"}
@@ -0,0 +1,57 @@
1
+ import { manifestedModels, partModels } from './model.js';
2
+ import { roomFrames } from './part.js';
3
+ import { housePart, manifested, roomHead } from './read.js';
4
+ const UNCERTAIN = { kind: 'transport-uncertainty' };
5
+ export function roomStreams(mode) {
6
+ const open = new Map();
7
+ let opened = 0;
8
+ async function head(entry, room, arriving, signal) {
9
+ const frames = roomFrames(room, arriving, signal);
10
+ const first = await frames();
11
+ if (first.kind !== 'frame')
12
+ return UNCERTAIN;
13
+ const held = await roomHead(first.payload, mode);
14
+ if (held === null || held.anchor !== entry.authority)
15
+ return UNCERTAIN;
16
+ const { models, seen } = await manifestedModels(held, entry);
17
+ opened += 1;
18
+ const id = String(opened);
19
+ open.set(id, { entry, head: held, frames, next: 1, seen });
20
+ signal?.addEventListener('abort', () => open.delete(id), { once: true });
21
+ return { ...manifested(held, `${id}:1`, models), bytes: first.bytes };
22
+ }
23
+ async function part(entry, token) {
24
+ const [id = '', at = ''] = token.split(':');
25
+ const room = open.get(id);
26
+ if (room === undefined || room.entry.locator !== entry.locator || String(room.next) !== at)
27
+ return UNCERTAIN;
28
+ const frame = await room.frames();
29
+ if (frame.kind !== 'frame') {
30
+ open.delete(id);
31
+ return UNCERTAIN;
32
+ }
33
+ const read = await housePart(room.head, frame.payload, mode);
34
+ if ('kind' in read) {
35
+ open.delete(id);
36
+ return read;
37
+ }
38
+ const { scope, ended } = read;
39
+ const first = room.next === 1;
40
+ room.next += 1;
41
+ room.seen = [
42
+ ...room.seen,
43
+ ...(scope.files.some((file) => file.path === entry.schema) ? [entry.schema] : []),
44
+ ];
45
+ if (ended)
46
+ open.delete(id);
47
+ return {
48
+ scope,
49
+ first,
50
+ ended,
51
+ models: await partModels(scope, entry, new Set(room.seen), ended),
52
+ pageToken: ended ? undefined : `${id}:${room.next}`,
53
+ bytes: frame.bytes,
54
+ };
55
+ }
56
+ return { head, part };
57
+ }
@@ -0,0 +1,7 @@
1
+ export declare const INBOX_CHANNEL_KINDS: readonly ['telegram-bot', 'telegram-business', 'whatsapp', 'messenger', 'instagram', 'viber', 'tiktok'];
2
+ export type InboxChannelKind = (typeof INBOX_CHANNEL_KINDS)[number];
3
+ export declare const INBOX_CHAT_TYPE = "inbox-chat";
4
+ export declare const INBOX_MESSAGE_TYPE = "inbox-message";
5
+ export declare const HOUSE_NAMEABLE_ROOM_TYPES: string[];
6
+ export declare const HOUSE_ROOM_TYPES: string;
7
+ //# sourceMappingURL=room-types.d.ts.map
@@ -0,0 +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,eAAe,eAAe,CAAC;AAE5C,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD,eAAO,MAAM,yBAAyB,UAAwC,CAAC;AAyB/E,eAAO,MAAM,gBAAgB,QA6FjB,CAAC"}
@@ -0,0 +1,125 @@
1
+ import { APP_REFERENCE_PATTERN } from './app-reference.js';
2
+ import { HOUSE_MODEL_TYPES } from './house-model.js';
3
+ export const INBOX_CHANNEL_KINDS = [
4
+ 'telegram-bot',
5
+ 'telegram-business',
6
+ 'whatsapp',
7
+ 'messenger',
8
+ 'instagram',
9
+ 'viber',
10
+ 'tiktok',
11
+ ];
12
+ export const INBOX_CHAT_TYPE = 'inbox-chat';
13
+ export const INBOX_MESSAGE_TYPE = 'inbox-message';
14
+ export const HOUSE_NAMEABLE_ROOM_TYPES = [INBOX_CHAT_TYPE, INBOX_MESSAGE_TYPE];
15
+ const WAYFINDING_MAP_TYPE = 'wayfinding-map';
16
+ const WAYFINDING_TICKET_TYPE = 'wayfinding-ticket';
17
+ const WAYFINDING_MAP_SECTIONS = [
18
+ 'Destination',
19
+ 'Notes',
20
+ 'Decisions so far',
21
+ 'Not yet specified',
22
+ 'Out of scope',
23
+ ];
24
+ const IDENTITY = "{ type: string, pattern: '^\\S+$' }";
25
+ const AGENT = [' agent: { type: string, format: uuid }', ' name: { type: string }'];
26
+ const ASSIGNEE = [
27
+ ' type: object',
28
+ ' properties:',
29
+ ' user: { type: string, format: uuid }',
30
+ ...AGENT,
31
+ ];
32
+ export const HOUSE_ROOM_TYPES = [
33
+ `${INBOX_CHAT_TYPE}:`,
34
+ ' properties:',
35
+ ' inbox: { type: string, format: uuid }',
36
+ ` channel: { enum: [${INBOX_CHANNEL_KINDS.join(', ')}] }`,
37
+ ` chat: ${IDENTITY}`,
38
+ ' customer:',
39
+ ' type: object',
40
+ ' properties:',
41
+ ' name: { type: string }',
42
+ ` username: ${IDENTITY}`,
43
+ ` phone: ${IDENTITY}`,
44
+ ' assignee:',
45
+ ...ASSIGNEE,
46
+ ' return_to:',
47
+ ' type: object',
48
+ ' properties:',
49
+ ...AGENT,
50
+ ' conversation: { type: string, format: uuid }',
51
+ ' required: [inbox, channel, chat, customer]',
52
+ `${INBOX_MESSAGE_TYPE}:`,
53
+ ' properties:',
54
+ ' direction: { enum: [in, out] }',
55
+ ' sender:',
56
+ ' type: object',
57
+ ' properties:',
58
+ ' name: { type: string }',
59
+ ` username: ${IDENTITY}`,
60
+ ' sent_at: { type: string, format: date-time }',
61
+ ` channel_message: ${IDENTITY}`,
62
+ ' state: { enum: [sending, sent, failed] }',
63
+ ' reason: { type: string }',
64
+ ' reactions:',
65
+ ' type: array',
66
+ ' items:',
67
+ ' type: object',
68
+ ' properties:',
69
+ ' reaction: { type: string, minLength: 1 }',
70
+ ' sender:',
71
+ ' type: object',
72
+ ' properties:',
73
+ ` id: ${IDENTITY}`,
74
+ ' name: { type: string }',
75
+ ` username: ${IDENTITY}`,
76
+ ' count: { type: integer, minimum: 1 }',
77
+ ' required: [reaction]',
78
+ ` post: ${IDENTITY}`,
79
+ ' assignee:',
80
+ ...ASSIGNEE,
81
+ ' sent_outside: { const: true }',
82
+ ' template:',
83
+ ' type: object',
84
+ ' properties:',
85
+ ' name: { type: string, minLength: 1 }',
86
+ ' language: { type: string, minLength: 1 }',
87
+ ' required: [name, language]',
88
+ ' values:',
89
+ ' type: object',
90
+ ' additionalProperties: { type: string }',
91
+ ' withheld: { const: true }',
92
+ ' required: [direction]',
93
+ ' if: { properties: { direction: { const: in } } }',
94
+ ' then: { required: [sent_at] }',
95
+ `${WAYFINDING_MAP_TYPE}:`,
96
+ ' properties:',
97
+ ' title: { type: string }',
98
+ ' required: [title]',
99
+ ' sections:',
100
+ ...WAYFINDING_MAP_SECTIONS.map((name) => ` ${name}: { kind: text, required: true }`),
101
+ `${WAYFINDING_TICKET_TYPE}:`,
102
+ ' properties:',
103
+ ' title: { type: string }',
104
+ ' kind: { enum: [research, prototype, grilling, task] }',
105
+ ' status: { enum: [open, in-progress, done, out-of-scope] }',
106
+ ` map: { type: string, reference: ${WAYFINDING_MAP_TYPE} }`,
107
+ ` blocked_by: { type: array, items: { type: string, reference: ${WAYFINDING_TICKET_TYPE} } }`,
108
+ ' assignee:',
109
+ ...ASSIGNEE,
110
+ ' conversation: { type: string, format: uuid }',
111
+ ` app: { type: string, pattern: '${APP_REFERENCE_PATTERN.source}' }`,
112
+ ' required: [title, kind, status, map, blocked_by]',
113
+ ' sections:',
114
+ ' Question: { kind: text }',
115
+ ' Answer: { kind: text }',
116
+ ' allOf:',
117
+ ' - if: { properties: { status: { enum: [in-progress, done, out-of-scope] } }, required: [status] }',
118
+ ' then: { required: [assignee] }',
119
+ ' else: { properties: { assignee: false } }',
120
+ ' - if: { properties: { assignee: { required: [agent] } }, required: [assignee] }',
121
+ ' else: { properties: { conversation: false } }',
122
+ ' - if: { properties: { status: { enum: [done, out-of-scope] } }, required: [status] }',
123
+ ' then: { sections: { Answer: { kind: text, required: true } } }',
124
+ HOUSE_MODEL_TYPES,
125
+ ].join('\n');
package/dist/site.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import type { Adapter } from '@agentshouse/mdruntime';
2
2
  import { type HouseSiteTransport } from './transport.js';
3
- export declare function createSiteAdapter(transport: HouseSiteTransport): Adapter;
3
+ export declare function createSiteAdapter(transport: HouseSiteTransport, snapshot?: boolean): Adapter;
4
4
  //# sourceMappingURL=site.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAMR,MAAM,wBAAwB,CAAC;AAsBhC,OAAO,EAKL,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAgCxB,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,kBAAkB,GAAG,OAAO,CAiIxE"}
1
+ {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAUR,MAAM,wBAAwB,CAAC;AAwBhC,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAgCxB,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAqN1F"}
package/dist/site.js CHANGED
@@ -1,79 +1,111 @@
1
- import { actionOutcome } from './actions.js';
1
+ import { stepped } from '@agentshouse/mdruntime';
2
+ import { actionOutcome, SITE_SUBMIT } from './actions.js';
2
3
  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';
4
+ import { streamedModels, viewModels } from './model.js';
5
+ import { bootstrapping, parseEnumeration, refreshFrom, viewOf, wholeView, } from './read.js';
6
+ import { roomStreams } from './room-stream.js';
5
7
  import { classifyDoor, classifyRead, discoveryFailure } from './refusal.js';
8
+ import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
6
9
  import { houseNotifications } from './stream.js';
7
- import { SITE_DOOR_BOOTSTRAP, SITE_DOOR_DISCOVER, SITE_DOOR_ENUMERATE, SITE_DOOR_SUBMIT, } from './transport.js';
10
+ import { SITE_DOOR_BOOTSTRAP, SITE_DOOR_DISCOVER, SITE_DOOR_ENUMERATE, SITE_DOOR_SUBMIT, SITE_FILES, } from './transport.js';
8
11
  import { followsVersion, logPosition, presented, reachedVersion, roomVersion, } from './version.js';
9
12
  const UNCERTAIN = { kind: 'transport-uncertainty' };
13
+ function unfound() {
14
+ return { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false };
15
+ }
10
16
  function siteRoom(tracked) {
11
17
  return {
12
18
  name: tracked.handle,
13
19
  locator: tracked.locator,
14
20
  handle: tracked.handle,
15
21
  roomVersion: roomVersion(tracked),
16
- operations: { read: [''], sync: [''], write: [], delete: [] },
17
- replica: { selection: tracked.locator, admission: 'admitted' },
22
+ operations: { write: [], delete: [] },
23
+ replica: { selection: tracked.locator, admission: 'admitted', offline: 'open' },
18
24
  providerOwnedPaths: [],
19
25
  };
20
26
  }
21
- function settled(refused) {
22
- return refused.kind === 'beyond-horizon' ? UNCERTAIN : refused;
23
- }
24
- export function createSiteAdapter(transport) {
27
+ export function createSiteAdapter(transport, snapshot = false) {
25
28
  const notifications = houseNotifications(transport, 'site');
26
29
  const tracked = new Map();
27
- async function enumeration(entry, from) {
30
+ const read = new Map();
31
+ const rooms = roomStreams('site');
32
+ function fileRoute(request) {
33
+ const entry = tracked.get(request.locator);
34
+ if (entry === undefined)
35
+ return { kind: 'room-not-readable', locator: request.locator };
36
+ return `${SITE_FILES}${entry.authority}/${encodeURIComponent(request.revision)}/${encodeURIComponent(request.path)}`;
37
+ }
38
+ async function enumerated(entry, from, page) {
28
39
  const answer = await transport.door(SITE_DOOR_ENUMERATE, {
29
- positions: [presented(entry.authority, from)],
40
+ positions: [
41
+ page === undefined
42
+ ? presented(entry.authority, from)
43
+ : {
44
+ ...presented(entry.authority, { position: page.after, logEpoch: from.logEpoch }),
45
+ page: { through: page.through, read: page.read },
46
+ },
47
+ ],
30
48
  });
31
49
  const classified = classifyRead(answer.status, answer.body, entry.locator);
32
50
  if (classified !== null)
33
51
  return classified;
34
- return parseEnumeration(answer.body, 'site') ?? UNCERTAIN;
52
+ const found = parseEnumeration(answer.body, 'site');
53
+ if (found === null)
54
+ return UNCERTAIN;
55
+ return viewOf(found, entry) ?? { kind: 'room-not-readable', locator: entry.locator };
35
56
  }
36
- async function caughtUp(entry) {
37
- const answer = await transport.read(`${SITE_DOOR_BOOTSTRAP}${entry.authority}`);
38
- const classified = classifyDoor(answer.status, answer.body, entry.locator);
39
- if (classified !== null)
40
- return { failure: classified };
41
- 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) };
57
+ async function enumeration(entry, from) {
58
+ const first = await enumerated(entry, from);
59
+ return 'kind' in first ? first : wholeView(first, (page) => enumerated(entry, from, page));
60
+ }
61
+ async function openedRoom(entry, arriving, signal) {
62
+ const cut = read.get(entry.locator);
63
+ const answered = await transport.streamed(`${SITE_DOOR_BOOTSTRAP}${entry.authority}${cut === undefined ? '' : `?expired=${cut}`}`, signal ?? new AbortController().signal);
64
+ if (answered.room === null)
65
+ return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN;
66
+ const head = await rooms.head(entry, answered.room, arriving, signal);
67
+ const at = head.kind === 'bootstrap' ? logPosition(head.roomVersion) : null;
68
+ if (at !== null)
69
+ read.set(entry.locator, at.position);
70
+ return head;
71
+ }
72
+ async function streamedPage(entry, token) {
73
+ const part = await rooms.part(entry, token);
74
+ if ('kind' in part)
75
+ return part;
76
+ notifications.reach(entry.authority, part.scope);
77
+ const { folders: _folders, ...page } = await stepped(bootstrapping(part.scope));
78
+ return {
79
+ ...page,
80
+ ...(part.models === undefined ? {} : { models: part.models }),
81
+ ...(part.pageToken === undefined ? {} : { pageToken: part.pageToken }),
82
+ bytes: part.bytes,
83
+ };
53
84
  }
54
85
  async function refreshed(entry, fromRoomVersion) {
55
86
  const from = logPosition(fromRoomVersion);
56
87
  if (from === null)
57
88
  return { kind: 'beyond-horizon' };
58
- const streamed = notifications.advance(entry, from);
89
+ const streamed = await stepped(notifications.advance(entry, from));
59
90
  if (streamed !== null) {
60
91
  const models = await streamedModels(streamed.schemas, entry, 'site');
61
92
  return models === undefined ? streamed.page : { ...streamed.page, models };
62
93
  }
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 };
94
+ const view = await enumeration(entry, from);
95
+ if ('kind' in view)
96
+ return view;
69
97
  notifications.reach(entry.authority, view);
70
- const answer = refreshFrom(view, fromRoomVersion);
98
+ const answer = await refreshFrom(view, fromRoomVersion);
71
99
  if (answer.kind !== 'net-advancement')
72
100
  return answer;
73
101
  const models = await viewModels(view, entry);
74
102
  return models === undefined ? answer : { ...answer, models };
75
103
  }
76
104
  return {
105
+ localSaves: true,
106
+ exhaustiveDiscovery: true,
107
+ roomTypes: HOUSE_ROOM_TYPES,
108
+ nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
77
109
  async authorize() {
78
110
  return {};
79
111
  },
@@ -96,14 +128,9 @@ export function createSiteAdapter(transport) {
96
128
  const entry = tracked.get(request.locator);
97
129
  if (entry === undefined)
98
130
  return { kind: 'room-not-readable', locator: request.locator };
99
- if (request.pageToken !== undefined)
100
- return UNCERTAIN;
101
- const held = await caughtUp(entry);
102
- if ('failure' in held)
103
- return held.failure;
104
- notifications.reach(entry.authority, held.scope);
105
- const models = await scopeModels(held.scope, entry);
106
- return { ...bootstrapOf(held.scope), ...(models === undefined ? {} : { models }) };
131
+ return request.pageToken === undefined
132
+ ? openedRoom(entry, request.arriving, request.signal)
133
+ : streamedPage(entry, request.pageToken);
107
134
  },
108
135
  async refresh(_authorization, request) {
109
136
  const entry = tracked.get(request.locator);
@@ -111,14 +138,43 @@ export function createSiteAdapter(transport) {
111
138
  return { kind: 'room-not-readable', locator: request.locator };
112
139
  if (request.pageToken !== undefined)
113
140
  return UNCERTAIN;
141
+ if (snapshot) {
142
+ return request.fromRoomVersion === roomVersion(entry)
143
+ ? { kind: 'still-current' }
144
+ : { kind: 'beyond-horizon' };
145
+ }
114
146
  return refreshed(entry, request.fromRoomVersion);
115
147
  },
116
148
  async mutate() {
117
- return { kind: 'rejected', affected: {}, code: 'write-grant', fields: {} };
149
+ return { kind: 'rejected', affected: {}, code: 'write-grant', fields: {}, retryable: false };
150
+ },
151
+ async attach() {
152
+ return { kind: 'rejected', code: 'write-grant', fields: {}, retryable: false };
118
153
  },
119
154
  async history() {
120
155
  return { kind: 'no-history' };
121
156
  },
157
+ async binary(_authorization, request) {
158
+ const route = fileRoute(request);
159
+ if (typeof route !== 'string')
160
+ return route;
161
+ const read = await transport.download(route);
162
+ if (read.status === 404)
163
+ return unfound();
164
+ return read.status === 200 && read.body !== null
165
+ ? { kind: 'binary', body: read.body, mediaType: read.mediaType }
166
+ : UNCERTAIN;
167
+ },
168
+ async link(_authorization, request) {
169
+ const route = fileRoute(request);
170
+ if (typeof route !== 'string')
171
+ return route;
172
+ return {
173
+ kind: 'link',
174
+ url: route,
175
+ name: request.path.slice(request.path.lastIndexOf('/') + 1),
176
+ };
177
+ },
122
178
  reached(locator, version, target) {
123
179
  return reachedVersion(version, target, tracked.get(locator)?.logEpoch);
124
180
  },
@@ -126,26 +182,39 @@ export function createSiteAdapter(transport) {
126
182
  return followsVersion(version, next);
127
183
  },
128
184
  async action(_authorization, request) {
129
- if (request.name !== 'submit') {
130
- return { kind: 'rejected', code: 'action-unimplemented', fields: { name: request.name } };
185
+ if (request.name !== SITE_SUBMIT) {
186
+ return {
187
+ kind: 'rejected',
188
+ code: 'action-unimplemented',
189
+ fields: { name: request.name },
190
+ retryable: false,
191
+ };
131
192
  }
132
193
  if (!transport.online())
133
194
  return UNCERTAIN;
134
- return actionOutcome(await transport.door(SITE_DOOR_SUBMIT, request.input), 'submit');
195
+ return actionOutcome(await transport.door(SITE_DOOR_SUBMIT, request.input), SITE_SUBMIT);
135
196
  },
136
- roomStream: {
137
- hold(rooms) {
138
- notifications.send(rooms.flatMap((room) => {
139
- const entry = tracked.get(room.locator);
140
- const at = logPosition(room.roomVersion);
141
- if (entry === undefined || at === null)
142
- return [];
143
- notifications.reach(entry.authority, at);
144
- return [presented(entry.authority, at)];
145
- }));
197
+ roomStream: snapshot
198
+ ? undefined
199
+ : {
200
+ hold(rooms) {
201
+ notifications.send(rooms.flatMap((room) => {
202
+ const entry = tracked.get(room.locator);
203
+ const at = logPosition(room.roomVersion);
204
+ if (entry === undefined || at === null)
205
+ return [];
206
+ notifications.reach(entry.authority, at);
207
+ return [presented(entry.authority, at)];
208
+ }));
209
+ },
210
+ reopen: notifications.reopen,
211
+ observe: notifications.observe,
212
+ published(locator, roomVersion) {
213
+ const at = logPosition(roomVersion);
214
+ if (at !== null)
215
+ notifications.published(locator, at);
216
+ },
146
217
  },
147
- observe: notifications.observe,
148
- },
149
218
  serviceMode: notifications.serviceMode,
150
219
  };
151
220
  }
package/dist/stream.d.ts CHANGED
@@ -1,20 +1,22 @@
1
- import type { ConversationStream, RefreshPage, RoomStreamEvent, ServiceModeSignal } from '@agentshouse/mdruntime';
1
+ import { type ConversationStream, type RefreshPage, type RoomStreamEvent, type ServiceModeSignal, type StreamReceipts } from '@agentshouse/mdruntime';
2
2
  import type { TrackedAuthority } from './discovery.js';
3
3
  import type { 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 = {
7
7
  page: RefreshPage;
8
- entries: readonly HouseStreamEntry[];
9
8
  schemas: ReadonlyMap<string, HouseStreamEntry>;
10
9
  };
11
10
  export type HouseNotifications = {
12
11
  runtime: string;
13
- observe(notify: (event: RoomStreamEvent) => void): () => void;
12
+ observe(notify: (event: RoomStreamEvent) => void, receipts: StreamReceipts): () => void;
14
13
  send(rooms: readonly HouseHeldRoom[]): void;
14
+ published(locator: string, at: LogPosition): void;
15
+ reopen(): void;
15
16
  track(authorities: readonly TrackedAuthority[]): void;
16
17
  reach(authority: string, at: LogPosition): void;
17
- advance(tracked: TrackedAuthority, from: LogPosition, holds?: HeldRevision): StreamedAdvancement | null;
18
+ edited(tracked: TrackedAuthority, from: LogPosition): readonly string[];
19
+ advance(tracked: TrackedAuthority, from: LogPosition, holds?: HeldRevision): Generator<number, StreamedAdvancement | null, void>;
18
20
  conversations: ConversationStream;
19
21
  serviceMode: ServiceModeSignal;
20
22
  };
@@ -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,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,CAoS5F"}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,kBAAkB,EAEvB,KAAK,WAAW,EAChB,KAAK,eAAe,EAEpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAEpB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAUvD,OAAO,KAAK,EACV,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,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,GAAG,IAAI,CAAC;IAC5C,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,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,SAAS,MAAM,EAAE,CAAC;IACxE,OAAO,CACL,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,WAAW,EACjB,KAAK,CAAC,EAAE,YAAY,GACnB,SAAS,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC;IACvD,aAAa,EAAE,kBAAkB,CAAC;IAClC,WAAW,EAAE,iBAAiB,CAAC;CAChC,CAAC;AAoFF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,kBAAkB,CAkkB5F"}