@fungi.computer/shiitake 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +155 -34
- package/dist/agent-control.d.ts +77 -5
- package/dist/agent-control.js +271 -65
- package/dist/client-entry.d.ts +15 -8
- package/dist/client-entry.js +7 -5
- package/dist/client-wire.d.ts +24 -8
- package/dist/client-wire.js +53 -11
- package/dist/client.d.ts +56 -11
- package/dist/client.js +1 -1
- package/dist/cloudflare.d.ts +42 -23
- package/dist/cloudflare.js +76 -54
- package/dist/cold-sessions.d.ts +28 -0
- package/dist/cold-sessions.js +1 -0
- package/dist/delegation.d.ts +214 -0
- package/dist/delegation.js +319 -0
- package/dist/effect.d.ts +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +2 -2
- package/dist/extension-contract.d.ts +10 -2
- package/dist/extensions.d.ts +6 -2
- package/dist/fleet.d.ts +43 -9
- package/dist/fleet.js +88 -25
- package/dist/hosted.js +1 -0
- package/dist/http-routes.d.ts +106 -22
- package/dist/http-routes.js +18 -1
- package/dist/index.d.ts +3 -2
- package/dist/internal/accepted-run-executor.d.ts +5 -0
- package/dist/internal/accepted-run-executor.js +10 -4
- package/dist/internal/admission-door.d.ts +3 -0
- package/dist/internal/admission-door.js +12 -2
- package/dist/internal/client-boundary.js +7 -1
- package/dist/internal/compaction/compaction-slot.d.ts +36 -0
- package/dist/internal/compaction/compaction-slot.js +41 -0
- package/dist/internal/compaction/compaction.d.ts +7 -4
- package/dist/internal/compaction/compaction.js +59 -30
- package/dist/internal/compaction/session-compaction.d.ts +8 -16
- package/dist/internal/compaction/session-compaction.js +358 -119
- package/dist/internal/compaction/settings.js +5 -0
- package/dist/internal/compaction/utils.d.ts +19 -16
- package/dist/internal/compaction/utils.js +30 -84
- package/dist/internal/contracts.d.ts +40 -13
- package/dist/internal/coordinator.js +28 -10
- package/dist/internal/effect-client.d.ts +4 -2
- package/dist/internal/effect-client.js +29 -0
- package/dist/internal/effect-contracts.d.ts +17 -4
- package/dist/internal/extension-runtime.d.ts +5 -0
- package/dist/internal/extension-runtime.js +184 -89
- package/dist/internal/host-meta.d.ts +7 -0
- package/dist/internal/model-acquisition.js +7 -1
- package/dist/internal/model-preference.d.ts +2 -0
- package/dist/internal/model-preference.js +2 -1
- package/dist/internal/promise-facade.js +4 -16
- package/dist/internal/run-failure-reason.d.ts +10 -0
- package/dist/internal/run-failure-reason.js +19 -0
- package/dist/internal/run-scope.d.ts +4 -1
- package/dist/internal/run-scope.js +38 -5
- package/dist/internal/runtime-coordinator.d.ts +2 -1
- package/dist/internal/runtime-coordinator.js +65 -4
- package/dist/internal/runtime-core.js +1 -0
- package/dist/internal/runtime-reconciliation.js +42 -6
- package/dist/internal/session-context.d.ts +1 -2
- package/dist/internal/session-context.js +8 -30
- package/dist/internal/session-status.d.ts +2 -1
- package/dist/internal/session-status.js +4 -1
- package/dist/internal/woodstock/client-projection.d.ts +6 -5
- package/dist/internal/woodstock/client-projection.js +79 -53
- package/dist/internal/woodstock/cold-sessions.d.ts +4 -0
- package/dist/internal/woodstock/cold-sessions.js +90 -0
- package/dist/internal/woodstock/extension-store.d.ts +2 -0
- package/dist/internal/woodstock/extension-store.js +4 -0
- package/dist/internal/woodstock/fleet-store.d.ts +5 -1
- package/dist/internal/woodstock/fleet-store.js +17 -5
- package/dist/internal/woodstock/index.d.ts +11 -3
- package/dist/internal/woodstock/index.js +33 -14
- package/dist/internal/woodstock/progress.d.ts +14 -1
- package/dist/internal/woodstock/progress.js +97 -19
- package/dist/internal/woodstock/run-recovery.js +6 -4
- package/dist/internal/woodstock/session-attention.d.ts +1 -1
- package/dist/internal/woodstock/session-attention.js +2 -2
- package/dist/internal/woodstock/session-entry.d.ts +109 -42
- package/dist/internal/woodstock/session-entry.js +63 -20
- package/dist/internal/woodstock/session-inbox.js +2 -0
- package/dist/internal/woodstock/session-store.d.ts +104 -24
- package/dist/internal/woodstock/session-store.js +937 -246
- package/dist/internal/woodstock/storage-admission.d.ts +8 -8
- package/dist/internal/woodstock/storage-admission.js +8 -34
- package/dist/internal/woodstock/storage-conformance.d.ts +15 -0
- package/dist/internal/woodstock/storage-conformance.js +489 -0
- package/dist/model-preference.d.ts +2 -2
- package/dist/model-preference.js +2 -2
- package/dist/node.js +1 -1
- package/dist/remote-client.js +8 -1
- package/dist/session-attention.d.ts +8 -1
- package/dist/session-attention.js +6 -1
- package/dist/testing.js +1 -0
- package/docs/api/README.md +18 -16
- package/docs/api/agent-control.md +171 -1
- package/docs/api/client-entry.md +26 -10
- package/docs/api/client.md +153 -37
- package/docs/api/cloudflare.md +76 -23
- package/docs/api/delegation.md +485 -0
- package/docs/api/effect.md +20 -2
- package/docs/api/extensions.md +21 -9
- package/docs/api/fleet.md +159 -104
- package/docs/api/http.md +4 -1
- package/docs/api/index.md +116 -20
- package/docs/api/internal/woodstock/storage-conformance.md +81 -0
- package/docs/api/model-preference.md +17 -0
- package/docs/api/testing.md +16 -4
- package/package.json +26 -14
- package/dist/internal/browser-sessions.d.ts +0 -31
- package/dist/internal/browser-sessions.js +0 -173
package/dist/fleet.js
CHANGED
|
@@ -71,6 +71,7 @@ const agentRowSchema = z.lazy(() => z.strictObject({
|
|
|
71
71
|
agent_id: z.string(),
|
|
72
72
|
name: z.string(),
|
|
73
73
|
actor_user_id: z.string(),
|
|
74
|
+
created_by: z.string().min(1).max(256).nullable(),
|
|
74
75
|
created_at: z.number().int().nonnegative(),
|
|
75
76
|
updated_at: z.number().int().nonnegative(),
|
|
76
77
|
}));
|
|
@@ -86,6 +87,7 @@ export const agentCreationSchema = z.lazy(() => z.strictObject({
|
|
|
86
87
|
name: z.string().min(1).max(256),
|
|
87
88
|
requestId: z.string().min(1).max(256),
|
|
88
89
|
configuration: z.string().min(1).max(4096),
|
|
90
|
+
createdBy: z.string().min(1).max(256).nullable().optional(),
|
|
89
91
|
}));
|
|
90
92
|
/** Owner-only rename input: an Agent's new display name. */
|
|
91
93
|
export const agentRenameSchema = z.lazy(() => z.strictObject({
|
|
@@ -110,6 +112,7 @@ function asDirectoryAgent(row) {
|
|
|
110
112
|
throw new DirectoryError(503, "partial_init");
|
|
111
113
|
return Object.freeze({
|
|
112
114
|
agentId: agent.value,
|
|
115
|
+
createdBy: parsed.data.created_by,
|
|
113
116
|
name: parsed.data.name,
|
|
114
117
|
createdAt: parsed.data.created_at,
|
|
115
118
|
updatedAt: parsed.data.updated_at,
|
|
@@ -142,6 +145,8 @@ export function createAgentDirectory(sql) {
|
|
|
142
145
|
const existing = readStored(agentId.value);
|
|
143
146
|
if (existing === null)
|
|
144
147
|
throw new DirectoryError(503, "partial_init");
|
|
148
|
+
if (existing.createdBy !== (command.createdBy ?? null))
|
|
149
|
+
throw new DirectoryError(409, "request_conflict");
|
|
145
150
|
return existing;
|
|
146
151
|
}
|
|
147
152
|
return null;
|
|
@@ -165,7 +170,7 @@ export function createAgentDirectory(sql) {
|
|
|
165
170
|
throw new DirectoryError(409, "name_taken");
|
|
166
171
|
const agentId = agentIds.agent.generate();
|
|
167
172
|
const now = Date.now();
|
|
168
|
-
store.reserve(agentId, command.name, actorUserId, now, command.requestId, command.configuration);
|
|
173
|
+
store.reserve(agentId, command.name, actorUserId, now, command.requestId, command.configuration, command.createdBy ?? null);
|
|
169
174
|
const created = readStored(agentId);
|
|
170
175
|
if (created === null)
|
|
171
176
|
throw new DirectoryError(503, "partial_init");
|
|
@@ -258,6 +263,7 @@ export const agentDelegationTaskSchema = z.lazy(() => z.strictObject({
|
|
|
258
263
|
}));
|
|
259
264
|
/** Exact host facts encoded over internal transports, without transcript content. */
|
|
260
265
|
export const commandSettlementSchema = Schema.Struct({
|
|
266
|
+
acceptedSender: Schema.NullOr(Schema.String),
|
|
261
267
|
terminal: SessionTerminalSchema,
|
|
262
268
|
content: Schema.String,
|
|
263
269
|
usage: Schema.Array(Schema.Struct({ provider: Schema.String, model: Schema.String, usage })),
|
|
@@ -287,7 +293,7 @@ export async function delegationProvenance(session, toolCallCode) {
|
|
|
287
293
|
* stay on that reply's own line and never travel onward.
|
|
288
294
|
*/
|
|
289
295
|
export function delegationSendMeta(sender, invocationMeta) {
|
|
290
|
-
const author = authorMeta(sender);
|
|
296
|
+
const author = authorMeta({ ...sender, kind: "agent" });
|
|
291
297
|
const inherited = invocationMeta === undefined
|
|
292
298
|
? undefined
|
|
293
299
|
: FungiMessageMetaSchema.parse(invocationMeta).delegation;
|
|
@@ -307,7 +313,11 @@ export function delegationSendMeta(sender, invocationMeta) {
|
|
|
307
313
|
*/
|
|
308
314
|
export function delegationReplyMeta(delegation, replierName) {
|
|
309
315
|
return {
|
|
310
|
-
...authorMeta({
|
|
316
|
+
...authorMeta({
|
|
317
|
+
id: delegation.reply.agentId,
|
|
318
|
+
name: replierName,
|
|
319
|
+
kind: "agent",
|
|
320
|
+
}),
|
|
311
321
|
delegation: {
|
|
312
322
|
ancestors: [...delegation.ancestors],
|
|
313
323
|
maxDepth: delegation.maxDepth,
|
|
@@ -325,11 +335,11 @@ const delegationRowSchema = z.lazy(() => z.strictObject({
|
|
|
325
335
|
}));
|
|
326
336
|
const decodeMail = Schema.decodeUnknownSync(Schema.fromJsonString(BirdDogMailSchema));
|
|
327
337
|
/**
|
|
328
|
-
*
|
|
338
|
+
* BirdDog submission and reply authentication. The durable row freezes
|
|
329
339
|
* correspondence before transport; an ambiguous delivery reuses that exact mail.
|
|
330
340
|
* Shiitake owns work deduplication, execution and settlement; Post owns replies.
|
|
331
341
|
*/
|
|
332
|
-
export function
|
|
342
|
+
export function createFleetCorrespondence(options) {
|
|
333
343
|
const store = createFleetDelegationStore(options.storage);
|
|
334
344
|
function read(requestId) {
|
|
335
345
|
const row = store.read(requestId);
|
|
@@ -351,7 +361,8 @@ export function createFleetDelegation(options) {
|
|
|
351
361
|
: undefined;
|
|
352
362
|
const ancestors = inherited?.ancestors ?? [];
|
|
353
363
|
const maxDepth = Math.min(grant.maxDepth ?? 8, inherited?.maxDepth ?? 8);
|
|
354
|
-
|
|
364
|
+
// Ancestry bounds work depth; it does not remove authorized peer targets.
|
|
365
|
+
if (parent.agentId === task.agentId)
|
|
355
366
|
throw new AgentGrantError("delegation_loop");
|
|
356
367
|
if (ancestors.length >= maxDepth)
|
|
357
368
|
throw new AgentGrantError("delegation_depth");
|
|
@@ -407,22 +418,6 @@ export function createFleetDelegation(options) {
|
|
|
407
418
|
store.recordReceipt(task.requestId, JSON.stringify(receipt));
|
|
408
419
|
return receipt;
|
|
409
420
|
}
|
|
410
|
-
async function delegate(grant, task) {
|
|
411
|
-
const accepted = await start(grant, task);
|
|
412
|
-
const row = read(task.requestId);
|
|
413
|
-
if (row === undefined)
|
|
414
|
-
throw new DirectoryError(503, "partial_init");
|
|
415
|
-
const mail = decodeMail(row.mail);
|
|
416
|
-
const settlement = await options.readSettlement(mail.to.agentId, mail.to.sessionId, accepted.commandId);
|
|
417
|
-
return settlement === undefined
|
|
418
|
-
? pending(1000)
|
|
419
|
-
: {
|
|
420
|
-
kind: "settled",
|
|
421
|
-
agentId: mail.to.agentId,
|
|
422
|
-
sessionId: mail.to.sessionId,
|
|
423
|
-
...settlement,
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
421
|
/**
|
|
427
422
|
* Authenticate one arriving reply against the frozen mail this owner sent.
|
|
428
423
|
* It must be BirdDog's single reply key for that mail, mirror its sender and
|
|
@@ -464,11 +459,79 @@ export function createFleetDelegation(options) {
|
|
|
464
459
|
reply,
|
|
465
460
|
};
|
|
466
461
|
}
|
|
462
|
+
/** Read only the existing acceptance receipt; do not reserve or deliver mail. */
|
|
463
|
+
function receipt(requestId) {
|
|
464
|
+
const encoded = read(requestId)?.receipt;
|
|
465
|
+
return encoded === undefined || encoded === null
|
|
466
|
+
? undefined
|
|
467
|
+
: parseAccepted(encoded);
|
|
468
|
+
}
|
|
467
469
|
return {
|
|
468
|
-
/**
|
|
469
|
-
authenticateReply,
|
|
470
|
-
/** Submit once and return the existing Shiitake admission receipt. */
|
|
470
|
+
/** Submit frozen mail or reuse its recorded Session admission receipt. */
|
|
471
471
|
start,
|
|
472
|
+
/** Match a reply to frozen mail and restore the caller's delegation path. */
|
|
473
|
+
authenticateReply,
|
|
474
|
+
/** Read an existing acceptance receipt without reserving or delivering mail. */
|
|
475
|
+
receipt,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
/** Local hosts may observe exact work directly; remote correspondence never pulls it. */
|
|
479
|
+
export function createFleetDelegation(options) {
|
|
480
|
+
const correspondence = createFleetCorrespondence(options);
|
|
481
|
+
const store = createFleetDelegationStore(options.storage);
|
|
482
|
+
async function delegate(grant, task) {
|
|
483
|
+
const accepted = await correspondence.start(grant, task);
|
|
484
|
+
const raw = store.read(task.requestId);
|
|
485
|
+
if (raw === undefined)
|
|
486
|
+
throw new DirectoryError(503, "partial_init");
|
|
487
|
+
const mail = decodeMail(delegationRowSchema.parse(raw).mail);
|
|
488
|
+
const settlement = await options.readSettlement(mail.to.agentId, mail.to.sessionId, accepted.commandId);
|
|
489
|
+
return settlement === undefined
|
|
490
|
+
? pending(1000)
|
|
491
|
+
: {
|
|
492
|
+
kind: "settled",
|
|
493
|
+
agentId: mail.to.agentId,
|
|
494
|
+
sessionId: mail.to.sessionId,
|
|
495
|
+
...settlement,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* The newest delegation this owner sent whose work has not settled at its
|
|
500
|
+
* target, so the owner is still waiting for a reply; undefined when none.
|
|
501
|
+
*/
|
|
502
|
+
async function outstanding() {
|
|
503
|
+
for (const raw of store.sent()) {
|
|
504
|
+
const row = delegationRowSchema.parse(raw);
|
|
505
|
+
if (row.receipt === null)
|
|
506
|
+
continue;
|
|
507
|
+
const { to } = decodeMail(row.mail);
|
|
508
|
+
const { commandId } = parseAccepted(row.receipt);
|
|
509
|
+
const target = {
|
|
510
|
+
agentId: to.agentId,
|
|
511
|
+
sessionId: to.sessionId,
|
|
512
|
+
commandId,
|
|
513
|
+
};
|
|
514
|
+
let settlement;
|
|
515
|
+
try {
|
|
516
|
+
settlement = await options.readSettlement(to.agentId, to.sessionId, commandId);
|
|
517
|
+
}
|
|
518
|
+
catch (cause) {
|
|
519
|
+
// Work whose state cannot be read is still owed; say why.
|
|
520
|
+
return {
|
|
521
|
+
...target,
|
|
522
|
+
unreadable: cause instanceof Error ? cause.message : "unreadable",
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
if (settlement === undefined)
|
|
526
|
+
return target;
|
|
527
|
+
}
|
|
528
|
+
return undefined;
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
/** The delegated work this owner still waits on, if any. */
|
|
532
|
+
outstanding,
|
|
533
|
+
/** Authenticate a reply against its frozen mail and restore the caller's path. */
|
|
534
|
+
...correspondence,
|
|
472
535
|
/** Submit or poll the exact turn, yielding Agar Pending until settled. */
|
|
473
536
|
delegate,
|
|
474
537
|
};
|
package/dist/hosted.js
CHANGED
|
@@ -169,6 +169,7 @@ function hostedPort(manifest, revision, grants) {
|
|
|
169
169
|
clear: () => Effect.fail(unavailable("Attention")),
|
|
170
170
|
}),
|
|
171
171
|
hasPendingSteer: () => Effect.fail(unavailable("Steering")),
|
|
172
|
+
sessionPosition: () => Effect.fail(unavailable("Session timeline")),
|
|
172
173
|
hasSuccessfulToolResult: () => Effect.fail(unavailable("A tool result")),
|
|
173
174
|
observe: () => Effect.die(unavailable("Observation")),
|
|
174
175
|
transact: () => Effect.fail(new ShiitakeError("extensions", "rejected", "A hosted extension has no Agent transaction")),
|
package/dist/http-routes.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
42
42
|
};
|
|
43
43
|
};
|
|
44
44
|
output: {
|
|
45
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "
|
|
45
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
46
46
|
readonly _tag: "ShiitakeError";
|
|
47
47
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
48
48
|
readonly message: string;
|
|
@@ -109,7 +109,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
109
109
|
};
|
|
110
110
|
};
|
|
111
111
|
output: {
|
|
112
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "
|
|
112
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
113
113
|
readonly _tag: "ShiitakeError";
|
|
114
114
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
115
115
|
readonly message: string;
|
|
@@ -150,7 +150,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
150
150
|
$get: {
|
|
151
151
|
input: {};
|
|
152
152
|
output: {
|
|
153
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "
|
|
153
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
154
154
|
readonly _tag: "ShiitakeError";
|
|
155
155
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
156
156
|
readonly message: string;
|
|
@@ -178,6 +178,34 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
178
178
|
status: 200;
|
|
179
179
|
};
|
|
180
180
|
};
|
|
181
|
+
} & {
|
|
182
|
+
"/sessions/:sessionId/pending/latest": {
|
|
183
|
+
$get: {
|
|
184
|
+
input: {
|
|
185
|
+
param: {
|
|
186
|
+
sessionId: string;
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
output: {
|
|
190
|
+
readonly requestId: string;
|
|
191
|
+
readonly meta?: any;
|
|
192
|
+
readonly origin?: {
|
|
193
|
+
readonly extensionId: string;
|
|
194
|
+
readonly correlation: string | null;
|
|
195
|
+
} | undefined;
|
|
196
|
+
readonly commandId: string;
|
|
197
|
+
readonly text: string;
|
|
198
|
+
readonly images?: readonly {
|
|
199
|
+
readonly type: "image";
|
|
200
|
+
readonly data: string;
|
|
201
|
+
readonly mimeType: string;
|
|
202
|
+
}[] | undefined;
|
|
203
|
+
readonly delivery: import("./client.js").PromptDelivery;
|
|
204
|
+
} | null;
|
|
205
|
+
outputFormat: "json";
|
|
206
|
+
status: 200;
|
|
207
|
+
};
|
|
208
|
+
};
|
|
181
209
|
} & {
|
|
182
210
|
"/sessions/:sessionId/model-preference": {
|
|
183
211
|
$get: {
|
|
@@ -210,7 +238,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
210
238
|
};
|
|
211
239
|
};
|
|
212
240
|
output: {
|
|
213
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "
|
|
241
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
214
242
|
readonly _tag: "ShiitakeError";
|
|
215
243
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
216
244
|
readonly message: string;
|
|
@@ -235,6 +263,47 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
235
263
|
status: 204;
|
|
236
264
|
};
|
|
237
265
|
};
|
|
266
|
+
} & {
|
|
267
|
+
"/sessions/:sessionId/withdraw": {
|
|
268
|
+
$post: {
|
|
269
|
+
input: {
|
|
270
|
+
json: {
|
|
271
|
+
readonly requestId: string;
|
|
272
|
+
};
|
|
273
|
+
} & {
|
|
274
|
+
param: {
|
|
275
|
+
sessionId: string;
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
output: {
|
|
279
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
280
|
+
readonly _tag: "ShiitakeError";
|
|
281
|
+
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
282
|
+
readonly message: string;
|
|
283
|
+
readonly requestId?: string | undefined;
|
|
284
|
+
};
|
|
285
|
+
outputFormat: "json";
|
|
286
|
+
status: 400;
|
|
287
|
+
} | {
|
|
288
|
+
input: {
|
|
289
|
+
json: {
|
|
290
|
+
readonly requestId: string;
|
|
291
|
+
};
|
|
292
|
+
} & {
|
|
293
|
+
param: {
|
|
294
|
+
sessionId: string;
|
|
295
|
+
};
|
|
296
|
+
};
|
|
297
|
+
output: {
|
|
298
|
+
readonly status: "accepted";
|
|
299
|
+
readonly commandId: string;
|
|
300
|
+
} | {
|
|
301
|
+
readonly status: "unknown";
|
|
302
|
+
};
|
|
303
|
+
outputFormat: "json";
|
|
304
|
+
status: 200;
|
|
305
|
+
};
|
|
306
|
+
};
|
|
238
307
|
} & {
|
|
239
308
|
"/sessions/:sessionId/command": {
|
|
240
309
|
$post: {
|
|
@@ -282,7 +351,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
282
351
|
};
|
|
283
352
|
};
|
|
284
353
|
output: {
|
|
285
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "
|
|
354
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
286
355
|
readonly _tag: "ShiitakeError";
|
|
287
356
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
288
357
|
readonly message: string;
|
|
@@ -384,6 +453,8 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
384
453
|
output: {
|
|
385
454
|
readonly status: "accepted";
|
|
386
455
|
readonly commandId: string;
|
|
456
|
+
} | {
|
|
457
|
+
readonly status: "unknown";
|
|
387
458
|
} | {
|
|
388
459
|
readonly status: "too_late";
|
|
389
460
|
readonly commandId: string;
|
|
@@ -409,7 +480,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
409
480
|
};
|
|
410
481
|
};
|
|
411
482
|
output: {
|
|
412
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "
|
|
483
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
413
484
|
readonly _tag: "ShiitakeError";
|
|
414
485
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
415
486
|
readonly message: string;
|
|
@@ -435,7 +506,9 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
435
506
|
readonly hasMore: boolean;
|
|
436
507
|
readonly oldestCursor?: string | null | undefined;
|
|
437
508
|
readonly messages: readonly {
|
|
509
|
+
readonly requestId?: string | undefined;
|
|
438
510
|
readonly meta?: any;
|
|
511
|
+
readonly data?: any;
|
|
439
512
|
readonly origin?: {
|
|
440
513
|
readonly extensionId: string;
|
|
441
514
|
readonly correlation: string | null;
|
|
@@ -499,6 +572,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
499
572
|
};
|
|
500
573
|
thoughtSignature?: string | undefined;
|
|
501
574
|
namespace?: string | undefined;
|
|
575
|
+
argumentsError?: string | undefined;
|
|
502
576
|
} | {
|
|
503
577
|
type: "thinking";
|
|
504
578
|
thinking: string;
|
|
@@ -555,20 +629,10 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
555
629
|
rawStopReason?: string | undefined;
|
|
556
630
|
endTurn?: boolean | undefined;
|
|
557
631
|
timestamp: number;
|
|
632
|
+
durationMs?: number | undefined;
|
|
558
633
|
} | {
|
|
559
|
-
role: "toolResult";
|
|
560
634
|
toolCallId: string;
|
|
561
|
-
|
|
562
|
-
content: ({
|
|
563
|
-
type: "text";
|
|
564
|
-
text: string;
|
|
565
|
-
textSignature?: string | undefined;
|
|
566
|
-
} | {
|
|
567
|
-
type: "image";
|
|
568
|
-
data: string;
|
|
569
|
-
mimeType: string;
|
|
570
|
-
})[];
|
|
571
|
-
details?: any;
|
|
635
|
+
role: "toolResult";
|
|
572
636
|
usage?: {
|
|
573
637
|
input: number;
|
|
574
638
|
output: number;
|
|
@@ -585,6 +649,18 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
585
649
|
total: number;
|
|
586
650
|
};
|
|
587
651
|
} | undefined;
|
|
652
|
+
content: ({
|
|
653
|
+
type: "text";
|
|
654
|
+
text: string;
|
|
655
|
+
textSignature?: string | undefined;
|
|
656
|
+
} | {
|
|
657
|
+
type: "image";
|
|
658
|
+
data: string;
|
|
659
|
+
mimeType: string;
|
|
660
|
+
})[];
|
|
661
|
+
timestamp: number;
|
|
662
|
+
durationMs?: number | undefined;
|
|
663
|
+
toolName: string;
|
|
588
664
|
nestedCalls?: {
|
|
589
665
|
calls: {
|
|
590
666
|
id: string;
|
|
@@ -600,7 +676,6 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
600
676
|
complete: boolean;
|
|
601
677
|
} | undefined;
|
|
602
678
|
isError: boolean;
|
|
603
|
-
timestamp: number;
|
|
604
679
|
};
|
|
605
680
|
}[];
|
|
606
681
|
readonly terminals: readonly {
|
|
@@ -623,7 +698,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
623
698
|
readonly sequence: number;
|
|
624
699
|
readonly at: number;
|
|
625
700
|
readonly trigger: import("./client.js").SessionCompactionTrigger;
|
|
626
|
-
readonly result: "nothing_to_compact" | "failed";
|
|
701
|
+
readonly result: "nothing_to_compact" | "failed" | "stale" | "insufficient";
|
|
627
702
|
readonly diagnostic: string | null;
|
|
628
703
|
}[] | undefined;
|
|
629
704
|
readonly sandboxFailures?: readonly {
|
|
@@ -632,7 +707,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
632
707
|
readonly executionId: string;
|
|
633
708
|
readonly adapter: string;
|
|
634
709
|
readonly buildSha: string | null;
|
|
635
|
-
readonly tag: "Unknown" | "SourceInvalid" | "GuestThrew" | "DeadlineExceeded" | "Cancelled" | "DispatchLost" | "SandboxCrashed" | "BadResponse" | "ResultTooLarge" | "ResultNotJson" | "CapabilityFailed";
|
|
710
|
+
readonly tag: "Unknown" | "SourceInvalid" | "GuestThrew" | "DeadlineExceeded" | "SandboxBusy" | "Cancelled" | "DispatchLost" | "SandboxCrashed" | "BadResponse" | "ResultTooLarge" | "ResultNotJson" | "CapabilityFailed";
|
|
636
711
|
readonly fields: {
|
|
637
712
|
readonly [x: string]: any;
|
|
638
713
|
};
|
|
@@ -641,6 +716,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
641
716
|
readonly pending: {
|
|
642
717
|
readonly total: number;
|
|
643
718
|
readonly prompts: readonly {
|
|
719
|
+
readonly requestId: string;
|
|
644
720
|
readonly meta?: any;
|
|
645
721
|
readonly origin?: {
|
|
646
722
|
readonly extensionId: string;
|
|
@@ -698,6 +774,13 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
698
774
|
readonly textTruncated: boolean;
|
|
699
775
|
readonly thinkingTruncated: boolean;
|
|
700
776
|
} | null;
|
|
777
|
+
readonly toolOutput: readonly {
|
|
778
|
+
readonly toolCallId: string;
|
|
779
|
+
readonly revision: number;
|
|
780
|
+
readonly tail: string;
|
|
781
|
+
readonly droppedBytes: number;
|
|
782
|
+
readonly totalBytes: number;
|
|
783
|
+
}[];
|
|
701
784
|
readonly extensions: {
|
|
702
785
|
readonly [x: string]: {
|
|
703
786
|
readonly status: "running";
|
|
@@ -717,6 +800,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
717
800
|
readonly since: number;
|
|
718
801
|
readonly extensionId: string;
|
|
719
802
|
readonly expiresAt?: number | undefined;
|
|
803
|
+
readonly inbox?: false | undefined;
|
|
720
804
|
}[];
|
|
721
805
|
readonly context?: {
|
|
722
806
|
readonly tokens: number;
|
|
@@ -759,7 +843,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
759
843
|
};
|
|
760
844
|
};
|
|
761
845
|
output: {
|
|
762
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "
|
|
846
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
763
847
|
readonly _tag: "ShiitakeError";
|
|
764
848
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
765
849
|
readonly message: string;
|
package/dist/http-routes.js
CHANGED
|
@@ -171,6 +171,10 @@ export function createShiitakeHttpRoutes(resolveSession, listSessions, createSes
|
|
|
171
171
|
}
|
|
172
172
|
const page = await listSessions(cursor);
|
|
173
173
|
return context.json(page, 200);
|
|
174
|
+
})
|
|
175
|
+
.get("/sessions/:sessionId/pending/latest", async (context) => {
|
|
176
|
+
const session = await resolveSession(decodeClientSessionId(context.req.param("sessionId")), context.get("shiitakeMeta"));
|
|
177
|
+
return context.json(await session.latestPending(), 200);
|
|
174
178
|
})
|
|
175
179
|
.get("/sessions/:sessionId/model-preference", async (context) => {
|
|
176
180
|
const session = await resolveSession(decodeClientSessionId(context.req.param("sessionId")), context.get("shiitakeMeta"));
|
|
@@ -187,6 +191,15 @@ export function createShiitakeHttpRoutes(resolveSession, listSessions, createSes
|
|
|
187
191
|
const session = await resolveSession(decodeClientSessionId(context.req.param("sessionId")), context.get("shiitakeMeta"));
|
|
188
192
|
await session.modelPreference.set(context.req.valid("json"));
|
|
189
193
|
return context.body(null, 204);
|
|
194
|
+
})
|
|
195
|
+
.post("/sessions/:sessionId/withdraw", validator("json", (value, context) => {
|
|
196
|
+
const decoded = decodeClientSessionCreationRequest(value);
|
|
197
|
+
if (decoded._tag === "Failure")
|
|
198
|
+
return context.json(toShiitakeErrorWire(invalidRequest("Withdrawal request is invalid")), 400);
|
|
199
|
+
return decoded.success;
|
|
200
|
+
}), async (context) => {
|
|
201
|
+
const session = await resolveSession(decodeClientSessionId(context.req.param("sessionId")), context.get("shiitakeMeta"));
|
|
202
|
+
return context.json(await session.withdraw(context.req.valid("json").requestId), 200);
|
|
190
203
|
})
|
|
191
204
|
.post("/sessions/:sessionId/command", validator("json", (value, context) => {
|
|
192
205
|
const decoded = decodeClientSessionCommandRequest(value);
|
|
@@ -238,5 +251,9 @@ export function createShiitakeHttpRoutes(resolveSession, listSessions, createSes
|
|
|
238
251
|
includeSandboxFailures: query.includeSandboxFailures === "true",
|
|
239
252
|
}));
|
|
240
253
|
})
|
|
241
|
-
.onError((error, context) => context.json(toShiitakeErrorWire(error),
|
|
254
|
+
.onError((error, context) => context.json(toShiitakeErrorWire(error), error instanceof ShiitakeError &&
|
|
255
|
+
error.boundary === "runtime" &&
|
|
256
|
+
error.cause === "rejected"
|
|
257
|
+
? 403
|
|
258
|
+
: 503));
|
|
242
259
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ import type { VerifiedAgentReply } from "./internal/woodstock/index.js";
|
|
|
5
5
|
import type { BirdDogEffect } from "@fungi.computer/bird-dog/effect";
|
|
6
6
|
import type { Accepted } from "./client.js";
|
|
7
7
|
import type { ShiitakeOptions, ShiitakeOwnerAgent } from "./internal/contracts.js";
|
|
8
|
-
export type { AbortCommand, AgentExtensionControls, BranchCommand, CompactCommand, CancelCommand, CancelResult, Accepted, ForkCommand, ForkedSession, PromptCommand, PromptDelivery, SendOptions, ReadOptions, SessionCommand, SessionCommandHandler, SessionExtensionControls, SessionExtensionSlots, SessionFrame, SessionMessage, SessionProgress, SessionContext, SessionSnapshot, SessionInboxState, SessionSummary, SessionSummaryPage, SessionSummaryRow, SessionActivity, AgentRunningWork, SessionTerminal, SessionCompactionMarker, SessionCompactionNotice, SessionCompactionTrigger, ShiitakeEventSourceConstructor, SessionWatch, ShiitakeAgent, ShiitakeSession, SubscribeOptions, TooLate, WatchOptions, } from "./client.js";
|
|
8
|
+
export type { AbortCommand, AgentExtensionControls, BranchCommand, CompactCommand, CancelCommand, CancelResult, Accepted, ForkCommand, ForkedSession, OperatorCommand, TitleCommand, PromptCommand, PromptDelivery, SendOptions, ReadOptions, SessionCommand, SessionCommandHandler, SessionExtensionControls, SessionExtensionSlots, SessionFrame, SessionMessage, SessionProgress, SessionToolOutput, SessionContext, SessionSnapshot, SessionInboxState, SessionSummary, SessionSummaryPage, SessionSummaryRow, SessionActivity, AgentRunningWork, SessionTerminal, SessionCompactionMarker, SessionCompactionNotice, SessionCompactionTrigger, ShiitakeEventSourceConstructor, SessionWatch, ShiitakeAgent, ShiitakeSession, SubscribeOptions, TooLate, UnknownRequest, WatchOptions, } from "./client.js";
|
|
9
|
+
export type { ColdSessionPage, ColdSessionPageRequest, ColdSessionRow, } from "./cold-sessions.js";
|
|
9
10
|
export { parseForkedSession, parseSessionFrame, parseSessionSnapshot, } from "./client.js";
|
|
10
|
-
export type { InstructionSnapshot, InstructionSource, ModelAcquisition, ModelDescription, ModelSource, RuntimeHost, RuntimePendingWork, SqliteCursor, SqliteOwner, SqliteValue, SqliteRow,
|
|
11
|
+
export type { InstructionSnapshot, InstructionSource, ModelAcquisition, ModelDescription, ModelSource, RuntimeHost, RuntimePendingWork, SqliteCursor, SqliteOwner, SqliteValue, SqliteRow, StoragePressureCheck, CompactionBudget, CompactionOptions, GrantsDelivery, GrantsDeliveryResult, ShiitakeOptions, ShiitakeOwnerAgent, ToolAccess, HostMetaOptions, StandardHostMetaResult, StandardHostMetaSchema, ToolSource, } from "./internal/contracts.js";
|
|
11
12
|
export type { BorrowedCompactionViewV0, CompactionHandoffErrorV0, CompactionHandoffResultV0, CompactionHandoffV0, ExtensionCapabilityV0, ExtensionGrantV0, ExtensionRosterV0, ExtensionSlotV0, ExtensionStatusV0, RosteredExtensionV0, } from "./extensions.js";
|
|
12
13
|
export { ShiitakeError, type ShiitakeErrorBoundary, type ShiitakeErrorCause, } from "./errors.js";
|
|
13
14
|
/** Public construction namespace for the Promise-facing durable harness. */
|
|
@@ -18,6 +18,11 @@ export declare function executeAcceptedRun(options: ShiitakeEffectOptions, coreD
|
|
|
18
18
|
readonly kind: "outcome_unknown";
|
|
19
19
|
} | {
|
|
20
20
|
kind: "failed" | "cancelled";
|
|
21
|
+
reason?: never;
|
|
21
22
|
} | {
|
|
22
23
|
kind: "completed";
|
|
24
|
+
reason?: never;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "failed";
|
|
27
|
+
reason: string;
|
|
23
28
|
}, never, never>;
|
|
@@ -7,10 +7,11 @@ import * as Schema from "effect/Schema";
|
|
|
7
7
|
import { ShiitakeError } from "../errors.js";
|
|
8
8
|
import { ExtensionId } from "../extension-contract.js";
|
|
9
9
|
import { publishOwnerObservation } from "./observation-kernel.js";
|
|
10
|
+
import { returnedFailureReason } from "./run-failure-reason.js";
|
|
10
11
|
import { coreProtocolDigest } from "./extension-roster.js";
|
|
11
12
|
import { runAcceptedCommand, runAcceptedCompaction, ToolSetupError, } from "./run-scope.js";
|
|
12
13
|
import { CompactionCommitError } from "./compaction/session-compaction.js";
|
|
13
|
-
import { isCompactCommand, loadAcceptedRun, readExtensionAdmissionOrigin, recordWorkSettlement, } from "./woodstock/index.js";
|
|
14
|
+
import { BrokenSessionError, isCompactCommand, loadAcceptedRun, readExtensionAdmissionOrigin, recordWorkSettlement, } from "./woodstock/index.js";
|
|
14
15
|
/** Bounded process-loss recovery for every accepted Agent turn. */
|
|
15
16
|
export const agentTurnRecovery = Object.freeze({ maxRecoveries: 2 });
|
|
16
17
|
function runtimeFailure(message) {
|
|
@@ -23,7 +24,9 @@ function runtimeFailure(message) {
|
|
|
23
24
|
*/
|
|
24
25
|
function failedDecision(error) {
|
|
25
26
|
const category = `${error.boundary}:${error.cause}`;
|
|
26
|
-
const diagnostic = error instanceof ToolSetupError ||
|
|
27
|
+
const diagnostic = error instanceof ToolSetupError ||
|
|
28
|
+
error instanceof CompactionCommitError ||
|
|
29
|
+
error instanceof BrokenSessionError
|
|
27
30
|
? error.diagnostic
|
|
28
31
|
: error.boundary === "model"
|
|
29
32
|
? error.message
|
|
@@ -114,7 +117,7 @@ export function executeAcceptedRun(options, coreDigest, job, extensions) {
|
|
|
114
117
|
: yield* coreProtocolDigest(fileTools.map((tool) => tool.descriptor.name)),
|
|
115
118
|
extensions: extensions?.runParticipants() ?? [],
|
|
116
119
|
};
|
|
117
|
-
const run = yield* runAcceptedCommand(options, prepared, executionContract, extensionModules, fileTools, extensionHandoff, extensions === undefined
|
|
120
|
+
const { run, failureReason } = yield* runAcceptedCommand(options, prepared, executionContract, extensionModules, fileTools, extensionHandoff, extensions === undefined
|
|
118
121
|
? undefined
|
|
119
122
|
: () => extensions.promptSections(runIdentity, participants));
|
|
120
123
|
switch (run.status) {
|
|
@@ -123,7 +126,10 @@ export function executeAcceptedRun(options, coreDigest, job, extensions) {
|
|
|
123
126
|
case "cancelled":
|
|
124
127
|
return { kind: "cancelled" };
|
|
125
128
|
case "failed":
|
|
126
|
-
return {
|
|
129
|
+
return {
|
|
130
|
+
kind: "failed",
|
|
131
|
+
reason: failureReason ?? returnedFailureReason(run),
|
|
132
|
+
};
|
|
127
133
|
}
|
|
128
134
|
}).pipe(Effect.catch((error) => logRunFailed(job.jobId, error).pipe(Effect.as(failedDecision(error)))));
|
|
129
135
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BirdDogMail } from "@fungi.computer/bird-dog";
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import type { AcceptedWork } from "./client-boundary.js";
|
|
4
|
+
import type { WithdrawResult } from "../client.js";
|
|
4
5
|
import type { Json } from "../extensions.js";
|
|
5
6
|
import { type HostMetaOptions } from "./host-meta.js";
|
|
6
7
|
import { ShiitakeError } from "../errors.js";
|
|
@@ -50,6 +51,8 @@ export type AdmitWithin = (request: AdmitRequest) => AdmitResult;
|
|
|
50
51
|
export type AdmissionBoundary = "request" | "reconcile";
|
|
51
52
|
/** The one accepted-command door. Nothing else writes accepted commands. */
|
|
52
53
|
export type AdmissionDoor = Readonly<{
|
|
54
|
+
/** In the same owner transaction as admission, keep the receipt or fence the key. */
|
|
55
|
+
withdraw: (sessionId: string, requestId: string) => Effect.Effect<WithdrawResult, ShiitakeError>;
|
|
53
56
|
/** Admit one prompt in its own transaction. */
|
|
54
57
|
admit: (request: AdmitRequest, boundary?: AdmissionBoundary) => Effect.Effect<AdmitResult, ShiitakeError>;
|
|
55
58
|
/**
|
|
@@ -2,9 +2,9 @@ import * as Effect from "effect/Effect";
|
|
|
2
2
|
import * as Schema from "effect/Schema";
|
|
3
3
|
import { checkHostMeta } from "./host-meta.js";
|
|
4
4
|
import { ShiitakeError } from "../errors.js";
|
|
5
|
-
import { decodeClientAcceptedCommand, decodeClientSessionId, requireDecoded, } from "./client-boundary.js";
|
|
5
|
+
import { decodeClientAcceptedCommand, decodeClientSessionId, decodeClientSessionCreationRequestId, requireDecoded, } from "./client-boundary.js";
|
|
6
6
|
import { publishOwnerObservation } from "./observation-kernel.js";
|
|
7
|
-
import { AdmissionOriginSchema, acceptSessionCommandOwned, readSessionCommandAdmissionByRequest, hasStoredSession, VerifiedAgentReplySchema, } from "./woodstock/index.js";
|
|
7
|
+
import { AdmissionOriginSchema, acceptSessionCommandOwned, readSessionCommandAdmissionByRequest, withdrawSessionRequestOwned, assertSessionRequestNotWithdrawn, hasStoredSession, VerifiedAgentReplySchema, } from "./woodstock/index.js";
|
|
8
8
|
const decodeOrigin = Schema.decodeUnknownResult(AdmissionOriginSchema, {
|
|
9
9
|
onExcessProperty: "error",
|
|
10
10
|
});
|
|
@@ -42,6 +42,7 @@ export function openAdmissionDoor(input) {
|
|
|
42
42
|
const admitDecoded = (request, touched) => {
|
|
43
43
|
const { sessionId, origin } = request;
|
|
44
44
|
const command = request.command;
|
|
45
|
+
assertSessionRequestNotWithdrawn(input.owner, sessionId, command.requestId);
|
|
45
46
|
if (request.requireExistingSession &&
|
|
46
47
|
!hasStoredSession(input.owner, sessionId))
|
|
47
48
|
throw new ShiitakeError("contract", "closed", "Session target is unavailable");
|
|
@@ -95,6 +96,15 @@ export function openAdmissionDoor(input) {
|
|
|
95
96
|
const transact = (body, boundary = "request") => run((admit) => body((request) => admit(decode(request))), boundary);
|
|
96
97
|
return Object.freeze({
|
|
97
98
|
transact,
|
|
99
|
+
withdraw: (sessionId, requestId) => Effect.try({
|
|
100
|
+
try: () => ({
|
|
101
|
+
sessionId: decodeClientSessionId(sessionId),
|
|
102
|
+
requestId: decodeClientSessionCreationRequestId({ requestId }),
|
|
103
|
+
}),
|
|
104
|
+
catch: (error) => error instanceof ShiitakeError
|
|
105
|
+
? error
|
|
106
|
+
: new ShiitakeError("contract", "invalid", "Withdrawal request is invalid"),
|
|
107
|
+
}).pipe(Effect.flatMap((decoded) => run(() => withdrawSessionRequestOwned(input.owner, decoded.sessionId, decoded.requestId), "request"))),
|
|
98
108
|
admit: (request, boundary) => Effect.try({
|
|
99
109
|
try: () => decode(request),
|
|
100
110
|
catch: (error) => error instanceof ShiitakeError
|
|
@@ -5,7 +5,13 @@ const encoder = new TextEncoder();
|
|
|
5
5
|
function boundedText(maximumBytes) {
|
|
6
6
|
return Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(maximumBytes), Schema.makeFilter((value) => encoder.encode(value).byteLength <= maximumBytes));
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
/** A lone UTF-16 surrogate; SQLite's UTF-8 text columns cannot store one. */
|
|
9
|
+
const loneSurrogate = /\p{Cs}/u;
|
|
10
|
+
/**
|
|
11
|
+
* Session and request ids are indexed SQLite text, so they must be well-formed
|
|
12
|
+
* Unicode: a lone surrogate would be stored as U+FFFD and never match again.
|
|
13
|
+
*/
|
|
14
|
+
const boundedId = boundedText(256).check(Schema.makeFilter((value) => !loneSurrogate.test(value)));
|
|
9
15
|
/**
|
|
10
16
|
* The one prompt-text grammar. Human, mail, host and extension prompts are all
|
|
11
17
|
* admitted through the accepted-command door under this bound. Text is
|