@caupulican/pi-adaptative 0.80.90 → 0.80.93

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 (44) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/core/agent-session.d.ts +18 -0
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +134 -35
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/compaction/compaction.d.ts +2 -2
  7. package/dist/core/compaction/compaction.d.ts.map +1 -1
  8. package/dist/core/compaction/compaction.js +15 -5
  9. package/dist/core/compaction/compaction.js.map +1 -1
  10. package/dist/core/context/brain-curator.d.ts +21 -0
  11. package/dist/core/context/brain-curator.d.ts.map +1 -1
  12. package/dist/core/context/brain-curator.js +66 -0
  13. package/dist/core/context/brain-curator.js.map +1 -1
  14. package/dist/core/context/context-composition.d.ts +2 -0
  15. package/dist/core/context/context-composition.d.ts.map +1 -1
  16. package/dist/core/context/context-composition.js +1 -1
  17. package/dist/core/context/context-composition.js.map +1 -1
  18. package/dist/core/profile-resource-selection.d.ts.map +1 -1
  19. package/dist/core/profile-resource-selection.js +19 -6
  20. package/dist/core/profile-resource-selection.js.map +1 -1
  21. package/dist/core/resource-loader.d.ts +22 -0
  22. package/dist/core/resource-loader.d.ts.map +1 -1
  23. package/dist/core/resource-loader.js +54 -0
  24. package/dist/core/resource-loader.js.map +1 -1
  25. package/dist/core/settings-manager.d.ts +8 -0
  26. package/dist/core/settings-manager.d.ts.map +1 -1
  27. package/dist/core/settings-manager.js +25 -0
  28. package/dist/core/settings-manager.js.map +1 -1
  29. package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
  30. package/dist/modes/interactive/components/profile-resource-editor.js +11 -4
  31. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
  32. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  33. package/dist/modes/interactive/interactive-mode.js +65 -14
  34. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  35. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  36. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  37. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  38. package/examples/extensions/sandbox/package-lock.json +2 -2
  39. package/examples/extensions/sandbox/package.json +1 -1
  40. package/examples/extensions/with-deps/package-lock.json +2 -2
  41. package/examples/extensions/with-deps/package.json +1 -1
  42. package/examples/sdk/12-full-control.ts +4 -0
  43. package/npm-shrinkwrap.json +12 -12
  44. package/package.json +4 -4
@@ -28,7 +28,7 @@ import { composeSubagentSystemPrompt } from "./autonomy/subagent-prompt.js";
28
28
  import { executeBashWithOperations } from "./bash-executor.js";
29
29
  import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
30
30
  // (module-scope helper for curation goal extraction defined below the imports)
31
- import { BrainCurator } from "./context/brain-curator.js";
31
+ import { BrainCurator, preDigestConversationText } from "./context/brain-curator.js";
32
32
  import { createFileArtifactStore } from "./context/context-artifacts.js";
33
33
  import { runContextAudit } from "./context/context-audit.js";
34
34
  import { buildContextCompositionReport, formatContextCompositionDashboard, } from "./context/context-composition.js";
@@ -228,6 +228,9 @@ export class AgentSession {
228
228
  * contextPolicy.curation setting is enabled AND the model passes the digest fitness gate. */
229
229
  _brainCurator = new BrainCurator();
230
230
  _lastCurationSkipReason = undefined;
231
+ _inertExtensionWarnings = [];
232
+ _lastPreDigestSkipReason = undefined;
233
+ _unboundToolGrantWarnings = [];
231
234
  _toolArtifactStore = undefined;
232
235
  _latestContextAuditReport = undefined;
233
236
  _latestPromptPolicyReport = undefined;
@@ -772,48 +775,113 @@ export class AgentSession {
772
775
  * fitness probe on THIS host (design: unfit or unprobed models are refused with a visible
773
776
  * reason, never silently degraded). Fire-and-forget; never throws into a turn.
774
777
  */
778
+ /**
779
+ * Resolve the curation model IFF every gate passes: setting enabled, model configured,
780
+ * resolvable+authed, and digest-fitness-proven on THIS host (canonical "provider/id" ref —
781
+ * runModelFitness stores reports under it, while settings.model may be a bare id or pattern).
782
+ * Sets _lastCurationSkipReason on refusal; never throws.
783
+ */
784
+ _resolveCurationModelIfFit() {
785
+ const settings = this.settingsManager.getContextCurationSettings();
786
+ if (!settings.enabled) {
787
+ // Never surface a stale refusal reason for a feature the user has since disabled.
788
+ this._lastCurationSkipReason = undefined;
789
+ return undefined;
790
+ }
791
+ if (!settings.model) {
792
+ this._lastCurationSkipReason = "curation_model_unset";
793
+ return undefined;
794
+ }
795
+ const resolved = resolveCliModel({ cliModel: settings.model, modelRegistry: this._modelRegistry });
796
+ if (!resolved.model || !this._modelRegistry.hasConfiguredAuth(resolved.model)) {
797
+ this._lastCurationSkipReason = "curation_model_unresolved";
798
+ return undefined;
799
+ }
800
+ const canonicalRef = `${resolved.model.provider}/${resolved.model.id}`;
801
+ const fitness = FitnessStore.forAgentDir(this._agentDir)
802
+ .getForHost()
803
+ .find((entry) => entry.model === canonicalRef);
804
+ const digestScore = fitness?.report.digest;
805
+ if (!digestScore) {
806
+ this._lastCurationSkipReason = "curation_model_unprobed";
807
+ return undefined;
808
+ }
809
+ if (digestScore.succeeded < Math.ceil(digestScore.total * (2 / 3))) {
810
+ this._lastCurationSkipReason = "curation_model_digest_unfit";
811
+ return undefined;
812
+ }
813
+ this._lastCurationSkipReason = undefined;
814
+ return resolved.model;
815
+ }
775
816
  _maybeDrainBrainCuration() {
776
817
  try {
777
- const settings = this.settingsManager.getContextCurationSettings();
778
- if (!settings.enabled) {
779
- // Never surface a stale refusal reason for a feature the user has since disabled.
780
- this._lastCurationSkipReason = undefined;
781
- return;
782
- }
783
818
  if (!this._brainCurator.hasWork() || this._brainCurator.isDraining)
784
819
  return;
785
- if (!settings.model) {
786
- this._lastCurationSkipReason = "curation_model_unset";
787
- return;
788
- }
789
- const resolved = resolveCliModel({ cliModel: settings.model, modelRegistry: this._modelRegistry });
790
- if (!resolved.model || !this._modelRegistry.hasConfiguredAuth(resolved.model)) {
791
- this._lastCurationSkipReason = "curation_model_unresolved";
792
- return;
793
- }
794
- // Match on the CANONICAL "provider/id" ref — runModelFitness stores reports under it,
795
- // while settings.model may be a bare id or pattern; comparing raw strings would refuse
796
- // forever with curation_model_unprobed even after a successful probe.
797
- const canonicalRef = `${resolved.model.provider}/${resolved.model.id}`;
798
- const fitness = FitnessStore.forAgentDir(this._agentDir)
799
- .getForHost()
800
- .find((entry) => entry.model === canonicalRef);
801
- const digestScore = fitness?.report.digest;
802
- if (!digestScore) {
803
- this._lastCurationSkipReason = "curation_model_unprobed";
804
- return;
805
- }
806
- if (digestScore.succeeded < Math.ceil(digestScore.total * (2 / 3))) {
807
- this._lastCurationSkipReason = "curation_model_digest_unfit";
820
+ const model = this._resolveCurationModelIfFit();
821
+ if (!model)
808
822
  return;
809
- }
810
- this._lastCurationSkipReason = undefined;
811
- void this._drainBrainCuration(resolved.model, settings.maxJobsPerTurn);
823
+ const settings = this.settingsManager.getContextCurationSettings();
824
+ void this._drainBrainCuration(model, settings.maxJobsPerTurn);
812
825
  }
813
826
  catch {
814
827
  // curation is a sidecar; it must never disrupt a turn
815
828
  }
816
829
  }
830
+ /**
831
+ * Compaction pre-digest gate (design surface 3): everything the drain gate requires PLUS a
832
+ * RUNTIME reliability proof — the curator must have run >=5 jobs on this session with a parse
833
+ * failure rate <=5% before it is trusted to pre-digest compaction input. Returns undefined
834
+ * (verbatim compaction, byte-for-byte today's behavior) whenever any gate refuses.
835
+ */
836
+ _buildCompactionPreDigest() {
837
+ try {
838
+ const model = this._resolveCurationModelIfFit();
839
+ if (!model)
840
+ return undefined;
841
+ const telemetry = this._brainCurator.telemetry();
842
+ if (telemetry.jobsRun < 5 || telemetry.parseFailures / telemetry.jobsRun > 0.05) {
843
+ this._lastPreDigestSkipReason = "curation_predigest_reliability_unproven";
844
+ return undefined;
845
+ }
846
+ this._lastPreDigestSkipReason = undefined;
847
+ return async (text, signal) => {
848
+ const result = await preDigestConversationText({
849
+ text,
850
+ signal,
851
+ complete: async ({ systemPrompt, userPrompt, signal: chunkSignal }) => {
852
+ const completion = await this.runIsolatedCompletion({
853
+ systemPrompt,
854
+ messages: [{ role: "user", content: [{ type: "text", text: userPrompt }], timestamp: Date.now() }],
855
+ model,
856
+ thinkingLevel: "off",
857
+ maxTokens: 512,
858
+ signal: chunkSignal,
859
+ cacheRetention: "short",
860
+ });
861
+ return {
862
+ text: completion.text,
863
+ costUsd: completion.usage.cost.total,
864
+ stopReason: String(completion.stopReason),
865
+ };
866
+ },
867
+ });
868
+ if (!this._disposed && result.totalChunks > 0) {
869
+ this.sessionManager.appendCustomEntry("brain-curation-predigest", {
870
+ version: 1,
871
+ totalChunks: result.totalChunks,
872
+ digested: result.digested,
873
+ failed: result.failed,
874
+ charsBefore: text.length,
875
+ charsAfter: result.text.length,
876
+ });
877
+ }
878
+ return result.text;
879
+ };
880
+ }
881
+ catch {
882
+ return undefined;
883
+ }
884
+ }
817
885
  async _drainBrainCuration(model, maxJobs) {
818
886
  try {
819
887
  // ACCUMULATE across all drained jobs (the drain runs the completer once PER job) —
@@ -932,6 +1000,15 @@ export class AgentSession {
932
1000
  },
933
1001
  spawned: { cost: spawned.cost, reports: spawned.reports },
934
1002
  adjustments: { memoryEvidenceTokens, enforcementSavedTokens },
1003
+ extraObservations: [
1004
+ ...this._resourceLoader.getAgentsDiagnostics().map((diagnostic) => diagnostic.message),
1005
+ ...this._inertExtensionWarnings,
1006
+ ...this._unboundToolGrantWarnings,
1007
+ // G14 (ratified): a user disable always beats a profile grant — surface the conflict.
1008
+ ...["tools", "skills", "prompts", "extensions"].flatMap((kind) => this.settingsManager
1009
+ .getProfileGrantsOverriddenByUserDisable(kind)
1010
+ .map((entry) => `profile grants ${kind} "${entry}" but your disable list overrides it (user disable wins; re-enable to use)`)),
1011
+ ],
935
1012
  });
936
1013
  }
937
1014
  /** Bounded plain-text rendering of {@link getContextCompositionReport} for the /context command. */
@@ -946,6 +1023,7 @@ export class AgentSession {
946
1023
  model: settings.model,
947
1024
  telemetry: this._brainCurator.telemetry(),
948
1025
  lastSkipReason: this._lastCurationSkipReason,
1026
+ lastPreDigestSkipReason: this._lastPreDigestSkipReason,
949
1027
  };
950
1028
  }
951
1029
  /** Read-only inspection of the latest prompt-enforcement report, for tests/debugging. */
@@ -3036,7 +3114,7 @@ export class AgentSession {
3036
3114
  }
3037
3115
  else {
3038
3116
  // Generate compaction result
3039
- const result = await compact(preparation, compactionModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFn);
3117
+ const result = await compact(preparation, compactionModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, this._buildCompactionPreDigest());
3040
3118
  summary = result.summary;
3041
3119
  firstKeptEntryId = result.firstKeptEntryId;
3042
3120
  tokensBefore = result.tokensBefore;
@@ -3335,7 +3413,7 @@ export class AgentSession {
3335
3413
  }
3336
3414
  else {
3337
3415
  // Generate compaction result
3338
- const compactResult = await compact(preparation, compactionModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFn);
3416
+ const compactResult = await compact(preparation, compactionModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFn, this._buildCompactionPreDigest());
3339
3417
  summary = compactResult.summary;
3340
3418
  firstKeptEntryId = compactResult.firstKeptEntryId;
3341
3419
  tokensBefore = compactResult.tokensBefore;
@@ -3650,6 +3728,7 @@ export class AgentSession {
3650
3728
  (this._toolProfileFilter.allow.length > 0 || this._toolProfileFilter.block.length > 0)));
3651
3729
  }
3652
3730
  _filterExtensionsForRuntime(extensions) {
3731
+ this._inertExtensionWarnings = [];
3653
3732
  if (this.settingsManager.getActiveResourceProfileNames().length === 0) {
3654
3733
  return this.settingsManager.hasExplicitActiveResourceProfileSelection()
3655
3734
  ? []
@@ -3663,6 +3742,13 @@ export class AgentSession {
3663
3742
  return extension;
3664
3743
  const tools = new Map(Array.from(extension.tools.entries()).filter(([name]) => this._isToolOrCommandAllowedByProfile(name)));
3665
3744
  const commands = new Map(Array.from(extension.commands.entries()).filter(([name]) => this._isToolOrCommandAllowedByProfile(name)));
3745
+ // G12: an extension the profile ALLOWS whose every tool and command the tools filter
3746
+ // then denies loads and runs lifecycle hooks but is completely uninvocable — surface
3747
+ // it instead of presenting a "loaded" extension that silently does nothing.
3748
+ if (extension.tools.size + extension.commands.size > 0 && tools.size + commands.size === 0) {
3749
+ const name = extension.path.split(/[\\/]/).pop() ?? extension.path;
3750
+ this._inertExtensionWarnings.push(`extension "${name}" is loaded but fully inert: the active profile's tools filter denies all ${extension.tools.size} tool(s) and ${extension.commands.size} command(s) it contributes`);
3751
+ }
3666
3752
  return { ...extension, tools, commands };
3667
3753
  });
3668
3754
  }
@@ -3867,14 +3953,27 @@ export class AgentSession {
3867
3953
  // stays grant-only: activation then still derives from the request/defaults above.
3868
3954
  const explicitAllowPatterns = toolProfileFilter?.allow.filter((pattern) => pattern !== "*") ?? [];
3869
3955
  if (explicitAllowPatterns.length > 0) {
3956
+ const boundPatterns = new Set();
3870
3957
  for (const toolName of this._toolRegistry.keys()) {
3871
3958
  if (!isAllowedTool(toolName))
3872
3959
  continue;
3960
+ for (const pattern of explicitAllowPatterns) {
3961
+ if (matchesResourceProfilePattern(toolName, [pattern]))
3962
+ boundPatterns.add(pattern);
3963
+ }
3873
3964
  if (matchesResourceProfilePattern(toolName, explicitAllowPatterns)) {
3874
3965
  nextActiveToolNames.push(toolName);
3875
3966
  autoActivated.push(toolName);
3876
3967
  }
3877
3968
  }
3969
+ // G13: an explicit grant that binds to NO registered tool is a silent no-op — typo'd
3970
+ // name, or the owning extension is not granted/loaded. Surface it.
3971
+ this._unboundToolGrantWarnings = explicitAllowPatterns
3972
+ .filter((pattern) => !boundPatterns.has(pattern))
3973
+ .map((pattern) => `profile tool grant "${pattern}" binds to no registered tool (typo, or the owning extension is not granted/loaded)`);
3974
+ }
3975
+ else {
3976
+ this._unboundToolGrantWarnings = [];
3878
3977
  }
3879
3978
  // artifact_retrieve companion auto-activation is enforced inside
3880
3979
  // setActiveToolsByName() itself (not duplicated here), so every activation path --