import type { Pace } from './pace.js'; import type { FailoverPolicy } from './failover.js'; import type { HandoffNote } from './handoff.js'; import type OpenAI from 'openai'; export type Message = OpenAI.Chat.ChatCompletionMessageParam; export interface OmniRouteConfig { baseURL: string; apiKey: string; model: string; maxTokens: number; temperature: number; maxTurns: number; } export interface ExecutionContext { os: string; cwd: string; shell: string; gitStatus: string | null; gitBranch: string | null; nodeVersion: string; env: Record; worktreePath?: string; /** * The model and provider serving this session. * * In the prompt so the agent can answer "who are you" and "what are you running" truthfully. * Asked without them, a distilled model reaches for the maker of whatever it was distilled from * — qwen3-coder answered "created by Anthropic", which nothing in KONECK had ever said. */ model?: string; provider?: string; } export interface ToolDefinition { name: string; description: string; parameters: Record; } export interface ToolCall { id: string; name: string; arguments: string; } export interface ToolResult { toolCallId: string; content: string; isError: boolean; } export interface TokenStats { promptTokens: number; completionTokens: number; totalTokens: number; turns: number; /** * Input tokens the provider served from its own cache, when it says so. * * A long run re-sends the whole conversation every request — 152 requests at a 66k average is * ten million tokens of which one or two per cent is new — so how much of that was charged at * the cache rate is the difference between an ordinary bill and a startling one. Providers * report it under three different names and most report nothing, in which case this stays 0 and * no claim is made about it either way. */ cachedTokens?: number; } export interface AgentConfig { provider: string; baseURL?: string; model: string; /** * How hard the model should think, sent as `reasoning_effort` where the endpoint takes it. * * It used to travel only as words in the prompt, which a reasoning model does not act on — so * every call reasoned at the provider's default and a shell command cost a full deliberation. */ pace?: Pace; maxTurns: number; requireApproval: boolean; useWorktree: boolean; verbose: boolean; cwd: string; /** Suppress all TUI output — used by parallel sub-agents */ silent?: boolean; /** CI mode: auto-approve prompts, route output to stderr, no banner/spinner */ ci?: boolean; /** Depth of spawn_agents nesting — sub-agents cannot spawn further agents */ spawnDepth?: number; /** Max spend in USD; agent stops when this is exceeded */ budget?: number; /** * How many parallel sub-agents may talk to the provider at once. * * They share one account, so this is a rate-limit setting, not a parallelism one — the right * value depends on the account, which is why it is configurable rather than fixed. */ agentConcurrency?: number; /** * Draw on the terminal's alternate screen, with the prompt pinned to the last row. * * Off by default because the alternate screen has no scrollback of its own: history becomes * something the application has to scroll, and anyone who relies on their terminal's own buffer * would lose it without asking. */ altScreen?: boolean; /** Whether the working animation runs. Off leaves a still screen; see /animation. */ animate?: boolean; autoCopy?: boolean; /** * Whether the live diff panel sits beside the conversation. * * Unset means it opens itself on a session's first change and is then left as the person leaves * it; false keeps it shut for good. Needs the alternate screen, since an inline transcript is * written into the terminal's own scrollback and has no frame to hold a second column. */ diffPanel?: boolean; /** * Whether a newer version is installed in the background without being asked. * * On by default. It only ever replaces a global npm install, never a checkout, and it says so * in one line when it has happened — the running session keeps the code it started with. */ autoUpdate?: boolean; /** * Clear the screen when the interface opens. `true` does what `clear` does — screen and * scrollback both; `'screen'` keeps the scrollback. On unless set false. */ clearOnStart?: boolean | 'screen'; /** Pick up an unfinished plan automatically when a session is resumed. On unless set false. */ resumeContinues?: boolean; /** * How much of the machine a run may touch: 'off', 'workspace-write', or 'read-only'. * * File tools are confined by KONECK itself, which is certain. Commands are confined by the * operating system where one can do it — bubblewrap on Linux — and not at all where it cannot, * which is stated rather than implied. */ sandbox?: import('./sandbox.js').SandboxMode; /** Directories writable alongside the workspace. */ addDirs?: string[]; /** Images to send with the first prompt, from -i/--image. */ images?: string[]; /** The model's context window, when you know it better than KONECK's table does. */ contextTokens?: number; /** * How long one turn may run, in minutes, before it is abandoned. * * Unset means the default: generous for a model served from this network, five minutes for a * hosted one. See turnTimeoutMs. */ turnTimeout?: number; /** * Whether KONECK may reduce its own tool list to fit a window that cannot hold it. * * True by default and only ever acts when the instructions and tool descriptions genuinely do * not fit the window the server reported. Set false to send everything regardless — the tools * are then complete and the model may have very little room to use them in. */ leanTools?: boolean; /** Response presentation style selected by the user: default, friendly, or minimal. */ personality?: 'default' | 'friendly' | 'minimal'; /** * Let the terminal hand mouse events to KONECK, so the wheel scrolls the transcript. * * Off by default. A terminal can only send mouse events to one place, so while this is on, * dragging to select, the right-click menu and middle-click paste all stop working — and being * able to copy a command out of the transcript matters more than the wheel. */ mouse?: boolean; /** Explicit API key — overrides env var lookup */ apiKey?: string; /** Internal identifier used to correlate audit events for one agent session */ auditSessionId?: string; /** Live streaming callbacks — used by Ink UI to show real-time output */ onChunk?: (text: string) => void; onToolCall?: (name: string, args: string) => void; /** `detail` carries why a tool failed, so a UI can show the reason rather than just a cross. */ /** * A tool finished. * * `args` is the call's own arguments, the same string the start hook was given. It is here because * the browser panel works out what a call was about by parsing them — so without them a completed * write had no path, the panel's header fell back to saying "write_file", and the diff it should * have opened never appeared. The end of a call knows as much about it as the beginning did. */ /** * The connection to the provider is down, or has come back. * * Called with the state of the outage while waiting, and with null once a request succeeds. It * exists so an interface can say "waiting for the network" rather than appearing to hang: a run * that is patiently retrying and a run that has died look identical from outside otherwise. */ onOffline?: (state: { provider: string; attempt: number; nextInMs: number; downForMs: number; } | null) => void; /** * Take back the partial reply just shown, because the turn is being attempted again. * * A retry re-asks from the same messages, so the model says its piece from the beginning — and * whatever the failed attempt managed is still on screen. Without this, a stream that died * mid-sentence and recovered on the sixth attempt showed the same half sentence six times. */ /** * The run has been gathering at length and changing nothing. * * Distinct from circling, which is the same few actions repeated. This is a run that keeps * reading new things and never acts on any of them — diligence right up until it becomes the * whole session. */ onReading?: (said: string, at: { calls: number; reads: number; }) => void; onStreamReset?: () => void; onToolResult?: (name: string, ok: boolean, elapsedMs: number, detail?: string, args?: string) => void; /** * A workspace hook ran. Reported so an interface can show what a hook did — a hook that blocks * a tool invisibly is indistinguishable from a bug in KONECK. */ onHook?: (outcome: import('./hooks.js').HookOutcome) => void; /** * Tools that were deferred and have now been loaded on request. * * Worth reporting: a session whose tool list grows mid-run is otherwise a silent change in what * the agent can do. */ onToolsLoaded?: (names: readonly string[]) => void; /** * Where KONECK keeps what it has learned. Defaults to the home directory. * * Exists because it must: a test that drives the engine writes down what a stub model refused, * and without somewhere else to put it that goes into the real store under the real provider and * model name. One test run left "omni:auto/best-coding refused above 4096" in a live install, * which would have had every future session planning against a stub's fiction. */ stateHome?: string; /** * The most recent line a running command has printed. * * A build or an install produces nothing at all until it exits, so a long one is * indistinguishable from a hang — a thirty-minute `npm install` was reported as being stuck. * Showing its last line is the difference between "working" and "frozen". */ onToolOutput?: (name: string, lastLine: string) => void; onTokens?: (stats: TokenStats) => void; /** * Told when a run with no turn limit has become extraordinary. * * Reported rather than stopped: a cap would override a decision that is not KONECK's to make. But * a run that answered one question with 289 requests and 21 million tokens said nothing while it * did so, and "no limit" was implemented as "no comment" — which are not the same thing. */ onRunawayWarning?: (said: string, spent: { requests: number; tokens: number; }) => void; /** * Told when the run appears to be circling: a small set of actions repeated with nothing written. * * A warning and never a stop. Calibrated on four real sessions, in which a long run that finished * properly was more repetitive than the cycle phase of one that wasted twenty million tokens — a * signal that thin can point something out, but must not end somebody's work. */ onCircling?: (said: string, churn: { distinct: number; repeats: number; }) => void; /** * Frames received on the current stream before any content has arrived. * * A gateway in front of several backends holds the connection open while it finds one, sending * frames that carry no content at all — OmniRoute labels them `omniroute-keepalive`. They are * proof the request was accepted and something is working on it, which is a different situation * from silence, and the two were previously indistinguishable to anyone watching. */ onStreamActivity?: (framesBeforeContent: number) => void; /** * Reasoning tokens as they arrive, with the running total of characters seen this turn. * * A reasoning model can spend a long time thinking before it writes anything, and the wait is * indistinguishable from a stall unless it is said out loud. The text is passed through for * callers that want to show it; the count is what a status line needs. */ onThinking?: (text: string, totalChars: number) => void; /** * Why a run stopped before finishing. * * Every reason used to be printed through the CLI writer, which a UI running the engine with * `silent` never sees — and the turn cap printed nothing at all. So a run that stopped * mid-investigation was indistinguishable from one that finished, and the only clue was that * asking it to continue worked. */ onIncomplete?: (reason: string) => void; /** * The model turned out not to support tool calling, so the run continued without it. * * Worth surfacing rather than silently degrading: an agent that cannot read or write files is a * different thing from one that can, and the user should know which they have before trusting an * answer about their code. */ onToolsUnsupported?: (model: string) => void; /** * The model wrote its tool calls as text and they were recovered and run. * * Distinct from `onToolsUnsupported`: the tools worked. Surfaced because it explains why a run * may be slower and occasionally less exact than the same model would be through the native * tool-calling field. */ onTextToolCalls?: (model: string) => void; /** * Which runtime was found to be serving, when a window probe answered. * * Set by measurement, not configuration: it decides whether the advice for raising a window says * OLLAMA_CONTEXT_LENGTH, `-c`, a Context Length slider or `--max-model-len`. Naming the wrong one * is worse than saying it is unknown, because it is specific enough to be followed. */ servedRuntime?: 'ollama' | 'llamacpp' | 'lmstudio' | 'vllm'; /** * The model started repeating itself while thinking, and the rest was hidden. * * Worth surfacing: the panel filling with one paragraph five times over looks like a broken * interface, and it is a model going nowhere. */ onThinkingLoop?: (copies: number, chars: number) => void; /** * A configured context window larger than the server will actually serve. * * Said rather than silently overruled: somebody who set the number and saw nothing change would * reasonably conclude the setting does nothing, when what happened is that the server's own limit * is the one being enforced. */ onWindowConflict?: (asked: number, serving: number, notice: string) => void; /** * The model said it cannot do something this run can, and was corrected. * * Worth surfacing rather than swallowing: somebody who has just read "I cannot visit websites" * needs to know which of the two to believe, and that a second attempt is being made. */ onFalseDenial?: (denied: readonly string[], notice: string) => void; /** * The reply began repeating itself and the stream was cut. * * Small local models degenerate this way — measured on a real run as five identical copies of * one paragraph in forty-five seconds, with no work done. Surfaced because it is the most * useful thing anyone can be told about a model that looks slow and produces nothing: the * window is probably too small for the conversation it is being given. */ onLooping?: (copies: number) => void; /** * The instructions and tool descriptions take up most of the model's window. * * Reported three different ways before it was measured — a model repeating itself, writing tool * calls as prose, and inventing its own origin — all of which are what too little room looks * like from outside. */ onCrowdedWindow?: (fixedTokens: number, limit: number, advice: string) => void; /** * The tool list was reduced to fit the window. * * Surfaced because a quietly shortened tool list reads as a capability that is missing rather * than one that is deferred until there is room for it. */ onLeanMode?: (dropped: string[], limit: number, notice: string) => void; /** * The git tools were withheld: the repository answering for this folder encloses it. * * Reported as git tools appearing in a project with no git at all — true, because `rev-parse` * walks up. A commit would have gone to the enclosing tree. */ onGitOutsideWorkspace?: (repoRoot: string, workspace: string) => void; /** The server told us its window rather than KONECK assuming one. */ onWindowDetected?: (limit: number, source: string) => void; /** * Part of the system prompt did not fit the window and was left out or shortened. * * Reported because a prompt silently reduced is guidance the model appears to be ignoring. */ onPromptTrimmed?: (dropped: string[], truncated: string[], notice: string) => void; /** * The server counted fewer prompt tokens than were sent, so it truncated. * * The only honest way to learn a window a server will not report. Nothing is reduced before * this fires — a guess is not a reason to take tools away. */ onTruncation?: (window: number, notice: string) => void; /** The model rejected the attached images, so they were dropped and the text sent alone. */ onVisionUnsupported?: (model: string) => void; /** * The conversation was summarised to fit the model's window. * * Worth surfacing: an answer given after compaction was given with less to go on, and someone * watching should know which answers those were. */ onCompress?: (before: number, after: number, limit: number) => void; /** A compaction that was asked for but had nothing to summarise; see `compactNow`. */ onCompactNoop?: (reason: string) => void; /** * Whether one tool call may proceed, asked before it runs. * * The engine holds no policy of its own: the interface knows which mode is selected and what * standing permissions the user has granted, and answers from that — prompting only when it * genuinely has to. Absent, everything is permitted, which is what one-shot runs want. */ /** * Interface-owned approval. `required` is true when workspace policy requires a confirmation * even if the session's normal posture is permissive. */ approve?: (name: string, argsJson: string, required?: boolean) => Promise; /** * What to do when the model in use stops being usable. Off unless somebody turns it on. * * A tool that silently moves your work to a different model is not one that respects the user, so * the default is to ask and nothing is enabled by default. */ /** * A name for this session that another session's user would recognise. * * Session ids are timestamps, and three of those in a list is a wall of digits. Used when * reporting to one session what another is doing to the same file. */ /** * A tool call's arguments as they stream in, so a write can be watched while it happens. * * Reporting only the finished call meant the panel showed a file after it had been written: the * work was over by the time there was anything to look at. */ onToolArgs?: (name: string, argsSoFar: string) => void; /** * What this turn leaves for whoever picks the work up: what changed, what was learned, what is * not finished. Derived from what happened rather than composed by the model, because a summary * a model writes about its own work is the least reliable one available. */ onHandoff?: (said: string, note: HandoffNote) => void; /** This machine sent an anonymous diagnostic report (or tried to). Surfaced so it is never covert. */ onTelemetry?: (sent: boolean, url: string) => void; /** * The run stopped because the plan's next step waits for the user. * * Surfaced so the browser can show it: a run that stops for the person's say-so must say so, or * it looks like it simply gave up. */ onAwaitingUser?: (step: string) => void; /** The model called ask_user: it has a question and is waiting for the person's answer. */ onAskUser?: (question: string, options: readonly string[]) => void; /** Send an anonymous diagnostic report when a run ends. Off unless set; needs telemetryUrl too. */ telemetry?: boolean; /** Where an enabled report goes. Nothing is sent until both this and telemetry are set. */ telemetryUrl?: string; sessionLabel?: string; failover?: FailoverPolicy; /** * Asked before switching, when the policy says ask. Its own hook rather than the tool-approval * one: a change of model is not a tool permission, and routing it through `approve` would let a * permissive mode agree to it without anybody being asked. */ confirmFailover?: (question: string) => Promise; /** Told after a switch actually happened, so the surface can say so where the work is shown. */ onFailover?: (message: string, to: { provider?: string; model: string; }) => void; /** * The run's plan, whenever the model writes or revises one. * * Surfaced so the person watching can see where it thinks it is. An agent that has quietly lost * the thread looks identical to one that is working, right up until it halts — unless it is * saying "step 4 of 7" while doing something from step 1. */ onPlan?: (plan: import('./plan.js').Plan) => void; /** * Reports the state of every parallel sub-agent whenever any of them changes. The whole array * is sent rather than a delta, because a UI redrawing a live panel wants the current picture, * not a change it has to apply to one it is holding. */ onAgentProgress?: (agents: readonly AgentProgress[]) => void; } /** What one parallel sub-agent is doing, as the UI needs to show it. */ export interface AgentProgress { /** Current tool or phase; queued agents have not started yet. */ activity?: string; index: number; task: string; status: 'running' | 'done' | 'failed'; tokens: number; turns: number; startedAt: number; endedAt?: number; /** The worktree branch it was given, when it got one. */ branch?: string; } export interface ReadFilesArgs { paths: string[]; /** First line to return, zero-based. Used to page through a file too big to send whole. */ offset?: number; /** How many lines to return from `offset`. */ limit?: number; } export interface WriteFileArgs { path: string; content: string; } export interface ApplyDiffArgs { path: string; diff: string; } export interface ExecuteCommandArgs { command: string; timeout?: number; cwd?: string; } export interface ListDirArgs { path: string; recursive?: boolean; } export interface GlobSearchArgs { pattern: string; cwd?: string; } export interface GrepSearchArgs { pattern: string; path?: string; file_pattern?: string; case_sensitive?: boolean; max_results?: number; } export interface MoveFileArgs { source: string; destination: string; } export interface DeleteFileArgs { path: string; recursive?: boolean; } export interface SearchReplaceArgs { path: string; search: string; replace: string; replace_all?: boolean; } export interface FetchUrlArgs { url: string; max_chars?: number; } export interface RememberArgs { content: string; category?: string; } export interface SpawnAgentsArgs { tasks: string[]; use_worktrees?: boolean; max_turns?: number; } export interface WebSearchArgs { query: string; num_results?: number; } export interface WorktreeInfo { path: string; branch: string; originalBranch: string; originalCwd: string; } export interface CLIArgs { task?: string; provider?: string; baseURL?: string; model?: string; maxTurns: number; requireApproval: boolean; useWorktree: boolean; verbose: boolean; cwd: string; help: boolean; showVersion: boolean; /** `--check-terminal`: report what this terminal can do, then exit. */ checkTerminal: boolean; listProviders: boolean; chatMode: boolean; resume?: string; listSessions: boolean; ci: boolean; json: boolean; budget?: number; initMode: boolean; completionShell?: string; doctorMode: boolean; statsMode: boolean; watchMode: boolean; watchPattern?: string; setupMode: boolean; prMode: boolean; updateMode: boolean; authMode: boolean; authAction?: string; resumeLast: boolean; resumePick: boolean; healthMode: boolean; /** `koneck web` — the browser interface, served locally. */ webMode: boolean; webPort?: number | undefined; /** * `koneck web list` — which web servers are running, and on which ports. * * A second project takes the next free port, which is predictable but not something to have to * remember; and the token that opens it cannot be guessed at all. */ webList?: boolean | undefined; webHost?: string | undefined; daemonMode: boolean; daemonAction?: string; issuesMode: boolean; issuesAction?: string; issueId?: number; pipelineMode: boolean; debugMode: boolean; debugCommand?: string; refactorMode: boolean; refactorAction?: string; /** Positional operands preserved exactly for native refactor commands. */ refactorArgs?: string[]; packsMode: boolean; packsAction?: string; packName?: string; testGenMode: boolean; testGenTarget?: string; serveMode: boolean; servePort?: number; explainMode: boolean; explainTarget?: string; askMode: boolean; summarizeMode: boolean; summarizeTarget?: string; indexMode: boolean; sandbox?: import('./sandbox.js').SandboxMode; addDirs?: string[]; images?: string[]; } //# sourceMappingURL=types.d.ts.map