@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
179 lines (178 loc) • 7.53 kB
TypeScript
/**
* Async delegation (N2) — spawn a background worker, collect it later, in any
* order — types.
*
* Delegation today is a synchronous tool call: the supervising agent blocks
* until the worker is done, so four investigations take four times as long as
* one and the supervisor cannot do anything useful while it waits. These types
* describe the background form — `spawn` hands back a `DelegateHandle`
* immediately, `collect` returns whichever worker finished FIRST regardless of
* which was started first, and each outcome carries a bounded summary plus a
* {@link BankedArtifactRef} to the worker's full report, which is banked to a
* file rather than pushed into the conversation.
*
* Naming: `agentNetwork.ts` already owns `DelegationRule` / `DelegationCondition`
* and `isolatedAgent.ts` owns `AgentDelegationTurnState`, so everything here
* carries the `Delegate` prefix (Critical Rule 9).
*/
import type { BankedArtifactRef } from "./artifact.js";
import type { AgentRunStatus } from "./isolatedAgent.js";
/** What a supervisor hands down when it spawns a background worker. */
export type DelegateSpawnOptions = {
/** The task, in the supervisor's own words. Required and non-empty. */
task: string;
/** What the worker may look at — files, directories, systems. */
scope?: string;
/** Brief slice of context handed down (never the whole rulebook). */
context?: string;
/** Model override for this worker. */
model?: string;
/** Provider override for this worker. */
provider?: string;
/** Read-only tool allowlist for the worker (tool names). */
tools?: string[];
/**
* Caller's session. Collection is scoped to it, and it is the key the task
* checklist's `delegatesPending` / `delegatesReady` counters are read by.
* Defaults to the session the host's tool context declares.
*/
sessionId?: string;
/** Caller's delegation depth; the worker runs one level deeper. */
depth?: number;
/** Short human label used in logs and in the banked report's name. */
label?: string;
/** Parent cancellation — an aborted parent cancels this worker. */
abortSignal?: AbortSignal;
/** Max agentic steps for the worker's research pass. */
maxSteps?: number;
/** Wall-clock budget for the worker's research pass (ms). */
budgetMs?: number;
};
/**
* Returned the moment a worker is spawned — before it has run anything.
* `queued` is true when the process-wide delegation pool was full and the
* worker is waiting for a slot.
*/
export type DelegateHandle = {
workerId: string;
spawnedAt: number;
queued: boolean;
};
/**
* A settled worker, claimed exactly once.
*
* `summary` is bounded; `report` points at the COMPLETE report on disk. The
* two are not alternatives — the summary is what the conversation carries, the
* report is what the evidence lives in.
*/
export type DelegateOutcome = {
workerId: string;
/** Human label the spawn was given (defaults to the worker id). */
label: string;
/** Reused from the isolated-agent runner — no parallel taxonomy. */
status: AgentRunStatus;
/** True for `completed` and `partial`: the worker produced usable evidence. */
ok: boolean;
/** Bounded narrative for the conversation. Never the whole report. */
summary: string;
/** The FULL report, always banked to a file. */
report: BankedArtifactRef;
durationMs: number;
toolCallsUsed: number;
/** Mechanical waste signatures the runner tripped, if any. */
wasteSignals?: string[];
/** `continueAgent()` handle when the worker was cut short mid-investigation. */
handle?: string;
/** Why the worker failed, when it did. */
error?: string;
};
/** `any` returns the first worker to finish; `all` waits for every one. */
export type DelegateCollectMode = "any" | "all";
/**
* What to collect. `waitMs` of 0 polls (return what is ready right now);
* omitting it uses the runtime default.
*/
export type DelegateCollectRequest = {
mode: DelegateCollectMode;
waitMs?: number;
sessionId?: string;
} | {
workerId: string;
waitMs?: number;
sessionId?: string;
};
/**
* Outcomes claimed by one collect call, in COMPLETION order — the order
* workers finished in, which has nothing to do with the order they were
* spawned in.
*/
export type DelegateCollectResult = {
/** Claimed exactly once: these outcomes are gone from the registry. */
completed: DelegateOutcome[];
/** Still running or waiting for a pool slot. */
pending: number;
/** Finished but not yet claimed. */
ready: number;
/**
* True when the wait expired before this call claimed what it asked for:
* for a named worker, that worker's outcome; for `any`, any outcome while
* work was still pending; for `all`, pending work remained when the wait
* ran out. A collect that claimed something reports `false` even if other
* work is still outstanding — `pending`/`ready` carry that.
*/
timedOut: boolean;
};
/** Lifecycle of one background job. `claimed` jobs are dropped immediately. */
export type DelegateJobPhase = "queued" | "running" | "ready" | "claimed";
/**
* Provider/model a model-invoked `delegate_task` spawn falls back to. The spawn
* schema deliberately exposes no `provider` — a model cannot name a provider it
* cannot see — so without a default the worker instance falls back to provider
* auto-selection, which on a host with stray credentials puts a worker on a
* provider nobody configured — observed live as workers walking several
* unconfigured providers before reaching the configured one. The model's own
* `model` argument still wins over `model` here.
*/
export type DelegateSpawnDefaults = {
provider?: string;
model?: string;
};
/** Per-host delegation policy, resolved from registration options. */
export type DelegateRuntimeSettings = {
/** Caller depth at which `delegate_task` refuses rather than spawning. */
maxDepth: number;
/** How long a spawned worker waits for a pool slot before giving up (ms). */
poolQueueTimeoutMs: number;
/** Default `waitMs` for a collect that does not name one (ms). */
defaultCollectWaitMs: number;
/** Defaults merged under every model-invoked `delegate_task` spawn. */
spawnDefaults?: DelegateSpawnDefaults;
};
/** Options for `NeuroLink.registerDelegationTools()`. */
export type DelegateRegistrationOptions = {
/**
* Caller depth at which further delegation is refused. Default 1: a
* background worker does not spawn background workers, because nothing
* would ever collect them.
*/
maxDepth?: number;
/**
* Raise the process-wide delegation pool to at least this many concurrent
* workers. The pool is shared with `registerAgentTool` and only ever rises.
*/
maxConcurrent?: number;
/** Queue wait before a spawned worker gives up on a pool slot (ms). */
poolQueueTimeoutMs?: number;
/** Provider/model for model-invoked spawns — see {@link DelegateSpawnDefaults}. */
spawnDefaults?: DelegateSpawnDefaults;
};
/** What `delegate_task` returns: the handle plus the outstanding counters. */
export type DelegateSpawnToolResult = DelegateHandle & {
pending: number;
ready: number;
};
/** Refusal shape shared with the agent tool registrar: recovery text included. */
export type DelegateRefusal = {
isError: true;
error: string;
};