@caupulican/pi-adaptative 0.80.75 → 0.80.77
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/core/agent-session.d.ts +38 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +83 -3
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/cost-guard.d.ts +55 -0
- package/dist/core/cost-guard.d.ts.map +1 -0
- package/dist/core/cost-guard.js +50 -0
- package/dist/core/cost-guard.js.map +1 -0
- package/dist/core/learning/reflection-engine.d.ts +7 -0
- package/dist/core/learning/reflection-engine.d.ts.map +1 -1
- package/dist/core/learning/reflection-engine.js +22 -13
- package/dist/core/learning/reflection-engine.js.map +1 -1
- package/dist/core/learning/skill-curator.d.ts +71 -0
- package/dist/core/learning/skill-curator.d.ts.map +1 -0
- package/dist/core/learning/skill-curator.js +179 -0
- package/dist/core/learning/skill-curator.js.map +1 -0
- package/dist/core/settings-manager.d.ts +10 -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 +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +7 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +38 -0
- 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
|
@@ -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 { downgradeReasoning, estimateTurnCostUsd, evaluateCostGuard } from "./cost-guard.js";
|
|
27
28
|
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
28
29
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
29
30
|
import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
|
|
@@ -33,6 +34,7 @@ import { disposeExtensionEventSubscriptions } from "./extensions/loader.js";
|
|
|
33
34
|
import { emitSessionShutdownEvent } from "./extensions/runner.js";
|
|
34
35
|
import { GatewayRegistry } from "./gateways/channel-provider.js";
|
|
35
36
|
import { decideDemand, ReflectionEngine, } from "./learning/reflection-engine.js";
|
|
37
|
+
import { isPromotedFrontmatter, SkillCurator } from "./learning/skill-curator.js";
|
|
36
38
|
import { EffectivenessTracker } from "./memory/effectiveness-tracker.js";
|
|
37
39
|
import { MemoryManager } from "./memory/memory-manager.js";
|
|
38
40
|
import { FileStoreProvider } from "./memory/providers/file-store.js";
|
|
@@ -130,6 +132,12 @@ export class AgentSession {
|
|
|
130
132
|
_gatewayRegistry = new GatewayRegistry();
|
|
131
133
|
/** Cache for getSpawnedUsage(), keyed by session entry count (Bug #22 — avoid O(N) per render frame). */
|
|
132
134
|
_spawnedUsageCache;
|
|
135
|
+
/** Latest proactive cost-guard decision (#34), for the host UI to surface. Undefined when disabled. */
|
|
136
|
+
_lastCostGuardDecision;
|
|
137
|
+
/** One-shot latch so the cost guard downgrades reasoning once per over-threshold episode, not every call. */
|
|
138
|
+
_costGuardDowngraded = false;
|
|
139
|
+
/** Lazily-built skill curator (#32) over `<agentDir>/skills`. */
|
|
140
|
+
_skillCuratorInstance;
|
|
133
141
|
/** Set on dispose so in-flight background reflection bails instead of writing to a dead session (Bug #21). */
|
|
134
142
|
_disposed = false;
|
|
135
143
|
/** Aborts in-flight background reflection completions on dispose (Bug #21). */
|
|
@@ -288,9 +296,71 @@ export class AgentSession {
|
|
|
288
296
|
if (this._extensionRunner.hasHandlers("context")) {
|
|
289
297
|
finalMessages = await this._extensionRunner.emitContext(currentMessages);
|
|
290
298
|
}
|
|
291
|
-
|
|
299
|
+
const gcMessages = this._applyContextGc(finalMessages, true).messages;
|
|
300
|
+
this._applyCostGuard(gcMessages);
|
|
301
|
+
return gcMessages;
|
|
292
302
|
};
|
|
293
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* Proactive per-turn cost guard (#34): estimate the USD cost of the about-to-be-submitted turn and,
|
|
306
|
+
* when it exceeds the user's ceiling, record a warning decision (for the host UI to surface) and —
|
|
307
|
+
* if configured to `downgrade` — step reasoning effort down ONCE per over-threshold episode to curb a
|
|
308
|
+
* runaway billing spike. Disabled by default (`maxTurnUsd<=0`), so it never alters behavior unless the
|
|
309
|
+
* user opts in. Best-effort: never throws into the turn.
|
|
310
|
+
*/
|
|
311
|
+
_applyCostGuard(messages) {
|
|
312
|
+
try {
|
|
313
|
+
const guard = this.settingsManager.getCostGuardSettings();
|
|
314
|
+
if (guard.maxTurnUsd <= 0 || !this.model?.cost) {
|
|
315
|
+
this._lastCostGuardDecision = undefined;
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const inputTokens = this._estimateCurrentContextTokens(messages);
|
|
319
|
+
const maxOutputTokens = this.model.maxTokens ?? 4096;
|
|
320
|
+
const estUsd = estimateTurnCostUsd({ inputTokens, maxOutputTokens, cost: this.model.cost });
|
|
321
|
+
const decision = evaluateCostGuard(estUsd, { maxTurnUsd: guard.maxTurnUsd, action: guard.action });
|
|
322
|
+
this._lastCostGuardDecision = decision;
|
|
323
|
+
if (!decision.over) {
|
|
324
|
+
this._costGuardDowngraded = false; // back under the ceiling — re-arm the one-shot downgrade
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (guard.action === "downgrade" && !this._costGuardDowngraded && this.supportsThinking()) {
|
|
328
|
+
const next = downgradeReasoning(this.thinkingLevel);
|
|
329
|
+
if (next !== this.thinkingLevel) {
|
|
330
|
+
this.setThinkingLevel(next);
|
|
331
|
+
this._costGuardDowngraded = true;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
// cost guard must never disrupt a turn
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/** Latest cost-guard decision (for the host footer/UI to surface a warning). Undefined if disabled. */
|
|
340
|
+
getLastCostGuardDecision() {
|
|
341
|
+
return this._lastCostGuardDecision;
|
|
342
|
+
}
|
|
343
|
+
get _skillCurator() {
|
|
344
|
+
if (!this._skillCuratorInstance) {
|
|
345
|
+
this._skillCuratorInstance = new SkillCurator(join(this._agentDir, "skills"));
|
|
346
|
+
}
|
|
347
|
+
return this._skillCuratorInstance;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Skill curator (#32): PROPOSE (never auto-apply) archival of stale reflection-promoted skills and
|
|
351
|
+
* consolidation of overlapping ones. The host surfaces these (e.g. a `/curate` command) for approval.
|
|
352
|
+
*/
|
|
353
|
+
proposeSkillCuration(options) {
|
|
354
|
+
return this._skillCurator.proposeCuration(Date.now(), options);
|
|
355
|
+
}
|
|
356
|
+
/** Archive a promoted skill into `skills/.archive/` (restorable, non-destructive). Returns true if moved. */
|
|
357
|
+
archivePromotedSkill(name) {
|
|
358
|
+
return this._skillCurator.archiveSkill(name);
|
|
359
|
+
}
|
|
360
|
+
/** Restore a previously-archived promoted skill. Returns true if moved back. */
|
|
361
|
+
restorePromotedSkill(name) {
|
|
362
|
+
return this._skillCurator.restoreSkill(name);
|
|
363
|
+
}
|
|
294
364
|
_installAgentTurnRefresh() {
|
|
295
365
|
const previousPrepareNextTurn = this.agent.prepareNextTurn?.bind(this.agent);
|
|
296
366
|
this.agent.prepareNextTurn = async (signal) => {
|
|
@@ -1282,6 +1352,11 @@ export class AgentSession {
|
|
|
1282
1352
|
return text; // Unknown or profile-blocked skill, pass through unchanged
|
|
1283
1353
|
try {
|
|
1284
1354
|
const content = readFileSync(skill.filePath, "utf-8");
|
|
1355
|
+
// Curator (#32): record use of a reflection-PROMOTED skill so stale ones can later be proposed
|
|
1356
|
+
// for archival. Only promoted skills carry the marker, so hand-authored skills are untouched.
|
|
1357
|
+
if (isPromotedFrontmatter(content)) {
|
|
1358
|
+
this._skillCurator.recordUse(skill.name, Date.now());
|
|
1359
|
+
}
|
|
1285
1360
|
const body = stripResourceProfileBlocks(stripFrontmatter(content)).trim();
|
|
1286
1361
|
const skillBlock = `<skill name="${skill.name}" location="${skill.filePath}">\nReferences are relative to ${skill.baseDir}.\n\n${body}\n</skill>`;
|
|
1287
1362
|
return args ? `${skillBlock}\n\n${args}` : skillBlock;
|
|
@@ -3405,7 +3480,7 @@ export class AgentSession {
|
|
|
3405
3480
|
const options = {
|
|
3406
3481
|
maxTokens: opts.maxTokens,
|
|
3407
3482
|
signal: opts.signal,
|
|
3408
|
-
cacheRetention: "none",
|
|
3483
|
+
cacheRetention: opts.cacheRetention ?? "none",
|
|
3409
3484
|
};
|
|
3410
3485
|
// pi-ai's `reasoning` option does not include "off" (that's the provider default already).
|
|
3411
3486
|
if (thinkingLevel !== "off") {
|
|
@@ -3465,6 +3540,9 @@ export class AgentSession {
|
|
|
3465
3540
|
thinkingLevel: input.thinkingLevel ?? "low",
|
|
3466
3541
|
maxTokens: plan.tokenBudget,
|
|
3467
3542
|
signal,
|
|
3543
|
+
// The reflection system prompt is static (#33) — let the provider cache the prefix so
|
|
3544
|
+
// repeated passes only pay for the variable tail.
|
|
3545
|
+
cacheRetention: "short",
|
|
3468
3546
|
});
|
|
3469
3547
|
const result = await new ReflectionEngine().reflect({
|
|
3470
3548
|
recentTurnText: input.recentTurnText,
|
|
@@ -3555,7 +3633,9 @@ export class AgentSession {
|
|
|
3555
3633
|
return; // do not overwrite an existing skill
|
|
3556
3634
|
mkdirSync(dir, { recursive: true });
|
|
3557
3635
|
const safeDescription = description.replace(/[\r\n]+/g, " ").trim();
|
|
3558
|
-
|
|
3636
|
+
// `promoted: true` marks this as reflection-generated so the curator (#32) can lifecycle-manage
|
|
3637
|
+
// it (archive/consolidate) WITHOUT ever touching hand-authored user skills.
|
|
3638
|
+
const content = `---\nname: ${name}\ndescription: ${safeDescription}\npromoted: true\n---\n\n<!-- Auto-generated by the reflection engine (R7 memory-to-behavior). Review and refine. -->\n\n${body.trim()}\n`;
|
|
3559
3639
|
writeFileSync(file, content, "utf-8");
|
|
3560
3640
|
}
|
|
3561
3641
|
catch {
|