reasonix 0.4.23 → 0.4.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  CODE_SYSTEM_PROMPT,
4
4
  codeSystemPrompt
5
- } from "./chunk-K6MR4SWS.js";
5
+ } from "./chunk-2BYEKJHX.js";
6
6
  export {
7
7
  CODE_SYSTEM_PROMPT,
8
8
  codeSystemPrompt
9
9
  };
10
- //# sourceMappingURL=prompt-VDN5U3YE.js.map
10
+ //# sourceMappingURL=prompt-6DMLWG2H.js.map
package/dist/index.d.ts CHANGED
@@ -1260,6 +1260,118 @@ interface PlanToolOptions {
1260
1260
  }
1261
1261
  declare function registerPlanTool(registry: ToolRegistry, opts?: PlanToolOptions): ToolRegistry;
1262
1262
 
1263
+ /**
1264
+ * Subagent runtime — isolated child loops for offloading exploration or
1265
+ * self-contained subtasks.
1266
+ *
1267
+ * Two surfaces sit on top of the same `spawnSubagent` core:
1268
+ *
1269
+ * 1. `registerSubagentTool` — exposes a low-level `spawn_subagent`
1270
+ * function-call tool. Library API. NOT registered into the model
1271
+ * tool list by `reasonix code` since 0.4.26 — Skills (with
1272
+ * `runAs: subagent` frontmatter) became the user-facing surface.
1273
+ * Kept exported because library callers and tests still want
1274
+ * direct access to the primitive.
1275
+ *
1276
+ * 2. `run_skill` (in src/tools/skills.ts) — when the resolved skill
1277
+ * has `runAs: subagent`, it calls `spawnSubagent` with the skill
1278
+ * body as the system prompt and the user's `arguments` as the
1279
+ * task. Subagent skills are listed in the pinned Skills index
1280
+ * with a 🧬 marker, which gives the model a clear pattern-match
1281
+ * trigger without forcing it to reason about "is this task big
1282
+ * enough to delegate."
1283
+ *
1284
+ * Why R1 specifically benefits:
1285
+ * - R1 reasoning tokens are expensive AND inflate the parent context.
1286
+ * A subagent runs its own private loop, then surfaces only the
1287
+ * distilled final answer back to the parent — the main session
1288
+ * never sees the reasoning trail.
1289
+ *
1290
+ * Invariants common to both surfaces:
1291
+ * - Serial only — no parallel spawn (MVP).
1292
+ * - Inherits parent's tool registry MINUS `spawn_subagent` itself
1293
+ * (no recursion via the tool API) and MINUS `submit_plan`
1294
+ * (subagents don't propose plans to the user).
1295
+ * - No hooks, no session — runs are ephemeral.
1296
+ * - Lower default `maxToolIters` than the parent (16 vs 64).
1297
+ * - Independent prefix cache (subagent's prefix has its own
1298
+ * fingerprint).
1299
+ * - Parent registry's plan-mode state propagates: subagents can't
1300
+ * escape `/plan`.
1301
+ * - Non-streaming child loop — the parent isn't watching deltas, so
1302
+ * streaming would only add an SSE parser to the critical path.
1303
+ * Cancellation still works via the AbortSignal.
1304
+ */
1305
+
1306
+ /**
1307
+ * Live event emitted by a running subagent. Surfaced via the optional
1308
+ * `sink` ref the TUI attaches its handler to. Side-channel only — these
1309
+ * events do NOT pass through the parent loop's `LoopEvent` stream
1310
+ * because subagents run inside a tool-dispatch frame, after the parent's
1311
+ * `step()` has already yielded `tool_start` and is awaiting the result.
1312
+ */
1313
+ interface SubagentEvent {
1314
+ kind: "start" | "progress" | "end";
1315
+ /** First ~30 chars of the task prompt — used for the TUI status row. */
1316
+ task: string;
1317
+ /** Iteration count inside the child loop (number of tool results so far). */
1318
+ iter?: number;
1319
+ /** Wall-clock ms since the subagent started. */
1320
+ elapsedMs?: number;
1321
+ /** First ~120 chars of the final assistant message. Set on `end`. */
1322
+ summary?: string;
1323
+ /** Error message if the subagent failed. Set on `end`. */
1324
+ error?: string;
1325
+ /** Total turns the subagent took. Set on `end`. */
1326
+ turns?: number;
1327
+ }
1328
+ /**
1329
+ * Mutable ref the registration writes through. The TUI sets `.current`
1330
+ * to its own handler on mount; nothing receives events before that
1331
+ * happens (and headless callers leave `.current = null`, which is the
1332
+ * library-mode default — they read the final result from the helper's
1333
+ * return value instead).
1334
+ */
1335
+ interface SubagentSink {
1336
+ current: ((ev: SubagentEvent) => void) | null;
1337
+ }
1338
+ interface SubagentToolOptions {
1339
+ /** Shared DeepSeek client. */
1340
+ client: DeepSeekClient;
1341
+ /**
1342
+ * Default system prompt used when the model doesn't pass one. Project
1343
+ * memory (REASONIX.md) is appended automatically when `projectRoot` is
1344
+ * set.
1345
+ */
1346
+ defaultSystem?: string;
1347
+ /** Project root for `applyProjectMemory` lookup. Omit in chat mode. */
1348
+ projectRoot?: string;
1349
+ /** Default model. `deepseek-chat` (V3) by default. */
1350
+ defaultModel?: string;
1351
+ /** Iteration ceiling. Lower than the parent (16 by default). */
1352
+ maxToolIters?: number;
1353
+ /** Maximum chars returned in the tool result. */
1354
+ maxResultChars?: number;
1355
+ /** Optional sink the TUI attaches its handler to for live updates. */
1356
+ sink?: SubagentSink;
1357
+ }
1358
+ /**
1359
+ * Register the spawn_subagent tool into the parent registry. Library
1360
+ * surface — `reasonix code` does NOT call this since 0.4.26 (Skills
1361
+ * with `runAs: subagent` are the user-facing surface), but library
1362
+ * consumers who want the low-level tool can opt in.
1363
+ */
1364
+ declare function registerSubagentTool(parentRegistry: ToolRegistry, opts: SubagentToolOptions): ToolRegistry;
1365
+ /**
1366
+ * Build a child ToolRegistry that copies every tool from `parent` except
1367
+ * those whose names are in `exclude`. Plan-mode state propagates so a
1368
+ * subagent spawned while the parent is under `/plan` cannot escape it.
1369
+ *
1370
+ * Exported for tests + library callers who want the same fork behavior
1371
+ * for their own nested-loop patterns.
1372
+ */
1373
+ declare function forkRegistryExcluding(parent: ToolRegistry, exclude: ReadonlySet<string>): ToolRegistry;
1374
+
1263
1375
  /**
1264
1376
  * Native shell tool — lets the model run commands inside the sandbox
1265
1377
  * root so it can actually verify its own work (run tests, check git
@@ -1391,6 +1503,38 @@ declare function prepareSpawn(argv: readonly string[], opts?: ResolveExecutableO
1391
1503
  args: string[];
1392
1504
  spawnOverrides: SpawnOptions;
1393
1505
  };
1506
+ /**
1507
+ * Locate `-Command` / `-c` in `args` and prepend the UTF-8 setup prelude
1508
+ * to its value. Returns the patched args, or `null` when no `-Command`
1509
+ * arg is present (in which case we leave the invocation untouched —
1510
+ * inline-expression and script-file modes have their own conventions
1511
+ * we don't want to silently rewrite).
1512
+ *
1513
+ * Why not always wrap: PowerShell's quoting semantics are finicky enough
1514
+ * that adding a prelude to a script file invocation could break it.
1515
+ * `-Command` is the case the model actually uses, and where mojibake
1516
+ * matters; targeting just it keeps the blast radius small.
1517
+ *
1518
+ * Exported for tests.
1519
+ */
1520
+ declare function injectPowerShellUtf8(args: readonly string[]): string[] | null;
1521
+ /**
1522
+ * Prefix a cmd.exe command line with `chcp 65001 >nul &` so output
1523
+ * (from cmd.exe and any child it spawns) is UTF-8-encoded. Without
1524
+ * this, on Chinese / Japanese / Korean Windows, `dir`, `findstr`,
1525
+ * `where`, etc. emit text in the system codepage (CP936, CP932,
1526
+ * CP949, …) and `chunk.toString()` — which decodes as UTF-8 — produces
1527
+ * garbled mojibake the model then sees as poisoned input on the next
1528
+ * turn.
1529
+ *
1530
+ * Scope: chcp affects ONLY this cmd.exe instance, which exits after
1531
+ * `/c`. No global console state changes. Single `&` (not `&&`) so the
1532
+ * command still runs even on the rare Windows builds where chcp
1533
+ * itself returns a non-zero exit (Win7 quirks; harmless on Win10+).
1534
+ *
1535
+ * Exported so tests can verify the wrapping shape.
1536
+ */
1537
+ declare function withUtf8Codepage(cmdline: string): string;
1394
1538
  /**
1395
1539
  * Quote an argument so cmd.exe parses it back as a single token. We
1396
1540
  * always wrap in double quotes when the arg contains whitespace or
@@ -2434,7 +2578,7 @@ declare function restoreSnapshots(snapshots: EditSnapshot[], rootDir: string): A
2434
2578
  * the Cache-First Loop is trying to conserve. The SEARCH/REPLACE spec
2435
2579
  * is the one unavoidable bloat; we trim everything else.
2436
2580
  */
2437
- declare const CODE_SYSTEM_PROMPT = "You are Reasonix Code, a coding assistant. You have filesystem tools (read_file, write_file, list_directory, search_files, etc.) rooted at the user's working directory.\n\n# When to propose a plan (submit_plan)\n\nYou have a `submit_plan` tool that shows the user a markdown plan and lets them Approve / Refine / Cancel before you execute. Use it proactively when the task is large enough to deserve a review gate:\n\n- Multi-file refactors or renames.\n- Architecture changes (moving modules, splitting / merging files, new abstractions).\n- Anything where \"undo\" after the fact would be expensive \u2014 migrations, destructive cleanups, API shape changes.\n- When the user's request is ambiguous and multiple reasonable interpretations exist \u2014 propose your reading as a plan and let them confirm.\n\nSkip submit_plan for small, obvious changes: one-line typo, clear bug with a clear fix, adding a missing import, renaming a local variable. Just do those.\n\nPlan body: one-sentence summary, then a file-by-file breakdown of what you'll change and why, and any risks or open questions. If some decisions are genuinely up to the user (naming, tradeoffs, out-of-scope possibilities), list them in an \"Open questions\" section \u2014 the user sees the plan in a picker and has a text input to answer your questions before approving. Don't pretend certainty you don't have; flagged questions are how the user tells you what they care about. After calling submit_plan, STOP \u2014 don't call any more tools, wait for the user's verdict.\n\n# Plan mode (/plan)\n\nThe user can ALSO enter \"plan mode\" via /plan, which is a stronger, explicit constraint:\n- Write tools (edit_file, write_file, create_directory, move_file) and non-allowlisted run_command calls are BOUNCED at dispatch \u2014 you'll get a tool result like \"unavailable in plan mode\". Don't retry them.\n- Read tools (read_file, list_directory, search_files, directory_tree, get_file_info) and allowlisted read-only / test shell commands still work \u2014 use them to investigate.\n- You MUST call submit_plan before anything will execute. Approve exits plan mode; Refine stays in; Cancel exits without implementing.\n\n\n# When to edit vs. when to explore\n\nOnly propose edits when the user explicitly asks you to change, fix, add, remove, refactor, or write something. Do NOT propose edits when the user asks you to:\n- analyze, read, explore, describe, or summarize a project\n- explain how something works\n- answer a question about the code\n\nIn those cases, use tools to gather what you need, then reply in prose. No SEARCH/REPLACE blocks, no file changes. If you're unsure what the user wants, ask.\n\nWhen you do propose edits, the user will review them and decide whether to `/apply` or `/discard`. Don't assume they'll accept \u2014 write as if each edit will be audited, because it will.\n\n# Editing files\n\nWhen you've been asked to change a file, output one or more SEARCH/REPLACE blocks in this exact format:\n\npath/to/file.ext\n<<<<<<< SEARCH\nexact existing lines from the file, including whitespace\n=======\nthe new lines\n>>>>>>> REPLACE\n\nRules:\n- Always read_file first so your SEARCH matches byte-for-byte. If it doesn't match, the edit is rejected and you'll have to retry with the exact current content.\n- One edit per block. Multiple blocks in one response are fine.\n- To create a new file, leave SEARCH empty:\n path/to/new.ts\n <<<<<<< SEARCH\n =======\n (whole file content here)\n >>>>>>> REPLACE\n- Do NOT use write_file to change existing files \u2014 the user reviews your edits as SEARCH/REPLACE. write_file is only for files you explicitly want to overwrite wholesale (rare).\n- Paths are relative to the working directory. Don't use absolute paths.\n\n# Trust what you already know\n\nBefore exploring the filesystem to answer a factual question, check whether the answer is already in context: the user's current message, earlier turns in this conversation (including prior tool results from `remember`), and the pinned memory blocks at the top of this prompt. When the user has stated a fact or you have remembered one, it outranks what the files say \u2014 don't re-derive from code what the user already told you. Explore when you genuinely don't know.\n\n# Exploration\n\n- Skip dependency, build, and VCS directories unless the user explicitly asks. The pinned .gitignore block (if any, below) is your authoritative denylist.\n- Prefer search_files / grep over list_directory when you know roughly what you're looking for \u2014 it saves context and avoids enumerating huge trees.\n\n# Style\n\n- Show edits; don't narrate them in prose. \"Here's the fix:\" is enough.\n- One short paragraph explaining *why*, then the blocks.\n- If you need to explore first (list / grep / read), do it with tool calls before writing any prose \u2014 silence while exploring is fine.\n";
2581
+ declare const CODE_SYSTEM_PROMPT = "You are Reasonix Code, a coding assistant. You have filesystem tools (read_file, write_file, list_directory, search_files, etc.) rooted at the user's working directory.\n\n# When to propose a plan (submit_plan)\n\nYou have a `submit_plan` tool that shows the user a markdown plan and lets them Approve / Refine / Cancel before you execute. Use it proactively when the task is large enough to deserve a review gate:\n\n- Multi-file refactors or renames.\n- Architecture changes (moving modules, splitting / merging files, new abstractions).\n- Anything where \"undo\" after the fact would be expensive \u2014 migrations, destructive cleanups, API shape changes.\n- When the user's request is ambiguous and multiple reasonable interpretations exist \u2014 propose your reading as a plan and let them confirm.\n\nSkip submit_plan for small, obvious changes: one-line typo, clear bug with a clear fix, adding a missing import, renaming a local variable. Just do those.\n\nPlan body: one-sentence summary, then a file-by-file breakdown of what you'll change and why, and any risks or open questions. If some decisions are genuinely up to the user (naming, tradeoffs, out-of-scope possibilities), list them in an \"Open questions\" section \u2014 the user sees the plan in a picker and has a text input to answer your questions before approving. Don't pretend certainty you don't have; flagged questions are how the user tells you what they care about. After calling submit_plan, STOP \u2014 don't call any more tools, wait for the user's verdict.\n\n# Plan mode (/plan)\n\nThe user can ALSO enter \"plan mode\" via /plan, which is a stronger, explicit constraint:\n- Write tools (edit_file, write_file, create_directory, move_file) and non-allowlisted run_command calls are BOUNCED at dispatch \u2014 you'll get a tool result like \"unavailable in plan mode\". Don't retry them.\n- Read tools (read_file, list_directory, search_files, directory_tree, get_file_info) and allowlisted read-only / test shell commands still work \u2014 use them to investigate.\n- You MUST call submit_plan before anything will execute. Approve exits plan mode; Refine stays in; Cancel exits without implementing.\n\n\n# Delegating to subagents via Skills (\uD83E\uDDEC)\n\nThe pinned Skills index below lists playbooks you can invoke with `run_skill`. Skills marked with **\uD83E\uDDEC** spawn an **isolated subagent** \u2014 a fresh child loop that runs the playbook in its own context and returns only the final answer. The subagent's tool calls and reasoning never enter your context, so \uD83E\uDDEC skills are how you keep the main session lean.\n\nTwo built-ins ship by default:\n- **\uD83E\uDDEC explore** \u2014 read-only investigation across the codebase. Use when the user says things like \"find all places that...\", \"how does X work across the project\", \"survey the code for Y\". Pass `arguments` describing the concrete question.\n- **\uD83E\uDDEC research** \u2014 combines web search + code reading. Use for \"is X supported by lib Y\", \"what's the canonical way to Z\", \"compare our impl to the spec\".\n\nWhen to delegate (call `run_skill` with a \uD83E\uDDEC skill):\n- The task would otherwise need >5 file reads or searches.\n- You only need the conclusion, not the exploration trail.\n- The work is self-contained (you can describe it in one paragraph).\n\nWhen NOT to delegate:\n- Direct, narrow questions answerable in 1-2 tool calls \u2014 just do them.\n- Anything where you need to track intermediate results yourself (planning, multi-step edits).\n- Anything that requires user interaction (subagents can't submit plans or ask you for clarification).\n\nAlways pass a clear, self-contained `arguments` \u2014 that text is the **only** context the subagent gets.\n\n# When to edit vs. when to explore\n\nOnly propose edits when the user explicitly asks you to change, fix, add, remove, refactor, or write something. Do NOT propose edits when the user asks you to:\n- analyze, read, explore, describe, or summarize a project\n- explain how something works\n- answer a question about the code\n\nIn those cases, use tools to gather what you need, then reply in prose. No SEARCH/REPLACE blocks, no file changes. If you're unsure what the user wants, ask.\n\nWhen you do propose edits, the user will review them and decide whether to `/apply` or `/discard`. Don't assume they'll accept \u2014 write as if each edit will be audited, because it will.\n\n# Editing files\n\nWhen you've been asked to change a file, output one or more SEARCH/REPLACE blocks in this exact format:\n\npath/to/file.ext\n<<<<<<< SEARCH\nexact existing lines from the file, including whitespace\n=======\nthe new lines\n>>>>>>> REPLACE\n\nRules:\n- Always read_file first so your SEARCH matches byte-for-byte. If it doesn't match, the edit is rejected and you'll have to retry with the exact current content.\n- One edit per block. Multiple blocks in one response are fine.\n- To create a new file, leave SEARCH empty:\n path/to/new.ts\n <<<<<<< SEARCH\n =======\n (whole file content here)\n >>>>>>> REPLACE\n- Do NOT use write_file to change existing files \u2014 the user reviews your edits as SEARCH/REPLACE. write_file is only for files you explicitly want to overwrite wholesale (rare).\n- Paths are relative to the working directory. Don't use absolute paths.\n\n# Trust what you already know\n\nBefore exploring the filesystem to answer a factual question, check whether the answer is already in context: the user's current message, earlier turns in this conversation (including prior tool results from `remember`), and the pinned memory blocks at the top of this prompt. When the user has stated a fact or you have remembered one, it outranks what the files say \u2014 don't re-derive from code what the user already told you. Explore when you genuinely don't know.\n\n# Exploration\n\n- Skip dependency, build, and VCS directories unless the user explicitly asks. The pinned .gitignore block (if any, below) is your authoritative denylist.\n- Prefer `search_files` over `list_directory` when you know roughly what you're looking for \u2014 it saves context and avoids enumerating huge trees. Note: `search_files` matches file NAMES; for searching file CONTENTS use `search_content`.\n- Available exploration tools: `read_file`, `list_directory`, `directory_tree`, `search_files` (filename match), `search_content` (content grep \u2014 use for \"where is X called\", \"find all references to Y\"), `get_file_info`. Don't call `grep` or other tools that aren't in this list \u2014 they don't exist as functions.\n\n# Path conventions\n\nTwo different rules depending on which tool:\n\n- **Filesystem tools** (`read_file`, `list_directory`, `search_files`, `edit_file`, etc.): paths are sandbox-relative. `/` means the project root, `/src/foo.ts` means `<project>/src/foo.ts`. Both relative (`src/foo.ts`) and POSIX-absolute (`/src/foo.ts`) forms work.\n- **`run_command`**: the command runs in a real OS shell with cwd pinned to the project root. Paths inside the shell command are interpreted by THAT shell, not by us. **Never use leading `/` in run_command arguments** \u2014 Windows treats `/tests` as drive-root `F:\\tests` (non-existent), POSIX shells treat it as filesystem root. Use plain relative paths (`tests`, `./tests`, `src/loop.ts`) instead.\n\n# Style\n\n- Show edits; don't narrate them in prose. \"Here's the fix:\" is enough.\n- One short paragraph explaining *why*, then the blocks.\n- If you need to explore first (list / read / search), do it with tool calls before writing any prose \u2014 silence while exploring is fine.\n";
2438
2582
  /**
2439
2583
  * Inject the project's `.gitignore` content into the system prompt as a
2440
2584
  * "respect this on top of the built-in denylist" hint. We don't parse
@@ -2595,4 +2739,126 @@ declare function compareVersions(a: string, b: string): number;
2595
2739
  */
2596
2740
  declare function isNpxInstall(): boolean;
2597
2741
 
2598
- export { AppendOnlyLog, type ApplyResult, type ApplyStatus, type BranchOptions, type BranchProgress, type BranchResult, type BranchSample, type BranchSelector, type BranchSummary, type BridgeOptions, type BridgeResult, CODE_SYSTEM_PROMPT, CacheFirstLoop, type CacheFirstLoopOptions, type CallToolResult, type ChatMessage, type ChatResponse, DEFAULT_MAX_RESULT_CHARS, DeepSeekClient, type DeepSeekClientOptions, type RenderOptions as DiffRenderOptions, type DiffReport, type DiffSide, type EditBlock, type EditSnapshot, type EventRole, type FilesystemToolsOptions, type FlattenDecision, type FlattenOptions, type GetLatestVersionOptions, type GetPromptResult, HOOK_EVENTS, HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME, type HarvestOptions, type HookConfig, type HookEvent, type HookOutcome, type HookPayload, type HookReport, type HookScope, type HookSettings, type HookSpawnInput, type HookSpawnResult, type HookSpawner, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type InspectionReport, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, LATEST_CACHE_TTL_MS, LATEST_FETCH_TIMEOUT_MS, type ListPromptsResult, type ListResourcesResult, type ListToolsResult, type LoadHookSettingsOptions, type LoopEvent, MCP_PROTOCOL_VERSION, MEMORY_INDEX_FILE, MEMORY_INDEX_MAX_CHARS, McpClient, type McpClientOptions, type McpContentBlock, type McpProgressHandler, type McpProgressInfo, type McpPrompt, type McpPromptArgument, type McpPromptMessage, type McpPromptResourceBlock, type McpResource, type McpResourceContents, type McpResourceContentsBlob, type McpResourceContentsText, type McpSpec, type McpTool, type McpToolSchema, type McpTransport, type MemoryEntry, type MemoryScope, MemoryStore, type MemoryStoreOptions, type MemoryToolsOptions, type MemoryType, type WriteInput as MemoryWriteInput, NeedsConfirmationError, PROJECT_MEMORY_FILE, PROJECT_MEMORY_MAX_CHARS, type PageContent, PlanProposedError, type PlanToolOptions, type ProgressNotificationParams, type ProjectMemory, type ReadResourceResult, type ReadTranscriptResult, type ReasonixConfig, type ReconfigurableOptions, type RepairReport, type ReplayStats, type ResolvedHook, type RetryInfo, type RetryOptions, type Role, type RunCommandResult, type RunHooksOptions, type ScavengeOptions, type ScavengeResult, type SearchResult, type SectionResult, type SessionInfo, SessionStats, type SessionSummary, type ShellToolsOptions, type SseMcpSpec, SseTransport, type SseTransportOptions, type StdioMcpSpec, StdioTransport, type StdioTransportOptions, StormBreaker, type StreamChunk, type ToolCall, type ToolCallContext, ToolCallRepair, type ToolCallRepairOptions, type ToolDefinition, type ToolFunctionSpec, ToolRegistry, type ToolSpec, type TranscriptMeta, type TranscriptRecord, type TruncationRepairResult, type TurnPair, type TurnStats, type TypedPlanState, USER_MEMORY_DIR, Usage, VERSION, VolatileScratch, type WebFetchOptions, type WebSearchOptions, type WebToolsOptions, aggregateBranchUsage, analyzeSchema, appendSessionMessage, applyEditBlock, applyEditBlocks, applyMemoryStack, applyProjectMemory, applyUserMemory, bridgeMcpTools, claudeEquivalentCost, codeSystemPrompt, compareVersions, computeReplayStats, costUsd, decideOutcome, defaultConfigPath, defaultSelector, deleteSession, diffTranscripts, emptyPlanState, fetchWithRetry, flattenMcpResult, flattenSchema, formatCommandResult, formatHookOutcomeMessage, formatLoopError, formatSearchResults, getLatestVersion, globalSettingsPath, harvest, healLoadedMessages, htmlToText, inputCostUsd, inspectMcpServer, isAllowed, isJsonRpcError, isNpxInstall, isPlanStateEmpty, isPlausibleKey, listSessions, loadApiKey, loadDotenv, loadHooks, loadSessionMessages, matchesTool, memoryEnabled, nestArguments, openTranscriptFile, outputCostUsd, parseEditBlocks, parseMcpSpec, parseMojeekResults, parseTranscript, prepareSpawn, projectHash, projectSettingsPath, quoteForCmdExe, readConfig, readProjectMemory, readTranscript, recordFromLoopEvent, redactKey, registerFilesystemTools, registerMemoryTools, registerPlanTool, registerShellTools, registerWebTools, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, resolveExecutable, restoreSnapshots, runBranches, runCommand, runHooks, sanitizeMemoryName, sanitizeName as sanitizeSessionName, saveApiKey, scavengeToolCalls, sessionPath, sessionsDir, similarity, snapshotBeforeEdits, stripHallucinatedToolMarkup, tokenizeCommand, truncateForModel, webFetch, webSearch, writeConfig, writeMeta, writeRecord };
2742
+ /**
2743
+ * Persistent per-turn usage log at `~/.reasonix/usage.jsonl`.
2744
+ *
2745
+ * Each line is a single `UsageRecord` — one turn's tokens + cost
2746
+ * snapshot — appended after every `assistant_final` event. This is
2747
+ * what drives `reasonix stats` (the dashboard, no-arg form), so the
2748
+ * user can see how much they've spent vs what the equivalent Claude
2749
+ * spend would have been. The Pillar 1 pitch (94–97% cost reduction
2750
+ * vs Claude, from the v0.3 hard-number table) becomes a fact users
2751
+ * can verify on their own machine.
2752
+ *
2753
+ * Format choices:
2754
+ * - **append-only JSONL** — one line per turn, durable, survives
2755
+ * abrupt exits. A corrupted tail line loses at most one record.
2756
+ * - **flat keys, no nesting** — readable with `jq` / `cut` / `awk`;
2757
+ * the model doesn't need to parse this, humans do.
2758
+ * - **best-effort writes** — disk errors never propagate into the
2759
+ * turn. We log nothing (no `console.error`) because the TUI is
2760
+ * rendering Ink; a silent skip is the least-worst failure mode.
2761
+ * - **no PII, no prompts, no completions** — the log contains
2762
+ * tokens and costs, that's it. Sessions are identified by the
2763
+ * user-chosen name (never a prompt).
2764
+ *
2765
+ * This file is deliberately NOT wired through project memory or
2766
+ * skills — those are content pins. Usage is pure telemetry.
2767
+ */
2768
+
2769
+ /** One turn's snapshot — serialized verbatim as a JSONL line. */
2770
+ interface UsageRecord {
2771
+ /** Epoch millis when the record was written. */
2772
+ ts: number;
2773
+ /** Session name if the turn ran inside a persisted session, `null` for ephemeral. */
2774
+ session: string | null;
2775
+ /** Model id the turn ran against (drives the pricing lookup). */
2776
+ model: string;
2777
+ promptTokens: number;
2778
+ completionTokens: number;
2779
+ cacheHitTokens: number;
2780
+ cacheMissTokens: number;
2781
+ /** Total cost of the turn in USD. */
2782
+ costUsd: number;
2783
+ /** What the same turn would have cost at Claude Sonnet 4.6 rates. */
2784
+ claudeEquivUsd: number;
2785
+ }
2786
+ /** Where the log lives. Tests override via `opts.path`. */
2787
+ declare function defaultUsageLogPath(homeDirOverride?: string): string;
2788
+ interface AppendUsageInput {
2789
+ session: string | null;
2790
+ model: string;
2791
+ usage: Usage;
2792
+ /** Override the timestamp (tests). */
2793
+ now?: number;
2794
+ /** Override the log path (tests). */
2795
+ path?: string;
2796
+ }
2797
+ /**
2798
+ * Append one record and return it. Swallows disk errors — the TUI
2799
+ * should keep working even if `~/.reasonix/` is read-only.
2800
+ *
2801
+ * Returns the record that was written (or would have been written
2802
+ * if the disk had cooperated) so tests / callers can assert on the
2803
+ * computed cost fields without a round trip through the log file.
2804
+ */
2805
+ declare function appendUsage(input: AppendUsageInput): UsageRecord;
2806
+ /**
2807
+ * Read + parse the log. Malformed lines are silently skipped so a
2808
+ * single corrupted write (half-flushed on power loss, user hand-edit)
2809
+ * doesn't throw away the rest of the history.
2810
+ */
2811
+ declare function readUsageLog(path?: string): UsageRecord[];
2812
+ /** One row of the `reasonix stats` dashboard — a rolled-up window. */
2813
+ interface UsageBucket {
2814
+ label: string;
2815
+ /** Start of the window as epoch millis. `0` = unbounded (all-time). */
2816
+ since: number;
2817
+ turns: number;
2818
+ promptTokens: number;
2819
+ completionTokens: number;
2820
+ cacheHitTokens: number;
2821
+ cacheMissTokens: number;
2822
+ costUsd: number;
2823
+ claudeEquivUsd: number;
2824
+ }
2825
+ /** Cache hit ratio for a bucket — zero denominator returns 0. */
2826
+ declare function bucketCacheHitRatio(b: UsageBucket): number;
2827
+ /** Savings vs Claude as a fraction (0.94 = 94% savings). 0 if Claude cost is 0. */
2828
+ declare function bucketSavingsFraction(b: UsageBucket): number;
2829
+ interface AggregateOptions {
2830
+ /** Override `Date.now()` for deterministic tests. */
2831
+ now?: number;
2832
+ }
2833
+ interface UsageAggregate {
2834
+ /** Fixed-order rolling windows: today, week, month, all-time. */
2835
+ buckets: UsageBucket[];
2836
+ /** Model id → turn count. Sorted descending; top entry is the "most used." */
2837
+ byModel: Array<{
2838
+ model: string;
2839
+ turns: number;
2840
+ }>;
2841
+ /** Session name → turn count. Sorted descending. Null sessions are grouped under `"(ephemeral)"`. */
2842
+ bySession: Array<{
2843
+ session: string;
2844
+ turns: number;
2845
+ }>;
2846
+ /** Earliest record's ts, or `null` when the log is empty. Drives "saved $X since <date>". */
2847
+ firstSeen: number | null;
2848
+ /** Latest record's ts, or `null` when the log is empty. */
2849
+ lastSeen: number | null;
2850
+ }
2851
+ /**
2852
+ * Fold a flat record list into the dashboard shape — rolling windows
2853
+ * plus model / session histograms. Windows are INCLUSIVE of boundary:
2854
+ * - today = last 24h (rolling, not calendar-day)
2855
+ * - week = last 7d
2856
+ * - month = last 30d
2857
+ * - all = every record
2858
+ * Rolling windows avoid "it's 00:03, 'today' is empty" surprises.
2859
+ */
2860
+ declare function aggregateUsage(records: UsageRecord[], opts?: AggregateOptions): UsageAggregate;
2861
+ /** File-size helper for the stats header — "1.2 MB" etc. Returns "" if missing. */
2862
+ declare function formatLogSize(path?: string): string;
2863
+
2864
+ export { type AggregateOptions, AppendOnlyLog, type AppendUsageInput, type ApplyResult, type ApplyStatus, type BranchOptions, type BranchProgress, type BranchResult, type BranchSample, type BranchSelector, type BranchSummary, type BridgeOptions, type BridgeResult, CODE_SYSTEM_PROMPT, CacheFirstLoop, type CacheFirstLoopOptions, type CallToolResult, type ChatMessage, type ChatResponse, DEFAULT_MAX_RESULT_CHARS, DeepSeekClient, type DeepSeekClientOptions, type RenderOptions as DiffRenderOptions, type DiffReport, type DiffSide, type EditBlock, type EditSnapshot, type EventRole, type FilesystemToolsOptions, type FlattenDecision, type FlattenOptions, type GetLatestVersionOptions, type GetPromptResult, HOOK_EVENTS, HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME, type HarvestOptions, type HookConfig, type HookEvent, type HookOutcome, type HookPayload, type HookReport, type HookScope, type HookSettings, type HookSpawnInput, type HookSpawnResult, type HookSpawner, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type InspectionReport, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, LATEST_CACHE_TTL_MS, LATEST_FETCH_TIMEOUT_MS, type ListPromptsResult, type ListResourcesResult, type ListToolsResult, type LoadHookSettingsOptions, type LoopEvent, MCP_PROTOCOL_VERSION, MEMORY_INDEX_FILE, MEMORY_INDEX_MAX_CHARS, McpClient, type McpClientOptions, type McpContentBlock, type McpProgressHandler, type McpProgressInfo, type McpPrompt, type McpPromptArgument, type McpPromptMessage, type McpPromptResourceBlock, type McpResource, type McpResourceContents, type McpResourceContentsBlob, type McpResourceContentsText, type McpSpec, type McpTool, type McpToolSchema, type McpTransport, type MemoryEntry, type MemoryScope, MemoryStore, type MemoryStoreOptions, type MemoryToolsOptions, type MemoryType, type WriteInput as MemoryWriteInput, NeedsConfirmationError, PROJECT_MEMORY_FILE, PROJECT_MEMORY_MAX_CHARS, type PageContent, PlanProposedError, type PlanToolOptions, type ProgressNotificationParams, type ProjectMemory, type ReadResourceResult, type ReadTranscriptResult, type ReasonixConfig, type ReconfigurableOptions, type RepairReport, type ReplayStats, type ResolvedHook, type RetryInfo, type RetryOptions, type Role, type RunCommandResult, type RunHooksOptions, type ScavengeOptions, type ScavengeResult, type SearchResult, type SectionResult, type SessionInfo, SessionStats, type SessionSummary, type ShellToolsOptions, type SseMcpSpec, SseTransport, type SseTransportOptions, type StdioMcpSpec, StdioTransport, type StdioTransportOptions, StormBreaker, type StreamChunk, type SubagentEvent, type SubagentSink, type SubagentToolOptions, type ToolCall, type ToolCallContext, ToolCallRepair, type ToolCallRepairOptions, type ToolDefinition, type ToolFunctionSpec, ToolRegistry, type ToolSpec, type TranscriptMeta, type TranscriptRecord, type TruncationRepairResult, type TurnPair, type TurnStats, type TypedPlanState, USER_MEMORY_DIR, Usage, type UsageAggregate, type UsageBucket, type UsageRecord, VERSION, VolatileScratch, type WebFetchOptions, type WebSearchOptions, type WebToolsOptions, aggregateBranchUsage, aggregateUsage, analyzeSchema, appendSessionMessage, appendUsage, applyEditBlock, applyEditBlocks, applyMemoryStack, applyProjectMemory, applyUserMemory, bridgeMcpTools, bucketCacheHitRatio, bucketSavingsFraction, claudeEquivalentCost, codeSystemPrompt, compareVersions, computeReplayStats, costUsd, decideOutcome, defaultConfigPath, defaultSelector, defaultUsageLogPath, deleteSession, diffTranscripts, emptyPlanState, fetchWithRetry, flattenMcpResult, flattenSchema, forkRegistryExcluding, formatCommandResult, formatHookOutcomeMessage, formatLogSize, formatLoopError, formatSearchResults, getLatestVersion, globalSettingsPath, harvest, healLoadedMessages, htmlToText, injectPowerShellUtf8, inputCostUsd, inspectMcpServer, isAllowed, isJsonRpcError, isNpxInstall, isPlanStateEmpty, isPlausibleKey, listSessions, loadApiKey, loadDotenv, loadHooks, loadSessionMessages, matchesTool, memoryEnabled, nestArguments, openTranscriptFile, outputCostUsd, parseEditBlocks, parseMcpSpec, parseMojeekResults, parseTranscript, prepareSpawn, projectHash, projectSettingsPath, quoteForCmdExe, readConfig, readProjectMemory, readTranscript, readUsageLog, recordFromLoopEvent, redactKey, registerFilesystemTools, registerMemoryTools, registerPlanTool, registerShellTools, registerSubagentTool, registerWebTools, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, resolveExecutable, restoreSnapshots, runBranches, runCommand, runHooks, sanitizeMemoryName, sanitizeName as sanitizeSessionName, saveApiKey, scavengeToolCalls, sessionPath, sessionsDir, similarity, snapshotBeforeEdits, stripHallucinatedToolMarkup, tokenizeCommand, truncateForModel, webFetch, webSearch, withUtf8Codepage, writeConfig, writeMeta, writeRecord };