@caupulican/pi-adaptative 0.80.60 → 0.80.62

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.
@@ -32,8 +32,10 @@ import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
32
32
  import { disposeExtensionEventSubscriptions } from "./extensions/loader.js";
33
33
  import { emitSessionShutdownEvent } from "./extensions/runner.js";
34
34
  import { decideDemand, ReflectionEngine, } from "./learning/reflection-engine.js";
35
+ import { EffectivenessTracker } from "./memory/effectiveness-tracker.js";
35
36
  import { MemoryManager } from "./memory/memory-manager.js";
36
37
  import { FileStoreProvider } from "./memory/providers/file-store.js";
38
+ import { TranscriptRecallProvider } from "./memory/providers/transcript-recall.js";
37
39
  import { compactToolResultDetailsForRetention } from "./message-retention.js";
38
40
  import { resolveProfileModelSettings } from "./model-resolver.js";
39
41
  import { expandPromptTemplate } from "./prompt-templates.js";
@@ -119,6 +121,8 @@ export class AgentSession {
119
121
  _isExplicitThinking;
120
122
  /** Plug-and-play memory subsystem. Recreated on each (re)initialize so reload is safe. */
121
123
  _memoryManager = new MemoryManager();
124
+ /** R4: tracks whether injected recall is actually used, to adapt the recall gate. */
125
+ _effectivenessTracker = new EffectivenessTracker();
122
126
  _isChildSession;
123
127
  /** Memory providers registered by extensions via pi.registerMemoryProvider, applied on (re)init. */
124
128
  _pendingMemoryProviders = [];
@@ -960,11 +964,31 @@ export class AgentSession {
960
964
  }
961
965
  return this._promptUnserialized(text, options);
962
966
  }
967
+ /**
968
+ * Zero-I/O gate for cross-session recall (R3): skip trivial turns (short acks, slash commands) so
969
+ * recall only runs when it could plausibly help. The provider's similarity cutoff is the real
970
+ * filter — this just avoids the index query on turns that obviously don't warrant it.
971
+ */
972
+ _shouldAttemptRecall(text) {
973
+ const t = text.trim();
974
+ if (t.length < 12 || t.startsWith("/"))
975
+ return false;
976
+ const words = t.split(/\s+/).filter((w) => w.length >= 3);
977
+ // R4 adaptive gate: if recall has rarely been used lately (enough samples to trust the signal),
978
+ // raise the bar so we only recall on clearly substantial turns — and relax it again once recall
979
+ // starts paying off. Never fully disabled, so the loop can recover.
980
+ const recallRarelyUseful = this._effectivenessTracker.sampleCount >= 5 && this._effectivenessTracker.usefulLately() < 0.15;
981
+ return words.length >= (recallRarelyUseful ? 6 : 3);
982
+ }
963
983
  async _promptUnserialized(text, options) {
964
984
  const expandPromptTemplates = options?.expandPromptTemplates ?? true;
965
985
  const processSlashCommands = options?.processSlashCommands ?? expandPromptTemplates;
966
986
  const preflightResult = options?.preflightResult;
967
987
  let messages;
988
+ // R4 effectiveness feedback: remember the recall page + the query so we can score, after the
989
+ // response, whether the agent actually used the recalled context.
990
+ let injectedRecall = "";
991
+ let recallQuery = "";
968
992
  try {
969
993
  // Handle extension commands first. Programmatic extension messages may opt
970
994
  // into command handling; if the agent is currently streaming, queue the
@@ -1043,8 +1067,25 @@ export class AgentSession {
1043
1067
  if (lastAssistant) {
1044
1068
  await this._checkCompaction(lastAssistant, false);
1045
1069
  }
1046
- // Build messages array (custom message if any, then user message)
1070
+ // Build messages array (recall page, then custom message if any, then user message)
1047
1071
  messages = [];
1072
+ // R3: cross-session similarity recall. For a substantive turn, ask the memory providers to
1073
+ // prefetch a relevant <memory_context> page from past sessions and prepend it as data ahead of
1074
+ // the user message. Best-effort and gated: trivial turns are skipped, and providers return ""
1075
+ // (no page) when nothing is relevant — so it stays net-negative and the GC packs stale pages.
1076
+ if (this._shouldAttemptRecall(expandedText)) {
1077
+ try {
1078
+ const recall = await this._memoryManager.prefetch(expandedText);
1079
+ if (recall) {
1080
+ injectedRecall = recall;
1081
+ recallQuery = expandedText;
1082
+ messages.push({ role: "user", content: [{ type: "text", text: recall }], timestamp: Date.now() });
1083
+ }
1084
+ }
1085
+ catch {
1086
+ // recall must never break a turn
1087
+ }
1088
+ }
1048
1089
  // Add user message
1049
1090
  const userContent = [{ type: "text", text: expandedText }];
1050
1091
  if (currentImages) {
@@ -1093,6 +1134,19 @@ export class AgentSession {
1093
1134
  }
1094
1135
  preflightResult?.(true);
1095
1136
  await this._runAgentPrompt(messages);
1137
+ // R4: score whether the agent actually used the recalled context, so the recall gate can adapt.
1138
+ if (injectedRecall) {
1139
+ const response = this._findLastAssistantMessage();
1140
+ const responseText = response
1141
+ ? response.content
1142
+ .filter((c) => c.type === "text")
1143
+ .map((c) => c.text)
1144
+ .join(" ")
1145
+ : "";
1146
+ if (responseText) {
1147
+ this._effectivenessTracker.recordRecallOutcome(injectedRecall, recallQuery, responseText);
1148
+ }
1149
+ }
1096
1150
  }
1097
1151
  /**
1098
1152
  * Try to execute an extension command. Returns true if command was found and executed.
@@ -2202,6 +2256,9 @@ export class AgentSession {
2202
2256
  await this._memoryManager.shutdownAll().catch(() => { });
2203
2257
  const manager = new MemoryManager();
2204
2258
  manager.registerProvider(new FileStoreProvider());
2259
+ // Bundled read-only cross-session recall (R3): indexes past-session transcripts and answers
2260
+ // prefetch() with a <memory_context> page. Never writes.
2261
+ manager.registerProvider(new TranscriptRecallProvider());
2205
2262
  for (const provider of this._pendingMemoryProviders) {
2206
2263
  try {
2207
2264
  manager.registerProvider(provider);