@sema-agent/core
Version:
Stateless, task-oriented AI agent core
49 lines (48 loc) • 3.75 kB
TypeScript
/**
* The tracked-cwd liveness probe and its diagnosis, shared by the three sites that spawn into the tracked
* logical cwd: the shell's foreground and background legs, and the watch tool's background leg. A second
* copy of either half would be a second answer to "is this directory usable?" and a second refusal
* wording for the same fact, so both are minted here once and each leg passes only its own tool name.
*/
import type { ExecutionEnv } from "../../internal/harness-types.js";
/**
* Is `dir` USABLE as a spawn cwd? "Dead" is the whole class, not just deletion: gone, replaced by a FILE
* of the same name or standing under one, or a symlink that does not resolve to a directory. `fileInfo`
* is lstat-semantics, so a symlink is judged by FOLLOWING it (`canonicalPath`, then one probe of the
* target — a link retargeted at a file resolves perfectly well and is still not a cwd).
*
* The failure direction is deliberately asymmetric. Where a failure says nothing about the chain (the
* initial lstat, the target's own), an error means death only when it SAYS so — refusing a command, or
* resetting somebody's working directory, over one flaky stat would be worse than the failure being
* diagnosed. The RESOLUTION step is the exception: a chain that will not resolve is no directory by any
* reading; see {@link resolutionFailureMeansDead} for the one code that step reads as death on top.
*/
export declare function isDeadCwd(env: ExecutionEnv, dir: string, signal?: AbortSignal): Promise<boolean>;
/**
* Dead-cwd diagnosis for a never-started command (spawn_error / shell_unavailable): a command that
* DELETED or REPLACED the tracked cwd (e.g. `opam clean` after `cd` into a build dir) leaves the
* persistent session pinned to a dead directory — every later spawn fails ENOENT/ENOTDIR with a message
* blaming the SHELL BINARY ("spawn /bin/bash ENOENT"), the model concludes bash is gone, and the run is
* permanently disabled (TB compile-compcert, 14 contaminated calls). The env can't distinguish this (spawn
* reports the same code either way), so probe here:
* - tracked cwd dead + task root alive → SELF-HEAL: reset the tracked cwd to the root, tell the model
* what happened and that a retry works;
* - tracked cwd dead + root ALSO dead (never promise a retry that must fail) → honest
* unrecoverable verdict;
* - cwd IS the root and it's dead (`rm -rf "$PWD"` in place — a diverged-guard would
* skip diagnosis entirely) → honest root-gone verdict (no reset target exists);
* - the caller knows NO root (`root === undefined` — a direct mount that was never handed one) → say the
* cwd is gone and that this tool has no reset target, and stop there. Calling that directory "the task
* root" or declaring the tree unrecoverable would both be claims the caller cannot back: the root may
* well be alive, and a `cd` to it recovers. Nothing is reset.
* - cwd healthy → return undefined (the spawn failure has another cause; keep the original error).
* Returns the model-facing message, or undefined when this is not a dead-cwd case. Uses only the public
* env faces (`fileInfo`/`canonicalPath`) — works for any env that maps never-started failures to
* spawn_error/shell_unavailable (contract note on ExecutionErrorCode).
*
* `root` is REQUIRED-but-nullable on purpose (not an optional parameter): a caller without a root has to
* say so at the call site, so "this mount has no root" can never be spelled the same way as "I forgot".
*/
export declare function diagnoseDeadCwd(env: ExecutionEnv, toolName: string, effectiveCwd: string, root: string | undefined, cwdRef?: {
current: string;
}, signal?: AbortSignal): Promise<string | undefined>;