@fungi.computer/shiitake 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +454 -2
  3. package/dist/agar-extension.d.ts +9 -0
  4. package/dist/agar-extension.js +38 -0
  5. package/dist/agent-control.d.ts +72 -0
  6. package/dist/agent-control.js +431 -0
  7. package/dist/client-entry.d.ts +211 -0
  8. package/dist/client-entry.js +101 -0
  9. package/dist/client-wire.d.ts +199 -0
  10. package/dist/client-wire.js +271 -0
  11. package/dist/client.d.ts +439 -0
  12. package/dist/client.js +4 -0
  13. package/dist/cloudflare.d.ts +220 -0
  14. package/dist/cloudflare.js +531 -0
  15. package/dist/effect.d.ts +42 -0
  16. package/dist/effect.js +20 -0
  17. package/dist/errors.d.ts +50 -0
  18. package/dist/errors.js +81 -0
  19. package/dist/extension-client.d.ts +38 -0
  20. package/dist/extension-client.js +56 -0
  21. package/dist/extension-contract.d.ts +73 -0
  22. package/dist/extension-contract.js +11 -0
  23. package/dist/extensions.d.ts +589 -0
  24. package/dist/extensions.js +263 -0
  25. package/dist/fleet.d.ts +297 -0
  26. package/dist/fleet.js +515 -0
  27. package/dist/hosted.d.ts +133 -0
  28. package/dist/hosted.js +265 -0
  29. package/dist/http-routes.d.ts +772 -0
  30. package/dist/http-routes.js +242 -0
  31. package/dist/http.d.ts +1 -0
  32. package/dist/http.js +1 -0
  33. package/dist/index.d.ts +35 -0
  34. package/dist/index.js +13 -0
  35. package/dist/internal/accepted-run-executor.d.ts +23 -0
  36. package/dist/internal/accepted-run-executor.js +129 -0
  37. package/dist/internal/admission-door.d.ts +75 -0
  38. package/dist/internal/admission-door.js +105 -0
  39. package/dist/internal/agent-close.d.ts +4 -0
  40. package/dist/internal/agent-close.js +42 -0
  41. package/dist/internal/browser-sessions.d.ts +31 -0
  42. package/dist/internal/browser-sessions.js +173 -0
  43. package/dist/internal/client-boundary.d.ts +262 -0
  44. package/dist/internal/client-boundary.js +202 -0
  45. package/dist/internal/compaction/compaction.d.ts +54 -0
  46. package/dist/internal/compaction/compaction.js +292 -0
  47. package/dist/internal/compaction/session-compaction.d.ts +69 -0
  48. package/dist/internal/compaction/session-compaction.js +330 -0
  49. package/dist/internal/compaction/settings.d.ts +11 -0
  50. package/dist/internal/compaction/settings.js +40 -0
  51. package/dist/internal/compaction/utils.d.ts +45 -0
  52. package/dist/internal/compaction/utils.js +319 -0
  53. package/dist/internal/contracts.d.ts +235 -0
  54. package/dist/internal/contracts.js +1 -0
  55. package/dist/internal/coordinator.d.ts +18 -0
  56. package/dist/internal/coordinator.js +362 -0
  57. package/dist/internal/document-codec.d.ts +11 -0
  58. package/dist/internal/document-codec.js +39 -0
  59. package/dist/internal/effect-client.d.ts +13 -0
  60. package/dist/internal/effect-client.js +102 -0
  61. package/dist/internal/effect-contracts.d.ts +236 -0
  62. package/dist/internal/effect-contracts.js +2 -0
  63. package/dist/internal/extension-command-registry.d.ts +11 -0
  64. package/dist/internal/extension-command-registry.js +29 -0
  65. package/dist/internal/extension-command-tools.d.ts +17 -0
  66. package/dist/internal/extension-command-tools.js +50 -0
  67. package/dist/internal/extension-context.d.ts +25 -0
  68. package/dist/internal/extension-context.js +35 -0
  69. package/dist/internal/extension-definitions.d.ts +5 -0
  70. package/dist/internal/extension-definitions.js +6 -0
  71. package/dist/internal/extension-hook.d.ts +25 -0
  72. package/dist/internal/extension-hook.js +46 -0
  73. package/dist/internal/extension-roster.d.ts +23 -0
  74. package/dist/internal/extension-roster.js +228 -0
  75. package/dist/internal/extension-runtime.d.ts +63 -0
  76. package/dist/internal/extension-runtime.js +1004 -0
  77. package/dist/internal/failure.d.ts +2 -0
  78. package/dist/internal/failure.js +9 -0
  79. package/dist/internal/host-meta.d.ts +41 -0
  80. package/dist/internal/host-meta.js +39 -0
  81. package/dist/internal/json-value.d.ts +4 -0
  82. package/dist/internal/json-value.js +3 -0
  83. package/dist/internal/model-acquisition.d.ts +21 -0
  84. package/dist/internal/model-acquisition.js +98 -0
  85. package/dist/internal/model-preference.d.ts +21 -0
  86. package/dist/internal/model-preference.js +20 -0
  87. package/dist/internal/observation-kernel.d.ts +28 -0
  88. package/dist/internal/observation-kernel.js +169 -0
  89. package/dist/internal/promise-facade.d.ts +18 -0
  90. package/dist/internal/promise-facade.js +172 -0
  91. package/dist/internal/run-scope.d.ts +27 -0
  92. package/dist/internal/run-scope.js +214 -0
  93. package/dist/internal/runtime-coordinator.d.ts +34 -0
  94. package/dist/internal/runtime-coordinator.js +233 -0
  95. package/dist/internal/runtime-core.d.ts +19 -0
  96. package/dist/internal/runtime-core.js +39 -0
  97. package/dist/internal/runtime-reconciliation.d.ts +18 -0
  98. package/dist/internal/runtime-reconciliation.js +162 -0
  99. package/dist/internal/session-context.d.ts +11 -0
  100. package/dist/internal/session-context.js +67 -0
  101. package/dist/internal/session-status.d.ts +10 -0
  102. package/dist/internal/session-status.js +52 -0
  103. package/dist/internal/woodstock/agent-reply.d.ts +25 -0
  104. package/dist/internal/woodstock/agent-reply.js +67 -0
  105. package/dist/internal/woodstock/client-projection.d.ts +29 -0
  106. package/dist/internal/woodstock/client-projection.js +512 -0
  107. package/dist/internal/woodstock/compaction-activity.d.ts +17 -0
  108. package/dist/internal/woodstock/compaction-activity.js +32 -0
  109. package/dist/internal/woodstock/cursor.d.ts +9 -0
  110. package/dist/internal/woodstock/cursor.js +44 -0
  111. package/dist/internal/woodstock/extension-store.d.ts +72 -0
  112. package/dist/internal/woodstock/extension-store.js +262 -0
  113. package/dist/internal/woodstock/fleet-store.d.ts +32 -0
  114. package/dist/internal/woodstock/fleet-store.js +93 -0
  115. package/dist/internal/woodstock/identities.d.ts +6 -0
  116. package/dist/internal/woodstock/identities.js +11 -0
  117. package/dist/internal/woodstock/index.d.ts +170 -0
  118. package/dist/internal/woodstock/index.js +546 -0
  119. package/dist/internal/woodstock/progress.d.ts +32 -0
  120. package/dist/internal/woodstock/progress.js +89 -0
  121. package/dist/internal/woodstock/run-recovery.d.ts +8 -0
  122. package/dist/internal/woodstock/run-recovery.js +95 -0
  123. package/dist/internal/woodstock/session-attention.d.ts +16 -0
  124. package/dist/internal/woodstock/session-attention.js +88 -0
  125. package/dist/internal/woodstock/session-documents.d.ts +18 -0
  126. package/dist/internal/woodstock/session-documents.js +51 -0
  127. package/dist/internal/woodstock/session-entry.d.ts +478 -0
  128. package/dist/internal/woodstock/session-entry.js +484 -0
  129. package/dist/internal/woodstock/session-grants.d.ts +24 -0
  130. package/dist/internal/woodstock/session-grants.js +84 -0
  131. package/dist/internal/woodstock/session-inbox.d.ts +56 -0
  132. package/dist/internal/woodstock/session-inbox.js +307 -0
  133. package/dist/internal/woodstock/session-store.d.ts +432 -0
  134. package/dist/internal/woodstock/session-store.js +2006 -0
  135. package/dist/internal/woodstock/storage-admission.d.ts +14 -0
  136. package/dist/internal/woodstock/storage-admission.js +44 -0
  137. package/dist/model-preference.d.ts +20 -0
  138. package/dist/model-preference.js +30 -0
  139. package/dist/node.d.ts +64 -0
  140. package/dist/node.js +256 -0
  141. package/dist/remote-client.d.ts +11 -0
  142. package/dist/remote-client.js +298 -0
  143. package/dist/session-attention.d.ts +35 -0
  144. package/dist/session-attention.js +22 -0
  145. package/dist/session-documents.d.ts +28 -0
  146. package/dist/session-documents.js +1 -0
  147. package/dist/testing.d.ts +54 -0
  148. package/dist/testing.js +215 -0
  149. package/docs/api/README.md +26 -0
  150. package/docs/api/agar-extension.md +40 -0
  151. package/docs/api/agent-control.md +151 -0
  152. package/docs/api/client-entry.md +344 -0
  153. package/docs/api/client.md +1112 -0
  154. package/docs/api/cloudflare.md +500 -0
  155. package/docs/api/effect.md +904 -0
  156. package/docs/api/extensions.md +2003 -0
  157. package/docs/api/fleet.md +706 -0
  158. package/docs/api/hosted.md +382 -0
  159. package/docs/api/http.md +111 -0
  160. package/docs/api/index.md +1116 -0
  161. package/docs/api/model-preference.md +151 -0
  162. package/docs/api/node.md +142 -0
  163. package/docs/api/testing.md +250 -0
  164. package/package.json +130 -3
package/dist/fleet.js ADDED
@@ -0,0 +1,515 @@
1
+ import { SessionTerminalSchema } from "./client-wire.js";
2
+ import { usage, createFleetDirectoryStore, createFleetDelegationStore, } from "./internal/woodstock/index.js";
3
+ import { pending } from "@fungi.computer/agar";
4
+ import { BirdDogMailSchema } from "@fungi.computer/bird-dog/effect";
5
+ import * as Schema from "effect/Schema";
6
+ import { parseAccepted } from "./client.js";
7
+ import { checkHostMeta } from "./internal/host-meta.js";
8
+ import { defineIdFamily } from "@fungi.computer/cairn/id";
9
+ import { z } from "zod";
10
+ import { authorMeta, FungiMessageMetaSchema, } from "@fungi.computer/agar/agents";
11
+ const identity = z.string().min(1).max(256);
12
+ /** Operations explicitly admitted by the host. */
13
+ export const agentActionSchema = z.enum([
14
+ "create",
15
+ "manage",
16
+ "read",
17
+ "send",
18
+ "delegate",
19
+ ]);
20
+ /** Attribution supplied by the admitting host, without Team semantics. */
21
+ export const agentProvenanceSchema = z.strictObject({
22
+ actor: identity,
23
+ workflowRunId: identity.optional(),
24
+ /** Host fingerprint of the enclosing model program, frozen with replay input. */
25
+ toolCallInputDigest: z
26
+ .string()
27
+ .regex(/^[a-f0-9]{64}$/)
28
+ .optional(),
29
+ session: z
30
+ .strictObject({ agentId: identity, sessionId: identity })
31
+ .optional(),
32
+ });
33
+ /** Fleet authority is supplied, never looked up in host policy. */
34
+ export const agentGrantSchema = z.strictObject({
35
+ agents: z.union([z.literal("all"), z.array(identity)]),
36
+ actions: z.array(agentActionSchema).max(5),
37
+ expiresAt: z.number().int().nonnegative(),
38
+ provenance: agentProvenanceSchema,
39
+ meta: z.json().optional(),
40
+ /** Delegation abuse bound; inherited paths may only narrow it. */
41
+ maxDepth: z.number().int().min(1).max(8).optional(),
42
+ });
43
+ /** Refusal of supplied authority; no host policy lookup is performed. */
44
+ export class AgentGrantError extends Error {
45
+ reason;
46
+ constructor(reason) {
47
+ super(reason);
48
+ this.reason = reason;
49
+ }
50
+ }
51
+ /** Check every operation against its explicit authority. */
52
+ export function assertAgentGrant(grant, action, agentId) {
53
+ const admitted = agentGrantSchema.parse(grant);
54
+ if (admitted.expiresAt <= Date.now())
55
+ throw new AgentGrantError("expired");
56
+ if (!admitted.actions.includes(action))
57
+ throw new AgentGrantError("action");
58
+ if (admitted.agents !== "all" &&
59
+ (agentId === undefined
60
+ ? action === "create"
61
+ : !admitted.agents.includes(agentId)))
62
+ throw new AgentGrantError("agent");
63
+ }
64
+ /** Stable Agent identities, retained across host compositions. */
65
+ export const agentIds = defineIdFamily({
66
+ family: "agent",
67
+ version: 1,
68
+ kinds: { agent: { prefix: "agent" } },
69
+ });
70
+ const agentRowSchema = z.strictObject({
71
+ agent_id: z.string(),
72
+ name: z.string(),
73
+ actor_user_id: z.string(),
74
+ created_at: z.number().int().nonnegative(),
75
+ updated_at: z.number().int().nonnegative(),
76
+ });
77
+ const receiptRowSchema = z.strictObject({
78
+ request_id: z.string(),
79
+ actor_user_id: z.string(),
80
+ name: z.string(),
81
+ agent_id: z.string(),
82
+ computer_mode: z.string().min(1).max(4096),
83
+ });
84
+ /** Stable replay input; configuration is an opaque host creation choice. */
85
+ export const agentCreationSchema = z.strictObject({
86
+ name: z.string().min(1).max(256),
87
+ requestId: z.string().min(1).max(256),
88
+ configuration: z.string().min(1).max(4096),
89
+ });
90
+ /** Owner-only rename input: an Agent's new display name. */
91
+ export const agentRenameSchema = z.strictObject({
92
+ name: z.string().min(1).max(256),
93
+ });
94
+ /** Directory input, replay or storage refusal. */
95
+ export class DirectoryError extends Error {
96
+ status;
97
+ code;
98
+ constructor(status, code) {
99
+ super(code);
100
+ this.status = status;
101
+ this.code = code;
102
+ }
103
+ }
104
+ function asDirectoryAgent(row) {
105
+ const parsed = agentRowSchema.safeParse(row);
106
+ if (!parsed.success)
107
+ throw new DirectoryError(503, "partial_init");
108
+ const agent = agentIds.agent.parse(parsed.data.agent_id);
109
+ if (!agent.ok)
110
+ throw new DirectoryError(503, "partial_init");
111
+ return Object.freeze({
112
+ agentId: agent.value,
113
+ name: parsed.data.name,
114
+ createdAt: parsed.data.created_at,
115
+ updatedAt: parsed.data.updated_at,
116
+ });
117
+ }
118
+ /**
119
+ * Agent identities, replay receipts and erasure roster. Mutations belong inside
120
+ * the caller's storage transaction, so lifecycle facts and host projections
121
+ * commit together. Physical table names retain the existing Hub store layout.
122
+ */
123
+ export function createAgentDirectory(sql) {
124
+ const store = createFleetDirectoryStore(sql);
125
+ function readStored(agentId) {
126
+ const row = store.read(agentId);
127
+ return row === undefined ? null : asDirectoryAgent(row);
128
+ }
129
+ function findCreation(actorUserId, command) {
130
+ const receipt = store.readReceipt(command.requestId);
131
+ if (receipt !== undefined) {
132
+ const parsedReceipt = receiptRowSchema.safeParse(receipt);
133
+ if (!parsedReceipt.success)
134
+ throw new DirectoryError(503, "partial_init");
135
+ if (parsedReceipt.data.actor_user_id !== actorUserId ||
136
+ parsedReceipt.data.name !== command.name ||
137
+ parsedReceipt.data.computer_mode !== command.configuration)
138
+ throw new DirectoryError(409, "request_conflict");
139
+ const agentId = agentIds.agent.parse(parsedReceipt.data.agent_id);
140
+ if (!agentId.ok)
141
+ throw new DirectoryError(503, "partial_init");
142
+ const existing = readStored(agentId.value);
143
+ if (existing === null)
144
+ throw new DirectoryError(503, "partial_init");
145
+ return existing;
146
+ }
147
+ return null;
148
+ }
149
+ function readCreation(grant, raw) {
150
+ assertAgentGrant(grant, "read");
151
+ const existing = findCreation(grant.provenance.actor, agentCreationSchema.parse(raw));
152
+ if (existing !== null)
153
+ assertAgentGrant(grant, "read", existing.agentId);
154
+ return existing;
155
+ }
156
+ function create(grant, raw) {
157
+ assertAgentGrant(grant, "create");
158
+ const command = agentCreationSchema.parse(raw);
159
+ const actorUserId = grant.provenance.actor;
160
+ const existing = findCreation(actorUserId, command);
161
+ if (existing !== null)
162
+ return { agent: existing, fact: null };
163
+ const duplicate = store.findByName(command.name);
164
+ if (duplicate !== undefined)
165
+ throw new DirectoryError(409, "name_taken");
166
+ const agentId = agentIds.agent.generate();
167
+ const now = Date.now();
168
+ store.reserve(agentId, command.name, actorUserId, now, command.requestId, command.configuration);
169
+ const created = readStored(agentId);
170
+ if (created === null)
171
+ throw new DirectoryError(503, "partial_init");
172
+ return {
173
+ agent: created,
174
+ fact: {
175
+ kind: "created",
176
+ agentId,
177
+ provenance: grant.provenance,
178
+ },
179
+ };
180
+ }
181
+ function list(grant) {
182
+ assertAgentGrant(grant, "read");
183
+ return store
184
+ .list()
185
+ .map(asDirectoryAgent)
186
+ .filter(({ agentId }) => grant.agents === "all" || grant.agents.includes(agentId));
187
+ }
188
+ function read(grant, agentId) {
189
+ assertAgentGrant(grant, "read", agentId);
190
+ return readStored(agentId);
191
+ }
192
+ /** The user who created an Agent; that user's own credentials follow it. */
193
+ function readCreator(grant, agentId) {
194
+ assertAgentGrant(grant, "read", agentId);
195
+ const raw = store.readCreator(agentId);
196
+ if (raw === undefined)
197
+ return null;
198
+ const creator = z.string().min(1).max(256).safeParse(raw.actor_user_id);
199
+ if (!creator.success)
200
+ throw new DirectoryError(503, "partial_init");
201
+ return creator.data;
202
+ }
203
+ function remove(grant, agentId) {
204
+ assertAgentGrant(grant, "manage", agentId);
205
+ if (readStored(agentId) === null)
206
+ return null;
207
+ store.remove(agentId);
208
+ return { kind: "deleted", agentId, provenance: grant.provenance };
209
+ }
210
+ /** Owner-only rename; refuses a name already held by another Agent. */
211
+ function rename(grant, agentId, raw) {
212
+ assertAgentGrant(grant, "manage", agentId);
213
+ const command = agentRenameSchema.parse(raw);
214
+ const existing = readStored(agentId);
215
+ if (existing === null)
216
+ throw new DirectoryError(404, "not_found");
217
+ if (command.name !== existing.name) {
218
+ const duplicate = store.findByName(command.name);
219
+ if (duplicate !== undefined)
220
+ throw new DirectoryError(409, "name_taken");
221
+ store.rename(agentId, command.name, Date.now());
222
+ }
223
+ const updated = readStored(agentId);
224
+ if (updated === null)
225
+ throw new DirectoryError(503, "partial_init");
226
+ return updated;
227
+ }
228
+ return {
229
+ /** Resolve a matching creation receipt before host admission of a new reservation. */
230
+ readCreation,
231
+ /** Reserve an Agent once and return its committed lifecycle fact. */
232
+ create,
233
+ /** Read one admitted Agent identity. */
234
+ read,
235
+ /** List only identities covered by the supplied grant. */
236
+ list,
237
+ /** Remove an identity while retaining its erasure address. */
238
+ remove,
239
+ /** Rename an existing Agent; refuses a name already held by another one. */
240
+ rename,
241
+ /** Read the host actor recorded at creation. */
242
+ readCreator,
243
+ /** Enumerate retained addresses for host erasure. */
244
+ listErasureIds: store.listErasureIds,
245
+ /** Whether the retained erasure roster covers this store. */
246
+ erasureRosterComplete: store.erasureRosterComplete,
247
+ };
248
+ }
249
+ /** Replay identity and work, with the parent supplied exclusively by the grant. */
250
+ export const agentDelegationTaskSchema = z.strictObject({
251
+ requestId: identity,
252
+ agentId: identity,
253
+ sessionId: identity.optional(),
254
+ text: z
255
+ .string()
256
+ .min(1)
257
+ .max(256 * 1024),
258
+ });
259
+ /** Exact host facts encoded over internal transports, without transcript content. */
260
+ export const commandSettlementSchema = Schema.Struct({
261
+ terminal: SessionTerminalSchema,
262
+ content: Schema.String,
263
+ usage: Schema.Array(Schema.Struct({ provider: Schema.String, model: Schema.String, usage })),
264
+ toolCalls: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
265
+ });
266
+ /**
267
+ * Provenance for one delegated send from the invoking Agent's Session. The
268
+ * model program's digest is frozen with the replay input, so a retried tool
269
+ * call that changed its code conflicts instead of replaying.
270
+ */
271
+ export async function delegationProvenance(session, toolCallCode) {
272
+ const provenance = {
273
+ actor: session.agentId,
274
+ session: { agentId: session.agentId, sessionId: session.sessionId },
275
+ };
276
+ if (toolCallCode === undefined)
277
+ return provenance;
278
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(toolCallCode));
279
+ return {
280
+ ...provenance,
281
+ toolCallInputDigest: Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join(""),
282
+ };
283
+ }
284
+ /**
285
+ * The host metadata one delegated send carries: the sending Agent as author
286
+ * and only the path inherited from the invoking line. A reply's verified facts
287
+ * stay on that reply's own line and never travel onward.
288
+ */
289
+ export function delegationSendMeta(sender, invocationMeta) {
290
+ const author = authorMeta(sender);
291
+ const inherited = invocationMeta === undefined
292
+ ? undefined
293
+ : FungiMessageMetaSchema.parse(invocationMeta).delegation;
294
+ return inherited === undefined
295
+ ? author
296
+ : {
297
+ ...author,
298
+ delegation: {
299
+ ancestors: [...inherited.ancestors],
300
+ maxDepth: inherited.maxDepth,
301
+ },
302
+ };
303
+ }
304
+ /**
305
+ * The host metadata for one authenticated reply line: the replying Agent is
306
+ * its author, and the verified reply rides beside the caller's own path.
307
+ */
308
+ export function delegationReplyMeta(delegation, replierName) {
309
+ return {
310
+ ...authorMeta({ id: delegation.reply.agentId, name: replierName }),
311
+ delegation: {
312
+ ancestors: [...delegation.ancestors],
313
+ maxDepth: delegation.maxDepth,
314
+ reply: delegation.reply,
315
+ },
316
+ };
317
+ }
318
+ const delegationMetadata = z.object({
319
+ delegation: FungiMessageMetaSchema.shape.delegation,
320
+ });
321
+ const delegationRowSchema = z.strictObject({
322
+ input: z.string(),
323
+ mail: z.string(),
324
+ receipt: z.string().nullable(),
325
+ });
326
+ const decodeMail = Schema.decodeUnknownSync(Schema.fromJsonString(BirdDogMailSchema));
327
+ /**
328
+ * Hosted BirdDog submission and Pending observation. The durable row freezes
329
+ * correspondence before transport; an ambiguous delivery reuses that exact mail.
330
+ * Shiitake owns work deduplication, execution and settlement; Post owns replies.
331
+ */
332
+ export function createFleetDelegation(options) {
333
+ const store = createFleetDelegationStore(options.storage);
334
+ function read(requestId) {
335
+ const row = store.read(requestId);
336
+ return row === undefined ? undefined : delegationRowSchema.parse(row);
337
+ }
338
+ async function start(grant, raw) {
339
+ const task = agentDelegationTaskSchema.parse(raw);
340
+ assertAgentGrant(grant, "delegate", task.agentId);
341
+ const parent = grant.provenance.session;
342
+ if (parent === undefined)
343
+ throw new AgentGrantError("agent");
344
+ assertAgentGrant(grant, "delegate", parent.agentId);
345
+ const meta = grant.meta === undefined
346
+ ? undefined
347
+ : checkHostMeta(undefined, grant.meta);
348
+ const objectMeta = z.record(z.string(), z.json()).safeParse(meta);
349
+ const inherited = objectMeta.success
350
+ ? delegationMetadata.parse(objectMeta.data).delegation
351
+ : undefined;
352
+ const ancestors = inherited?.ancestors ?? [];
353
+ const maxDepth = Math.min(grant.maxDepth ?? 8, inherited?.maxDepth ?? 8);
354
+ if (ancestors.includes(task.agentId) || parent.agentId === task.agentId)
355
+ throw new AgentGrantError("delegation_loop");
356
+ if (ancestors.length >= maxDepth)
357
+ throw new AgentGrantError("delegation_depth");
358
+ const delegation = { ancestors: [...ancestors, parent.agentId], maxDepth };
359
+ const outgoingMeta = {
360
+ ...(objectMeta.success
361
+ ? objectMeta.data
362
+ : meta === undefined
363
+ ? {}
364
+ : { hostMeta: meta }),
365
+ delegation,
366
+ };
367
+ checkHostMeta(undefined, outgoingMeta);
368
+ const input = JSON.stringify({
369
+ task,
370
+ provenance: grant.provenance,
371
+ meta,
372
+ maxDepth,
373
+ });
374
+ let row = read(task.requestId);
375
+ if (row === undefined) {
376
+ let sessionId = task.sessionId;
377
+ if (sessionId === undefined) {
378
+ const opened = await options.open(task.agentId);
379
+ try {
380
+ sessionId = await opened.agent.createSession(task.requestId);
381
+ }
382
+ finally {
383
+ await opened.release();
384
+ }
385
+ }
386
+ const from = { kind: "agent", ...parent };
387
+ let mail = {
388
+ key: task.requestId,
389
+ from,
390
+ to: { kind: "agent", agentId: task.agentId, sessionId },
391
+ replyTo: [from],
392
+ content: task.text,
393
+ createdAt: Date.now(),
394
+ };
395
+ mail = { ...mail, meta: outgoingMeta };
396
+ store.reserve(task.requestId, input, JSON.stringify(mail));
397
+ row = read(task.requestId);
398
+ }
399
+ if (row === undefined)
400
+ throw new DirectoryError(503, "partial_init");
401
+ if (row.input !== input)
402
+ throw new DirectoryError(409, "request_conflict");
403
+ if (row.receipt !== null)
404
+ return parseAccepted(row.receipt);
405
+ assertAgentGrant(grant, "delegate", task.agentId);
406
+ const receipt = parseAccepted(JSON.stringify(await options.deliver(decodeMail(row.mail))));
407
+ store.recordReceipt(task.requestId, JSON.stringify(receipt));
408
+ return receipt;
409
+ }
410
+ async function delegate(grant, task) {
411
+ const accepted = await start(grant, task);
412
+ const row = read(task.requestId);
413
+ if (row === undefined)
414
+ throw new DirectoryError(503, "partial_init");
415
+ const mail = decodeMail(row.mail);
416
+ const settlement = await options.readSettlement(mail.to.agentId, mail.to.sessionId, accepted.commandId);
417
+ return settlement === undefined
418
+ ? pending(1000)
419
+ : {
420
+ kind: "settled",
421
+ agentId: mail.to.agentId,
422
+ sessionId: mail.to.sessionId,
423
+ ...settlement,
424
+ };
425
+ }
426
+ /**
427
+ * Authenticate one arriving reply against the frozen mail this owner sent.
428
+ * It must be BirdDog's single reply key for that mail, mirror its sender and
429
+ * recipient exactly, carry BirdDog's settlement facts and, once the send's
430
+ * receipt is recorded, name that receipt's `commandId`. The result restores
431
+ * the caller's own delegation path and the verified reply; it never reads
432
+ * the reply's text or any settlement metadata.
433
+ */
434
+ function authenticateReply(mail) {
435
+ // This owner sends to one correspondent, so BirdDog's reply index is zero.
436
+ const suffix = ":reply:0";
437
+ if (mail.replyTo.length !== 0 ||
438
+ mail.reply === undefined ||
439
+ !mail.key.endsWith(suffix))
440
+ return undefined;
441
+ const row = read(mail.key.slice(0, -suffix.length));
442
+ if (row === undefined)
443
+ return undefined;
444
+ const sent = decodeMail(row.mail);
445
+ if (mail.from.agentId !== sent.to.agentId ||
446
+ mail.from.sessionId !== sent.to.sessionId ||
447
+ mail.to.agentId !== sent.from.agentId ||
448
+ mail.to.sessionId !== sent.from.sessionId)
449
+ return undefined;
450
+ if (row.receipt !== null &&
451
+ parseAccepted(row.receipt).commandId !== mail.reply.commandId)
452
+ return undefined;
453
+ const path = delegationMetadata.parse(sent.meta).delegation;
454
+ if (path === undefined)
455
+ return undefined;
456
+ const reply = {
457
+ agentId: mail.from.agentId,
458
+ sessionId: mail.from.sessionId,
459
+ ...mail.reply,
460
+ };
461
+ return {
462
+ ancestors: path.ancestors.slice(0, -1),
463
+ maxDepth: path.maxDepth,
464
+ reply,
465
+ };
466
+ }
467
+ return {
468
+ /** Authenticate a reply against its frozen mail and restore the caller's path. */
469
+ authenticateReply,
470
+ /** Submit once and return the existing Shiitake admission receipt. */
471
+ start,
472
+ /** Submit or poll the exact turn, yielding Agar Pending until settled. */
473
+ delegate,
474
+ };
475
+ }
476
+ /**
477
+ * Addressed Session operations over the existing Shiitake client. Each operation
478
+ * checks a fresh supplied grant and releases its transport without stopping work.
479
+ */
480
+ export function createFleetSessions(open) {
481
+ async function withAgent(grant, action, agentId, run) {
482
+ assertAgentGrant(grant, action, agentId);
483
+ const meta = grant.meta === undefined
484
+ ? undefined
485
+ : checkHostMeta(undefined, grant.meta);
486
+ const access = await open(agentId, meta);
487
+ try {
488
+ assertAgentGrant(grant, action, agentId);
489
+ return await run(access.agent);
490
+ }
491
+ finally {
492
+ await access.release();
493
+ }
494
+ }
495
+ return {
496
+ /** Enumerate existing Sessions through the owner's bounded cursor. */
497
+ list: (grant, agentId, after) => withAgent(grant, "read", agentId, (agent) => agent.listSessions(after)),
498
+ /** Open the exact saved conversation without creating work. */
499
+ read: (grant, agentId, sessionId) => withAgent(grant, "read", agentId, (agent) => agent.session(sessionId).read()),
500
+ /** Create a Session once under the caller's replay identity. */
501
+ create: (grant, agentId, requestId) => withAgent(grant, "manage", agentId, (agent) => agent.createSession(requestId)),
502
+ /** Remove a Session through its runtime owner. */
503
+ remove: (grant, agentId, sessionId) => withAgent(grant, "manage", agentId, (agent) => agent.deleteSession(sessionId)),
504
+ /** Send once; an omitted Session is created under the same replay identity. */
505
+ send: (grant, agentId, sessionId, message) => withAgent(grant, "send", agentId, async (agent) => {
506
+ const target = sessionId ?? (await agent.createSession(message.requestId));
507
+ const accepted = await agent
508
+ .session(target)
509
+ .send(message.text, message.delivery === undefined
510
+ ? { requestId: message.requestId }
511
+ : { requestId: message.requestId, delivery: message.delivery });
512
+ return { ...accepted, sessionId: target };
513
+ }),
514
+ };
515
+ }
@@ -0,0 +1,133 @@
1
+ import { z } from "zod";
2
+ import { type ExtensionCapabilityV0, type ExtensionManifestV0, type RosteredExtensionV0 } from "./extensions.js";
3
+ /** App method names a hosted extension's backend serves. */
4
+ export declare const agentExtensionMethodNames: Readonly<{
5
+ readonly prompt: "agentExtensionPrompt";
6
+ readonly tools: "agentExtensionTools";
7
+ readonly call: "agentExtensionCall";
8
+ }>;
9
+ /** Capabilities a hosted extension may request in this protocol version. */
10
+ export declare const hostedExtensionCapabilities: readonly ExtensionCapabilityV0[];
11
+ /** The run a hosted call is made for; the host derives it, never the model. */
12
+ export declare const hostedRunSchema: z.ZodObject<{
13
+ sessionId: z.ZodString;
14
+ workId: z.ZodString;
15
+ origin: z.ZodNullable<z.ZodObject<{
16
+ extensionId: z.ZodString;
17
+ correlation: z.ZodNullable<z.ZodString>;
18
+ }, z.core.$strict>>;
19
+ }, z.core.$strict>;
20
+ /** A parsed {@link hostedRunSchema} value. */
21
+ export type HostedRun = z.output<typeof hostedRunSchema>;
22
+ /** `agentExtensionPrompt` input: the run whose model request needs sections. */
23
+ export declare const hostedPromptInputSchema: z.ZodObject<{
24
+ run: z.ZodObject<{
25
+ sessionId: z.ZodString;
26
+ workId: z.ZodString;
27
+ origin: z.ZodNullable<z.ZodObject<{
28
+ extensionId: z.ZodString;
29
+ correlation: z.ZodNullable<z.ZodString>;
30
+ }, z.core.$strict>>;
31
+ }, z.core.$strict>;
32
+ }, z.core.$strict>;
33
+ /** A parsed {@link hostedPromptInputSchema} value. */
34
+ export type HostedPromptInput = z.output<typeof hostedPromptInputSchema>;
35
+ /** `agentExtensionPrompt` output: at most 16 named sections of at most 8 KiB each. */
36
+ export declare const hostedPromptOutputSchema: z.ZodObject<{
37
+ sections: z.ZodArray<z.ZodObject<{
38
+ name: z.ZodString;
39
+ text: z.ZodString;
40
+ }, z.core.$strict>>;
41
+ }, z.core.$strict>;
42
+ /** A parsed {@link hostedPromptOutputSchema} value. */
43
+ export type HostedPromptOutput = z.output<typeof hostedPromptOutputSchema>;
44
+ /** A run's tool module as at most 64 dotted operations with bounded JSON Schemas. */
45
+ export declare const hostedModuleDescriptorSchema: z.ZodObject<{
46
+ description: z.ZodOptional<z.ZodString>;
47
+ operations: z.ZodRecord<z.ZodString, z.ZodObject<{
48
+ replay: z.ZodEnum<{
49
+ unsafe: "unsafe";
50
+ safe: "safe";
51
+ }>;
52
+ description: z.ZodOptional<z.ZodString>;
53
+ input: z.ZodJSONSchema;
54
+ output: z.ZodNullable<z.ZodJSONSchema>;
55
+ }, z.core.$strict>>;
56
+ }, z.core.$strict>;
57
+ /** A parsed {@link hostedModuleDescriptorSchema} value. */
58
+ export type HostedModuleDescriptor = z.output<typeof hostedModuleDescriptorSchema>;
59
+ /** `agentExtensionTools` input: the run the tools are captured for. */
60
+ export declare const hostedToolsInputSchema: z.ZodObject<{
61
+ run: z.ZodObject<{
62
+ sessionId: z.ZodString;
63
+ workId: z.ZodString;
64
+ origin: z.ZodNullable<z.ZodObject<{
65
+ extensionId: z.ZodString;
66
+ correlation: z.ZodNullable<z.ZodString>;
67
+ }, z.core.$strict>>;
68
+ }, z.core.$strict>;
69
+ }, z.core.$strict>;
70
+ /** A parsed {@link hostedToolsInputSchema} value. */
71
+ export type HostedToolsInput = z.output<typeof hostedToolsInputSchema>;
72
+ /** `agentExtensionTools` output: the run's module, or null when it has none. */
73
+ export declare const hostedToolsOutputSchema: z.ZodObject<{
74
+ module: z.ZodNullable<z.ZodObject<{
75
+ description: z.ZodOptional<z.ZodString>;
76
+ operations: z.ZodRecord<z.ZodString, z.ZodObject<{
77
+ replay: z.ZodEnum<{
78
+ unsafe: "unsafe";
79
+ safe: "safe";
80
+ }>;
81
+ description: z.ZodOptional<z.ZodString>;
82
+ input: z.ZodJSONSchema;
83
+ output: z.ZodNullable<z.ZodJSONSchema>;
84
+ }, z.core.$strict>>;
85
+ }, z.core.$strict>>;
86
+ }, z.core.$strict>;
87
+ /** A parsed {@link hostedToolsOutputSchema} value. */
88
+ export type HostedToolsOutput = z.output<typeof hostedToolsOutputSchema>;
89
+ /** `agentExtensionCall` input: one operation, its input, and the host's call identity. */
90
+ export declare const hostedCallInputSchema: z.ZodObject<{
91
+ run: z.ZodObject<{
92
+ sessionId: z.ZodString;
93
+ workId: z.ZodString;
94
+ origin: z.ZodNullable<z.ZodObject<{
95
+ extensionId: z.ZodString;
96
+ correlation: z.ZodNullable<z.ZodString>;
97
+ }, z.core.$strict>>;
98
+ }, z.core.$strict>;
99
+ operation: z.ZodString;
100
+ input: z.ZodJSONSchema;
101
+ invocation: z.ZodObject<{
102
+ executionId: z.ZodString;
103
+ toolCallId: z.ZodOptional<z.ZodString>;
104
+ deadlineAt: z.ZodNullable<z.ZodNumber>;
105
+ }, z.core.$strict>;
106
+ }, z.core.$strict>;
107
+ /** A parsed {@link hostedCallInputSchema} value. */
108
+ export type HostedCallInput = z.output<typeof hostedCallInputSchema>;
109
+ /** `agentExtensionCall` output: the operation's JSON result. */
110
+ export declare const hostedCallOutputSchema: z.ZodObject<{
111
+ value: z.ZodJSONSchema;
112
+ }, z.core.$strict>;
113
+ /** A parsed {@link hostedCallOutputSchema} value. */
114
+ export type HostedCallOutput = z.output<typeof hostedCallOutputSchema>;
115
+ /**
116
+ * The guest side of one hosted extension. Inputs are already parsed by the
117
+ * App method door with the schemas above; outputs are bounded here.
118
+ */
119
+ export type HostedAgentExtension = Readonly<{
120
+ manifest: ExtensionManifestV0;
121
+ /** Build the run's prompt sections. */
122
+ prompt: (input: HostedPromptInput) => Promise<HostedPromptOutput>;
123
+ /** Describe the run's tool module. */
124
+ tools: (input: HostedToolsInput) => Promise<HostedToolsOutput>;
125
+ /** Run one operation of the run's tool module. */
126
+ call: (input: HostedCallInput) => Promise<HostedCallOutput>;
127
+ }>;
128
+ /**
129
+ * Serve one extension's prompt sections and tools to a remote Agent. Throws
130
+ * when the extension requests a capability hosting does not provide yet, so a
131
+ * build fails instead of silently dropping it.
132
+ */
133
+ export declare function hostAgentExtension(rostered: RosteredExtensionV0): HostedAgentExtension;