@caupulican/pi-adaptative 0.80.79 → 0.80.81
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 +56 -0
- package/dist/core/agent-session.d.ts +15 -14
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +202 -9
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/cost/daily-usage.d.ts +23 -0
- package/dist/core/cost/daily-usage.d.ts.map +1 -0
- package/dist/core/cost/daily-usage.js +135 -0
- package/dist/core/cost/daily-usage.js.map +1 -0
- package/dist/core/model-router/config-diagnostics.d.ts +4 -0
- package/dist/core/model-router/config-diagnostics.d.ts.map +1 -0
- package/dist/core/model-router/config-diagnostics.js +26 -0
- package/dist/core/model-router/config-diagnostics.js.map +1 -0
- package/dist/core/model-router/intent-classifier.d.ts +3 -0
- package/dist/core/model-router/intent-classifier.d.ts.map +1 -0
- package/dist/core/model-router/intent-classifier.js +12 -0
- package/dist/core/model-router/intent-classifier.js.map +1 -0
- package/dist/core/model-router/session-buffer.d.ts +21 -0
- package/dist/core/model-router/session-buffer.d.ts.map +1 -0
- package/dist/core/model-router/session-buffer.js +20 -0
- package/dist/core/model-router/session-buffer.js.map +1 -0
- package/dist/core/model-router/status.d.ts +17 -0
- package/dist/core/model-router/status.d.ts.map +1 -0
- package/dist/core/model-router/status.js +66 -0
- package/dist/core/model-router/status.js.map +1 -0
- package/dist/core/model-router/tool-escalation.d.ts +9 -0
- package/dist/core/model-router/tool-escalation.d.ts.map +1 -0
- package/dist/core/model-router/tool-escalation.js +97 -0
- package/dist/core/model-router/tool-escalation.js.map +1 -0
- package/dist/core/resource-loader.d.ts +6 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +49 -34
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/resource-profile-equality.d.ts +5 -0
- package/dist/core/resource-profile-equality.d.ts.map +1 -0
- package/dist/core/resource-profile-equality.js +18 -0
- package/dist/core/resource-profile-equality.js.map +1 -0
- package/dist/core/settings-manager.d.ts +11 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +7 -0
- 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 +2 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +5 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +3 -3
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +6 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +15 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +84 -15
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- 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/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
16
16
|
import { basename, dirname, join } from "node:path";
|
|
17
17
|
import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, modelsAreEqual, resetApiProviders, streamSimple, } from "@caupulican/pi-ai";
|
|
18
|
-
import { getAgentDir } from "../config.js";
|
|
18
|
+
import { getAgentDir, getSessionsDir } from "../config.js";
|
|
19
19
|
import { theme } from "../modes/interactive/theme/theme.js";
|
|
20
20
|
import { stripFrontmatter } from "../utils/frontmatter.js";
|
|
21
21
|
import { resolvePath } from "../utils/paths.js";
|
|
@@ -24,6 +24,7 @@ import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth
|
|
|
24
24
|
import { executeBashWithOperations } from "./bash-executor.js";
|
|
25
25
|
import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
|
|
26
26
|
import { applyContextGc } from "./context-gc.js";
|
|
27
|
+
import { aggregateDailyUsageFromSessionFiles, aggregateDailyUsageFromSessionRoot, formatDailyUsageBreakdown, getLocalDayWindow, } from "./cost/daily-usage.js";
|
|
27
28
|
import { downgradeReasoning, estimateTurnCostUsd, evaluateCostGuard } from "./cost-guard.js";
|
|
28
29
|
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
29
30
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
@@ -42,6 +43,11 @@ import { TranscriptRecallProvider } from "./memory/providers/transcript-recall.j
|
|
|
42
43
|
import { compactToolResultDetailsForRetention } from "./message-retention.js";
|
|
43
44
|
import { createCustomMessage } from "./messages.js";
|
|
44
45
|
import { resolveCliModel, resolveProfileModelSettings } from "./model-resolver.js";
|
|
46
|
+
import { collectModelRouterConfigDiagnostics } from "./model-router/config-diagnostics.js";
|
|
47
|
+
import { classifyModelRouterIntent } from "./model-router/intent-classifier.js";
|
|
48
|
+
import { bufferModelRouterSessionCustomMessage, bufferModelRouterSessionMessage, createModelRouterSessionBuffer, flushModelRouterSessionBuffer, } from "./model-router/session-buffer.js";
|
|
49
|
+
import { formatModelRouterStatus, getRecentModelRouterDecisions, MODEL_ROUTER_DECISION_CUSTOM_TYPE, } from "./model-router/status.js";
|
|
50
|
+
import { shouldEscalateModelRouterTool } from "./model-router/tool-escalation.js";
|
|
45
51
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
46
52
|
import { stripResourceProfileBlocks } from "./resource-profile-blocks.js";
|
|
47
53
|
import { classifyToolTrust, UNTRUSTED_BOUNDARY_SYSTEM_RULE, wrapUntrustedText } from "./security/untrusted-boundary.js";
|
|
@@ -77,6 +83,12 @@ const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high"];
|
|
|
77
83
|
// ============================================================================
|
|
78
84
|
// AgentSession Class
|
|
79
85
|
// ============================================================================
|
|
86
|
+
function formatModelRouterModel(model) {
|
|
87
|
+
return `${model.provider}/${model.id}`;
|
|
88
|
+
}
|
|
89
|
+
function persistModelRouterDecision(sessionManager, decision) {
|
|
90
|
+
sessionManager.appendCustomEntry(MODEL_ROUTER_DECISION_CUSTOM_TYPE, decision);
|
|
91
|
+
}
|
|
80
92
|
export class AgentSession {
|
|
81
93
|
agent;
|
|
82
94
|
sessionManager;
|
|
@@ -132,10 +144,18 @@ export class AgentSession {
|
|
|
132
144
|
_gatewayRegistry = new GatewayRegistry();
|
|
133
145
|
/** Cache for getSpawnedUsage(), keyed by session entry count (Bug #22 — avoid O(N) per render frame). */
|
|
134
146
|
_spawnedUsageCache;
|
|
147
|
+
_dailyUsageCache;
|
|
135
148
|
/** Latest proactive cost-guard decision (#34), for the host UI to surface. Undefined when disabled. */
|
|
136
149
|
_lastCostGuardDecision;
|
|
137
150
|
/** One-shot latch so the cost guard downgrades reasoning once per over-threshold episode, not every call. */
|
|
138
151
|
_costGuardDowngraded = false;
|
|
152
|
+
/** Active model-router intent for the current transient routed turn, if any. */
|
|
153
|
+
_activeModelRouterIntent;
|
|
154
|
+
_modelRouterSessionBuffer;
|
|
155
|
+
_modelRouterEscalationRequested = false;
|
|
156
|
+
_lastModelRouterDecision;
|
|
157
|
+
_lastModelRouterSkipReason;
|
|
158
|
+
_lastModelRouterIntent;
|
|
139
159
|
/** Lazily-built skill curator (#32) over `<agentDir>/skills`. */
|
|
140
160
|
_skillCuratorInstance;
|
|
141
161
|
/** Set on dispose so in-flight background reflection bails instead of writing to a dead session (Bug #21). */
|
|
@@ -456,6 +476,15 @@ export class AgentSession {
|
|
|
456
476
|
}
|
|
457
477
|
_installAgentToolHooks() {
|
|
458
478
|
this.agent.beforeToolCall = async ({ toolCall, args }) => {
|
|
479
|
+
if (this._activeModelRouterIntent &&
|
|
480
|
+
shouldEscalateModelRouterTool({ intent: this._activeModelRouterIntent, toolName: toolCall.name, args })) {
|
|
481
|
+
this._modelRouterEscalationRequested = true;
|
|
482
|
+
this.agent.abort();
|
|
483
|
+
return {
|
|
484
|
+
block: true,
|
|
485
|
+
reason: "Model router escalation required: a cheap research turn attempted a mutating tool. Retry the turn on the configured expensive model.",
|
|
486
|
+
};
|
|
487
|
+
}
|
|
459
488
|
const runner = this._extensionRunner;
|
|
460
489
|
if (!runner.hasHandlers("tool_call")) {
|
|
461
490
|
return undefined;
|
|
@@ -562,8 +591,16 @@ export class AgentSession {
|
|
|
562
591
|
// accumulate until the interactive Node process hits the V8 heap limit.
|
|
563
592
|
if (event.type === "message_end") {
|
|
564
593
|
compactToolResultDetailsForRetention(event.message);
|
|
594
|
+
const modelRouterBuffer = this._modelRouterSessionBuffer;
|
|
595
|
+
if (modelRouterBuffer && event.message.role === "custom") {
|
|
596
|
+
bufferModelRouterSessionCustomMessage(modelRouterBuffer, event.message);
|
|
597
|
+
}
|
|
598
|
+
else if (modelRouterBuffer &&
|
|
599
|
+
(event.message.role === "user" || event.message.role === "assistant" || event.message.role === "toolResult")) {
|
|
600
|
+
bufferModelRouterSessionMessage(modelRouterBuffer, event.message);
|
|
601
|
+
}
|
|
565
602
|
// Check if this is a custom message from extensions
|
|
566
|
-
if (event.message.role === "custom") {
|
|
603
|
+
else if (event.message.role === "custom") {
|
|
567
604
|
// Persist as CustomMessageEntry
|
|
568
605
|
this.sessionManager.appendCustomMessageEntry(event.message.customType, event.message.content, event.message.display, event.message.details);
|
|
569
606
|
}
|
|
@@ -1086,6 +1123,140 @@ export class AgentSession {
|
|
|
1086
1123
|
await this._drainQueuedExtensionCommands();
|
|
1087
1124
|
}
|
|
1088
1125
|
}
|
|
1126
|
+
_resolveModelRouterModelForIntent(intent) {
|
|
1127
|
+
const settings = this.settingsManager.getModelRouterSettings();
|
|
1128
|
+
const modelLabel = intent === "research" ? "cheap model" : "expensive model";
|
|
1129
|
+
if (!settings.enabled) {
|
|
1130
|
+
this._lastModelRouterSkipReason = "disabled";
|
|
1131
|
+
return undefined;
|
|
1132
|
+
}
|
|
1133
|
+
const modelPattern = intent === "research" ? settings.cheapModel : settings.expensiveModel;
|
|
1134
|
+
if (!modelPattern) {
|
|
1135
|
+
this._lastModelRouterSkipReason = `${modelLabel} unset`;
|
|
1136
|
+
return undefined;
|
|
1137
|
+
}
|
|
1138
|
+
const resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry: this._modelRegistry });
|
|
1139
|
+
if (!resolved.model) {
|
|
1140
|
+
this._lastModelRouterSkipReason = `${modelLabel} unresolved: ${modelPattern}`;
|
|
1141
|
+
return undefined;
|
|
1142
|
+
}
|
|
1143
|
+
const resolvedName = formatModelRouterModel(resolved.model);
|
|
1144
|
+
if (!this._modelRegistry.hasConfiguredAuth(resolved.model)) {
|
|
1145
|
+
this._lastModelRouterSkipReason = `${modelLabel} missing auth: ${resolvedName}`;
|
|
1146
|
+
return undefined;
|
|
1147
|
+
}
|
|
1148
|
+
this._lastModelRouterSkipReason = undefined;
|
|
1149
|
+
return resolved.model;
|
|
1150
|
+
}
|
|
1151
|
+
_resolveModelRouterTurnModel(prompt) {
|
|
1152
|
+
const intent = classifyModelRouterIntent(prompt);
|
|
1153
|
+
this._lastModelRouterIntent = intent;
|
|
1154
|
+
return this._resolveModelRouterModelForIntent(intent);
|
|
1155
|
+
}
|
|
1156
|
+
getModelRouterStatus(formatLabel) {
|
|
1157
|
+
const recentDecisions = getRecentModelRouterDecisions(this.sessionManager.getEntries());
|
|
1158
|
+
const lastDecision = this._lastModelRouterDecision ?? recentDecisions.at(-1);
|
|
1159
|
+
const historicalDecisions = this._lastModelRouterDecision ? recentDecisions : recentDecisions.slice(0, -1);
|
|
1160
|
+
const settings = this.settingsManager.getModelRouterSettings();
|
|
1161
|
+
const lines = [
|
|
1162
|
+
formatModelRouterStatus(settings, lastDecision, formatLabel, historicalDecisions, this._lastModelRouterSkipReason, this._lastModelRouterIntent ?? lastDecision?.intent),
|
|
1163
|
+
];
|
|
1164
|
+
const diagnostics = collectModelRouterConfigDiagnostics(settings, this._modelRegistry);
|
|
1165
|
+
if (diagnostics.length > 0) {
|
|
1166
|
+
lines.push(formatLabel ? formatLabel("Config diagnostics:") : "Config diagnostics:");
|
|
1167
|
+
for (const diagnostic of diagnostics) {
|
|
1168
|
+
lines.push(`- ${diagnostic}`);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
return lines.join("\n");
|
|
1172
|
+
}
|
|
1173
|
+
async _runAgentPromptWithModelRouter(messages, routedModel, routedIntent, persistDecision = true) {
|
|
1174
|
+
if (!routedModel) {
|
|
1175
|
+
await this._runAgentPrompt(messages);
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
const previousModel = this.agent.state.model;
|
|
1179
|
+
const previousThinkingLevel = this.agent.state.thinkingLevel;
|
|
1180
|
+
const previousActiveModelRouterIntent = this._activeModelRouterIntent;
|
|
1181
|
+
const previousModelRouterSessionBuffer = this._modelRouterSessionBuffer;
|
|
1182
|
+
const previousModelRouterEscalationRequested = this._modelRouterEscalationRequested;
|
|
1183
|
+
const bufferRoutedTurn = routedIntent === "research";
|
|
1184
|
+
const originalHistoryLength = this.agent.state.messages.length;
|
|
1185
|
+
let retryModel;
|
|
1186
|
+
let completedDecision = routedIntent
|
|
1187
|
+
? {
|
|
1188
|
+
intent: routedIntent,
|
|
1189
|
+
routedModel: formatModelRouterModel(routedModel),
|
|
1190
|
+
outcome: "routed",
|
|
1191
|
+
}
|
|
1192
|
+
: undefined;
|
|
1193
|
+
let thrownError;
|
|
1194
|
+
if (routedIntent) {
|
|
1195
|
+
this._lastModelRouterDecision = completedDecision;
|
|
1196
|
+
}
|
|
1197
|
+
this._activeModelRouterIntent = routedIntent;
|
|
1198
|
+
if (bufferRoutedTurn) {
|
|
1199
|
+
this._modelRouterSessionBuffer = createModelRouterSessionBuffer();
|
|
1200
|
+
this._modelRouterEscalationRequested = false;
|
|
1201
|
+
}
|
|
1202
|
+
if (!modelsAreEqual(this.model, routedModel)) {
|
|
1203
|
+
this.agent.state.model = routedModel;
|
|
1204
|
+
this.agent.state.thinkingLevel = clampThinkingLevel(routedModel, previousThinkingLevel);
|
|
1205
|
+
}
|
|
1206
|
+
try {
|
|
1207
|
+
await this._runAgentPrompt(messages);
|
|
1208
|
+
if (bufferRoutedTurn && this._modelRouterEscalationRequested) {
|
|
1209
|
+
this.agent.state.messages.splice(originalHistoryLength);
|
|
1210
|
+
retryModel = this._resolveModelRouterModelForIntent("modify") ?? previousModel;
|
|
1211
|
+
completedDecision = {
|
|
1212
|
+
intent: routedIntent,
|
|
1213
|
+
routedModel: formatModelRouterModel(routedModel),
|
|
1214
|
+
outcome: "escalated",
|
|
1215
|
+
retryModel: formatModelRouterModel(retryModel),
|
|
1216
|
+
};
|
|
1217
|
+
this._lastModelRouterDecision = completedDecision;
|
|
1218
|
+
}
|
|
1219
|
+
else if (bufferRoutedTurn && this._modelRouterSessionBuffer) {
|
|
1220
|
+
flushModelRouterSessionBuffer(this._modelRouterSessionBuffer, (message) => {
|
|
1221
|
+
this.sessionManager.appendMessage(message);
|
|
1222
|
+
}, (customType, content, display, details) => {
|
|
1223
|
+
this.sessionManager.appendCustomMessageEntry(customType, content, display, details);
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
catch (error) {
|
|
1228
|
+
thrownError = error;
|
|
1229
|
+
if (completedDecision) {
|
|
1230
|
+
completedDecision = { ...completedDecision, outcome: "failed" };
|
|
1231
|
+
this._lastModelRouterDecision = completedDecision;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
finally {
|
|
1235
|
+
this.agent.state.model = previousModel;
|
|
1236
|
+
this.agent.state.thinkingLevel = previousThinkingLevel;
|
|
1237
|
+
this._activeModelRouterIntent = previousActiveModelRouterIntent;
|
|
1238
|
+
this._modelRouterSessionBuffer = previousModelRouterSessionBuffer;
|
|
1239
|
+
this._modelRouterEscalationRequested = previousModelRouterEscalationRequested;
|
|
1240
|
+
}
|
|
1241
|
+
if (retryModel && !thrownError) {
|
|
1242
|
+
try {
|
|
1243
|
+
await this._runAgentPromptWithModelRouter(messages, retryModel, "modify", false);
|
|
1244
|
+
}
|
|
1245
|
+
catch (error) {
|
|
1246
|
+
thrownError = error;
|
|
1247
|
+
if (completedDecision) {
|
|
1248
|
+
completedDecision = { ...completedDecision, outcome: "failed" };
|
|
1249
|
+
this._lastModelRouterDecision = completedDecision;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
if (persistDecision && completedDecision) {
|
|
1254
|
+
persistModelRouterDecision(this.sessionManager, completedDecision);
|
|
1255
|
+
}
|
|
1256
|
+
if (thrownError) {
|
|
1257
|
+
throw thrownError;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1089
1260
|
async _handlePostAgentRun() {
|
|
1090
1261
|
const msg = this._lastAssistantMessage;
|
|
1091
1262
|
this._lastAssistantMessage = undefined;
|
|
@@ -1149,6 +1320,8 @@ export class AgentSession {
|
|
|
1149
1320
|
const processSlashCommands = options?.processSlashCommands ?? expandPromptTemplates;
|
|
1150
1321
|
const preflightResult = options?.preflightResult;
|
|
1151
1322
|
let messages;
|
|
1323
|
+
let routedTurnModel;
|
|
1324
|
+
let routedTurnIntent;
|
|
1152
1325
|
// R4 effectiveness feedback: remember the recall page + the query so we can score, after the
|
|
1153
1326
|
// response, whether the agent actually used the recalled context.
|
|
1154
1327
|
let injectedRecall = "";
|
|
@@ -1211,18 +1384,21 @@ export class AgentSession {
|
|
|
1211
1384
|
}
|
|
1212
1385
|
// Flush any pending bash messages before the new prompt
|
|
1213
1386
|
this._flushPendingBashMessages();
|
|
1387
|
+
routedTurnModel = this._resolveModelRouterTurnModel(expandedText);
|
|
1388
|
+
routedTurnIntent = routedTurnModel ? classifyModelRouterIntent(expandedText) : undefined;
|
|
1389
|
+
const requestModel = routedTurnModel ?? this.model;
|
|
1214
1390
|
// Validate model
|
|
1215
|
-
if (!
|
|
1391
|
+
if (!requestModel) {
|
|
1216
1392
|
throw new Error(formatNoModelSelectedMessage());
|
|
1217
1393
|
}
|
|
1218
|
-
if (!this._modelRegistry.hasConfiguredAuth(
|
|
1219
|
-
const isOAuth = this._modelRegistry.isUsingOAuth(
|
|
1394
|
+
if (!this._modelRegistry.hasConfiguredAuth(requestModel)) {
|
|
1395
|
+
const isOAuth = this._modelRegistry.isUsingOAuth(requestModel);
|
|
1220
1396
|
if (isOAuth) {
|
|
1221
|
-
throw new Error(`Authentication failed for "${
|
|
1397
|
+
throw new Error(`Authentication failed for "${requestModel.provider}". ` +
|
|
1222
1398
|
`Credentials may have expired or network is unavailable. ` +
|
|
1223
|
-
`Run '/login ${
|
|
1399
|
+
`Run '/login ${requestModel.provider}' to re-authenticate.`);
|
|
1224
1400
|
}
|
|
1225
|
-
throw new Error(formatNoApiKeyFoundMessage(
|
|
1401
|
+
throw new Error(formatNoApiKeyFoundMessage(requestModel.provider));
|
|
1226
1402
|
}
|
|
1227
1403
|
// Check if we need to compact before sending (catches aborted responses).
|
|
1228
1404
|
// Do not call agent.continue() here: the next model turn must include the
|
|
@@ -1301,7 +1477,7 @@ export class AgentSession {
|
|
|
1301
1477
|
return;
|
|
1302
1478
|
}
|
|
1303
1479
|
preflightResult?.(true);
|
|
1304
|
-
await this.
|
|
1480
|
+
await this._runAgentPromptWithModelRouter(messages, routedTurnModel, routedTurnIntent);
|
|
1305
1481
|
// R4: score whether the agent actually used the recalled context, so the recall gate can adapt.
|
|
1306
1482
|
if (injectedRecall) {
|
|
1307
1483
|
const response = this._findLastAssistantMessage();
|
|
@@ -3464,6 +3640,23 @@ export class AgentSession {
|
|
|
3464
3640
|
this._spawnedUsageCache = { entryCount, totals };
|
|
3465
3641
|
return totals;
|
|
3466
3642
|
}
|
|
3643
|
+
getDailyUsageTotals(now = new Date()) {
|
|
3644
|
+
const sessionDir = this.sessionManager.getSessionDir();
|
|
3645
|
+
const scope = this.sessionManager.usesDefaultSessionDir() ? getSessionsDir() : sessionDir;
|
|
3646
|
+
const nowMs = now.getTime();
|
|
3647
|
+
if (this._dailyUsageCache?.sessionDir === scope && this._dailyUsageCache.expiresAt > nowMs) {
|
|
3648
|
+
return this._dailyUsageCache.totals;
|
|
3649
|
+
}
|
|
3650
|
+
const window = getLocalDayWindow(now);
|
|
3651
|
+
const totals = this.sessionManager.usesDefaultSessionDir()
|
|
3652
|
+
? aggregateDailyUsageFromSessionRoot(scope, window)
|
|
3653
|
+
: aggregateDailyUsageFromSessionFiles(sessionDir, window);
|
|
3654
|
+
this._dailyUsageCache = { sessionDir: scope, expiresAt: nowMs + 10_000, totals };
|
|
3655
|
+
return totals;
|
|
3656
|
+
}
|
|
3657
|
+
getDailyUsageBreakdown(formatLabel, now = new Date()) {
|
|
3658
|
+
return formatDailyUsageBreakdown(this.getDailyUsageTotals(now), formatLabel);
|
|
3659
|
+
}
|
|
3467
3660
|
/**
|
|
3468
3661
|
* Run a one-shot LLM completion fully ISOLATED from the main session — the load-bearing
|
|
3469
3662
|
* primitive for the native reflection engine (adaptive-agent design §6c/§7).
|