@fungi.computer/shiitake 0.7.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.
@@ -28,19 +28,15 @@ const SessionActivitySchema = Schema.Struct({
28
28
  Schema.Struct({
29
29
  state: Schema.Literal("queued"),
30
30
  commandId: Schema.NonEmptyString,
31
- runningWork: Schema.NullOr(Schema.Union([
32
- Schema.Struct({
33
- kind: Schema.Literal("session"),
34
- sessionId: Schema.NonEmptyString,
35
- commandId: Schema.NonEmptyString,
36
- title: Schema.String,
37
- }),
38
- Schema.Struct({
39
- kind: Schema.Literal("host"),
40
- lane: Schema.NonEmptyString,
41
- jobId: Schema.NonEmptyString,
42
- }),
43
- ])),
31
+ runningWork: Schema.NullOr(Schema.Struct({
32
+ phase: Schema.Literals([
33
+ "thinking",
34
+ "streaming",
35
+ "running_tool",
36
+ "waiting_on_you",
37
+ "cancelling",
38
+ ]),
39
+ })),
44
40
  }),
45
41
  Schema.Struct({
46
42
  state: Schema.Literal("running"),
package/dist/client.d.ts CHANGED
@@ -132,17 +132,9 @@ export type SessionTerminal = Readonly<{
132
132
  */
133
133
  meta?: Json;
134
134
  }>;
135
- /** Blocking executor identity and Session display metadata, never messages or provider data. */
135
+ /** The occupying work's current phase; never its identity or transcript. */
136
136
  export type AgentRunningWork = Readonly<{
137
- kind: "session";
138
- sessionId: string;
139
- commandId: string;
140
- /** Owner-issued display title; empty when no title is known. */
141
- title: string;
142
- }> | Readonly<{
143
- kind: "host";
144
- lane: string;
145
- jobId: string;
137
+ phase: "thinking" | "streaming" | "running_tool" | "waiting_on_you" | "cancelling";
146
138
  }>;
147
139
  /**
148
140
  * Raw facts about what a Session is doing now. Shiitake publishes facts only;
package/dist/fleet.d.ts CHANGED
@@ -272,7 +272,11 @@ export declare function delegationSendMeta(sender: Readonly<{
272
272
  * while the verified reply retains the failed recipient's identity.
273
273
  */
274
274
  export declare function delegationReplyMeta(delegation: AuthenticatedDelegationReply, replierName: string): DelegationReplyMeta;
275
- /** An original one-way report can only name an ancestor of its Agent sender. */
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
+ */
276
280
  export declare function isOneWayAncestorNotice(mail: BirdDogMail): boolean;
277
281
  /**
278
282
  * Delegated work an owner still waits on; `unreadable` says why its state
package/dist/fleet.js CHANGED
@@ -361,7 +361,11 @@ export function delegationReplyMeta(delegation, replierName) {
361
361
  const delegationMetadata = z.object({
362
362
  delegation: FungiMessageMetaSchema.shape.delegation,
363
363
  });
364
- /** An original one-way report can only name an ancestor of its Agent sender. */
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
+ */
365
369
  export function isOneWayAncestorNotice(mail) {
366
370
  if (mail.reply !== undefined ||
367
371
  mail.replyTo.length !== 0 ||
@@ -818,14 +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
- readonly title: string;
825
- } | {
826
- readonly kind: "host";
827
- readonly lane: string;
828
- readonly jobId: string;
821
+ readonly phase: "thinking" | "streaming" | "running_tool" | "waiting_on_you" | "cancelling";
829
822
  } | null;
830
823
  } | {
831
824
  readonly state: "running";
@@ -57,6 +57,14 @@ function deliverObservation(listener, reason) {
57
57
  listener.reset = true;
58
58
  }
59
59
  }
60
+ /** Queued views reconcile occupancy without replaying another Session's facts. */
61
+ function notifyQueuedViews(listeners, sourceSessionId) {
62
+ for (const listener of listeners)
63
+ if (listener.queued &&
64
+ !listener.settlementOnly &&
65
+ listener.sessionId !== sourceSessionId)
66
+ deliverObservation(listener, "activity");
67
+ }
60
68
  /** Notify observers of one Session's durable or process-local change. */
61
69
  export function publishOwnerObservation(store, sessionId, reason = "durable") {
62
70
  const publishers = ownerPublishers.get(store);
@@ -131,11 +139,12 @@ export function openObservationKernel(store) {
131
139
  };
132
140
  const publish = (publication) => {
133
141
  if (publication.kind === "agent-execution") {
134
- for (const listener of active)
135
- if (listener.queued && !listener.settlementOnly)
136
- deliverObservation(listener, "activity");
142
+ notifyQueuedViews(active, null);
137
143
  return;
138
144
  }
145
+ // Reuse bounded activity hints for the occupant's changing phase.
146
+ if (publication.sessionId !== null && publication.reason !== "settlement")
147
+ notifyQueuedViews(active, publication.sessionId);
139
148
  const listeners = publication.sessionId === null
140
149
  ? active
141
150
  : sessions.get(publication.sessionId);
@@ -7,6 +7,7 @@ import type { ShiitakeEffectOptions } from "./effect-contracts.js";
7
7
  import type { CompactionHandoffV0, PromptSectionV0 } from "../extensions.js";
8
8
  import { type ManualCompactionOutcome } from "./compaction/session-compaction.js";
9
9
  import { type PreparedSessionRun, type RunContract } from "./woodstock/index.js";
10
+ export declare const runFinalizerGraceMs = 1000;
10
11
  declare const toolSetupMessages: {
11
12
  readonly missing: "Instruction capabilities have no tool runtime";
12
13
  readonly runtime: "Tool runtime failed";
@@ -12,9 +12,11 @@ import { openCompactionSettings } from "./compaction/settings.js";
12
12
  import { acquireOwnedModel } from "./model-acquisition.js";
13
13
  import { returnedFailureReason, unavailableRunReason, } from "./run-failure-reason.js";
14
14
  import { isCompactCommand, openMuleRecorder, readSessionGrants, recordLiveRetryEvent, recordSandboxFailure, takeSessionControls, } from "./woodstock/index.js";
15
- // A run writes its terminal before scope release. Give each foreign release
16
- // one second to clean up without blocking the next Watchdog job indefinitely.
17
- const runFinalizerGraceMs = 1_000;
15
+ // A run writes its terminal before scope release. Allow one second per foreign
16
+ // release before detaching it: the held-model regression exercises the full
17
+ // grace and proves Watchdog can settle and claim the next priority-1 steer.
18
+ // Keep the test's eventual queue-idle budget tied to this product bound.
19
+ export const runFinalizerGraceMs = 1_000;
18
20
  function boundedRunFinalizer(name, workId, release) {
19
21
  return Effect.gen(function* () {
20
22
  // A detached release may finish later; interrupting a finalizer itself
@@ -3,6 +3,7 @@ import { WatchdogError } from "@fungi.computer/watchdog";
3
3
  import { makeRuntime as makeWatchdogRuntime, } from "@fungi.computer/watchdog/effect";
4
4
  import * as Effect from "effect/Effect";
5
5
  import * as Latch from "effect/Latch";
6
+ import { readSessionPhase } from "./session-status.js";
6
7
  import { readOutstandingDelegationSync } from "../fleet.js";
7
8
  import { publishAgentExecutionChange } from "./observation-kernel.js";
8
9
  import { ShiitakeError } from "../errors.js";
@@ -12,7 +13,7 @@ import { openCompactionSettings } from "./compaction/settings.js";
12
13
  import { admitExtensionRosterV0, } from "./extension-roster.js";
13
14
  import { coreConsumer, reportConsumer, openExtensionRuntime, } from "./extension-runtime.js";
14
15
  import { reconcileRuntime } from "./runtime-reconciliation.js";
15
- import { advanceCursor, createSessionWithinTransaction, deliverSessionGrants, prepareExtensionStore, recordSessionDelegator, projectSessionRun, openStorageAdmission, readCursor, readSessionInboxPage, readSessionSummary, } from "./woodstock/index.js";
16
+ import { advanceCursor, createSessionWithinTransaction, deliverSessionGrants, prepareExtensionStore, recordSessionDelegator, projectSessionRun, openStorageAdmission, readCursor, readSessionInboxPage, } from "./woodstock/index.js";
16
17
  function runtimeFailure(message) {
17
18
  return new ShiitakeError("runtime", "rejected", message);
18
19
  }
@@ -285,12 +286,16 @@ export function openRuntimeCoordinator(options, coreDigest, admitted, mailRoom)
285
286
  ? "host"
286
287
  : "serial")
287
288
  : undefined;
288
- if (work === undefined || work.kind === "host")
289
- return Effect.succeed(work ?? null);
290
- return readSessionSummary(options.store, work.sessionId).pipe(Effect.map((summary) => ({
291
- ...work,
292
- title: summary?.title ?? "",
293
- })));
289
+ if (work === undefined)
290
+ return Effect.succeed(null);
291
+ if (work.kind === "host")
292
+ return Effect.succeed({ phase: "thinking" });
293
+ return Effect.try({
294
+ try: () => ({
295
+ phase: readSessionPhase(options.store, work.sessionId, watchdog.transactional.readQueueHead(work.sessionId)),
296
+ }),
297
+ catch: (cause) => new ShiitakeError("runtime", "rejected", "Running phase is unavailable", undefined, cause),
298
+ });
294
299
  }), Effect.mapError((cause) => new ShiitakeError("runtime", "rejected", "Running work is unavailable", undefined, cause))),
295
300
  extensions,
296
301
  commit: bound.commit,
@@ -7,5 +7,7 @@ export type ObservedActivity = Readonly<{
7
7
  readProgress: () => SessionProgress | null;
8
8
  toolOutput: readonly SessionToolOutput[];
9
9
  }>;
10
+ /** Project only the occupying Session's activity, without disclosing its data. */
11
+ export declare function readSessionPhase(owner: SqliteOwner, sessionId: string, head: WatchdogQueueHead): AgentRunningWork["phase"];
10
12
  /** Join Watchdog authority with Woodstock facts and process-local progress. */
11
13
  export declare function readSessionActivity(owner: SqliteOwner, sessionId: string, head: WatchdogQueueHead, runningWork: AgentRunningWork | null): ObservedActivity;
@@ -1,4 +1,20 @@
1
- import { readProcessProgressForWork, readProcessToolOutputForWork, readProcessCompaction, readLatestLiveRetryForWork, readActiveToolForWork, readLastModelChoiceSequence, } from "./woodstock/index.js";
1
+ import { readProcessProgressForWork, readProcessToolOutputForWork, readProcessCompaction, readLatestLiveRetryForWork, readActiveToolForWork, readLastModelChoiceSequence, readSessionAttention, hasProcessTextForWork, } from "./woodstock/index.js";
2
+ /** Project only the occupying Session's activity, without disclosing its data. */
3
+ export function readSessionPhase(owner, sessionId, head) {
4
+ const { facts } = readSessionActivity(owner, sessionId, head, null);
5
+ if (facts.cancellationRequested)
6
+ return "cancelling";
7
+ if (readSessionAttention(owner, sessionId).some((item) => item.inbox !== false && item.waitingOn === undefined))
8
+ return "waiting_on_you";
9
+ if (facts.compaction !== null || facts.retry !== null)
10
+ return "thinking";
11
+ if (facts.activeTool !== null)
12
+ return "running_tool";
13
+ if (head.state === "running" &&
14
+ hasProcessTextForWork(owner, sessionId, head.jobId))
15
+ return "streaming";
16
+ return "thinking";
17
+ }
2
18
  /** Join Watchdog authority with Woodstock facts and process-local progress. */
3
19
  export function readSessionActivity(owner, sessionId, head, runningWork) {
4
20
  const lastModelChoiceSequence = readLastModelChoiceSequence(owner, sessionId);
@@ -533,10 +533,11 @@ export function recordLiveRetryEvent(owner, accepted, event, turn) {
533
533
  sameIdentity(current.event.run, event.run));
534
534
  }).pipe(Effect.mapError(listenerFailure));
535
535
  }
536
- const listenerFailure = () => ({
536
+ const listenerFailure = (cause) => ({
537
537
  _tag: "MuleError",
538
538
  phase: "listener",
539
539
  message: "Woodstock could not record the Mule event",
540
+ cause,
540
541
  });
541
542
  /**
542
543
  * Open the one scoped listener that projects Mule's exact event stream into
@@ -32,6 +32,8 @@ export declare function openToolProgress(owner: SqliteOwner, accepted: AcceptedC
32
32
  export declare function readProcessProgress(owner: SqliteOwner, sessionId: string): SessionProgress | null;
33
33
  /** Read progress only when it belongs to the current durable work head. */
34
34
  export declare function readProcessProgressForWork(owner: SqliteOwner, sessionId: string, workId: string): SessionProgress | null;
35
+ /** Read a phase fact without flattening another Session's private text tails. */
36
+ export declare function hasProcessTextForWork(owner: SqliteOwner, sessionId: string, workId: string): boolean;
35
37
  export type ProgressMessageStart = Extract<MuleEvent, {
36
38
  type: "message_start";
37
39
  }>;
@@ -189,6 +189,11 @@ export function readProcessProgressForWork(owner, sessionId, workId) {
189
189
  const current = progressFor(owner).get(sessionId);
190
190
  return snapshot(current?.workId === workId ? current.value : null);
191
191
  }
192
+ /** Read a phase fact without flattening another Session's private text tails. */
193
+ export function hasProcessTextForWork(owner, sessionId, workId) {
194
+ const current = progressByOwner.get(owner)?.get(sessionId);
195
+ return current?.workId === workId && (current.value?.text.length ?? 0) > 0;
196
+ }
192
197
  /**
193
198
  * Keep live model progress in the current process only.
194
199
  *
package/dist/testing.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { type FauxResponseStep } from "@fungi.computer/mule/testing";
2
2
  import type { ExtensionRosterV0, Json, RosteredExtensionV0 } from "./extensions.js";
3
3
  import { type ModelSource, type SessionSnapshot, type ShiitakeOwnerAgent, type ShiitakeOptions, type SqliteOwner } from "./index.js";
4
+ /** Product finalizer grace, exposed for tests that wait for durable queue settlement. */
5
+ export { runFinalizerGraceMs } from "./internal/run-scope.js";
4
6
  /** A deterministic model source for trusted tests; no provider account or I/O. */
5
7
  export declare function fauxModelSource(replies: readonly FauxResponseStep[]): ModelSource;
6
8
  /** One scripted model tool call; the scripted sandbox runs exactly this call. */
package/dist/testing.js CHANGED
@@ -7,6 +7,8 @@ import * as z from "zod";
7
7
  import { fauxAssistantMessage, fauxToolCall, } from "@earendil-works/pi-ai/providers/faux";
8
8
  import { GuestThrew } from "@fungi.computer/mycelium";
9
9
  import { Shiitake, } from "./index.js";
10
+ /** Product finalizer grace, exposed for tests that wait for durable queue settlement. */
11
+ export { runFinalizerGraceMs } from "./internal/run-scope.js";
10
12
  const settlementBudgetSchema = z.int().min(1).max(1_000).default(1_000);
11
13
  /** A deterministic model source for trusted tests; no provider account or I/O. */
12
14
  export function fauxModelSource(replies) {
@@ -409,22 +409,13 @@ Durable outcome for one accepted work item on the selected Session lineage.
409
409
  ### AgentRunningWork
410
410
 
411
411
  ```ts
412
- type AgentRunningWork =
413
- | Readonly<{
414
- kind: "session";
415
- sessionId: string;
416
- commandId: string;
417
- title: string;
418
- }>
419
- | Readonly<{
420
- kind: "host";
421
- lane: string;
422
- jobId: string;
423
- }>;
412
+ type AgentRunningWork = Readonly<{
413
+ phase:
414
+ "thinking" | "streaming" | "running_tool" | "waiting_on_you" | "cancelling";
415
+ }>;
424
416
  ```
425
417
 
426
- Blocking executor identity and Session display metadata, never messages or
427
- provider data.
418
+ The occupying work's current phase; never its identity or transcript.
428
419
 
429
420
  ---
430
421
 
package/docs/api/fleet.md CHANGED
@@ -28,9 +28,9 @@ new AgentGrantError(reason): AgentGrantError;
28
28
 
29
29
  ###### Parameters
30
30
 
31
- | Parameter | Type |
32
- | --------- | ------------------------------------------------------------------------------------- |
33
- | `reason` | `"agent"` \| `"expired"` \| `"action"` \| `"delegation_loop"` \| `"delegation_depth"` |
31
+ | Parameter | Type |
32
+ | --------- | ----------------------------------------------------------------------------------------------------------------- |
33
+ | `reason` | \| `"agent"` \| `"expired"` \| `"action"` \| `"delegation_loop"` \| `"delegation_depth"` \| `"delegator_missing"` |
34
34
 
35
35
  ###### Returns
36
36
 
@@ -47,7 +47,13 @@ Error.constructor;
47
47
  ##### reason
48
48
 
49
49
  ```ts
50
- readonly reason: "agent" | "expired" | "action" | "delegation_loop" | "delegation_depth";
50
+ readonly reason:
51
+ | "agent"
52
+ | "expired"
53
+ | "action"
54
+ | "delegation_loop"
55
+ | "delegation_depth"
56
+ | "delegator_missing";
51
57
  ```
52
58
 
53
59
  ---
@@ -554,6 +560,7 @@ const agentDelegationTaskSchema: ZodLazy<
554
560
  steer: "steer";
555
561
  }>
556
562
  >;
563
+ goal: ZodOptional<ZodUnion<readonly [ZodString, ZodLiteral<false>]>>;
557
564
  text: ZodString;
558
565
  },
559
566
  $strict
@@ -759,6 +766,28 @@ the verified reply retains the failed recipient's identity.
759
766
 
760
767
  ---
761
768
 
769
+ ### isOneWayAncestorNotice()
770
+
771
+ ```ts
772
+ function isOneWayAncestorNotice(mail): boolean;
773
+ ```
774
+
775
+ Shape check for a one-way ancestor notice, not sender provenance. The Local mail
776
+ receiver checks mesh membership before calling it. This addressed mail is still
777
+ work and may steer the parent's running turn when admitted.
778
+
779
+ #### Parameters
780
+
781
+ | Parameter | Type |
782
+ | --------- | ------------- |
783
+ | `mail` | `BirdDogMail` |
784
+
785
+ #### Returns
786
+
787
+ `boolean`
788
+
789
+ ---
790
+
762
791
  ### readOutstandingDelegationSync()
763
792
 
764
793
  ```ts
@@ -91,6 +91,17 @@ type MemoryHost = Readonly<{
91
91
 
92
92
  Explicitly driven in-process host for trusted tests and runnable examples.
93
93
 
94
+ ## Variables
95
+
96
+ ### runFinalizerGraceMs
97
+
98
+ ```ts
99
+ const runFinalizerGraceMs: 1000 = 1_000;
100
+ ```
101
+
102
+ Product finalizer grace, exposed for tests that wait for durable queue
103
+ settlement.
104
+
94
105
  ## Functions
95
106
 
96
107
  ### fauxModelSource()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/shiitake",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "description": "Effect-native durable Session acceptance and bounded run orchestration",
6
6
  "type": "module",
@@ -89,16 +89,16 @@
89
89
  "@fungi.computer/bird-dog": "0.3.1",
90
90
  "@fungi.computer/cairn": "0.1.0",
91
91
  "@fungi.computer/carrier": "0.2.0",
92
- "@fungi.computer/knapsack": "0.4.1",
92
+ "@fungi.computer/knapsack": "0.4.2",
93
93
  "@fungi.computer/mule": "0.2.1",
94
- "@fungi.computer/mycelium": "0.3.0",
94
+ "@fungi.computer/mycelium": "0.3.1",
95
95
  "@fungi.computer/seam": "0.2.0",
96
96
  "@fungi.computer/watchdog": "0.1.0",
97
97
  "effect": "4.0.2",
98
98
  "zod": "4.4.3",
99
99
  "@earendil-works/pi-ai": "1.3.0",
100
100
  "@fungi.computer/gills": "0.2.0",
101
- "@fungi.computer/agar": "0.6.0"
101
+ "@fungi.computer/agar": "0.6.1"
102
102
  },
103
103
  "devDependencies": {
104
104
  "@cloudflare/workers-types": "5.20260828.1",