@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.
- package/README.md +2 -0
- package/README.zh-TW.md +2 -0
- package/dist/agent-cli-instructions.md +5 -0
- package/dist/agent-cli.js +13 -0
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +1 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/backend/codex-session-lookup.d.ts +59 -0
- package/dist/backend/codex-session-lookup.js +139 -0
- package/dist/backend/codex-session-lookup.js.map +1 -0
- package/dist/backend/codex.d.ts +11 -0
- package/dist/backend/codex.js +115 -7
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/types.d.ts +12 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/mcp-tools.js +2 -0
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/cli.js +68 -2
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +11 -0
- package/dist/daemon.js +173 -23
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +90 -6
- package/dist/delivery-outbox.js +301 -39
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-outbox.test.js +268 -14
- package/dist/delivery-outbox.test.js.map +1 -1
- package/dist/delivery-queue-evidence.d.ts +15 -0
- package/dist/delivery-queue-evidence.js +35 -0
- package/dist/delivery-queue-evidence.js.map +1 -0
- package/dist/delivery-queue-evidence.test.d.ts +1 -0
- package/dist/delivery-queue-evidence.test.js +23 -0
- package/dist/delivery-queue-evidence.test.js.map +1 -0
- package/dist/delivery-reconciliation.d.ts +49 -0
- package/dist/delivery-reconciliation.js +296 -0
- package/dist/delivery-reconciliation.js.map +1 -0
- package/dist/delivery-reconciliation.test.d.ts +1 -0
- package/dist/delivery-reconciliation.test.js +411 -0
- package/dist/delivery-reconciliation.test.js.map +1 -0
- package/dist/fleet-manager.d.ts +2 -1
- package/dist/fleet-manager.js +61 -32
- package/dist/fleet-manager.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +13 -0
- package/dist/instance-lifecycle.js +85 -10
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.js +1 -0
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +6 -0
- package/dist/locale.js.map +1 -1
- package/dist/outbound-handlers.d.ts +3 -0
- package/dist/outbound-handlers.js +38 -1
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +7 -0
- package/dist/outbound-schemas.js +14 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/pane-write-lock.d.ts +2 -0
- package/dist/pane-write-lock.js +19 -0
- package/dist/pane-write-lock.js.map +1 -1
- package/dist/scheduler/scheduler.d.ts +5 -1
- package/dist/scheduler/scheduler.js +60 -20
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/scheduler/scheduler.test.js +41 -2
- package/dist/scheduler/scheduler.test.js.map +1 -1
- package/dist/scheduler/types.d.ts +1 -1
- package/dist/tmux-manager.d.ts +3 -3
- package/dist/tmux-manager.js +6 -6
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-permissions.js +6 -6
- package/dist/tool-permissions.js.map +1 -1
- package/dist/transcript-monitor.d.ts +3 -1
- package/dist/transcript-monitor.js +15 -0
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +9 -0
- package/dist/transcript-sources.js +14 -0
- package/dist/transcript-sources.js.map +1 -1
- 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":"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"}
|
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -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. */
|
package/dist/fleet-manager.js
CHANGED
|
@@ -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)
|
|
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
|
-
|
|
736
|
-
|
|
737
|
-
|
|
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
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
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
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
}
|
|
5631
|
-
|
|
5632
|
-
|
|
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;
|