@fungi.computer/shiitake 0.3.1 → 0.4.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 +18 -8
- package/dist/agent-control.d.ts +19 -15
- package/dist/agent-control.js +66 -126
- 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 +145 -13
- package/dist/delegation.js +109 -37
- package/dist/effect.d.ts +1 -1
- package/dist/fleet.d.ts +30 -8
- package/dist/fleet.js +142 -38
- 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 +2 -0
- 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 +3 -2
- package/dist/internal/woodstock/fleet-store.js +43 -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/delegation.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as Schema from "effect/Schema";
|
|
|
3
3
|
import { type BirdDogEffect } from "@fungi.computer/bird-dog/effect";
|
|
4
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,19 +19,23 @@ 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
|
/**
|
|
@@ -41,8 +45,11 @@ export declare class AgentDelegation {
|
|
|
41
45
|
* settled or cancelled the work, and its meta carries the verified 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;
|
|
46
53
|
}
|
|
47
54
|
/** A current correspondent no longer has disclosure authority. */
|
|
48
55
|
export declare class AgentDisclosureRefused extends Error {
|
|
@@ -63,7 +70,7 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
63
70
|
readonly to: {
|
|
64
71
|
readonly kind: "agent";
|
|
65
72
|
readonly agentId: string;
|
|
66
|
-
readonly sessionId
|
|
73
|
+
readonly sessionId?: string;
|
|
67
74
|
};
|
|
68
75
|
readonly replyTo: readonly {
|
|
69
76
|
readonly kind: "agent";
|
|
@@ -75,7 +82,37 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
75
82
|
readonly commandId: string;
|
|
76
83
|
readonly outcome: "completed" | "failed" | "cancelled" | "interrupted" | "outcome_unknown";
|
|
77
84
|
readonly diagnostic?: string;
|
|
85
|
+
} | {
|
|
86
|
+
readonly outcome: "undeliverable";
|
|
87
|
+
readonly reason: "recipient_offline" | "recipient_left";
|
|
88
|
+
} | {
|
|
89
|
+
readonly outcome: "accepted";
|
|
90
|
+
readonly commandId: string;
|
|
91
|
+
};
|
|
92
|
+
} | {
|
|
93
|
+
readonly key: string;
|
|
94
|
+
readonly reply: {
|
|
95
|
+
readonly outcome: "undeliverable";
|
|
96
|
+
readonly reason: "recipient_offline" | "recipient_left";
|
|
78
97
|
};
|
|
98
|
+
readonly content: string;
|
|
99
|
+
readonly createdAt: number;
|
|
100
|
+
readonly from: {
|
|
101
|
+
readonly kind: "agent";
|
|
102
|
+
readonly agentId: string;
|
|
103
|
+
readonly sessionId?: string;
|
|
104
|
+
};
|
|
105
|
+
readonly to: {
|
|
106
|
+
readonly kind: "agent";
|
|
107
|
+
readonly agentId: string;
|
|
108
|
+
readonly sessionId?: string;
|
|
109
|
+
};
|
|
110
|
+
readonly replyTo: readonly {
|
|
111
|
+
readonly kind: "agent";
|
|
112
|
+
readonly agentId: string;
|
|
113
|
+
readonly sessionId: string;
|
|
114
|
+
}[];
|
|
115
|
+
readonly meta?: Schema.Json;
|
|
79
116
|
}, {
|
|
80
117
|
readonly key: string;
|
|
81
118
|
readonly content: string;
|
|
@@ -88,7 +125,7 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
88
125
|
readonly to: {
|
|
89
126
|
readonly kind: "agent";
|
|
90
127
|
readonly agentId: string;
|
|
91
|
-
readonly sessionId
|
|
128
|
+
readonly sessionId?: string;
|
|
92
129
|
};
|
|
93
130
|
readonly replyTo: readonly {
|
|
94
131
|
readonly kind: "agent";
|
|
@@ -100,7 +137,37 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
100
137
|
readonly commandId: string;
|
|
101
138
|
readonly outcome: "completed" | "failed" | "cancelled" | "interrupted" | "outcome_unknown";
|
|
102
139
|
readonly diagnostic?: string;
|
|
140
|
+
} | {
|
|
141
|
+
readonly outcome: "undeliverable";
|
|
142
|
+
readonly reason: "recipient_offline" | "recipient_left";
|
|
143
|
+
} | {
|
|
144
|
+
readonly outcome: "accepted";
|
|
145
|
+
readonly commandId: string;
|
|
146
|
+
};
|
|
147
|
+
} | {
|
|
148
|
+
readonly key: string;
|
|
149
|
+
readonly reply: {
|
|
150
|
+
readonly outcome: "undeliverable";
|
|
151
|
+
readonly reason: "recipient_offline" | "recipient_left";
|
|
152
|
+
};
|
|
153
|
+
readonly content: string;
|
|
154
|
+
readonly createdAt: number;
|
|
155
|
+
readonly from: {
|
|
156
|
+
readonly kind: "agent";
|
|
157
|
+
readonly agentId: string;
|
|
158
|
+
readonly sessionId?: string;
|
|
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,37 @@ 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: "undeliverable";
|
|
197
|
+
readonly reason: "recipient_offline" | "recipient_left";
|
|
198
|
+
} | {
|
|
199
|
+
readonly outcome: "accepted";
|
|
200
|
+
readonly commandId: string;
|
|
201
|
+
};
|
|
202
|
+
} | {
|
|
203
|
+
readonly key: string;
|
|
204
|
+
readonly reply: {
|
|
205
|
+
readonly outcome: "undeliverable";
|
|
206
|
+
readonly reason: "recipient_offline" | "recipient_left";
|
|
207
|
+
};
|
|
208
|
+
readonly content: string;
|
|
209
|
+
readonly createdAt: number;
|
|
210
|
+
readonly from: {
|
|
211
|
+
readonly kind: "agent";
|
|
212
|
+
readonly agentId: string;
|
|
213
|
+
readonly sessionId?: string;
|
|
214
|
+
};
|
|
215
|
+
readonly to: {
|
|
216
|
+
readonly kind: "agent";
|
|
217
|
+
readonly agentId: string;
|
|
218
|
+
readonly sessionId?: string;
|
|
128
219
|
};
|
|
220
|
+
readonly replyTo: readonly {
|
|
221
|
+
readonly kind: "agent";
|
|
222
|
+
readonly agentId: string;
|
|
223
|
+
readonly sessionId: string;
|
|
224
|
+
}[];
|
|
225
|
+
readonly meta?: Schema.Json;
|
|
129
226
|
}, {
|
|
130
227
|
readonly key: string;
|
|
131
228
|
readonly content: string;
|
|
@@ -138,7 +235,7 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
138
235
|
readonly to: {
|
|
139
236
|
readonly kind: "agent";
|
|
140
237
|
readonly agentId: string;
|
|
141
|
-
readonly sessionId
|
|
238
|
+
readonly sessionId?: string;
|
|
142
239
|
};
|
|
143
240
|
readonly replyTo: readonly {
|
|
144
241
|
readonly kind: "agent";
|
|
@@ -150,9 +247,40 @@ export declare function agentMailRoute(namespace: PostRoute<never, never>["names
|
|
|
150
247
|
readonly commandId: string;
|
|
151
248
|
readonly outcome: "completed" | "failed" | "cancelled" | "interrupted" | "outcome_unknown";
|
|
152
249
|
readonly diagnostic?: string;
|
|
250
|
+
} | {
|
|
251
|
+
readonly outcome: "undeliverable";
|
|
252
|
+
readonly reason: "recipient_offline" | "recipient_left";
|
|
253
|
+
} | {
|
|
254
|
+
readonly outcome: "accepted";
|
|
255
|
+
readonly commandId: string;
|
|
256
|
+
};
|
|
257
|
+
} | {
|
|
258
|
+
readonly key: string;
|
|
259
|
+
readonly reply: {
|
|
260
|
+
readonly outcome: "undeliverable";
|
|
261
|
+
readonly reason: "recipient_offline" | "recipient_left";
|
|
262
|
+
};
|
|
263
|
+
readonly content: string;
|
|
264
|
+
readonly createdAt: number;
|
|
265
|
+
readonly from: {
|
|
266
|
+
readonly kind: "agent";
|
|
267
|
+
readonly agentId: string;
|
|
268
|
+
readonly sessionId?: string;
|
|
269
|
+
};
|
|
270
|
+
readonly to: {
|
|
271
|
+
readonly kind: "agent";
|
|
272
|
+
readonly agentId: string;
|
|
273
|
+
readonly sessionId?: string;
|
|
153
274
|
};
|
|
275
|
+
readonly replyTo: readonly {
|
|
276
|
+
readonly kind: "agent";
|
|
277
|
+
readonly agentId: string;
|
|
278
|
+
readonly sessionId: string;
|
|
279
|
+
}[];
|
|
280
|
+
readonly meta?: Schema.Json;
|
|
154
281
|
}> & Omit<Readonly<{
|
|
155
282
|
addressKind: string;
|
|
283
|
+
deadlineMs?: number | undefined;
|
|
156
284
|
address: Schema.Codec<unknown, unknown>;
|
|
157
285
|
event: Schema.Codec<unknown, unknown>;
|
|
158
286
|
encodeEnvelope: (envelope: PostEnvelope) => PostEnvelope;
|
|
@@ -181,10 +309,14 @@ export declare class AgentMail {
|
|
|
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
|
+
/** Stage the acceptance reply through this Post owner without leaving the transaction. */
|
|
315
|
+
stageAcceptedNotice(mail: BirdDogMail, scopeId: string): void;
|
|
186
316
|
/** BirdDog's mail room: disclosed settlement replies fan out over Post. */
|
|
187
|
-
mailRoom(): BirdDogEffect
|
|
317
|
+
mailRoom(): BirdDogEffect & {
|
|
318
|
+
stageAcceptedNotice: (mail: BirdDogMail, scopeId: string) => void;
|
|
319
|
+
};
|
|
188
320
|
}
|
|
189
321
|
/**
|
|
190
322
|
* The Agent wire decoders a host lends Oyster's machine-link client, so the
|
|
@@ -197,7 +329,7 @@ export declare const machineAgentCodecs: {
|
|
|
197
329
|
}> | null;
|
|
198
330
|
/** One serialized settlement from a hosted or machine-homed Agent. */
|
|
199
331
|
readonly settlement: (encoded: string) => CommandSettlement;
|
|
200
|
-
/** Throws unless an acknowledgment carries a
|
|
332
|
+
/** Throws unless an acknowledgment carries a valid acceptance or Held result. */
|
|
201
333
|
readonly accepted: (receipt: Schema.Json) => void;
|
|
202
334
|
};
|
|
203
335
|
/**
|
package/dist/delegation.js
CHANGED
|
@@ -3,15 +3,14 @@
|
|
|
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";
|
|
13
12
|
import { PostEnvelopeSchema, definePostRoute, } from "@fungi.computer/carrier/post";
|
|
14
|
-
import {
|
|
13
|
+
import { parseDelegationReceipt, } from "./client.js";
|
|
15
14
|
import { decodeClientSessionCreationRequest } from "./internal/client-boundary.js";
|
|
16
15
|
/** Host-owned identity and admission around BirdDog's ordinary envelope. */
|
|
17
16
|
export class AgentDelegation {
|
|
@@ -37,16 +36,6 @@ export class AgentDelegation {
|
|
|
37
36
|
throw new Error("Agent is outside this scope");
|
|
38
37
|
return { ...identity, agents, source };
|
|
39
38
|
}
|
|
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
39
|
/** Recipient-local disclosure through the public host, never another Agent's RPC. */
|
|
51
40
|
async settlement(sender, sessionId, commandId, host) {
|
|
52
41
|
const scope = await this.scope(new AbortController().signal);
|
|
@@ -65,7 +54,16 @@ export class AgentDelegation {
|
|
|
65
54
|
const scope = await this.scope(signal);
|
|
66
55
|
if (!scope.agents.some(({ agentId }) => agentId === input.agentId))
|
|
67
56
|
throw new Error("Agent is outside this scope");
|
|
68
|
-
const requestId = await
|
|
57
|
+
const requestId = await agentDelegationRequestKey({
|
|
58
|
+
owner: {
|
|
59
|
+
kind: "hosted",
|
|
60
|
+
scopeId: scope.scopeId,
|
|
61
|
+
agentId: scope.agentId,
|
|
62
|
+
},
|
|
63
|
+
sourceSessionId,
|
|
64
|
+
targetAgentId: input.agentId,
|
|
65
|
+
requestId: input.requestId,
|
|
66
|
+
});
|
|
69
67
|
signal.throwIfAborted();
|
|
70
68
|
if (input.delivery === "steer") {
|
|
71
69
|
const opened = await this.deps.directory().open(input.agentId, signal);
|
|
@@ -104,12 +102,25 @@ export class AgentDelegation {
|
|
|
104
102
|
const scope = await this.scope(signal);
|
|
105
103
|
if (!scope.agents.some(({ agentId }) => agentId === input.agentId))
|
|
106
104
|
throw new Error("Agent is outside this scope");
|
|
107
|
-
const requestId = await
|
|
105
|
+
const requestId = await agentDelegationRequestKey({
|
|
106
|
+
owner: {
|
|
107
|
+
kind: "hosted",
|
|
108
|
+
scopeId: scope.scopeId,
|
|
109
|
+
agentId: scope.agentId,
|
|
110
|
+
},
|
|
111
|
+
sourceSessionId,
|
|
112
|
+
targetAgentId: input.agentId,
|
|
113
|
+
requestId: input.requestId,
|
|
114
|
+
});
|
|
108
115
|
signal.throwIfAborted();
|
|
109
116
|
const opened = await this.deps.directory().open(input.agentId, signal);
|
|
110
117
|
try {
|
|
111
|
-
const
|
|
112
|
-
|
|
118
|
+
const receipt = this.fleet().receipt(requestId);
|
|
119
|
+
if (receipt?.sessionId !== undefined &&
|
|
120
|
+
receipt.sessionId !== input.sessionId)
|
|
121
|
+
return { status: "unknown" };
|
|
122
|
+
const session = opened.agent.session(receipt?.sessionId ?? input.sessionId);
|
|
123
|
+
const admitted = receipt ?? (await session.withdraw(requestId));
|
|
113
124
|
return admitted.status === "accepted"
|
|
114
125
|
? await session.cancel({
|
|
115
126
|
commandId: admitted.commandId,
|
|
@@ -133,46 +144,81 @@ export class AgentDelegation {
|
|
|
133
144
|
return undefined;
|
|
134
145
|
const scope = await this.scope(new AbortController().signal);
|
|
135
146
|
const replier = scope.agents.find(({ agentId }) => agentId === delegation.reply.agentId);
|
|
136
|
-
|
|
147
|
+
const undeliverable = delegation.reply.outcome === "undeliverable";
|
|
148
|
+
if (replier === undefined && !undeliverable)
|
|
137
149
|
return undefined;
|
|
138
|
-
const meta = delegationReplyMeta(delegation, replier.
|
|
150
|
+
const meta = delegationReplyMeta(delegation, replier?.name ?? delegation.reply.agentId);
|
|
139
151
|
return { mail: { ...mail, meta }, reply: delegation.reply };
|
|
140
152
|
}
|
|
141
|
-
|
|
142
|
-
async receive(raw) {
|
|
153
|
+
parseAddressedMail(raw) {
|
|
143
154
|
const envelope = Schema.decodeUnknownOption(PostEnvelopeSchema)(raw);
|
|
144
155
|
if (envelope._tag === "None" || envelope.value.kind !== "agentMail")
|
|
145
|
-
return
|
|
156
|
+
return undefined;
|
|
146
157
|
const address = Schema.decodeUnknownOption(agentMailAddressSchema)(envelope.value.address);
|
|
147
|
-
const
|
|
148
|
-
if (address._tag === "None" ||
|
|
158
|
+
const mail = Schema.decodeUnknownOption(BirdDogMailSchema)(envelope.value.event);
|
|
159
|
+
if (address._tag === "None" || mail._tag === "None")
|
|
160
|
+
return undefined;
|
|
161
|
+
return {
|
|
162
|
+
envelope: envelope.value,
|
|
163
|
+
address: address.value,
|
|
164
|
+
mail: mail.value,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/** Admit one addressed Agent mail for this Agent, or refuse it. */
|
|
168
|
+
async receive(raw) {
|
|
169
|
+
const addressed = this.parseAddressedMail(raw);
|
|
170
|
+
if (addressed === undefined)
|
|
149
171
|
return "refused";
|
|
150
|
-
const mail =
|
|
172
|
+
const { envelope, address, mail } = addressed;
|
|
173
|
+
let authenticated;
|
|
151
174
|
const valid = async () => {
|
|
152
175
|
const scope = await this.scope(new AbortController().signal);
|
|
153
|
-
|
|
154
|
-
|
|
176
|
+
const frozenUndeliverable = authenticated?.reply.outcome === "undeliverable" &&
|
|
177
|
+
authenticated.reply.agentId === mail.from.agentId;
|
|
178
|
+
return (address.scopeId === scope.scopeId &&
|
|
179
|
+
address.agentId === scope.agentId &&
|
|
155
180
|
mail.to.agentId === scope.agentId &&
|
|
156
|
-
envelope.
|
|
157
|
-
|
|
181
|
+
envelope.key === mail.key &&
|
|
182
|
+
(frozenUndeliverable ||
|
|
183
|
+
scope.agents.some(({ agentId }) => agentId === mail.from.agentId)) &&
|
|
184
|
+
mail.replyTo.every(({ agentId }) => scope.agents.some((agent) => agent.agentId === agentId)) &&
|
|
158
185
|
(mail.replyTo.length === 0 ||
|
|
159
186
|
(mail.replyTo.length === 1 &&
|
|
160
187
|
mail.replyTo[0]?.agentId === mail.from.agentId &&
|
|
161
188
|
mail.replyTo[0].sessionId === mail.from.sessionId)));
|
|
162
189
|
};
|
|
190
|
+
if (mail.replyTo.length === 0)
|
|
191
|
+
authenticated = await this.authenticatedReply(mail);
|
|
163
192
|
if (!(await valid()))
|
|
164
193
|
return "refused";
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
194
|
+
if (mail.reply?.outcome === "accepted")
|
|
195
|
+
return this.recordAcceptedNotice(mail, valid);
|
|
196
|
+
const admitted = mail.replyTo.length > 0 ? { mail, reply: undefined } : authenticated;
|
|
168
197
|
if (admitted === undefined)
|
|
169
198
|
return "refused";
|
|
199
|
+
return this.acceptAdmitted(admitted, address.scopeId, valid);
|
|
200
|
+
}
|
|
201
|
+
async recordAcceptedNotice(mail, valid) {
|
|
202
|
+
if (mail.to.sessionId === undefined)
|
|
203
|
+
return "refused";
|
|
204
|
+
await this.deps.prepare(mail);
|
|
205
|
+
if (!(await valid()))
|
|
206
|
+
return "refused";
|
|
207
|
+
const receipt = this.fleet().recordAcceptedNotice(mail);
|
|
208
|
+
return receipt === undefined ? "refused" : { status: "accepted", receipt };
|
|
209
|
+
}
|
|
210
|
+
async acceptAdmitted(admitted, scopeId, valid) {
|
|
170
211
|
await this.deps.prepare(admitted.mail);
|
|
171
212
|
if (!(await valid()))
|
|
172
213
|
return "refused";
|
|
214
|
+
const stageAcceptedNotice = (notice) => {
|
|
215
|
+
this.deps.stageAcceptedNotice(notice, scopeId);
|
|
216
|
+
};
|
|
217
|
+
const receipt = await this.deps.accept(admitted.mail, admitted.reply, stageAcceptedNotice);
|
|
218
|
+
const sessionId = admitted.mail.to.sessionId ?? receipt.sessionId;
|
|
173
219
|
return {
|
|
174
220
|
status: "accepted",
|
|
175
|
-
receipt:
|
|
221
|
+
receipt: sessionId === undefined ? receipt : { ...receipt, sessionId },
|
|
176
222
|
};
|
|
177
223
|
}
|
|
178
224
|
}
|
|
@@ -194,6 +240,13 @@ export function agentMailRoute(namespace, replySettlement) {
|
|
|
194
240
|
deliver: async (envelope) => {
|
|
195
241
|
const mail = Schema.decodeUnknownSync(BirdDogMailSchema)(envelope.event);
|
|
196
242
|
if (mail.reply !== undefined &&
|
|
243
|
+
mail.reply.outcome !== "accepted" &&
|
|
244
|
+
mail.reply.outcome !== "undeliverable" &&
|
|
245
|
+
mail.to.sessionId === undefined)
|
|
246
|
+
return "refused";
|
|
247
|
+
if (mail.reply !== undefined &&
|
|
248
|
+
mail.reply.outcome !== "undeliverable" &&
|
|
249
|
+
mail.reply.outcome !== "accepted" &&
|
|
197
250
|
(await replySettlement(mail)) === undefined)
|
|
198
251
|
return "refused";
|
|
199
252
|
return namespace.getByName(name).deliver(envelope);
|
|
@@ -219,18 +272,27 @@ export class AgentMail {
|
|
|
219
272
|
event: mail,
|
|
220
273
|
};
|
|
221
274
|
}
|
|
222
|
-
/** Submit one mail and return
|
|
275
|
+
/** Submit one mail and return recipient acceptance or relay-held custody. */
|
|
223
276
|
async send(mail) {
|
|
224
277
|
this.deps.post().send(await this.envelope(mail));
|
|
225
278
|
// Borrow the existing Post lane once, not a new waiter, loop or supervisor.
|
|
226
279
|
await this.deps.drain();
|
|
227
280
|
const row = this.deps.post().read(mail.key);
|
|
228
281
|
if (row?.state === "delivered" && row.receipt !== undefined)
|
|
229
|
-
return
|
|
282
|
+
return parseDelegationReceipt(JSON.stringify(row.receipt));
|
|
230
283
|
if (row?.state === "dead")
|
|
231
284
|
throw new Error(`Agent delivery ${row.reason}`);
|
|
232
285
|
throw new Error("Agent did not return a delegated work receipt");
|
|
233
286
|
}
|
|
287
|
+
/** Stage the acceptance reply through this Post owner without leaving the transaction. */
|
|
288
|
+
stageAcceptedNotice(mail, scopeId) {
|
|
289
|
+
this.deps.post().send({
|
|
290
|
+
key: mail.key,
|
|
291
|
+
kind: "agentMail",
|
|
292
|
+
address: { scopeId, agentId: mail.to.agentId },
|
|
293
|
+
event: mail,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
234
296
|
/** BirdDog's mail room: disclosed settlement replies fan out over Post. */
|
|
235
297
|
mailRoom() {
|
|
236
298
|
return {
|
|
@@ -268,6 +330,16 @@ export class AgentMail {
|
|
|
268
330
|
},
|
|
269
331
|
catch: (cause) => new BirdDogError("Agent reply fan-out is unavailable", { cause }),
|
|
270
332
|
}),
|
|
333
|
+
postMail: (mail) => Effect.tryPromise({
|
|
334
|
+
try: async () => {
|
|
335
|
+
this.deps.post().send(await this.envelope(mail));
|
|
336
|
+
await this.deps.wake();
|
|
337
|
+
},
|
|
338
|
+
catch: (cause) => new BirdDogError("Agent mail staging is unavailable", { cause }),
|
|
339
|
+
}),
|
|
340
|
+
stageAcceptedNotice: (mail, scopeId) => {
|
|
341
|
+
this.stageAcceptedNotice(mail, scopeId);
|
|
342
|
+
},
|
|
271
343
|
drainOutbox: () => Effect.void,
|
|
272
344
|
nextWakeAt: () => Effect.try({
|
|
273
345
|
try: () => this.deps.post().nextWake() ?? undefined,
|
|
@@ -290,9 +362,9 @@ export const machineAgentCodecs = {
|
|
|
290
362
|
settlement(encoded) {
|
|
291
363
|
return Schema.decodeUnknownSync(Schema.fromJsonString(commandSettlementSchema))(encoded);
|
|
292
364
|
},
|
|
293
|
-
/** Throws unless an acknowledgment carries a
|
|
365
|
+
/** Throws unless an acknowledgment carries a valid acceptance or Held result. */
|
|
294
366
|
accepted(receipt) {
|
|
295
|
-
|
|
367
|
+
parseDelegationReceipt(JSON.stringify(receipt));
|
|
296
368
|
},
|
|
297
369
|
};
|
|
298
370
|
/**
|
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>;
|
package/dist/fleet.d.ts
CHANGED
|
@@ -1,14 +1,34 @@
|
|
|
1
|
+
export { commandSettlementSchema } from "./client-wire.js";
|
|
1
2
|
import { type VerifiedAgentReply } from "./internal/woodstock/index.js";
|
|
2
3
|
import type { CommandSettlement } from "./internal/contracts.js";
|
|
3
4
|
import { type Pending } from "@fungi.computer/agar";
|
|
4
5
|
import type { BirdDogMail } from "@fungi.computer/bird-dog";
|
|
5
|
-
import
|
|
6
|
-
import { type Accepted, type ShiitakeAgent } from "./client.js";
|
|
6
|
+
import { type Accepted, type DelegationReceipt, type ShiitakeAgent } from "./client.js";
|
|
7
7
|
import type { Json } from "./extensions.js";
|
|
8
8
|
import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
|
|
9
9
|
import { type CairnId } from "@fungi.computer/cairn/id";
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
import { type AuthorMeta } from "@fungi.computer/agar/agents";
|
|
12
|
+
/** Inputs shared by Local and hosted delegation request identity. */
|
|
13
|
+
export type AgentDelegationRequestKeyInput = Readonly<{
|
|
14
|
+
owner: Readonly<{
|
|
15
|
+
kind: "local";
|
|
16
|
+
agentId: string;
|
|
17
|
+
}> | Readonly<{
|
|
18
|
+
kind: "hosted";
|
|
19
|
+
scopeId: string;
|
|
20
|
+
agentId: string;
|
|
21
|
+
}>;
|
|
22
|
+
sourceSessionId: string;
|
|
23
|
+
targetAgentId: string;
|
|
24
|
+
requestId: string;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* One idempotency identity for a caller-owned delegation request.
|
|
28
|
+
* The target Session is deliberately excluded: a sessionless send learns it
|
|
29
|
+
* only after acceptance, when recall must still resolve the original receipt.
|
|
30
|
+
*/
|
|
31
|
+
export declare function agentDelegationRequestKey(input: AgentDelegationRequestKeyInput): Promise<string>;
|
|
12
32
|
/** Operations explicitly admitted by the host. */
|
|
13
33
|
export declare const agentActionSchema: z.ZodEnum<{
|
|
14
34
|
send: "send";
|
|
@@ -69,7 +89,7 @@ export declare class AgentGrantError extends Error {
|
|
|
69
89
|
export declare function assertAgentGrant(grant: AgentGrant, action: AgentAction, agentId?: string): void;
|
|
70
90
|
/** Stable Agent identities, retained across host compositions. */
|
|
71
91
|
export declare const agentIds: Readonly<{
|
|
72
|
-
readonly agent: import("@fungi.computer/cairn
|
|
92
|
+
readonly agent: import("@fungi.computer/cairn").IdCodec<"agent", "agent", 1>;
|
|
73
93
|
}>;
|
|
74
94
|
/** An ordinary Agent directory identity. */
|
|
75
95
|
export type AgentId = CairnId<"agent", "agent">;
|
|
@@ -169,8 +189,6 @@ export declare const agentDelegationTaskSchema: z.ZodLazy<z.ZodObject<{
|
|
|
169
189
|
}, z.core.$strict>>;
|
|
170
190
|
/** A caller reuses this task unchanged while polling Pending. */
|
|
171
191
|
export type AgentDelegationTask = z.infer<typeof agentDelegationTaskSchema>;
|
|
172
|
-
/** Exact host facts encoded over internal transports, without transcript content. */
|
|
173
|
-
export declare const commandSettlementSchema: Schema.Codec<CommandSettlement>;
|
|
174
192
|
/** Exact settled Session facts, including Pi usage and tool-call records. */
|
|
175
193
|
export type AgentDelegationOutcome = Readonly<{
|
|
176
194
|
kind: "settled";
|
|
@@ -181,7 +199,7 @@ export type AgentDelegationOutcome = Readonly<{
|
|
|
181
199
|
export type FleetDelegationOptions = Readonly<{
|
|
182
200
|
storage: WatchdogSqliteOwner;
|
|
183
201
|
/** Admit the frozen addressed mail through the destination owner. */
|
|
184
|
-
deliver: (mail: BirdDogMail) => Promise<
|
|
202
|
+
deliver: (mail: BirdDogMail) => Promise<DelegationReceipt>;
|
|
185
203
|
/** Read exact work facts at the target owner, independent of transcript selection. */
|
|
186
204
|
readSettlement: (agentId: string, sessionId: string, commandId: string) => Promise<CommandSettlement | undefined>;
|
|
187
205
|
/** Acquire a Session client only when creating the delegated conversation. */
|
|
@@ -258,22 +276,26 @@ export type OutstandingDelegation = Readonly<{
|
|
|
258
276
|
*/
|
|
259
277
|
export declare function createFleetCorrespondence(options: Omit<FleetDelegationOptions, "readSettlement">): {
|
|
260
278
|
/** Submit frozen mail or reuse its recorded Session admission receipt. */
|
|
261
|
-
start: (grant: AgentGrant, raw: AgentDelegationTask) => Promise<
|
|
279
|
+
start: (grant: AgentGrant, raw: AgentDelegationTask) => Promise<DelegationReceipt>;
|
|
262
280
|
/** Match a reply to frozen mail and restore the caller's delegation path. */
|
|
263
281
|
authenticateReply: (mail: BirdDogMail) => AuthenticatedDelegationReply | undefined;
|
|
264
282
|
/** Read an existing acceptance receipt without reserving or delivering mail. */
|
|
265
283
|
receipt: (requestId: string) => Accepted | undefined;
|
|
284
|
+
/** Consume a recipient's stable `<mailKey>:accepted` notice. */
|
|
285
|
+
recordAcceptedNotice: (mail: BirdDogMail) => Accepted | undefined;
|
|
266
286
|
};
|
|
267
287
|
/** Local hosts may observe exact work directly; remote correspondence never pulls it. */
|
|
268
288
|
export declare function createFleetDelegation(options: FleetDelegationOptions): {
|
|
269
289
|
/** Submit or poll the exact turn, yielding Agar Pending until settled. */
|
|
270
290
|
delegate: (grant: AgentGrant, task: AgentDelegationTask) => Promise<Pending | AgentDelegationOutcome>;
|
|
271
291
|
/** Submit frozen mail or reuse its recorded Session admission receipt. */
|
|
272
|
-
start: (grant: AgentGrant, raw: AgentDelegationTask) => Promise<
|
|
292
|
+
start: (grant: AgentGrant, raw: AgentDelegationTask) => Promise<DelegationReceipt>;
|
|
273
293
|
/** Match a reply to frozen mail and restore the caller's delegation path. */
|
|
274
294
|
authenticateReply: (mail: BirdDogMail) => AuthenticatedDelegationReply | undefined;
|
|
275
295
|
/** Read an existing acceptance receipt without reserving or delivering mail. */
|
|
276
296
|
receipt: (requestId: string) => Accepted | undefined;
|
|
297
|
+
/** Consume a recipient's stable `<mailKey>:accepted` notice. */
|
|
298
|
+
recordAcceptedNotice: (mail: BirdDogMail) => Accepted | undefined;
|
|
277
299
|
/** The delegated work this owner still waits on, if any. */
|
|
278
300
|
outstanding: () => Promise<OutstandingDelegation | undefined>;
|
|
279
301
|
};
|