@katyella/legio 0.2.2 → 0.2.3
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/CHANGELOG.md +11 -1
- package/package.json +1 -1
- package/src/commands/gateway.test.ts +34 -2
- package/src/commands/gateway.ts +25 -21
- package/src/index.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.3] - 2026-03-02
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Coordinator and sling beacons now explain what legio is — previously Claude Code on fresh machines rejected thin beacons as "unrecognized" foreign content (same fix as gateway in v0.2.1)
|
|
14
|
+
- Coordinator beacon startup instructions use `legio status` instead of `bd ready` / `legio group status` (doesn't assume beads is installed)
|
|
15
|
+
- `legio doctor` no longer flags persistent agent identity files (coordinator, gateway, monitor) as stale — they legitimately exist outside the agent manifest
|
|
16
|
+
- Gateway greeting mail ("Gateway is online and ready") is only sent after confirmed beacon delivery — previously it was sent even when the beacon was rejected
|
|
17
|
+
- 2399 tests across 79 test files (up from 2397 across 79)
|
|
18
|
+
|
|
10
19
|
## [0.2.2] - 2026-03-02
|
|
11
20
|
|
|
12
21
|
### Fixed
|
|
@@ -201,7 +210,8 @@ Initial public release on npm as [`@katyella/legio`](https://www.npmjs.com/packa
|
|
|
201
210
|
- E2E lifecycle tests via Playwright
|
|
202
211
|
- Vitest test runner with forks pool for CI compatibility
|
|
203
212
|
|
|
204
|
-
[Unreleased]: https://github.com/katyella/legio/compare/v0.2.
|
|
213
|
+
[Unreleased]: https://github.com/katyella/legio/compare/v0.2.3...HEAD
|
|
214
|
+
[0.2.3]: https://github.com/katyella/legio/compare/v0.2.2...v0.2.3
|
|
205
215
|
[0.2.2]: https://github.com/katyella/legio/compare/v0.2.1...v0.2.2
|
|
206
216
|
[0.2.1]: https://github.com/katyella/legio/compare/v0.2.0...v0.2.1
|
|
207
217
|
[0.2.0]: https://github.com/katyella/legio/compare/v0.1.3...v0.2.0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@katyella/legio",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Multi-agent orchestration for Claude Code — spawn worker agents in git worktrees via tmux, coordinate through SQLite mail, merge with tiered conflict resolution",
|
|
5
5
|
"author": "Matthew Wojtowicz",
|
|
6
6
|
"license": "MIT",
|
|
@@ -518,8 +518,26 @@ describe("startGateway", () => {
|
|
|
518
518
|
expect(enterCalls).toHaveLength(2); // follow-up Enter + retry Enter
|
|
519
519
|
});
|
|
520
520
|
|
|
521
|
-
test("sends greeting mail to human after beacon delivery", async () => {
|
|
522
|
-
const {
|
|
521
|
+
test("sends greeting mail to human after confirmed beacon delivery", async () => {
|
|
522
|
+
const { tmux } = makeFakeTmux();
|
|
523
|
+
// Simulate hooks transitioning session out of "booting" after a few sleep calls
|
|
524
|
+
let sleepCount = 0;
|
|
525
|
+
const deps: GatewayDeps = {
|
|
526
|
+
_tmux: tmux,
|
|
527
|
+
_sleep: async () => {
|
|
528
|
+
sleepCount++;
|
|
529
|
+
// After 3 sleeps (waitForTuiReady + follow-up Enter delay + first poll),
|
|
530
|
+
// transition session to "working" like hooks would
|
|
531
|
+
if (sleepCount === 3) {
|
|
532
|
+
const { store } = openSessionStore(legioDir);
|
|
533
|
+
try {
|
|
534
|
+
store.updateState("gateway", "working");
|
|
535
|
+
} finally {
|
|
536
|
+
store.close();
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
};
|
|
523
541
|
await captureStdout(() => gatewayCommand(["start", "--no-attach"], deps));
|
|
524
542
|
// Verify mail.db has the greeting
|
|
525
543
|
const { createMailStore } = await import("../mail/store.ts");
|
|
@@ -535,6 +553,20 @@ describe("startGateway", () => {
|
|
|
535
553
|
mailDb.close();
|
|
536
554
|
}
|
|
537
555
|
});
|
|
556
|
+
|
|
557
|
+
test("does not send greeting mail when beacon delivery fails", async () => {
|
|
558
|
+
const { deps } = makeDeps();
|
|
559
|
+
await captureStdout(() => gatewayCommand(["start", "--no-attach"], deps));
|
|
560
|
+
// verifyBeaconDelivery returns false (session stays in "booting"), so no greeting mail
|
|
561
|
+
const { createMailStore } = await import("../mail/store.ts");
|
|
562
|
+
const mailDb = createMailStore(join(legioDir, "mail.db"));
|
|
563
|
+
try {
|
|
564
|
+
const msgs = mailDb.getAll({ from: "gateway", to: "human" });
|
|
565
|
+
expect(msgs).toHaveLength(0);
|
|
566
|
+
} finally {
|
|
567
|
+
mailDb.close();
|
|
568
|
+
}
|
|
569
|
+
});
|
|
538
570
|
});
|
|
539
571
|
|
|
540
572
|
describe("stopGateway", () => {
|
package/src/commands/gateway.ts
CHANGED
|
@@ -118,7 +118,7 @@ async function verifyBeaconDelivery(
|
|
|
118
118
|
tmuxSession: string,
|
|
119
119
|
beacon: string,
|
|
120
120
|
sleep: (ms: number) => Promise<void>,
|
|
121
|
-
): Promise<
|
|
121
|
+
): Promise<boolean> {
|
|
122
122
|
const MAX_CHECKS = 10;
|
|
123
123
|
const INTERVAL_MS = 2_000;
|
|
124
124
|
|
|
@@ -127,7 +127,7 @@ async function verifyBeaconDelivery(
|
|
|
127
127
|
const session = store.getByName(GATEWAY_NAME);
|
|
128
128
|
if (session && session.state !== "booting") {
|
|
129
129
|
// Beacon confirmed — gateway transitioned out of booting
|
|
130
|
-
return;
|
|
130
|
+
return true;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -142,7 +142,9 @@ async function verifyBeaconDelivery(
|
|
|
142
142
|
const finalSession = store.getByName(GATEWAY_NAME);
|
|
143
143
|
if (!finalSession || finalSession.state === "booting") {
|
|
144
144
|
process.stderr.write("[legio] Warning: gateway beacon delivery could not be confirmed\n");
|
|
145
|
+
return false;
|
|
145
146
|
}
|
|
147
|
+
return true;
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
/**
|
|
@@ -305,25 +307,27 @@ async function startGateway(args: string[], deps: GatewayDeps = {}): Promise<voi
|
|
|
305
307
|
|
|
306
308
|
// Verify delivery: poll store until state transitions out of booting.
|
|
307
309
|
// Must complete before the finally block closes the store.
|
|
308
|
-
await verifyBeaconDelivery(store, tmux, tmuxSession, beacon, sleep);
|
|
309
|
-
|
|
310
|
-
// Send greeting mail to human
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
310
|
+
const confirmed = await verifyBeaconDelivery(store, tmux, tmuxSession, beacon, sleep);
|
|
311
|
+
|
|
312
|
+
// Send greeting mail to human only after confirmed beacon delivery
|
|
313
|
+
if (confirmed) {
|
|
314
|
+
const { createMailStore } = await import("../mail/store.ts");
|
|
315
|
+
const mailDb = createMailStore(join(legioDir, "mail.db"));
|
|
316
|
+
try {
|
|
317
|
+
mailDb.insert({
|
|
318
|
+
id: "",
|
|
319
|
+
from: GATEWAY_NAME,
|
|
320
|
+
to: "human",
|
|
321
|
+
subject: "Gateway online",
|
|
322
|
+
body: "Gateway is online and ready. Send a message to start chatting.",
|
|
323
|
+
type: "status",
|
|
324
|
+
priority: "normal",
|
|
325
|
+
threadId: null,
|
|
326
|
+
audience: "human",
|
|
327
|
+
});
|
|
328
|
+
} finally {
|
|
329
|
+
mailDb.close();
|
|
330
|
+
}
|
|
327
331
|
}
|
|
328
332
|
|
|
329
333
|
if (shouldAttach) {
|
package/src/index.ts
CHANGED
|
@@ -45,7 +45,7 @@ import { worktreeCommand } from "./commands/worktree.ts";
|
|
|
45
45
|
import { LegioError, WorktreeError } from "./errors.ts";
|
|
46
46
|
import { setQuiet } from "./logging/color.ts";
|
|
47
47
|
|
|
48
|
-
const VERSION = "0.2.
|
|
48
|
+
const VERSION = "0.2.3";
|
|
49
49
|
|
|
50
50
|
const HELP = `legio v${VERSION} — Multi-agent orchestration for Claude Code
|
|
51
51
|
|