@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.
- package/README.md +59 -21
- package/dist/agent-control.d.ts +1 -1
- package/dist/agent-control.js +40 -21
- package/dist/client-visitor.d.ts +2 -0
- package/dist/client-visitor.js +6 -1
- package/dist/client-wire.d.ts +4 -0
- package/dist/client-wire.js +26 -15
- package/dist/client.d.ts +23 -11
- package/dist/client.js +22 -0
- package/dist/cloudflare.d.ts +20 -0
- package/dist/cloudflare.js +4 -1
- package/dist/delegation.d.ts +5 -0
- package/dist/delegation.js +24 -8
- package/dist/effect.d.ts +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +2 -0
- package/dist/extensions.d.ts +1 -1
- package/dist/fleet.d.ts +14 -2
- package/dist/fleet.js +81 -19
- package/dist/http-routes.d.ts +12 -16
- package/dist/http.d.ts +1 -0
- package/dist/http.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/internal/admission-door.d.ts +1 -1
- package/dist/internal/admission-door.js +8 -4
- package/dist/internal/contracts.d.ts +5 -0
- package/dist/internal/coordinator.js +107 -50
- package/dist/internal/effect-contracts.d.ts +5 -0
- package/dist/internal/extension-runtime.js +5 -3
- package/dist/internal/observation-kernel.js +12 -3
- package/dist/internal/promise-facade.js +2 -0
- package/dist/internal/run-scope.d.ts +1 -0
- package/dist/internal/run-scope.js +29 -7
- package/dist/internal/runtime-coordinator.d.ts +14 -0
- package/dist/internal/runtime-coordinator.js +57 -10
- package/dist/internal/runtime-core.d.ts +3 -0
- package/dist/internal/runtime-core.js +1 -0
- package/dist/internal/runtime-reconciliation.js +4 -1
- package/dist/internal/session-context.d.ts +5 -5
- package/dist/internal/session-context.js +80 -83
- package/dist/internal/session-status.d.ts +4 -2
- package/dist/internal/session-status.js +22 -4
- package/dist/internal/woodstock/client-projection.js +5 -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 +3 -3
- package/dist/internal/woodstock/index.js +18 -7
- package/dist/internal/woodstock/progress.d.ts +13 -1
- package/dist/internal/woodstock/progress.js +130 -24
- package/dist/internal/woodstock/session-delegator.d.ts +2 -1
- package/dist/internal/woodstock/session-store.d.ts +14 -2
- package/dist/internal/woodstock/session-store.js +329 -66
- package/dist/node.js +11 -2
- package/dist/server-visitor-stream.js +33 -24
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +2 -0
- package/docs/api/agent-control.md +3 -2
- package/docs/api/client.md +79 -19
- package/docs/api/cloudflare.md +16 -0
- package/docs/api/delegation.md +84 -50
- package/docs/api/effect.md +85 -7
- package/docs/api/extensions.md +8 -8
- package/docs/api/fleet.md +118 -29
- package/docs/api/http.md +28 -0
- package/docs/api/index.md +39 -15
- package/docs/api/testing.md +20 -7
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -55,10 +55,12 @@ without waiting for the command response or matching text.
|
|
|
55
55
|
npm install @fungi.computer/shiitake
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
The package ships ESM only
|
|
59
|
-
packages [Cairn](../cairn/README.md), [Mule](../mule/README.md),
|
|
58
|
+
The package ships ESM only. Its runtime dependencies include Effect, Zod, Pi and
|
|
59
|
+
the Fungi packages [Cairn](../cairn/README.md), [Mule](../mule/README.md),
|
|
60
60
|
[Mycelium](../mycelium/README.md), [Watchdog](../watchdog/README.md),
|
|
61
|
-
[Bird Dog](../bird-dog/README.md) and Knapsack
|
|
61
|
+
[Bird Dog](../bird-dog/README.md) and Knapsack. See the [manifest](package.json)
|
|
62
|
+
for the complete list. Hono `^4.13.5` is a peer dependency for the HTTP
|
|
63
|
+
adapters.
|
|
62
64
|
|
|
63
65
|
`Shiitake.make(options)` is the Promise constructor. The Effect 4 constructor is
|
|
64
66
|
`make` from `@fungi.computer/shiitake/effect`. Both use the same owner. The host
|
|
@@ -134,6 +136,17 @@ a Session performs no I/O. A client uses `createShiitakeClient` from the
|
|
|
134
136
|
`/client` entry with the host's authorized transport. Releasing that client does
|
|
135
137
|
not cancel host work.
|
|
136
138
|
|
|
139
|
+
Live `progress` frames carry `SessionProgressDelta`: independent text and
|
|
140
|
+
thinking `{ offset, append }` values, with offsets in UTF-16 string units.
|
|
141
|
+
`appendSessionProgress` from `/client` folds appends idempotently; a gap waits
|
|
142
|
+
for the owner's reset. A read/watch/reset baseline carries only the latest 64
|
|
143
|
+
KiB per channel, its start offsets, and accurate truncation flags. A watch
|
|
144
|
+
continues after that tail; the eventual durable message is always complete.
|
|
145
|
+
Progress stays process-local, in bounded chunks, and never writes SQLite. Tiny
|
|
146
|
+
provider deltas are coalesced (first append immediately, then each 4,096 string
|
|
147
|
+
units or within 100 ms); durable completion carries any remaining partial chunk.
|
|
148
|
+
Tool tails travel only in `tool_output`, not in message progress.
|
|
149
|
+
|
|
137
150
|
`session.readSettlement(commandId, { timeoutMs, signal })` returns that exact
|
|
138
151
|
command's `CommandSettlement` (terminal, answer, usage and tool-call count),
|
|
139
152
|
independent of the selected transcript branch. `timeoutMs` defaults to zero and
|
|
@@ -354,24 +367,31 @@ The `/agent-control` source exposes
|
|
|
354
367
|
`agents.setModel({agentId, model, reasoning?})` and `agents.models({})` when its
|
|
355
368
|
host authorizes those operations. `name` labels the new Agent and `reasoning`
|
|
356
369
|
(`minimal` to `max`, one of `modelReasoningLevels`) needs a `model`.
|
|
357
|
-
`agents.create`
|
|
358
|
-
|
|
359
|
-
`
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
`
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
`
|
|
374
|
-
|
|
370
|
+
`agents.create` creates the Agent and its first Session through the same
|
|
371
|
+
idempotent Session-creation door as `agents.createSession`. It returns `agentId`
|
|
372
|
+
and a usable `sessionId`: send work immediately with `agents.send` and
|
|
373
|
+
`delivery: "steer"`, without a separate `createSession` call. Retrying the same
|
|
374
|
+
creation request reuses that first Session.
|
|
375
|
+
|
|
376
|
+
It also returns the Agent's `name`, the requested `model` and `reasoning` (the
|
|
377
|
+
stored default; absent when it inherits), and the `workingDirectory` its tools
|
|
378
|
+
use; `agents.list` reports the same facts for existing Agents. A host that can
|
|
379
|
+
archive also accepts `archived: true` there to list only the archived ones
|
|
380
|
+
(other hosts refuse that input). A host can offer `agents.archive({agentId})`
|
|
381
|
+
and `agents.unarchive`: an Agent archiving an Agent it created hides it from
|
|
382
|
+
lists and delegation without deleting anything, and the host refuses any other
|
|
383
|
+
target, the caller itself, or an Agent with a running command or delegated work
|
|
384
|
+
still waiting. Each refusal, and a send to an archived Agent, reaches the caller
|
|
385
|
+
as a `CapabilityFailed` diagnostic such as `archive:not_creator: …`,
|
|
386
|
+
`archive:self`, `archive:running`, `archive:pending`, `archive:unreadable` (its
|
|
387
|
+
store predates the schema) or `archive:archived`. `agents.wait` accepts
|
|
388
|
+
`timeoutMs` from 1 to 30000. Models use `provider/model` spelling and must be
|
|
389
|
+
offered by that host; `agents.models` returns at most 200 offered addresses from
|
|
390
|
+
signed-in or configured providers. A host model refusal reaches the caller as a
|
|
391
|
+
`CapabilityFailed` diagnostic such as `model:rejected: …` with up to ten
|
|
392
|
+
addresses from the host's offered catalog. If catalog diagnostics are
|
|
393
|
+
unavailable, the original refusal remains; other failures propagate unchanged.
|
|
394
|
+
The `/model-preference` resolver orders explicit Session, explicit Agent, the
|
|
375
395
|
person's last manual switch, Team default, then available login. Hosts own those
|
|
376
396
|
stored layers and credential admission: choosing a model never bypasses an
|
|
377
397
|
owner's Team or Agent key-source restriction. A host can refuse an unrunnable
|
|
@@ -402,6 +422,24 @@ a host-supplied fetch.
|
|
|
402
422
|
interrupted tools against the existing replay rules. They are local proofs, not
|
|
403
423
|
a guarantee about another deployment's storage or sandbox.
|
|
404
424
|
|
|
425
|
+
## Breaking live-progress API
|
|
426
|
+
|
|
427
|
+
The S4 offset/append contract replaces cumulative progress frames:
|
|
428
|
+
|
|
429
|
+
- `progress` frames carry `SessionProgressDelta`, with separate text and
|
|
430
|
+
thinking `{ offset, append }` channels. Offsets count UTF-16 code units.
|
|
431
|
+
- `SessionProgress` is the bounded snapshot shape and now requires `textOffset`
|
|
432
|
+
and `thinkingOffset`, alongside its truncation flags.
|
|
433
|
+
- `appendSessionProgress(current, delta)` is the new shared fold for progress
|
|
434
|
+
frames. Seed it from the **same watch's `snapshot.progress`**, replace it on a
|
|
435
|
+
reset, and retain it across unrelated updates until the message completes or
|
|
436
|
+
the queue becomes idle. A separate earlier `read()` is not a watch baseline.
|
|
437
|
+
- The visitor stream sends live `activity` frames as well as `progress` frames.
|
|
438
|
+
An activity frame updates activity facts; it is not a replacement snapshot.
|
|
439
|
+
|
|
440
|
+
All producers and consumers must use this contract together. There is no
|
|
441
|
+
cumulative-frame compatibility format.
|
|
442
|
+
|
|
405
443
|
## Client entries
|
|
406
444
|
|
|
407
445
|
A host selects client entries separately from its durable extension roster.
|
package/dist/agent-control.d.ts
CHANGED
|
@@ -127,7 +127,7 @@ export type AgentDelegationInput = Readonly<{
|
|
|
127
127
|
sessionId: string;
|
|
128
128
|
delivery: "steer";
|
|
129
129
|
}> | Readonly<{
|
|
130
|
-
/** Omitted
|
|
130
|
+
/** Omitted to open a new Session when the recipient accepts. */
|
|
131
131
|
sessionId?: string;
|
|
132
132
|
delivery: "followUp";
|
|
133
133
|
/**
|
package/dist/agent-control.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CapabilityFailed, Mycelium, } from "@fungi.computer/mycelium";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { ShiitakeError } from "./errors.js";
|
|
4
|
+
import { reportCatchFailure } from "./internal/failure.js";
|
|
4
5
|
import { AGENT_REPLY_TAG } from "./internal/woodstock/index.js";
|
|
5
6
|
import { modelReasoningLevels, parseModelAddress, } from "./model-preference.js";
|
|
6
7
|
const id = z.string().min(1).max(256);
|
|
@@ -149,6 +150,7 @@ const agentOutput = z.strictObject({
|
|
|
149
150
|
workingDirectory: z.string().min(1).max(4096).exactOptional(),
|
|
150
151
|
});
|
|
151
152
|
const agentsOutput = z.array(agentOutput);
|
|
153
|
+
const createdAgentOutput = agentOutput.extend({ sessionId: id });
|
|
152
154
|
const offeredModelLimit = 200;
|
|
153
155
|
const modelsOutput = z.strictObject({
|
|
154
156
|
models: z
|
|
@@ -340,21 +342,32 @@ export class AgentCreateRefused extends Error {
|
|
|
340
342
|
}
|
|
341
343
|
}
|
|
342
344
|
/** Admit the host's model refusal as a typed capability failure; rethrow anything else. */
|
|
343
|
-
function admitModelRefusal(operation, cause) {
|
|
344
|
-
if (cause instanceof ShiitakeError && cause.boundary === "model")
|
|
345
|
-
throw
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
345
|
+
async function admitModelRefusal(operation, cause, models, signal) {
|
|
346
|
+
if (!(cause instanceof ShiitakeError && cause.boundary === "model"))
|
|
347
|
+
throw cause;
|
|
348
|
+
let offered = [];
|
|
349
|
+
try {
|
|
350
|
+
offered = (await models?.(signal)) ?? [];
|
|
351
|
+
}
|
|
352
|
+
catch (catalogFailure) {
|
|
353
|
+
// Catalog diagnostics must not replace the original model refusal.
|
|
354
|
+
signal.throwIfAborted();
|
|
355
|
+
reportCatchFailure("agent-control.model-catalog", catalogFailure);
|
|
356
|
+
}
|
|
357
|
+
const addresses = offered.slice(0, 10).map((model) => model.address);
|
|
358
|
+
const suffix = addresses.length === 0 ? "" : ` Offered: ${addresses.join(", ")}.`;
|
|
359
|
+
throw new CapabilityFailed({
|
|
360
|
+
namespace: "agents",
|
|
361
|
+
operation,
|
|
362
|
+
cause: {
|
|
363
|
+
kind: "OperationFailed",
|
|
364
|
+
message: `model:${cause.cause}: ${cause.message}${suffix}`,
|
|
365
|
+
},
|
|
366
|
+
rawCause: cause,
|
|
367
|
+
});
|
|
355
368
|
}
|
|
356
369
|
/** Admit a creation refusal or a model refusal as a typed failure; rethrow anything else. */
|
|
357
|
-
function admitCreateFailure(cause) {
|
|
370
|
+
function admitCreateFailure(cause, models, signal) {
|
|
358
371
|
if (cause instanceof AgentCreateRefused)
|
|
359
372
|
throw new CapabilityFailed({
|
|
360
373
|
namespace: "agents",
|
|
@@ -365,10 +378,11 @@ function admitCreateFailure(cause) {
|
|
|
365
378
|
},
|
|
366
379
|
rawCause: cause,
|
|
367
380
|
});
|
|
368
|
-
return admitModelRefusal("create", cause);
|
|
381
|
+
return admitModelRefusal("create", cause, models, signal);
|
|
369
382
|
}
|
|
370
383
|
/** Compose one authorized Agent-control projection for a single run lease. */
|
|
371
384
|
export function createAgentControlSource(directory) {
|
|
385
|
+
const createSession = (operation, agentId, requestId, signal) => withAgent(directory, operation, agentId, signal, (agent) => agent.createSession(requestId));
|
|
372
386
|
const create = directory.create;
|
|
373
387
|
const send = directory.send;
|
|
374
388
|
const recall = directory.recall;
|
|
@@ -378,9 +392,9 @@ export function createAgentControlSource(directory) {
|
|
|
378
392
|
? undefined
|
|
379
393
|
: Mycelium.operation({
|
|
380
394
|
replay: "unsafe",
|
|
381
|
-
description: "Create one ordinary Agent (a subagent to spawn work on) in the current authorized scope
|
|
395
|
+
description: "Create one ordinary Agent (a subagent to spawn work on) and its first Session in the current authorized scope. Returns agentId and a usable sessionId: immediately agents.send with delivery: steer, then collect the reply with agents.wait; no separate createSession is needed. Reuse requestId when retrying the same creation and first Session. Optional name labels the Agent; model is a host-offered provider/model from agents.models; reasoning (minimal, low, medium, high, xhigh or max) needs model. The result also returns name, workingDirectory (the host's project directory), and the requested model/reasoning default, absent when inherited. An unoffered model fails with model:rejected and up to ten offered addresses. A new Agent starts with the base roster; extensions may enable only extension ids the creating Agent holds. An unheld extension fails with create:exceeds_creator and creates nothing.",
|
|
382
396
|
input: createInput,
|
|
383
|
-
output:
|
|
397
|
+
output: createdAgentOutput,
|
|
384
398
|
execute: async ({ requestId, name, model, reasoning, extensions }, context) => {
|
|
385
399
|
let request = { requestId };
|
|
386
400
|
if (name !== undefined)
|
|
@@ -394,7 +408,12 @@ export function createAgentControlSource(directory) {
|
|
|
394
408
|
model: reasoning === undefined ? address : { ...address, reasoning },
|
|
395
409
|
};
|
|
396
410
|
}
|
|
397
|
-
|
|
411
|
+
const created = await create(request, context.signal).catch((cause) => admitCreateFailure(cause, directory.models, context.signal));
|
|
412
|
+
const sessionId = await createSession("create", created.agentId, requestId, context.signal);
|
|
413
|
+
return createdAgentOutput.parse({
|
|
414
|
+
...agentAnswer(created),
|
|
415
|
+
sessionId,
|
|
416
|
+
});
|
|
398
417
|
},
|
|
399
418
|
});
|
|
400
419
|
const createOperations = createOperation === undefined ? {} : { create: createOperation };
|
|
@@ -404,7 +423,7 @@ export function createAgentControlSource(directory) {
|
|
|
404
423
|
: {
|
|
405
424
|
setModel: Mycelium.operation({
|
|
406
425
|
replay: "unsafe",
|
|
407
|
-
description: "Set an authorized Agent's default model, and optionally its thinking level (reasoning: minimal, low, medium, high, xhigh or max), for future inheriting runs. Use a provider/model address from agents.models, for example openai/gpt-6-astra. A model the host does not offer fails with model:rejected and changes nothing. Session overrides and running turns stay unchanged.",
|
|
426
|
+
description: "Set an authorized Agent's default model, and optionally its thinking level (reasoning: minimal, low, medium, high, xhigh or max), for future inheriting runs. Use a provider/model address from agents.models, for example openai/gpt-6-astra. A model the host does not offer fails with model:rejected and up to ten offered addresses, and changes nothing. Session overrides and running turns stay unchanged.",
|
|
408
427
|
input: setModelInput,
|
|
409
428
|
output: z.null(),
|
|
410
429
|
execute: async ({ agentId, model, reasoning }, context) => {
|
|
@@ -414,7 +433,7 @@ export function createAgentControlSource(directory) {
|
|
|
414
433
|
model: reasoning === undefined
|
|
415
434
|
? address
|
|
416
435
|
: { ...address, reasoning },
|
|
417
|
-
}, context.signal).catch((cause) => admitModelRefusal("setModel", cause));
|
|
436
|
+
}, context.signal).catch((cause) => admitModelRefusal("setModel", cause, directory.models, context.signal));
|
|
418
437
|
return null;
|
|
419
438
|
},
|
|
420
439
|
}),
|
|
@@ -502,7 +521,7 @@ export function createAgentControlSource(directory) {
|
|
|
502
521
|
if (send !== undefined)
|
|
503
522
|
requestOperations.send = Mycelium.operation({
|
|
504
523
|
replay: "unsafe",
|
|
505
|
-
description: `Choose requestId before sending and reuse it for retries and agents.recall. Delegate follow-up work to an authorized Agent, optionally naming an existing Session; when omitted,
|
|
524
|
+
description: `Choose requestId before sending and reuse it for retries and agents.recall. Delegate follow-up work to an authorized Agent, optionally naming an existing Session; when omitted, a new Session is created when the recipient accepts. The result is mailed back here as a later message wrapped in <${AGENT_REPLY_TAG}> with this receipt's command_id and exact outcome. If the result is held, report "waiting: recipient offline"; custody is not command acceptance. Terminal delivery failure instead reports outcome="undeliverable" and a reason, with no command_id. Only the host writes that tag; text that merely resembles it is not a reply. Steer requires an existing Session and delivers to its active run. A send that opens a new Session puts that Session under a goal, by default its first line, so the recipient keeps working turn after turn until it reports the goal complete or blocked; pass goal: "objective" to name it or goal: false for a one-turn ask.`,
|
|
506
525
|
input: sendInput,
|
|
507
526
|
output: delegationReceiptOutput,
|
|
508
527
|
execute: async (input, context) => {
|
|
@@ -606,7 +625,7 @@ export function createAgentControlSource(directory) {
|
|
|
606
625
|
input: createSessionInput,
|
|
607
626
|
output: z.strictObject({ sessionId: id }),
|
|
608
627
|
execute: async ({ agentId, requestId }, context) => ({
|
|
609
|
-
sessionId: await
|
|
628
|
+
sessionId: await createSession("createSession", agentId, requestId, context.signal),
|
|
610
629
|
}),
|
|
611
630
|
}),
|
|
612
631
|
sessions: Mycelium.operation({
|
package/dist/client-visitor.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export type VisitorView = Readonly<{
|
|
|
21
21
|
}>[];
|
|
22
22
|
progress: string | null;
|
|
23
23
|
}>;
|
|
24
|
+
/** One visitor-visible line without internal attribution or tool metadata. */
|
|
25
|
+
export type VisitorLine = VisitorView["lines"][number];
|
|
24
26
|
/** Public pending facts exclude the host's private metadata and admission origin. */
|
|
25
27
|
export type VisitorPendingPrompt = Pick<SessionPendingPrompt, "commandId" | "requestId" | "text" | "delivery" | "images">;
|
|
26
28
|
/** Project one owner-selected prompt without exposing host identity or correlation metadata. */
|
package/dist/client-visitor.js
CHANGED
|
@@ -172,7 +172,12 @@ export function visitorSnapshot(snapshot, visitorId) {
|
|
|
172
172
|
toolOutput: [],
|
|
173
173
|
progress: !ownWork || snapshot.progress === null
|
|
174
174
|
? null
|
|
175
|
-
: {
|
|
175
|
+
: {
|
|
176
|
+
...snapshot.progress,
|
|
177
|
+
thinking: "",
|
|
178
|
+
thinkingOffset: 0,
|
|
179
|
+
thinkingTruncated: false,
|
|
180
|
+
},
|
|
176
181
|
extensions: {},
|
|
177
182
|
attention: [],
|
|
178
183
|
};
|
package/dist/client-wire.d.ts
CHANGED
|
@@ -149,6 +149,8 @@ export declare const SessionSnapshotSchema: Schema.Struct<{
|
|
|
149
149
|
revision: number;
|
|
150
150
|
text: string;
|
|
151
151
|
thinking: string;
|
|
152
|
+
textOffset: number;
|
|
153
|
+
thinkingOffset: number;
|
|
152
154
|
textTruncated: boolean;
|
|
153
155
|
thinkingTruncated: boolean;
|
|
154
156
|
}>, Readonly<{
|
|
@@ -156,6 +158,8 @@ export declare const SessionSnapshotSchema: Schema.Struct<{
|
|
|
156
158
|
revision: number;
|
|
157
159
|
text: string;
|
|
158
160
|
thinking: string;
|
|
161
|
+
textOffset: number;
|
|
162
|
+
thinkingOffset: number;
|
|
159
163
|
textTruncated: boolean;
|
|
160
164
|
thinkingTruncated: boolean;
|
|
161
165
|
}>, never, never>>;
|
package/dist/client-wire.js
CHANGED
|
@@ -28,18 +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.
|
|
32
|
-
Schema.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
jobId: Schema.NonEmptyString,
|
|
41
|
-
}),
|
|
42
|
-
])),
|
|
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
|
+
})),
|
|
43
40
|
}),
|
|
44
41
|
Schema.Struct({
|
|
45
42
|
state: Schema.Literal("running"),
|
|
@@ -110,11 +107,14 @@ const SessionCompactionNoticeSchema = Schema.Struct({
|
|
|
110
107
|
]),
|
|
111
108
|
diagnostic: Schema.NullOr(Schema.String),
|
|
112
109
|
});
|
|
110
|
+
const ProgressTailSchema = Schema.String.check(Schema.makeFilter((value) => new TextEncoder().encode(value).byteLength <= 65_536));
|
|
113
111
|
const SessionProgressSchema = Schema.Struct({
|
|
114
112
|
messageId: Schema.String,
|
|
115
113
|
revision: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
116
|
-
text:
|
|
117
|
-
thinking:
|
|
114
|
+
text: ProgressTailSchema,
|
|
115
|
+
thinking: ProgressTailSchema,
|
|
116
|
+
textOffset: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
117
|
+
thinkingOffset: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
118
118
|
textTruncated: Schema.Boolean,
|
|
119
119
|
thinkingTruncated: Schema.Boolean,
|
|
120
120
|
});
|
|
@@ -190,7 +190,18 @@ export const SessionFrameSchema = Schema.Union([
|
|
|
190
190
|
}),
|
|
191
191
|
Schema.Struct({
|
|
192
192
|
kind: Schema.Literal("progress"),
|
|
193
|
-
progress:
|
|
193
|
+
progress: Schema.Struct({
|
|
194
|
+
messageId: Schema.String,
|
|
195
|
+
revision: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
196
|
+
text: Schema.Struct({
|
|
197
|
+
offset: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
198
|
+
append: ProgressTailSchema,
|
|
199
|
+
}),
|
|
200
|
+
thinking: Schema.Struct({
|
|
201
|
+
offset: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
202
|
+
append: ProgressTailSchema,
|
|
203
|
+
}),
|
|
204
|
+
}),
|
|
194
205
|
activity: SessionActivitySchema,
|
|
195
206
|
}),
|
|
196
207
|
Schema.Struct({
|
package/dist/client.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { createShiitakeClient, type ShiitakeClientOptions, } from "./remote-clie
|
|
|
11
11
|
export { bindAgentExtension, bindSessionExtension, type BoundAgentExtensionV0, type BoundCallV0, type BoundSessionExtensionV0, type ExtensionViewV0, type ScopedCommandsV0, } from "./extension-client.js";
|
|
12
12
|
export { AcceptedSchema, DelegationReceiptSchema, HeldSchema, parseAccepted, parseDelegationReceipt, parseCancelResult, parseLatestPendingPrompt, parseWithdrawResult, parseForkedSession, parseSessionFrame, parseSessionSnapshot, parseSessionSummaryPage, parseShiitakeError, } from "./client-wire.js";
|
|
13
13
|
export type { AbortCommand, BranchCommand, CompactCommand, CancelCommand, ForkCommand, OperatorCommand, PromptCommand, PromptDelivery, SessionCommand, TitleCommand, } from "./internal/client-boundary.js";
|
|
14
|
-
export { participantSnapshot, visitorPendingPrompt, visitorSnapshot, visitorView, type ParticipantPolicy, type VisitorPendingPrompt, type VisitorView, } from "./client-visitor.js";
|
|
14
|
+
export { participantSnapshot, visitorPendingPrompt, visitorSnapshot, visitorView, type ParticipantPolicy, type VisitorLine, type VisitorPendingPrompt, type VisitorView, } from "./client-visitor.js";
|
|
15
15
|
/** Semantic acknowledgement returned after one durable acceptance commit. */
|
|
16
16
|
export type Accepted = Readonly<{
|
|
17
17
|
/** Indicates that the owner transaction committed. */
|
|
@@ -132,15 +132,9 @@ export type SessionTerminal = Readonly<{
|
|
|
132
132
|
*/
|
|
133
133
|
meta?: Json;
|
|
134
134
|
}>;
|
|
135
|
-
/**
|
|
135
|
+
/** The occupying work's current phase; never its identity or transcript. */
|
|
136
136
|
export type AgentRunningWork = Readonly<{
|
|
137
|
-
|
|
138
|
-
sessionId: string;
|
|
139
|
-
commandId: string;
|
|
140
|
-
}> | Readonly<{
|
|
141
|
-
kind: "host";
|
|
142
|
-
lane: string;
|
|
143
|
-
jobId: string;
|
|
137
|
+
phase: "thinking" | "streaming" | "running_tool" | "waiting_on_you" | "cancelling";
|
|
144
138
|
}>;
|
|
145
139
|
/**
|
|
146
140
|
* Raw facts about what a Session is doing now. Shiitake publishes facts only;
|
|
@@ -213,9 +207,27 @@ export type SessionProgress = Readonly<{
|
|
|
213
207
|
revision: number;
|
|
214
208
|
text: string;
|
|
215
209
|
thinking: string;
|
|
210
|
+
/** UTF-16 offsets of the retained tails, not byte offsets. */
|
|
211
|
+
textOffset: number;
|
|
212
|
+
thinkingOffset: number;
|
|
216
213
|
textTruncated: boolean;
|
|
217
214
|
thinkingTruncated: boolean;
|
|
218
215
|
}>;
|
|
216
|
+
/** Append-only progress on the wire. Offsets count JavaScript string units. */
|
|
217
|
+
export type SessionProgressDelta = Readonly<{
|
|
218
|
+
messageId: string;
|
|
219
|
+
revision: number;
|
|
220
|
+
text: Readonly<{
|
|
221
|
+
offset: number;
|
|
222
|
+
append: string;
|
|
223
|
+
}>;
|
|
224
|
+
thinking: Readonly<{
|
|
225
|
+
offset: number;
|
|
226
|
+
append: string;
|
|
227
|
+
}>;
|
|
228
|
+
}>;
|
|
229
|
+
/** Fold a decoded append once; a missing prefix waits for an owner reset. */
|
|
230
|
+
export declare function appendSessionProgress(current: SessionProgress | null, delta: SessionProgressDelta): SessionProgress | null;
|
|
219
231
|
/** Accepted prompt waiting to enter the conversation, not a scheduling command. */
|
|
220
232
|
export type SessionPendingPrompt = Readonly<{
|
|
221
233
|
/** Caller identity, available even before the command response reaches its client. */
|
|
@@ -370,9 +382,9 @@ export type SessionFrame = Readonly<{
|
|
|
370
382
|
*/
|
|
371
383
|
context?: SessionContext;
|
|
372
384
|
}> | Readonly<{
|
|
373
|
-
/**
|
|
385
|
+
/** Only new text/thinking; snapshots, not frames, carry retained tails. */
|
|
374
386
|
kind: "progress";
|
|
375
|
-
progress:
|
|
387
|
+
progress: SessionProgressDelta;
|
|
376
388
|
/** Raw facts about the Session's activity read with this progress. */
|
|
377
389
|
activity: SessionActivity;
|
|
378
390
|
}> | Readonly<{
|
package/dist/client.js
CHANGED
|
@@ -3,3 +3,25 @@ export { createShiitakeClient, } from "./remote-client.js";
|
|
|
3
3
|
export { bindAgentExtension, bindSessionExtension, } from "./extension-client.js";
|
|
4
4
|
export { AcceptedSchema, DelegationReceiptSchema, HeldSchema, parseAccepted, parseDelegationReceipt, parseCancelResult, parseLatestPendingPrompt, parseWithdrawResult, parseForkedSession, parseSessionFrame, parseSessionSnapshot, parseSessionSummaryPage, parseShiitakeError, } from "./client-wire.js";
|
|
5
5
|
export { participantSnapshot, visitorPendingPrompt, visitorSnapshot, visitorView, } from "./client-visitor.js";
|
|
6
|
+
/** Fold a decoded append once; a missing prefix waits for an owner reset. */
|
|
7
|
+
export function appendSessionProgress(current, delta) {
|
|
8
|
+
const previous = current?.messageId === delta.messageId ? current : null;
|
|
9
|
+
if (previous !== null && previous.revision >= delta.revision)
|
|
10
|
+
return current;
|
|
11
|
+
const textEnd = previous === null ? 0 : previous.textOffset + previous.text.length;
|
|
12
|
+
const thinkingEnd = previous === null ? 0 : previous.thinkingOffset + previous.thinking.length;
|
|
13
|
+
if (delta.text.offset > textEnd || delta.thinking.offset > thinkingEnd)
|
|
14
|
+
return current;
|
|
15
|
+
return {
|
|
16
|
+
messageId: delta.messageId,
|
|
17
|
+
revision: delta.revision,
|
|
18
|
+
text: (previous?.text ?? "") +
|
|
19
|
+
delta.text.append.slice(Math.max(0, textEnd - delta.text.offset)),
|
|
20
|
+
thinking: (previous?.thinking ?? "") +
|
|
21
|
+
delta.thinking.append.slice(Math.max(0, thinkingEnd - delta.thinking.offset)),
|
|
22
|
+
textOffset: previous?.textOffset ?? 0,
|
|
23
|
+
thinkingOffset: previous?.thinkingOffset ?? 0,
|
|
24
|
+
textTruncated: previous?.textTruncated ?? false,
|
|
25
|
+
thinkingTruncated: previous?.thinkingTruncated ?? false,
|
|
26
|
+
};
|
|
27
|
+
}
|
package/dist/cloudflare.d.ts
CHANGED
|
@@ -102,6 +102,11 @@ export type CloudflareShiitakeOptions = Readonly<{
|
|
|
102
102
|
* refuses for storage pressure. The budget and thresholds are the host's.
|
|
103
103
|
*/
|
|
104
104
|
storagePressure?: StoragePressureCheck;
|
|
105
|
+
/** Host-owned synchronous check at the single new-work admission door. */
|
|
106
|
+
admission?: Readonly<{
|
|
107
|
+
/** Check the host's current flag synchronously inside admission's transaction. */
|
|
108
|
+
assertOpen: () => void;
|
|
109
|
+
}>;
|
|
105
110
|
/** Compaction budgets; absent uses Pi's defaults for every model. */
|
|
106
111
|
compaction?: CompactionOptions;
|
|
107
112
|
/** The host's explicit extension roster and grants; there is no default. */
|
|
@@ -127,6 +132,21 @@ export type CloudflareShiitakeOptions = Readonly<{
|
|
|
127
132
|
}>;
|
|
128
133
|
/** Cloudflare host lifecycle; alarm forwarding stays outside the neutral client. */
|
|
129
134
|
export type CloudflareShiitake = ShiitakeOwnerAgent & Readonly<{
|
|
135
|
+
/** Read durable queued/running Session or unresolved delegated work in the
|
|
136
|
+
* host's SQLite transaction, immediately before changing archived state.
|
|
137
|
+
* A confirmed remote settlement must be recorded locally first; unavailable
|
|
138
|
+
* remote settlement remains busy, not idle. Never use an in-memory counter.
|
|
139
|
+
*/
|
|
140
|
+
busySync: () => Readonly<{
|
|
141
|
+
kind: "session";
|
|
142
|
+
sessionId: string;
|
|
143
|
+
commandId: string;
|
|
144
|
+
}> | Readonly<{
|
|
145
|
+
kind: "delegation";
|
|
146
|
+
agentId: string;
|
|
147
|
+
sessionId: string;
|
|
148
|
+
commandId: string;
|
|
149
|
+
}> | undefined;
|
|
130
150
|
/** Exact settled command facts, outside the neutral Session transport. */
|
|
131
151
|
readSettlement: (sessionId: string, commandId: string) => Promise<CommandSettlement | undefined>;
|
|
132
152
|
/**
|
package/dist/cloudflare.js
CHANGED
|
@@ -277,6 +277,8 @@ function effectOptions(state, options, runtime) {
|
|
|
277
277
|
Object.assign(base, { meta: options.meta });
|
|
278
278
|
if (options.storagePressure !== undefined)
|
|
279
279
|
Object.assign(base, { storagePressure: options.storagePressure });
|
|
280
|
+
if (options.admission !== undefined)
|
|
281
|
+
Object.assign(base, { admission: options.admission });
|
|
280
282
|
if (options.compaction !== undefined)
|
|
281
283
|
Object.assign(base, { compaction: options.compaction });
|
|
282
284
|
return tools === undefined ? base : { ...base, tools };
|
|
@@ -319,7 +321,7 @@ export function makeCloudflareShiitake(unnamed, options) {
|
|
|
319
321
|
const coreOptions = effectOptions(state, options, host.runtime);
|
|
320
322
|
return import("./internal/runtime-core.js")
|
|
321
323
|
.then(({ openRuntimeCore }) => runShiitakePromise(openRuntimeCore(coreOptions, options.mailRoom)))
|
|
322
|
-
.then(async ({ agent, activeSessions, acceptMail, readSettlement }) => {
|
|
324
|
+
.then(async ({ agent, activeSessions, busySync, acceptMail, readSettlement, }) => {
|
|
323
325
|
const handle = toPromiseOwner(agent);
|
|
324
326
|
try {
|
|
325
327
|
await options.initialize?.(handle);
|
|
@@ -340,6 +342,7 @@ export function makeCloudflareShiitake(unnamed, options) {
|
|
|
340
342
|
},
|
|
341
343
|
alarm: host.alarm,
|
|
342
344
|
activeSessions: () => runShiitakePromise(activeSessions),
|
|
345
|
+
busySync,
|
|
343
346
|
abortActive: async () => {
|
|
344
347
|
for (const sessionId of await runShiitakePromise(activeSessions))
|
|
345
348
|
await runShiitakePromise(agent.session(sessionId).abort);
|
package/dist/delegation.d.ts
CHANGED
|
@@ -31,6 +31,11 @@ export declare class AgentDelegation {
|
|
|
31
31
|
}>);
|
|
32
32
|
private fleetOwner;
|
|
33
33
|
private fleet;
|
|
34
|
+
/** Best-effort remote refresh before an owner-local archive transaction.
|
|
35
|
+
* Confirmed settlements are recorded by Shiitake; unreadable targets leave
|
|
36
|
+
* their local accepted receipt outstanding for busySync to refuse.
|
|
37
|
+
*/
|
|
38
|
+
refreshOutstanding(readSettlement: (agentId: string, sessionId: string, commandId: string) => Promise<CommandSettlement | undefined>): Promise<void>;
|
|
34
39
|
private scope;
|
|
35
40
|
/** Recipient-local disclosure through the public host, never another Agent's RPC. */
|
|
36
41
|
settlement(sender: DelegationIdentity, sessionId: string, commandId: string, host: Pick<ShiitakeHost, "readSettlement">): Promise<CommandSettlement | undefined>;
|
package/dist/delegation.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* addressed Agent mail over Carrier Post, and the Agent grammar a host hands
|
|
4
4
|
* to Oyster's machine link as ports. Oyster core never imports this engine.
|
|
5
5
|
*/
|
|
6
|
-
import { commandSettlementSchema, createAgentDirectory, createFleetCorrespondence, agentDelegationRequestKey, delegationProvenance, delegationReplyMeta, delegationSendMeta, settlementReadableBy, } from "./fleet.js";
|
|
6
|
+
import { commandSettlementSchema, createAgentDirectory, createFleetCorrespondence, createFleetDelegation, agentDelegationRequestKey, delegationProvenance, delegationReplyMeta, delegationSendMeta, settlementReadableBy, } from "./fleet.js";
|
|
7
7
|
import * as Schema from "effect/Schema";
|
|
8
8
|
import { agentMailAddressSchema } from "@fungi.computer/seam/machine-link-protocol";
|
|
9
9
|
import { BirdDogMailSchema, BirdDogError, settlementReplies, } from "@fungi.computer/bird-dog/effect";
|
|
@@ -27,6 +27,19 @@ export class AgentDelegation {
|
|
|
27
27
|
deliver: this.deps.deliver,
|
|
28
28
|
}));
|
|
29
29
|
}
|
|
30
|
+
/** Best-effort remote refresh before an owner-local archive transaction.
|
|
31
|
+
* Confirmed settlements are recorded by Shiitake; unreadable targets leave
|
|
32
|
+
* their local accepted receipt outstanding for busySync to refuse.
|
|
33
|
+
*/
|
|
34
|
+
async refreshOutstanding(readSettlement) {
|
|
35
|
+
const fleet = createFleetDelegation({
|
|
36
|
+
storage: this.deps.storage,
|
|
37
|
+
deliver: this.deps.deliver,
|
|
38
|
+
open: (agentId) => this.deps.directory().open(agentId, new AbortController().signal),
|
|
39
|
+
readSettlement,
|
|
40
|
+
});
|
|
41
|
+
await fleet.outstanding();
|
|
42
|
+
}
|
|
30
43
|
async scope(signal) {
|
|
31
44
|
const agents = await this.deps.directory().list(signal);
|
|
32
45
|
const identity = await this.deps.identity();
|
|
@@ -72,19 +85,22 @@ export class AgentDelegation {
|
|
|
72
85
|
});
|
|
73
86
|
const meta = delegationSendMeta({ id: scope.agentId, name: scope.source.name }, invocation.meta);
|
|
74
87
|
signal.throwIfAborted();
|
|
88
|
+
let task = {
|
|
89
|
+
agentId: input.agentId,
|
|
90
|
+
sessionId: input.sessionId,
|
|
91
|
+
delivery: input.delivery,
|
|
92
|
+
text: input.text,
|
|
93
|
+
requestId,
|
|
94
|
+
};
|
|
95
|
+
if (input.delivery === "followUp" && input.goal !== undefined)
|
|
96
|
+
task = { ...task, goal: input.goal };
|
|
75
97
|
const accepted = await this.fleet().start({
|
|
76
98
|
agents: scope.agents.map(({ agentId }) => agentId),
|
|
77
99
|
actions: ["delegate"],
|
|
78
100
|
expiresAt: this.deps.expiresAt(),
|
|
79
101
|
provenance,
|
|
80
102
|
meta,
|
|
81
|
-
},
|
|
82
|
-
agentId: input.agentId,
|
|
83
|
-
sessionId: input.sessionId,
|
|
84
|
-
delivery: input.delivery,
|
|
85
|
-
text: input.text,
|
|
86
|
-
requestId,
|
|
87
|
-
});
|
|
103
|
+
}, task);
|
|
88
104
|
return accepted;
|
|
89
105
|
}
|
|
90
106
|
/** Resolve this sender's exact request, then let the recipient guard pending work. */
|
package/dist/effect.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { Accepted } from "./client.js";
|
|
|
8
8
|
import type { ShiitakeError } from "./errors.js";
|
|
9
9
|
import type { ShiitakeEffectOptions, ShiitakeEffectOwner } from "./internal/effect-contracts.js";
|
|
10
10
|
import type { SqliteOwner } from "./internal/contracts.js";
|
|
11
|
-
export type { AbortCommand, BranchCommand, CompactCommand, CancelCommand, CancelResult, Accepted, ForkCommand, ForkedSession, PromptCommand, PromptDelivery, ReadOptions, SessionCommand, SessionCommandHandler, SessionFrame, SessionMessage, SessionProgress, SessionToolOutput, SessionSnapshot, SessionActivity, AgentRunningWork, SubscribeOptions, TooLate, WatchOptions, } from "./client.js";
|
|
11
|
+
export type { AbortCommand, BranchCommand, CompactCommand, CancelCommand, CancelResult, Accepted, ForkCommand, ForkedSession, PromptCommand, PromptDelivery, ReadOptions, SessionCommand, SessionCommandHandler, SessionFrame, SessionMessage, SessionProgress, SessionProgressDelta, SessionToolOutput, SessionSnapshot, SessionActivity, AgentRunningWork, SubscribeOptions, TooLate, WatchOptions, } from "./client.js";
|
|
12
12
|
export type { InstructionSnapshot, InstructionSource, ModelAcquisition, ModelDescription, ModelSource, RuntimeHost, RuntimePendingWork, SqliteCursor, SqliteOwner, SqliteValue, SqliteRow, GrantsDelivery, GrantsDeliveryResult, ShiitakeOptions, ToolAccess, HostMetaOptions, StandardHostMetaResult, StandardHostMetaSchema, ToolSource, } from "./internal/contracts.js";
|
|
13
13
|
export type { InstructionSourceEffect, ModelAcquisitionEffect, ModelSourceEffect, RuntimeHostEffect, ShiitakeEffectAgent, ShiitakeEffectAgentExtensions, ShiitakeEffectCommandHandler, ShiitakeEffectOptions, ShiitakeEffectOwner, ShiitakeEffectSession, ShiitakeEffectSessionExtension, ShiitakeEffectSendOptions, ShiitakeEffectWatch, ToolSourceEffect, } from "./internal/effect-contracts.js";
|
|
14
14
|
export type { SessionSummary } from "./client.js";
|
package/dist/errors.d.ts
CHANGED
|
@@ -4,14 +4,14 @@ declare const ShiitakeErrorBoundarySchema: Schema.Literals<readonly ["contract",
|
|
|
4
4
|
/** Stable boundary that rejected a Shiitake operation. */
|
|
5
5
|
export type ShiitakeErrorBoundary = Schema.Schema.Type<typeof ShiitakeErrorBoundarySchema>;
|
|
6
6
|
/** Safe failure category exposed by both Shiitake facades. */
|
|
7
|
-
declare const ShiitakeErrorCauseSchema: Schema.Literals<readonly ["closed", "conflict", "incompatible", "invalid", "rejected", "unavailable", "state_reset_required", "storage_pressure"]>;
|
|
7
|
+
declare const ShiitakeErrorCauseSchema: Schema.Literals<readonly ["closed", "conflict", "incompatible", "invalid", "rejected", "unavailable", "state_reset_required", "storage_pressure", "archived"]>;
|
|
8
8
|
/** Safe failure category exposed by both Shiitake facades. */
|
|
9
9
|
export type ShiitakeErrorCause = Schema.Schema.Type<typeof ShiitakeErrorCauseSchema>;
|
|
10
10
|
/** Canonical serialized Shiitake failure. */
|
|
11
11
|
export declare const ShiitakeErrorWireSchema: Schema.Struct<{
|
|
12
12
|
readonly _tag: Schema.Literal<"ShiitakeError">;
|
|
13
13
|
readonly boundary: Schema.Literals<readonly ["contract", "extensions", "instructions", "model", "runtime", "store", "tools"]>;
|
|
14
|
-
readonly cause: Schema.Literals<readonly ["closed", "conflict", "incompatible", "invalid", "rejected", "unavailable", "state_reset_required", "storage_pressure"]>;
|
|
14
|
+
readonly cause: Schema.Literals<readonly ["closed", "conflict", "incompatible", "invalid", "rejected", "unavailable", "state_reset_required", "storage_pressure", "archived"]>;
|
|
15
15
|
readonly message: Schema.String;
|
|
16
16
|
readonly requestId: Schema.optionalKey<Schema.String>;
|
|
17
17
|
}>;
|