@ponythewhite/base-context 1.0.7 → 1.0.8

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 (133) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/base-context-runtime/pyproject.toml +1 -1
  3. package/dist/base-context-runtime/src/rlm/__init__.py +3 -0
  4. package/dist/base-context-runtime/src/rlm/harness.py +86 -48
  5. package/dist/base-context-runtime/test/test_harness.py +215 -140
  6. package/dist/base-context-runtime/uv.lock +1 -1
  7. package/dist/build-info.json +1 -1
  8. package/dist/bundle/amazon-bedrock.js +3 -3
  9. package/dist/bundle/{anthropic-UV5GZDGU.js → anthropic-5PSCZEER.js} +2 -7
  10. package/dist/bundle/{azure-openai-responses-WSNPXLYC.js → azure-openai-responses-AYTLJR33.js} +2 -2
  11. package/dist/bundle/{bundled-modules-PS66L3IT.js → bundled-modules-IGMJH2MU.js} +5 -5
  12. package/dist/bundle/{chunk-UPVDERMD.js → chunk-2IPDSTDR.js} +11 -9
  13. package/dist/bundle/chunk-D3KOF57W.js +22040 -0
  14. package/dist/bundle/{chunk-VCW3YDFF.js → chunk-EXEFSSF4.js} +1475 -562
  15. package/dist/bundle/{chunk-VSIYFWYI.js → chunk-KT2BNEPE.js} +21 -8
  16. package/dist/bundle/{chunk-KTNETTHI.js → chunk-Q3MBSBEB.js} +3 -101
  17. package/dist/bundle/{chunk-LFPWHI42.js → chunk-QKIFBA7N.js} +2 -2
  18. package/dist/bundle/{chunk-PFROC4EV.js → chunk-RVJVXZCS.js} +12 -9
  19. package/dist/bundle/{cli-main-MMHTK7EB.js → cli-main-BUK7QTHO.js} +4 -4
  20. package/dist/bundle/cli.js +1 -1
  21. package/dist/bundle/{google-T32FMD4G.js → google-HTMCUJ7X.js} +2 -2
  22. package/dist/bundle/{google-vertex-56KP6I3B.js → google-vertex-ZQO65AXF.js} +2 -2
  23. package/dist/bundle/{main-XAIVH4Y7.js → main-2FWHT2WZ.js} +5 -5
  24. package/dist/bundle/{mistral-WGBMYIVU.js → mistral-SB7LU56G.js} +1 -1
  25. package/dist/bundle/{openai-codex-responses-PIV2RQ3G.js → openai-codex-responses-5CEW4VN3.js} +53 -27
  26. package/dist/bundle/{openai-completions-64C3JPR4.js → openai-completions-BAZJQSS2.js} +1 -1
  27. package/dist/bundle/{openai-responses-UI7RSTDV.js → openai-responses-VHOE427P.js} +48 -5
  28. package/dist/core/agent-session.d.ts +6 -1
  29. package/dist/core/agent-session.d.ts.map +1 -1
  30. package/dist/core/agent-session.js +177 -24
  31. package/dist/core/agent-session.js.map +1 -1
  32. package/dist/core/canonical-context.d.ts +8 -0
  33. package/dist/core/canonical-context.d.ts.map +1 -1
  34. package/dist/core/canonical-context.js +37 -5
  35. package/dist/core/canonical-context.js.map +1 -1
  36. package/dist/core/compaction/compaction.d.ts +2 -2
  37. package/dist/core/compaction/compaction.d.ts.map +1 -1
  38. package/dist/core/compaction/compaction.js +31 -13
  39. package/dist/core/compaction/compaction.js.map +1 -1
  40. package/dist/core/context-epoch.d.ts +1 -1
  41. package/dist/core/context-epoch.d.ts.map +1 -1
  42. package/dist/core/context-epoch.js +2 -1
  43. package/dist/core/context-epoch.js.map +1 -1
  44. package/dist/core/extensions/types.d.ts +2 -2
  45. package/dist/core/extensions/types.d.ts.map +1 -1
  46. package/dist/core/extensions/types.js.map +1 -1
  47. package/dist/core/history-index-worker.d.ts.map +1 -1
  48. package/dist/core/history-index-worker.js +29 -8
  49. package/dist/core/history-index-worker.js.map +1 -1
  50. package/dist/core/history-index.d.ts +11 -1
  51. package/dist/core/history-index.d.ts.map +1 -1
  52. package/dist/core/history-index.js +2 -1
  53. package/dist/core/history-index.js.map +1 -1
  54. package/dist/core/inference-coordinator.d.ts.map +1 -1
  55. package/dist/core/inference-coordinator.js +108 -13
  56. package/dist/core/inference-coordinator.js.map +1 -1
  57. package/dist/core/kernel/bootstrap.d.ts.map +1 -1
  58. package/dist/core/kernel/bootstrap.js +97 -107
  59. package/dist/core/kernel/bootstrap.js.map +1 -1
  60. package/dist/core/kernel/repl-manager.d.ts.map +1 -1
  61. package/dist/core/kernel/repl-manager.js +11 -0
  62. package/dist/core/kernel/repl-manager.js.map +1 -1
  63. package/dist/core/kernel/shared.d.ts +2 -0
  64. package/dist/core/kernel/shared.d.ts.map +1 -1
  65. package/dist/core/kernel/shared.js.map +1 -1
  66. package/dist/core/messages.d.ts.map +1 -1
  67. package/dist/core/messages.js +4 -1
  68. package/dist/core/messages.js.map +1 -1
  69. package/dist/core/prompts/rlm.d.ts.map +1 -1
  70. package/dist/core/prompts/rlm.js +4 -2
  71. package/dist/core/prompts/rlm.js.map +1 -1
  72. package/dist/core/public-context.d.ts.map +1 -1
  73. package/dist/core/public-context.js +16 -0
  74. package/dist/core/public-context.js.map +1 -1
  75. package/dist/core/refinement/refinement.d.ts +19 -5
  76. package/dist/core/refinement/refinement.d.ts.map +1 -1
  77. package/dist/core/refinement/refinement.js +90 -35
  78. package/dist/core/refinement/refinement.js.map +1 -1
  79. package/dist/core/request-view-selection.d.ts +10 -2
  80. package/dist/core/request-view-selection.d.ts.map +1 -1
  81. package/dist/core/request-view-selection.js +235 -100
  82. package/dist/core/request-view-selection.js.map +1 -1
  83. package/dist/core/retained-tool-output.d.ts +28 -0
  84. package/dist/core/retained-tool-output.d.ts.map +1 -0
  85. package/dist/core/retained-tool-output.js +102 -0
  86. package/dist/core/retained-tool-output.js.map +1 -0
  87. package/dist/core/selective-recovery.d.ts +47 -3
  88. package/dist/core/selective-recovery.d.ts.map +1 -1
  89. package/dist/core/selective-recovery.js +428 -109
  90. package/dist/core/selective-recovery.js.map +1 -1
  91. package/dist/core/session-history-index.d.ts +3 -1
  92. package/dist/core/session-history-index.d.ts.map +1 -1
  93. package/dist/core/session-history-index.js +1 -1
  94. package/dist/core/session-history-index.js.map +1 -1
  95. package/dist/core/settings-manager.d.ts +5 -0
  96. package/dist/core/settings-manager.d.ts.map +1 -1
  97. package/dist/core/settings-manager.js +3 -0
  98. package/dist/core/settings-manager.js.map +1 -1
  99. package/dist/core/system-prompt.d.ts +3 -1
  100. package/dist/core/system-prompt.d.ts.map +1 -1
  101. package/dist/core/system-prompt.js +3 -3
  102. package/dist/core/system-prompt.js.map +1 -1
  103. package/dist/core/task-frame.d.ts +1 -1
  104. package/dist/core/task-frame.d.ts.map +1 -1
  105. package/dist/core/task-frame.js +21 -1
  106. package/dist/core/task-frame.js.map +1 -1
  107. package/dist/core/task-state-reader.d.ts +10 -2
  108. package/dist/core/task-state-reader.d.ts.map +1 -1
  109. package/dist/core/task-state-reader.js +75 -9
  110. package/dist/core/task-state-reader.js.map +1 -1
  111. package/dist/core/tools/ipython.d.ts +5 -0
  112. package/dist/core/tools/ipython.d.ts.map +1 -1
  113. package/dist/core/tools/ipython.js +59 -17
  114. package/dist/core/tools/ipython.js.map +1 -1
  115. package/dist/core/tools/prime-context.d.ts.map +1 -1
  116. package/dist/core/tools/prime-context.js +1 -1
  117. package/dist/core/tools/prime-context.js.map +1 -1
  118. package/dist/core/view-units.d.ts +4 -0
  119. package/dist/core/view-units.d.ts.map +1 -1
  120. package/dist/core/view-units.js +28 -19
  121. package/dist/core/view-units.js.map +1 -1
  122. package/dist/installer.mjs +98 -77
  123. package/dist/modes/interactive/components/refinement-outcome-message.d.ts.map +1 -1
  124. package/dist/modes/interactive/components/refinement-outcome-message.js.map +1 -1
  125. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  126. package/dist/modes/interactive/interactive-mode.js +5 -5
  127. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  128. package/dist/skills/skill-creator/SKILL.md +18 -1
  129. package/docs/settings.md +45 -0
  130. package/docs/skills.md +77 -2
  131. package/package.json +4 -4
  132. package/skills/skill-creator/SKILL.md +18 -1
  133. package/dist/bundle/chunk-W46VZFI6.js +0 -21681
@@ -5,7 +5,7 @@ import { tmpdir } from "node:os";
5
5
  import { basename, dirname, join, resolve } from "node:path";
6
6
  import { isDeepStrictEqual } from "node:util";
7
7
  import { Agent, AgentContinueError, AgentOutputLimitError, } from "@ponythewhite/base-context-agent";
8
- import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isTransientProviderFailure, modelsAreEqual, resetApiProviders, supportsFastMode, } from "@ponythewhite/base-context-ai";
8
+ import { clampThinkingLevel, cleanupSessionResources, conservativeTextTokenCost, getSupportedThinkingLevels, isContextOverflow, isTransientProviderFailure, modelsAreEqual, resetApiProviders, supportsFastMode, } from "@ponythewhite/base-context-ai";
9
9
  import { theme } from "../modes/interactive/theme/theme.js";
10
10
  import { PRODUCT } from "../product-identity.js";
11
11
  import { sleep } from "../utils/sleep.js";
@@ -31,13 +31,13 @@ import { snapshotPathIn } from "./kernel/state-snapshot.js";
31
31
  import { convertToLlm, createCompactionOutcomeMessage, createHeartbeatPromptMessage, createRefinementOutcomeMessage, createRlmChildFailureMessage, createRlmChildTerminalNoticeMessage, createSessionSlashCommandMessage, createSessionSlashCommandResultMessage, HEARTBEAT_PROMPT_CUSTOM_TYPE, HEARTBEAT_PROMPT_PREVIEW_LABEL, IPYTHON_STATE_RESTORED_CUSTOM_TYPE, isSessionSlashCommandMessage, RLM_CHILD_FAILURE_CUSTOM_TYPE, RLM_CHILD_TERMINAL_NOTICE_CUSTOM_TYPE, } from "./messages.js";
32
32
  import { throwIfPromptAdmissionCancelled } from "./prompt-admission.js";
33
33
  import { expandPromptTemplate } from "./prompt-templates.js";
34
- import { appendGlobalRefinement, applyRefinementProposal, generateRefinementId, getGlobalHarnessStateDir, getLocalHarnessStateDir, getRefinementHistory, inferRefinementResultScope, loadGlobalRefinementHistory, loadHarnessState, mergeHarnessStates, mergeRefinementHistory, normalizeRefinementProposal, planRefinement, prepareRefinementApplication, REFINE_SKILL_NAME, reviewAutoRefine, saveHarnessState, takeNativePlannerRequestWrite, withRefinementBaseline, } from "./refinement/index.js";
34
+ import { appendGlobalRefinement, applyRefinementProposal, generateRefinementId, getGlobalHarnessStateDir, getLocalHarnessStateDir, getRefinementHistory, getWorkspaceHarnessStateDir, inferRefinementResultScope, loadGlobalRefinementHistory, loadHarnessState, mergeHarnessStates, mergeRefinementHistory, normalizeRefinementProposal, planRefinement, prepareRefinementApplication, REFINE_SKILL_NAME, reviewAutoRefine, saveHarnessState, takeNativePlannerRequestWrite, withRefinementBaseline, } from "./refinement/index.js";
35
35
  import { PublicContextBudgetError } from "./request-view-selection.js";
36
36
  import { resolveConfigValue } from "./resolve-config-value.js";
37
37
  import { assertResourceCurrent } from "./resource-view.js";
38
38
  import { createDefaultRlmSubagentSessionName, createRlmDeleteSubagentHostHandler, createRlmFindModelsHostHandler, createRlmListSubagentsHostHandler, createRlmRunHostHandler, findRlmModelMatches, normalizeRequestedRlmSubagentModel, normalizeRequestedRlmSubagentSessionName, normalizeRequestedRlmSubagentThinkingLevel, } from "./rlm-runtime.js";
39
39
  import { captureSelectedSkill, captureSkillDescriptor, readSkillSelection, sameSelectedSkills, selectedSkillBlock, selectedSkillIdentity, } from "./selected-skills.js";
40
- import { createNativeRecoveryRefusal, DEFAULT_NATIVE_RECOVERY_LIMITS, NativeRecoveryBudgetRefusal, parseNativeRecoveryInput, recoverCapturedHistory, } from "./selective-recovery.js";
40
+ import { createNativeRecoveryRefusal, DEFAULT_NATIVE_RECOVERY_LIMITS, NativeRecoveryBudgetRefusal, NativeRecoveryCursorStore, parseNativeRecoveryInput, recoverCapturedHistory, } from "./selective-recovery.js";
41
41
  import { modelRequestHeaders, SemanticEdgeRecorder, semanticEdgeLedgerPath, wrapStreamFnWithSemanticEdges, } from "./semantic-edges.js";
42
42
  import { ActionStore, canSelectSessionAction, queuedMessageLaneDeliveryPolicy, transitionSessionAction, } from "./session-action-store.js";
43
43
  import { readSessionBootstrap } from "./session-bootstrap.js";
@@ -281,6 +281,7 @@ export class AgentSession {
281
281
  requests;
282
282
  runtimeServices;
283
283
  _contextCompiler = new CanonicalContextCompiler();
284
+ _failedAutomaticCompaction;
284
285
  _contextEpochsEnabled;
285
286
  _initialContextMode;
286
287
  _contextMode;
@@ -444,6 +445,8 @@ export class AgentSession {
444
445
  _modelRegistry;
445
446
  _toolRegistry = new Map();
446
447
  _nativeRecoveryTools = new WeakMap();
448
+ _nativeRecoveryCursors = new NativeRecoveryCursorStore();
449
+ _nativeRecoveryCursorSource;
447
450
  _nativeRecoveryProducer = new AsyncLocalStorage();
448
451
  _toolDefinitions = new Map();
449
452
  _toolPromptSnippets = new Map();
@@ -452,6 +455,7 @@ export class AgentSession {
452
455
  _baseSystemPromptOptions;
453
456
  _assistantTurnsSinceAutoRefine = 0;
454
457
  _lastAutoRefineReviewAt = 0;
458
+ _lastAutoRefineEvidence;
455
459
  _autoRefineInProgress = false;
456
460
  _autoRefineAdmissionClosed = false;
457
461
  _autoRefineOperations = new Set();
@@ -610,7 +614,8 @@ export class AgentSession {
610
614
  const epochContext = getCanonicalEpochContext(messages);
611
615
  if (epochContext)
612
616
  this._contextMode = epochContext.mode;
613
- const unbudgetedPublic = !contextEpochsEnabled && Boolean(epochContext?.toolContinuations?.length);
617
+ const unbudgetedPublic = !contextEpochsEnabled &&
618
+ Boolean(epochContext?.toolContinuations?.length || epochContext?.taskFrameRebased || epochContext?.checkpoint);
614
619
  const nativeSkills = Boolean(contextEpochsEnabled || epochContext?.checkpoint || unbudgetedPublic);
615
620
  const skillPolicy = nativeSkills ? (this._nativeRecoveryEnabled() ? "enabled" : "unavailable") : undefined;
616
621
  if (this._baseSystemPromptOptions.nativeSkillSelection !== skillPolicy) {
@@ -637,6 +642,7 @@ export class AgentSession {
637
642
  message.content.some((part) => part.type !== "text" || part.textSignature !== undefined)));
638
643
  let accepted;
639
644
  let acceptedBody;
645
+ let acceptedResponseIdentity;
640
646
  let acceptedReplayContract;
641
647
  let acknowledged;
642
648
  const commitFailure = (cause) => {
@@ -656,7 +662,7 @@ export class AgentSession {
656
662
  candidate.projection.pendingPublicMessageGroups?.length))
657
663
  throw new Error("Tool continuation requires its exact public candidate before epoch ACK");
658
664
  assertResourceCurrent(resource);
659
- const representation = contextEpochRepresentation(candidate.request, candidate.assessment, limits.maxSourceBytes, unbudgetedPublic);
665
+ const representation = contextEpochRepresentation(candidate.request, candidate.assessment, limits.maxSourceBytes, unbudgetedPublic, candidate.responseItemIdentity);
660
666
  const replayContract = "replayContract" in candidate.projection &&
661
667
  candidate.projection.replayContract === "message-groups"
662
668
  ? "message-groups"
@@ -669,6 +675,7 @@ export class AgentSession {
669
675
  candidate.publicMessages !== undefined,
670
676
  candidate.selectedUnitIds,
671
677
  ]);
678
+ acceptedResponseIdentity = candidate.responseItemIdentity;
672
679
  if (accepted !== undefined) {
673
680
  if (accepted !== selection)
674
681
  throw new Error("Captured epoch request selection changed after acceptance");
@@ -738,12 +745,13 @@ export class AgentSession {
738
745
  }
739
746
  return;
740
747
  }
748
+ if (epochContext.taskFrameRebased && acceptedBody === undefined)
749
+ throw new Error("Task frame rebase requires a committed epoch boundary");
741
750
  if (!committed)
742
751
  return;
743
752
  if (acceptedBody === undefined || request.body !== acceptedBody)
744
753
  throw new Error("Committed context epoch requires a compatible final provider projection");
745
- if (contextEpochRepresentation(request, assessment, limits.maxSourceBytes, unbudgetedPublic) !==
746
- committed.representation)
754
+ if (contextEpochRepresentation(request, assessment, limits.maxSourceBytes, unbudgetedPublic, acceptedResponseIdentity) !== committed.representation)
747
755
  throw new Error("Context epoch representation changed without a committed boundary");
748
756
  if (committed.resourceRevision !== epochContext.resourceRevision)
749
757
  throw new Error("Context epoch resource revision requires a committed boundary");
@@ -844,9 +852,41 @@ export class AgentSession {
844
852
  error.source.sessionFile !== owner.sessionFile ||
845
853
  error.source.leafId !== owner.manager.getLeafId())
846
854
  return false;
855
+ // Rebase this captured frame before paying for a summary. The replacement still
856
+ // needs the normal full provider projection and epoch ACK before MAIN can send.
857
+ if (error.taskFrameRebaseAvailable) {
858
+ this._contextCompiler.requestTaskFrameRebase();
859
+ return "reprepare";
860
+ }
861
+ if (error.mandatoryAssessment?.status === "over-budget")
862
+ return false;
847
863
  // The loop released its unsent inference and captured projection before this handoff.
848
864
  // Continue in-place only after the real summary ACK and canonical owner adoption.
849
- return this._runAutoCompaction("threshold", false, owner, true);
865
+ const key = error.getCompactionKey();
866
+ const current = {
867
+ key: key ?? "",
868
+ systemPrompt: this.agent.state.systemPrompt,
869
+ tools: this.agent.state.tools,
870
+ settings: JSON.stringify(this.settingsManager.getCompactionSettings()),
871
+ };
872
+ const failed = this._failedAutomaticCompaction;
873
+ if (key &&
874
+ failed &&
875
+ failed.key === key &&
876
+ failed.systemPrompt === current.systemPrompt &&
877
+ failed.tools === current.tools &&
878
+ failed.settings === current.settings)
879
+ return false;
880
+ this._failedAutomaticCompaction = undefined;
881
+ let succeeded = false;
882
+ try {
883
+ succeeded = await this._runAutoCompaction("threshold", false, owner, true, error);
884
+ return succeeded;
885
+ }
886
+ finally {
887
+ if (!succeeded && key && this._isCompactionSourceOwnerCurrent(owner))
888
+ this._failedAutomaticCompaction = current;
889
+ }
850
890
  });
851
891
  this.agent.bindProviderFailureRecoveryOwner({
852
892
  recover: (message, signal) => this._handleRetryableError(message, signal),
@@ -4107,6 +4147,9 @@ export class AgentSession {
4107
4147
  * the latest state reaches disk instead of racing process exit.
4108
4148
  */
4109
4149
  async disposeAsync(options) {
4150
+ this._failedAutomaticCompaction = undefined;
4151
+ this._contextCompiler.clear();
4152
+ this._nativeRecoveryCursors.clear();
4110
4153
  for (const admission of this._rlmChildAdmissions)
4111
4154
  admission.cancel("Parent session disposed");
4112
4155
  if (this._disposeAsyncPromise)
@@ -4340,6 +4383,9 @@ export class AgentSession {
4340
4383
  return this._disposeCallbacksPromise;
4341
4384
  }
4342
4385
  dispose() {
4386
+ this._failedAutomaticCompaction = undefined;
4387
+ this._contextCompiler.clear();
4388
+ this._nativeRecoveryCursors.clear();
4343
4389
  if (this._disposed) {
4344
4390
  return;
4345
4391
  }
@@ -4583,7 +4629,7 @@ export class AgentSession {
4583
4629
  }
4584
4630
  return Array.from(unique);
4585
4631
  }
4586
- _rebuildSystemPrompt(toolNames, nativeEpoch = this.sessionManager.isPersisted() && this._contextEpochsEnabled) {
4632
+ _rebuildSystemPrompt(toolNames, nativeEpoch = this.sessionManager.isPersisted() && this._contextEpochsEnabled, operation = this._baseSystemPromptOptions?.errorFixSelection?.operation) {
4587
4633
  const validToolNames = toolNames.filter((name) => this._toolRegistry.has(name));
4588
4634
  const toolSnippets = {};
4589
4635
  const promptGuidelines = [];
@@ -4617,6 +4663,14 @@ export class AgentSession {
4617
4663
  rlmDepth: this._rlmDepth,
4618
4664
  rlmParentAgent: this._rlmParentAgent,
4619
4665
  harnessState: this._loadMergedHarnessState(),
4666
+ errorFixSelection: {
4667
+ enabled: this.settingsManager.getLearningEnabled(),
4668
+ tools: validToolNames,
4669
+ operation,
4670
+ goal: this._goalState.objective,
4671
+ environment: { cwd: this._cwd, platform: process.platform },
4672
+ countTokens: conservativeTextTokenCost,
4673
+ },
4620
4674
  genericMcpServers: this._mcpManager?.getEnabledPersistentGenericServers(),
4621
4675
  };
4622
4676
  return buildSystemPrompt(this._baseSystemPromptOptions);
@@ -6099,6 +6153,12 @@ export class AgentSession {
6099
6153
  const preparationAction = activeTurns().at(-1);
6100
6154
  if (!preparationAction)
6101
6155
  return undefined;
6156
+ if (this.settingsManager.getLearningEnabled() ||
6157
+ this._baseSystemPromptOptions.errorFixSelection?.enabled) {
6158
+ // Capture advice for this task before extensions and provider preparation.
6159
+ // Tool continuations keep the same prompt until the next task boundary.
6160
+ this._baseSystemPrompt = this._rebuildSystemPrompt(this.getActiveToolNames(), this._baseSystemPromptOptions.nativeSkillSelection !== undefined, preparationAction.payload.text);
6161
+ }
6102
6162
  const basePromptSnapshot = this._baseSystemPrompt;
6103
6163
  const result = await this._extensionRunner.emitBeforeAgentStart(preparationAction.payload.text, preparationAction.payload.images, basePromptSnapshot, this._baseSystemPromptOptions);
6104
6164
  if (activeTurns().at(-1) !== preparationAction)
@@ -7759,7 +7819,11 @@ export class AgentSession {
7759
7819
  const summaryRequests = requests[captureNativeCompactionRequests]();
7760
7820
  let summaryFailure;
7761
7821
  try {
7762
- nativeCompaction = await compact(preparation, model, apiKey, headers, customInstructions, signal, thinkingLevel, summaryCall, summaryRequests);
7822
+ nativeCompaction = await compact(preparation, model, apiKey, headers, customInstructions, signal, thinkingLevel, summaryCall, summaryRequests, options.capacity
7823
+ ? (wrapper) => options.capacity.remainingSummaryTokens(new Set(pathEntries
7824
+ .slice(pathEntries.findIndex((entry) => entry.id === preparation.firstKeptEntryId))
7825
+ .map((entry) => entry.id)), wrapper)
7826
+ : undefined);
7763
7827
  ({ summary, firstKeptEntryId, tokensBefore, details, usage } = nativeCompaction);
7764
7828
  }
7765
7829
  catch (error) {
@@ -8320,9 +8384,9 @@ export class AgentSession {
8320
8384
  }
8321
8385
  }
8322
8386
  catch (error) {
8323
- // Auto-refine is opportunistic; manual /refine remains available.
8324
- // Stamp the cooldown so a persistently failing refine doesn't retry
8325
- // (via a retained pending review) on every agent end.
8387
+ // Consume this exact automatic proposal after failure. New evidence can
8388
+ // request a new review; manual /refine remains independent.
8389
+ this._pendingAutoRefineReview = undefined;
8326
8390
  this._lastAutoRefineReviewAt = Date.now();
8327
8391
  if (error instanceof RefineSkippedError) {
8328
8392
  // A skipped round is consumed like a reviewer decline, not retained for retry.
@@ -8362,7 +8426,78 @@ export class AgentSession {
8362
8426
  throw new Error(`autoRefine.model thinkingLevel ${selection.thinkingLevel} is not supported by ${selection.provider}/${selection.modelId}`);
8363
8427
  return { model: { ...model, cost: { ...model.cost } }, thinkingLevel: selection.thinkingLevel, explicit: true };
8364
8428
  }
8429
+ async _autoRefineEvidence(harnessState) {
8430
+ const manager = this.sessionManager;
8431
+ const goalEvidence = ({ active, status, goalId, objective, tokenBudget, lastError }) => ({
8432
+ active,
8433
+ status,
8434
+ goalId,
8435
+ objective,
8436
+ tokenBudget,
8437
+ lastError,
8438
+ });
8439
+ const frontier = manager.supportsCapturedHistoryReads()
8440
+ ? await manager.readBranchHistory(async (history) => {
8441
+ const goal = (await history.branchBootstrap()).goalState;
8442
+ const limits = this.settingsManager.getCanonicalContextLimits();
8443
+ let goalState = emptyGoalState();
8444
+ if (goal) {
8445
+ const hydrated = await history.hydrateEntry(goal.id, limits.maxSourceBytes);
8446
+ if (!hydrated ||
8447
+ hydrated.source.retention === "retained-import" ||
8448
+ hydrated.entry.type !== "custom" ||
8449
+ hydrated.entry.customType !== GOAL_STATE_CUSTOM_TYPE ||
8450
+ !isPersistedGoalState(hydrated.entry.data))
8451
+ throw new RefineSkippedError("Automatic refinement goal source is unavailable or ineligible");
8452
+ goalState = normalizeGoalState(hydrated.entry.data);
8453
+ }
8454
+ let id = history.source.leafId;
8455
+ const maxEntries = limits.maxMessages;
8456
+ for (let scanned = 0; scanned < maxEntries; scanned++) {
8457
+ const entry = id ? await history.get(id) : undefined;
8458
+ if (id && !entry)
8459
+ throw new RefineSkippedError("Automatic refinement task frontier is unavailable");
8460
+ const message = entry?.kind === "message" ? entry : undefined;
8461
+ if (message || !entry)
8462
+ return {
8463
+ sessionId: history.source.sessionId,
8464
+ sessionFile: history.source.sessionFile,
8465
+ message: message ? [message.id, message.revision] : undefined,
8466
+ goal: goalEvidence(goalState),
8467
+ };
8468
+ id = entry.parentId;
8469
+ }
8470
+ throw new RefineSkippedError("Automatic refinement task frontier exceeds the context read limit");
8471
+ })
8472
+ : (() => {
8473
+ const branch = manager.getBranch().slice().reverse();
8474
+ const message = branch.find((entry) => entry.type === "message");
8475
+ return {
8476
+ sessionId: manager.getSessionId(),
8477
+ sessionFile: manager.getSessionFile(),
8478
+ message: message ? [message.id, message.timestamp] : undefined,
8479
+ goal: goalEvidence(this._loadPersistedGoalState()),
8480
+ };
8481
+ })();
8482
+ return {
8483
+ ...frontier,
8484
+ systemPrompt: this.agent.state.systemPrompt,
8485
+ model: JSON.stringify([this.model?.provider, this.model?.id, this.settingsManager.getAutoRefineModel()]),
8486
+ harnessEntries: Object.fromEntries(Object.entries(harnessState.entries).map(([kind, entries]) => [
8487
+ kind,
8488
+ Object.fromEntries(Object.entries(entries).map(([id, { version: _version, created_at: _created, updated_at: _updated, ...entry }]) => [id, entry])),
8489
+ ])),
8490
+ };
8491
+ }
8365
8492
  async _reviewAutoRefine(context, signal) {
8493
+ const harnessState = this._loadMergedHarnessState();
8494
+ const evidence = await this._autoRefineEvidence(harnessState);
8495
+ signal?.throwIfAborted();
8496
+ if (isDeepStrictEqual(evidence, this._lastAutoRefineEvidence))
8497
+ return { shouldRefine: false, rationale: "No new task evidence since the previous automatic review." };
8498
+ // Reserve before any inference wait. Equivalent running, declined or failed
8499
+ // automatic reviews do not buy another call; explicit /refine stays independent.
8500
+ this._lastAutoRefineEvidence = evidence;
8366
8501
  if (this._autoRefineReviewer)
8367
8502
  return this._autoRefineReviewer(context, signal);
8368
8503
  const selected = this._resolveRefinementModel();
@@ -8370,7 +8505,6 @@ export class AgentSession {
8370
8505
  return { shouldRefine: false, rationale: "No model selected." };
8371
8506
  const { model, thinkingLevel, explicit } = selected;
8372
8507
  const messages = structuredClone(this.agent.state.messages);
8373
- const harnessState = this._loadMergedHarnessState();
8374
8508
  const reviewContext = { ...context };
8375
8509
  const requests = this.requests[captureNativeReviewerRequests]();
8376
8510
  try {
@@ -8382,10 +8516,10 @@ export class AgentSession {
8382
8516
  await requests.dispose();
8383
8517
  }
8384
8518
  }
8385
- /** Global harness state overlaid with this session's local state, when persisted. */
8519
+ /** Merge existing session/global memory with the selected project's workspace memory. */
8386
8520
  _loadMergedHarnessState() {
8387
8521
  const localHarnessStateDir = this._localHarnessStateDir();
8388
- return mergeHarnessStates(loadHarnessState(getGlobalHarnessStateDir(), "global"), localHarnessStateDir ? loadHarnessState(localHarnessStateDir, "local") : undefined);
8522
+ return mergeHarnessStates(loadHarnessState(getGlobalHarnessStateDir(), "global"), localHarnessStateDir ? loadHarnessState(localHarnessStateDir, "local") : undefined, loadHarnessState(getWorkspaceHarnessStateDir(this._cwd), "workspace"));
8389
8523
  }
8390
8524
  async _loadRefinementHistory() {
8391
8525
  const limits = {
@@ -8843,7 +8977,21 @@ export class AgentSession {
8843
8977
  }
8844
8978
  if (!this._isCompactionOwnerCurrent(owner))
8845
8979
  return false;
8846
- const assistantIsFromBeforeCompaction = compactionTimestamp !== undefined && assistantMessage.timestamp <= compactionTimestamp;
8980
+ let assistantIsFromBeforeCompaction = compactionTimestamp !== undefined && assistantMessage.timestamp <= compactionTimestamp;
8981
+ const assistantEntryId = this._findAssistantEntryIdForMessage(assistantMessage);
8982
+ if (assistantEntryId && owner.manager.supportsCapturedHistoryReads()) {
8983
+ const ordered = await owner.manager.readBranchHistory(async (history) => {
8984
+ const assistant = await history.get(assistantEntryId);
8985
+ const boundary = (await history.branchBootstrap()).latestCompaction;
8986
+ return assistant && boundary ? assistant.sequence <= boundary.sequence : undefined;
8987
+ });
8988
+ if (!this._isCompactionOwnerCurrent(owner))
8989
+ return false;
8990
+ // Provider messages are timestamped before request preparation, which may
8991
+ // commit an epoch. Canonical append order, not that start time, owns staleness.
8992
+ if (ordered !== undefined)
8993
+ assistantIsFromBeforeCompaction = ordered;
8994
+ }
8847
8995
  // Case 1: Overflow - takes priority over a pending model request so the error
8848
8996
  // strip + retry still happen; the compaction it runs consumes the request.
8849
8997
  if (!assistantIsFromBeforeCompaction &&
@@ -8935,7 +9083,7 @@ export class AgentSession {
8935
9083
  this._emit({ type: "message_start", message: outcomeMessage });
8936
9084
  this._emit({ type: "message_end", message: outcomeMessage });
8937
9085
  }
8938
- async _runAutoCompaction(reason, willRetry, capturedOwner, resumeInPlace = false) {
9086
+ async _runAutoCompaction(reason, willRetry, capturedOwner, resumeInPlace = false, capacity) {
8939
9087
  const checkpoint = this._pendingCheckpoint;
8940
9088
  const pending = this._pendingRequestedCompaction;
8941
9089
  const owner = checkpoint?.owner ?? pending?.owner ?? capturedOwner ?? this._captureCompactionOwner();
@@ -9018,6 +9166,7 @@ export class AgentSession {
9018
9166
  signal: abort.signal,
9019
9167
  // Only confirmed public-budget recovery resumes within the same invocation.
9020
9168
  budgetPressure: resumeInPlace,
9169
+ capacity,
9021
9170
  });
9022
9171
  const result = committed.result;
9023
9172
  // Release this captured source before arming success. A later release failure still carries the ACK.
@@ -9613,7 +9762,12 @@ export class AgentSession {
9613
9762
  producer?.skillOwner?.writer.assertCurrent();
9614
9763
  if (this.sessionManager !== manager)
9615
9764
  throw new Error("Native recovery source changed");
9616
- return await manager.readBranchHistory((history) => recoverCapturedHistory(history.branchContext, request, { ...DEFAULT_NATIVE_RECOVERY_LIMITS, maxBytes: responseBytes }, signal));
9765
+ const cursorSource = JSON.stringify([manager.getSessionId(), manager.getSessionFile()]);
9766
+ if (this._nativeRecoveryCursorSource !== cursorSource) {
9767
+ this._nativeRecoveryCursors.clear();
9768
+ this._nativeRecoveryCursorSource = cursorSource;
9769
+ }
9770
+ return await manager.readBranchHistory((history) => recoverCapturedHistory(history.branchContext, request, { ...DEFAULT_NATIVE_RECOVERY_LIMITS, maxBytes: responseBytes }, signal, this._nativeRecoveryCursors));
9617
9771
  }
9618
9772
  catch (error) {
9619
9773
  signal?.throwIfAborted();
@@ -9762,6 +9916,7 @@ export class AgentSession {
9762
9916
  BASE_CONTEXT_RLM_DEPTH: String(this._rlmDepth),
9763
9917
  BASE_CONTEXT_RLM_MAX_DEPTH: String(this._rlmMaxDepth),
9764
9918
  BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR: getGlobalHarnessStateDir(),
9919
+ BASE_CONTEXT_WORKSPACE_HARNESS_STATE_DIR: getWorkspaceHarnessStateDir(this._cwd),
9765
9920
  };
9766
9921
  const rlmSessionDir = this._ensureRlmSessionDir();
9767
9922
  if (rlmSessionDir) {
@@ -9854,15 +10009,13 @@ export class AgentSession {
9854
10009
  if (message?.role !== "assistant")
9855
10010
  return;
9856
10011
  if (this.sessionManager.isPersisted()) {
9857
- const acknowledged = this._assistantEntryIds.get(message);
9858
- if (!acknowledged ||
9859
- acknowledged.sessionId !== this.sessionId ||
9860
- acknowledged.sessionFile !== this.sessionFile)
10012
+ const entryId = this._findAssistantEntryIdForMessage(message);
10013
+ if (!entryId)
9861
10014
  throw new Error("Cannot identify the acknowledged assistant response removed by retry control");
9862
10015
  if (this._contextOmissions?.sessionId !== this.sessionId ||
9863
10016
  this._contextOmissions?.sessionFile !== this.sessionFile)
9864
10017
  this._contextOmissions = { sessionId: this.sessionId, sessionFile: this.sessionFile, ids: new Set() };
9865
- this._contextOmissions.ids.add(acknowledged.entryId);
10018
+ this._contextOmissions.ids.add(entryId);
9866
10019
  }
9867
10020
  this.agent.state.messages = messages.slice(0, -1);
9868
10021
  }