@fungi.computer/bird-dog 0.1.0 → 0.2.1

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/dist/effect.d.ts CHANGED
@@ -2,12 +2,25 @@ import type { WatchdogQueuedJob, WatchdogStoredJob } from "@fungi.computer/watch
2
2
  import * as Effect from "effect/Effect";
3
3
  import * as Schema from "effect/Schema";
4
4
  import type { BirdDogAgentJobInput, BirdDogAgentJobPayload, BirdDogMail, BirdDogSettlement } from "./index.js";
5
+ /** The maximum UTF-8 size of one mail body. */
6
+ export declare const BIRD_DOG_MAIL_CONTENT_MAX_BYTES: number;
5
7
  /** Parser for an ordinary agent Session address. */
6
8
  export declare const BirdDogAgentReferenceSchema: Schema.Struct<{
7
9
  readonly kind: Schema.Literal<"agent">;
8
10
  readonly agentId: Schema.String;
9
11
  readonly sessionId: Schema.String;
10
12
  }>;
13
+ /** An addressed Agent may create its first Session when accepting this mail. */
14
+ export declare const BirdDogAgentRecipientReferenceSchema: Schema.Struct<{
15
+ readonly kind: Schema.Literal<"agent">;
16
+ readonly agentId: Schema.String;
17
+ readonly sessionId: Schema.optionalKey<Schema.String>;
18
+ }>;
19
+ /** The Hub's stable system identity for a terminal offline disposition. */
20
+ export declare const BirdDogSystemIdentitySchema: Schema.Struct<{
21
+ readonly kind: Schema.Literal<"system">;
22
+ readonly systemId: Schema.Literal<"hub">;
23
+ }>;
11
24
  /** The most characters a reply's failure diagnostic may carry. */
12
25
  export declare const BIRD_DOG_DIAGNOSTIC_MAX_LENGTH = 200;
13
26
  /**
@@ -15,11 +28,33 @@ export declare const BIRD_DOG_DIAGNOSTIC_MAX_LENGTH = 200;
15
28
  * replying Session's command, its exact Watchdog outcome and, for work that did
16
29
  * not complete, the Session's bounded terminal diagnostic.
17
30
  */
18
- export declare const BirdDogReplySchema: Schema.Struct<{
31
+ export declare const SettledBirdDogReplySchema: Schema.Struct<{
19
32
  readonly commandId: Schema.String;
20
33
  readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
21
34
  readonly diagnostic: Schema.optionalKey<Schema.String>;
22
35
  }>;
36
+ /** A terminal delivery failure, correlated by the original mail key only. */
37
+ export declare const BirdDogUndeliverableReasonSchema: Schema.Literals<readonly ["recipient_offline", "recipient_left"]>;
38
+ export declare const UndeliverableBirdDogReplySchema: Schema.Struct<{
39
+ readonly outcome: Schema.Literal<"undeliverable">;
40
+ readonly reason: Schema.Literals<readonly ["recipient_offline", "recipient_left"]>;
41
+ }>;
42
+ /** Recipient acceptance, before the command has settled. */
43
+ export declare const AcceptedBirdDogReplySchema: Schema.Struct<{
44
+ readonly outcome: Schema.Literal<"accepted">;
45
+ readonly commandId: Schema.String;
46
+ }>;
47
+ export declare const BirdDogReplySchema: Schema.Union<readonly [Schema.Struct<{
48
+ readonly commandId: Schema.String;
49
+ readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
50
+ readonly diagnostic: Schema.optionalKey<Schema.String>;
51
+ }>, Schema.Struct<{
52
+ readonly outcome: Schema.Literal<"undeliverable">;
53
+ readonly reason: Schema.Literals<readonly ["recipient_offline", "recipient_left"]>;
54
+ }>, Schema.Struct<{
55
+ readonly outcome: Schema.Literal<"accepted">;
56
+ readonly commandId: Schema.String;
57
+ }>]>;
23
58
  /**
24
59
  * One secret-safe line of at most `BIRD_DOG_DIAGNOSTIC_MAX_LENGTH` characters
25
60
  * from a Session's terminal diagnostic, or nothing when it is blank. Control
@@ -28,18 +63,53 @@ export declare const BirdDogReplySchema: Schema.Struct<{
28
63
  * line is cut, so a cut never exposes part of one.
29
64
  */
30
65
  export declare function boundedDiagnostic(text: string): string | undefined;
31
- /** Parser for addressed agent correspondence. */
32
- export declare const BirdDogMailSchema: Schema.Struct<{
66
+ export declare const BirdDogMailSchema: Schema.Union<readonly [Schema.Struct<{
67
+ readonly from: Schema.Struct<{
68
+ readonly kind: Schema.Literal<"agent">;
69
+ readonly agentId: Schema.String;
70
+ readonly sessionId: Schema.String;
71
+ }>;
72
+ /** Settlement or acceptance facts, present only on a reply. */
73
+ readonly reply: Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
74
+ readonly commandId: Schema.String;
75
+ readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
76
+ readonly diagnostic: Schema.optionalKey<Schema.String>;
77
+ }>, Schema.Struct<{
78
+ readonly outcome: Schema.Literal<"accepted">;
79
+ readonly commandId: Schema.String;
80
+ }>, Schema.Struct<{
81
+ readonly outcome: Schema.Literal<"undeliverable">;
82
+ readonly reason: Schema.Literal<"recipient_left">;
83
+ }>]>>;
33
84
  readonly key: Schema.String;
34
85
  readonly to: Schema.Struct<{
35
86
  readonly kind: Schema.Literal<"agent">;
36
87
  readonly agentId: Schema.String;
37
- readonly sessionId: Schema.String;
88
+ readonly sessionId: Schema.optionalKey<Schema.String>;
38
89
  }>;
39
- readonly from: Schema.Struct<{
90
+ readonly replyTo: Schema.$Array<Schema.Struct<{
40
91
  readonly kind: Schema.Literal<"agent">;
41
92
  readonly agentId: Schema.String;
42
93
  readonly sessionId: Schema.String;
94
+ }>>;
95
+ readonly content: Schema.String;
96
+ readonly createdAt: Schema.Number;
97
+ readonly meta: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
98
+ }>, Schema.Struct<{
99
+ readonly from: Schema.Struct<{
100
+ readonly kind: Schema.Literal<"system">;
101
+ readonly systemId: Schema.Literal<"hub">;
102
+ }>;
103
+ /** Only the Hub may author this offline disposition. */
104
+ readonly reply: Schema.Struct<{
105
+ readonly outcome: Schema.Literal<"undeliverable">;
106
+ readonly reason: Schema.Literal<"recipient_offline">;
107
+ }>;
108
+ readonly key: Schema.String;
109
+ readonly to: Schema.Struct<{
110
+ readonly kind: Schema.Literal<"agent">;
111
+ readonly agentId: Schema.String;
112
+ readonly sessionId: Schema.optionalKey<Schema.String>;
43
113
  }>;
44
114
  readonly replyTo: Schema.$Array<Schema.Struct<{
45
115
  readonly kind: Schema.Literal<"agent">;
@@ -48,15 +118,8 @@ export declare const BirdDogMailSchema: Schema.Struct<{
48
118
  }>>;
49
119
  readonly content: Schema.String;
50
120
  readonly createdAt: Schema.Number;
51
- /** The sender's opaque host metadata; BirdDog never reads it. */
52
121
  readonly meta: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
53
- /** Settlement facts, present only on a reply BirdDog built. */
54
- readonly reply: Schema.optionalKey<Schema.Struct<{
55
- readonly commandId: Schema.String;
56
- readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
57
- readonly diagnostic: Schema.optionalKey<Schema.String>;
58
- }>>;
59
- }>;
122
+ }>]>;
60
123
  /** Parser for accepted Session work. */
61
124
  export declare const BirdDogAgentWorkSchema: Schema.Struct<{
62
125
  readonly jobId: Schema.String;
@@ -72,17 +135,53 @@ export declare const BirdDogAgentJobPayloadSchema: Schema.Struct<{
72
135
  readonly sessionId: Schema.String;
73
136
  readonly commandFactId: Schema.String;
74
137
  }>;
75
- readonly mail: Schema.optional<Schema.Struct<{
138
+ readonly mail: Schema.optional<Schema.Union<readonly [Schema.Struct<{
139
+ readonly from: Schema.Struct<{
140
+ readonly kind: Schema.Literal<"agent">;
141
+ readonly agentId: Schema.String;
142
+ readonly sessionId: Schema.String;
143
+ }>;
144
+ /** Settlement or acceptance facts, present only on a reply. */
145
+ readonly reply: Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
146
+ readonly commandId: Schema.String;
147
+ readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
148
+ readonly diagnostic: Schema.optionalKey<Schema.String>;
149
+ }>, Schema.Struct<{
150
+ readonly outcome: Schema.Literal<"accepted">;
151
+ readonly commandId: Schema.String;
152
+ }>, Schema.Struct<{
153
+ readonly outcome: Schema.Literal<"undeliverable">;
154
+ readonly reason: Schema.Literal<"recipient_left">;
155
+ }>]>>;
76
156
  readonly key: Schema.String;
77
157
  readonly to: Schema.Struct<{
78
158
  readonly kind: Schema.Literal<"agent">;
79
159
  readonly agentId: Schema.String;
80
- readonly sessionId: Schema.String;
160
+ readonly sessionId: Schema.optionalKey<Schema.String>;
81
161
  }>;
82
- readonly from: Schema.Struct<{
162
+ readonly replyTo: Schema.$Array<Schema.Struct<{
83
163
  readonly kind: Schema.Literal<"agent">;
84
164
  readonly agentId: Schema.String;
85
165
  readonly sessionId: Schema.String;
166
+ }>>;
167
+ readonly content: Schema.String;
168
+ readonly createdAt: Schema.Number;
169
+ readonly meta: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
170
+ }>, Schema.Struct<{
171
+ readonly from: Schema.Struct<{
172
+ readonly kind: Schema.Literal<"system">;
173
+ readonly systemId: Schema.Literal<"hub">;
174
+ }>;
175
+ /** Only the Hub may author this offline disposition. */
176
+ readonly reply: Schema.Struct<{
177
+ readonly outcome: Schema.Literal<"undeliverable">;
178
+ readonly reason: Schema.Literal<"recipient_offline">;
179
+ }>;
180
+ readonly key: Schema.String;
181
+ readonly to: Schema.Struct<{
182
+ readonly kind: Schema.Literal<"agent">;
183
+ readonly agentId: Schema.String;
184
+ readonly sessionId: Schema.optionalKey<Schema.String>;
86
185
  }>;
87
186
  readonly replyTo: Schema.$Array<Schema.Struct<{
88
187
  readonly kind: Schema.Literal<"agent">;
@@ -91,15 +190,8 @@ export declare const BirdDogAgentJobPayloadSchema: Schema.Struct<{
91
190
  }>>;
92
191
  readonly content: Schema.String;
93
192
  readonly createdAt: Schema.Number;
94
- /** The sender's opaque host metadata; BirdDog never reads it. */
95
193
  readonly meta: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
96
- /** Settlement facts, present only on a reply BirdDog built. */
97
- readonly reply: Schema.optionalKey<Schema.Struct<{
98
- readonly commandId: Schema.String;
99
- readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
100
- readonly diagnostic: Schema.optionalKey<Schema.String>;
101
- }>>;
102
- }>>;
194
+ }>]>>;
103
195
  }>;
104
196
  /** Parser for accepted work entering BirdDog's transactional projection. */
105
197
  export declare const BirdDogAgentJobInputSchema: Schema.Struct<{
@@ -111,17 +203,53 @@ export declare const BirdDogAgentJobInputSchema: Schema.Struct<{
111
203
  readonly maxRecoveries: Schema.Int;
112
204
  }>>;
113
205
  }>;
114
- readonly mail: Schema.optional<Schema.Struct<{
206
+ readonly mail: Schema.optional<Schema.Union<readonly [Schema.Struct<{
207
+ readonly from: Schema.Struct<{
208
+ readonly kind: Schema.Literal<"agent">;
209
+ readonly agentId: Schema.String;
210
+ readonly sessionId: Schema.String;
211
+ }>;
212
+ /** Settlement or acceptance facts, present only on a reply. */
213
+ readonly reply: Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
214
+ readonly commandId: Schema.String;
215
+ readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
216
+ readonly diagnostic: Schema.optionalKey<Schema.String>;
217
+ }>, Schema.Struct<{
218
+ readonly outcome: Schema.Literal<"accepted">;
219
+ readonly commandId: Schema.String;
220
+ }>, Schema.Struct<{
221
+ readonly outcome: Schema.Literal<"undeliverable">;
222
+ readonly reason: Schema.Literal<"recipient_left">;
223
+ }>]>>;
115
224
  readonly key: Schema.String;
116
225
  readonly to: Schema.Struct<{
117
226
  readonly kind: Schema.Literal<"agent">;
118
227
  readonly agentId: Schema.String;
119
- readonly sessionId: Schema.String;
228
+ readonly sessionId: Schema.optionalKey<Schema.String>;
120
229
  }>;
121
- readonly from: Schema.Struct<{
230
+ readonly replyTo: Schema.$Array<Schema.Struct<{
122
231
  readonly kind: Schema.Literal<"agent">;
123
232
  readonly agentId: Schema.String;
124
233
  readonly sessionId: Schema.String;
234
+ }>>;
235
+ readonly content: Schema.String;
236
+ readonly createdAt: Schema.Number;
237
+ readonly meta: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
238
+ }>, Schema.Struct<{
239
+ readonly from: Schema.Struct<{
240
+ readonly kind: Schema.Literal<"system">;
241
+ readonly systemId: Schema.Literal<"hub">;
242
+ }>;
243
+ /** Only the Hub may author this offline disposition. */
244
+ readonly reply: Schema.Struct<{
245
+ readonly outcome: Schema.Literal<"undeliverable">;
246
+ readonly reason: Schema.Literal<"recipient_offline">;
247
+ }>;
248
+ readonly key: Schema.String;
249
+ readonly to: Schema.Struct<{
250
+ readonly kind: Schema.Literal<"agent">;
251
+ readonly agentId: Schema.String;
252
+ readonly sessionId: Schema.optionalKey<Schema.String>;
125
253
  }>;
126
254
  readonly replyTo: Schema.$Array<Schema.Struct<{
127
255
  readonly kind: Schema.Literal<"agent">;
@@ -130,15 +258,8 @@ export declare const BirdDogAgentJobInputSchema: Schema.Struct<{
130
258
  }>>;
131
259
  readonly content: Schema.String;
132
260
  readonly createdAt: Schema.Number;
133
- /** The sender's opaque host metadata; BirdDog never reads it. */
134
261
  readonly meta: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
135
- /** Settlement facts, present only on a reply BirdDog built. */
136
- readonly reply: Schema.optionalKey<Schema.Struct<{
137
- readonly commandId: Schema.String;
138
- readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
139
- readonly diagnostic: Schema.optionalKey<Schema.String>;
140
- }>>;
141
- }>>;
262
+ }>]>>;
142
263
  }>;
143
264
  /** BirdDog could not complete one owned store or transport operation. */
144
265
  export declare class BirdDogError extends Error {
@@ -172,6 +293,8 @@ export type EffectBirdDogOptions = Readonly<{
172
293
  export type BirdDogEffect = Readonly<{
173
294
  /** Queue replies owed by a settled agent job. */
174
295
  fanOut: (settlement: BirdDogSettlement) => Effect.Effect<void, BirdDogError>;
296
+ /** Durably queue one authored mail item in this Agent's existing outbox. */
297
+ postMail?: ((mail: BirdDogMail) => Effect.Effect<void, BirdDogError>) | undefined;
175
298
  /** Deliver due replies and schedule any required retry wake. */
176
299
  drainOutbox: () => Effect.Effect<void, BirdDogError>;
177
300
  /** Earliest owed reply delivery, including retries; absent after delivery. */
@@ -189,6 +312,8 @@ export declare const decodeAgentJob: (job: WatchdogStoredJob) => BirdDogAgentJob
189
312
  * are presented. A reply never carries the settled work's host metadata.
190
313
  */
191
314
  export declare function settlementReplies(settlement: BirdDogSettlement, createdAt: number): readonly BirdDogMail[];
315
+ /** Build the recipient's one stable acceptance notice for one original mail. */
316
+ export declare function acceptedNotice(mail: BirdDogMail, sessionId: string, commandId: string, createdAt: number): BirdDogMail;
192
317
  /**
193
318
  * Whether this mail room's outbox still owes any reply a delivery attempt.
194
319
  * Delivered receipts and exhausted dead letters stay but owe nothing.
package/dist/effect.js CHANGED
@@ -2,12 +2,29 @@ import * as Effect from "effect/Effect";
2
2
  import * as Schema from "effect/Schema";
3
3
  import { makePostOutbox, postOutboxPending, } from "@fungi.computer/carrier/post";
4
4
  const Identifier = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(256));
5
+ /** The maximum UTF-8 size of one mail body. */
6
+ export const BIRD_DOG_MAIL_CONTENT_MAX_BYTES = 256 * 1024;
7
+ const MailContent = Schema.String.check(Schema.makeFilter((content) => new TextEncoder().encode(content).byteLength <=
8
+ BIRD_DOG_MAIL_CONTENT_MAX_BYTES, {
9
+ message: `Mail content must be at most ${String(BIRD_DOG_MAIL_CONTENT_MAX_BYTES)} UTF-8 bytes`,
10
+ }));
5
11
  /** Parser for an ordinary agent Session address. */
6
12
  export const BirdDogAgentReferenceSchema = Schema.Struct({
7
13
  kind: Schema.Literal("agent"),
8
14
  agentId: Identifier,
9
15
  sessionId: Identifier,
10
16
  });
17
+ /** An addressed Agent may create its first Session when accepting this mail. */
18
+ export const BirdDogAgentRecipientReferenceSchema = Schema.Struct({
19
+ kind: Schema.Literal("agent"),
20
+ agentId: Identifier,
21
+ sessionId: Schema.optionalKey(Identifier),
22
+ });
23
+ /** The Hub's stable system identity for a terminal offline disposition. */
24
+ export const BirdDogSystemIdentitySchema = Schema.Struct({
25
+ kind: Schema.Literal("system"),
26
+ systemId: Schema.Literal("hub"),
27
+ });
11
28
  /** The most characters a reply's failure diagnostic may carry. */
12
29
  export const BIRD_DOG_DIAGNOSTIC_MAX_LENGTH = 200;
13
30
  /**
@@ -15,7 +32,7 @@ export const BIRD_DOG_DIAGNOSTIC_MAX_LENGTH = 200;
15
32
  * replying Session's command, its exact Watchdog outcome and, for work that did
16
33
  * not complete, the Session's bounded terminal diagnostic.
17
34
  */
18
- export const BirdDogReplySchema = Schema.Struct({
35
+ export const SettledBirdDogReplySchema = Schema.Struct({
19
36
  commandId: Identifier,
20
37
  outcome: Schema.Literals([
21
38
  "completed",
@@ -26,6 +43,33 @@ export const BirdDogReplySchema = Schema.Struct({
26
43
  ]),
27
44
  diagnostic: Schema.optionalKey(Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(BIRD_DOG_DIAGNOSTIC_MAX_LENGTH))),
28
45
  });
46
+ /** A terminal delivery failure, correlated by the original mail key only. */
47
+ export const BirdDogUndeliverableReasonSchema = Schema.Literals([
48
+ "recipient_offline",
49
+ "recipient_left",
50
+ ]);
51
+ export const UndeliverableBirdDogReplySchema = Schema.Struct({
52
+ outcome: Schema.Literal("undeliverable"),
53
+ reason: BirdDogUndeliverableReasonSchema,
54
+ });
55
+ /** Recipient acceptance, before the command has settled. */
56
+ export const AcceptedBirdDogReplySchema = Schema.Struct({
57
+ outcome: Schema.Literal("accepted"),
58
+ commandId: Identifier,
59
+ });
60
+ export const BirdDogReplySchema = Schema.Union([
61
+ SettledBirdDogReplySchema,
62
+ UndeliverableBirdDogReplySchema,
63
+ AcceptedBirdDogReplySchema,
64
+ ]);
65
+ const BirdDogAgentReplySchema = Schema.Union([
66
+ SettledBirdDogReplySchema,
67
+ AcceptedBirdDogReplySchema,
68
+ Schema.Struct({
69
+ outcome: Schema.Literal("undeliverable"),
70
+ reason: Schema.Literal("recipient_left"),
71
+ }),
72
+ ]);
29
73
  const REDACTED = "[redacted]";
30
74
  // An authorization scheme's credential, e.g. `Bearer <token>`.
31
75
  const schemeCredential = /\b(bearer|basic)\s+[^\s"',;]+/giu;
@@ -53,18 +97,32 @@ export function boundedDiagnostic(text) {
53
97
  return line.length === 0 ? undefined : line;
54
98
  }
55
99
  /** Parser for addressed agent correspondence. */
56
- export const BirdDogMailSchema = Schema.Struct({
100
+ const BirdDogMailFields = {
57
101
  key: Identifier,
58
- to: BirdDogAgentReferenceSchema,
59
- from: BirdDogAgentReferenceSchema,
102
+ to: BirdDogAgentRecipientReferenceSchema,
60
103
  replyTo: Schema.Array(BirdDogAgentReferenceSchema),
61
- content: Schema.String,
104
+ content: MailContent,
62
105
  createdAt: Schema.Number.check(Schema.isGreaterThanOrEqualTo(0)),
63
106
  /** The sender's opaque host metadata; BirdDog never reads it. */
64
107
  meta: Schema.optionalKey(Schema.Json),
65
- /** Settlement facts, present only on a reply BirdDog built. */
66
- reply: Schema.optionalKey(BirdDogReplySchema),
67
- });
108
+ };
109
+ export const BirdDogMailSchema = Schema.Union([
110
+ Schema.Struct({
111
+ ...BirdDogMailFields,
112
+ from: BirdDogAgentReferenceSchema,
113
+ /** Settlement or acceptance facts, present only on a reply. */
114
+ reply: Schema.optionalKey(BirdDogAgentReplySchema),
115
+ }),
116
+ Schema.Struct({
117
+ ...BirdDogMailFields,
118
+ from: BirdDogSystemIdentitySchema,
119
+ /** Only the Hub may author this offline disposition. */
120
+ reply: Schema.Struct({
121
+ outcome: Schema.Literal("undeliverable"),
122
+ reason: Schema.Literal("recipient_offline"),
123
+ }),
124
+ }),
125
+ ]);
68
126
  /** Parser for accepted Session work. */
69
127
  export const BirdDogAgentWorkSchema = Schema.Struct({
70
128
  jobId: Identifier,
@@ -135,13 +193,13 @@ export function settlementReplies(settlement, createdAt) {
135
193
  settlement.diagnostic === undefined
136
194
  ? undefined
137
195
  : boundedDiagnostic(settlement.diagnostic);
138
- const reply = Schema.decodeUnknownSync(BirdDogReplySchema)(diagnostic === undefined
196
+ const reply = Schema.decodeUnknownSync(SettledBirdDogReplySchema)(diagnostic === undefined
139
197
  ? { commandId: work.commandFactId, outcome }
140
198
  : { commandId: work.commandFactId, outcome, diagnostic });
141
199
  return mail.replyTo.map((destination, index) => ({
142
200
  key: `${mail.key}:reply:${String(index)}`,
143
201
  to: destination,
144
- from: mail.to,
202
+ from: { ...mail.to, sessionId: work.sessionId },
145
203
  replyTo: [],
146
204
  content: settlement.content.length > 0
147
205
  ? settlement.content
@@ -150,6 +208,24 @@ export function settlementReplies(settlement, createdAt) {
150
208
  reply,
151
209
  }));
152
210
  }
211
+ /** Build the recipient's one stable acceptance notice for one original mail. */
212
+ export function acceptedNotice(mail, sessionId, commandId, createdAt) {
213
+ const recipientSession = Schema.decodeUnknownSync(Identifier)(sessionId);
214
+ const sender = Schema.decodeUnknownSync(BirdDogAgentReferenceSchema)(mail.from);
215
+ const reply = Schema.decodeUnknownSync(AcceptedBirdDogReplySchema)({
216
+ outcome: "accepted",
217
+ commandId,
218
+ });
219
+ return {
220
+ key: `${mail.key}:accepted`,
221
+ to: sender,
222
+ from: { ...mail.to, sessionId: recipientSession },
223
+ replyTo: [],
224
+ content: "Work accepted.",
225
+ createdAt,
226
+ reply,
227
+ };
228
+ }
153
229
  /**
154
230
  * Whether this mail room's outbox still owes any reply a delivery attempt.
155
231
  * Delivered receipts and exhausted dead letters stay but owe nothing.
@@ -182,8 +258,16 @@ export const make = (options) => {
182
258
  if (wake !== undefined)
183
259
  yield* options.scheduleWake(wake);
184
260
  });
261
+ const postMail = (mail) => Effect.gen(function* () {
262
+ const createdAt = options.now();
263
+ const hint = yield* post.enqueue(mail, createdAt);
264
+ if (hint !== undefined)
265
+ yield* options.scheduleWake(hint);
266
+ return;
267
+ });
185
268
  return Effect.succeed({
186
269
  fanOut,
270
+ postMail,
187
271
  drainOutbox: post.drain,
188
272
  nextWakeAt: post.nextWakeAt,
189
273
  });
package/dist/index.d.ts CHANGED
@@ -6,18 +6,16 @@ export type BirdDogAgentReference = Readonly<{
6
6
  agentId: string;
7
7
  sessionId: string;
8
8
  }>;
9
- /** Addressed agent correspondence carried with durable work when a reply is owed. */
10
- export type BirdDogMail = Readonly<{
11
- key: string;
12
- to: BirdDogAgentReference;
13
- from: BirdDogAgentReference;
14
- replyTo: readonly BirdDogAgentReference[];
15
- content: string;
16
- createdAt: number;
17
- /** The sender's opaque host metadata; BirdDog never reads it. */
18
- meta?: BirdDogMeta;
19
- /** Settlement facts, present only on a reply BirdDog built. */
20
- reply?: BirdDogReply;
9
+ /** An addressed Agent whose first Session may be created on acceptance. */
10
+ export type BirdDogAgentRecipientReference = Readonly<{
11
+ kind: "agent";
12
+ agentId: string;
13
+ sessionId?: string;
14
+ }>;
15
+ /** The Hub's stable system identity for a terminal offline disposition. */
16
+ export type BirdDogSystemIdentity = Readonly<{
17
+ kind: "system";
18
+ systemId: "hub";
21
19
  }>;
22
20
  /**
23
21
  * What a reply reports about the settled work beside its content. The
@@ -25,13 +23,59 @@ export type BirdDogMail = Readonly<{
25
23
  * against the mail it sent.
26
24
  */
27
25
  export type BirdDogReply = Readonly<{
26
+ /** The recipient accepted the work into the named Session. */
27
+ commandId: string;
28
+ outcome: "accepted";
29
+ }> | Readonly<{
28
30
  /** The replying Session's accepted command: the sender's receipt `commandId`. */
29
31
  commandId: string;
30
32
  /** The exact Watchdog outcome; failures are never collapsed. */
31
33
  outcome: "completed" | "failed" | "interrupted" | "cancelled" | "outcome_unknown";
32
34
  /** Bounded terminal diagnostic for work that did not complete or cancel. */
33
35
  diagnostic?: string;
36
+ }> | Readonly<{
37
+ outcome: "undeliverable";
38
+ reason: "recipient_offline" | "recipient_left";
39
+ }>;
40
+ /** Facts an Agent may author; the Hub owns recipient-offline disposition. */
41
+ export type BirdDogAgentReply = Exclude<BirdDogReply, {
42
+ outcome: "undeliverable";
43
+ }> | Readonly<{
44
+ outcome: "undeliverable";
45
+ reason: "recipient_left";
46
+ }>;
47
+ /**
48
+ * Addressed correspondence. Agents author ordinary replies and recipient-left
49
+ * notices; only the Hub system authors recipient-offline dispositions.
50
+ */
51
+ export type BirdDogMail = Readonly<{
52
+ key: string;
53
+ to: BirdDogAgentRecipientReference;
54
+ replyTo: readonly BirdDogAgentReference[];
55
+ content: string;
56
+ createdAt: number;
57
+ /** The sender's opaque host metadata; BirdDog never reads it. */
58
+ meta?: BirdDogMeta;
59
+ from: BirdDogAgentReference;
60
+ /** Settlement or acceptance facts, present only on a reply. */
61
+ reply?: BirdDogAgentReply;
62
+ }> | Readonly<{
63
+ key: string;
64
+ to: BirdDogAgentRecipientReference;
65
+ replyTo: readonly BirdDogAgentReference[];
66
+ content: string;
67
+ createdAt: number;
68
+ /** The sender's opaque host metadata; BirdDog never reads it. */
69
+ meta?: BirdDogMeta;
70
+ /** The Hub owns offline disposition authorship, not the recipient. */
71
+ from: BirdDogSystemIdentity;
72
+ reply: Readonly<{
73
+ outcome: "undeliverable";
74
+ reason: "recipient_offline";
75
+ }>;
34
76
  }>;
77
+ /** Construct the one stable system reply correlated to the exact sent letter. */
78
+ export declare function undeliverableReply(original: BirdDogMail, createdAt: number): BirdDogMail;
35
79
  /** Opaque JSON a host attached to the work; BirdDog only carries it. */
36
80
  export type BirdDogMeta = typeof Schema.Json.Type;
37
81
  /** The accepted Session work BirdDog points at from an opaque Watchdog job. */
package/dist/index.js CHANGED
@@ -1,5 +1,19 @@
1
1
  import { agentJobFromInput, decodeAgentJob } from "./effect.js";
2
2
  import { createPromiseBirdDog } from "./internal/promise-mail-room.js";
3
+ /** Construct the one stable system reply correlated to the exact sent letter. */
4
+ export function undeliverableReply(original, createdAt) {
5
+ if (original.reply !== undefined || original.from.kind !== "agent")
6
+ throw new Error("A reply cannot receive another undeliverable disposition");
7
+ return {
8
+ key: `${original.key}:reply:0`,
9
+ from: { kind: "system", systemId: "hub" },
10
+ to: original.from,
11
+ replyTo: [],
12
+ content: "undeliverable",
13
+ createdAt,
14
+ reply: { outcome: "undeliverable", reason: "recipient_offline" },
15
+ };
16
+ }
3
17
  /** Agent-aware mail translation over Watchdog's payload-blind job grammar. */
4
18
  export const BirdDog = Object.freeze({
5
19
  agentJob: agentJobFromInput,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/bird-dog",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "description": "Durable addressed agent mail over generic Watchdog work",
6
6
  "type": "module",
@@ -23,7 +23,7 @@
23
23
  "LICENSE"
24
24
  ],
25
25
  "dependencies": {
26
- "@fungi.computer/carrier": "0.1.0",
26
+ "@fungi.computer/carrier": "0.2.0",
27
27
  "@fungi.computer/watchdog": "0.1.0",
28
28
  "effect": "4.0.2"
29
29
  },