@ai-ecoverse/slicc-agent 5.5.4 → 5.7.0
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 +10 -4
- package/dist/agent.js +4 -0
- package/dist/agent.js.map +2 -2
- package/dist/approvals/index.js +87 -45
- package/dist/approvals/index.js.map +2 -2
- package/dist/approvals/judge.js +7 -1
- package/dist/approvals/judge.js.map +2 -2
- package/dist/approvals/record.js +7 -2
- package/dist/approvals/record.js.map +2 -2
- package/dist/approvals/sudo-client.js +112 -0
- package/dist/approvals/sudo-client.js.map +7 -0
- package/dist/approvals/sudo.js +166 -0
- package/dist/approvals/sudo.js.map +7 -0
- package/dist/guests/attribution.js +19 -0
- package/dist/guests/attribution.js.map +2 -2
- package/dist/guests/client.js +4 -3
- package/dist/guests/client.js.map +2 -2
- package/dist/guests/index.js +116 -63
- package/dist/guests/index.js.map +2 -2
- package/dist/guests/review.js +262 -0
- package/dist/guests/review.js.map +7 -0
- package/dist/guests/seats.js.map +2 -2
- package/dist/guests/serve.js +13 -13
- package/dist/guests/serve.js.map +2 -2
- package/dist/host.js +48 -14
- package/dist/host.js.map +2 -2
- package/dist/index.js.map +2 -2
- package/dist/owner.js +22 -1
- package/dist/owner.js.map +2 -2
- package/dist/page.js +2 -0
- package/dist/page.js.map +2 -2
- package/dist/services.js +10 -0
- package/dist/services.js.map +2 -2
- package/dist/serving.js +2 -0
- package/dist/serving.js.map +2 -2
- package/dist/spectrum/agent-port.js +1 -1
- package/dist/spectrum/agent-port.js.map +2 -2
- package/dist/spectrum/messages.js +50 -6
- package/dist/spectrum/messages.js.map +2 -2
- package/dist/spectrum/proposals.js +7 -3
- package/dist/spectrum/proposals.js.map +2 -2
- package/dist/types/agent.d.ts +2 -0
- package/dist/types/approvals/index.d.ts +11 -3
- package/dist/types/approvals/judge.d.ts +10 -0
- package/dist/types/approvals/record.d.ts +6 -0
- package/dist/types/approvals/sudo-client.d.ts +12 -0
- package/dist/types/approvals/sudo.d.ts +52 -0
- package/dist/types/guests/attribution.d.ts +16 -0
- package/dist/types/guests/index.d.ts +7 -4
- package/dist/types/guests/review.d.ts +68 -0
- package/dist/types/guests/seats.d.ts +12 -1
- package/dist/types/guests/serve.d.ts +3 -3
- package/dist/types/host.d.ts +5 -2
- package/dist/types/index.d.ts +2 -1
- package/dist/types/owner.d.ts +4 -0
- package/dist/types/page.d.ts +2 -0
- package/dist/types/services.d.ts +3 -1
- package/dist/types/serving.d.ts +2 -0
- package/dist/types/spectrum/messages.d.ts +2 -0
- package/dist/types/spectrum/proposals.d.ts +2 -1
- package/dist/worker.js +7 -1
- package/dist/worker.js.map +2 -2
- package/package.json +2 -2
package/dist/guests/index.js
CHANGED
|
@@ -2,9 +2,22 @@ import { replicatedState } from "@earendil-works/chord";
|
|
|
2
2
|
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
3
3
|
import { InboxDoc } from "@earendil-works/pi-durable";
|
|
4
4
|
import { scrubText } from "../secrets/scrub.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ASKED_KIND,
|
|
7
|
+
GUEST_INPUT_KIND,
|
|
8
|
+
guestContent
|
|
9
|
+
} from "./attribution.js";
|
|
6
10
|
import { GUEST_REQUEST } from "./guard.js";
|
|
7
11
|
import { DEFAULT_GUEST_POLICY } from "./policy.js";
|
|
12
|
+
import {
|
|
13
|
+
endReview,
|
|
14
|
+
GuestReviewsDoc,
|
|
15
|
+
KEPT_SUBMISSIONS,
|
|
16
|
+
liveReviews,
|
|
17
|
+
MAX_QUEUED,
|
|
18
|
+
pruneReviews,
|
|
19
|
+
told
|
|
20
|
+
} from "./review.js";
|
|
8
21
|
import {
|
|
9
22
|
checkDraft,
|
|
10
23
|
cleanLabel,
|
|
@@ -14,7 +27,6 @@ import {
|
|
|
14
27
|
} from "./seats.js";
|
|
15
28
|
const MAX_PROPOSAL = 32e3;
|
|
16
29
|
const MAX_REQUEST = 128;
|
|
17
|
-
const KEPT_DELIVERED = 100;
|
|
18
30
|
const LONGEST_TIMER = 2 ** 31 - 1;
|
|
19
31
|
const copy = (value) => JSON.parse(JSON.stringify(value));
|
|
20
32
|
const unanswered = (reason) => ({
|
|
@@ -40,10 +52,10 @@ async function withdrawQueued(harness, agents, record, using) {
|
|
|
40
52
|
);
|
|
41
53
|
for (const id of queued) await harness.abortSubmission(id, using, conversation.id);
|
|
42
54
|
}
|
|
43
|
-
async function deliver(record, text,
|
|
44
|
-
const conversation = await agents.conversation(record.agentId, using);
|
|
55
|
+
async function deliver(record, text, requestId, options, using) {
|
|
56
|
+
const conversation = await options.agents.conversation(record.agentId, using);
|
|
45
57
|
if (!conversation) return unanswered("gone");
|
|
46
|
-
const data = { seat: record.id, name: record.label, approval };
|
|
58
|
+
const data = { seat: record.id, name: record.label, approval: null };
|
|
47
59
|
const submitted = await conversation.submit(
|
|
48
60
|
{
|
|
49
61
|
type: "input",
|
|
@@ -54,7 +66,16 @@ async function deliver(record, text, approval, requestId, agents, using) {
|
|
|
54
66
|
},
|
|
55
67
|
using
|
|
56
68
|
);
|
|
57
|
-
|
|
69
|
+
const id = String(submitted.id);
|
|
70
|
+
await options.harness.commit(async (tx) => {
|
|
71
|
+
const kept = (await tx.doc(GuestsDoc)).seats[record.id];
|
|
72
|
+
if (!kept || kept.submissions?.some((entry) => entry.id === id)) return;
|
|
73
|
+
kept.submissions = [
|
|
74
|
+
...kept.submissions ?? [],
|
|
75
|
+
{ id, approval: null, tools: record.gates.tools }
|
|
76
|
+
].slice(-KEPT_SUBMISSIONS);
|
|
77
|
+
}, using);
|
|
78
|
+
return { state: "submitted", submissionId: id, reason: null };
|
|
58
79
|
}
|
|
59
80
|
async function prepare(record, request) {
|
|
60
81
|
const text = typeof request?.text === "string" ? request.text.trim() : "";
|
|
@@ -63,24 +84,57 @@ async function prepare(record, request) {
|
|
|
63
84
|
const asked = typeof request.requestId === "string" && request.requestId && request.requestId.length <= MAX_REQUEST ? request.requestId : crypto.randomUUID();
|
|
64
85
|
return { text: await scrubText(text), requestId: `${GUEST_REQUEST}${record.id}:${asked}` };
|
|
65
86
|
}
|
|
66
|
-
async function place(record, prepared, options, using) {
|
|
87
|
+
async function place(record, prepared, options, using, at) {
|
|
67
88
|
const gate = record.gates.messages;
|
|
68
|
-
if (gate === "off")
|
|
69
|
-
|
|
70
|
-
if (!options.approvals) return unanswered("unavailable");
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
89
|
+
if (gate === "off") return deliver(record, prepared.text, prepared.requestId, options, using);
|
|
90
|
+
const { reviews } = options;
|
|
91
|
+
if (!reviews || !options.approvals) return unanswered("unavailable");
|
|
92
|
+
const conversation = await options.agents.conversation(record.agentId, using);
|
|
93
|
+
if (!conversation) return unanswered("gone");
|
|
94
|
+
return options.harness.commit(async (tx) => {
|
|
95
|
+
const doc = await tx.doc(GuestReviewsDoc);
|
|
96
|
+
const again = Object.values(doc.reviews).find(
|
|
97
|
+
(review) => review.seat === record.id && review.request === prepared.requestId
|
|
98
|
+
);
|
|
99
|
+
if (again) return { state: "queued", submissionId: null, review: again.id, reason: null };
|
|
100
|
+
if (liveReviews(doc, record.id).length > MAX_QUEUED) return unanswered("overflow");
|
|
101
|
+
const id = `apr-${crypto.randomUUID()}`;
|
|
102
|
+
const task = await tx.createTask(
|
|
103
|
+
reviews.task,
|
|
104
|
+
{ review: id },
|
|
105
|
+
{ ownership: { kind: "conversation" }, conversationId: conversation.id, background: true }
|
|
106
|
+
);
|
|
107
|
+
doc.reviews[id] = {
|
|
108
|
+
id,
|
|
74
109
|
seat: record.id,
|
|
75
110
|
agentId: record.agentId,
|
|
76
|
-
|
|
111
|
+
label: record.label,
|
|
112
|
+
text: prepared.text,
|
|
113
|
+
request: prepared.requestId,
|
|
77
114
|
tier: gate,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
115
|
+
tools: record.gates.tools,
|
|
116
|
+
n: doc.next,
|
|
117
|
+
task: Number(task),
|
|
118
|
+
state: "queued",
|
|
119
|
+
reason: null,
|
|
120
|
+
createdAt: at,
|
|
121
|
+
decidedAt: null,
|
|
122
|
+
submissionId: null,
|
|
123
|
+
ended: false,
|
|
124
|
+
conversation: Number(conversation.id)
|
|
125
|
+
};
|
|
126
|
+
doc.next += 1;
|
|
127
|
+
pruneReviews(doc);
|
|
128
|
+
const asked = {
|
|
129
|
+
seat: record.id,
|
|
130
|
+
name: record.label,
|
|
131
|
+
review: id,
|
|
132
|
+
text: prepared.text,
|
|
133
|
+
at
|
|
134
|
+
};
|
|
135
|
+
await tx.appendEntry(conversation.id, { kind: ASKED_KIND, data: { ...asked } });
|
|
136
|
+
return { state: "queued", submissionId: null, review: id, reason: null };
|
|
137
|
+
}, using);
|
|
84
138
|
}
|
|
85
139
|
function keyedLine() {
|
|
86
140
|
const locks = /* @__PURE__ */ new Map();
|
|
@@ -94,7 +148,7 @@ function keyedLine() {
|
|
|
94
148
|
return next;
|
|
95
149
|
};
|
|
96
150
|
}
|
|
97
|
-
const pendingIn = (approvals) => (id) => (approvals?.state.value ?? []).filter((item) => item.seat === id && item.state === "pending").length;
|
|
151
|
+
const pendingIn = (approvals, reviews) => (id) => (approvals?.state.value ?? []).filter((item) => item.seat === id && item.state === "pending").length + liveReviews(reviews.value, id).filter((review) => review.state === "queued").length;
|
|
98
152
|
function shownSeats(records, links, at, pendingOf) {
|
|
99
153
|
return Object.values(records).filter((record) => liveSeat(record, at)).sort((a, b) => a.createdAt - b.createdAt || a.id.localeCompare(b.id)).map((record) => ({
|
|
100
154
|
id: record.id,
|
|
@@ -112,27 +166,41 @@ async function createGuests(options) {
|
|
|
112
166
|
const now = options.now ?? Date.now;
|
|
113
167
|
const context = options.context ?? BACKGROUND_CONTEXT;
|
|
114
168
|
const guestPolicy = () => options.policy?.current().guests ?? DEFAULT_GUEST_POLICY;
|
|
115
|
-
const records = await harness.commit(
|
|
116
|
-
|
|
169
|
+
const records = await harness.commit(async (tx) => {
|
|
170
|
+
await tx.doc(GuestReviewsDoc);
|
|
171
|
+
return copy((await tx.doc(GuestsDoc)).seats);
|
|
172
|
+
}, context);
|
|
173
|
+
const seatsDoc = await harness.documentState(GuestsDoc, context);
|
|
174
|
+
const reviews = await harness.documentState(
|
|
175
|
+
GuestReviewsDoc,
|
|
117
176
|
context
|
|
118
177
|
);
|
|
119
178
|
const links = /* @__PURE__ */ new Map();
|
|
120
179
|
const ended = /* @__PURE__ */ new Set();
|
|
121
|
-
const gone = /* @__PURE__ */ new
|
|
122
|
-
const shown = () => shownSeats(records, links, now(), pendingIn(options.approvals));
|
|
180
|
+
const gone = /* @__PURE__ */ new Map();
|
|
181
|
+
const shown = () => shownSeats(records, links, now(), pendingIn(options.approvals, reviews));
|
|
123
182
|
const seats = replicatedState(shown());
|
|
124
183
|
let timer;
|
|
125
184
|
const locked = keyedLine();
|
|
126
185
|
const cleanup = (record, using) => locked(record.id, async () => {
|
|
127
186
|
await withdrawQueued(harness, agents, record, using);
|
|
187
|
+
await harness.commit(async (tx) => {
|
|
188
|
+
const doc = await tx.doc(GuestReviewsDoc);
|
|
189
|
+
for (const review of liveReviews(doc, record.id))
|
|
190
|
+
if (review.state === "queued") {
|
|
191
|
+
endReview(review, "withdrawn", "revoked", now());
|
|
192
|
+
await told(tx, review);
|
|
193
|
+
}
|
|
194
|
+
}, using);
|
|
128
195
|
await options.approvals?.withdrawSeat(record.id, using);
|
|
129
196
|
});
|
|
130
197
|
const end = (id, using = context) => {
|
|
131
|
-
const
|
|
132
|
-
if (
|
|
133
|
-
|
|
198
|
+
const going = gone.get(id);
|
|
199
|
+
if (going !== void 0) return going;
|
|
200
|
+
const cleaning = cleanup(records[id], using);
|
|
201
|
+
gone.set(id, cleaning);
|
|
134
202
|
for (const listener of [...ended]) listener(id);
|
|
135
|
-
return
|
|
203
|
+
return cleaning;
|
|
136
204
|
};
|
|
137
205
|
const publish = () => {
|
|
138
206
|
const at = now();
|
|
@@ -148,51 +216,32 @@ async function createGuests(options) {
|
|
|
148
216
|
timer = setTimeout(publish, Math.min(Math.max(0, next - at), LONGEST_TIMER));
|
|
149
217
|
};
|
|
150
218
|
publish();
|
|
219
|
+
const offSeats = seatsDoc.subscribe((value) => {
|
|
220
|
+
for (const id of Object.keys(records)) delete records[id];
|
|
221
|
+
Object.assign(records, copy(value.seats));
|
|
222
|
+
publish();
|
|
223
|
+
});
|
|
151
224
|
const write = (change) => harness.commit(async (tx) => {
|
|
152
225
|
const doc = await tx.doc(GuestsDoc);
|
|
153
226
|
const changed = change(doc.seats);
|
|
154
227
|
return changed ? copy(changed) : void 0;
|
|
155
228
|
}, context);
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const requestId = `${GUEST_REQUEST}${record.id}:${item.id}`;
|
|
165
|
-
const sent = await deliver(record, text, item.id, requestId, agents, context);
|
|
166
|
-
if (sent.state !== "submitted") return;
|
|
167
|
-
const changed = await write((all) => {
|
|
168
|
-
const kept = all[record.id];
|
|
169
|
-
if (!kept) return void 0;
|
|
170
|
-
kept.delivered = [...kept.delivered ?? [], item.id].slice(-KEPT_DELIVERED);
|
|
171
|
-
return kept;
|
|
172
|
-
});
|
|
173
|
-
if (changed) records[record.id] = changed;
|
|
174
|
-
};
|
|
175
|
-
const approved = (items) => {
|
|
176
|
-
for (const item of items) {
|
|
177
|
-
if (item.kind !== "guest-message" || item.state !== "approved" || !item.seat) continue;
|
|
178
|
-
const record = records[item.seat];
|
|
179
|
-
if (!record || !liveSeat(record, now()) || delivering.has(item.id)) continue;
|
|
180
|
-
if (record.delivered?.includes(item.id)) continue;
|
|
181
|
-
delivering.add(item.id);
|
|
182
|
-
void deliverOne(record.id, item).catch(() => void 0);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
const offPending = options.approvals?.state.subscribe((items) => {
|
|
186
|
-
seats.replace(context, shown());
|
|
187
|
-
approved(items);
|
|
229
|
+
const recount = () => seats.replace(context, shown());
|
|
230
|
+
const offPending = options.approvals?.state.subscribe(recount);
|
|
231
|
+
const offReviews = reviews.subscribe(recount);
|
|
232
|
+
options.reviews?.bind({
|
|
233
|
+
approvals: options.approvals,
|
|
234
|
+
agents,
|
|
235
|
+
line: locked,
|
|
236
|
+
alwaysAllowed: () => guestPolicy().always.messages
|
|
188
237
|
});
|
|
189
|
-
approved(options.approvals?.state.value ?? []);
|
|
190
238
|
const current = (guest) => {
|
|
191
239
|
const record = records[guest.id];
|
|
192
240
|
return record && liveSeat(record, now()) && record.epoch === guest.epoch ? record : void 0;
|
|
193
241
|
};
|
|
194
242
|
return {
|
|
195
243
|
seats,
|
|
244
|
+
reviews,
|
|
196
245
|
async open(draft, using) {
|
|
197
246
|
const at = now();
|
|
198
247
|
const problem = checkDraft(draft, at, guestPolicy());
|
|
@@ -267,13 +316,17 @@ async function createGuests(options) {
|
|
|
267
316
|
if ("state" in prepared) return prepared;
|
|
268
317
|
return locked(record.id, async () => {
|
|
269
318
|
const still = current(guest);
|
|
270
|
-
return still ? place(still, prepared, options, using) : unanswered("revoked");
|
|
319
|
+
return still ? place(still, prepared, options, using, now()) : unanswered("revoked");
|
|
271
320
|
});
|
|
272
321
|
},
|
|
273
322
|
policy: guestPolicy,
|
|
274
323
|
dispose() {
|
|
275
324
|
if (timer) clearTimeout(timer);
|
|
276
325
|
offPending?.();
|
|
326
|
+
offReviews();
|
|
327
|
+
offSeats();
|
|
328
|
+
seatsDoc.dispose();
|
|
329
|
+
reviews.dispose();
|
|
277
330
|
ended.clear();
|
|
278
331
|
}
|
|
279
332
|
};
|
package/dist/guests/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/guests/index.ts"],
|
|
4
|
-
"sourcesContent": ["import { type Context, type ReplicatedState, replicatedState } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport { type Harness, InboxDoc } from '@earendil-works/pi-durable';\nimport type { Agents } from '../agents.ts';\nimport type { Approval, Approvals } from '../approvals/index.ts';\nimport type { ApprovalPolicies } from '../approvals/policy.ts';\nimport { scrubText } from '../secrets/scrub.ts';\nimport type { GuestPrincipal } from '../serving.ts';\nimport { GUEST_INPUT_KIND, type GuestInput, guestContent } from './attribution.ts';\nimport { GUEST_REQUEST } from './guard.ts';\nimport { DEFAULT_GUEST_POLICY, type GuestPolicy } from './policy.ts';\nimport {\n checkDraft,\n cleanLabel,\n type GuestDraft,\n type GuestSeat,\n GuestsDoc,\n liveSeat,\n MAX_SEAT_MS,\n type SeatRecord,\n} from './seats.ts';\n\nexport type GuestOpened = { seat: GuestSeat; error: null } | { seat: null; error: string };\n\nexport type Proposed =\n | { state: 'submitted'; submissionId: string; reason: null }\n | { state: 'pending'; submissionId: null; approval: string; reason: null }\n | { state: 'unanswered'; submissionId: null; reason: string };\n\nexport interface ProposeRequest {\n text: string;\n requestId: string | null;\n}\n\nexport interface Guests {\n readonly seats: ReplicatedState<GuestSeat[]>;\n open(draft: GuestDraft, context: Context): Promise<GuestOpened>;\n revoke(id: string, context: Context): Promise<{ revoked: boolean }>;\n admit(id: string, label: string): GuestPrincipal | null;\n record(id: string): SeatRecord | undefined;\n current(guest: GuestPrincipal): SeatRecord | undefined;\n joined(id: string): () => void;\n onEnd(listener: (id: string) => void): () => void;\n propose(guest: GuestPrincipal, request: ProposeRequest, context: Context): Promise<Proposed>;\n policy(): GuestPolicy;\n dispose(): void;\n}\n\nexport interface GuestsOptions {\n harness: Pick<Harness, 'commit' | 'abortSubmission'>;\n agents: Pick<Agents, 'state' | 'conversation'>;\n approvals?: Pick<Approvals, 'state' | 'request' | 'withdrawSeat'>;\n policy?: Pick<ApprovalPolicies, 'current'>;\n now?: () => number;\n context?: Context;\n}\n\nexport const MAX_PROPOSAL = 32_000;\nconst MAX_REQUEST = 128;\nconst KEPT_DELIVERED = 100;\nconst LONGEST_TIMER = 2 ** 31 - 1;\n\nconst copy = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nconst unanswered = (reason: string): Proposed => ({\n state: 'unanswered',\n submissionId: null,\n reason,\n});\n\nfunction prune(all: Record<string, SeatRecord>, at: number) {\n for (const [id, record] of Object.entries(all)) {\n const over = record.revoked ?? record.expiresAt;\n if (over !== null && at - over > MAX_SEAT_MS) delete all[id];\n }\n}\n\nasync function withdrawQueued(\n harness: GuestsOptions['harness'],\n agents: GuestsOptions['agents'],\n record: SeatRecord,\n using: Context\n) {\n const conversation = await agents.conversation(record.agentId, using);\n if (!conversation) return;\n const queued = await harness.commit(\n async (tx) =>\n (await tx.doc(InboxDoc, conversation.id)).items\n .filter((item) => {\n const entry = item.entry as { kind?: string; data?: { seat?: unknown } } | undefined;\n return entry?.kind === GUEST_INPUT_KIND && entry.data?.seat === record.id;\n })\n .map((item) => item.id),\n using\n );\n for (const id of queued) await harness.abortSubmission(id, using, conversation.id);\n}\n\nasync function deliver(\n record: SeatRecord,\n text: string,\n approval: string | null,\n requestId: string,\n agents: GuestsOptions['agents'],\n using: Context\n): Promise<Proposed> {\n const conversation = await agents.conversation(record.agentId, using);\n if (!conversation) return unanswered('gone');\n const data: GuestInput = { seat: record.id, name: record.label, approval };\n const submitted = await conversation.submit(\n {\n type: 'input',\n content: guestContent(record.label, text),\n whenBusy: 'followUp',\n entry: { kind: GUEST_INPUT_KIND, data: { ...data } },\n requestId,\n },\n using\n );\n return { state: 'submitted', submissionId: String(submitted.id), reason: null };\n}\n\ntype Prepared = { text: string; requestId: string };\n\nasync function prepare(record: SeatRecord, request: ProposeRequest): Promise<Prepared | Proposed> {\n const text = typeof request?.text === 'string' ? request.text.trim() : '';\n if (!text) return unanswered('empty');\n if (text.length > MAX_PROPOSAL) return unanswered('too-long');\n const asked =\n typeof request.requestId === 'string' &&\n request.requestId &&\n request.requestId.length <= MAX_REQUEST\n ? request.requestId\n : crypto.randomUUID();\n return { text: await scrubText(text), requestId: `${GUEST_REQUEST}${record.id}:${asked}` };\n}\n\nasync function place(\n record: SeatRecord,\n prepared: Prepared,\n options: GuestsOptions,\n using: Context\n): Promise<Proposed> {\n const gate = record.gates.messages;\n if (gate === 'off')\n return deliver(record, prepared.text, null, prepared.requestId, options.agents, using);\n if (!options.approvals) return unanswered('unavailable');\n const item = await options.approvals.request(\n {\n kind: 'guest-message',\n seat: record.id,\n agentId: record.agentId,\n requester: { kind: 'guest', name: record.label, seat: record.id },\n tier: gate,\n detail: { text: prepared.text, attachments: [] },\n },\n using\n );\n if (item.state !== 'pending') return unanswered(item.reason ?? item.state);\n return { state: 'pending', submissionId: null, approval: item.id, reason: null };\n}\n\nfunction keyedLine() {\n const locks = new Map<string, Promise<unknown>>();\n return <T>(id: string, run: () => Promise<T>): Promise<T> => {\n const next = (locks.get(id) ?? Promise.resolve()).then(run, run);\n const settled = next.catch(() => undefined);\n locks.set(id, settled);\n void settled.then(() => {\n if (locks.get(id) === settled) locks.delete(id);\n });\n return next;\n };\n}\n\nconst pendingIn =\n (approvals: GuestsOptions['approvals']) =>\n (id: string): number =>\n (approvals?.state.value ?? []).filter((item) => item.seat === id && item.state === 'pending')\n .length;\n\nfunction shownSeats(\n records: Record<string, SeatRecord>,\n links: ReadonlyMap<string, number>,\n at: number,\n pendingOf: (id: string) => number\n): GuestSeat[] {\n return Object.values(records)\n .filter((record) => liveSeat(record, at))\n .sort((a, b) => a.createdAt - b.createdAt || a.id.localeCompare(b.id))\n .map((record) => ({\n id: record.id,\n label: record.label,\n agentId: record.agentId,\n createdAt: record.createdAt,\n expiresAt: record.expiresAt,\n gates: { ...record.gates },\n connected: (links.get(record.id) ?? 0) > 0,\n pending: pendingOf(record.id),\n }));\n}\n\nexport async function createGuests(options: GuestsOptions): Promise<Guests> {\n const { harness, agents } = options;\n const now = options.now ?? Date.now;\n const context = options.context ?? BACKGROUND_CONTEXT;\n const guestPolicy = () => options.policy?.current().guests ?? DEFAULT_GUEST_POLICY;\n const records: Record<string, SeatRecord> = await harness.commit(\n async (tx) => copy((await tx.doc(GuestsDoc)).seats),\n context\n );\n const links = new Map<string, number>();\n const ended = new Set<(id: string) => void>();\n const gone = new Set<string>();\n const shown = () => shownSeats(records, links, now(), pendingIn(options.approvals));\n const seats = replicatedState<GuestSeat[]>(shown());\n let timer: ReturnType<typeof setTimeout> | undefined;\n const locked = keyedLine();\n const cleanup = (record: SeatRecord, using: Context) =>\n locked(record.id, async () => {\n await withdrawQueued(harness, agents, record, using);\n await options.approvals?.withdrawSeat(record.id, using);\n });\n const end = (id: string, using: Context = context): Promise<void> => {\n const record = records[id];\n if (gone.has(id) || !record) return Promise.resolve();\n gone.add(id);\n for (const listener of [...ended]) listener(id);\n return cleanup(record, using);\n };\n const publish = () => {\n const at = now();\n for (const record of Object.values(records))\n if (!liveSeat(record, at)) void end(record.id).catch(() => undefined);\n seats.replace(context, shown());\n if (timer) clearTimeout(timer);\n timer = undefined;\n const next = Math.min(\n ...Object.values(records)\n .filter((record) => liveSeat(record, at) && record.expiresAt !== null)\n .map((record) => record.expiresAt as number)\n );\n if (Number.isFinite(next))\n timer = setTimeout(publish, Math.min(Math.max(0, next - at), LONGEST_TIMER));\n };\n publish();\n const write = (change: (seats: Record<string, SeatRecord>) => SeatRecord | undefined) =>\n harness.commit(async (tx) => {\n const doc = await tx.doc(GuestsDoc);\n const changed = change(doc.seats);\n return changed ? copy(changed) : undefined;\n }, context);\n const delivering = new Set<string>();\n const deliverOne = (seat: string, item: Approval) =>\n locked(seat, async () => {\n const record = records[seat];\n if (!record || !liveSeat(record, now())) return;\n await deliverLocked(record, item);\n });\n const deliverLocked = async (record: SeatRecord, item: Approval) => {\n const text = (item.detail as { text?: string }).text ?? '';\n const requestId = `${GUEST_REQUEST}${record.id}:${item.id}`;\n const sent = await deliver(record, text, item.id, requestId, agents, context);\n if (sent.state !== 'submitted') return;\n const changed = await write((all) => {\n const kept = all[record.id];\n if (!kept) return undefined;\n kept.delivered = [...(kept.delivered ?? []), item.id].slice(-KEPT_DELIVERED);\n return kept;\n });\n if (changed) records[record.id] = changed;\n };\n const approved = (items: readonly Approval[]) => {\n for (const item of items) {\n if (item.kind !== 'guest-message' || item.state !== 'approved' || !item.seat) continue;\n const record = records[item.seat];\n if (!record || !liveSeat(record, now()) || delivering.has(item.id)) continue;\n if (record.delivered?.includes(item.id)) continue;\n delivering.add(item.id);\n void deliverOne(record.id, item).catch(() => undefined);\n }\n };\n const offPending = options.approvals?.state.subscribe((items) => {\n seats.replace(context, shown());\n approved(items);\n });\n approved(options.approvals?.state.value ?? []);\n const current = (guest: GuestPrincipal) => {\n const record = records[guest.id];\n return record && liveSeat(record, now()) && record.epoch === guest.epoch ? record : undefined;\n };\n return {\n seats,\n async open(draft, using) {\n const at = now();\n const problem = checkDraft(draft, at, guestPolicy());\n if (problem) return { seat: null, error: problem };\n if (!agents.state().cones[draft.agentId])\n return { seat: null, error: `There is no cone ${draft.agentId}.` };\n const record: SeatRecord = {\n id: draft.id,\n label: cleanLabel(draft.label),\n agentId: draft.agentId,\n expiresAt: draft.expiresAt,\n gates: { messages: draft.gates.messages, tools: draft.gates.tools },\n createdAt: at,\n epoch: 1,\n revoked: null,\n };\n const kept = await harness.commit(async (tx) => {\n const doc = await tx.doc(GuestsDoc);\n if (doc.seats[record.id]) return null;\n prune(doc.seats, at);\n doc.seats[record.id] = copy(record);\n return copy(doc.seats);\n }, using);\n if (!kept) return { seat: null, error: 'That guest seat already exists.' };\n for (const id of Object.keys(records)) delete records[id];\n Object.assign(records, kept);\n publish();\n return { seat: shown().find((seat) => seat.id === record.id) as GuestSeat, error: null };\n },\n async revoke(id, using) {\n const at = now();\n const changed = await write((all) => {\n const record = all[id];\n if (!record || record.revoked !== null) return undefined;\n record.revoked = at;\n record.epoch += 1;\n return record;\n });\n if (!changed) return { revoked: false };\n records[id] = changed;\n const ending = end(id, using);\n publish();\n await ending;\n return { revoked: true };\n },\n admit(id, label) {\n const record = records[id];\n if (!record || !liveSeat(record, now()) || record.label !== cleanLabel(label)) return null;\n return { id: record.id, name: record.label, epoch: record.epoch };\n },\n record: (id) => records[id],\n current,\n joined(id) {\n links.set(id, (links.get(id) ?? 0) + 1);\n seats.replace(context, shown());\n let left = false;\n return () => {\n if (left) return;\n left = true;\n links.set(id, (links.get(id) ?? 1) - 1);\n seats.replace(context, shown());\n };\n },\n onEnd(listener) {\n ended.add(listener);\n return () => ended.delete(listener);\n },\n async propose(guest, request, using) {\n const record = current(guest);\n if (!record) return unanswered('revoked');\n const prepared = await prepare(record, request);\n if ('state' in prepared) return prepared;\n return locked(record.id, async () => {\n const still = current(guest);\n return still ? place(still, prepared, options, using) : unanswered('revoked');\n });\n },\n policy: guestPolicy,\n dispose() {\n if (timer) clearTimeout(timer);\n offPending?.();\n ended.clear();\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAA6C,uBAAuB;AACpE,SAAS,0BAA0B;AACnC,
|
|
4
|
+
"sourcesContent": ["import { type Context, type ReplicatedState, replicatedState } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport { type DocumentState, type Harness, InboxDoc } from '@earendil-works/pi-durable';\nimport type { Agents } from '../agents.ts';\nimport type { Approvals } from '../approvals/index.ts';\nimport type { ApprovalPolicies } from '../approvals/policy.ts';\nimport { scrubText } from '../secrets/scrub.ts';\nimport type { GuestPrincipal } from '../serving.ts';\nimport {\n ASKED_KIND,\n GUEST_INPUT_KIND,\n type GuestAsked,\n type GuestInput,\n guestContent,\n} from './attribution.ts';\nimport { GUEST_REQUEST } from './guard.ts';\nimport { DEFAULT_GUEST_POLICY, type GuestPolicy } from './policy.ts';\nimport {\n endReview,\n type GuestReviews,\n GuestReviewsDoc,\n KEPT_SUBMISSIONS,\n liveReviews,\n MAX_QUEUED,\n pruneReviews,\n type ReviewsState,\n told,\n} from './review.ts';\nimport {\n checkDraft,\n cleanLabel,\n type GuestDraft,\n type GuestSeat,\n GuestsDoc,\n type GuestsState,\n liveSeat,\n MAX_SEAT_MS,\n type SeatRecord,\n} from './seats.ts';\n\nexport type GuestOpened = { seat: GuestSeat; error: null } | { seat: null; error: string };\n\nexport type Proposed =\n | { state: 'submitted'; submissionId: string; reason: null }\n | { state: 'queued'; submissionId: null; review: string; reason: null }\n | { state: 'unanswered'; submissionId: null; reason: string };\n\nexport interface ProposeRequest {\n text: string;\n requestId: string | null;\n}\n\nexport interface Guests {\n readonly seats: ReplicatedState<GuestSeat[]>;\n readonly reviews: ReplicatedState<ReviewsState | null>;\n open(draft: GuestDraft, context: Context): Promise<GuestOpened>;\n revoke(id: string, context: Context): Promise<{ revoked: boolean }>;\n admit(id: string, label: string): GuestPrincipal | null;\n record(id: string): SeatRecord | undefined;\n current(guest: GuestPrincipal): SeatRecord | undefined;\n joined(id: string): () => void;\n onEnd(listener: (id: string) => void): () => void;\n propose(guest: GuestPrincipal, request: ProposeRequest, context: Context): Promise<Proposed>;\n policy(): GuestPolicy;\n dispose(): void;\n}\n\nexport interface GuestsOptions {\n harness: Pick<Harness, 'commit' | 'abortSubmission' | 'documentState'>;\n agents: Pick<Agents, 'state' | 'conversation'>;\n approvals?: Pick<Approvals, 'state' | 'request' | 'withdrawSeat' | 'expire' | 'withdraw'>;\n policy?: Pick<ApprovalPolicies, 'current'>;\n reviews?: Pick<GuestReviews, 'task' | 'bind'>;\n now?: () => number;\n context?: Context;\n}\n\nexport const MAX_PROPOSAL = 32_000;\nconst MAX_REQUEST = 128;\nconst LONGEST_TIMER = 2 ** 31 - 1;\n\nconst copy = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nconst unanswered = (reason: string): Proposed => ({\n state: 'unanswered',\n submissionId: null,\n reason,\n});\n\nfunction prune(all: Record<string, SeatRecord>, at: number) {\n for (const [id, record] of Object.entries(all)) {\n const over = record.revoked ?? record.expiresAt;\n if (over !== null && at - over > MAX_SEAT_MS) delete all[id];\n }\n}\n\nasync function withdrawQueued(\n harness: GuestsOptions['harness'],\n agents: GuestsOptions['agents'],\n record: SeatRecord,\n using: Context\n) {\n const conversation = await agents.conversation(record.agentId, using);\n if (!conversation) return;\n const queued = await harness.commit(\n async (tx) =>\n (await tx.doc(InboxDoc, conversation.id)).items\n .filter((item) => {\n const entry = item.entry as { kind?: string; data?: { seat?: unknown } } | undefined;\n return entry?.kind === GUEST_INPUT_KIND && entry.data?.seat === record.id;\n })\n .map((item) => item.id),\n using\n );\n for (const id of queued) await harness.abortSubmission(id, using, conversation.id);\n}\n\nasync function deliver(\n record: SeatRecord,\n text: string,\n requestId: string,\n options: GuestsOptions,\n using: Context\n): Promise<Proposed> {\n const conversation = await options.agents.conversation(record.agentId, using);\n if (!conversation) return unanswered('gone');\n const data: GuestInput = { seat: record.id, name: record.label, approval: null };\n const submitted = await conversation.submit(\n {\n type: 'input',\n content: guestContent(record.label, text),\n whenBusy: 'followUp',\n entry: { kind: GUEST_INPUT_KIND, data: { ...data } },\n requestId,\n },\n using\n );\n const id = String(submitted.id);\n await options.harness.commit(async (tx) => {\n const kept = (await tx.doc(GuestsDoc)).seats[record.id];\n if (!kept || kept.submissions?.some((entry) => entry.id === id)) return;\n kept.submissions = [\n ...(kept.submissions ?? []),\n { id, approval: null, tools: record.gates.tools },\n ].slice(-KEPT_SUBMISSIONS);\n }, using);\n return { state: 'submitted', submissionId: id, reason: null };\n}\n\ntype Prepared = { text: string; requestId: string };\n\nasync function prepare(record: SeatRecord, request: ProposeRequest): Promise<Prepared | Proposed> {\n const text = typeof request?.text === 'string' ? request.text.trim() : '';\n if (!text) return unanswered('empty');\n if (text.length > MAX_PROPOSAL) return unanswered('too-long');\n const asked =\n typeof request.requestId === 'string' &&\n request.requestId &&\n request.requestId.length <= MAX_REQUEST\n ? request.requestId\n : crypto.randomUUID();\n return { text: await scrubText(text), requestId: `${GUEST_REQUEST}${record.id}:${asked}` };\n}\n\nasync function place(\n record: SeatRecord,\n prepared: Prepared,\n options: GuestsOptions,\n using: Context,\n at: number\n): Promise<Proposed> {\n const gate = record.gates.messages;\n if (gate === 'off') return deliver(record, prepared.text, prepared.requestId, options, using);\n const { reviews } = options;\n if (!reviews || !options.approvals) return unanswered('unavailable');\n const conversation = await options.agents.conversation(record.agentId, using);\n if (!conversation) return unanswered('gone');\n return options.harness.commit(async (tx): Promise<Proposed> => {\n const doc = await tx.doc(GuestReviewsDoc);\n const again = Object.values(doc.reviews).find(\n (review) => review.seat === record.id && review.request === prepared.requestId\n );\n if (again) return { state: 'queued', submissionId: null, review: again.id, reason: null };\n if (liveReviews(doc, record.id).length > MAX_QUEUED) return unanswered('overflow');\n const id = `apr-${crypto.randomUUID()}`;\n const task = await tx.createTask(\n reviews.task,\n { review: id },\n { ownership: { kind: 'conversation' }, conversationId: conversation.id, background: true }\n );\n doc.reviews[id] = {\n id,\n seat: record.id,\n agentId: record.agentId,\n label: record.label,\n text: prepared.text,\n request: prepared.requestId,\n tier: gate,\n tools: record.gates.tools,\n n: doc.next,\n task: Number(task),\n state: 'queued',\n reason: null,\n createdAt: at,\n decidedAt: null,\n submissionId: null,\n ended: false,\n conversation: Number(conversation.id),\n };\n doc.next += 1;\n pruneReviews(doc);\n const asked: GuestAsked = {\n seat: record.id,\n name: record.label,\n review: id,\n text: prepared.text,\n at,\n };\n await tx.appendEntry(conversation.id, { kind: ASKED_KIND, data: { ...asked } });\n return { state: 'queued', submissionId: null, review: id, reason: null };\n }, using);\n}\n\nfunction keyedLine() {\n const locks = new Map<string, Promise<unknown>>();\n return <T>(id: string, run: () => Promise<T>): Promise<T> => {\n const next = (locks.get(id) ?? Promise.resolve()).then(run, run);\n const settled = next.catch(() => undefined);\n locks.set(id, settled);\n void settled.then(() => {\n if (locks.get(id) === settled) locks.delete(id);\n });\n return next;\n };\n}\n\nconst pendingIn =\n (approvals: GuestsOptions['approvals'], reviews: DocumentState<ReviewsState>) =>\n (id: string): number =>\n (approvals?.state.value ?? []).filter((item) => item.seat === id && item.state === 'pending')\n .length + liveReviews(reviews.value, id).filter((review) => review.state === 'queued').length;\n\nfunction shownSeats(\n records: Record<string, SeatRecord>,\n links: ReadonlyMap<string, number>,\n at: number,\n pendingOf: (id: string) => number\n): GuestSeat[] {\n return Object.values(records)\n .filter((record) => liveSeat(record, at))\n .sort((a, b) => a.createdAt - b.createdAt || a.id.localeCompare(b.id))\n .map((record) => ({\n id: record.id,\n label: record.label,\n agentId: record.agentId,\n createdAt: record.createdAt,\n expiresAt: record.expiresAt,\n gates: { ...record.gates },\n connected: (links.get(record.id) ?? 0) > 0,\n pending: pendingOf(record.id),\n }));\n}\n\nexport async function createGuests(options: GuestsOptions): Promise<Guests> {\n const { harness, agents } = options;\n const now = options.now ?? Date.now;\n const context = options.context ?? BACKGROUND_CONTEXT;\n const guestPolicy = () => options.policy?.current().guests ?? DEFAULT_GUEST_POLICY;\n const records: Record<string, SeatRecord> = await harness.commit(async (tx) => {\n await tx.doc(GuestReviewsDoc);\n return copy((await tx.doc(GuestsDoc)).seats);\n }, context);\n const seatsDoc = (await harness.documentState(GuestsDoc, context)) as DocumentState<GuestsState>;\n const reviews = (await harness.documentState(\n GuestReviewsDoc,\n context\n )) as DocumentState<ReviewsState>;\n const links = new Map<string, number>();\n const ended = new Set<(id: string) => void>();\n const gone = new Map<string, Promise<void>>();\n const shown = () => shownSeats(records, links, now(), pendingIn(options.approvals, reviews));\n const seats = replicatedState<GuestSeat[]>(shown());\n let timer: ReturnType<typeof setTimeout> | undefined;\n const locked = keyedLine();\n const cleanup = (record: SeatRecord, using: Context) =>\n locked(record.id, async () => {\n await withdrawQueued(harness, agents, record, using);\n await harness.commit(async (tx) => {\n const doc = await tx.doc(GuestReviewsDoc);\n for (const review of liveReviews(doc, record.id))\n if (review.state === 'queued') {\n endReview(review, 'withdrawn', 'revoked', now());\n await told(tx, review);\n }\n }, using);\n await options.approvals?.withdrawSeat(record.id, using);\n });\n const end = (id: string, using: Context = context): Promise<void> => {\n const going = gone.get(id);\n if (going !== undefined) return going;\n const cleaning = cleanup(records[id] as SeatRecord, using);\n gone.set(id, cleaning);\n for (const listener of [...ended]) listener(id);\n return cleaning;\n };\n const publish = () => {\n const at = now();\n for (const record of Object.values(records))\n if (!liveSeat(record, at)) void end(record.id).catch(() => undefined);\n seats.replace(context, shown());\n if (timer) clearTimeout(timer);\n timer = undefined;\n const next = Math.min(\n ...Object.values(records)\n .filter((record) => liveSeat(record, at) && record.expiresAt !== null)\n .map((record) => record.expiresAt as number)\n );\n if (Number.isFinite(next))\n timer = setTimeout(publish, Math.min(Math.max(0, next - at), LONGEST_TIMER));\n };\n publish();\n const offSeats = seatsDoc.subscribe((value) => {\n for (const id of Object.keys(records)) delete records[id];\n Object.assign(records, copy((value as GuestsState).seats));\n publish();\n });\n const write = (change: (seats: Record<string, SeatRecord>) => SeatRecord | undefined) =>\n harness.commit(async (tx) => {\n const doc = await tx.doc(GuestsDoc);\n const changed = change(doc.seats);\n return changed ? copy(changed) : undefined;\n }, context);\n const recount = () => seats.replace(context, shown());\n const offPending = options.approvals?.state.subscribe(recount);\n const offReviews = reviews.subscribe(recount);\n options.reviews?.bind({\n approvals: options.approvals as NonNullable<GuestsOptions['approvals']>,\n agents,\n line: locked,\n alwaysAllowed: () => guestPolicy().always.messages,\n });\n const current = (guest: GuestPrincipal) => {\n const record = records[guest.id];\n return record && liveSeat(record, now()) && record.epoch === guest.epoch ? record : undefined;\n };\n return {\n seats,\n reviews,\n async open(draft, using) {\n const at = now();\n const problem = checkDraft(draft, at, guestPolicy());\n if (problem) return { seat: null, error: problem };\n if (!agents.state().cones[draft.agentId])\n return { seat: null, error: `There is no cone ${draft.agentId}.` };\n const record: SeatRecord = {\n id: draft.id,\n label: cleanLabel(draft.label),\n agentId: draft.agentId,\n expiresAt: draft.expiresAt,\n gates: { messages: draft.gates.messages, tools: draft.gates.tools },\n createdAt: at,\n epoch: 1,\n revoked: null,\n };\n const kept = await harness.commit(async (tx) => {\n const doc = await tx.doc(GuestsDoc);\n if (doc.seats[record.id]) return null;\n prune(doc.seats, at);\n doc.seats[record.id] = copy(record);\n return copy(doc.seats);\n }, using);\n if (!kept) return { seat: null, error: 'That guest seat already exists.' };\n for (const id of Object.keys(records)) delete records[id];\n Object.assign(records, kept);\n publish();\n return { seat: shown().find((seat) => seat.id === record.id) as GuestSeat, error: null };\n },\n async revoke(id, using) {\n const at = now();\n const changed = await write((all) => {\n const record = all[id];\n if (!record || record.revoked !== null) return undefined;\n record.revoked = at;\n record.epoch += 1;\n return record;\n });\n if (!changed) return { revoked: false };\n records[id] = changed;\n const ending = end(id, using);\n publish();\n await ending;\n return { revoked: true };\n },\n admit(id, label) {\n const record = records[id];\n if (!record || !liveSeat(record, now()) || record.label !== cleanLabel(label)) return null;\n return { id: record.id, name: record.label, epoch: record.epoch };\n },\n record: (id) => records[id],\n current,\n joined(id) {\n links.set(id, (links.get(id) ?? 0) + 1);\n seats.replace(context, shown());\n let left = false;\n return () => {\n if (left) return;\n left = true;\n links.set(id, (links.get(id) ?? 1) - 1);\n seats.replace(context, shown());\n };\n },\n onEnd(listener) {\n ended.add(listener);\n return () => ended.delete(listener);\n },\n async propose(guest, request, using) {\n const record = current(guest);\n if (!record) return unanswered('revoked');\n const prepared = await prepare(record, request);\n if ('state' in prepared) return prepared;\n return locked(record.id, async () => {\n const still = current(guest);\n return still ? place(still, prepared, options, using, now()) : unanswered('revoked');\n });\n },\n policy: guestPolicy,\n dispose() {\n if (timer) clearTimeout(timer);\n offPending?.();\n offReviews();\n offSeats();\n seatsDoc.dispose();\n reviews.dispose();\n ended.clear();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|