@caupulican/pi-adaptative 0.81.2 → 0.81.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.
- package/CHANGELOG.md +32 -0
- package/dist/core/agent-session.d.ts +2 -23
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +139 -147
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +2 -0
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +3 -0
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/billing-failover-controller.d.ts +38 -0
- package/dist/core/billing-failover-controller.d.ts.map +1 -0
- package/dist/core/billing-failover-controller.js +78 -0
- package/dist/core/billing-failover-controller.js.map +1 -0
- package/dist/core/billing-failover.d.ts +25 -0
- package/dist/core/billing-failover.d.ts.map +1 -0
- package/dist/core/billing-failover.js +21 -0
- package/dist/core/billing-failover.js.map +1 -0
- package/dist/core/compaction-support.d.ts +10 -1
- package/dist/core/compaction-support.d.ts.map +1 -1
- package/dist/core/compaction-support.js +68 -9
- package/dist/core/compaction-support.js.map +1 -1
- package/dist/core/context-pipeline.d.ts +1 -0
- package/dist/core/context-pipeline.d.ts.map +1 -1
- package/dist/core/context-pipeline.js +51 -14
- package/dist/core/context-pipeline.js.map +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +9 -9
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/failure-corpus.d.ts +49 -0
- package/dist/core/failure-corpus.d.ts.map +1 -0
- package/dist/core/failure-corpus.js +61 -0
- package/dist/core/failure-corpus.js.map +1 -0
- package/dist/core/model-router/config-diagnostics.d.ts +1 -1
- package/dist/core/model-router/config-diagnostics.d.ts.map +1 -1
- package/dist/core/model-router/config-diagnostics.js +29 -1
- package/dist/core/model-router/config-diagnostics.js.map +1 -1
- package/dist/core/model-router/fitness-gate.d.ts +25 -0
- package/dist/core/model-router/fitness-gate.d.ts.map +1 -0
- package/dist/core/model-router/fitness-gate.js +34 -0
- package/dist/core/model-router/fitness-gate.js.map +1 -0
- package/dist/core/model-router/status.d.ts +29 -1
- package/dist/core/model-router/status.d.ts.map +1 -1
- package/dist/core/model-router/status.js +31 -4
- package/dist/core/model-router/status.js.map +1 -1
- package/dist/core/model-router-controller.d.ts +10 -1
- package/dist/core/model-router-controller.d.ts.map +1 -1
- package/dist/core/model-router-controller.js +110 -27
- package/dist/core/model-router-controller.js.map +1 -1
- package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
- package/dist/core/models/default-model-suggestions.js +18 -0
- package/dist/core/models/default-model-suggestions.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +16 -0
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +88 -1
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/scout-controller.d.ts +44 -0
- package/dist/core/scout-controller.d.ts.map +1 -0
- package/dist/core/scout-controller.js +165 -0
- package/dist/core/scout-controller.js.map +1 -0
- package/dist/core/settings-manager.d.ts +17 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +47 -3
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +4 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +6 -8
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/context-scout.d.ts +19 -0
- package/dist/core/tools/context-scout.d.ts.map +1 -0
- package/dist/core/tools/context-scout.js +47 -0
- package/dist/core/tools/context-scout.js.map +1 -0
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/fitness-role-selector.d.ts +1 -1
- package/dist/modes/interactive/components/fitness-role-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/fitness-role-selector.js +5 -0
- package/dist/modes/interactive/components/fitness-role-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +77 -7
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +13 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/local-model-commands.d.ts +5 -2
- package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
- package/dist/modes/interactive/local-model-commands.js +129 -1
- package/dist/modes/interactive/local-model-commands.js.map +1 -1
- package/dist/modes/interactive/session-flow-commands.d.ts +16 -0
- package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-flow-commands.js +32 -0
- package/dist/modes/interactive/session-flow-commands.js.map +1 -1
- package/docs/bug-ledger.md +113 -0
- package/docs/index.md +1 -0
- package/docs/models.md +9 -0
- package/docs/providers.md +14 -0
- package/docs/scout.md +73 -0
- package/docs/settings.md +16 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/12-full-control.ts +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -1,21 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentSession - Core abstraction for agent lifecycle and session management.
|
|
3
|
-
*
|
|
4
|
-
* This class is shared between all run modes (interactive, print, rpc).
|
|
5
|
-
* It encapsulates:
|
|
6
|
-
* - Agent state access
|
|
7
|
-
* - Event subscription with automatic session persistence
|
|
8
|
-
* - Model and thinking level management
|
|
9
|
-
* - Compaction (manual and auto)
|
|
10
|
-
* - Bash execution
|
|
11
|
-
* - Session switching and branching
|
|
12
|
-
*
|
|
13
|
-
* Modes use this class and add their own I/O layer on top.
|
|
14
|
-
*/
|
|
15
1
|
import { readFileSync } from "node:fs";
|
|
16
2
|
import { basename, dirname, join } from "node:path";
|
|
17
3
|
import { classifyFailure, compactToolResultDetailsForRetention, computeRetryDelayMs, createCustomMessage, DEFAULT_RETRY_POLICY, RetryController, sleepAbortable, withStreamIdleWatchdog, } from "@caupulican/pi-agent-core";
|
|
18
|
-
import { calculateContextTokens, compact, estimateContextTokens, getLatestCompactionEntry, prepareCompaction, shouldCompact, } from "@caupulican/pi-agent-core/node";
|
|
4
|
+
import { calculateContextTokens, compact, createDeterministicCompaction, estimateContextTokens, getLatestCompactionEntry, prepareCompaction, runCompactionLoop, shouldCompact, } from "@caupulican/pi-agent-core/node";
|
|
19
5
|
import { cleanupSessionResources, isContextOverflow, streamSimple } from "@caupulican/pi-ai";
|
|
20
6
|
import { getAgentDir } from "../config.js";
|
|
21
7
|
import { stripFrontmatter } from "../utils/frontmatter.js";
|
|
@@ -24,11 +10,13 @@ import { buildForegroundEnvelope, formatForegroundEnvelopeObservation } from "./
|
|
|
24
10
|
import { AutonomyTelemetry } from "./autonomy-telemetry.js";
|
|
25
11
|
import { BackgroundLaneController } from "./background-lane-controller.js";
|
|
26
12
|
import { BashExecutionController } from "./bash-execution-controller.js";
|
|
13
|
+
import { BillingFailoverController, ExhaustedProviderRegistry } from "./billing-failover-controller.js";
|
|
27
14
|
import { CompactionSupport } from "./compaction-support.js";
|
|
28
15
|
import { buildContextCompositionReport, formatContextCompositionDashboard, } from "./context/context-composition.js";
|
|
29
16
|
import { ContextPipeline } from "./context-pipeline.js";
|
|
30
17
|
import { downgradeReasoning, estimateTurnCostUsd, evaluateCostGuard } from "./cost-guard.js";
|
|
31
18
|
import { appendWorkerResultSnapshot, getWorkerResultSnapshots } from "./delegation/session-worker-result.js";
|
|
19
|
+
import { FailureCorpusRecorder } from "./failure-corpus.js";
|
|
32
20
|
import { GatewayRegistry } from "./gateways/channel-provider.js";
|
|
33
21
|
import { GoalLoopController } from "./goal-loop-controller.js";
|
|
34
22
|
import { buildGoalRuntimeSnapshot, } from "./goals/goal-runtime-snapshot.js";
|
|
@@ -116,52 +104,34 @@ export class AgentSession {
|
|
|
116
104
|
_unsubscribeAgent;
|
|
117
105
|
_eventListeners = [];
|
|
118
106
|
_extensionsChangedListeners = [];
|
|
119
|
-
/** Tracks pending steering messages for UI display. Removed when delivered. */
|
|
120
107
|
_steeringMessages = [];
|
|
121
|
-
/** Tracks pending follow-up messages for UI display. Removed when delivered. */
|
|
122
108
|
_followUpMessages = [];
|
|
123
|
-
/** Tracks extension slash commands queued while the agent is streaming. */
|
|
124
109
|
_queuedExtensionCommands = [];
|
|
125
|
-
/** Messages queued to be included with the next user prompt as context ("asides"). */
|
|
126
110
|
_pendingNextTurnMessages = [];
|
|
127
|
-
/** Serializes prompt() submissions made while streaming so queued steering/follow-ups keep user-typed FIFO order. */
|
|
128
111
|
_streamingPromptSubmissionTail = Promise.resolve();
|
|
129
112
|
/**
|
|
130
113
|
* The last tool set requested via setActiveToolsByName BEFORE model-capability filtering, so
|
|
131
114
|
* switching from a small-window model back to a large one restores the full requested set.
|
|
132
115
|
*/
|
|
133
116
|
_requestedActiveToolNames;
|
|
134
|
-
// Compaction/context hygiene state
|
|
135
117
|
_compactionAbortController = undefined;
|
|
136
118
|
_autoCompactionAbortController = undefined;
|
|
137
119
|
_overflowRecoveryAttempted = false;
|
|
138
120
|
_unboundToolGrantWarnings = [];
|
|
139
|
-
// Branch summarization state
|
|
140
121
|
_branchSummaryAbortController = undefined;
|
|
141
|
-
// Auto-retry driver — owns the attempt counter and abortable backoff (reliability kernel).
|
|
142
122
|
_retryController;
|
|
143
|
-
// Bash execution state
|
|
144
|
-
/** User-facing model + thinking-level selection (see model-selection-controller.ts). */
|
|
145
123
|
_modelSelection;
|
|
146
|
-
/** Standalone bash-command execution (see bash-execution-controller.ts); owns the bash abort
|
|
147
|
-
* controller and the streaming-deferred pending-message queue. */
|
|
148
124
|
_bash;
|
|
149
|
-
/** Resource-profile tool/extension gating + reload-time profile model re-apply (see profile-filter-controller.ts). */
|
|
150
125
|
_profileFilter;
|
|
151
|
-
/** Agent tool-call gate: escalation, autonomy gating, extension hooks, untrusted boundary (see tool-gate-controller.ts). */
|
|
152
126
|
_toolGate;
|
|
153
|
-
// Extension system
|
|
154
127
|
_extensionRunner;
|
|
155
128
|
_turnIndex = 0;
|
|
156
|
-
/** G7: per-turn foreground CapabilityEnvelope auto-built for visibility (observe-only; not enforced). */
|
|
157
129
|
_currentForegroundEnvelope;
|
|
158
130
|
_resourceLoader;
|
|
159
131
|
_customTools;
|
|
160
132
|
_cwd;
|
|
161
133
|
_agentDir;
|
|
162
134
|
_collectWorkspaceSources;
|
|
163
|
-
/** Owns the cached per-server OllamaRuntime instances and the local-model router readiness gate
|
|
164
|
-
* (see local-runtime-controller.ts). */
|
|
165
135
|
_localRuntimeController;
|
|
166
136
|
/** Assembles the session's base system prompt from live session state (see
|
|
167
137
|
* system-prompt-builder.ts); owns the paired _baseSystemPromptOptions. */
|
|
@@ -177,7 +147,6 @@ export class AgentSession {
|
|
|
177
147
|
* latest retrieval/prompt-inclusion reports, the reload-safe MemoryManager, the recall
|
|
178
148
|
* effectiveness tracker, and the extension-contributed pending providers. */
|
|
179
149
|
_memory;
|
|
180
|
-
/** Summarizer model/auth/settings resolution (see compaction-support.ts). */
|
|
181
150
|
_compactionSupport;
|
|
182
151
|
/** Per-turn context-shaping subsystem (see context-pipeline.ts); owns the latest
|
|
183
152
|
* audit/policy/correlation/enforcement/gc reports, the brain-curation sidecar + its skip reasons,
|
|
@@ -190,28 +159,22 @@ export class AgentSession {
|
|
|
190
159
|
_toolProfileFilter;
|
|
191
160
|
_isExplicitModel;
|
|
192
161
|
_isExplicitThinking;
|
|
193
|
-
/** R8: registry for deployment-supplied gateway channels + schedulers (lifecycle driven by the host runner). */
|
|
194
162
|
_gatewayRegistry = new GatewayRegistry();
|
|
195
|
-
/** Cache for getSpawnedUsage(), keyed by session entry count (Bug #22 — avoid O(N) per render frame). */
|
|
196
163
|
/** Usage/cost/stats accounting, /context estimate, and session export (see session-analytics.ts);
|
|
197
164
|
* owns the spawned-usage and daily-usage memo caches. */
|
|
198
165
|
_analytics;
|
|
199
|
-
/** In-file session-tree branch switching + fork-selector reads (see session-tree-navigator.ts). */
|
|
200
166
|
_treeNavigator;
|
|
201
|
-
/** Latest proactive cost-guard decision (#34), for the host UI to surface. Undefined when disabled. */
|
|
202
167
|
_lastCostGuardDecision;
|
|
203
|
-
/** One-shot latch so the cost guard downgrades reasoning once per over-threshold episode, not every call. */
|
|
204
168
|
_costGuardDowngraded = false;
|
|
205
169
|
/** Per-turn model-router subsystem (see model-router-controller.ts); owns the transient route/intent,
|
|
206
170
|
* the cheap-turn session buffer, the escalation/retry flags, and the sticky last-decision/skip-reason
|
|
207
171
|
* used by the status report. Its parallel routed drive path delegates every turn back to
|
|
208
172
|
* {@link _runAgentPrompt} so the drive loop stays host-side. */
|
|
209
173
|
_modelRouter;
|
|
210
|
-
|
|
174
|
+
_billingFailover;
|
|
175
|
+
_failureCorpus;
|
|
211
176
|
_skillCuratorInstance;
|
|
212
|
-
/** Set on dispose so in-flight background reflection bails instead of writing to a dead session (Bug #21). */
|
|
213
177
|
_disposed = false;
|
|
214
|
-
/** Aborts in-flight background reflection completions on dispose (Bug #21). */
|
|
215
178
|
_reflectionAbort = new AbortController();
|
|
216
179
|
/** Native reflection engine + learning-apply/rollback path (see reflection-controller.ts); owns no
|
|
217
180
|
* session state, applies durable writes through the bundled memory tool and the session log. */
|
|
@@ -229,7 +192,6 @@ export class AgentSession {
|
|
|
229
192
|
_extensionShutdownHandler;
|
|
230
193
|
_extensionErrorListener;
|
|
231
194
|
_extensionErrorUnsubscriber;
|
|
232
|
-
// Model registry for API key resolution
|
|
233
195
|
_modelRegistry;
|
|
234
196
|
/** Tool-registry assembly + the self-modification-safe extension reload (see runtime-builder.ts);
|
|
235
197
|
* owns the base/wrapped tool definitions, the live tool registry, and the per-tool prompt
|
|
@@ -314,6 +276,7 @@ export class AgentSession {
|
|
|
314
276
|
getLearningDecisionSnapshots: () => this.getLearningDecisionSnapshots(),
|
|
315
277
|
getLearningAuditRecords: () => this.getLearningAuditRecords(),
|
|
316
278
|
});
|
|
279
|
+
this._modelRegistry = config.modelRegistry;
|
|
317
280
|
this._backgroundLanes = new BackgroundLaneController({
|
|
318
281
|
isDisposed: () => this._disposed,
|
|
319
282
|
isChildSession: () => this._isChildSession,
|
|
@@ -323,6 +286,7 @@ export class AgentSession {
|
|
|
323
286
|
getSessionManager: () => this.sessionManager,
|
|
324
287
|
getSettingsManager: () => this.settingsManager,
|
|
325
288
|
getModelRegistry: () => this._modelRegistry,
|
|
289
|
+
isModelExhausted: (model) => this._billingFailover.isExhausted(`${model.provider}/${model.id}`),
|
|
326
290
|
getModel: () => this.model ?? undefined,
|
|
327
291
|
getCapabilityEnvelope: () => this.capabilityEnvelope,
|
|
328
292
|
getModelCapabilityProfile: () => this.getModelCapabilityProfile(),
|
|
@@ -353,6 +317,12 @@ export class AgentSession {
|
|
|
353
317
|
getModelRegistry: () => this._modelRegistry,
|
|
354
318
|
isRawStream: () => this._isRawStreamSimple(this.agent.streamFn),
|
|
355
319
|
getRequiredRequestAuth: (model) => this._getRequiredRequestAuth(model),
|
|
320
|
+
isModelExhausted: (ref) => this._billingFailover.isExhausted(ref),
|
|
321
|
+
getStoredFitnessReport: (ref) => this.getStoredFitnessReports().find((entry) => entry.model === ref)?.report,
|
|
322
|
+
// Live context is an over-estimate of the span to summarize (includes the kept tail) —
|
|
323
|
+
// conservative in the safe direction for the summarizer capacity check.
|
|
324
|
+
estimateSummarizationInputTokens: () => this._pipeline.estimateCurrentContextTokens(this.agent.state.messages),
|
|
325
|
+
emitWarning: (message) => this._emit({ type: "warning", message }),
|
|
356
326
|
});
|
|
357
327
|
this._pipeline = new ContextPipeline({
|
|
358
328
|
getTurnIndex: () => this._turnIndex,
|
|
@@ -367,12 +337,25 @@ export class AgentSession {
|
|
|
367
337
|
addSpawnedUsage: (usage, opts) => this.addSpawnedUsage(usage, opts),
|
|
368
338
|
runIsolatedCompletion: (opts) => this.runIsolatedCompletion(opts),
|
|
369
339
|
});
|
|
340
|
+
this._failureCorpus = new FailureCorpusRecorder({
|
|
341
|
+
filePath: join(this._agentDir, "state", "failure-corpus.jsonl"),
|
|
342
|
+
});
|
|
343
|
+
this._billingFailover = new BillingFailoverController({
|
|
344
|
+
agent: this.agent,
|
|
345
|
+
modelRegistry: this._modelRegistry,
|
|
346
|
+
exhausted: new ExhaustedProviderRegistry(),
|
|
347
|
+
subscriptionHop: this.settingsManager.getFailoverSettings().subscriptionHop,
|
|
348
|
+
emit: (event) => this._emit(event),
|
|
349
|
+
recordFailure: (args) => this._failureCorpus.record(args),
|
|
350
|
+
});
|
|
370
351
|
this._modelRouter = new ModelRouterController({
|
|
371
352
|
getAgent: () => this.agent,
|
|
372
353
|
getModel: () => this.model ?? undefined,
|
|
373
354
|
getSettingsManager: () => this.settingsManager,
|
|
374
355
|
getSessionManager: () => this.sessionManager,
|
|
375
356
|
getModelRegistry: () => this._modelRegistry,
|
|
357
|
+
isModelExhausted: (model) => this._billingFailover.isExhausted(`${model.provider}/${model.id}`),
|
|
358
|
+
getFailoverStatus: () => ({ ...this._billingFailover.getStatus(), failureStats: this._failureCorpus.stats() }),
|
|
376
359
|
getAgentDir: () => this._agentDir,
|
|
377
360
|
getReflectionSignal: () => this._reflectionAbort.signal,
|
|
378
361
|
getBaseSystemPrompt: () => this._baseSystemPrompt,
|
|
@@ -407,7 +390,6 @@ export class AgentSession {
|
|
|
407
390
|
getGoalRuntimeSnapshot: (settings) => this.getGoalRuntimeSnapshot(settings),
|
|
408
391
|
prompt: (text, options) => this.prompt(text, options),
|
|
409
392
|
});
|
|
410
|
-
this._modelRegistry = config.modelRegistry;
|
|
411
393
|
this._extensionRunnerRef = config.extensionRunnerRef;
|
|
412
394
|
this._initialActiveToolNames = config.initialActiveToolNames;
|
|
413
395
|
this._allowedToolNames = config.allowedToolNames ? new Set(config.allowedToolNames) : undefined;
|
|
@@ -423,9 +405,11 @@ export class AgentSession {
|
|
|
423
405
|
this._runtimeBuilder = new RuntimeBuilder({
|
|
424
406
|
getAgent: () => this.agent,
|
|
425
407
|
getCwd: () => this._cwd,
|
|
408
|
+
getAgentDir: () => this._agentDir,
|
|
426
409
|
getSessionManager: () => this.sessionManager,
|
|
427
410
|
getSettingsManager: () => this.settingsManager,
|
|
428
411
|
getModelRegistry: () => this._modelRegistry,
|
|
412
|
+
isModelExhausted: (model) => this._billingFailover.isExhausted(`${model.provider}/${model.id}`),
|
|
429
413
|
getResourceLoader: () => this._resourceLoader,
|
|
430
414
|
getExtensionRunner: () => this._extensionRunner,
|
|
431
415
|
setExtensionRunner: (runner) => {
|
|
@@ -1530,7 +1514,6 @@ export class AgentSession {
|
|
|
1530
1514
|
async _ensureRouteModelReady(resolved) {
|
|
1531
1515
|
return this._localRuntimeController.ensureRouteModelReady(resolved);
|
|
1532
1516
|
}
|
|
1533
|
-
/** Model-router status + config diagnostics report. Delegates to {@link ModelRouterController}. */
|
|
1534
1517
|
getModelRouterStatus(formatLabel) {
|
|
1535
1518
|
return this._modelRouter.getStatus(formatLabel);
|
|
1536
1519
|
}
|
|
@@ -1543,6 +1526,8 @@ export class AgentSession {
|
|
|
1543
1526
|
if (this._isRetryableError(msg) && (await this._prepareRetry(msg))) {
|
|
1544
1527
|
return true;
|
|
1545
1528
|
}
|
|
1529
|
+
if (await this._billingFailover.handleAssistantError(msg))
|
|
1530
|
+
return false;
|
|
1546
1531
|
if (msg.stopReason === "error" && this._retryController.attempt > 0) {
|
|
1547
1532
|
this._emit({
|
|
1548
1533
|
type: "auto_retry_end",
|
|
@@ -2190,7 +2175,7 @@ export class AgentSession {
|
|
|
2190
2175
|
else {
|
|
2191
2176
|
// Generate compaction result
|
|
2192
2177
|
const compactionSignal = this._compactionAbortController.signal;
|
|
2193
|
-
const result = await this._compactWithRetry(() => compact(preparation, compactionModel, apiKey, headers, customInstructions, compactionSignal, compactionThinkingLevel, this.agent.streamFn, this._buildCompactionPreDigest()), compactionSignal);
|
|
2178
|
+
const result = await this._compactWithRetry(() => compact(preparation, compactionModel, apiKey, headers, customInstructions, compactionSignal, compactionThinkingLevel, this.agent.streamFn, this._buildCompactionPreDigest()), compactionSignal, compactionModel.provider);
|
|
2194
2179
|
summary = result.summary;
|
|
2195
2180
|
firstKeptEntryId = result.firstKeptEntryId;
|
|
2196
2181
|
tokensBefore = result.tokensBefore;
|
|
@@ -2388,10 +2373,16 @@ export class AgentSession {
|
|
|
2388
2373
|
*/
|
|
2389
2374
|
async _runAutoCompaction(reason, willRetry) {
|
|
2390
2375
|
const settings = this._getAdaptedCompactionSettings();
|
|
2376
|
+
const model = this.model;
|
|
2391
2377
|
this._emit({ type: "compaction_start", reason });
|
|
2378
|
+
const hadQueuedMessages = this.agent.hasQueuedMessages();
|
|
2392
2379
|
this._autoCompactionAbortController = new AbortController();
|
|
2380
|
+
const signal = this._autoCompactionAbortController.signal;
|
|
2381
|
+
let fromExtension = false;
|
|
2382
|
+
let lastCompaction;
|
|
2383
|
+
let extensionCancelled = false;
|
|
2393
2384
|
try {
|
|
2394
|
-
if (!
|
|
2385
|
+
if (!model) {
|
|
2395
2386
|
this._emit({
|
|
2396
2387
|
type: "compaction_end",
|
|
2397
2388
|
reason,
|
|
@@ -2402,113 +2393,107 @@ export class AgentSession {
|
|
|
2402
2393
|
});
|
|
2403
2394
|
return false;
|
|
2404
2395
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
this.
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2396
|
+
const contextWindow = model.contextWindow;
|
|
2397
|
+
const triggerThreshold = Math.max(0, contextWindow - settings.reserveTokens);
|
|
2398
|
+
const margin = Math.max(0, Math.floor(0.01 * contextWindow));
|
|
2399
|
+
let measureReads = 0;
|
|
2400
|
+
const outcome = await runCompactionLoop({
|
|
2401
|
+
getBranch: () => this.sessionManager.getBranch(),
|
|
2402
|
+
measureLiveTokens: () => {
|
|
2403
|
+
const measured = this._pipeline.estimateCurrentContextTokens(this.agent.state.messages);
|
|
2404
|
+
return measureReads++ === 0 ? Math.max(measured, triggerThreshold + 1) : measured;
|
|
2405
|
+
},
|
|
2406
|
+
getTriggerThreshold: () => triggerThreshold,
|
|
2407
|
+
getMargin: () => margin,
|
|
2408
|
+
getBaseKeepRecentTokens: () => settings.keepRecentTokens,
|
|
2409
|
+
resolveModelAndAuth: async (modelTier) => this._resolveCompactionModelAndAuth(modelTier === "session" ? model : this._resolveCompactionModel(model), model),
|
|
2410
|
+
summarizeAndVerify: async (params, compactModel, apiKey, headers, branchEntries) => {
|
|
2411
|
+
fromExtension = false;
|
|
2412
|
+
const adaptedSettings = { ...settings, keepRecentTokens: params.keepRecentTokens };
|
|
2413
|
+
const preparation = prepareCompaction(branchEntries, adaptedSettings);
|
|
2414
|
+
if (!preparation) {
|
|
2415
|
+
throw new Error("already compacted");
|
|
2416
|
+
}
|
|
2417
|
+
const compactionThinkingLevel = this._resolveCompactionThinkingLevel(compactModel, model);
|
|
2418
|
+
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
2419
|
+
const extensionResult = (await this._extensionRunner.emit({
|
|
2420
|
+
type: "session_before_compact",
|
|
2421
|
+
preparation,
|
|
2422
|
+
branchEntries,
|
|
2423
|
+
customInstructions: undefined,
|
|
2424
|
+
signal,
|
|
2425
|
+
}));
|
|
2426
|
+
if (extensionResult?.cancel) {
|
|
2427
|
+
extensionCancelled = true;
|
|
2428
|
+
throw new Error("auto-compaction-cancelled");
|
|
2429
|
+
}
|
|
2430
|
+
if (extensionResult?.compaction) {
|
|
2431
|
+
fromExtension = true;
|
|
2432
|
+
return { result: extensionResult.compaction };
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
const compactResult = await this._compactWithRetry(() => compact(preparation, compactModel, apiKey, headers, undefined, signal, compactionThinkingLevel, this.agent.streamFn, this._buildCompactionPreDigest(), { chunked: params.chunked }), signal, compactModel.provider);
|
|
2436
|
+
return { result: compactResult };
|
|
2437
|
+
},
|
|
2438
|
+
buildDeterministicCheckpoint: async () => {
|
|
2439
|
+
const branch = this.sessionManager.getBranch();
|
|
2440
|
+
const preparation = prepareCompaction(branch, {
|
|
2441
|
+
...settings,
|
|
2442
|
+
});
|
|
2443
|
+
if (!preparation) {
|
|
2444
|
+
throw new Error("already compacted");
|
|
2445
|
+
}
|
|
2446
|
+
fromExtension = false;
|
|
2447
|
+
return { result: createDeterministicCompaction(preparation) };
|
|
2448
|
+
},
|
|
2449
|
+
apply: async (result) => {
|
|
2450
|
+
lastCompaction = result;
|
|
2451
|
+
this.sessionManager.appendCompaction(result.summary, result.firstKeptEntryId, result.tokensBefore, result.details, fromExtension);
|
|
2452
|
+
this.agent.state.messages = this.sessionManager.buildSessionContext().messages;
|
|
2453
|
+
const newEntries = this.sessionManager.getEntries();
|
|
2454
|
+
const savedCompactionEntry = newEntries.find((entry) => entry.type === "compaction" && entry.summary === result.summary);
|
|
2455
|
+
if (this._extensionRunner && savedCompactionEntry) {
|
|
2456
|
+
await this._extensionRunner.emit({
|
|
2457
|
+
type: "session_compact",
|
|
2458
|
+
compactionEntry: savedCompactionEntry,
|
|
2459
|
+
fromExtension,
|
|
2460
|
+
});
|
|
2461
|
+
}
|
|
2462
|
+
},
|
|
2463
|
+
onTransition: ({ cycle, cause }) => {
|
|
2464
|
+
this._emit({
|
|
2465
|
+
type: "warning",
|
|
2466
|
+
message: `auto-compaction cycle ${cycle}: ${cause} — retrying from step 0`,
|
|
2467
|
+
});
|
|
2468
|
+
},
|
|
2469
|
+
signal,
|
|
2470
|
+
});
|
|
2471
|
+
if (outcome.kind === "skip") {
|
|
2428
2472
|
this._emit({
|
|
2429
2473
|
type: "compaction_end",
|
|
2430
2474
|
reason,
|
|
2431
2475
|
result: undefined,
|
|
2432
2476
|
aborted: false,
|
|
2433
2477
|
willRetry: false,
|
|
2434
|
-
skipReason:
|
|
2478
|
+
skipReason: outcome.reason,
|
|
2435
2479
|
});
|
|
2436
|
-
return
|
|
2480
|
+
return hadQueuedMessages || this.agent.hasQueuedMessages();
|
|
2437
2481
|
}
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
const extensionResult = (await this._extensionRunner.emit({
|
|
2442
|
-
type: "session_before_compact",
|
|
2443
|
-
preparation,
|
|
2444
|
-
branchEntries: pathEntries,
|
|
2445
|
-
customInstructions: undefined,
|
|
2446
|
-
signal: this._autoCompactionAbortController.signal,
|
|
2447
|
-
}));
|
|
2448
|
-
if (extensionResult?.cancel) {
|
|
2449
|
-
this._emit({
|
|
2450
|
-
type: "compaction_end",
|
|
2451
|
-
reason,
|
|
2452
|
-
result: undefined,
|
|
2453
|
-
aborted: true,
|
|
2454
|
-
willRetry: false,
|
|
2455
|
-
});
|
|
2482
|
+
if (outcome.kind === "failed") {
|
|
2483
|
+
if (outcome.reason === "aborted") {
|
|
2484
|
+
this._emit({ type: "compaction_end", reason, result: undefined, aborted: true, willRetry: false });
|
|
2456
2485
|
return false;
|
|
2457
2486
|
}
|
|
2458
|
-
|
|
2459
|
-
extensionCompaction = extensionResult.compaction;
|
|
2460
|
-
fromExtension = true;
|
|
2461
|
-
}
|
|
2487
|
+
throw new Error(outcome.reason);
|
|
2462
2488
|
}
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
let tokensBefore;
|
|
2466
|
-
let details;
|
|
2467
|
-
if (extensionCompaction) {
|
|
2468
|
-
// Extension provided compaction content
|
|
2469
|
-
summary = extensionCompaction.summary;
|
|
2470
|
-
firstKeptEntryId = extensionCompaction.firstKeptEntryId;
|
|
2471
|
-
tokensBefore = extensionCompaction.tokensBefore;
|
|
2472
|
-
details = extensionCompaction.details;
|
|
2473
|
-
}
|
|
2474
|
-
else {
|
|
2475
|
-
// Generate compaction result
|
|
2476
|
-
const autoCompactionSignal = this._autoCompactionAbortController.signal;
|
|
2477
|
-
const compactResult = await this._compactWithRetry(() => compact(preparation, compactionModel, apiKey, headers, undefined, autoCompactionSignal, compactionThinkingLevel, this.agent.streamFn, this._buildCompactionPreDigest()), autoCompactionSignal);
|
|
2478
|
-
summary = compactResult.summary;
|
|
2479
|
-
firstKeptEntryId = compactResult.firstKeptEntryId;
|
|
2480
|
-
tokensBefore = compactResult.tokensBefore;
|
|
2481
|
-
details = compactResult.details;
|
|
2482
|
-
}
|
|
2483
|
-
if (this._autoCompactionAbortController.signal.aborted) {
|
|
2484
|
-
this._emit({
|
|
2485
|
-
type: "compaction_end",
|
|
2486
|
-
reason,
|
|
2487
|
-
result: undefined,
|
|
2488
|
-
aborted: true,
|
|
2489
|
-
willRetry: false,
|
|
2490
|
-
});
|
|
2489
|
+
if (extensionCancelled || signal.aborted) {
|
|
2490
|
+
this._emit({ type: "compaction_end", reason, result: undefined, aborted: true, willRetry: false });
|
|
2491
2491
|
return false;
|
|
2492
2492
|
}
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
this.agent.state.messages = sessionContext.messages;
|
|
2497
|
-
// Get the saved compaction entry for the extension event
|
|
2498
|
-
const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary);
|
|
2499
|
-
if (this._extensionRunner && savedCompactionEntry) {
|
|
2500
|
-
await this._extensionRunner.emit({
|
|
2501
|
-
type: "session_compact",
|
|
2502
|
-
compactionEntry: savedCompactionEntry,
|
|
2503
|
-
fromExtension,
|
|
2504
|
-
});
|
|
2493
|
+
const result = outcome.kind === "success" ? outcome.result : lastCompaction;
|
|
2494
|
+
if (!result) {
|
|
2495
|
+
throw new Error("Auto-compaction succeeded without a result");
|
|
2505
2496
|
}
|
|
2506
|
-
const result = {
|
|
2507
|
-
summary,
|
|
2508
|
-
firstKeptEntryId,
|
|
2509
|
-
tokensBefore,
|
|
2510
|
-
details,
|
|
2511
|
-
};
|
|
2512
2497
|
this._emit({ type: "compaction_end", reason, result, aborted: false, willRetry });
|
|
2513
2498
|
if (willRetry) {
|
|
2514
2499
|
const messages = this.agent.state.messages;
|
|
@@ -2518,9 +2503,7 @@ export class AgentSession {
|
|
|
2518
2503
|
}
|
|
2519
2504
|
return true;
|
|
2520
2505
|
}
|
|
2521
|
-
|
|
2522
|
-
// Continue once so queued messages are delivered.
|
|
2523
|
-
return this.agent.hasQueuedMessages();
|
|
2506
|
+
return hadQueuedMessages || this.agent.hasQueuedMessages();
|
|
2524
2507
|
}
|
|
2525
2508
|
catch (error) {
|
|
2526
2509
|
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
@@ -2548,7 +2531,7 @@ export class AgentSession {
|
|
|
2548
2531
|
* same failure via auto-retry. Caller aborts are never retried; sleepAbortable rejects
|
|
2549
2532
|
* with the abort reason if the signal fires mid-backoff.
|
|
2550
2533
|
*/
|
|
2551
|
-
async _compactWithRetry(run, signal) {
|
|
2534
|
+
async _compactWithRetry(run, signal, provider) {
|
|
2552
2535
|
const retrySettings = this.settingsManager.getRetrySettings();
|
|
2553
2536
|
const maxAttempts = retrySettings.enabled ? Math.max(1, retrySettings.maxRetries + 1) : 1;
|
|
2554
2537
|
const policy = {
|
|
@@ -2565,7 +2548,8 @@ export class AgentSession {
|
|
|
2565
2548
|
if (signal.aborted || attempt >= maxAttempts)
|
|
2566
2549
|
throw error;
|
|
2567
2550
|
const message = error instanceof Error ? error.message : String(error);
|
|
2568
|
-
const classified = classifyFailure({ message });
|
|
2551
|
+
const classified = classifyFailure({ message, provider });
|
|
2552
|
+
this._failureCorpus.record({ provider, message, classified });
|
|
2569
2553
|
if (!classified.retryable)
|
|
2570
2554
|
throw error;
|
|
2571
2555
|
await sleepAbortable(computeRetryDelayMs(policy, attempt, { retryAfterMs: classified.retryAfterMs }), signal);
|
|
@@ -2851,10 +2835,18 @@ export class AgentSession {
|
|
|
2851
2835
|
if (message.stopReason !== "error" || !message.errorMessage)
|
|
2852
2836
|
return false;
|
|
2853
2837
|
const contextWindow = this.model?.contextWindow ?? 0;
|
|
2854
|
-
|
|
2838
|
+
const classified = classifyFailure({
|
|
2855
2839
|
message: message.errorMessage,
|
|
2856
2840
|
contextOverflow: isContextOverflow(message, contextWindow),
|
|
2857
|
-
|
|
2841
|
+
provider: message.provider,
|
|
2842
|
+
});
|
|
2843
|
+
this._failureCorpus.record({
|
|
2844
|
+
provider: message.provider,
|
|
2845
|
+
modelId: message.model,
|
|
2846
|
+
message: message.errorMessage,
|
|
2847
|
+
classified,
|
|
2848
|
+
});
|
|
2849
|
+
return classified.retryable;
|
|
2858
2850
|
}
|
|
2859
2851
|
/**
|
|
2860
2852
|
* Prepare a retryable error for continuation with exponential backoff.
|