@os-eco/overstory-cli 0.6.4 → 0.6.6
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 +61 -61
- package/agents/builder.md +16 -16
- package/agents/coordinator.md +57 -57
- package/agents/issue-reviews.md +71 -0
- package/agents/lead.md +43 -42
- package/agents/merger.md +15 -15
- package/agents/monitor.md +37 -37
- package/agents/pr-reviews.md +60 -0
- package/agents/prioritize.md +110 -0
- package/agents/release.md +56 -0
- package/agents/reviewer.md +15 -15
- package/agents/scout.md +18 -18
- package/agents/supervisor.md +78 -78
- package/package.json +1 -1
- package/src/agents/checkpoint.test.ts +2 -2
- package/src/agents/hooks-deployer.test.ts +59 -25
- package/src/agents/hooks-deployer.ts +24 -6
- package/src/agents/identity.test.ts +27 -27
- package/src/agents/identity.ts +10 -10
- package/src/agents/lifecycle.test.ts +6 -6
- package/src/agents/lifecycle.ts +2 -2
- package/src/agents/overlay.test.ts +14 -14
- package/src/agents/overlay.ts +14 -14
- package/src/commands/agents.test.ts +5 -5
- package/src/commands/agents.ts +10 -9
- package/src/commands/clean.test.ts +5 -5
- package/src/commands/clean.ts +5 -5
- package/src/commands/completions.test.ts +10 -10
- package/src/commands/completions.ts +26 -28
- package/src/commands/coordinator.test.ts +4 -4
- package/src/commands/coordinator.ts +13 -13
- package/src/commands/costs.test.ts +45 -45
- package/src/commands/costs.ts +1 -1
- package/src/commands/dashboard.ts +11 -11
- package/src/commands/doctor.ts +4 -4
- package/src/commands/errors.ts +1 -1
- package/src/commands/feed.ts +1 -1
- package/src/commands/group.ts +3 -3
- package/src/commands/hooks.test.ts +7 -7
- package/src/commands/hooks.ts +7 -7
- package/src/commands/init.test.ts +6 -2
- package/src/commands/init.ts +19 -19
- package/src/commands/inspect.test.ts +16 -16
- package/src/commands/inspect.ts +19 -19
- package/src/commands/log.test.ts +21 -21
- package/src/commands/log.ts +10 -10
- package/src/commands/logs.ts +1 -1
- package/src/commands/mail.test.ts +7 -7
- package/src/commands/mail.ts +28 -11
- package/src/commands/merge.test.ts +8 -8
- package/src/commands/merge.ts +15 -15
- package/src/commands/metrics.test.ts +7 -7
- package/src/commands/metrics.ts +3 -3
- package/src/commands/monitor.test.ts +5 -5
- package/src/commands/monitor.ts +5 -5
- package/src/commands/nudge.test.ts +1 -1
- package/src/commands/nudge.ts +1 -1
- package/src/commands/prime.test.ts +5 -5
- package/src/commands/prime.ts +8 -8
- package/src/commands/replay.ts +1 -1
- package/src/commands/run.test.ts +1 -1
- package/src/commands/run.ts +2 -2
- package/src/commands/sling.test.ts +89 -7
- package/src/commands/sling.ts +109 -18
- package/src/commands/spec.test.ts +2 -2
- package/src/commands/spec.ts +13 -14
- package/src/commands/status.test.ts +99 -3
- package/src/commands/status.ts +19 -20
- package/src/commands/stop.test.ts +1 -1
- package/src/commands/stop.ts +2 -2
- package/src/commands/supervisor.test.ts +10 -10
- package/src/commands/supervisor.ts +14 -14
- package/src/commands/trace.test.ts +7 -7
- package/src/commands/trace.ts +10 -10
- package/src/commands/watch.ts +5 -5
- package/src/commands/worktree.test.ts +208 -32
- package/src/commands/worktree.ts +56 -18
- package/src/doctor/consistency.test.ts +14 -14
- package/src/doctor/dependencies.test.ts +5 -5
- package/src/doctor/dependencies.ts +2 -2
- package/src/doctor/logs.ts +1 -1
- package/src/doctor/merge-queue.test.ts +4 -4
- package/src/doctor/structure.test.ts +1 -1
- package/src/doctor/structure.ts +1 -1
- package/src/doctor/version.test.ts +3 -3
- package/src/doctor/version.ts +1 -1
- package/src/e2e/init-sling-lifecycle.test.ts +8 -4
- package/src/errors.ts +1 -1
- package/src/index.ts +13 -11
- package/src/mail/broadcast.test.ts +1 -1
- package/src/mail/client.test.ts +7 -7
- package/src/mail/client.ts +2 -2
- package/src/mail/store.test.ts +3 -3
- package/src/merge/queue.test.ts +12 -12
- package/src/merge/queue.ts +2 -2
- package/src/merge/resolver.test.ts +159 -7
- package/src/merge/resolver.ts +46 -2
- package/src/metrics/store.test.ts +44 -44
- package/src/metrics/store.ts +2 -2
- package/src/metrics/summary.test.ts +35 -35
- package/src/mulch/client.test.ts +1 -1
- package/src/mulch/client.ts +1 -1
- package/src/sessions/compat.test.ts +3 -3
- package/src/sessions/compat.ts +1 -1
- package/src/sessions/store.test.ts +4 -4
- package/src/sessions/store.ts +2 -2
- package/src/types.ts +14 -14
- package/src/watchdog/daemon.test.ts +10 -10
- package/src/watchdog/daemon.ts +1 -1
- package/src/watchdog/health.test.ts +1 -1
- package/src/worktree/manager.test.ts +20 -20
- package/src/worktree/manager.ts +120 -4
- package/src/worktree/tmux.test.ts +8 -3
- package/src/worktree/tmux.ts +19 -18
- package/templates/CLAUDE.md.tmpl +27 -27
- package/templates/hooks.json.tmpl +15 -11
- package/templates/overlay.md.tmpl +7 -7
|
@@ -33,7 +33,7 @@ afterEach(async () => {
|
|
|
33
33
|
function makeSession(overrides: Partial<SessionMetrics> = {}): SessionMetrics {
|
|
34
34
|
return {
|
|
35
35
|
agentName: "test-agent",
|
|
36
|
-
|
|
36
|
+
taskId: "test-task-123",
|
|
37
37
|
capability: "builder",
|
|
38
38
|
startedAt: new Date("2026-01-01T00:00:00Z").toISOString(),
|
|
39
39
|
completedAt: new Date("2026-01-01T00:05:00Z").toISOString(),
|
|
@@ -66,9 +66,9 @@ describe("generateSummary", () => {
|
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
test("counts total and completed sessions correctly", () => {
|
|
69
|
-
store.recordSession(makeSession({
|
|
70
|
-
store.recordSession(makeSession({
|
|
71
|
-
store.recordSession(makeSession({
|
|
69
|
+
store.recordSession(makeSession({ taskId: "task-1", completedAt: "2026-01-01T00:05:00Z" }));
|
|
70
|
+
store.recordSession(makeSession({ taskId: "task-2", completedAt: null }));
|
|
71
|
+
store.recordSession(makeSession({ taskId: "task-3", completedAt: "2026-01-01T00:10:00Z" }));
|
|
72
72
|
|
|
73
73
|
const summary = generateSummary(store);
|
|
74
74
|
|
|
@@ -79,21 +79,21 @@ describe("generateSummary", () => {
|
|
|
79
79
|
test("groups by capability with correct counts and avg durations", () => {
|
|
80
80
|
store.recordSession(
|
|
81
81
|
makeSession({
|
|
82
|
-
|
|
82
|
+
taskId: "task-1",
|
|
83
83
|
capability: "builder",
|
|
84
84
|
durationMs: 100_000,
|
|
85
85
|
}),
|
|
86
86
|
);
|
|
87
87
|
store.recordSession(
|
|
88
88
|
makeSession({
|
|
89
|
-
|
|
89
|
+
taskId: "task-2",
|
|
90
90
|
capability: "builder",
|
|
91
91
|
durationMs: 200_000,
|
|
92
92
|
}),
|
|
93
93
|
);
|
|
94
94
|
store.recordSession(
|
|
95
95
|
makeSession({
|
|
96
|
-
|
|
96
|
+
taskId: "task-3",
|
|
97
97
|
capability: "scout",
|
|
98
98
|
durationMs: 50_000,
|
|
99
99
|
}),
|
|
@@ -112,10 +112,10 @@ describe("generateSummary", () => {
|
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
test("respects the limit parameter for recentSessions", () => {
|
|
115
|
-
store.recordSession(makeSession({
|
|
116
|
-
store.recordSession(makeSession({
|
|
117
|
-
store.recordSession(makeSession({
|
|
118
|
-
store.recordSession(makeSession({
|
|
115
|
+
store.recordSession(makeSession({ taskId: "task-1" }));
|
|
116
|
+
store.recordSession(makeSession({ taskId: "task-2" }));
|
|
117
|
+
store.recordSession(makeSession({ taskId: "task-3" }));
|
|
118
|
+
store.recordSession(makeSession({ taskId: "task-4" }));
|
|
119
119
|
|
|
120
120
|
const summary = generateSummary(store, 2);
|
|
121
121
|
|
|
@@ -124,9 +124,9 @@ describe("generateSummary", () => {
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
test("sessions without completedAt counted in total but not completed", () => {
|
|
127
|
-
store.recordSession(makeSession({
|
|
128
|
-
store.recordSession(makeSession({
|
|
129
|
-
store.recordSession(makeSession({
|
|
127
|
+
store.recordSession(makeSession({ taskId: "task-1", completedAt: null }));
|
|
128
|
+
store.recordSession(makeSession({ taskId: "task-2", completedAt: null }));
|
|
129
|
+
store.recordSession(makeSession({ taskId: "task-3", completedAt: "2026-01-01T00:05:00Z" }));
|
|
130
130
|
|
|
131
131
|
const summary = generateSummary(store);
|
|
132
132
|
|
|
@@ -137,7 +137,7 @@ describe("generateSummary", () => {
|
|
|
137
137
|
test("aggregates token totals across all sessions", () => {
|
|
138
138
|
store.recordSession(
|
|
139
139
|
makeSession({
|
|
140
|
-
|
|
140
|
+
taskId: "task-1",
|
|
141
141
|
inputTokens: 10_000,
|
|
142
142
|
outputTokens: 2_000,
|
|
143
143
|
cacheReadTokens: 50_000,
|
|
@@ -147,7 +147,7 @@ describe("generateSummary", () => {
|
|
|
147
147
|
);
|
|
148
148
|
store.recordSession(
|
|
149
149
|
makeSession({
|
|
150
|
-
|
|
150
|
+
taskId: "task-2",
|
|
151
151
|
inputTokens: 20_000,
|
|
152
152
|
outputTokens: 3_000,
|
|
153
153
|
cacheReadTokens: 80_000,
|
|
@@ -166,7 +166,7 @@ describe("generateSummary", () => {
|
|
|
166
166
|
});
|
|
167
167
|
|
|
168
168
|
test("token totals are zero when no sessions have token data", () => {
|
|
169
|
-
store.recordSession(makeSession({
|
|
169
|
+
store.recordSession(makeSession({ taskId: "task-1" }));
|
|
170
170
|
|
|
171
171
|
const summary = generateSummary(store);
|
|
172
172
|
|
|
@@ -180,14 +180,14 @@ describe("generateSummary", () => {
|
|
|
180
180
|
test("token totals skip null cost entries gracefully", () => {
|
|
181
181
|
store.recordSession(
|
|
182
182
|
makeSession({
|
|
183
|
-
|
|
183
|
+
taskId: "task-1",
|
|
184
184
|
inputTokens: 100,
|
|
185
185
|
estimatedCostUsd: 0.5,
|
|
186
186
|
}),
|
|
187
187
|
);
|
|
188
188
|
store.recordSession(
|
|
189
189
|
makeSession({
|
|
190
|
-
|
|
190
|
+
taskId: "task-2",
|
|
191
191
|
inputTokens: 200,
|
|
192
192
|
estimatedCostUsd: null, // no cost data
|
|
193
193
|
}),
|
|
@@ -202,7 +202,7 @@ describe("generateSummary", () => {
|
|
|
202
202
|
test("capability breakdown excludes incomplete sessions from avgDurationMs", () => {
|
|
203
203
|
store.recordSession(
|
|
204
204
|
makeSession({
|
|
205
|
-
|
|
205
|
+
taskId: "task-1",
|
|
206
206
|
capability: "builder",
|
|
207
207
|
durationMs: 100_000,
|
|
208
208
|
completedAt: null,
|
|
@@ -210,14 +210,14 @@ describe("generateSummary", () => {
|
|
|
210
210
|
);
|
|
211
211
|
store.recordSession(
|
|
212
212
|
makeSession({
|
|
213
|
-
|
|
213
|
+
taskId: "task-2",
|
|
214
214
|
capability: "builder",
|
|
215
215
|
durationMs: 200_000,
|
|
216
216
|
}),
|
|
217
217
|
);
|
|
218
218
|
store.recordSession(
|
|
219
219
|
makeSession({
|
|
220
|
-
|
|
220
|
+
taskId: "task-3",
|
|
221
221
|
capability: "builder",
|
|
222
222
|
durationMs: 300_000,
|
|
223
223
|
}),
|
|
@@ -242,8 +242,8 @@ describe("formatSummary", () => {
|
|
|
242
242
|
});
|
|
243
243
|
|
|
244
244
|
test("shows total/completed/average duration", () => {
|
|
245
|
-
store.recordSession(makeSession({
|
|
246
|
-
store.recordSession(makeSession({
|
|
245
|
+
store.recordSession(makeSession({ taskId: "task-1", durationMs: 100_000 }));
|
|
246
|
+
store.recordSession(makeSession({ taskId: "task-2", durationMs: 200_000 }));
|
|
247
247
|
|
|
248
248
|
const summary = generateSummary(store);
|
|
249
249
|
const formatted = formatSummary(summary);
|
|
@@ -254,8 +254,8 @@ describe("formatSummary", () => {
|
|
|
254
254
|
});
|
|
255
255
|
|
|
256
256
|
test("shows capability breakdown", () => {
|
|
257
|
-
store.recordSession(makeSession({
|
|
258
|
-
store.recordSession(makeSession({
|
|
257
|
+
store.recordSession(makeSession({ taskId: "task-1", capability: "builder" }));
|
|
258
|
+
store.recordSession(makeSession({ taskId: "task-2", capability: "scout" }));
|
|
259
259
|
|
|
260
260
|
const summary = generateSummary(store);
|
|
261
261
|
const formatted = formatSummary(summary);
|
|
@@ -268,14 +268,14 @@ describe("formatSummary", () => {
|
|
|
268
268
|
test("shows recent sessions with status (done vs running)", () => {
|
|
269
269
|
store.recordSession(
|
|
270
270
|
makeSession({
|
|
271
|
-
|
|
271
|
+
taskId: "task-1",
|
|
272
272
|
agentName: "agent-done",
|
|
273
273
|
completedAt: "2026-01-01T00:05:00Z",
|
|
274
274
|
}),
|
|
275
275
|
);
|
|
276
276
|
store.recordSession(
|
|
277
277
|
makeSession({
|
|
278
|
-
|
|
278
|
+
taskId: "task-2",
|
|
279
279
|
agentName: "agent-running",
|
|
280
280
|
completedAt: null,
|
|
281
281
|
}),
|
|
@@ -293,7 +293,7 @@ describe("formatSummary", () => {
|
|
|
293
293
|
});
|
|
294
294
|
|
|
295
295
|
test("formatDuration: <1000ms shows ms", () => {
|
|
296
|
-
store.recordSession(makeSession({
|
|
296
|
+
store.recordSession(makeSession({ taskId: "task-1", durationMs: 500 }));
|
|
297
297
|
|
|
298
298
|
const summary = generateSummary(store);
|
|
299
299
|
const formatted = formatSummary(summary);
|
|
@@ -302,7 +302,7 @@ describe("formatSummary", () => {
|
|
|
302
302
|
});
|
|
303
303
|
|
|
304
304
|
test("formatDuration: <60000ms shows seconds", () => {
|
|
305
|
-
store.recordSession(makeSession({
|
|
305
|
+
store.recordSession(makeSession({ taskId: "task-1", durationMs: 5_500 }));
|
|
306
306
|
|
|
307
307
|
const summary = generateSummary(store);
|
|
308
308
|
const formatted = formatSummary(summary);
|
|
@@ -311,7 +311,7 @@ describe("formatSummary", () => {
|
|
|
311
311
|
});
|
|
312
312
|
|
|
313
313
|
test("formatDuration: >=60000ms shows minutes+seconds", () => {
|
|
314
|
-
store.recordSession(makeSession({
|
|
314
|
+
store.recordSession(makeSession({ taskId: "task-1", durationMs: 125_000 }));
|
|
315
315
|
|
|
316
316
|
const summary = generateSummary(store);
|
|
317
317
|
const formatted = formatSummary(summary);
|
|
@@ -322,7 +322,7 @@ describe("formatSummary", () => {
|
|
|
322
322
|
test("shows token usage section when sessions have token data", () => {
|
|
323
323
|
store.recordSession(
|
|
324
324
|
makeSession({
|
|
325
|
-
|
|
325
|
+
taskId: "task-1",
|
|
326
326
|
inputTokens: 15_000,
|
|
327
327
|
outputTokens: 3_000,
|
|
328
328
|
cacheReadTokens: 100_000,
|
|
@@ -344,7 +344,7 @@ describe("formatSummary", () => {
|
|
|
344
344
|
});
|
|
345
345
|
|
|
346
346
|
test("hides token usage section when no token data exists", () => {
|
|
347
|
-
store.recordSession(makeSession({
|
|
347
|
+
store.recordSession(makeSession({ taskId: "task-1" }));
|
|
348
348
|
|
|
349
349
|
const summary = generateSummary(store);
|
|
350
350
|
const formatted = formatSummary(summary);
|
|
@@ -355,7 +355,7 @@ describe("formatSummary", () => {
|
|
|
355
355
|
test("shows per-session cost in recent sessions", () => {
|
|
356
356
|
store.recordSession(
|
|
357
357
|
makeSession({
|
|
358
|
-
|
|
358
|
+
taskId: "task-1",
|
|
359
359
|
agentName: "agent-costly",
|
|
360
360
|
inputTokens: 10_000,
|
|
361
361
|
outputTokens: 2_000,
|
|
@@ -373,7 +373,7 @@ describe("formatSummary", () => {
|
|
|
373
373
|
test("formats large token counts with M suffix", () => {
|
|
374
374
|
store.recordSession(
|
|
375
375
|
makeSession({
|
|
376
|
-
|
|
376
|
+
taskId: "task-1",
|
|
377
377
|
inputTokens: 2_500_000,
|
|
378
378
|
outputTokens: 500_000,
|
|
379
379
|
cacheReadTokens: 0,
|
package/src/mulch/client.test.ts
CHANGED
|
@@ -360,7 +360,7 @@ describe("createMulchClient", () => {
|
|
|
360
360
|
await addProc.exited;
|
|
361
361
|
|
|
362
362
|
const client = createMulchClient(tempDir);
|
|
363
|
-
// The flag is passed correctly, but may fail if the
|
|
363
|
+
// The flag is passed correctly, but may fail if the task ID is invalid
|
|
364
364
|
// or if other required fields are missing. This test documents that the
|
|
365
365
|
// flag is properly passed to the CLI.
|
|
366
366
|
try {
|
package/src/mulch/client.ts
CHANGED
|
@@ -116,7 +116,7 @@ export function createMulchClient(cwd: string): MulchClient {
|
|
|
116
116
|
args: string[],
|
|
117
117
|
context: string,
|
|
118
118
|
): Promise<{ stdout: string; stderr: string }> {
|
|
119
|
-
const { stdout, stderr, exitCode } = await runCommand(["
|
|
119
|
+
const { stdout, stderr, exitCode } = await runCommand(["ml", ...args], cwd);
|
|
120
120
|
if (exitCode !== 0) {
|
|
121
121
|
throw new AgentError(`mulch ${context} failed (exit ${exitCode}): ${stderr.trim()}`);
|
|
122
122
|
}
|
|
@@ -39,7 +39,7 @@ function makeJsonSession(overrides: Record<string, unknown> = {}): Record<string
|
|
|
39
39
|
capability: "builder",
|
|
40
40
|
worktreePath: "/tmp/worktrees/test-agent",
|
|
41
41
|
branchName: "overstory/test-agent/task-1",
|
|
42
|
-
|
|
42
|
+
taskId: "task-1",
|
|
43
43
|
tmuxSession: "overstory-test-agent",
|
|
44
44
|
state: "working",
|
|
45
45
|
pid: 12345,
|
|
@@ -198,7 +198,7 @@ describe("data integrity", () => {
|
|
|
198
198
|
capability: "scout",
|
|
199
199
|
worktreePath: "/tmp/worktrees/full-agent",
|
|
200
200
|
branchName: "overstory/full-agent/task-42",
|
|
201
|
-
|
|
201
|
+
taskId: "task-42",
|
|
202
202
|
tmuxSession: "overstory-full-agent",
|
|
203
203
|
state: "stalled",
|
|
204
204
|
pid: 99999,
|
|
@@ -221,7 +221,7 @@ describe("data integrity", () => {
|
|
|
221
221
|
expect(result?.capability).toBe("scout");
|
|
222
222
|
expect(result?.worktreePath).toBe("/tmp/worktrees/full-agent");
|
|
223
223
|
expect(result?.branchName).toBe("overstory/full-agent/task-42");
|
|
224
|
-
expect(result?.
|
|
224
|
+
expect(result?.taskId).toBe("task-42");
|
|
225
225
|
expect(result?.tmuxSession).toBe("overstory-full-agent");
|
|
226
226
|
expect(result?.state).toBe("stalled");
|
|
227
227
|
expect(result?.pid).toBe(99999);
|
package/src/sessions/compat.ts
CHANGED
|
@@ -25,7 +25,7 @@ function normalizeSession(raw: Record<string, unknown>): AgentSession {
|
|
|
25
25
|
capability: raw.capability as string,
|
|
26
26
|
worktreePath: raw.worktreePath as string,
|
|
27
27
|
branchName: raw.branchName as string,
|
|
28
|
-
|
|
28
|
+
taskId: raw.taskId as string,
|
|
29
29
|
tmuxSession: raw.tmuxSession as string,
|
|
30
30
|
state: raw.state as AgentSession["state"],
|
|
31
31
|
pid: (raw.pid as number | null) ?? null,
|
|
@@ -35,7 +35,7 @@ function makeSession(overrides: Partial<AgentSession> = {}): AgentSession {
|
|
|
35
35
|
capability: "builder",
|
|
36
36
|
worktreePath: "/tmp/worktrees/test-agent",
|
|
37
37
|
branchName: "overstory/test-agent/task-1",
|
|
38
|
-
|
|
38
|
+
taskId: "task-1",
|
|
39
39
|
tmuxSession: "overstory-test-agent",
|
|
40
40
|
state: "booting",
|
|
41
41
|
pid: 12345,
|
|
@@ -81,7 +81,7 @@ describe("upsert", () => {
|
|
|
81
81
|
capability: "scout",
|
|
82
82
|
worktreePath: "/tmp/worktrees/roundtrip",
|
|
83
83
|
branchName: "overstory/roundtrip-agent/task-42",
|
|
84
|
-
|
|
84
|
+
taskId: "task-42",
|
|
85
85
|
tmuxSession: "overstory-roundtrip-agent",
|
|
86
86
|
state: "working",
|
|
87
87
|
pid: 99999,
|
|
@@ -547,11 +547,11 @@ describe("edge cases", () => {
|
|
|
547
547
|
});
|
|
548
548
|
|
|
549
549
|
test("empty string fields are stored correctly", () => {
|
|
550
|
-
const session = makeSession({
|
|
550
|
+
const session = makeSession({ taskId: "", capability: "builder" });
|
|
551
551
|
store.upsert(session);
|
|
552
552
|
|
|
553
553
|
const result = store.getByName("test-agent");
|
|
554
|
-
expect(result?.
|
|
554
|
+
expect(result?.taskId).toBe("");
|
|
555
555
|
});
|
|
556
556
|
});
|
|
557
557
|
|
package/src/sessions/store.ts
CHANGED
|
@@ -113,7 +113,7 @@ function rowToSession(row: SessionRow): AgentSession {
|
|
|
113
113
|
capability: row.capability,
|
|
114
114
|
worktreePath: row.worktree_path,
|
|
115
115
|
branchName: row.branch_name,
|
|
116
|
-
|
|
116
|
+
taskId: row.task_id,
|
|
117
117
|
tmuxSession: row.tmux_session,
|
|
118
118
|
state: row.state as AgentState,
|
|
119
119
|
pid: row.pid,
|
|
@@ -276,7 +276,7 @@ export function createSessionStore(dbPath: string): SessionStore {
|
|
|
276
276
|
$capability: session.capability,
|
|
277
277
|
$worktree_path: session.worktreePath,
|
|
278
278
|
$branch_name: session.branchName,
|
|
279
|
-
$task_id: session.
|
|
279
|
+
$task_id: session.taskId,
|
|
280
280
|
$tmux_session: session.tmuxSession,
|
|
281
281
|
$state: session.state,
|
|
282
282
|
$pid: session.pid,
|
package/src/types.ts
CHANGED
|
@@ -129,7 +129,7 @@ export interface AgentSession {
|
|
|
129
129
|
capability: string; // Which agent definition
|
|
130
130
|
worktreePath: string;
|
|
131
131
|
branchName: string;
|
|
132
|
-
|
|
132
|
+
taskId: string; // Task being worked
|
|
133
133
|
tmuxSession: string; // Tmux session name
|
|
134
134
|
state: AgentState;
|
|
135
135
|
pid: number | null; // Claude Code PID
|
|
@@ -151,7 +151,7 @@ export interface AgentIdentity {
|
|
|
151
151
|
sessionsCompleted: number;
|
|
152
152
|
expertiseDomains: string[];
|
|
153
153
|
recentTasks: Array<{
|
|
154
|
-
|
|
154
|
+
taskId: string;
|
|
155
155
|
summary: string;
|
|
156
156
|
completedAt: string;
|
|
157
157
|
}>;
|
|
@@ -210,7 +210,7 @@ export interface MailMessage {
|
|
|
210
210
|
|
|
211
211
|
/** Worker signals task completion to supervisor. */
|
|
212
212
|
export interface WorkerDonePayload {
|
|
213
|
-
|
|
213
|
+
taskId: string;
|
|
214
214
|
branch: string;
|
|
215
215
|
exitCode: number;
|
|
216
216
|
filesModified: string[];
|
|
@@ -219,7 +219,7 @@ export interface WorkerDonePayload {
|
|
|
219
219
|
/** Supervisor signals branch is verified and ready for merge. */
|
|
220
220
|
export interface MergeReadyPayload {
|
|
221
221
|
branch: string;
|
|
222
|
-
|
|
222
|
+
taskId: string;
|
|
223
223
|
agentName: string;
|
|
224
224
|
filesModified: string[];
|
|
225
225
|
}
|
|
@@ -227,14 +227,14 @@ export interface MergeReadyPayload {
|
|
|
227
227
|
/** Merger signals branch was merged successfully. */
|
|
228
228
|
export interface MergedPayload {
|
|
229
229
|
branch: string;
|
|
230
|
-
|
|
230
|
+
taskId: string;
|
|
231
231
|
tier: ResolutionTier;
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
/** Merger signals merge failed, needs rework. */
|
|
235
235
|
export interface MergeFailedPayload {
|
|
236
236
|
branch: string;
|
|
237
|
-
|
|
237
|
+
taskId: string;
|
|
238
238
|
conflictFiles: string[];
|
|
239
239
|
errorMessage: string;
|
|
240
240
|
}
|
|
@@ -242,7 +242,7 @@ export interface MergeFailedPayload {
|
|
|
242
242
|
/** Any agent escalates an issue to a higher-level decision-maker. */
|
|
243
243
|
export interface EscalationPayload {
|
|
244
244
|
severity: "warning" | "error" | "critical";
|
|
245
|
-
|
|
245
|
+
taskId: string | null;
|
|
246
246
|
context: string;
|
|
247
247
|
}
|
|
248
248
|
|
|
@@ -254,7 +254,7 @@ export interface HealthCheckPayload {
|
|
|
254
254
|
|
|
255
255
|
/** Coordinator dispatches work to a supervisor. */
|
|
256
256
|
export interface DispatchPayload {
|
|
257
|
-
|
|
257
|
+
taskId: string;
|
|
258
258
|
specPath: string;
|
|
259
259
|
capability: Capability;
|
|
260
260
|
fileScope: string[];
|
|
@@ -262,7 +262,7 @@ export interface DispatchPayload {
|
|
|
262
262
|
|
|
263
263
|
/** Supervisor assigns work to a specific worker. */
|
|
264
264
|
export interface AssignPayload {
|
|
265
|
-
|
|
265
|
+
taskId: string;
|
|
266
266
|
specPath: string;
|
|
267
267
|
workerName: string;
|
|
268
268
|
branch: string;
|
|
@@ -284,7 +284,7 @@ export interface MailPayloadMap {
|
|
|
284
284
|
|
|
285
285
|
export interface OverlayConfig {
|
|
286
286
|
agentName: string;
|
|
287
|
-
|
|
287
|
+
taskId: string;
|
|
288
288
|
specPath: string | null;
|
|
289
289
|
branchName: string;
|
|
290
290
|
worktreePath: string;
|
|
@@ -312,7 +312,7 @@ export type ResolutionTier = "clean-merge" | "auto-resolve" | "ai-resolve" | "re
|
|
|
312
312
|
|
|
313
313
|
export interface MergeEntry {
|
|
314
314
|
branchName: string;
|
|
315
|
-
|
|
315
|
+
taskId: string;
|
|
316
316
|
agentName: string;
|
|
317
317
|
filesModified: string[];
|
|
318
318
|
enqueuedAt: string;
|
|
@@ -376,7 +376,7 @@ export interface LogEvent {
|
|
|
376
376
|
|
|
377
377
|
export interface SessionMetrics {
|
|
378
378
|
agentName: string;
|
|
379
|
-
|
|
379
|
+
taskId: string;
|
|
380
380
|
capability: string;
|
|
381
381
|
startedAt: string;
|
|
382
382
|
completedAt: string | null;
|
|
@@ -645,7 +645,7 @@ export interface MulchCompactResult {
|
|
|
645
645
|
*/
|
|
646
646
|
export interface SessionCheckpoint {
|
|
647
647
|
agentName: string;
|
|
648
|
-
|
|
648
|
+
taskId: string;
|
|
649
649
|
sessionId: string; // The AgentSession.id that created this checkpoint
|
|
650
650
|
timestamp: string; // ISO
|
|
651
651
|
progressSummary: string; // Human-readable summary of work done so far
|
|
@@ -677,7 +677,7 @@ export interface AgentLayers {
|
|
|
677
677
|
sandbox: {
|
|
678
678
|
worktreePath: string;
|
|
679
679
|
branchName: string;
|
|
680
|
-
|
|
680
|
+
taskId: string;
|
|
681
681
|
};
|
|
682
682
|
session: {
|
|
683
683
|
id: string;
|
|
@@ -66,7 +66,7 @@ function makeSession(overrides: Partial<AgentSession> = {}): AgentSession {
|
|
|
66
66
|
capability: "builder",
|
|
67
67
|
worktreePath: "/tmp/test",
|
|
68
68
|
branchName: "overstory/test-agent/test-task",
|
|
69
|
-
|
|
69
|
+
taskId: "test-task",
|
|
70
70
|
tmuxSession: "overstory-test-agent",
|
|
71
71
|
state: "working",
|
|
72
72
|
pid: process.pid, // Use our own PID so isProcessRunning returns true
|
|
@@ -798,7 +798,7 @@ describe("daemon tick", () => {
|
|
|
798
798
|
agentName: "old-agent",
|
|
799
799
|
worktreePath: "/tmp/test",
|
|
800
800
|
branchName: "overstory/old-agent/task",
|
|
801
|
-
|
|
801
|
+
taskId: "task",
|
|
802
802
|
tmuxSession: "overstory-old-agent",
|
|
803
803
|
state: "working",
|
|
804
804
|
pid: process.pid,
|
|
@@ -1133,7 +1133,7 @@ describe("daemon mulch failure recording", () => {
|
|
|
1133
1133
|
const session = makeSession({
|
|
1134
1134
|
agentName: "dying-agent",
|
|
1135
1135
|
capability: "builder",
|
|
1136
|
-
|
|
1136
|
+
taskId: "task-123",
|
|
1137
1137
|
tmuxSession: "overstory-dying-agent",
|
|
1138
1138
|
state: "working",
|
|
1139
1139
|
lastActivity: new Date().toISOString(),
|
|
@@ -1158,7 +1158,7 @@ describe("daemon mulch failure recording", () => {
|
|
|
1158
1158
|
expect(failureMock.calls[0]?.tier).toBe(0);
|
|
1159
1159
|
expect(failureMock.calls[0]?.session.agentName).toBe("dying-agent");
|
|
1160
1160
|
expect(failureMock.calls[0]?.session.capability).toBe("builder");
|
|
1161
|
-
expect(failureMock.calls[0]?.session.
|
|
1161
|
+
expect(failureMock.calls[0]?.session.taskId).toBe("task-123");
|
|
1162
1162
|
// Reason should be either the reconciliationNote or default "Process terminated"
|
|
1163
1163
|
expect(failureMock.calls[0]?.reason).toBeDefined();
|
|
1164
1164
|
});
|
|
@@ -1169,7 +1169,7 @@ describe("daemon mulch failure recording", () => {
|
|
|
1169
1169
|
const session = makeSession({
|
|
1170
1170
|
agentName: "triaged-agent",
|
|
1171
1171
|
capability: "scout",
|
|
1172
|
-
|
|
1172
|
+
taskId: "task-456",
|
|
1173
1173
|
tmuxSession: "overstory-triaged-agent",
|
|
1174
1174
|
state: "stalled",
|
|
1175
1175
|
lastActivity: staleActivity,
|
|
@@ -1198,7 +1198,7 @@ describe("daemon mulch failure recording", () => {
|
|
|
1198
1198
|
expect(failureMock.calls[0]?.tier).toBe(1);
|
|
1199
1199
|
expect(failureMock.calls[0]?.session.agentName).toBe("triaged-agent");
|
|
1200
1200
|
expect(failureMock.calls[0]?.session.capability).toBe("scout");
|
|
1201
|
-
expect(failureMock.calls[0]?.session.
|
|
1201
|
+
expect(failureMock.calls[0]?.session.taskId).toBe("task-456");
|
|
1202
1202
|
expect(failureMock.calls[0]?.triageSuggestion).toBe("terminate");
|
|
1203
1203
|
expect(failureMock.calls[0]?.reason).toContain("AI triage");
|
|
1204
1204
|
});
|
|
@@ -1267,11 +1267,11 @@ describe("daemon mulch failure recording", () => {
|
|
|
1267
1267
|
expect(failureMock.calls).toHaveLength(0);
|
|
1268
1268
|
});
|
|
1269
1269
|
|
|
1270
|
-
test("recordFailure includes evidenceBead when
|
|
1270
|
+
test("recordFailure includes evidenceBead when taskId is present", async () => {
|
|
1271
1271
|
const session = makeSession({
|
|
1272
1272
|
agentName: "beaded-agent",
|
|
1273
1273
|
capability: "builder",
|
|
1274
|
-
|
|
1274
|
+
taskId: "task-789",
|
|
1275
1275
|
tmuxSession: "overstory-beaded-agent",
|
|
1276
1276
|
state: "working",
|
|
1277
1277
|
lastActivity: new Date().toISOString(),
|
|
@@ -1292,7 +1292,7 @@ describe("daemon mulch failure recording", () => {
|
|
|
1292
1292
|
});
|
|
1293
1293
|
|
|
1294
1294
|
expect(failureMock.calls).toHaveLength(1);
|
|
1295
|
-
expect(failureMock.calls[0]?.session.
|
|
1295
|
+
expect(failureMock.calls[0]?.session.taskId).toBe("task-789");
|
|
1296
1296
|
});
|
|
1297
1297
|
|
|
1298
1298
|
test("Tier 0: recordFailure called at escalation level 3+ (progressive termination)", async () => {
|
|
@@ -1301,7 +1301,7 @@ describe("daemon mulch failure recording", () => {
|
|
|
1301
1301
|
const session = makeSession({
|
|
1302
1302
|
agentName: "doomed-agent",
|
|
1303
1303
|
capability: "builder",
|
|
1304
|
-
|
|
1304
|
+
taskId: "task-999",
|
|
1305
1305
|
tmuxSession: "overstory-doomed-agent",
|
|
1306
1306
|
state: "stalled",
|
|
1307
1307
|
lastActivity: staleActivity,
|
package/src/watchdog/daemon.ts
CHANGED
|
@@ -73,7 +73,7 @@ async function recordFailure(
|
|
|
73
73
|
type: "failure",
|
|
74
74
|
description,
|
|
75
75
|
tags: ["watchdog", "auto-recorded"],
|
|
76
|
-
evidenceBead: session.
|
|
76
|
+
evidenceBead: session.taskId || undefined,
|
|
77
77
|
});
|
|
78
78
|
} catch {
|
|
79
79
|
// Fire-and-forget: recording failures must not break the watchdog
|
|
@@ -36,7 +36,7 @@ function makeSession(overrides: Partial<AgentSession> = {}): AgentSession {
|
|
|
36
36
|
capability: "builder",
|
|
37
37
|
worktreePath: "/tmp/test",
|
|
38
38
|
branchName: "overstory/test-agent/test-task",
|
|
39
|
-
|
|
39
|
+
taskId: "test-task",
|
|
40
40
|
tmuxSession: "overstory-test-agent",
|
|
41
41
|
state: "booting",
|
|
42
42
|
pid: ALIVE_PID,
|