@agentshouse/mdruntime 0.1.0-c3903a68dcbbf → 0.1.0-c3fa75348e547

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 (206) hide show
  1. package/AGENTS.md +270 -181
  2. package/README.md +22 -12
  3. package/dist/acquisition.d.ts +14 -0
  4. package/dist/acquisition.d.ts.map +1 -0
  5. package/dist/acquisition.js +24 -0
  6. package/dist/adapter.d.ts +160 -67
  7. package/dist/adapter.d.ts.map +1 -1
  8. package/dist/adapter.js +1 -9
  9. package/dist/attached-model.d.ts +11 -0
  10. package/dist/attached-model.d.ts.map +1 -0
  11. package/dist/attached-model.js +47 -0
  12. package/dist/attempt.d.ts +16 -0
  13. package/dist/attempt.d.ts.map +1 -0
  14. package/dist/attempt.js +70 -0
  15. package/dist/binary.d.ts +23 -0
  16. package/dist/binary.d.ts.map +1 -0
  17. package/dist/binary.js +8 -0
  18. package/dist/bootstrap.d.ts +21 -6
  19. package/dist/bootstrap.d.ts.map +1 -1
  20. package/dist/bootstrap.js +114 -38
  21. package/dist/change-set.d.ts +2 -0
  22. package/dist/change-set.d.ts.map +1 -1
  23. package/dist/change-set.js +50 -21
  24. package/dist/change.d.ts +15 -0
  25. package/dist/change.d.ts.map +1 -1
  26. package/dist/change.js +26 -1
  27. package/dist/chat/acts.d.ts +33 -0
  28. package/dist/chat/acts.d.ts.map +1 -0
  29. package/dist/chat/acts.js +36 -0
  30. package/dist/chat/context.d.ts +3 -0
  31. package/dist/chat/context.d.ts.map +1 -0
  32. package/dist/chat/context.js +10 -0
  33. package/dist/chat/input.d.ts +9 -0
  34. package/dist/chat/input.d.ts.map +1 -0
  35. package/dist/chat/input.js +99 -0
  36. package/dist/chat/kit.d.ts +61 -0
  37. package/dist/chat/kit.d.ts.map +1 -0
  38. package/dist/chat/kit.js +123 -0
  39. package/dist/chat/lasting.d.ts +6 -0
  40. package/dist/chat/lasting.d.ts.map +1 -0
  41. package/dist/chat/lasting.js +19 -0
  42. package/dist/chat/list.d.ts +27 -0
  43. package/dist/chat/list.d.ts.map +1 -0
  44. package/dist/chat/list.js +63 -0
  45. package/dist/chat/markdown.d.ts +20 -0
  46. package/dist/chat/markdown.d.ts.map +1 -0
  47. package/dist/chat/markdown.js +188 -0
  48. package/dist/chat/message.d.ts +6 -0
  49. package/dist/chat/message.d.ts.map +1 -0
  50. package/dist/chat/message.js +192 -0
  51. package/dist/chat/messages.d.ts +22 -0
  52. package/dist/chat/messages.d.ts.map +1 -0
  53. package/dist/chat/messages.js +155 -0
  54. package/dist/chat/name.d.ts +6 -0
  55. package/dist/chat/name.d.ts.map +1 -0
  56. package/dist/chat/name.js +37 -0
  57. package/dist/chat/opened.d.ts +11 -0
  58. package/dist/chat/opened.d.ts.map +1 -0
  59. package/dist/chat/opened.js +17 -0
  60. package/dist/chat/overlay.d.ts +31 -0
  61. package/dist/chat/overlay.d.ts.map +1 -0
  62. package/dist/chat/overlay.js +97 -0
  63. package/dist/chat/parts.d.ts +57 -0
  64. package/dist/chat/parts.d.ts.map +1 -0
  65. package/dist/chat/parts.js +119 -0
  66. package/dist/chat/process.d.ts +20 -0
  67. package/dist/chat/process.d.ts.map +1 -0
  68. package/dist/chat/process.js +32 -0
  69. package/dist/chat/provider.d.ts +63 -0
  70. package/dist/chat/provider.d.ts.map +1 -0
  71. package/dist/chat/provider.js +629 -0
  72. package/dist/chat/question.d.ts +9 -0
  73. package/dist/chat/question.d.ts.map +1 -0
  74. package/dist/chat/question.js +80 -0
  75. package/dist/chat/questions.d.ts +31 -0
  76. package/dist/chat/questions.d.ts.map +1 -0
  77. package/dist/chat/questions.js +111 -0
  78. package/dist/chat/read.d.ts +5 -0
  79. package/dist/chat/read.d.ts.map +1 -0
  80. package/dist/chat/read.js +13 -0
  81. package/dist/chat/ready.d.ts +34 -0
  82. package/dist/chat/ready.d.ts.map +1 -0
  83. package/dist/chat/ready.js +91 -0
  84. package/dist/chat/style.d.ts +4 -0
  85. package/dist/chat/style.d.ts.map +1 -0
  86. package/dist/chat/style.js +302 -0
  87. package/dist/chat.d.ts +14 -0
  88. package/dist/chat.d.ts.map +1 -0
  89. package/dist/chat.js +8 -0
  90. package/dist/client.d.ts +28 -20
  91. package/dist/client.d.ts.map +1 -1
  92. package/dist/client.js +78 -32
  93. package/dist/collection.d.ts +21 -14
  94. package/dist/collection.d.ts.map +1 -1
  95. package/dist/collection.js +270 -32
  96. package/dist/connection.d.ts +50 -38
  97. package/dist/connection.d.ts.map +1 -1
  98. package/dist/connection.js +2245 -876
  99. package/dist/conversation.d.ts +46 -19
  100. package/dist/conversation.d.ts.map +1 -1
  101. package/dist/conversation.js +632 -221
  102. package/dist/durability.d.ts +7 -1
  103. package/dist/durability.d.ts.map +1 -1
  104. package/dist/entry.d.ts +17 -0
  105. package/dist/entry.d.ts.map +1 -0
  106. package/dist/entry.js +156 -0
  107. package/dist/expectation.d.ts +21 -0
  108. package/dist/expectation.d.ts.map +1 -0
  109. package/dist/expectation.js +70 -0
  110. package/dist/failure.d.ts +13 -0
  111. package/dist/failure.d.ts.map +1 -0
  112. package/dist/failure.js +19 -0
  113. package/dist/fence.d.ts +6 -0
  114. package/dist/fence.d.ts.map +1 -0
  115. package/dist/fence.js +10 -0
  116. package/dist/host.d.ts +24 -3
  117. package/dist/host.d.ts.map +1 -1
  118. package/dist/host.js +128 -70
  119. package/dist/identity.d.ts +5 -0
  120. package/dist/identity.d.ts.map +1 -1
  121. package/dist/identity.js +19 -1
  122. package/dist/index.d.ts +16 -9
  123. package/dist/index.d.ts.map +1 -1
  124. package/dist/index.js +5 -1
  125. package/dist/memory.d.ts +21 -7
  126. package/dist/memory.d.ts.map +1 -1
  127. package/dist/memory.js +164 -47
  128. package/dist/model.d.ts +17 -14
  129. package/dist/model.d.ts.map +1 -1
  130. package/dist/model.js +16 -4
  131. package/dist/observation.d.ts +6 -1
  132. package/dist/observation.d.ts.map +1 -1
  133. package/dist/observation.js +38 -17
  134. package/dist/open.d.ts +2 -0
  135. package/dist/open.d.ts.map +1 -0
  136. package/dist/open.js +1 -0
  137. package/dist/outcome.d.ts +35 -22
  138. package/dist/outcome.d.ts.map +1 -1
  139. package/dist/outcome.js +42 -1
  140. package/dist/overlay.d.ts +16 -3
  141. package/dist/overlay.d.ts.map +1 -1
  142. package/dist/overlay.js +118 -77
  143. package/dist/paths.d.ts +0 -1
  144. package/dist/paths.d.ts.map +1 -1
  145. package/dist/paths.js +0 -16
  146. package/dist/query.d.ts +47 -6
  147. package/dist/query.d.ts.map +1 -1
  148. package/dist/query.js +86 -38
  149. package/dist/reach.d.ts +45 -0
  150. package/dist/reach.d.ts.map +1 -0
  151. package/dist/reach.js +306 -0
  152. package/dist/react.d.ts +44 -0
  153. package/dist/react.d.ts.map +1 -0
  154. package/dist/react.js +66 -0
  155. package/dist/read-side.d.ts +16 -5
  156. package/dist/read-side.d.ts.map +1 -1
  157. package/dist/read-side.js +408 -137
  158. package/dist/readiness.d.ts +53 -0
  159. package/dist/readiness.d.ts.map +1 -0
  160. package/dist/readiness.js +33 -0
  161. package/dist/reads.d.ts +11 -0
  162. package/dist/reads.d.ts.map +1 -0
  163. package/dist/reads.js +98 -0
  164. package/dist/recovery.d.ts +25 -0
  165. package/dist/recovery.d.ts.map +1 -0
  166. package/dist/recovery.js +197 -0
  167. package/dist/refresh.d.ts +13 -2
  168. package/dist/refresh.d.ts.map +1 -1
  169. package/dist/refresh.js +51 -36
  170. package/dist/replica.d.ts +10 -7
  171. package/dist/replica.d.ts.map +1 -1
  172. package/dist/replica.js +131 -79
  173. package/dist/room-draft.d.ts +41 -9
  174. package/dist/room-draft.d.ts.map +1 -1
  175. package/dist/room-draft.js +139 -28
  176. package/dist/rows.d.ts +11 -14
  177. package/dist/rows.d.ts.map +1 -1
  178. package/dist/rows.js +87 -50
  179. package/dist/save.d.ts +32 -3
  180. package/dist/save.d.ts.map +1 -1
  181. package/dist/save.js +465 -93
  182. package/dist/search.d.ts +39 -8
  183. package/dist/search.d.ts.map +1 -1
  184. package/dist/search.js +132 -46
  185. package/dist/seed.d.ts +16 -0
  186. package/dist/seed.d.ts.map +1 -0
  187. package/dist/seed.js +61 -0
  188. package/dist/shared.d.ts +11 -0
  189. package/dist/shared.d.ts.map +1 -0
  190. package/dist/shared.js +368 -0
  191. package/dist/status.d.ts +6 -2
  192. package/dist/status.d.ts.map +1 -1
  193. package/dist/status.js +0 -8
  194. package/dist/store.d.ts +87 -32
  195. package/dist/store.d.ts.map +1 -1
  196. package/dist/store.js +558 -225
  197. package/dist/turn.d.ts +7 -0
  198. package/dist/turn.d.ts.map +1 -0
  199. package/dist/turn.js +45 -0
  200. package/dist/wire.d.ts +162 -19
  201. package/dist/wire.d.ts.map +1 -1
  202. package/dist/wire.js +82 -67
  203. package/package.json +31 -7
  204. package/dist/attachment.d.ts +0 -3
  205. package/dist/attachment.d.ts.map +0 -1
  206. package/dist/attachment.js +0 -8
package/dist/client.js CHANGED
@@ -1,5 +1,11 @@
1
1
  import { observed } from './observation.js';
2
- import { answer, AUTHORIZATION_ENDED, carried, envelope, PORT_CLOSED, PROTOCOL_MISMATCH, } from './wire.js';
2
+ import { runtimeError } from './failure.js';
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,8 +13,10 @@ 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
- port.postMessage(envelope(message));
19
+ port.postMessage(message);
12
20
  };
13
21
  function begin(id, topic, first) {
14
22
  const handle = observed(first, () => {
@@ -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,16 +59,10 @@ 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
- const wire = carried(event.data);
57
- if (wire === null)
58
- return;
59
- if (!wire.matching) {
60
- refuse(PROTOCOL_MISMATCH);
61
- return;
62
- }
63
- const message = answer(wire.message);
65
+ const message = answer(event.data);
64
66
  if (message === null)
65
67
  return;
66
68
  if (message.kind === 'refusal') {
@@ -68,7 +70,11 @@ export function connectMdRuntime(port) {
68
70
  return;
69
71
  }
70
72
  if (message.kind === 'ended') {
71
- 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));
72
78
  return;
73
79
  }
74
80
  if (message.kind === 'event') {
@@ -78,31 +84,34 @@ export function connectMdRuntime(port) {
78
84
  const waiting = opening.get(message.id);
79
85
  if (waiting !== undefined) {
80
86
  opening.delete(message.id);
81
- if (message.kind === 'failure')
82
- waiting.fail(new Error(message.message));
87
+ if (message.kind === 'failure') {
88
+ waiting.fail(runtimeError(message.code, message.message, message.retryable));
89
+ }
83
90
  else
84
91
  waiting.settle(begin(message.id, waiting.topic, message.value));
92
+ drawing?.();
85
93
  return;
86
94
  }
87
95
  const called = calls.get(message.id);
88
96
  if (called === undefined)
89
97
  return;
90
98
  calls.delete(message.id);
91
- if (message.kind === 'failure')
92
- called.fail(new Error(message.message));
99
+ if (message.kind === 'failure') {
100
+ called.fail(runtimeError(message.code, message.message, message.retryable));
101
+ }
93
102
  else
94
103
  called.settle(message.value);
95
104
  });
96
105
  port.addEventListener('close', () => {
97
- end(PORT_CLOSED);
106
+ end(runtimeError('port-closed', PORT_CLOSED));
98
107
  });
99
108
  port.start();
100
109
  send({ kind: 'hello' });
101
110
  function ask(member, input) {
102
111
  if (ended !== null)
103
- return Promise.reject(new Error(ended));
112
+ return Promise.reject(ended);
104
113
  if (refusal !== null)
105
- return Promise.reject(new Error(refusal.reason));
114
+ return Promise.reject(refused(refusal));
106
115
  const id = (sequence += 1);
107
116
  return new Promise((settle, fail) => {
108
117
  calls.set(id, { settle: settle, fail });
@@ -111,26 +120,53 @@ export function connectMdRuntime(port) {
111
120
  }
112
121
  function watch(topic, input) {
113
122
  if (ended !== null)
114
- return Promise.reject(new Error(ended));
123
+ return Promise.reject(ended);
115
124
  const id = (sequence += 1);
116
125
  if (refusal !== null) {
117
126
  return topic === 'connection-refusal'
118
127
  ? Promise.resolve(begin(id, topic, refusal))
119
- : Promise.reject(new Error(refusal.reason));
128
+ : Promise.reject(refused(refusal));
120
129
  }
121
130
  return new Promise((settle, fail) => {
122
131
  opening.set(id, { topic, settle, fail });
123
132
  send({ kind: 'observe', id, topic, input });
133
+ drawing?.();
124
134
  });
125
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();
162
+ });
163
+ return settling;
164
+ }
126
165
  return {
127
166
  rooms: () => ask('rooms', []),
128
- slots: () => ask('slots', []),
129
167
  query: ((query) => ask('query', [query])),
130
168
  search: (request) => ask('search', [request]),
131
169
  types: (locator) => ask('types', [locator]),
132
- syncStatus: () => ask('syncStatus', []),
133
- serviceMode: () => ask('serviceMode', []),
134
170
  roomDraft: {
135
171
  list: () => ask('roomDraft.list', []),
136
172
  read: (room, path) => ask('roomDraft.read', [room, path]),
@@ -138,7 +174,6 @@ export function connectMdRuntime(port) {
138
174
  discard: (room, path, revision) => ask('roomDraft.discard', [room, path, revision]),
139
175
  },
140
176
  save: (room, roomDrafts) => ask('save', [room, roomDrafts]),
141
- outcome: (identifier) => ask('outcome', [identifier]),
142
177
  presentation: {
143
178
  read: () => ask('presentation.read', []),
144
179
  write: (value) => ask('presentation.write', [value]),
@@ -147,7 +182,7 @@ export function connectMdRuntime(port) {
147
182
  list: () => ask('conversation.list', []),
148
183
  read: (conversation, options) => ask('conversation.read', [conversation, options]),
149
184
  create: (input) => ask('conversation.create', [input]),
150
- send: (conversation) => ask('conversation.send', [conversation]),
185
+ send: (conversation, operation, draft) => ask('conversation.send', [conversation, operation, draft]),
151
186
  answer: (conversation, answer) => ask('conversation.answer', [conversation, answer]),
152
187
  interrupt: (conversation) => ask('conversation.interrupt', [conversation]),
153
188
  kill: (conversation) => ask('conversation.kill', [conversation]),
@@ -155,17 +190,28 @@ export function connectMdRuntime(port) {
155
190
  delete: (conversation) => ask('conversation.delete', [conversation]),
156
191
  },
157
192
  chatDraft: {
158
- write: (conversation, draft) => ask('chatDraft.write', [conversation, draft]),
193
+ write: (conversation, draft, revision) => ask('chatDraft.write', [conversation, draft, revision]),
159
194
  discard: (conversation) => ask('chatDraft.discard', [conversation]),
160
195
  },
161
196
  history: (locator, path, query) => ask('history', [locator, path, query]),
197
+ binaryFile: {
198
+ read: (room, path, revision) => ask('binaryFile.read', [room, path, revision]),
199
+ readReference: (reference) => ask('binaryFile.readReference', [reference]),
200
+ },
162
201
  call: (name, input) => ask('call', [name, input]),
163
202
  reached: (locator, roomVersion, path) => ask('reached', [locator, roomVersion, path]),
164
203
  log: () => ask('log', []),
165
- storageLoss: () => ask('storageLoss', []),
166
204
  persistence: () => ask('persistence', []),
167
- deploymentFacts: () => ask('deploymentFacts', []),
205
+ retry: () => ask('retry', []),
168
206
  observe: ((topic, ...input) => watch(topic, input)),
169
207
  reauthorize: () => ask('reauthorize', []),
208
+ settled,
170
209
  };
171
210
  }
211
+ const turns = new WeakMap();
212
+ export function inTurn(client, work) {
213
+ const next = (turns.get(client) ?? Promise.resolve()).then(work).catch(() => undefined);
214
+ turns.set(client, next);
215
+ return next;
216
+ }
217
+ 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;
@@ -13,20 +16,24 @@ export type Composition = {
13
16
  roots: readonly string[];
14
17
  model: ModelState;
15
18
  } | null;
16
- untypedFolders: readonly string[];
17
19
  };
18
- export declare function untyped(roots: readonly string[], path: string): boolean;
19
20
  export declare function governing(composition: Composition, path: string): ModelState;
20
- type Previous = {
21
- text: Readonly<Record<string, string>>;
22
- collection: Collection;
23
- };
24
- export declare function parseCollection(text: Readonly<Record<string, string>>, composition: Composition, previous?: Previous): Collection;
25
- export declare function readCollection(text: Readonly<Record<string, string>>, composition: Composition, previous?: Previous): {
26
- kind: 'read';
27
- collection: Collection;
28
- } | 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;
29
25
  export declare function validRoot(root: unknown): root is string;
30
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;
31
38
  export {};
32
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;IAClE,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC,CAAC;AASF,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvE;AAED,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,CAQZ;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,CASpD;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,CA4BZ"}
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
@@ -1,47 +1,94 @@
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 } from './shared.js';
5
+ import { 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) {
6
9
  return (composition.selection !== null &&
7
10
  composition.selection.roots.some((root) => beneath(root, path)));
8
11
  }
9
- export function untyped(roots, path) {
10
- return roots.some((root) => beneath(root, path));
11
- }
12
12
  export function governing(composition, path) {
13
13
  return composition.selection !== null && selected(composition, path)
14
14
  ? composition.selection.model
15
15
  : composition.model;
16
16
  }
17
- function parsedAt(text, path, previous) {
18
- const retained = previous?.collection.documents[path];
19
- return retained !== undefined && previous?.text[path] === text[path]
20
- ? { ok: true, document: retained }
21
- : parse(text[path], path);
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;
22
67
  }
23
- export function parseCollection(text, composition, previous) {
24
- const documents = {};
25
- for (const path of Object.keys(text).sort()) {
26
- if (untyped(composition.untypedFolders, path))
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)
27
72
  continue;
28
- const parsed = parsedAt(text, path, previous);
29
- if (parsed.ok)
30
- documents[path] = parsed.document;
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];
31
80
  }
32
- return { documents, composition };
33
81
  }
34
- export function readCollection(text, composition, previous) {
35
- const documents = {};
36
- for (const path of Object.keys(text).sort()) {
37
- if (untyped(composition.untypedFolders, path))
38
- continue;
39
- const parsed = parsedAt(text, path, previous);
40
- if (!parsed.ok)
41
- return { kind: 'refused', refusal: parsed.refusal };
42
- documents[path] = parsed.document;
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 };
43
90
  }
44
- return { kind: 'read', collection: { documents, composition } };
91
+ return null;
45
92
  }
46
93
  export function validRoot(root) {
47
94
  return typeof root === 'string' && normalize(root) === root;
@@ -61,22 +108,213 @@ export function validateCollection(text, binaryTargets, composition) {
61
108
  const collection = {};
62
109
  const selection = {};
63
110
  for (const [path, content] of Object.entries(text)) {
64
- if (untyped(composition.untypedFolders, path))
65
- continue;
66
111
  if (selected(composition, path))
67
112
  selection[path] = content;
68
113
  else
69
114
  collection[path] = content;
70
115
  }
71
- const linked = binaryTargets.filter((path) => !untyped(composition.untypedFolders, path));
72
116
  const verdicts = [
73
- validateAgainst(collection, linked.filter((path) => !selected(composition, path)), composition.model),
117
+ validateAgainst(collection, binaryTargets.filter((path) => !selected(composition, path)), composition.model),
74
118
  ];
75
119
  if (composition.selection !== null) {
76
- verdicts.push(validateAgainst(selection, linked.filter((path) => selected(composition, path)), composition.selection.model));
120
+ verdicts.push(validateAgainst(selection, binaryTargets.filter((path) => selected(composition, path)), composition.selection.model));
77
121
  }
78
122
  const refusals = verdicts.filter((verdict) => verdict.verdict === 'refuse');
79
123
  if (refusals.length === 0)
80
124
  return verdicts[0];
81
125
  return refusals.find((refusal) => refusal.rule !== 'model-unavailable') ?? refusals[0];
82
126
  }
127
+ const referrersOf = new WeakMap();
128
+ function typing(left, right) {
129
+ return (sameTyping(left.model, right.model) &&
130
+ (left.selection === null || right.selection === null
131
+ ? left.selection === right.selection
132
+ : sameTyping(left.selection.model, right.selection.model)) &&
133
+ JSON.stringify(left.selection?.roots ?? null) === JSON.stringify(right.selection?.roots ?? null));
134
+ }
135
+ function declared(content) {
136
+ if (content === undefined)
137
+ return undefined;
138
+ const read = parseFrontmatter(content);
139
+ return read.ok ? (read.frontmatter.type ?? 'any') : undefined;
140
+ }
141
+ function typedIn(content) {
142
+ const type = declared(content);
143
+ return type !== undefined && type !== 'any';
144
+ }
145
+ function modelOf(composition, path) {
146
+ const model = governing(composition, path);
147
+ return model.status === 'unavailable' ? null : { types: model.types };
148
+ }
149
+ function checked(path, content, composition) {
150
+ return checkDocument(path, content, modelOf(composition, path) ?? NO_TYPES);
151
+ }
152
+ function targetsOf(check) {
153
+ if (!check.ok)
154
+ return [];
155
+ return [...new Set(check.references.flatMap(({ target }) => (target === null ? [] : [target])))];
156
+ }
157
+ function referencedBy(path, content, composition) {
158
+ const model = modelOf(composition, path);
159
+ if (model === null)
160
+ return [];
161
+ const read = parseFrontmatter(content, path);
162
+ const type = read.ok ? read.frontmatter.type : undefined;
163
+ if (!read.ok || type === undefined || !Object.hasOwn(model.types, type))
164
+ return [];
165
+ const definition = model.types[type];
166
+ return [
167
+ ...new Set([
168
+ ...references(read.frontmatter, definition).flatMap(({ value }) => {
169
+ const found = target(path, value);
170
+ return found === null ? [] : [found];
171
+ }),
172
+ ...sectionReferences(path, content, definition).flatMap(({ target }) => target === null ? [] : [target]),
173
+ ]),
174
+ ];
175
+ }
176
+ function reindexed(index, changed) {
177
+ const referrers = new Map();
178
+ const touch = (target) => {
179
+ let held = referrers.get(target);
180
+ if (held === undefined) {
181
+ held = new Set(Object.hasOwn(index.referrers, target) ? index.referrers[target] : []);
182
+ referrers.set(target, held);
183
+ }
184
+ return held;
185
+ };
186
+ for (const [path, found] of changed) {
187
+ const before = Object.hasOwn(index.targets, path) ? index.targets[path] : [];
188
+ for (const target of before)
189
+ touch(target).delete(path);
190
+ for (const target of found)
191
+ touch(target).add(path);
192
+ }
193
+ const emptied = [...referrers].filter(([, held]) => held.size === 0).map(([target]) => target);
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]
198
+ .filter(([, held]) => held.size > 0)
199
+ .map(([target, held]) => [target, [...held]]), emptied),
200
+ };
201
+ }
202
+ function heldIndex(held, composition) {
203
+ const index = referrersOf.get(held.text);
204
+ return index !== undefined && typing(index.composition, composition) ? index : null;
205
+ }
206
+ const indexing = new WeakMap();
207
+ export function indexReferrers(held, composition) {
208
+ if (heldIndex(held, composition) !== null)
209
+ return;
210
+ const running = indexing.get(held.text);
211
+ if (running !== undefined)
212
+ return running;
213
+ const targets = new Map();
214
+ const referrers = new Map();
215
+ const filled = inUnits(Object.keys(held.text), (path) => {
216
+ const content = held.text[path];
217
+ const found = referencedBy(path, content, composition);
218
+ if (found.length > 0)
219
+ targets.set(path, found);
220
+ for (const target of found)
221
+ referrers.set(target, [...(referrers.get(target) ?? []), path]);
222
+ return ROW_WEIGHT + content.length;
223
+ });
224
+ const indexed = () => {
225
+ referrersOf.set(held.text, {
226
+ composition,
227
+ targets: patched(shared({}), targets),
228
+ referrers: patched(shared({}), referrers),
229
+ });
230
+ };
231
+ if (!(filled instanceof Promise))
232
+ return indexed();
233
+ const building = filled.then(indexed).finally(() => indexing.delete(held.text));
234
+ indexing.set(held.text, building);
235
+ return building;
236
+ }
237
+ export function referencesIndexed(held, composition) {
238
+ return heldIndex(held, composition) !== null;
239
+ }
240
+ export function referring(held, composition, targets) {
241
+ const index = heldIndex(held, composition);
242
+ if (index === null)
243
+ return null;
244
+ const found = new Set();
245
+ for (const target of targets) {
246
+ if (!Object.hasOwn(index.referrers, target))
247
+ continue;
248
+ for (const file of index.referrers[target])
249
+ found.add(file);
250
+ }
251
+ return found;
252
+ }
253
+ export function available(composition) {
254
+ return (composition.model.status !== 'unavailable' &&
255
+ (composition.selection === null || composition.selection.model.status !== 'unavailable'));
256
+ }
257
+ export function changedCleanly(next, previous, composition) {
258
+ if (!typing(previous.composition, composition))
259
+ return false;
260
+ const paths = new Set([
261
+ ...differing(previous.text, next.text),
262
+ ...differing(previous.binaries, next.binaries, () => true),
263
+ ]);
264
+ const binary = (path) => Object.hasOwn(next.binaries, path);
265
+ const within = (file) => (path) => selected(composition, path) === selected(composition, file);
266
+ const reaching = (file, check) => {
267
+ if (!check.ok)
268
+ return false;
269
+ const part = within(file);
270
+ return check.references.every((reference) => resolves(reference, (path) => (part(path) ? declared(next.text[path]) : undefined), (path) => part(path) && binary(path)));
271
+ };
272
+ const changedTargets = new Map();
273
+ const shifted = [];
274
+ for (const path of paths) {
275
+ const now = next.text[path];
276
+ const was = previous.text[path];
277
+ const modeled = modelOf(composition, path) !== null;
278
+ if ((now === undefined) !== (was === undefined) ||
279
+ declared(now) !== declared(was) ||
280
+ binary(path) !== Object.hasOwn(previous.binaries, path)) {
281
+ shifted.push(path);
282
+ }
283
+ if (!modeled && typedIn(was) && !typedIn(now))
284
+ return false;
285
+ if (now === undefined) {
286
+ if (modeled)
287
+ changedTargets.set(path, []);
288
+ continue;
289
+ }
290
+ if (binary(path))
291
+ return false;
292
+ if (!modeled) {
293
+ if (declared(now) === undefined)
294
+ return false;
295
+ if (!typedIn(now) && !checked(path, now, composition).ok)
296
+ return false;
297
+ continue;
298
+ }
299
+ const check = checked(path, now, composition);
300
+ if (!reaching(path, check))
301
+ return false;
302
+ changedTargets.set(path, targetsOf(check));
303
+ }
304
+ const index = heldIndex(previous, composition);
305
+ if (index === null && shifted.length > 0)
306
+ return false;
307
+ for (const target of shifted) {
308
+ const files = index !== null && Object.hasOwn(index.referrers, target) ? index.referrers[target] : [];
309
+ for (const file of files) {
310
+ if (paths.has(file))
311
+ continue;
312
+ if (!reaching(file, checked(file, next.text[file], composition)))
313
+ return false;
314
+ }
315
+ }
316
+ if (index !== null) {
317
+ referrersOf.set(next.text, changedTargets.size === 0 ? index : reindexed(index, changedTargets));
318
+ }
319
+ return true;
320
+ }