@ponythewhite/base-context 1.0.8 → 1.0.9

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.
@@ -282,6 +282,7 @@ export class AgentSession {
282
282
  runtimeServices;
283
283
  _contextCompiler = new CanonicalContextCompiler();
284
284
  _failedAutomaticCompaction;
285
+ _failedThresholdCompaction;
285
286
  _contextEpochsEnabled;
286
287
  _initialContextMode;
287
288
  _contextMode;
@@ -714,7 +715,7 @@ export class AgentSession {
714
715
  acknowledged = { entryId, result };
715
716
  try {
716
717
  assertResourceCurrent(resource);
717
- if (this.sessionManager !== epochManager || epochManager.getLeafId() !== entryId)
718
+ if (this.sessionManager !== epochManager || !compaction.isCurrent())
718
719
  throw new Error("Context epoch source changed before adoption");
719
720
  this.agent.state.messages = prepared.messages;
720
721
  committed = prepared.checkpoint;
@@ -805,7 +806,7 @@ export class AgentSession {
805
806
  committedEntry = { sessionId: epochContext.source.sessionId, entryId };
806
807
  requestContract = nextContract;
807
808
  assertResourceCurrent(resource);
808
- if (this.sessionManager !== epochManager || epochManager.getLeafId() !== entryId)
809
+ if (this.sessionManager !== epochManager || !compaction.isCurrent())
809
810
  throw new Error("Context contract source changed before acceptance");
810
811
  }
811
812
  if (!sameSelectedSkills(committed?.selectedSkills, epochContext.selectedSkills))
@@ -822,7 +823,7 @@ export class AgentSession {
822
823
  throw commitFailure(cause);
823
824
  }
824
825
  }
825
- : undefined);
826
+ : undefined, compaction.isCurrent);
826
827
  }
827
828
  this._compactionSetupFailure = undefined;
828
829
  return { messages, adoptMessages: true, streamContext: captured, release: () => captured.dispose() };
@@ -850,7 +851,7 @@ export class AgentSession {
850
851
  !error.source.persistent ||
851
852
  error.source.sessionId !== owner.sessionId ||
852
853
  error.source.sessionFile !== owner.sessionFile ||
853
- error.source.leafId !== owner.manager.getLeafId())
854
+ !(error.isSourceCurrent?.() ?? error.source.leafId === owner.manager.getLeafId()))
854
855
  return false;
855
856
  // Rebase this captured frame before paying for a summary. The replacement still
856
857
  // needs the normal full provider projection and epoch ACK before MAIN can send.
@@ -1178,7 +1179,7 @@ export class AgentSession {
1178
1179
  };
1179
1180
  // ACK is authoritative even if later setup or release fails.
1180
1181
  this._contextMode = mode;
1181
- if (this.sessionManager !== manager || manager.getLeafId() !== entryId)
1182
+ if (this.sessionManager !== manager || !compaction.isCurrent())
1182
1183
  throw new Error("Context mode source changed before adoption");
1183
1184
  assertResourceCurrent(resource);
1184
1185
  this.agent.state.messages = prepared.messages;
@@ -2880,6 +2881,25 @@ export class AgentSession {
2880
2881
  return timestamp;
2881
2882
  });
2882
2883
  }
2884
+ _thresholdCompactionConfiguration() {
2885
+ return JSON.stringify({
2886
+ model: this.model,
2887
+ thinkingLevel: this.thinkingLevel,
2888
+ settings: this.settingsManager.getCompactionSettings(),
2889
+ });
2890
+ }
2891
+ _hasFailedThresholdCompaction() {
2892
+ const failed = this._failedThresholdCompaction;
2893
+ if (!failed)
2894
+ return false;
2895
+ if (failed.isCurrent() &&
2896
+ failed.configuration === this._thresholdCompactionConfiguration() &&
2897
+ failed.systemPrompt === this.agent.state.systemPrompt &&
2898
+ failed.tools === this.agent.state.tools)
2899
+ return true;
2900
+ this._failedThresholdCompaction = undefined;
2901
+ return false;
2902
+ }
2883
2903
  async _thresholdCompactionNeeded(context, owner = this._captureCompactionOwner(), goalOwner = {
2884
2904
  ...this._captureGoalContinuationOwner(owner.signal),
2885
2905
  checkpointOwner: owner,
@@ -2898,6 +2918,8 @@ export class AgentSession {
2898
2918
  const contextTokens = this._getThresholdContextTokens(context.message, compactionTimestamp);
2899
2919
  if (contextTokens === undefined || !shouldCompact(contextTokens, contextWindow, settings))
2900
2920
  return false;
2921
+ if (this._hasFailedThresholdCompaction())
2922
+ return false;
2901
2923
  // Keep the existing threshold-specific goal winner; do not import W74's natural wait policy here.
2902
2924
  if (!this._isGoalContinuationOwnerCurrent(goalOwner))
2903
2925
  return true;
@@ -4148,6 +4170,7 @@ export class AgentSession {
4148
4170
  */
4149
4171
  async disposeAsync(options) {
4150
4172
  this._failedAutomaticCompaction = undefined;
4173
+ this._failedThresholdCompaction = undefined;
4151
4174
  this._contextCompiler.clear();
4152
4175
  this._nativeRecoveryCursors.clear();
4153
4176
  for (const admission of this._rlmChildAdmissions)
@@ -4384,6 +4407,7 @@ export class AgentSession {
4384
4407
  }
4385
4408
  dispose() {
4386
4409
  this._failedAutomaticCompaction = undefined;
4410
+ this._failedThresholdCompaction = undefined;
4387
4411
  this._contextCompiler.clear();
4388
4412
  this._nativeRecoveryCursors.clear();
4389
4413
  if (this._disposed) {
@@ -9029,6 +9053,8 @@ export class AgentSession {
9029
9053
  if (contextTokens === undefined)
9030
9054
  return false;
9031
9055
  if (shouldCompact(contextTokens, contextWindow, settings)) {
9056
+ if (this._hasFailedThresholdCompaction())
9057
+ return false;
9032
9058
  if (!this._pendingCheckpoint && queueAutonomousContinuation) {
9033
9059
  if (!(await this._queueGoalContinuationForThresholdCompaction(assistantMessage, goalOwner)) &&
9034
9060
  this._isGoalContinuationOwnerCurrent(goalOwner)) {
@@ -9128,6 +9154,21 @@ export class AgentSession {
9128
9154
  let compaction;
9129
9155
  let committed;
9130
9156
  let failure;
9157
+ const thresholdConfiguration = reason === "threshold" && !resumeInPlace
9158
+ ? {
9159
+ configuration: this._thresholdCompactionConfiguration(),
9160
+ systemPrompt: this.agent.state.systemPrompt,
9161
+ tools: this.agent.state.tools,
9162
+ }
9163
+ : undefined;
9164
+ const rememberThresholdFailure = () => {
9165
+ if (thresholdConfiguration &&
9166
+ !abort.signal.aborted &&
9167
+ this._isCompactionOwnerCurrent(owner) &&
9168
+ compaction?.isCurrent()) {
9169
+ this._failedThresholdCompaction = { ...thresholdConfiguration, isCurrent: compaction.isCurrent };
9170
+ }
9171
+ };
9131
9172
  try {
9132
9173
  const selected = this._resolveCompactionModel();
9133
9174
  const model = selected?.model;
@@ -9148,6 +9189,7 @@ export class AgentSession {
9148
9189
  : authResult.error;
9149
9190
  await this._endCompactionUnsuccessfully(reason, "failed", `Compaction failed: ${detail}`, { owner });
9150
9191
  this._clearQueuedAutonomousContinuationsAfterSkippedThresholdCompaction(reason === "threshold" && shouldContinueAfterCompaction, queuedAutonomousContinuationsForThisCompaction);
9192
+ rememberThresholdFailure();
9151
9193
  resumeAfterFailure();
9152
9194
  return false;
9153
9195
  }
@@ -9224,6 +9266,7 @@ export class AgentSession {
9224
9266
  ? `Requested compaction failed: ${errorMessage}`
9225
9267
  : `Auto-compaction failed: ${errorMessage}`, { customInstructions, owner });
9226
9268
  }
9269
+ rememberThresholdFailure();
9227
9270
  resumeAfterFailure();
9228
9271
  return false;
9229
9272
  }