@fungi.computer/shiitake 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +59 -21
  2. package/dist/agent-control.d.ts +94 -2
  3. package/dist/agent-control.js +147 -26
  4. package/dist/client-entry.d.ts +20 -0
  5. package/dist/client-entry.js +5 -0
  6. package/dist/client-visitor.d.ts +33 -0
  7. package/dist/client-visitor.js +231 -0
  8. package/dist/client-wire.d.ts +9 -0
  9. package/dist/client-wire.js +24 -3
  10. package/dist/client.d.ts +27 -6
  11. package/dist/client.js +23 -0
  12. package/dist/cloudflare.d.ts +20 -0
  13. package/dist/cloudflare.js +4 -1
  14. package/dist/delegation.d.ts +79 -53
  15. package/dist/delegation.js +26 -21
  16. package/dist/effect.d.ts +26 -1
  17. package/dist/effect.js +14 -1
  18. package/dist/errors.d.ts +2 -2
  19. package/dist/errors.js +2 -0
  20. package/dist/extensions.d.ts +22 -3
  21. package/dist/extensions.js +1 -1
  22. package/dist/fleet.d.ts +25 -2
  23. package/dist/fleet.js +108 -24
  24. package/dist/hosted.js +3 -0
  25. package/dist/http-routes.d.ts +23 -9
  26. package/dist/http.d.ts +1 -0
  27. package/dist/http.js +1 -0
  28. package/dist/index.d.ts +1 -1
  29. package/dist/internal/accepted-run-executor.js +2 -1
  30. package/dist/internal/admission-door.d.ts +6 -1
  31. package/dist/internal/admission-door.js +33 -3
  32. package/dist/internal/contracts.d.ts +19 -1
  33. package/dist/internal/coordinator.js +115 -52
  34. package/dist/internal/effect-client.js +1 -1
  35. package/dist/internal/effect-contracts.d.ts +9 -2
  36. package/dist/internal/extension-command-registry.d.ts +2 -1
  37. package/dist/internal/extension-command-registry.js +3 -1
  38. package/dist/internal/extension-runtime.d.ts +3 -1
  39. package/dist/internal/extension-runtime.js +18 -8
  40. package/dist/internal/model-acquisition.js +2 -0
  41. package/dist/internal/observation-kernel.d.ts +2 -0
  42. package/dist/internal/observation-kernel.js +17 -6
  43. package/dist/internal/promise-facade.js +2 -0
  44. package/dist/internal/run-scope.d.ts +14 -0
  45. package/dist/internal/run-scope.js +58 -15
  46. package/dist/internal/runtime-coordinator.d.ts +14 -0
  47. package/dist/internal/runtime-coordinator.js +60 -11
  48. package/dist/internal/runtime-core.d.ts +3 -0
  49. package/dist/internal/runtime-core.js +1 -0
  50. package/dist/internal/runtime-reconciliation.js +123 -25
  51. package/dist/internal/session-context.d.ts +5 -5
  52. package/dist/internal/session-context.js +80 -83
  53. package/dist/internal/session-status.d.ts +2 -2
  54. package/dist/internal/session-status.js +5 -3
  55. package/dist/internal/woodstock/agent-reply.d.ts +5 -0
  56. package/dist/internal/woodstock/agent-reply.js +3 -0
  57. package/dist/internal/woodstock/client-projection.js +13 -11
  58. package/dist/internal/woodstock/fleet-store.d.ts +3 -2
  59. package/dist/internal/woodstock/fleet-store.js +24 -4
  60. package/dist/internal/woodstock/index.d.ts +7 -3
  61. package/dist/internal/woodstock/index.js +23 -6
  62. package/dist/internal/woodstock/progress.d.ts +11 -1
  63. package/dist/internal/woodstock/progress.js +126 -25
  64. package/dist/internal/woodstock/session-delegator.d.ts +13 -0
  65. package/dist/internal/woodstock/session-delegator.js +44 -0
  66. package/dist/internal/woodstock/session-entry.d.ts +12 -2
  67. package/dist/internal/woodstock/session-store.d.ts +16 -2
  68. package/dist/internal/woodstock/session-store.js +349 -66
  69. package/dist/internal/woodstock/work-senders.d.ts +32 -0
  70. package/dist/internal/woodstock/work-senders.js +70 -0
  71. package/dist/node.js +11 -2
  72. package/dist/remote-client.js +7 -2
  73. package/dist/server-visitor-stream.d.ts +13 -0
  74. package/dist/server-visitor-stream.js +124 -0
  75. package/dist/session-attention.d.ts +30 -0
  76. package/dist/session-attention.js +14 -0
  77. package/docs/api/agent-control.md +212 -15
  78. package/docs/api/client-entry.md +3 -0
  79. package/docs/api/client.md +209 -5
  80. package/docs/api/cloudflare.md +23 -1
  81. package/docs/api/delegation.md +284 -77
  82. package/docs/api/effect.md +100 -12
  83. package/docs/api/extensions.md +73 -12
  84. package/docs/api/fleet.md +138 -25
  85. package/docs/api/http.md +28 -0
  86. package/docs/api/index.md +43 -17
  87. package/docs/api/testing.md +9 -7
  88. package/package.json +10 -6
@@ -0,0 +1,32 @@
1
+ import { type AttentionRecipient } from "../../session-attention.js";
2
+ import type { SqliteOwner } from "../contracts.js";
3
+ /** One open item of a Session that waits on the Agent that sent its work. */
4
+ export type WaitingOnYou = Readonly<{
5
+ itemId: string;
6
+ kind: string;
7
+ summary: string;
8
+ }>;
9
+ /**
10
+ * Woodstock records which Agent Session sent each accepted run, beside the
11
+ * run's admission. Attention that an extension raises during a run waits on
12
+ * this sender first; a run no Agent sent has no row.
13
+ */
14
+ export declare function prepareWorkSenders(owner: SqliteOwner): void;
15
+ /** Record the sender of one accepted run; a replay writes the same row. */
16
+ export declare function recordWorkSender(owner: SqliteOwner, sessionId: string, workId: string, sender: AttentionRecipient): void;
17
+ /** The Agent Session that sent this run, or undefined when none did. */
18
+ export declare function readWorkSender(owner: SqliteOwner, sessionId: string, workId: string): AttentionRecipient | undefined;
19
+ /**
20
+ * The Session's open attention that waits on the sender of this run, newest
21
+ * first as stored, bounded. Escalated items wait on someone else and are
22
+ * absent; a run no Agent sent names none.
23
+ */
24
+ export declare function readWaitingOnSender(owner: SqliteOwner, sessionId: string, workId: string): readonly WaitingOnYou[];
25
+ /**
26
+ * The Session's open attention that waits on any of these Agent Sessions,
27
+ * newest first as stored, bounded: what a reply or report to them names.
28
+ */
29
+ export declare function readWaitingOn(owner: SqliteOwner, sessionId: string, recipients: readonly Readonly<{
30
+ agentId: string;
31
+ sessionId: string;
32
+ }>[]): readonly WaitingOnYou[];
@@ -0,0 +1,70 @@
1
+ import * as Schema from "effect/Schema";
2
+ import { AttentionRecipientSchema, } from "../../session-attention.js";
3
+ import { readSessionAttention } from "./session-attention.js";
4
+ /** The most open items one settlement names as waiting on its sender. */
5
+ const maximumWaitingOnYou = 8;
6
+ const decodeRow = Schema.decodeUnknownSync(Schema.Struct({
7
+ agent_id: Schema.String,
8
+ sender_session_id: Schema.String,
9
+ name: Schema.String,
10
+ }));
11
+ const decodeRecipient = Schema.decodeUnknownSync(AttentionRecipientSchema);
12
+ /**
13
+ * Woodstock records which Agent Session sent each accepted run, beside the
14
+ * run's admission. Attention that an extension raises during a run waits on
15
+ * this sender first; a run no Agent sent has no row.
16
+ */
17
+ export function prepareWorkSenders(owner) {
18
+ owner.sql.exec(`CREATE TABLE IF NOT EXISTS session_work_senders (
19
+ session_id TEXT NOT NULL,
20
+ work_id TEXT NOT NULL,
21
+ agent_id TEXT NOT NULL,
22
+ sender_session_id TEXT NOT NULL,
23
+ name TEXT NOT NULL,
24
+ PRIMARY KEY (session_id, work_id)
25
+ )`);
26
+ }
27
+ /** Record the sender of one accepted run; a replay writes the same row. */
28
+ export function recordWorkSender(owner, sessionId, workId, sender) {
29
+ owner.sql.exec(`INSERT OR IGNORE INTO session_work_senders
30
+ (session_id, work_id, agent_id, sender_session_id, name)
31
+ VALUES (?, ?, ?, ?, ?)`, sessionId, workId, sender.agentId, sender.sessionId, sender.name);
32
+ }
33
+ /** The Agent Session that sent this run, or undefined when none did. */
34
+ export function readWorkSender(owner, sessionId, workId) {
35
+ const raw = owner.sql
36
+ .exec("SELECT agent_id, sender_session_id, name FROM session_work_senders WHERE session_id = ? AND work_id = ?", sessionId, workId)
37
+ .toArray()[0];
38
+ if (raw === undefined)
39
+ return undefined;
40
+ const row = decodeRow(raw);
41
+ return decodeRecipient({
42
+ agentId: row.agent_id,
43
+ sessionId: row.sender_session_id,
44
+ name: row.name,
45
+ });
46
+ }
47
+ /**
48
+ * The Session's open attention that waits on the sender of this run, newest
49
+ * first as stored, bounded. Escalated items wait on someone else and are
50
+ * absent; a run no Agent sent names none.
51
+ */
52
+ export function readWaitingOnSender(owner, sessionId, workId) {
53
+ const sender = readWorkSender(owner, sessionId, workId);
54
+ return sender === undefined ? [] : readWaitingOn(owner, sessionId, [sender]);
55
+ }
56
+ /**
57
+ * The Session's open attention that waits on any of these Agent Sessions,
58
+ * newest first as stored, bounded: what a reply or report to them names.
59
+ */
60
+ export function readWaitingOn(owner, sessionId, recipients) {
61
+ return readSessionAttention(owner, sessionId)
62
+ .filter((item) => recipients.some((recipient) => item.waitingOn?.agentId === recipient.agentId &&
63
+ item.waitingOn.sessionId === recipient.sessionId))
64
+ .slice(0, maximumWaitingOnYou)
65
+ .map((item) => ({
66
+ itemId: item.id,
67
+ kind: item.kind,
68
+ summary: item.summary,
69
+ }));
70
+ }
package/dist/node.js CHANGED
@@ -50,8 +50,13 @@ export function nodeRuntimeHost(observeFailure) {
50
50
  wakeTimer = setTimeout(() => {
51
51
  wakeTimer = undefined;
52
52
  wakeAt = undefined;
53
+ // A capped wait may finish before the actual deadline.
54
+ if (at > Date.now()) {
55
+ scheduleWake(at);
56
+ return;
57
+ }
53
58
  void bound?.requestWake().catch(reportFailure);
54
- }, Math.max(0, at - Date.now()));
59
+ }, Math.min(2_147_483_647, Math.max(0, at - Date.now())));
55
60
  };
56
61
  const runtime = Object.freeze({
57
62
  bind: ({ reconcile, pendingWork }) => {
@@ -133,7 +138,11 @@ export function nodeRuntimeHost(observeFailure) {
133
138
  return Object.freeze({
134
139
  runtime,
135
140
  scheduleWake,
136
- close: () => bound?.close() ?? Promise.resolve(),
141
+ close: () => {
142
+ scheduleWake(null);
143
+ closed = true;
144
+ return bound?.close() ?? Promise.resolve();
145
+ },
137
146
  });
138
147
  }
139
148
  /**
@@ -72,7 +72,12 @@ function remoteEvents(url, clientClosed, sources, eventSource) {
72
72
  closed: false,
73
73
  };
74
74
  const emit = (kind, event) => {
75
- Queue.offerUnsafe(queue, { kind, event });
75
+ if (!Queue.offerUnsafe(queue, { kind, event })) {
76
+ // A dropped frame breaks this watch's ordering. Fail instead
77
+ // of delivering an incomplete history to the client.
78
+ Queue.failCauseUnsafe(queue, Cause.fail(unavailable("Session stream client fell behind")));
79
+ Effect.runSync(closeRemoteSource(active, sources));
80
+ }
76
81
  };
77
82
  active.source.addEventListener("snapshot", (event) => {
78
83
  emit("snapshot", event);
@@ -91,7 +96,7 @@ function remoteEvents(url, clientClosed, sources, eventSource) {
91
96
  return active;
92
97
  },
93
98
  catch: () => unavailable("Session stream could not open"),
94
- }), (active) => closeRemoteSource(active, sources))).pipe(Stream.mapEffect(decodeRemoteEvent), Stream.interruptWhen(clientClosed.await));
99
+ }), (active) => closeRemoteSource(active, sources)), { bufferSize: 256, strategy: "dropping" }).pipe(Stream.mapEffect(decodeRemoteEvent), Stream.interruptWhen(clientClosed.await));
95
100
  }
96
101
  function sessionUrl(baseUrl, sessionId) {
97
102
  return `${baseUrl}/sessions/${encodeURIComponent(sessionId)}`;
@@ -0,0 +1,13 @@
1
+ import type { SessionSnapshot, SessionWatch } from "./client.js";
2
+ export type VisitorStreamError = "channel_visitor_stream_expiry_failed" | "channel_visitor_stream_close_failed" | "channel_visitor_stream_heartbeat_failed" | "channel_visitor_stream_failed";
3
+ export declare function visitorStream(options: Readonly<{
4
+ watch: SessionWatch;
5
+ visitorId: string;
6
+ filter: (snapshot: SessionSnapshot) => SessionSnapshot;
7
+ expiresAt: number;
8
+ now: () => number;
9
+ release: () => void;
10
+ canRead: () => Promise<boolean>;
11
+ read: () => Promise<SessionSnapshot>;
12
+ onError: (event: VisitorStreamError, cause: unknown) => void;
13
+ }>): ReadableStream<Uint8Array>;
@@ -0,0 +1,124 @@
1
+ import { SSEStreamingApi } from "hono/streaming";
2
+ import { visitorSnapshot } from "./client-visitor.js";
3
+ export function visitorStream(options) {
4
+ const { readable, writable } = new TransformStream();
5
+ const stream = new SSEStreamingApi(writable, readable);
6
+ let closing;
7
+ let ending = false;
8
+ let heartbeat;
9
+ const expiry = setTimeout(() => {
10
+ void close().catch((cause) => {
11
+ options.onError("channel_visitor_stream_expiry_failed", cause);
12
+ });
13
+ }, Math.max(0, options.expiresAt - options.now()));
14
+ const close = () => {
15
+ if (closing !== undefined)
16
+ return closing;
17
+ ending = true;
18
+ clearTimeout(expiry);
19
+ clearTimeout(heartbeat);
20
+ options.release();
21
+ closing = Promise.resolve().then(async () => {
22
+ stream.abort();
23
+ try {
24
+ await options.watch.close();
25
+ }
26
+ finally {
27
+ await stream.close();
28
+ }
29
+ });
30
+ return closing;
31
+ };
32
+ stream.onAbort(() => {
33
+ void close().catch((cause) => {
34
+ options.onError("channel_visitor_stream_close_failed", cause);
35
+ });
36
+ });
37
+ const admitted = async () => !ending &&
38
+ options.now() < options.expiresAt &&
39
+ (await options.canRead()) &&
40
+ !ending;
41
+ const beat = async () => {
42
+ if (!(await admitted())) {
43
+ await close();
44
+ return;
45
+ }
46
+ await stream.write(": heartbeat\n\n");
47
+ if (!ending)
48
+ heartbeat = setTimeout(() => {
49
+ void beat().catch((cause) => {
50
+ options.onError("channel_visitor_stream_heartbeat_failed", cause);
51
+ void close().catch((closeCause) => {
52
+ options.onError("channel_visitor_stream_close_failed", closeCause);
53
+ });
54
+ });
55
+ }, 30_000);
56
+ };
57
+ const publish = async (snapshot) => {
58
+ if (!(await admitted()))
59
+ return false;
60
+ const baseline = visitorSnapshot(options.filter(snapshot), options.visitorId);
61
+ await stream.writeSSE({ event: "session", data: JSON.stringify(baseline) });
62
+ return !ending;
63
+ };
64
+ const pump = async () => {
65
+ try {
66
+ let snapshot = options.watch.snapshot;
67
+ if (!(await publish(snapshot)))
68
+ return;
69
+ heartbeat = setTimeout(() => {
70
+ void beat().catch((cause) => {
71
+ options.onError("channel_visitor_stream_heartbeat_failed", cause);
72
+ void close().catch((closeCause) => {
73
+ options.onError("channel_visitor_stream_close_failed", closeCause);
74
+ });
75
+ });
76
+ }, 30_000);
77
+ for await (const frame of options.watch.changes) {
78
+ if (ending)
79
+ break;
80
+ if (frame.kind === "tool_output")
81
+ continue;
82
+ if (frame.kind === "progress" || frame.kind === "activity") {
83
+ if (!(await admitted()))
84
+ break;
85
+ snapshot = { ...snapshot, activity: frame.activity };
86
+ const baseline = visitorSnapshot(options.filter(snapshot), options.visitorId);
87
+ if (baseline.activity.queue.state === "idle") {
88
+ if (frame.kind === "activity")
89
+ await stream.writeSSE({
90
+ event: "session",
91
+ data: JSON.stringify(baseline),
92
+ });
93
+ continue;
94
+ }
95
+ await stream.writeSSE({
96
+ event: "frame",
97
+ data: JSON.stringify(frame.kind === "activity"
98
+ ? { kind: "activity", activity: baseline.activity }
99
+ : {
100
+ ...frame,
101
+ progress: {
102
+ ...frame.progress,
103
+ thinking: { offset: 0, append: "" },
104
+ },
105
+ activity: baseline.activity,
106
+ }),
107
+ });
108
+ continue;
109
+ }
110
+ snapshot =
111
+ frame.kind === "reset" ? frame.baseline : await options.read();
112
+ if (!(await publish(snapshot)))
113
+ break;
114
+ }
115
+ }
116
+ finally {
117
+ await close();
118
+ }
119
+ };
120
+ void pump().catch((cause) => {
121
+ options.onError("channel_visitor_stream_failed", cause);
122
+ });
123
+ return stream.responseReadable.pipeThrough(new TransformStream());
124
+ }
@@ -1,7 +1,21 @@
1
1
  import * as Schema from "effect/Schema";
2
+ /**
3
+ * The Agent Session that sent the work an item belongs to. The host derives it
4
+ * from the accepted mail; it is the only party the item waits on until it is
5
+ * escalated.
6
+ */
7
+ export declare const AttentionRecipientSchema: Schema.Struct<{
8
+ readonly agentId: Schema.NonEmptyString;
9
+ readonly sessionId: Schema.NonEmptyString;
10
+ readonly name: Schema.NonEmptyString;
11
+ }>;
12
+ /** Decoded Agent Session an attention item waits on. */
13
+ export type AttentionRecipient = typeof AttentionRecipientSchema.Type;
2
14
  /**
3
15
  * An extension-owned request for human attention; expiry is owned by its timer.
4
16
  * Items marked `inbox: false` remain visible without changing the inbox state.
17
+ * An item with `waitingOn` waits on that Agent first and is never a person's
18
+ * inbox item until it is escalated past every Agent.
5
19
  */
6
20
  export declare const AttentionItemSchema: Schema.Struct<{
7
21
  readonly id: Schema.NonEmptyString;
@@ -11,6 +25,12 @@ export declare const AttentionItemSchema: Schema.Struct<{
11
25
  readonly expiresAt: Schema.optional<Schema.Int>;
12
26
  /** This item does not change the Session's inbox state; omission does. */
13
27
  readonly inbox: Schema.optional<Schema.Literal<false>>;
28
+ /** The Agent this item waits on; absent means a person. */
29
+ readonly waitingOn: Schema.optional<Schema.Struct<{
30
+ readonly agentId: Schema.NonEmptyString;
31
+ readonly sessionId: Schema.NonEmptyString;
32
+ readonly name: Schema.NonEmptyString;
33
+ }>>;
14
34
  }>;
15
35
  /** Input to the granted attention door, scoped to the calling extension. */
16
36
  export type AttentionItem = typeof AttentionItemSchema.Type;
@@ -23,6 +43,11 @@ export declare const SessionAttentionSchema: Schema.Struct<{
23
43
  readonly since: Schema.Int;
24
44
  readonly expiresAt: Schema.optional<Schema.Int>;
25
45
  readonly inbox: Schema.optional<Schema.Literal<false>>;
46
+ readonly waitingOn: Schema.optional<Schema.Struct<{
47
+ readonly agentId: Schema.NonEmptyString;
48
+ readonly sessionId: Schema.NonEmptyString;
49
+ readonly name: Schema.NonEmptyString;
50
+ }>>;
26
51
  }>;
27
52
  /** One open attention item in a Session observation. */
28
53
  export type SessionAttention = typeof SessionAttentionSchema.Type;
@@ -39,4 +64,9 @@ export declare const SessionAttentionListSchema: Schema.$Array<Schema.Struct<{
39
64
  readonly since: Schema.Int;
40
65
  readonly expiresAt: Schema.optional<Schema.Int>;
41
66
  readonly inbox: Schema.optional<Schema.Literal<false>>;
67
+ readonly waitingOn: Schema.optional<Schema.Struct<{
68
+ readonly agentId: Schema.NonEmptyString;
69
+ readonly sessionId: Schema.NonEmptyString;
70
+ readonly name: Schema.NonEmptyString;
71
+ }>>;
42
72
  }>>;
@@ -1,9 +1,21 @@
1
1
  import * as Schema from "effect/Schema";
2
2
  const identity = Schema.NonEmptyString.check(Schema.isMaxLength(256));
3
3
  const instant = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
4
+ /**
5
+ * The Agent Session that sent the work an item belongs to. The host derives it
6
+ * from the accepted mail; it is the only party the item waits on until it is
7
+ * escalated.
8
+ */
9
+ export const AttentionRecipientSchema = Schema.Struct({
10
+ agentId: identity,
11
+ sessionId: identity,
12
+ name: identity,
13
+ });
4
14
  /**
5
15
  * An extension-owned request for human attention; expiry is owned by its timer.
6
16
  * Items marked `inbox: false` remain visible without changing the inbox state.
17
+ * An item with `waitingOn` waits on that Agent first and is never a person's
18
+ * inbox item until it is escalated past every Agent.
7
19
  */
8
20
  export const AttentionItemSchema = Schema.Struct({
9
21
  id: identity,
@@ -13,6 +25,8 @@ export const AttentionItemSchema = Schema.Struct({
13
25
  expiresAt: Schema.optional(instant),
14
26
  /** This item does not change the Session's inbox state; omission does. */
15
27
  inbox: Schema.optional(Schema.Literal(false)),
28
+ /** The Agent this item waits on; absent means a person. */
29
+ waitingOn: Schema.optional(AttentionRecipientSchema),
16
30
  });
17
31
  /** Public identity includes the owner so two extensions can use the same id. */
18
32
  export const SessionAttentionSchema = AttentionItemSchema.mapFields((fields) => ({
@@ -31,7 +31,7 @@ new AgentArchiveRefused(reason, message): AgentArchiveRefused;
31
31
 
32
32
  | Parameter | Type |
33
33
  | --------- | ---------------------------------------------------------------------------------------------- |
34
- | `reason` | \| `"running"` \| `"pending"` \| `"archived"` \| `"not_creator"` \| `"self"` \| `"unreadable"` |
34
+ | `reason` | \| `"archived"` \| `"running"` \| `"pending"` \| `"not_creator"` \| `"self"` \| `"unreadable"` |
35
35
  | `message` | `string` |
36
36
 
37
37
  ###### Returns
@@ -62,9 +62,9 @@ Error.name;
62
62
 
63
63
  ```ts
64
64
  readonly reason:
65
+ | "archived"
65
66
  | "running"
66
67
  | "pending"
67
- | "archived"
68
68
  | "not_creator"
69
69
  | "self"
70
70
  | "unreadable";
@@ -75,8 +75,9 @@ readonly reason:
75
75
  ### AgentCreateRefused
76
76
 
77
77
  A host's refusal of an Agent creating an Agent: the creator asked to give the
78
- created Agent more than it holds, or the host cannot tell which Agent is
79
- creating. It reaches the caller as an `agents.create` failure.
78
+ created Agent more than it holds, the host cannot tell which Agent is creating,
79
+ or the request id belongs to another creating Agent or Session. It reaches the
80
+ caller as an `agents.create` failure.
80
81
 
81
82
  #### Extends
82
83
 
@@ -92,10 +93,10 @@ new AgentCreateRefused(reason, message): AgentCreateRefused;
92
93
 
93
94
  ###### Parameters
94
95
 
95
- | Parameter | Type |
96
- | --------- | ------------------------------------- |
97
- | `reason` | `"exceeds_creator"` \| `"no_creator"` |
98
- | `message` | `string` |
96
+ | Parameter | Type |
97
+ | --------- | ------------------------------------------------------------- |
98
+ | `reason` | `"exceeds_creator"` \| `"no_creator"` \| `"request_conflict"` |
99
+ | `message` | `string` |
99
100
 
100
101
  ###### Returns
101
102
 
@@ -124,11 +125,62 @@ Error.name;
124
125
  ##### reason
125
126
 
126
127
  ```ts
127
- readonly reason: "exceeds_creator" | "no_creator";
128
+ readonly reason: "exceeds_creator" | "no_creator" | "request_conflict";
128
129
  ```
129
130
 
130
131
  ## Type Aliases
131
132
 
133
+ ### AgentControlAnswerInput
134
+
135
+ ```ts
136
+ type AgentControlAnswerInput = Readonly<{
137
+ agentId: string;
138
+ sessionId: string;
139
+ askId: string;
140
+ requestId: string;
141
+ answers: MyceliumJsonValue;
142
+ }>;
143
+ ```
144
+
145
+ Sender-authenticated submission for one child Session's routed Ask.
146
+
147
+ ---
148
+
149
+ ### AgentControlEscalateInput
150
+
151
+ ```ts
152
+ type AgentControlEscalateInput = Readonly<{
153
+ agentId: string;
154
+ sessionId: string;
155
+ itemId: string;
156
+ note?: string;
157
+ }>;
158
+ ```
159
+
160
+ Sender-authenticated escalation of one item a child Session left waiting.
161
+
162
+ ---
163
+
164
+ ### AgentControlAnswerOutput
165
+
166
+ ```ts
167
+ type AgentControlAnswerOutput = z.infer<typeof answerOutput>;
168
+ ```
169
+
170
+ Typed outcome of a sender's answer to a child Ask.
171
+
172
+ ---
173
+
174
+ ### AgentControlEscalateOutput
175
+
176
+ ```ts
177
+ type AgentControlEscalateOutput = z.infer<typeof escalateOutput>;
178
+ ```
179
+
180
+ Typed outcome of a sender's escalation of a child's item.
181
+
182
+ ---
183
+
132
184
  ### ArchiveRefusalReason
133
185
 
134
186
  ```ts
@@ -173,10 +225,17 @@ One host-offered model usable as an Agent's create or setModel choice.
173
225
  ```ts
174
226
  type AgentDelegationInput = Readonly<{
175
227
  agentId: string;
176
- sessionId: string;
177
228
  text: string;
178
229
  requestId: string;
179
- delivery: "steer" | "followUp";
230
+ }> &
231
+ | Readonly<{
232
+ sessionId: string;
233
+ delivery: "steer";
234
+ }>
235
+ | Readonly<{
236
+ sessionId?: string;
237
+ delivery: "followUp";
238
+ goal?: string | false;
180
239
  }>;
181
240
  ```
182
241
 
@@ -226,6 +285,16 @@ type AgentControlDirectory = Readonly<{
226
285
  invocation?,
227
286
  ) => Promise<Accepted | Held>;
228
287
  recall?: (input, sourceSessionId, signal) => Promise<CancelResult>;
288
+ answer?: (
289
+ input,
290
+ senderSessionId,
291
+ signal,
292
+ ) => Promise<AgentControlAnswerOutput>;
293
+ escalate?: (
294
+ input,
295
+ senderSessionId,
296
+ signal,
297
+ ) => Promise<AgentControlEscalateOutput>;
229
298
  create?: (input, signal) => Promise<AgentControlAgent>;
230
299
  setModel?: (input, signal) => Promise<void>;
231
300
  archive?: (input, signal) => Promise<void>;
@@ -248,6 +317,126 @@ Host-owned discovery and per-operation access to ordinary Agents.
248
317
 
249
318
  ## Variables
250
319
 
320
+ ### answerOutput
321
+
322
+ ```ts
323
+ const answerOutput: ZodDiscriminatedUnion<
324
+ [
325
+ ZodObject<
326
+ {
327
+ status: ZodLiteral<"answered">;
328
+ via: ZodEnum<{
329
+ in_turn: "in_turn";
330
+ follow_up: "follow_up";
331
+ }>;
332
+ commandId: ZodExactOptional<ZodString>;
333
+ },
334
+ $strict
335
+ >,
336
+ ZodObject<
337
+ {
338
+ status: ZodLiteral<"not_found">;
339
+ },
340
+ $strict
341
+ >,
342
+ ZodObject<
343
+ {
344
+ status: ZodLiteral<"closed">;
345
+ reason: ZodEnum<{
346
+ cancelled: "cancelled";
347
+ undeliverable: "undeliverable";
348
+ answered: "answered";
349
+ declined: "declined";
350
+ withdrawn: "withdrawn";
351
+ expired: "expired";
352
+ }>;
353
+ },
354
+ $strict
355
+ >,
356
+ ZodObject<
357
+ {
358
+ status: ZodLiteral<"refused">;
359
+ reason: ZodEnum<{
360
+ not_sender: "not_sender";
361
+ no_provenance: "no_provenance";
362
+ }>;
363
+ },
364
+ $strict
365
+ >,
366
+ ZodObject<
367
+ {
368
+ status: ZodLiteral<"unavailable">;
369
+ reason: ZodLiteral<"unsupported">;
370
+ },
371
+ $strict
372
+ >,
373
+ ],
374
+ "status"
375
+ >;
376
+ ```
377
+
378
+ Typed outcome of a sender's answer to a child Ask.
379
+
380
+ ---
381
+
382
+ ### escalateOutput
383
+
384
+ ```ts
385
+ const escalateOutput: ZodDiscriminatedUnion<
386
+ [
387
+ ZodObject<
388
+ {
389
+ status: ZodLiteral<"escalated">;
390
+ to: ZodLiteral<"person">;
391
+ },
392
+ $strict
393
+ >,
394
+ ZodObject<
395
+ {
396
+ status: ZodLiteral<"not_found">;
397
+ },
398
+ $strict
399
+ >,
400
+ ZodObject<
401
+ {
402
+ status: ZodLiteral<"closed">;
403
+ reason: ZodEnum<{
404
+ cancelled: "cancelled";
405
+ undeliverable: "undeliverable";
406
+ answered: "answered";
407
+ declined: "declined";
408
+ withdrawn: "withdrawn";
409
+ expired: "expired";
410
+ }>;
411
+ },
412
+ $strict
413
+ >,
414
+ ZodObject<
415
+ {
416
+ status: ZodLiteral<"refused">;
417
+ reason: ZodEnum<{
418
+ not_sender: "not_sender";
419
+ no_provenance: "no_provenance";
420
+ }>;
421
+ },
422
+ $strict
423
+ >,
424
+ ZodObject<
425
+ {
426
+ status: ZodLiteral<"unavailable">;
427
+ reason: ZodLiteral<"unsupported">;
428
+ },
429
+ $strict
430
+ >,
431
+ ],
432
+ "status"
433
+ >;
434
+ ```
435
+
436
+ Typed outcome of a sender's escalation of a child's item.
437
+
438
+ ---
439
+
251
440
  ### archiveRefusalReasons
252
441
 
253
442
  ```ts
@@ -279,10 +468,16 @@ function waitForAgentSettlement(
279
468
  ): Promise<
280
469
  Readonly<{
281
470
  acceptedSender: string | null;
471
+ delegator: string | null;
282
472
  terminal: SessionTerminal;
283
473
  content: string;
284
474
  usage: readonly Pick<AssistantMessage, "model" | "provider" | "usage">[];
285
475
  toolCalls: number;
476
+ waitingOnYou: readonly Readonly<{
477
+ itemId: string;
478
+ kind: string;
479
+ summary: string;
480
+ }>[];
286
481
  }>
287
482
  >;
288
483
  ```
@@ -301,10 +496,12 @@ Await one exact durable terminal without opening a transcript observation.
301
496
 
302
497
  #### Returns
303
498
 
304
- `Promise`\<`Readonly`\<\{ `acceptedSender`: `string` \| `null`; `terminal`:
305
- [`SessionTerminal`](client.md#sessionterminal); `content`: `string`; `usage`:
306
- readonly `Pick`\<`AssistantMessage`, `"model"` \| `"provider"` \| `"usage"`\>[];
307
- `toolCalls`: `number`; \}\>\>
499
+ `Promise`\<`Readonly`\<\{ `acceptedSender`: `string` \| `null`; `delegator`:
500
+ `string` \| `null`; `terminal`: [`SessionTerminal`](client.md#sessionterminal);
501
+ `content`: `string`; `usage`: readonly `Pick`\<`AssistantMessage`, `"model"` \|
502
+ `"provider"` \| `"usage"`\>[]; `toolCalls`: `number`; `waitingOnYou`: readonly
503
+ `Readonly`\<\{ `itemId`: `string`; `kind`: `string`; `summary`: `string`;
504
+ \}\>[]; \}\>\>
308
505
 
309
506
  ---
310
507