UNPKG

@juspay/neurolink

Version:

The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002

111 lines (110 loc) • 5.75 kB
/** * Background commands (N4) — run a command detached, bank every byte it * writes, monitor it while it runs. * * A reviewing agent has to run real commands — a build, a test suite, a linter * whose output IS the evidence for a finding — and the naive shapes both fail. * `bashTool` blocks the loop, hands the model a shell, and truncates its own * output at 100 KB. A `child_process` call with a string command is a shell * injection with extra steps. * * This module keeps three promises instead: * * - **Detached.** `startBackgroundCommand` returns a `taskId` immediately; the * agent keeps working and asks about the command when it wants to. * - **Nothing discarded.** Both streams are written straight to files as they * arrive and the COMPLETE files are banked as artifacts (N3) when the * command settles. The conversation gets a bounded tail plus a read-back * call. The single bound is `maxOutputBytes`, and reaching it is a state * (`output-limit`) the caller can see, not a silent cut. * - **Hardened by contract.** argv arrays with `shell: false`, an exact-match * executable allowlist, a cwd that must resolve through symlinks inside a * declared root, and a timeout that escalates SIGTERM → SIGKILL. * * Completion reaches the model the same way a delegate's does (N2.3): the * `running` / `finished` counters ride on every command tool result and — via * the checklist — on every `tasks_list`. The core generate loop is untouched. * * @module agent/backgroundCommands */ import type { NeuroLink } from "../neurolink.js"; import type { BackgroundCommandCounts, BackgroundCommandHandle, BackgroundCommandOptions, BackgroundCommandOutputPage, BackgroundCommandPageRequest, BackgroundCommandPolicy, BackgroundCommandStatus, MCPExecutableTool } from "../types/index.js"; /** * Declare what this host may execute. There is no default: until this is * called, every start is refused, because "run whatever the model asks" is not * a defensible default for a primitive that spawns processes. */ export declare function setBackgroundCommandPolicy(host: NeuroLink, policy: BackgroundCommandPolicy): void; /** The policy in force for a host, or undefined when none was declared. */ export declare function getBackgroundCommandPolicy(host: NeuroLink): BackgroundCommandPolicy | undefined; /** * Commands for a host's session: `running` have not settled, `finished` have. * Carried on every command tool result and on every `ChecklistToolResult`, so * the model learns a build finished without polling for it. */ export declare function backgroundCommandCounts(host: NeuroLink, sessionId?: string): BackgroundCommandCounts; /** * Start a command in the background and get its task id immediately. * * @throws when no policy is set, argv is malformed, the executable is not * allowlisted, the policy vetoes the command, or the cwd escapes the * sandbox root. Every message names its own recovery step. */ export declare function startBackgroundCommand(host: NeuroLink, argv: string[], options: BackgroundCommandOptions): Promise<BackgroundCommandHandle>; /** * Everything known about one command right now — synchronous, because the * job state is live and a monitor that has to be awaited is a monitor nobody * calls mid-loop. * * @throws when the task id is unknown to this host */ export declare function getBackgroundCommandStatus(host: NeuroLink, taskId: string): BackgroundCommandStatus; /** * Wait for a command to settle. * * `timeoutMs` bounds the WAIT, not the command: when it elapses the current * (still running) status is returned rather than throwing, so a caller can * poll in bounded steps without ever losing the job. * * @throws when the task id is unknown to this host */ export declare function awaitBackgroundCommand(host: NeuroLink, taskId: string, opts?: { timeoutMs?: number; }): Promise<BackgroundCommandStatus>; /** * Kill a running command: SIGTERM (or the signal you name), SIGKILL five * seconds later if it is still there. Resolves with the settled status, so a * caller never has to guess whether the output was banked yet. * * Killing an already-settled command is a no-op that returns its status — * the outcome is not discarded. * * @throws when the task id is unknown to this host */ export declare function killBackgroundCommand(host: NeuroLink, taskId: string, signal?: NodeJS.Signals): Promise<BackgroundCommandStatus>; /** * Kill every unsettled command this host started. Host lifecycle only * (`shutdown()`/`dispose()`): a disposed instance must not leave child * processes running with nobody left to collect them. * * @returns how many commands were signalled */ export declare function killAllBackgroundCommands(host: NeuroLink): Promise<number>; /** * Read one character window of a command's output, straight from its log file. * * Works while the command is still running — that is the monitor case — and * after it settled. Character offsets, `totalSize` and `hasMore` match * `retrieve_context` exactly, so paging code written for one works on the * other. * * @throws when the task id is unknown to this host */ export declare function readBackgroundCommandOutput(host: NeuroLink, taskId: string, page: BackgroundCommandPageRequest): Promise<BackgroundCommandOutputPage>; /** * The four model-facing command tools, bound to `host`. Register them with * `host.registerTool()` (see `NeuroLink.registerBackgroundCommandTools()`), * never on the tool registry directly: only the "user-defined" category * reaches the LLM's tool schema. */ export declare function createBackgroundCommandTools(host: NeuroLink): Record<string, MCPExecutableTool>;