@fungi.computer/shiitake 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +59 -21
  2. package/dist/agent-control.d.ts +94 -2
  3. package/dist/agent-control.js +147 -26
  4. package/dist/client-entry.d.ts +20 -0
  5. package/dist/client-entry.js +5 -0
  6. package/dist/client-visitor.d.ts +33 -0
  7. package/dist/client-visitor.js +231 -0
  8. package/dist/client-wire.d.ts +9 -0
  9. package/dist/client-wire.js +24 -3
  10. package/dist/client.d.ts +27 -6
  11. package/dist/client.js +23 -0
  12. package/dist/cloudflare.d.ts +20 -0
  13. package/dist/cloudflare.js +4 -1
  14. package/dist/delegation.d.ts +79 -53
  15. package/dist/delegation.js +26 -21
  16. package/dist/effect.d.ts +26 -1
  17. package/dist/effect.js +14 -1
  18. package/dist/errors.d.ts +2 -2
  19. package/dist/errors.js +2 -0
  20. package/dist/extensions.d.ts +22 -3
  21. package/dist/extensions.js +1 -1
  22. package/dist/fleet.d.ts +25 -2
  23. package/dist/fleet.js +108 -24
  24. package/dist/hosted.js +3 -0
  25. package/dist/http-routes.d.ts +23 -9
  26. package/dist/http.d.ts +1 -0
  27. package/dist/http.js +1 -0
  28. package/dist/index.d.ts +1 -1
  29. package/dist/internal/accepted-run-executor.js +2 -1
  30. package/dist/internal/admission-door.d.ts +6 -1
  31. package/dist/internal/admission-door.js +33 -3
  32. package/dist/internal/contracts.d.ts +19 -1
  33. package/dist/internal/coordinator.js +115 -52
  34. package/dist/internal/effect-client.js +1 -1
  35. package/dist/internal/effect-contracts.d.ts +9 -2
  36. package/dist/internal/extension-command-registry.d.ts +2 -1
  37. package/dist/internal/extension-command-registry.js +3 -1
  38. package/dist/internal/extension-runtime.d.ts +3 -1
  39. package/dist/internal/extension-runtime.js +18 -8
  40. package/dist/internal/model-acquisition.js +2 -0
  41. package/dist/internal/observation-kernel.d.ts +2 -0
  42. package/dist/internal/observation-kernel.js +17 -6
  43. package/dist/internal/promise-facade.js +2 -0
  44. package/dist/internal/run-scope.d.ts +14 -0
  45. package/dist/internal/run-scope.js +58 -15
  46. package/dist/internal/runtime-coordinator.d.ts +14 -0
  47. package/dist/internal/runtime-coordinator.js +60 -11
  48. package/dist/internal/runtime-core.d.ts +3 -0
  49. package/dist/internal/runtime-core.js +1 -0
  50. package/dist/internal/runtime-reconciliation.js +123 -25
  51. package/dist/internal/session-context.d.ts +5 -5
  52. package/dist/internal/session-context.js +80 -83
  53. package/dist/internal/session-status.d.ts +2 -2
  54. package/dist/internal/session-status.js +5 -3
  55. package/dist/internal/woodstock/agent-reply.d.ts +5 -0
  56. package/dist/internal/woodstock/agent-reply.js +3 -0
  57. package/dist/internal/woodstock/client-projection.js +13 -11
  58. package/dist/internal/woodstock/fleet-store.d.ts +3 -2
  59. package/dist/internal/woodstock/fleet-store.js +24 -4
  60. package/dist/internal/woodstock/index.d.ts +7 -3
  61. package/dist/internal/woodstock/index.js +23 -6
  62. package/dist/internal/woodstock/progress.d.ts +11 -1
  63. package/dist/internal/woodstock/progress.js +126 -25
  64. package/dist/internal/woodstock/session-delegator.d.ts +13 -0
  65. package/dist/internal/woodstock/session-delegator.js +44 -0
  66. package/dist/internal/woodstock/session-entry.d.ts +12 -2
  67. package/dist/internal/woodstock/session-store.d.ts +16 -2
  68. package/dist/internal/woodstock/session-store.js +349 -66
  69. package/dist/internal/woodstock/work-senders.d.ts +32 -0
  70. package/dist/internal/woodstock/work-senders.js +70 -0
  71. package/dist/node.js +11 -2
  72. package/dist/remote-client.js +7 -2
  73. package/dist/server-visitor-stream.d.ts +13 -0
  74. package/dist/server-visitor-stream.js +124 -0
  75. package/dist/session-attention.d.ts +30 -0
  76. package/dist/session-attention.js +14 -0
  77. package/docs/api/agent-control.md +212 -15
  78. package/docs/api/client-entry.md +3 -0
  79. package/docs/api/client.md +209 -5
  80. package/docs/api/cloudflare.md +23 -1
  81. package/docs/api/delegation.md +284 -77
  82. package/docs/api/effect.md +100 -12
  83. package/docs/api/extensions.md +73 -12
  84. package/docs/api/fleet.md +138 -25
  85. package/docs/api/http.md +28 -0
  86. package/docs/api/index.md +43 -17
  87. package/docs/api/testing.md +9 -7
  88. package/package.json +10 -6
@@ -0,0 +1,231 @@
1
+ import { authorMeta, messageAuthor, FungiMessageMetaSchema, } from "@fungi.computer/agar/agents";
2
+ /** Filter participant delivery only; the holder's stored Session is unchanged. */
3
+ export function participantSnapshot(snapshot, owner) {
4
+ const active = snapshot.activity.queue.state !== "idle" &&
5
+ owner.turnIsCurrent(snapshot.sessionId, snapshot.activity.queue.commandId);
6
+ return {
7
+ ...snapshot,
8
+ messages: snapshot.messages.filter(({ id, commandId, value }) => value.role !== "assistant" ||
9
+ value.provider === "operator" ||
10
+ (commandId !== null &&
11
+ owner.deliverAgentReply(snapshot.sessionId, commandId, {
12
+ kind: "message",
13
+ messageId: id,
14
+ }))),
15
+ progress: active ? snapshot.progress : null,
16
+ activity: active
17
+ ? snapshot.activity
18
+ : {
19
+ ...snapshot.activity,
20
+ queue: { state: "idle" },
21
+ cancellationRequested: false,
22
+ activeTool: null,
23
+ retry: null,
24
+ lastModelChoiceSequence: null,
25
+ compaction: null,
26
+ },
27
+ };
28
+ }
29
+ function sourceRequestId(meta) {
30
+ const parsed = FungiMessageMetaSchema.parse(meta);
31
+ if (parsed.sourceRequestId === undefined)
32
+ throw new Error("Visitor entry is missing its original request identity");
33
+ return parsed.sourceRequestId;
34
+ }
35
+ /** Project one owner-selected prompt without exposing host identity or correlation metadata. */
36
+ export function visitorPendingPrompt(prompt, visitorId) {
37
+ if (messageAuthor(prompt.meta)?.id !== visitorId)
38
+ throw new Error("Visitor pending lookup returned another author's prompt");
39
+ const projected = {
40
+ commandId: prompt.commandId,
41
+ requestId: sourceRequestId(prompt.meta),
42
+ text: prompt.text,
43
+ delivery: prompt.delivery,
44
+ };
45
+ if (prompt.images !== undefined)
46
+ return { ...projected, images: prompt.images };
47
+ return projected;
48
+ }
49
+ function userText(value) {
50
+ return Array.isArray(value.content)
51
+ ? value.content
52
+ .flatMap((part) => (part.type === "text" ? [part.text] : []))
53
+ .join("")
54
+ : value.content;
55
+ }
56
+ function visitorCommands(snapshot, visitorId) {
57
+ const commands = new Set();
58
+ for (const message of snapshot.messages)
59
+ if (message.value.role === "user" &&
60
+ messageAuthor(message.meta)?.id === visitorId &&
61
+ message.commandId !== null)
62
+ commands.add(message.commandId);
63
+ for (const prompt of snapshot.pending.prompts)
64
+ if (messageAuthor(prompt.meta)?.id === visitorId)
65
+ commands.add(prompt.commandId);
66
+ return commands;
67
+ }
68
+ function visibleMessages(snapshot, visitorId) {
69
+ const commands = visitorCommands(snapshot, visitorId);
70
+ return snapshot.messages.filter(({ commandId, value, meta }) => value.role === "user"
71
+ ? messageAuthor(meta)?.id === visitorId
72
+ : value.role === "assistant" &&
73
+ (value.provider === "operator" ||
74
+ (commandId !== null && commands.has(commandId))));
75
+ }
76
+ /** Visitor-scoped Session facts use the visitor's public address, never the internal Session ID. */
77
+ export function visitorSnapshot(snapshot, visitorId) {
78
+ const commands = visitorCommands(snapshot, visitorId);
79
+ const messages = [];
80
+ for (const { id, sequence, commandId, value, meta } of visibleMessages(snapshot, visitorId)) {
81
+ if (value.role === "user") {
82
+ messages.push({
83
+ id,
84
+ sequence,
85
+ commandId,
86
+ requestId: sourceRequestId(meta),
87
+ value: {
88
+ role: "user",
89
+ content: userText(value),
90
+ timestamp: value.timestamp,
91
+ },
92
+ });
93
+ }
94
+ else if (value.role === "assistant") {
95
+ const message = {
96
+ id,
97
+ sequence,
98
+ commandId,
99
+ value: {
100
+ role: "assistant",
101
+ content: value.content.filter((part) => part.type === "text"),
102
+ api: "visitor",
103
+ provider: "visitor",
104
+ model: "visitor",
105
+ usage: {
106
+ input: 0,
107
+ output: 0,
108
+ cacheRead: 0,
109
+ cacheWrite: 0,
110
+ totalTokens: 0,
111
+ cost: {
112
+ input: 0,
113
+ output: 0,
114
+ cacheRead: 0,
115
+ cacheWrite: 0,
116
+ total: 0,
117
+ },
118
+ },
119
+ stopReason: value.stopReason,
120
+ timestamp: value.timestamp,
121
+ },
122
+ };
123
+ messages.push(value.provider === "operator"
124
+ ? {
125
+ ...message,
126
+ // A visitor never sees which member answered.
127
+ meta: authorMeta({
128
+ id: "channel-operator",
129
+ name: "A person from the team",
130
+ }),
131
+ }
132
+ : message);
133
+ }
134
+ }
135
+ const ownWork = snapshot.activity.queue.state !== "idle" &&
136
+ commands.has(snapshot.activity.queue.commandId);
137
+ const pendingPrompts = snapshot.pending.prompts
138
+ .filter(({ meta }) => messageAuthor(meta)?.id === visitorId)
139
+ .map((prompt) => visitorPendingPrompt(prompt, visitorId));
140
+ const projected = {
141
+ sessionId: visitorId,
142
+ cursor: snapshot.cursor,
143
+ hasMore: snapshot.hasMore,
144
+ messages,
145
+ terminals: snapshot.terminals
146
+ .filter(({ commandId }) => commands.has(commandId))
147
+ .map(({ commandId, outcome, sequence }) => ({
148
+ commandId,
149
+ outcome,
150
+ sequence,
151
+ diagnostic: null,
152
+ })),
153
+ pending: {
154
+ total: pendingPrompts.length,
155
+ hasMore: snapshot.pending.hasMore,
156
+ prompts: pendingPrompts,
157
+ },
158
+ activity: {
159
+ queue: !ownWork
160
+ ? { state: "idle" }
161
+ : snapshot.activity.queue.state === "queued"
162
+ ? { ...snapshot.activity.queue, runningWork: null }
163
+ : snapshot.activity.queue,
164
+ cancellationRequested: ownWork && snapshot.activity.cancellationRequested,
165
+ activeTool: null,
166
+ lastModelChoiceSequence: null,
167
+ compaction: null,
168
+ retry: !ownWork || snapshot.activity.retry === null
169
+ ? null
170
+ : { ...snapshot.activity.retry, errorMessage: "Chat is retrying" },
171
+ },
172
+ toolOutput: [],
173
+ progress: !ownWork || snapshot.progress === null
174
+ ? null
175
+ : {
176
+ ...snapshot.progress,
177
+ thinking: "",
178
+ thinkingOffset: 0,
179
+ thinkingTruncated: false,
180
+ },
181
+ extensions: {},
182
+ attention: [],
183
+ };
184
+ return snapshot.oldestCursor === undefined
185
+ ? projected
186
+ : { ...projected, oldestCursor: snapshot.oldestCursor };
187
+ }
188
+ /** Project only this visitor's host-attributed prompts, their replies, and explicit operator replies. */
189
+ export function visitorView(snapshot, visitorId) {
190
+ if (snapshot === null)
191
+ return { status: "idle", lines: [], progress: null };
192
+ const lines = [];
193
+ for (const { id, value } of visibleMessages(snapshot, visitorId)) {
194
+ if (value.role === "user") {
195
+ lines.push({
196
+ id,
197
+ from: "visitor",
198
+ text: userText(value),
199
+ });
200
+ }
201
+ else if (value.role === "assistant") {
202
+ const text = value.content
203
+ .flatMap((part) => (part.type === "text" ? [part.text] : []))
204
+ .join("")
205
+ .trim();
206
+ if (text.length > 0)
207
+ lines.push({
208
+ id,
209
+ from: value.provider === "operator" ? "operator" : "agent",
210
+ text,
211
+ });
212
+ }
213
+ }
214
+ for (const prompt of snapshot.pending.prompts)
215
+ if (messageAuthor(prompt.meta)?.id === visitorId)
216
+ lines.push({
217
+ id: `pending:${prompt.commandId}`,
218
+ from: "visitor",
219
+ text: prompt.text,
220
+ });
221
+ const commands = visitorCommands(snapshot, visitorId);
222
+ const working = (snapshot.activity.queue.state !== "idle" &&
223
+ commands.has(snapshot.activity.queue.commandId)) ||
224
+ snapshot.pending.prompts.some(({ meta }) => messageAuthor(meta)?.id === visitorId);
225
+ const progress = working ? (snapshot.progress?.text.trim() ?? "") : "";
226
+ return {
227
+ status: working ? "working" : "idle",
228
+ lines,
229
+ progress: progress.length > 0 ? progress : null,
230
+ };
231
+ }
@@ -149,6 +149,8 @@ export declare const SessionSnapshotSchema: Schema.Struct<{
149
149
  revision: number;
150
150
  text: string;
151
151
  thinking: string;
152
+ textOffset: number;
153
+ thinkingOffset: number;
152
154
  textTruncated: boolean;
153
155
  thinkingTruncated: boolean;
154
156
  }>, Readonly<{
@@ -156,6 +158,8 @@ export declare const SessionSnapshotSchema: Schema.Struct<{
156
158
  revision: number;
157
159
  text: string;
158
160
  thinking: string;
161
+ textOffset: number;
162
+ thinkingOffset: number;
159
163
  textTruncated: boolean;
160
164
  thinkingTruncated: boolean;
161
165
  }>, never, never>>;
@@ -175,6 +179,11 @@ export declare const SessionSnapshotSchema: Schema.Struct<{
175
179
  readonly since: Schema.Int;
176
180
  readonly expiresAt: Schema.optional<Schema.Int>;
177
181
  readonly inbox: Schema.optional<Schema.Literal<false>>;
182
+ readonly waitingOn: Schema.optional<Schema.Struct<{
183
+ readonly agentId: Schema.NonEmptyString;
184
+ readonly sessionId: Schema.NonEmptyString;
185
+ readonly name: Schema.NonEmptyString;
186
+ }>>;
178
187
  }>>;
179
188
  readonly context: Schema.optionalKey<Schema.Struct<{
180
189
  readonly tokens: Schema.Int;
@@ -33,6 +33,7 @@ const SessionActivitySchema = Schema.Struct({
33
33
  kind: Schema.Literal("session"),
34
34
  sessionId: Schema.NonEmptyString,
35
35
  commandId: Schema.NonEmptyString,
36
+ title: Schema.String,
36
37
  }),
37
38
  Schema.Struct({
38
39
  kind: Schema.Literal("host"),
@@ -110,11 +111,14 @@ const SessionCompactionNoticeSchema = Schema.Struct({
110
111
  ]),
111
112
  diagnostic: Schema.NullOr(Schema.String),
112
113
  });
114
+ const ProgressTailSchema = Schema.String.check(Schema.makeFilter((value) => new TextEncoder().encode(value).byteLength <= 65_536));
113
115
  const SessionProgressSchema = Schema.Struct({
114
116
  messageId: Schema.String,
115
117
  revision: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
116
- text: Schema.String,
117
- thinking: Schema.String,
118
+ text: ProgressTailSchema,
119
+ thinking: ProgressTailSchema,
120
+ textOffset: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
121
+ thinkingOffset: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
118
122
  textTruncated: Schema.Boolean,
119
123
  thinkingTruncated: Schema.Boolean,
120
124
  });
@@ -190,7 +194,18 @@ export const SessionFrameSchema = Schema.Union([
190
194
  }),
191
195
  Schema.Struct({
192
196
  kind: Schema.Literal("progress"),
193
- progress: SessionProgressSchema,
197
+ progress: Schema.Struct({
198
+ messageId: Schema.String,
199
+ revision: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
200
+ text: Schema.Struct({
201
+ offset: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
202
+ append: ProgressTailSchema,
203
+ }),
204
+ thinking: Schema.Struct({
205
+ offset: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
206
+ append: ProgressTailSchema,
207
+ }),
208
+ }),
194
209
  activity: SessionActivitySchema,
195
210
  }),
196
211
  Schema.Struct({
@@ -282,10 +297,16 @@ export function parseForkedSession(value) {
282
297
  /** Exact command facts shared by Session clients and host fleet transports. */
283
298
  export const commandSettlementSchema = Schema.Struct({
284
299
  acceptedSender: Schema.NullOr(Schema.String),
300
+ delegator: Schema.NullOr(Schema.String),
285
301
  terminal: SessionTerminalSchema,
286
302
  content: Schema.String,
287
303
  usage: Schema.Array(Schema.Struct({ provider: Schema.String, model: Schema.String, usage })),
288
304
  toolCalls: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
305
+ waitingOnYou: Schema.Array(Schema.Struct({
306
+ itemId: Schema.String,
307
+ kind: Schema.String,
308
+ summary: Schema.String,
309
+ })).check(Schema.isMaxLength(8)),
289
310
  });
290
311
  /** A timeout is null on the JSON wire and undefined at the client boundary. */
291
312
  export function parseCommandSettlement(value) {
package/dist/client.d.ts CHANGED
@@ -5,12 +5,13 @@ export { type SessionAttention } from "./session-attention.js";
5
5
  import type { SandboxFailureEvent } from "@fungi.computer/mycelium";
6
6
  import type { ModelPreference } from "./internal/model-preference.js";
7
7
  import type { Message } from "@fungi.computer/mule/effect";
8
- import type { ExtensionSlotV0, ExtensionStatusV0, Json } from "./extensions.js";
8
+ import type { ExtensionCommandCallerV0, ExtensionSlotV0, ExtensionStatusV0, Json } from "./extensions.js";
9
9
  import type { AbortCommand, BranchCommand, CompactCommand, OperatorCommand, TitleCommand, CancelCommand, ForkCommand, PromptCommand, PromptDelivery, SessionCommand } from "./internal/client-boundary.js";
10
10
  export { createShiitakeClient, type ShiitakeClientOptions, } from "./remote-client.js";
11
11
  export { bindAgentExtension, bindSessionExtension, type BoundAgentExtensionV0, type BoundCallV0, type BoundSessionExtensionV0, type ExtensionViewV0, type ScopedCommandsV0, } from "./extension-client.js";
12
12
  export { AcceptedSchema, DelegationReceiptSchema, HeldSchema, parseAccepted, parseDelegationReceipt, parseCancelResult, parseLatestPendingPrompt, parseWithdrawResult, parseForkedSession, parseSessionFrame, parseSessionSnapshot, parseSessionSummaryPage, parseShiitakeError, } from "./client-wire.js";
13
13
  export type { AbortCommand, BranchCommand, CompactCommand, CancelCommand, ForkCommand, OperatorCommand, PromptCommand, PromptDelivery, SessionCommand, TitleCommand, } from "./internal/client-boundary.js";
14
+ export { participantSnapshot, visitorPendingPrompt, visitorSnapshot, visitorView, type ParticipantPolicy, type VisitorLine, type VisitorPendingPrompt, type VisitorView, } from "./client-visitor.js";
14
15
  /** Semantic acknowledgement returned after one durable acceptance commit. */
15
16
  export type Accepted = Readonly<{
16
17
  /** Indicates that the owner transaction committed. */
@@ -131,11 +132,13 @@ export type SessionTerminal = Readonly<{
131
132
  */
132
133
  meta?: Json;
133
134
  }>;
134
- /** Work occupying the executor partition that this queued command requires, without message or provider data. */
135
+ /** Blocking executor identity and Session display metadata, never messages or provider data. */
135
136
  export type AgentRunningWork = Readonly<{
136
137
  kind: "session";
137
138
  sessionId: string;
138
139
  commandId: string;
140
+ /** Owner-issued display title; empty when no title is known. */
141
+ title: string;
139
142
  }> | Readonly<{
140
143
  kind: "host";
141
144
  lane: string;
@@ -185,8 +188,8 @@ export type SessionActivity = Readonly<{
185
188
  export type SessionExtensionSlots = Readonly<Record<string, ExtensionSlotV0>>;
186
189
  /** Generic Session-scope extension commands. */
187
190
  export type SessionExtensionControls = Readonly<{
188
- /** Call one Session-scope command with its encoded input; resolves the encoded output. */
189
- call: (command: string, input: Json) => Promise<Json>;
191
+ /** Call one Session-scope command with its encoded input and optional host caller. */
192
+ call: (command: string, input: Json, caller?: ExtensionCommandCallerV0) => Promise<Json>;
190
193
  }>;
191
194
  /** Generic Agent-scope extension controls. */
192
195
  export type AgentExtensionControls = Readonly<{
@@ -212,9 +215,27 @@ export type SessionProgress = Readonly<{
212
215
  revision: number;
213
216
  text: string;
214
217
  thinking: string;
218
+ /** UTF-16 offsets of the retained tails, not byte offsets. */
219
+ textOffset: number;
220
+ thinkingOffset: number;
215
221
  textTruncated: boolean;
216
222
  thinkingTruncated: boolean;
217
223
  }>;
224
+ /** Append-only progress on the wire. Offsets count JavaScript string units. */
225
+ export type SessionProgressDelta = Readonly<{
226
+ messageId: string;
227
+ revision: number;
228
+ text: Readonly<{
229
+ offset: number;
230
+ append: string;
231
+ }>;
232
+ thinking: Readonly<{
233
+ offset: number;
234
+ append: string;
235
+ }>;
236
+ }>;
237
+ /** Fold a decoded append once; a missing prefix waits for an owner reset. */
238
+ export declare function appendSessionProgress(current: SessionProgress | null, delta: SessionProgressDelta): SessionProgress | null;
218
239
  /** Accepted prompt waiting to enter the conversation, not a scheduling command. */
219
240
  export type SessionPendingPrompt = Readonly<{
220
241
  /** Caller identity, available even before the command response reaches its client. */
@@ -369,9 +390,9 @@ export type SessionFrame = Readonly<{
369
390
  */
370
391
  context?: SessionContext;
371
392
  }> | Readonly<{
372
- /** Bounded process-local assistant progress. */
393
+ /** Only new text/thinking; snapshots, not frames, carry retained tails. */
373
394
  kind: "progress";
374
- progress: SessionProgress;
395
+ progress: SessionProgressDelta;
375
396
  /** Raw facts about the Session's activity read with this progress. */
376
397
  activity: SessionActivity;
377
398
  }> | Readonly<{
package/dist/client.js CHANGED
@@ -2,3 +2,26 @@ export {} from "./session-attention.js";
2
2
  export { createShiitakeClient, } from "./remote-client.js";
3
3
  export { bindAgentExtension, bindSessionExtension, } from "./extension-client.js";
4
4
  export { AcceptedSchema, DelegationReceiptSchema, HeldSchema, parseAccepted, parseDelegationReceipt, parseCancelResult, parseLatestPendingPrompt, parseWithdrawResult, parseForkedSession, parseSessionFrame, parseSessionSnapshot, parseSessionSummaryPage, parseShiitakeError, } from "./client-wire.js";
5
+ export { participantSnapshot, visitorPendingPrompt, visitorSnapshot, visitorView, } from "./client-visitor.js";
6
+ /** Fold a decoded append once; a missing prefix waits for an owner reset. */
7
+ export function appendSessionProgress(current, delta) {
8
+ const previous = current?.messageId === delta.messageId ? current : null;
9
+ if (previous !== null && previous.revision >= delta.revision)
10
+ return current;
11
+ const textEnd = previous === null ? 0 : previous.textOffset + previous.text.length;
12
+ const thinkingEnd = previous === null ? 0 : previous.thinkingOffset + previous.thinking.length;
13
+ if (delta.text.offset > textEnd || delta.thinking.offset > thinkingEnd)
14
+ return current;
15
+ return {
16
+ messageId: delta.messageId,
17
+ revision: delta.revision,
18
+ text: (previous?.text ?? "") +
19
+ delta.text.append.slice(Math.max(0, textEnd - delta.text.offset)),
20
+ thinking: (previous?.thinking ?? "") +
21
+ delta.thinking.append.slice(Math.max(0, thinkingEnd - delta.thinking.offset)),
22
+ textOffset: previous?.textOffset ?? 0,
23
+ thinkingOffset: previous?.thinkingOffset ?? 0,
24
+ textTruncated: previous?.textTruncated ?? false,
25
+ thinkingTruncated: previous?.thinkingTruncated ?? false,
26
+ };
27
+ }
@@ -102,6 +102,11 @@ export type CloudflareShiitakeOptions = Readonly<{
102
102
  * refuses for storage pressure. The budget and thresholds are the host's.
103
103
  */
104
104
  storagePressure?: StoragePressureCheck;
105
+ /** Host-owned synchronous check at the single new-work admission door. */
106
+ admission?: Readonly<{
107
+ /** Check the host's current flag synchronously inside admission's transaction. */
108
+ assertOpen: () => void;
109
+ }>;
105
110
  /** Compaction budgets; absent uses Pi's defaults for every model. */
106
111
  compaction?: CompactionOptions;
107
112
  /** The host's explicit extension roster and grants; there is no default. */
@@ -127,6 +132,21 @@ export type CloudflareShiitakeOptions = Readonly<{
127
132
  }>;
128
133
  /** Cloudflare host lifecycle; alarm forwarding stays outside the neutral client. */
129
134
  export type CloudflareShiitake = ShiitakeOwnerAgent & Readonly<{
135
+ /** Read durable queued/running Session or unresolved delegated work in the
136
+ * host's SQLite transaction, immediately before changing archived state.
137
+ * A confirmed remote settlement must be recorded locally first; unavailable
138
+ * remote settlement remains busy, not idle. Never use an in-memory counter.
139
+ */
140
+ busySync: () => Readonly<{
141
+ kind: "session";
142
+ sessionId: string;
143
+ commandId: string;
144
+ }> | Readonly<{
145
+ kind: "delegation";
146
+ agentId: string;
147
+ sessionId: string;
148
+ commandId: string;
149
+ }> | undefined;
130
150
  /** Exact settled command facts, outside the neutral Session transport. */
131
151
  readSettlement: (sessionId: string, commandId: string) => Promise<CommandSettlement | undefined>;
132
152
  /**
@@ -277,6 +277,8 @@ function effectOptions(state, options, runtime) {
277
277
  Object.assign(base, { meta: options.meta });
278
278
  if (options.storagePressure !== undefined)
279
279
  Object.assign(base, { storagePressure: options.storagePressure });
280
+ if (options.admission !== undefined)
281
+ Object.assign(base, { admission: options.admission });
280
282
  if (options.compaction !== undefined)
281
283
  Object.assign(base, { compaction: options.compaction });
282
284
  return tools === undefined ? base : { ...base, tools };
@@ -319,7 +321,7 @@ export function makeCloudflareShiitake(unnamed, options) {
319
321
  const coreOptions = effectOptions(state, options, host.runtime);
320
322
  return import("./internal/runtime-core.js")
321
323
  .then(({ openRuntimeCore }) => runShiitakePromise(openRuntimeCore(coreOptions, options.mailRoom)))
322
- .then(async ({ agent, activeSessions, acceptMail, readSettlement }) => {
324
+ .then(async ({ agent, activeSessions, busySync, acceptMail, readSettlement, }) => {
323
325
  const handle = toPromiseOwner(agent);
324
326
  try {
325
327
  await options.initialize?.(handle);
@@ -340,6 +342,7 @@ export function makeCloudflareShiitake(unnamed, options) {
340
342
  },
341
343
  alarm: host.alarm,
342
344
  activeSessions: () => runShiitakePromise(activeSessions),
345
+ busySync,
343
346
  abortActive: async () => {
344
347
  for (const sessionId of await runShiitakePromise(activeSessions))
345
348
  await runShiitakePromise(agent.session(sessionId).abort);