@ponythewhite/base-context 1.0.6 → 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.
- package/CHANGELOG.md +25 -0
- package/dist/base-context-runtime/pyproject.toml +1 -1
- package/dist/base-context-runtime/src/rlm/__init__.py +3 -0
- package/dist/base-context-runtime/src/rlm/harness.py +86 -48
- package/dist/base-context-runtime/test/test_harness.py +215 -140
- package/dist/base-context-runtime/uv.lock +1 -1
- package/dist/build-info.json +1 -1
- package/dist/bundle/amazon-bedrock.js +3 -3
- package/dist/bundle/{anthropic-UV5GZDGU.js → anthropic-5PSCZEER.js} +2 -7
- package/dist/bundle/{azure-openai-responses-WSNPXLYC.js → azure-openai-responses-AYTLJR33.js} +2 -2
- package/dist/bundle/{bundled-modules-MEACET4U.js → bundled-modules-IGMJH2MU.js} +5 -5
- package/dist/bundle/{chunk-UPVDERMD.js → chunk-2IPDSTDR.js} +11 -9
- package/dist/bundle/chunk-D3KOF57W.js +22040 -0
- package/dist/bundle/{chunk-QKI6KEGP.js → chunk-EXEFSSF4.js} +1509 -577
- package/dist/bundle/{chunk-VSIYFWYI.js → chunk-KT2BNEPE.js} +21 -8
- package/dist/bundle/{chunk-KTNETTHI.js → chunk-Q3MBSBEB.js} +3 -101
- package/dist/bundle/{chunk-LFPWHI42.js → chunk-QKIFBA7N.js} +2 -2
- package/dist/bundle/{chunk-LIZVVGHR.js → chunk-RVJVXZCS.js} +13 -10
- package/dist/bundle/{cli-main-ANUJY7Y4.js → cli-main-BUK7QTHO.js} +4 -4
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/{google-T32FMD4G.js → google-HTMCUJ7X.js} +2 -2
- package/dist/bundle/{google-vertex-56KP6I3B.js → google-vertex-ZQO65AXF.js} +2 -2
- package/dist/bundle/{main-4GIKOHIT.js → main-2FWHT2WZ.js} +5 -5
- package/dist/bundle/{mistral-WGBMYIVU.js → mistral-SB7LU56G.js} +1 -1
- package/dist/bundle/{openai-codex-responses-PIV2RQ3G.js → openai-codex-responses-5CEW4VN3.js} +53 -27
- package/dist/bundle/{openai-completions-64C3JPR4.js → openai-completions-BAZJQSS2.js} +1 -1
- package/dist/bundle/{openai-responses-UI7RSTDV.js → openai-responses-VHOE427P.js} +48 -5
- package/dist/core/agent-session.d.ts +6 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +189 -31
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/canonical-context.d.ts +8 -0
- package/dist/core/canonical-context.d.ts.map +1 -1
- package/dist/core/canonical-context.js +37 -5
- package/dist/core/canonical-context.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +2 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +31 -13
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/context-epoch.d.ts +1 -1
- package/dist/core/context-epoch.d.ts.map +1 -1
- package/dist/core/context-epoch.js +20 -12
- package/dist/core/context-epoch.js.map +1 -1
- package/dist/core/extensions/types.d.ts +2 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/history-index-worker.d.ts.map +1 -1
- package/dist/core/history-index-worker.js +29 -8
- package/dist/core/history-index-worker.js.map +1 -1
- package/dist/core/history-index.d.ts +11 -1
- package/dist/core/history-index.d.ts.map +1 -1
- package/dist/core/history-index.js +2 -1
- package/dist/core/history-index.js.map +1 -1
- package/dist/core/inference-coordinator.d.ts.map +1 -1
- package/dist/core/inference-coordinator.js +110 -13
- package/dist/core/inference-coordinator.js.map +1 -1
- package/dist/core/kernel/bootstrap.d.ts.map +1 -1
- package/dist/core/kernel/bootstrap.js +97 -107
- package/dist/core/kernel/bootstrap.js.map +1 -1
- package/dist/core/kernel/repl-manager.d.ts.map +1 -1
- package/dist/core/kernel/repl-manager.js +11 -0
- package/dist/core/kernel/repl-manager.js.map +1 -1
- package/dist/core/kernel/shared.d.ts +2 -0
- package/dist/core/kernel/shared.d.ts.map +1 -1
- package/dist/core/kernel/shared.js.map +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +4 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/prompts/rlm.d.ts.map +1 -1
- package/dist/core/prompts/rlm.js +4 -2
- package/dist/core/prompts/rlm.js.map +1 -1
- package/dist/core/public-context.d.ts.map +1 -1
- package/dist/core/public-context.js +16 -0
- package/dist/core/public-context.js.map +1 -1
- package/dist/core/refinement/refinement.d.ts +19 -5
- package/dist/core/refinement/refinement.d.ts.map +1 -1
- package/dist/core/refinement/refinement.js +90 -35
- package/dist/core/refinement/refinement.js.map +1 -1
- package/dist/core/request-view-selection.d.ts +12 -4
- package/dist/core/request-view-selection.d.ts.map +1 -1
- package/dist/core/request-view-selection.js +242 -100
- package/dist/core/request-view-selection.js.map +1 -1
- package/dist/core/retained-tool-output.d.ts +28 -0
- package/dist/core/retained-tool-output.d.ts.map +1 -0
- package/dist/core/retained-tool-output.js +102 -0
- package/dist/core/retained-tool-output.js.map +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/selective-recovery.d.ts +47 -3
- package/dist/core/selective-recovery.d.ts.map +1 -1
- package/dist/core/selective-recovery.js +428 -109
- package/dist/core/selective-recovery.js.map +1 -1
- package/dist/core/session-history-index.d.ts +3 -1
- package/dist/core/session-history-index.d.ts.map +1 -1
- package/dist/core/session-history-index.js +1 -1
- package/dist/core/session-history-index.js.map +1 -1
- package/dist/core/settings-manager.d.ts +5 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +3 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts +3 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +3 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/task-frame.d.ts +1 -1
- package/dist/core/task-frame.d.ts.map +1 -1
- package/dist/core/task-frame.js +21 -1
- package/dist/core/task-frame.js.map +1 -1
- package/dist/core/task-state-reader.d.ts +10 -2
- package/dist/core/task-state-reader.d.ts.map +1 -1
- package/dist/core/task-state-reader.js +75 -9
- package/dist/core/task-state-reader.js.map +1 -1
- package/dist/core/tools/ipython.d.ts +5 -0
- package/dist/core/tools/ipython.d.ts.map +1 -1
- package/dist/core/tools/ipython.js +59 -17
- package/dist/core/tools/ipython.js.map +1 -1
- package/dist/core/tools/prime-context.d.ts.map +1 -1
- package/dist/core/tools/prime-context.js +1 -1
- package/dist/core/tools/prime-context.js.map +1 -1
- package/dist/core/view-units.d.ts +4 -0
- package/dist/core/view-units.d.ts.map +1 -1
- package/dist/core/view-units.js +28 -19
- package/dist/core/view-units.js.map +1 -1
- package/dist/installer.mjs +98 -77
- package/dist/modes/interactive/components/refinement-outcome-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/refinement-outcome-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +5 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/skills/skill-creator/SKILL.md +18 -1
- package/docs/settings.md +45 -0
- package/docs/skills.md +77 -2
- package/package.json +4 -4
- package/skills/skill-creator/SKILL.md +18 -1
- 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();
|
|
@@ -592,7 +596,9 @@ export class AgentSession {
|
|
|
592
596
|
try {
|
|
593
597
|
const messages = await captured.readHistory(async (view) => {
|
|
594
598
|
const sameSource = controls?.sessionId === view.source.sessionId && controls?.sessionFile === view.source.sessionFile;
|
|
595
|
-
const result = await this._contextCompiler.compile(view, limits, sameSource ? omitted : undefined, {}, resource, this._initialContextMode,
|
|
599
|
+
const result = await this._contextCompiler.compile(view, limits, sameSource ? omitted : undefined, {}, resource, this._initialContextMode,
|
|
600
|
+
// This MAIN owner binds public admission below, independently of optional token budgets.
|
|
601
|
+
true);
|
|
596
602
|
if (sameSource && this._contextOmissions === controls) {
|
|
597
603
|
// Only prune this captured set. A newer control or source switch must survive this read.
|
|
598
604
|
for (const id of omitted)
|
|
@@ -608,7 +614,9 @@ export class AgentSession {
|
|
|
608
614
|
const epochContext = getCanonicalEpochContext(messages);
|
|
609
615
|
if (epochContext)
|
|
610
616
|
this._contextMode = epochContext.mode;
|
|
611
|
-
const
|
|
617
|
+
const unbudgetedPublic = !contextEpochsEnabled &&
|
|
618
|
+
Boolean(epochContext?.toolContinuations?.length || epochContext?.taskFrameRebased || epochContext?.checkpoint);
|
|
619
|
+
const nativeSkills = Boolean(contextEpochsEnabled || epochContext?.checkpoint || unbudgetedPublic);
|
|
612
620
|
const skillPolicy = nativeSkills ? (this._nativeRecoveryEnabled() ? "enabled" : "unavailable") : undefined;
|
|
613
621
|
if (this._baseSystemPromptOptions.nativeSkillSelection !== skillPolicy) {
|
|
614
622
|
const previousBase = this._baseSystemPrompt;
|
|
@@ -618,20 +626,23 @@ export class AgentSession {
|
|
|
618
626
|
if (epochContext?.resourceRevision !== undefined &&
|
|
619
627
|
getCanonicalViewUnits(messages)?.length !== messages.length)
|
|
620
628
|
throw new Error("Current resource view requires tracked canonical context");
|
|
621
|
-
if (epochContext?.checkpoint
|
|
629
|
+
if ((epochContext?.checkpoint || unbudgetedPublic) &&
|
|
630
|
+
getCanonicalViewUnits(messages)?.length !== messages.length)
|
|
622
631
|
throw new Error("Committed context epoch cannot admit untracked transient messages");
|
|
623
632
|
if (epochContext &&
|
|
624
|
-
(contextEpochsEnabled || epochContext.checkpoint) &&
|
|
633
|
+
(contextEpochsEnabled || epochContext.checkpoint || unbudgetedPublic) &&
|
|
625
634
|
getCanonicalViewUnits(messages)?.length === messages.length) {
|
|
626
635
|
let committed = epochContext.checkpoint;
|
|
627
636
|
let committedEntry = epochContext.checkpointEntry;
|
|
628
|
-
const fixed = epochContext.mode === "off" ||
|
|
637
|
+
const fixed = epochContext.mode === "off" ||
|
|
638
|
+
(committed?.policyOnly === true && !contextEpochsEnabled && !unbudgetedPublic);
|
|
629
639
|
let requestContract = fixed ? retainedContextRequestContract(committed) : undefined;
|
|
630
640
|
const nativeTail = messages.some((message) => message.role === "toolResult" ||
|
|
631
641
|
(message.role === "assistant" &&
|
|
632
642
|
message.content.some((part) => part.type !== "text" || part.textSignature !== undefined)));
|
|
633
643
|
let accepted;
|
|
634
644
|
let acceptedBody;
|
|
645
|
+
let acceptedResponseIdentity;
|
|
635
646
|
let acceptedReplayContract;
|
|
636
647
|
let acknowledged;
|
|
637
648
|
const commitFailure = (cause) => {
|
|
@@ -651,7 +662,7 @@ export class AgentSession {
|
|
|
651
662
|
candidate.projection.pendingPublicMessageGroups?.length))
|
|
652
663
|
throw new Error("Tool continuation requires its exact public candidate before epoch ACK");
|
|
653
664
|
assertResourceCurrent(resource);
|
|
654
|
-
const representation = contextEpochRepresentation(candidate.request, candidate.assessment, limits.maxSourceBytes);
|
|
665
|
+
const representation = contextEpochRepresentation(candidate.request, candidate.assessment, limits.maxSourceBytes, unbudgetedPublic, candidate.responseItemIdentity);
|
|
655
666
|
const replayContract = "replayContract" in candidate.projection &&
|
|
656
667
|
candidate.projection.replayContract === "message-groups"
|
|
657
668
|
? "message-groups"
|
|
@@ -664,6 +675,7 @@ export class AgentSession {
|
|
|
664
675
|
candidate.publicMessages !== undefined,
|
|
665
676
|
candidate.selectedUnitIds,
|
|
666
677
|
]);
|
|
678
|
+
acceptedResponseIdentity = candidate.responseItemIdentity;
|
|
667
679
|
if (accepted !== undefined) {
|
|
668
680
|
if (accepted !== selection)
|
|
669
681
|
throw new Error("Captured epoch request selection changed after acceptance");
|
|
@@ -691,7 +703,7 @@ export class AgentSession {
|
|
|
691
703
|
const prepared = prepareCanonicalEpoch(candidate.publicMessages ?? messages, candidate.selectedUnitIds, representation, limits.maxSourceBytes, replayContract, publicWindow);
|
|
692
704
|
if (JSON.stringify(prepared.checkpoint.source) !== JSON.stringify(candidate.source))
|
|
693
705
|
throw new Error("Context epoch candidate does not match its captured source");
|
|
694
|
-
const tokensBefore = candidate.originalAssessment
|
|
706
|
+
const tokensBefore = candidate.originalAssessment?.estimatedInputTokens ?? null;
|
|
695
707
|
const result = {
|
|
696
708
|
summary: "",
|
|
697
709
|
firstKeptEntryId: prepared.checkpoint.literalTailId,
|
|
@@ -733,12 +745,13 @@ export class AgentSession {
|
|
|
733
745
|
}
|
|
734
746
|
return;
|
|
735
747
|
}
|
|
748
|
+
if (epochContext.taskFrameRebased && acceptedBody === undefined)
|
|
749
|
+
throw new Error("Task frame rebase requires a committed epoch boundary");
|
|
736
750
|
if (!committed)
|
|
737
751
|
return;
|
|
738
752
|
if (acceptedBody === undefined || request.body !== acceptedBody)
|
|
739
753
|
throw new Error("Committed context epoch requires a compatible final provider projection");
|
|
740
|
-
if (contextEpochRepresentation(request, assessment, limits.maxSourceBytes) !==
|
|
741
|
-
committed.representation)
|
|
754
|
+
if (contextEpochRepresentation(request, assessment, limits.maxSourceBytes, unbudgetedPublic, acceptedResponseIdentity) !== committed.representation)
|
|
742
755
|
throw new Error("Context epoch representation changed without a committed boundary");
|
|
743
756
|
if (committed.resourceRevision !== epochContext.resourceRevision)
|
|
744
757
|
throw new Error("Context epoch resource revision requires a committed boundary");
|
|
@@ -839,9 +852,41 @@ export class AgentSession {
|
|
|
839
852
|
error.source.sessionFile !== owner.sessionFile ||
|
|
840
853
|
error.source.leafId !== owner.manager.getLeafId())
|
|
841
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;
|
|
842
863
|
// The loop released its unsent inference and captured projection before this handoff.
|
|
843
864
|
// Continue in-place only after the real summary ACK and canonical owner adoption.
|
|
844
|
-
|
|
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
|
+
}
|
|
845
890
|
});
|
|
846
891
|
this.agent.bindProviderFailureRecoveryOwner({
|
|
847
892
|
recover: (message, signal) => this._handleRetryableError(message, signal),
|
|
@@ -4102,6 +4147,9 @@ export class AgentSession {
|
|
|
4102
4147
|
* the latest state reaches disk instead of racing process exit.
|
|
4103
4148
|
*/
|
|
4104
4149
|
async disposeAsync(options) {
|
|
4150
|
+
this._failedAutomaticCompaction = undefined;
|
|
4151
|
+
this._contextCompiler.clear();
|
|
4152
|
+
this._nativeRecoveryCursors.clear();
|
|
4105
4153
|
for (const admission of this._rlmChildAdmissions)
|
|
4106
4154
|
admission.cancel("Parent session disposed");
|
|
4107
4155
|
if (this._disposeAsyncPromise)
|
|
@@ -4335,6 +4383,9 @@ export class AgentSession {
|
|
|
4335
4383
|
return this._disposeCallbacksPromise;
|
|
4336
4384
|
}
|
|
4337
4385
|
dispose() {
|
|
4386
|
+
this._failedAutomaticCompaction = undefined;
|
|
4387
|
+
this._contextCompiler.clear();
|
|
4388
|
+
this._nativeRecoveryCursors.clear();
|
|
4338
4389
|
if (this._disposed) {
|
|
4339
4390
|
return;
|
|
4340
4391
|
}
|
|
@@ -4578,7 +4629,7 @@ export class AgentSession {
|
|
|
4578
4629
|
}
|
|
4579
4630
|
return Array.from(unique);
|
|
4580
4631
|
}
|
|
4581
|
-
_rebuildSystemPrompt(toolNames, nativeEpoch = this.sessionManager.isPersisted() && this._contextEpochsEnabled) {
|
|
4632
|
+
_rebuildSystemPrompt(toolNames, nativeEpoch = this.sessionManager.isPersisted() && this._contextEpochsEnabled, operation = this._baseSystemPromptOptions?.errorFixSelection?.operation) {
|
|
4582
4633
|
const validToolNames = toolNames.filter((name) => this._toolRegistry.has(name));
|
|
4583
4634
|
const toolSnippets = {};
|
|
4584
4635
|
const promptGuidelines = [];
|
|
@@ -4612,6 +4663,14 @@ export class AgentSession {
|
|
|
4612
4663
|
rlmDepth: this._rlmDepth,
|
|
4613
4664
|
rlmParentAgent: this._rlmParentAgent,
|
|
4614
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
|
+
},
|
|
4615
4674
|
genericMcpServers: this._mcpManager?.getEnabledPersistentGenericServers(),
|
|
4616
4675
|
};
|
|
4617
4676
|
return buildSystemPrompt(this._baseSystemPromptOptions);
|
|
@@ -6094,6 +6153,12 @@ export class AgentSession {
|
|
|
6094
6153
|
const preparationAction = activeTurns().at(-1);
|
|
6095
6154
|
if (!preparationAction)
|
|
6096
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
|
+
}
|
|
6097
6162
|
const basePromptSnapshot = this._baseSystemPrompt;
|
|
6098
6163
|
const result = await this._extensionRunner.emitBeforeAgentStart(preparationAction.payload.text, preparationAction.payload.images, basePromptSnapshot, this._baseSystemPromptOptions);
|
|
6099
6164
|
if (activeTurns().at(-1) !== preparationAction)
|
|
@@ -7754,7 +7819,11 @@ export class AgentSession {
|
|
|
7754
7819
|
const summaryRequests = requests[captureNativeCompactionRequests]();
|
|
7755
7820
|
let summaryFailure;
|
|
7756
7821
|
try {
|
|
7757
|
-
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);
|
|
7758
7827
|
({ summary, firstKeptEntryId, tokensBefore, details, usage } = nativeCompaction);
|
|
7759
7828
|
}
|
|
7760
7829
|
catch (error) {
|
|
@@ -7816,7 +7885,7 @@ export class AgentSession {
|
|
|
7816
7885
|
try {
|
|
7817
7886
|
this._assertCompactionOwner(owner);
|
|
7818
7887
|
const bootstrap = await readSessionBootstrap(owner.manager, this.settingsManager.getCanonicalContextLimits(), {
|
|
7819
|
-
|
|
7888
|
+
purpose: "read",
|
|
7820
7889
|
initialContextMode: this._initialContextMode,
|
|
7821
7890
|
});
|
|
7822
7891
|
this._assertCompactionOwner(owner);
|
|
@@ -8315,9 +8384,9 @@ export class AgentSession {
|
|
|
8315
8384
|
}
|
|
8316
8385
|
}
|
|
8317
8386
|
catch (error) {
|
|
8318
|
-
//
|
|
8319
|
-
//
|
|
8320
|
-
|
|
8387
|
+
// Consume this exact automatic proposal after failure. New evidence can
|
|
8388
|
+
// request a new review; manual /refine remains independent.
|
|
8389
|
+
this._pendingAutoRefineReview = undefined;
|
|
8321
8390
|
this._lastAutoRefineReviewAt = Date.now();
|
|
8322
8391
|
if (error instanceof RefineSkippedError) {
|
|
8323
8392
|
// A skipped round is consumed like a reviewer decline, not retained for retry.
|
|
@@ -8357,7 +8426,78 @@ export class AgentSession {
|
|
|
8357
8426
|
throw new Error(`autoRefine.model thinkingLevel ${selection.thinkingLevel} is not supported by ${selection.provider}/${selection.modelId}`);
|
|
8358
8427
|
return { model: { ...model, cost: { ...model.cost } }, thinkingLevel: selection.thinkingLevel, explicit: true };
|
|
8359
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
|
+
}
|
|
8360
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;
|
|
8361
8501
|
if (this._autoRefineReviewer)
|
|
8362
8502
|
return this._autoRefineReviewer(context, signal);
|
|
8363
8503
|
const selected = this._resolveRefinementModel();
|
|
@@ -8365,7 +8505,6 @@ export class AgentSession {
|
|
|
8365
8505
|
return { shouldRefine: false, rationale: "No model selected." };
|
|
8366
8506
|
const { model, thinkingLevel, explicit } = selected;
|
|
8367
8507
|
const messages = structuredClone(this.agent.state.messages);
|
|
8368
|
-
const harnessState = this._loadMergedHarnessState();
|
|
8369
8508
|
const reviewContext = { ...context };
|
|
8370
8509
|
const requests = this.requests[captureNativeReviewerRequests]();
|
|
8371
8510
|
try {
|
|
@@ -8377,10 +8516,10 @@ export class AgentSession {
|
|
|
8377
8516
|
await requests.dispose();
|
|
8378
8517
|
}
|
|
8379
8518
|
}
|
|
8380
|
-
/**
|
|
8519
|
+
/** Merge existing session/global memory with the selected project's workspace memory. */
|
|
8381
8520
|
_loadMergedHarnessState() {
|
|
8382
8521
|
const localHarnessStateDir = this._localHarnessStateDir();
|
|
8383
|
-
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"));
|
|
8384
8523
|
}
|
|
8385
8524
|
async _loadRefinementHistory() {
|
|
8386
8525
|
const limits = {
|
|
@@ -8838,7 +8977,21 @@ export class AgentSession {
|
|
|
8838
8977
|
}
|
|
8839
8978
|
if (!this._isCompactionOwnerCurrent(owner))
|
|
8840
8979
|
return false;
|
|
8841
|
-
|
|
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
|
+
}
|
|
8842
8995
|
// Case 1: Overflow - takes priority over a pending model request so the error
|
|
8843
8996
|
// strip + retry still happen; the compaction it runs consumes the request.
|
|
8844
8997
|
if (!assistantIsFromBeforeCompaction &&
|
|
@@ -8930,7 +9083,7 @@ export class AgentSession {
|
|
|
8930
9083
|
this._emit({ type: "message_start", message: outcomeMessage });
|
|
8931
9084
|
this._emit({ type: "message_end", message: outcomeMessage });
|
|
8932
9085
|
}
|
|
8933
|
-
async _runAutoCompaction(reason, willRetry, capturedOwner, resumeInPlace = false) {
|
|
9086
|
+
async _runAutoCompaction(reason, willRetry, capturedOwner, resumeInPlace = false, capacity) {
|
|
8934
9087
|
const checkpoint = this._pendingCheckpoint;
|
|
8935
9088
|
const pending = this._pendingRequestedCompaction;
|
|
8936
9089
|
const owner = checkpoint?.owner ?? pending?.owner ?? capturedOwner ?? this._captureCompactionOwner();
|
|
@@ -9013,6 +9166,7 @@ export class AgentSession {
|
|
|
9013
9166
|
signal: abort.signal,
|
|
9014
9167
|
// Only confirmed public-budget recovery resumes within the same invocation.
|
|
9015
9168
|
budgetPressure: resumeInPlace,
|
|
9169
|
+
capacity,
|
|
9016
9170
|
});
|
|
9017
9171
|
const result = committed.result;
|
|
9018
9172
|
// Release this captured source before arming success. A later release failure still carries the ACK.
|
|
@@ -9608,7 +9762,12 @@ export class AgentSession {
|
|
|
9608
9762
|
producer?.skillOwner?.writer.assertCurrent();
|
|
9609
9763
|
if (this.sessionManager !== manager)
|
|
9610
9764
|
throw new Error("Native recovery source changed");
|
|
9611
|
-
|
|
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));
|
|
9612
9771
|
}
|
|
9613
9772
|
catch (error) {
|
|
9614
9773
|
signal?.throwIfAborted();
|
|
@@ -9757,6 +9916,7 @@ export class AgentSession {
|
|
|
9757
9916
|
BASE_CONTEXT_RLM_DEPTH: String(this._rlmDepth),
|
|
9758
9917
|
BASE_CONTEXT_RLM_MAX_DEPTH: String(this._rlmMaxDepth),
|
|
9759
9918
|
BASE_CONTEXT_GLOBAL_HARNESS_STATE_DIR: getGlobalHarnessStateDir(),
|
|
9919
|
+
BASE_CONTEXT_WORKSPACE_HARNESS_STATE_DIR: getWorkspaceHarnessStateDir(this._cwd),
|
|
9760
9920
|
};
|
|
9761
9921
|
const rlmSessionDir = this._ensureRlmSessionDir();
|
|
9762
9922
|
if (rlmSessionDir) {
|
|
@@ -9849,15 +10009,13 @@ export class AgentSession {
|
|
|
9849
10009
|
if (message?.role !== "assistant")
|
|
9850
10010
|
return;
|
|
9851
10011
|
if (this.sessionManager.isPersisted()) {
|
|
9852
|
-
const
|
|
9853
|
-
if (!
|
|
9854
|
-
acknowledged.sessionId !== this.sessionId ||
|
|
9855
|
-
acknowledged.sessionFile !== this.sessionFile)
|
|
10012
|
+
const entryId = this._findAssistantEntryIdForMessage(message);
|
|
10013
|
+
if (!entryId)
|
|
9856
10014
|
throw new Error("Cannot identify the acknowledged assistant response removed by retry control");
|
|
9857
10015
|
if (this._contextOmissions?.sessionId !== this.sessionId ||
|
|
9858
10016
|
this._contextOmissions?.sessionFile !== this.sessionFile)
|
|
9859
10017
|
this._contextOmissions = { sessionId: this.sessionId, sessionFile: this.sessionFile, ids: new Set() };
|
|
9860
|
-
this._contextOmissions.ids.add(
|
|
10018
|
+
this._contextOmissions.ids.add(entryId);
|
|
9861
10019
|
}
|
|
9862
10020
|
this.agent.state.messages = messages.slice(0, -1);
|
|
9863
10021
|
}
|
|
@@ -11977,7 +12135,7 @@ export class AgentSession {
|
|
|
11977
12135
|
if (label && !summaryText) {
|
|
11978
12136
|
await this.sessionManager.appendLabelChange(targetId, label);
|
|
11979
12137
|
}
|
|
11980
|
-
const { context: sessionContext } = await readSessionBootstrap(this.sessionManager, this.settingsManager.getCanonicalContextLimits(), {
|
|
12138
|
+
const { context: sessionContext } = await readSessionBootstrap(this.sessionManager, this.settingsManager.getCanonicalContextLimits(), { purpose: "read", initialContextMode: this._initialContextMode });
|
|
11981
12139
|
this.agent.state.messages = sessionContext.messages;
|
|
11982
12140
|
this._contextOmissions = undefined;
|
|
11983
12141
|
this._mergeUnpersistedOutcomes(this.agent.state.messages);
|