launchMailboxWake function
- required ExecutionEnv env,
- String? wakeExecutable,
- required String cwd,
- required String sessionId,
- String? sessionName,
Spawns the detached headless wake run of an asleep mailbox in cwd
(the command comes from mailboxWakeCommand). Returns an error text or
null on success.
gh-1744: a mailbox cwd can name a directory that does not exist (a
legacy lossy slug decode, or a workspace moved away) — spawning there
dies as ProcessException: No such file or directory and the asleep
mailbox can never be woken. The working directory is verified first
(existing and not a plain file) and the refusal names the path, so the
tool note explains the wake failure instead of surfacing a raw process
exception.
What is guaranteed is backend kind semantics, not link resolution: the
real IO env stats THROUGH symlinks (FileStat.stat — a link to a
directory reports FileKind.directory, so symlinked workspaces wake),
and MemoryExecutionEnv has no symlink model, so no current backend
produces FileKind.symlink here. The guard's explicit symlink
allowance is defensive, for a backend honoring pi's documented lstat
contract ("symlinks are not followed automatically"): there a
symlink-kind path naming an existing directory must still wake — the
spawn itself follows the link (pinned by
test/cli/agent_cli_mailbox_wake_test.dart). On such a backend a link
to a plain file also passes this check and fails at the spawn; that
hole is accepted while no backend reports the kind.
Implementation
Future<String?> launchMailboxWake({
required ExecutionEnv env,
String? wakeExecutable,
required String cwd,
required String sessionId,
String? sessionName,
}) async {
final info = await env.fileInfo(cwd);
final kind = info.valueOrNull?.kind;
if (info.isErr || (kind != FileKind.directory && kind != FileKind.symlink)) {
return 'working directory does not exist: $cwd';
}
final command = mailboxWakeCommand(
wakeExecutable: wakeExecutable,
sessionId: sessionId,
sessionName: sessionName,
);
final result = await env.exec(command, options: ShellExecOptions(cwd: cwd));
return result.isOk
? null
: 'shell exec failed: ${result.errorOrNull?.message ?? 'unknown error'}';
}