mindweave 3.4.1 → 3.4.2

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.
Files changed (72) hide show
  1. package/README.md +26 -3
  2. package/dist/cli/App.js +52 -8
  3. package/dist/cli/App.js.map +1 -1
  4. package/dist/cli/commands.js +1 -0
  5. package/dist/cli/commands.js.map +1 -1
  6. package/dist/cli/components/ToolLine.js +5 -3
  7. package/dist/cli/components/ToolLine.js.map +1 -1
  8. package/dist/cli/components/WorkGroup.js +82 -64
  9. package/dist/cli/components/WorkGroup.js.map +1 -1
  10. package/dist/cli/startupArgs.js +24 -4
  11. package/dist/cli/startupArgs.js.map +1 -1
  12. package/dist/cli/transcript.js +14 -0
  13. package/dist/cli/transcript.js.map +1 -1
  14. package/dist/cli/workLines.js +40 -0
  15. package/dist/cli/workLines.js.map +1 -1
  16. package/dist/core/turnRunner.js +35 -23
  17. package/dist/core/turnRunner.js.map +1 -1
  18. package/dist/dynamo/contextWindow.js +28 -36
  19. package/dist/dynamo/contextWindow.js.map +1 -1
  20. package/dist/dynamo/engine.js +51 -9
  21. package/dist/dynamo/engine.js.map +1 -1
  22. package/dist/dynamo/guard.js +1 -1
  23. package/dist/dynamo/guard.js.map +1 -1
  24. package/dist/dynamo/prompt.js +1 -1
  25. package/dist/headless/args.js +82 -0
  26. package/dist/headless/args.js.map +1 -0
  27. package/dist/headless/main.js +87 -0
  28. package/dist/headless/main.js.map +1 -0
  29. package/dist/headless/output.js +150 -0
  30. package/dist/headless/output.js.map +1 -0
  31. package/dist/headless/run.js +188 -0
  32. package/dist/headless/run.js.map +1 -0
  33. package/dist/index.js +8 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/main.js +5 -0
  36. package/dist/main.js.map +1 -1
  37. package/dist/memory/compaction.js +94 -2
  38. package/dist/memory/compaction.js.map +1 -1
  39. package/dist/memory/rewind.js +10 -6
  40. package/dist/memory/rewind.js.map +1 -1
  41. package/dist/memory/session.js +2 -2
  42. package/dist/memory/session.js.map +1 -1
  43. package/dist/memory/sessionMemory.js +270 -69
  44. package/dist/memory/sessionMemory.js.map +1 -1
  45. package/dist/memory/types.js.map +1 -1
  46. package/dist/project/context.js +34 -6
  47. package/dist/project/context.js.map +1 -1
  48. package/dist/tools/coauthor.js +154 -0
  49. package/dist/tools/coauthor.js.map +1 -0
  50. package/dist/tools/codeIntel.js +38 -10
  51. package/dist/tools/codeIntel.js.map +1 -1
  52. package/dist/tools/docOutline.js +250 -0
  53. package/dist/tools/docOutline.js.map +1 -0
  54. package/dist/tools/editRipple.js +29 -2
  55. package/dist/tools/editRipple.js.map +1 -1
  56. package/dist/tools/emptyResult.js +24 -0
  57. package/dist/tools/emptyResult.js.map +1 -0
  58. package/dist/tools/glob.js +11 -2
  59. package/dist/tools/glob.js.map +1 -1
  60. package/dist/tools/grep.js +4 -3
  61. package/dist/tools/grep.js.map +1 -1
  62. package/dist/tools/missingPath.js +90 -0
  63. package/dist/tools/missingPath.js.map +1 -0
  64. package/dist/tools/readFile.js +3 -1
  65. package/dist/tools/readFile.js.map +1 -1
  66. package/dist/tools/rebuildDetail.js +9 -5
  67. package/dist/tools/rebuildDetail.js.map +1 -1
  68. package/dist/tools/runCommand.js +5 -1
  69. package/dist/tools/runCommand.js.map +1 -1
  70. package/dist/tools/webSearch.js +3 -1
  71. package/dist/tools/webSearch.js.map +1 -1
  72. package/package.json +2 -2
@@ -246,44 +246,31 @@ export const COLD_COMPACT_MIN_IDLE_MS = 15 * 60 * 1000;
246
246
  /** After a compaction for a cold cache, wait this long before doing it again. */
247
247
  export const COLD_COMPACT_COOLDOWN_MS = 30 * 60 * 1000;
248
248
  /**
249
- * Fraction of the prefix a clear must reclaim to pay for the cache rewrite it causes.
249
+ * How many further calls a clear is assumed to keep paying off for.
250
250
  *
251
- * Clearing an old tool body is not free on a warm cache: it changes the cached prefix,
252
- * so the next request rewrites what remains at 1.25x instead of reading it at 0.1x.
253
- * Writing out the break-even, with P the prefix and R what the clear reclaims:
251
+ * Clearing an old tool body or image is not free on a warm cache: the provider holds the
252
+ * conversation as a prefix, a change inside it invalidates everything after the change, and
253
+ * the next request writes that tail again at the full input price (plus a write premium, where
254
+ * the provider charges one) where it would have read it at the cached price. Written out, with
255
+ * T the tokens from the first changed message to the end, R the tokens the clear reclaims, h the
256
+ * cached read price and w the write price:
254
257
  *
255
- * keep for N steps = N * 0.1P
256
- * clear, then N-1 = 1.25(P-R) + (N-1) * 0.1(P-R)
257
- * break-even at N = 11.5 * (P - R) / R
258
+ * paid once, on the next call = T * (w - h)
259
+ * saved on every later call = R * h
260
+ * worth it when R * h * horizon >= T * (w - h)
258
261
  *
259
- * which is brutal at small R: reclaiming 10% of a 40K prefix needs ~103 further steps
260
- * to pay off, 50% needs ~12, and 75% needs ~4. A turn does not have 103 steps. So a
261
- * clear that frees a little is not a small win, it is a real loss, and the only clears
262
- * worth making on a warm cache are the big ones.
262
+ * The prices decide it, and they differ by an order of magnitude between providers: a cached read
263
+ * costs 10% of input on one, 2% on another, 20% on a third. At 2% the saving per call is so
264
+ * small that a clear which rewrites a 25K tail to reclaim 26K needs about 47 further calls to
265
+ * break even; at 10% the same clear needs about 11. The same clear is right on one provider and
266
+ * a loss on another, which a fixed fraction of the prefix could not tell apart.
263
267
  *
264
- * 0.35 sits where the break-even (~21 steps) is still optimistic but no longer absurd,
265
- * and it is deliberately a FRACTION rather than a token count: the thing being paid for
266
- * is proportional to the prefix, so the threshold has to be too.
267
- *
268
- * MEASURED against the derivation above (61 real sessions, 2026-09-16), and it is too
269
- * pessimistic. Do not tighten this on the strength of the arithmetic:
270
- *
271
- * - A clear does not rewrite the whole remaining prefix, only what follows the first
272
- * cleared result. Real clears cost 6-12K extra uncached tokens on a ~45K prompt,
273
- * not the ~43K that `1.25(P-R)` charges.
274
- * - Sessions run far past one turn. Clears were followed by 37-82 more calls on
275
- * average, so savings keep accruing long after the break-even horizon of one turn.
276
- * - The 0.1 / 1.25 ratios are one provider's. DeepSeek reads cache at 2% of input
277
- * with no write premium; GLM Flash at 20%.
278
- *
279
- * Net of cache rewrites AND the re-reads clearing causes (about 18% of file reads re-read
280
- * something that had been cleared), clearing saved 4.3% of spend on DeepSeek V4 Flash and
281
- * 10.3% on GLM 5.3 Flash, and 28 of 32 individual clears paid for themselves, including
282
- * most below this threshold. The policy is left as is because it is already net positive
283
- * and loosening it trades a few percent of saving for more re-reads, which is not yet
284
- * measured.
268
+ * A long run is 30 to 80 calls after a clear, so 40 is where it is still a fair bet. Past it
269
+ * the clear is declined and the context is allowed to fill: carrying the extra tokens is a
270
+ * cached read, cheap, and the clear is made anyway when the cache goes cold or the autocompact
271
+ * bar is close, both of which cost nothing extra.
285
272
  */
286
- const CLEAR_WORTH_FRACTION = 0.35;
273
+ export const CLEAR_HORIZON_STEPS = 40;
287
274
  /**
288
275
  * Whether a proposed microcompaction should actually be committed.
289
276
  *
@@ -293,8 +280,8 @@ const CLEAR_WORTH_FRACTION = 0.35;
293
280
  * the rewrite is free. Any reclaim at all is profit.
294
281
  * - `urgent`: context is close enough to the autocompact bar that FITTING matters more
295
282
  * than cost. A request that does not fit cannot be sent at any price.
296
- * - otherwise: only when the clear reclaims enough of the prefix to out-earn the cache
297
- * write it forces, per the arithmetic above.
283
+ * - otherwise: only when what it saves over the next calls out-earns the cache rewrite
284
+ * it forces, at this provider's prices (see `CLEAR_HORIZON_STEPS`).
298
285
  *
299
286
  * Pure, because the alternative is discovering the economics were wrong from a bill.
300
287
  */
@@ -308,6 +295,11 @@ export function clearIsWorthIt(opts) {
308
295
  // expensive summarization, and a prompt that overflows is not a cost question.
309
296
  if (opts.before >= opts.autoBar * 0.9)
310
297
  return true;
311
- return reclaimed >= opts.before * CLEAR_WORTH_FRACTION;
298
+ const write = opts.price.cacheWrite ?? opts.price.cacheMiss;
299
+ const paidOnce = opts.rewritten * Math.max(0, write - opts.price.cacheHit);
300
+ // A provider that charges the same for a hit as for a miss has nothing to lose by a rewrite.
301
+ if (paidOnce <= 0)
302
+ return true;
303
+ return reclaimed * opts.price.cacheHit * (opts.horizon ?? CLEAR_HORIZON_STEPS) >= paidOnce;
312
304
  }
313
305
  //# sourceMappingURL=contextWindow.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"contextWindow.js","sourceRoot":"","sources":["../../src/dynamo/contextWindow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"contextWindow.js","sourceRoot":"","sources":["../../src/dynamo/contextWindow.ts"],"names":[],"mappings":"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"}
@@ -53,10 +53,10 @@ import { directoryNotesFor } from "../memory/projectNotes.js";
53
53
  import { rippleCheck } from "../tools/editRipple.js";
54
54
  import { fullReadPaths, visibleReadRanges } from "../memory/presence.js";
55
55
  import { RESTORE_MAX_TOKENS_PER_FILE, renderRestored, restoreBudgetFor, selectForRestore, } from "../memory/restore.js";
56
- import { KEEP_LAST_N, KEEP_LAST_N_BOUNDARY, CLEARED_STUB, summaryRequest, SUMMARY_SYSTEM_PROMPT, dropOldestRounds, estimateEntriesTokens, estimateTokens, estimateTokensForChars, formatTranscriptForSummary, isContinuation, microcompact, spliceSummary, summaryForWire, summaryIsHistory, usableSummary, } from "../memory/compaction.js";
56
+ import { KEEP_LAST_N, KEEP_LAST_N_BOUNDARY, CLEARED_STUB, summaryRequest, SUMMARY_SYSTEM_PROMPT, dropOldestRounds, estimateEntriesTokens, estimateTokens, estimateTokensForChars, formatTranscriptForSummary, isContinuation, isRecapStub, microcompact, RECAP_STUB_NUDGE, spliceSummary, summaryForWire, summaryWireIsHistory, hasUnsettledHistory, settleSummaries, firstChangedEntry, SUMMARY_HISTORY_NOTE, usableSummary, } from "../memory/compaction.js";
57
57
  import { MAX_PARALLEL_CALLS, partitionCalls, runLimited } from "./toolBatches.js";
58
58
  import { loadPlanArtifact, completePlanArtifact, renderPlanBlock, planDivergenceStop } from "./planArtifact.js";
59
- import { autoCompactThreshold, microCompactThreshold, cacheLikelyCold, cacheStatus, cacheLifetimeFor, cacheIsDiscounted, shouldCompactForCold, coldCompactionLine, clearIsWorthIt, measuredOverhead, sharpContextWindow, } from "./contextWindow.js";
59
+ import { autoCompactThreshold, microCompactThreshold, cacheLikelyCold, isCold, cacheStatus, cacheLifetimeFor, cacheIsDiscounted, shouldCompactForCold, coldCompactionLine, clearIsWorthIt, measuredOverhead, sharpContextWindow, } from "./contextWindow.js";
60
60
  import { todoReminderDue, todoReminderText } from "./todoReminder.js";
61
61
  import { refreshSessionMemory, sessionMemoryDue, settleSessionMemory } from "../memory/sessionMemory.js";
62
62
  import { compactFromSessionMemory } from "../memory/sessionMemoryCompact.js";
@@ -187,8 +187,12 @@ directoryNotes = [],
187
187
  /** Where the previous turn left the shell, when this turn started back at the root. */
188
188
  cwdResetFrom = "",
189
189
  /** A running Marathon's standing block (see marathonPrompt.ts), or "". */
190
- marathon = "") {
190
+ marathon = "",
191
+ /** What stands in for rewriting a compaction summary the user has written past (see SUMMARY_HISTORY_NOTE), or "". */
192
+ summaryNote = "") {
191
193
  const parts = [];
194
+ if (summaryNote)
195
+ parts.push(summaryNote);
192
196
  // Each turn starts at the project root, and a model that `cd`-ed into a subfolder last
193
197
  // turn does not know that unless it is told. It was not, and a real session ran
194
198
  // `cargo run` from the root expecting the subfolder: "could not find Cargo.toml".
@@ -540,7 +544,7 @@ directoryNotes = []) {
540
544
  // A summary is sent as background once the user has written after it, so its
541
545
  // hand-off "next step" cannot keep overriding what they actually asked.
542
546
  const said = e.role === "summary"
543
- ? summaryForWire(e.content, summaryIsHistory(e, session.transcript.slice(i + 1)))
547
+ ? summaryForWire(e.content, summaryWireIsHistory(e, session.transcript.slice(i + 1)))
544
548
  : e.arrival ? arrivalNote(e.arrival, e.content) : e.content;
545
549
  if (refs && refs.length > 0) {
546
550
  const images = [];
@@ -631,7 +635,7 @@ directoryNotes = []) {
631
635
  // "keep working toward the goal" would stop being an independent check.
632
636
  session.marathon?.status === "running" && (session.toolContext.subagentDepth ?? 0) === 0
633
637
  ? marathonBlock(session.marathon.goal, session.marathon.turnsSpent === 0 ? "plan" : "run")
634
- : ""),
638
+ : "", hasUnsettledHistory(session.transcript) ? SUMMARY_HISTORY_NOTE : ""),
635
639
  tools,
636
640
  model: session.modelConfig,
637
641
  sessionId: session.id,
@@ -1287,6 +1291,7 @@ async function respondTurn(session, options = {}) {
1287
1291
  // shorter than it was.
1288
1292
  session.lastCallAt = Date.now();
1289
1293
  (session.lastCallByModel ??= {})[session.modelConfig.model] = session.lastCallAt;
1294
+ session.lastRequest = { request, sent: session.transcript.slice(0, request.messages.length) };
1290
1295
  result = await streamModel(request, options);
1291
1296
  }
1292
1297
  catch (error) {
@@ -1409,10 +1414,11 @@ async function respondTurn(session, options = {}) {
1409
1414
  // what the user sees. ONE retry — a gate that can fire twice is a loop.
1410
1415
  if (!replyRegated) {
1411
1416
  const fault = replyFault(content, mutatedThisTurn);
1412
- if (fault) {
1417
+ const copiedStub = isRecapStub(content);
1418
+ if (fault || copiedStub) {
1413
1419
  replyRegated = true;
1414
1420
  overlongReplyAt = session.transcript.length - 1; // the draft pushed just above
1415
- session.transcript.push({ role: "user", content: replyRewrite(fault), synthetic: true });
1421
+ session.transcript.push({ role: "user", content: fault ? replyRewrite(fault) : RECAP_STUB_NUDGE, synthetic: true });
1416
1422
  // The draft has been streaming into the UI's buffer, unrendered. Drop it, or
1417
1423
  // the rewrite would append to it and the user would read both.
1418
1424
  options.onEvent?.({ type: "replyReset" });
@@ -2473,7 +2479,7 @@ async function sweepSessionMemory(session, options, mode) {
2473
2479
  options.onEvent?.({ type: "usage", ...usage });
2474
2480
  countUsage(session, usage, options);
2475
2481
  recordAuxCall(session, usage, "notes");
2476
- }).then(async (wrote) => {
2482
+ }, (unseenTokens) => sharedNotesPrefix(session, unseenTokens)).then(async (wrote) => {
2477
2483
  if (wrote)
2478
2484
  await options.persist?.(); // durable: the notes sidecar is written by the persister
2479
2485
  return wrote;
@@ -2483,6 +2489,35 @@ async function sweepSessionMemory(session, options, mode) {
2483
2489
  else
2484
2490
  void run.catch(() => { });
2485
2491
  }
2492
+ /**
2493
+ * The main request the notes writer may build on, or null when it should send its own.
2494
+ *
2495
+ * Building on it means the provider reads the whole conversation from the cache the last call just
2496
+ * filled, at the cached price, instead of being sent the unseen part at full price. That is only
2497
+ * cheaper while the copy is there (this model was called within its cache lifetime), the request still
2498
+ * describes the transcript (it ended on an entry that is still in the same place), and the cached read
2499
+ * of the whole prefix costs less than the full-price read of what is new.
2500
+ */
2501
+ async function sharedNotesPrefix(session, unseenTokens) {
2502
+ const sent = session.lastRequest;
2503
+ const model = session.modelConfig.model;
2504
+ if (!sent || sent.request.model?.model !== model || session.transcript.length === 0)
2505
+ return null;
2506
+ // The provider holds the beginning of the conversation as it was sent. It only counts as the beginning
2507
+ // of this one if nothing in it has been rewritten since (a clear, a compaction, a settled summary).
2508
+ if (sent.sent.length > session.transcript.length || sent.sent.some((e, i) => e !== session.transcript[i]))
2509
+ return null;
2510
+ const status = cacheStatus({ now: Date.now(), model, lastByModel: session.lastCallByModel, ttlMs: cacheLifetimeFor(model) });
2511
+ if (status !== "warm" || !cacheIsDiscounted(model))
2512
+ return null;
2513
+ const price = manifestForModel(model).price(model);
2514
+ const prefixTokens = estimateEntriesTokens(sent.sent) + (session.contextOverhead?.model === model ? session.contextOverhead.tokens : 0);
2515
+ if (prefixTokens * price.cacheHit > unseenTokens * price.cacheMiss)
2516
+ return null;
2517
+ // Same system prompt, tools and model as the main request; the conversation as it is now.
2518
+ const { messages } = buildRequest(session, [], await loadImagePayloads(session));
2519
+ return { request: { ...sent.request, messages }, entries: session.transcript.length, last: session.transcript[session.transcript.length - 1] };
2520
+ }
2486
2521
  /**
2487
2522
  * How much of the context window is in use, in tokens.
2488
2523
  *
@@ -2590,11 +2625,18 @@ async function maybeCompact(session, options) {
2590
2625
  const proposed = microcompact(session.transcript).entries;
2591
2626
  const before = estimateEntriesTokens(session.transcript);
2592
2627
  const after = estimateEntriesTokens(proposed);
2593
- if (clearIsWorthIt({ before, after, cold, autoBar })) {
2628
+ // What the provider would have to write again: everything from the first message the clear changes.
2629
+ const rewritten = estimateEntriesTokens(proposed.slice(firstChangedEntry(session.transcript, proposed)));
2630
+ if (clearIsWorthIt({ before, after, cold, autoBar, rewritten, price: manifestForModel(model).price(model) })) {
2631
+ // The prefix is being rewritten anyway, so a summary the user has written past is settled in the same move.
2594
2632
  session.transcript = keepClearedOriginals(session, proposed);
2633
+ session.transcript = settleSummaries(session.transcript);
2595
2634
  // Silent by design — trimming stale context is background machinery.
2596
2635
  }
2597
2636
  }
2637
+ // With the provider's copy gone there is nothing to lose by it either, clear or no clear.
2638
+ if (isCold(status))
2639
+ session.transcript = settleSummaries(session.transcript);
2598
2640
  // The history is about to be sent at full price whatever it contains, so this is the cheapest
2599
2641
  // moment to shrink it, and the session notes make that free: no model call. Only the top-level
2600
2642
  // agent; a sub-agent starts with no cache of its own and its work is short.