@fungi.computer/shiitake 0.8.0 → 0.9.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.
@@ -10,6 +10,7 @@ import * as Schema from "effect/Schema";
10
10
  import * as Scope from "effect/Scope";
11
11
  import * as Sink from "effect/Sink";
12
12
  import * as Stream from "effect/Stream";
13
+ import { sessionSilenceDeadlineMs } from "./session-liveness.js";
13
14
  import { parseAccepted, parseCommandSettlement, parseWithdrawResult, parseCancelResult, parseLatestPendingPrompt, parseForkedSession, parseCreatedSession, parseDeletedSession, parseExtensionOutput, parseExtensionStatus, parseExtensionStatuses, parseSessionFrame, parseSessionSnapshot, parseSessionSummaryPage, parseShiitakeError, } from "./client-wire.js";
14
15
  import { ShiitakeError } from "./errors.js";
15
16
  import { decodeClientReadOptions, decodeClientSessionCreationRequestId, decodeClientPageCursor, decodeClientSessionId, decodeClientSubscribeOptions, } from "./internal/client-boundary.js";
@@ -96,7 +97,10 @@ function remoteEvents(url, clientClosed, sources, eventSource) {
96
97
  return active;
97
98
  },
98
99
  catch: () => unavailable("Session stream could not open"),
99
- }), (active) => closeRemoteSource(active, sources)), { bufferSize: 256, strategy: "dropping" }).pipe(Stream.mapEffect(decodeRemoteEvent), Stream.interruptWhen(clientClosed.await));
100
+ }), (active) => closeRemoteSource(active, sources)), { bufferSize: 256, strategy: "dropping" }).pipe(Stream.mapEffect(decodeRemoteEvent), Stream.timeoutOrElse({
101
+ duration: sessionSilenceDeadlineMs,
102
+ orElse: () => Stream.fail(unavailable("Session feed is connected but silent; the service may be busy")),
103
+ }), Stream.interruptWhen(clientClosed.await));
100
104
  }
101
105
  function sessionUrl(baseUrl, sessionId) {
102
106
  return `${baseUrl}/sessions/${encodeURIComponent(sessionId)}`;
@@ -1,5 +1,6 @@
1
1
  import { SSEStreamingApi } from "hono/streaming";
2
2
  import { visitorSnapshot } from "./client-visitor.js";
3
+ import { encodeSessionFrame } from "./internal/observation-kernel.js";
3
4
  export function visitorStream(options) {
4
5
  const { readable, writable } = new TransformStream();
5
6
  const stream = new SSEStreamingApi(writable, readable);
@@ -43,7 +44,7 @@ export function visitorStream(options) {
43
44
  await close();
44
45
  return;
45
46
  }
46
- await stream.write(": heartbeat\n\n");
47
+ // Keep the visitor authorization/expiry check, not a second liveness clock.
47
48
  if (!ending)
48
49
  heartbeat = setTimeout(() => {
49
50
  void beat().catch((cause) => {
@@ -77,6 +78,15 @@ export function visitorStream(options) {
77
78
  for await (const frame of options.watch.changes) {
78
79
  if (ending)
79
80
  break;
81
+ if (frame.kind === "heartbeat") {
82
+ if (!(await admitted()))
83
+ break;
84
+ await stream.writeSSE({
85
+ event: "frame",
86
+ data: encodeSessionFrame(frame).data,
87
+ });
88
+ continue;
89
+ }
80
90
  if (frame.kind === "tool_output")
81
91
  continue;
82
92
  if (frame.kind === "progress" || frame.kind === "activity") {
@@ -0,0 +1,4 @@
1
+ /** Process-local liveness cadence, not a durable Session fact. */
2
+ export declare const sessionHeartbeatIntervalMs = 15000;
3
+ /** Silence loses observation; only the service owner's lease check proves death. */
4
+ export declare const sessionSilenceDeadlineMs = 45000;
@@ -0,0 +1,4 @@
1
+ /** Process-local liveness cadence, not a durable Session fact. */
2
+ export const sessionHeartbeatIntervalMs = 15_000;
3
+ /** Silence loses observation; only the service owner's lease check proves death. */
4
+ export const sessionSilenceDeadlineMs = 45_000;
@@ -471,7 +471,7 @@ function waitForAgentSettlement(
471
471
  delegator: string | null;
472
472
  terminal: SessionTerminal;
473
473
  content: string;
474
- usage: readonly Pick<AssistantMessage, "model" | "provider" | "usage">[];
474
+ usage: readonly Pick<AssistantMessage, "model" | "usage" | "provider">[];
475
475
  toolCalls: number;
476
476
  waitingOnYou: readonly Readonly<{
477
477
  itemId: string;
@@ -499,7 +499,7 @@ Await one exact durable terminal without opening a transcript observation.
499
499
  `Promise`\<`Readonly`\<\{ `acceptedSender`: `string` \| `null`; `delegator`:
500
500
  `string` \| `null`; `terminal`: [`SessionTerminal`](client.md#sessionterminal);
501
501
  `content`: `string`; `usage`: readonly `Pick`\<`AssistantMessage`, `"model"` \|
502
- `"provider"` \| `"usage"`\>[]; `toolCalls`: `number`; `waitingOnYou`: readonly
502
+ `"usage"` \| `"provider"`\>[]; `toolCalls`: `number`; `waitingOnYou`: readonly
503
503
  `Readonly`\<\{ `itemId`: `string`; `kind`: `string`; `summary`: `string`;
504
504
  \}\>[]; \}\>\>
505
505
 
@@ -678,6 +678,9 @@ Bounded Session baseline at one opaque owner cursor.
678
678
 
679
679
  ```ts
680
680
  type SessionFrame =
681
+ | Readonly<{
682
+ kind: "heartbeat";
683
+ }>
681
684
  | Readonly<{
682
685
  kind: "activity";
683
686
  activity: SessionActivity;
@@ -1049,6 +1052,26 @@ const DelegationReceiptSchema: Schema.Codec<DelegationReceipt>;
1049
1052
 
1050
1053
  A delegated send is accepted by its owner or held in durable mail custody.
1051
1054
 
1055
+ ---
1056
+
1057
+ ### sessionHeartbeatIntervalMs
1058
+
1059
+ ```ts
1060
+ const sessionHeartbeatIntervalMs: 15000 = 15_000;
1061
+ ```
1062
+
1063
+ Process-local liveness cadence, not a durable Session fact.
1064
+
1065
+ ---
1066
+
1067
+ ### sessionSilenceDeadlineMs
1068
+
1069
+ ```ts
1070
+ const sessionSilenceDeadlineMs: 45000 = 45_000;
1071
+ ```
1072
+
1073
+ Silence loses observation; only the service owner's lease check proves death.
1074
+
1052
1075
  ## Functions
1053
1076
 
1054
1077
  ### participantSnapshot()
@@ -68,7 +68,7 @@ settlement(
68
68
  delegator: string | null;
69
69
  terminal: SessionTerminal;
70
70
  content: string;
71
- usage: readonly Pick<AssistantMessage, "model" | "provider" | "usage">[];
71
+ usage: readonly Pick<AssistantMessage, "model" | "usage" | "provider">[];
72
72
  toolCalls: number;
73
73
  waitingOnYou: readonly Readonly<{
74
74
  itemId: string;
@@ -95,7 +95,7 @@ Recipient-local disclosure through the public host, never another Agent's RPC.
95
95
  `Promise`\< \| `Readonly`\<\{ `acceptedSender`: `string` \| `null`; `delegator`:
96
96
  `string` \| `null`; `terminal`: [`SessionTerminal`](client.md#sessionterminal);
97
97
  `content`: `string`; `usage`: readonly `Pick`\<`AssistantMessage`, `"model"` \|
98
- `"provider"` \| `"usage"`\>[]; `toolCalls`: `number`; `waitingOnYou`: readonly
98
+ `"usage"` \| `"provider"`\>[]; `toolCalls`: `number`; `waitingOnYou`: readonly
99
99
  `Readonly`\<\{ `itemId`: `string`; `kind`: `string`; `summary`: `string`;
100
100
  \}\>[]; \}\> \| `undefined`\>
101
101
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/shiitake",
3
- "version": "0.8.0",
3
+ "version": "0.9.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,9 +89,9 @@
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.2",
93
- "@fungi.computer/mule": "0.2.1",
94
- "@fungi.computer/mycelium": "0.3.1",
92
+ "@fungi.computer/knapsack": "0.4.3",
93
+ "@fungi.computer/mule": "0.2.2",
94
+ "@fungi.computer/mycelium": "0.3.2",
95
95
  "@fungi.computer/seam": "0.2.0",
96
96
  "@fungi.computer/watchdog": "0.1.0",
97
97
  "effect": "4.0.2",