Skip to content

replay <path> fails with Unknown command: replay (regression in 0.17.2, works in 0.17.1) #771

@kacper-mikolajczak

Description

@kacper-mikolajczak

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.2replay <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 replayreplay requires path; replay /tmp/missing.adENOENT: ... 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions