@fungi.computer/shiitake 0.3.1 → 0.5.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 +19 -18
- package/dist/agent-control.d.ts +24 -19
- package/dist/agent-control.js +69 -128
- package/dist/client-wire.d.ts +12 -3
- package/dist/client-wire.js +33 -8
- package/dist/client.d.ts +34 -3
- package/dist/client.js +1 -1
- package/dist/cloudflare.d.ts +3 -1
- package/dist/cloudflare.js +2 -2
- package/dist/delegation.d.ts +151 -17
- package/dist/delegation.js +174 -41
- package/dist/effect.d.ts +1 -1
- package/dist/fleet.d.ts +37 -10
- package/dist/fleet.js +217 -48
- package/dist/http-routes.d.ts +84 -12
- package/dist/http-routes.js +15 -1
- package/dist/index.d.ts +2 -2
- package/dist/internal/admission-door.d.ts +2 -0
- package/dist/internal/admission-door.js +9 -2
- package/dist/internal/client-boundary.d.ts +10 -0
- package/dist/internal/client-boundary.js +10 -0
- package/dist/internal/compaction/compaction.js +1 -1
- package/dist/internal/contracts.d.ts +2 -0
- package/dist/internal/coordinator.d.ts +3 -1
- package/dist/internal/coordinator.js +98 -13
- package/dist/internal/effect-client.d.ts +3 -1
- package/dist/internal/effect-client.js +3 -2
- package/dist/internal/effect-contracts.d.ts +6 -2
- package/dist/internal/model-acquisition.js +2 -0
- package/dist/internal/observation-kernel.d.ts +8 -4
- package/dist/internal/observation-kernel.js +44 -9
- package/dist/internal/promise-facade.d.ts +1 -1
- package/dist/internal/promise-facade.js +1 -1
- package/dist/internal/run-scope.js +37 -8
- package/dist/internal/runtime-coordinator.d.ts +5 -0
- package/dist/internal/runtime-coordinator.js +8 -4
- package/dist/internal/runtime-core.d.ts +2 -1
- package/dist/internal/runtime-core.js +6 -1
- package/dist/internal/session-context.d.ts +1 -1
- package/dist/internal/session-context.js +84 -13
- package/dist/internal/woodstock/agent-reply.d.ts +7 -2
- package/dist/internal/woodstock/agent-reply.js +22 -13
- package/dist/internal/woodstock/fleet-store.d.ts +4 -2
- package/dist/internal/woodstock/fleet-store.js +70 -6
- package/dist/internal/woodstock/index.d.ts +2 -0
- package/dist/internal/woodstock/index.js +11 -5
- package/dist/internal/woodstock/run-recovery.js +4 -1
- package/dist/internal/woodstock/session-entry.d.ts +13 -3
- package/dist/internal/woodstock/session-entry.js +1 -0
- package/dist/internal/woodstock/session-store.d.ts +4 -0
- package/dist/internal/woodstock/session-store.js +26 -19
- package/dist/remote-client.js +5 -1
- package/dist/testing.d.ts +5 -2
- package/dist/testing.js +4 -2
- package/docs/api/agent-control.md +18 -12
- package/docs/api/client.md +78 -0
- package/docs/api/delegation.md +72 -55
- package/docs/api/effect.md +5 -0
- package/docs/api/fleet.md +23 -23
- package/docs/api/http.md +2 -1
- package/docs/api/index.md +13 -0
- package/docs/api/testing.md +3 -3
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -20,16 +20,7 @@ To add your own behaviour, start the extension guide at
|
|
|
20
20
|
## Community-funded
|
|
21
21
|
|
|
22
22
|
We don't take VC money. Fungi is built in the open and proudly backed by its
|
|
23
|
-
community
|
|
24
|
-
|
|
25
|
-
$SHIT is not $SHIIT.
|
|
26
|
-
|
|
27
|
-
Official contract address (Solana):
|
|
28
|
-
`412ECWXN3jGbd1kn8ck7eeupWttkmrFkNm9JqfAzpump`
|
|
29
|
-
|
|
30
|
-
This is the only official address. Fungi will never DM you about the token.
|
|
31
|
-
Nothing here is financial advice, and the token isn't a share in Fungi. More at
|
|
32
|
-
[fungi.computer/funding](https://fungi.computer/funding).
|
|
23
|
+
community.
|
|
33
24
|
|
|
34
25
|
## Recover an uncertain send
|
|
35
26
|
|
|
@@ -143,6 +134,16 @@ a Session performs no I/O. A client uses `createShiitakeClient` from the
|
|
|
143
134
|
`/client` entry with the host's authorized transport. Releasing that client does
|
|
144
135
|
not cancel host work.
|
|
145
136
|
|
|
137
|
+
`session.readSettlement(commandId, { timeoutMs, signal })` returns that exact
|
|
138
|
+
command's `CommandSettlement` (terminal, answer, usage and tool-call count),
|
|
139
|
+
independent of the selected transcript branch. `timeoutMs` defaults to zero and
|
|
140
|
+
is bounded to 30,000 ms. A pending command returns `undefined` at the deadline;
|
|
141
|
+
an unknown command fails. Aborting the read never cancels the command.
|
|
142
|
+
`agents.wait` uses this read and settlement hints, not a watch or a transcript
|
|
143
|
+
read. In-process and HTTP clients share the contract; the HTTP encoding is
|
|
144
|
+
`GET /sessions/:sessionId/settlement?commandId=...&timeoutMs=...`, with `null`
|
|
145
|
+
for a pending command.
|
|
146
|
+
|
|
146
147
|
Cancellation targets an accepted `commandId`. `too_late` is an ordinary outcome
|
|
147
148
|
when that work can no longer be withdrawn. `session.abort()` atomically requests
|
|
148
149
|
cancellation of the running turn and every queued prompt in that Session.
|
|
@@ -553,14 +554,14 @@ is ordinary follow-up work: an idle parent wakes a new run, and an active parent
|
|
|
553
554
|
queues it behind the running work, which it never interrupts, so it runs next.
|
|
554
555
|
Each fact on that line has one setter:
|
|
555
556
|
|
|
556
|
-
| Fact | Setter | Rule
|
|
557
|
-
| ----------------------------------- | ------------------------------------------------------------------------------------ |
|
|
558
|
-
| Content | BirdDog `settlementReplies` | The child's last assistant text, or the host's cancellation text such as `Work cancelled by Ada.`, verbatim; with neither, one sentence naming the outcome.
|
|
559
|
-
| `reply.commandId` | BirdDog, from the child's accepted command |
|
|
560
|
-
| `reply.outcome`, `reply.diagnostic` | BirdDog, from the exact Watchdog outcome and the child Session's terminal diagnostic | `completed`, `failed`, `interrupted`, `cancelled` and `outcome_unknown` stay distinct. Only `failed`, `interrupted` and `outcome_unknown` carry a diagnostic, bounded by `boundedDiagnostic`.
|
|
561
|
-
| Authentication | `fleet.authenticateReply(mail)` at the parent's host | The key is the frozen send's `<key>:reply:0`, `replyTo` is empty, `from`/`to` mirror the frozen send exactly
|
|
562
|
-
| Author and `meta.delegation.reply` | The parent's host, only after authentication | The author is the replying child Agent. The settling actor, a human canceller and the parent never author a reply.
|
|
563
|
-
| Model-visible provenance | Shiitake, from the `reply` the host passes to `acceptMail(mail, meta, reply)` | The model sees the line inside `<fungi_agent_reply agent_id=… session_id=… command_id=… outcome=… [diagnostic=…]>`.
|
|
557
|
+
| Fact | Setter | Rule |
|
|
558
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
559
|
+
| Content | BirdDog `settlementReplies` | The child's last assistant text, or the host's cancellation text such as `Work cancelled by Ada.`, verbatim; with neither, one sentence naming the outcome. |
|
|
560
|
+
| `reply.commandId` | BirdDog, from the child's accepted command | Settled work replies carry the parent's `agents.send` receipt `commandId`; undeliverable replies have no command ID. |
|
|
561
|
+
| `reply.outcome`, `reply.diagnostic` | BirdDog, from the exact Watchdog outcome and the child Session's terminal diagnostic | `completed`, `failed`, `interrupted`, `cancelled` and `outcome_unknown` stay distinct. Only `failed`, `interrupted` and `outcome_unknown` carry a diagnostic, bounded by `boundedDiagnostic`. `undeliverable` instead carries `recipient_offline` or `recipient_left`. |
|
|
562
|
+
| Authentication | `fleet.authenticateReply(mail)` at the parent's host | The key is the frozen send's `<key>:reply:0`, `replyTo` is empty, and `from`/`to` mirror the frozen send exactly. Settled replies correlate with the receipt's `commandId`; undeliverable replies correlate only by the frozen mail key and typed reason. Otherwise the result is `undefined` and the host refuses the mail. |
|
|
563
|
+
| Author and `meta.delegation.reply` | The parent's host, only after authentication | The author is the replying child Agent. The settling actor, a human canceller and the parent never author a reply. |
|
|
564
|
+
| Model-visible provenance | Shiitake, from the `reply` the host passes to `acceptMail(mail, meta, reply)` | The model sees the line inside `<fungi_agent_reply agent_id=… session_id=… [command_id=…] outcome=… [diagnostic=…] [reason=…]>`. |
|
|
564
565
|
|
|
565
566
|
The provider role stays `user`. Shiitake renders the tag from the verified
|
|
566
567
|
facts, never from the text, and neutralizes the tag in every other user-role
|
package/dist/agent-control.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CapabilitySource, type CapabilitySession } from "@fungi.computer/mycelium";
|
|
2
|
-
import type { Accepted, CancelResult, ShiitakeAgent, ShiitakeSession } from "./client.js";
|
|
2
|
+
import type { Accepted, CancelResult, Held, ShiitakeAgent, ShiitakeSession } from "./client.js";
|
|
3
3
|
import { type ModelPreference } from "./model-preference.js";
|
|
4
4
|
/**
|
|
5
5
|
* Why a host refuses an archive or restore: the caller did not create the
|
|
@@ -42,15 +42,19 @@ export type AgentControlModel = Readonly<{
|
|
|
42
42
|
export type AgentDelegationInput = Readonly<{
|
|
43
43
|
/** Target Agent in the host's authorized scope. */
|
|
44
44
|
agentId: string;
|
|
45
|
-
/** Target conversation on that Agent. */
|
|
46
|
-
sessionId: string;
|
|
47
45
|
/** Work to admit with the requested delivery. */
|
|
48
46
|
text: string;
|
|
49
47
|
/** Caller-owned identity reused for retries and request-based recall. */
|
|
50
48
|
requestId: string;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
}> & (Readonly<{
|
|
50
|
+
/** An existing conversation is required for a live steer. */
|
|
51
|
+
sessionId: string;
|
|
52
|
+
delivery: "steer";
|
|
53
|
+
}> | Readonly<{
|
|
54
|
+
/** Omitted for an Agent whose first Session is created on acceptance. */
|
|
55
|
+
sessionId?: string;
|
|
56
|
+
delivery: "followUp";
|
|
57
|
+
}>);
|
|
54
58
|
/** Exact authorized destination and caller's original send identity. */
|
|
55
59
|
export type AgentRecallInput = Readonly<{
|
|
56
60
|
/** The authorized target Agent. */
|
|
@@ -78,7 +82,7 @@ export type AgentControlDirectory = Readonly<{
|
|
|
78
82
|
archived?: boolean;
|
|
79
83
|
}>) => Promise<readonly AgentControlAgent[]>;
|
|
80
84
|
/** Host-scoped sends; follow-up work replies to the invoking Session. */
|
|
81
|
-
send?: (input: AgentDelegationInput, sourceSessionId: string, signal: AbortSignal, invocation?: AgentDelegationInvocation) => Promise<Accepted>;
|
|
85
|
+
send?: (input: AgentDelegationInput, sourceSessionId: string, signal: AbortSignal, invocation?: AgentDelegationInvocation) => Promise<Accepted | Held>;
|
|
82
86
|
/** Recall under the same host-owned request identity as send. Never abort active work. */
|
|
83
87
|
recall?: (input: AgentRecallInput, sourceSessionId: string, signal: AbortSignal) => Promise<CancelResult>;
|
|
84
88
|
/** Present only when this host authorizes ordinary Agent creation. */
|
|
@@ -113,7 +117,7 @@ export type AgentControlDirectory = Readonly<{
|
|
|
113
117
|
/** Optional host admission check before accepting a model-backed send. */
|
|
114
118
|
checkModel?: (input: Readonly<{
|
|
115
119
|
agentId: string;
|
|
116
|
-
sessionId
|
|
120
|
+
sessionId?: string;
|
|
117
121
|
}>, signal: AbortSignal) => Promise<void>;
|
|
118
122
|
/** Recheck authorization and open one addressed Agent for this operation. */
|
|
119
123
|
open: (agentId: string, signal: AbortSignal) => Promise<Readonly<{
|
|
@@ -122,23 +126,24 @@ export type AgentControlDirectory = Readonly<{
|
|
|
122
126
|
release: () => Promise<void>;
|
|
123
127
|
}>>;
|
|
124
128
|
}>;
|
|
125
|
-
/**
|
|
126
|
-
export declare function
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
/** Await one exact durable terminal without opening a transcript observation. */
|
|
130
|
+
export declare function waitForAgentSettlement(session: ShiitakeSession, commandId: string, timeoutMs: number, signal: AbortSignal, startedAt?: number): Promise<Readonly<{
|
|
131
|
+
acceptedSender: string | null;
|
|
132
|
+
terminal: import("./client.js").SessionTerminal;
|
|
133
|
+
content: string;
|
|
134
|
+
usage: readonly Pick<import("@fungi.computer/mule/effect").AssistantMessage, "provider" | "model" | "usage">[];
|
|
135
|
+
toolCalls: number;
|
|
132
136
|
}>>;
|
|
133
137
|
/**
|
|
134
138
|
* A host's refusal of an Agent creating an Agent: the creator asked to give the
|
|
135
|
-
* created Agent more than it holds,
|
|
136
|
-
* creating
|
|
139
|
+
* created Agent more than it holds, the host cannot tell which Agent is
|
|
140
|
+
* creating, or the request id belongs to another creating Agent or Session. It
|
|
141
|
+
* reaches the caller as an `agents.create` failure.
|
|
137
142
|
*/
|
|
138
143
|
export declare class AgentCreateRefused extends Error {
|
|
139
|
-
readonly reason: "exceeds_creator" | "no_creator";
|
|
144
|
+
readonly reason: "exceeds_creator" | "no_creator" | "request_conflict";
|
|
140
145
|
readonly name = "AgentCreateRefused";
|
|
141
|
-
constructor(reason: "exceeds_creator" | "no_creator", message: string);
|
|
146
|
+
constructor(reason: "exceeds_creator" | "no_creator" | "request_conflict", message: string);
|
|
142
147
|
}
|
|
143
148
|
/** Compose one authorized Agent-control projection for a single run lease. */
|
|
144
149
|
export declare function createAgentControlSource(directory: AgentControlDirectory): CapabilitySource;
|
package/dist/agent-control.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
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";
|
|
5
4
|
import { AGENT_REPLY_TAG } from "./internal/woodstock/index.js";
|
|
6
5
|
import { modelReasoningLevels, parseModelAddress, } from "./model-preference.js";
|
|
7
6
|
const id = z.string().min(1).max(256);
|
|
@@ -49,14 +48,28 @@ const readInput = sessionInput.extend({
|
|
|
49
48
|
/** A page's `oldestCursor`: read the page strictly before it. */
|
|
50
49
|
before: pageCursor.nullable().default(null),
|
|
51
50
|
});
|
|
52
|
-
const sendArgs =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
const sendArgs = z.discriminatedUnion("delivery", [
|
|
52
|
+
z.strictObject({
|
|
53
|
+
agentId: id,
|
|
54
|
+
sessionId: id,
|
|
55
|
+
requestId,
|
|
56
|
+
text: z
|
|
57
|
+
.string()
|
|
58
|
+
.min(1)
|
|
59
|
+
.max(256 * 1024),
|
|
60
|
+
delivery: z.literal("steer"),
|
|
61
|
+
}),
|
|
62
|
+
z.strictObject({
|
|
63
|
+
agentId: id,
|
|
64
|
+
sessionId: id.optional(),
|
|
65
|
+
requestId,
|
|
66
|
+
text: z
|
|
67
|
+
.string()
|
|
68
|
+
.min(1)
|
|
69
|
+
.max(256 * 1024),
|
|
70
|
+
delivery: z.literal("followUp"),
|
|
71
|
+
}),
|
|
72
|
+
]);
|
|
60
73
|
const sendInput = sendArgs;
|
|
61
74
|
const cancelInput = sessionInput.extend({ commandId: id });
|
|
62
75
|
const recallInput = sessionInput.extend({ requestId });
|
|
@@ -113,8 +126,6 @@ const terminalIdentityOutput = z.strictObject({
|
|
|
113
126
|
});
|
|
114
127
|
/** Characters of a completed command's final assistant text that wait returns. */
|
|
115
128
|
const finalTextLimit = 16_384;
|
|
116
|
-
/** Messages scanned for that text: the same bound as one `read` page. */
|
|
117
|
-
const finalTextReadLimit = 200;
|
|
118
129
|
const diagnosticField = { diagnostic: z.string().max(200) };
|
|
119
130
|
const finalTextFields = {
|
|
120
131
|
text: z.string().max(finalTextLimit),
|
|
@@ -130,6 +141,12 @@ const acceptedOutput = z.strictObject({
|
|
|
130
141
|
status: z.literal("accepted"),
|
|
131
142
|
commandId: id,
|
|
132
143
|
});
|
|
144
|
+
const heldOutput = z.strictObject({
|
|
145
|
+
status: z.literal("held"),
|
|
146
|
+
reason: z.literal("recipient_offline"),
|
|
147
|
+
message: z.literal("waiting: recipient offline"),
|
|
148
|
+
});
|
|
149
|
+
const delegationReceiptOutput = z.union([acceptedOutput, heldOutput]);
|
|
133
150
|
const cancelOutput = z.discriminatedUnion("status", [
|
|
134
151
|
acceptedOutput,
|
|
135
152
|
z.strictObject({ status: z.literal("unknown") }),
|
|
@@ -229,111 +246,21 @@ async function withAgent(directory, operation, agentId, signal, use) {
|
|
|
229
246
|
await opened.release();
|
|
230
247
|
}
|
|
231
248
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
let onAbort;
|
|
238
|
-
const promise = new Promise((_, reject) => {
|
|
239
|
-
onAbort = () => {
|
|
240
|
-
reject(signal.reason instanceof Error
|
|
241
|
-
? signal.reason
|
|
242
|
-
: new Error("Agent wait cancelled"));
|
|
243
|
-
};
|
|
244
|
-
if (signal.aborted) {
|
|
245
|
-
onAbort();
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
249
|
-
timer = setTimeout(() => {
|
|
250
|
-
reject(new Error("Agent wait timed out"));
|
|
251
|
-
}, timeoutMs);
|
|
252
|
-
});
|
|
253
|
-
return {
|
|
254
|
-
promise,
|
|
255
|
-
dispose: () => {
|
|
256
|
-
if (timer !== undefined)
|
|
257
|
-
clearTimeout(timer);
|
|
258
|
-
if (onAbort !== undefined)
|
|
259
|
-
signal.removeEventListener("abort", onAbort);
|
|
260
|
-
},
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
async function raceCancellation(value, cancellation) {
|
|
264
|
-
return Promise.race([value, cancellation]);
|
|
265
|
-
}
|
|
266
|
-
/** The text of the command's newest assistant message in a bounded page, if it has any. */
|
|
267
|
-
function finalAssistantText(snapshot, commandId) {
|
|
268
|
-
for (let index = snapshot.messages.length - 1; index >= 0; index -= 1) {
|
|
269
|
-
const { commandId: owner, value } = snapshot.messages[index] ?? {};
|
|
270
|
-
if (owner !== commandId || value?.role !== "assistant")
|
|
271
|
-
continue;
|
|
272
|
-
const text = value.content
|
|
273
|
-
.flatMap((block) => (block.type === "text" ? [block.text] : []))
|
|
274
|
-
.join("");
|
|
275
|
-
return text.length === 0 ? undefined : text;
|
|
276
|
-
}
|
|
277
|
-
return undefined;
|
|
278
|
-
}
|
|
279
|
-
/** Observe one durable terminal; timeout or abort closes observation, not the work. */
|
|
280
|
-
export async function waitForAgentTerminal(session, commandId, timeoutMs, signal, startedAt = Date.now()) {
|
|
281
|
-
const remaining = Math.max(1, timeoutMs - (Date.now() - startedAt));
|
|
282
|
-
const cancellation = createCancellation(signal, remaining);
|
|
283
|
-
let watch;
|
|
284
|
-
let lateWatch;
|
|
285
|
-
let disposed = false;
|
|
286
|
-
const watchPromise = session.watch();
|
|
287
|
-
void watchPromise.then((resolvedWatch) => {
|
|
288
|
-
if (watch === undefined) {
|
|
289
|
-
if (disposed)
|
|
290
|
-
void resolvedWatch.close().catch((cause) => {
|
|
291
|
-
reportCatchFailure("late-agent-watch-close", cause);
|
|
292
|
-
});
|
|
293
|
-
else
|
|
294
|
-
lateWatch = resolvedWatch;
|
|
295
|
-
}
|
|
296
|
-
}, () => {
|
|
297
|
-
return Promise.resolve();
|
|
249
|
+
/** Await one exact durable terminal without opening a transcript observation. */
|
|
250
|
+
export async function waitForAgentSettlement(session, commandId, timeoutMs, signal, startedAt = Date.now()) {
|
|
251
|
+
const settled = await session.readSettlement(commandId, {
|
|
252
|
+
timeoutMs: Math.max(0, timeoutMs - (Date.now() - startedAt)),
|
|
253
|
+
signal,
|
|
298
254
|
});
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
let terminals = watch.snapshot.terminals;
|
|
303
|
-
const initial = terminalFor({ terminals }, commandId);
|
|
304
|
-
if (initial !== undefined)
|
|
305
|
-
return initial;
|
|
306
|
-
const iterator = watch.changes[Symbol.asyncIterator]();
|
|
307
|
-
for (;;) {
|
|
308
|
-
const next = await raceCancellation(iterator.next(), cancellation.promise);
|
|
309
|
-
if (next.done)
|
|
310
|
-
throw new Error("Agent Session observation closed");
|
|
311
|
-
if (next.value.kind === "reset") {
|
|
312
|
-
terminals = next.value.baseline.terminals;
|
|
313
|
-
}
|
|
314
|
-
else if (next.value.kind === "update") {
|
|
315
|
-
terminals = next.value.terminals;
|
|
316
|
-
}
|
|
317
|
-
else
|
|
318
|
-
continue;
|
|
319
|
-
const terminal = terminalFor({ terminals }, commandId);
|
|
320
|
-
if (terminal !== undefined)
|
|
321
|
-
return terminal;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
finally {
|
|
325
|
-
disposed = true;
|
|
326
|
-
cancellation.dispose();
|
|
327
|
-
if (watch !== undefined)
|
|
328
|
-
await watch.close();
|
|
329
|
-
else if (lateWatch !== undefined)
|
|
330
|
-
await lateWatch.close();
|
|
331
|
-
}
|
|
255
|
+
if (settled === undefined)
|
|
256
|
+
throw new Error("Agent wait timed out");
|
|
257
|
+
return settled;
|
|
332
258
|
}
|
|
333
259
|
/**
|
|
334
260
|
* A host's refusal of an Agent creating an Agent: the creator asked to give the
|
|
335
|
-
* created Agent more than it holds,
|
|
336
|
-
* creating
|
|
261
|
+
* created Agent more than it holds, the host cannot tell which Agent is
|
|
262
|
+
* creating, or the request id belongs to another creating Agent or Session. It
|
|
263
|
+
* reaches the caller as an `agents.create` failure.
|
|
337
264
|
*/
|
|
338
265
|
export class AgentCreateRefused extends Error {
|
|
339
266
|
reason;
|
|
@@ -480,13 +407,32 @@ export function createAgentControlSource(directory) {
|
|
|
480
407
|
if (send !== undefined)
|
|
481
408
|
requestOperations.send = Mycelium.operation({
|
|
482
409
|
replay: "unsafe",
|
|
483
|
-
description: `Choose requestId before sending and reuse it for retries and agents.recall. Delegate follow-up work to an authorized Session
|
|
410
|
+
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, its first 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.`,
|
|
484
411
|
input: sendInput,
|
|
485
|
-
output:
|
|
412
|
+
output: delegationReceiptOutput,
|
|
486
413
|
execute: async (input, context) => {
|
|
487
|
-
const
|
|
414
|
+
const parsed = sendArgs.parse(input);
|
|
415
|
+
const { agentId, sessionId } = parsed;
|
|
416
|
+
let delegation;
|
|
417
|
+
if (parsed.delivery === "steer")
|
|
418
|
+
delegation = parsed;
|
|
419
|
+
else if (sessionId === undefined)
|
|
420
|
+
delegation = {
|
|
421
|
+
agentId,
|
|
422
|
+
requestId: parsed.requestId,
|
|
423
|
+
text: parsed.text,
|
|
424
|
+
delivery: "followUp",
|
|
425
|
+
};
|
|
426
|
+
else
|
|
427
|
+
delegation = {
|
|
428
|
+
agentId,
|
|
429
|
+
sessionId,
|
|
430
|
+
requestId: parsed.requestId,
|
|
431
|
+
text: parsed.text,
|
|
432
|
+
delivery: "followUp",
|
|
433
|
+
};
|
|
488
434
|
await directory
|
|
489
|
-
.checkModel?.({ agentId, sessionId }, context.signal)
|
|
435
|
+
.checkModel?.(sessionId === undefined ? { agentId } : { agentId, sessionId }, context.signal)
|
|
490
436
|
.catch((cause) => admitArchiveRefusal("send", cause));
|
|
491
437
|
if (session === undefined)
|
|
492
438
|
throw new Error("Agent sends require an invoking Session");
|
|
@@ -508,7 +454,10 @@ export function createAgentControlSource(directory) {
|
|
|
508
454
|
};
|
|
509
455
|
if (session.meta !== undefined)
|
|
510
456
|
invocation = { ...invocation, meta: session.meta };
|
|
511
|
-
|
|
457
|
+
const receipt = await send(delegation, session.sessionId, context.signal, invocation).catch((cause) => admitArchiveRefusal("send", cause));
|
|
458
|
+
return delegationReceiptOutput.parse(receipt.status === "accepted"
|
|
459
|
+
? { status: receipt.status, commandId: receipt.commandId }
|
|
460
|
+
: receipt);
|
|
512
461
|
},
|
|
513
462
|
});
|
|
514
463
|
if (recall !== undefined)
|
|
@@ -603,23 +552,15 @@ export function createAgentControlSource(directory) {
|
|
|
603
552
|
output: terminalOutput,
|
|
604
553
|
execute: async ({ agentId, sessionId, commandId, timeoutMs }, context) => {
|
|
605
554
|
const startedAt = Date.now();
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
const terminal = await waitForAgentTerminal(session, commandId, timeoutMs, context.signal, startedAt);
|
|
609
|
-
if (terminal.outcome !== "completed")
|
|
610
|
-
return { settled: terminal, text: undefined };
|
|
611
|
-
return {
|
|
612
|
-
settled: terminal,
|
|
613
|
-
text: finalAssistantText(await session.read({ limit: finalTextReadLimit }), terminal.commandId),
|
|
614
|
-
};
|
|
615
|
-
});
|
|
555
|
+
const settlement = await withAgent(directory, "wait", agentId, context.signal, (agent) => waitForAgentSettlement(agent.session(sessionId), commandId, timeoutMs, context.signal, startedAt));
|
|
556
|
+
const { terminal: settled, content: text } = settlement;
|
|
616
557
|
const result = {
|
|
617
558
|
commandId: settled.commandId,
|
|
618
559
|
outcome: settled.outcome,
|
|
619
560
|
};
|
|
620
561
|
if (settled.diagnostic !== null)
|
|
621
562
|
result.diagnostic = settled.diagnostic.slice(0, 200);
|
|
622
|
-
if (text
|
|
563
|
+
if (settled.outcome === "completed" && text.length > 0) {
|
|
623
564
|
result.text = text.slice(0, finalTextLimit);
|
|
624
565
|
result.truncated = text.length > finalTextLimit;
|
|
625
566
|
}
|
package/dist/client-wire.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import type { CommandSettlement } from "./internal/contracts.js";
|
|
1
2
|
import * as Schema from "effect/Schema";
|
|
2
|
-
import type { Accepted, CancelResult, WithdrawResult, ForkedSession, SessionFrame, SessionPendingPrompt, SessionSnapshot, SessionSummaryPage
|
|
3
|
+
import type { Accepted, DelegationReceipt, Held, CancelResult, WithdrawResult, ForkedSession, SessionFrame, SessionPendingPrompt, SessionSnapshot, SessionSummaryPage } from "./client.js";
|
|
3
4
|
import { ShiitakeError } from "./errors.js";
|
|
4
5
|
import type { ExtensionSlotV0, ExtensionStatusV0, Json } from "./extensions.js";
|
|
5
|
-
/** Existing terminal wire grammar, shared by exact host settlement facts. */
|
|
6
|
-
export declare const SessionTerminalSchema: Schema.Codec<SessionTerminal>;
|
|
7
6
|
export declare const SessionSnapshotSchema: Schema.Struct<{
|
|
8
7
|
readonly sessionId: Schema.String;
|
|
9
8
|
readonly cursor: Schema.NullOr<Schema.String>;
|
|
@@ -185,6 +184,12 @@ export declare const SessionSnapshotSchema: Schema.Struct<{
|
|
|
185
184
|
export declare const SessionFrameSchema: Schema.Codec<SessionFrame>;
|
|
186
185
|
/** Decode a durable command acceptance value at an RPC boundary. */
|
|
187
186
|
export declare const AcceptedSchema: Schema.Codec<Accepted>;
|
|
187
|
+
/** Parse the relay's custody acknowledgment without implying Agent acceptance. */
|
|
188
|
+
export declare const HeldSchema: Schema.Codec<Held>;
|
|
189
|
+
/** A delegated send is accepted by its owner or held in durable mail custody. */
|
|
190
|
+
export declare const DelegationReceiptSchema: Schema.Codec<DelegationReceipt>;
|
|
191
|
+
/** Decode one delegated send's acceptance or durable-custody result. */
|
|
192
|
+
export declare function parseDelegationReceipt(value: string): DelegationReceipt;
|
|
188
193
|
/** Decode the owner's atomic request resolution. */
|
|
189
194
|
export declare function parseWithdrawResult(value: string): WithdrawResult;
|
|
190
195
|
/** Parse one serialized extension status. */
|
|
@@ -201,6 +206,10 @@ export declare function parseCancelResult(value: string): CancelResult;
|
|
|
201
206
|
export declare function parseLatestPendingPrompt(value: string): SessionPendingPrompt | null;
|
|
202
207
|
/** Parse one serialized fork result using Shiitake's public client grammar. */
|
|
203
208
|
export declare function parseForkedSession(value: string): ForkedSession;
|
|
209
|
+
/** Exact command facts shared by Session clients and host fleet transports. */
|
|
210
|
+
export declare const commandSettlementSchema: Schema.Codec<CommandSettlement>;
|
|
211
|
+
/** A timeout is null on the JSON wire and undefined at the client boundary. */
|
|
212
|
+
export declare function parseCommandSettlement(value: string): CommandSettlement | undefined;
|
|
204
213
|
/** Parse one serialized snapshot using Shiitake's public client grammar. */
|
|
205
214
|
export declare function parseSessionSnapshot(value: string): SessionSnapshot;
|
|
206
215
|
/** Parse one serialized frame using Shiitake's public client grammar. */
|
package/dist/client-wire.js
CHANGED
|
@@ -5,7 +5,7 @@ import * as Schema from "effect/Schema";
|
|
|
5
5
|
import { ShiitakeError, ShiitakeErrorWireSchema } from "./errors.js";
|
|
6
6
|
import { JsonValueSchema } from "./internal/json-value.js";
|
|
7
7
|
import { HostMetaSchema } from "./internal/host-meta.js";
|
|
8
|
-
import { StoredPiMessageSchema, ExtensionAdmissionOriginSchema, } from "./internal/woodstock/index.js";
|
|
8
|
+
import { StoredPiMessageSchema, usage, ExtensionAdmissionOriginSchema, } from "./internal/woodstock/index.js";
|
|
9
9
|
const ExtensionOriginSchema = ExtensionAdmissionOriginSchema.mapFields(({ extensionId, correlation }) => ({ extensionId, correlation }));
|
|
10
10
|
const SessionMessageSchema = Schema.Struct({
|
|
11
11
|
requestId: Schema.optionalKey(Schema.String),
|
|
@@ -82,14 +82,9 @@ const ExtensionStatusSchema = Schema.Struct({
|
|
|
82
82
|
stoppedAt: Schema.NullOr(Schema.Number),
|
|
83
83
|
});
|
|
84
84
|
/** Existing terminal wire grammar, shared by exact host settlement facts. */
|
|
85
|
-
|
|
85
|
+
const SessionTerminalSchema = Schema.Struct({
|
|
86
86
|
commandId: Schema.String,
|
|
87
|
-
outcome: Schema.Literals([
|
|
88
|
-
"completed",
|
|
89
|
-
"cancelled",
|
|
90
|
-
"failed",
|
|
91
|
-
"interrupted",
|
|
92
|
-
]),
|
|
87
|
+
outcome: Schema.Literals(["completed", "cancelled", "failed", "interrupted"]),
|
|
93
88
|
sequence: Schema.Int.check(Schema.isGreaterThan(0)),
|
|
94
89
|
diagnostic: Schema.NullOr(Schema.String),
|
|
95
90
|
meta: Schema.optionalKey(HostMetaSchema),
|
|
@@ -169,6 +164,10 @@ export const SessionSnapshotSchema = Schema.Struct({
|
|
|
169
164
|
context: Schema.optionalKey(SessionContextSchema),
|
|
170
165
|
});
|
|
171
166
|
export const SessionFrameSchema = Schema.Union([
|
|
167
|
+
Schema.Struct({
|
|
168
|
+
kind: Schema.Literal("activity"),
|
|
169
|
+
activity: SessionActivitySchema,
|
|
170
|
+
}),
|
|
172
171
|
Schema.Struct({
|
|
173
172
|
kind: Schema.Literal("tool_output"),
|
|
174
173
|
toolOutput: ToolOutputSchema,
|
|
@@ -205,7 +204,21 @@ export const SessionFrameSchema = Schema.Union([
|
|
|
205
204
|
export const AcceptedSchema = Schema.Struct({
|
|
206
205
|
status: Schema.Literal("accepted"),
|
|
207
206
|
commandId: Schema.String,
|
|
207
|
+
sessionId: Schema.optionalKey(Schema.String),
|
|
208
208
|
});
|
|
209
|
+
/** Parse the relay's custody acknowledgment without implying Agent acceptance. */
|
|
210
|
+
export const HeldSchema = Schema.Struct({
|
|
211
|
+
status: Schema.Literal("held"),
|
|
212
|
+
reason: Schema.Literal("recipient_offline"),
|
|
213
|
+
message: Schema.Literal("waiting: recipient offline"),
|
|
214
|
+
});
|
|
215
|
+
/** A delegated send is accepted by its owner or held in durable mail custody. */
|
|
216
|
+
export const DelegationReceiptSchema = Schema.Union([AcceptedSchema, HeldSchema]);
|
|
217
|
+
const decodeDelegationReceipt = Schema.decodeUnknownSync(Schema.fromJsonString(DelegationReceiptSchema), { onExcessProperty: "error" });
|
|
218
|
+
/** Decode one delegated send's acceptance or durable-custody result. */
|
|
219
|
+
export function parseDelegationReceipt(value) {
|
|
220
|
+
return decodeDelegationReceipt(value);
|
|
221
|
+
}
|
|
209
222
|
const UnknownRequestSchema = Schema.Struct({
|
|
210
223
|
status: Schema.Literal("unknown"),
|
|
211
224
|
});
|
|
@@ -266,6 +279,18 @@ export function parseLatestPendingPrompt(value) {
|
|
|
266
279
|
export function parseForkedSession(value) {
|
|
267
280
|
return decodeForkedSession(value);
|
|
268
281
|
}
|
|
282
|
+
/** Exact command facts shared by Session clients and host fleet transports. */
|
|
283
|
+
export const commandSettlementSchema = Schema.Struct({
|
|
284
|
+
acceptedSender: Schema.NullOr(Schema.String),
|
|
285
|
+
terminal: SessionTerminalSchema,
|
|
286
|
+
content: Schema.String,
|
|
287
|
+
usage: Schema.Array(Schema.Struct({ provider: Schema.String, model: Schema.String, usage })),
|
|
288
|
+
toolCalls: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
289
|
+
});
|
|
290
|
+
/** A timeout is null on the JSON wire and undefined at the client boundary. */
|
|
291
|
+
export function parseCommandSettlement(value) {
|
|
292
|
+
return (Schema.decodeUnknownSync(Schema.fromJsonString(Schema.NullOr(commandSettlementSchema)))(value) ?? undefined);
|
|
293
|
+
}
|
|
269
294
|
/** Parse one serialized snapshot using Shiitake's public client grammar. */
|
|
270
295
|
export function parseSessionSnapshot(value) {
|
|
271
296
|
return decodeSnapshot(value);
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { SessionAttention } from "./session-attention.js";
|
|
2
|
+
import type { CommandSettlement } from "./internal/contracts.js";
|
|
3
|
+
export type { CommandSettlement } from "./internal/contracts.js";
|
|
2
4
|
export { type SessionAttention } from "./session-attention.js";
|
|
3
5
|
import type { SandboxFailureEvent } from "@fungi.computer/mycelium";
|
|
4
6
|
import type { ModelPreference } from "./internal/model-preference.js";
|
|
@@ -7,7 +9,7 @@ import type { ExtensionSlotV0, ExtensionStatusV0, Json } from "./extensions.js";
|
|
|
7
9
|
import type { AbortCommand, BranchCommand, CompactCommand, OperatorCommand, TitleCommand, CancelCommand, ForkCommand, PromptCommand, PromptDelivery, SessionCommand } from "./internal/client-boundary.js";
|
|
8
10
|
export { createShiitakeClient, type ShiitakeClientOptions, } from "./remote-client.js";
|
|
9
11
|
export { bindAgentExtension, bindSessionExtension, type BoundAgentExtensionV0, type BoundCallV0, type BoundSessionExtensionV0, type ExtensionViewV0, type ScopedCommandsV0, } from "./extension-client.js";
|
|
10
|
-
export { AcceptedSchema, parseAccepted, parseCancelResult, parseLatestPendingPrompt, parseWithdrawResult, parseForkedSession, parseSessionFrame, parseSessionSnapshot, parseSessionSummaryPage, parseShiitakeError, } from "./client-wire.js";
|
|
12
|
+
export { AcceptedSchema, DelegationReceiptSchema, HeldSchema, parseAccepted, parseDelegationReceipt, parseCancelResult, parseLatestPendingPrompt, parseWithdrawResult, parseForkedSession, parseSessionFrame, parseSessionSnapshot, parseSessionSummaryPage, parseShiitakeError, } from "./client-wire.js";
|
|
11
13
|
export type { AbortCommand, BranchCommand, CompactCommand, CancelCommand, ForkCommand, OperatorCommand, PromptCommand, PromptDelivery, SessionCommand, TitleCommand, } from "./internal/client-boundary.js";
|
|
12
14
|
/** Semantic acknowledgement returned after one durable acceptance commit. */
|
|
13
15
|
export type Accepted = Readonly<{
|
|
@@ -15,7 +17,17 @@ export type Accepted = Readonly<{
|
|
|
15
17
|
status: "accepted";
|
|
16
18
|
/** Owner-issued durable identity for the accepted work. */
|
|
17
19
|
commandId: string;
|
|
20
|
+
/** The recipient's Session, when this is delegated mail acceptance. */
|
|
21
|
+
sessionId?: string;
|
|
18
22
|
}>;
|
|
23
|
+
/** Durable relay custody, not acceptance of a command by the recipient. */
|
|
24
|
+
export type Held = Readonly<{
|
|
25
|
+
status: "held";
|
|
26
|
+
reason: "recipient_offline";
|
|
27
|
+
message: "waiting: recipient offline";
|
|
28
|
+
}>;
|
|
29
|
+
/** A delegation send can be accepted by the Agent or held by its relay. */
|
|
30
|
+
export type DelegationReceipt = Accepted | Held;
|
|
19
31
|
/**
|
|
20
32
|
* Cancelling found nothing left to cancel: the command is unknown to this
|
|
21
33
|
* Session, has already started (for an `onlyIfPending` cancel) or has already
|
|
@@ -311,11 +323,16 @@ export type SessionSnapshot = Readonly<{
|
|
|
311
323
|
extensions: SessionExtensionSlots;
|
|
312
324
|
/** Open requests for human attention from extensions. */
|
|
313
325
|
attention: readonly SessionAttention[];
|
|
314
|
-
/** Context use at the
|
|
326
|
+
/** Context use at the tip; before reads carry this tip figure too, and it
|
|
327
|
+
* is absent before the Session has context. */
|
|
315
328
|
context?: SessionContext;
|
|
316
329
|
}>;
|
|
317
330
|
/** One bounded replay or live frame. */
|
|
318
331
|
export type SessionFrame = Readonly<{
|
|
332
|
+
/** Process-local activity change for the Session's current queue head. */
|
|
333
|
+
kind: "activity";
|
|
334
|
+
activity: SessionActivity;
|
|
335
|
+
}> | Readonly<{
|
|
319
336
|
kind: "tool_output";
|
|
320
337
|
toolOutput: readonly SessionToolOutput[];
|
|
321
338
|
activity: SessionActivity;
|
|
@@ -344,7 +361,12 @@ export type SessionFrame = Readonly<{
|
|
|
344
361
|
extensions: SessionExtensionSlots;
|
|
345
362
|
/** Open requests for human attention from extensions. */
|
|
346
363
|
attention: readonly SessionAttention[];
|
|
347
|
-
/**
|
|
364
|
+
/**
|
|
365
|
+
* Context use at the Session tip when the update moved history and is
|
|
366
|
+
* the final page; live pages with more to come and non-final replay pages
|
|
367
|
+
* carry no figure. Absent before the Session has context; absent figures
|
|
368
|
+
* leave the last one in place.
|
|
369
|
+
*/
|
|
348
370
|
context?: SessionContext;
|
|
349
371
|
}> | Readonly<{
|
|
350
372
|
/** Bounded process-local assistant progress. */
|
|
@@ -425,6 +447,15 @@ export type ShiitakeSession = Readonly<{
|
|
|
425
447
|
}>;
|
|
426
448
|
/** Read a bounded durable baseline. */
|
|
427
449
|
read: (input?: ReadOptions) => Promise<SessionSnapshot>;
|
|
450
|
+
/**
|
|
451
|
+
* Read one exact command's settlement, independent of the selected lineage.
|
|
452
|
+
* Wait at most timeoutMs (0–30000, default 0) for a settlement hint.
|
|
453
|
+
* Timeout returns undefined; abort releases only the read, never the work.
|
|
454
|
+
*/
|
|
455
|
+
readSettlement: (commandId: string, options?: Readonly<{
|
|
456
|
+
timeoutMs?: number;
|
|
457
|
+
signal?: AbortSignal;
|
|
458
|
+
}>) => Promise<CommandSettlement | undefined>;
|
|
428
459
|
/** Subscribe to strict-after replay and live frames. */
|
|
429
460
|
subscribe: (input: SubscribeOptions) => AsyncIterable<SessionFrame>;
|
|
430
461
|
/** Open a race-safe baseline plus live changes resource. */
|
package/dist/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export {} from "./session-attention.js";
|
|
2
2
|
export { createShiitakeClient, } from "./remote-client.js";
|
|
3
3
|
export { bindAgentExtension, bindSessionExtension, } from "./extension-client.js";
|
|
4
|
-
export { AcceptedSchema, parseAccepted, parseCancelResult, parseLatestPendingPrompt, parseWithdrawResult, parseForkedSession, parseSessionFrame, parseSessionSnapshot, parseSessionSummaryPage, parseShiitakeError, } from "./client-wire.js";
|
|
4
|
+
export { AcceptedSchema, DelegationReceiptSchema, HeldSchema, parseAccepted, parseDelegationReceipt, parseCancelResult, parseLatestPendingPrompt, parseWithdrawResult, parseForkedSession, parseSessionFrame, parseSessionSnapshot, parseSessionSummaryPage, parseShiitakeError, } from "./client-wire.js";
|
package/dist/cloudflare.d.ts
CHANGED
|
@@ -133,7 +133,9 @@ export type CloudflareShiitake = ShiitakeOwnerAgent & Readonly<{
|
|
|
133
133
|
* Admit host-authorized mail, preserving its parent address and opaque
|
|
134
134
|
* metadata. `reply` is the host's authentication of a delegated reply.
|
|
135
135
|
*/
|
|
136
|
-
acceptMail: (mail: BirdDogMail, meta?: Json, reply?: VerifiedAgentReply) =>
|
|
136
|
+
acceptMail: (mail: BirdDogMail, meta?: Json, reply?: VerifiedAgentReply, stageAcceptedNotice?: (mail: BirdDogMail) => void,
|
|
137
|
+
/** Grants for a new Session, written in the acceptance transaction. */
|
|
138
|
+
sessionGrantDelivery?: (sessionId: string) => GrantsDelivery) => Promise<Accepted>;
|
|
137
139
|
/** Deliver the native owner alarm to the bound runtime. */
|
|
138
140
|
alarm: () => Promise<void>;
|
|
139
141
|
/** Snapshot actual live executors, independent of instruction leases. */
|