@wrongstack/core 0.8.0 → 0.8.4

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.
@@ -1,22 +1,22 @@
1
- export { C as CompactorOptions, a as DefaultErrorHandler, b as DefaultRetryPolicy, H as HybridCompactor, T as ToolExecutor } from '../tool-executor-Cx2ndr0L.js';
1
+ export { C as CompactorOptions, a as DefaultErrorHandler, b as DefaultRetryPolicy, H as HybridCompactor, T as ToolExecutor } from '../tool-executor-QwfWnQZ8.js';
2
2
  import { m as Provider, d as Context } from '../context-z2x5gv_V.js';
3
3
  import { a as Compactor, C as CompactReport } from '../compactor-Mw7-rNyb.js';
4
4
  import { M as MessageSelector } from '../selector-DkvgYVS4.js';
5
5
  import { E as EventBus } from '../events-DyhxkstG.js';
6
6
  import { b as MiddlewareHandler } from '../system-prompt-CWA6ml-d.js';
7
7
  import { e as ContextWindowAggressiveOn, i as ContextWindowPolicy } from '../config-Bi4Q0fnz.js';
8
- import { c as Agent, R as RunResult, w as SystemPromptContributor } from '../index-BciJeH8g.js';
8
+ import { c as Agent, R as RunResult, w as SystemPromptContributor } from '../index-oYZeWsuJ.js';
9
9
  import { D as DoneCondition } from '../multi-agent-CRMznZmf.js';
10
10
  import { J as JournalEntry } from '../goal-store-C7jcumEh.js';
11
- import { A as AgentFactory } from '../agent-subagent-runner-Bzeueq2J.js';
12
- import { f as DispatchClassifier, d as DefaultMultiAgentCoordinator } from '../multi-agent-coordinator-tlSWD4cE.js';
11
+ import { A as AgentFactory } from '../agent-subagent-runner-Cav3yEJM.js';
12
+ import { f as DispatchClassifier, d as DefaultMultiAgentCoordinator } from '../multi-agent-coordinator-IQKrMfXz.js';
13
13
  import { a as SkillLoader, b as SkillManifest, S as SkillEntry } from '../skill-CxuWrsKK.js';
14
14
  import { a as WstackPaths } from '../wstack-paths-gCrJ631C.js';
15
15
  import '../retry-policy-OwtKNxo8.js';
16
16
  import '../models-registry-BcYJDKLm.js';
17
17
  import '../logger-DDd5C--Z.js';
18
18
  import '../observability-BhnVLBLS.js';
19
- import '../secret-scrubber-nI8qjaqW.js';
19
+ import '../secret-scrubber-CyE1-EMG.js';
20
20
  import 'node:events';
21
21
 
22
22
  interface SkillLoaderOptions {
@@ -204,12 +204,28 @@ declare class AutoCompactionMiddleware {
204
204
  private readonly failureMode;
205
205
  private readonly policyProvider?;
206
206
  /**
207
- * Overhead factor applied to the rough message-token estimate to produce a
208
- * figure comparable to the real API request size (system prompt + tool defs).
209
- * Without this factor, raw message tokens undercount real load by 15-50% in
210
- * short conversations, causing premature compaction triggers.
207
+ * Calibration factor applied to the estimator output. The estimator is now
208
+ * expected to already include messages + system prompt + tool definitions
209
+ * (see `estimateRequestTokens.total`), so no overhead boost is needed. Kept
210
+ * as a constant so a future calibration against real provider tokenization
211
+ * (BPE vs the chars/3.5 rough estimator) can dial this without touching the
212
+ * threshold-check math.
213
+ *
214
+ * Historical note: was 1.3 when the estimator only counted messages — that
215
+ * double-counted system+tools once the estimator was upgraded, firing
216
+ * compaction ~30% earlier than intended (e.g. real 56% load showed as 73%).
211
217
  */
212
218
  private static readonly OVERHEAD_FACTOR;
219
+ /**
220
+ * Once a compaction attempt reduces nothing (preserveK protects everything,
221
+ * no oversized tool_results remain to elide), retrying on every iteration
222
+ * just spams `compaction.fired` events without making progress. We remember
223
+ * the no-op and skip until either the pressure level escalates or context
224
+ * has grown by at least this many tokens since the failed attempt.
225
+ */
226
+ private static readonly NOOP_RETRY_DELTA_TOKENS;
227
+ /** Tracks the most recent no-op attempt so we can avoid re-firing per turn. */
228
+ private lastNoopAttempt;
213
229
  /**
214
230
  * @param compactor Compactor to use for compaction.
215
231
  * @param maxContext Provider's max context window in tokens.
@@ -230,6 +246,13 @@ declare class AutoCompactionMiddleware {
230
246
  * denominator when the active model changes. */
231
247
  setMaxContext(maxContext: number): void;
232
248
  handler(): MiddlewareHandler<Context>;
249
+ /**
250
+ * Returns true when the previous compaction at the same or higher pressure
251
+ * level reduced nothing and context has not grown materially since. Prevents
252
+ * a stuck preserveK window from spamming compaction events every iteration.
253
+ */
254
+ private shouldSkipNoopRetry;
255
+ private recordAttempt;
233
256
  private compact;
234
257
  }
235
258
 
@@ -1004,6 +1004,7 @@ var FsError = class extends WrongStackError {
1004
1004
  };
1005
1005
 
1006
1006
  // src/execution/auto-compaction-middleware.ts
1007
+ var LEVEL_RANK = { warn: 0, soft: 1, hard: 2 };
1007
1008
  var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
1008
1009
  name = "AutoCompaction";
1009
1010
  compactor;
@@ -1018,12 +1019,28 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
1018
1019
  failureMode;
1019
1020
  policyProvider;
1020
1021
  /**
1021
- * Overhead factor applied to the rough message-token estimate to produce a
1022
- * figure comparable to the real API request size (system prompt + tool defs).
1023
- * Without this factor, raw message tokens undercount real load by 15-50% in
1024
- * short conversations, causing premature compaction triggers.
1022
+ * Calibration factor applied to the estimator output. The estimator is now
1023
+ * expected to already include messages + system prompt + tool definitions
1024
+ * (see `estimateRequestTokens.total`), so no overhead boost is needed. Kept
1025
+ * as a constant so a future calibration against real provider tokenization
1026
+ * (BPE vs the chars/3.5 rough estimator) can dial this without touching the
1027
+ * threshold-check math.
1028
+ *
1029
+ * Historical note: was 1.3 when the estimator only counted messages — that
1030
+ * double-counted system+tools once the estimator was upgraded, firing
1031
+ * compaction ~30% earlier than intended (e.g. real 56% load showed as 73%).
1032
+ */
1033
+ static OVERHEAD_FACTOR = 1;
1034
+ /**
1035
+ * Once a compaction attempt reduces nothing (preserveK protects everything,
1036
+ * no oversized tool_results remain to elide), retrying on every iteration
1037
+ * just spams `compaction.fired` events without making progress. We remember
1038
+ * the no-op and skip until either the pressure level escalates or context
1039
+ * has grown by at least this many tokens since the failed attempt.
1025
1040
  */
1026
- static OVERHEAD_FACTOR = 1.3;
1041
+ static NOOP_RETRY_DELTA_TOKENS = 2e3;
1042
+ /** Tracks the most recent no-op attempt so we can avoid re-firing per turn. */
1043
+ lastNoopAttempt = null;
1027
1044
  /**
1028
1045
  * @param compactor Compactor to use for compaction.
1029
1046
  * @param maxContext Provider's max context window in tokens.
@@ -1065,19 +1082,44 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
1065
1082
  hard: this.hardThreshold
1066
1083
  };
1067
1084
  const aggressiveOn = policy?.aggressiveOn ?? this.aggressiveOn;
1068
- if (load >= thresholds.hard) {
1069
- await this.compact(ctx, true, { level: "hard", tokens, load });
1070
- } else if (load >= thresholds.soft) {
1071
- await this.compact(ctx, aggressiveOn !== "hard", { level: "soft", tokens, load });
1072
- } else if (load >= thresholds.warn) {
1073
- await this.compact(ctx, aggressiveOn === "warn", { level: "warn", tokens, load });
1085
+ const level = load >= thresholds.hard ? "hard" : load >= thresholds.soft ? "soft" : load >= thresholds.warn ? "warn" : null;
1086
+ if (!level) {
1087
+ this.lastNoopAttempt = null;
1088
+ return next(ctx);
1074
1089
  }
1090
+ if (this.shouldSkipNoopRetry(level, tokens)) {
1091
+ return next(ctx);
1092
+ }
1093
+ const aggressive = level === "hard" ? true : level === "soft" ? aggressiveOn !== "hard" : aggressiveOn === "warn";
1094
+ await this.compact(ctx, aggressive, { level, tokens, load });
1075
1095
  return next(ctx);
1076
1096
  };
1077
1097
  }
1098
+ /**
1099
+ * Returns true when the previous compaction at the same or higher pressure
1100
+ * level reduced nothing and context has not grown materially since. Prevents
1101
+ * a stuck preserveK window from spamming compaction events every iteration.
1102
+ */
1103
+ shouldSkipNoopRetry(level, tokens) {
1104
+ const stuck = this.lastNoopAttempt;
1105
+ if (!stuck) return false;
1106
+ if (LEVEL_RANK[level] > LEVEL_RANK[stuck.level]) return false;
1107
+ const delta = tokens - stuck.tokens;
1108
+ return delta < _AutoCompactionMiddleware.NOOP_RETRY_DELTA_TOKENS;
1109
+ }
1110
+ recordAttempt(level, tokens, report) {
1111
+ const reduced = report.before > report.after;
1112
+ const repaired = !!report.repaired;
1113
+ if (reduced || repaired) {
1114
+ this.lastNoopAttempt = null;
1115
+ } else {
1116
+ this.lastNoopAttempt = { level, tokens };
1117
+ }
1118
+ }
1078
1119
  async compact(ctx, aggressive, pressure) {
1079
1120
  try {
1080
1121
  const report = await this.compactor.compact(ctx, { aggressive });
1122
+ this.recordAttempt(pressure.level, pressure.tokens, report);
1081
1123
  this.events?.emit("compaction.fired", {
1082
1124
  level: pressure.level,
1083
1125
  tokens: pressure.tokens,