@fungi.computer/shiitake 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +59 -21
  2. package/dist/agent-control.d.ts +1 -1
  3. package/dist/agent-control.js +40 -21
  4. package/dist/client-visitor.d.ts +2 -0
  5. package/dist/client-visitor.js +6 -1
  6. package/dist/client-wire.d.ts +4 -0
  7. package/dist/client-wire.js +26 -15
  8. package/dist/client.d.ts +23 -11
  9. package/dist/client.js +22 -0
  10. package/dist/cloudflare.d.ts +20 -0
  11. package/dist/cloudflare.js +4 -1
  12. package/dist/delegation.d.ts +5 -0
  13. package/dist/delegation.js +24 -8
  14. package/dist/effect.d.ts +1 -1
  15. package/dist/errors.d.ts +2 -2
  16. package/dist/errors.js +2 -0
  17. package/dist/extensions.d.ts +1 -1
  18. package/dist/fleet.d.ts +14 -2
  19. package/dist/fleet.js +81 -19
  20. package/dist/http-routes.d.ts +12 -16
  21. package/dist/http.d.ts +1 -0
  22. package/dist/http.js +1 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/internal/admission-door.d.ts +1 -1
  25. package/dist/internal/admission-door.js +8 -4
  26. package/dist/internal/contracts.d.ts +5 -0
  27. package/dist/internal/coordinator.js +107 -50
  28. package/dist/internal/effect-contracts.d.ts +5 -0
  29. package/dist/internal/extension-runtime.js +5 -3
  30. package/dist/internal/observation-kernel.js +12 -3
  31. package/dist/internal/promise-facade.js +2 -0
  32. package/dist/internal/run-scope.d.ts +1 -0
  33. package/dist/internal/run-scope.js +29 -7
  34. package/dist/internal/runtime-coordinator.d.ts +14 -0
  35. package/dist/internal/runtime-coordinator.js +57 -10
  36. package/dist/internal/runtime-core.d.ts +3 -0
  37. package/dist/internal/runtime-core.js +1 -0
  38. package/dist/internal/runtime-reconciliation.js +4 -1
  39. package/dist/internal/session-context.d.ts +5 -5
  40. package/dist/internal/session-context.js +80 -83
  41. package/dist/internal/session-status.d.ts +4 -2
  42. package/dist/internal/session-status.js +22 -4
  43. package/dist/internal/woodstock/client-projection.js +5 -11
  44. package/dist/internal/woodstock/fleet-store.d.ts +3 -2
  45. package/dist/internal/woodstock/fleet-store.js +24 -4
  46. package/dist/internal/woodstock/index.d.ts +3 -3
  47. package/dist/internal/woodstock/index.js +18 -7
  48. package/dist/internal/woodstock/progress.d.ts +13 -1
  49. package/dist/internal/woodstock/progress.js +130 -24
  50. package/dist/internal/woodstock/session-delegator.d.ts +2 -1
  51. package/dist/internal/woodstock/session-store.d.ts +14 -2
  52. package/dist/internal/woodstock/session-store.js +329 -66
  53. package/dist/node.js +11 -2
  54. package/dist/server-visitor-stream.js +33 -24
  55. package/dist/testing.d.ts +2 -0
  56. package/dist/testing.js +2 -0
  57. package/docs/api/agent-control.md +3 -2
  58. package/docs/api/client.md +79 -19
  59. package/docs/api/cloudflare.md +16 -0
  60. package/docs/api/delegation.md +84 -50
  61. package/docs/api/effect.md +85 -7
  62. package/docs/api/extensions.md +8 -8
  63. package/docs/api/fleet.md +118 -29
  64. package/docs/api/http.md +28 -0
  65. package/docs/api/index.md +39 -15
  66. package/docs/api/testing.md +20 -7
  67. package/package.json +5 -5
package/dist/errors.js CHANGED
@@ -21,6 +21,8 @@ const ShiitakeErrorCauseSchema = Schema.Literals([
21
21
  "state_reset_required",
22
22
  /** Owner storage is near its budget; new Sessions and prompts wait. */
23
23
  "storage_pressure",
24
+ /** This Agent is archived; new Sessions and prompts require restoration. */
25
+ "archived",
24
26
  ]);
25
27
  const requestIdSchema = Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.isMaxLength(256)));
26
28
  /** Canonical serialized Shiitake failure. */
@@ -77,7 +77,7 @@ declare const AdmissionError_base: new <A extends Record<string, any> = {}>(args
77
77
  } & Readonly<A>;
78
78
  /** The door refused an extension admission. */
79
79
  export declare class AdmissionError extends AdmissionError_base<{
80
- reason: "invalid" | "conflict" | "closed";
80
+ reason: "invalid" | "conflict" | "closed" | "archived";
81
81
  message: string;
82
82
  }> {
83
83
  }
package/dist/fleet.d.ts CHANGED
@@ -82,8 +82,8 @@ export type AgentLifecycleFact = Readonly<{
82
82
  }>;
83
83
  /** Refusal of supplied authority; no host policy lookup is performed. */
84
84
  export declare class AgentGrantError extends Error {
85
- readonly reason: "agent" | "action" | "expired" | "delegation_loop" | "delegation_depth";
86
- constructor(reason: "agent" | "action" | "expired" | "delegation_loop" | "delegation_depth");
85
+ readonly reason: "agent" | "action" | "expired" | "delegation_loop" | "delegation_depth" | "delegator_missing";
86
+ constructor(reason: "agent" | "action" | "expired" | "delegation_loop" | "delegation_depth" | "delegator_missing");
87
87
  }
88
88
  /** Check every operation against its explicit authority. */
89
89
  export declare function assertAgentGrant(grant: AgentGrant, action: AgentAction, agentId?: string): void;
@@ -196,6 +196,7 @@ export declare const agentDelegationTaskSchema: z.ZodLazy<z.ZodObject<{
196
196
  followUp: "followUp";
197
197
  steer: "steer";
198
198
  }>>;
199
+ goal: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
199
200
  text: z.ZodString;
200
201
  }, z.core.$strict>>;
201
202
  /** A caller reuses this task unchanged while polling Pending. */
@@ -271,6 +272,12 @@ export declare function delegationSendMeta(sender: Readonly<{
271
272
  * while the verified reply retains the failed recipient's identity.
272
273
  */
273
274
  export declare function delegationReplyMeta(delegation: AuthenticatedDelegationReply, replierName: string): DelegationReplyMeta;
275
+ /**
276
+ * Shape check for a one-way ancestor notice, not sender provenance. The Local
277
+ * mail receiver checks mesh membership before calling it. This addressed mail
278
+ * is still work and may steer the parent's running turn when admitted.
279
+ */
280
+ export declare function isOneWayAncestorNotice(mail: BirdDogMail): boolean;
274
281
  /**
275
282
  * Delegated work an owner still waits on; `unreadable` says why its state
276
283
  * could not be read, when it could not.
@@ -281,6 +288,11 @@ export type OutstandingDelegation = Readonly<{
281
288
  commandId: string;
282
289
  unreadable?: string;
283
290
  }>;
291
+ /** Read confirmed sender receipts that have no locally observed settlement.
292
+ * Call inside the Agent owner's transaction that also changes archived state.
293
+ * Remote reads happen separately; an unobserved settlement remains busy.
294
+ */
295
+ export declare function readOutstandingDelegationSync(storage: WatchdogSqliteOwner): OutstandingDelegation | undefined;
284
296
  /**
285
297
  * BirdDog submission and reply authentication. The durable row freezes
286
298
  * correspondence before transport; an ambiguous delivery reuses that exact mail.
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";
@@ -280,17 +280,23 @@ export function settlementReadableBy(facts, agentId) {
280
280
  (facts.acceptedSender === null && facts.delegator === agentId));
281
281
  }
282
282
  /** Replay identity and work, with the parent supplied exclusively by the grant. */
283
- export const agentDelegationTaskSchema = z.lazy(() => z.strictObject({
283
+ export const agentDelegationTaskSchema = z.lazy(() => z
284
+ .strictObject({
284
285
  requestId: identity,
285
286
  agentId: identity,
286
287
  sessionId: identity.optional(),
287
288
  /** A live steer into the recipient's running turn; queued follow-up when omitted. */
288
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(),
289
294
  text: z
290
295
  .string()
291
296
  .min(1)
292
297
  .max(256 * 1024),
293
- }));
298
+ })
299
+ .refine(({ goal, sessionId, delivery }) => goal === undefined || (sessionId === undefined && delivery !== "steer"), { path: ["goal"], error: "goal requires a new-Session follow-up" }));
294
300
  /**
295
301
  * Provenance for one delegated send from the invoking Agent's Session. The
296
302
  * model program's digest is frozen with the replay input, so a retried tool
@@ -355,6 +361,23 @@ export function delegationReplyMeta(delegation, replierName) {
355
361
  const delegationMetadata = z.object({
356
362
  delegation: FungiMessageMetaSchema.shape.delegation,
357
363
  });
364
+ /**
365
+ * Shape check for a one-way ancestor notice, not sender provenance. The Local
366
+ * mail receiver checks mesh membership before calling it. This addressed mail
367
+ * is still work and may steer the parent's running turn when admitted.
368
+ */
369
+ export function isOneWayAncestorNotice(mail) {
370
+ if (mail.reply !== undefined ||
371
+ mail.replyTo.length !== 0 ||
372
+ mail.from.kind !== "agent" ||
373
+ mail.to.sessionId === undefined)
374
+ return false;
375
+ const parsed = delegationMetadata.safeParse(mail.meta);
376
+ return (parsed.success &&
377
+ parsed.data.delegation !== undefined &&
378
+ parsed.data.delegation.ancestors.includes(mail.to.agentId) &&
379
+ parsed.data.delegation.ancestors.at(-1) === mail.from.agentId);
380
+ }
358
381
  const delegationRowSchema = z.lazy(() => z.strictObject({
359
382
  input: z.string(),
360
383
  mail: z.string(),
@@ -375,6 +398,25 @@ function bindRecipientSession(mail, sessionId) {
375
398
  to: { ...mail.to, sessionId },
376
399
  };
377
400
  }
401
+ /** Read confirmed sender receipts that have no locally observed settlement.
402
+ * Call inside the Agent owner's transaction that also changes archived state.
403
+ * Remote reads happen separately; an unobserved settlement remains busy.
404
+ */
405
+ export function readOutstandingDelegationSync(storage) {
406
+ const raw = createFleetDelegationStore(storage).unsettled()[0];
407
+ if (raw === undefined)
408
+ return undefined;
409
+ const row = delegationRowSchema.parse(raw);
410
+ const mail = decodeMail(row.mail);
411
+ const sessionId = mail.to.sessionId;
412
+ if (sessionId === undefined)
413
+ throw new DirectoryError(503, "partial_init");
414
+ return {
415
+ agentId: mail.to.agentId,
416
+ sessionId,
417
+ commandId: parseAccepted(z.string().parse(row.receipt)).commandId,
418
+ };
419
+ }
378
420
  /**
379
421
  * BirdDog submission and reply authentication. The durable row freezes
380
422
  * correspondence before transport; an ambiguous delivery reuses that exact mail.
@@ -402,13 +444,18 @@ export function createFleetCorrespondence(options) {
402
444
  : undefined;
403
445
  const ancestors = inherited?.ancestors ?? [];
404
446
  const maxDepth = Math.min(grant.maxDepth ?? 8, inherited?.maxDepth ?? 8);
405
- // Ancestry bounds work depth; it does not remove authorized peer targets.
447
+ // A report to an ancestor is one-way: the parent handles the work but
448
+ // must not send its settlement back as a new prompt to the worker.
406
449
  if (parent.agentId === task.agentId)
407
450
  throw new AgentGrantError("delegation_loop");
408
451
  if (ancestors.length >= maxDepth)
409
452
  throw new AgentGrantError("delegation_depth");
410
453
  const path = { ancestors: [...ancestors, parent.agentId], maxDepth };
411
- const delegation = task.delivery === "steer" ? { ...path, steer: true } : path;
454
+ const delegation = {
455
+ ...path,
456
+ ...(task.delivery === "steer" && { steer: true }),
457
+ ...(task.goal !== undefined && { goal: task.goal }),
458
+ };
412
459
  const outgoingMeta = {
413
460
  ...(objectMeta.success
414
461
  ? objectMeta.data
@@ -427,10 +474,20 @@ export function createFleetCorrespondence(options) {
427
474
  let row = read(task.requestId);
428
475
  if (row === undefined) {
429
476
  const from = { kind: "agent", ...parent };
477
+ const ancestor = ancestors.includes(task.agentId);
478
+ // A sessionless report to a parent goes to the creator's recorded
479
+ // Session. Never create a fresh Session merely to carry a notice.
480
+ const targetSessionId = task.sessionId ??
481
+ (ancestor
482
+ ? readSessionDelegator(options.storage, parent.sessionId)?.replyTo.find(({ agentId }) => agentId === task.agentId)
483
+ ?.sessionId
484
+ : undefined);
485
+ if (ancestor && targetSessionId === undefined)
486
+ throw new AgentGrantError("delegator_missing");
430
487
  let mail = {
431
488
  key: task.requestId,
432
489
  from,
433
- to: task.sessionId === undefined
490
+ to: targetSessionId === undefined
434
491
  ? {
435
492
  kind: "agent",
436
493
  agentId: task.agentId,
@@ -438,9 +495,9 @@ export function createFleetCorrespondence(options) {
438
495
  : {
439
496
  kind: "agent",
440
497
  agentId: task.agentId,
441
- sessionId: task.sessionId,
498
+ sessionId: targetSessionId,
442
499
  },
443
- replyTo: [from],
500
+ replyTo: ancestor ? [] : [from],
444
501
  content: task.text,
445
502
  createdAt: Date.now(),
446
503
  };
@@ -677,6 +734,8 @@ export function createFleetDelegation(options) {
677
734
  if (sessionId === undefined)
678
735
  throw new DirectoryError(503, "partial_init");
679
736
  const settlement = await options.readSettlement(mail.to.agentId, sessionId, accepted.commandId);
737
+ if (settlement !== undefined)
738
+ store.recordSettled(task.requestId);
680
739
  return settlement === undefined
681
740
  ? pending(1000)
682
741
  : {
@@ -691,7 +750,8 @@ export function createFleetDelegation(options) {
691
750
  * target, so the owner is still waiting for a reply; undefined when none.
692
751
  */
693
752
  async function outstanding() {
694
- for (const raw of store.sent()) {
753
+ let waiting;
754
+ for (const raw of store.unsettled()) {
695
755
  const row = delegationRowSchema.parse(raw);
696
756
  if (row.receipt === null)
697
757
  continue;
@@ -706,21 +766,23 @@ export function createFleetDelegation(options) {
706
766
  sessionId: to.sessionId,
707
767
  commandId,
708
768
  };
709
- let settlement;
710
- try {
711
- settlement = await options.readSettlement(to.agentId, to.sessionId, commandId);
712
- }
713
- catch (cause) {
769
+ const result = await options
770
+ .readSettlement(to.agentId, to.sessionId, commandId)
771
+ .then((settlement) => ({ kind: "read", settlement }), (cause) => ({ kind: "unreadable", cause }));
772
+ if (result.kind === "unreadable") {
714
773
  // Work whose state cannot be read is still owed; say why.
715
- return {
774
+ waiting ??= {
716
775
  ...target,
717
- unreadable: cause instanceof Error ? cause.message : "unreadable",
776
+ unreadable: result.cause instanceof Error ? result.cause.message : "unreadable",
718
777
  };
778
+ continue;
719
779
  }
720
- if (settlement === undefined)
721
- return target;
780
+ if (result.settlement === undefined)
781
+ waiting ??= target;
782
+ else
783
+ store.recordSettled(decodeMail(row.mail).key);
722
784
  }
723
- return undefined;
785
+ return waiting;
724
786
  }
725
787
  return {
726
788
  /** The delegated work this owner still waits on, if any. */
@@ -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;
@@ -557,7 +557,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
557
557
  };
558
558
  };
559
559
  output: {
560
- 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";
561
561
  readonly _tag: "ShiitakeError";
562
562
  readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
563
563
  readonly message: string;
@@ -818,13 +818,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
818
818
  readonly state: "queued";
819
819
  readonly commandId: string;
820
820
  readonly runningWork: {
821
- readonly kind: "session";
822
- readonly sessionId: string;
823
- readonly commandId: string;
824
- } | {
825
- readonly kind: "host";
826
- readonly lane: string;
827
- readonly jobId: string;
821
+ readonly phase: "thinking" | "streaming" | "running_tool" | "waiting_on_you" | "cancelling";
828
822
  } | null;
829
823
  } | {
830
824
  readonly state: "running";
@@ -849,6 +843,8 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
849
843
  readonly revision: number;
850
844
  readonly text: string;
851
845
  readonly thinking: string;
846
+ readonly textOffset: number;
847
+ readonly thinkingOffset: number;
852
848
  readonly textTruncated: boolean;
853
849
  readonly thinkingTruncated: boolean;
854
850
  } | null;
@@ -926,7 +922,7 @@ export declare function createShiitakeHttpRoutes(resolveSession: ShiitakeSession
926
922
  };
927
923
  };
928
924
  output: {
929
- readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure";
925
+ readonly cause: "closed" | "conflict" | "incompatible" | "invalid" | "rejected" | "unavailable" | "state_reset_required" | "storage_pressure" | "archived";
930
926
  readonly _tag: "ShiitakeError";
931
927
  readonly boundary: "contract" | "extensions" | "instructions" | "model" | "runtime" | "store" | "tools";
932
928
  readonly message: string;
package/dist/http.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { createShiitakeHttpRoutes, type ShiitakeHttpEnv, type ShiitakeSessionResolver, } from "./http-routes.js";
2
+ export { toShiitakeErrorWire } from "./errors.js";
package/dist/http.js CHANGED
@@ -1 +1,2 @@
1
1
  export { createShiitakeHttpRoutes, } from "./http-routes.js";
2
+ export { toShiitakeErrorWire } from "./errors.js";
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";
@@ -32,7 +32,7 @@ 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. */
35
+ /** Synchronous owner-local side effect on first admission, in its transaction. */
36
36
  onAccepted?: (sessionId: string, commandId: string) => void;
37
37
  /**
38
38
  * New work that continues earlier work in this Session (for example, the
@@ -5,7 +5,7 @@ import { checkHostMeta } from "./host-meta.js";
5
5
  import { ShiitakeError } from "../errors.js";
6
6
  import { decodeClientAcceptedCommand, decodeClientSessionId, decodeClientSessionCreationRequestId, requireDecoded, } from "./client-boundary.js";
7
7
  import { publishOwnerObservation } from "./observation-kernel.js";
8
- import { AdmissionOriginSchema, acceptSessionCommandOwned, readSessionCommandAdmissionByRequest, withdrawSessionRequestOwned, assertSessionRequestNotWithdrawn, hasStoredSession, readWorkSender, recordWorkSender, recordSessionDelegator, VerifiedAgentReplySchema, } from "./woodstock/index.js";
8
+ import { AdmissionOriginSchema, acceptSessionCommandOwned, readSessionCommandAdmissionByRequest, withdrawSessionRequestOwned, assertSessionRequestNotWithdrawn, hasStoredSession, hasRecordedRunTerminal, readWorkSender, recordWorkSender, VerifiedAgentReplySchema, } from "./woodstock/index.js";
9
9
  const decodeOrigin = Schema.decodeUnknownResult(AdmissionOriginSchema, {
10
10
  onExcessProperty: "error",
11
11
  });
@@ -87,7 +87,8 @@ export function openAdmissionDoor(input) {
87
87
  const targetWorkId = command.type === "prompt" &&
88
88
  command.delivery === "steer" &&
89
89
  head.state === "running" &&
90
- !head.cancellationRequested
90
+ !head.cancellationRequested &&
91
+ !hasRecordedRunTerminal(input.owner, sessionId, head.jobId)
91
92
  ? head.jobId
92
93
  : undefined;
93
94
  const accepted = acceptSessionCommandOwned(input.owner, sessionId, command, request.meta, origin, input.contract(), targetWorkId === undefined
@@ -100,8 +101,11 @@ export function openAdmissionDoor(input) {
100
101
  if (sender !== undefined)
101
102
  recordWorkSender(input.owner, sessionId, accepted.accepted.commandId, sender);
102
103
  }
103
- recordSessionDelegator(input.owner, sessionId, request.mail);
104
- request.onAccepted?.(sessionId, accepted.accepted.commandId);
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);
105
109
  touched.set(sessionId, (touched.get(sessionId) ?? false) || accepted.delivery === "new");
106
110
  return Object.freeze({
107
111
  status: "accepted",
@@ -188,6 +188,11 @@ export type ShiitakeOptions = Readonly<{
188
188
  store: SqliteOwner;
189
189
  /** Host storage-pressure check; absent never refuses for storage pressure. */
190
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
+ }>;
191
196
  /** Compaction budgets; absent uses Pi's defaults for every model. */
192
197
  compaction?: CompactionOptions;
193
198
  /** Runtime host that schedules the opaque durable reconciler. */