@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/dist/cloudflare.js
CHANGED
|
@@ -333,10 +333,10 @@ export function makeCloudflareShiitake(unnamed, options) {
|
|
|
333
333
|
return Object.freeze({
|
|
334
334
|
...handle,
|
|
335
335
|
readSettlement: (sessionId, commandId) => runShiitakePromise(readSettlement(sessionId, commandId)),
|
|
336
|
-
acceptMail: (mail, meta, reply) => {
|
|
336
|
+
acceptMail: (mail, meta, reply, stageAcceptedNotice, sessionGrantDelivery) => {
|
|
337
337
|
if (options.mailRoom === undefined)
|
|
338
338
|
return Promise.reject(runtimeFailure("unavailable", "Addressed mail is unavailable"));
|
|
339
|
-
return runShiitakePromise(acceptMail(mail, meta, reply));
|
|
339
|
+
return runShiitakePromise(acceptMail(mail, meta, reply, stageAcceptedNotice, sessionGrantDelivery));
|
|
340
340
|
},
|
|
341
341
|
alarm: host.alarm,
|
|
342
342
|
activeSessions: () => runShiitakePromise(activeSessions),
|
package/dist/delegation.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
|
|
2
2
|
import * as Schema from "effect/Schema";
|
|
3
3
|
import { type BirdDogEffect } from "@fungi.computer/bird-dog/effect";
|
|
4
|
-
import type
|
|
4
|
+
import { type BirdDogMail } from "@fungi.computer/bird-dog";
|
|
5
5
|
import { type PostRoute, type PostRuntime, type PostEnvelope, type PostDeliveryResult } from "@fungi.computer/carrier/post";
|
|
6
|
-
import { type Accepted, type CancelResult } from "./client.js";
|
|
6
|
+
import { type Accepted, type DelegationReceipt, type CancelResult } from "./client.js";
|
|
7
7
|
import type { CommandSettlement, ShiitakeHost, VerifiedAgentReply } from "./index.js";
|
|
8
8
|
import type { AgentControlDirectory, AgentDelegationInput, AgentDelegationInvocation, AgentRecallInput } from "./agent-control.js";
|
|
9
9
|
/** Shiitake host-pack identity; core consumes OysterAgents, never this engine. */
|
|
@@ -19,30 +19,39 @@ export declare class AgentDelegation {
|
|
|
19
19
|
expiresAt: () => number;
|
|
20
20
|
identity: () => DelegationIdentity | null | Promise<DelegationIdentity | null>;
|
|
21
21
|
directory: () => AgentControlDirectory;
|
|
22
|
-
deliver: (mail: BirdDogMail) => Promise<
|
|
22
|
+
deliver: (mail: BirdDogMail) => Promise<DelegationReceipt>;
|
|
23
23
|
prepare: (mail: BirdDogMail) => Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Synchronously stage the accepted notice in this recipient's durable
|
|
26
|
+
* outbox, in the same transaction as acceptance.
|
|
27
|
+
*/
|
|
28
|
+
stageAcceptedNotice: (mail: BirdDogMail, scopeId: string) => void;
|
|
24
29
|
/** Admit mail; `reply` is present only for an authenticated reply. */
|
|
25
|
-
accept: (mail: BirdDogMail, reply
|
|
30
|
+
accept: (mail: BirdDogMail, reply: VerifiedAgentReply | undefined, stageAcceptedNotice: (mail: BirdDogMail) => void) => Promise<Accepted>;
|
|
26
31
|
}>);
|
|
27
32
|
private fleetOwner;
|
|
28
33
|
private fleet;
|
|
29
34
|
private scope;
|
|
30
|
-
private requestKey;
|
|
31
35
|
/** Recipient-local disclosure through the public host, never another Agent's RPC. */
|
|
32
36
|
settlement(sender: DelegationIdentity, sessionId: string, commandId: string, host: Pick<ShiitakeHost, "readSettlement">): Promise<CommandSettlement | undefined>;
|
|
33
37
|
/** Delegate work to an in-scope Agent: steer its Session, or start fleet correspondence. */
|
|
34
|
-
send(input: AgentDelegationInput, sourceSessionId: string, signal: AbortSignal, invocation?: AgentDelegationInvocation): Promise<
|
|
38
|
+
send(input: AgentDelegationInput, sourceSessionId: string, signal: AbortSignal, invocation?: AgentDelegationInvocation): Promise<DelegationReceipt>;
|
|
35
39
|
/** Resolve this sender's exact request, then let the recipient guard pending work. */
|
|
36
40
|
recall(input: AgentRecallInput, sourceSessionId: string, signal: AbortSignal): Promise<CancelResult>;
|
|
37
41
|
/**
|
|
38
42
|
* A reply-less envelope is only ever BirdDog's reply to work this Agent
|
|
39
43
|
* sent. Admit it only after the fleet authenticates it against that frozen
|
|
40
|
-
* mail
|
|
41
|
-
*
|
|
44
|
+
* mail. A recipient-left reply remains Agent-authored; a recipient-offline
|
|
45
|
+
* reply is Hub-authored. Its meta carries only the authenticated reply.
|
|
42
46
|
*/
|
|
43
47
|
private authenticatedReply;
|
|
48
|
+
private parseAddressedMail;
|
|
44
49
|
/** Admit one addressed Agent mail for this Agent, or refuse it. */
|
|
45
50
|
receive(raw: PostEnvelope): Promise<PostDeliveryResult>;
|
|
51
|
+
private recordAcceptedNotice;
|
|
52
|
+
private acceptAdmitted;
|
|
53
|
+
/** Admit local outbox exhaustion through the same Fleet/system reply gate. */
|
|
54
|
+
localUndeliverable(original: BirdDogMail): Promise<PostDeliveryResult>;
|
|
46
55
|
}
|
|
47
56
|
/** A current correspondent no longer has disclosure authority. */
|
|
48
57
|
export declare class AgentDisclosureRefused extends Error {
|
|
@@ -63,7 +72,7 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
63
72
|
readonly to: {
|
|
64
73
|
readonly kind: "agent";
|
|
65
74
|
readonly agentId: string;
|
|
66
|
-
readonly sessionId
|
|
75
|
+
readonly sessionId?: string;
|
|
67
76
|
};
|
|
68
77
|
readonly replyTo: readonly {
|
|
69
78
|
readonly kind: "agent";
|
|
@@ -75,7 +84,36 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
75
84
|
readonly commandId: string;
|
|
76
85
|
readonly outcome: "completed" | "failed" | "cancelled" | "interrupted" | "outcome_unknown";
|
|
77
86
|
readonly diagnostic?: string;
|
|
87
|
+
} | {
|
|
88
|
+
readonly outcome: "accepted";
|
|
89
|
+
readonly commandId: string;
|
|
90
|
+
} | {
|
|
91
|
+
readonly outcome: "undeliverable";
|
|
92
|
+
readonly reason: "recipient_left";
|
|
93
|
+
};
|
|
94
|
+
} | {
|
|
95
|
+
readonly key: string;
|
|
96
|
+
readonly reply: {
|
|
97
|
+
readonly outcome: "undeliverable";
|
|
98
|
+
readonly reason: "recipient_offline";
|
|
99
|
+
};
|
|
100
|
+
readonly content: string;
|
|
101
|
+
readonly createdAt: number;
|
|
102
|
+
readonly from: {
|
|
103
|
+
readonly kind: "system";
|
|
104
|
+
readonly systemId: "hub";
|
|
78
105
|
};
|
|
106
|
+
readonly to: {
|
|
107
|
+
readonly kind: "agent";
|
|
108
|
+
readonly agentId: string;
|
|
109
|
+
readonly sessionId?: string;
|
|
110
|
+
};
|
|
111
|
+
readonly replyTo: readonly {
|
|
112
|
+
readonly kind: "agent";
|
|
113
|
+
readonly agentId: string;
|
|
114
|
+
readonly sessionId: string;
|
|
115
|
+
}[];
|
|
116
|
+
readonly meta?: Schema.Json;
|
|
79
117
|
}, {
|
|
80
118
|
readonly key: string;
|
|
81
119
|
readonly content: string;
|
|
@@ -88,7 +126,7 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
88
126
|
readonly to: {
|
|
89
127
|
readonly kind: "agent";
|
|
90
128
|
readonly agentId: string;
|
|
91
|
-
readonly sessionId
|
|
129
|
+
readonly sessionId?: string;
|
|
92
130
|
};
|
|
93
131
|
readonly replyTo: readonly {
|
|
94
132
|
readonly kind: "agent";
|
|
@@ -100,7 +138,36 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
100
138
|
readonly commandId: string;
|
|
101
139
|
readonly outcome: "completed" | "failed" | "cancelled" | "interrupted" | "outcome_unknown";
|
|
102
140
|
readonly diagnostic?: string;
|
|
141
|
+
} | {
|
|
142
|
+
readonly outcome: "accepted";
|
|
143
|
+
readonly commandId: string;
|
|
144
|
+
} | {
|
|
145
|
+
readonly outcome: "undeliverable";
|
|
146
|
+
readonly reason: "recipient_left";
|
|
147
|
+
};
|
|
148
|
+
} | {
|
|
149
|
+
readonly key: string;
|
|
150
|
+
readonly reply: {
|
|
151
|
+
readonly outcome: "undeliverable";
|
|
152
|
+
readonly reason: "recipient_offline";
|
|
153
|
+
};
|
|
154
|
+
readonly content: string;
|
|
155
|
+
readonly createdAt: number;
|
|
156
|
+
readonly from: {
|
|
157
|
+
readonly kind: "system";
|
|
158
|
+
readonly systemId: "hub";
|
|
159
|
+
};
|
|
160
|
+
readonly to: {
|
|
161
|
+
readonly kind: "agent";
|
|
162
|
+
readonly agentId: string;
|
|
163
|
+
readonly sessionId?: string;
|
|
103
164
|
};
|
|
165
|
+
readonly replyTo: readonly {
|
|
166
|
+
readonly kind: "agent";
|
|
167
|
+
readonly agentId: string;
|
|
168
|
+
readonly sessionId: string;
|
|
169
|
+
}[];
|
|
170
|
+
readonly meta?: Schema.Json;
|
|
104
171
|
}, import("@fungi.computer/seam/machine-link-protocol").AgentMailAddress, {
|
|
105
172
|
readonly key: string;
|
|
106
173
|
readonly content: string;
|
|
@@ -113,7 +180,7 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
113
180
|
readonly to: {
|
|
114
181
|
readonly kind: "agent";
|
|
115
182
|
readonly agentId: string;
|
|
116
|
-
readonly sessionId
|
|
183
|
+
readonly sessionId?: string;
|
|
117
184
|
};
|
|
118
185
|
readonly replyTo: readonly {
|
|
119
186
|
readonly kind: "agent";
|
|
@@ -125,7 +192,36 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
125
192
|
readonly commandId: string;
|
|
126
193
|
readonly outcome: "completed" | "failed" | "cancelled" | "interrupted" | "outcome_unknown";
|
|
127
194
|
readonly diagnostic?: string;
|
|
195
|
+
} | {
|
|
196
|
+
readonly outcome: "accepted";
|
|
197
|
+
readonly commandId: string;
|
|
198
|
+
} | {
|
|
199
|
+
readonly outcome: "undeliverable";
|
|
200
|
+
readonly reason: "recipient_left";
|
|
201
|
+
};
|
|
202
|
+
} | {
|
|
203
|
+
readonly key: string;
|
|
204
|
+
readonly reply: {
|
|
205
|
+
readonly outcome: "undeliverable";
|
|
206
|
+
readonly reason: "recipient_offline";
|
|
207
|
+
};
|
|
208
|
+
readonly content: string;
|
|
209
|
+
readonly createdAt: number;
|
|
210
|
+
readonly from: {
|
|
211
|
+
readonly kind: "system";
|
|
212
|
+
readonly systemId: "hub";
|
|
213
|
+
};
|
|
214
|
+
readonly to: {
|
|
215
|
+
readonly kind: "agent";
|
|
216
|
+
readonly agentId: string;
|
|
217
|
+
readonly sessionId?: string;
|
|
128
218
|
};
|
|
219
|
+
readonly replyTo: readonly {
|
|
220
|
+
readonly kind: "agent";
|
|
221
|
+
readonly agentId: string;
|
|
222
|
+
readonly sessionId: string;
|
|
223
|
+
}[];
|
|
224
|
+
readonly meta?: Schema.Json;
|
|
129
225
|
}, {
|
|
130
226
|
readonly key: string;
|
|
131
227
|
readonly content: string;
|
|
@@ -138,7 +234,7 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
138
234
|
readonly to: {
|
|
139
235
|
readonly kind: "agent";
|
|
140
236
|
readonly agentId: string;
|
|
141
|
-
readonly sessionId
|
|
237
|
+
readonly sessionId?: string;
|
|
142
238
|
};
|
|
143
239
|
readonly replyTo: readonly {
|
|
144
240
|
readonly kind: "agent";
|
|
@@ -150,9 +246,39 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
150
246
|
readonly commandId: string;
|
|
151
247
|
readonly outcome: "completed" | "failed" | "cancelled" | "interrupted" | "outcome_unknown";
|
|
152
248
|
readonly diagnostic?: string;
|
|
249
|
+
} | {
|
|
250
|
+
readonly outcome: "accepted";
|
|
251
|
+
readonly commandId: string;
|
|
252
|
+
} | {
|
|
253
|
+
readonly outcome: "undeliverable";
|
|
254
|
+
readonly reason: "recipient_left";
|
|
255
|
+
};
|
|
256
|
+
} | {
|
|
257
|
+
readonly key: string;
|
|
258
|
+
readonly reply: {
|
|
259
|
+
readonly outcome: "undeliverable";
|
|
260
|
+
readonly reason: "recipient_offline";
|
|
261
|
+
};
|
|
262
|
+
readonly content: string;
|
|
263
|
+
readonly createdAt: number;
|
|
264
|
+
readonly from: {
|
|
265
|
+
readonly kind: "system";
|
|
266
|
+
readonly systemId: "hub";
|
|
267
|
+
};
|
|
268
|
+
readonly to: {
|
|
269
|
+
readonly kind: "agent";
|
|
270
|
+
readonly agentId: string;
|
|
271
|
+
readonly sessionId?: string;
|
|
153
272
|
};
|
|
273
|
+
readonly replyTo: readonly {
|
|
274
|
+
readonly kind: "agent";
|
|
275
|
+
readonly agentId: string;
|
|
276
|
+
readonly sessionId: string;
|
|
277
|
+
}[];
|
|
278
|
+
readonly meta?: Schema.Json;
|
|
154
279
|
}> & Omit<Readonly<{
|
|
155
280
|
addressKind: string;
|
|
281
|
+
deadlineMs?: number | undefined;
|
|
156
282
|
address: Schema.Codec<unknown, unknown>;
|
|
157
283
|
event: Schema.Codec<unknown, unknown>;
|
|
158
284
|
encodeEnvelope: (envelope: PostEnvelope) => PostEnvelope;
|
|
@@ -167,7 +293,7 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
167
293
|
/** The host's one Post lane, as Agent mail borrows it. */
|
|
168
294
|
export type AgentMailPost = Pick<PostRuntime<{
|
|
169
295
|
agentMail: ReturnType<typeof agentMailRoute>;
|
|
170
|
-
}>, "send" | "read" | "nextWake">;
|
|
296
|
+
}>, "send" | "read" | "nextWake" | "deadLetters">;
|
|
171
297
|
/** Recipient-owned replies and receipt-bearing submissions over the host's one Post lane. */
|
|
172
298
|
export declare class AgentMail {
|
|
173
299
|
private readonly deps;
|
|
@@ -177,14 +303,22 @@ export declare class AgentMail {
|
|
|
177
303
|
identity: () => DelegationIdentity | null | Promise<DelegationIdentity | null>;
|
|
178
304
|
/** Only a typed disclosure refusal becomes undefined; missing settled facts must fail. */
|
|
179
305
|
replySettlement: AgentReplySettlement;
|
|
306
|
+
/** Route a terminal reply for this sender through its Fleet admission. */
|
|
307
|
+
onUndeliverable: (original: BirdDogMail, reply: BirdDogMail) => Promise<PostDeliveryResult>;
|
|
180
308
|
drain: () => Promise<void>;
|
|
181
309
|
wake: () => Promise<void>;
|
|
182
310
|
}>);
|
|
183
311
|
private envelope;
|
|
184
|
-
/** Submit one mail and return
|
|
185
|
-
send(mail: BirdDogMail): Promise<
|
|
312
|
+
/** Submit one mail and return recipient acceptance or relay-held custody. */
|
|
313
|
+
send(mail: BirdDogMail): Promise<DelegationReceipt>;
|
|
314
|
+
/** Reconcile exhausted original outbox mail into one local system reply. */
|
|
315
|
+
stageUndeliverableReplies(): Promise<void>;
|
|
316
|
+
/** Stage the acceptance reply through this Post owner without leaving the transaction. */
|
|
317
|
+
stageAcceptedNotice(mail: BirdDogMail, scopeId: string): void;
|
|
186
318
|
/** BirdDog's mail room: disclosed settlement replies fan out over Post. */
|
|
187
|
-
mailRoom(): BirdDogEffect
|
|
319
|
+
mailRoom(): BirdDogEffect & {
|
|
320
|
+
stageAcceptedNotice: (mail: BirdDogMail, scopeId: string) => void;
|
|
321
|
+
};
|
|
188
322
|
}
|
|
189
323
|
/**
|
|
190
324
|
* The Agent wire decoders a host lends Oyster's machine-link client, so the
|
|
@@ -197,7 +331,7 @@ export declare const machineAgentCodecs: {
|
|
|
197
331
|
}> | null;
|
|
198
332
|
/** One serialized settlement from a hosted or machine-homed Agent. */
|
|
199
333
|
readonly settlement: (encoded: string) => CommandSettlement;
|
|
200
|
-
/** Throws unless an acknowledgment carries a
|
|
334
|
+
/** Throws unless an acknowledgment carries a valid acceptance or Held result. */
|
|
201
335
|
readonly accepted: (receipt: Schema.Json) => void;
|
|
202
336
|
};
|
|
203
337
|
/**
|
package/dist/delegation.js
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
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, delegationProvenance, delegationReplyMeta, delegationSendMeta, } from "./fleet.js";
|
|
7
|
-
import { deterministicReplayKey } from "@fungi.computer/cairn";
|
|
6
|
+
import { commandSettlementSchema, createAgentDirectory, createFleetCorrespondence, agentDelegationRequestKey, delegationProvenance, delegationReplyMeta, delegationSendMeta, } from "./fleet.js";
|
|
8
7
|
import * as Schema from "effect/Schema";
|
|
9
8
|
import { agentMailAddressSchema } from "@fungi.computer/seam/machine-link-protocol";
|
|
10
9
|
import { BirdDogMailSchema, BirdDogError, settlementReplies, } from "@fungi.computer/bird-dog/effect";
|
|
11
10
|
import * as Effect from "effect/Effect";
|
|
12
11
|
import { messageAuthor } from "@fungi.computer/agar/agents";
|
|
12
|
+
import { undeliverableReply } from "@fungi.computer/bird-dog";
|
|
13
13
|
import { PostEnvelopeSchema, definePostRoute, } from "@fungi.computer/carrier/post";
|
|
14
|
-
import {
|
|
14
|
+
import { parseDelegationReceipt, } from "./client.js";
|
|
15
15
|
import { decodeClientSessionCreationRequest } from "./internal/client-boundary.js";
|
|
16
16
|
/** Host-owned identity and admission around BirdDog's ordinary envelope. */
|
|
17
17
|
export class AgentDelegation {
|
|
@@ -37,16 +37,6 @@ export class AgentDelegation {
|
|
|
37
37
|
throw new Error("Agent is outside this scope");
|
|
38
38
|
return { ...identity, agents, source };
|
|
39
39
|
}
|
|
40
|
-
requestKey(scope, input, sourceSessionId) {
|
|
41
|
-
return deterministicReplayKey("oyster-agent-delegation", [
|
|
42
|
-
scope.scopeId,
|
|
43
|
-
scope.agentId,
|
|
44
|
-
sourceSessionId,
|
|
45
|
-
input.agentId,
|
|
46
|
-
input.sessionId,
|
|
47
|
-
input.requestId,
|
|
48
|
-
]);
|
|
49
|
-
}
|
|
50
40
|
/** Recipient-local disclosure through the public host, never another Agent's RPC. */
|
|
51
41
|
async settlement(sender, sessionId, commandId, host) {
|
|
52
42
|
const scope = await this.scope(new AbortController().signal);
|
|
@@ -65,7 +55,16 @@ export class AgentDelegation {
|
|
|
65
55
|
const scope = await this.scope(signal);
|
|
66
56
|
if (!scope.agents.some(({ agentId }) => agentId === input.agentId))
|
|
67
57
|
throw new Error("Agent is outside this scope");
|
|
68
|
-
const requestId = await
|
|
58
|
+
const requestId = await agentDelegationRequestKey({
|
|
59
|
+
owner: {
|
|
60
|
+
kind: "hosted",
|
|
61
|
+
scopeId: scope.scopeId,
|
|
62
|
+
agentId: scope.agentId,
|
|
63
|
+
},
|
|
64
|
+
sourceSessionId,
|
|
65
|
+
targetAgentId: input.agentId,
|
|
66
|
+
requestId: input.requestId,
|
|
67
|
+
});
|
|
69
68
|
signal.throwIfAborted();
|
|
70
69
|
if (input.delivery === "steer") {
|
|
71
70
|
const opened = await this.deps.directory().open(input.agentId, signal);
|
|
@@ -104,12 +103,25 @@ export class AgentDelegation {
|
|
|
104
103
|
const scope = await this.scope(signal);
|
|
105
104
|
if (!scope.agents.some(({ agentId }) => agentId === input.agentId))
|
|
106
105
|
throw new Error("Agent is outside this scope");
|
|
107
|
-
const requestId = await
|
|
106
|
+
const requestId = await agentDelegationRequestKey({
|
|
107
|
+
owner: {
|
|
108
|
+
kind: "hosted",
|
|
109
|
+
scopeId: scope.scopeId,
|
|
110
|
+
agentId: scope.agentId,
|
|
111
|
+
},
|
|
112
|
+
sourceSessionId,
|
|
113
|
+
targetAgentId: input.agentId,
|
|
114
|
+
requestId: input.requestId,
|
|
115
|
+
});
|
|
108
116
|
signal.throwIfAborted();
|
|
109
117
|
const opened = await this.deps.directory().open(input.agentId, signal);
|
|
110
118
|
try {
|
|
111
|
-
const
|
|
112
|
-
|
|
119
|
+
const receipt = this.fleet().receipt(requestId);
|
|
120
|
+
if (receipt?.sessionId !== undefined &&
|
|
121
|
+
receipt.sessionId !== input.sessionId)
|
|
122
|
+
return { status: "unknown" };
|
|
123
|
+
const session = opened.agent.session(receipt?.sessionId ?? input.sessionId);
|
|
124
|
+
const admitted = receipt ?? (await session.withdraw(requestId));
|
|
113
125
|
return admitted.status === "accepted"
|
|
114
126
|
? await session.cancel({
|
|
115
127
|
commandId: admitted.commandId,
|
|
@@ -124,8 +136,8 @@ export class AgentDelegation {
|
|
|
124
136
|
/**
|
|
125
137
|
* A reply-less envelope is only ever BirdDog's reply to work this Agent
|
|
126
138
|
* sent. Admit it only after the fleet authenticates it against that frozen
|
|
127
|
-
* mail
|
|
128
|
-
*
|
|
139
|
+
* mail. A recipient-left reply remains Agent-authored; a recipient-offline
|
|
140
|
+
* reply is Hub-authored. Its meta carries only the authenticated reply.
|
|
129
141
|
*/
|
|
130
142
|
async authenticatedReply(mail) {
|
|
131
143
|
const delegation = this.fleet().authenticateReply(mail);
|
|
@@ -133,48 +145,110 @@ export class AgentDelegation {
|
|
|
133
145
|
return undefined;
|
|
134
146
|
const scope = await this.scope(new AbortController().signal);
|
|
135
147
|
const replier = scope.agents.find(({ agentId }) => agentId === delegation.reply.agentId);
|
|
136
|
-
|
|
148
|
+
const undeliverable = delegation.reply.outcome === "undeliverable";
|
|
149
|
+
if (replier === undefined && !undeliverable)
|
|
137
150
|
return undefined;
|
|
138
|
-
const meta = delegationReplyMeta(delegation, replier.
|
|
151
|
+
const meta = delegationReplyMeta(delegation, replier?.name ?? delegation.reply.agentId);
|
|
139
152
|
return { mail: { ...mail, meta }, reply: delegation.reply };
|
|
140
153
|
}
|
|
141
|
-
|
|
142
|
-
async receive(raw) {
|
|
154
|
+
parseAddressedMail(raw) {
|
|
143
155
|
const envelope = Schema.decodeUnknownOption(PostEnvelopeSchema)(raw);
|
|
144
156
|
if (envelope._tag === "None" || envelope.value.kind !== "agentMail")
|
|
145
|
-
return
|
|
157
|
+
return undefined;
|
|
146
158
|
const address = Schema.decodeUnknownOption(agentMailAddressSchema)(envelope.value.address);
|
|
147
|
-
const
|
|
148
|
-
if (address._tag === "None" ||
|
|
159
|
+
const mail = Schema.decodeUnknownOption(BirdDogMailSchema)(envelope.value.event);
|
|
160
|
+
if (address._tag === "None" || mail._tag === "None")
|
|
161
|
+
return undefined;
|
|
162
|
+
return {
|
|
163
|
+
envelope: envelope.value,
|
|
164
|
+
address: address.value,
|
|
165
|
+
mail: mail.value,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** Admit one addressed Agent mail for this Agent, or refuse it. */
|
|
169
|
+
async receive(raw) {
|
|
170
|
+
const addressed = this.parseAddressedMail(raw);
|
|
171
|
+
if (addressed === undefined)
|
|
149
172
|
return "refused";
|
|
150
|
-
const mail =
|
|
173
|
+
const { envelope, address, mail } = addressed;
|
|
174
|
+
let authenticated;
|
|
151
175
|
const valid = async () => {
|
|
152
176
|
const scope = await this.scope(new AbortController().signal);
|
|
153
|
-
|
|
154
|
-
|
|
177
|
+
const sender = mail.from;
|
|
178
|
+
const authenticatedUndeliverable = authenticated?.reply.outcome === "undeliverable";
|
|
179
|
+
return (address.scopeId === scope.scopeId &&
|
|
180
|
+
address.agentId === scope.agentId &&
|
|
155
181
|
mail.to.agentId === scope.agentId &&
|
|
156
|
-
envelope.
|
|
157
|
-
|
|
182
|
+
envelope.key === mail.key &&
|
|
183
|
+
(authenticatedUndeliverable ||
|
|
184
|
+
(sender.kind === "agent" &&
|
|
185
|
+
scope.agents.some(({ agentId }) => agentId === sender.agentId))) &&
|
|
186
|
+
mail.replyTo.every(({ agentId }) => scope.agents.some((agent) => agent.agentId === agentId)) &&
|
|
158
187
|
(mail.replyTo.length === 0 ||
|
|
159
188
|
(mail.replyTo.length === 1 &&
|
|
160
|
-
|
|
161
|
-
mail.replyTo[0]
|
|
189
|
+
sender.kind === "agent" &&
|
|
190
|
+
mail.replyTo[0]?.agentId === sender.agentId &&
|
|
191
|
+
mail.replyTo[0].sessionId === sender.sessionId)));
|
|
162
192
|
};
|
|
193
|
+
if (mail.replyTo.length === 0)
|
|
194
|
+
authenticated = await this.authenticatedReply(mail);
|
|
163
195
|
if (!(await valid()))
|
|
164
196
|
return "refused";
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
197
|
+
if (mail.reply?.outcome === "accepted")
|
|
198
|
+
return this.recordAcceptedNotice(mail, valid);
|
|
199
|
+
const admitted = mail.replyTo.length > 0 ? { mail, reply: undefined } : authenticated;
|
|
168
200
|
if (admitted === undefined)
|
|
169
201
|
return "refused";
|
|
202
|
+
return this.acceptAdmitted(admitted, address.scopeId, valid);
|
|
203
|
+
}
|
|
204
|
+
async recordAcceptedNotice(mail, valid) {
|
|
205
|
+
if (mail.to.sessionId === undefined)
|
|
206
|
+
return "refused";
|
|
207
|
+
await this.deps.prepare(mail);
|
|
208
|
+
if (!(await valid()))
|
|
209
|
+
return "refused";
|
|
210
|
+
const receipt = this.fleet().recordAcceptedNotice(mail);
|
|
211
|
+
return receipt === undefined ? "refused" : { status: "accepted", receipt };
|
|
212
|
+
}
|
|
213
|
+
async acceptAdmitted(admitted, scopeId, valid) {
|
|
170
214
|
await this.deps.prepare(admitted.mail);
|
|
171
215
|
if (!(await valid()))
|
|
172
216
|
return "refused";
|
|
217
|
+
const stageAcceptedNotice = (notice) => {
|
|
218
|
+
this.deps.stageAcceptedNotice(notice, scopeId);
|
|
219
|
+
};
|
|
220
|
+
const receipt = await this.deps.accept(admitted.mail, admitted.reply, stageAcceptedNotice);
|
|
221
|
+
if (admitted.reply?.outcome === "undeliverable" &&
|
|
222
|
+
admitted.mail.from.kind === "system") {
|
|
223
|
+
this.fleet().recordUndeliverableReply(admitted.mail.key.slice(0, -":reply:0".length));
|
|
224
|
+
}
|
|
225
|
+
const sessionId = admitted.mail.to.sessionId ?? receipt.sessionId;
|
|
173
226
|
return {
|
|
174
227
|
status: "accepted",
|
|
175
|
-
receipt:
|
|
228
|
+
receipt: sessionId === undefined ? receipt : { ...receipt, sessionId },
|
|
176
229
|
};
|
|
177
230
|
}
|
|
231
|
+
/** Admit local outbox exhaustion through the same Fleet/system reply gate. */
|
|
232
|
+
async localUndeliverable(original) {
|
|
233
|
+
const identity = await this.deps.identity();
|
|
234
|
+
if (identity === null)
|
|
235
|
+
return "refused";
|
|
236
|
+
let reply;
|
|
237
|
+
try {
|
|
238
|
+
reply = undeliverableReply(original, Date.now());
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return "refused";
|
|
242
|
+
}
|
|
243
|
+
if (!this.fleet().recordLocalUndeliverable(original, reply))
|
|
244
|
+
return "refused";
|
|
245
|
+
return await this.receive({
|
|
246
|
+
key: reply.key,
|
|
247
|
+
kind: "agentMail",
|
|
248
|
+
address: { scopeId: identity.scopeId, agentId: identity.agentId },
|
|
249
|
+
event: reply,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
178
252
|
}
|
|
179
253
|
/** A current correspondent no longer has disclosure authority. */
|
|
180
254
|
export class AgentDisclosureRefused extends Error {
|
|
@@ -194,6 +268,13 @@ export function agentMailRoute(namespace, replySettlement) {
|
|
|
194
268
|
deliver: async (envelope) => {
|
|
195
269
|
const mail = Schema.decodeUnknownSync(BirdDogMailSchema)(envelope.event);
|
|
196
270
|
if (mail.reply !== undefined &&
|
|
271
|
+
mail.reply.outcome !== "accepted" &&
|
|
272
|
+
mail.reply.outcome !== "undeliverable" &&
|
|
273
|
+
mail.to.sessionId === undefined)
|
|
274
|
+
return "refused";
|
|
275
|
+
if (mail.reply !== undefined &&
|
|
276
|
+
mail.reply.outcome !== "undeliverable" &&
|
|
277
|
+
mail.reply.outcome !== "accepted" &&
|
|
197
278
|
(await replySettlement(mail)) === undefined)
|
|
198
279
|
return "refused";
|
|
199
280
|
return namespace.getByName(name).deliver(envelope);
|
|
@@ -219,18 +300,60 @@ export class AgentMail {
|
|
|
219
300
|
event: mail,
|
|
220
301
|
};
|
|
221
302
|
}
|
|
222
|
-
/** Submit one mail and return
|
|
303
|
+
/** Submit one mail and return recipient acceptance or relay-held custody. */
|
|
223
304
|
async send(mail) {
|
|
224
305
|
this.deps.post().send(await this.envelope(mail));
|
|
225
306
|
// Borrow the existing Post lane once, not a new waiter, loop or supervisor.
|
|
226
307
|
await this.deps.drain();
|
|
227
308
|
const row = this.deps.post().read(mail.key);
|
|
228
309
|
if (row?.state === "delivered" && row.receipt !== undefined)
|
|
229
|
-
return
|
|
310
|
+
return parseDelegationReceipt(JSON.stringify(row.receipt));
|
|
230
311
|
if (row?.state === "dead")
|
|
231
312
|
throw new Error(`Agent delivery ${row.reason}`);
|
|
232
313
|
throw new Error("Agent did not return a delegated work receipt");
|
|
233
314
|
}
|
|
315
|
+
/** Reconcile exhausted original outbox mail into one local system reply. */
|
|
316
|
+
async stageUndeliverableReplies() {
|
|
317
|
+
const identity = await this.deps.identity();
|
|
318
|
+
if (identity === null)
|
|
319
|
+
return;
|
|
320
|
+
const post = this.deps.post();
|
|
321
|
+
let cursor;
|
|
322
|
+
do {
|
|
323
|
+
const page = post.deadLetters(64, cursor);
|
|
324
|
+
for (const letter of page.letters) {
|
|
325
|
+
if (letter.reason !== "exhausted" ||
|
|
326
|
+
letter.envelope.kind !== "agentMail")
|
|
327
|
+
continue;
|
|
328
|
+
const envelope = Schema.decodeUnknownOption(PostEnvelopeSchema)(letter.envelope);
|
|
329
|
+
if (envelope._tag === "None" || envelope.value.kind !== "agentMail")
|
|
330
|
+
continue;
|
|
331
|
+
const original = Schema.decodeUnknownOption(BirdDogMailSchema)(envelope.value.event);
|
|
332
|
+
const address = Schema.decodeUnknownOption(agentMailAddressSchema)(envelope.value.address);
|
|
333
|
+
if (original._tag === "None" ||
|
|
334
|
+
address._tag === "None" ||
|
|
335
|
+
original.value.key !== envelope.value.key ||
|
|
336
|
+
original.value.reply !== undefined ||
|
|
337
|
+
original.value.from.kind !== "agent" ||
|
|
338
|
+
original.value.from.agentId !== identity.agentId ||
|
|
339
|
+
address.value.scopeId !== identity.scopeId ||
|
|
340
|
+
address.value.agentId !== original.value.to.agentId)
|
|
341
|
+
continue;
|
|
342
|
+
const reply = undeliverableReply(original.value, Date.now());
|
|
343
|
+
await this.deps.onUndeliverable(original.value, reply);
|
|
344
|
+
}
|
|
345
|
+
cursor = page.nextCursor;
|
|
346
|
+
} while (cursor !== undefined);
|
|
347
|
+
}
|
|
348
|
+
/** Stage the acceptance reply through this Post owner without leaving the transaction. */
|
|
349
|
+
stageAcceptedNotice(mail, scopeId) {
|
|
350
|
+
this.deps.post().send({
|
|
351
|
+
key: mail.key,
|
|
352
|
+
kind: "agentMail",
|
|
353
|
+
address: { scopeId, agentId: mail.to.agentId },
|
|
354
|
+
event: mail,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
234
357
|
/** BirdDog's mail room: disclosed settlement replies fan out over Post. */
|
|
235
358
|
mailRoom() {
|
|
236
359
|
return {
|
|
@@ -268,6 +391,16 @@ export class AgentMail {
|
|
|
268
391
|
},
|
|
269
392
|
catch: (cause) => new BirdDogError("Agent reply fan-out is unavailable", { cause }),
|
|
270
393
|
}),
|
|
394
|
+
postMail: (mail) => Effect.tryPromise({
|
|
395
|
+
try: async () => {
|
|
396
|
+
this.deps.post().send(await this.envelope(mail));
|
|
397
|
+
await this.deps.wake();
|
|
398
|
+
},
|
|
399
|
+
catch: (cause) => new BirdDogError("Agent mail staging is unavailable", { cause }),
|
|
400
|
+
}),
|
|
401
|
+
stageAcceptedNotice: (mail, scopeId) => {
|
|
402
|
+
this.stageAcceptedNotice(mail, scopeId);
|
|
403
|
+
},
|
|
271
404
|
drainOutbox: () => Effect.void,
|
|
272
405
|
nextWakeAt: () => Effect.try({
|
|
273
406
|
try: () => this.deps.post().nextWake() ?? undefined,
|
|
@@ -290,9 +423,9 @@ export const machineAgentCodecs = {
|
|
|
290
423
|
settlement(encoded) {
|
|
291
424
|
return Schema.decodeUnknownSync(Schema.fromJsonString(commandSettlementSchema))(encoded);
|
|
292
425
|
},
|
|
293
|
-
/** Throws unless an acknowledgment carries a
|
|
426
|
+
/** Throws unless an acknowledgment carries a valid acceptance or Held result. */
|
|
294
427
|
accepted(receipt) {
|
|
295
|
-
|
|
428
|
+
parseDelegationReceipt(JSON.stringify(receipt));
|
|
296
429
|
},
|
|
297
430
|
};
|
|
298
431
|
/**
|
package/dist/effect.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export type ShiitakeEffectHost = Readonly<{
|
|
|
22
22
|
readSettlement: (sessionId: string, commandId: string) => Effect.Effect<CommandSettlement | undefined, ShiitakeError>;
|
|
23
23
|
agent: ShiitakeEffectOwner;
|
|
24
24
|
/** Durably admit one addressed mail item through the host owner. */
|
|
25
|
-
acceptMail: (mail: BirdDogMail, meta?: HostJson, reply?: VerifiedAgentReply) => Effect.Effect<Accepted, ShiitakeError>;
|
|
25
|
+
acceptMail: (mail: BirdDogMail, meta?: HostJson, reply?: VerifiedAgentReply, stageAcceptedNotice?: (mail: BirdDogMail) => void) => Effect.Effect<Accepted, ShiitakeError>;
|
|
26
26
|
}>;
|
|
27
27
|
/** Open the Effect-native host composition with addressed mail. */
|
|
28
28
|
export declare function openHost(options: ShiitakeEffectOptions, mailRoom: BirdDogEffect): Effect.Effect<ShiitakeEffectHost, ShiitakeError>;
|