@fungi.computer/shiitake 0.2.0 → 0.3.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 (114) hide show
  1. package/README.md +155 -34
  2. package/dist/agent-control.d.ts +77 -5
  3. package/dist/agent-control.js +271 -65
  4. package/dist/client-entry.d.ts +15 -8
  5. package/dist/client-entry.js +7 -5
  6. package/dist/client-wire.d.ts +24 -8
  7. package/dist/client-wire.js +53 -11
  8. package/dist/client.d.ts +56 -11
  9. package/dist/client.js +1 -1
  10. package/dist/cloudflare.d.ts +42 -23
  11. package/dist/cloudflare.js +76 -54
  12. package/dist/cold-sessions.d.ts +28 -0
  13. package/dist/cold-sessions.js +1 -0
  14. package/dist/delegation.d.ts +214 -0
  15. package/dist/delegation.js +319 -0
  16. package/dist/effect.d.ts +1 -1
  17. package/dist/errors.d.ts +2 -2
  18. package/dist/errors.js +2 -2
  19. package/dist/extension-contract.d.ts +10 -2
  20. package/dist/extensions.d.ts +6 -2
  21. package/dist/fleet.d.ts +43 -9
  22. package/dist/fleet.js +88 -25
  23. package/dist/hosted.js +1 -0
  24. package/dist/http-routes.d.ts +113 -29
  25. package/dist/http-routes.js +18 -1
  26. package/dist/index.d.ts +3 -2
  27. package/dist/internal/accepted-run-executor.d.ts +5 -0
  28. package/dist/internal/accepted-run-executor.js +10 -4
  29. package/dist/internal/admission-door.d.ts +3 -0
  30. package/dist/internal/admission-door.js +12 -2
  31. package/dist/internal/client-boundary.js +7 -1
  32. package/dist/internal/compaction/compaction-slot.d.ts +36 -0
  33. package/dist/internal/compaction/compaction-slot.js +41 -0
  34. package/dist/internal/compaction/compaction.d.ts +11 -8
  35. package/dist/internal/compaction/compaction.js +62 -34
  36. package/dist/internal/compaction/session-compaction.d.ts +8 -16
  37. package/dist/internal/compaction/session-compaction.js +358 -119
  38. package/dist/internal/compaction/settings.d.ts +1 -1
  39. package/dist/internal/compaction/settings.js +5 -0
  40. package/dist/internal/compaction/utils.d.ts +19 -16
  41. package/dist/internal/compaction/utils.js +30 -84
  42. package/dist/internal/contracts.d.ts +40 -13
  43. package/dist/internal/coordinator.js +28 -10
  44. package/dist/internal/effect-client.d.ts +4 -2
  45. package/dist/internal/effect-client.js +29 -0
  46. package/dist/internal/effect-contracts.d.ts +17 -4
  47. package/dist/internal/extension-runtime.d.ts +5 -0
  48. package/dist/internal/extension-runtime.js +184 -89
  49. package/dist/internal/host-meta.d.ts +7 -0
  50. package/dist/internal/model-acquisition.js +7 -1
  51. package/dist/internal/model-preference.d.ts +4 -2
  52. package/dist/internal/model-preference.js +2 -1
  53. package/dist/internal/promise-facade.js +4 -16
  54. package/dist/internal/run-failure-reason.d.ts +10 -0
  55. package/dist/internal/run-failure-reason.js +19 -0
  56. package/dist/internal/run-scope.d.ts +4 -1
  57. package/dist/internal/run-scope.js +38 -5
  58. package/dist/internal/runtime-coordinator.d.ts +2 -1
  59. package/dist/internal/runtime-coordinator.js +65 -4
  60. package/dist/internal/runtime-core.js +1 -0
  61. package/dist/internal/runtime-reconciliation.js +42 -6
  62. package/dist/internal/session-context.d.ts +1 -2
  63. package/dist/internal/session-context.js +8 -30
  64. package/dist/internal/session-status.d.ts +2 -1
  65. package/dist/internal/session-status.js +4 -1
  66. package/dist/internal/woodstock/client-projection.d.ts +6 -5
  67. package/dist/internal/woodstock/client-projection.js +79 -53
  68. package/dist/internal/woodstock/cold-sessions.d.ts +4 -0
  69. package/dist/internal/woodstock/cold-sessions.js +90 -0
  70. package/dist/internal/woodstock/extension-store.d.ts +2 -0
  71. package/dist/internal/woodstock/extension-store.js +4 -0
  72. package/dist/internal/woodstock/fleet-store.d.ts +5 -1
  73. package/dist/internal/woodstock/fleet-store.js +17 -5
  74. package/dist/internal/woodstock/index.d.ts +11 -3
  75. package/dist/internal/woodstock/index.js +33 -14
  76. package/dist/internal/woodstock/progress.d.ts +14 -1
  77. package/dist/internal/woodstock/progress.js +97 -19
  78. package/dist/internal/woodstock/run-recovery.js +6 -4
  79. package/dist/internal/woodstock/session-attention.d.ts +1 -1
  80. package/dist/internal/woodstock/session-attention.js +2 -2
  81. package/dist/internal/woodstock/session-entry.d.ts +110 -43
  82. package/dist/internal/woodstock/session-entry.js +63 -20
  83. package/dist/internal/woodstock/session-inbox.js +2 -0
  84. package/dist/internal/woodstock/session-store.d.ts +104 -24
  85. package/dist/internal/woodstock/session-store.js +937 -246
  86. package/dist/internal/woodstock/storage-admission.d.ts +8 -8
  87. package/dist/internal/woodstock/storage-admission.js +8 -34
  88. package/dist/internal/woodstock/storage-conformance.d.ts +15 -0
  89. package/dist/internal/woodstock/storage-conformance.js +489 -0
  90. package/dist/model-preference.d.ts +2 -2
  91. package/dist/model-preference.js +2 -2
  92. package/dist/node.js +1 -1
  93. package/dist/remote-client.js +8 -1
  94. package/dist/session-attention.d.ts +8 -1
  95. package/dist/session-attention.js +6 -1
  96. package/dist/testing.d.ts +1 -1
  97. package/dist/testing.js +1 -0
  98. package/docs/api/README.md +18 -16
  99. package/docs/api/agent-control.md +171 -1
  100. package/docs/api/client-entry.md +26 -10
  101. package/docs/api/client.md +153 -37
  102. package/docs/api/cloudflare.md +76 -23
  103. package/docs/api/delegation.md +485 -0
  104. package/docs/api/effect.md +20 -2
  105. package/docs/api/extensions.md +21 -9
  106. package/docs/api/fleet.md +159 -104
  107. package/docs/api/http.md +4 -1
  108. package/docs/api/index.md +116 -20
  109. package/docs/api/internal/woodstock/storage-conformance.md +81 -0
  110. package/docs/api/model-preference.md +27 -10
  111. package/docs/api/testing.md +16 -4
  112. package/package.json +26 -14
  113. package/dist/internal/browser-sessions.d.ts +0 -31
  114. package/dist/internal/browser-sessions.js +0 -173
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Shiitake's Agent delegation composition for hosts: fleet correspondence,
3
+ * addressed Agent mail over Carrier Post, and the Agent grammar a host hands
4
+ * to Oyster's machine link as ports. Oyster core never imports this engine.
5
+ */
6
+ import { commandSettlementSchema, createAgentDirectory, createFleetCorrespondence, delegationProvenance, delegationReplyMeta, delegationSendMeta, } from "./fleet.js";
7
+ import { deterministicReplayKey } from "@fungi.computer/cairn";
8
+ import * as Schema from "effect/Schema";
9
+ import { agentMailAddressSchema } from "@fungi.computer/seam/machine-link-protocol";
10
+ import { BirdDogMailSchema, BirdDogError, settlementReplies, } from "@fungi.computer/bird-dog/effect";
11
+ import * as Effect from "effect/Effect";
12
+ import { messageAuthor } from "@fungi.computer/agar/agents";
13
+ import { PostEnvelopeSchema, definePostRoute, } from "@fungi.computer/carrier/post";
14
+ import { parseAccepted } from "./client.js";
15
+ import { decodeClientSessionCreationRequest } from "./internal/client-boundary.js";
16
+ /** Host-owned identity and admission around BirdDog's ordinary envelope. */
17
+ export class AgentDelegation {
18
+ deps;
19
+ constructor(deps) {
20
+ this.deps = deps;
21
+ }
22
+ fleetOwner;
23
+ fleet() {
24
+ return (this.fleetOwner ??= createFleetCorrespondence({
25
+ storage: this.deps.storage,
26
+ open: (agentId) => this.deps.directory().open(agentId, new AbortController().signal),
27
+ deliver: this.deps.deliver,
28
+ }));
29
+ }
30
+ async scope(signal) {
31
+ const agents = await this.deps.directory().list(signal);
32
+ const identity = await this.deps.identity();
33
+ if (identity === null)
34
+ throw new Error("Agent scope is unavailable");
35
+ const source = agents.find(({ agentId }) => agentId === identity.agentId);
36
+ if (source === undefined)
37
+ throw new Error("Agent is outside this scope");
38
+ return { ...identity, agents, source };
39
+ }
40
+ requestKey(scope, input, sourceSessionId) {
41
+ return deterministicReplayKey("oyster-agent-delegation", [
42
+ scope.scopeId,
43
+ scope.agentId,
44
+ sourceSessionId,
45
+ input.agentId,
46
+ input.sessionId,
47
+ input.requestId,
48
+ ]);
49
+ }
50
+ /** Recipient-local disclosure through the public host, never another Agent's RPC. */
51
+ async settlement(sender, sessionId, commandId, host) {
52
+ const scope = await this.scope(new AbortController().signal);
53
+ if (sender.scopeId !== scope.scopeId ||
54
+ !scope.agents.some(({ agentId }) => agentId === sender.agentId))
55
+ throw new AgentDisclosureRefused();
56
+ const facts = await host.readSettlement(sessionId, commandId);
57
+ if (facts !== undefined && facts.acceptedSender !== sender.agentId)
58
+ throw new AgentDisclosureRefused();
59
+ return facts;
60
+ }
61
+ /** Delegate work to an in-scope Agent: steer its Session, or start fleet correspondence. */
62
+ async send(input, sourceSessionId, signal, invocation) {
63
+ if (invocation === undefined)
64
+ throw new Error("Delegation requires a tool invocation");
65
+ const scope = await this.scope(signal);
66
+ if (!scope.agents.some(({ agentId }) => agentId === input.agentId))
67
+ throw new Error("Agent is outside this scope");
68
+ const requestId = await this.requestKey(scope, input, sourceSessionId);
69
+ signal.throwIfAborted();
70
+ if (input.delivery === "steer") {
71
+ const opened = await this.deps.directory().open(input.agentId, signal);
72
+ try {
73
+ return await opened.agent.session(input.sessionId).send(input.text, {
74
+ requestId,
75
+ delivery: "steer",
76
+ });
77
+ }
78
+ finally {
79
+ await opened.release();
80
+ }
81
+ }
82
+ const provenance = await delegationProvenance({
83
+ agentId: scope.agentId,
84
+ sessionId: sourceSessionId,
85
+ });
86
+ const meta = delegationSendMeta({ id: scope.agentId, name: scope.source.name }, invocation.meta);
87
+ signal.throwIfAborted();
88
+ const accepted = await this.fleet().start({
89
+ agents: scope.agents.map(({ agentId }) => agentId),
90
+ actions: ["delegate"],
91
+ expiresAt: this.deps.expiresAt(),
92
+ provenance,
93
+ meta,
94
+ }, {
95
+ agentId: input.agentId,
96
+ sessionId: input.sessionId,
97
+ text: input.text,
98
+ requestId,
99
+ });
100
+ return accepted;
101
+ }
102
+ /** Resolve this sender's exact request, then let the recipient guard pending work. */
103
+ async recall(input, sourceSessionId, signal) {
104
+ const scope = await this.scope(signal);
105
+ if (!scope.agents.some(({ agentId }) => agentId === input.agentId))
106
+ throw new Error("Agent is outside this scope");
107
+ const requestId = await this.requestKey(scope, input, sourceSessionId);
108
+ signal.throwIfAborted();
109
+ const opened = await this.deps.directory().open(input.agentId, signal);
110
+ try {
111
+ const session = opened.agent.session(input.sessionId);
112
+ const admitted = this.fleet().receipt(requestId) ?? (await session.withdraw(requestId));
113
+ return admitted.status === "accepted"
114
+ ? await session.cancel({
115
+ commandId: admitted.commandId,
116
+ onlyIfPending: true,
117
+ })
118
+ : admitted;
119
+ }
120
+ finally {
121
+ await opened.release();
122
+ }
123
+ }
124
+ /**
125
+ * A reply-less envelope is only ever BirdDog's reply to work this Agent
126
+ * sent. Admit it only after the fleet authenticates it against that frozen
127
+ * mail; the line is then authored by the replying Agent, never by whoever
128
+ * settled or cancelled the work, and its meta carries the verified reply.
129
+ */
130
+ async authenticatedReply(mail) {
131
+ const delegation = this.fleet().authenticateReply(mail);
132
+ if (delegation === undefined)
133
+ return undefined;
134
+ const scope = await this.scope(new AbortController().signal);
135
+ const replier = scope.agents.find(({ agentId }) => agentId === delegation.reply.agentId);
136
+ if (replier === undefined)
137
+ return undefined;
138
+ const meta = delegationReplyMeta(delegation, replier.name);
139
+ return { mail: { ...mail, meta }, reply: delegation.reply };
140
+ }
141
+ /** Admit one addressed Agent mail for this Agent, or refuse it. */
142
+ async receive(raw) {
143
+ const envelope = Schema.decodeUnknownOption(PostEnvelopeSchema)(raw);
144
+ if (envelope._tag === "None" || envelope.value.kind !== "agentMail")
145
+ return "refused";
146
+ const address = Schema.decodeUnknownOption(agentMailAddressSchema)(envelope.value.address);
147
+ const parsed = Schema.decodeUnknownOption(BirdDogMailSchema)(envelope.value.event);
148
+ if (address._tag === "None" || parsed._tag === "None")
149
+ return "refused";
150
+ const mail = parsed.value;
151
+ const valid = async () => {
152
+ const scope = await this.scope(new AbortController().signal);
153
+ return (address.value.scopeId === scope.scopeId &&
154
+ address.value.agentId === scope.agentId &&
155
+ mail.to.agentId === scope.agentId &&
156
+ envelope.value.key === mail.key &&
157
+ [mail.from, ...mail.replyTo].every((ref) => scope.agents.some(({ agentId }) => agentId === ref.agentId)) &&
158
+ (mail.replyTo.length === 0 ||
159
+ (mail.replyTo.length === 1 &&
160
+ mail.replyTo[0]?.agentId === mail.from.agentId &&
161
+ mail.replyTo[0].sessionId === mail.from.sessionId)));
162
+ };
163
+ if (!(await valid()))
164
+ return "refused";
165
+ const admitted = mail.replyTo.length > 0
166
+ ? { mail, reply: undefined }
167
+ : await this.authenticatedReply(mail);
168
+ if (admitted === undefined)
169
+ return "refused";
170
+ await this.deps.prepare(admitted.mail);
171
+ if (!(await valid()))
172
+ return "refused";
173
+ return {
174
+ status: "accepted",
175
+ receipt: await this.deps.accept(admitted.mail, admitted.reply),
176
+ };
177
+ }
178
+ }
179
+ /** A current correspondent no longer has disclosure authority. */
180
+ export class AgentDisclosureRefused extends Error {
181
+ constructor() {
182
+ super("Agent is not an admitted original correspondent");
183
+ }
184
+ }
185
+ /** Current disclosure admission runs at every transport attempt, including frozen retries. */
186
+ export function agentMailRoute(namespace, replySettlement) {
187
+ return definePostRoute({
188
+ addressKind: "agent",
189
+ address: agentMailAddressSchema,
190
+ event: BirdDogMailSchema,
191
+ name: ({ agentId }) => agentId,
192
+ namespace: {
193
+ getByName: (name) => ({
194
+ deliver: async (envelope) => {
195
+ const mail = Schema.decodeUnknownSync(BirdDogMailSchema)(envelope.event);
196
+ if (mail.reply !== undefined &&
197
+ (await replySettlement(mail)) === undefined)
198
+ return "refused";
199
+ return namespace.getByName(name).deliver(envelope);
200
+ },
201
+ }),
202
+ },
203
+ });
204
+ }
205
+ /** Recipient-owned replies and receipt-bearing submissions over the host's one Post lane. */
206
+ export class AgentMail {
207
+ deps;
208
+ constructor(deps) {
209
+ this.deps = deps;
210
+ }
211
+ async envelope(mail) {
212
+ const identity = await this.deps.identity();
213
+ if (identity === null)
214
+ throw new Error("Agent scope is unavailable");
215
+ return {
216
+ key: mail.key,
217
+ kind: "agentMail",
218
+ address: { scopeId: identity.scopeId, agentId: mail.to.agentId },
219
+ event: mail,
220
+ };
221
+ }
222
+ /** Submit one mail and return the recipient's Accepted receipt. */
223
+ async send(mail) {
224
+ this.deps.post().send(await this.envelope(mail));
225
+ // Borrow the existing Post lane once, not a new waiter, loop or supervisor.
226
+ await this.deps.drain();
227
+ const row = this.deps.post().read(mail.key);
228
+ if (row?.state === "delivered" && row.receipt !== undefined)
229
+ return parseAccepted(JSON.stringify(row.receipt));
230
+ if (row?.state === "dead")
231
+ throw new Error(`Agent delivery ${row.reason}`);
232
+ throw new Error("Agent did not return a delegated work receipt");
233
+ }
234
+ /** BirdDog's mail room: disclosed settlement replies fan out over Post. */
235
+ mailRoom() {
236
+ return {
237
+ fanOut: (settlement) => Effect.tryPromise({
238
+ try: async () => {
239
+ const author = messageAuthor(settlement.meta);
240
+ const replies = settlementReplies(settlement.job.outcome === "cancelled"
241
+ ? {
242
+ ...settlement,
243
+ content: author === undefined
244
+ ? "Work cancelled."
245
+ : `Work cancelled by ${author.name}.`,
246
+ }
247
+ : settlement, Date.now());
248
+ const admitted = [];
249
+ for (const mail of replies) {
250
+ const facts = await this.deps.replySettlement(mail);
251
+ if (facts === undefined)
252
+ continue;
253
+ admitted.push(await this.envelope({
254
+ ...mail,
255
+ content: facts.terminal.outcome === "cancelled"
256
+ ? mail.content
257
+ : facts.content || mail.content,
258
+ }));
259
+ }
260
+ this.deps.storage.transactionSync(() => {
261
+ for (const envelope of admitted) {
262
+ if (this.deps.post().read(envelope.key) === undefined)
263
+ this.deps.post().send(envelope);
264
+ }
265
+ });
266
+ if (admitted.length > 0)
267
+ await this.deps.wake();
268
+ },
269
+ catch: (cause) => new BirdDogError("Agent reply fan-out is unavailable", { cause }),
270
+ }),
271
+ drainOutbox: () => Effect.void,
272
+ nextWakeAt: () => Effect.try({
273
+ try: () => this.deps.post().nextWake() ?? undefined,
274
+ catch: (cause) => new BirdDogError("Agent reply wake is unavailable", { cause }),
275
+ }),
276
+ };
277
+ }
278
+ }
279
+ /**
280
+ * The Agent wire decoders a host lends Oyster's machine-link client, so the
281
+ * device link routes Agent work without owning Shiitake's grammar.
282
+ */
283
+ export const machineAgentCodecs = {
284
+ /** The Session-creation intent, or null when the body is not one. */
285
+ sessionCreation(body) {
286
+ const decoded = decodeClientSessionCreationRequest(body);
287
+ return decoded._tag === "Failure" ? null : decoded.success;
288
+ },
289
+ /** One serialized settlement from a hosted or machine-homed Agent. */
290
+ settlement(encoded) {
291
+ return Schema.decodeUnknownSync(Schema.fromJsonString(commandSettlementSchema))(encoded);
292
+ },
293
+ /** Throws unless an acknowledgment carries a well-formed Accepted receipt. */
294
+ accepted(receipt) {
295
+ parseAccepted(JSON.stringify(receipt));
296
+ },
297
+ };
298
+ /**
299
+ * Machine-homed Agent identities in the ordinary directory over the
300
+ * enrollment's own store: one Team identity per enrollment receipt, reserved
301
+ * without opening a hosted Agent. The enrolling member is the grant actor.
302
+ */
303
+ export function machineAgentReservations(sql) {
304
+ const directory = createAgentDirectory(sql);
305
+ return (reservation) => {
306
+ const grant = {
307
+ agents: "all",
308
+ actions: ["create", "read"],
309
+ expiresAt: Number.MAX_SAFE_INTEGER,
310
+ provenance: { actor: reservation.memberId },
311
+ };
312
+ const { agent } = directory.create(grant, {
313
+ requestId: reservation.requestId,
314
+ name: reservation.name,
315
+ configuration: "machine",
316
+ });
317
+ return { agentId: agent.agentId };
318
+ };
319
+ }
package/dist/effect.d.ts CHANGED
@@ -8,7 +8,7 @@ import type { Accepted } from "./client.js";
8
8
  import type { ShiitakeError } from "./errors.js";
9
9
  import type { ShiitakeEffectOptions, ShiitakeEffectOwner } from "./internal/effect-contracts.js";
10
10
  import type { SqliteOwner } from "./internal/contracts.js";
11
- export type { AbortCommand, BranchCommand, CompactCommand, CancelCommand, CancelResult, Accepted, ForkCommand, ForkedSession, PromptCommand, PromptDelivery, ReadOptions, SessionCommand, SessionCommandHandler, SessionFrame, SessionMessage, SessionProgress, SessionSnapshot, SessionActivity, AgentRunningWork, SubscribeOptions, TooLate, WatchOptions, } from "./client.js";
11
+ export type { AbortCommand, BranchCommand, CompactCommand, CancelCommand, CancelResult, Accepted, ForkCommand, ForkedSession, PromptCommand, PromptDelivery, ReadOptions, SessionCommand, SessionCommandHandler, SessionFrame, SessionMessage, SessionProgress, SessionToolOutput, SessionSnapshot, SessionActivity, AgentRunningWork, SubscribeOptions, TooLate, WatchOptions, } from "./client.js";
12
12
  export type { InstructionSnapshot, InstructionSource, ModelAcquisition, ModelDescription, ModelSource, RuntimeHost, RuntimePendingWork, SqliteCursor, SqliteOwner, SqliteValue, SqliteRow, GrantsDelivery, GrantsDeliveryResult, ShiitakeOptions, ToolAccess, HostMetaOptions, StandardHostMetaResult, StandardHostMetaSchema, ToolSource, } from "./internal/contracts.js";
13
13
  export type { InstructionSourceEffect, ModelAcquisitionEffect, ModelSourceEffect, RuntimeHostEffect, ShiitakeEffectAgent, ShiitakeEffectAgentExtensions, ShiitakeEffectCommandHandler, ShiitakeEffectOptions, ShiitakeEffectOwner, ShiitakeEffectSession, ShiitakeEffectSessionExtension, ShiitakeEffectSendOptions, ShiitakeEffectWatch, ToolSourceEffect, } from "./internal/effect-contracts.js";
14
14
  export type { SessionSummary } from "./client.js";
package/dist/errors.d.ts CHANGED
@@ -4,14 +4,14 @@ declare const ShiitakeErrorBoundarySchema: Schema.Literals<readonly ["contract",
4
4
  /** Stable boundary that rejected a Shiitake operation. */
5
5
  export type ShiitakeErrorBoundary = Schema.Schema.Type<typeof ShiitakeErrorBoundarySchema>;
6
6
  /** Safe failure category exposed by both Shiitake facades. */
7
- declare const ShiitakeErrorCauseSchema: Schema.Literals<readonly ["closed", "conflict", "incompatible", "invalid", "rejected", "unavailable", "storage_pressure", "context_too_large"]>;
7
+ declare const ShiitakeErrorCauseSchema: Schema.Literals<readonly ["closed", "conflict", "incompatible", "invalid", "rejected", "unavailable", "state_reset_required", "storage_pressure"]>;
8
8
  /** Safe failure category exposed by both Shiitake facades. */
9
9
  export type ShiitakeErrorCause = Schema.Schema.Type<typeof ShiitakeErrorCauseSchema>;
10
10
  /** Canonical serialized Shiitake failure. */
11
11
  export declare const ShiitakeErrorWireSchema: Schema.Struct<{
12
12
  readonly _tag: Schema.Literal<"ShiitakeError">;
13
13
  readonly boundary: Schema.Literals<readonly ["contract", "extensions", "instructions", "model", "runtime", "store", "tools"]>;
14
- readonly cause: Schema.Literals<readonly ["closed", "conflict", "incompatible", "invalid", "rejected", "unavailable", "storage_pressure", "context_too_large"]>;
14
+ readonly cause: Schema.Literals<readonly ["closed", "conflict", "incompatible", "invalid", "rejected", "unavailable", "state_reset_required", "storage_pressure"]>;
15
15
  readonly message: Schema.String;
16
16
  readonly requestId: Schema.optionalKey<Schema.String>;
17
17
  }>;
package/dist/errors.js CHANGED
@@ -17,10 +17,10 @@ const ShiitakeErrorCauseSchema = Schema.Literals([
17
17
  "invalid",
18
18
  "rejected",
19
19
  "unavailable",
20
+ /** Stored extension state needs an explicitly authorized reset, not a migration. */
21
+ "state_reset_required",
20
22
  /** Owner storage is near its budget; new Sessions and prompts wait. */
21
23
  "storage_pressure",
22
- /** One model context read is larger than the owner can safely decode. */
23
- "context_too_large",
24
24
  ]);
25
25
  const requestIdSchema = Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.isMaxLength(256)));
26
26
  /** Canonical serialized Shiitake failure. */
@@ -69,5 +69,13 @@ export type ExtensionContractV0<C extends CommandsV0, P, PI> = Readonly<{
69
69
  commands: C;
70
70
  projection: ProjectionSpecV0<P, PI> | null;
71
71
  }>;
72
- /** Activity already observed by the client, never an execution authority. */
73
- export type ClientActivityV0 = "idle" | "working" | "blocked" | "unknown";
72
+ /** What a client observed about the Session, never an execution authority. */
73
+ export type ClientActivityStateV0 = "idle" | "working" | "blocked" | "unknown";
74
+ /**
75
+ * Activity already observed by the client. `header` is only the short heading
76
+ * of what blocks the Session; it never carries question bodies or answers.
77
+ */
78
+ export type ClientActivityV0 = Readonly<{
79
+ state: ClientActivityStateV0;
80
+ header?: string;
81
+ }>;
@@ -14,7 +14,7 @@ import type { StateUpdate } from "./internal/extension-hook.js";
14
14
  import type { DocumentHandlesV0, DocumentsV0 } from "./session-documents.js";
15
15
  import { ExtensionId, extensionFingerprintSchema, extensionRevisionSchema, type CommandsV0, type ExtensionCapabilityV0, type ExtensionContractV0, type ExtensionManifestV0 } from "./extension-contract.js";
16
16
  export { ExtensionId, extensionIdentifier, extensionVersion, } from "./extension-contract.js";
17
- export type { AnyCommandSpecV0, ClientActivityV0, CommandsV0, CommandSpecV0, ExtensionCapabilityV0, ExtensionContractV0, ExtensionManifestV0, ProjectionSpecV0, ShiitakeExtensionProtocolV0, } from "./extension-contract.js";
17
+ export type { AnyCommandSpecV0, ClientActivityStateV0, ClientActivityV0, CommandsV0, CommandSpecV0, ExtensionCapabilityV0, ExtensionContractV0, ExtensionManifestV0, ProjectionSpecV0, ShiitakeExtensionProtocolV0, } from "./extension-contract.js";
18
18
  import { type RegisteredExtensionCommandV0 } from "./internal/extension-command-registry.js";
19
19
  export type { RegisteredExtensionCommandV0 } from "./internal/extension-command-registry.js";
20
20
  import { type CommandToolsV0 } from "./internal/extension-command-tools.js";
@@ -68,7 +68,7 @@ declare const ExtensionStateError_base: new <A extends Record<string, any> = {}>
68
68
  } & Readonly<A>;
69
69
  /** Durable record failure: stored or supplied values broke their schema or bounds. */
70
70
  export declare class ExtensionStateError extends ExtensionStateError_base<{
71
- reason: "decode" | "encode" | "bound" | "unavailable";
71
+ reason: "decode" | "encode" | "bound" | "unavailable" | "session_absent";
72
72
  message: string;
73
73
  }> {
74
74
  }
@@ -318,6 +318,8 @@ export type ExtensionContextV0<S extends CollectionsV0, Config extends Json, D e
318
318
  send: (input: SendInputV0) => Effect.Effect<SendResultV0, AdmissionError, ExtensionTx>;
319
319
  timers: TimersV0;
320
320
  attention: AttentionV0;
321
+ /** Committed selected Session position, or zero before its first entry. Requires state. */
322
+ sessionPosition: (sessionId: string) => Effect.Effect<number, ExtensionStateError, ExtensionRead>;
321
323
  /** Observe accepted steering without consuming Mule's next control delivery. Requires run_tools. */
322
324
  hasPendingSteer: (run: Pick<RunIdentityV0, "sessionId" | "workId">) => Effect.Effect<boolean, ExtensionStateError, ExtensionRead>;
323
325
  /** Whether this run durably recorded a successful tool outcome. Requires run_tools. */
@@ -549,6 +551,8 @@ export type ExtensionSlotV0 = Readonly<{
549
551
  }>;
550
552
  /** Borrowed view of the exact objects already built for compaction. */
551
553
  export type BorrowedCompactionViewV0 = Readonly<{
554
+ /** Session being compacted; a host binds the Agent itself. */
555
+ sessionId: string;
552
556
  /** Exact Pi preparation array summarized by the history call. */
553
557
  messagesToSummarize: readonly Message[];
554
558
  /** Exact Pi preparation array summarized for a split turn, when present. */
package/dist/fleet.d.ts CHANGED
@@ -8,7 +8,7 @@ import type { Json } from "./extensions.js";
8
8
  import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
9
9
  import { type CairnId } from "@fungi.computer/cairn/id";
10
10
  import { z } from "zod";
11
- import { type FungiAuthor } from "@fungi.computer/agar/agents";
11
+ import { type AuthorMeta } from "@fungi.computer/agar/agents";
12
12
  /** Operations explicitly admitted by the host. */
13
13
  export declare const agentActionSchema: z.ZodEnum<{
14
14
  send: "send";
@@ -78,6 +78,7 @@ export declare const agentCreationSchema: z.ZodLazy<z.ZodObject<{
78
78
  name: z.ZodString;
79
79
  requestId: z.ZodString;
80
80
  configuration: z.ZodString;
81
+ createdBy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
81
82
  }, z.core.$strict>>;
82
83
  /** One idempotent directory reservation. */
83
84
  export type AgentCreation = z.infer<typeof agentCreationSchema>;
@@ -90,6 +91,8 @@ export type AgentRename = z.infer<typeof agentRenameSchema>;
90
91
  /** One durable Agent directory record. */
91
92
  export type DirectoryAgent = Readonly<{
92
93
  agentId: AgentId;
94
+ /** The ordinary Agent that created this Agent; never the credential owner. */
95
+ createdBy: string | null;
93
96
  name: string;
94
97
  createdAt: number;
95
98
  updatedAt: number;
@@ -112,6 +115,8 @@ export declare function createAgentDirectory(sql: WatchdogSqliteOwner["sql"]): {
112
115
  create: (grant: AgentGrant, raw: AgentCreation) => {
113
116
  agent: Readonly<{
114
117
  agentId: AgentId;
118
+ /** The ordinary Agent that created this Agent; never the credential owner. */
119
+ createdBy: string | null;
115
120
  name: string;
116
121
  createdAt: number;
117
122
  updatedAt: number;
@@ -120,6 +125,8 @@ export declare function createAgentDirectory(sql: WatchdogSqliteOwner["sql"]): {
120
125
  } | {
121
126
  agent: Readonly<{
122
127
  agentId: AgentId;
128
+ /** The ordinary Agent that created this Agent; never the credential owner. */
129
+ createdBy: string | null;
123
130
  name: string;
124
131
  createdAt: number;
125
132
  updatedAt: number;
@@ -196,7 +203,7 @@ export type AuthenticatedDelegationReply = Readonly<{
196
203
  }>;
197
204
  /** The host metadata a delegated send carries, as JSON. */
198
205
  export type DelegationSendMeta = {
199
- author: FungiAuthor;
206
+ author: AuthorMeta["author"];
200
207
  delegation?: {
201
208
  ancestors: string[];
202
209
  maxDepth: number;
@@ -204,7 +211,7 @@ export type DelegationSendMeta = {
204
211
  };
205
212
  /** The host metadata on one authenticated reply line, as JSON. */
206
213
  export type DelegationReplyMeta = Readonly<{
207
- author: FungiAuthor;
214
+ author: AuthorMeta["author"];
208
215
  delegation: Readonly<{
209
216
  ancestors: readonly string[];
210
217
  maxDepth: number;
@@ -225,24 +232,50 @@ export declare function delegationProvenance(session: Readonly<{
225
232
  * and only the path inherited from the invoking line. A reply's verified facts
226
233
  * stay on that reply's own line and never travel onward.
227
234
  */
228
- export declare function delegationSendMeta(sender: FungiAuthor, invocationMeta?: Json): DelegationSendMeta;
235
+ export declare function delegationSendMeta(sender: Readonly<{
236
+ id: string;
237
+ name: string;
238
+ }>, invocationMeta?: Json): DelegationSendMeta;
229
239
  /**
230
240
  * The host metadata for one authenticated reply line: the replying Agent is
231
241
  * its author, and the verified reply rides beside the caller's own path.
232
242
  */
233
243
  export declare function delegationReplyMeta(delegation: AuthenticatedDelegationReply, replierName: string): DelegationReplyMeta;
234
244
  /**
235
- * Hosted BirdDog submission and Pending observation. The durable row freezes
245
+ * Delegated work an owner still waits on; `unreadable` says why its state
246
+ * could not be read, when it could not.
247
+ */
248
+ export type OutstandingDelegation = Readonly<{
249
+ agentId: string;
250
+ sessionId: string;
251
+ commandId: string;
252
+ unreadable?: string;
253
+ }>;
254
+ /**
255
+ * BirdDog submission and reply authentication. The durable row freezes
236
256
  * correspondence before transport; an ambiguous delivery reuses that exact mail.
237
257
  * Shiitake owns work deduplication, execution and settlement; Post owns replies.
238
258
  */
239
- export declare function createFleetDelegation(options: FleetDelegationOptions): {
240
- /** Authenticate a reply against its frozen mail and restore the caller's path. */
241
- authenticateReply: (mail: BirdDogMail) => AuthenticatedDelegationReply | undefined;
242
- /** Submit once and return the existing Shiitake admission receipt. */
259
+ export declare function createFleetCorrespondence(options: Omit<FleetDelegationOptions, "readSettlement">): {
260
+ /** Submit frozen mail or reuse its recorded Session admission receipt. */
243
261
  start: (grant: AgentGrant, raw: AgentDelegationTask) => Promise<Accepted>;
262
+ /** Match a reply to frozen mail and restore the caller's delegation path. */
263
+ authenticateReply: (mail: BirdDogMail) => AuthenticatedDelegationReply | undefined;
264
+ /** Read an existing acceptance receipt without reserving or delivering mail. */
265
+ receipt: (requestId: string) => Accepted | undefined;
266
+ };
267
+ /** Local hosts may observe exact work directly; remote correspondence never pulls it. */
268
+ export declare function createFleetDelegation(options: FleetDelegationOptions): {
244
269
  /** Submit or poll the exact turn, yielding Agar Pending until settled. */
245
270
  delegate: (grant: AgentGrant, task: AgentDelegationTask) => Promise<Pending | AgentDelegationOutcome>;
271
+ /** Submit frozen mail or reuse its recorded Session admission receipt. */
272
+ start: (grant: AgentGrant, raw: AgentDelegationTask) => Promise<Accepted>;
273
+ /** Match a reply to frozen mail and restore the caller's delegation path. */
274
+ authenticateReply: (mail: BirdDogMail) => AuthenticatedDelegationReply | undefined;
275
+ /** Read an existing acceptance receipt without reserving or delivering mail. */
276
+ receipt: (requestId: string) => Accepted | undefined;
277
+ /** The delegated work this owner still waits on, if any. */
278
+ outstanding: () => Promise<OutstandingDelegation | undefined>;
246
279
  };
247
280
  /** Acquire ordinary Session access; an in-process host applies meta with withMeta. */
248
281
  export type FleetAgentAccess = (agentId: string, meta?: Json) => Promise<Readonly<{
@@ -280,6 +313,7 @@ export declare function createFleetSessions(open: FleetAgentAccess): {
280
313
  pending: import("./client.js").SessionPendingPrompts;
281
314
  activity: import("./client.js").SessionActivity;
282
315
  progress: import("./client.js").SessionProgress | null;
316
+ toolOutput: readonly import("./client.js").SessionToolOutput[];
283
317
  extensions: import("./client.js").SessionExtensionSlots;
284
318
  attention: readonly import("./session-attention.js").SessionAttention[];
285
319
  context?: import("./client.js").SessionContext;