Summary
On 0.17.2, agent-device replay <path> always fails with Error (INVALID_ARGS): Unknown command: replay, even though the command is documented, shows up in --help, and has a registered handler. Only the replay export ... subcommand works.
This is a regression introduced in 0.17.2 — replay <path> works correctly on 0.17.1 (last known good). The sibling test command works on both versions.
Environment
- agent-device: 0.17.2 (broken) / 0.17.1 (works). 0.17.2 was
latest on npm at time of report.
- Node: 22.9.0
- OS: macOS (darwin 25.5.0)
- Xcode / Android SDK: not relevant — this is a CLI command-routing bug, reproducible without a device/session.
Steps to reproduce
npm install -g agent-device@0.17.2
# kill any running daemon so the new version is used:
pkill -f 'agent-device/dist/src/internal/daemon.js'
agent-device replay # no args
agent-device replay ./flow.ad # any path, existing or not
Expected
The .ad script is replayed (or, with no/invalid path, a replay-specific validation error such as replay requires path / ENOENT).
Actual (0.17.2)
$ agent-device replay
Error (INVALID_ARGS): Unknown command: replay
Hint: Check command arguments and run --help for usage examples.
Diagnostic ID: ...
$ agent-device replay /tmp/does-not-exist.ad
Error (INVALID_ARGS): Unknown command: replay
Meanwhile, on the same 0.17.2 install:
$ agent-device replay --help # prints help fine (separate help registry)
$ agent-device replay export f.ad ... # works
$ agent-device test ./flow.ad # works (reaches the replay engine)
Version comparison
| Version |
agent-device replay <path> |
Behavior |
| 0.17.1 |
works |
replay → replay requires path; replay /tmp/missing.ad → ENOENT: ... open '/tmp/missing.ad' (reaches replay engine, spins up agent-device-replay-daemon) |
| 0.17.2 |
broken |
replay / replay <path> → Unknown command: replay |
Verified on 0.17.1 after downgrading and restarting the daemon:
$ agent-device --version
0.17.1
$ agent-device replay
Error (INVALID_ARGS): replay requires path
$ agent-device replay /tmp/does-not-exist.ad
Error (UNKNOWN): ENOENT: no such file or directory, open '/tmp/does-not-exist.ad'
Note: a running daemon caches its code at spawn time, so a version change is not picked up until the existing internal/daemon.js process is killed and a fresh daemon spawns. The downgrade alone isn't enough — restart the daemon.
Root cause (from inspecting the published dist)
The CLI dispatcher treats the truthiness of a command handler's return value as "was this command handled?". If the local handler exists but returns a falsy value, the dispatcher falls through and throws Unknown command.
In 0.17.2, replay was given a local CLI handler whose normal replay <path> branch validates the args and then returns false (only replay export returns a real result):
// 0.17.2 cli.js (minified)
th = async e => {
let { positionals: t } = e;
return "export" !== t[0]
? (function ({ positionals: e, flags: t }) {
if (e.length > 1) throw new f("INVALID_ARGS", "replay accepts exactly one input path: replay <path>");
if (t.replayExportFormat !== undefined || t.out !== undefined) throw /* ... */;
return !1; // <-- returns false for the normal `replay <path>` path
})(e)
: await tw(e); // `replay export ...` works
};
Dispatcher:
function t_(e){ let n = tA[e.command]; return n ? await n({...e}) : !!z(e.command) && await tS({...e}); }
// ... else if (await t_({command:er,...})) return;
// throw new f("INVALID_ARGS", `Unknown command: ${er}`);
tA.replay exists, so await t_(...) resolves to false, the else if is falsy, and Unknown command: replay is thrown.
In 0.17.1, replay and test were defined side-by-side in the same generic args-builder map (both routed through the daemon), which is why both worked:
// 0.17.1 (minified)
let ev = {
replay: (e,t) => ({ ..._(t), path: I(e[0], "replay requires path"), update: t.replayUpdate, backend: t.replayMaestro ? "maestro" : void 0, env: t.replayEnv }),
test: (e,t) => ({ ..._(t), paths: e, update: t.replayUpdate, ... }),
...
};
So the regression is replay being moved from the shared generic/daemon path (where test still lives) into a local handler that returns false on the happy path.
Suggested fix
Restore 0.17.1 behavior: route replay through the same generic/daemon args-builder path as test. If a local handler is kept, its replay <path> branch must actually execute the replay and return a truthy result on success so the dispatcher's truthiness check passes.
Workaround
- Pin to
agent-device@0.17.1 (and restart the daemon), or
- Use
agent-device test <path.ad> -e KEY=VALUE ... — same replay engine, same -e env-substitution flags.
Summary
On 0.17.2,
agent-device replay <path>always fails withError (INVALID_ARGS): Unknown command: replay, even though the command is documented, shows up in--help, and has a registered handler. Only thereplay export ...subcommand works.This is a regression introduced in 0.17.2 —
replay <path>works correctly on 0.17.1 (last known good). The siblingtestcommand works on both versions.Environment
lateston npm at time of report.Steps to reproduce
Expected
The
.adscript is replayed (or, with no/invalid path, areplay-specific validation error such asreplay requires path/ENOENT).Actual (0.17.2)
Meanwhile, on the same 0.17.2 install:
Version comparison
agent-device replay <path>replay→replay requires path;replay /tmp/missing.ad→ENOENT: ... open '/tmp/missing.ad'(reaches replay engine, spins upagent-device-replay-daemon)replay/replay <path>→Unknown command: replayVerified on 0.17.1 after downgrading and restarting the daemon:
Root cause (from inspecting the published dist)
The CLI dispatcher treats the truthiness of a command handler's return value as "was this command handled?". If the local handler exists but returns a falsy value, the dispatcher falls through and throws
Unknown command.In 0.17.2,
replaywas given a local CLI handler whose normalreplay <path>branch validates the args and then returnsfalse(onlyreplay exportreturns a real result):Dispatcher:
tA.replayexists, soawait t_(...)resolves tofalse, theelse ifis falsy, andUnknown command: replayis thrown.In 0.17.1,
replayandtestwere defined side-by-side in the same generic args-builder map (both routed through the daemon), which is why both worked:So the regression is
replaybeing moved from the shared generic/daemon path (whereteststill lives) into a local handler that returnsfalseon the happy path.Suggested fix
Restore 0.17.1 behavior: route
replaythrough the same generic/daemon args-builder path astest. If a local handler is kept, itsreplay <path>branch must actually execute the replay and return a truthy result on success so the dispatcher's truthiness check passes.Workaround
agent-device@0.17.1(and restart the daemon), oragent-device test <path.ad> -e KEY=VALUE ...— same replay engine, same-eenv-substitution flags.