@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.
- package/dist/__tests__/auth-doctor-integration.test.d.ts +14 -0
- package/dist/__tests__/auth-doctor-integration.test.js +130 -0
- package/dist/__tests__/auth-doctor-integration.test.js.map +1 -0
- package/dist/__tests__/auth-guard.integration.test.d.ts +12 -0
- package/dist/__tests__/auth-guard.integration.test.js +132 -0
- package/dist/__tests__/auth-guard.integration.test.js.map +1 -0
- package/dist/__tests__/auth-guard.test.d.ts +17 -0
- package/dist/__tests__/auth-guard.test.js +483 -0
- package/dist/__tests__/auth-guard.test.js.map +1 -0
- package/dist/__tests__/done-state-guard.integration.test.d.ts +1 -0
- package/dist/__tests__/done-state-guard.integration.test.js +281 -0
- package/dist/__tests__/done-state-guard.integration.test.js.map +1 -0
- package/dist/__tests__/done-state-guard.test.d.ts +1 -0
- package/dist/__tests__/done-state-guard.test.js +327 -0
- package/dist/__tests__/done-state-guard.test.js.map +1 -0
- package/dist/__tests__/registry.register.test.d.ts +8 -0
- package/dist/__tests__/registry.register.test.js +109 -0
- package/dist/__tests__/registry.register.test.js.map +1 -0
- package/dist/__tests__/start-team-id.test.d.ts +9 -0
- package/dist/__tests__/start-team-id.test.js +160 -0
- package/dist/__tests__/start-team-id.test.js.map +1 -0
- package/dist/__tests__/tmux-runtime.test.d.ts +1 -0
- package/dist/__tests__/tmux-runtime.test.js +113 -0
- package/dist/__tests__/tmux-runtime.test.js.map +1 -0
- package/dist/cli/auth.d.ts +11 -0
- package/dist/cli/auth.js +92 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/index.js +132 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/local.d.ts +4 -2
- package/dist/cli/local.js +257 -108
- package/dist/cli/local.js.map +1 -1
- package/dist/cli/migrate.d.ts +1 -0
- package/dist/cli/migrate.js +14 -10
- package/dist/cli/migrate.js.map +1 -1
- package/dist/cli/start.d.ts +11 -0
- package/dist/cli/start.js +46 -24
- package/dist/cli/start.js.map +1 -1
- package/dist/cli/test.d.ts +1 -0
- package/dist/cli/test.js +21 -10
- package/dist/cli/test.js.map +1 -1
- package/dist/cli/watcher.d.ts +27 -0
- package/dist/cli/watcher.js +365 -0
- package/dist/cli/watcher.js.map +1 -0
- package/dist/config/schema.d.ts +13 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/core/auth-guard.d.ts +155 -0
- package/dist/core/auth-guard.js +498 -0
- package/dist/core/auth-guard.js.map +1 -0
- package/dist/core/auth-sync.d.ts +105 -0
- package/dist/core/auth-sync.js +263 -0
- package/dist/core/auth-sync.js.map +1 -0
- package/dist/core/daemon/context-template.js +65 -0
- package/dist/core/daemon/context-template.js.map +1 -1
- package/dist/core/daemon/done-state-guard.d.ts +63 -0
- package/dist/core/daemon/done-state-guard.js +102 -0
- package/dist/core/daemon/done-state-guard.js.map +1 -0
- package/dist/core/daemon/tmux-session.d.ts +1 -0
- package/dist/core/daemon/tmux-session.js +1 -1
- package/dist/core/daemon/tmux-session.js.map +1 -1
- package/dist/core/daemon.d.ts +18 -1
- package/dist/core/daemon.js +158 -37
- package/dist/core/daemon.js.map +1 -1
- package/dist/core/registry.d.ts +11 -1
- package/dist/core/registry.js +32 -1
- package/dist/core/registry.js.map +1 -1
- package/dist/core/tmux-runtime.d.ts +11 -2
- package/dist/core/tmux-runtime.js +45 -19
- package/dist/core/tmux-runtime.js.map +1 -1
- package/dist/core/tmux.d.ts +1 -1
- package/dist/core/tmux.js +7 -3
- package/dist/core/tmux.js.map +1 -1
- 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":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAEL,oBAAoB,EACpB,0BAA0B,EAC1B,+BAA+B,EAC/B,yBAAyB,EACzB,eAAe,GAEhB,MAAM,oCAAoC,CAAC;AAG5C,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E,MAAM,QAAQ,GAAG,eAAe,CAAC;AACjC,MAAM,cAAc,GAAG,eAAe,CAAC;AAEvC,SAAS,SAAS,CAAC,YAAkC,EAAE;IACrD,OAAO;QACL,QAAQ,EAAE,SAAS;QACnB,YAAY,EAAE,MAAM;QACpB,QAAQ,EAAE,QAAQ;QAClB,KAAK,EAAE,kBAAkB;QACzB,MAAM,EAAE,QAAQ;QAChB,WAAW,EAAE,IAAI;QACjB,UAAU,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC,WAAW,EAAE;QAC1D,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,WAAW,EAAE,IAAI;QACjB,KAAK,EAAE,CAAC,sBAAsB,CAAC;QAC/B,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,YAAoC,EAAE;IACzD,OAAO;QACL,UAAU,EAAE,UAAU;QACtB,YAAY,EAAE,MAAM;QACpB,aAAa,EAAE,YAAY;QAC3B,WAAW,EAAE,QAAQ;QACrB,KAAK,EAAE,yBAAyB;QAChC,MAAM,EAAE,eAAe;QACvB,MAAM,EAAE,WAAW;QACnB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,WAAW,EAAE,IAAI;QACjB,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtC,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,YAAgC,EAAE;IACvD,OAAO;QACL,EAAE,EAAE,aAAa;QACjB,IAAI,EAAE,SAAS;QACf,aAAa,EAAE,YAAY;QAC3B,KAAK,EAAE,UAAU;QACjB,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,YAAqC,EAAE;IACxD,OAAO;QACL,YAAY,EAAE,EAAE;QAChB,UAAU,EAAE,EAAE;QACd,iBAAiB,EAAE,EAAE;QACrB,gBAAgB,EAAE,KAAK;QACvB,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,+CAA+C;AAC/C,8EAA8E;AAE9E,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,QAAQ,GAAG,oBAAoB,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAClF,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;QAC3D,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,MAAM,GAAG;YACb,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;YACrD,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;SACtD,CAAC;QACF,MAAM,QAAQ,GAAG,oBAAoB,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QAC3E,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,6CAA6C;AAC7C,8EAA8E;AAE9E,QAAQ,CAAC,4CAA4C,EAAE,GAAG,EAAE;IAC1D,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,QAAQ,GAAG,oBAAoB,CAAC,SAAS,EAAE,CAAC,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,mFAAmF;QACnF,MAAM,QAAQ,GAAG,oBAAoB,CAAC,SAAS,CAAC,EAAE,iBAAiB,EAAE,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACzF,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;QACzD,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,QAAQ,GAAG,oBAAoB,CACnC,SAAS,CAAC;YACR,iBAAiB,EAAE,CAAC,WAAW,EAAE,EAAE,WAAW,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;SACzE,CAAC,CACH,CAAC;QACF,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,MAAM,QAAQ,GAAG,oBAAoB,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,CAAC,aAAa,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACpF,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;QACzD,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,gDAAgD;AAChD,8EAA8E;AAE9E,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC7C,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,QAAQ,GAAG,oBAAoB,CACnC,SAAS,CAAC;YACR,gBAAgB,EAAE,IAAI;YACtB,YAAY,EAAE,CAAC,SAAS,EAAE,CAAC;SAC5B,CAAC,CACH,CAAC;QACF,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,QAAQ,GAAG,oBAAoB,CAAC,SAAS,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC7E,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,qEAAqE;AACrE,8EAA8E;AAE9E,QAAQ,CAAC,6CAA6C,EAAE,GAAG,EAAE;IAC3D,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACtF,uEAAuE;QACvE,8DAA8D;QAC9D,MAAM,KAAK,GAAmB;YAC5B,YAAY,EAAE,EAAE,EAAE,0BAA0B;YAC5C,UAAU,EAAE,EAAE,EAAE,uBAAuB;YACvC,iBAAiB,EAAE;gBACjB,WAAW,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC;aAC/E;YACD,gBAAgB,EAAE,KAAK;SACxB,CAAC;QAEF,MAAM,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,6DAA6D;QAC7D,MAAM,KAAK,GAAmB;YAC5B,YAAY,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;YAC/D,UAAU,EAAE,EAAE;YACd,iBAAiB,EAAE,EAAE;YACrB,gBAAgB,EAAE,KAAK;SACxB,CAAC;QAEF,MAAM,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,KAAK,GAAmB;YAC5B,YAAY,EAAE,EAAE;YAChB,UAAU,EAAE,EAAE;YACd,iBAAiB,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;YACxD,gBAAgB,EAAE,KAAK;SACxB,CAAC;QAEF,MAAM,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qFAAqF,EAAE,GAAG,EAAE;QAC7F,oFAAoF;QACpF,+CAA+C;QAC/C,MAAM,KAAK,GAAmB;YAC5B,YAAY,EAAE,EAAE;YAChB,UAAU,EAAE,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YAClD,iBAAiB,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;YACzD,gBAAgB,EAAE,KAAK;SACxB,CAAC;QAEF,2FAA2F;QAC3F,0FAA0F;QAC1F,MAAM,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC7C,sDAAsD;QACtD,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,gCAAgC;AAChC,8EAA8E;AAE9E,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,MAAM,GAAG;YACb,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YACnE,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YACzE,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;SACtE,CAAC;QAEF,MAAM,MAAM,GAAG,0BAA0B,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,0BAA0B,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,MAAM,GAAG,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;QACzD,MAAM,CAAC,0BAA0B,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,MAAM,GAAG;YACb,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YACrD,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;SACrD,CAAC;QACF,MAAM,CAAC,0BAA0B,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,qCAAqC;AACrC,8EAA8E;AAE9E,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,QAAQ,GAAG;YACf,WAAW,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;YAC7E,WAAW,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;YACnF,WAAW,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;SAC5E,CAAC;QAEF,MAAM,MAAM,GAAG,+BAA+B,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACnE,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,QAAQ,GAAG,CAAC,WAAW,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;QAEhG,MAAM,MAAM,GAAG,+BAA+B,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACnE,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,+BAA+B,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,QAAQ,GAAG,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QACrF,MAAM,CAAC,+BAA+B,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,+BAA+B;AAC/B,8EAA8E;AAE9E,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,QAAQ,GAAoB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QACrF,MAAM,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAC9C,mDAAmD,CACpD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,QAAQ,GAAoB,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;QACnF,MAAM,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAC9C,iDAAiD,CAClD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,QAAQ,GAAoB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;QACvF,MAAM,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAC9C,qDAAqD,CACtD,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,yCAAyC;AACzC,8EAA8E;AAE9E,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,CAAC,eAAe,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,iBAAiB,EAAE,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,qCAAqC;AACrC,8EAA8E;AAE9E,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,KAAK,GAAmB;YAC5B,YAAY,EAAE,EAAE;YAChB,UAAU,EAAE,EAAE;YACd,iBAAiB,EAAE,EAAE;YACrB,yCAAyC;SAC1C,CAAC;QACF,MAAM,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,MAAM,GAAG,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAG,0BAA0B,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC9D,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,QAAQ,GAAoB;YAChC;gBACE,UAAU,EAAE,IAAI;gBAChB,YAAY,EAAE,MAAM;gBACpB,MAAM,EAAE,WAAW;gBACnB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACpC,oCAAoC;aACrC;SACF,CAAC;QACF,MAAM,MAAM,GAAG,+BAA+B,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACnE,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -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 {};
|