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.
- package/README.md +23 -0
- package/dist/cli/{chunk-K6MR4SWS.js → chunk-2BYEKJHX.js} +119 -10
- package/dist/cli/chunk-2BYEKJHX.js.map +1 -0
- package/dist/cli/index.js +773 -81
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{prompt-VDN5U3YE.js → prompt-6DMLWG2H.js} +2 -2
- package/dist/index.d.ts +268 -2
- package/dist/index.js +656 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-K6MR4SWS.js.map +0 -1
- /package/dist/cli/{prompt-VDN5U3YE.js.map → prompt-6DMLWG2H.js.map} +0 -0
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CODE_SYSTEM_PROMPT,
|
|
4
4
|
codeSystemPrompt
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-2BYEKJHX.js";
|
|
6
6
|
export {
|
|
7
7
|
CODE_SYSTEM_PROMPT,
|
|
8
8
|
codeSystemPrompt
|
|
9
9
|
};
|
|
10
|
-
//# sourceMappingURL=prompt-
|
|
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
|
|
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
|
-
|
|
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 };
|