@agentmeshhq/agent 0.4.6 → 0.4.11

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 (73) hide show
  1. package/dist/__tests__/auth-doctor-integration.test.d.ts +14 -0
  2. package/dist/__tests__/auth-doctor-integration.test.js +130 -0
  3. package/dist/__tests__/auth-doctor-integration.test.js.map +1 -0
  4. package/dist/__tests__/auth-guard.integration.test.d.ts +12 -0
  5. package/dist/__tests__/auth-guard.integration.test.js +132 -0
  6. package/dist/__tests__/auth-guard.integration.test.js.map +1 -0
  7. package/dist/__tests__/auth-guard.test.d.ts +17 -0
  8. package/dist/__tests__/auth-guard.test.js +483 -0
  9. package/dist/__tests__/auth-guard.test.js.map +1 -0
  10. package/dist/__tests__/done-state-guard.integration.test.d.ts +1 -0
  11. package/dist/__tests__/done-state-guard.integration.test.js +281 -0
  12. package/dist/__tests__/done-state-guard.integration.test.js.map +1 -0
  13. package/dist/__tests__/done-state-guard.test.d.ts +1 -0
  14. package/dist/__tests__/done-state-guard.test.js +327 -0
  15. package/dist/__tests__/done-state-guard.test.js.map +1 -0
  16. package/dist/__tests__/registry.register.test.d.ts +8 -0
  17. package/dist/__tests__/registry.register.test.js +109 -0
  18. package/dist/__tests__/registry.register.test.js.map +1 -0
  19. package/dist/__tests__/start-team-id.test.d.ts +9 -0
  20. package/dist/__tests__/start-team-id.test.js +160 -0
  21. package/dist/__tests__/start-team-id.test.js.map +1 -0
  22. package/dist/__tests__/tmux-runtime.test.d.ts +1 -0
  23. package/dist/__tests__/tmux-runtime.test.js +113 -0
  24. package/dist/__tests__/tmux-runtime.test.js.map +1 -0
  25. package/dist/cli/auth.d.ts +11 -0
  26. package/dist/cli/auth.js +92 -0
  27. package/dist/cli/auth.js.map +1 -0
  28. package/dist/cli/index.js +132 -1
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/cli/local.d.ts +4 -2
  31. package/dist/cli/local.js +257 -108
  32. package/dist/cli/local.js.map +1 -1
  33. package/dist/cli/migrate.d.ts +1 -0
  34. package/dist/cli/migrate.js +14 -10
  35. package/dist/cli/migrate.js.map +1 -1
  36. package/dist/cli/start.d.ts +11 -0
  37. package/dist/cli/start.js +46 -24
  38. package/dist/cli/start.js.map +1 -1
  39. package/dist/cli/test.d.ts +1 -0
  40. package/dist/cli/test.js +21 -10
  41. package/dist/cli/test.js.map +1 -1
  42. package/dist/cli/watcher.d.ts +27 -0
  43. package/dist/cli/watcher.js +365 -0
  44. package/dist/cli/watcher.js.map +1 -0
  45. package/dist/config/schema.d.ts +13 -0
  46. package/dist/config/schema.js.map +1 -1
  47. package/dist/core/auth-guard.d.ts +155 -0
  48. package/dist/core/auth-guard.js +498 -0
  49. package/dist/core/auth-guard.js.map +1 -0
  50. package/dist/core/auth-sync.d.ts +105 -0
  51. package/dist/core/auth-sync.js +263 -0
  52. package/dist/core/auth-sync.js.map +1 -0
  53. package/dist/core/daemon/context-template.js +65 -0
  54. package/dist/core/daemon/context-template.js.map +1 -1
  55. package/dist/core/daemon/done-state-guard.d.ts +63 -0
  56. package/dist/core/daemon/done-state-guard.js +102 -0
  57. package/dist/core/daemon/done-state-guard.js.map +1 -0
  58. package/dist/core/daemon/tmux-session.d.ts +1 -0
  59. package/dist/core/daemon/tmux-session.js +1 -1
  60. package/dist/core/daemon/tmux-session.js.map +1 -1
  61. package/dist/core/daemon.d.ts +18 -1
  62. package/dist/core/daemon.js +158 -37
  63. package/dist/core/daemon.js.map +1 -1
  64. package/dist/core/registry.d.ts +11 -1
  65. package/dist/core/registry.js +32 -1
  66. package/dist/core/registry.js.map +1 -1
  67. package/dist/core/tmux-runtime.d.ts +11 -2
  68. package/dist/core/tmux-runtime.js +45 -19
  69. package/dist/core/tmux-runtime.js.map +1 -1
  70. package/dist/core/tmux.d.ts +1 -1
  71. package/dist/core/tmux.js +7 -3
  72. package/dist/core/tmux.js.map +1 -1
  73. package/package.json +1 -1
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Integration + regression tests for done-state guard (Epic #497)
3
+ *
4
+ * These tests verify the end-to-end restart continuity behaviour described in
5
+ * the epic acceptance criteria:
6
+ *
7
+ * AC1: Restarting with a prior completed PR + handoff → stays idle
8
+ * AC2: Restarting with an active in-progress claim → resumes correctly
9
+ * AC3: Agent without valid active assignment → idle-awaiting-assignment
10
+ * AC4: Stale completed handoff is NOT replayed after restart
11
+ * AC5: Blocked (automation paused) → no speculative execution
12
+ * AC6: Failure of guard evaluation → fail-safe to idle (no speculative resume)
13
+ *
14
+ * Integration layer: uses the guard + the registry filter helpers together,
15
+ * simulating what the daemon does at startup.
16
+ */
17
+ import { describe, expect, it } from "vitest";
18
+ import { evaluateRestartState, filterActiveClaimsForAgent, filterCompletedHandoffsForAgent, formatRestartLifecycleLog, isPriorWorkDone, } from "../core/daemon/done-state-guard.js";
19
+ // ---------------------------------------------------------------------------
20
+ // Helpers
21
+ // ---------------------------------------------------------------------------
22
+ const ME = "forge-dev-1-agent-id";
23
+ const PO = "po-agent-id";
24
+ function claim(scope, status = "active") {
25
+ return {
26
+ claim_id: `claim-${scope}`,
27
+ workspace_id: "agentmesh",
28
+ agent_id: ME,
29
+ scope,
30
+ status,
31
+ ttl_seconds: 1800,
32
+ expires_at: new Date(Date.now() + 1_800_000).toISOString(),
33
+ created_at: new Date().toISOString(),
34
+ released_at: null,
35
+ paths: [],
36
+ };
37
+ }
38
+ function handoff(id, status, toAgent = ME) {
39
+ return {
40
+ handoff_id: id,
41
+ workspace_id: "agentmesh",
42
+ from_agent_id: PO,
43
+ to_agent_id: toAgent,
44
+ scope: `Task: ${id}`,
45
+ reason: "Work item",
46
+ status,
47
+ created_at: new Date().toISOString(),
48
+ accepted_at: status !== "pending" ? new Date().toISOString() : null,
49
+ rejected_at: status === "rejected" ? new Date().toISOString() : null,
50
+ completed_at: status === "completed" ? new Date().toISOString() : null,
51
+ };
52
+ }
53
+ function inboxItem(id, status = "pending") {
54
+ return {
55
+ id,
56
+ type: "handoff",
57
+ from_agent_id: PO,
58
+ scope: `Task inbox: ${id}`,
59
+ reason: "Please do this",
60
+ status,
61
+ created_at: new Date().toISOString(),
62
+ };
63
+ }
64
+ /**
65
+ * Simulates the full startup evaluation pipeline in daemon.start():
66
+ * 1. listClaims → filter for this agent
67
+ * 2. fetchHandoffsForAgent → filter completed
68
+ * 3. checkInbox → pass raw
69
+ * 4. evaluateRestartState
70
+ */
71
+ function simulateStartup(opts) {
72
+ const activeClaims = filterActiveClaimsForAgent(opts.allClaims ?? [], ME);
73
+ const completedHandoffs = filterCompletedHandoffsForAgent(opts.allHandoffs ?? [], ME);
74
+ const input = {
75
+ activeClaims,
76
+ inboxItems: opts.inboxItems ?? [],
77
+ completedHandoffs,
78
+ automationPaused: opts.automationPaused ?? false,
79
+ };
80
+ const decision = evaluateRestartState(input);
81
+ const logLine = formatRestartLifecycleLog(decision);
82
+ return { ...input, decision, logLine };
83
+ }
84
+ // ---------------------------------------------------------------------------
85
+ // AC1: Prior completed PR + handoff → stays idle
86
+ // ---------------------------------------------------------------------------
87
+ describe("AC1: prior PR + completion handoff → idle on restart", () => {
88
+ it("agent that completed a task and sent handoff comes up idle", () => {
89
+ // Scenario: agent did work, opened PR, sent completion handoff.
90
+ // Claim was released when handoff was marked completed.
91
+ const result = simulateStartup({
92
+ allClaims: [
93
+ claim("handoff:hnd-42", "released"), // claim released on completion
94
+ ],
95
+ allHandoffs: [
96
+ handoff("hnd-42", "completed"), // handoff marked completed by PO
97
+ ],
98
+ inboxItems: [],
99
+ });
100
+ expect(result.decision.state).toBe("idle");
101
+ expect(result.logLine).toContain("state=idle");
102
+ expect(result.logLine).toContain("prior cycle is done");
103
+ });
104
+ it("daemon startup log includes structured lifecycle event", () => {
105
+ const result = simulateStartup({
106
+ allClaims: [],
107
+ allHandoffs: [handoff("hnd-88", "completed")],
108
+ });
109
+ // Verify the log line matches the expected pattern for observability
110
+ expect(result.logLine).toMatch(/^\[RESTART\] state=idle reason=/);
111
+ });
112
+ });
113
+ // ---------------------------------------------------------------------------
114
+ // AC2: Active in-progress claim → resumes correctly
115
+ // ---------------------------------------------------------------------------
116
+ describe("AC2: active claim → resumes in-progress work", () => {
117
+ it("agent with active claim resumes exactly its own assignment", () => {
118
+ const result = simulateStartup({
119
+ allClaims: [
120
+ claim("handoff:hnd-99"), // still active
121
+ ],
122
+ allHandoffs: [],
123
+ inboxItems: [],
124
+ });
125
+ expect(result.decision.state).toBe("resumed");
126
+ expect(result.logLine).toContain("state=resumed");
127
+ expect(result.decision.reason).toContain("handoff:hnd-99");
128
+ });
129
+ it("agent with active claim ignores completed handoffs from other cycles", () => {
130
+ // Old completed handoff present alongside new active claim — should still resume
131
+ const result = simulateStartup({
132
+ allClaims: [claim("handoff:hnd-current")],
133
+ allHandoffs: [
134
+ handoff("hnd-old-1", "completed"), // stale prior cycle
135
+ handoff("hnd-old-2", "completed"),
136
+ ],
137
+ inboxItems: [],
138
+ });
139
+ expect(result.decision.state).toBe("resumed");
140
+ });
141
+ it("only this agent's active claims count — other agents' claims are ignored", () => {
142
+ const otherAgentClaim = {
143
+ ...claim("handoff:hnd-other"),
144
+ agent_id: "different-agent-id",
145
+ };
146
+ const result = simulateStartup({
147
+ allClaims: [otherAgentClaim],
148
+ allHandoffs: [handoff("hnd-42", "completed")],
149
+ });
150
+ // Other agent's claim doesn't count for this agent
151
+ expect(result.decision.state).toBe("idle");
152
+ expect(result.activeClaims).toHaveLength(0);
153
+ });
154
+ });
155
+ // ---------------------------------------------------------------------------
156
+ // AC3: No valid active assignment → idle-awaiting-assignment
157
+ // ---------------------------------------------------------------------------
158
+ describe("AC3: no active assignment → idle-awaiting-assignment", () => {
159
+ it("brand-new agent with no claims, no handoffs, no inbox → idle", () => {
160
+ const result = simulateStartup({});
161
+ expect(result.decision.state).toBe("idle");
162
+ expect(isPriorWorkDone(result)).toBe(true);
163
+ });
164
+ it("agent with expired claim → idle (not resumed)", () => {
165
+ const result = simulateStartup({
166
+ allClaims: [claim("handoff:hnd-x", "expired")],
167
+ });
168
+ expect(result.activeClaims).toHaveLength(0);
169
+ expect(result.decision.state).toBe("idle");
170
+ });
171
+ });
172
+ // ---------------------------------------------------------------------------
173
+ // AC4: Stale completed handoff is NOT replayed after restart (regression)
174
+ // ---------------------------------------------------------------------------
175
+ describe("AC4: stale completed handoff is not replayed", () => {
176
+ it("completed handoff in inbox does not trigger resume", () => {
177
+ // Regression scenario: the old handoff is still visible in inbox with
178
+ // status=pending but the handoff itself is completed.
179
+ // Guard uses claims, not raw inbox, to determine active work.
180
+ const result = simulateStartup({
181
+ allClaims: [], // claim was released after task completion
182
+ allHandoffs: [handoff("hnd-done", "completed")],
183
+ inboxItems: [inboxItem("hnd-done")], // still in inbox as pending (UI artifact)
184
+ });
185
+ expect(result.decision.state).toBe("idle");
186
+ expect(result.logLine).not.toContain("state=resumed");
187
+ });
188
+ it("multiple stale completed handoffs all ignored — stays idle", () => {
189
+ const result = simulateStartup({
190
+ allClaims: [],
191
+ allHandoffs: [
192
+ handoff("hnd-1", "completed"),
193
+ handoff("hnd-2", "completed"),
194
+ handoff("hnd-3", "rejected"),
195
+ ],
196
+ inboxItems: [],
197
+ });
198
+ expect(result.decision.state).toBe("idle");
199
+ expect(result.completedHandoffs).toHaveLength(3);
200
+ });
201
+ it("completed handoffs from OTHER agents are not counted as this agent's done work", () => {
202
+ // Ensure filterCompletedHandoffsForAgent is properly scoped
203
+ const result = simulateStartup({
204
+ allClaims: [claim("handoff:hnd-mine")], // this agent has active claim
205
+ allHandoffs: [
206
+ handoff("hnd-theirs", "completed", "different-agent"), // some other agent's handoff
207
+ ],
208
+ });
209
+ // Other agent's completed handoffs don't affect THIS agent's resume decision
210
+ expect(result.completedHandoffs).toHaveLength(0); // filtered out
211
+ expect(result.decision.state).toBe("resumed"); // still resumes own work
212
+ });
213
+ });
214
+ // ---------------------------------------------------------------------------
215
+ // AC5: Blocked state (automation paused)
216
+ // ---------------------------------------------------------------------------
217
+ describe("AC5: automation paused → blocked, no speculative execution", () => {
218
+ it("paused automation → blocked even if active claim exists", () => {
219
+ const result = simulateStartup({
220
+ allClaims: [claim("handoff:hnd-paused")],
221
+ automationPaused: true,
222
+ });
223
+ expect(result.decision.state).toBe("blocked");
224
+ expect(result.logLine).toContain("state=blocked");
225
+ expect(result.logLine).toContain("automation is paused");
226
+ });
227
+ it("isPriorWorkDone returns false when blocked (blocked ≠ done)", () => {
228
+ const result = simulateStartup({ automationPaused: true });
229
+ expect(isPriorWorkDone(result)).toBe(false);
230
+ });
231
+ });
232
+ // ---------------------------------------------------------------------------
233
+ // AC6: Fail-safe to idle on guard evaluation error
234
+ // ---------------------------------------------------------------------------
235
+ describe("AC6: fail-safe to idle on evaluation error", () => {
236
+ it("empty/malformed input does not throw — defaults to idle", () => {
237
+ // Simulate what happens when HQ returns empty/null data
238
+ const input = {
239
+ activeClaims: [],
240
+ inboxItems: [],
241
+ completedHandoffs: [],
242
+ };
243
+ expect(() => evaluateRestartState(input)).not.toThrow();
244
+ const decision = evaluateRestartState(input);
245
+ expect(decision.state).toBe("idle");
246
+ });
247
+ it("guard is deterministic — same input always produces same output", () => {
248
+ const input = {
249
+ activeClaims: [claim("handoff:deterministic")],
250
+ inboxItems: [],
251
+ completedHandoffs: [],
252
+ };
253
+ const results = Array.from({ length: 10 }, () => evaluateRestartState(input));
254
+ for (const r of results) {
255
+ expect(r.state).toBe("resumed");
256
+ expect(r.reason).toBe(results[0].reason);
257
+ }
258
+ });
259
+ });
260
+ // ---------------------------------------------------------------------------
261
+ // Observability: structured log output validation
262
+ // ---------------------------------------------------------------------------
263
+ describe("observability: structured lifecycle log", () => {
264
+ it("log line always starts with [RESTART] prefix", () => {
265
+ const states = [
266
+ ["resumed", "active claim found"],
267
+ ["idle", "no active work"],
268
+ ["blocked", "paused"],
269
+ ];
270
+ for (const [state, reason] of states) {
271
+ const log = formatRestartLifecycleLog({ state, reason });
272
+ expect(log.startsWith("[RESTART]")).toBe(true);
273
+ }
274
+ });
275
+ it("log line is parseable key=value format", () => {
276
+ const log = formatRestartLifecycleLog({ state: "idle", reason: "test reason" });
277
+ expect(log).toContain("state=idle");
278
+ expect(log).toContain("reason=test reason");
279
+ });
280
+ });
281
+ //# sourceMappingURL=done-state-guard.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"done-state-guard.integration.test.js","sourceRoot":"","sources":["../../src/__tests__/done-state-guard.integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,327 @@
1
+ /**
2
+ * Unit tests for done-state guard (Epic #497)
3
+ *
4
+ * Tests cover:
5
+ * - All three restart states: resumed, idle, blocked
6
+ * - Completion-state guard: prior PR + completed handoff → idle
7
+ * - Claim-aware resume: valid active claim → resumed
8
+ * - Automation-paused → blocked
9
+ * - Stale replay prevention: no claims + completed handoffs → idle
10
+ * - Fail-safe defaults (empty inputs)
11
+ * - Helper functions: filterActiveClaimsForAgent, filterCompletedHandoffsForAgent
12
+ * - formatRestartLifecycleLog output
13
+ * - isPriorWorkDone convenience wrapper
14
+ */
15
+ import { describe, expect, it } from "vitest";
16
+ import { evaluateRestartState, filterActiveClaimsForAgent, filterCompletedHandoffsForAgent, formatRestartLifecycleLog, isPriorWorkDone, } from "../core/daemon/done-state-guard.js";
17
+ // ---------------------------------------------------------------------------
18
+ // Test fixtures
19
+ // ---------------------------------------------------------------------------
20
+ const AGENT_ID = "agent-abc-123";
21
+ const OTHER_AGENT_ID = "agent-xyz-999";
22
+ function makeClaim(overrides = {}) {
23
+ return {
24
+ claim_id: "claim-1",
25
+ workspace_id: "ws-1",
26
+ agent_id: AGENT_ID,
27
+ scope: "handoff:hnd-test",
28
+ status: "active",
29
+ ttl_seconds: 1800,
30
+ expires_at: new Date(Date.now() + 1_800_000).toISOString(),
31
+ created_at: new Date().toISOString(),
32
+ released_at: null,
33
+ paths: ["agent/forge-dev-1/**"],
34
+ ...overrides,
35
+ };
36
+ }
37
+ function makeHandoff(overrides = {}) {
38
+ return {
39
+ handoff_id: "hnd-test",
40
+ workspace_id: "ws-1",
41
+ from_agent_id: "agent-po-1",
42
+ to_agent_id: AGENT_ID,
43
+ scope: "PR #42 ready for review",
44
+ reason: "Work complete",
45
+ status: "completed",
46
+ created_at: new Date().toISOString(),
47
+ accepted_at: new Date().toISOString(),
48
+ rejected_at: null,
49
+ completed_at: new Date().toISOString(),
50
+ ...overrides,
51
+ };
52
+ }
53
+ function makeInboxItem(overrides = {}) {
54
+ return {
55
+ id: "hnd-inbox-1",
56
+ type: "handoff",
57
+ from_agent_id: "agent-po-1",
58
+ scope: "New task",
59
+ reason: "Do this",
60
+ status: "pending",
61
+ created_at: new Date().toISOString(),
62
+ ...overrides,
63
+ };
64
+ }
65
+ function makeInput(overrides = {}) {
66
+ return {
67
+ activeClaims: [],
68
+ inboxItems: [],
69
+ completedHandoffs: [],
70
+ automationPaused: false,
71
+ ...overrides,
72
+ };
73
+ }
74
+ // ---------------------------------------------------------------------------
75
+ // 1. Core evaluateRestartState — resume branch
76
+ // ---------------------------------------------------------------------------
77
+ describe("evaluateRestartState: resume", () => {
78
+ it("returns resumed when active claim exists for this agent", () => {
79
+ const decision = evaluateRestartState(makeInput({ activeClaims: [makeClaim()] }));
80
+ expect(decision.state).toBe("resumed");
81
+ expect(decision.reason).toContain("active claim(s) found");
82
+ expect(decision.reason).toContain("handoff:hnd-test");
83
+ });
84
+ it("includes all claim scopes in reason when multiple active claims exist", () => {
85
+ const claims = [
86
+ makeClaim({ claim_id: "c1", scope: "handoff:hnd-1" }),
87
+ makeClaim({ claim_id: "c2", scope: "handoff:hnd-2" }),
88
+ ];
89
+ const decision = evaluateRestartState(makeInput({ activeClaims: claims }));
90
+ expect(decision.state).toBe("resumed");
91
+ expect(decision.reason).toContain("handoff:hnd-1");
92
+ expect(decision.reason).toContain("handoff:hnd-2");
93
+ });
94
+ });
95
+ // ---------------------------------------------------------------------------
96
+ // 2. Core evaluateRestartState — idle branch
97
+ // ---------------------------------------------------------------------------
98
+ describe("evaluateRestartState: idle — no prior work", () => {
99
+ it("returns idle when there are no claims, no inbox, no completed handoffs", () => {
100
+ const decision = evaluateRestartState(makeInput());
101
+ expect(decision.state).toBe("idle");
102
+ expect(decision.reason).toContain("no active claims");
103
+ });
104
+ it("returns idle with completed-handoff detail when prior PR+handoff is done", () => {
105
+ // This is the key acceptance criterion: PR opened + completion handoff sent → idle
106
+ const decision = evaluateRestartState(makeInput({ completedHandoffs: [makeHandoff()] }));
107
+ expect(decision.state).toBe("idle");
108
+ expect(decision.reason).toContain("prior cycle is done");
109
+ expect(decision.reason).toContain("1 completed handoff");
110
+ });
111
+ it("returns idle with multiple completed handoffs", () => {
112
+ const decision = evaluateRestartState(makeInput({
113
+ completedHandoffs: [makeHandoff(), makeHandoff({ handoff_id: "hnd-2" })],
114
+ }));
115
+ expect(decision.state).toBe("idle");
116
+ expect(decision.reason).toContain("2 completed handoff");
117
+ });
118
+ it("returns idle when inbox has items but no active claim (will accept fresh)", () => {
119
+ const decision = evaluateRestartState(makeInput({ inboxItems: [makeInboxItem()] }));
120
+ expect(decision.state).toBe("idle");
121
+ expect(decision.reason).toContain("pending inbox items");
122
+ expect(decision.reason).toContain("accept fresh on startup");
123
+ });
124
+ });
125
+ // ---------------------------------------------------------------------------
126
+ // 3. Core evaluateRestartState — blocked branch
127
+ // ---------------------------------------------------------------------------
128
+ describe("evaluateRestartState: blocked", () => {
129
+ it("returns blocked when automation is paused regardless of claims", () => {
130
+ const decision = evaluateRestartState(makeInput({
131
+ automationPaused: true,
132
+ activeClaims: [makeClaim()],
133
+ }));
134
+ expect(decision.state).toBe("blocked");
135
+ expect(decision.reason).toContain("automation is paused");
136
+ });
137
+ it("returns blocked when automation is paused even with no claims", () => {
138
+ const decision = evaluateRestartState(makeInput({ automationPaused: true }));
139
+ expect(decision.state).toBe("blocked");
140
+ });
141
+ });
142
+ // ---------------------------------------------------------------------------
143
+ // 4. Completion-state guard (explicit acceptance criteria from epic)
144
+ // ---------------------------------------------------------------------------
145
+ describe("completion-state guard (Epic #497 explicit)", () => {
146
+ it("prior PR + completion handoff → restart stays idle, never resumes stale task", () => {
147
+ // Simulate: agent previously submitted PR and sent completion handoff.
148
+ // No active claims remain (claim was released on completion).
149
+ const input = {
150
+ activeClaims: [], // claim released after PR
151
+ inboxItems: [], // no new pending inbox
152
+ completedHandoffs: [
153
+ makeHandoff({ status: "completed", scope: "PR #42: feature ready for merge" }),
154
+ ],
155
+ automationPaused: false,
156
+ };
157
+ const decision = evaluateRestartState(input);
158
+ expect(decision.state).toBe("idle");
159
+ expect(isPriorWorkDone(input)).toBe(true);
160
+ });
161
+ it("agent with open handoff and active claim → resumes correctly", () => {
162
+ // Simulate: agent has in-progress work with an active claim.
163
+ const input = {
164
+ activeClaims: [makeClaim({ scope: "handoff:hnd-in-progress" })],
165
+ inboxItems: [],
166
+ completedHandoffs: [],
167
+ automationPaused: false,
168
+ };
169
+ const decision = evaluateRestartState(input);
170
+ expect(decision.state).toBe("resumed");
171
+ expect(isPriorWorkDone(input)).toBe(false);
172
+ });
173
+ it("rejected handoff also counts as done — agent stays idle", () => {
174
+ const input = {
175
+ activeClaims: [],
176
+ inboxItems: [],
177
+ completedHandoffs: [makeHandoff({ status: "rejected" })],
178
+ automationPaused: false,
179
+ };
180
+ const decision = evaluateRestartState(input);
181
+ expect(decision.state).toBe("idle");
182
+ expect(isPriorWorkDone(input)).toBe(true);
183
+ });
184
+ it("stale completed handoff is not replayed — idle even with inbox items from old cycle", () => {
185
+ // Regression: inbox item present (already-completed handoff still in inbox as done)
186
+ // but no active claim → should NOT auto-resume
187
+ const input = {
188
+ activeClaims: [],
189
+ inboxItems: [makeInboxItem({ status: "pending" })],
190
+ completedHandoffs: [makeHandoff({ status: "completed" })],
191
+ automationPaused: false,
192
+ };
193
+ // There's a pending inbox item, but the prior handoff is done and there's no active claim.
194
+ // The guard should come up idle — inbox item will be accepted fresh, not as stale resume.
195
+ const decision = evaluateRestartState(input);
196
+ // No active claim → idle, regardless of pending inbox
197
+ expect(decision.state).toBe("idle");
198
+ });
199
+ });
200
+ // ---------------------------------------------------------------------------
201
+ // 5. filterActiveClaimsForAgent
202
+ // ---------------------------------------------------------------------------
203
+ describe("filterActiveClaimsForAgent", () => {
204
+ it("returns only active claims for the specified agent", () => {
205
+ const claims = [
206
+ makeClaim({ claim_id: "c1", agent_id: AGENT_ID, status: "active" }),
207
+ makeClaim({ claim_id: "c2", agent_id: OTHER_AGENT_ID, status: "active" }),
208
+ makeClaim({ claim_id: "c3", agent_id: AGENT_ID, status: "released" }),
209
+ ];
210
+ const result = filterActiveClaimsForAgent(claims, AGENT_ID);
211
+ expect(result).toHaveLength(1);
212
+ expect(result[0].claim_id).toBe("c1");
213
+ });
214
+ it("returns empty array when no claims match", () => {
215
+ expect(filterActiveClaimsForAgent([], AGENT_ID)).toHaveLength(0);
216
+ });
217
+ it("returns empty array when all claims belong to other agents", () => {
218
+ const claims = [makeClaim({ agent_id: OTHER_AGENT_ID })];
219
+ expect(filterActiveClaimsForAgent(claims, AGENT_ID)).toHaveLength(0);
220
+ });
221
+ it("returns empty array when all claims are released/expired", () => {
222
+ const claims = [
223
+ makeClaim({ agent_id: AGENT_ID, status: "released" }),
224
+ makeClaim({ agent_id: AGENT_ID, status: "expired" }),
225
+ ];
226
+ expect(filterActiveClaimsForAgent(claims, AGENT_ID)).toHaveLength(0);
227
+ });
228
+ });
229
+ // ---------------------------------------------------------------------------
230
+ // 6. filterCompletedHandoffsForAgent
231
+ // ---------------------------------------------------------------------------
232
+ describe("filterCompletedHandoffsForAgent", () => {
233
+ it("returns completed handoffs sent to the specified agent", () => {
234
+ const handoffs = [
235
+ makeHandoff({ handoff_id: "h1", to_agent_id: AGENT_ID, status: "completed" }),
236
+ makeHandoff({ handoff_id: "h2", to_agent_id: OTHER_AGENT_ID, status: "completed" }),
237
+ makeHandoff({ handoff_id: "h3", to_agent_id: AGENT_ID, status: "pending" }),
238
+ ];
239
+ const result = filterCompletedHandoffsForAgent(handoffs, AGENT_ID);
240
+ expect(result).toHaveLength(1);
241
+ expect(result[0].handoff_id).toBe("h1");
242
+ });
243
+ it("returns rejected handoffs too (they are also done)", () => {
244
+ const handoffs = [makeHandoff({ handoff_id: "h1", to_agent_id: AGENT_ID, status: "rejected" })];
245
+ const result = filterCompletedHandoffsForAgent(handoffs, AGENT_ID);
246
+ expect(result).toHaveLength(1);
247
+ expect(result[0].handoff_id).toBe("h1");
248
+ });
249
+ it("returns empty array on empty input", () => {
250
+ expect(filterCompletedHandoffsForAgent([], AGENT_ID)).toHaveLength(0);
251
+ });
252
+ it("does not include handoffs addressed to other agents", () => {
253
+ const handoffs = [makeHandoff({ to_agent_id: OTHER_AGENT_ID, status: "completed" })];
254
+ expect(filterCompletedHandoffsForAgent(handoffs, AGENT_ID)).toHaveLength(0);
255
+ });
256
+ });
257
+ // ---------------------------------------------------------------------------
258
+ // 7. formatRestartLifecycleLog
259
+ // ---------------------------------------------------------------------------
260
+ describe("formatRestartLifecycleLog", () => {
261
+ it("produces correct format for resumed state", () => {
262
+ const decision = { state: "resumed", reason: "active claim found" };
263
+ expect(formatRestartLifecycleLog(decision)).toBe("[RESTART] state=resumed reason=active claim found");
264
+ });
265
+ it("produces correct format for idle state", () => {
266
+ const decision = { state: "idle", reason: "prior cycle is done" };
267
+ expect(formatRestartLifecycleLog(decision)).toBe("[RESTART] state=idle reason=prior cycle is done");
268
+ });
269
+ it("produces correct format for blocked state", () => {
270
+ const decision = { state: "blocked", reason: "automation is paused" };
271
+ expect(formatRestartLifecycleLog(decision)).toBe("[RESTART] state=blocked reason=automation is paused");
272
+ });
273
+ });
274
+ // ---------------------------------------------------------------------------
275
+ // 8. isPriorWorkDone convenience wrapper
276
+ // ---------------------------------------------------------------------------
277
+ describe("isPriorWorkDone", () => {
278
+ it("returns true for idle state (no active claims, no pending work)", () => {
279
+ expect(isPriorWorkDone(makeInput())).toBe(true);
280
+ });
281
+ it("returns true when prior handoff is completed", () => {
282
+ expect(isPriorWorkDone(makeInput({ completedHandoffs: [makeHandoff()] }))).toBe(true);
283
+ });
284
+ it("returns false when agent has active claim (work in-flight)", () => {
285
+ expect(isPriorWorkDone(makeInput({ activeClaims: [makeClaim()] }))).toBe(false);
286
+ });
287
+ it("returns false when automation is paused (blocked, not idle)", () => {
288
+ expect(isPriorWorkDone(makeInput({ automationPaused: true }))).toBe(false);
289
+ });
290
+ });
291
+ // ---------------------------------------------------------------------------
292
+ // 9. Edge cases & fail-safe defaults
293
+ // ---------------------------------------------------------------------------
294
+ describe("fail-safe edge cases", () => {
295
+ it("handles all empty inputs gracefully (no throws)", () => {
296
+ expect(() => evaluateRestartState(makeInput())).not.toThrow();
297
+ });
298
+ it("automationPaused=undefined treated as not paused", () => {
299
+ const input = {
300
+ activeClaims: [],
301
+ inboxItems: [],
302
+ completedHandoffs: [],
303
+ // automationPaused intentionally omitted
304
+ };
305
+ const decision = evaluateRestartState(input);
306
+ expect(decision.state).toBe("idle");
307
+ });
308
+ it("unknown claim status is treated as not active", () => {
309
+ const claims = [makeClaim({ status: "unknown_future_status" })];
310
+ const filtered = filterActiveClaimsForAgent(claims, AGENT_ID);
311
+ expect(filtered).toHaveLength(0);
312
+ });
313
+ it("handoff with no to_agent_id is excluded from completed filter", () => {
314
+ const handoffs = [
315
+ {
316
+ handoff_id: "h1",
317
+ workspace_id: "ws-1",
318
+ status: "completed",
319
+ created_at: new Date().toISOString(),
320
+ // to_agent_id intentionally missing
321
+ },
322
+ ];
323
+ const result = filterCompletedHandoffsForAgent(handoffs, AGENT_ID);
324
+ expect(result).toHaveLength(0);
325
+ });
326
+ });
327
+ //# sourceMappingURL=done-state-guard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"done-state-guard.test.js","sourceRoot":"","sources":["../../src/__tests__/done-state-guard.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Unit tests for registerAgent() — verifies team_id is correctly
3
+ * included/excluded in the registration payload sent to the hub.
4
+ *
5
+ * This guards against the hub/CLI contract drift where team_id was
6
+ * required by the hub for worker agents but never sent by the CLI.
7
+ */
8
+ export {};