@agentshouse/mdruntime 0.1.0-cf6041a06849c → 0.1.0-cf6e25bcd52f6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -10
- package/dist/adapter.d.ts +144 -29
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +5 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +53 -6
- package/dist/attempt.d.ts +16 -0
- package/dist/attempt.d.ts.map +1 -0
- package/dist/attempt.js +70 -0
- package/dist/binary.d.ts +18 -0
- package/dist/binary.d.ts.map +1 -0
- package/dist/binary.js +8 -0
- package/dist/bootstrap.d.ts +41 -6
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +224 -45
- package/dist/chain.d.ts +11 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +64 -0
- package/dist/change-set.d.ts +9 -14
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -56
- package/dist/change.d.ts +45 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +34 -0
- package/dist/client.d.ts +35 -14
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +156 -19
- package/dist/collection.d.ts +14 -13
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +66 -54
- package/dist/compile.d.ts +16 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +765 -0
- package/dist/connection.d.ts +63 -20
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +3889 -743
- package/dist/conversation.d.ts +42 -12
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +642 -188
- package/dist/digest.d.ts +7 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +113 -0
- package/dist/durability.d.ts +5 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +19 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +151 -0
- package/dist/expectation.d.ts +21 -0
- package/dist/expectation.d.ts.map +1 -0
- package/dist/expectation.js +70 -0
- package/dist/failure.d.ts +13 -0
- package/dist/failure.d.ts.map +1 -0
- package/dist/failure.js +19 -0
- package/dist/fence.d.ts +6 -0
- package/dist/fence.d.ts.map +1 -0
- package/dist/fence.js +10 -0
- package/dist/file.d.ts +2 -19
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +7 -122
- package/dist/fulltext.d.ts +27 -0
- package/dist/fulltext.d.ts.map +1 -0
- package/dist/fulltext.js +368 -0
- package/dist/host.d.ts +15 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +134 -21
- package/dist/identity.d.ts +4 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +16 -1
- package/dist/index.d.ts +19 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/live.d.ts +22 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +124 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +208 -0
- package/dist/memory.d.ts +13 -5
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +140 -31
- package/dist/messages.d.ts +28 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +90 -0
- package/dist/model.d.ts +20 -4
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +19 -2
- package/dist/name.d.ts +4 -0
- package/dist/name.d.ts.map +1 -0
- package/dist/name.js +122 -0
- package/dist/observation.d.ts +20 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +113 -12
- package/dist/open.d.ts +2 -0
- package/dist/open.d.ts.map +1 -0
- package/dist/open.js +1 -0
- package/dist/opened.d.ts +22 -0
- package/dist/opened.d.ts.map +1 -0
- package/dist/opened.js +107 -0
- package/dist/outcome.d.ts +73 -3
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +81 -1
- package/dist/overlay.d.ts +15 -5
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +136 -180
- package/dist/paths.d.ts +1 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +4 -16
- package/dist/query.d.ts +71 -5
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +100 -26
- package/dist/reach.d.ts +18 -4
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +247 -84
- package/dist/react.d.ts +31 -19
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +13 -21
- package/dist/readiness.d.ts +64 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +33 -0
- package/dist/reads.d.ts +11 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +100 -0
- package/dist/recovery.d.ts +25 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +198 -0
- package/dist/refresh.d.ts +19 -8
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +125 -57
- package/dist/replica.d.ts +59 -33
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +322 -155
- package/dist/room-draft.d.ts +41 -9
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +186 -37
- package/dist/rows.d.ts +39 -34
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +197 -80
- package/dist/save.d.ts +74 -4
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +900 -178
- package/dist/search.d.ts +41 -29
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +37 -105
- package/dist/seed.d.ts +16 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +75 -0
- package/dist/shared.d.ts +16 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +431 -0
- package/dist/status.d.ts +11 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -9
- package/dist/store.d.ts +132 -58
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +379 -344
- package/dist/stored.d.ts +154 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +578 -0
- package/dist/tables.d.ts +362 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +627 -0
- package/dist/turn.d.ts +9 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +75 -0
- package/dist/validation.d.ts +30 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +162 -0
- package/dist/windows.d.ts +22 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +89 -0
- package/dist/wire.d.ts +173 -33
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +69 -31
- package/dist/words.d.ts +19 -0
- package/dist/words.d.ts.map +1 -0
- package/dist/words.js +156 -0
- package/package.json +26 -15
- package/AGENTS.md +0 -231
- package/dist/attachment.d.ts +0 -3
- package/dist/attachment.d.ts.map +0 -1
- package/dist/attachment.js +0 -8
- package/dist/read-side.d.ts +0 -18
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -377
package/dist/reach.js
CHANGED
|
@@ -1,34 +1,46 @@
|
|
|
1
1
|
import { unheldView } from './conversation.js';
|
|
2
2
|
import { modelTypes, NO_MODEL } from './model.js';
|
|
3
|
-
import {
|
|
3
|
+
import { changeOf, digestApplied, digestOf, observing, } from './observation.js';
|
|
4
|
+
import { runtimeError } from './failure.js';
|
|
5
|
+
import { failure, lockedRoom, ROOM_LOCKED, undeclared, UNREADABLE, } from './outcome.js';
|
|
4
6
|
import { beneath } from './paths.js';
|
|
5
|
-
import {
|
|
6
|
-
import { moveTarget, readParts } from './room-draft.js';
|
|
7
|
+
import { moveTarget, readParts, staleWrite, } from './room-draft.js';
|
|
7
8
|
import { documentKey } from './rows.js';
|
|
9
|
+
import { undeclaredSearch } from './search.js';
|
|
8
10
|
const UNHELD_TYPES = { collection: modelTypes(NO_MODEL), typedSelection: null };
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return Promise.resolve(UNDECLARED);
|
|
11
|
+
function unconversed(name) {
|
|
12
|
+
return () => Promise.resolve(undeclared(name));
|
|
12
13
|
}
|
|
13
14
|
const NO_CONVERSATION = {
|
|
14
15
|
list: () => Promise.resolve([]),
|
|
15
16
|
read: (conversation) => Promise.resolve(unheldView(conversation)),
|
|
16
|
-
create:
|
|
17
|
-
send:
|
|
18
|
-
answer:
|
|
19
|
-
interrupt:
|
|
20
|
-
kill:
|
|
21
|
-
cancel:
|
|
22
|
-
delete:
|
|
17
|
+
create: unconversed('conversation.create'),
|
|
18
|
+
send: unconversed('conversation.send'),
|
|
19
|
+
answer: unconversed('conversation.answer'),
|
|
20
|
+
interrupt: unconversed('conversation.interrupt'),
|
|
21
|
+
kill: unconversed('conversation.kill'),
|
|
22
|
+
cancel: unconversed('conversation.cancel'),
|
|
23
|
+
delete: unconversed('conversation.delete'),
|
|
23
24
|
};
|
|
24
25
|
const NO_CHAT_DRAFT = {
|
|
25
26
|
write: () => Promise.resolve(),
|
|
27
|
+
prepend: () => Promise.resolve(),
|
|
26
28
|
discard: () => Promise.resolve(),
|
|
27
29
|
};
|
|
30
|
+
function areasOf(reach) {
|
|
31
|
+
return reach.areas ?? [];
|
|
32
|
+
}
|
|
33
|
+
function typedAreas(reach, room) {
|
|
34
|
+
return areasOf(reach).filter((area) => area.room === room && area.types.length > 0);
|
|
35
|
+
}
|
|
36
|
+
function fenced(reach, room, path) {
|
|
37
|
+
return areasOf(reach).some((area) => area.room === room && beneath(area.path, path));
|
|
38
|
+
}
|
|
28
39
|
export function holds(reach, room) {
|
|
29
40
|
return (reach === null ||
|
|
30
41
|
reach.rooms.includes(room) ||
|
|
31
|
-
reach.documents.some((document) => document.room === room)
|
|
42
|
+
reach.documents.some((document) => document.room === room) ||
|
|
43
|
+
typedAreas(reach, room).length > 0);
|
|
32
44
|
}
|
|
33
45
|
function wholly(reach, room) {
|
|
34
46
|
return reach === null || reach.rooms.includes(room);
|
|
@@ -38,15 +50,19 @@ export function governs(reach, room) {
|
|
|
38
50
|
return { collection: true, selection: room.typedSelection !== null };
|
|
39
51
|
}
|
|
40
52
|
const selected = (path) => room.typedSelection?.roots.some((root) => beneath(root, path)) ?? false;
|
|
41
|
-
const paths =
|
|
42
|
-
.
|
|
43
|
-
|
|
53
|
+
const paths = [
|
|
54
|
+
...reach.documents
|
|
55
|
+
.filter((document) => document.room === room.locator)
|
|
56
|
+
.map(({ path }) => path),
|
|
57
|
+
...typedAreas(reach, room.locator).map(({ path }) => path),
|
|
58
|
+
];
|
|
44
59
|
return { collection: paths.some((path) => !selected(path)), selection: paths.some(selected) };
|
|
45
60
|
}
|
|
46
61
|
function covers(reach, room, path) {
|
|
47
62
|
return (reach === null ||
|
|
48
|
-
reach
|
|
49
|
-
|
|
63
|
+
(!fenced(reach, room, path) &&
|
|
64
|
+
(reach.rooms.includes(room) ||
|
|
65
|
+
reach.documents.some((document) => document.room === room && document.path === path))));
|
|
50
66
|
}
|
|
51
67
|
function conversing(reach) {
|
|
52
68
|
return reach === null || reach.conversations;
|
|
@@ -59,25 +75,13 @@ export function planReach(reach) {
|
|
|
59
75
|
documents: [
|
|
60
76
|
...new Set(reach.documents.map(({ room, path }) => documentKey(room, path))),
|
|
61
77
|
].sort(),
|
|
78
|
+
areas: areasOf(reach)
|
|
79
|
+
.map(({ room, path, types }) => ({ room, path, types: [...new Set(types)].sort() }))
|
|
80
|
+
.sort((left, right) => documentKey(left.room, left.path) < documentKey(right.room, right.path) ? -1 : 1),
|
|
62
81
|
};
|
|
63
82
|
}
|
|
64
83
|
function roomsWithin(reach, rooms) {
|
|
65
|
-
|
|
66
|
-
return rooms;
|
|
67
|
-
return rooms
|
|
68
|
-
.filter((room) => holds(reach, room.locator))
|
|
69
|
-
.map((room) => {
|
|
70
|
-
if (wholly(reach, room.locator))
|
|
71
|
-
return room;
|
|
72
|
-
const governed = governs(reach, room);
|
|
73
|
-
return {
|
|
74
|
-
...room,
|
|
75
|
-
model: governed.collection ? room.model : modelTypes(NO_MODEL),
|
|
76
|
-
typedSelection: governed.selection ? room.typedSelection : null,
|
|
77
|
-
roomVersion: null,
|
|
78
|
-
attachments: [],
|
|
79
|
-
};
|
|
80
|
-
});
|
|
84
|
+
return rooms.filter((room) => wholly(reach, room.locator));
|
|
81
85
|
}
|
|
82
86
|
function typesWithin(reach, room, types) {
|
|
83
87
|
if (room === undefined)
|
|
@@ -89,7 +93,7 @@ function typesWithin(reach, room, types) {
|
|
|
89
93
|
};
|
|
90
94
|
}
|
|
91
95
|
function moves(reach, room, move) {
|
|
92
|
-
return move === undefined || covers(reach, room, moveTarget(move));
|
|
96
|
+
return move === undefined || covers(reach, move.room ?? room, moveTarget(move));
|
|
93
97
|
}
|
|
94
98
|
function draftWithin(reach, draft) {
|
|
95
99
|
return covers(reach, draft.room, draft.path) && moves(reach, draft.room, draft.parts.move);
|
|
@@ -103,7 +107,7 @@ function shownDraft(reach, draft) {
|
|
|
103
107
|
function writeWithin(reach, write) {
|
|
104
108
|
return write.outcome === 'written'
|
|
105
109
|
? { outcome: 'written', roomDraft: shownDraft(reach, write.roomDraft) }
|
|
106
|
-
:
|
|
110
|
+
: staleWrite(shownDraft(reach, write.current));
|
|
107
111
|
}
|
|
108
112
|
function savedWithin(reach, room, result) {
|
|
109
113
|
if (reach === null || result.outcome === 'saved')
|
|
@@ -122,76 +126,128 @@ function savedWithin(reach, room, result) {
|
|
|
122
126
|
function statusWithin(reach, status) {
|
|
123
127
|
return {
|
|
124
128
|
...status,
|
|
125
|
-
attentionRequired: status.attentionRequired.filter((entry) => covers(reach, entry.room, entry.path)),
|
|
129
|
+
attentionRequired: status.attentionRequired.filter((entry) => 'refusal' in entry ? wholly(reach, entry.room) : covers(reach, entry.room, entry.path)),
|
|
130
|
+
unsent: status.unsent.filter((change) => covers(reach, change.room, change.path)),
|
|
126
131
|
};
|
|
127
132
|
}
|
|
128
133
|
function queueWithin(reach, room, entries) {
|
|
129
|
-
return entries
|
|
130
|
-
(
|
|
134
|
+
return entries
|
|
135
|
+
.filter(({ batch }) => batch.changes.every((change) => covers(reach, room, change.path) &&
|
|
136
|
+
(change.kind !== 'move' || covers(reach, room, change.to))))
|
|
137
|
+
.map((entry) => {
|
|
138
|
+
const { between, ...batch } = entry.batch;
|
|
139
|
+
if (between === undefined)
|
|
140
|
+
return entry;
|
|
141
|
+
return wholly(reach, between.source.room) && wholly(reach, between.destination.room)
|
|
142
|
+
? entry
|
|
143
|
+
: { ...entry, batch };
|
|
144
|
+
});
|
|
131
145
|
}
|
|
132
146
|
function unreached(room) {
|
|
133
|
-
return
|
|
147
|
+
return runtimeError('room-not-held', `No Local Replica for ${room}`);
|
|
148
|
+
}
|
|
149
|
+
function lockedError(room) {
|
|
150
|
+
return runtimeError(ROOM_LOCKED, `${room} is locked until House confirms its access`, true);
|
|
151
|
+
}
|
|
152
|
+
function deviceUnreached() {
|
|
153
|
+
return runtimeError('device-unreached', 'Only the panel cleans up this device');
|
|
134
154
|
}
|
|
135
|
-
|
|
155
|
+
const ATTACH_REQUIRED = 'attach-required';
|
|
156
|
+
function attachRequired() {
|
|
157
|
+
return runtimeError(ATTACH_REQUIRED, 'App code takes a file in with binaryFile.attach');
|
|
158
|
+
}
|
|
159
|
+
function carriesFile(parts) {
|
|
160
|
+
return parts !== null && typeof parts === 'object' && Object.hasOwn(parts, 'file');
|
|
161
|
+
}
|
|
162
|
+
export function narrowed(runtime, given, told) {
|
|
136
163
|
const { md, watch } = runtime;
|
|
137
164
|
let reach = given;
|
|
165
|
+
let port = told;
|
|
138
166
|
const open = new Set();
|
|
139
|
-
function following(begin) {
|
|
167
|
+
function following(begin, locking = false) {
|
|
140
168
|
let stop = begin();
|
|
141
169
|
const restart = () => {
|
|
142
170
|
stop();
|
|
143
171
|
stop = begin();
|
|
144
172
|
};
|
|
145
173
|
open.add(restart);
|
|
174
|
+
const unwatch = locking ? runtime.watchLocks(restart) : () => { };
|
|
146
175
|
return () => {
|
|
147
176
|
open.delete(restart);
|
|
177
|
+
unwatch();
|
|
148
178
|
stop();
|
|
149
179
|
};
|
|
150
180
|
}
|
|
181
|
+
const unlocking = (rooms) => {
|
|
182
|
+
const locked = (rooms ?? []).filter((room) => runtime.locked(room));
|
|
183
|
+
return locked.length === 0
|
|
184
|
+
? null
|
|
185
|
+
: { pending: locked.map((room) => ({ room, awaits: 'unlock' })) };
|
|
186
|
+
};
|
|
187
|
+
const unlocked = (entries) => entries.filter((entry) => !runtime.locked(entry.room));
|
|
188
|
+
const intoLocked = (held) => held.some((move) => move?.room !== undefined && runtime.locked(move.room));
|
|
151
189
|
const within = (topic, input, deliver) => {
|
|
152
190
|
if (topic === 'rooms') {
|
|
153
191
|
return following(() => watch(topic, input, (rooms) => deliver(roomsWithin(reach, rooms))));
|
|
154
192
|
}
|
|
193
|
+
if (topic === 'readiness') {
|
|
194
|
+
return following(() => runtime.watchReadiness(port, reach, deliver));
|
|
195
|
+
}
|
|
155
196
|
if (topic === 'sync-status') {
|
|
156
|
-
return following(() => watch(topic, input, (status) =>
|
|
197
|
+
return following(() => watch(topic, input, (status) => {
|
|
198
|
+
const within = statusWithin(reach, status);
|
|
199
|
+
deliver({ ...within, attentionRequired: unlocked(within.attentionRequired) });
|
|
200
|
+
}), true);
|
|
157
201
|
}
|
|
158
202
|
if (topic === 'room-drafts') {
|
|
159
|
-
return following(() => watch(topic, input, (drafts) => deliver(draftsWithin(reach, drafts))));
|
|
203
|
+
return following(() => watch(topic, input, (drafts) => deliver(unlocked(draftsWithin(reach, drafts)))), true);
|
|
160
204
|
}
|
|
161
205
|
if (topic === 'queue') {
|
|
162
206
|
const room = input[0];
|
|
163
207
|
return following(() => {
|
|
164
|
-
if (!holds(reach, room)) {
|
|
208
|
+
if (!holds(reach, room) || runtime.locked(room)) {
|
|
165
209
|
deliver([]);
|
|
166
210
|
return () => { };
|
|
167
211
|
}
|
|
168
212
|
return watch(topic, input, (entries) => deliver(queueWithin(reach, room, entries)));
|
|
169
|
-
});
|
|
213
|
+
}, true);
|
|
170
214
|
}
|
|
171
215
|
if (topic === 'query') {
|
|
172
216
|
const plan = runtime.plan(input[0], reach);
|
|
173
217
|
let held = null;
|
|
174
218
|
return following(() => {
|
|
219
|
+
const locked = unlocking(plan.rooms);
|
|
220
|
+
if (locked !== null) {
|
|
221
|
+
const answer = { result: locked, keys: [] };
|
|
222
|
+
if (held === null || changeOf(held, answer) !== null)
|
|
223
|
+
deliver(answer);
|
|
224
|
+
held = digestOf(answer);
|
|
225
|
+
return () => { };
|
|
226
|
+
}
|
|
175
227
|
let first = true;
|
|
176
|
-
|
|
228
|
+
let open = true;
|
|
229
|
+
const heard = (message) => {
|
|
177
230
|
if (!first) {
|
|
178
|
-
held =
|
|
231
|
+
held = digestApplied(held, message);
|
|
179
232
|
deliver(message);
|
|
180
233
|
return;
|
|
181
234
|
}
|
|
182
235
|
first = false;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
deliver(next.change);
|
|
236
|
+
const change = held === null ? message : changeOf(held, message);
|
|
237
|
+
held = digestOf(message);
|
|
238
|
+
if (change !== null)
|
|
239
|
+
deliver(change);
|
|
240
|
+
};
|
|
241
|
+
const current = { ...plan, reach: planReach(reach), locked: runtime.lockedRooms() };
|
|
242
|
+
const stop = runtime.watchPlan(current, (message) => {
|
|
243
|
+
if (open)
|
|
244
|
+
heard(message);
|
|
193
245
|
});
|
|
194
|
-
|
|
246
|
+
return () => {
|
|
247
|
+
open = false;
|
|
248
|
+
stop();
|
|
249
|
+
};
|
|
250
|
+
}, true);
|
|
195
251
|
}
|
|
196
252
|
if (topic === 'conversation') {
|
|
197
253
|
return following(() => {
|
|
@@ -201,7 +257,7 @@ export function narrowed(runtime, given) {
|
|
|
201
257
|
return () => { };
|
|
202
258
|
});
|
|
203
259
|
}
|
|
204
|
-
if (topic === 'conversations') {
|
|
260
|
+
if (topic === 'conversations' || topic === 'agents') {
|
|
205
261
|
return following(() => {
|
|
206
262
|
if (conversing(reach))
|
|
207
263
|
return watch(topic, input, deliver);
|
|
@@ -211,48 +267,149 @@ export function narrowed(runtime, given) {
|
|
|
211
267
|
}
|
|
212
268
|
return watch(topic, input, deliver);
|
|
213
269
|
};
|
|
270
|
+
const files = new Map();
|
|
271
|
+
let handed = 0;
|
|
272
|
+
async function hand(reading) {
|
|
273
|
+
const answer = await reading;
|
|
274
|
+
if (answer.kind !== 'binary')
|
|
275
|
+
return answer;
|
|
276
|
+
handed += 1;
|
|
277
|
+
const file = String(handed);
|
|
278
|
+
files.set(file, answer.file);
|
|
279
|
+
return { ...answer, file };
|
|
280
|
+
}
|
|
214
281
|
const conversation = () => (conversing(reach) ? md.conversation : NO_CONVERSATION);
|
|
215
282
|
const chatDraft = () => (conversing(reach) ? md.chatDraft : NO_CHAT_DRAFT);
|
|
216
283
|
const view = {
|
|
217
284
|
...md,
|
|
218
|
-
rooms: () => roomsWithin(reach,
|
|
219
|
-
query: ((input) =>
|
|
220
|
-
|
|
285
|
+
rooms: () => md.rooms().then((rooms) => roomsWithin(reach, rooms)),
|
|
286
|
+
query: ((input) => {
|
|
287
|
+
const plan = runtime.plan(input, reach);
|
|
288
|
+
return Promise.resolve(unlocking(plan.rooms) ?? runtime.query({ ...plan, locked: runtime.lockedRooms() }));
|
|
289
|
+
}),
|
|
290
|
+
search: (input) => port.search === 'required'
|
|
291
|
+
? runtime.search(input, reach, runtime.lockedRooms())
|
|
292
|
+
: Promise.resolve(undeclaredSearch()),
|
|
221
293
|
types(room) {
|
|
294
|
+
if (holds(reach, room) && runtime.locked(room))
|
|
295
|
+
throw lockedError(room);
|
|
222
296
|
if (wholly(reach, room))
|
|
223
297
|
return md.types(room);
|
|
224
298
|
if (!holds(reach, room))
|
|
225
299
|
return structuredClone(UNHELD_TYPES);
|
|
226
|
-
|
|
227
|
-
return typesWithin(reach, listed, md.types(room));
|
|
300
|
+
return typesWithin(reach, runtime.room(room), md.types(room));
|
|
228
301
|
},
|
|
229
302
|
async save(room, roomDrafts, writtenBy) {
|
|
230
303
|
if (!holds(reach, room))
|
|
231
304
|
throw unreached(room);
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
305
|
+
if (runtime.locked(room))
|
|
306
|
+
return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
|
|
307
|
+
const held = (await Promise.all(roomDrafts.map(({ path }) => md.roomDraft.read(room, path)))).map((draft) => draft?.parts.move);
|
|
308
|
+
const outside = roomDrafts.find(({ path }, at) => !covers(reach, room, path) || !moves(reach, room, held[at]));
|
|
309
|
+
if (outside !== undefined) {
|
|
310
|
+
return { outcome: 'refused', code: 'stale', retryable: false, file: outside.path };
|
|
311
|
+
}
|
|
312
|
+
if (intoLocked(held))
|
|
313
|
+
return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
|
|
236
314
|
return savedWithin(reach, room, await md.save(room, roomDrafts, writtenBy));
|
|
237
315
|
},
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
316
|
+
async saveParts(room, parts, writtenBy) {
|
|
317
|
+
if (!holds(reach, room))
|
|
318
|
+
throw unreached(room);
|
|
319
|
+
const filed = reach === null ? undefined : parts.find(({ parts: one }) => carriesFile(one));
|
|
320
|
+
if (filed !== undefined) {
|
|
321
|
+
return { outcome: 'refused', code: ATTACH_REQUIRED, retryable: false, file: filed.path };
|
|
322
|
+
}
|
|
323
|
+
if (runtime.locked(room))
|
|
324
|
+
return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
|
|
325
|
+
const held = parts.map(({ parts: one }) => readParts(one).move);
|
|
326
|
+
if (parts.some(({ path }, at) => !covers(reach, room, path) || !moves(reach, room, held[at]))) {
|
|
327
|
+
throw unreached(room);
|
|
328
|
+
}
|
|
329
|
+
if (intoLocked(held))
|
|
330
|
+
return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
|
|
331
|
+
return savedWithin(reach, room, await md.saveParts(room, parts, writtenBy));
|
|
332
|
+
},
|
|
333
|
+
resolve(room, path, destination) {
|
|
334
|
+
const at = runtime.located(room, path, destination);
|
|
335
|
+
return at !== null && !covers(reach, at.room, at.path)
|
|
336
|
+
? Promise.resolve(UNREADABLE)
|
|
337
|
+
: md.resolve(room, path, destination);
|
|
338
|
+
},
|
|
339
|
+
history: (room, path, query) => !covers(reach, room, path)
|
|
340
|
+
? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
|
|
341
|
+
: runtime.locked(room)
|
|
342
|
+
? Promise.resolve(lockedRoom(room))
|
|
343
|
+
: md.history(room, path, query),
|
|
344
|
+
binaryFile: {
|
|
345
|
+
read: (room, path, revision) => !covers(reach, room, path)
|
|
346
|
+
? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
|
|
347
|
+
: runtime.locked(room)
|
|
348
|
+
? Promise.resolve(lockedRoom(room))
|
|
349
|
+
: hand(md.binaryFile.read(room, path, revision)),
|
|
350
|
+
link: (room, path, revision) => !covers(reach, room, path)
|
|
351
|
+
? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
|
|
352
|
+
: runtime.locked(room)
|
|
353
|
+
? Promise.resolve(lockedRoom(room))
|
|
354
|
+
: md.binaryFile.link(room, path, revision),
|
|
355
|
+
show: (room, path, revision) => !covers(reach, room, path)
|
|
356
|
+
? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
|
|
357
|
+
: runtime.locked(room)
|
|
358
|
+
? Promise.resolve(lockedRoom(room))
|
|
359
|
+
: hand(md.binaryFile.show(room, path, revision)),
|
|
360
|
+
attach: (room, file) => !wholly(reach, room)
|
|
361
|
+
? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
|
|
362
|
+
: runtime.locked(room)
|
|
363
|
+
? Promise.resolve(lockedRoom(room))
|
|
364
|
+
: md.binaryFile.attach(room, file),
|
|
365
|
+
async part(file, part) {
|
|
366
|
+
const key = files.get(file);
|
|
367
|
+
if (key === undefined)
|
|
368
|
+
return null;
|
|
369
|
+
const bytes = await md.binaryFile.part(key, part);
|
|
370
|
+
if (bytes === null)
|
|
371
|
+
files.delete(file);
|
|
372
|
+
return bytes;
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
reached: async (room, roomVersion, path) => (path === undefined ? wholly(reach, room) : covers(reach, room, path)) &&
|
|
376
|
+
!runtime.locked(room) &&
|
|
377
|
+
(await md.reached(room, roomVersion, path)),
|
|
243
378
|
log: () => (reach === null ? md.log() : ''),
|
|
379
|
+
cleanUp: () => (reach === null ? md.cleanUp() : Promise.reject(deviceUnreached())),
|
|
244
380
|
roomDraft: {
|
|
245
|
-
list: () => md.roomDraft.list().then((drafts) => draftsWithin(reach, drafts)),
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
381
|
+
list: () => md.roomDraft.list().then((drafts) => unlocked(draftsWithin(reach, drafts))),
|
|
382
|
+
async read(room, path) {
|
|
383
|
+
if (!covers(reach, room, path))
|
|
384
|
+
return null;
|
|
385
|
+
if (runtime.locked(room))
|
|
386
|
+
throw lockedError(room);
|
|
387
|
+
return shownDraft(reach, await md.roomDraft.read(room, path));
|
|
388
|
+
},
|
|
389
|
+
write(room, path, revision, parts, next) {
|
|
390
|
+
try {
|
|
391
|
+
if (reach !== null && carriesFile(parts))
|
|
392
|
+
throw attachRequired();
|
|
393
|
+
const move = readParts(parts).move;
|
|
394
|
+
if (!covers(reach, room, path) || !moves(reach, room, move))
|
|
395
|
+
throw unreached(room);
|
|
396
|
+
if (runtime.locked(room))
|
|
397
|
+
throw lockedError(room);
|
|
398
|
+
if (intoLocked([move]))
|
|
399
|
+
throw lockedError(move.room);
|
|
250
400
|
}
|
|
251
|
-
|
|
401
|
+
catch (error) {
|
|
402
|
+
return Promise.reject(error);
|
|
403
|
+
}
|
|
404
|
+
return md.roomDraft
|
|
405
|
+
.write(room, path, revision, parts, next)
|
|
406
|
+
.then((written) => writeWithin(reach, written));
|
|
252
407
|
},
|
|
253
408
|
async discard(room, path, revision) {
|
|
254
409
|
if (!covers(reach, room, path))
|
|
255
|
-
return
|
|
410
|
+
return staleWrite(null);
|
|
411
|
+
if (runtime.locked(room))
|
|
412
|
+
throw lockedError(room);
|
|
256
413
|
return writeWithin(reach, await md.roomDraft.discard(room, path, revision));
|
|
257
414
|
},
|
|
258
415
|
},
|
|
@@ -260,7 +417,7 @@ export function narrowed(runtime, given) {
|
|
|
260
417
|
list: () => conversation().list(),
|
|
261
418
|
read: (id, options) => conversation().read(id, options),
|
|
262
419
|
create: (input) => conversation().create(input),
|
|
263
|
-
send: (id) => conversation().send(id),
|
|
420
|
+
send: (id, operation, draft) => conversation().send(id, operation, draft),
|
|
264
421
|
answer: (id, answer) => conversation().answer(id, answer),
|
|
265
422
|
interrupt: (id) => conversation().interrupt(id),
|
|
266
423
|
kill: (id) => conversation().kill(id),
|
|
@@ -268,7 +425,8 @@ export function narrowed(runtime, given) {
|
|
|
268
425
|
delete: (id) => conversation().delete(id),
|
|
269
426
|
},
|
|
270
427
|
chatDraft: {
|
|
271
|
-
write: (id, draft) => chatDraft().write(id, draft),
|
|
428
|
+
write: (id, draft, revision) => chatDraft().write(id, draft, revision),
|
|
429
|
+
prepend: (id, draft) => chatDraft().prepend(id, draft),
|
|
272
430
|
discard: (id) => chatDraft().discard(id),
|
|
273
431
|
},
|
|
274
432
|
observe: observing(within),
|
|
@@ -281,5 +439,10 @@ export function narrowed(runtime, given) {
|
|
|
281
439
|
for (const restart of [...open])
|
|
282
440
|
restart();
|
|
283
441
|
},
|
|
442
|
+
facts(next) {
|
|
443
|
+
port = next;
|
|
444
|
+
for (const restart of [...open])
|
|
445
|
+
restart();
|
|
446
|
+
},
|
|
284
447
|
};
|
|
285
448
|
}
|
package/dist/react.d.ts
CHANGED
|
@@ -1,30 +1,42 @@
|
|
|
1
1
|
import type { MdRuntimeClient } from './client.js';
|
|
2
|
-
import type { AppRoom, ConnectionRefusal, SaveResult } from './connection.js';
|
|
3
|
-
import type { ServiceMode } from './adapter.js';
|
|
4
|
-
import type { ConversationEntry, ConversationOptions, ConversationView } from './conversation.js';
|
|
2
|
+
import type { AppRoom, ConnectionRefusal, SavedRoomDraft, SaveResult } from './connection.js';
|
|
3
|
+
import type { Member, ServiceMode } from './adapter.js';
|
|
4
|
+
import type { ConversationAgent, ConversationEntry, ConversationOptions, ConversationView } from './conversation.js';
|
|
5
|
+
import { type RuntimeError } from './failure.js';
|
|
5
6
|
import type { QueueEntry } from './outcome.js';
|
|
6
|
-
import type { DocumentQuery, DocumentResult, GroupQuery, GroupResult } from './query.js';
|
|
7
|
+
import type { DocumentQuery, DocumentResult, GroupQuery, GroupResult, PendingResult } from './query.js';
|
|
8
|
+
import type { Readiness } from './readiness.js';
|
|
7
9
|
import type { RoomDraft, RoomDraftParts } from './room-draft.js';
|
|
8
10
|
import type { SyncStatus } from './status.js';
|
|
9
11
|
type Client = MdRuntimeClient | null;
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export declare function useObserve(client: Client, topic: '
|
|
17
|
-
export declare function useObserve(client: Client, topic: '
|
|
18
|
-
export declare function useObserve(client: Client, topic: '
|
|
19
|
-
export declare function useObserve(client: Client, topic: '
|
|
20
|
-
export declare function useObserve(client: Client, topic: '
|
|
21
|
-
export declare function useObserve(client: Client, topic: '
|
|
22
|
-
export declare function useObserve(client: Client, topic: '
|
|
12
|
+
export type Observed<T> = {
|
|
13
|
+
readonly state: 'loading';
|
|
14
|
+
} | {
|
|
15
|
+
readonly state: 'ready';
|
|
16
|
+
readonly value: T;
|
|
17
|
+
};
|
|
18
|
+
export declare function useObserve(client: Client, topic: 'deployment-facts'): Observed<unknown>;
|
|
19
|
+
export declare function useObserve(client: Client, topic: 'rooms'): Observed<readonly AppRoom[]>;
|
|
20
|
+
export declare function useObserve(client: Client, topic: 'readiness'): Observed<Readiness>;
|
|
21
|
+
export declare function useObserve(client: Client, topic: 'connection-refusal'): Observed<ConnectionRefusal | null>;
|
|
22
|
+
export declare function useObserve(client: Client, topic: 'storage-loss'): Observed<string | null>;
|
|
23
|
+
export declare function useObserve(client: Client, topic: 'sync-status'): Observed<SyncStatus>;
|
|
24
|
+
export declare function useObserve(client: Client, topic: 'service-mode'): Observed<ServiceMode>;
|
|
25
|
+
export declare function useObserve(client: Client, topic: 'member'): Observed<Member | null>;
|
|
26
|
+
export declare function useObserve(client: Client, topic: 'query', query: DocumentQuery): Observed<DocumentResult | PendingResult>;
|
|
27
|
+
export declare function useObserve(client: Client, topic: 'query', query: GroupQuery): Observed<GroupResult | PendingResult>;
|
|
28
|
+
export declare function useObserve(client: Client, topic: 'queue', locator: string): Observed<readonly QueueEntry[]>;
|
|
29
|
+
export declare function useObserve(client: Client, topic: 'room-drafts'): Observed<readonly RoomDraft[]>;
|
|
30
|
+
export declare function useObserve(client: Client, topic: 'presentation'): Observed<unknown>;
|
|
31
|
+
export declare function useObserve(client: Client, topic: 'conversation', conversation: string, options: ConversationOptions): Observed<ConversationView>;
|
|
32
|
+
export declare function useObserve(client: Client, topic: 'conversations'): Observed<readonly ConversationEntry[]>;
|
|
33
|
+
export declare function useObserve(client: Client, topic: 'agents'): Observed<readonly ConversationAgent[]>;
|
|
23
34
|
export type Saving = {
|
|
24
35
|
readonly busy: boolean;
|
|
25
36
|
readonly outcome: SaveResult | null;
|
|
26
|
-
readonly failure:
|
|
27
|
-
save(room: string,
|
|
37
|
+
readonly failure: RuntimeError | null;
|
|
38
|
+
save(room: string, parts: ReadonlyMap<string, RoomDraftParts>): Promise<SaveResult>;
|
|
39
|
+
saveWritten(room: string, roomDrafts: readonly SavedRoomDraft[]): Promise<SaveResult>;
|
|
28
40
|
};
|
|
29
41
|
export declare function useSave(client: MdRuntimeClient): Saving;
|
|
30
42
|
export {};
|
package/dist/react.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"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"}
|
package/dist/react.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { useCallback,
|
|
1
|
+
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
|
2
|
+
import { failed } from './failure.js';
|
|
3
|
+
const LOADING = { state: 'loading' };
|
|
2
4
|
export function useObserve(client, topic, ...input) {
|
|
3
5
|
const key = JSON.stringify([topic, ...input]);
|
|
4
6
|
const [held, setHeld] = useState(null);
|
|
5
7
|
const opened = useRef(input);
|
|
6
8
|
opened.current = input;
|
|
7
|
-
|
|
9
|
+
useLayoutEffect(() => {
|
|
8
10
|
if (client === null)
|
|
9
11
|
return;
|
|
10
12
|
let live = true;
|
|
@@ -18,7 +20,7 @@ export function useObserve(client, topic, ...input) {
|
|
|
18
20
|
observation = open;
|
|
19
21
|
const take = (value) => {
|
|
20
22
|
if (live)
|
|
21
|
-
setHeld({ client, key, value });
|
|
23
|
+
setHeld({ client, key, observed: { state: 'ready', value } });
|
|
22
24
|
};
|
|
23
25
|
take(open.current());
|
|
24
26
|
open.subscribe(take);
|
|
@@ -28,35 +30,25 @@ export function useObserve(client, topic, ...input) {
|
|
|
28
30
|
observation?.close();
|
|
29
31
|
};
|
|
30
32
|
}, [client, key]);
|
|
31
|
-
return held !== null && held.client === client && held.key === key ? held.
|
|
33
|
+
return held !== null && held.client === client && held.key === key ? held.observed : LOADING;
|
|
32
34
|
}
|
|
33
35
|
const IDLE = { busy: false, outcome: null, failure: null };
|
|
34
|
-
async function written(client, room, roomDrafts) {
|
|
35
|
-
const kept = [];
|
|
36
|
-
for (const [path, parts] of roomDrafts) {
|
|
37
|
-
const prior = await client.roomDraft.read(room, path);
|
|
38
|
-
const write = await client.roomDraft.write(room, path, prior?.revision ?? null, parts);
|
|
39
|
-
if (write.outcome !== 'written' || write.roomDraft === null) {
|
|
40
|
-
throw new Error(`the Room Draft at ${path} was not written`);
|
|
41
|
-
}
|
|
42
|
-
kept.push({ path, revision: write.roomDraft.revision });
|
|
43
|
-
}
|
|
44
|
-
return client.save(room, kept);
|
|
45
|
-
}
|
|
46
36
|
export function useSave(client) {
|
|
47
37
|
const [state, setState] = useState(IDLE);
|
|
48
|
-
const
|
|
38
|
+
const settled = useCallback(async (saving) => {
|
|
49
39
|
setState({ busy: true, outcome: null, failure: null });
|
|
50
40
|
try {
|
|
51
|
-
const outcome = await
|
|
41
|
+
const outcome = await saving();
|
|
52
42
|
setState({ busy: false, outcome, failure: null });
|
|
53
43
|
return outcome;
|
|
54
44
|
}
|
|
55
45
|
catch (error) {
|
|
56
|
-
const failure =
|
|
46
|
+
const failure = failed(error);
|
|
57
47
|
setState({ busy: false, outcome: null, failure });
|
|
58
48
|
throw failure;
|
|
59
49
|
}
|
|
60
|
-
}, [
|
|
61
|
-
|
|
50
|
+
}, []);
|
|
51
|
+
const save = useCallback((room, parts) => settled(() => client.saveParts(room, [...parts].map(([path, one]) => ({ path, parts: one })))), [client, settled]);
|
|
52
|
+
const saveWritten = useCallback((room, roomDrafts) => settled(() => client.save(room, roomDrafts)), [client, settled]);
|
|
53
|
+
return { ...state, save, saveWritten };
|
|
62
54
|
}
|