@mindstudio-ai/remy 0.1.312 → 0.1.313

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.
@@ -122,8 +122,9 @@ declare class HeadlessSession {
122
122
  /** Apply queued tool block updates to state.messages. Safe to call any time. */
123
123
  private applyPendingBlockUpdates;
124
124
  /**
125
- * Forced compaction gate. If lastContextSize exceeds the threshold, compact
126
- * before letting the upcoming turn run. Coalesces with any in-flight
125
+ * Forced compaction gate. If lastContextSize exceeds the upcoming turn's
126
+ * model threshold (per-model — see ModelContextLimits in models/surfaces),
127
+ * compact before letting the turn run. Coalesces with any in-flight
127
128
  * compaction (e.g., one already started by /compact or a tool call). No
128
129
  * timeout — compaction takes as long as it takes.
129
130
  *
package/dist/headless.js CHANGED
@@ -486,37 +486,59 @@ var MODEL_SURFACES = {
486
486
  userPickable: false
487
487
  }
488
488
  };
489
+ var DEFAULT_SUGGEST_COMPACT_AT = 3e5;
490
+ var TEXT_MODELS = {
491
+ // Anthropic 1M-context, flat-priced.
492
+ "claude-5-opus": { forceCompactAt: 85e4 },
493
+ "claude-4-8-opus": { forceCompactAt: 85e4 },
494
+ "claude-4-7-opus": { forceCompactAt: 85e4 },
495
+ // Anthropic 1M-context with 2x long-context pricing above 200K input.
496
+ "claude-4-6-opus": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
497
+ "claude-4-6-sonnet": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
498
+ "claude-fable-5": { forceCompactAt: 85e4 },
499
+ "claude-fable-5-1": { forceCompactAt: 85e4 },
500
+ "claude-5-sonnet": { forceCompactAt: 85e4 },
501
+ // OpenAI gpt-5.5/5.6: ~1M window, but the usable input ceiling under
502
+ // `truncation: 'auto'` is ~794K (output + reasoning reserve), and all
503
+ // rates double above 272K input.
504
+ "gpt-5.5": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
505
+ "gpt-5.6-sol": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
506
+ "gpt-5.6-terra": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
507
+ "gpt-5.6-luna": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
508
+ // Google ~1M-context; only 3.1-pro is tiered (higher rates above 200K).
509
+ "gemini-3-pro": { forceCompactAt: 85e4 },
510
+ "gemini-3.1-pro": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
511
+ "gemini-3-flash": { forceCompactAt: 85e4 },
512
+ "gemini-3.5-flash": { forceCompactAt: 85e4 },
513
+ "gemini-3.7-flash": { forceCompactAt: 85e4 },
514
+ // 256K window; its 200K pricing tier sits above the gate, so no nudge.
515
+ "grok-build-0.1": { forceCompactAt: 18e4 },
516
+ "grok-4.5": { forceCompactAt: 4e5 },
517
+ // 500K window
518
+ "grok-4.6": { forceCompactAt: 4e5 },
519
+ // 500K window
520
+ "glm-5.2": { forceCompactAt: 85e4 },
521
+ "muse-spark-1.1": { forceCompactAt: 85e4 },
522
+ "kimi-k2-7-code": { forceCompactAt: 2e5 },
523
+ // 262K window
524
+ "kimi-k3": { forceCompactAt: 85e4 },
525
+ "deepseek-v4-flash-0731": { forceCompactAt: 85e4 },
526
+ "qwen3.8-2.4t-a95b-deepinfra": { forceCompactAt: 2e5 },
527
+ // 262K window
528
+ "qwen3.8-27b-deepinfra": { forceCompactAt: 2e5 },
529
+ // 262K window
530
+ "minimax-m3": { forceCompactAt: 42e4 }
531
+ // 524K window
532
+ };
533
+ var DEFAULT_CONTEXT_LIMITS = { forceCompactAt: 85e4 };
534
+ function getContextLimits(modelId) {
535
+ return TEXT_MODELS[modelId] ?? DEFAULT_CONTEXT_LIMITS;
536
+ }
537
+ function getSuggestCompactAt(modelId) {
538
+ return getContextLimits(modelId).suggestCompactAt ?? DEFAULT_SUGGEST_COMPACT_AT;
539
+ }
489
540
  var ALLOWED_MODELS_BY_TYPE = {
490
- text: [
491
- "claude-5-opus",
492
- "claude-4-8-opus",
493
- "claude-4-7-opus",
494
- "claude-4-6-opus",
495
- "claude-4-6-sonnet",
496
- "claude-fable-5",
497
- "claude-fable-5-1",
498
- "claude-5-sonnet",
499
- "gpt-5.5",
500
- "gpt-5.6-sol",
501
- "gpt-5.6-terra",
502
- "gpt-5.6-luna",
503
- "gemini-3-pro",
504
- "gemini-3.1-pro",
505
- "gemini-3-flash",
506
- "gemini-3.5-flash",
507
- "gemini-3.7-flash",
508
- "grok-build-0.1",
509
- "grok-4.5",
510
- "grok-4.6",
511
- "glm-5.2",
512
- "muse-spark-1.1",
513
- "kimi-k2-7-code",
514
- "kimi-k3",
515
- "deepseek-v4-flash-0731",
516
- "qwen3.8-2.4t-a95b-deepinfra",
517
- "qwen3.8-27b-deepinfra",
518
- "minimax-m3"
519
- ]
541
+ text: Object.keys(TEXT_MODELS)
520
542
  // vision: undefined — unconstrained
521
543
  // image_generation: undefined — unconstrained
522
544
  };
@@ -559,6 +581,11 @@ function getEffectiveModelSurfaces() {
559
581
  function resolveModel(surfaceId, models, fallback) {
560
582
  return models?.[surfaceId] ?? fallback ?? orgDefaultModels[surfaceId] ?? MODEL_SURFACES[surfaceId].default;
561
583
  }
584
+ function resolveParentModel(models, fallback, buildModel) {
585
+ const override = buildModel ? filterModelPicks({ parent: buildModel }).parent : void 0;
586
+ const baseline = resolveModel("parent", models, fallback);
587
+ return { baseline, effective: override ?? baseline };
588
+ }
562
589
 
563
590
  // src/orgContext.ts
564
591
  var log3 = createLogger("orgContext");
@@ -8616,6 +8643,12 @@ function classify(line, matches) {
8616
8643
  tailClean = SEPARATORS_ONLY.test(gap);
8617
8644
  }
8618
8645
  }
8646
+ function chipCase(label) {
8647
+ if (label.startsWith("`")) {
8648
+ return label;
8649
+ }
8650
+ return label.charAt(0).toUpperCase() + label.slice(1);
8651
+ }
8619
8652
  function parseSuggestions(raw) {
8620
8653
  if (!raw.includes(SUGGEST_MARKER)) {
8621
8654
  return { text: raw, suggestions: [] };
@@ -8645,7 +8678,7 @@ function parseSuggestions(raw) {
8645
8678
  const key = `${m.label}::${m.message}`;
8646
8679
  if (!seen.has(key)) {
8647
8680
  seen.add(key);
8648
- suggestions.push({ label: m.label, message: m.message });
8681
+ suggestions.push({ label: chipCase(m.label), message: m.message });
8649
8682
  }
8650
8683
  }
8651
8684
  let rebuilt = "";
@@ -8745,10 +8778,12 @@ async function runTurn(params) {
8745
8778
  onBackgroundComplete
8746
8779
  } = params;
8747
8780
  const tools2 = getToolDefinitions();
8748
- const buildModelOverride = buildModel ? filterModelPicks({ parent: buildModel }).parent : void 0;
8749
- const baseline = resolveModel("parent", state.models, model);
8750
- const parentModel = buildModelOverride ?? baseline;
8751
- const modelOverride = buildModelOverride && buildModelOverride !== baseline ? { from: baseline } : void 0;
8781
+ const { baseline, effective: parentModel } = resolveParentModel(
8782
+ state.models,
8783
+ model,
8784
+ buildModel
8785
+ );
8786
+ const modelOverride = parentModel !== baseline ? { from: baseline } : void 0;
8752
8787
  const totalAttachments = entries.reduce(
8753
8788
  (n, e) => n + (e.attachments?.length ?? 0),
8754
8789
  0
@@ -8756,7 +8791,7 @@ async function runTurn(params) {
8756
8791
  log15.info("Turn started", {
8757
8792
  requestId,
8758
8793
  model,
8759
- buildModel: buildModelOverride,
8794
+ buildModel: modelOverride ? parentModel : void 0,
8760
8795
  toolCount: tools2.length,
8761
8796
  ...entries.length > 1 && { entryCount: entries.length },
8762
8797
  ...totalAttachments > 0 && { attachmentCount: totalAttachments }
@@ -9591,12 +9626,13 @@ function loadPassiveResults() {
9591
9626
  }
9592
9627
  return [];
9593
9628
  }
9594
- function writeStats(stats, queue, passiveResults) {
9629
+ function writeStats(stats, queue, passiveResults, suggestCompactAt) {
9595
9630
  try {
9596
9631
  writeFileAtomicSync(
9597
9632
  STATS_FILE,
9598
9633
  JSON.stringify({
9599
9634
  ...stats,
9635
+ suggestCompactAt,
9600
9636
  queue,
9601
9637
  passiveResults
9602
9638
  })
@@ -9798,7 +9834,6 @@ var USER_FACING_TOOLS = /* @__PURE__ */ new Set([
9798
9834
  "confirmDestructiveAction",
9799
9835
  "presentPublishPlan"
9800
9836
  ]);
9801
- var FORCED_COMPACTION_THRESHOLD_TOKENS = 85e4;
9802
9837
  var HeadlessSession = class {
9803
9838
  // Configuration
9804
9839
  opts;
@@ -10071,7 +10106,15 @@ var HeadlessSession = class {
10071
10106
  /** Persist sessionStats + queue snapshot + passive pen to .remy-stats.json. */
10072
10107
  persistStats() {
10073
10108
  this.sessionStats.updatedAt = Date.now();
10074
- writeStats(this.sessionStats, this.queue.snapshot(), this.passivePen);
10109
+ const suggestCompactAt = getSuggestCompactAt(
10110
+ resolveParentModel(this.state.models, this.opts.model).effective
10111
+ );
10112
+ writeStats(
10113
+ this.sessionStats,
10114
+ this.queue.snapshot(),
10115
+ this.passivePen,
10116
+ suggestCompactAt
10117
+ );
10075
10118
  }
10076
10119
  //////////////////////////////////////////////////////////////////////////////
10077
10120
  // Background completions (tool-block mutation; message delivery via queue)
@@ -10101,8 +10144,9 @@ var HeadlessSession = class {
10101
10144
  saveSession(this.state);
10102
10145
  }
10103
10146
  /**
10104
- * Forced compaction gate. If lastContextSize exceeds the threshold, compact
10105
- * before letting the upcoming turn run. Coalesces with any in-flight
10147
+ * Forced compaction gate. If lastContextSize exceeds the upcoming turn's
10148
+ * model threshold (per-model — see ModelContextLimits in models/surfaces),
10149
+ * compact before letting the turn run. Coalesces with any in-flight
10106
10150
  * compaction (e.g., one already started by /compact or a tool call). No
10107
10151
  * timeout — compaction takes as long as it takes.
10108
10152
  *
@@ -10113,13 +10157,15 @@ var HeadlessSession = class {
10113
10157
  * On compaction failure we don't bail — the turn proceeds and surfaces any
10114
10158
  * downstream overflow through the existing "prompt is too long" path.
10115
10159
  */
10116
- async runForcedCompactionIfNeeded(requestId) {
10117
- if (this.sessionStats.lastContextSize <= FORCED_COMPACTION_THRESHOLD_TOKENS) {
10160
+ async runForcedCompactionIfNeeded(requestId, parentModel) {
10161
+ const threshold = getContextLimits(parentModel).forceCompactAt;
10162
+ if (this.sessionStats.lastContextSize <= threshold) {
10118
10163
  return;
10119
10164
  }
10120
10165
  log17.info("Forced compaction gate triggered", {
10121
10166
  contextSize: this.sessionStats.lastContextSize,
10122
- threshold: FORCED_COMPACTION_THRESHOLD_TOKENS,
10167
+ threshold,
10168
+ model: parentModel,
10123
10169
  requestId
10124
10170
  });
10125
10171
  try {
@@ -10601,7 +10647,12 @@ var HeadlessSession = class {
10601
10647
  }
10602
10648
  return steered;
10603
10649
  };
10604
- await this.runForcedCompactionIfNeeded(requestId);
10650
+ const parentModel = resolveParentModel(
10651
+ this.state.models,
10652
+ this.opts.model,
10653
+ params.buildModel
10654
+ ).effective;
10655
+ await this.runForcedCompactionIfNeeded(requestId, parentModel);
10605
10656
  try {
10606
10657
  await runTurn({
10607
10658
  state: this.state,
package/dist/index.js CHANGED
@@ -2111,6 +2111,12 @@ var init_compaction = __esm({
2111
2111
  });
2112
2112
 
2113
2113
  // src/models/surfaces.ts
2114
+ function getContextLimits(modelId) {
2115
+ return TEXT_MODELS[modelId] ?? DEFAULT_CONTEXT_LIMITS;
2116
+ }
2117
+ function getSuggestCompactAt(modelId) {
2118
+ return getContextLimits(modelId).suggestCompactAt ?? DEFAULT_SUGGEST_COMPACT_AT;
2119
+ }
2114
2120
  function setOrgDefaultModels(models) {
2115
2121
  orgDefaultModels = models;
2116
2122
  }
@@ -2149,7 +2155,12 @@ function getEffectiveModelSurfaces() {
2149
2155
  function resolveModel(surfaceId, models, fallback) {
2150
2156
  return models?.[surfaceId] ?? fallback ?? orgDefaultModels[surfaceId] ?? MODEL_SURFACES[surfaceId].default;
2151
2157
  }
2152
- var MODEL_SURFACES, ALLOWED_MODELS_BY_TYPE, orgDefaultModels;
2158
+ function resolveParentModel(models, fallback, buildModel) {
2159
+ const override = buildModel ? filterModelPicks({ parent: buildModel }).parent : void 0;
2160
+ const baseline = resolveModel("parent", models, fallback);
2161
+ return { baseline, effective: override ?? baseline };
2162
+ }
2163
+ var MODEL_SURFACES, DEFAULT_SUGGEST_COMPACT_AT, TEXT_MODELS, DEFAULT_CONTEXT_LIMITS, ALLOWED_MODELS_BY_TYPE, orgDefaultModels;
2153
2164
  var init_surfaces = __esm({
2154
2165
  "src/models/surfaces.ts"() {
2155
2166
  "use strict";
@@ -2241,37 +2252,53 @@ var init_surfaces = __esm({
2241
2252
  userPickable: false
2242
2253
  }
2243
2254
  };
2255
+ DEFAULT_SUGGEST_COMPACT_AT = 3e5;
2256
+ TEXT_MODELS = {
2257
+ // Anthropic 1M-context, flat-priced.
2258
+ "claude-5-opus": { forceCompactAt: 85e4 },
2259
+ "claude-4-8-opus": { forceCompactAt: 85e4 },
2260
+ "claude-4-7-opus": { forceCompactAt: 85e4 },
2261
+ // Anthropic 1M-context with 2x long-context pricing above 200K input.
2262
+ "claude-4-6-opus": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
2263
+ "claude-4-6-sonnet": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
2264
+ "claude-fable-5": { forceCompactAt: 85e4 },
2265
+ "claude-fable-5-1": { forceCompactAt: 85e4 },
2266
+ "claude-5-sonnet": { forceCompactAt: 85e4 },
2267
+ // OpenAI gpt-5.5/5.6: ~1M window, but the usable input ceiling under
2268
+ // `truncation: 'auto'` is ~794K (output + reasoning reserve), and all
2269
+ // rates double above 272K input.
2270
+ "gpt-5.5": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
2271
+ "gpt-5.6-sol": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
2272
+ "gpt-5.6-terra": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
2273
+ "gpt-5.6-luna": { forceCompactAt: 6e5, suggestCompactAt: 25e4 },
2274
+ // Google ~1M-context; only 3.1-pro is tiered (higher rates above 200K).
2275
+ "gemini-3-pro": { forceCompactAt: 85e4 },
2276
+ "gemini-3.1-pro": { forceCompactAt: 85e4, suggestCompactAt: 18e4 },
2277
+ "gemini-3-flash": { forceCompactAt: 85e4 },
2278
+ "gemini-3.5-flash": { forceCompactAt: 85e4 },
2279
+ "gemini-3.7-flash": { forceCompactAt: 85e4 },
2280
+ // 256K window; its 200K pricing tier sits above the gate, so no nudge.
2281
+ "grok-build-0.1": { forceCompactAt: 18e4 },
2282
+ "grok-4.5": { forceCompactAt: 4e5 },
2283
+ // 500K window
2284
+ "grok-4.6": { forceCompactAt: 4e5 },
2285
+ // 500K window
2286
+ "glm-5.2": { forceCompactAt: 85e4 },
2287
+ "muse-spark-1.1": { forceCompactAt: 85e4 },
2288
+ "kimi-k2-7-code": { forceCompactAt: 2e5 },
2289
+ // 262K window
2290
+ "kimi-k3": { forceCompactAt: 85e4 },
2291
+ "deepseek-v4-flash-0731": { forceCompactAt: 85e4 },
2292
+ "qwen3.8-2.4t-a95b-deepinfra": { forceCompactAt: 2e5 },
2293
+ // 262K window
2294
+ "qwen3.8-27b-deepinfra": { forceCompactAt: 2e5 },
2295
+ // 262K window
2296
+ "minimax-m3": { forceCompactAt: 42e4 }
2297
+ // 524K window
2298
+ };
2299
+ DEFAULT_CONTEXT_LIMITS = { forceCompactAt: 85e4 };
2244
2300
  ALLOWED_MODELS_BY_TYPE = {
2245
- text: [
2246
- "claude-5-opus",
2247
- "claude-4-8-opus",
2248
- "claude-4-7-opus",
2249
- "claude-4-6-opus",
2250
- "claude-4-6-sonnet",
2251
- "claude-fable-5",
2252
- "claude-fable-5-1",
2253
- "claude-5-sonnet",
2254
- "gpt-5.5",
2255
- "gpt-5.6-sol",
2256
- "gpt-5.6-terra",
2257
- "gpt-5.6-luna",
2258
- "gemini-3-pro",
2259
- "gemini-3.1-pro",
2260
- "gemini-3-flash",
2261
- "gemini-3.5-flash",
2262
- "gemini-3.7-flash",
2263
- "grok-build-0.1",
2264
- "grok-4.5",
2265
- "grok-4.6",
2266
- "glm-5.2",
2267
- "muse-spark-1.1",
2268
- "kimi-k2-7-code",
2269
- "kimi-k3",
2270
- "deepseek-v4-flash-0731",
2271
- "qwen3.8-2.4t-a95b-deepinfra",
2272
- "qwen3.8-27b-deepinfra",
2273
- "minimax-m3"
2274
- ]
2301
+ text: Object.keys(TEXT_MODELS)
2275
2302
  // vision: undefined — unconstrained
2276
2303
  // image_generation: undefined — unconstrained
2277
2304
  };
@@ -8921,6 +8948,12 @@ function classify(line, matches) {
8921
8948
  tailClean = SEPARATORS_ONLY.test(gap);
8922
8949
  }
8923
8950
  }
8951
+ function chipCase(label) {
8952
+ if (label.startsWith("`")) {
8953
+ return label;
8954
+ }
8955
+ return label.charAt(0).toUpperCase() + label.slice(1);
8956
+ }
8924
8957
  function parseSuggestions(raw) {
8925
8958
  if (!raw.includes(SUGGEST_MARKER)) {
8926
8959
  return { text: raw, suggestions: [] };
@@ -8950,7 +8983,7 @@ function parseSuggestions(raw) {
8950
8983
  const key = `${m.label}::${m.message}`;
8951
8984
  if (!seen.has(key)) {
8952
8985
  seen.add(key);
8953
- suggestions.push({ label: m.label, message: m.message });
8986
+ suggestions.push({ label: chipCase(m.label), message: m.message });
8954
8987
  }
8955
8988
  }
8956
8989
  let rebuilt = "";
@@ -9401,10 +9434,12 @@ async function runTurn(params) {
9401
9434
  onBackgroundComplete
9402
9435
  } = params;
9403
9436
  const tools2 = getToolDefinitions();
9404
- const buildModelOverride = buildModel ? filterModelPicks({ parent: buildModel }).parent : void 0;
9405
- const baseline = resolveModel("parent", state.models, model);
9406
- const parentModel = buildModelOverride ?? baseline;
9407
- const modelOverride = buildModelOverride && buildModelOverride !== baseline ? { from: baseline } : void 0;
9437
+ const { baseline, effective: parentModel } = resolveParentModel(
9438
+ state.models,
9439
+ model,
9440
+ buildModel
9441
+ );
9442
+ const modelOverride = parentModel !== baseline ? { from: baseline } : void 0;
9408
9443
  const totalAttachments = entries.reduce(
9409
9444
  (n, e) => n + (e.attachments?.length ?? 0),
9410
9445
  0
@@ -9412,7 +9447,7 @@ async function runTurn(params) {
9412
9447
  log14.info("Turn started", {
9413
9448
  requestId,
9414
9449
  model,
9415
- buildModel: buildModelOverride,
9450
+ buildModel: modelOverride ? parentModel : void 0,
9416
9451
  toolCount: tools2.length,
9417
9452
  ...entries.length > 1 && { entryCount: entries.length },
9418
9453
  ...totalAttachments > 0 && { attachmentCount: totalAttachments }
@@ -10551,12 +10586,13 @@ function loadPassiveResults() {
10551
10586
  }
10552
10587
  return [];
10553
10588
  }
10554
- function writeStats(stats, queue, passiveResults) {
10589
+ function writeStats(stats, queue, passiveResults, suggestCompactAt) {
10555
10590
  try {
10556
10591
  writeFileAtomicSync(
10557
10592
  STATS_FILE,
10558
10593
  JSON.stringify({
10559
10594
  ...stats,
10595
+ suggestCompactAt,
10560
10596
  queue,
10561
10597
  passiveResults
10562
10598
  })
@@ -10767,7 +10803,7 @@ var headless_exports = {};
10767
10803
  __export(headless_exports, {
10768
10804
  HeadlessSession: () => HeadlessSession
10769
10805
  });
10770
- var log17, EXTERNAL_TOOL_TIMEOUT_MS, LONG_RUNNING_TOOLS, LONG_RUNNING_TOOL_TIMEOUT_MS, USER_FACING_TOOLS, FORCED_COMPACTION_THRESHOLD_TOKENS, HeadlessSession;
10806
+ var log17, EXTERNAL_TOOL_TIMEOUT_MS, LONG_RUNNING_TOOLS, LONG_RUNNING_TOOL_TIMEOUT_MS, USER_FACING_TOOLS, HeadlessSession;
10771
10807
  var init_headless = __esm({
10772
10808
  "src/headless/index.ts"() {
10773
10809
  "use strict";
@@ -10798,7 +10834,6 @@ var init_headless = __esm({
10798
10834
  "confirmDestructiveAction",
10799
10835
  "presentPublishPlan"
10800
10836
  ]);
10801
- FORCED_COMPACTION_THRESHOLD_TOKENS = 85e4;
10802
10837
  HeadlessSession = class {
10803
10838
  // Configuration
10804
10839
  opts;
@@ -11071,7 +11106,15 @@ var init_headless = __esm({
11071
11106
  /** Persist sessionStats + queue snapshot + passive pen to .remy-stats.json. */
11072
11107
  persistStats() {
11073
11108
  this.sessionStats.updatedAt = Date.now();
11074
- writeStats(this.sessionStats, this.queue.snapshot(), this.passivePen);
11109
+ const suggestCompactAt = getSuggestCompactAt(
11110
+ resolveParentModel(this.state.models, this.opts.model).effective
11111
+ );
11112
+ writeStats(
11113
+ this.sessionStats,
11114
+ this.queue.snapshot(),
11115
+ this.passivePen,
11116
+ suggestCompactAt
11117
+ );
11075
11118
  }
11076
11119
  //////////////////////////////////////////////////////////////////////////////
11077
11120
  // Background completions (tool-block mutation; message delivery via queue)
@@ -11101,8 +11144,9 @@ var init_headless = __esm({
11101
11144
  saveSession(this.state);
11102
11145
  }
11103
11146
  /**
11104
- * Forced compaction gate. If lastContextSize exceeds the threshold, compact
11105
- * before letting the upcoming turn run. Coalesces with any in-flight
11147
+ * Forced compaction gate. If lastContextSize exceeds the upcoming turn's
11148
+ * model threshold (per-model — see ModelContextLimits in models/surfaces),
11149
+ * compact before letting the turn run. Coalesces with any in-flight
11106
11150
  * compaction (e.g., one already started by /compact or a tool call). No
11107
11151
  * timeout — compaction takes as long as it takes.
11108
11152
  *
@@ -11113,13 +11157,15 @@ var init_headless = __esm({
11113
11157
  * On compaction failure we don't bail — the turn proceeds and surfaces any
11114
11158
  * downstream overflow through the existing "prompt is too long" path.
11115
11159
  */
11116
- async runForcedCompactionIfNeeded(requestId) {
11117
- if (this.sessionStats.lastContextSize <= FORCED_COMPACTION_THRESHOLD_TOKENS) {
11160
+ async runForcedCompactionIfNeeded(requestId, parentModel) {
11161
+ const threshold = getContextLimits(parentModel).forceCompactAt;
11162
+ if (this.sessionStats.lastContextSize <= threshold) {
11118
11163
  return;
11119
11164
  }
11120
11165
  log17.info("Forced compaction gate triggered", {
11121
11166
  contextSize: this.sessionStats.lastContextSize,
11122
- threshold: FORCED_COMPACTION_THRESHOLD_TOKENS,
11167
+ threshold,
11168
+ model: parentModel,
11123
11169
  requestId
11124
11170
  });
11125
11171
  try {
@@ -11601,7 +11647,12 @@ var init_headless = __esm({
11601
11647
  }
11602
11648
  return steered;
11603
11649
  };
11604
- await this.runForcedCompactionIfNeeded(requestId);
11650
+ const parentModel = resolveParentModel(
11651
+ this.state.models,
11652
+ this.opts.model,
11653
+ params.buildModel
11654
+ ).effective;
11655
+ await this.runForcedCompactionIfNeeded(requestId, parentModel);
11605
11656
  try {
11606
11657
  await runTurn({
11607
11658
  state: this.state,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/remy",
3
- "version": "0.1.312",
3
+ "version": "0.1.313",
4
4
  "description": "Remy coding agent",
5
5
  "repository": {
6
6
  "type": "git",