@agentshouse/mdruntime 0.1.0-cebcd51f70ed1 → 0.1.0-cedb03eaa48b6

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 (204) hide show
  1. package/AGENTS.md +283 -222
  2. package/README.md +20 -10
  3. package/dist/adapter.d.ts +95 -20
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +15 -2
  7. package/dist/attempt.d.ts +16 -0
  8. package/dist/attempt.d.ts.map +1 -0
  9. package/dist/attempt.js +70 -0
  10. package/dist/binary.d.ts +23 -0
  11. package/dist/binary.d.ts.map +1 -0
  12. package/dist/binary.js +8 -0
  13. package/dist/bootstrap.d.ts +18 -5
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +113 -37
  16. package/dist/change-set.d.ts +2 -0
  17. package/dist/change-set.d.ts.map +1 -1
  18. package/dist/change-set.js +49 -16
  19. package/dist/change.d.ts +15 -0
  20. package/dist/change.d.ts.map +1 -1
  21. package/dist/change.js +26 -1
  22. package/dist/chat/acts.d.ts +33 -0
  23. package/dist/chat/acts.d.ts.map +1 -0
  24. package/dist/chat/acts.js +36 -0
  25. package/dist/chat/context.d.ts +3 -0
  26. package/dist/chat/context.d.ts.map +1 -0
  27. package/dist/chat/context.js +10 -0
  28. package/dist/chat/earlier.d.ts +10 -0
  29. package/dist/chat/earlier.d.ts.map +1 -0
  30. package/dist/chat/earlier.js +54 -0
  31. package/dist/chat/input.d.ts +9 -0
  32. package/dist/chat/input.d.ts.map +1 -0
  33. package/dist/chat/input.js +99 -0
  34. package/dist/chat/kit.d.ts +61 -0
  35. package/dist/chat/kit.d.ts.map +1 -0
  36. package/dist/chat/kit.js +123 -0
  37. package/dist/chat/lasting.d.ts +6 -0
  38. package/dist/chat/lasting.d.ts.map +1 -0
  39. package/dist/chat/lasting.js +19 -0
  40. package/dist/chat/list.d.ts +27 -0
  41. package/dist/chat/list.d.ts.map +1 -0
  42. package/dist/chat/list.js +99 -0
  43. package/dist/chat/markdown.d.ts +20 -0
  44. package/dist/chat/markdown.d.ts.map +1 -0
  45. package/dist/chat/markdown.js +188 -0
  46. package/dist/chat/message.d.ts +6 -0
  47. package/dist/chat/message.d.ts.map +1 -0
  48. package/dist/chat/message.js +192 -0
  49. package/dist/chat/messages.d.ts +22 -0
  50. package/dist/chat/messages.d.ts.map +1 -0
  51. package/dist/chat/messages.js +155 -0
  52. package/dist/chat/name.d.ts +6 -0
  53. package/dist/chat/name.d.ts.map +1 -0
  54. package/dist/chat/name.js +37 -0
  55. package/dist/chat/opened.d.ts +11 -0
  56. package/dist/chat/opened.d.ts.map +1 -0
  57. package/dist/chat/opened.js +17 -0
  58. package/dist/chat/overlay.d.ts +31 -0
  59. package/dist/chat/overlay.d.ts.map +1 -0
  60. package/dist/chat/overlay.js +97 -0
  61. package/dist/chat/parts.d.ts +57 -0
  62. package/dist/chat/parts.d.ts.map +1 -0
  63. package/dist/chat/parts.js +119 -0
  64. package/dist/chat/process.d.ts +20 -0
  65. package/dist/chat/process.d.ts.map +1 -0
  66. package/dist/chat/process.js +32 -0
  67. package/dist/chat/provider.d.ts +63 -0
  68. package/dist/chat/provider.d.ts.map +1 -0
  69. package/dist/chat/provider.js +628 -0
  70. package/dist/chat/question.d.ts +9 -0
  71. package/dist/chat/question.d.ts.map +1 -0
  72. package/dist/chat/question.js +69 -0
  73. package/dist/chat/questions.d.ts +31 -0
  74. package/dist/chat/questions.d.ts.map +1 -0
  75. package/dist/chat/questions.js +111 -0
  76. package/dist/chat/read.d.ts +5 -0
  77. package/dist/chat/read.d.ts.map +1 -0
  78. package/dist/chat/read.js +13 -0
  79. package/dist/chat/ready.d.ts +34 -0
  80. package/dist/chat/ready.d.ts.map +1 -0
  81. package/dist/chat/ready.js +91 -0
  82. package/dist/chat/style.d.ts +4 -0
  83. package/dist/chat/style.d.ts.map +1 -0
  84. package/dist/chat/style.js +302 -0
  85. package/dist/chat.d.ts +14 -0
  86. package/dist/chat.d.ts.map +1 -0
  87. package/dist/chat.js +8 -0
  88. package/dist/client.d.ts +30 -13
  89. package/dist/client.d.ts.map +1 -1
  90. package/dist/client.js +79 -16
  91. package/dist/collection.d.ts +21 -12
  92. package/dist/collection.d.ts.map +1 -1
  93. package/dist/collection.js +301 -23
  94. package/dist/connection.d.ts +50 -16
  95. package/dist/connection.d.ts.map +1 -1
  96. package/dist/connection.js +1936 -415
  97. package/dist/conversation.d.ts +31 -10
  98. package/dist/conversation.d.ts.map +1 -1
  99. package/dist/conversation.js +521 -170
  100. package/dist/durability.d.ts +7 -1
  101. package/dist/durability.d.ts.map +1 -1
  102. package/dist/entry.d.ts +17 -0
  103. package/dist/entry.d.ts.map +1 -0
  104. package/dist/entry.js +157 -0
  105. package/dist/expectation.d.ts +21 -0
  106. package/dist/expectation.d.ts.map +1 -0
  107. package/dist/expectation.js +70 -0
  108. package/dist/failure.d.ts +13 -0
  109. package/dist/failure.d.ts.map +1 -0
  110. package/dist/failure.js +19 -0
  111. package/dist/fence.d.ts +6 -0
  112. package/dist/fence.d.ts.map +1 -0
  113. package/dist/fence.js +10 -0
  114. package/dist/host.d.ts +15 -2
  115. package/dist/host.d.ts.map +1 -1
  116. package/dist/host.js +102 -19
  117. package/dist/identity.d.ts +5 -0
  118. package/dist/identity.d.ts.map +1 -1
  119. package/dist/identity.js +19 -1
  120. package/dist/index.d.ts +16 -8
  121. package/dist/index.d.ts.map +1 -1
  122. package/dist/index.js +5 -0
  123. package/dist/memory.d.ts +11 -4
  124. package/dist/memory.d.ts.map +1 -1
  125. package/dist/memory.js +91 -24
  126. package/dist/model.d.ts +14 -4
  127. package/dist/model.d.ts.map +1 -1
  128. package/dist/model.js +12 -1
  129. package/dist/observation.d.ts +7 -2
  130. package/dist/observation.d.ts.map +1 -1
  131. package/dist/observation.js +29 -12
  132. package/dist/open.d.ts +2 -0
  133. package/dist/open.d.ts.map +1 -0
  134. package/dist/open.js +1 -0
  135. package/dist/outcome.d.ts +38 -3
  136. package/dist/outcome.d.ts.map +1 -1
  137. package/dist/outcome.js +45 -1
  138. package/dist/overlay.d.ts +16 -3
  139. package/dist/overlay.d.ts.map +1 -1
  140. package/dist/overlay.js +118 -77
  141. package/dist/query.d.ts +60 -4
  142. package/dist/query.d.ts.map +1 -1
  143. package/dist/query.js +41 -38
  144. package/dist/reach.d.ts +8 -3
  145. package/dist/reach.d.ts.map +1 -1
  146. package/dist/reach.js +73 -24
  147. package/dist/react.d.ts +31 -20
  148. package/dist/react.d.ts.map +1 -1
  149. package/dist/react.js +13 -21
  150. package/dist/read-side.d.ts +16 -7
  151. package/dist/read-side.d.ts.map +1 -1
  152. package/dist/read-side.js +493 -102
  153. package/dist/readiness.d.ts +53 -0
  154. package/dist/readiness.d.ts.map +1 -0
  155. package/dist/readiness.js +33 -0
  156. package/dist/reads.d.ts +11 -0
  157. package/dist/reads.d.ts.map +1 -0
  158. package/dist/reads.js +98 -0
  159. package/dist/recovery.d.ts +25 -0
  160. package/dist/recovery.d.ts.map +1 -0
  161. package/dist/recovery.js +197 -0
  162. package/dist/refresh.d.ts +13 -3
  163. package/dist/refresh.d.ts.map +1 -1
  164. package/dist/refresh.js +49 -38
  165. package/dist/replica.d.ts +13 -6
  166. package/dist/replica.d.ts.map +1 -1
  167. package/dist/replica.js +143 -70
  168. package/dist/room-draft.d.ts +38 -9
  169. package/dist/room-draft.d.ts.map +1 -1
  170. package/dist/room-draft.js +134 -25
  171. package/dist/rows.d.ts +10 -13
  172. package/dist/rows.d.ts.map +1 -1
  173. package/dist/rows.js +19 -14
  174. package/dist/save.d.ts +32 -1
  175. package/dist/save.d.ts.map +1 -1
  176. package/dist/save.js +390 -45
  177. package/dist/search.d.ts +52 -8
  178. package/dist/search.d.ts.map +1 -1
  179. package/dist/search.js +192 -47
  180. package/dist/seed.d.ts +16 -0
  181. package/dist/seed.d.ts.map +1 -0
  182. package/dist/seed.js +75 -0
  183. package/dist/shared.d.ts +11 -0
  184. package/dist/shared.d.ts.map +1 -0
  185. package/dist/shared.js +368 -0
  186. package/dist/status.d.ts +6 -2
  187. package/dist/status.d.ts.map +1 -1
  188. package/dist/status.js +0 -8
  189. package/dist/store.d.ts +89 -18
  190. package/dist/store.d.ts.map +1 -1
  191. package/dist/store.js +573 -200
  192. package/dist/turn.d.ts +7 -0
  193. package/dist/turn.d.ts.map +1 -0
  194. package/dist/turn.js +51 -0
  195. package/dist/windows.d.ts +26 -0
  196. package/dist/windows.d.ts.map +1 -0
  197. package/dist/windows.js +93 -0
  198. package/dist/wire.d.ts +157 -33
  199. package/dist/wire.d.ts.map +1 -1
  200. package/dist/wire.js +65 -31
  201. package/package.json +26 -14
  202. package/dist/attachment.d.ts +0 -3
  203. package/dist/attachment.d.ts.map +0 -1
  204. package/dist/attachment.js +0 -8
package/dist/client.js CHANGED
@@ -1,5 +1,11 @@
1
1
  import { observed } from './observation.js';
2
+ import { runtimeError } from './failure.js';
2
3
  import { answer, AUTHORIZATION_ENDED, PORT_CLOSED, } from './wire.js';
4
+ // React renders an arrived value in a later task and opens what that render observes as it commits, so this many frames opening nothing mean the page drew what it observes.
5
+ const SETTLED_FRAMES = 2;
6
+ function refused(refusal) {
7
+ return runtimeError(refusal.code, refusal.code, refusal.retryable);
8
+ }
3
9
  export function connectMdRuntime(port) {
4
10
  let sequence = 0;
5
11
  let refusal = null;
@@ -7,6 +13,8 @@ export function connectMdRuntime(port) {
7
13
  const calls = new Map();
8
14
  const opening = new Map();
9
15
  const observations = new Map();
16
+ let drawing = null;
17
+ let settling = null;
10
18
  const send = (message) => {
11
19
  port.postMessage(message);
12
20
  };
@@ -25,7 +33,7 @@ export function connectMdRuntime(port) {
25
33
  if (topic === 'connection-refusal')
26
34
  handle.receive(reported);
27
35
  }
28
- const failure = new Error(reported.reason);
36
+ const failure = refused(reported);
29
37
  for (const [id, waiting] of opening) {
30
38
  if (waiting.topic === 'connection-refusal')
31
39
  waiting.settle(begin(id, waiting.topic, reported));
@@ -36,12 +44,12 @@ export function connectMdRuntime(port) {
36
44
  for (const waiting of calls.values())
37
45
  waiting.fail(failure);
38
46
  calls.clear();
47
+ drawing?.();
39
48
  }
40
- function end(reason) {
49
+ function end(failure) {
41
50
  if (ended !== null)
42
51
  return;
43
- ended = reason;
44
- const failure = new Error(reason);
52
+ ended = failure;
45
53
  for (const waiting of opening.values())
46
54
  waiting.fail(failure);
47
55
  opening.clear();
@@ -51,6 +59,7 @@ export function connectMdRuntime(port) {
51
59
  for (const { handle } of observations.values())
52
60
  handle.end();
53
61
  observations.clear();
62
+ drawing?.();
54
63
  }
55
64
  port.addEventListener('message', (event) => {
56
65
  const message = answer(event.data);
@@ -61,7 +70,11 @@ export function connectMdRuntime(port) {
61
70
  return;
62
71
  }
63
72
  if (message.kind === 'ended') {
64
- end(AUTHORIZATION_ENDED);
73
+ for (const { topic, handle } of observations.values()) {
74
+ if (topic === 'member')
75
+ handle.receive(null);
76
+ }
77
+ end(runtimeError('authorization-ended', AUTHORIZATION_ENDED));
65
78
  return;
66
79
  }
67
80
  if (message.kind === 'event') {
@@ -71,31 +84,34 @@ export function connectMdRuntime(port) {
71
84
  const waiting = opening.get(message.id);
72
85
  if (waiting !== undefined) {
73
86
  opening.delete(message.id);
74
- if (message.kind === 'failure')
75
- waiting.fail(new Error(message.message));
87
+ if (message.kind === 'failure') {
88
+ waiting.fail(runtimeError(message.code, message.message, message.retryable));
89
+ }
76
90
  else
77
91
  waiting.settle(begin(message.id, waiting.topic, message.value));
92
+ drawing?.();
78
93
  return;
79
94
  }
80
95
  const called = calls.get(message.id);
81
96
  if (called === undefined)
82
97
  return;
83
98
  calls.delete(message.id);
84
- if (message.kind === 'failure')
85
- called.fail(new Error(message.message));
99
+ if (message.kind === 'failure') {
100
+ called.fail(runtimeError(message.code, message.message, message.retryable));
101
+ }
86
102
  else
87
103
  called.settle(message.value);
88
104
  });
89
105
  port.addEventListener('close', () => {
90
- end(PORT_CLOSED);
106
+ end(runtimeError('port-closed', PORT_CLOSED));
91
107
  });
92
108
  port.start();
93
109
  send({ kind: 'hello' });
94
110
  function ask(member, input) {
95
111
  if (ended !== null)
96
- return Promise.reject(new Error(ended));
112
+ return Promise.reject(ended);
97
113
  if (refusal !== null)
98
- return Promise.reject(new Error(refusal.reason));
114
+ return Promise.reject(refused(refusal));
99
115
  const id = (sequence += 1);
100
116
  return new Promise((settle, fail) => {
101
117
  calls.set(id, { settle: settle, fail });
@@ -104,17 +120,47 @@ export function connectMdRuntime(port) {
104
120
  }
105
121
  function watch(topic, input) {
106
122
  if (ended !== null)
107
- return Promise.reject(new Error(ended));
123
+ return Promise.reject(ended);
108
124
  const id = (sequence += 1);
109
125
  if (refusal !== null) {
110
126
  return topic === 'connection-refusal'
111
127
  ? Promise.resolve(begin(id, topic, refusal))
112
- : Promise.reject(new Error(refusal.reason));
128
+ : Promise.reject(refused(refusal));
113
129
  }
114
130
  return new Promise((settle, fail) => {
115
131
  opening.set(id, { topic, settle, fail });
116
132
  send({ kind: 'observe', id, topic, input });
133
+ drawing?.();
134
+ });
135
+ }
136
+ function settled() {
137
+ if (settling !== null)
138
+ return settling;
139
+ settling = new Promise((resolve) => {
140
+ let moved = 0;
141
+ const check = () => {
142
+ moved += 1;
143
+ if (opening.size > 0)
144
+ return;
145
+ const at = moved;
146
+ const waited = (frames) => {
147
+ requestAnimationFrame(() => {
148
+ if (frames < SETTLED_FRAMES) {
149
+ waited(frames + 1);
150
+ return;
151
+ }
152
+ if (moved !== at || opening.size > 0)
153
+ return;
154
+ drawing = null;
155
+ resolve();
156
+ });
157
+ };
158
+ waited(1);
159
+ };
160
+ drawing = check;
161
+ check();
117
162
  });
163
+ return settling;
118
164
  }
119
165
  return {
120
166
  rooms: () => ask('rooms', []),
@@ -128,15 +174,17 @@ export function connectMdRuntime(port) {
128
174
  discard: (room, path, revision) => ask('roomDraft.discard', [room, path, revision]),
129
175
  },
130
176
  save: (room, roomDrafts) => ask('save', [room, roomDrafts]),
177
+ saveParts: (room, parts) => ask('saveParts', [room, parts]),
131
178
  presentation: {
132
179
  read: () => ask('presentation.read', []),
133
180
  write: (value) => ask('presentation.write', [value]),
181
+ edit: (members) => ask('presentation.edit', [members]),
134
182
  },
135
183
  conversation: {
136
184
  list: () => ask('conversation.list', []),
137
185
  read: (conversation, options) => ask('conversation.read', [conversation, options]),
138
186
  create: (input) => ask('conversation.create', [input]),
139
- send: (conversation) => ask('conversation.send', [conversation]),
187
+ send: (conversation, operation, draft) => ask('conversation.send', [conversation, operation, draft]),
140
188
  answer: (conversation, answer) => ask('conversation.answer', [conversation, answer]),
141
189
  interrupt: (conversation) => ask('conversation.interrupt', [conversation]),
142
190
  kill: (conversation) => ask('conversation.kill', [conversation]),
@@ -144,15 +192,30 @@ export function connectMdRuntime(port) {
144
192
  delete: (conversation) => ask('conversation.delete', [conversation]),
145
193
  },
146
194
  chatDraft: {
147
- write: (conversation, draft) => ask('chatDraft.write', [conversation, draft]),
195
+ write: (conversation, draft, revision) => ask('chatDraft.write', [conversation, draft, revision]),
196
+ prepend: (conversation, draft) => ask('chatDraft.prepend', [conversation, draft]),
148
197
  discard: (conversation) => ask('chatDraft.discard', [conversation]),
149
198
  },
150
199
  history: (locator, path, query) => ask('history', [locator, path, query]),
200
+ binaryFile: {
201
+ read: (room, path, revision) => ask('binaryFile.read', [room, path, revision]),
202
+ link: (room, path, revision) => ask('binaryFile.link', [room, path, revision]),
203
+ readReference: (reference) => ask('binaryFile.readReference', [reference]),
204
+ },
151
205
  call: (name, input) => ask('call', [name, input]),
152
206
  reached: (locator, roomVersion, path) => ask('reached', [locator, roomVersion, path]),
153
207
  log: () => ask('log', []),
154
208
  persistence: () => ask('persistence', []),
209
+ retry: () => ask('retry', []),
155
210
  observe: ((topic, ...input) => watch(topic, input)),
156
211
  reauthorize: () => ask('reauthorize', []),
212
+ settled,
157
213
  };
158
214
  }
215
+ const turns = new WeakMap();
216
+ export function inTurn(client, work) {
217
+ const turn = (turns.get(client) ?? Promise.resolve()).then(work);
218
+ turns.set(client, turn.then(() => undefined, () => undefined));
219
+ return turn;
220
+ }
221
+ export { unanswered } from './wire.js';
@@ -1,11 +1,14 @@
1
- import { type Document, type Validation } from '@agentshouse/mdmodel';
2
- import type { ModelState } from './model.js';
1
+ import { type Document, type Frontmatter, type Validation } from '@agentshouse/mdmodel';
2
+ import { type ModelState } from './model.js';
3
3
  import type { MdModelIdentity } from './adapter.js';
4
+ import { type Paths } from './shared.js';
4
5
  import type { Refused } from './transfer.js';
6
+ import { type Later } from './turn.js';
5
7
  export declare const MD_MODEL: MdModelIdentity;
6
8
  export type Collection = {
7
- documents: Readonly<Record<string, Document>>;
9
+ text: Readonly<Record<string, string>>;
8
10
  composition: Composition;
11
+ document(path: string): Document | undefined;
9
12
  };
10
13
  export type Composition = {
11
14
  model: ModelState;
@@ -15,16 +18,22 @@ export type Composition = {
15
18
  } | null;
16
19
  };
17
20
  export declare function governing(composition: Composition, path: string): ModelState;
18
- type Previous = {
19
- text: Readonly<Record<string, string>>;
20
- collection: Collection;
21
- };
22
- export declare function parseCollection(text: Readonly<Record<string, string>>, composition: Composition, previous?: Previous): Collection;
23
- export declare function readCollection(text: Readonly<Record<string, string>>, composition: Composition, previous?: Previous): {
24
- kind: 'read';
25
- collection: Collection;
26
- } | Refused;
21
+ export declare function collectionOf(text: Readonly<Record<string, string>>, composition: Composition, previous?: Collection): Collection;
22
+ export declare function passingFrontmatter(collection: Collection, path: string): Frontmatter | undefined;
23
+ export declare function documentsOf(collection: Collection): Generator<readonly [string, Document]>;
24
+ export declare function unreadable(text: Readonly<Record<string, string>>, previous?: Readonly<Record<string, string>>): Refused | null;
27
25
  export declare function validRoot(root: unknown): root is string;
28
26
  export declare function validateCollection(text: Readonly<Record<string, string>>, binaryTargets: readonly string[], composition: Composition): Validation;
27
+ type Held = {
28
+ text: Paths<string>;
29
+ binaries: Paths<unknown>;
30
+ };
31
+ export declare function indexReferrers(held: Held, composition: Composition): Later<void>;
32
+ export declare function referencesIndexed(held: Held, composition: Composition): boolean;
33
+ export declare function referring(held: Held, composition: Composition, targets: Iterable<string>): ReadonlySet<string> | null;
34
+ export declare function available(composition: Composition): boolean;
35
+ export declare function changedCleanly(next: Held, previous: Held & {
36
+ composition: Composition;
37
+ }, composition: Composition): boolean;
29
38
  export {};
30
39
  //# sourceMappingURL=collection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAEb,KAAK,UAAU,EAEhB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,eAAO,MAAM,QAAQ,EAAE,eAAwE,CAAC;AAIhG,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC9C,WAAW,EAAE,WAAW,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE;QAAE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,GAAG,IAAI,CAAC;CACnE,CAAC;AASF,wBAAgB,SAAS,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAI5E;AAED,KAAK,QAAQ,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC;AAanF,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACtC,WAAW,EAAE,WAAW,EACxB,QAAQ,CAAC,EAAE,QAAQ,GAClB,UAAU,CAOZ;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACtC,WAAW,EAAE,WAAW,EACxB,QAAQ,CAAC,EAAE,QAAQ,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,GAAG,OAAO,CAQpD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,MAAM,CAEvD;AAwBD,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACtC,aAAa,EAAE,SAAS,MAAM,EAAE,EAChC,WAAW,EAAE,WAAW,GACvB,UAAU,CA0BZ"}
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,8 @@
1
- import { parse, validate, VERSION, } from '@agentshouse/mdmodel';
1
+ import { checkDocument, parseFrontmatter, references, sectionReferences, target, parse, resolves, validate, VERSION, } from '@agentshouse/mdmodel';
2
2
  import { beneath, normalize } from './paths.js';
3
+ import { sameTyping } from './model.js';
4
+ import { differing, patched, shared, sizeOf } from './shared.js';
5
+ import { after, inUnits, ROW_WEIGHT } from './turn.js';
3
6
  export const MD_MODEL = { identity: '@agentshouse/mdmodel', version: VERSION };
4
7
  const NO_TYPES = { types: {} };
5
8
  function selected(composition, path) {
@@ -11,30 +14,81 @@ export function governing(composition, path) {
11
14
  ? composition.selection.model
12
15
  : composition.model;
13
16
  }
14
- function parsedAt(text, path, previous) {
15
- const retained = previous?.collection.documents[path];
16
- return retained !== undefined && previous?.text[path] === text[path]
17
- ? { ok: true, document: retained }
18
- : parse(text[path], path);
19
- }
20
- export function parseCollection(text, composition, previous) {
21
- const documents = {};
22
- for (const path of Object.keys(text).sort()) {
23
- const parsed = parsedAt(text, path, previous);
24
- if (parsed.ok)
25
- documents[path] = parsed.document;
17
+ const parsedIn = new WeakMap();
18
+ function carried(previous, text) {
19
+ const held = parsedIn.get(previous);
20
+ if (held === undefined)
21
+ return shared({});
22
+ const kept = patched(held.base, held.added);
23
+ held.base = kept;
24
+ held.added = new Map();
25
+ return patched(kept, [], differing(previous.text, text));
26
+ }
27
+ function cachedIn(collection, path) {
28
+ const held = parsedIn.get(collection);
29
+ if (held === undefined)
30
+ return undefined;
31
+ if (held.added.has(path))
32
+ return held.added.get(path);
33
+ return Object.hasOwn(held.base, path) ? held.base[path] : undefined;
34
+ }
35
+ export function collectionOf(text, composition, previous) {
36
+ const parsed = {
37
+ base: previous === undefined ? shared({}) : carried(previous, text),
38
+ added: new Map(),
39
+ };
40
+ const collection = {
41
+ text,
42
+ composition,
43
+ document(path) {
44
+ const held = cachedIn(collection, path);
45
+ if (held !== undefined)
46
+ return held ?? undefined;
47
+ const content = text[path];
48
+ if (content === undefined)
49
+ return undefined;
50
+ const read = parse(content, path);
51
+ parsed.added.set(path, read.ok ? read.document : null);
52
+ return read.ok ? read.document : undefined;
53
+ },
54
+ };
55
+ parsedIn.set(collection, parsed);
56
+ return collection;
57
+ }
58
+ export function passingFrontmatter(collection, path) {
59
+ const held = cachedIn(collection, path);
60
+ if (held !== undefined)
61
+ return held ?? undefined;
62
+ const content = collection.text[path];
63
+ if (content === undefined)
64
+ return undefined;
65
+ const read = parseFrontmatter(content, path);
66
+ return read.ok ? read.frontmatter : undefined;
67
+ }
68
+ export function* documentsOf(collection) {
69
+ for (const path of Object.keys(collection.text).sort()) {
70
+ const held = cachedIn(collection, path);
71
+ if (held === null)
72
+ continue;
73
+ if (held !== undefined) {
74
+ yield [path, held];
75
+ continue;
76
+ }
77
+ const read = parse(collection.text[path], path);
78
+ if (read.ok)
79
+ yield [path, read.document];
26
80
  }
27
- return { documents, composition };
28
- }
29
- export function readCollection(text, composition, previous) {
30
- const documents = {};
31
- for (const path of Object.keys(text).sort()) {
32
- const parsed = parsedAt(text, path, previous);
33
- if (!parsed.ok)
34
- return { kind: 'refused', refusal: parsed.refusal };
35
- documents[path] = parsed.document;
81
+ }
82
+ export function unreadable(text, previous) {
83
+ const paths = previous === undefined
84
+ ? Object.keys(text)
85
+ : [...differing(previous, text)].filter((path) => text[path] !== undefined);
86
+ for (const path of paths.sort()) {
87
+ const read = parse(text[path], path);
88
+ if (!read.ok)
89
+ return { kind: 'refused', refusal: read.refusal };
36
90
  }
37
- return { kind: 'read', collection: { documents, composition } };
91
+ return null;
38
92
  }
39
93
  export function validRoot(root) {
40
94
  return typeof root === 'string' && normalize(root) === root;
@@ -70,3 +124,227 @@ export function validateCollection(text, binaryTargets, composition) {
70
124
  return verdicts[0];
71
125
  return refusals.find((refusal) => refusal.rule !== 'model-unavailable') ?? refusals[0];
72
126
  }
127
+ const NO_REFERRERS = shared({});
128
+ const referrersOf = new WeakMap();
129
+ function typing(left, right) {
130
+ return (sameTyping(left.model, right.model) &&
131
+ (left.selection === null || right.selection === null
132
+ ? left.selection === right.selection
133
+ : sameTyping(left.selection.model, right.selection.model)) &&
134
+ JSON.stringify(left.selection?.roots ?? null) === JSON.stringify(right.selection?.roots ?? null));
135
+ }
136
+ function declared(content) {
137
+ if (content === undefined)
138
+ return undefined;
139
+ const read = parseFrontmatter(content);
140
+ return read.ok ? (read.frontmatter.type ?? 'any') : undefined;
141
+ }
142
+ function typedIn(content) {
143
+ const type = declared(content);
144
+ return type !== undefined && type !== 'any';
145
+ }
146
+ function modelOf(composition, path) {
147
+ const model = governing(composition, path);
148
+ return model.status === 'unavailable' ? null : { types: model.types };
149
+ }
150
+ function checked(path, content, composition) {
151
+ return checkDocument(path, content, modelOf(composition, path) ?? NO_TYPES);
152
+ }
153
+ function targetsOf(check) {
154
+ if (!check.ok)
155
+ return [];
156
+ return [...new Set(check.references.flatMap(({ target }) => (target === null ? [] : [target])))];
157
+ }
158
+ function referencedBy(path, content, composition) {
159
+ const model = modelOf(composition, path);
160
+ if (model === null)
161
+ return [];
162
+ const read = parseFrontmatter(content, path);
163
+ const type = read.ok ? read.frontmatter.type : undefined;
164
+ if (!read.ok || type === undefined || !Object.hasOwn(model.types, type))
165
+ return [];
166
+ const definition = model.types[type];
167
+ return [
168
+ ...new Set([
169
+ ...references(read.frontmatter, definition).flatMap(({ value }) => {
170
+ const found = target(path, value);
171
+ return found === null ? [] : [found];
172
+ }),
173
+ ...sectionReferences(path, content, definition).flatMap(({ target }) => target === null ? [] : [target]),
174
+ ]),
175
+ ];
176
+ }
177
+ function referrersOfTarget(index, target) {
178
+ return Object.hasOwn(index.referrers, target) ? index.referrers[target] : NO_REFERRERS;
179
+ }
180
+ function reindexed(index, changed) {
181
+ const referrers = new Map();
182
+ const held = (target) => referrers.get(target) ?? referrersOfTarget(index, target);
183
+ for (const [path, found] of changed) {
184
+ const before = Object.hasOwn(index.targets, path) ? index.targets[path] : [];
185
+ for (const target of before) {
186
+ if (!found.includes(target))
187
+ referrers.set(target, patched(held(target), [], [path]));
188
+ }
189
+ for (const target of found) {
190
+ if (!before.includes(target))
191
+ referrers.set(target, patched(held(target), [[path, true]]));
192
+ }
193
+ }
194
+ return {
195
+ composition: index.composition,
196
+ targets: patched(index.targets, [...changed].filter(([, found]) => found.length > 0), [...changed].filter(([, found]) => found.length === 0).map(([path]) => path)),
197
+ referrers: patched(index.referrers, [...referrers].filter(([, files]) => sizeOf(files) > 0), [...referrers].filter(([, files]) => sizeOf(files) === 0).map(([target]) => target)),
198
+ };
199
+ }
200
+ function storedIn(entries) {
201
+ let stored = shared({});
202
+ let unit = [];
203
+ return after(inUnits(entries, (entry) => {
204
+ unit.push(entry);
205
+ return ROW_WEIGHT;
206
+ }, () => {
207
+ stored = patched(stored, unit);
208
+ unit = [];
209
+ }), () => stored);
210
+ }
211
+ function storedReferrers(referrers) {
212
+ const sets = new Map();
213
+ let unit = new Map();
214
+ const pairs = function* () {
215
+ for (const [target, files] of referrers)
216
+ for (const file of files)
217
+ yield [target, file];
218
+ };
219
+ return after(inUnits(pairs(), ([target, file]) => {
220
+ const files = unit.get(target);
221
+ if (files === undefined)
222
+ unit.set(target, [file]);
223
+ else
224
+ files.push(file);
225
+ return ROW_WEIGHT;
226
+ }, () => {
227
+ for (const [target, files] of unit) {
228
+ sets.set(target, patched(sets.get(target) ?? NO_REFERRERS, files.map((file) => [file, true])));
229
+ }
230
+ unit = new Map();
231
+ }), () => storedIn(sets));
232
+ }
233
+ function heldIndex(held, composition) {
234
+ const index = referrersOf.get(held.text);
235
+ return index !== undefined && typing(index.composition, composition) ? index : null;
236
+ }
237
+ const indexing = new WeakMap();
238
+ export function indexReferrers(held, composition) {
239
+ if (heldIndex(held, composition) !== null)
240
+ return;
241
+ const running = indexing.get(held.text);
242
+ if (running !== undefined)
243
+ return running;
244
+ const targets = new Map();
245
+ const referrers = new Map();
246
+ const filled = inUnits(Object.keys(held.text), (path) => {
247
+ const content = held.text[path];
248
+ const found = referencedBy(path, content, composition);
249
+ if (found.length > 0)
250
+ targets.set(path, found);
251
+ for (const target of found) {
252
+ const files = referrers.get(target);
253
+ if (files === undefined)
254
+ referrers.set(target, [path]);
255
+ else
256
+ files.push(path);
257
+ }
258
+ return ROW_WEIGHT + content.length;
259
+ });
260
+ const indexed = after(filled, () => after(storedIn(targets), (storedTargets) => after(storedReferrers(referrers), (stored) => {
261
+ referrersOf.set(held.text, { composition, targets: storedTargets, referrers: stored });
262
+ })));
263
+ if (!(indexed instanceof Promise))
264
+ return;
265
+ const building = indexed.finally(() => indexing.delete(held.text));
266
+ indexing.set(held.text, building);
267
+ return building;
268
+ }
269
+ export function referencesIndexed(held, composition) {
270
+ return heldIndex(held, composition) !== null;
271
+ }
272
+ export function referring(held, composition, targets) {
273
+ const index = heldIndex(held, composition);
274
+ if (index === null)
275
+ return null;
276
+ const found = new Set();
277
+ for (const target of targets) {
278
+ for (const file of Object.keys(referrersOfTarget(index, target)))
279
+ found.add(file);
280
+ }
281
+ return found;
282
+ }
283
+ export function available(composition) {
284
+ return (composition.model.status !== 'unavailable' &&
285
+ (composition.selection === null || composition.selection.model.status !== 'unavailable'));
286
+ }
287
+ export function changedCleanly(next, previous, composition) {
288
+ if (!typing(previous.composition, composition))
289
+ return false;
290
+ const paths = new Set([
291
+ ...differing(previous.text, next.text),
292
+ ...differing(previous.binaries, next.binaries, () => true),
293
+ ]);
294
+ const binary = (path) => Object.hasOwn(next.binaries, path);
295
+ const within = (file) => (path) => selected(composition, path) === selected(composition, file);
296
+ const reaching = (file, check) => {
297
+ if (!check.ok)
298
+ return false;
299
+ const part = within(file);
300
+ return check.references.every((reference) => resolves(reference, (path) => (part(path) ? declared(next.text[path]) : undefined), (path) => part(path) && binary(path)));
301
+ };
302
+ const changedTargets = new Map();
303
+ const shifted = [];
304
+ for (const path of paths) {
305
+ const now = next.text[path];
306
+ const was = previous.text[path];
307
+ const modeled = modelOf(composition, path) !== null;
308
+ if ((now === undefined) !== (was === undefined) ||
309
+ declared(now) !== declared(was) ||
310
+ binary(path) !== Object.hasOwn(previous.binaries, path)) {
311
+ shifted.push(path);
312
+ }
313
+ if (!modeled && typedIn(was) && !typedIn(now))
314
+ return false;
315
+ if (now === undefined) {
316
+ if (modeled)
317
+ changedTargets.set(path, []);
318
+ continue;
319
+ }
320
+ if (binary(path))
321
+ return false;
322
+ if (!modeled) {
323
+ if (declared(now) === undefined)
324
+ return false;
325
+ if (!typedIn(now) && !checked(path, now, composition).ok)
326
+ return false;
327
+ continue;
328
+ }
329
+ const check = checked(path, now, composition);
330
+ if (!reaching(path, check))
331
+ return false;
332
+ changedTargets.set(path, targetsOf(check));
333
+ }
334
+ const index = heldIndex(previous, composition);
335
+ if (index === null && shifted.length > 0)
336
+ return false;
337
+ for (const target of shifted) {
338
+ const files = index === null ? [] : Object.keys(referrersOfTarget(index, target));
339
+ for (const file of files) {
340
+ if (paths.has(file))
341
+ continue;
342
+ if (!reaching(file, checked(file, next.text[file], composition)))
343
+ return false;
344
+ }
345
+ }
346
+ if (index !== null) {
347
+ referrersOf.set(next.text, changedTargets.size === 0 ? index : reindexed(index, changedTargets));
348
+ }
349
+ return true;
350
+ }