@fungi.computer/shiitake 0.3.0 → 0.4.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 +18 -8
- package/dist/agent-control.d.ts +19 -15
- package/dist/agent-control.js +66 -126
- package/dist/client-wire.d.ts +12 -3
- package/dist/client-wire.js +33 -8
- package/dist/client.d.ts +34 -3
- package/dist/client.js +1 -1
- package/dist/cloudflare.d.ts +3 -1
- package/dist/cloudflare.js +2 -2
- package/dist/delegation.d.ts +145 -13
- package/dist/delegation.js +109 -37
- package/dist/effect.d.ts +1 -1
- package/dist/fleet.d.ts +30 -8
- package/dist/fleet.js +142 -38
- package/dist/http-routes.d.ts +91 -19
- package/dist/http-routes.js +15 -1
- package/dist/index.d.ts +2 -2
- package/dist/internal/admission-door.d.ts +2 -0
- package/dist/internal/admission-door.js +2 -0
- package/dist/internal/client-boundary.d.ts +10 -0
- package/dist/internal/client-boundary.js +10 -0
- package/dist/internal/compaction/compaction.d.ts +5 -5
- package/dist/internal/compaction/compaction.js +4 -5
- package/dist/internal/compaction/settings.d.ts +1 -1
- package/dist/internal/compaction/utils.d.ts +1 -1
- package/dist/internal/contracts.d.ts +2 -0
- package/dist/internal/coordinator.d.ts +3 -1
- package/dist/internal/coordinator.js +98 -13
- package/dist/internal/effect-client.d.ts +3 -1
- package/dist/internal/effect-client.js +3 -2
- package/dist/internal/effect-contracts.d.ts +6 -2
- package/dist/internal/model-acquisition.js +2 -0
- package/dist/internal/model-preference.d.ts +2 -2
- package/dist/internal/observation-kernel.d.ts +8 -4
- package/dist/internal/observation-kernel.js +44 -9
- package/dist/internal/promise-facade.d.ts +1 -1
- package/dist/internal/promise-facade.js +1 -1
- package/dist/internal/run-scope.js +37 -8
- package/dist/internal/runtime-coordinator.d.ts +5 -0
- package/dist/internal/runtime-coordinator.js +8 -4
- package/dist/internal/runtime-core.d.ts +2 -1
- package/dist/internal/runtime-core.js +6 -1
- package/dist/internal/session-context.d.ts +1 -1
- package/dist/internal/session-context.js +84 -13
- package/dist/internal/woodstock/agent-reply.d.ts +7 -2
- package/dist/internal/woodstock/agent-reply.js +22 -13
- package/dist/internal/woodstock/fleet-store.d.ts +3 -2
- package/dist/internal/woodstock/fleet-store.js +43 -6
- package/dist/internal/woodstock/index.d.ts +2 -0
- package/dist/internal/woodstock/index.js +11 -5
- package/dist/internal/woodstock/run-recovery.js +4 -1
- package/dist/internal/woodstock/session-entry.d.ts +14 -4
- package/dist/internal/woodstock/session-entry.js +1 -0
- package/dist/internal/woodstock/session-store.d.ts +4 -0
- package/dist/internal/woodstock/session-store.js +26 -19
- package/dist/remote-client.js +5 -1
- package/dist/testing.d.ts +6 -3
- package/dist/testing.js +4 -2
- package/docs/api/agent-control.md +18 -12
- package/docs/api/client.md +78 -0
- package/docs/api/delegation.md +72 -55
- package/docs/api/effect.md +5 -0
- package/docs/api/fleet.md +23 -23
- package/docs/api/http.md +2 -1
- package/docs/api/index.md +13 -0
- package/docs/api/model-preference.md +10 -10
- package/docs/api/testing.md +3 -3
- package/package.json +9 -8
package/dist/fleet.js
CHANGED
|
@@ -1,13 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
export { commandSettlementSchema } from "./client-wire.js";
|
|
2
|
+
import { createFleetDirectoryStore, createFleetDelegationStore, } from "./internal/woodstock/index.js";
|
|
3
3
|
import { pending } from "@fungi.computer/agar";
|
|
4
4
|
import { BirdDogMailSchema } from "@fungi.computer/bird-dog/effect";
|
|
5
5
|
import * as Schema from "effect/Schema";
|
|
6
|
-
import { parseAccepted } from "./client.js";
|
|
6
|
+
import { parseAccepted, parseDelegationReceipt, } from "./client.js";
|
|
7
7
|
import { checkHostMeta } from "./internal/host-meta.js";
|
|
8
8
|
import { defineIdFamily } from "@fungi.computer/cairn/id";
|
|
9
|
+
import { deterministicReplayKey } from "@fungi.computer/cairn";
|
|
9
10
|
import { z } from "zod";
|
|
10
11
|
import { authorMeta, FungiMessageMetaSchema, } from "@fungi.computer/agar/agents";
|
|
12
|
+
/**
|
|
13
|
+
* One idempotency identity for a caller-owned delegation request.
|
|
14
|
+
* The target Session is deliberately excluded: a sessionless send learns it
|
|
15
|
+
* only after acceptance, when recall must still resolve the original receipt.
|
|
16
|
+
*/
|
|
17
|
+
export async function agentDelegationRequestKey(input) {
|
|
18
|
+
const ownerIdentity = input.owner.kind === "hosted"
|
|
19
|
+
? [input.owner.scopeId, input.owner.agentId]
|
|
20
|
+
: [input.owner.agentId];
|
|
21
|
+
return deterministicReplayKey("agent-delegation-request", [
|
|
22
|
+
input.owner.kind,
|
|
23
|
+
...ownerIdentity,
|
|
24
|
+
input.sourceSessionId,
|
|
25
|
+
input.targetAgentId,
|
|
26
|
+
input.requestId,
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
11
29
|
const identity = z.string().min(1).max(256);
|
|
12
30
|
/** Operations explicitly admitted by the host. */
|
|
13
31
|
export const agentActionSchema = z.enum([
|
|
@@ -261,14 +279,6 @@ export const agentDelegationTaskSchema = z.lazy(() => z.strictObject({
|
|
|
261
279
|
.min(1)
|
|
262
280
|
.max(256 * 1024),
|
|
263
281
|
}));
|
|
264
|
-
/** Exact host facts encoded over internal transports, without transcript content. */
|
|
265
|
-
export const commandSettlementSchema = Schema.Struct({
|
|
266
|
-
acceptedSender: Schema.NullOr(Schema.String),
|
|
267
|
-
terminal: SessionTerminalSchema,
|
|
268
|
-
content: Schema.String,
|
|
269
|
-
usage: Schema.Array(Schema.Struct({ provider: Schema.String, model: Schema.String, usage })),
|
|
270
|
-
toolCalls: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
271
|
-
});
|
|
272
282
|
/**
|
|
273
283
|
* Provenance for one delegated send from the invoking Agent's Session. The
|
|
274
284
|
* model program's digest is frozen with the replay input, so a retried tool
|
|
@@ -331,9 +341,23 @@ const delegationMetadata = z.object({
|
|
|
331
341
|
const delegationRowSchema = z.lazy(() => z.strictObject({
|
|
332
342
|
input: z.string(),
|
|
333
343
|
mail: z.string(),
|
|
344
|
+
state: z.enum(["pending", "held", "accepted"]),
|
|
334
345
|
receipt: z.string().nullable(),
|
|
335
346
|
}));
|
|
347
|
+
const heldRecipientOffline = {
|
|
348
|
+
status: "held",
|
|
349
|
+
reason: "recipient_offline",
|
|
350
|
+
message: "waiting: recipient offline",
|
|
351
|
+
};
|
|
336
352
|
const decodeMail = Schema.decodeUnknownSync(Schema.fromJsonString(BirdDogMailSchema));
|
|
353
|
+
function bindRecipientSession(mail, sessionId) {
|
|
354
|
+
if (mail.from.sessionId === undefined)
|
|
355
|
+
throw new DirectoryError(503, "partial_init");
|
|
356
|
+
return {
|
|
357
|
+
...mail,
|
|
358
|
+
to: { ...mail.to, sessionId },
|
|
359
|
+
};
|
|
360
|
+
}
|
|
337
361
|
/**
|
|
338
362
|
* BirdDog submission and reply authentication. The durable row freezes
|
|
339
363
|
* correspondence before transport; an ambiguous delivery reuses that exact mail.
|
|
@@ -384,21 +408,20 @@ export function createFleetCorrespondence(options) {
|
|
|
384
408
|
});
|
|
385
409
|
let row = read(task.requestId);
|
|
386
410
|
if (row === undefined) {
|
|
387
|
-
let sessionId = task.sessionId;
|
|
388
|
-
if (sessionId === undefined) {
|
|
389
|
-
const opened = await options.open(task.agentId);
|
|
390
|
-
try {
|
|
391
|
-
sessionId = await opened.agent.createSession(task.requestId);
|
|
392
|
-
}
|
|
393
|
-
finally {
|
|
394
|
-
await opened.release();
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
411
|
const from = { kind: "agent", ...parent };
|
|
398
412
|
let mail = {
|
|
399
413
|
key: task.requestId,
|
|
400
414
|
from,
|
|
401
|
-
to:
|
|
415
|
+
to: task.sessionId === undefined
|
|
416
|
+
? {
|
|
417
|
+
kind: "agent",
|
|
418
|
+
agentId: task.agentId,
|
|
419
|
+
}
|
|
420
|
+
: {
|
|
421
|
+
kind: "agent",
|
|
422
|
+
agentId: task.agentId,
|
|
423
|
+
sessionId: task.sessionId,
|
|
424
|
+
},
|
|
402
425
|
replyTo: [from],
|
|
403
426
|
content: task.text,
|
|
404
427
|
createdAt: Date.now(),
|
|
@@ -411,11 +434,26 @@ export function createFleetCorrespondence(options) {
|
|
|
411
434
|
throw new DirectoryError(503, "partial_init");
|
|
412
435
|
if (row.input !== input)
|
|
413
436
|
throw new DirectoryError(409, "request_conflict");
|
|
414
|
-
if (row.
|
|
437
|
+
if (row.state === "held")
|
|
438
|
+
return heldRecipientOffline;
|
|
439
|
+
if (row.state === "accepted") {
|
|
440
|
+
if (row.receipt === null)
|
|
441
|
+
throw new DirectoryError(503, "partial_init");
|
|
415
442
|
return parseAccepted(row.receipt);
|
|
443
|
+
}
|
|
416
444
|
assertAgentGrant(grant, "delegate", task.agentId);
|
|
417
|
-
const receipt =
|
|
418
|
-
|
|
445
|
+
const receipt = parseDelegationReceipt(JSON.stringify(await options.deliver(decodeMail(row.mail))));
|
|
446
|
+
if (receipt.status === "held")
|
|
447
|
+
store.recordHeld(task.requestId);
|
|
448
|
+
else {
|
|
449
|
+
const sent = decodeMail(row.mail);
|
|
450
|
+
const sessionId = receipt.sessionId ?? sent.to.sessionId;
|
|
451
|
+
if (sessionId === undefined)
|
|
452
|
+
throw new DirectoryError(503, "partial_init");
|
|
453
|
+
const accepted = { ...receipt, sessionId };
|
|
454
|
+
store.recordReceipt(task.requestId, JSON.stringify(accepted), row.mail, JSON.stringify(bindRecipientSession(sent, sessionId)));
|
|
455
|
+
return accepted;
|
|
456
|
+
}
|
|
419
457
|
return receipt;
|
|
420
458
|
}
|
|
421
459
|
/**
|
|
@@ -431,28 +469,45 @@ export function createFleetCorrespondence(options) {
|
|
|
431
469
|
const suffix = ":reply:0";
|
|
432
470
|
if (mail.replyTo.length !== 0 ||
|
|
433
471
|
mail.reply === undefined ||
|
|
472
|
+
mail.reply.outcome === "accepted" ||
|
|
434
473
|
!mail.key.endsWith(suffix))
|
|
435
474
|
return undefined;
|
|
436
475
|
const row = read(mail.key.slice(0, -suffix.length));
|
|
437
476
|
if (row === undefined)
|
|
438
477
|
return undefined;
|
|
439
478
|
const sent = decodeMail(row.mail);
|
|
479
|
+
if (mail.reply.outcome !== "undeliverable" &&
|
|
480
|
+
mail.from.sessionId === undefined)
|
|
481
|
+
return undefined;
|
|
440
482
|
if (mail.from.agentId !== sent.to.agentId ||
|
|
441
483
|
mail.from.sessionId !== sent.to.sessionId ||
|
|
442
484
|
mail.to.agentId !== sent.from.agentId ||
|
|
443
485
|
mail.to.sessionId !== sent.from.sessionId)
|
|
444
486
|
return undefined;
|
|
445
|
-
if (
|
|
446
|
-
|
|
447
|
-
|
|
487
|
+
if (mail.reply.outcome !== "undeliverable" &&
|
|
488
|
+
row.state === "accepted" &&
|
|
489
|
+
row.receipt !== null) {
|
|
490
|
+
const sentResult = parseAccepted(row.receipt);
|
|
491
|
+
if (sentResult.commandId !== mail.reply.commandId)
|
|
492
|
+
return undefined;
|
|
493
|
+
}
|
|
448
494
|
const path = delegationMetadata.parse(sent.meta).delegation;
|
|
449
495
|
if (path === undefined)
|
|
450
496
|
return undefined;
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
497
|
+
let reply;
|
|
498
|
+
if (mail.reply.outcome === "undeliverable") {
|
|
499
|
+
const undeliverable = { agentId: mail.from.agentId, ...mail.reply };
|
|
500
|
+
reply =
|
|
501
|
+
mail.from.sessionId === undefined
|
|
502
|
+
? undeliverable
|
|
503
|
+
: { ...undeliverable, sessionId: mail.from.sessionId };
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
const sessionId = mail.from.sessionId;
|
|
507
|
+
if (sessionId === undefined)
|
|
508
|
+
return undefined;
|
|
509
|
+
reply = { agentId: mail.from.agentId, sessionId, ...mail.reply };
|
|
510
|
+
}
|
|
456
511
|
return {
|
|
457
512
|
ancestors: path.ancestors.slice(0, -1),
|
|
458
513
|
maxDepth: path.maxDepth,
|
|
@@ -461,10 +516,48 @@ export function createFleetCorrespondence(options) {
|
|
|
461
516
|
}
|
|
462
517
|
/** Read only the existing acceptance receipt; do not reserve or deliver mail. */
|
|
463
518
|
function receipt(requestId) {
|
|
464
|
-
const
|
|
465
|
-
return
|
|
519
|
+
const row = read(requestId);
|
|
520
|
+
return row?.state !== "accepted" || row.receipt === null
|
|
466
521
|
? undefined
|
|
467
|
-
: parseAccepted(
|
|
522
|
+
: parseAccepted(row.receipt);
|
|
523
|
+
}
|
|
524
|
+
/** Atomically bind the recipient Session carried by a late acceptance notice. */
|
|
525
|
+
function recordAcceptedNotice(mail) {
|
|
526
|
+
const suffix = ":accepted";
|
|
527
|
+
if (mail.replyTo.length !== 0 ||
|
|
528
|
+
mail.reply?.outcome !== "accepted" ||
|
|
529
|
+
mail.from.sessionId === undefined ||
|
|
530
|
+
!mail.key.endsWith(suffix))
|
|
531
|
+
return undefined;
|
|
532
|
+
const requestId = mail.key.slice(0, -suffix.length);
|
|
533
|
+
const row = read(requestId);
|
|
534
|
+
if (row === undefined)
|
|
535
|
+
return undefined;
|
|
536
|
+
const sent = decodeMail(row.mail);
|
|
537
|
+
const path = delegationMetadata.safeParse(sent.meta);
|
|
538
|
+
if (sent.from.sessionId === undefined ||
|
|
539
|
+
!path.success ||
|
|
540
|
+
path.data.delegation === undefined ||
|
|
541
|
+
mail.key !== `${sent.key}:accepted` ||
|
|
542
|
+
mail.from.agentId !== sent.to.agentId ||
|
|
543
|
+
(sent.to.sessionId !== undefined &&
|
|
544
|
+
sent.to.sessionId !== mail.from.sessionId) ||
|
|
545
|
+
mail.to.agentId !== sent.from.agentId ||
|
|
546
|
+
mail.to.sessionId !== sent.from.sessionId)
|
|
547
|
+
return undefined;
|
|
548
|
+
if (row.state === "accepted" && row.receipt !== null) {
|
|
549
|
+
const current = parseAccepted(row.receipt);
|
|
550
|
+
if (current.commandId !== mail.reply.commandId ||
|
|
551
|
+
current.sessionId !== mail.from.sessionId)
|
|
552
|
+
return undefined;
|
|
553
|
+
}
|
|
554
|
+
const accepted = {
|
|
555
|
+
status: "accepted",
|
|
556
|
+
commandId: mail.reply.commandId,
|
|
557
|
+
sessionId: mail.from.sessionId,
|
|
558
|
+
};
|
|
559
|
+
store.recordReceipt(requestId, JSON.stringify(accepted), row.mail, JSON.stringify(bindRecipientSession(sent, mail.from.sessionId)));
|
|
560
|
+
return accepted;
|
|
468
561
|
}
|
|
469
562
|
return {
|
|
470
563
|
/** Submit frozen mail or reuse its recorded Session admission receipt. */
|
|
@@ -473,6 +566,8 @@ export function createFleetCorrespondence(options) {
|
|
|
473
566
|
authenticateReply,
|
|
474
567
|
/** Read an existing acceptance receipt without reserving or delivering mail. */
|
|
475
568
|
receipt,
|
|
569
|
+
/** Consume a recipient's stable `<mailKey>:accepted` notice. */
|
|
570
|
+
recordAcceptedNotice,
|
|
476
571
|
};
|
|
477
572
|
}
|
|
478
573
|
/** Local hosts may observe exact work directly; remote correspondence never pulls it. */
|
|
@@ -485,13 +580,18 @@ export function createFleetDelegation(options) {
|
|
|
485
580
|
if (raw === undefined)
|
|
486
581
|
throw new DirectoryError(503, "partial_init");
|
|
487
582
|
const mail = decodeMail(delegationRowSchema.parse(raw).mail);
|
|
488
|
-
|
|
583
|
+
if (accepted.status === "held")
|
|
584
|
+
return pending(1000);
|
|
585
|
+
const sessionId = mail.to.sessionId ?? accepted.sessionId;
|
|
586
|
+
if (sessionId === undefined)
|
|
587
|
+
throw new DirectoryError(503, "partial_init");
|
|
588
|
+
const settlement = await options.readSettlement(mail.to.agentId, sessionId, accepted.commandId);
|
|
489
589
|
return settlement === undefined
|
|
490
590
|
? pending(1000)
|
|
491
591
|
: {
|
|
492
592
|
kind: "settled",
|
|
493
593
|
agentId: mail.to.agentId,
|
|
494
|
-
sessionId
|
|
594
|
+
sessionId,
|
|
495
595
|
...settlement,
|
|
496
596
|
};
|
|
497
597
|
}
|
|
@@ -504,8 +604,12 @@ export function createFleetDelegation(options) {
|
|
|
504
604
|
const row = delegationRowSchema.parse(raw);
|
|
505
605
|
if (row.receipt === null)
|
|
506
606
|
continue;
|
|
607
|
+
if (row.state !== "accepted")
|
|
608
|
+
continue;
|
|
507
609
|
const { to } = decodeMail(row.mail);
|
|
508
610
|
const { commandId } = parseAccepted(row.receipt);
|
|
611
|
+
if (to.sessionId === undefined)
|
|
612
|
+
throw new DirectoryError(503, "partial_init");
|
|
509
613
|
const target = {
|
|
510
614
|
agentId: to.agentId,
|
|
511
615
|
sessionId: to.sessionId,
|
package/dist/http-routes.d.ts
CHANGED
|
@@ -217,7 +217,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
217
217
|
output: {
|
|
218
218
|
readonly providerId: string;
|
|
219
219
|
readonly modelId: string;
|
|
220
|
-
readonly reasoning?: "
|
|
220
|
+
readonly reasoning?: "high" | "low" | "max" | "medium" | "minimal" | "xhigh" | undefined;
|
|
221
221
|
} | null;
|
|
222
222
|
outputFormat: "json";
|
|
223
223
|
status: 200;
|
|
@@ -230,7 +230,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
230
230
|
json: {
|
|
231
231
|
readonly providerId: string;
|
|
232
232
|
readonly modelId: string;
|
|
233
|
-
readonly reasoning?: "
|
|
233
|
+
readonly reasoning?: "high" | "low" | "max" | "medium" | "minimal" | "xhigh" | undefined;
|
|
234
234
|
} | null;
|
|
235
235
|
} & {
|
|
236
236
|
param: {
|
|
@@ -251,7 +251,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
251
251
|
json: {
|
|
252
252
|
readonly providerId: string;
|
|
253
253
|
readonly modelId: string;
|
|
254
|
-
readonly reasoning?: "
|
|
254
|
+
readonly reasoning?: "high" | "low" | "max" | "medium" | "minimal" | "xhigh" | undefined;
|
|
255
255
|
} | null;
|
|
256
256
|
} & {
|
|
257
257
|
param: {
|
|
@@ -297,6 +297,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
297
297
|
output: {
|
|
298
298
|
readonly status: "accepted";
|
|
299
299
|
readonly commandId: string;
|
|
300
|
+
readonly sessionId?: string | undefined;
|
|
300
301
|
} | {
|
|
301
302
|
readonly status: "unknown";
|
|
302
303
|
};
|
|
@@ -453,6 +454,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
453
454
|
output: {
|
|
454
455
|
readonly status: "accepted";
|
|
455
456
|
readonly commandId: string;
|
|
457
|
+
readonly sessionId?: string | undefined;
|
|
456
458
|
} | {
|
|
457
459
|
readonly status: "unknown";
|
|
458
460
|
} | {
|
|
@@ -465,6 +467,75 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
465
467
|
status: 200;
|
|
466
468
|
};
|
|
467
469
|
};
|
|
470
|
+
} & {
|
|
471
|
+
"/sessions/:sessionId/settlement": {
|
|
472
|
+
$get: {
|
|
473
|
+
input: {
|
|
474
|
+
query: {
|
|
475
|
+
readonly commandId: string | string[];
|
|
476
|
+
readonly timeoutMs: string | string[];
|
|
477
|
+
};
|
|
478
|
+
} & {
|
|
479
|
+
param: {
|
|
480
|
+
sessionId: string;
|
|
481
|
+
};
|
|
482
|
+
};
|
|
483
|
+
output: {
|
|
484
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
485
|
+
readonly _tag: "ShiitakeError";
|
|
486
|
+
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
487
|
+
readonly message: string;
|
|
488
|
+
readonly requestId?: string | undefined;
|
|
489
|
+
};
|
|
490
|
+
outputFormat: "json";
|
|
491
|
+
status: 400;
|
|
492
|
+
} | {
|
|
493
|
+
input: {
|
|
494
|
+
query: {
|
|
495
|
+
readonly commandId: string | string[];
|
|
496
|
+
readonly timeoutMs: string | string[];
|
|
497
|
+
};
|
|
498
|
+
} & {
|
|
499
|
+
param: {
|
|
500
|
+
sessionId: string;
|
|
501
|
+
};
|
|
502
|
+
};
|
|
503
|
+
output: {
|
|
504
|
+
readonly acceptedSender: string | null;
|
|
505
|
+
readonly terminal: {
|
|
506
|
+
readonly commandId: string;
|
|
507
|
+
readonly outcome: "completed" | "cancelled" | "failed" | "interrupted";
|
|
508
|
+
readonly sequence: number;
|
|
509
|
+
readonly diagnostic: string | null;
|
|
510
|
+
readonly meta?: any;
|
|
511
|
+
};
|
|
512
|
+
readonly content: string;
|
|
513
|
+
readonly usage: readonly {
|
|
514
|
+
model: string;
|
|
515
|
+
provider: import("@fungi.computer/mule/effect").ProviderId;
|
|
516
|
+
usage: {
|
|
517
|
+
input: number;
|
|
518
|
+
output: number;
|
|
519
|
+
cacheRead: number;
|
|
520
|
+
cacheWrite: number;
|
|
521
|
+
cacheWrite1h?: number | undefined;
|
|
522
|
+
reasoning?: number | undefined;
|
|
523
|
+
totalTokens: number;
|
|
524
|
+
cost: {
|
|
525
|
+
input: number;
|
|
526
|
+
output: number;
|
|
527
|
+
cacheRead: number;
|
|
528
|
+
cacheWrite: number;
|
|
529
|
+
total: number;
|
|
530
|
+
};
|
|
531
|
+
};
|
|
532
|
+
}[];
|
|
533
|
+
readonly toolCalls: number;
|
|
534
|
+
} | null;
|
|
535
|
+
outputFormat: "json";
|
|
536
|
+
status: 200;
|
|
537
|
+
};
|
|
538
|
+
};
|
|
468
539
|
} & {
|
|
469
540
|
"/sessions/:sessionId": {
|
|
470
541
|
$get: {
|
|
@@ -579,13 +650,13 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
579
650
|
thinkingSignature?: string | undefined;
|
|
580
651
|
redacted?: boolean | undefined;
|
|
581
652
|
})[];
|
|
582
|
-
api: import("@
|
|
583
|
-
provider: import("@
|
|
653
|
+
api: import("@fungi.computer/mule/effect").Api;
|
|
654
|
+
provider: import("@fungi.computer/mule/effect").ProviderId;
|
|
584
655
|
model: string;
|
|
585
656
|
responseModel?: string | undefined;
|
|
586
657
|
responseId?: string | undefined;
|
|
587
658
|
providerThinkingLevel?: string | undefined;
|
|
588
|
-
thinkingLevel?: import("@
|
|
659
|
+
thinkingLevel?: import("@fungi.computer/mule/effect").ModelThinkingLevel | undefined;
|
|
589
660
|
diagnostics?: {
|
|
590
661
|
type: string;
|
|
591
662
|
timestamp: number;
|
|
@@ -615,7 +686,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
615
686
|
total: number;
|
|
616
687
|
};
|
|
617
688
|
};
|
|
618
|
-
stopReason: import("@
|
|
689
|
+
stopReason: import("@fungi.computer/mule/effect").StopReason;
|
|
619
690
|
deferred?: {
|
|
620
691
|
provider: string;
|
|
621
692
|
modelId: string;
|
|
@@ -626,6 +697,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
626
697
|
data?: any;
|
|
627
698
|
} | undefined;
|
|
628
699
|
errorMessage?: string | undefined;
|
|
700
|
+
providerError?: string | undefined;
|
|
629
701
|
rawStopReason?: string | undefined;
|
|
630
702
|
endTurn?: boolean | undefined;
|
|
631
703
|
timestamp: number;
|
|
@@ -633,6 +705,17 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
633
705
|
} | {
|
|
634
706
|
toolCallId: string;
|
|
635
707
|
role: "toolResult";
|
|
708
|
+
timestamp: number;
|
|
709
|
+
content: ({
|
|
710
|
+
type: "text";
|
|
711
|
+
text: string;
|
|
712
|
+
textSignature?: string | undefined;
|
|
713
|
+
} | {
|
|
714
|
+
type: "image";
|
|
715
|
+
data: string;
|
|
716
|
+
mimeType: string;
|
|
717
|
+
})[];
|
|
718
|
+
toolName: string;
|
|
636
719
|
usage?: {
|
|
637
720
|
input: number;
|
|
638
721
|
output: number;
|
|
@@ -649,18 +732,6 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
649
732
|
total: number;
|
|
650
733
|
};
|
|
651
734
|
} | 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;
|
|
664
735
|
nestedCalls?: {
|
|
665
736
|
calls: {
|
|
666
737
|
id: string;
|
|
@@ -676,6 +747,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
676
747
|
complete: boolean;
|
|
677
748
|
} | undefined;
|
|
678
749
|
isError: boolean;
|
|
750
|
+
durationMs?: number | undefined;
|
|
679
751
|
};
|
|
680
752
|
}[];
|
|
681
753
|
readonly terminals: readonly {
|
package/dist/http-routes.js
CHANGED
|
@@ -6,7 +6,7 @@ import { validator } from "hono/validator";
|
|
|
6
6
|
import * as Schema from "effect/Schema";
|
|
7
7
|
import { ShiitakeError, toShiitakeErrorWire } from "./errors.js";
|
|
8
8
|
import { JsonValueSchema } from "./internal/json-value.js";
|
|
9
|
-
import { decodeClientIdentity, decodeClientEventsQuery, decodeClientReadQuery, decodeClientSessionCommandRequest, decodeClientSessionCreationRequest, decodeClientPageCursor, decodeClientSessionId, } from "./internal/client-boundary.js";
|
|
9
|
+
import { decodeClientIdentity, decodeClientEventsQuery, decodeClientReadQuery, decodeClientSettlementQuery, decodeClientSessionCommandRequest, decodeClientSessionCreationRequest, decodeClientPageCursor, decodeClientSessionId, } from "./internal/client-boundary.js";
|
|
10
10
|
const decodeJsonValue = Schema.decodeUnknownResult(Schema.fromJsonString(JsonValueSchema));
|
|
11
11
|
/** One encoded extension output; its contract, not Hono, types the value. */
|
|
12
12
|
function jsonResponse(value) {
|
|
@@ -218,6 +218,20 @@ export function createShiitakeHttpRoutes(resolveSession, listSessions, createSes
|
|
|
218
218
|
return context.body(null, 204);
|
|
219
219
|
}
|
|
220
220
|
return context.json(await session.command(command), 200);
|
|
221
|
+
})
|
|
222
|
+
.get("/sessions/:sessionId/settlement", validator("query", (value, context) => {
|
|
223
|
+
const decoded = decodeClientSettlementQuery(value);
|
|
224
|
+
if (decoded._tag === "Failure")
|
|
225
|
+
return context.json(toShiitakeErrorWire(invalidRequest("Settlement request is invalid")), 400);
|
|
226
|
+
return decoded.success;
|
|
227
|
+
}), async (context) => {
|
|
228
|
+
const session = await resolveSession(decodeClientSessionId(context.req.param("sessionId")), context.get("shiitakeMeta"));
|
|
229
|
+
const { commandId, timeoutMs } = context.req.valid("query");
|
|
230
|
+
const settled = await session.readSettlement(commandId, {
|
|
231
|
+
timeoutMs,
|
|
232
|
+
signal: context.req.raw.signal,
|
|
233
|
+
});
|
|
234
|
+
return context.json(settled ?? null, 200);
|
|
221
235
|
})
|
|
222
236
|
.get("/sessions/:sessionId", validator("query", (value, context) => {
|
|
223
237
|
const decoded = decodeClientReadQuery(value);
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ 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, 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";
|
|
8
|
+
export type { AbortCommand, AgentExtensionControls, BranchCommand, CompactCommand, CancelCommand, CancelResult, Accepted, DelegationReceipt, Held, 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
9
|
export type { ColdSessionPage, ColdSessionPageRequest, ColdSessionRow, } from "./cold-sessions.js";
|
|
10
10
|
export { parseForkedSession, parseSessionFrame, parseSessionSnapshot, } from "./client.js";
|
|
11
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";
|
|
@@ -22,7 +22,7 @@ export type ShiitakeHost = Readonly<{
|
|
|
22
22
|
readSettlement: (sessionId: string, commandId: string) => Promise<CommandSettlement | undefined>;
|
|
23
23
|
agent: ShiitakeOwnerAgent;
|
|
24
24
|
/** Durably admit one addressed mail item through the host owner. */
|
|
25
|
-
acceptMail: (mail: BirdDogMail, meta?: HostJson, reply?: VerifiedAgentReply) => Promise<Accepted>;
|
|
25
|
+
acceptMail: (mail: BirdDogMail, meta?: HostJson, reply?: VerifiedAgentReply, stageAcceptedNotice?: (mail: BirdDogMail) => void) => Promise<Accepted>;
|
|
26
26
|
}>;
|
|
27
27
|
/** Construct the host-only Promise facade over the canonical Effect program. */
|
|
28
28
|
export declare function makeHost(options: ShiitakeOptions, mailRoom: BirdDogEffect): Promise<ShiitakeHost>;
|
|
@@ -32,6 +32,8 @@ export type AdmitRequest = Readonly<{
|
|
|
32
32
|
* host passes it, after checking the reply against the mail it sent.
|
|
33
33
|
*/
|
|
34
34
|
reply?: VerifiedAgentReply;
|
|
35
|
+
/** Synchronous owner-local side effect committed with a mail admission. */
|
|
36
|
+
onAccepted?: (sessionId: string, commandId: string) => void;
|
|
35
37
|
}>;
|
|
36
38
|
/** Outcome of one door admission. */
|
|
37
39
|
export type AdmitResult = Readonly<{
|
|
@@ -35,6 +35,7 @@ export function openAdmissionDoor(input) {
|
|
|
35
35
|
when: request.when,
|
|
36
36
|
mail: request.mail,
|
|
37
37
|
reply: decodeReply(request),
|
|
38
|
+
onAccepted: request.onAccepted,
|
|
38
39
|
meta: request.meta === undefined
|
|
39
40
|
? undefined
|
|
40
41
|
: checkHostMeta(input.hostMeta, request.meta),
|
|
@@ -69,6 +70,7 @@ export function openAdmissionDoor(input) {
|
|
|
69
70
|
input.enqueue(work, request.mail);
|
|
70
71
|
}
|
|
71
72
|
: undefined, targetWorkId, request.reply);
|
|
73
|
+
request.onAccepted?.(sessionId, accepted.accepted.commandId);
|
|
72
74
|
touched.set(sessionId, (touched.get(sessionId) ?? false) || accepted.delivery === "new");
|
|
73
75
|
return Object.freeze({
|
|
74
76
|
status: "accepted",
|
|
@@ -8,6 +8,16 @@ export declare const PromptImageSchema: Schema.Struct<{
|
|
|
8
8
|
}>;
|
|
9
9
|
/** Delivery policy for input accepted while a Session run is active. */
|
|
10
10
|
export type PromptDelivery = "steer" | "followUp";
|
|
11
|
+
/** One bounded exact-command read, shared by the in-process and HTTP doors. */
|
|
12
|
+
export declare const decodeClientSettlementRequest: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => Result.Result<{
|
|
13
|
+
readonly commandId: string;
|
|
14
|
+
readonly timeoutMs: number;
|
|
15
|
+
}, Schema.SchemaError>;
|
|
16
|
+
/** Query encoding of the same bounded exact-command read. */
|
|
17
|
+
export declare const decodeClientSettlementQuery: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => Result.Result<{
|
|
18
|
+
readonly commandId: string;
|
|
19
|
+
readonly timeoutMs: number;
|
|
20
|
+
}, Schema.SchemaError>;
|
|
11
21
|
declare const SessionCreationRequestSchema: Schema.Struct<{
|
|
12
22
|
readonly requestId: Schema.String;
|
|
13
23
|
}>;
|
|
@@ -27,6 +27,16 @@ export const PromptImageSchema = Schema.Struct({
|
|
|
27
27
|
const PromptImagesSchema = Schema.Array(PromptImageSchema).check(Schema.isMinLength(1), Schema.isMaxLength(8));
|
|
28
28
|
const PromptDeliverySchema = Schema.Literals(["steer", "followUp"]);
|
|
29
29
|
const SessionIdSchema = boundedId;
|
|
30
|
+
/** One bounded exact-command read, shared by the in-process and HTTP doors. */
|
|
31
|
+
export const decodeClientSettlementRequest = Schema.decodeUnknownResult(Schema.Struct({
|
|
32
|
+
commandId: boundedId,
|
|
33
|
+
timeoutMs: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(30_000)),
|
|
34
|
+
}), { onExcessProperty: "error" });
|
|
35
|
+
/** Query encoding of the same bounded exact-command read. */
|
|
36
|
+
export const decodeClientSettlementQuery = Schema.decodeUnknownResult(Schema.Struct({
|
|
37
|
+
commandId: boundedId,
|
|
38
|
+
timeoutMs: Schema.NumberFromString.check(Schema.isInt(), Schema.isBetween({ minimum: 0, maximum: 30_000 })),
|
|
39
|
+
}), { onExcessProperty: "error" });
|
|
30
40
|
const decodeSessionPageCursorValue = Schema.decodeUnknownResult(boundedText(512));
|
|
31
41
|
const SessionCreationRequestSchema = Schema.Struct({ requestId: boundedId });
|
|
32
42
|
const PromptCommandSchema = Schema.Struct({
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Range selection follows Pi Durable spec §8.7 (`earendil-works/pi` at
|
|
6
6
|
* 8efd314f) and token estimates are pi-ai's own.
|
|
7
7
|
*/
|
|
8
|
-
import type { Api, Model, Usage } from "@
|
|
8
|
+
import type { Api, Model, Usage } from "@fungi.computer/mule/effect";
|
|
9
9
|
import type { MuleStreamFunction } from "@fungi.computer/mule/effect";
|
|
10
10
|
import * as Effect from "effect/Effect";
|
|
11
11
|
import type { BorrowedCompactionViewV0 } from "../../extensions.js";
|
|
@@ -14,14 +14,14 @@ import { createFileOps, type CompactionSettings } from "./utils.js";
|
|
|
14
14
|
export type CompactionSourceEntry = Readonly<{
|
|
15
15
|
id: string;
|
|
16
16
|
kind: "message";
|
|
17
|
-
message: import("@
|
|
17
|
+
message: import("@fungi.computer/mule/effect").Message;
|
|
18
18
|
}> | Readonly<{
|
|
19
19
|
id: string;
|
|
20
20
|
kind: "compaction";
|
|
21
21
|
/** Pi's entry head: the first entry the summary keeps. */
|
|
22
22
|
head: string;
|
|
23
23
|
/** Pi's entry model message: the summary the model reads. */
|
|
24
|
-
model: import("@
|
|
24
|
+
model: import("@fungi.computer/mule/effect").UserMessage;
|
|
25
25
|
usage: Usage;
|
|
26
26
|
details?: Readonly<{
|
|
27
27
|
readFiles: readonly string[];
|
|
@@ -30,8 +30,8 @@ export type CompactionSourceEntry = Readonly<{
|
|
|
30
30
|
}>;
|
|
31
31
|
export type CompactionPreparation = Readonly<{
|
|
32
32
|
firstKeptEntryId: string;
|
|
33
|
-
messagesToSummarize: readonly import("@
|
|
34
|
-
turnPrefixMessages: readonly import("@
|
|
33
|
+
messagesToSummarize: readonly import("@fungi.computer/mule/effect").Message[];
|
|
34
|
+
turnPrefixMessages: readonly import("@fungi.computer/mule/effect").Message[];
|
|
35
35
|
isSplitTurn: boolean;
|
|
36
36
|
tokensBefore: number;
|
|
37
37
|
previousSummary?: string;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { normalizeContext } from "@earendil-works/pi-ai/utils/transcript";
|
|
2
|
+
import { streamModel } from "@fungi.computer/mule/effect";
|
|
1
3
|
// Leaf helpers, not Pi's root entry, which also evaluates its validators.
|
|
2
4
|
import { estimateContextTokens, estimateMessageTokens, } from "@earendil-works/pi-ai/utils/estimate";
|
|
3
5
|
import { retryAssistantCall } from "@earendil-works/pi-ai/utils/retry";
|
|
4
6
|
import { contentText } from "@earendil-works/pi-ai/utils/text";
|
|
5
|
-
import { normalizeContext } from "@earendil-works/pi-ai/utils/transcript";
|
|
6
7
|
import { uuidv7 } from "@earendil-works/pi-ai/utils/uuid";
|
|
7
8
|
import * as Effect from "effect/Effect";
|
|
8
9
|
import { ShiitakeError } from "../../errors.js";
|
|
9
10
|
import { computeFileLists, createFileOps, extractFileOpsFromMessage, formatFileOperations, serializeConversation, SUMMARIZATION_PROMPT, SUMMARIZATION_SYSTEM_PROMPT, TURN_PREFIX_SUMMARIZATION_PROMPT, UPDATE_SUMMARIZATION_PROMPT, compactionSummaryText, summaryFailure, } from "./utils.js";
|
|
10
|
-
/** A summary
|
|
11
|
+
/** A summary uses the stream failure diagnostic, never failed-turn provider prose. */
|
|
11
12
|
function failure(message) {
|
|
12
13
|
return new ShiitakeError("model", "rejected", message);
|
|
13
14
|
}
|
|
@@ -210,9 +211,7 @@ function completeSummarization(model, context, options, streamFn) {
|
|
|
210
211
|
cacheRetention: "none",
|
|
211
212
|
sessionId: options.sessionId ?? uuidv7(),
|
|
212
213
|
};
|
|
213
|
-
return retryAssistantCall(() => streamFn
|
|
214
|
-
? Promise.resolve(streamFn(model, normalizeContext(context), requestOptions)).then((stream) => stream.result())
|
|
215
|
-
: import("@earendil-works/pi-ai/compat").then(({ completeSimple }) => completeSimple(model, context, requestOptions)), RETRY_POLICY, requestOptions.signal);
|
|
214
|
+
return retryAssistantCall(() => Promise.resolve((streamFn ?? streamModel)(model, normalizeContext(context), requestOptions)).then((stream) => stream.result()), RETRY_POLICY, requestOptions.signal);
|
|
216
215
|
}
|
|
217
216
|
function buildContext(promptText) {
|
|
218
217
|
return {
|