@fungi.computer/bird-dog 0.2.1 → 0.3.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/README.md CHANGED
@@ -74,6 +74,7 @@ const room = await BirdDog.make({
74
74
 
75
75
  // 1. Turn accepted work into a Watchdog job. Hand this to Watchdog's ingest.
76
76
  const job = BirdDog.agentJob({
77
+ delivery: "followUp",
77
78
  work: {
78
79
  jobId: "job-1",
79
80
  sessionId: child.sessionId,
@@ -114,14 +115,14 @@ are 1 to 256 characters. There is no special kind for subagents.
114
115
  must identify the request across retries. BirdDog derives every reply key from
115
116
  it.
116
117
 
117
- **Agent jobs.** `BirdDog.agentJob({ work, mail? })` validates the input and
118
- returns a queued Watchdog job. The job's `queue` is the target `sessionId`, its
119
- lane is `default`, its priority is `0`, and its payload carries the `sessionId`,
120
- the `commandFactId` and the optional mail. `work.recovery` passes straight
121
- through to Watchdog's recovery policy. Because it is synchronous, you can call
122
- it inside an owner transaction and pass the result to Watchdog's
123
- `transactional.enqueueAcceptedJob`. `BirdDog.decodeAgentJob(job)` parses the
124
- payload back on the executor side. Watchdog never reads it.
118
+ **Agent jobs.** `BirdDog.agentJob({ work, delivery, mail? })` validates the
119
+ input and returns a queued Watchdog job. The job's `queue` is the target
120
+ `sessionId`, its lane is `default`, its priority is `0`, and its payload carries
121
+ the `sessionId`, the `commandFactId` and the optional mail. `work.recovery`
122
+ passes straight through to Watchdog's recovery policy. Because it is
123
+ synchronous, you can call it inside an owner transaction and pass the result to
124
+ Watchdog's `transactional.enqueueAcceptedJob`. `BirdDog.decodeAgentJob(job)`
125
+ parses the payload back on the executor side. Watchdog never reads it.
125
126
 
126
127
  **Fan-out.** `fanOut({ job, content, diagnostic? })` takes a settled job, the
127
128
  content owed to correspondents and the Session's terminal diagnostic. It writes
package/dist/effect.d.ts CHANGED
@@ -23,18 +23,37 @@ export declare const BirdDogSystemIdentitySchema: Schema.Struct<{
23
23
  }>;
24
24
  /** The most characters a reply's failure diagnostic may carry. */
25
25
  export declare const BIRD_DOG_DIAGNOSTIC_MAX_LENGTH = 200;
26
+ /** The most open items one reply names as waiting on its recipient. */
27
+ export declare const BIRD_DOG_WAITING_MAX_ITEMS = 8;
28
+ /**
29
+ * One open item of the replying Session that waits on this reply's recipient.
30
+ * BirdDog carries the three bounded fields; the replying host owns their
31
+ * meaning and the recipient host owns every decision about them.
32
+ */
33
+ export declare const BirdDogWaitingItemSchema: Schema.Struct<{
34
+ readonly itemId: Schema.String;
35
+ readonly kind: Schema.String;
36
+ readonly summary: Schema.String;
37
+ }>;
26
38
  /**
27
39
  * Parser for the settlement facts a reply carries beside its content: the
28
- * replying Session's command, its exact Watchdog outcome and, for work that did
29
- * not complete, the Session's bounded terminal diagnostic.
40
+ * replying Session's command, its exact Watchdog outcome, for work that did
41
+ * not complete the Session's bounded terminal diagnostic and, when the
42
+ * replying host names any, the open items now waiting on the recipient.
30
43
  */
31
44
  export declare const SettledBirdDogReplySchema: Schema.Struct<{
32
45
  readonly commandId: Schema.String;
33
46
  readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
34
47
  readonly diagnostic: Schema.optionalKey<Schema.String>;
48
+ readonly waitingOnYou: Schema.optionalKey<Schema.$Array<Schema.Struct<{
49
+ readonly itemId: Schema.String;
50
+ readonly kind: Schema.String;
51
+ readonly summary: Schema.String;
52
+ }>>>;
35
53
  }>;
36
54
  /** A terminal delivery failure, correlated by the original mail key only. */
37
55
  export declare const BirdDogUndeliverableReasonSchema: Schema.Literals<readonly ["recipient_offline", "recipient_left"]>;
56
+ /** Parser for a terminal delivery failure reply. */
38
57
  export declare const UndeliverableBirdDogReplySchema: Schema.Struct<{
39
58
  readonly outcome: Schema.Literal<"undeliverable">;
40
59
  readonly reason: Schema.Literals<readonly ["recipient_offline", "recipient_left"]>;
@@ -44,10 +63,16 @@ export declare const AcceptedBirdDogReplySchema: Schema.Struct<{
44
63
  readonly outcome: Schema.Literal<"accepted">;
45
64
  readonly commandId: Schema.String;
46
65
  }>;
66
+ /** Parser for any reply: settled, accepted or undeliverable. */
47
67
  export declare const BirdDogReplySchema: Schema.Union<readonly [Schema.Struct<{
48
68
  readonly commandId: Schema.String;
49
69
  readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
50
70
  readonly diagnostic: Schema.optionalKey<Schema.String>;
71
+ readonly waitingOnYou: Schema.optionalKey<Schema.$Array<Schema.Struct<{
72
+ readonly itemId: Schema.String;
73
+ readonly kind: Schema.String;
74
+ readonly summary: Schema.String;
75
+ }>>>;
51
76
  }>, Schema.Struct<{
52
77
  readonly outcome: Schema.Literal<"undeliverable">;
53
78
  readonly reason: Schema.Literals<readonly ["recipient_offline", "recipient_left"]>;
@@ -63,6 +88,7 @@ export declare const BirdDogReplySchema: Schema.Union<readonly [Schema.Struct<{
63
88
  * line is cut, so a cut never exposes part of one.
64
89
  */
65
90
  export declare function boundedDiagnostic(text: string): string | undefined;
91
+ /** Parser for addressed Agent mail, from an Agent or from the Hub system. */
66
92
  export declare const BirdDogMailSchema: Schema.Union<readonly [Schema.Struct<{
67
93
  readonly from: Schema.Struct<{
68
94
  readonly kind: Schema.Literal<"agent">;
@@ -74,6 +100,11 @@ export declare const BirdDogMailSchema: Schema.Union<readonly [Schema.Struct<{
74
100
  readonly commandId: Schema.String;
75
101
  readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
76
102
  readonly diagnostic: Schema.optionalKey<Schema.String>;
103
+ readonly waitingOnYou: Schema.optionalKey<Schema.$Array<Schema.Struct<{
104
+ readonly itemId: Schema.String;
105
+ readonly kind: Schema.String;
106
+ readonly summary: Schema.String;
107
+ }>>>;
77
108
  }>, Schema.Struct<{
78
109
  readonly outcome: Schema.Literal<"accepted">;
79
110
  readonly commandId: Schema.String;
@@ -146,6 +177,11 @@ export declare const BirdDogAgentJobPayloadSchema: Schema.Struct<{
146
177
  readonly commandId: Schema.String;
147
178
  readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
148
179
  readonly diagnostic: Schema.optionalKey<Schema.String>;
180
+ readonly waitingOnYou: Schema.optionalKey<Schema.$Array<Schema.Struct<{
181
+ readonly itemId: Schema.String;
182
+ readonly kind: Schema.String;
183
+ readonly summary: Schema.String;
184
+ }>>>;
149
185
  }>, Schema.Struct<{
150
186
  readonly outcome: Schema.Literal<"accepted">;
151
187
  readonly commandId: Schema.String;
@@ -203,6 +239,7 @@ export declare const BirdDogAgentJobInputSchema: Schema.Struct<{
203
239
  readonly maxRecoveries: Schema.Int;
204
240
  }>>;
205
241
  }>;
242
+ readonly delivery: Schema.Literals<readonly ["steer", "followUp"]>;
206
243
  readonly mail: Schema.optional<Schema.Union<readonly [Schema.Struct<{
207
244
  readonly from: Schema.Struct<{
208
245
  readonly kind: Schema.Literal<"agent">;
@@ -214,6 +251,11 @@ export declare const BirdDogAgentJobInputSchema: Schema.Struct<{
214
251
  readonly commandId: Schema.String;
215
252
  readonly outcome: Schema.Literals<readonly ["completed", "failed", "interrupted", "cancelled", "outcome_unknown"]>;
216
253
  readonly diagnostic: Schema.optionalKey<Schema.String>;
254
+ readonly waitingOnYou: Schema.optionalKey<Schema.$Array<Schema.Struct<{
255
+ readonly itemId: Schema.String;
256
+ readonly kind: Schema.String;
257
+ readonly summary: Schema.String;
258
+ }>>>;
217
259
  }>, Schema.Struct<{
218
260
  readonly outcome: Schema.Literal<"accepted">;
219
261
  readonly commandId: Schema.String;
@@ -307,9 +349,15 @@ export declare const decodeAgentJob: (job: WatchdogStoredJob) => BirdDogAgentJob
307
349
  /**
308
350
  * Build the replies owed by one settlement for a host-owned Post transport.
309
351
  * Each reply carries the settled content verbatim (or, when there is none, one
310
- * sentence naming the outcome, so the reply is always admissible text) and its
311
- * settlement facts as typed fields; the recipient's host authenticates them and decides how they
312
- * are presented. A reply never carries the settled work's host metadata.
352
+ * sentence naming the outcome and its diagnostic, so the reply is always
353
+ * admissible text) and its settlement facts as typed fields; the recipient's
354
+ * host authenticates them and decides how they are presented. A reply never
355
+ * carries the settled work's host metadata.
356
+ *
357
+ * Work a delegator's mail admitted replies to that mail under its `:reply:`
358
+ * key. Any other settled work, whatever started its turn, is a report to the
359
+ * Session's delegator of record, when the host names one (it does so once the
360
+ * Session is idle), under the delegator mail's `:report:` key.
313
361
  */
314
362
  export declare function settlementReplies(settlement: BirdDogSettlement, createdAt: number): readonly BirdDogMail[];
315
363
  /** Build the recipient's one stable acceptance notice for one original mail. */
package/dist/effect.js CHANGED
@@ -27,10 +27,23 @@ export const BirdDogSystemIdentitySchema = Schema.Struct({
27
27
  });
28
28
  /** The most characters a reply's failure diagnostic may carry. */
29
29
  export const BIRD_DOG_DIAGNOSTIC_MAX_LENGTH = 200;
30
+ /** The most open items one reply names as waiting on its recipient. */
31
+ export const BIRD_DOG_WAITING_MAX_ITEMS = 8;
32
+ /**
33
+ * One open item of the replying Session that waits on this reply's recipient.
34
+ * BirdDog carries the three bounded fields; the replying host owns their
35
+ * meaning and the recipient host owns every decision about them.
36
+ */
37
+ export const BirdDogWaitingItemSchema = Schema.Struct({
38
+ itemId: Identifier,
39
+ kind: Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(64)),
40
+ summary: Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(200)),
41
+ });
30
42
  /**
31
43
  * Parser for the settlement facts a reply carries beside its content: the
32
- * replying Session's command, its exact Watchdog outcome and, for work that did
33
- * not complete, the Session's bounded terminal diagnostic.
44
+ * replying Session's command, its exact Watchdog outcome, for work that did
45
+ * not complete the Session's bounded terminal diagnostic and, when the
46
+ * replying host names any, the open items now waiting on the recipient.
34
47
  */
35
48
  export const SettledBirdDogReplySchema = Schema.Struct({
36
49
  commandId: Identifier,
@@ -42,12 +55,14 @@ export const SettledBirdDogReplySchema = Schema.Struct({
42
55
  "outcome_unknown",
43
56
  ]),
44
57
  diagnostic: Schema.optionalKey(Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(BIRD_DOG_DIAGNOSTIC_MAX_LENGTH))),
58
+ waitingOnYou: Schema.optionalKey(Schema.Array(BirdDogWaitingItemSchema).check(Schema.isMinLength(1), Schema.isMaxLength(BIRD_DOG_WAITING_MAX_ITEMS))),
45
59
  });
46
60
  /** A terminal delivery failure, correlated by the original mail key only. */
47
61
  export const BirdDogUndeliverableReasonSchema = Schema.Literals([
48
62
  "recipient_offline",
49
63
  "recipient_left",
50
64
  ]);
65
+ /** Parser for a terminal delivery failure reply. */
51
66
  export const UndeliverableBirdDogReplySchema = Schema.Struct({
52
67
  outcome: Schema.Literal("undeliverable"),
53
68
  reason: BirdDogUndeliverableReasonSchema,
@@ -57,6 +72,7 @@ export const AcceptedBirdDogReplySchema = Schema.Struct({
57
72
  outcome: Schema.Literal("accepted"),
58
73
  commandId: Identifier,
59
74
  });
75
+ /** Parser for any reply: settled, accepted or undeliverable. */
60
76
  export const BirdDogReplySchema = Schema.Union([
61
77
  SettledBirdDogReplySchema,
62
78
  UndeliverableBirdDogReplySchema,
@@ -106,6 +122,7 @@ const BirdDogMailFields = {
106
122
  /** The sender's opaque host metadata; BirdDog never reads it. */
107
123
  meta: Schema.optionalKey(Schema.Json),
108
124
  };
125
+ /** Parser for addressed Agent mail, from an Agent or from the Hub system. */
109
126
  export const BirdDogMailSchema = Schema.Union([
110
127
  Schema.Struct({
111
128
  ...BirdDogMailFields,
@@ -143,6 +160,7 @@ export const BirdDogAgentJobPayloadSchema = Schema.Struct({
143
160
  /** Parser for accepted work entering BirdDog's transactional projection. */
144
161
  export const BirdDogAgentJobInputSchema = Schema.Struct({
145
162
  work: BirdDogAgentWorkSchema,
163
+ delivery: Schema.Literals(["steer", "followUp"]),
146
164
  mail: Schema.optional(BirdDogMailSchema),
147
165
  });
148
166
  /** BirdDog could not complete one owned store or transport operation. */
@@ -166,7 +184,7 @@ export const agentJobFromInput = (input) => {
166
184
  jobId: accepted.work.jobId,
167
185
  queue: accepted.work.sessionId,
168
186
  lane: "default",
169
- priority: 0,
187
+ priority: accepted.delivery === "steer" ? 1 : 0,
170
188
  payload,
171
189
  state: "queued",
172
190
  };
@@ -179,13 +197,23 @@ export const decodeAgentJob = (job) => Schema.decodeUnknownSync(BirdDogAgentJobP
179
197
  /**
180
198
  * Build the replies owed by one settlement for a host-owned Post transport.
181
199
  * Each reply carries the settled content verbatim (or, when there is none, one
182
- * sentence naming the outcome, so the reply is always admissible text) and its
183
- * settlement facts as typed fields; the recipient's host authenticates them and decides how they
184
- * are presented. A reply never carries the settled work's host metadata.
200
+ * sentence naming the outcome and its diagnostic, so the reply is always
201
+ * admissible text) and its settlement facts as typed fields; the recipient's
202
+ * host authenticates them and decides how they are presented. A reply never
203
+ * carries the settled work's host metadata.
204
+ *
205
+ * Work a delegator's mail admitted replies to that mail under its `:reply:`
206
+ * key. Any other settled work, whatever started its turn, is a report to the
207
+ * Session's delegator of record, when the host names one (it does so once the
208
+ * Session is idle), under the delegator mail's `:report:` key.
185
209
  */
186
210
  export function settlementReplies(settlement, createdAt) {
187
211
  const { work, mail } = decodeAgentJob(settlement.job);
188
- if (mail === undefined)
212
+ const answered = mail !== undefined && mail.replyTo.length > 0;
213
+ const owed = answered
214
+ ? { key: mail.key, agentId: mail.to.agentId, replyTo: mail.replyTo }
215
+ : settlement.delegator;
216
+ if (owed === undefined)
189
217
  return [];
190
218
  const outcome = settlement.job.outcome;
191
219
  const diagnostic = outcome === "completed" ||
@@ -193,17 +221,29 @@ export function settlementReplies(settlement, createdAt) {
193
221
  settlement.diagnostic === undefined
194
222
  ? undefined
195
223
  : boundedDiagnostic(settlement.diagnostic);
196
- const reply = Schema.decodeUnknownSync(SettledBirdDogReplySchema)(diagnostic === undefined
197
- ? { commandId: work.commandFactId, outcome }
198
- : { commandId: work.commandFactId, outcome, diagnostic });
199
- return mail.replyTo.map((destination, index) => ({
200
- key: `${mail.key}:reply:${String(index)}`,
224
+ const facts = { commandId: work.commandFactId, outcome };
225
+ if (diagnostic !== undefined)
226
+ facts.diagnostic = diagnostic;
227
+ if (settlement.waitingOnYou !== undefined &&
228
+ settlement.waitingOnYou.length > 0)
229
+ facts.waitingOnYou = settlement.waitingOnYou;
230
+ const reply = Schema.decodeUnknownSync(SettledBirdDogReplySchema)(facts);
231
+ return owed.replyTo.map((destination, index) => ({
232
+ key: answered
233
+ ? `${owed.key}:reply:${String(index)}`
234
+ : `${owed.key}:report:${work.commandFactId}:${String(index)}`,
201
235
  to: destination,
202
- from: { ...mail.to, sessionId: work.sessionId },
236
+ from: {
237
+ kind: "agent",
238
+ agentId: owed.agentId,
239
+ sessionId: work.sessionId,
240
+ },
203
241
  replyTo: [],
204
242
  content: settlement.content.length > 0
205
243
  ? settlement.content
206
- : `The delegated work ended ${outcome} without reply text.`,
244
+ : `The delegated work ended ${outcome}${diagnostic === undefined
245
+ ? " without reply text."
246
+ : `: ${diagnostic}`}`,
207
247
  createdAt,
208
248
  reply,
209
249
  }));
package/dist/index.d.ts CHANGED
@@ -33,6 +33,8 @@ export type BirdDogReply = Readonly<{
33
33
  outcome: "completed" | "failed" | "interrupted" | "cancelled" | "outcome_unknown";
34
34
  /** Bounded terminal diagnostic for work that did not complete or cancel. */
35
35
  diagnostic?: string;
36
+ /** Open items of the replying Session that now wait on this recipient. */
37
+ waitingOnYou?: readonly BirdDogWaitingItem[];
36
38
  }> | Readonly<{
37
39
  outcome: "undeliverable";
38
40
  reason: "recipient_offline" | "recipient_left";
@@ -98,6 +100,7 @@ export type BirdDogAgentJobPayload = Readonly<{
98
100
  /** Input for projecting accepted agent work into Watchdog's generic grammar. */
99
101
  export type BirdDogAgentJobInput = Readonly<{
100
102
  work: BirdDogAgentWork;
103
+ delivery: "steer" | "followUp";
101
104
  mail?: BirdDogMail | undefined;
102
105
  }>;
103
106
  /** String-backed durable outbox owned by the BirdDog host. */
@@ -125,10 +128,24 @@ export type BirdDogOptions = Readonly<{
125
128
  /** Return the host's current wall-clock time in milliseconds. */
126
129
  now: () => number;
127
130
  }>;
131
+ /**
132
+ * Who sent a Session its work first: the mail that opened the relationship.
133
+ * `agentId` is the Session's own Agent, the author of every report.
134
+ */
135
+ export type BirdDogDelegator = Readonly<{
136
+ key: string;
137
+ agentId: string;
138
+ replyTo: readonly BirdDogAgentReference[];
139
+ }>;
128
140
  /** Completed generic work plus the content owed to its correspondents. */
129
141
  export type BirdDogSettlement = Readonly<{
130
142
  job: WatchdogSettledJob;
131
143
  content: string;
144
+ /**
145
+ * The Session's delegator of record, named only when this settlement leaves
146
+ * the Session idle. Work without a replying mail of its own reports to it.
147
+ */
148
+ delegator?: BirdDogDelegator;
132
149
  /**
133
150
  * The Session's recorded terminal diagnostic. Replies carry it, bounded by
134
151
  * `boundedDiagnostic`, only for failed, interrupted and outcome_unknown work.
@@ -139,6 +156,17 @@ export type BirdDogSettlement = Readonly<{
139
156
  * example, naming who cancelled). Replies never carry it.
140
157
  */
141
158
  meta?: BirdDogMeta;
159
+ /**
160
+ * Open items of the settled Session that now wait on the correspondent this
161
+ * work came from. Replies carry them beside the settlement facts.
162
+ */
163
+ waitingOnYou?: readonly BirdDogWaitingItem[];
164
+ }>;
165
+ /** One open item named by a reply as waiting on its recipient. */
166
+ export type BirdDogWaitingItem = Readonly<{
167
+ itemId: string;
168
+ kind: string;
169
+ summary: string;
142
170
  }>;
143
171
  /** Durable reply delivery behind the mail-room interface. */
144
172
  export type BirdDogRuntime = Readonly<{
@@ -89,6 +89,7 @@ Effect host ports for the durable mail room.
89
89
  ```ts
90
90
  type BirdDogEffect = Readonly<{
91
91
  fanOut: (settlement) => Effect.Effect<void, BirdDogError>;
92
+ postMail?: (mail) => Effect.Effect<void, BirdDogError>;
92
93
  drainOutbox: () => Effect.Effect<void, BirdDogError>;
93
94
  nextWakeAt: () => Effect.Effect<number | undefined, BirdDogError>;
94
95
  }>;
@@ -98,6 +99,16 @@ Effect-native durable reply delivery.
98
99
 
99
100
  ## Variables
100
101
 
102
+ ### BIRD\_DOG\_MAIL\_CONTENT\_MAX\_BYTES
103
+
104
+ ```ts
105
+ const BIRD_DOG_MAIL_CONTENT_MAX_BYTES: number;
106
+ ```
107
+
108
+ The maximum UTF-8 size of one mail body.
109
+
110
+ ---
111
+
101
112
  ### BirdDogAgentReferenceSchema
102
113
 
103
114
  ```ts
@@ -112,6 +123,33 @@ Parser for an ordinary agent Session address.
112
123
 
113
124
  ---
114
125
 
126
+ ### BirdDogAgentRecipientReferenceSchema
127
+
128
+ ```ts
129
+ const BirdDogAgentRecipientReferenceSchema: Struct<{
130
+ kind: Literal<"agent">;
131
+ agentId: String;
132
+ sessionId: optionalKey<String>;
133
+ }>;
134
+ ```
135
+
136
+ An addressed Agent may create its first Session when accepting this mail.
137
+
138
+ ---
139
+
140
+ ### BirdDogSystemIdentitySchema
141
+
142
+ ```ts
143
+ const BirdDogSystemIdentitySchema: Struct<{
144
+ kind: Literal<"system">;
145
+ systemId: Literal<"hub">;
146
+ }>;
147
+ ```
148
+
149
+ The Hub's stable system identity for a terminal offline disposition.
150
+
151
+ ---
152
+
115
153
  ### BIRD\_DOG\_DIAGNOSTIC\_MAX\_LENGTH
116
154
 
117
155
  ```ts
@@ -122,10 +160,36 @@ The most characters a reply's failure diagnostic may carry.
122
160
 
123
161
  ---
124
162
 
125
- ### BirdDogReplySchema
163
+ ### BIRD\_DOG\_WAITING\_MAX\_ITEMS
126
164
 
127
165
  ```ts
128
- const BirdDogReplySchema: Struct<{
166
+ const BIRD_DOG_WAITING_MAX_ITEMS: 8 = 8;
167
+ ```
168
+
169
+ The most open items one reply names as waiting on its recipient.
170
+
171
+ ---
172
+
173
+ ### BirdDogWaitingItemSchema
174
+
175
+ ```ts
176
+ const BirdDogWaitingItemSchema: Struct<{
177
+ itemId: String;
178
+ kind: String;
179
+ summary: String;
180
+ }>;
181
+ ```
182
+
183
+ One open item of the replying Session that waits on this reply's recipient.
184
+ BirdDog carries the three bounded fields; the replying host owns their meaning
185
+ and the recipient host owns every decision about them.
186
+
187
+ ---
188
+
189
+ ### SettledBirdDogReplySchema
190
+
191
+ ```ts
192
+ const SettledBirdDogReplySchema: Struct<{
129
193
  commandId: String;
130
194
  outcome: Literals<
131
195
  readonly [
@@ -137,41 +201,68 @@ const BirdDogReplySchema: Struct<{
137
201
  ]
138
202
  >;
139
203
  diagnostic: optionalKey<String>;
204
+ waitingOnYou: optionalKey<
205
+ $Array<
206
+ Struct<{
207
+ itemId: String;
208
+ kind: String;
209
+ summary: String;
210
+ }>
211
+ >
212
+ >;
140
213
  }>;
141
214
  ```
142
215
 
143
216
  Parser for the settlement facts a reply carries beside its content: the replying
144
- Session's command, its exact Watchdog outcome and, for work that did not
145
- complete, the Session's bounded terminal diagnostic.
217
+ Session's command, its exact Watchdog outcome, for work that did not complete
218
+ the Session's bounded terminal diagnostic and, when the replying host names any,
219
+ the open items now waiting on the recipient.
146
220
 
147
221
  ---
148
222
 
149
- ### BirdDogMailSchema
223
+ ### BirdDogUndeliverableReasonSchema
150
224
 
151
225
  ```ts
152
- const BirdDogMailSchema: Struct<{
153
- key: String;
154
- to: Struct<{
155
- kind: Literal<"agent">;
156
- agentId: String;
157
- sessionId: String;
158
- }>;
159
- from: Struct<{
160
- kind: Literal<"agent">;
161
- agentId: String;
162
- sessionId: String;
163
- }>;
164
- replyTo: $Array<
165
- Struct<{
166
- kind: Literal<"agent">;
167
- agentId: String;
168
- sessionId: String;
169
- }>
170
- >;
171
- content: String;
172
- createdAt: Number;
173
- meta: optionalKey<Codec<Json, Json, never, never>>;
174
- reply: optionalKey<
226
+ const BirdDogUndeliverableReasonSchema: Literals<
227
+ readonly ["recipient_offline", "recipient_left"]
228
+ >;
229
+ ```
230
+
231
+ A terminal delivery failure, correlated by the original mail key only.
232
+
233
+ ---
234
+
235
+ ### UndeliverableBirdDogReplySchema
236
+
237
+ ```ts
238
+ const UndeliverableBirdDogReplySchema: Struct<{
239
+ outcome: Literal<"undeliverable">;
240
+ reason: Literals<readonly ["recipient_offline", "recipient_left"]>;
241
+ }>;
242
+ ```
243
+
244
+ Parser for a terminal delivery failure reply.
245
+
246
+ ---
247
+
248
+ ### AcceptedBirdDogReplySchema
249
+
250
+ ```ts
251
+ const AcceptedBirdDogReplySchema: Struct<{
252
+ outcome: Literal<"accepted">;
253
+ commandId: String;
254
+ }>;
255
+ ```
256
+
257
+ Recipient acceptance, before the command has settled.
258
+
259
+ ---
260
+
261
+ ### BirdDogReplySchema
262
+
263
+ ```ts
264
+ const BirdDogReplySchema: Union<
265
+ readonly [
175
266
  Struct<{
176
267
  commandId: String;
177
268
  outcome: Literals<
@@ -184,12 +275,94 @@ const BirdDogMailSchema: Struct<{
184
275
  ]
185
276
  >;
186
277
  diagnostic: optionalKey<String>;
187
- }>
188
- >;
189
- }>;
278
+ waitingOnYou: optionalKey<
279
+ $Array<
280
+ Struct<{
281
+ itemId: String;
282
+ kind: String;
283
+ summary: String;
284
+ }>
285
+ >
286
+ >;
287
+ }>,
288
+ Struct<{
289
+ outcome: Literal<"undeliverable">;
290
+ reason: Literals<readonly ["recipient_offline", "recipient_left"]>;
291
+ }>,
292
+ Struct<{
293
+ outcome: Literal<"accepted">;
294
+ commandId: String;
295
+ }>,
296
+ ]
297
+ >;
298
+ ```
299
+
300
+ Parser for any reply: settled, accepted or undeliverable.
301
+
302
+ ---
303
+
304
+ ### BirdDogMailSchema
305
+
306
+ ```ts
307
+ const BirdDogMailSchema: Union<readonly [Struct<{
308
+ key: String;
309
+ to: Struct<{
310
+ kind: Literal<"agent">;
311
+ agentId: String;
312
+ sessionId: optionalKey<String>;
313
+ }>;
314
+ replyTo: $Array<Struct<{
315
+ kind: Literal<"agent">;
316
+ agentId: String;
317
+ sessionId: String;
318
+ }>>;
319
+ content: String;
320
+ createdAt: Number;
321
+ meta: optionalKey<Codec<Json, Json, never, never>>;
322
+ from: Struct<{
323
+ kind: Literal<"agent">;
324
+ agentId: String;
325
+ sessionId: String;
326
+ }>;
327
+ reply: optionalKey<Union<readonly [Struct<{
328
+ commandId: String;
329
+ outcome: Literals<readonly [..., ..., ..., ..., ...]>;
330
+ diagnostic: optionalKey<String>;
331
+ waitingOnYou: optionalKey<$Array<...>>;
332
+ }>, Struct<{
333
+ outcome: Literal<"accepted">;
334
+ commandId: String;
335
+ }>, Struct<{
336
+ outcome: Literal<"undeliverable">;
337
+ reason: Literal<"recipient_left">;
338
+ }>]>>;
339
+ }>, Struct<{
340
+ key: String;
341
+ to: Struct<{
342
+ kind: Literal<"agent">;
343
+ agentId: String;
344
+ sessionId: optionalKey<String>;
345
+ }>;
346
+ replyTo: $Array<Struct<{
347
+ kind: Literal<"agent">;
348
+ agentId: String;
349
+ sessionId: String;
350
+ }>>;
351
+ content: String;
352
+ createdAt: Number;
353
+ meta: optionalKey<Codec<Json, Json, never, never>>;
354
+ from: Struct<{
355
+ kind: Literal<"system">;
356
+ systemId: Literal<"hub">;
357
+ }>;
358
+ reply: Struct<{
359
+ outcome: Literal<"undeliverable">;
360
+ reason: Literal<"recipient_offline">;
361
+ }>;
362
+ }>]>;
190
363
  ```
191
364
 
192
- Parser for addressed agent correspondence.
365
+ Parser for addressed Agent mail, from an Agent or from the Hub system.
193
366
 
194
367
  ---
195
368
 
@@ -217,49 +390,54 @@ Parser for accepted Session work.
217
390
  ```ts
218
391
  const BirdDogAgentJobPayloadSchema: Struct<{
219
392
  work: Struct<{
220
- sessionId: String;
221
- commandFactId: String;
393
+ sessionId: String;
394
+ commandFactId: String;
222
395
  }>;
223
- mail: optional<
224
- Struct<{
225
- key: String;
226
- to: Struct<{
396
+ mail: optional<Union<readonly [Struct<{
397
+ key: String;
398
+ to: Struct<{
227
399
  kind: Literal<"agent">;
228
400
  agentId: String;
401
+ sessionId: optionalKey<String>;
402
+ }>;
403
+ replyTo: $Array<Struct<{
404
+ kind: Literal<...>;
405
+ agentId: String;
229
406
  sessionId: String;
230
- }>;
231
- from: Struct<{
407
+ }>>;
408
+ content: String;
409
+ createdAt: Number;
410
+ meta: optionalKey<Codec<Json, Json, never, never>>;
411
+ from: Struct<{
232
412
  kind: Literal<"agent">;
233
413
  agentId: String;
234
414
  sessionId: String;
235
- }>;
236
- replyTo: $Array<
237
- Struct<{
238
- kind: Literal<"agent">;
239
- agentId: String;
240
- sessionId: String;
241
- }>
242
- >;
243
- content: String;
244
- createdAt: Number;
245
- meta: optionalKey<Codec<Json, Json, never, never>>;
246
- reply: optionalKey<
247
- Struct<{
248
- commandId: String;
249
- outcome: Literals<
250
- readonly [
251
- "completed",
252
- "failed",
253
- "interrupted",
254
- "cancelled",
255
- "outcome_unknown",
256
- ]
257
- >;
258
- diagnostic: optionalKey<String>;
259
- }>
260
- >;
261
- }>
262
- >;
415
+ }>;
416
+ reply: optionalKey<Union<readonly [Struct<...>, Struct<...>, Struct<...>]>>;
417
+ }>, Struct<{
418
+ key: String;
419
+ to: Struct<{
420
+ kind: Literal<"agent">;
421
+ agentId: String;
422
+ sessionId: optionalKey<String>;
423
+ }>;
424
+ replyTo: $Array<Struct<{
425
+ kind: Literal<...>;
426
+ agentId: String;
427
+ sessionId: String;
428
+ }>>;
429
+ content: String;
430
+ createdAt: Number;
431
+ meta: optionalKey<Codec<Json, Json, never, never>>;
432
+ from: Struct<{
433
+ kind: Literal<"system">;
434
+ systemId: Literal<"hub">;
435
+ }>;
436
+ reply: Struct<{
437
+ outcome: Literal<"undeliverable">;
438
+ reason: Literal<"recipient_offline">;
439
+ }>;
440
+ }>]>>;
263
441
  }>;
264
442
  ```
265
443
 
@@ -272,55 +450,58 @@ Parser for the payload that Watchdog stores without interpretation.
272
450
  ```ts
273
451
  const BirdDogAgentJobInputSchema: Struct<{
274
452
  work: Struct<{
275
- jobId: String;
276
- sessionId: String;
277
- commandFactId: String;
278
- recovery: optional<
279
- Struct<{
453
+ jobId: String;
454
+ sessionId: String;
455
+ commandFactId: String;
456
+ recovery: optional<Struct<{
280
457
  maxRecoveries: Int;
281
- }>
282
- >;
458
+ }>>;
283
459
  }>;
284
- mail: optional<
285
- Struct<{
286
- key: String;
287
- to: Struct<{
460
+ mail: optional<Union<readonly [Struct<{
461
+ key: String;
462
+ to: Struct<{
288
463
  kind: Literal<"agent">;
289
464
  agentId: String;
465
+ sessionId: optionalKey<String>;
466
+ }>;
467
+ replyTo: $Array<Struct<{
468
+ kind: Literal<...>;
469
+ agentId: String;
290
470
  sessionId: String;
291
- }>;
292
- from: Struct<{
471
+ }>>;
472
+ content: String;
473
+ createdAt: Number;
474
+ meta: optionalKey<Codec<Json, Json, never, never>>;
475
+ from: Struct<{
293
476
  kind: Literal<"agent">;
294
477
  agentId: String;
295
478
  sessionId: String;
296
- }>;
297
- replyTo: $Array<
298
- Struct<{
299
- kind: Literal<"agent">;
300
- agentId: String;
301
- sessionId: String;
302
- }>
303
- >;
304
- content: String;
305
- createdAt: Number;
306
- meta: optionalKey<Codec<Json, Json, never, never>>;
307
- reply: optionalKey<
308
- Struct<{
309
- commandId: String;
310
- outcome: Literals<
311
- readonly [
312
- "completed",
313
- "failed",
314
- "interrupted",
315
- "cancelled",
316
- "outcome_unknown",
317
- ]
318
- >;
319
- diagnostic: optionalKey<String>;
320
- }>
321
- >;
322
- }>
323
- >;
479
+ }>;
480
+ reply: optionalKey<Union<readonly [Struct<...>, Struct<...>, Struct<...>]>>;
481
+ }>, Struct<{
482
+ key: String;
483
+ to: Struct<{
484
+ kind: Literal<"agent">;
485
+ agentId: String;
486
+ sessionId: optionalKey<String>;
487
+ }>;
488
+ replyTo: $Array<Struct<{
489
+ kind: Literal<...>;
490
+ agentId: String;
491
+ sessionId: String;
492
+ }>>;
493
+ content: String;
494
+ createdAt: Number;
495
+ meta: optionalKey<Codec<Json, Json, never, never>>;
496
+ from: Struct<{
497
+ kind: Literal<"system">;
498
+ systemId: Literal<"hub">;
499
+ }>;
500
+ reply: Struct<{
501
+ outcome: Literal<"undeliverable">;
502
+ reason: Literal<"recipient_offline">;
503
+ }>;
504
+ }>]>>;
324
505
  }>;
325
506
  ```
326
507
 
@@ -406,35 +587,20 @@ Decode the opaque payload at BirdDog's side of the Watchdog seam.
406
587
  ### settlementReplies()
407
588
 
408
589
  ```ts
409
- function settlementReplies(
410
- settlement,
411
- createdAt,
412
- ): readonly Readonly<{
413
- key: string;
414
- to: BirdDogAgentReference;
415
- from: BirdDogAgentReference;
416
- replyTo: readonly Readonly<{
417
- kind: "agent";
418
- agentId: string;
419
- sessionId: string;
420
- }>[];
421
- content: string;
422
- createdAt: number;
423
- meta?: Json;
424
- reply?: Readonly<{
425
- commandId: string;
426
- outcome:
427
- "failed" | "completed" | "interrupted" | "cancelled" | "outcome_unknown";
428
- diagnostic?: string;
429
- }>;
430
- }>[];
590
+ function settlementReplies(settlement, createdAt): readonly BirdDogMail[];
431
591
  ```
432
592
 
433
593
  Build the replies owed by one settlement for a host-owned Post transport. Each
434
594
  reply carries the settled content verbatim (or, when there is none, one sentence
435
- naming the outcome, so the reply is always admissible text) and its settlement
436
- facts as typed fields; the recipient's host authenticates them and decides how
437
- they are presented. A reply never carries the settled work's host metadata.
595
+ naming the outcome and its diagnostic, so the reply is always admissible text)
596
+ and its settlement facts as typed fields; the recipient's host authenticates
597
+ them and decides how they are presented. A reply never carries the settled
598
+ work's host metadata.
599
+
600
+ Work a delegator's mail admitted replies to that mail under its `:reply:` key.
601
+ Any other settled work, whatever started its turn, is a report to the Session's
602
+ delegator of record, when the host names one (it does so once the Session is
603
+ idle), under the delegator mail's `:report:` key.
438
604
 
439
605
  #### Parameters
440
606
 
@@ -445,14 +611,30 @@ they are presented. A reply never carries the settled work's host metadata.
445
611
 
446
612
  #### Returns
447
613
 
448
- readonly `Readonly`\<\{ `key`: `string`; `to`:
449
- [`BirdDogAgentReference`](index.md#birddogagentreference); `from`:
450
- [`BirdDogAgentReference`](index.md#birddogagentreference); `replyTo`: readonly
451
- `Readonly`\<\{ `kind`: `"agent"`; `agentId`: `string`; `sessionId`: `string`;
452
- \}\>[]; `content`: `string`; `createdAt`: `number`; `meta?`: `Json`; `reply?`:
453
- `Readonly`\<\{ `commandId`: `string`; `outcome`: `"failed"` \| `"completed"` \|
454
- `"interrupted"` \| `"cancelled"` \| `"outcome_unknown"`; `diagnostic?`:
455
- `string`; \}\>; \}\>[]
614
+ readonly [`BirdDogMail`](index.md#birddogmail)[]
615
+
616
+ ---
617
+
618
+ ### acceptedNotice()
619
+
620
+ ```ts
621
+ function acceptedNotice(mail, sessionId, commandId, createdAt): BirdDogMail;
622
+ ```
623
+
624
+ Build the recipient's one stable acceptance notice for one original mail.
625
+
626
+ #### Parameters
627
+
628
+ | Parameter | Type |
629
+ | ----------- | ------------------------------------- |
630
+ | `mail` | [`BirdDogMail`](index.md#birddogmail) |
631
+ | `sessionId` | `string` |
632
+ | `commandId` | `string` |
633
+ | `createdAt` | `number` |
634
+
635
+ #### Returns
636
+
637
+ [`BirdDogMail`](index.md#birddogmail)
456
638
 
457
639
  ---
458
640
 
@@ -483,6 +665,7 @@ Delivered receipts and exhausted dead letters stay but owe nothing.
483
665
  function make(options): Effect<
484
666
  Readonly<{
485
667
  fanOut: (settlement) => Effect<void, BirdDogError>;
668
+ postMail?: (mail) => Effect<void, BirdDogError>;
486
669
  drainOutbox: () => Effect<void, BirdDogError>;
487
670
  nextWakeAt: () => Effect<number | undefined, BirdDogError>;
488
671
  }>
@@ -500,6 +683,7 @@ Construct BirdDog's Effect-native mail room over Post's shared outbox.
500
683
  #### Returns
501
684
 
502
685
  `Effect`\<`Readonly`\<\{ `fanOut`: (`settlement`) => `Effect`\<`void`,
686
+ [`BirdDogError`](#birddogerror)\>; `postMail?`: (`mail`) => `Effect`\<`void`,
503
687
  [`BirdDogError`](#birddogerror)\>; `drainOutbox`: () => `Effect`\<`void`,
504
688
  [`BirdDogError`](#birddogerror)\>; `nextWakeAt`: () => `Effect`\<`number` \|
505
689
  `undefined`, [`BirdDogError`](#birddogerror)\>; \}\>\>
package/docs/api/index.md CHANGED
@@ -24,42 +24,118 @@ An ordinary agent Session address. Relationships between agents live elsewhere.
24
24
 
25
25
  ---
26
26
 
27
- ### BirdDogMail
27
+ ### BirdDogAgentRecipientReference
28
28
 
29
29
  ```ts
30
- type BirdDogMail = Readonly<{
31
- key: string;
32
- to: BirdDogAgentReference;
33
- from: BirdDogAgentReference;
34
- replyTo: readonly BirdDogAgentReference[];
35
- content: string;
36
- createdAt: number;
37
- meta?: BirdDogMeta;
38
- reply?: BirdDogReply;
30
+ type BirdDogAgentRecipientReference = Readonly<{
31
+ kind: "agent";
32
+ agentId: string;
33
+ sessionId?: string;
39
34
  }>;
40
35
  ```
41
36
 
42
- Addressed agent correspondence carried with durable work when a reply is owed.
37
+ An addressed Agent whose first Session may be created on acceptance.
43
38
 
44
39
  ---
45
40
 
46
- ### BirdDogReply
41
+ ### BirdDogSystemIdentity
47
42
 
48
43
  ```ts
49
- type BirdDogReply = Readonly<{
50
- commandId: string;
51
- outcome:
52
- "completed" | "failed" | "interrupted" | "cancelled" | "outcome_unknown";
53
- diagnostic?: string;
44
+ type BirdDogSystemIdentity = Readonly<{
45
+ kind: "system";
46
+ systemId: "hub";
54
47
  }>;
55
48
  ```
56
49
 
50
+ The Hub's stable system identity for a terminal offline disposition.
51
+
52
+ ---
53
+
54
+ ### BirdDogReply
55
+
56
+ ```ts
57
+ type BirdDogReply =
58
+ | Readonly<{
59
+ commandId: string;
60
+ outcome: "accepted";
61
+ }>
62
+ | Readonly<{
63
+ commandId: string;
64
+ outcome:
65
+ | "completed"
66
+ | "failed"
67
+ | "interrupted"
68
+ | "cancelled"
69
+ | "outcome_unknown";
70
+ diagnostic?: string;
71
+ waitingOnYou?: readonly BirdDogWaitingItem[];
72
+ }>
73
+ | Readonly<{
74
+ outcome: "undeliverable";
75
+ reason: "recipient_offline" | "recipient_left";
76
+ }>;
77
+ ```
78
+
57
79
  What a reply reports about the settled work beside its content. The recipient's
58
80
  host trusts these facts only after it authenticates the reply against the mail
59
81
  it sent.
60
82
 
61
83
  ---
62
84
 
85
+ ### BirdDogAgentReply
86
+
87
+ ```ts
88
+ type BirdDogAgentReply =
89
+ | Exclude<
90
+ BirdDogReply,
91
+ {
92
+ outcome: "undeliverable";
93
+ }
94
+ >
95
+ | Readonly<{
96
+ outcome: "undeliverable";
97
+ reason: "recipient_left";
98
+ }>;
99
+ ```
100
+
101
+ Facts an Agent may author; the Hub owns recipient-offline disposition.
102
+
103
+ ---
104
+
105
+ ### BirdDogMail
106
+
107
+ ```ts
108
+ type BirdDogMail =
109
+ | Readonly<{
110
+ key: string;
111
+ to: BirdDogAgentRecipientReference;
112
+ replyTo: readonly BirdDogAgentReference[];
113
+ content: string;
114
+ createdAt: number;
115
+ meta?: BirdDogMeta;
116
+ from: BirdDogAgentReference;
117
+ reply?: BirdDogAgentReply;
118
+ }>
119
+ | Readonly<{
120
+ key: string;
121
+ to: BirdDogAgentRecipientReference;
122
+ replyTo: readonly BirdDogAgentReference[];
123
+ content: string;
124
+ createdAt: number;
125
+ meta?: BirdDogMeta;
126
+ from: BirdDogSystemIdentity;
127
+ reply: Readonly<{
128
+ outcome: "undeliverable";
129
+ reason: "recipient_offline";
130
+ }>;
131
+ }>;
132
+ ```
133
+
134
+ Addressed correspondence. Agents author ordinary replies and recipient-left
135
+ notices; only the Hub system authors recipient-offline dispositions.
136
+
137
+ ---
138
+
63
139
  ### BirdDogMeta
64
140
 
65
141
  ```ts
@@ -154,14 +230,31 @@ Host ports for BirdDog's durable mail delivery.
154
230
 
155
231
  ---
156
232
 
233
+ ### BirdDogDelegator
234
+
235
+ ```ts
236
+ type BirdDogDelegator = Readonly<{
237
+ key: string;
238
+ agentId: string;
239
+ replyTo: readonly BirdDogAgentReference[];
240
+ }>;
241
+ ```
242
+
243
+ Who sent a Session its work first: the mail that opened the relationship.
244
+ `agentId` is the Session's own Agent, the author of every report.
245
+
246
+ ---
247
+
157
248
  ### BirdDogSettlement
158
249
 
159
250
  ```ts
160
251
  type BirdDogSettlement = Readonly<{
161
252
  job: WatchdogSettledJob;
162
253
  content: string;
254
+ delegator?: BirdDogDelegator;
163
255
  diagnostic?: string;
164
256
  meta?: BirdDogMeta;
257
+ waitingOnYou?: readonly BirdDogWaitingItem[];
165
258
  }>;
166
259
  ```
167
260
 
@@ -169,6 +262,20 @@ Completed generic work plus the content owed to its correspondents.
169
262
 
170
263
  ---
171
264
 
265
+ ### BirdDogWaitingItem
266
+
267
+ ```ts
268
+ type BirdDogWaitingItem = Readonly<{
269
+ itemId: string;
270
+ kind: string;
271
+ summary: string;
272
+ }>;
273
+ ```
274
+
275
+ One open item named by a reply as waiting on its recipient.
276
+
277
+ ---
278
+
172
279
  ### BirdDogRuntime
173
280
 
174
281
  ```ts
@@ -203,3 +310,24 @@ const BirdDog: BirdDogApi;
203
310
  ```
204
311
 
205
312
  Agent-aware mail translation over Watchdog's payload-blind job grammar.
313
+
314
+ ## Functions
315
+
316
+ ### undeliverableReply()
317
+
318
+ ```ts
319
+ function undeliverableReply(original, createdAt): BirdDogMail;
320
+ ```
321
+
322
+ Construct the one stable system reply correlated to the exact sent letter.
323
+
324
+ #### Parameters
325
+
326
+ | Parameter | Type |
327
+ | ----------- | ----------------------------- |
328
+ | `original` | [`BirdDogMail`](#birddogmail) |
329
+ | `createdAt` | `number` |
330
+
331
+ #### Returns
332
+
333
+ [`BirdDogMail`](#birddogmail)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/bird-dog",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "description": "Durable addressed agent mail over generic Watchdog work",
6
6
  "type": "module",