@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.
- package/README.md +59 -21
- package/dist/agent-control.d.ts +94 -2
- package/dist/agent-control.js +147 -26
- package/dist/client-entry.d.ts +20 -0
- package/dist/client-entry.js +5 -0
- package/dist/client-visitor.d.ts +33 -0
- package/dist/client-visitor.js +231 -0
- package/dist/client-wire.d.ts +9 -0
- package/dist/client-wire.js +24 -3
- package/dist/client.d.ts +27 -6
- package/dist/client.js +23 -0
- package/dist/cloudflare.d.ts +20 -0
- package/dist/cloudflare.js +4 -1
- package/dist/delegation.d.ts +79 -53
- package/dist/delegation.js +26 -21
- package/dist/effect.d.ts +26 -1
- package/dist/effect.js +14 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +2 -0
- package/dist/extensions.d.ts +22 -3
- package/dist/extensions.js +1 -1
- package/dist/fleet.d.ts +25 -2
- package/dist/fleet.js +108 -24
- package/dist/hosted.js +3 -0
- package/dist/http-routes.d.ts +23 -9
- package/dist/http.d.ts +1 -0
- package/dist/http.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/internal/accepted-run-executor.js +2 -1
- package/dist/internal/admission-door.d.ts +6 -1
- package/dist/internal/admission-door.js +33 -3
- package/dist/internal/contracts.d.ts +19 -1
- package/dist/internal/coordinator.js +115 -52
- package/dist/internal/effect-client.js +1 -1
- package/dist/internal/effect-contracts.d.ts +9 -2
- package/dist/internal/extension-command-registry.d.ts +2 -1
- package/dist/internal/extension-command-registry.js +3 -1
- package/dist/internal/extension-runtime.d.ts +3 -1
- package/dist/internal/extension-runtime.js +18 -8
- package/dist/internal/model-acquisition.js +2 -0
- package/dist/internal/observation-kernel.d.ts +2 -0
- package/dist/internal/observation-kernel.js +17 -6
- package/dist/internal/promise-facade.js +2 -0
- package/dist/internal/run-scope.d.ts +14 -0
- package/dist/internal/run-scope.js +58 -15
- package/dist/internal/runtime-coordinator.d.ts +14 -0
- package/dist/internal/runtime-coordinator.js +60 -11
- package/dist/internal/runtime-core.d.ts +3 -0
- package/dist/internal/runtime-core.js +1 -0
- package/dist/internal/runtime-reconciliation.js +123 -25
- package/dist/internal/session-context.d.ts +5 -5
- package/dist/internal/session-context.js +80 -83
- package/dist/internal/session-status.d.ts +2 -2
- package/dist/internal/session-status.js +5 -3
- package/dist/internal/woodstock/agent-reply.d.ts +5 -0
- package/dist/internal/woodstock/agent-reply.js +3 -0
- package/dist/internal/woodstock/client-projection.js +13 -11
- package/dist/internal/woodstock/fleet-store.d.ts +3 -2
- package/dist/internal/woodstock/fleet-store.js +24 -4
- package/dist/internal/woodstock/index.d.ts +7 -3
- package/dist/internal/woodstock/index.js +23 -6
- package/dist/internal/woodstock/progress.d.ts +11 -1
- package/dist/internal/woodstock/progress.js +126 -25
- package/dist/internal/woodstock/session-delegator.d.ts +13 -0
- package/dist/internal/woodstock/session-delegator.js +44 -0
- package/dist/internal/woodstock/session-entry.d.ts +12 -2
- package/dist/internal/woodstock/session-store.d.ts +16 -2
- package/dist/internal/woodstock/session-store.js +349 -66
- package/dist/internal/woodstock/work-senders.d.ts +32 -0
- package/dist/internal/woodstock/work-senders.js +70 -0
- package/dist/node.js +11 -2
- package/dist/remote-client.js +7 -2
- package/dist/server-visitor-stream.d.ts +13 -0
- package/dist/server-visitor-stream.js +124 -0
- package/dist/session-attention.d.ts +30 -0
- package/dist/session-attention.js +14 -0
- package/docs/api/agent-control.md +212 -15
- package/docs/api/client-entry.md +3 -0
- package/docs/api/client.md +209 -5
- package/docs/api/cloudflare.md +23 -1
- package/docs/api/delegation.md +284 -77
- package/docs/api/effect.md +100 -12
- package/docs/api/extensions.md +73 -12
- package/docs/api/fleet.md +138 -25
- package/docs/api/http.md +28 -0
- package/docs/api/index.md +43 -17
- package/docs/api/testing.md +9 -7
- package/package.json +10 -6
package/dist/fleet.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { commandSettlementSchema } from "./client-wire.js";
|
|
2
|
-
import { createFleetDirectoryStore, createFleetDelegationStore, } from "./internal/woodstock/index.js";
|
|
2
|
+
import { createFleetDirectoryStore, createFleetDelegationStore, readSessionDelegator, } 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";
|
|
@@ -269,16 +269,34 @@ export function createAgentDirectory(sql) {
|
|
|
269
269
|
erasureRosterComplete: store.erasureRosterComplete,
|
|
270
270
|
};
|
|
271
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Whether an Agent may read one command's settlement: the Agent whose mail the
|
|
274
|
+
* command answered, or the Session's delegator of record for work no Agent's
|
|
275
|
+
* mail admitted (goal, loop and person turns, the work its reports carry).
|
|
276
|
+
* Another Agent's correspondence stays with that Agent.
|
|
277
|
+
*/
|
|
278
|
+
export function settlementReadableBy(facts, agentId) {
|
|
279
|
+
return (facts.acceptedSender === agentId ||
|
|
280
|
+
(facts.acceptedSender === null && facts.delegator === agentId));
|
|
281
|
+
}
|
|
272
282
|
/** Replay identity and work, with the parent supplied exclusively by the grant. */
|
|
273
|
-
export const agentDelegationTaskSchema = z.lazy(() => z
|
|
283
|
+
export const agentDelegationTaskSchema = z.lazy(() => z
|
|
284
|
+
.strictObject({
|
|
274
285
|
requestId: identity,
|
|
275
286
|
agentId: identity,
|
|
276
287
|
sessionId: identity.optional(),
|
|
288
|
+
/** A live steer into the recipient's running turn; queued follow-up when omitted. */
|
|
289
|
+
delivery: z.enum(["followUp", "steer"]).optional(),
|
|
290
|
+
/** Only a new-Session follow-up carries goal intent. */
|
|
291
|
+
goal: z
|
|
292
|
+
.union([z.string().min(1).max(4_000), z.literal(false)])
|
|
293
|
+
.optional(),
|
|
277
294
|
text: z
|
|
278
295
|
.string()
|
|
279
296
|
.min(1)
|
|
280
297
|
.max(256 * 1024),
|
|
281
|
-
})
|
|
298
|
+
})
|
|
299
|
+
.refine(({ goal, sessionId, delivery }) => goal === undefined || (sessionId === undefined && delivery !== "steer"), { path: ["goal"], error: "goal requires a new-Session follow-up" }));
|
|
282
300
|
/**
|
|
283
301
|
* Provenance for one delegated send from the invoking Agent's Session. The
|
|
284
302
|
* model program's digest is frozen with the replay input, so a retried tool
|
|
@@ -343,6 +361,19 @@ export function delegationReplyMeta(delegation, replierName) {
|
|
|
343
361
|
const delegationMetadata = z.object({
|
|
344
362
|
delegation: FungiMessageMetaSchema.shape.delegation,
|
|
345
363
|
});
|
|
364
|
+
/** An original one-way report can only name an ancestor of its Agent sender. */
|
|
365
|
+
export function isOneWayAncestorNotice(mail) {
|
|
366
|
+
if (mail.reply !== undefined ||
|
|
367
|
+
mail.replyTo.length !== 0 ||
|
|
368
|
+
mail.from.kind !== "agent" ||
|
|
369
|
+
mail.to.sessionId === undefined)
|
|
370
|
+
return false;
|
|
371
|
+
const parsed = delegationMetadata.safeParse(mail.meta);
|
|
372
|
+
return (parsed.success &&
|
|
373
|
+
parsed.data.delegation !== undefined &&
|
|
374
|
+
parsed.data.delegation.ancestors.includes(mail.to.agentId) &&
|
|
375
|
+
parsed.data.delegation.ancestors.at(-1) === mail.from.agentId);
|
|
376
|
+
}
|
|
346
377
|
const delegationRowSchema = z.lazy(() => z.strictObject({
|
|
347
378
|
input: z.string(),
|
|
348
379
|
mail: z.string(),
|
|
@@ -363,6 +394,25 @@ function bindRecipientSession(mail, sessionId) {
|
|
|
363
394
|
to: { ...mail.to, sessionId },
|
|
364
395
|
};
|
|
365
396
|
}
|
|
397
|
+
/** Read confirmed sender receipts that have no locally observed settlement.
|
|
398
|
+
* Call inside the Agent owner's transaction that also changes archived state.
|
|
399
|
+
* Remote reads happen separately; an unobserved settlement remains busy.
|
|
400
|
+
*/
|
|
401
|
+
export function readOutstandingDelegationSync(storage) {
|
|
402
|
+
const raw = createFleetDelegationStore(storage).unsettled()[0];
|
|
403
|
+
if (raw === undefined)
|
|
404
|
+
return undefined;
|
|
405
|
+
const row = delegationRowSchema.parse(raw);
|
|
406
|
+
const mail = decodeMail(row.mail);
|
|
407
|
+
const sessionId = mail.to.sessionId;
|
|
408
|
+
if (sessionId === undefined)
|
|
409
|
+
throw new DirectoryError(503, "partial_init");
|
|
410
|
+
return {
|
|
411
|
+
agentId: mail.to.agentId,
|
|
412
|
+
sessionId,
|
|
413
|
+
commandId: parseAccepted(z.string().parse(row.receipt)).commandId,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
366
416
|
/**
|
|
367
417
|
* BirdDog submission and reply authentication. The durable row freezes
|
|
368
418
|
* correspondence before transport; an ambiguous delivery reuses that exact mail.
|
|
@@ -390,12 +440,18 @@ export function createFleetCorrespondence(options) {
|
|
|
390
440
|
: undefined;
|
|
391
441
|
const ancestors = inherited?.ancestors ?? [];
|
|
392
442
|
const maxDepth = Math.min(grant.maxDepth ?? 8, inherited?.maxDepth ?? 8);
|
|
393
|
-
//
|
|
443
|
+
// A report to an ancestor is one-way: the parent handles the work but
|
|
444
|
+
// must not send its settlement back as a new prompt to the worker.
|
|
394
445
|
if (parent.agentId === task.agentId)
|
|
395
446
|
throw new AgentGrantError("delegation_loop");
|
|
396
447
|
if (ancestors.length >= maxDepth)
|
|
397
448
|
throw new AgentGrantError("delegation_depth");
|
|
398
|
-
const
|
|
449
|
+
const path = { ancestors: [...ancestors, parent.agentId], maxDepth };
|
|
450
|
+
const delegation = {
|
|
451
|
+
...path,
|
|
452
|
+
...(task.delivery === "steer" && { steer: true }),
|
|
453
|
+
...(task.goal !== undefined && { goal: task.goal }),
|
|
454
|
+
};
|
|
399
455
|
const outgoingMeta = {
|
|
400
456
|
...(objectMeta.success
|
|
401
457
|
? objectMeta.data
|
|
@@ -414,10 +470,20 @@ export function createFleetCorrespondence(options) {
|
|
|
414
470
|
let row = read(task.requestId);
|
|
415
471
|
if (row === undefined) {
|
|
416
472
|
const from = { kind: "agent", ...parent };
|
|
473
|
+
const ancestor = ancestors.includes(task.agentId);
|
|
474
|
+
// A sessionless report to a parent goes to the creator's recorded
|
|
475
|
+
// Session. Never create a fresh Session merely to carry a notice.
|
|
476
|
+
const targetSessionId = task.sessionId ??
|
|
477
|
+
(ancestor
|
|
478
|
+
? readSessionDelegator(options.storage, parent.sessionId)?.replyTo.find(({ agentId }) => agentId === task.agentId)
|
|
479
|
+
?.sessionId
|
|
480
|
+
: undefined);
|
|
481
|
+
if (ancestor && targetSessionId === undefined)
|
|
482
|
+
throw new AgentGrantError("delegator_missing");
|
|
417
483
|
let mail = {
|
|
418
484
|
key: task.requestId,
|
|
419
485
|
from,
|
|
420
|
-
to:
|
|
486
|
+
to: targetSessionId === undefined
|
|
421
487
|
? {
|
|
422
488
|
kind: "agent",
|
|
423
489
|
agentId: task.agentId,
|
|
@@ -425,9 +491,9 @@ export function createFleetCorrespondence(options) {
|
|
|
425
491
|
: {
|
|
426
492
|
kind: "agent",
|
|
427
493
|
agentId: task.agentId,
|
|
428
|
-
sessionId:
|
|
494
|
+
sessionId: targetSessionId,
|
|
429
495
|
},
|
|
430
|
-
replyTo: [from],
|
|
496
|
+
replyTo: ancestor ? [] : [from],
|
|
431
497
|
content: task.text,
|
|
432
498
|
createdAt: Date.now(),
|
|
433
499
|
};
|
|
@@ -471,13 +537,23 @@ export function createFleetCorrespondence(options) {
|
|
|
471
537
|
*/
|
|
472
538
|
function authenticateReply(mail) {
|
|
473
539
|
// This owner sends to one correspondent, so BirdDog's reply index is zero.
|
|
474
|
-
|
|
540
|
+
// A report names the idle Session's delegator mail and the settled command.
|
|
475
541
|
if (mail.replyTo.length !== 0 ||
|
|
476
542
|
mail.reply === undefined ||
|
|
477
|
-
mail.reply.outcome === "accepted"
|
|
478
|
-
|
|
543
|
+
mail.reply.outcome === "accepted")
|
|
544
|
+
return undefined;
|
|
545
|
+
const reportAt = mail.key.lastIndexOf(":report:");
|
|
546
|
+
// A report's key names the command it settles.
|
|
547
|
+
const isReport = reportAt >= 0 &&
|
|
548
|
+
"commandId" in mail.reply &&
|
|
549
|
+
mail.key.slice(reportAt) === `:report:${mail.reply.commandId}:0`;
|
|
550
|
+
const mailKey = isReport
|
|
551
|
+
? mail.key.slice(0, reportAt)
|
|
552
|
+
: mail.key.endsWith(":reply:0")
|
|
553
|
+
? mail.key.slice(0, -":reply:0".length)
|
|
554
|
+
: undefined;
|
|
555
|
+
if (mailKey === undefined)
|
|
479
556
|
return undefined;
|
|
480
|
-
const mailKey = mail.key.slice(0, -suffix.length);
|
|
481
557
|
const row = read(mailKey);
|
|
482
558
|
if (row === undefined || row.state === "undeliverable")
|
|
483
559
|
return undefined;
|
|
@@ -508,7 +584,8 @@ export function createFleetCorrespondence(options) {
|
|
|
508
584
|
mail.reply.reason === "recipient_offline" &&
|
|
509
585
|
!systemUndeliverable)
|
|
510
586
|
return undefined;
|
|
511
|
-
if (
|
|
587
|
+
if (!isReport &&
|
|
588
|
+
mail.reply.outcome !== "undeliverable" &&
|
|
512
589
|
row.state === "accepted" &&
|
|
513
590
|
row.receipt !== null) {
|
|
514
591
|
const sentResult = parseAccepted(row.receipt);
|
|
@@ -627,7 +704,9 @@ export function createFleetCorrespondence(options) {
|
|
|
627
704
|
start,
|
|
628
705
|
/** Match a reply to frozen mail and restore the caller's delegation path. */
|
|
629
706
|
authenticateReply,
|
|
707
|
+
/** Bind exhausted local delivery to its frozen sender correspondence. */
|
|
630
708
|
recordLocalUndeliverable,
|
|
709
|
+
/** Prevent replay after a system undeliverable reply is admitted. */
|
|
631
710
|
recordUndeliverableReply,
|
|
632
711
|
/** Read an existing acceptance receipt without reserving or delivering mail. */
|
|
633
712
|
receipt,
|
|
@@ -651,6 +730,8 @@ export function createFleetDelegation(options) {
|
|
|
651
730
|
if (sessionId === undefined)
|
|
652
731
|
throw new DirectoryError(503, "partial_init");
|
|
653
732
|
const settlement = await options.readSettlement(mail.to.agentId, sessionId, accepted.commandId);
|
|
733
|
+
if (settlement !== undefined)
|
|
734
|
+
store.recordSettled(task.requestId);
|
|
654
735
|
return settlement === undefined
|
|
655
736
|
? pending(1000)
|
|
656
737
|
: {
|
|
@@ -665,7 +746,8 @@ export function createFleetDelegation(options) {
|
|
|
665
746
|
* target, so the owner is still waiting for a reply; undefined when none.
|
|
666
747
|
*/
|
|
667
748
|
async function outstanding() {
|
|
668
|
-
|
|
749
|
+
let waiting;
|
|
750
|
+
for (const raw of store.unsettled()) {
|
|
669
751
|
const row = delegationRowSchema.parse(raw);
|
|
670
752
|
if (row.receipt === null)
|
|
671
753
|
continue;
|
|
@@ -680,21 +762,23 @@ export function createFleetDelegation(options) {
|
|
|
680
762
|
sessionId: to.sessionId,
|
|
681
763
|
commandId,
|
|
682
764
|
};
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
settlement
|
|
686
|
-
|
|
687
|
-
catch (cause) {
|
|
765
|
+
const result = await options
|
|
766
|
+
.readSettlement(to.agentId, to.sessionId, commandId)
|
|
767
|
+
.then((settlement) => ({ kind: "read", settlement }), (cause) => ({ kind: "unreadable", cause }));
|
|
768
|
+
if (result.kind === "unreadable") {
|
|
688
769
|
// Work whose state cannot be read is still owed; say why.
|
|
689
|
-
|
|
770
|
+
waiting ??= {
|
|
690
771
|
...target,
|
|
691
|
-
unreadable: cause instanceof Error ? cause.message : "unreadable",
|
|
772
|
+
unreadable: result.cause instanceof Error ? result.cause.message : "unreadable",
|
|
692
773
|
};
|
|
774
|
+
continue;
|
|
693
775
|
}
|
|
694
|
-
if (settlement === undefined)
|
|
695
|
-
|
|
776
|
+
if (result.settlement === undefined)
|
|
777
|
+
waiting ??= target;
|
|
778
|
+
else
|
|
779
|
+
store.recordSettled(decodeMail(row.mail).key);
|
|
696
780
|
}
|
|
697
|
-
return
|
|
781
|
+
return waiting;
|
|
698
782
|
}
|
|
699
783
|
return {
|
|
700
784
|
/** The delegated work this owner still waits on, if any. */
|
package/dist/hosted.js
CHANGED
|
@@ -166,12 +166,15 @@ function hostedPort(manifest, revision, grants) {
|
|
|
166
166
|
}),
|
|
167
167
|
attention: Object.freeze({
|
|
168
168
|
set: () => Effect.fail(unavailable("Attention")),
|
|
169
|
+
awaitsPerson: () => Effect.fail(unavailable("Attention")),
|
|
169
170
|
clear: () => Effect.fail(unavailable("Attention")),
|
|
170
171
|
}),
|
|
171
172
|
hasPendingSteer: () => Effect.fail(unavailable("Steering")),
|
|
173
|
+
workSender: () => Effect.fail(unavailable("Run sender")),
|
|
172
174
|
sessionPosition: () => Effect.fail(unavailable("Session timeline")),
|
|
173
175
|
hasSuccessfulToolResult: () => Effect.fail(unavailable("A tool result")),
|
|
174
176
|
observe: () => Effect.die(unavailable("Observation")),
|
|
177
|
+
isObserved: () => false,
|
|
175
178
|
transact: () => Effect.fail(new ShiitakeError("extensions", "rejected", "A hosted extension has no Agent transaction")),
|
|
176
179
|
}),
|
|
177
180
|
});
|
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" | "state_reset_required" | "storage_pressure";
|
|
45
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure" | "archived";
|
|
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" | "state_reset_required" | "storage_pressure";
|
|
112
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure" | "archived";
|
|
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" | "state_reset_required" | "storage_pressure";
|
|
153
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure" | "archived";
|
|
154
154
|
readonly _tag: "ShiitakeError";
|
|
155
155
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
156
156
|
readonly message: string;
|
|
@@ -238,7 +238,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
238
238
|
};
|
|
239
239
|
};
|
|
240
240
|
output: {
|
|
241
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
241
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure" | "archived";
|
|
242
242
|
readonly _tag: "ShiitakeError";
|
|
243
243
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
244
244
|
readonly message: string;
|
|
@@ -276,7 +276,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
276
276
|
};
|
|
277
277
|
};
|
|
278
278
|
output: {
|
|
279
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
279
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure" | "archived";
|
|
280
280
|
readonly _tag: "ShiitakeError";
|
|
281
281
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
282
282
|
readonly message: string;
|
|
@@ -352,7 +352,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
352
352
|
};
|
|
353
353
|
};
|
|
354
354
|
output: {
|
|
355
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
355
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure" | "archived";
|
|
356
356
|
readonly _tag: "ShiitakeError";
|
|
357
357
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
358
358
|
readonly message: string;
|
|
@@ -481,7 +481,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
481
481
|
};
|
|
482
482
|
};
|
|
483
483
|
output: {
|
|
484
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
484
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure" | "archived";
|
|
485
485
|
readonly _tag: "ShiitakeError";
|
|
486
486
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
487
487
|
readonly message: string;
|
|
@@ -502,6 +502,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
502
502
|
};
|
|
503
503
|
output: {
|
|
504
504
|
readonly acceptedSender: string | null;
|
|
505
|
+
readonly delegator: string | null;
|
|
505
506
|
readonly terminal: {
|
|
506
507
|
readonly commandId: string;
|
|
507
508
|
readonly outcome: "completed" | "cancelled" | "failed" | "interrupted";
|
|
@@ -531,6 +532,11 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
531
532
|
};
|
|
532
533
|
}[];
|
|
533
534
|
readonly toolCalls: number;
|
|
535
|
+
readonly waitingOnYou: readonly {
|
|
536
|
+
readonly itemId: string;
|
|
537
|
+
readonly kind: string;
|
|
538
|
+
readonly summary: string;
|
|
539
|
+
}[];
|
|
534
540
|
} | null;
|
|
535
541
|
outputFormat: "json";
|
|
536
542
|
status: 200;
|
|
@@ -551,7 +557,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
551
557
|
};
|
|
552
558
|
};
|
|
553
559
|
output: {
|
|
554
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
560
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure" | "archived";
|
|
555
561
|
readonly _tag: "ShiitakeError";
|
|
556
562
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
557
563
|
readonly message: string;
|
|
@@ -815,6 +821,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
815
821
|
readonly kind: "session";
|
|
816
822
|
readonly sessionId: string;
|
|
817
823
|
readonly commandId: string;
|
|
824
|
+
readonly title: string;
|
|
818
825
|
} | {
|
|
819
826
|
readonly kind: "host";
|
|
820
827
|
readonly lane: string;
|
|
@@ -843,6 +850,8 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
843
850
|
readonly revision: number;
|
|
844
851
|
readonly text: string;
|
|
845
852
|
readonly thinking: string;
|
|
853
|
+
readonly textOffset: number;
|
|
854
|
+
readonly thinkingOffset: number;
|
|
846
855
|
readonly textTruncated: boolean;
|
|
847
856
|
readonly thinkingTruncated: boolean;
|
|
848
857
|
} | null;
|
|
@@ -873,6 +882,11 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
873
882
|
readonly extensionId: string;
|
|
874
883
|
readonly expiresAt?: number | undefined;
|
|
875
884
|
readonly inbox?: false | undefined;
|
|
885
|
+
readonly waitingOn?: {
|
|
886
|
+
readonly agentId: string;
|
|
887
|
+
readonly sessionId: string;
|
|
888
|
+
readonly name: string;
|
|
889
|
+
} | undefined;
|
|
876
890
|
}[];
|
|
877
891
|
readonly context?: {
|
|
878
892
|
readonly tokens: number;
|
|
@@ -915,7 +929,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
|
|
|
915
929
|
};
|
|
916
930
|
};
|
|
917
931
|
output: {
|
|
918
|
-
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
|
|
932
|
+
readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure" | "archived";
|
|
919
933
|
readonly _tag: "ShiitakeError";
|
|
920
934
|
readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
|
|
921
935
|
readonly message: string;
|
package/dist/http.d.ts
CHANGED
package/dist/http.js
CHANGED
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, 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";
|
|
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, SessionProgressDelta, 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";
|
|
@@ -9,7 +9,7 @@ import { ExtensionId } from "../extension-contract.js";
|
|
|
9
9
|
import { publishOwnerObservation } from "./observation-kernel.js";
|
|
10
10
|
import { returnedFailureReason } from "./run-failure-reason.js";
|
|
11
11
|
import { coreProtocolDigest } from "./extension-roster.js";
|
|
12
|
-
import { runAcceptedCommand, runAcceptedCompaction, ToolSetupError, } from "./run-scope.js";
|
|
12
|
+
import { runAcceptedCommand, runAcceptedCompaction, RunRejectedError, ToolSetupError, } from "./run-scope.js";
|
|
13
13
|
import { CompactionCommitError } from "./compaction/session-compaction.js";
|
|
14
14
|
import { BrokenSessionError, isCompactCommand, loadAcceptedRun, readExtensionAdmissionOrigin, recordWorkSettlement, } from "./woodstock/index.js";
|
|
15
15
|
/** Bounded process-loss recovery for every accepted Agent turn. */
|
|
@@ -25,6 +25,7 @@ function runtimeFailure(message) {
|
|
|
25
25
|
function failedDecision(error) {
|
|
26
26
|
const category = `${error.boundary}:${error.cause}`;
|
|
27
27
|
const diagnostic = error instanceof ToolSetupError ||
|
|
28
|
+
error instanceof RunRejectedError ||
|
|
28
29
|
error instanceof CompactionCommitError ||
|
|
29
30
|
error instanceof BrokenSessionError
|
|
30
31
|
? error.diagnostic
|
|
@@ -32,8 +32,13 @@ 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
|
|
35
|
+
/** Synchronous owner-local side effect on first admission, in its transaction. */
|
|
36
36
|
onAccepted?: (sessionId: string, commandId: string) => void;
|
|
37
|
+
/**
|
|
38
|
+
* New work that continues earlier work in this Session (for example, the
|
|
39
|
+
* follow-up that delivers an answer) keeps that work's sender.
|
|
40
|
+
*/
|
|
41
|
+
continuesWorkId?: string | undefined;
|
|
37
42
|
}>;
|
|
38
43
|
/** Outcome of one door admission. */
|
|
39
44
|
export type AdmitResult = Readonly<{
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { messageAuthor } from "@fungi.computer/agar/agents";
|
|
1
2
|
import * as Effect from "effect/Effect";
|
|
2
3
|
import * as Schema from "effect/Schema";
|
|
3
4
|
import { checkHostMeta } from "./host-meta.js";
|
|
4
5
|
import { ShiitakeError } from "../errors.js";
|
|
5
6
|
import { decodeClientAcceptedCommand, decodeClientSessionId, decodeClientSessionCreationRequestId, requireDecoded, } from "./client-boundary.js";
|
|
6
7
|
import { publishOwnerObservation } from "./observation-kernel.js";
|
|
7
|
-
import { AdmissionOriginSchema, acceptSessionCommandOwned, readSessionCommandAdmissionByRequest, withdrawSessionRequestOwned, assertSessionRequestNotWithdrawn, hasStoredSession, VerifiedAgentReplySchema, } from "./woodstock/index.js";
|
|
8
|
+
import { AdmissionOriginSchema, acceptSessionCommandOwned, readSessionCommandAdmissionByRequest, withdrawSessionRequestOwned, assertSessionRequestNotWithdrawn, hasStoredSession, hasRecordedRunTerminal, readWorkSender, recordWorkSender, VerifiedAgentReplySchema, } from "./woodstock/index.js";
|
|
8
9
|
const decodeOrigin = Schema.decodeUnknownResult(AdmissionOriginSchema, {
|
|
9
10
|
onExcessProperty: "error",
|
|
10
11
|
});
|
|
@@ -29,6 +30,24 @@ function decodeReply(request) {
|
|
|
29
30
|
throw new ShiitakeError("contract", "invalid", "Verified Agent reply does not match its mail");
|
|
30
31
|
return reply;
|
|
31
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* The Agent Session whose work this admission is: the sender of delegated
|
|
35
|
+
* mail that expects a reply, or the sender of the earlier work this one
|
|
36
|
+
* continues. Replies and every other admission have none.
|
|
37
|
+
*/
|
|
38
|
+
function workSender(owner, request) {
|
|
39
|
+
const mail = request.mail;
|
|
40
|
+
if (mail?.from.kind === "agent" && mail.replyTo.length > 0)
|
|
41
|
+
return {
|
|
42
|
+
agentId: mail.from.agentId,
|
|
43
|
+
sessionId: mail.from.sessionId,
|
|
44
|
+
// The host-authenticated author names the sender; mail.meta is the sender's own claim.
|
|
45
|
+
name: messageAuthor(request.meta)?.name ?? mail.from.agentId,
|
|
46
|
+
};
|
|
47
|
+
return request.continuesWorkId === undefined
|
|
48
|
+
? undefined
|
|
49
|
+
: readWorkSender(owner, request.sessionId, request.continuesWorkId);
|
|
50
|
+
}
|
|
32
51
|
/** Bind the door to one owner, run contract, Watchdog queue and host wake. */
|
|
33
52
|
export function openAdmissionDoor(input) {
|
|
34
53
|
/** The door's one decode, done before any owner operation when possible. */
|
|
@@ -41,6 +60,7 @@ export function openAdmissionDoor(input) {
|
|
|
41
60
|
mail: request.mail,
|
|
42
61
|
reply: decodeReply(request),
|
|
43
62
|
onAccepted: request.onAccepted,
|
|
63
|
+
continuesWorkId: request.continuesWorkId,
|
|
44
64
|
meta: request.meta === undefined
|
|
45
65
|
? undefined
|
|
46
66
|
: checkHostMeta(input.hostMeta, request.meta),
|
|
@@ -67,7 +87,8 @@ export function openAdmissionDoor(input) {
|
|
|
67
87
|
const targetWorkId = command.type === "prompt" &&
|
|
68
88
|
command.delivery === "steer" &&
|
|
69
89
|
head.state === "running" &&
|
|
70
|
-
!head.cancellationRequested
|
|
90
|
+
!head.cancellationRequested &&
|
|
91
|
+
!hasRecordedRunTerminal(input.owner, sessionId, head.jobId)
|
|
71
92
|
? head.jobId
|
|
72
93
|
: undefined;
|
|
73
94
|
const accepted = acceptSessionCommandOwned(input.owner, sessionId, command, request.meta, origin, input.contract(), targetWorkId === undefined
|
|
@@ -75,7 +96,16 @@ export function openAdmissionDoor(input) {
|
|
|
75
96
|
input.enqueue(work, request.mail);
|
|
76
97
|
}
|
|
77
98
|
: undefined, targetWorkId, request.reply);
|
|
78
|
-
|
|
99
|
+
if (accepted.delivery === "new" && targetWorkId === undefined) {
|
|
100
|
+
const sender = workSender(input.owner, request);
|
|
101
|
+
if (sender !== undefined)
|
|
102
|
+
recordWorkSender(input.owner, sessionId, accepted.accepted.commandId, sender);
|
|
103
|
+
}
|
|
104
|
+
// The mail notice is staged in the same transaction as the first command.
|
|
105
|
+
// Replaying that admission must not stage a new notice with a different
|
|
106
|
+
// createdAt under the original Post key.
|
|
107
|
+
if (accepted.delivery === "new")
|
|
108
|
+
request.onAccepted?.(sessionId, accepted.accepted.commandId);
|
|
79
109
|
touched.set(sessionId, (touched.get(sessionId) ?? false) || accepted.delivery === "new");
|
|
80
110
|
return Object.freeze({
|
|
81
111
|
status: "accepted",
|
|
@@ -3,7 +3,7 @@ import type { SessionTerminal, ShiitakeAgent } from "../client.js";
|
|
|
3
3
|
import type { GrantsDelivery, GrantsDeliveryResult } from "./woodstock/index.js";
|
|
4
4
|
import type { Json } from "../extensions.js";
|
|
5
5
|
import type { ExtensionRosterV0 } from "../extensions.js";
|
|
6
|
-
import type { Api, AssistantMessage, Model, MuleStreamFunction } from "@fungi.computer/mule/effect";
|
|
6
|
+
import type { Api, AssistantMessage, Model, MuleStreamFunction, ProviderRejection } from "@fungi.computer/mule/effect";
|
|
7
7
|
import type { AccessOptions, CapabilitySource, ExecutionOptions, ModuleRegistration, Sandbox } from "@fungi.computer/mycelium";
|
|
8
8
|
import type { ModelPreference } from "./model-preference.js";
|
|
9
9
|
import type { WatchdogSqliteValue, WatchdogSqliteRow, WatchdogSqliteOwner, WatchdogSqliteCursor } from "@fungi.computer/watchdog";
|
|
@@ -47,6 +47,8 @@ export type ModelAcquisition = Readonly<{
|
|
|
47
47
|
* Unknown messages must return undefined.
|
|
48
48
|
*/
|
|
49
49
|
failureReason?: (message: AssistantMessage) => string | undefined;
|
|
50
|
+
/** Bounded provider facts for this door's exact failure, never SDK prose. */
|
|
51
|
+
rejection?: (message: AssistantMessage) => ProviderRejection | undefined;
|
|
50
52
|
/** Provider prose for this door's exact failed turn; never a diagnostic. */
|
|
51
53
|
providerError?: (message: AssistantMessage) => string | undefined;
|
|
52
54
|
/** Release the acquired model resources. */
|
|
@@ -186,6 +188,11 @@ export type ShiitakeOptions = Readonly<{
|
|
|
186
188
|
store: SqliteOwner;
|
|
187
189
|
/** Host storage-pressure check; absent never refuses for storage pressure. */
|
|
188
190
|
storagePressure?: StoragePressureCheck;
|
|
191
|
+
/** Host-owned, synchronous admission for new Sessions and prompts only. */
|
|
192
|
+
admission?: Readonly<{
|
|
193
|
+
/** Check the host's current flag synchronously inside admission's transaction. */
|
|
194
|
+
assertOpen: () => void;
|
|
195
|
+
}>;
|
|
189
196
|
/** Compaction budgets; absent uses Pi's defaults for every model. */
|
|
190
197
|
compaction?: CompactionOptions;
|
|
191
198
|
/** Runtime host that schedules the opaque durable reconciler. */
|
|
@@ -253,6 +260,8 @@ export type { HostMetaOptions, StandardHostMetaResult, StandardHostMetaSchema, }
|
|
|
253
260
|
export type CommandSettlement = Readonly<{
|
|
254
261
|
/** Original Agent sender recorded at acceptance, never the terminal's canceller. */
|
|
255
262
|
acceptedSender: string | null;
|
|
263
|
+
/** Agent that first sent this Session work and receives its reports, if any. */
|
|
264
|
+
delegator: string | null;
|
|
256
265
|
/** Durable terminal and the command that ended the work's opaque metadata. */
|
|
257
266
|
terminal: SessionTerminal;
|
|
258
267
|
/** Last completed assistant text; cancelled work returns no answer. */
|
|
@@ -261,4 +270,13 @@ export type CommandSettlement = Readonly<{
|
|
|
261
270
|
usage: readonly Pick<AssistantMessage, "provider" | "model" | "usage">[];
|
|
262
271
|
/** Distinct tool-call intentions belonging to this exact work. */
|
|
263
272
|
toolCalls: number;
|
|
273
|
+
/**
|
|
274
|
+
* Open items of the Session that now wait on the Agent that sent this work.
|
|
275
|
+
* Child-authored data: summaries inform, only the sender's host checks decide.
|
|
276
|
+
*/
|
|
277
|
+
waitingOnYou: readonly Readonly<{
|
|
278
|
+
itemId: string;
|
|
279
|
+
kind: string;
|
|
280
|
+
summary: string;
|
|
281
|
+
}>[];
|
|
264
282
|
}>;
|