@ai-ecoverse/slicc-agent 5.6.0 → 5.8.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 -6
- package/dist/agent.js +8 -2
- package/dist/agent.js.map +2 -2
- package/dist/approvals/index.js +62 -29
- package/dist/approvals/index.js.map +2 -2
- package/dist/approvals/record.js.map +1 -1
- package/dist/control.js +2 -1
- package/dist/control.js.map +2 -2
- 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/guard.js +168 -23
- package/dist/guests/guard.js.map +3 -3
- package/dist/guests/index.js +129 -67
- 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 +14 -8
- package/dist/host.js.map +2 -2
- package/dist/services.js +2 -0
- package/dist/services.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 +4 -0
- package/dist/types/approvals/index.d.ts +5 -2
- package/dist/types/approvals/record.d.ts +2 -0
- package/dist/types/control.d.ts +1 -1
- package/dist/types/guests/attribution.d.ts +16 -0
- package/dist/types/guests/guard.d.ts +29 -2
- package/dist/types/guests/index.d.ts +10 -4
- package/dist/types/guests/review.d.ts +68 -0
- package/dist/types/guests/seats.d.ts +21 -1
- package/dist/types/guests/serve.d.ts +3 -3
- package/dist/types/services.d.ts +5 -2
- package/dist/types/spectrum/messages.d.ts +2 -0
- package/dist/types/spectrum/proposals.d.ts +2 -1
- package/package.json +3 -3
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { BACKGROUND_CONTEXT, withCancel } from "@earendil-works/chord/context";
|
|
2
|
+
import {
|
|
3
|
+
defineDoc,
|
|
4
|
+
defineExtension,
|
|
5
|
+
defineTask
|
|
6
|
+
} from "@earendil-works/pi-durable";
|
|
7
|
+
import { ApprovalsDoc } from "../approvals/record.js";
|
|
8
|
+
import {
|
|
9
|
+
GUEST_INPUT_KIND,
|
|
10
|
+
guestContent,
|
|
11
|
+
REVIEWED_KIND
|
|
12
|
+
} from "./attribution.js";
|
|
13
|
+
import { GUEST_REQUEST } from "./guard.js";
|
|
14
|
+
import { GuestsDoc, liveSeat } from "./seats.js";
|
|
15
|
+
const GuestReviewsDoc = defineDoc({
|
|
16
|
+
kind: "slicc.guest-reviews",
|
|
17
|
+
version: 1,
|
|
18
|
+
scope: "session",
|
|
19
|
+
initial: () => ({ next: 1, reviews: {} })
|
|
20
|
+
});
|
|
21
|
+
const GUEST_REVIEWS = "slicc-guest-reviews";
|
|
22
|
+
const REVIEW_TASK = "slicc.guest-review";
|
|
23
|
+
const MAX_QUEUED = 8;
|
|
24
|
+
const KEPT_REVIEWS = 100;
|
|
25
|
+
const KEPT_SUBMISSIONS = 100;
|
|
26
|
+
const DONE = { status: "terminal", outcome: { status: "completed", result: null } };
|
|
27
|
+
const liveReviews = (state, seat) => Object.values(state?.reviews ?? {}).filter((review) => review.seat === seat && !review.ended).sort((a, b) => a.n - b.n);
|
|
28
|
+
function endReview(review, state, reason, at) {
|
|
29
|
+
review.state = state;
|
|
30
|
+
review.reason = reason;
|
|
31
|
+
review.decidedAt = review.decidedAt ?? at;
|
|
32
|
+
review.ended = true;
|
|
33
|
+
}
|
|
34
|
+
async function told(tx, review) {
|
|
35
|
+
if (review.told) return;
|
|
36
|
+
review.told = true;
|
|
37
|
+
const data = { review: review.id, outcome: review.state };
|
|
38
|
+
await tx.appendEntry(review.conversation, {
|
|
39
|
+
kind: REVIEWED_KIND,
|
|
40
|
+
data: { ...data }
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function pruneReviews(state, kept = KEPT_REVIEWS) {
|
|
44
|
+
const ended2 = Object.values(state.reviews).filter((review) => review.ended).sort((a, b) => a.n - b.n);
|
|
45
|
+
for (const review of ended2.slice(0, Math.max(0, ended2.length - kept)))
|
|
46
|
+
delete state.reviews[review.id];
|
|
47
|
+
}
|
|
48
|
+
const deliveryId = (seat, review) => `${GUEST_REQUEST}${seat}:${review}`;
|
|
49
|
+
const reviewOf = async (tx, id) => (await tx.doc(GuestReviewsDoc)).reviews[id];
|
|
50
|
+
const ended = (task) => task.status === "terminal" || task.status === "completing";
|
|
51
|
+
async function turn(id, runtime, context) {
|
|
52
|
+
await runtime.commit(async (tx) => {
|
|
53
|
+
const doc = await tx.doc(GuestReviewsDoc);
|
|
54
|
+
const review = doc.reviews[id];
|
|
55
|
+
if (!review || review.ended) return DONE;
|
|
56
|
+
const seat = (await tx.doc(GuestsDoc)).seats[review.seat];
|
|
57
|
+
if (!liveSeat(seat, runtime.now())) {
|
|
58
|
+
endReview(review, "withdrawn", "revoked", runtime.now());
|
|
59
|
+
await told(tx, review);
|
|
60
|
+
return DONE;
|
|
61
|
+
}
|
|
62
|
+
const before = liveReviews(doc, review.seat).filter((other) => other.n < review.n).reverse();
|
|
63
|
+
for (const prior of before) {
|
|
64
|
+
const record = await tx.task(prior.task);
|
|
65
|
+
if (record && !ended(record.state))
|
|
66
|
+
return {
|
|
67
|
+
status: "waiting",
|
|
68
|
+
checkpoint: { phase: "turn", after: prior.task },
|
|
69
|
+
on: [prior.task],
|
|
70
|
+
policy: "allSettled"
|
|
71
|
+
};
|
|
72
|
+
endReview(prior, "unanswered", "failed", runtime.now());
|
|
73
|
+
await told(tx, prior);
|
|
74
|
+
}
|
|
75
|
+
return { status: "running", checkpoint: { phase: "ask" } };
|
|
76
|
+
}, context);
|
|
77
|
+
}
|
|
78
|
+
async function ask(id, runtime, host, context) {
|
|
79
|
+
const first = (await runtime.snapshot(GuestReviewsDoc, context))?.reviews[id];
|
|
80
|
+
if (!first) return runtime.commit(async () => DONE, context);
|
|
81
|
+
return host.line(first.seat, () => asking(id, runtime, host, context));
|
|
82
|
+
}
|
|
83
|
+
async function asking(id, runtime, host, context) {
|
|
84
|
+
const review = (await runtime.snapshot(GuestReviewsDoc, context))?.reviews[id];
|
|
85
|
+
const seat = review && (await runtime.snapshot(GuestsDoc, context))?.seats[review.seat];
|
|
86
|
+
if (seat?.gates.messages === "off")
|
|
87
|
+
return runtime.commit(async (tx) => {
|
|
88
|
+
const kept = await reviewOf(tx, id);
|
|
89
|
+
if (!kept || kept.ended) return DONE;
|
|
90
|
+
kept.state = "approved";
|
|
91
|
+
kept.reason = "always";
|
|
92
|
+
kept.auto = true;
|
|
93
|
+
kept.decidedAt = runtime.now();
|
|
94
|
+
await told(tx, kept);
|
|
95
|
+
return { status: "running", checkpoint: { phase: "deliver" } };
|
|
96
|
+
}, context);
|
|
97
|
+
if (review && !review.ended)
|
|
98
|
+
await host.approvals.request(
|
|
99
|
+
{
|
|
100
|
+
id: review.id,
|
|
101
|
+
kind: "guest-message",
|
|
102
|
+
seat: review.seat,
|
|
103
|
+
agentId: review.agentId,
|
|
104
|
+
requester: { kind: "guest", name: review.label, seat: review.seat },
|
|
105
|
+
tier: review.tier,
|
|
106
|
+
detail: { text: review.text, attachments: [] }
|
|
107
|
+
},
|
|
108
|
+
BACKGROUND_CONTEXT
|
|
109
|
+
);
|
|
110
|
+
await runtime.commit(async (tx) => {
|
|
111
|
+
const kept = await reviewOf(tx, id);
|
|
112
|
+
if (!kept || kept.ended) return DONE;
|
|
113
|
+
kept.state = "pending";
|
|
114
|
+
return { status: "running", checkpoint: { phase: "decide" } };
|
|
115
|
+
}, context);
|
|
116
|
+
}
|
|
117
|
+
const itemIn = (doc, id) => doc?.items.find((item) => item.id === id);
|
|
118
|
+
async function settledOrDue(runtime, id, context) {
|
|
119
|
+
const { context: using, cancel } = withCancel(context);
|
|
120
|
+
const watch = await runtime.watchDoc(ApprovalsDoc, using);
|
|
121
|
+
try {
|
|
122
|
+
const now = itemIn(watch?.value, id);
|
|
123
|
+
if (now?.state !== "pending" || now.expiresAt <= runtime.now()) return { item: now };
|
|
124
|
+
const slept = runtime.sleep(now.expiresAt, using);
|
|
125
|
+
slept.catch(() => void 0);
|
|
126
|
+
const changed = new Promise((resolve) => {
|
|
127
|
+
watch?.start(async (value) => {
|
|
128
|
+
if (itemIn(value, id)?.state !== "pending") resolve();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
await Promise.race([slept, changed]);
|
|
132
|
+
return null;
|
|
133
|
+
} finally {
|
|
134
|
+
cancel();
|
|
135
|
+
await watch?.stop();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function decide(id, runtime, host, context) {
|
|
139
|
+
let item;
|
|
140
|
+
for (; ; ) {
|
|
141
|
+
const seen = await settledOrDue(runtime, id, context);
|
|
142
|
+
if (!seen) continue;
|
|
143
|
+
item = seen.item;
|
|
144
|
+
if (item?.state !== "pending") break;
|
|
145
|
+
item = await host.approvals.expire(id, BACKGROUND_CONTEXT);
|
|
146
|
+
if (item?.state !== "pending") break;
|
|
147
|
+
}
|
|
148
|
+
const settled = item;
|
|
149
|
+
await runtime.commit(async (tx) => {
|
|
150
|
+
const review = await reviewOf(tx, id);
|
|
151
|
+
if (!review || review.ended) return DONE;
|
|
152
|
+
const at = settled?.decidedAt ?? runtime.now();
|
|
153
|
+
if (settled?.state === "approved") {
|
|
154
|
+
review.state = "approved";
|
|
155
|
+
review.decidedAt = at;
|
|
156
|
+
await told(tx, review);
|
|
157
|
+
if (settled.answer === "always" && host.alwaysAllowed()) {
|
|
158
|
+
const record = (await tx.doc(GuestsDoc)).seats[review.seat];
|
|
159
|
+
if (record && liveSeat(record, runtime.now())) {
|
|
160
|
+
record.gates.messages = "off";
|
|
161
|
+
record.always = { by: settled.decidedBy, approval: settled.id, at };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return { status: "running", checkpoint: { phase: "deliver" } };
|
|
165
|
+
}
|
|
166
|
+
endReview(review, settled?.state ?? "unanswered", settled?.reason ?? null, at);
|
|
167
|
+
await told(tx, review);
|
|
168
|
+
return DONE;
|
|
169
|
+
}, context);
|
|
170
|
+
}
|
|
171
|
+
async function deliver(id, runtime, host, context) {
|
|
172
|
+
const review = (await runtime.snapshot(GuestReviewsDoc, context))?.reviews[id];
|
|
173
|
+
if (!review || review.ended) return runtime.commit(async () => DONE, context);
|
|
174
|
+
await host.line(review.seat, async () => {
|
|
175
|
+
const seat = (await runtime.snapshot(GuestsDoc, context))?.seats[review.seat];
|
|
176
|
+
const live = liveSeat(seat, runtime.now());
|
|
177
|
+
const conversation = live ? await host.agents.conversation(review.agentId, BACKGROUND_CONTEXT) : void 0;
|
|
178
|
+
const approval = review.auto ? null : review.id;
|
|
179
|
+
const data = { seat: review.seat, name: review.label, approval };
|
|
180
|
+
const submitted = conversation ? await conversation.submit(
|
|
181
|
+
{
|
|
182
|
+
type: "input",
|
|
183
|
+
content: guestContent(review.label, review.text),
|
|
184
|
+
whenBusy: "followUp",
|
|
185
|
+
entry: { kind: GUEST_INPUT_KIND, data: { ...data } },
|
|
186
|
+
requestId: deliveryId(review.seat, review.id)
|
|
187
|
+
},
|
|
188
|
+
BACKGROUND_CONTEXT
|
|
189
|
+
) : void 0;
|
|
190
|
+
await runtime.commit(async (tx) => {
|
|
191
|
+
const kept = await reviewOf(tx, id);
|
|
192
|
+
if (!kept) return DONE;
|
|
193
|
+
kept.ended = true;
|
|
194
|
+
if (!submitted) {
|
|
195
|
+
kept.reason = live ? "gone" : "revoked";
|
|
196
|
+
return DONE;
|
|
197
|
+
}
|
|
198
|
+
kept.submissionId = String(submitted.id);
|
|
199
|
+
const record = (await tx.doc(GuestsDoc)).seats[kept.seat];
|
|
200
|
+
if (record) {
|
|
201
|
+
record.delivered = [...record.delivered ?? [], kept.id].slice(-KEPT_SUBMISSIONS);
|
|
202
|
+
record.submissions = [
|
|
203
|
+
...record.submissions ?? [],
|
|
204
|
+
{ id: kept.submissionId, approval, tools: kept.tools }
|
|
205
|
+
].slice(-KEPT_SUBMISSIONS);
|
|
206
|
+
}
|
|
207
|
+
return DONE;
|
|
208
|
+
}, context);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function reviewTask(lookup) {
|
|
212
|
+
return defineTask({
|
|
213
|
+
name: REVIEW_TASK,
|
|
214
|
+
version: 1,
|
|
215
|
+
initial: () => ({ phase: "turn" }),
|
|
216
|
+
phases: {
|
|
217
|
+
turn: (task, runtime, context) => turn(task.input.review, runtime, context),
|
|
218
|
+
ask: async (task, runtime, context) => ask(task.input.review, runtime, await lookup(), context),
|
|
219
|
+
decide: async (task, runtime, context) => decide(task.input.review, runtime, await lookup(), context),
|
|
220
|
+
deliver: async (task, runtime, context) => deliver(task.input.review, runtime, await lookup(), context)
|
|
221
|
+
},
|
|
222
|
+
async abort(task, runtime, context) {
|
|
223
|
+
const host = await lookup();
|
|
224
|
+
await host.approvals.withdraw(task.input.review, "aborted", BACKGROUND_CONTEXT);
|
|
225
|
+
await runtime.commit(async (tx) => {
|
|
226
|
+
const review = await reviewOf(tx, task.input.review);
|
|
227
|
+
if (review && !review.ended) {
|
|
228
|
+
endReview(review, "withdrawn", "aborted", runtime.now());
|
|
229
|
+
await told(tx, review);
|
|
230
|
+
}
|
|
231
|
+
return { status: "terminal", outcome: { status: "aborted" } };
|
|
232
|
+
}, context);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function guestReviews() {
|
|
237
|
+
let bind;
|
|
238
|
+
const bound = new Promise((resolve) => {
|
|
239
|
+
bind = resolve;
|
|
240
|
+
});
|
|
241
|
+
const task = reviewTask(() => bound);
|
|
242
|
+
return {
|
|
243
|
+
extension: defineExtension({ name: GUEST_REVIEWS, tasks: [task] }),
|
|
244
|
+
task,
|
|
245
|
+
bind: (host) => bind(host)
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
export {
|
|
249
|
+
GUEST_REVIEWS,
|
|
250
|
+
GuestReviewsDoc,
|
|
251
|
+
KEPT_REVIEWS,
|
|
252
|
+
KEPT_SUBMISSIONS,
|
|
253
|
+
MAX_QUEUED,
|
|
254
|
+
REVIEW_TASK,
|
|
255
|
+
deliveryId,
|
|
256
|
+
endReview,
|
|
257
|
+
guestReviews,
|
|
258
|
+
liveReviews,
|
|
259
|
+
pruneReviews,
|
|
260
|
+
told
|
|
261
|
+
};
|
|
262
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/guests/review.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT, withCancel } from '@earendil-works/chord/context';\nimport {\n type ConversationId,\n defineDoc,\n defineExtension,\n defineTask,\n type Extension,\n type NextTaskState,\n type TaskId,\n type TaskRuntime,\n type Tx,\n} from '@earendil-works/pi-durable';\nimport type { Agents } from '../agents.ts';\nimport type { Approval, Approvals, ApprovalsState } from '../approvals/index.ts';\nimport { ApprovalsDoc } from '../approvals/record.ts';\nimport {\n GUEST_INPUT_KIND,\n type GuestInput,\n type GuestOutcome,\n type GuestReviewed,\n guestContent,\n REVIEWED_KIND,\n} from './attribution.ts';\nimport { GUEST_REQUEST } from './guard.ts';\nimport { type GuestGate, GuestsDoc, liveSeat } from './seats.ts';\n\nexport type ReviewState =\n | 'queued'\n | 'pending'\n | 'approved'\n | 'rejected'\n | 'unanswered'\n | 'withdrawn';\n\nexport type Review = {\n id: string;\n seat: string;\n agentId: string;\n label: string;\n text: string;\n request: string;\n tier: GuestGate;\n tools: GuestGate;\n n: number;\n task: number;\n state: ReviewState;\n reason: string | null;\n createdAt: number;\n decidedAt: number | null;\n submissionId: string | null;\n ended: boolean;\n conversation: number;\n auto?: boolean;\n told?: boolean;\n};\n\nexport type ReviewsState = { next: number; reviews: Record<string, Review> };\n\nexport const GuestReviewsDoc = defineDoc<ReviewsState>({\n kind: 'slicc.guest-reviews',\n version: 1,\n scope: 'session',\n initial: () => ({ next: 1, reviews: {} }),\n});\n\nexport const GUEST_REVIEWS = 'slicc-guest-reviews';\nexport const REVIEW_TASK = 'slicc.guest-review';\nexport const MAX_QUEUED = 8;\nexport const KEPT_REVIEWS = 100;\nexport const KEPT_SUBMISSIONS = 100;\n\nexport interface ReviewHost {\n approvals: Pick<Approvals, 'request' | 'expire' | 'withdraw'>;\n agents: Pick<Agents, 'conversation'>;\n line<T>(seat: string, run: () => Promise<T>): Promise<T>;\n alwaysAllowed(): boolean;\n}\n\ntype Input = { review: string };\ntype Phase =\n | { phase: 'turn'; after?: number }\n | { phase: 'ask' }\n | { phase: 'decide' }\n | { phase: 'deliver' };\ntype Runtime = TaskRuntime<Input, Phase, null, object>;\ntype Next = NextTaskState<Phase, null>;\n\nconst DONE: Next = { status: 'terminal', outcome: { status: 'completed', result: null } };\n\nexport const liveReviews = (state: ReviewsState | null | undefined, seat: string): Review[] =>\n Object.values(state?.reviews ?? {})\n .filter((review) => review.seat === seat && !review.ended)\n .sort((a, b) => a.n - b.n);\n\nexport function endReview(review: Review, state: ReviewState, reason: string | null, at: number) {\n review.state = state;\n review.reason = reason;\n review.decidedAt = review.decidedAt ?? at;\n review.ended = true;\n}\n\nexport async function told(tx: Pick<Tx, 'appendEntry'>, review: Review): Promise<void> {\n if (review.told) return;\n review.told = true;\n const data: GuestReviewed = { review: review.id, outcome: review.state as GuestOutcome };\n await tx.appendEntry(review.conversation as unknown as ConversationId, {\n kind: REVIEWED_KIND,\n data: { ...data },\n });\n}\n\nexport function pruneReviews(state: ReviewsState, kept = KEPT_REVIEWS) {\n const ended = Object.values(state.reviews)\n .filter((review) => review.ended)\n .sort((a, b) => a.n - b.n);\n for (const review of ended.slice(0, Math.max(0, ended.length - kept)))\n delete state.reviews[review.id];\n}\n\nexport const deliveryId = (seat: string, review: string): string =>\n `${GUEST_REQUEST}${seat}:${review}`;\n\nconst reviewOf = async (tx: Tx, id: string) => (await tx.doc(GuestReviewsDoc)).reviews[id];\n\nconst ended = (task: { status: string }) =>\n task.status === 'terminal' || task.status === 'completing';\n\nasync function turn(id: string, runtime: Runtime, context: Context) {\n await runtime.commit(async (tx): Promise<Next> => {\n const doc = await tx.doc(GuestReviewsDoc);\n const review = doc.reviews[id];\n if (!review || review.ended) return DONE;\n const seat = (await tx.doc(GuestsDoc)).seats[review.seat];\n if (!liveSeat(seat, runtime.now())) {\n endReview(review, 'withdrawn', 'revoked', runtime.now());\n await told(tx, review);\n return DONE;\n }\n const before = liveReviews(doc, review.seat)\n .filter((other) => other.n < review.n)\n .reverse();\n for (const prior of before) {\n const record = await tx.task(prior.task as unknown as TaskId);\n if (record && !ended(record.state))\n return {\n status: 'waiting',\n checkpoint: { phase: 'turn', after: prior.task },\n on: [prior.task as unknown as TaskId],\n policy: 'allSettled',\n };\n endReview(prior, 'unanswered', 'failed', runtime.now());\n await told(tx, prior);\n }\n return { status: 'running', checkpoint: { phase: 'ask' } };\n }, context);\n}\n\nasync function ask(id: string, runtime: Runtime, host: ReviewHost, context: Context) {\n const first = (await runtime.snapshot(GuestReviewsDoc, context))?.reviews[id];\n if (!first) return runtime.commit(async () => DONE, context);\n return host.line(first.seat, () => asking(id, runtime, host, context));\n}\n\nasync function asking(id: string, runtime: Runtime, host: ReviewHost, context: Context) {\n const review = (await runtime.snapshot(GuestReviewsDoc, context))?.reviews[id];\n const seat = review && (await runtime.snapshot(GuestsDoc, context))?.seats[review.seat];\n if (seat?.gates.messages === 'off')\n return runtime.commit(async (tx): Promise<Next> => {\n const kept = await reviewOf(tx, id);\n if (!kept || kept.ended) return DONE;\n kept.state = 'approved';\n kept.reason = 'always';\n kept.auto = true;\n kept.decidedAt = runtime.now();\n await told(tx, kept);\n return { status: 'running', checkpoint: { phase: 'deliver' } };\n }, context);\n if (review && !review.ended)\n await host.approvals.request(\n {\n id: review.id,\n kind: 'guest-message',\n seat: review.seat,\n agentId: review.agentId,\n requester: { kind: 'guest', name: review.label, seat: review.seat },\n tier: review.tier,\n detail: { text: review.text, attachments: [] },\n },\n BACKGROUND_CONTEXT\n );\n await runtime.commit(async (tx): Promise<Next> => {\n const kept = await reviewOf(tx, id);\n if (!kept || kept.ended) return DONE;\n kept.state = 'pending';\n return { status: 'running', checkpoint: { phase: 'decide' } };\n }, context);\n}\n\nconst itemIn = (doc: Readonly<ApprovalsState> | null | undefined, id: string) =>\n doc?.items.find((item) => item.id === id);\n\nasync function settledOrDue(\n runtime: Runtime,\n id: string,\n context: Context\n): Promise<{ item: Approval | undefined } | null> {\n const { context: using, cancel } = withCancel(context);\n const watch = await runtime.watchDoc(ApprovalsDoc, using);\n try {\n const now = itemIn(watch?.value, id);\n if (now?.state !== 'pending' || now.expiresAt <= runtime.now()) return { item: now };\n const slept = runtime.sleep(now.expiresAt, using);\n slept.catch(() => undefined);\n const changed = new Promise<void>((resolve) => {\n watch?.start(async (value) => {\n if (itemIn(value, id)?.state !== 'pending') resolve();\n });\n });\n await Promise.race([slept, changed]);\n return null;\n } finally {\n cancel();\n await watch?.stop();\n }\n}\n\nasync function decide(id: string, runtime: Runtime, host: ReviewHost, context: Context) {\n let item: Approval | undefined;\n for (;;) {\n const seen = await settledOrDue(runtime, id, context);\n if (!seen) continue;\n item = seen.item;\n if (item?.state !== 'pending') break;\n item = await host.approvals.expire(id, BACKGROUND_CONTEXT);\n if (item?.state !== 'pending') break;\n }\n const settled = item;\n await runtime.commit(async (tx): Promise<Next> => {\n const review = await reviewOf(tx, id);\n if (!review || review.ended) return DONE;\n const at = settled?.decidedAt ?? runtime.now();\n if (settled?.state === 'approved') {\n review.state = 'approved';\n review.decidedAt = at;\n await told(tx, review);\n if (settled.answer === 'always' && host.alwaysAllowed()) {\n const record = (await tx.doc(GuestsDoc)).seats[review.seat];\n if (record && liveSeat(record, runtime.now())) {\n record.gates.messages = 'off';\n record.always = { by: settled.decidedBy, approval: settled.id, at };\n }\n }\n return { status: 'running', checkpoint: { phase: 'deliver' } };\n }\n endReview(review, settled?.state ?? 'unanswered', settled?.reason ?? null, at);\n await told(tx, review);\n return DONE;\n }, context);\n}\n\nasync function deliver(id: string, runtime: Runtime, host: ReviewHost, context: Context) {\n const review = (await runtime.snapshot(GuestReviewsDoc, context))?.reviews[id];\n if (!review || review.ended) return runtime.commit(async () => DONE, context);\n await host.line(review.seat, async () => {\n const seat = (await runtime.snapshot(GuestsDoc, context))?.seats[review.seat];\n const live = liveSeat(seat, runtime.now());\n const conversation = live\n ? await host.agents.conversation(review.agentId, BACKGROUND_CONTEXT)\n : undefined;\n const approval = review.auto ? null : review.id;\n const data: GuestInput = { seat: review.seat, name: review.label, approval };\n const submitted = conversation\n ? await conversation.submit(\n {\n type: 'input',\n content: guestContent(review.label, review.text),\n whenBusy: 'followUp',\n entry: { kind: GUEST_INPUT_KIND, data: { ...data } },\n requestId: deliveryId(review.seat, review.id),\n },\n BACKGROUND_CONTEXT\n )\n : undefined;\n await runtime.commit(async (tx): Promise<Next> => {\n const kept = await reviewOf(tx, id);\n if (!kept) return DONE;\n kept.ended = true;\n if (!submitted) {\n kept.reason = live ? 'gone' : 'revoked';\n return DONE;\n }\n kept.submissionId = String(submitted.id);\n const record = (await tx.doc(GuestsDoc)).seats[kept.seat];\n if (record) {\n record.delivered = [...(record.delivered ?? []), kept.id].slice(-KEPT_SUBMISSIONS);\n record.submissions = [\n ...(record.submissions ?? []),\n { id: kept.submissionId, approval, tools: kept.tools },\n ].slice(-KEPT_SUBMISSIONS);\n }\n return DONE;\n }, context);\n });\n}\n\nexport interface GuestReviews {\n readonly extension: Extension;\n readonly task: ReturnType<typeof reviewTask>;\n bind(host: ReviewHost): void;\n}\n\nfunction reviewTask(lookup: () => Promise<ReviewHost>) {\n return defineTask<Input, Phase, null>({\n name: REVIEW_TASK,\n version: 1,\n initial: () => ({ phase: 'turn' }),\n phases: {\n turn: (task, runtime, context) => turn(task.input.review, runtime, context),\n ask: async (task, runtime, context) =>\n ask(task.input.review, runtime, await lookup(), context),\n decide: async (task, runtime, context) =>\n decide(task.input.review, runtime, await lookup(), context),\n deliver: async (task, runtime, context) =>\n deliver(task.input.review, runtime, await lookup(), context),\n },\n async abort(task, runtime, context) {\n const host = await lookup();\n await host.approvals.withdraw(task.input.review, 'aborted', BACKGROUND_CONTEXT);\n await runtime.commit(async (tx) => {\n const review = await reviewOf(tx, task.input.review);\n if (review && !review.ended) {\n endReview(review, 'withdrawn', 'aborted', runtime.now());\n await told(tx, review);\n }\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context);\n },\n });\n}\n\nexport function guestReviews(): GuestReviews {\n let bind: (host: ReviewHost) => void;\n const bound = new Promise<ReviewHost>((resolve) => {\n bind = resolve;\n });\n const task = reviewTask(() => bound);\n return {\n extension: defineExtension({ name: GUEST_REVIEWS, tasks: [task] }),\n task,\n bind: (host) => bind(host),\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["ended"]
|
|
7
|
+
}
|
package/dist/guests/seats.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/guests/seats.ts"],
|
|
4
|
-
"sourcesContent": ["import { defineDoc } from '@earendil-works/pi-durable';\nimport type { ApprovalTier } from '../approvals/record.ts';\nimport {\n DAY_MS,\n DEFAULT_GUEST_POLICY,\n type GuestPolicy,\n gateKind,\n gateRefusal,\n MAX_SEAT_DAYS,\n} from './policy.ts';\n\nexport type GuestGate = ApprovalTier | 'off';\n\nexport type GuestGates = {\n messages: GuestGate;\n tools: GuestGate;\n};\n\nexport type GuestDraft = {\n id: string;\n label: string;\n agentId: string;\n expiresAt: number | null;\n gates: GuestGates;\n};\n\nexport type SeatRecord = GuestDraft & {\n createdAt: number;\n epoch: number;\n revoked: number | null;\n delivered?: string[];\n};\n\nexport interface GuestSeat {\n id: string;\n label: string;\n agentId: string;\n createdAt: number;\n expiresAt: number | null;\n gates: GuestGates;\n connected: boolean;\n pending: number;\n}\n\nexport type GuestsState = { seats: Record<string, SeatRecord> };\n\nexport const GuestsDoc = defineDoc<GuestsState>({\n kind: 'slicc.guests',\n version: 1,\n scope: 'session',\n initial: () => ({ seats: {} }),\n});\n\nexport const MAX_SEAT_MS = MAX_SEAT_DAYS * DAY_MS;\nexport const MAX_LABEL = 64;\nconst MAX_ID = 256;\n\nexport function cleanLabel(raw: unknown): string {\n return String(raw ?? '')\n .replace(/[\\p{Cc}\\p{Cf}]/gu, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, MAX_LABEL);\n}\n\nexport function isGate(value: unknown): value is GuestGate {\n if (typeof value !== 'string') return false;\n if (value === 'user' || value === 'agent' || value === 'cone' || value === 'off') return true;\n return /^scoop:\\S+$/.test(value);\n}\n\nexport function checkDraft(\n draft: GuestDraft,\n now: number,\n policy: GuestPolicy = DEFAULT_GUEST_POLICY\n): string | null {\n if (typeof draft?.id !== 'string' || !draft.id || draft.id.length > MAX_ID)\n return 'A guest seat needs an id from the tray hub.';\n if (!cleanLabel(draft.label)) return 'A guest seat needs a label.';\n if (!isGate(draft.gates?.messages) || !isGate(draft.gates?.tools))\n return 'A guest seat needs a known gate for messages and for tools.';\n if (!policy.allowedMessageGates.includes(gateKind(draft.gates.messages)))\n return gateRefusal(\"a guest's messages\", policy.allowedMessageGates);\n if (!policy.allowedToolGates.includes(gateKind(draft.gates.tools)))\n return gateRefusal(\"a guest's tool calls\", policy.allowedToolGates);\n const { expiresAt } = draft;\n if (expiresAt !== null && (typeof expiresAt !== 'number' || !Number.isFinite(expiresAt)))\n return 'A guest seat expires at a time, or never.';\n if (expiresAt !== null && expiresAt <= now) return 'That expiry has already passed.';\n const days = policy.maxSeatDays;\n if (expiresAt !== null && expiresAt - now > days * DAY_MS)\n return `A guest seat lasts at most ${days} ${days === 1 ? 'day' : 'days'}.`;\n return null;\n}\n\nexport function liveSeat(seat: SeatRecord | undefined, now: number): boolean {\n return Boolean(\n seat && seat.revoked === null && (seat.expiresAt === null || seat.expiresAt > now)\n );\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,iBAAiB;AAE1B;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;
|
|
4
|
+
"sourcesContent": ["import { defineDoc } from '@earendil-works/pi-durable';\nimport type { ApprovalTier, DecidedBy } from '../approvals/record.ts';\nimport {\n DAY_MS,\n DEFAULT_GUEST_POLICY,\n type GuestPolicy,\n gateKind,\n gateRefusal,\n MAX_SEAT_DAYS,\n} from './policy.ts';\n\nexport type GuestGate = ApprovalTier | 'off';\n\nexport type GuestGates = {\n messages: GuestGate;\n tools: GuestGate;\n};\n\nexport type GuestDraft = {\n id: string;\n label: string;\n agentId: string;\n expiresAt: number | null;\n gates: GuestGates;\n};\n\nexport type SeatSubmission = { id: string; approval: string | null; tools: GuestGate };\n\nexport type AlwaysTool = {\n approval: string;\n tool: string;\n arguments: string;\n by: DecidedBy | null;\n at: number;\n};\n\nexport type SeatRecord = GuestDraft & {\n createdAt: number;\n epoch: number;\n revoked: number | null;\n delivered?: string[];\n submissions?: SeatSubmission[];\n always?: { by: DecidedBy | null; approval: string; at: number };\n alwaysTools?: AlwaysTool[];\n};\n\nexport interface GuestSeat {\n id: string;\n label: string;\n agentId: string;\n createdAt: number;\n expiresAt: number | null;\n gates: GuestGates;\n connected: boolean;\n pending: number;\n alwaysTools: AlwaysTool[];\n}\n\nexport type GuestsState = { seats: Record<string, SeatRecord> };\n\nexport const GuestsDoc = defineDoc<GuestsState>({\n kind: 'slicc.guests',\n version: 1,\n scope: 'session',\n initial: () => ({ seats: {} }),\n});\n\nexport const MAX_SEAT_MS = MAX_SEAT_DAYS * DAY_MS;\nexport const MAX_LABEL = 64;\nconst MAX_ID = 256;\n\nexport function cleanLabel(raw: unknown): string {\n return String(raw ?? '')\n .replace(/[\\p{Cc}\\p{Cf}]/gu, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, MAX_LABEL);\n}\n\nexport function isGate(value: unknown): value is GuestGate {\n if (typeof value !== 'string') return false;\n if (value === 'user' || value === 'agent' || value === 'cone' || value === 'off') return true;\n return /^scoop:\\S+$/.test(value);\n}\n\nexport function checkDraft(\n draft: GuestDraft,\n now: number,\n policy: GuestPolicy = DEFAULT_GUEST_POLICY\n): string | null {\n if (typeof draft?.id !== 'string' || !draft.id || draft.id.length > MAX_ID)\n return 'A guest seat needs an id from the tray hub.';\n if (!cleanLabel(draft.label)) return 'A guest seat needs a label.';\n if (!isGate(draft.gates?.messages) || !isGate(draft.gates?.tools))\n return 'A guest seat needs a known gate for messages and for tools.';\n if (!policy.allowedMessageGates.includes(gateKind(draft.gates.messages)))\n return gateRefusal(\"a guest's messages\", policy.allowedMessageGates);\n if (!policy.allowedToolGates.includes(gateKind(draft.gates.tools)))\n return gateRefusal(\"a guest's tool calls\", policy.allowedToolGates);\n const { expiresAt } = draft;\n if (expiresAt !== null && (typeof expiresAt !== 'number' || !Number.isFinite(expiresAt)))\n return 'A guest seat expires at a time, or never.';\n if (expiresAt !== null && expiresAt <= now) return 'That expiry has already passed.';\n const days = policy.maxSeatDays;\n if (expiresAt !== null && expiresAt - now > days * DAY_MS)\n return `A guest seat lasts at most ${days} ${days === 1 ? 'day' : 'days'}.`;\n return null;\n}\n\nexport function liveSeat(seat: SeatRecord | undefined, now: number): boolean {\n return Boolean(\n seat && seat.revoked === null && (seat.expiresAt === null || seat.expiresAt > now)\n );\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAE1B;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAmDA,MAAM,YAAY,UAAuB;AAAA,EAC9C,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,OAAO,CAAC,EAAE;AAC9B,CAAC;AAEM,MAAM,cAAc,gBAAgB;AACpC,MAAM,YAAY;AACzB,MAAM,SAAS;AAER,SAAS,WAAW,KAAsB;AAC/C,SAAO,OAAO,OAAO,EAAE,EACpB,QAAQ,oBAAoB,GAAG,EAC/B,QAAQ,QAAQ,GAAG,EACnB,KAAK,EACL,MAAM,GAAG,SAAS;AACvB;AAEO,SAAS,OAAO,OAAoC;AACzD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,UAAU,UAAU,UAAU,WAAW,UAAU,UAAU,UAAU,MAAO,QAAO;AACzF,SAAO,cAAc,KAAK,KAAK;AACjC;AAEO,SAAS,WACd,OACA,KACA,SAAsB,sBACP;AACf,MAAI,OAAO,OAAO,OAAO,YAAY,CAAC,MAAM,MAAM,MAAM,GAAG,SAAS;AAClE,WAAO;AACT,MAAI,CAAC,WAAW,MAAM,KAAK,EAAG,QAAO;AACrC,MAAI,CAAC,OAAO,MAAM,OAAO,QAAQ,KAAK,CAAC,OAAO,MAAM,OAAO,KAAK;AAC9D,WAAO;AACT,MAAI,CAAC,OAAO,oBAAoB,SAAS,SAAS,MAAM,MAAM,QAAQ,CAAC;AACrE,WAAO,YAAY,sBAAsB,OAAO,mBAAmB;AACrE,MAAI,CAAC,OAAO,iBAAiB,SAAS,SAAS,MAAM,MAAM,KAAK,CAAC;AAC/D,WAAO,YAAY,wBAAwB,OAAO,gBAAgB;AACpE,QAAM,EAAE,UAAU,IAAI;AACtB,MAAI,cAAc,SAAS,OAAO,cAAc,YAAY,CAAC,OAAO,SAAS,SAAS;AACpF,WAAO;AACT,MAAI,cAAc,QAAQ,aAAa,IAAK,QAAO;AACnD,QAAM,OAAO,OAAO;AACpB,MAAI,cAAc,QAAQ,YAAY,MAAM,OAAO;AACjD,WAAO,8BAA8B,IAAI,IAAI,SAAS,IAAI,QAAQ,MAAM;AAC1E,SAAO;AACT;AAEO,SAAS,SAAS,MAA8B,KAAsB;AAC3E,SAAO;AAAA,IACL,QAAQ,KAAK,YAAY,SAAS,KAAK,cAAc,QAAQ,KAAK,YAAY;AAAA,EAChF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/guests/serve.js
CHANGED
|
@@ -2,20 +2,20 @@ import { forbidden, replicatedState } from "@earendil-works/chord";
|
|
|
2
2
|
import { PortLink } from "../wire.js";
|
|
3
3
|
import { GUESTS_MANAGE_DENIED, GUESTS_MANAGE_LATER } from "./policy.js";
|
|
4
4
|
import { guestView } from "./view.js";
|
|
5
|
-
function proposalsOf(
|
|
6
|
-
return (
|
|
7
|
-
id:
|
|
8
|
-
text:
|
|
9
|
-
state:
|
|
10
|
-
reason:
|
|
11
|
-
createdAt:
|
|
12
|
-
decidedAt:
|
|
5
|
+
function proposalsOf(state, seat) {
|
|
6
|
+
return Object.values(state?.reviews ?? {}).filter((review) => review.seat === seat).sort((a, b) => a.n - b.n).map((review) => ({
|
|
7
|
+
id: review.id,
|
|
8
|
+
text: review.text,
|
|
9
|
+
state: review.state,
|
|
10
|
+
reason: review.reason,
|
|
11
|
+
createdAt: review.createdAt,
|
|
12
|
+
decidedAt: review.decidedAt
|
|
13
13
|
}));
|
|
14
14
|
}
|
|
15
15
|
const managing = (guests) => Promise.reject(
|
|
16
16
|
forbidden(guests.policy().manageGuests === "deny" ? GUESTS_MANAGE_DENIED : GUESTS_MANAGE_LATER)
|
|
17
17
|
);
|
|
18
|
-
function guestOpener(guests, agents, views, context
|
|
18
|
+
function guestOpener(guests, agents, views, context) {
|
|
19
19
|
return (principal) => {
|
|
20
20
|
const record = guests.current(principal);
|
|
21
21
|
if (!record) return void 0;
|
|
@@ -35,10 +35,10 @@ function guestOpener(guests, agents, views, context, approvals) {
|
|
|
35
35
|
expiresAt: record.expiresAt,
|
|
36
36
|
gates: { ...record.gates }
|
|
37
37
|
});
|
|
38
|
-
let listed = JSON.stringify(proposalsOf(
|
|
38
|
+
let listed = JSON.stringify(proposalsOf(guests.reviews.value, record.id));
|
|
39
39
|
const proposals = replicatedState(JSON.parse(listed));
|
|
40
|
-
const offProposals =
|
|
41
|
-
const next = JSON.stringify(proposalsOf(
|
|
40
|
+
const offProposals = guests.reviews.subscribe((state) => {
|
|
41
|
+
const next = JSON.stringify(proposalsOf(state, record.id));
|
|
42
42
|
if (next === listed) return;
|
|
43
43
|
listed = next;
|
|
44
44
|
proposals.replace(context, JSON.parse(next));
|
|
@@ -54,7 +54,7 @@ function guestOpener(guests, agents, views, context, approvals) {
|
|
|
54
54
|
},
|
|
55
55
|
dispose() {
|
|
56
56
|
off();
|
|
57
|
-
offProposals
|
|
57
|
+
offProposals();
|
|
58
58
|
}
|
|
59
59
|
};
|
|
60
60
|
};
|
package/dist/guests/serve.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/guests/serve.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context, Host, ReplicatedState } from '@earendil-works/chord';\nimport { forbidden, replicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { Agents } from '../agents.ts';\nimport type {
|
|
5
|
-
"mappings": "AACA,SAAS,WAAW,uBAAuB;
|
|
4
|
+
"sourcesContent": ["import type { Context, Host, ReplicatedState } from '@earendil-works/chord';\nimport { forbidden, replicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { Agents } from '../agents.ts';\nimport type { AgentGuest, GuestProposal, GuestShown } from '../services.ts';\nimport type { AgentPrincipal, GuestPrincipal } from '../serving.ts';\nimport { type PortEndpoint, PortLink } from '../wire.ts';\nimport type { Guests } from './index.ts';\nimport { GUESTS_MANAGE_DENIED, GUESTS_MANAGE_LATER } from './policy.ts';\nimport type { ReviewsState } from './review.ts';\nimport { guestView } from './view.ts';\n\nexport function proposalsOf(state: ReviewsState | null | undefined, seat: string): GuestProposal[] {\n return Object.values(state?.reviews ?? {})\n .filter((review) => review.seat === seat)\n .sort((a, b) => a.n - b.n)\n .map((review) => ({\n id: review.id,\n text: review.text,\n state: review.state,\n reason: review.reason,\n createdAt: review.createdAt,\n decidedAt: review.decidedAt,\n }));\n}\n\nconst managing = (guests: Pick<Guests, 'policy'>): Promise<never> =>\n Promise.reject(\n forbidden(guests.policy().manageGuests === 'deny' ? GUESTS_MANAGE_DENIED : GUESTS_MANAGE_LATER)\n );\n\nexport function guestOpener(\n guests: Guests,\n agents: Pick<Agents, 'state'>,\n views: ReplicatedState<Record<string, ConversationView>>,\n context: Context\n) {\n return (principal: GuestPrincipal): { value: AgentGuest; dispose(): void } | undefined => {\n const record = guests.current(principal);\n if (!record) return undefined;\n const pick = (all: Record<string, ConversationView> | undefined) => all?.[record.agentId];\n let shown = pick(views.value);\n const transcript = replicatedState<ConversationView>(guestView(shown, record.id));\n const off = views.subscribe((all) => {\n if (pick(all) === shown) return;\n shown = pick(all);\n transcript.replace(context, guestView(shown, record.id));\n });\n const seat = replicatedState<GuestShown>({\n id: record.id,\n label: record.label,\n agentId: record.agentId,\n name: agents.state().cones[record.agentId]?.name ?? record.agentId,\n expiresAt: record.expiresAt,\n gates: { ...record.gates },\n });\n let listed = JSON.stringify(proposalsOf(guests.reviews.value, record.id));\n const proposals = replicatedState<GuestProposal[]>(JSON.parse(listed) as GuestProposal[]);\n const offProposals = guests.reviews.subscribe((state) => {\n const next = JSON.stringify(proposalsOf(state, record.id));\n if (next === listed) return;\n listed = next;\n proposals.replace(context, JSON.parse(next) as GuestProposal[]);\n });\n return {\n value: {\n transcript,\n seat,\n proposals,\n propose: (request, using) => guests.propose(principal, request, using),\n guestOpen: () => managing(guests),\n guestRevoke: () => managing(guests),\n },\n dispose() {\n off();\n offProposals();\n },\n };\n };\n}\n\nexport function admitGuest(\n guests: Guests,\n endpoint: PortEndpoint,\n asked: { id: string; label: string },\n serverId: string,\n link: number,\n host: Pick<Host, 'accept'>\n): void {\n const port = new PortLink(endpoint);\n const guest = guests.admit(String(asked.id), String(asked.label));\n if (!guest) {\n port.close('this guest seat is not valid');\n return;\n }\n void port.ended.then(guests.joined(guest.id));\n port.greet(serverId);\n host.accept(port, { principal: { seat: 'guest', link, guest } satisfies AgentPrincipal });\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,WAAW,uBAAuB;AAK3C,SAA4B,gBAAgB;AAE5C,SAAS,sBAAsB,2BAA2B;AAE1D,SAAS,iBAAiB;AAEnB,SAAS,YAAY,OAAwC,MAA+B;AACjG,SAAO,OAAO,OAAO,OAAO,WAAW,CAAC,CAAC,EACtC,OAAO,CAAC,WAAW,OAAO,SAAS,IAAI,EACvC,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,EACxB,IAAI,CAAC,YAAY;AAAA,IAChB,IAAI,OAAO;AAAA,IACX,MAAM,OAAO;AAAA,IACb,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO;AAAA,IACf,WAAW,OAAO;AAAA,IAClB,WAAW,OAAO;AAAA,EACpB,EAAE;AACN;AAEA,MAAM,WAAW,CAAC,WAChB,QAAQ;AAAA,EACN,UAAU,OAAO,OAAO,EAAE,iBAAiB,SAAS,uBAAuB,mBAAmB;AAChG;AAEK,SAAS,YACd,QACA,QACA,OACA,SACA;AACA,SAAO,CAAC,cAAkF;AACxF,UAAM,SAAS,OAAO,QAAQ,SAAS;AACvC,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,OAAO,CAAC,QAAsD,MAAM,OAAO,OAAO;AACxF,QAAI,QAAQ,KAAK,MAAM,KAAK;AAC5B,UAAM,aAAa,gBAAkC,UAAU,OAAO,OAAO,EAAE,CAAC;AAChF,UAAM,MAAM,MAAM,UAAU,CAAC,QAAQ;AACnC,UAAI,KAAK,GAAG,MAAM,MAAO;AACzB,cAAQ,KAAK,GAAG;AAChB,iBAAW,QAAQ,SAAS,UAAU,OAAO,OAAO,EAAE,CAAC;AAAA,IACzD,CAAC;AACD,UAAM,OAAO,gBAA4B;AAAA,MACvC,IAAI,OAAO;AAAA,MACX,OAAO,OAAO;AAAA,MACd,SAAS,OAAO;AAAA,MAChB,MAAM,OAAO,MAAM,EAAE,MAAM,OAAO,OAAO,GAAG,QAAQ,OAAO;AAAA,MAC3D,WAAW,OAAO;AAAA,MAClB,OAAO,EAAE,GAAG,OAAO,MAAM;AAAA,IAC3B,CAAC;AACD,QAAI,SAAS,KAAK,UAAU,YAAY,OAAO,QAAQ,OAAO,OAAO,EAAE,CAAC;AACxE,UAAM,YAAY,gBAAiC,KAAK,MAAM,MAAM,CAAoB;AACxF,UAAM,eAAe,OAAO,QAAQ,UAAU,CAAC,UAAU;AACvD,YAAM,OAAO,KAAK,UAAU,YAAY,OAAO,OAAO,EAAE,CAAC;AACzD,UAAI,SAAS,OAAQ;AACrB,eAAS;AACT,gBAAU,QAAQ,SAAS,KAAK,MAAM,IAAI,CAAoB;AAAA,IAChE,CAAC;AACD,WAAO;AAAA,MACL,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,CAAC,SAAS,UAAU,OAAO,QAAQ,WAAW,SAAS,KAAK;AAAA,QACrE,WAAW,MAAM,SAAS,MAAM;AAAA,QAChC,aAAa,MAAM,SAAS,MAAM;AAAA,MACpC;AAAA,MACA,UAAU;AACR,YAAI;AACJ,qBAAa;AAAA,MACf;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,WACd,QACA,UACA,OACA,UACA,MACA,MACM;AACN,QAAM,OAAO,IAAI,SAAS,QAAQ;AAClC,QAAM,QAAQ,OAAO,MAAM,OAAO,MAAM,EAAE,GAAG,OAAO,MAAM,KAAK,CAAC;AAChE,MAAI,CAAC,OAAO;AACV,SAAK,MAAM,8BAA8B;AACzC;AAAA,EACF;AACA,OAAK,KAAK,MAAM,KAAK,OAAO,OAAO,MAAM,EAAE,CAAC;AAC5C,OAAK,MAAM,QAAQ;AACnB,OAAK,OAAO,MAAM,EAAE,WAAW,EAAE,MAAM,SAAS,MAAM,MAAM,EAA2B,CAAC;AAC1F;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/host.js
CHANGED
|
@@ -109,6 +109,18 @@ function approving(agent, policy, judge, context) {
|
|
|
109
109
|
context
|
|
110
110
|
);
|
|
111
111
|
}
|
|
112
|
+
async function guestsOf(agent, approvals, policy, context) {
|
|
113
|
+
const guests = await createGuests({
|
|
114
|
+
harness: agent.harness,
|
|
115
|
+
agents: agent.agents,
|
|
116
|
+
approvals,
|
|
117
|
+
policy,
|
|
118
|
+
reviews: agent.reviews,
|
|
119
|
+
context
|
|
120
|
+
});
|
|
121
|
+
agent.gate.bind({ approvals, policy: () => guests.policy() });
|
|
122
|
+
return guests;
|
|
123
|
+
}
|
|
112
124
|
const noRevision = () => replicatedState({ revision: 0 });
|
|
113
125
|
async function hostAgent(agent, options = {}) {
|
|
114
126
|
const context = options.context ?? BACKGROUND_CONTEXT;
|
|
@@ -136,13 +148,7 @@ async function hostAgent(agent, options = {}) {
|
|
|
136
148
|
views: mounted.views,
|
|
137
149
|
context
|
|
138
150
|
});
|
|
139
|
-
const guests = await
|
|
140
|
-
harness: agent.harness,
|
|
141
|
-
agents: agent.agents,
|
|
142
|
-
approvals,
|
|
143
|
-
policy,
|
|
144
|
-
context
|
|
145
|
-
});
|
|
151
|
+
const guests = await guestsOf(agent, approvals, policy, context);
|
|
146
152
|
const control = {
|
|
147
153
|
...createAgentControl(
|
|
148
154
|
agent.harness,
|
|
@@ -213,7 +219,7 @@ async function hostAgent(agent, options = {}) {
|
|
|
213
219
|
settings,
|
|
214
220
|
remote,
|
|
215
221
|
pages,
|
|
216
|
-
guest: guestOpener(guests, agent.agents, mounted.views, context
|
|
222
|
+
guest: guestOpener(guests, agent.agents, mounted.views, context),
|
|
217
223
|
decide: (by) => (id, decision, reason, using) => approvals.decide(id, decision, reason, by, using)
|
|
218
224
|
});
|
|
219
225
|
const offGuests = guests.onEnd((id) => {
|
package/dist/host.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/host.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type Context,\n createHost,\n forbidden,\n type Host,\n type Readable,\n type ReplicatedState,\n replicatedState,\n resource,\n type Self,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { Agent } from './agent.ts';\nimport {\n type ApprovalJudge,\n type Approvals,\n attachApprovals,\n type Decider,\n} from './approvals/index.ts';\nimport { type ApprovalPolicies, alwaysFor, attachApprovalPolicy } from './approvals/policy.ts';\nimport { type AgentSudo, createSudo, ownedSudo } from './approvals/sudo.ts';\nimport type { ChangesView } from './changes/git.ts';\nimport type { ChangesRuntime } from './changes/index.ts';\nimport { createAgentControl, type HostLicks, type HostScoops } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport type { FreezerRuntime, FrozenCone } from './freezer/index.ts';\nimport { createGuests, type Guests } from './guests/index.ts';\nimport { admitGuest, guestOpener } from './guests/serve.ts';\nimport type { UserClients } from './kernel/users.ts';\nimport type { McpRuntime } from './mcp/index.ts';\nimport type { MemoryEntry, MemoryScope } from './memory/format.ts';\nimport type { MemoryRuntime } from './memory/index.ts';\nimport { createMonitor, type MonitorOptions } from './monitor/index.ts';\nimport { viewRemote } from './readonly.ts';\nimport { type AgentRemote, createRemote } from './remote/host.ts';\nimport { createPages, ownedPages, type Pages } from './remote/pages.ts';\nimport type { SecretName } from './secrets/names.ts';\nimport type { VaultState } from './secrets/vault.ts';\nimport {\n AgentControl,\n type AgentGuest,\n AgentGuestService,\n AgentPagesService,\n AgentRemoteService,\n AgentSettings,\n AgentSudoService,\n AgentTranscript,\n CONTROL_METHODS,\n CONTROL_READS,\n type Command,\n GUEST_METHODS,\n OWNER_DEVICE_METHODS,\n PAGE_METHODS,\n type PrivateRevision,\n REMOTE_METHODS,\n REMOTE_READS,\n SETTINGS_METHODS,\n SUDO_METHODS,\n} from './services.ts';\nimport {\n AGENT_HOST,\n type AgentPrincipal,\n deviceOf,\n type GuestPrincipal,\n guestOf,\n principalKey,\n readable,\n readablesOf,\n type Seat,\n seatOf,\n served,\n} from './serving.ts';\nimport type { SkillsRuntime } from './skills/index.ts';\nimport type { SprinklesRuntime } from './sprinkles/index.ts';\nimport type { Sprinkle } from './sprinkles/kind.ts';\nimport { slimmer, type Thumbnail, thumbnail } from './thumbs.ts';\nimport { type LabelsRuntime, visibleLabels, watchLabel } from './titles/labels.ts';\nimport type { Uploads } from './uploads.ts';\nimport { agentViews } from './views.ts';\nimport { type PortEndpoint, PortLink } from './wire.ts';\n\nexport interface ConnectOptions {\n readOnly?: boolean;\n pages?: boolean;\n guest?: { id: string; label: string };\n device?: string;\n}\n\nexport interface AgentHost {\n readonly serverId: string;\n readonly agent: Agent;\n readonly users?: UserClients;\n readonly pages: Pages;\n readonly guests: Guests;\n readonly approvals: Approvals;\n connect(endpoint: PortEndpoint, options?: ConnectOptions): void;\n close(): Promise<void>;\n}\n\nconst noSettings: AgentSettings = {\n state: replicatedState({ models: [], accounts: [] }),\n connect: async () => {\n throw new Error('this agent has no accounts');\n },\n disconnect: async () => {},\n signIn: async () => null,\n dismissMoved: async () => {},\n};\n\nasync function follow(\n agent: Agent,\n context: Context,\n slim: (view: ConversationView) => ConversationView\n) {\n let attached = await agent.cone().viewState(context);\n const state = replicatedState<ConversationView>(slim(attached.value));\n const mirror = (value: ConversationView, from: Context) => state.replace(from, slim(value));\n let off = attached.subscribe(mirror);\n const stop = agent.onCone(async (conversation) => {\n const next = await conversation.viewState(context);\n off();\n attached.dispose();\n attached = next;\n state.replace(context, slim(next.value));\n off = next.subscribe(mirror);\n });\n return {\n state,\n refresh: () => state.replace(context, slim(attached.value)),\n dispose() {\n stop();\n off();\n attached.dispose();\n },\n };\n}\n\nexport interface HostSecrets {\n vault: ReplicatedState<VaultState>;\n list(): readonly SecretName[];\n close?(): void;\n}\n\nconst idleVault = replicatedState<VaultState>({\n port: 'none',\n needed: false,\n version: 0,\n locked: 'no',\n held: 0,\n});\n\nfunction gatedControls(\n control: AgentControl,\n shown: (entry: number) => boolean,\n views: () => readonly ConversationView[],\n labels: ReplicatedState<Record<string, string>>\n) {\n const visible = (entry: number) =>\n views().some((view) => view.entries.some((item) => item.id === entry));\n const gated =\n (allowed: (entry: number) => boolean): AgentControl['image'] =>\n async (ref, using) =>\n allowed(ref.entry) ? control.image(ref, using) : { data: null, mimeType: null };\n const trusted: AgentControl = { ...control, image: gated(shown) };\n const viewed: AgentControl = { ...control, image: gated(visible), toolLabel: watchLabel(labels) };\n return { trusted, viewed };\n}\n\nfunction approving(\n agent: Agent,\n policy: ApprovalPolicies,\n judge: ApprovalJudge | undefined,\n context: Context\n): Promise<Approvals> {\n return attachApprovals(\n {\n harness: agent.harness,\n guestAlways: alwaysFor(policy),\n sudoApprover: () => policy.current().sudo.approver,\n ...(judge ? { judge } : {}),\n },\n context\n );\n}\n\nconst noRevision = () => replicatedState<PrivateRevision>({ revision: 0 });\n\nexport async function hostAgent(\n agent: Agent,\n options: {\n serverId?: string;\n context?: Context;\n settings?: AgentSettings;\n licks?: HostLicks;\n scoops?: HostScoops;\n skills?: SkillsRuntime;\n sprinkles?: SprinklesRuntime;\n memory?: MemoryRuntime;\n freezer?: FreezerRuntime;\n changes?: ChangesRuntime;\n mcp?: Pick<McpRuntime, 'close'>;\n users?: UserClients;\n monitor?: Pick<MonitorOptions, 'kernel' | 'groups' | 'users'>;\n pages?: Pages;\n secrets?: HostSecrets;\n uploads?: Uploads;\n thumbnail?: Thumbnail;\n labels?: LabelsRuntime;\n approvals?: Approvals;\n judge?: ApprovalJudge;\n } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n let refresh = () => {};\n const slims = slimmer(options.thumbnail ?? thumbnail, () => refresh());\n const transcript = await follow(agent, context, slims.slim);\n const { state } = transcript;\n const mounted = await agentViews(agent, context, slims.slim);\n refresh = () => {\n transcript.refresh();\n mounted.refresh();\n };\n const touch = () => options.freezer?.touch();\n const untouch = [state.subscribe(touch), mounted.views.subscribe(touch)];\n const pages = options.pages ?? createPages();\n const deliveries = trackDeliveries(agent.harness, state, context);\n const policy = await attachApprovalPolicy(agent.harness, context);\n const approvals = options.approvals ?? (await approving(agent, policy, options.judge, context));\n const monitor = createMonitor({\n ...options.monitor,\n harness: agent.harness,\n agents: agent.agents,\n views: mounted.views,\n context,\n });\n const guests = await createGuests({\n harness: agent.harness,\n agents: agent.agents,\n approvals,\n policy,\n context,\n });\n const control: AgentControl = {\n ...createAgentControl(\n agent.harness,\n agent,\n deliveries,\n options.licks,\n options.scoops,\n options.skills,\n options.sprinkles,\n options.memory,\n options.freezer,\n options.changes,\n monitor,\n options.secrets?.list,\n options.uploads,\n options.labels,\n guests\n ),\n approvalPolicySet: (change, using) => policy.set(change, using),\n };\n const remote = createRemote({\n harness: agent.harness,\n send: (request, using) => control.send(request, using),\n vault: options.secrets?.vault ?? idleVault,\n async conversation(agentId, using) {\n if (!agentId) return agent.cone();\n const found = await agent.agents.conversation(agentId, using);\n if (!found) throw new Error(`There is no agent ${agentId}.`);\n return found;\n },\n });\n const shownViews = () => [state.value, ...Object.values(mounted.views.value)];\n const kept = options.labels?.labels ?? replicatedState<Record<string, string>>({});\n const labels = visibleLabels(kept, [state, mounted.views], shownViews);\n const transcriptService = {\n state,\n deliveries: deliveries.state,\n agents: mounted.agents,\n views: mounted.views,\n commands: options.skills?.commands ?? replicatedState<Command[]>([]),\n privateRevision: options.skills?.privateRevision ?? noRevision(),\n sprinkles: options.sprinkles?.sprinkles ?? replicatedState<Sprinkle[]>([]),\n memories: options.memory?.memories ?? replicatedState<MemoryEntry[]>([]),\n memoryScopes: options.memory?.scopes ?? replicatedState<MemoryScope[]>([]),\n frozen: options.freezer?.frozen ?? replicatedState<FrozenCone[]>([]),\n changes:\n options.changes?.view ?? replicatedState<ChangesView>({ unavailable: null, changes: [] }),\n monitor: monitor.state,\n vault: options.secrets?.vault ?? idleVault,\n labels: labels.state,\n guests: guests.seats,\n approvals: approvals.state,\n approvalPolicy: policy.state,\n };\n const sudo = createSudo({\n approvals,\n agents: () => agent.agents.state(),\n ...(options.licks ? { licks: options.licks.licks } : {}),\n });\n const settings = options.settings ?? noSettings;\n const gates = gatedControls(control, slims.shown, shownViews, transcriptService.labels);\n const { trusted, viewed } = gates;\n const host = serveAgent({\n trusted,\n viewed,\n transcript: transcriptService,\n viewMonitor: monitor.viewState,\n sudo,\n settings,\n remote,\n pages,\n guest: guestOpener(guests, agent.agents, mounted.views, context, approvals.state),\n decide: (by) => (id, decision, reason, using) =>\n approvals.decide(id, decision, reason, by, using),\n });\n const offGuests = guests.onEnd((id) => {\n void host.invalidatePrincipal((principal) => guestOf(principal)?.id === id);\n });\n let links = 0;\n return {\n serverId,\n agent,\n pages,\n guests,\n approvals,\n ...(options.users ? { users: options.users } : {}),\n connect(endpoint, connectOptions) {\n if (connectOptions?.guest) {\n links += 1;\n admitGuest(guests, endpoint, connectOptions.guest, serverId, links, host);\n return;\n }\n const seat: Seat = connectOptions?.readOnly\n ? 'view'\n : connectOptions?.pages === false\n ? 'control'\n : 'owner';\n links += 1;\n const link = new PortLink(endpoint);\n link.greet(serverId);\n const device = connectOptions?.device ? { device: connectOptions.device } : {};\n host.accept(link, {\n principal: { seat, link: links, ...device } satisfies AgentPrincipal,\n });\n },\n async close() {\n offGuests();\n await host.close();\n guests.dispose();\n await pages.dispose();\n await monitor.dispose();\n await remote.dispose();\n deliveries.dispose();\n sudo.close();\n approvals.close();\n transcript.dispose();\n mounted.dispose();\n labels.dispose();\n slims.dispose();\n for (const off of untouch) off();\n options.freezer?.close();\n options.secrets?.close?.();\n await options.changes?.close(context);\n await options.memory?.close(context);\n await options.sprinkles?.close(context);\n await options.skills?.close(context);\n await options.mcp?.close(context);\n await options.scoops?.runtime.close(context);\n await options.licks?.sources.close(context);\n await agent.close();\n await options.users?.close();\n },\n };\n}\n\ninterface Served {\n trusted: AgentControl;\n viewed: AgentControl;\n transcript: Record<keyof AgentTranscript, ReplicatedState<unknown>>;\n viewMonitor: ReplicatedState<unknown>;\n sudo?: AgentSudo;\n settings: AgentSettings;\n remote: AgentRemote;\n pages: Pages;\n guest?: (principal: GuestPrincipal) => { value: AgentGuest; dispose(): void } | undefined;\n decide: (by: Decider) => AgentControl['decide'];\n}\n\nexport const CLOSED_TO_GUESTS = 'a guest seat has only its transcript and propose';\n\nconst opened = new WeakSet<object>();\n\nexport const openToGuests = <F extends object>(factory: F): F => {\n opened.add(factory);\n return factory;\n};\n\nexport function closedToGuests(host: Host): Host {\n const serve = ((type: never, factory: (...args: unknown[]) => unknown, options?: never) =>\n host.serve(\n type,\n (opened.has(factory)\n ? factory\n : (input: unknown, principal: unknown, self: unknown) => {\n if (seatOf(principal as never) === 'guest') throw forbidden(CLOSED_TO_GUESTS);\n return factory(input, principal, self);\n }) as never,\n options\n )) as Host['serve'];\n return new Proxy(host, {\n get(target, key) {\n if (key === 'serve') return serve;\n const value = Reflect.get(target, key, target) as unknown;\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n}\n\nexport function lazily<T>(state: () => ReplicatedState<T>): Readable<T> {\n return {\n get value() {\n return readable(state()).value;\n },\n subscribe: (listener) => readable(state()).subscribe(listener),\n events: (listener) => readable(state()).events(listener),\n };\n}\n\nconst seated = (serving: Served, seat: Seat, device: string | undefined): AgentControl => ({\n ...(seat === 'view' ? serving.viewed : serving.trusted),\n decide: serving.decide({\n by: seat === 'owner' ? 'owner' : 'follower',\n ...(device ? { device } : {}),\n }),\n});\n\nconst VIEW_METHODS = [...CONTROL_READS, ...OWNER_DEVICE_METHODS];\n\nexport function serveAgent(serving: Served): Host {\n const host = closedToGuests(createHost({ name: AGENT_HOST, principalKey }));\n const viewing = { ...serving.transcript, monitor: serving.viewMonitor };\n host.serve(AgentControl, ((_input: unknown, principal: unknown) => {\n const seat = seatOf(principal);\n const control = seated(serving, seat, deviceOf(principal));\n if (seat === 'view') return { kind: 'view', ...served(() => control, VIEW_METHODS) };\n return { kind: 'control', ...served(() => control, CONTROL_METHODS) };\n }) as never);\n host.serve(AgentTranscript, ((_input: unknown, principal: unknown) => ({\n kind: 'transcript',\n ...readablesOf(seatOf(principal) === 'view' ? viewing : serving.transcript),\n })) as never);\n host.serve(AgentSettings, ((_input: unknown, principal: unknown) => ({\n kind: seatOf(principal) === 'view' ? 'view' : 'settings',\n state: readable(serving.settings.state),\n ...(seatOf(principal) === 'view' ? {} : served(() => serving.settings, SETTINGS_METHODS)),\n })) as never);\n host.serve(AgentRemoteService, ((_input: unknown, principal: unknown) => {\n if (seatOf(principal) !== 'view')\n return { kind: 'remote', ...served(() => serving.remote, REMOTE_METHODS) };\n const scoped = viewRemote(serving.remote);\n return { kind: 'watch', ...served(() => scoped, REMOTE_READS) };\n }) as never);\n host.serve(AgentPagesService, ((_input: unknown, principal: unknown, self: Self) => {\n if (seatOf(principal) !== 'owner') return { kind: 'none' };\n const held = self.resources({\n mine: resource({\n rev: 1,\n open: () => ownedPages(serving.pages),\n close: (mine) => mine.release(),\n }),\n });\n return { kind: 'pages', ...served(() => held.mine.value, PAGE_METHODS) };\n }) as never);\n host.serve(\n AgentGuestService,\n openToGuests((_input: unknown, principal: unknown, self: Self) => {\n const guest = guestOf(principal as never);\n if (!guest) return { kind: 'none' };\n const opening = serving.guest?.(guest);\n if (!opening) throw forbidden(CLOSED_TO_GUESTS);\n const held = self.resources({\n seat: resource({ rev: 1, open: () => opening, close: (made) => made.dispose() }),\n });\n const later = (name: 'transcript' | 'seat' | 'proposals') =>\n lazily(() => held.seat.value.value[name] as ReplicatedState<unknown>);\n return {\n kind: 'guest',\n transcript: later('transcript'),\n seat: later('seat'),\n proposals: later('proposals'),\n ...served(() => held.seat.value.value, GUEST_METHODS),\n };\n }) as never\n );\n host.serve(AgentSudoService, ((_input: unknown, principal: unknown, self: Self) => {\n const sudo = serving.sudo;\n if (seatOf(principal) !== 'owner' || !sudo) return { kind: 'none' };\n const held = self.resources({\n mine: resource({ rev: 1, open: () => ownedSudo(sudo), close: (mine) => mine.release() }),\n });\n return {\n kind: 'sudo',\n approvals: readable(sudo.approvals),\n ...served(() => held.mine.value, SUDO_METHODS),\n };\n }) as never);\n return host;\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import {\n type Context,\n createHost,\n forbidden,\n type Host,\n type Readable,\n type ReplicatedState,\n replicatedState,\n resource,\n type Self,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { Agent } from './agent.ts';\nimport {\n type ApprovalJudge,\n type Approvals,\n attachApprovals,\n type Decider,\n} from './approvals/index.ts';\nimport { type ApprovalPolicies, alwaysFor, attachApprovalPolicy } from './approvals/policy.ts';\nimport { type AgentSudo, createSudo, ownedSudo } from './approvals/sudo.ts';\nimport type { ChangesView } from './changes/git.ts';\nimport type { ChangesRuntime } from './changes/index.ts';\nimport { createAgentControl, type HostLicks, type HostScoops } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport type { FreezerRuntime, FrozenCone } from './freezer/index.ts';\nimport { createGuests, type Guests } from './guests/index.ts';\nimport { admitGuest, guestOpener } from './guests/serve.ts';\nimport type { UserClients } from './kernel/users.ts';\nimport type { McpRuntime } from './mcp/index.ts';\nimport type { MemoryEntry, MemoryScope } from './memory/format.ts';\nimport type { MemoryRuntime } from './memory/index.ts';\nimport { createMonitor, type MonitorOptions } from './monitor/index.ts';\nimport { viewRemote } from './readonly.ts';\nimport { type AgentRemote, createRemote } from './remote/host.ts';\nimport { createPages, ownedPages, type Pages } from './remote/pages.ts';\nimport type { SecretName } from './secrets/names.ts';\nimport type { VaultState } from './secrets/vault.ts';\nimport {\n AgentControl,\n type AgentGuest,\n AgentGuestService,\n AgentPagesService,\n AgentRemoteService,\n AgentSettings,\n AgentSudoService,\n AgentTranscript,\n CONTROL_METHODS,\n CONTROL_READS,\n type Command,\n GUEST_METHODS,\n OWNER_DEVICE_METHODS,\n PAGE_METHODS,\n type PrivateRevision,\n REMOTE_METHODS,\n REMOTE_READS,\n SETTINGS_METHODS,\n SUDO_METHODS,\n} from './services.ts';\nimport {\n AGENT_HOST,\n type AgentPrincipal,\n deviceOf,\n type GuestPrincipal,\n guestOf,\n principalKey,\n readable,\n readablesOf,\n type Seat,\n seatOf,\n served,\n} from './serving.ts';\nimport type { SkillsRuntime } from './skills/index.ts';\nimport type { SprinklesRuntime } from './sprinkles/index.ts';\nimport type { Sprinkle } from './sprinkles/kind.ts';\nimport { slimmer, type Thumbnail, thumbnail } from './thumbs.ts';\nimport { type LabelsRuntime, visibleLabels, watchLabel } from './titles/labels.ts';\nimport type { Uploads } from './uploads.ts';\nimport { agentViews } from './views.ts';\nimport { type PortEndpoint, PortLink } from './wire.ts';\n\nexport interface ConnectOptions {\n readOnly?: boolean;\n pages?: boolean;\n guest?: { id: string; label: string };\n device?: string;\n}\n\nexport interface AgentHost {\n readonly serverId: string;\n readonly agent: Agent;\n readonly users?: UserClients;\n readonly pages: Pages;\n readonly guests: Guests;\n readonly approvals: Approvals;\n connect(endpoint: PortEndpoint, options?: ConnectOptions): void;\n close(): Promise<void>;\n}\n\nconst noSettings: AgentSettings = {\n state: replicatedState({ models: [], accounts: [] }),\n connect: async () => {\n throw new Error('this agent has no accounts');\n },\n disconnect: async () => {},\n signIn: async () => null,\n dismissMoved: async () => {},\n};\n\nasync function follow(\n agent: Agent,\n context: Context,\n slim: (view: ConversationView) => ConversationView\n) {\n let attached = await agent.cone().viewState(context);\n const state = replicatedState<ConversationView>(slim(attached.value));\n const mirror = (value: ConversationView, from: Context) => state.replace(from, slim(value));\n let off = attached.subscribe(mirror);\n const stop = agent.onCone(async (conversation) => {\n const next = await conversation.viewState(context);\n off();\n attached.dispose();\n attached = next;\n state.replace(context, slim(next.value));\n off = next.subscribe(mirror);\n });\n return {\n state,\n refresh: () => state.replace(context, slim(attached.value)),\n dispose() {\n stop();\n off();\n attached.dispose();\n },\n };\n}\n\nexport interface HostSecrets {\n vault: ReplicatedState<VaultState>;\n list(): readonly SecretName[];\n close?(): void;\n}\n\nconst idleVault = replicatedState<VaultState>({\n port: 'none',\n needed: false,\n version: 0,\n locked: 'no',\n held: 0,\n});\n\nfunction gatedControls(\n control: AgentControl,\n shown: (entry: number) => boolean,\n views: () => readonly ConversationView[],\n labels: ReplicatedState<Record<string, string>>\n) {\n const visible = (entry: number) =>\n views().some((view) => view.entries.some((item) => item.id === entry));\n const gated =\n (allowed: (entry: number) => boolean): AgentControl['image'] =>\n async (ref, using) =>\n allowed(ref.entry) ? control.image(ref, using) : { data: null, mimeType: null };\n const trusted: AgentControl = { ...control, image: gated(shown) };\n const viewed: AgentControl = { ...control, image: gated(visible), toolLabel: watchLabel(labels) };\n return { trusted, viewed };\n}\n\nfunction approving(\n agent: Agent,\n policy: ApprovalPolicies,\n judge: ApprovalJudge | undefined,\n context: Context\n): Promise<Approvals> {\n return attachApprovals(\n {\n harness: agent.harness,\n guestAlways: alwaysFor(policy),\n sudoApprover: () => policy.current().sudo.approver,\n ...(judge ? { judge } : {}),\n },\n context\n );\n}\n\nasync function guestsOf(\n agent: Agent,\n approvals: Approvals,\n policy: ApprovalPolicies,\n context: Context\n): Promise<Guests> {\n const guests = await createGuests({\n harness: agent.harness,\n agents: agent.agents,\n approvals,\n policy,\n reviews: agent.reviews,\n context,\n });\n agent.gate.bind({ approvals, policy: () => guests.policy() });\n return guests;\n}\n\nconst noRevision = () => replicatedState<PrivateRevision>({ revision: 0 });\n\nexport async function hostAgent(\n agent: Agent,\n options: {\n serverId?: string;\n context?: Context;\n settings?: AgentSettings;\n licks?: HostLicks;\n scoops?: HostScoops;\n skills?: SkillsRuntime;\n sprinkles?: SprinklesRuntime;\n memory?: MemoryRuntime;\n freezer?: FreezerRuntime;\n changes?: ChangesRuntime;\n mcp?: Pick<McpRuntime, 'close'>;\n users?: UserClients;\n monitor?: Pick<MonitorOptions, 'kernel' | 'groups' | 'users'>;\n pages?: Pages;\n secrets?: HostSecrets;\n uploads?: Uploads;\n thumbnail?: Thumbnail;\n labels?: LabelsRuntime;\n approvals?: Approvals;\n judge?: ApprovalJudge;\n } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n let refresh = () => {};\n const slims = slimmer(options.thumbnail ?? thumbnail, () => refresh());\n const transcript = await follow(agent, context, slims.slim);\n const { state } = transcript;\n const mounted = await agentViews(agent, context, slims.slim);\n refresh = () => {\n transcript.refresh();\n mounted.refresh();\n };\n const touch = () => options.freezer?.touch();\n const untouch = [state.subscribe(touch), mounted.views.subscribe(touch)];\n const pages = options.pages ?? createPages();\n const deliveries = trackDeliveries(agent.harness, state, context);\n const policy = await attachApprovalPolicy(agent.harness, context);\n const approvals = options.approvals ?? (await approving(agent, policy, options.judge, context));\n const monitor = createMonitor({\n ...options.monitor,\n harness: agent.harness,\n agents: agent.agents,\n views: mounted.views,\n context,\n });\n const guests = await guestsOf(agent, approvals, policy, context);\n const control: AgentControl = {\n ...createAgentControl(\n agent.harness,\n agent,\n deliveries,\n options.licks,\n options.scoops,\n options.skills,\n options.sprinkles,\n options.memory,\n options.freezer,\n options.changes,\n monitor,\n options.secrets?.list,\n options.uploads,\n options.labels,\n guests\n ),\n approvalPolicySet: (change, using) => policy.set(change, using),\n };\n const remote = createRemote({\n harness: agent.harness,\n send: (request, using) => control.send(request, using),\n vault: options.secrets?.vault ?? idleVault,\n async conversation(agentId, using) {\n if (!agentId) return agent.cone();\n const found = await agent.agents.conversation(agentId, using);\n if (!found) throw new Error(`There is no agent ${agentId}.`);\n return found;\n },\n });\n const shownViews = () => [state.value, ...Object.values(mounted.views.value)];\n const kept = options.labels?.labels ?? replicatedState<Record<string, string>>({});\n const labels = visibleLabels(kept, [state, mounted.views], shownViews);\n const transcriptService = {\n state,\n deliveries: deliveries.state,\n agents: mounted.agents,\n views: mounted.views,\n commands: options.skills?.commands ?? replicatedState<Command[]>([]),\n privateRevision: options.skills?.privateRevision ?? noRevision(),\n sprinkles: options.sprinkles?.sprinkles ?? replicatedState<Sprinkle[]>([]),\n memories: options.memory?.memories ?? replicatedState<MemoryEntry[]>([]),\n memoryScopes: options.memory?.scopes ?? replicatedState<MemoryScope[]>([]),\n frozen: options.freezer?.frozen ?? replicatedState<FrozenCone[]>([]),\n changes:\n options.changes?.view ?? replicatedState<ChangesView>({ unavailable: null, changes: [] }),\n monitor: monitor.state,\n vault: options.secrets?.vault ?? idleVault,\n labels: labels.state,\n guests: guests.seats,\n approvals: approvals.state,\n approvalPolicy: policy.state,\n };\n const sudo = createSudo({\n approvals,\n agents: () => agent.agents.state(),\n ...(options.licks ? { licks: options.licks.licks } : {}),\n });\n const settings = options.settings ?? noSettings;\n const gates = gatedControls(control, slims.shown, shownViews, transcriptService.labels);\n const { trusted, viewed } = gates;\n const host = serveAgent({\n trusted,\n viewed,\n transcript: transcriptService,\n viewMonitor: monitor.viewState,\n sudo,\n settings,\n remote,\n pages,\n guest: guestOpener(guests, agent.agents, mounted.views, context),\n decide: (by) => (id, decision, reason, using) =>\n approvals.decide(id, decision, reason, by, using),\n });\n const offGuests = guests.onEnd((id) => {\n void host.invalidatePrincipal((principal) => guestOf(principal)?.id === id);\n });\n let links = 0;\n return {\n serverId,\n agent,\n pages,\n guests,\n approvals,\n ...(options.users ? { users: options.users } : {}),\n connect(endpoint, connectOptions) {\n if (connectOptions?.guest) {\n links += 1;\n admitGuest(guests, endpoint, connectOptions.guest, serverId, links, host);\n return;\n }\n const seat: Seat = connectOptions?.readOnly\n ? 'view'\n : connectOptions?.pages === false\n ? 'control'\n : 'owner';\n links += 1;\n const link = new PortLink(endpoint);\n link.greet(serverId);\n const device = connectOptions?.device ? { device: connectOptions.device } : {};\n host.accept(link, {\n principal: { seat, link: links, ...device } satisfies AgentPrincipal,\n });\n },\n async close() {\n offGuests();\n await host.close();\n guests.dispose();\n await pages.dispose();\n await monitor.dispose();\n await remote.dispose();\n deliveries.dispose();\n sudo.close();\n approvals.close();\n transcript.dispose();\n mounted.dispose();\n labels.dispose();\n slims.dispose();\n for (const off of untouch) off();\n options.freezer?.close();\n options.secrets?.close?.();\n await options.changes?.close(context);\n await options.memory?.close(context);\n await options.sprinkles?.close(context);\n await options.skills?.close(context);\n await options.mcp?.close(context);\n await options.scoops?.runtime.close(context);\n await options.licks?.sources.close(context);\n await agent.close();\n await options.users?.close();\n },\n };\n}\n\ninterface Served {\n trusted: AgentControl;\n viewed: AgentControl;\n transcript: Record<keyof AgentTranscript, ReplicatedState<unknown>>;\n viewMonitor: ReplicatedState<unknown>;\n sudo?: AgentSudo;\n settings: AgentSettings;\n remote: AgentRemote;\n pages: Pages;\n guest?: (principal: GuestPrincipal) => { value: AgentGuest; dispose(): void } | undefined;\n decide: (by: Decider) => AgentControl['decide'];\n}\n\nexport const CLOSED_TO_GUESTS = 'a guest seat has only its transcript and propose';\n\nconst opened = new WeakSet<object>();\n\nexport const openToGuests = <F extends object>(factory: F): F => {\n opened.add(factory);\n return factory;\n};\n\nexport function closedToGuests(host: Host): Host {\n const serve = ((type: never, factory: (...args: unknown[]) => unknown, options?: never) =>\n host.serve(\n type,\n (opened.has(factory)\n ? factory\n : (input: unknown, principal: unknown, self: unknown) => {\n if (seatOf(principal as never) === 'guest') throw forbidden(CLOSED_TO_GUESTS);\n return factory(input, principal, self);\n }) as never,\n options\n )) as Host['serve'];\n return new Proxy(host, {\n get(target, key) {\n if (key === 'serve') return serve;\n const value = Reflect.get(target, key, target) as unknown;\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n}\n\nexport function lazily<T>(state: () => ReplicatedState<T>): Readable<T> {\n return {\n get value() {\n return readable(state()).value;\n },\n subscribe: (listener) => readable(state()).subscribe(listener),\n events: (listener) => readable(state()).events(listener),\n };\n}\n\nconst seated = (serving: Served, seat: Seat, device: string | undefined): AgentControl => ({\n ...(seat === 'view' ? serving.viewed : serving.trusted),\n decide: serving.decide({\n by: seat === 'owner' ? 'owner' : 'follower',\n ...(device ? { device } : {}),\n }),\n});\n\nconst VIEW_METHODS = [...CONTROL_READS, ...OWNER_DEVICE_METHODS];\n\nexport function serveAgent(serving: Served): Host {\n const host = closedToGuests(createHost({ name: AGENT_HOST, principalKey }));\n const viewing = { ...serving.transcript, monitor: serving.viewMonitor };\n host.serve(AgentControl, ((_input: unknown, principal: unknown) => {\n const seat = seatOf(principal);\n const control = seated(serving, seat, deviceOf(principal));\n if (seat === 'view') return { kind: 'view', ...served(() => control, VIEW_METHODS) };\n return { kind: 'control', ...served(() => control, CONTROL_METHODS) };\n }) as never);\n host.serve(AgentTranscript, ((_input: unknown, principal: unknown) => ({\n kind: 'transcript',\n ...readablesOf(seatOf(principal) === 'view' ? viewing : serving.transcript),\n })) as never);\n host.serve(AgentSettings, ((_input: unknown, principal: unknown) => ({\n kind: seatOf(principal) === 'view' ? 'view' : 'settings',\n state: readable(serving.settings.state),\n ...(seatOf(principal) === 'view' ? {} : served(() => serving.settings, SETTINGS_METHODS)),\n })) as never);\n host.serve(AgentRemoteService, ((_input: unknown, principal: unknown) => {\n if (seatOf(principal) !== 'view')\n return { kind: 'remote', ...served(() => serving.remote, REMOTE_METHODS) };\n const scoped = viewRemote(serving.remote);\n return { kind: 'watch', ...served(() => scoped, REMOTE_READS) };\n }) as never);\n host.serve(AgentPagesService, ((_input: unknown, principal: unknown, self: Self) => {\n if (seatOf(principal) !== 'owner') return { kind: 'none' };\n const held = self.resources({\n mine: resource({\n rev: 1,\n open: () => ownedPages(serving.pages),\n close: (mine) => mine.release(),\n }),\n });\n return { kind: 'pages', ...served(() => held.mine.value, PAGE_METHODS) };\n }) as never);\n host.serve(\n AgentGuestService,\n openToGuests((_input: unknown, principal: unknown, self: Self) => {\n const guest = guestOf(principal as never);\n if (!guest) return { kind: 'none' };\n const opening = serving.guest?.(guest);\n if (!opening) throw forbidden(CLOSED_TO_GUESTS);\n const held = self.resources({\n seat: resource({ rev: 1, open: () => opening, close: (made) => made.dispose() }),\n });\n const later = (name: 'transcript' | 'seat' | 'proposals') =>\n lazily(() => held.seat.value.value[name] as ReplicatedState<unknown>);\n return {\n kind: 'guest',\n transcript: later('transcript'),\n seat: later('seat'),\n proposals: later('proposals'),\n ...served(() => held.seat.value.value, GUEST_METHODS),\n };\n }) as never\n );\n host.serve(AgentSudoService, ((_input: unknown, principal: unknown, self: Self) => {\n const sudo = serving.sudo;\n if (seatOf(principal) !== 'owner' || !sudo) return { kind: 'none' };\n const held = self.resources({\n mine: resource({ rev: 1, open: () => ownedSudo(sudo), close: (mine) => mine.release() }),\n });\n return {\n kind: 'sudo',\n approvals: readable(sudo.approvals),\n ...served(() => held.mine.value, SUDO_METHODS),\n };\n }) as never);\n return host;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/services.js
CHANGED
|
@@ -38,6 +38,7 @@ const CONTROL_METHODS = keys({
|
|
|
38
38
|
toolLabel: true,
|
|
39
39
|
guestOpen: true,
|
|
40
40
|
guestRevoke: true,
|
|
41
|
+
guestAlwaysForget: true,
|
|
41
42
|
approvalPolicySet: true,
|
|
42
43
|
decide: true
|
|
43
44
|
});
|
|
@@ -45,6 +46,7 @@ const OWNER_DEVICE_METHODS = [
|
|
|
45
46
|
"decide",
|
|
46
47
|
"guestOpen",
|
|
47
48
|
"guestRevoke",
|
|
49
|
+
"guestAlwaysForget",
|
|
48
50
|
"approvalPolicySet"
|
|
49
51
|
];
|
|
50
52
|
const CONTROL_READS = [
|