@songsid/agend 2.1.7-beta.4 → 2.1.7-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +2 -0
  2. package/README.zh-TW.md +2 -0
  3. package/dist/agent-cli-instructions.md +5 -0
  4. package/dist/agent-cli.js +13 -0
  5. package/dist/agent-cli.js.map +1 -1
  6. package/dist/agent-endpoint.js +1 -0
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/backend/codex-session-lookup.d.ts +59 -0
  9. package/dist/backend/codex-session-lookup.js +139 -0
  10. package/dist/backend/codex-session-lookup.js.map +1 -0
  11. package/dist/backend/codex.d.ts +11 -0
  12. package/dist/backend/codex.js +115 -7
  13. package/dist/backend/codex.js.map +1 -1
  14. package/dist/backend/types.d.ts +12 -0
  15. package/dist/backend/types.js.map +1 -1
  16. package/dist/channel/mcp-tools.js +2 -0
  17. package/dist/channel/mcp-tools.js.map +1 -1
  18. package/dist/cli.js +68 -2
  19. package/dist/cli.js.map +1 -1
  20. package/dist/daemon.d.ts +11 -0
  21. package/dist/daemon.js +173 -23
  22. package/dist/daemon.js.map +1 -1
  23. package/dist/delivery-outbox.d.ts +90 -6
  24. package/dist/delivery-outbox.js +301 -39
  25. package/dist/delivery-outbox.js.map +1 -1
  26. package/dist/delivery-outbox.test.js +268 -14
  27. package/dist/delivery-outbox.test.js.map +1 -1
  28. package/dist/delivery-queue-evidence.d.ts +15 -0
  29. package/dist/delivery-queue-evidence.js +35 -0
  30. package/dist/delivery-queue-evidence.js.map +1 -0
  31. package/dist/delivery-queue-evidence.test.d.ts +1 -0
  32. package/dist/delivery-queue-evidence.test.js +23 -0
  33. package/dist/delivery-queue-evidence.test.js.map +1 -0
  34. package/dist/delivery-reconciliation.d.ts +49 -0
  35. package/dist/delivery-reconciliation.js +296 -0
  36. package/dist/delivery-reconciliation.js.map +1 -0
  37. package/dist/delivery-reconciliation.test.d.ts +1 -0
  38. package/dist/delivery-reconciliation.test.js +411 -0
  39. package/dist/delivery-reconciliation.test.js.map +1 -0
  40. package/dist/fleet-manager.d.ts +2 -1
  41. package/dist/fleet-manager.js +61 -32
  42. package/dist/fleet-manager.js.map +1 -1
  43. package/dist/instance-lifecycle.d.ts +13 -0
  44. package/dist/instance-lifecycle.js +85 -10
  45. package/dist/instance-lifecycle.js.map +1 -1
  46. package/dist/instructions.js +1 -0
  47. package/dist/instructions.js.map +1 -1
  48. package/dist/locale.js +6 -0
  49. package/dist/locale.js.map +1 -1
  50. package/dist/outbound-handlers.d.ts +3 -0
  51. package/dist/outbound-handlers.js +38 -1
  52. package/dist/outbound-handlers.js.map +1 -1
  53. package/dist/outbound-schemas.d.ts +7 -0
  54. package/dist/outbound-schemas.js +14 -0
  55. package/dist/outbound-schemas.js.map +1 -1
  56. package/dist/pane-write-lock.d.ts +2 -0
  57. package/dist/pane-write-lock.js +19 -0
  58. package/dist/pane-write-lock.js.map +1 -1
  59. package/dist/scheduler/scheduler.d.ts +5 -1
  60. package/dist/scheduler/scheduler.js +60 -20
  61. package/dist/scheduler/scheduler.js.map +1 -1
  62. package/dist/scheduler/scheduler.test.js +41 -2
  63. package/dist/scheduler/scheduler.test.js.map +1 -1
  64. package/dist/scheduler/types.d.ts +1 -1
  65. package/dist/tmux-manager.d.ts +3 -3
  66. package/dist/tmux-manager.js +6 -6
  67. package/dist/tmux-manager.js.map +1 -1
  68. package/dist/tool-permissions.js +6 -6
  69. package/dist/tool-permissions.js.map +1 -1
  70. package/dist/transcript-monitor.d.ts +3 -1
  71. package/dist/transcript-monitor.js +15 -0
  72. package/dist/transcript-monitor.js.map +1 -1
  73. package/dist/transcript-sources.d.ts +9 -0
  74. package/dist/transcript-sources.js +14 -0
  75. package/dist/transcript-sources.js.map +1 -1
  76. package/package.json +1 -1
@@ -0,0 +1,411 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import { DeliveryOutbox } from "./delivery-outbox.js";
6
+ import { queueResumePolicyForAttempt } from "./delivery-queue-evidence.js";
7
+ import { finishTargetReconciliation, transcriptDeltaHasDeliveryMarker } from "./delivery-reconciliation.js";
8
+ const id = "00000000-0000-4000-8000-000000000041";
9
+ const marker = `[agend-delivery-id:${id}]`;
10
+ const roots = [];
11
+ afterEach(() => {
12
+ for (const root of roots.splice(0))
13
+ rmSync(root, { recursive: true, force: true });
14
+ });
15
+ function makeAttempt(options = { enterStarted: true }) {
16
+ const root = mkdtempSync(join(tmpdir(), "agend-reconciliation-"));
17
+ roots.push(root);
18
+ const outbox = new DeliveryOutbox(join(root, "outbox.db"), "manager-1");
19
+ const row = outbox.admit({
20
+ operationId: "op-reconcile",
21
+ sourceKey: "source:op-reconcile:worker",
22
+ sourceInstance: "source",
23
+ sourceDaemonBootId: "source-boot",
24
+ targetInstance: "worker",
25
+ kind: options.kind ?? "fleet_inbound",
26
+ payload: { type: options.kind === "raw_paste" ? "raw_paste" : "fleet_inbound", content: options.payloadContent ?? "work", meta: {} },
27
+ }).delivery;
28
+ const claimed = outbox.claimNext("manager-1", () => "target-boot-1", new Set());
29
+ expect(outbox.begin(row.deliveryId, "target-boot-1", claimed.attemptNo, {
30
+ backend: options.backend ?? "claude-code",
31
+ backendVersion: null,
32
+ windowId: options.windowId === undefined ? "@old-worker" : options.windowId,
33
+ transcriptPath: options.transcriptPath ?? null,
34
+ transcriptOffset: options.transcriptOffset ?? null,
35
+ transcriptSessionId: options.transcriptPath ?? null,
36
+ submissionMode: options.submissionMode ?? "idle_submit",
37
+ queueResumePolicy: options.queueResumePolicy
38
+ ?? queueResumePolicyForAttempt(options.backend ?? "claude-code", null, options.submissionMode ?? "idle_submit"),
39
+ })).toBe("begun");
40
+ if (options.enterStarted)
41
+ expect(outbox.markEnterStarted(row.deliveryId, "target-boot-1", claimed.attemptNo)).toBe(true);
42
+ expect(outbox.recoverForBoot("manager-2").reconciliationPending).toBe(1);
43
+ return { outbox, row, candidate: outbox.getReconciliationCandidates("worker")[0] };
44
+ }
45
+ describe("durable transcript marker reconciliation", () => {
46
+ it("requires the exact marker at the beginning of a Claude user entry", () => {
47
+ expect(transcriptDeltaHasDeliveryMarker(JSON.stringify({
48
+ type: "user",
49
+ message: { role: "user", content: [{ type: "text", text: `${marker}\n[from:worker] do the work` }] },
50
+ }), "claude-code", id)).toBe(true);
51
+ });
52
+ it("requires the exact marker at the beginning of a Codex user entry", () => {
53
+ expect(transcriptDeltaHasDeliveryMarker(JSON.stringify({
54
+ type: "response_item",
55
+ payload: { type: "message", role: "user", content: [{ type: "input_text", text: `${marker}\n[from:worker] do the work` }] },
56
+ }), "codex", id)).toBe(true);
57
+ });
58
+ it("does not accept a marker after the first Claude user text item", () => {
59
+ const entry = JSON.stringify({
60
+ type: "user",
61
+ message: { role: "user", content: [
62
+ { type: "text", text: "preceding content" },
63
+ { type: "text", text: `${marker}\n[from:worker] do the work` },
64
+ ] },
65
+ });
66
+ expect(transcriptDeltaHasDeliveryMarker(entry, "claude-code", id)).toBe(false);
67
+ });
68
+ it("does not accept a marker after the first Codex user text item", () => {
69
+ const entry = JSON.stringify({
70
+ type: "response_item",
71
+ payload: { type: "message", role: "user", content: [
72
+ { type: "input_text", text: "preceding content" },
73
+ { type: "input_text", text: `${marker}\n[from:worker] do the work` },
74
+ ] },
75
+ });
76
+ expect(transcriptDeltaHasDeliveryMarker(entry, "codex", id)).toBe(false);
77
+ });
78
+ it("does not accept quoted, embedded, assistant, system, or tool-result occurrences", () => {
79
+ const entries = [
80
+ { type: "user", message: { role: "user", content: [{ type: "text", text: `quoted: ${marker}` }] } },
81
+ { type: "assistant", message: { role: "assistant", content: [{ type: "text", text: marker }] } },
82
+ { type: "user", message: { role: "user", content: [{ type: "tool_result", text: marker }] } },
83
+ { type: "response_item", payload: { type: "function_call_output", output: marker } },
84
+ ].map(entry => JSON.stringify(entry)).join("\n");
85
+ expect(transcriptDeltaHasDeliveryMarker(entries, "claude-code", id)).toBe(false);
86
+ expect(transcriptDeltaHasDeliveryMarker(entries, "codex", id)).toBe(false);
87
+ });
88
+ it("fails closed for unknown transcript backends and malformed lines", () => {
89
+ expect(transcriptDeltaHasDeliveryMarker(`not-json ${marker}`, "muse", id)).toBe(false);
90
+ expect(transcriptDeltaHasDeliveryMarker("{ truncated", "claude-code", id)).toBe(false);
91
+ });
92
+ it("uses a persisted Claude user-entry marker as positive proof only after the old CLI exits", async () => {
93
+ const root = mkdtempSync(join(tmpdir(), "agend-reconciliation-positive-"));
94
+ roots.push(root);
95
+ const transcriptPath = join(root, "session.jsonl");
96
+ const h = makeAttempt({ enterStarted: true, transcriptPath, transcriptOffset: 0 });
97
+ writeFileSync(transcriptPath, `${JSON.stringify({
98
+ type: "user",
99
+ message: { role: "user", content: [{ type: "text", text: `[agend-delivery-id:${h.row.deliveryId}]\n[from:worker] work` }] },
100
+ })}\n`);
101
+ const result = await finishTargetReconciliation(h.outbox, {
102
+ targetInstance: "worker",
103
+ sessionName: "test-session",
104
+ savedWindowId: "@old-worker",
105
+ attempts: [{ candidate: h.candidate, paneWindowId: "@old-worker", panePid: null, pane: "", paneCaptureError: null }],
106
+ }, true);
107
+ expect(result).toMatchObject({ delivered: 1, retry: 0, uncertain: 0, safeToStart: true });
108
+ expect(h.outbox.get(h.row.deliveryId)?.state).toBe("delivered");
109
+ h.outbox.close();
110
+ });
111
+ it("retries composer-only marker evidence only when the write-ahead fence proves Enter never started", async () => {
112
+ const h = makeAttempt({ enterStarted: false });
113
+ const result = await finishTargetReconciliation(h.outbox, {
114
+ targetInstance: "worker",
115
+ sessionName: "test-session",
116
+ savedWindowId: "@old-worker",
117
+ attempts: [{
118
+ candidate: h.candidate,
119
+ paneWindowId: "@old-worker",
120
+ panePid: null,
121
+ pane: `${marker}\n› [from:worker] work`,
122
+ paneCaptureError: null,
123
+ }],
124
+ }, true);
125
+ expect(result).toMatchObject({ delivered: 0, retry: 1, uncertain: 0, safeToStart: true });
126
+ expect(h.outbox.get(h.row.deliveryId)).toMatchObject({ state: "retry_wait", reconciliationPending: false });
127
+ h.outbox.close();
128
+ });
129
+ it("does not read a positive transcript as proof until window retirement is confirmed", async () => {
130
+ const root = mkdtempSync(join(tmpdir(), "agend-reconciliation-live-cli-"));
131
+ roots.push(root);
132
+ const transcriptPath = join(root, "session.jsonl");
133
+ const h = makeAttempt({ enterStarted: true, transcriptPath, transcriptOffset: 0 });
134
+ writeFileSync(transcriptPath, `${JSON.stringify({
135
+ type: "user",
136
+ message: { role: "user", content: [{ type: "text", text: `[agend-delivery-id:${h.row.deliveryId}]\n[from:worker] work` }] },
137
+ })}\n`);
138
+ const result = await finishTargetReconciliation(h.outbox, {
139
+ targetInstance: "worker",
140
+ sessionName: "test-session",
141
+ savedWindowId: "@old-worker",
142
+ attempts: [{ candidate: h.candidate, paneWindowId: "@old-worker", panePid: process.pid, pane: "", paneCaptureError: null }],
143
+ }, false);
144
+ expect(result).toMatchObject({ delivered: 0, retry: 0, uncertain: 1, safeToStart: false });
145
+ expect(h.outbox.get(h.row.deliveryId)?.state).toBe("uncertain");
146
+ h.outbox.close();
147
+ });
148
+ it("retries a stable transcript miss for an idle submit after the old CLI exits", async () => {
149
+ const root = mkdtempSync(join(tmpdir(), "agend-reconciliation-idle-miss-"));
150
+ roots.push(root);
151
+ const transcriptPath = join(root, "session.jsonl");
152
+ writeFileSync(transcriptPath, "");
153
+ const h = makeAttempt({
154
+ enterStarted: true,
155
+ backend: "codex",
156
+ submissionMode: "idle_submit",
157
+ transcriptPath,
158
+ transcriptOffset: 0,
159
+ });
160
+ const kill = vi.spyOn(process, "kill").mockImplementation(() => {
161
+ const err = new Error("no such process");
162
+ err.code = "ESRCH";
163
+ throw err;
164
+ });
165
+ try {
166
+ const result = await finishTargetReconciliation(h.outbox, {
167
+ targetInstance: "worker",
168
+ sessionName: "test-session",
169
+ savedWindowId: "@old-worker",
170
+ attempts: [{ candidate: h.candidate, paneWindowId: "@old-worker", panePid: 424242, pane: "", paneCaptureError: null }],
171
+ }, true);
172
+ expect(result).toMatchObject({ delivered: 0, retry: 1, uncertain: 0, safeToStart: true });
173
+ expect(h.outbox.get(h.row.deliveryId)).toMatchObject({ state: "retry_wait", reconciliationPending: false });
174
+ }
175
+ finally {
176
+ kill.mockRestore();
177
+ h.outbox.close();
178
+ }
179
+ });
180
+ it("retries raw_paste only when the exact bytes remain at the end of the pre-kill composer", async () => {
181
+ const raw = " /compact\n--keep-space ";
182
+ const h = makeAttempt({
183
+ enterStarted: false,
184
+ kind: "raw_paste",
185
+ payloadContent: raw,
186
+ submissionMode: "raw_paste",
187
+ });
188
+ const kill = vi.spyOn(process, "kill").mockImplementation(() => {
189
+ const err = new Error("no such process");
190
+ err.code = "ESRCH";
191
+ throw err;
192
+ });
193
+ try {
194
+ const result = await finishTargetReconciliation(h.outbox, {
195
+ targetInstance: "worker",
196
+ sessionName: "test-session",
197
+ savedWindowId: "@old-worker",
198
+ attempts: [{
199
+ candidate: h.candidate,
200
+ paneWindowId: "@old-worker",
201
+ panePid: 424242,
202
+ pane: `old transcript\n❯ ${raw}`,
203
+ paneCaptureError: null,
204
+ }],
205
+ }, true);
206
+ expect(result).toMatchObject({ delivered: 0, retry: 1, uncertain: 0, safeToStart: true });
207
+ expect(h.outbox.get(h.row.deliveryId)).toMatchObject({ state: "retry_wait" });
208
+ }
209
+ finally {
210
+ kill.mockRestore();
211
+ h.outbox.close();
212
+ }
213
+ });
214
+ it("keeps raw_paste uncertain when W1 says no Enter but the exact composer bytes are absent", async () => {
215
+ const raw = "/compact";
216
+ const h = makeAttempt({
217
+ enterStarted: false,
218
+ kind: "raw_paste",
219
+ payloadContent: raw,
220
+ submissionMode: "raw_paste",
221
+ });
222
+ const kill = vi.spyOn(process, "kill").mockImplementation(() => {
223
+ const err = new Error("no such process");
224
+ err.code = "ESRCH";
225
+ throw err;
226
+ });
227
+ try {
228
+ const result = await finishTargetReconciliation(h.outbox, {
229
+ targetInstance: "worker",
230
+ sessionName: "test-session",
231
+ savedWindowId: "@old-worker",
232
+ attempts: [{
233
+ candidate: h.candidate,
234
+ paneWindowId: "@old-worker",
235
+ panePid: 424242,
236
+ pane: "CLI resumed with a clean prompt",
237
+ paneCaptureError: null,
238
+ }],
239
+ }, true);
240
+ expect(result).toMatchObject({ delivered: 0, retry: 0, uncertain: 1, safeToStart: true });
241
+ expect(h.outbox.get(h.row.deliveryId)).toMatchObject({ state: "uncertain" });
242
+ }
243
+ finally {
244
+ kill.mockRestore();
245
+ h.outbox.close();
246
+ }
247
+ });
248
+ it("does not treat a raw command ending in newline as exact composer evidence", async () => {
249
+ const raw = "/compact\n";
250
+ const h = makeAttempt({
251
+ enterStarted: false,
252
+ kind: "raw_paste",
253
+ payloadContent: raw,
254
+ submissionMode: "raw_paste",
255
+ });
256
+ const kill = vi.spyOn(process, "kill").mockImplementation(() => {
257
+ const err = new Error("no such process");
258
+ err.code = "ESRCH";
259
+ throw err;
260
+ });
261
+ try {
262
+ const result = await finishTargetReconciliation(h.outbox, {
263
+ targetInstance: "worker",
264
+ sessionName: "test-session",
265
+ savedWindowId: "@old-worker",
266
+ attempts: [{
267
+ candidate: h.candidate,
268
+ paneWindowId: "@old-worker",
269
+ panePid: 424242,
270
+ pane: `old transcript\n❯ ${raw}\n`,
271
+ paneCaptureError: null,
272
+ }],
273
+ }, true);
274
+ expect(result).toMatchObject({ delivered: 0, retry: 0, uncertain: 1, safeToStart: true });
275
+ expect(h.outbox.get(h.row.deliveryId)).toMatchObject({ state: "uncertain" });
276
+ }
277
+ finally {
278
+ kill.mockRestore();
279
+ h.outbox.close();
280
+ }
281
+ });
282
+ it("keeps a transcript miss uncertain when the old CLI pid is unknown", async () => {
283
+ const root = mkdtempSync(join(tmpdir(), "agend-reconciliation-unknown-pid-"));
284
+ roots.push(root);
285
+ const transcriptPath = join(root, "session.jsonl");
286
+ writeFileSync(transcriptPath, "");
287
+ const h = makeAttempt({
288
+ enterStarted: true,
289
+ backend: "codex",
290
+ submissionMode: "idle_submit",
291
+ transcriptPath,
292
+ transcriptOffset: 0,
293
+ });
294
+ const result = await finishTargetReconciliation(h.outbox, {
295
+ targetInstance: "worker",
296
+ sessionName: "test-session",
297
+ savedWindowId: "@old-worker",
298
+ attempts: [{ candidate: h.candidate, paneWindowId: "@old-worker", panePid: null, pane: "", paneCaptureError: null }],
299
+ }, true);
300
+ expect(result).toMatchObject({ delivered: 0, retry: 0, uncertain: 1, safeToStart: true });
301
+ expect(h.outbox.get(h.row.deliveryId)).toMatchObject({ state: "uncertain", reconciliationPending: false });
302
+ h.outbox.close();
303
+ });
304
+ it.each([
305
+ ["Codex native queue", "codex", "native_queue_handoff", "unknown"],
306
+ ["Claude steering", "claude-code", "steer", "may_resume"],
307
+ ])("keeps %s uncertain when transcript has no marker and queue resume is not ruled out", async (_label, backend, submissionMode, queueResumePolicy) => {
308
+ const root = mkdtempSync(join(tmpdir(), "agend-reconciliation-queue-unknown-"));
309
+ roots.push(root);
310
+ const transcriptPath = join(root, "session.jsonl");
311
+ writeFileSync(transcriptPath, "");
312
+ const h = makeAttempt({
313
+ enterStarted: true,
314
+ backend,
315
+ submissionMode,
316
+ queueResumePolicy,
317
+ transcriptPath,
318
+ transcriptOffset: 0,
319
+ });
320
+ const result = await finishTargetReconciliation(h.outbox, {
321
+ targetInstance: "worker",
322
+ sessionName: "test-session",
323
+ savedWindowId: "@old-worker",
324
+ attempts: [{ candidate: h.candidate, paneWindowId: "@old-worker", panePid: 424242, pane: "", paneCaptureError: null }],
325
+ }, true);
326
+ expect(result).toMatchObject({ delivered: 0, retry: 0, uncertain: 1, safeToStart: true });
327
+ expect(h.outbox.get(h.row.deliveryId)).toMatchObject({ state: "uncertain", reconciliationPending: false });
328
+ h.outbox.close();
329
+ });
330
+ it("allows transcript absence to retry only for an exact policy that proves resume cannot replay a queue item", async () => {
331
+ const root = mkdtempSync(join(tmpdir(), "agend-reconciliation-queue-ephemeral-"));
332
+ roots.push(root);
333
+ const transcriptPath = join(root, "rollout.jsonl");
334
+ writeFileSync(transcriptPath, "");
335
+ const h = makeAttempt({
336
+ enterStarted: true,
337
+ backend: "codex",
338
+ submissionMode: "native_queue_handoff",
339
+ queueResumePolicy: "does_not_resume",
340
+ transcriptPath,
341
+ transcriptOffset: 0,
342
+ });
343
+ const kill = vi.spyOn(process, "kill").mockImplementation(() => {
344
+ const err = new Error("no such process");
345
+ err.code = "ESRCH";
346
+ throw err;
347
+ });
348
+ try {
349
+ const result = await finishTargetReconciliation(h.outbox, {
350
+ targetInstance: "worker",
351
+ sessionName: "test-session",
352
+ savedWindowId: "@old-worker",
353
+ attempts: [{ candidate: h.candidate, paneWindowId: "@old-worker", panePid: 424242, pane: "", paneCaptureError: null }],
354
+ }, true);
355
+ expect(result).toMatchObject({ delivered: 0, retry: 1, uncertain: 0, safeToStart: true });
356
+ expect(h.outbox.get(h.row.deliveryId)).toMatchObject({ state: "retry_wait" });
357
+ }
358
+ finally {
359
+ kill.mockRestore();
360
+ h.outbox.close();
361
+ }
362
+ });
363
+ it("keeps the target fenced when the tmux window is gone but its captured process is still alive", async () => {
364
+ const h = makeAttempt({ enterStarted: true });
365
+ const kill = vi.spyOn(process, "kill").mockImplementation(() => {
366
+ const err = new Error("permission denied");
367
+ err.code = "EPERM";
368
+ throw err;
369
+ });
370
+ try {
371
+ const result = await finishTargetReconciliation(h.outbox, {
372
+ targetInstance: "worker",
373
+ sessionName: "test-session",
374
+ savedWindowId: "@old-worker",
375
+ attempts: [{ candidate: h.candidate, paneWindowId: "@old-worker", panePid: 424242, pane: "", paneCaptureError: null }],
376
+ }, true);
377
+ expect(result).toMatchObject({ delivered: 0, retry: 0, uncertain: 1, safeToStart: false });
378
+ expect(h.outbox.get(h.row.deliveryId)?.state).toBe("uncertain");
379
+ }
380
+ finally {
381
+ kill.mockRestore();
382
+ h.outbox.close();
383
+ }
384
+ });
385
+ it("keeps the target fenced when a pending attempt has no window identity", async () => {
386
+ const h = makeAttempt({ enterStarted: true, windowId: null });
387
+ const result = await finishTargetReconciliation(h.outbox, {
388
+ targetInstance: "worker",
389
+ sessionName: "test-session",
390
+ savedWindowId: "@old-worker",
391
+ attempts: [{ candidate: h.candidate, paneWindowId: null, panePid: null, pane: null, paneCaptureError: "old window id unavailable" }],
392
+ }, true);
393
+ expect(result).toMatchObject({ delivered: 0, retry: 0, uncertain: 1, safeToStart: false });
394
+ expect(h.outbox.get(h.row.deliveryId)?.state).toBe("uncertain");
395
+ h.outbox.close();
396
+ });
397
+ it("allows an ordinary start when no reconciliation rows exist even if retirement is unconfirmed", async () => {
398
+ const root = mkdtempSync(join(tmpdir(), "agend-reconciliation-empty-"));
399
+ roots.push(root);
400
+ const outbox = new DeliveryOutbox(join(root, "outbox.db"), "manager-1");
401
+ const result = await finishTargetReconciliation(outbox, {
402
+ targetInstance: "worker",
403
+ sessionName: "test-session",
404
+ savedWindowId: "@old-worker",
405
+ attempts: [],
406
+ }, false);
407
+ expect(result).toMatchObject({ delivered: 0, retry: 0, uncertain: 0, safeToStart: true });
408
+ outbox.close();
409
+ });
410
+ });
411
+ //# sourceMappingURL=delivery-reconciliation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delivery-reconciliation.test.js","sourceRoot":"","sources":["../src/delivery-reconciliation.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,gCAAgC,EAAE,MAAM,8BAA8B,CAAC;AAE5G,MAAM,EAAE,GAAG,sCAAsC,CAAC;AAClD,MAAM,MAAM,GAAG,sBAAsB,EAAE,GAAG,CAAC;AAC3C,MAAM,KAAK,GAAa,EAAE,CAAC;AAE3B,SAAS,CAAC,GAAG,EAAE;IACb,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAAE,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACrF,CAAC,CAAC,CAAC;AAEH,SAAS,WAAW,CAAC,UAUjB,EAAE,YAAY,EAAE,IAAI,EAAE;IACxB,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,uBAAuB,CAAC,CAAC,CAAC;IAClE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC;IACxE,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC;QACvB,WAAW,EAAE,cAAc;QAC3B,SAAS,EAAE,4BAA4B;QACvC,cAAc,EAAE,QAAQ;QACxB,kBAAkB,EAAE,aAAa;QACjC,cAAc,EAAE,QAAQ;QACxB,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,eAAe;QACrC,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,cAAc,IAAI,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE;KACrI,CAAC,CAAC,QAAQ,CAAC;IACZ,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,eAAe,EAAE,IAAI,GAAG,EAAE,CAAE,CAAC;IACjF,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,eAAe,EAAE,OAAO,CAAC,SAAS,EAAE;QACtE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,aAAa;QACzC,cAAc,EAAE,IAAI;QACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ;QAC3E,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,IAAI;QAC9C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,IAAI,IAAI;QAClD,mBAAmB,EAAE,OAAO,CAAC,cAAc,IAAI,IAAI;QACnD,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,aAAa;QACvD,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;eACvC,2BAA2B,CAAC,OAAO,CAAC,OAAO,IAAI,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,cAAc,IAAI,aAAa,CAAC;KAClH,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClB,IAAI,OAAO,CAAC,YAAY;QAAE,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,EAAE,eAAe,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzH,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,2BAA2B,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC;AACtF,CAAC;AAED,QAAQ,CAAC,0CAA0C,EAAE,GAAG,EAAE;IACxD,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,CAAC,gCAAgC,CAAC,IAAI,CAAC,SAAS,CAAC;YACrD,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,6BAA6B,EAAE,CAAC,EAAE;SACrG,CAAC,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,gCAAgC,CAAC,IAAI,CAAC,SAAS,CAAC;YACrD,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,MAAM,6BAA6B,EAAE,CAAC,EAAE;SAC5H,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;YAC3B,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;oBAChC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,EAAE;oBAC3C,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,6BAA6B,EAAE;iBAC/D,EAAE;SACJ,CAAC,CAAC;QACH,MAAM,CAAC,gCAAgC,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;YAC3B,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;oBACjD,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,mBAAmB,EAAE;oBACjD,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,MAAM,6BAA6B,EAAE;iBACrE,EAAE;SACJ,CAAC,CAAC;QACH,MAAM,CAAC,gCAAgC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QACzF,MAAM,OAAO,GAAG;YACd,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;YACnG,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;YAChG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;YAC7F,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;SACrF,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,CAAC,gCAAgC,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjF,MAAM,CAAC,gCAAgC,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,gCAAgC,CAAC,YAAY,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvF,MAAM,CAAC,gCAAgC,CAAC,aAAa,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0FAA0F,EAAE,KAAK,IAAI,EAAE;QACxG,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gCAAgC,CAAC,CAAC,CAAC;QAC3E,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,WAAW,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC,EAAE,CAAC,CAAC;QACnF,aAAa,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YAC9C,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,CAAC,CAAC,GAAG,CAAC,UAAU,uBAAuB,EAAE,CAAC,EAAE;SAC5H,CAAC,IAAI,CAAC,CAAC;QACR,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;YACxD,cAAc,EAAE,QAAQ;YACxB,WAAW,EAAE,cAAc;YAC3B,aAAa,EAAE,aAAa;YAC5B,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;SACrH,EAAE,IAAI,CAAC,CAAC;QACT,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAChE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kGAAkG,EAAE,KAAK,IAAI,EAAE;QAChH,MAAM,CAAC,GAAG,WAAW,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;YACxD,cAAc,EAAE,QAAQ;YACxB,WAAW,EAAE,cAAc;YAC3B,aAAa,EAAE,aAAa;YAC5B,QAAQ,EAAE,CAAC;oBACT,SAAS,EAAE,CAAC,CAAC,SAAS;oBACtB,YAAY,EAAE,aAAa;oBAC3B,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE,GAAG,MAAM,wBAAwB;oBACvC,gBAAgB,EAAE,IAAI;iBACvB,CAAC;SACH,EAAE,IAAI,CAAC,CAAC;QACT,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5G,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mFAAmF,EAAE,KAAK,IAAI,EAAE;QACjG,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gCAAgC,CAAC,CAAC,CAAC;QAC3E,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,WAAW,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC,EAAE,CAAC,CAAC;QACnF,aAAa,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YAC9C,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,CAAC,CAAC,GAAG,CAAC,UAAU,uBAAuB,EAAE,CAAC,EAAE;SAC5H,CAAC,IAAI,CAAC,CAAC;QACR,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;YACxD,cAAc,EAAE,QAAQ;YACxB,WAAW,EAAE,cAAc;YAC3B,aAAa,EAAE,aAAa;YAC5B,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;SAC5H,EAAE,KAAK,CAAC,CAAC;QACV,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAChE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;QAC3F,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iCAAiC,CAAC,CAAC,CAAC;QAC5E,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QACnD,aAAa,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,WAAW,CAAC;YACpB,YAAY,EAAE,IAAI;YAClB,OAAO,EAAE,OAAO;YAChB,cAAc,EAAE,aAAa;YAC7B,cAAc;YACd,gBAAgB,EAAE,CAAC;SACpB,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE;YAC7D,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAA0B,CAAC;YAClE,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC;YACnB,MAAM,GAAG,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;gBACxD,cAAc,EAAE,QAAQ;gBACxB,WAAW,EAAE,cAAc;gBAC3B,aAAa,EAAE,aAAa;gBAC5B,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;aACvH,EAAE,IAAI,CAAC,CAAC;YACT,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9G,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wFAAwF,EAAE,KAAK,IAAI,EAAE;QACtG,MAAM,GAAG,GAAG,4BAA4B,CAAC;QACzC,MAAM,CAAC,GAAG,WAAW,CAAC;YACpB,YAAY,EAAE,KAAK;YACnB,IAAI,EAAE,WAAW;YACjB,cAAc,EAAE,GAAG;YACnB,cAAc,EAAE,WAAW;SAC5B,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE;YAC7D,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAA0B,CAAC;YAClE,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC;YACnB,MAAM,GAAG,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;gBACxD,cAAc,EAAE,QAAQ;gBACxB,WAAW,EAAE,cAAc;gBAC3B,aAAa,EAAE,aAAa;gBAC5B,QAAQ,EAAE,CAAC;wBACT,SAAS,EAAE,CAAC,CAAC,SAAS;wBACtB,YAAY,EAAE,aAAa;wBAC3B,OAAO,EAAE,MAAM;wBACf,IAAI,EAAE,qBAAqB,GAAG,EAAE;wBAChC,gBAAgB,EAAE,IAAI;qBACvB,CAAC;aACH,EAAE,IAAI,CAAC,CAAC;YACT,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAChF,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yFAAyF,EAAE,KAAK,IAAI,EAAE;QACvG,MAAM,GAAG,GAAG,UAAU,CAAC;QACvB,MAAM,CAAC,GAAG,WAAW,CAAC;YACpB,YAAY,EAAE,KAAK;YACnB,IAAI,EAAE,WAAW;YACjB,cAAc,EAAE,GAAG;YACnB,cAAc,EAAE,WAAW;SAC5B,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE;YAC7D,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAA0B,CAAC;YAClE,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC;YACnB,MAAM,GAAG,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;gBACxD,cAAc,EAAE,QAAQ;gBACxB,WAAW,EAAE,cAAc;gBAC3B,aAAa,EAAE,aAAa;gBAC5B,QAAQ,EAAE,CAAC;wBACT,SAAS,EAAE,CAAC,CAAC,SAAS;wBACtB,YAAY,EAAE,aAAa;wBAC3B,OAAO,EAAE,MAAM;wBACf,IAAI,EAAE,iCAAiC;wBACvC,gBAAgB,EAAE,IAAI;qBACvB,CAAC;aACH,EAAE,IAAI,CAAC,CAAC;YACT,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/E,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;QACzF,MAAM,GAAG,GAAG,YAAY,CAAC;QACzB,MAAM,CAAC,GAAG,WAAW,CAAC;YACpB,YAAY,EAAE,KAAK;YACnB,IAAI,EAAE,WAAW;YACjB,cAAc,EAAE,GAAG;YACnB,cAAc,EAAE,WAAW;SAC5B,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE;YAC7D,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAA0B,CAAC;YAClE,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC;YACnB,MAAM,GAAG,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;gBACxD,cAAc,EAAE,QAAQ;gBACxB,WAAW,EAAE,cAAc;gBAC3B,aAAa,EAAE,aAAa;gBAC5B,QAAQ,EAAE,CAAC;wBACT,SAAS,EAAE,CAAC,CAAC,SAAS;wBACtB,YAAY,EAAE,aAAa;wBAC3B,OAAO,EAAE,MAAM;wBACf,IAAI,EAAE,qBAAqB,GAAG,IAAI;wBAClC,gBAAgB,EAAE,IAAI;qBACvB,CAAC;aACH,EAAE,IAAI,CAAC,CAAC;YACT,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/E,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mCAAmC,CAAC,CAAC,CAAC;QAC9E,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QACnD,aAAa,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,WAAW,CAAC;YACpB,YAAY,EAAE,IAAI;YAClB,OAAO,EAAE,OAAO;YAChB,cAAc,EAAE,aAAa;YAC7B,cAAc;YACd,gBAAgB,EAAE,CAAC;SACpB,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;YACxD,cAAc,EAAE,QAAQ;YACxB,WAAW,EAAE,cAAc;YAC3B,aAAa,EAAE,aAAa;YAC5B,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;SACrH,EAAE,IAAI,CAAC,CAAC;QACT,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3G,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC;QACN,CAAC,oBAAoB,EAAE,OAAO,EAAE,sBAA+B,EAAE,SAAkB,CAAC;QACpF,CAAC,iBAAiB,EAAE,aAAa,EAAE,OAAgB,EAAE,YAAqB,CAAC;KAC5E,CAAC,CAAC,oFAAoF,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,EAAE;QACpJ,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,qCAAqC,CAAC,CAAC,CAAC;QAChF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QACnD,aAAa,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,WAAW,CAAC;YACpB,YAAY,EAAE,IAAI;YAClB,OAAO;YACP,cAAc;YACd,iBAAiB;YACjB,cAAc;YACd,gBAAgB,EAAE,CAAC;SACpB,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;YACxD,cAAc,EAAE,QAAQ;YACxB,WAAW,EAAE,cAAc;YAC3B,aAAa,EAAE,aAAa;YAC5B,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;SACvH,EAAE,IAAI,CAAC,CAAC;QACT,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3G,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2GAA2G,EAAE,KAAK,IAAI,EAAE;QACzH,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,uCAAuC,CAAC,CAAC,CAAC;QAClF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QACnD,aAAa,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,WAAW,CAAC;YACpB,YAAY,EAAE,IAAI;YAClB,OAAO,EAAE,OAAO;YAChB,cAAc,EAAE,sBAAsB;YACtC,iBAAiB,EAAE,iBAAiB;YACpC,cAAc;YACd,gBAAgB,EAAE,CAAC;SACpB,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE;YAC7D,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAA0B,CAAC;YAClE,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC;YACnB,MAAM,GAAG,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;gBACxD,cAAc,EAAE,QAAQ;gBACxB,WAAW,EAAE,cAAc;gBAC3B,aAAa,EAAE,aAAa;gBAC5B,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;aACvH,EAAE,IAAI,CAAC,CAAC;YACT,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAChF,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8FAA8F,EAAE,KAAK,IAAI,EAAE;QAC5G,MAAM,CAAC,GAAG,WAAW,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE;YAC7D,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,mBAAmB,CAA0B,CAAC;YACpE,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC;YACnB,MAAM,GAAG,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;gBACxD,cAAc,EAAE,QAAQ;gBACxB,WAAW,EAAE,cAAc;gBAC3B,aAAa,EAAE,aAAa;gBAC5B,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;aACvH,EAAE,IAAI,CAAC,CAAC;YACT,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;YAC3F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAClE,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;QACrF,MAAM,CAAC,GAAG,WAAW,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,CAAC,MAAM,EAAE;YACxD,cAAc,EAAE,QAAQ;YACxB,WAAW,EAAE,cAAc;YAC3B,aAAa,EAAE,aAAa;YAC5B,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,CAAC;SACrI,EAAE,IAAI,CAAC,CAAC;QACT,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3F,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAChE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8FAA8F,EAAE,KAAK,IAAI,EAAE;QAC5G,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,6BAA6B,CAAC,CAAC,CAAC;QACxE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC;QACxE,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,MAAM,EAAE;YACtD,cAAc,EAAE,QAAQ;YACxB,WAAW,EAAE,cAAc;YAC3B,aAAa,EAAE,aAAa;YAC5B,QAAQ,EAAE,EAAE;SACb,EAAE,KAAK,CAAC,CAAC;QACV,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1F,MAAM,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -16,7 +16,7 @@ import { InstanceLifecycle, type LifecycleContext } from "./instance-lifecycle.j
16
16
  import { type ArchiverContext } from "./topic-archiver.js";
17
17
  import { type StatuslineWatcherContext } from "./statusline-watcher.js";
18
18
  import { type OutboundContext } from "./outbound-handlers.js";
19
- import { DeliveryOutbox } from "./delivery-outbox.js";
19
+ import { DeliveryOutbox, type DeliveryStatusSelector, type DeliveryStatusPage } from "./delivery-outbox.js";
20
20
  export declare const DURABLE_DELIVERY_LANE_ALERT_MS: number;
21
21
  import { type RawConfigPatch } from "./settings-api.js";
22
22
  import { type AgentEndpointContext } from "./agent-endpoint.js";
@@ -356,6 +356,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
356
356
  state: string;
357
357
  duplicate: boolean;
358
358
  };
359
+ queryDurableDeliveryStatus(callerInstance: string, selector: DeliveryStatusSelector): DeliveryStatusPage;
359
360
  getDaemonBootId(instanceName: string): string | undefined;
360
361
  markDurableResponseDelivered(sourceInstance: string, operationId: string): void;
361
362
  /** Called after a replacement Daemon object is installed in lifecycle.daemons. */
@@ -580,9 +580,6 @@ export class FleetManager {
580
580
  if (!this.deliveryOutboxRecovered) {
581
581
  const recovered = outbox.recoverForBoot(this.managerBootId);
582
582
  this.logger.info({ ...recovered }, "Recovered durable delivery outbox for this process boot");
583
- if (recovered.uncertain > 0) {
584
- this.notifyFleetError(`${recovered.uncertain} durable delivery outcome(s) became uncertain during process restart; inspect the sender's delivery notice before retrying.`);
585
- }
586
583
  this.deliveryOutboxRecovered = true;
587
584
  }
588
585
  outbox.on("admitted", () => this.scheduleDeliveryOutboxPump());
@@ -601,7 +598,7 @@ export class FleetManager {
601
598
  outbox.on("expired", (event) => {
602
599
  this.scheduleDeliveryOutboxPump();
603
600
  if (event.count)
604
- this.notifyFleetError(`${event.count} durable delivery row(s) expired before their target became available; sender outcome notice queued.`);
601
+ this.notifyFleetError(`${event.count} durable delivery row(s) reached the 24-hour outbox bound; ${event.uncertain ?? 0} unresolved submissions are uncertain and sender notices were queued.`);
605
602
  });
606
603
  this.scheduleDeliveryOutboxPump();
607
604
  }
@@ -632,6 +629,10 @@ export class FleetManager {
632
629
  duplicate: !admitted.inserted,
633
630
  };
634
631
  }
632
+ queryDurableDeliveryStatus(callerInstance, selector) {
633
+ this.ensureDeliveryOutbox();
634
+ return this.deliveryOutbox.queryStatusForInstance(callerInstance, selector);
635
+ }
635
636
  getDaemonBootId(instanceName) {
636
637
  return this.daemons.get(instanceName)?.bootId;
637
638
  }
@@ -716,26 +717,42 @@ export class FleetManager {
716
717
  const target = claimed.targetInstance;
717
718
  const attempt = claimed.attemptNo;
718
719
  const payload = { ...claimed.payload };
720
+ const laneReleased = this.waitForDurableLaneRelease(claimed);
719
721
  const rawMeta = payload.meta && typeof payload.meta === "object"
720
722
  ? payload.meta
721
723
  : {};
722
- payload.meta = {
723
- ...rawMeta,
724
- delivery_id: claimed.deliveryId,
725
- delivery_attempt: String(claimed.attemptNo),
726
- from_instance: typeof rawMeta.from_instance === "string" && rawMeta.from_instance
727
- ? rawMeta.from_instance
728
- : claimed.sourceInstance,
729
- correlation_id: claimed.correlationId ?? String(rawMeta.correlation_id ?? ""),
730
- };
731
724
  // Arm the visible liveness alert as soon as this generation owns the lane,
732
725
  // including time spent waiting for a handoff/idle gate to return.
733
- const laneReleased = this.waitForDurableLaneRelease(claimed);
734
726
  try {
735
- const sent = await this.deliverToInstance(target, payload, {
736
- isCrossInstance: true,
737
- waitForIdle: claimed.kind !== "steer",
738
- });
727
+ let sent;
728
+ if (claimed.kind === "raw_paste") {
729
+ if (payload.type !== "raw_paste" || typeof payload.content !== "string") {
730
+ throw new Error("Durable raw_paste row has an invalid payload");
731
+ }
732
+ // Preserve the schedule's exact bytes and raw route. The target daemon
733
+ // owns readiness, paste, one Enter, and the fenced outbox transition.
734
+ sent = await this.deliverToInstance(target, {
735
+ type: "raw_paste",
736
+ content: payload.content,
737
+ delivery_id: claimed.deliveryId,
738
+ delivery_attempt: String(claimed.attemptNo),
739
+ }, { waitForIdle: false });
740
+ }
741
+ else {
742
+ payload.meta = {
743
+ ...rawMeta,
744
+ delivery_id: claimed.deliveryId,
745
+ delivery_attempt: String(claimed.attemptNo),
746
+ from_instance: typeof rawMeta.from_instance === "string" && rawMeta.from_instance
747
+ ? rawMeta.from_instance
748
+ : claimed.sourceInstance,
749
+ correlation_id: claimed.correlationId ?? String(rawMeta.correlation_id ?? ""),
750
+ };
751
+ sent = await this.deliverToInstance(target, payload, {
752
+ isCrossInstance: true,
753
+ waitForIdle: claimed.kind !== "steer",
754
+ });
755
+ }
739
756
  if (!sent) {
740
757
  const current = outbox.get(claimed.deliveryId);
741
758
  if (current?.state === "delivering") {
@@ -3043,7 +3060,7 @@ export class FleetManager {
3043
3060
  ...DEFAULT_SCHEDULER_CONFIG,
3044
3061
  ...this.fleetConfig?.defaults.scheduler,
3045
3062
  };
3046
- this.scheduler = new Scheduler(join(this.dataDir, "scheduler.db"), (schedule) => this.handleScheduleTrigger(schedule), schedulerConfig, (name) => this.fleetConfig?.instances?.[name] != null || !!this.classicChannels?.getAll().some(ch => ch.instanceName === name));
3063
+ this.scheduler = new Scheduler(join(this.dataDir, "scheduler.db"), (schedule, runId) => this.handleScheduleTrigger(schedule, runId), schedulerConfig, (name) => this.fleetConfig?.instances?.[name] != null || !!this.classicChannels?.getAll().some(ch => ch.instanceName === name));
3047
3064
  this.scheduler.init();
3048
3065
  this.logger.info("Scheduler initialized");
3049
3066
  // Tips share the daily-report clock but remain an independent internal
@@ -5600,7 +5617,7 @@ export class FleetManager {
5600
5617
  }
5601
5618
  }
5602
5619
  // ===================== Scheduler =====================
5603
- async handleScheduleTrigger(schedule) {
5620
+ async handleScheduleTrigger(schedule, stableRunId = randomUUID()) {
5604
5621
  const { target, reply_chat_id, reply_thread_id, message, label, id, source, silent } = schedule;
5605
5622
  const RATE_LIMIT_DEFER_THRESHOLD = 85;
5606
5623
  const rl = this.statuslineWatcher.getRateLimits(target);
@@ -5618,20 +5635,32 @@ export class FleetManager {
5618
5635
  }
5619
5636
  // Silent mode: paste directly to tmux pane — no channel message.
5620
5637
  if (silent) {
5621
- const ipc = this.instanceIpcClients.get(target);
5622
- if (ipc) {
5623
- ipc.send({
5638
+ // The outbox commit is the schedule's durable acceptance point. The run
5639
+ // key is the scheduled fire instant (or an invocation UUID for a manual
5640
+ // trigger), so restart/catch-up reuses the same raw paste row.
5641
+ this.ensureDeliveryOutbox();
5642
+ const runKey = encodeURIComponent(stableRunId);
5643
+ const sourceKey = `schedule:${encodeURIComponent(id)}:${runKey}:${encodeURIComponent(target)}:raw_paste`;
5644
+ const operationId = `schedule:${id}:${stableRunId}`;
5645
+ const sourceBootId = this.daemons.get(source)?.bootId ?? this.managerBootId;
5646
+ const admitted = this.deliveryOutbox.admit({
5647
+ operationId,
5648
+ sourceKey,
5649
+ sourceInstance: source,
5650
+ sourceDaemonBootId: sourceBootId,
5651
+ targetInstance: target,
5652
+ kind: "raw_paste",
5653
+ correlationId: operationId,
5654
+ payload: {
5624
5655
  type: "raw_paste",
5625
5656
  content: message,
5626
- delivery_epoch: this.getDeliveryEpoch(target),
5627
- });
5628
- this.scheduler.recordRun(id, "delivered");
5629
- this.logger.info({ target, scheduleId: id, label }, "Silent schedule injected via raw_paste");
5630
- }
5631
- else {
5632
- this.scheduler.recordRun(id, "instance_offline", "IPC not connected");
5633
- this.logger.warn({ target, scheduleId: id }, "Silent schedule: IPC not connected, skipping");
5634
- }
5657
+ schedule_id: id,
5658
+ schedule_run_id: stableRunId,
5659
+ },
5660
+ });
5661
+ this.scheduler.recordRun(id, "queued", `durable raw_paste delivery_id=${admitted.delivery.deliveryId}`);
5662
+ this.logger.info({ target, scheduleId: id, runId: stableRunId, deliveryId: admitted.delivery.deliveryId, duplicate: !admitted.inserted }, "Silent schedule durably admitted as raw_paste");
5663
+ this.scheduleDeliveryOutboxPump();
5635
5664
  return;
5636
5665
  }
5637
5666
  const schedulerDefaults = this.fleetConfig?.defaults.scheduler;