reasonix 0.3.0-alpha.6 → 0.3.1
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/dist/cli/index.js +126 -12
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +92 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -244,6 +244,14 @@ declare class AppendOnlyLog {
|
|
|
244
244
|
private _entries;
|
|
245
245
|
append(message: ChatMessage): void;
|
|
246
246
|
extend(messages: ChatMessage[]): void;
|
|
247
|
+
/**
|
|
248
|
+
* Bulk-replace entries. Intentionally named to be hard to reach for —
|
|
249
|
+
* this is the one mutation path that breaks the log's append-only
|
|
250
|
+
* spirit, reserved for compaction flows (`/compact`) and recovery
|
|
251
|
+
* where the caller has consciously decided to drop old history. Any
|
|
252
|
+
* other use is almost certainly wrong; append() is what you want.
|
|
253
|
+
*/
|
|
254
|
+
compactInPlace(replacement: ChatMessage[]): void;
|
|
247
255
|
get entries(): readonly ChatMessage[];
|
|
248
256
|
toMessages(): ChatMessage[];
|
|
249
257
|
get length(): number;
|
|
@@ -379,6 +387,13 @@ interface SessionSummary {
|
|
|
379
387
|
claudeEquivalentUsd: number;
|
|
380
388
|
savingsVsClaudePct: number;
|
|
381
389
|
cacheHitRatio: number;
|
|
390
|
+
/**
|
|
391
|
+
* Most recent turn's prompt-token count. Used by the TUI's context
|
|
392
|
+
* gauge: we can't know the next call's cost without making it, but
|
|
393
|
+
* the last turn's prompt tokens is the floor (next call is last
|
|
394
|
+
* prompt + user delta + any new tool outputs).
|
|
395
|
+
*/
|
|
396
|
+
lastPromptTokens: number;
|
|
382
397
|
}
|
|
383
398
|
declare class SessionStats {
|
|
384
399
|
readonly turns: TurnStats[];
|
|
@@ -514,6 +529,21 @@ declare class CacheFirstLoop {
|
|
|
514
529
|
private _turn;
|
|
515
530
|
private _streamPreference;
|
|
516
531
|
constructor(opts: CacheFirstLoopOptions);
|
|
532
|
+
/**
|
|
533
|
+
* Shrink the log by re-truncating oversized tool results to a tighter
|
|
534
|
+
* cap, and persist the result back to disk so the next launch doesn't
|
|
535
|
+
* re-inherit a fat session file. Returns a summary the TUI can
|
|
536
|
+
* display.
|
|
537
|
+
*
|
|
538
|
+
* Only tool-role messages are touched (same rationale as
|
|
539
|
+
* {@link healLoadedMessages}). User and assistant messages carry
|
|
540
|
+
* authored intent we can't mechanically shrink without losing
|
|
541
|
+
* meaning.
|
|
542
|
+
*/
|
|
543
|
+
compact(tightCapChars?: number): {
|
|
544
|
+
healedCount: number;
|
|
545
|
+
charsSaved: number;
|
|
546
|
+
};
|
|
517
547
|
private appendAndPersist;
|
|
518
548
|
/**
|
|
519
549
|
* Reconfigure model/harvest/branch/stream mid-session. The loop's log,
|
|
@@ -524,6 +554,7 @@ declare class CacheFirstLoop {
|
|
|
524
554
|
configure(opts: ReconfigurableOptions): void;
|
|
525
555
|
private buildMessages;
|
|
526
556
|
step(userInput: string): AsyncGenerator<LoopEvent>;
|
|
557
|
+
private forceSummaryAfterIterLimit;
|
|
527
558
|
run(userInput: string, onEvent?: (ev: LoopEvent) => void): Promise<string>;
|
|
528
559
|
private assistantMessage;
|
|
529
560
|
}
|
|
@@ -1246,6 +1277,6 @@ declare function redactKey(key: string): string;
|
|
|
1246
1277
|
|
|
1247
1278
|
/** Reasonix — DeepSeek-native agent framework. Library entry point. */
|
|
1248
1279
|
|
|
1249
|
-
declare const VERSION = "0.3.
|
|
1280
|
+
declare const VERSION = "0.3.1";
|
|
1250
1281
|
|
|
1251
1282
|
export { AppendOnlyLog, type BranchOptions, type BranchProgress, type BranchResult, type BranchSample, type BranchSelector, type BranchSummary, type BridgeOptions, type BridgeResult, 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 EventRole, type FlattenDecision, type FlattenOptions, type HarvestOptions, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, type ListToolsResult, type LoopEvent, MCP_PROTOCOL_VERSION, McpClient, type McpClientOptions, type McpContentBlock, type McpSpec, type McpTool, type McpToolSchema, type McpTransport, type ReadTranscriptResult, type ReasonixConfig, type ReconfigurableOptions, type RepairReport, type ReplayStats, type RetryInfo, type RetryOptions, type Role, type ScavengeOptions, type ScavengeResult, type SessionInfo, SessionStats, type SessionSummary, type SseMcpSpec, SseTransport, type SseTransportOptions, type StdioMcpSpec, StdioTransport, type StdioTransportOptions, StormBreaker, type StreamChunk, type ToolCall, 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, aggregateBranchUsage, analyzeSchema, appendSessionMessage, bridgeMcpTools, claudeEquivalentCost, computeReplayStats, costUsd, defaultConfigPath, defaultSelector, deleteSession, diffTranscripts, emptyPlanState, fetchWithRetry, flattenMcpResult, flattenSchema, formatLoopError, harvest, healLoadedMessages, isJsonRpcError, isPlanStateEmpty, isPlausibleKey, listSessions, loadApiKey, loadDotenv, loadSessionMessages, nestArguments, openTranscriptFile, parseMcpSpec, parseTranscript, readConfig, readTranscript, recordFromLoopEvent, redactKey, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, runBranches, sanitizeName as sanitizeSessionName, saveApiKey, scavengeToolCalls, sessionPath, sessionsDir, similarity, truncateForModel, writeConfig, writeMeta, writeRecord };
|
package/dist/index.js
CHANGED
|
@@ -636,6 +636,16 @@ var AppendOnlyLog = class {
|
|
|
636
636
|
extend(messages) {
|
|
637
637
|
for (const m of messages) this.append(m);
|
|
638
638
|
}
|
|
639
|
+
/**
|
|
640
|
+
* Bulk-replace entries. Intentionally named to be hard to reach for —
|
|
641
|
+
* this is the one mutation path that breaks the log's append-only
|
|
642
|
+
* spirit, reserved for compaction flows (`/compact`) and recovery
|
|
643
|
+
* where the caller has consciously decided to drop old history. Any
|
|
644
|
+
* other use is almost certainly wrong; append() is what you want.
|
|
645
|
+
*/
|
|
646
|
+
compactInPlace(replacement) {
|
|
647
|
+
this._entries = [...replacement];
|
|
648
|
+
}
|
|
639
649
|
get entries() {
|
|
640
650
|
return this._entries;
|
|
641
651
|
}
|
|
@@ -914,7 +924,8 @@ import {
|
|
|
914
924
|
readFileSync,
|
|
915
925
|
readdirSync,
|
|
916
926
|
statSync,
|
|
917
|
-
unlinkSync
|
|
927
|
+
unlinkSync,
|
|
928
|
+
writeFileSync
|
|
918
929
|
} from "fs";
|
|
919
930
|
import { homedir } from "os";
|
|
920
931
|
import { dirname, join } from "path";
|
|
@@ -983,6 +994,17 @@ function deleteSession(name) {
|
|
|
983
994
|
return false;
|
|
984
995
|
}
|
|
985
996
|
}
|
|
997
|
+
function rewriteSession(name, messages) {
|
|
998
|
+
const path = sessionPath(name);
|
|
999
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
1000
|
+
const body = messages.map((m) => JSON.stringify(m)).join("\n");
|
|
1001
|
+
writeFileSync(path, body ? `${body}
|
|
1002
|
+
` : "", "utf8");
|
|
1003
|
+
try {
|
|
1004
|
+
chmodSync(path, 384);
|
|
1005
|
+
} catch {
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
986
1008
|
function countLines(path) {
|
|
987
1009
|
try {
|
|
988
1010
|
const raw = readFileSync(path, "utf8");
|
|
@@ -1041,12 +1063,14 @@ var SessionStats = class {
|
|
|
1041
1063
|
return denom > 0 ? hit / denom : 0;
|
|
1042
1064
|
}
|
|
1043
1065
|
summary() {
|
|
1066
|
+
const last = this.turns[this.turns.length - 1];
|
|
1044
1067
|
return {
|
|
1045
1068
|
turns: this.turns.length,
|
|
1046
1069
|
totalCostUsd: round(this.totalCost, 6),
|
|
1047
1070
|
claudeEquivalentUsd: round(this.totalClaudeEquivalent, 6),
|
|
1048
1071
|
savingsVsClaudePct: round(this.savingsVsClaude * 100, 2),
|
|
1049
|
-
cacheHitRatio: round(this.aggregateCacheHitRatio, 4)
|
|
1072
|
+
cacheHitRatio: round(this.aggregateCacheHitRatio, 4),
|
|
1073
|
+
lastPromptTokens: last?.usage.promptTokens ?? 0
|
|
1050
1074
|
};
|
|
1051
1075
|
}
|
|
1052
1076
|
};
|
|
@@ -1083,7 +1107,7 @@ var CacheFirstLoop = class {
|
|
|
1083
1107
|
this.prefix = opts.prefix;
|
|
1084
1108
|
this.tools = opts.tools ?? new ToolRegistry();
|
|
1085
1109
|
this.model = opts.model ?? "deepseek-chat";
|
|
1086
|
-
this.maxToolIters = opts.maxToolIters ??
|
|
1110
|
+
this.maxToolIters = opts.maxToolIters ?? 24;
|
|
1087
1111
|
if (typeof opts.branch === "number") {
|
|
1088
1112
|
this.branchOptions = { budget: opts.branch };
|
|
1089
1113
|
} else if (opts.branch && typeof opts.branch === "object") {
|
|
@@ -1118,6 +1142,33 @@ var CacheFirstLoop = class {
|
|
|
1118
1142
|
this.resumedMessageCount = 0;
|
|
1119
1143
|
}
|
|
1120
1144
|
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Shrink the log by re-truncating oversized tool results to a tighter
|
|
1147
|
+
* cap, and persist the result back to disk so the next launch doesn't
|
|
1148
|
+
* re-inherit a fat session file. Returns a summary the TUI can
|
|
1149
|
+
* display.
|
|
1150
|
+
*
|
|
1151
|
+
* Only tool-role messages are touched (same rationale as
|
|
1152
|
+
* {@link healLoadedMessages}). User and assistant messages carry
|
|
1153
|
+
* authored intent we can't mechanically shrink without losing
|
|
1154
|
+
* meaning.
|
|
1155
|
+
*/
|
|
1156
|
+
compact(tightCapChars = 4e3) {
|
|
1157
|
+
const before = this.log.toMessages();
|
|
1158
|
+
const { messages, healedCount, healedFrom } = healLoadedMessages(before, tightCapChars);
|
|
1159
|
+
const afterBytes = messages.filter((m) => m.role === "tool").reduce((s, m) => s + (typeof m.content === "string" ? m.content.length : 0), 0);
|
|
1160
|
+
const charsSaved = healedFrom - afterBytes;
|
|
1161
|
+
if (healedCount > 0) {
|
|
1162
|
+
this.log.compactInPlace(messages);
|
|
1163
|
+
if (this.sessionName) {
|
|
1164
|
+
try {
|
|
1165
|
+
rewriteSession(this.sessionName, messages);
|
|
1166
|
+
} catch {
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
return { healedCount, charsSaved };
|
|
1171
|
+
}
|
|
1121
1172
|
appendAndPersist(message) {
|
|
1122
1173
|
this.log.append(message);
|
|
1123
1174
|
if (this.sessionName) {
|
|
@@ -1355,7 +1406,38 @@ var CacheFirstLoop = class {
|
|
|
1355
1406
|
};
|
|
1356
1407
|
}
|
|
1357
1408
|
}
|
|
1358
|
-
yield
|
|
1409
|
+
yield* this.forceSummaryAfterIterLimit();
|
|
1410
|
+
}
|
|
1411
|
+
async *forceSummaryAfterIterLimit() {
|
|
1412
|
+
try {
|
|
1413
|
+
const messages = this.buildMessages(null);
|
|
1414
|
+
const resp = await this.client.chat({
|
|
1415
|
+
model: this.model,
|
|
1416
|
+
messages
|
|
1417
|
+
// no tools → model is forced to answer in text
|
|
1418
|
+
});
|
|
1419
|
+
const summary = resp.content?.trim() || "(model returned no text; try a narrower question or raise --max-tool-iters)";
|
|
1420
|
+
const annotated = `[tool-call budget (${this.maxToolIters}) reached \u2014 forcing summary from what I found]
|
|
1421
|
+
|
|
1422
|
+
${summary}`;
|
|
1423
|
+
const summaryStats = this.stats.record(this._turn, this.model, resp.usage ?? new Usage());
|
|
1424
|
+
this.appendAndPersist({ role: "assistant", content: summary });
|
|
1425
|
+
yield {
|
|
1426
|
+
turn: this._turn,
|
|
1427
|
+
role: "assistant_final",
|
|
1428
|
+
content: annotated,
|
|
1429
|
+
stats: summaryStats
|
|
1430
|
+
};
|
|
1431
|
+
yield { turn: this._turn, role: "done", content: summary };
|
|
1432
|
+
} catch (err) {
|
|
1433
|
+
yield {
|
|
1434
|
+
turn: this._turn,
|
|
1435
|
+
role: "error",
|
|
1436
|
+
content: "",
|
|
1437
|
+
error: `tool-call budget (${this.maxToolIters}) reached and the fallback summary call failed: ${err.message}. Run /clear and retry with a narrower question, or pass --max-tool-iters higher.`
|
|
1438
|
+
};
|
|
1439
|
+
yield { turn: this._turn, role: "done", content: "" };
|
|
1440
|
+
}
|
|
1359
1441
|
}
|
|
1360
1442
|
async run(userInput, onEvent) {
|
|
1361
1443
|
let final = "";
|
|
@@ -1578,12 +1660,14 @@ function summarizeTurns(turns) {
|
|
|
1578
1660
|
}
|
|
1579
1661
|
const cacheHitRatio = hit + miss > 0 ? hit / (hit + miss) : 0;
|
|
1580
1662
|
const savingsVsClaude = totalClaude > 0 ? 1 - totalCost / totalClaude : 0;
|
|
1663
|
+
const lastTurn = turns[turns.length - 1];
|
|
1581
1664
|
return {
|
|
1582
1665
|
turns: turns.length,
|
|
1583
1666
|
totalCostUsd: round2(totalCost, 6),
|
|
1584
1667
|
claudeEquivalentUsd: round2(totalClaude, 6),
|
|
1585
1668
|
savingsVsClaudePct: round2(savingsVsClaude * 100, 2),
|
|
1586
|
-
cacheHitRatio: round2(cacheHitRatio, 4)
|
|
1669
|
+
cacheHitRatio: round2(cacheHitRatio, 4),
|
|
1670
|
+
lastPromptTokens: lastTurn?.usage.promptTokens ?? 0
|
|
1587
1671
|
};
|
|
1588
1672
|
}
|
|
1589
1673
|
function round2(n, digits) {
|
|
@@ -2365,7 +2449,7 @@ function parseMcpSpec(input) {
|
|
|
2365
2449
|
}
|
|
2366
2450
|
|
|
2367
2451
|
// src/config.ts
|
|
2368
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
2452
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
2369
2453
|
import { homedir as homedir2 } from "os";
|
|
2370
2454
|
import { dirname as dirname2, join as join2 } from "path";
|
|
2371
2455
|
function defaultConfigPath() {
|
|
@@ -2382,7 +2466,7 @@ function readConfig(path = defaultConfigPath()) {
|
|
|
2382
2466
|
}
|
|
2383
2467
|
function writeConfig(cfg, path = defaultConfigPath()) {
|
|
2384
2468
|
mkdirSync2(dirname2(path), { recursive: true });
|
|
2385
|
-
|
|
2469
|
+
writeFileSync2(path, JSON.stringify(cfg, null, 2), "utf8");
|
|
2386
2470
|
try {
|
|
2387
2471
|
chmodSync2(path, 384);
|
|
2388
2472
|
} catch {
|
|
@@ -2408,7 +2492,7 @@ function redactKey(key) {
|
|
|
2408
2492
|
}
|
|
2409
2493
|
|
|
2410
2494
|
// src/index.ts
|
|
2411
|
-
var VERSION = "0.3.
|
|
2495
|
+
var VERSION = "0.3.1";
|
|
2412
2496
|
export {
|
|
2413
2497
|
AppendOnlyLog,
|
|
2414
2498
|
CacheFirstLoop,
|