reasonix 0.4.16 → 0.4.17

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-2P2MZLCE.js";
5
+ } from "./chunk-3YQRWFES.js";
6
6
  export {
7
7
  CODE_SYSTEM_PROMPT,
8
8
  codeSystemPrompt
9
9
  };
10
- //# sourceMappingURL=prompt-MMANQ36Z.js.map
10
+ //# sourceMappingURL=prompt-HK5XLH55.js.map
package/dist/index.d.ts CHANGED
@@ -718,6 +718,61 @@ declare function healLoadedMessages(messages: ChatMessage[], maxChars: number):
718
718
  */
719
719
  declare function formatLoopError(err: Error): string;
720
720
 
721
+ /**
722
+ * Project memory — a user-authored `REASONIX.md` in the project root
723
+ * that gets pinned into the immutable-prefix system prompt.
724
+ *
725
+ * Design notes:
726
+ *
727
+ * - The file lands in `ImmutablePrefix.system`, so the whole memory
728
+ * block is hashed into the cache prefix fingerprint. Editing the
729
+ * file invalidates the prefix; unchanged memory across sessions
730
+ * keeps the DeepSeek prefix cache warm. That matches Pillar 1 —
731
+ * memory is a deliberate, stable prefix, not per-turn drift.
732
+ * - Only one source: the working-root `REASONIX.md`. No parent walk,
733
+ * no `~/.reasonix/REASONIX.md`, no CLAUDE.md fallback. User-global
734
+ * memory can come later; for v1 one file == one mental model.
735
+ * - Truncated at 8 000 chars (≈ 2k tokens). `.gitignore` gets 2 000
736
+ * because it's a constraint dump; memory gets more headroom because
737
+ * it's deliberate instructions.
738
+ * - Opt-out via `REASONIX_MEMORY=off|false|0`. No CLI flag — memory
739
+ * is a file, `rm REASONIX.md` is the other opt-out.
740
+ */
741
+ declare const PROJECT_MEMORY_FILE = "REASONIX.md";
742
+ declare const PROJECT_MEMORY_MAX_CHARS = 8000;
743
+ interface ProjectMemory {
744
+ /** Absolute path the memory was read from. */
745
+ path: string;
746
+ /** Post-truncation content (may include a "… (truncated N chars)" marker). */
747
+ content: string;
748
+ /** Original byte length before truncation. */
749
+ originalChars: number;
750
+ /** True iff `originalChars > PROJECT_MEMORY_MAX_CHARS`. */
751
+ truncated: boolean;
752
+ }
753
+ /**
754
+ * Read `REASONIX.md` from `rootDir`. Returns `null` when the file is
755
+ * missing, unreadable, or empty (whitespace-only counts as empty — an
756
+ * empty memory file shouldn't perturb the cache prefix).
757
+ */
758
+ declare function readProjectMemory(rootDir: string): ProjectMemory | null;
759
+ /**
760
+ * Resolve whether project memory should be read. Default: on.
761
+ * `REASONIX_MEMORY=off|false|0` turns it off (CI, reproducing issues,
762
+ * intentional offline runs).
763
+ */
764
+ declare function memoryEnabled(): boolean;
765
+ /**
766
+ * Return `basePrompt` with the project's `REASONIX.md` appended as a
767
+ * "Project memory" section. No-op when the file is absent, empty, or
768
+ * memory is disabled via env.
769
+ *
770
+ * The appended block is deterministic — identical input ⇒ identical
771
+ * output — so every session that opens against the same memory file
772
+ * gets the same prefix hash.
773
+ */
774
+ declare function applyProjectMemory(basePrompt: string, rootDir: string): string;
775
+
721
776
  /**
722
777
  * Built-in filesystem tools for `reasonix code`.
723
778
  *
@@ -1879,7 +1934,8 @@ declare const CODE_SYSTEM_PROMPT = "You are Reasonix Code, a coding assistant. Y
1879
1934
  * the file — we hand it to the model as-is. Truncate long ones so we
1880
1935
  * don't eat context budget on huge generated ignore lists.
1881
1936
  *
1882
- * Missing or unreadable .gitignore returns the base prompt unchanged.
1937
+ * Stacking order (stable for cache prefix):
1938
+ * base prompt → project memory (REASONIX.md) → .gitignore block
1883
1939
  */
1884
1940
  declare function codeSystemPrompt(rootDir: string): string;
1885
1941
 
@@ -1953,6 +2009,6 @@ declare function redactKey(key: string): string;
1953
2009
 
1954
2010
  /** Reasonix — DeepSeek-native agent framework. Library entry point. */
1955
2011
 
1956
- declare const VERSION = "0.4.16";
2012
+ declare const VERSION = "0.4.17";
1957
2013
 
1958
- 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 GetPromptResult, type HarvestOptions, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type InspectionReport, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, type ListPromptsResult, type ListResourcesResult, type ListToolsResult, type LoopEvent, MCP_PROTOCOL_VERSION, 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, NeedsConfirmationError, type PageContent, type ProgressNotificationParams, type ReadResourceResult, type ReadTranscriptResult, type ReasonixConfig, type ReconfigurableOptions, type RepairReport, type ReplayStats, type RetryInfo, type RetryOptions, type Role, type RunCommandResult, 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, Usage, VERSION, VolatileScratch, type WebFetchOptions, type WebSearchOptions, type WebToolsOptions, aggregateBranchUsage, analyzeSchema, appendSessionMessage, applyEditBlock, applyEditBlocks, bridgeMcpTools, claudeEquivalentCost, codeSystemPrompt, computeReplayStats, costUsd, defaultConfigPath, defaultSelector, deleteSession, diffTranscripts, emptyPlanState, fetchWithRetry, flattenMcpResult, flattenSchema, formatCommandResult, formatLoopError, formatSearchResults, harvest, healLoadedMessages, htmlToText, inputCostUsd, inspectMcpServer, isAllowed, isJsonRpcError, isPlanStateEmpty, isPlausibleKey, listSessions, loadApiKey, loadDotenv, loadSessionMessages, nestArguments, openTranscriptFile, outputCostUsd, parseEditBlocks, parseMcpSpec, parseMojeekResults, parseTranscript, readConfig, readTranscript, recordFromLoopEvent, redactKey, registerFilesystemTools, registerShellTools, registerWebTools, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, restoreSnapshots, runBranches, runCommand, sanitizeName as sanitizeSessionName, saveApiKey, scavengeToolCalls, sessionPath, sessionsDir, similarity, snapshotBeforeEdits, stripHallucinatedToolMarkup, tokenizeCommand, truncateForModel, webFetch, webSearch, writeConfig, writeMeta, writeRecord };
2014
+ 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 GetPromptResult, type HarvestOptions, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type InspectionReport, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, type ListPromptsResult, type ListResourcesResult, type ListToolsResult, type LoopEvent, MCP_PROTOCOL_VERSION, 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, NeedsConfirmationError, PROJECT_MEMORY_FILE, PROJECT_MEMORY_MAX_CHARS, type PageContent, type ProgressNotificationParams, type ProjectMemory, type ReadResourceResult, type ReadTranscriptResult, type ReasonixConfig, type ReconfigurableOptions, type RepairReport, type ReplayStats, type RetryInfo, type RetryOptions, type Role, type RunCommandResult, 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, Usage, VERSION, VolatileScratch, type WebFetchOptions, type WebSearchOptions, type WebToolsOptions, aggregateBranchUsage, analyzeSchema, appendSessionMessage, applyEditBlock, applyEditBlocks, applyProjectMemory, bridgeMcpTools, claudeEquivalentCost, codeSystemPrompt, computeReplayStats, costUsd, defaultConfigPath, defaultSelector, deleteSession, diffTranscripts, emptyPlanState, fetchWithRetry, flattenMcpResult, flattenSchema, formatCommandResult, formatLoopError, formatSearchResults, harvest, healLoadedMessages, htmlToText, inputCostUsd, inspectMcpServer, isAllowed, isJsonRpcError, isPlanStateEmpty, isPlausibleKey, listSessions, loadApiKey, loadDotenv, loadSessionMessages, memoryEnabled, nestArguments, openTranscriptFile, outputCostUsd, parseEditBlocks, parseMcpSpec, parseMojeekResults, parseTranscript, readConfig, readProjectMemory, readTranscript, recordFromLoopEvent, redactKey, registerFilesystemTools, registerShellTools, registerWebTools, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, restoreSnapshots, runBranches, runCommand, sanitizeName as sanitizeSessionName, saveApiKey, scavengeToolCalls, sessionPath, sessionsDir, similarity, snapshotBeforeEdits, stripHallucinatedToolMarkup, tokenizeCommand, truncateForModel, webFetch, webSearch, writeConfig, writeMeta, writeRecord };
package/dist/index.js CHANGED
@@ -1848,6 +1848,49 @@ function formatLoopError(err) {
1848
1848
  return msg;
1849
1849
  }
1850
1850
 
1851
+ // src/project-memory.ts
1852
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
1853
+ import { join as join2 } from "path";
1854
+ var PROJECT_MEMORY_FILE = "REASONIX.md";
1855
+ var PROJECT_MEMORY_MAX_CHARS = 8e3;
1856
+ function readProjectMemory(rootDir) {
1857
+ const path = join2(rootDir, PROJECT_MEMORY_FILE);
1858
+ if (!existsSync2(path)) return null;
1859
+ let raw;
1860
+ try {
1861
+ raw = readFileSync2(path, "utf8");
1862
+ } catch {
1863
+ return null;
1864
+ }
1865
+ const trimmed = raw.trim();
1866
+ if (!trimmed) return null;
1867
+ const originalChars = trimmed.length;
1868
+ const truncated = originalChars > PROJECT_MEMORY_MAX_CHARS;
1869
+ const content = truncated ? `${trimmed.slice(0, PROJECT_MEMORY_MAX_CHARS)}
1870
+ \u2026 (truncated ${originalChars - PROJECT_MEMORY_MAX_CHARS} chars)` : trimmed;
1871
+ return { path, content, originalChars, truncated };
1872
+ }
1873
+ function memoryEnabled() {
1874
+ const env = process.env.REASONIX_MEMORY;
1875
+ if (env === "off" || env === "false" || env === "0") return false;
1876
+ return true;
1877
+ }
1878
+ function applyProjectMemory(basePrompt, rootDir) {
1879
+ if (!memoryEnabled()) return basePrompt;
1880
+ const mem = readProjectMemory(rootDir);
1881
+ if (!mem) return basePrompt;
1882
+ return `${basePrompt}
1883
+
1884
+ # Project memory (REASONIX.md)
1885
+
1886
+ The user pinned these notes about this project \u2014 treat them as authoritative context for every turn:
1887
+
1888
+ \`\`\`
1889
+ ${mem.content}
1890
+ \`\`\`
1891
+ `;
1892
+ }
1893
+
1851
1894
  // src/tools/filesystem.ts
1852
1895
  import { promises as fs } from "fs";
1853
1896
  import * as pathMod from "path";
@@ -2580,12 +2623,12 @@ ${i + 1}. ${r.title}`);
2580
2623
  }
2581
2624
 
2582
2625
  // src/env.ts
2583
- import { readFileSync as readFileSync2 } from "fs";
2626
+ import { readFileSync as readFileSync3 } from "fs";
2584
2627
  import { resolve as resolve3 } from "path";
2585
2628
  function loadDotenv(path = ".env") {
2586
2629
  let raw;
2587
2630
  try {
2588
- raw = readFileSync2(resolve3(process.cwd(), path), "utf8");
2631
+ raw = readFileSync3(resolve3(process.cwd(), path), "utf8");
2589
2632
  } catch {
2590
2633
  return;
2591
2634
  }
@@ -2604,7 +2647,7 @@ function loadDotenv(path = ".env") {
2604
2647
  }
2605
2648
 
2606
2649
  // src/transcript.ts
2607
- import { createWriteStream, readFileSync as readFileSync3 } from "fs";
2650
+ import { createWriteStream, readFileSync as readFileSync4 } from "fs";
2608
2651
  function recordFromLoopEvent(ev, extra) {
2609
2652
  const rec = {
2610
2653
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -2655,7 +2698,7 @@ function openTranscriptFile(path, meta) {
2655
2698
  return stream;
2656
2699
  }
2657
2700
  function readTranscript(path) {
2658
- const raw = readFileSync3(path, "utf8");
2701
+ const raw = readFileSync4(path, "utf8");
2659
2702
  return parseTranscript(raw);
2660
2703
  }
2661
2704
  function isPlanStateEmptyShape(s) {
@@ -3710,7 +3753,7 @@ async function trySection(load) {
3710
3753
  }
3711
3754
 
3712
3755
  // src/code/edit-blocks.ts
3713
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
3756
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
3714
3757
  import { dirname as dirname3, resolve as resolve4 } from "path";
3715
3758
  var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
3716
3759
  function parseEditBlocks(text) {
@@ -3739,7 +3782,7 @@ function applyEditBlock(block, rootDir) {
3739
3782
  };
3740
3783
  }
3741
3784
  const searchEmpty = block.search.length === 0;
3742
- const exists = existsSync2(absTarget);
3785
+ const exists = existsSync3(absTarget);
3743
3786
  try {
3744
3787
  if (!exists) {
3745
3788
  if (!searchEmpty) {
@@ -3753,7 +3796,7 @@ function applyEditBlock(block, rootDir) {
3753
3796
  writeFileSync2(absTarget, block.replace, "utf8");
3754
3797
  return { path: block.path, status: "created" };
3755
3798
  }
3756
- const content = readFileSync4(absTarget, "utf8");
3799
+ const content = readFileSync5(absTarget, "utf8");
3757
3800
  if (searchEmpty) {
3758
3801
  return {
3759
3802
  path: block.path,
@@ -3787,12 +3830,12 @@ function snapshotBeforeEdits(blocks, rootDir) {
3787
3830
  if (seen.has(b.path)) continue;
3788
3831
  seen.add(b.path);
3789
3832
  const abs = resolve4(absRoot, b.path);
3790
- if (!existsSync2(abs)) {
3833
+ if (!existsSync3(abs)) {
3791
3834
  snapshots.push({ path: b.path, prevContent: null });
3792
3835
  continue;
3793
3836
  }
3794
3837
  try {
3795
- snapshots.push({ path: b.path, prevContent: readFileSync4(abs, "utf8") });
3838
+ snapshots.push({ path: b.path, prevContent: readFileSync5(abs, "utf8") });
3796
3839
  } catch {
3797
3840
  snapshots.push({ path: b.path, prevContent: null });
3798
3841
  }
@@ -3812,7 +3855,7 @@ function restoreSnapshots(snapshots, rootDir) {
3812
3855
  }
3813
3856
  try {
3814
3857
  if (snap.prevContent === null) {
3815
- if (existsSync2(abs)) unlinkSync2(abs);
3858
+ if (existsSync3(abs)) unlinkSync2(abs);
3816
3859
  return {
3817
3860
  path: snap.path,
3818
3861
  status: "applied",
@@ -3835,8 +3878,8 @@ function sep() {
3835
3878
  }
3836
3879
 
3837
3880
  // src/code/prompt.ts
3838
- import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
3839
- import { join as join3 } from "path";
3881
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
3882
+ import { join as join4 } from "path";
3840
3883
  var 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.
3841
3884
 
3842
3885
  # When to edit vs. when to explore
@@ -3885,18 +3928,19 @@ Rules:
3885
3928
  - If you need to explore first (list / grep / read), do it with tool calls before writing any prose \u2014 silence while exploring is fine.
3886
3929
  `;
3887
3930
  function codeSystemPrompt(rootDir) {
3888
- const gitignorePath = join3(rootDir, ".gitignore");
3889
- if (!existsSync3(gitignorePath)) return CODE_SYSTEM_PROMPT;
3931
+ const withMemory = applyProjectMemory(CODE_SYSTEM_PROMPT, rootDir);
3932
+ const gitignorePath = join4(rootDir, ".gitignore");
3933
+ if (!existsSync4(gitignorePath)) return withMemory;
3890
3934
  let content;
3891
3935
  try {
3892
- content = readFileSync5(gitignorePath, "utf8");
3936
+ content = readFileSync6(gitignorePath, "utf8");
3893
3937
  } catch {
3894
- return CODE_SYSTEM_PROMPT;
3938
+ return withMemory;
3895
3939
  }
3896
3940
  const MAX = 2e3;
3897
3941
  const truncated = content.length > MAX ? `${content.slice(0, MAX)}
3898
3942
  \u2026 (truncated ${content.length - MAX} chars)` : content;
3899
- return `${CODE_SYSTEM_PROMPT}
3943
+ return `${withMemory}
3900
3944
 
3901
3945
  # Project .gitignore
3902
3946
 
@@ -3909,15 +3953,15 @@ ${truncated}
3909
3953
  }
3910
3954
 
3911
3955
  // src/config.ts
3912
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
3956
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
3913
3957
  import { homedir as homedir2 } from "os";
3914
- import { dirname as dirname4, join as join4 } from "path";
3958
+ import { dirname as dirname4, join as join5 } from "path";
3915
3959
  function defaultConfigPath() {
3916
- return join4(homedir2(), ".reasonix", "config.json");
3960
+ return join5(homedir2(), ".reasonix", "config.json");
3917
3961
  }
3918
3962
  function readConfig(path = defaultConfigPath()) {
3919
3963
  try {
3920
- const raw = readFileSync6(path, "utf8");
3964
+ const raw = readFileSync7(path, "utf8");
3921
3965
  const parsed = JSON.parse(raw);
3922
3966
  if (parsed && typeof parsed === "object") return parsed;
3923
3967
  } catch {
@@ -3952,7 +3996,7 @@ function redactKey(key) {
3952
3996
  }
3953
3997
 
3954
3998
  // src/index.ts
3955
- var VERSION = "0.4.16";
3999
+ var VERSION = "0.4.17";
3956
4000
  export {
3957
4001
  AppendOnlyLog,
3958
4002
  CODE_SYSTEM_PROMPT,
@@ -3963,6 +4007,8 @@ export {
3963
4007
  MCP_PROTOCOL_VERSION,
3964
4008
  McpClient,
3965
4009
  NeedsConfirmationError,
4010
+ PROJECT_MEMORY_FILE,
4011
+ PROJECT_MEMORY_MAX_CHARS,
3966
4012
  SessionStats,
3967
4013
  SseTransport,
3968
4014
  StdioTransport,
@@ -3977,6 +4023,7 @@ export {
3977
4023
  appendSessionMessage,
3978
4024
  applyEditBlock,
3979
4025
  applyEditBlocks,
4026
+ applyProjectMemory,
3980
4027
  bridgeMcpTools,
3981
4028
  claudeEquivalentCost,
3982
4029
  codeSystemPrompt,
@@ -4006,6 +4053,7 @@ export {
4006
4053
  loadApiKey,
4007
4054
  loadDotenv,
4008
4055
  loadSessionMessages,
4056
+ memoryEnabled,
4009
4057
  nestArguments,
4010
4058
  openTranscriptFile,
4011
4059
  outputCostUsd,
@@ -4014,6 +4062,7 @@ export {
4014
4062
  parseMojeekResults,
4015
4063
  parseTranscript,
4016
4064
  readConfig,
4065
+ readProjectMemory,
4017
4066
  readTranscript,
4018
4067
  recordFromLoopEvent,
4019
4068
  redactKey,