@fungi.computer/shiitake 0.0.0-stage → 0.1.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.
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 +77 -0
  6. package/dist/agent-control.js +434 -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 +313 -0
  26. package/dist/fleet.js +540 -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 +291 -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 +36 -0
  114. package/dist/internal/woodstock/fleet-store.js +100 -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 +884 -0
  156. package/docs/api/extensions.md +2003 -0
  157. package/docs/api/fleet.md +734 -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
@@ -0,0 +1,484 @@
1
+ import { CompactionInstructionsSchema } from "../client-boundary.js";
2
+ import { HostMetaSchema } from "../host-meta.js";
3
+ import { userContextMessage, VerifiedAgentReplySchema } from "./agent-reply.js";
4
+ import { SandboxFailureEventSchema } from "@fungi.computer/mycelium";
5
+ import * as Schema from "effect/Schema";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Result from "effect/Result";
8
+ import { ModelPreferenceSchema } from "../model-preference.js";
9
+ const closed = { onExcessProperty: "error" };
10
+ const encoder = new TextEncoder();
11
+ const boundedText = (maximumBytes) => Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.makeFilter((value) => encoder.encode(value).byteLength <= maximumBytes)));
12
+ const boundedId = boundedText(256);
13
+ const digest = Schema.String.pipe(Schema.check(Schema.isPattern(/^[0-9a-f]{64}$/u)));
14
+ const jsonValue = Schema.suspend(() => Schema.Union([
15
+ Schema.String,
16
+ Schema.Number,
17
+ Schema.Boolean,
18
+ Schema.Null,
19
+ Schema.Array(jsonValue),
20
+ Schema.Record(Schema.String, jsonValue),
21
+ ]));
22
+ const textContent = Schema.Struct({
23
+ type: Schema.Literal("text"),
24
+ text: Schema.String,
25
+ textSignature: Schema.optionalKey(Schema.String),
26
+ });
27
+ const thinkingContent = Schema.Struct({
28
+ type: Schema.Literal("thinking"),
29
+ thinking: Schema.String,
30
+ thinkingSignature: Schema.optionalKey(Schema.String),
31
+ redacted: Schema.optionalKey(Schema.Boolean),
32
+ });
33
+ const imageContent = Schema.Struct({
34
+ type: Schema.Literal("image"),
35
+ data: Schema.String,
36
+ mimeType: Schema.String,
37
+ });
38
+ const StoredToolCallSchema = Schema.Struct({
39
+ type: Schema.Literal("toolCall"),
40
+ id: Schema.String,
41
+ name: Schema.String,
42
+ arguments: Schema.Record(Schema.String, jsonValue),
43
+ thoughtSignature: Schema.optionalKey(Schema.String),
44
+ namespace: Schema.optionalKey(Schema.String),
45
+ });
46
+ /** Provider-native usage grammar shared by stored messages and settlement facts. */
47
+ export const usage = Schema.Struct({
48
+ input: Schema.Number,
49
+ output: Schema.Number,
50
+ cacheRead: Schema.Number,
51
+ cacheWrite: Schema.Number,
52
+ cacheWrite1h: Schema.optionalKey(Schema.Number),
53
+ reasoning: Schema.optionalKey(Schema.Number),
54
+ totalTokens: Schema.Number,
55
+ cost: Schema.Struct({
56
+ input: Schema.Number,
57
+ output: Schema.Number,
58
+ cacheRead: Schema.Number,
59
+ cacheWrite: Schema.Number,
60
+ total: Schema.Number,
61
+ }),
62
+ });
63
+ const userMessage = Schema.Struct({
64
+ role: Schema.Literal("user"),
65
+ content: Schema.Union([
66
+ Schema.String,
67
+ Schema.mutable(Schema.Array(Schema.Union([textContent, imageContent]))),
68
+ ]),
69
+ timestamp: Schema.Number,
70
+ });
71
+ const promptMessage = Schema.Struct({
72
+ role: Schema.Literal("user"),
73
+ content: Schema.String,
74
+ timestamp: Schema.Number,
75
+ });
76
+ const storedToolParameters = Schema.Record(Schema.String, jsonValue);
77
+ const storedToolSampling = Schema.Union([
78
+ Schema.Literal(false),
79
+ Schema.Struct({
80
+ type: Schema.Literal("json_schema"),
81
+ strict: Schema.Literals(["prefer", "require"]),
82
+ }),
83
+ Schema.Struct({
84
+ type: Schema.Literal("grammar"),
85
+ variants: Schema.Struct({
86
+ openai_lark: Schema.optionalKey(Schema.String),
87
+ openai_regex: Schema.optionalKey(Schema.String),
88
+ }),
89
+ }),
90
+ ]);
91
+ const storedPiTool = Schema.Struct({
92
+ name: Schema.String,
93
+ description: Schema.String,
94
+ parameters: storedToolParameters,
95
+ constrainedSampling: Schema.optionalKey(storedToolSampling),
96
+ });
97
+ const systemMessage = Schema.Struct({
98
+ role: Schema.Literal("system"),
99
+ content: Schema.Union([
100
+ Schema.String,
101
+ Schema.mutable(Schema.Array(textContent)),
102
+ ]),
103
+ sections: Schema.optionalKey(Schema.Record(Schema.String, Schema.Union([Schema.String, Schema.Null]))),
104
+ toolsAdded: Schema.optionalKey(Schema.mutable(Schema.Array(storedPiTool))),
105
+ toolsRemoved: Schema.optionalKey(Schema.mutable(Schema.Array(Schema.Struct({ name: Schema.String })))),
106
+ timestamp: Schema.Number,
107
+ });
108
+ const assistantMessage = Schema.Struct({
109
+ role: Schema.Literal("assistant"),
110
+ content: Schema.mutable(Schema.Array(Schema.Union([textContent, thinkingContent, StoredToolCallSchema]))),
111
+ api: Schema.String,
112
+ provider: Schema.String,
113
+ model: Schema.String,
114
+ responseModel: Schema.optionalKey(Schema.String),
115
+ responseId: Schema.optionalKey(Schema.String),
116
+ diagnostics: Schema.optionalKey(Schema.mutable(Schema.Array(Schema.Struct({
117
+ type: Schema.String,
118
+ timestamp: Schema.Number,
119
+ error: Schema.optionalKey(Schema.Struct({
120
+ name: Schema.optionalKey(Schema.String),
121
+ message: Schema.String,
122
+ stack: Schema.optionalKey(Schema.String),
123
+ code: Schema.optionalKey(Schema.Union([Schema.String, Schema.Number])),
124
+ })),
125
+ details: Schema.optionalKey(Schema.Record(Schema.String, jsonValue)),
126
+ })))),
127
+ usage,
128
+ stopReason: Schema.Literals([
129
+ "pending",
130
+ "stop",
131
+ "length",
132
+ "toolUse",
133
+ "error",
134
+ "aborted",
135
+ "deferred",
136
+ ]),
137
+ deferred: Schema.optionalKey(Schema.Struct({
138
+ provider: Schema.String,
139
+ modelId: Schema.String,
140
+ api: Schema.String,
141
+ id: Schema.String,
142
+ expiresAt: Schema.optionalKey(Schema.Number),
143
+ pollAfterMs: Schema.optionalKey(Schema.Number),
144
+ data: Schema.optionalKey(jsonValue),
145
+ })),
146
+ errorMessage: Schema.optionalKey(Schema.String),
147
+ rawStopReason: Schema.optionalKey(Schema.String),
148
+ providerThinkingLevel: Schema.optionalKey(Schema.String),
149
+ endTurn: Schema.optionalKey(Schema.Boolean),
150
+ timestamp: Schema.Number,
151
+ });
152
+ const toolResultMessage = Schema.Struct({
153
+ role: Schema.Literal("toolResult"),
154
+ toolCallId: Schema.String,
155
+ toolName: Schema.String,
156
+ content: Schema.mutable(Schema.Array(Schema.Union([textContent, imageContent]))),
157
+ details: Schema.optionalKey(jsonValue),
158
+ usage: Schema.optionalKey(usage),
159
+ isError: Schema.Boolean,
160
+ timestamp: Schema.Number,
161
+ });
162
+ /** Exact Pi message grammar stored by Woodstock without normalization. */
163
+ export const StoredPiMessageSchema = Schema.Union([
164
+ systemMessage,
165
+ userMessage,
166
+ assistantMessage,
167
+ toolResultMessage,
168
+ ]);
169
+ const causalKind = Schema.Literals([
170
+ "run",
171
+ "turn",
172
+ "message",
173
+ "tool_call",
174
+ "tool_result",
175
+ "terminal",
176
+ ]);
177
+ const CausalIdentitySchema = Schema.suspend(() => Schema.Struct({
178
+ kind: causalKind,
179
+ value: boundedId,
180
+ parent: Schema.optional(CausalIdentitySchema),
181
+ }));
182
+ const LiveRetryEventSchema = Schema.Union([
183
+ Schema.Struct({
184
+ type: Schema.Literal("auto_retry_start"),
185
+ run: CausalIdentitySchema,
186
+ attempt: Schema.Number,
187
+ maxAttempts: Schema.Number,
188
+ delayMs: Schema.Number,
189
+ errorMessage: Schema.String,
190
+ }),
191
+ Schema.Struct({
192
+ type: Schema.Literal("auto_retry_attempt"),
193
+ run: CausalIdentitySchema,
194
+ attempt: Schema.Number,
195
+ }),
196
+ Schema.Struct({
197
+ type: Schema.Literal("auto_retry_end"),
198
+ run: CausalIdentitySchema,
199
+ attempt: Schema.Number,
200
+ success: Schema.Boolean,
201
+ finalError: Schema.optionalKey(Schema.String),
202
+ }),
203
+ ]);
204
+ const fingerprint = Schema.String.pipe(Schema.check(Schema.isPattern(/^sha256:[0-9a-f]{64}$/u)));
205
+ /**
206
+ * A roster fingerprint snapshot. Admission records what was offered then;
207
+ * execution facts record what the reopened owner actually ran.
208
+ */
209
+ const runContract = Schema.Struct({
210
+ core: digest,
211
+ extensions: Schema.Array(Schema.Tuple([boundedId, fingerprint])).pipe(Schema.check(Schema.isMaxLength(128))),
212
+ });
213
+ const StoredToolResultSchema = Schema.Struct({
214
+ content: Schema.mutable(Schema.Array(Schema.Union([textContent, imageContent]))),
215
+ details: Schema.optional(jsonValue),
216
+ usage: Schema.optional(usage),
217
+ isError: Schema.Boolean,
218
+ terminate: Schema.optional(Schema.Boolean),
219
+ });
220
+ const common = { workId: boundedId };
221
+ /** What started one compaction: the user, the context threshold, or an overflow. */
222
+ const CompactionTriggerSchema = Schema.Literals([
223
+ "manual",
224
+ "threshold",
225
+ "overflow",
226
+ ]);
227
+ /** Closed grammar stored in one retained Session entry JSON column. */
228
+ const StoredSessionEntrySchema = Schema.Union([
229
+ Schema.Struct({
230
+ ...common,
231
+ kind: Schema.Literal("sandbox_failure"),
232
+ event: SandboxFailureEventSchema,
233
+ }),
234
+ Schema.Struct({
235
+ kind: Schema.Literal("document"),
236
+ workId: Schema.Null,
237
+ extensionId: boundedId,
238
+ name: boundedId,
239
+ valueJson: Schema.String,
240
+ }),
241
+ Schema.Struct({
242
+ kind: Schema.Literal("model_preference"),
243
+ workId: Schema.Null,
244
+ preference: Schema.NullOr(ModelPreferenceSchema),
245
+ }),
246
+ Schema.Struct({
247
+ ...common,
248
+ kind: Schema.Literal("command"),
249
+ meta: Schema.optionalKey(HostMetaSchema),
250
+ /** Present only when the host admitted this line as a verified Agent reply. */
251
+ reply: Schema.optionalKey(VerifiedAgentReplySchema),
252
+ delivery: Schema.optionalKey(Schema.Literals(["steer", "followUp"])),
253
+ requestId: boundedId,
254
+ targetWorkId: Schema.optionalKey(boundedId),
255
+ receiptId: boundedId,
256
+ message: userMessage,
257
+ contract: runContract,
258
+ }),
259
+ // A manual compaction request: accepted Session work with no message. It
260
+ // never enters model context, a title or the client message projection.
261
+ Schema.Struct({
262
+ ...common,
263
+ kind: Schema.Literal("command"),
264
+ work: Schema.Literal("compact"),
265
+ /** The user's focus for the summary, as Pi's `/compact <instructions>`. */
266
+ instructions: Schema.optionalKey(CompactionInstructionsSchema),
267
+ meta: Schema.optionalKey(HostMetaSchema),
268
+ requestId: boundedId,
269
+ receiptId: boundedId,
270
+ timestamp: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
271
+ contract: runContract,
272
+ }),
273
+ Schema.Struct({
274
+ kind: Schema.Literal("operator"),
275
+ workId: Schema.Null,
276
+ requestId: boundedId,
277
+ author: Schema.Literals(["operator", "party"]),
278
+ meta: Schema.optionalKey(HostMetaSchema),
279
+ message: promptMessage,
280
+ }),
281
+ Schema.Struct({
282
+ ...common,
283
+ kind: Schema.Literal("cancellation"),
284
+ requestId: boundedId,
285
+ receiptId: boundedId,
286
+ targetWorkId: boundedId,
287
+ meta: Schema.optionalKey(HostMetaSchema),
288
+ }),
289
+ Schema.Struct({
290
+ ...common,
291
+ kind: Schema.Literal("model_selection"),
292
+ provider: boundedId,
293
+ model: boundedId,
294
+ catalogRevision: boundedId,
295
+ definitionRevision: boundedId,
296
+ selectionPolicyRevision: boundedId,
297
+ }),
298
+ Schema.Struct({
299
+ ...common,
300
+ kind: Schema.Literal("run_started"),
301
+ run: CausalIdentitySchema,
302
+ contract: runContract,
303
+ }),
304
+ Schema.Struct({
305
+ ...common,
306
+ kind: Schema.Literal("turn_started"),
307
+ run: CausalIdentitySchema,
308
+ turn: CausalIdentitySchema,
309
+ }),
310
+ Schema.Struct({
311
+ ...common,
312
+ kind: Schema.Literal("model_prompt"),
313
+ run: CausalIdentitySchema,
314
+ turn: CausalIdentitySchema,
315
+ attempt: Schema.Number.pipe(Schema.check(Schema.isInt()), Schema.check(Schema.isGreaterThan(0))),
316
+ contract: runContract,
317
+ systemPrompt: Schema.String.pipe(Schema.check(Schema.makeFilter((value) => encoder.encode(value).byteLength <= 1024 * 1024))),
318
+ }),
319
+ Schema.Struct({
320
+ ...common,
321
+ kind: Schema.Literal("message_start"),
322
+ run: CausalIdentitySchema,
323
+ turn: CausalIdentitySchema,
324
+ message: CausalIdentitySchema,
325
+ role: Schema.Literals(["assistant", "system", "toolResult", "user"]),
326
+ }),
327
+ Schema.Struct({
328
+ ...common,
329
+ kind: Schema.Literal("message"),
330
+ run: CausalIdentitySchema,
331
+ turn: CausalIdentitySchema,
332
+ message: CausalIdentitySchema,
333
+ value: StoredPiMessageSchema,
334
+ }),
335
+ Schema.Struct({
336
+ ...common,
337
+ kind: Schema.Literal("tool_intent"),
338
+ replay: Schema.Literals(["safe", "unsafe"]).pipe(Schema.withDecodingDefaultType(Effect.succeed("unsafe"))),
339
+ contract: runContract,
340
+ run: CausalIdentitySchema,
341
+ turn: CausalIdentitySchema,
342
+ toolCall: CausalIdentitySchema,
343
+ call: StoredToolCallSchema,
344
+ }),
345
+ Schema.Struct({
346
+ ...common,
347
+ kind: Schema.Literal("tool_outcome"),
348
+ run: CausalIdentitySchema,
349
+ turn: CausalIdentitySchema,
350
+ toolCall: CausalIdentitySchema,
351
+ call: StoredToolCallSchema,
352
+ result: StoredToolResultSchema,
353
+ }),
354
+ Schema.Struct({
355
+ ...common,
356
+ kind: Schema.Literal("live_retry"),
357
+ event: LiveRetryEventSchema,
358
+ }),
359
+ Schema.Struct({
360
+ ...common,
361
+ kind: Schema.Literal("compaction"),
362
+ resumesRun: Schema.optionalKey(Schema.Boolean),
363
+ // Optional only because Sessions compacted before these facts existed
364
+ // are still live; every new compaction records both.
365
+ trigger: Schema.optionalKey(CompactionTriggerSchema),
366
+ /** Estimated context tokens right after this compaction. */
367
+ tokensAfter: Schema.optionalKey(Schema.Number),
368
+ firstKeptEntryId: boundedId,
369
+ summary: Schema.String,
370
+ tokensBefore: Schema.Number,
371
+ usage,
372
+ contextRevision: boundedId,
373
+ details: Schema.optionalKey(Schema.Struct({
374
+ readFiles: Schema.Array(Schema.String),
375
+ modifiedFiles: Schema.Array(Schema.String),
376
+ })),
377
+ }),
378
+ // A compaction that committed nothing: a manual request with nothing to
379
+ // summarize, or any compaction that failed. Clients show it as a result.
380
+ Schema.Struct({
381
+ ...common,
382
+ kind: Schema.Literal("compaction_notice"),
383
+ trigger: CompactionTriggerSchema,
384
+ result: Schema.Literals(["nothing_to_compact", "failed"]),
385
+ diagnostic: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(200))),
386
+ }),
387
+ Schema.Struct({
388
+ ...common,
389
+ kind: Schema.Literal("run_terminal"),
390
+ run: CausalIdentitySchema,
391
+ terminal: CausalIdentitySchema,
392
+ outcome: Schema.Literals(["completed", "cancelled", "failed"]),
393
+ }),
394
+ Schema.Struct({
395
+ ...common,
396
+ kind: Schema.Literal("work_terminal"),
397
+ diagnostic: Schema.optionalKey(Schema.String),
398
+ outcome: Schema.Literals([
399
+ "completed",
400
+ "interrupted",
401
+ "cancelled",
402
+ "failed",
403
+ ]),
404
+ }),
405
+ ]);
406
+ const StoredSessionEntryTextSchema = Schema.fromJsonString(StoredSessionEntrySchema);
407
+ const JsonTextSchema = Schema.fromJsonString(Schema.Unknown);
408
+ const decodeEntryJson = Schema.decodeUnknownResult(StoredSessionEntryTextSchema, closed);
409
+ const encodeJson = Schema.encodeSync(JsonTextSchema);
410
+ const validateEntryJson = Schema.decodeUnknownSync(StoredSessionEntryTextSchema, closed);
411
+ /** Whether accepted work is a manual compaction request. */
412
+ export function isCompactCommand(entry) {
413
+ return "work" in entry;
414
+ }
415
+ /** The prompt a command carries; a compaction request carries none. */
416
+ export function promptCommand(entry) {
417
+ return entry.kind === "command" && !isCompactCommand(entry)
418
+ ? entry
419
+ : undefined;
420
+ }
421
+ /** When the command was accepted, as its own data records it. */
422
+ export function commandTimestamp(entry) {
423
+ return isCompactCommand(entry) ? entry.timestamp : entry.message.timestamp;
424
+ }
425
+ /**
426
+ * What a later model run sees of an operator line: always a user-role note,
427
+ * so a human's words are never presented as the model's own output.
428
+ */
429
+ export function operatorContextMessage(entry) {
430
+ return userContextMessage(entry.author === "party"
431
+ ? entry.message
432
+ : {
433
+ role: "user",
434
+ content: `[A human operator replied to the user on your behalf]\n${entry.message.content}`,
435
+ timestamp: entry.message.timestamp,
436
+ });
437
+ }
438
+ /**
439
+ * What a model run sees of an accepted command: its stored user line, wrapped
440
+ * as a verified Agent reply only when the host admitted it as one.
441
+ */
442
+ export function commandContextMessage(entry) {
443
+ return userContextMessage(entry.message, entry.reply);
444
+ }
445
+ /**
446
+ * What a client sees of an operator line: the party's words as a user
447
+ * message, the operator's as an assistant message from provider `operator`.
448
+ */
449
+ export function operatorClientMessage(entry) {
450
+ if (entry.author === "party")
451
+ return entry.message;
452
+ const zero = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
453
+ return {
454
+ role: "assistant",
455
+ content: [{ type: "text", text: entry.message.content }],
456
+ api: "operator",
457
+ provider: "operator",
458
+ model: "operator",
459
+ usage: {
460
+ input: 0,
461
+ output: 0,
462
+ cacheRead: 0,
463
+ cacheWrite: 0,
464
+ totalTokens: 0,
465
+ cost: zero,
466
+ },
467
+ stopReason: "stop",
468
+ timestamp: entry.message.timestamp,
469
+ };
470
+ }
471
+ /** Decode the one JSON representation admitted by retained Session storage. */
472
+ export function decodeStoredSessionEntry(value) {
473
+ return Result.map(decodeEntryJson(value), (entry) => entry.kind === "command" &&
474
+ !isCompactCommand(entry) &&
475
+ entry.delivery === undefined
476
+ ? { ...entry, delivery: "followUp" }
477
+ : entry);
478
+ }
479
+ /** Serialize Pi's JSON representation, then validate the retained entry. */
480
+ export function encodeStoredSessionEntry(entry) {
481
+ const encoded = encodeJson(entry);
482
+ validateEntryJson(encoded);
483
+ return encoded;
484
+ }
@@ -0,0 +1,24 @@
1
+ import type { Grants } from "@fungi.computer/mycelium";
2
+ import type { SqliteOwner } from "../contracts.js";
3
+ /** One versioned delivery of a Session's grants. */
4
+ export type GrantsDelivery = Readonly<{
5
+ version: number;
6
+ grants: Grants;
7
+ }>;
8
+ /** Whether a delivery replaced the stored grants or repeated them. */
9
+ export type GrantsDeliveryResult = "applied" | "unchanged";
10
+ /** Woodstock keeps each Session's newest delivered grants beside it. */
11
+ export declare function prepareSessionGrants(owner: SqliteOwner): void;
12
+ /**
13
+ * Store one versioned delivery of a Session's grants. A delivery may arrive
14
+ * before the Session's first prompt. An older version is stale and never
15
+ * writes. The same version with other grants replaces the stored grants: the
16
+ * host recomputed its decision (for example after a deploy) without a new
17
+ * version, and the newest computation always wins over the old grant. Only
18
+ * the in-process host delivers grants.
19
+ */
20
+ export declare function deliverSessionGrants(owner: SqliteOwner, sessionId: string, delivery: GrantsDelivery): GrantsDeliveryResult;
21
+ /** One Session's newest delivery; null when the host delivered none. */
22
+ export declare function readSessionGrants(owner: SqliteOwner, sessionId: string): GrantsDelivery | null;
23
+ /** A fork keeps its source's delivery, so it never widens to the default. */
24
+ export declare function copySessionGrants(owner: SqliteOwner, fromSessionId: string, toSessionId: string): void;
@@ -0,0 +1,84 @@
1
+ import * as Result from "effect/Result";
2
+ import * as Schema from "effect/Schema";
3
+ import { ShiitakeError } from "../../errors.js";
4
+ /**
5
+ * One Session's tool grants as the host delivered them: module or extension
6
+ * ids mapped to a grant level. Bounded, so a host cannot grow the row.
7
+ */
8
+ const SessionGrantsSchema = Schema.Record(Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(160)), Schema.Literals(["none", "read", "write"])).check(Schema.makeFilter((grants) => Object.keys(grants).length <= 256));
9
+ /**
10
+ * One versioned delivery of a host decision. The host owns the decision and
11
+ * its version; Shiitake keeps only the newest delivery it has seen.
12
+ */
13
+ const GrantsDeliverySchema = Schema.Struct({
14
+ /** The host's own version of this decision; deliveries only move forward. */
15
+ version: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(Number.MAX_SAFE_INTEGER)),
16
+ grants: SessionGrantsSchema,
17
+ });
18
+ const decodeDelivery = Schema.decodeUnknownResult(GrantsDeliverySchema, {
19
+ onExcessProperty: "error",
20
+ });
21
+ const decodeStoredGrants = Schema.decodeUnknownResult(Schema.fromJsonString(SessionGrantsSchema));
22
+ const encodeGrants = Schema.encodeSync(Schema.fromJsonString(SessionGrantsSchema));
23
+ const decodeRow = Schema.decodeUnknownResult(Schema.Struct({ version: Schema.Int, grants_json: Schema.String }));
24
+ /** Woodstock keeps each Session's newest delivered grants beside it. */
25
+ export function prepareSessionGrants(owner) {
26
+ owner.sql.exec(`CREATE TABLE IF NOT EXISTS session_tool_grants (
27
+ session_id TEXT PRIMARY KEY,
28
+ version INTEGER NOT NULL,
29
+ grants_json TEXT NOT NULL
30
+ )`);
31
+ }
32
+ /**
33
+ * Store one versioned delivery of a Session's grants. A delivery may arrive
34
+ * before the Session's first prompt. An older version is stale and never
35
+ * writes. The same version with other grants replaces the stored grants: the
36
+ * host recomputed its decision (for example after a deploy) without a new
37
+ * version, and the newest computation always wins over the old grant. Only
38
+ * the in-process host delivers grants.
39
+ */
40
+ export function deliverSessionGrants(owner, sessionId, delivery) {
41
+ const decoded = decodeDelivery(delivery);
42
+ if (Result.isFailure(decoded))
43
+ throw new ShiitakeError("contract", "invalid", "Session grants delivery is invalid");
44
+ const grantsJson = encodeGrants(decoded.success.grants);
45
+ const stored = readStoredRow(owner, sessionId);
46
+ if (stored !== undefined) {
47
+ if (decoded.success.version < stored.version)
48
+ throw new ShiitakeError("contract", "conflict", "Session grants delivery is older than the stored delivery");
49
+ if (decoded.success.version === stored.version &&
50
+ stored.grantsJson === grantsJson)
51
+ return "unchanged";
52
+ }
53
+ owner.sql.exec(`INSERT INTO session_tool_grants (session_id, version, grants_json)
54
+ VALUES (?, ?, ?)
55
+ ON CONFLICT(session_id) DO UPDATE SET
56
+ version = excluded.version, grants_json = excluded.grants_json`, sessionId, decoded.success.version, grantsJson);
57
+ return "applied";
58
+ }
59
+ function readStoredRow(owner, sessionId) {
60
+ const [raw] = owner.sql
61
+ .exec("SELECT version, grants_json FROM session_tool_grants WHERE session_id = ?", sessionId)
62
+ .toArray();
63
+ if (raw === undefined)
64
+ return undefined;
65
+ const row = decodeRow(raw);
66
+ if (Result.isFailure(row))
67
+ throw new ShiitakeError("store", "unavailable", "Stored Session grants are invalid");
68
+ return { version: row.success.version, grantsJson: row.success.grants_json };
69
+ }
70
+ /** One Session's newest delivery; null when the host delivered none. */
71
+ export function readSessionGrants(owner, sessionId) {
72
+ const stored = readStoredRow(owner, sessionId);
73
+ if (stored === undefined)
74
+ return null;
75
+ const grants = decodeStoredGrants(stored.grantsJson);
76
+ if (Result.isFailure(grants))
77
+ throw new ShiitakeError("store", "unavailable", "Stored Session grants are invalid");
78
+ return { version: stored.version, grants: grants.success };
79
+ }
80
+ /** A fork keeps its source's delivery, so it never widens to the default. */
81
+ export function copySessionGrants(owner, fromSessionId, toSessionId) {
82
+ owner.sql.exec(`INSERT INTO session_tool_grants (session_id, version, grants_json)
83
+ SELECT ?, version, grants_json FROM session_tool_grants WHERE session_id = ?`, toSessionId, fromSessionId);
84
+ }
@@ -0,0 +1,56 @@
1
+ import type { WatchdogQueueTransition } from "@fungi.computer/watchdog/effect";
2
+ import type { SessionSummaryPage, SessionSummaryRow } from "../../client.js";
3
+ import type { SqliteOwner } from "../contracts.js";
4
+ /** Create the inbox table, its keyset and revision indexes and the revision clock. */
5
+ export declare function prepareSessionInbox(owner: SqliteOwner): void;
6
+ /** Conversation facts Woodstock supplies with each Session write. */
7
+ export type SessionInboxFacts = Readonly<{
8
+ sessionId: string;
9
+ /** Conversation activity; absent for display-only writes such as a title. */
10
+ activityAt?: number;
11
+ /**
12
+ * First prompt title; null when the stored lineage is unreadable, and
13
+ * undefined when this write cannot change it.
14
+ */
15
+ title?: string | null;
16
+ }>;
17
+ /**
18
+ * Upsert title and recency in the caller's Session transaction. A new row
19
+ * starts `done`: nothing has run, and Watchdog's projector owns run state.
20
+ */
21
+ export declare function touchSessionInbox(owner: SqliteOwner, facts: SessionInboxFacts): void;
22
+ /** Record a deletion in the Session owner's transaction and revision stream. */
23
+ export declare function recordSessionInboxDeletion(owner: SqliteOwner, sessionId: string): void;
24
+ /** Drop tombstones the current member subscribers have all acknowledged. */
25
+ export declare function pruneSessionInboxTombstones(owner: SqliteOwner, through: number): void;
26
+ /**
27
+ * Watchdog's same-transaction projector. Queues that are not saved Sessions
28
+ * have no row and are left alone.
29
+ */
30
+ export declare function projectSessionRun(owner: SqliteOwner, transition: WatchdogQueueTransition, now: number): void;
31
+ /** Refresh attention using the current queue head in the extension transaction. */
32
+ export declare function projectSessionAttention(owner: SqliteOwner, sessionId: string, head: WatchdogQueueTransition["head"]): void;
33
+ /** Revisioned Session inbox changes after one owner revision, oldest first. */
34
+ export type SessionInboxChanges = Readonly<{
35
+ rows: readonly (SessionSummaryRow | Readonly<{
36
+ sessionId: string;
37
+ deleted: true;
38
+ }>)[];
39
+ /** Revision of the last returned row, or `after` when none changed. */
40
+ through: number;
41
+ /** Latest owner revision; `through < latest` means more remain. */
42
+ latest: number;
43
+ }>;
44
+ /**
45
+ * A bounded strict-after read of changed rows. Each Session appears once, at
46
+ * its latest revision, so pending delivery is bounded by Session count.
47
+ */
48
+ export declare function readSessionInboxChanges(owner: SqliteOwner, after: number, limit: number): SessionInboxChanges;
49
+ /** Indexed lookup using the same row decoder and availability as the inbox listing. */
50
+ export declare function readSessionInboxSummary(owner: SqliteOwner, sessionId: string): SessionSummaryRow | undefined;
51
+ /**
52
+ * One bounded page in tier, then newest-activity order: a single indexed
53
+ * statement regardless of how many Sessions the Agent holds. A row whose
54
+ * stored facts are unreadable is its own `unavailable` row.
55
+ */
56
+ export declare function readSessionInboxPage(owner: SqliteOwner, after: string | undefined): SessionSummaryPage;