@caupulican/pi-adaptative 0.80.58 → 0.80.60

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/core/agent-session.d.ts +58 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +155 -5
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/extension-metadata.d.ts +20 -0
  7. package/dist/core/extension-metadata.d.ts.map +1 -0
  8. package/dist/core/extension-metadata.js +115 -0
  9. package/dist/core/extension-metadata.js.map +1 -0
  10. package/dist/core/extensions/runner.d.ts +1 -0
  11. package/dist/core/extensions/runner.d.ts.map +1 -1
  12. package/dist/core/extensions/runner.js +3 -0
  13. package/dist/core/extensions/runner.js.map +1 -1
  14. package/dist/core/learning/reflection-engine.d.ts +57 -0
  15. package/dist/core/learning/reflection-engine.d.ts.map +1 -0
  16. package/dist/core/learning/reflection-engine.js +118 -0
  17. package/dist/core/learning/reflection-engine.js.map +1 -0
  18. package/dist/core/memory/memory-manager.d.ts +8 -0
  19. package/dist/core/memory/memory-manager.d.ts.map +1 -1
  20. package/dist/core/memory/memory-manager.js +14 -2
  21. package/dist/core/memory/memory-manager.js.map +1 -1
  22. package/dist/core/resource-loader.d.ts +26 -0
  23. package/dist/core/resource-loader.d.ts.map +1 -1
  24. package/dist/core/resource-loader.js +53 -14
  25. package/dist/core/resource-loader.js.map +1 -1
  26. package/dist/core/system-prompt.d.ts +3 -0
  27. package/dist/core/system-prompt.d.ts.map +1 -1
  28. package/dist/core/system-prompt.js +54 -7
  29. package/dist/core/system-prompt.js.map +1 -1
  30. package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
  31. package/dist/modes/interactive/components/profile-resource-editor.js +6 -2
  32. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
  33. package/dist/modes/interactive/interactive-mode.d.ts +13 -0
  34. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  35. package/dist/modes/interactive/interactive-mode.js +69 -4
  36. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  37. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  38. package/dist/modes/rpc/rpc-mode.js +1 -1
  39. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  40. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  41. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  42. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  43. package/examples/extensions/sandbox/package-lock.json +2 -2
  44. package/examples/extensions/sandbox/package.json +1 -1
  45. package/examples/extensions/with-deps/package-lock.json +2 -2
  46. package/examples/extensions/with-deps/package.json +1 -1
  47. package/examples/sdk/12-full-control.ts +3 -0
  48. package/npm-shrinkwrap.json +12 -12
  49. package/package.json +4 -4
@@ -31,6 +31,7 @@ import { createCoreDiagnosticsToolDefinitions } from "./extensions/builtin.js";
31
31
  import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
32
32
  import { disposeExtensionEventSubscriptions } from "./extensions/loader.js";
33
33
  import { emitSessionShutdownEvent } from "./extensions/runner.js";
34
+ import { decideDemand, ReflectionEngine, } from "./learning/reflection-engine.js";
34
35
  import { MemoryManager } from "./memory/memory-manager.js";
35
36
  import { FileStoreProvider } from "./memory/providers/file-store.js";
36
37
  import { compactToolResultDetailsForRetention } from "./message-retention.js";
@@ -776,7 +777,7 @@ export class AgentSession {
776
777
  }
777
778
  /** File-based prompt templates */
778
779
  get promptTemplates() {
779
- return this._resourceLoader.getPrompts().prompts;
780
+ return this._resourceLoader.getActivePrompts();
780
781
  }
781
782
  _normalizePromptSnippet(text) {
782
783
  if (!text)
@@ -885,7 +886,9 @@ export class AgentSession {
885
886
  ...loaderAppendSystemPrompt,
886
887
  ].filter((part) => Boolean(part));
887
888
  const appendSystemPrompt = appendSystemPromptParts.length > 0 ? appendSystemPromptParts.join("\n\n") : undefined;
888
- const loadedSkills = this._resourceLoader.getSkills().skills;
889
+ // Only surface skills the active profile permits — the agent must not be told about (or able
890
+ // to invoke) a skill its profile blocks.
891
+ const loadedSkills = this._resourceLoader.getActiveSkills();
889
892
  const loadedContextFiles = this._resourceLoader.getAgentsFiles().agentsFiles;
890
893
  this._baseSystemPromptOptions = {
891
894
  cwd: this._cwd,
@@ -896,6 +899,7 @@ export class AgentSession {
896
899
  selectedTools: validToolNames,
897
900
  toolSnippets,
898
901
  promptGuidelines,
902
+ extensions: [...this._extensionRunner.activeExtensions],
899
903
  };
900
904
  return buildSystemPrompt(this._baseSystemPromptOptions);
901
905
  }
@@ -1132,9 +1136,11 @@ export class AgentSession {
1132
1136
  const spaceIndex = text.indexOf(" ");
1133
1137
  const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);
1134
1138
  const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
1135
- const skill = this.resourceLoader.getSkills().skills.find((s) => s.name === skillName);
1139
+ // Resolve only against profile-active skills so a `/skill:` the active profile blocks cannot be
1140
+ // expanded/invoked — by the user OR the agent — even if it loaded before a runtime profile switch.
1141
+ const skill = this.resourceLoader.getActiveSkills().find((s) => s.name === skillName);
1136
1142
  if (!skill)
1137
- return text; // Unknown skill, pass through
1143
+ return text; // Unknown or profile-blocked skill, pass through unchanged
1138
1144
  try {
1139
1145
  const content = readFileSync(skill.filePath, "utf-8");
1140
1146
  const body = stripResourceProfileBlocks(stripFrontmatter(content)).trim();
@@ -2030,7 +2036,7 @@ export class AgentSession {
2030
2036
  source: "prompt",
2031
2037
  sourceInfo: template.sourceInfo,
2032
2038
  }));
2033
- const skills = this._resourceLoader.getSkills().skills.map((skill) => ({
2039
+ const skills = this._resourceLoader.getActiveSkills().map((skill) => ({
2034
2040
  name: `skill:${skill.name}`,
2035
2041
  description: skill.description,
2036
2042
  source: "skill",
@@ -3204,6 +3210,150 @@ export class AgentSession {
3204
3210
  }
3205
3211
  return { cost, reports };
3206
3212
  }
3213
+ /**
3214
+ * Run a one-shot LLM completion fully ISOLATED from the main session — the load-bearing
3215
+ * primitive for the native reflection engine (adaptive-agent design §6c/§7).
3216
+ *
3217
+ * Isolation invariants (audited by codex): builds a fresh {@link Context} (no main history), runs
3218
+ * with `tools: []`, sets `cacheRetention: "none"`, and passes **no `sessionId`** — so it cannot
3219
+ * mutate `agent.state.messages`, cannot append session entries, cannot touch the tool registry,
3220
+ * and cannot churn the main session's prompt cache. Mirrors `generateSummary()`'s mechanics.
3221
+ *
3222
+ * Returns the result even on an error/aborted stop reason (callers — e.g. a background reflection
3223
+ * microtask — decide whether to act); it does not throw on a model-level error.
3224
+ */
3225
+ async runIsolatedCompletion(opts) {
3226
+ const model = opts.model ?? this.model;
3227
+ if (!model) {
3228
+ throw new Error("runIsolatedCompletion: no model available");
3229
+ }
3230
+ const thinkingLevel = opts.thinkingLevel ?? "off";
3231
+ // Fresh, isolated context: explicit messages, no tools, nothing from the main session.
3232
+ const context = {
3233
+ systemPrompt: opts.systemPrompt,
3234
+ messages: opts.messages,
3235
+ tools: [],
3236
+ };
3237
+ // Isolate the prompt cache and DELIBERATELY omit sessionId so no session-aware caching/routing
3238
+ // can entangle this call with the main session.
3239
+ const options = {
3240
+ maxTokens: opts.maxTokens,
3241
+ signal: opts.signal,
3242
+ cacheRetention: "none",
3243
+ };
3244
+ // pi-ai's `reasoning` option does not include "off" (that's the provider default already).
3245
+ if (thinkingLevel !== "off") {
3246
+ options.reasoning = thinkingLevel;
3247
+ }
3248
+ // When streamFn is the raw streamSimple (e.g. in tests), auth must be injected explicitly.
3249
+ // Throw only when auth genuinely fails — providers that authenticate without an API key
3250
+ // (OAuth, local no-key) legitimately return ok with an undefined apiKey.
3251
+ if (this.agent.streamFn === streamSimple) {
3252
+ const auth = await this._modelRegistry.getApiKeyAndHeaders(model);
3253
+ if (!auth.ok) {
3254
+ throw new Error(auth.error);
3255
+ }
3256
+ options.apiKey = auth.apiKey;
3257
+ options.headers = auth.headers;
3258
+ }
3259
+ const stream = await this.agent.streamFn(model, context, options);
3260
+ const result = await stream.result();
3261
+ const text = result.content
3262
+ .filter((c) => c.type === "text")
3263
+ .map((c) => c.text)
3264
+ .join("");
3265
+ const usage = result.usage ?? {
3266
+ input: 0,
3267
+ output: 0,
3268
+ cacheRead: 0,
3269
+ cacheWrite: 0,
3270
+ totalTokens: 0,
3271
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
3272
+ };
3273
+ return { text, usage, stopReason: result.stopReason };
3274
+ }
3275
+ /**
3276
+ * Native end-of-loop reflection pass (R2). Demand-gates (zero-I/O), and when warranted runs the
3277
+ * {@link ReflectionEngine} via an isolated completion ({@link runIsolatedCompletion}), applies the
3278
+ * resulting memory writes through the bundled `memory` tool, and accounts the reflection's token
3279
+ * cost via the cost-aggregation surface so it stays visible and net-negative-auditable.
3280
+ *
3281
+ * Returns `null` when the gate skips (or in a child session, which must not learn). The whole pass
3282
+ * is best-effort: a model/parse error yields no writes, never throws into the caller.
3283
+ */
3284
+ async runReflectionPass(input) {
3285
+ if (this._isChildSession)
3286
+ return null;
3287
+ const plan = decideDemand(input.signals);
3288
+ if (plan.act === "skip")
3289
+ return null;
3290
+ const complete = (systemPrompt, userPrompt) => this.runIsolatedCompletion({
3291
+ systemPrompt,
3292
+ messages: [{ role: "user", content: [{ type: "text", text: userPrompt }], timestamp: Date.now() }],
3293
+ model: input.model,
3294
+ thinkingLevel: input.thinkingLevel ?? "low",
3295
+ maxTokens: plan.tokenBudget,
3296
+ signal: input.signal,
3297
+ });
3298
+ const result = await new ReflectionEngine().reflect({
3299
+ recentTurnText: input.recentTurnText,
3300
+ // Read memory FRESH (not the prefix-cache-frozen system-prompt block) so confront-before-write
3301
+ // sees writes made earlier this session.
3302
+ existingMemory: this._memoryManager.buildSystemPromptBlockFresh() || "",
3303
+ plan,
3304
+ complete,
3305
+ });
3306
+ for (const write of result.writes) {
3307
+ await this._applyReflectionWrite(write, input.signal);
3308
+ }
3309
+ // Account the reflection's spend so it surfaces in the footer roll-up (net-token visibility).
3310
+ // Idempotent on reportId so a retried/duplicated pass cannot double-count.
3311
+ if (result.usage.cost.total > 0 || result.usage.totalTokens > 0) {
3312
+ this.addSpawnedUsage(result.usage, { label: "reflection", reportId: input.reportId });
3313
+ }
3314
+ return result;
3315
+ }
3316
+ /**
3317
+ * Apply one reflection write through the bundled `memory` tool. `memory_replace`/`memory_remove`
3318
+ * don't carry a target file, so we try MEMORY.md first and fall back to USER.md when the substring
3319
+ * isn't found there. Best-effort: failures are swallowed (reflection must never break a turn).
3320
+ */
3321
+ async _applyReflectionWrite(write, signal) {
3322
+ const memTool = this._memoryManager.getToolDefinitions().find((t) => t.name === "memory");
3323
+ const exec = memTool?.execute;
3324
+ if (!exec)
3325
+ return;
3326
+ const run = (params) => exec("reflection", params, signal, undefined, undefined);
3327
+ if (write.kind === "memory_add") {
3328
+ try {
3329
+ await run({ action: "add", target: write.section === "USER" ? "user" : "memory", content: write.text });
3330
+ }
3331
+ catch {
3332
+ // best-effort; reflection writes must never throw into the turn loop
3333
+ }
3334
+ return;
3335
+ }
3336
+ // replace / remove carry no target file — try MEMORY.md, then USER.md. The memory tool reports
3337
+ // outcomes via `details.success` (it catches its own errors rather than throwing). Only a
3338
+ // genuine "not found in the file" justifies trying the other file; a real failure for a file
3339
+ // (budget exceeded / drift) must NOT fall through and mutate the wrong target.
3340
+ for (const target of ["memory", "user"]) {
3341
+ try {
3342
+ const params = write.kind === "memory_replace"
3343
+ ? { action: "replace", target, oldContent: write.target, content: write.text }
3344
+ : { action: "remove", target, oldContent: write.target };
3345
+ const res = await run(params);
3346
+ if (res?.details?.success === true)
3347
+ return; // applied
3348
+ if (!/not found/i.test(String(res?.details?.error ?? "")))
3349
+ return; // real failure — don't misapply
3350
+ // substring simply absent from this file — try the next target
3351
+ }
3352
+ catch {
3353
+ // defensive: if the tool ever does throw, try the next target
3354
+ }
3355
+ }
3356
+ }
3207
3357
  getContextUsage() {
3208
3358
  const model = this.model;
3209
3359
  if (!model)