@caupulican/pi-adaptative 0.80.88 → 0.80.90
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 +39 -0
- package/dist/core/agent-session.d.ts +35 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +279 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/context/brain-curator.d.ts +88 -0
- package/dist/core/context/brain-curator.d.ts.map +1 -0
- package/dist/core/context/brain-curator.js +192 -0
- package/dist/core/context/brain-curator.js.map +1 -0
- package/dist/core/context/context-composition.d.ts +122 -0
- package/dist/core/context/context-composition.d.ts.map +1 -0
- package/dist/core/context/context-composition.js +163 -0
- package/dist/core/context/context-composition.js.map +1 -0
- package/dist/core/context/context-prompt-enforcement.d.ts +13 -0
- package/dist/core/context/context-prompt-enforcement.d.ts.map +1 -1
- package/dist/core/context/context-prompt-enforcement.js +17 -2
- package/dist/core/context/context-prompt-enforcement.js.map +1 -1
- package/dist/core/context-gc.d.ts +13 -0
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +6 -0
- package/dist/core/context-gc.js.map +1 -1
- package/dist/core/research/model-fitness.d.ts +3 -0
- package/dist/core/research/model-fitness.d.ts.map +1 -1
- package/dist/core/research/model-fitness.js +54 -3
- package/dist/core/research/model-fitness.js.map +1 -1
- package/dist/core/settings-manager.d.ts +13 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +19 -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 +6 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/modes/interactive/components/fitness-role-selector.d.ts +13 -0
- package/dist/modes/interactive/components/fitness-role-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/fitness-role-selector.js +65 -0
- package/dist/modes/interactive/components/fitness-role-selector.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +4 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +84 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +91 -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
|
@@ -27,8 +27,11 @@ import { appendLaneRecordSnapshot, getLaneRecordSnapshots } from "./autonomy/ses
|
|
|
27
27
|
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
|
+
// (module-scope helper for curation goal extraction defined below the imports)
|
|
31
|
+
import { BrainCurator } from "./context/brain-curator.js";
|
|
30
32
|
import { createFileArtifactStore } from "./context/context-artifacts.js";
|
|
31
33
|
import { runContextAudit } from "./context/context-audit.js";
|
|
34
|
+
import { buildContextCompositionReport, formatContextCompositionDashboard, } from "./context/context-composition.js";
|
|
32
35
|
import { enforcePromptPolicy } from "./context/context-prompt-enforcement.js";
|
|
33
36
|
import { correlateWithContextGc, planPromptPolicy, } from "./context/context-prompt-policy.js";
|
|
34
37
|
import { defaultMemoryPromptInclusionReport, sanitizeMemoryRetrievalReportForDiagnostics, } from "./context/memory-diagnostics.js";
|
|
@@ -107,6 +110,23 @@ export function parseSkillBlock(text) {
|
|
|
107
110
|
}
|
|
108
111
|
/** customType for spawned-usage roll-up entries (Cost Aggregation, Model A). */
|
|
109
112
|
export const SPAWNED_USAGE_CUSTOM_TYPE = "spawned_usage";
|
|
113
|
+
/** Latest user prompt text in the provider-visible array (curation goal line; bounded by caller). */
|
|
114
|
+
function latestUserPromptText(messages) {
|
|
115
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
116
|
+
const message = messages[index];
|
|
117
|
+
if (!message || message.role !== "user")
|
|
118
|
+
continue;
|
|
119
|
+
if (typeof message.content === "string")
|
|
120
|
+
return message.content;
|
|
121
|
+
const text = message.content
|
|
122
|
+
.filter((part) => part.type === "text")
|
|
123
|
+
.map((part) => part.text)
|
|
124
|
+
.join("\n");
|
|
125
|
+
if (text.length > 0)
|
|
126
|
+
return text;
|
|
127
|
+
}
|
|
128
|
+
return "";
|
|
129
|
+
}
|
|
110
130
|
// ============================================================================
|
|
111
131
|
// Constants
|
|
112
132
|
// ============================================================================
|
|
@@ -204,6 +224,10 @@ export class AgentSession {
|
|
|
204
224
|
_autoCompactionAbortController = undefined;
|
|
205
225
|
_overflowRecoveryAttempted = false;
|
|
206
226
|
_latestContextGcReport = undefined;
|
|
227
|
+
/** Brain-curation sidecar (design: brain-context-curation-design.md). Inert unless the
|
|
228
|
+
* contextPolicy.curation setting is enabled AND the model passes the digest fitness gate. */
|
|
229
|
+
_brainCurator = new BrainCurator();
|
|
230
|
+
_lastCurationSkipReason = undefined;
|
|
207
231
|
_toolArtifactStore = undefined;
|
|
208
232
|
_latestContextAuditReport = undefined;
|
|
209
233
|
_latestPromptPolicyReport = undefined;
|
|
@@ -424,6 +448,9 @@ export class AgentSession {
|
|
|
424
448
|
const gcResult = this._applyContextGc(finalMessages, true);
|
|
425
449
|
this._correlatePromptPolicyWithContextGc(gcResult.report);
|
|
426
450
|
const enforcementResult = this._runPromptEnforcement(gcResult.messages, shadowReport);
|
|
451
|
+
this._enqueueRelevanceCuration(gcResult.messages, shadowReport);
|
|
452
|
+
// Fire-and-forget: the local curator overlaps the frontier call; it never blocks a turn.
|
|
453
|
+
this._maybeDrainBrainCuration();
|
|
427
454
|
// Appended LAST, after gc and enforcement, so the bounded evidence block is
|
|
428
455
|
// never packed/stubbed/reshaped by either pass and always reflects this turn's
|
|
429
456
|
// fresh retrieval. Because nothing downstream trims it, memory-prompt-block.ts's
|
|
@@ -685,6 +712,7 @@ export class AgentSession {
|
|
|
685
712
|
_runPromptEnforcement(messages, shadowReport) {
|
|
686
713
|
try {
|
|
687
714
|
const persistedSettings = this.settingsManager.getContextPromptEnforcementSettings();
|
|
715
|
+
const curationEnabled = this.settingsManager.getContextCurationSettings().enabled;
|
|
688
716
|
const settings = {
|
|
689
717
|
...persistedSettings,
|
|
690
718
|
// Runtime fact, never assumed: artifact_retrieve is a companion affordance
|
|
@@ -692,6 +720,7 @@ export class AgentSession {
|
|
|
692
720
|
// tools can differ turn to turn -- see context-prompt-enforcement.ts's doc
|
|
693
721
|
// comment on why this is checked separately from hasAvailableRetrievalPath.
|
|
694
722
|
retrievalToolAvailable: this.getActiveToolNames().includes("artifact_retrieve"),
|
|
723
|
+
brainRelevance: curationEnabled ? (itemId) => this._brainCurator.getRelevance(itemId) : undefined,
|
|
695
724
|
};
|
|
696
725
|
const result = enforcePromptPolicy(messages, shadowReport, settings);
|
|
697
726
|
this._latestPromptEnforcementReport = result.report;
|
|
@@ -703,6 +732,222 @@ export class AgentSession {
|
|
|
703
732
|
return { messages, report };
|
|
704
733
|
}
|
|
705
734
|
}
|
|
735
|
+
/**
|
|
736
|
+
* Enqueue relevance-scoring jobs for stale, artifact-backed tool outputs the enforcement
|
|
737
|
+
* pilot could act on. Pure queueing — the verdicts only ever take effect through the
|
|
738
|
+
* asymmetric advisory lever inside enforcePromptPolicy. Never throws into a turn.
|
|
739
|
+
*/
|
|
740
|
+
_enqueueRelevanceCuration(messages, shadowReport) {
|
|
741
|
+
try {
|
|
742
|
+
const settings = this.settingsManager.getContextCurationSettings();
|
|
743
|
+
if (!settings.enabled)
|
|
744
|
+
return;
|
|
745
|
+
const goal = latestUserPromptText(messages).slice(0, 400);
|
|
746
|
+
for (const item of shadowReport.items) {
|
|
747
|
+
if (!item.hasAvailableRetrievalPath)
|
|
748
|
+
continue;
|
|
749
|
+
const message = messages[item.messageIndex];
|
|
750
|
+
if (!message || message.role !== "toolResult" || message.toolCallId !== item.toolCallId)
|
|
751
|
+
continue;
|
|
752
|
+
if (message.isError)
|
|
753
|
+
continue;
|
|
754
|
+
const details = message.details;
|
|
755
|
+
if (details?.contextGc?.packed === true || details?.promptPolicy?.enforced === true)
|
|
756
|
+
continue;
|
|
757
|
+
const text = message.content
|
|
758
|
+
.filter((part) => part.type === "text")
|
|
759
|
+
.map((part) => part.text)
|
|
760
|
+
.join("\n");
|
|
761
|
+
if (text.length === 0)
|
|
762
|
+
continue;
|
|
763
|
+
this._brainCurator.enqueue({ kind: "relevance", key: item.itemId, content: text.slice(0, 4000), goal });
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
catch {
|
|
767
|
+
// curation is a sidecar; it must never disrupt a turn
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Drain gate: settings on, model configured+authed, and the model has PASSED the digest
|
|
772
|
+
* fitness probe on THIS host (design: unfit or unprobed models are refused with a visible
|
|
773
|
+
* reason, never silently degraded). Fire-and-forget; never throws into a turn.
|
|
774
|
+
*/
|
|
775
|
+
_maybeDrainBrainCuration() {
|
|
776
|
+
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
|
+
if (!this._brainCurator.hasWork() || this._brainCurator.isDraining)
|
|
784
|
+
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";
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
this._lastCurationSkipReason = undefined;
|
|
811
|
+
void this._drainBrainCuration(resolved.model, settings.maxJobsPerTurn);
|
|
812
|
+
}
|
|
813
|
+
catch {
|
|
814
|
+
// curation is a sidecar; it must never disrupt a turn
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
async _drainBrainCuration(model, maxJobs) {
|
|
818
|
+
try {
|
|
819
|
+
// ACCUMULATE across all drained jobs (the drain runs the completer once PER job) —
|
|
820
|
+
// keeping only the last job's usage would under-report every multi-job drain.
|
|
821
|
+
let spentUsage;
|
|
822
|
+
const results = await this._brainCurator.drain({
|
|
823
|
+
maxJobs,
|
|
824
|
+
complete: async ({ systemPrompt, userPrompt, signal }) => {
|
|
825
|
+
const completion = await this.runIsolatedCompletion({
|
|
826
|
+
systemPrompt,
|
|
827
|
+
messages: [{ role: "user", content: [{ type: "text", text: userPrompt }], timestamp: Date.now() }],
|
|
828
|
+
model,
|
|
829
|
+
thinkingLevel: "off",
|
|
830
|
+
maxTokens: 256,
|
|
831
|
+
signal,
|
|
832
|
+
// Both curation system prompts are static — the provider can cache the prefix.
|
|
833
|
+
cacheRetention: "short",
|
|
834
|
+
});
|
|
835
|
+
const usage = completion.usage;
|
|
836
|
+
if (!spentUsage) {
|
|
837
|
+
spentUsage = structuredClone(usage);
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
spentUsage.input += usage.input;
|
|
841
|
+
spentUsage.output += usage.output;
|
|
842
|
+
spentUsage.cacheRead += usage.cacheRead;
|
|
843
|
+
spentUsage.cacheWrite += usage.cacheWrite;
|
|
844
|
+
spentUsage.totalTokens += usage.totalTokens;
|
|
845
|
+
spentUsage.cost.input += usage.cost.input;
|
|
846
|
+
spentUsage.cost.output += usage.cost.output;
|
|
847
|
+
spentUsage.cost.cacheRead += usage.cost.cacheRead;
|
|
848
|
+
spentUsage.cost.cacheWrite += usage.cost.cacheWrite;
|
|
849
|
+
spentUsage.cost.total += usage.cost.total;
|
|
850
|
+
}
|
|
851
|
+
return {
|
|
852
|
+
text: completion.text,
|
|
853
|
+
costUsd: completion.usage.cost.total,
|
|
854
|
+
stopReason: String(completion.stopReason),
|
|
855
|
+
};
|
|
856
|
+
},
|
|
857
|
+
});
|
|
858
|
+
// Honest accounting even for free local models: token visibility is the contract.
|
|
859
|
+
if (spentUsage && (spentUsage.cost.total > 0 || spentUsage.totalTokens > 0)) {
|
|
860
|
+
this.addSpawnedUsage(spentUsage, { label: "context-curator" });
|
|
861
|
+
}
|
|
862
|
+
if (this._disposed || results.length === 0)
|
|
863
|
+
return;
|
|
864
|
+
this.sessionManager.appendCustomEntry("brain-curation", {
|
|
865
|
+
version: 1,
|
|
866
|
+
results: results.map((result) => ({
|
|
867
|
+
key: result.key,
|
|
868
|
+
kind: result.kind,
|
|
869
|
+
ok: result.ok,
|
|
870
|
+
ms: result.ms,
|
|
871
|
+
...(result.digest !== undefined ? { digest: result.digest } : {}),
|
|
872
|
+
...(result.relevant !== undefined ? { relevant: result.relevant, confidence: result.confidence } : {}),
|
|
873
|
+
})),
|
|
874
|
+
telemetry: this._brainCurator.telemetry(),
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
catch {
|
|
878
|
+
// curation is a sidecar; it must never disrupt a turn
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Context composition dashboard data: decomposes the per-request payload (system prompt, tool
|
|
883
|
+
* schemas, extension contributions, message classes incl. GC/policy stubs and recall pages)
|
|
884
|
+
* plus background spend, so users can see exactly what their integrations cost per request.
|
|
885
|
+
* Read-only: uses the GC report path (writePayloads=false), never mutates anything.
|
|
886
|
+
*/
|
|
887
|
+
getContextCompositionReport() {
|
|
888
|
+
const rawMessages = this.agent.state.messages.slice();
|
|
889
|
+
const gcResult = this._applyContextGc(rawMessages, false);
|
|
890
|
+
const activeNames = new Set(this.getActiveToolNames());
|
|
891
|
+
const extensions = this._resourceLoader.getExtensions().extensions;
|
|
892
|
+
const extensionToolNames = new Set(extensions.flatMap((extension) => [...extension.tools.keys()]));
|
|
893
|
+
const usage = this.getContextUsage();
|
|
894
|
+
const enforcementItems = this.getPromptEnforcementReport().items;
|
|
895
|
+
const curationStatus = this.getContextCurationStatus();
|
|
896
|
+
const spawned = this.getSpawnedUsage();
|
|
897
|
+
const promptInclusion = this.getMemoryPromptInclusionReport();
|
|
898
|
+
const memoryEvidenceTokens = promptInclusion.status === "included" ? Math.ceil(promptInclusion.blockChars / 4) : 0;
|
|
899
|
+
// Enforcement stubs are applied at SEND time (not persisted), so the message view here
|
|
900
|
+
// still holds raw text for them; subtract what stubbing reclaims per request.
|
|
901
|
+
const enforcementSavedTokens = enforcementItems
|
|
902
|
+
.filter((item) => item.enforced && typeof item.originalChars === "number")
|
|
903
|
+
.reduce((sum, item) => sum + Math.max(0, Math.ceil((item.originalChars ?? 0) / 4) - 50), 0);
|
|
904
|
+
return buildContextCompositionReport({
|
|
905
|
+
systemPrompt: this.systemPrompt ?? "",
|
|
906
|
+
tools: this.getAllTools()
|
|
907
|
+
.filter((tool) => activeNames.has(tool.name))
|
|
908
|
+
.map((tool) => ({
|
|
909
|
+
name: tool.name,
|
|
910
|
+
description: tool.description,
|
|
911
|
+
parameters: tool.parameters,
|
|
912
|
+
source: extensionToolNames.has(tool.name) ? "extension" : "built-in",
|
|
913
|
+
})),
|
|
914
|
+
extensions: extensions.map((extension) => ({
|
|
915
|
+
name: basename(extension.path),
|
|
916
|
+
path: extension.path,
|
|
917
|
+
toolNames: [...extension.tools.keys()],
|
|
918
|
+
commandCount: extension.commands.size,
|
|
919
|
+
})),
|
|
920
|
+
messages: gcResult.messages,
|
|
921
|
+
providerReportedTokens: usage?.tokens ?? null,
|
|
922
|
+
contextWindow: usage?.contextWindow ?? this.model?.contextWindow ?? null,
|
|
923
|
+
gc: { packedCount: gcResult.report.packedCount, savedTokens: gcResult.report.savedTokens },
|
|
924
|
+
enforcement: {
|
|
925
|
+
enforcedCount: enforcementItems.filter((item) => item.enforced).length,
|
|
926
|
+
advisoryEvictions: enforcementItems.filter((item) => item.advisory === "brain_irrelevant").length,
|
|
927
|
+
},
|
|
928
|
+
curation: {
|
|
929
|
+
enabled: curationStatus.enabled,
|
|
930
|
+
telemetry: curationStatus.telemetry,
|
|
931
|
+
lastSkipReason: curationStatus.lastSkipReason,
|
|
932
|
+
},
|
|
933
|
+
spawned: { cost: spawned.cost, reports: spawned.reports },
|
|
934
|
+
adjustments: { memoryEvidenceTokens, enforcementSavedTokens },
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
/** Bounded plain-text rendering of {@link getContextCompositionReport} for the /context command. */
|
|
938
|
+
formatContextCompositionDashboard() {
|
|
939
|
+
return formatContextCompositionDashboard(this.getContextCompositionReport());
|
|
940
|
+
}
|
|
941
|
+
/** Curation status for diagnostics/dashboard: settings, live telemetry, last refusal reason. */
|
|
942
|
+
getContextCurationStatus() {
|
|
943
|
+
const settings = this.settingsManager.getContextCurationSettings();
|
|
944
|
+
return {
|
|
945
|
+
enabled: settings.enabled,
|
|
946
|
+
model: settings.model,
|
|
947
|
+
telemetry: this._brainCurator.telemetry(),
|
|
948
|
+
lastSkipReason: this._lastCurationSkipReason,
|
|
949
|
+
};
|
|
950
|
+
}
|
|
706
951
|
/** Read-only inspection of the latest prompt-enforcement report, for tests/debugging. */
|
|
707
952
|
getPromptEnforcementReport() {
|
|
708
953
|
return this._latestPromptEnforcementReport ?? { turnIndex: this._turnIndex, items: [] };
|
|
@@ -877,6 +1122,7 @@ export class AgentSession {
|
|
|
877
1122
|
// default provider actually emits are never recognized as semantic-memory pages and
|
|
878
1123
|
// accumulate raw for the life of the session — the exact growth Bug #7 GC exists to stop.
|
|
879
1124
|
const providerMarkers = this._memoryManager.getContextMarkers();
|
|
1125
|
+
const curationSettings = this.settingsManager.getContextCurationSettings();
|
|
880
1126
|
const result = applyContextGc(messages, {
|
|
881
1127
|
...settings,
|
|
882
1128
|
semanticMemory: {
|
|
@@ -886,6 +1132,22 @@ export class AgentSession {
|
|
|
886
1132
|
cwd: this._cwd,
|
|
887
1133
|
storageDir: this._contextGcStorageDir(),
|
|
888
1134
|
writePayloads,
|
|
1135
|
+
curation: curationSettings.enabled
|
|
1136
|
+
? {
|
|
1137
|
+
resolveDigest: (digestKey) => this._brainCurator.getDigest(digestKey),
|
|
1138
|
+
// Only the real per-turn pass enqueues work; the read-only report path
|
|
1139
|
+
// (writePayloads=false) stays side-effect free.
|
|
1140
|
+
onPacked: writePayloads
|
|
1141
|
+
? (record, originalText) => {
|
|
1142
|
+
this._brainCurator.enqueue({
|
|
1143
|
+
kind: "stub_digest",
|
|
1144
|
+
key: record.key ?? record.toolCallId,
|
|
1145
|
+
content: originalText,
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
: undefined,
|
|
1149
|
+
}
|
|
1150
|
+
: undefined,
|
|
889
1151
|
});
|
|
890
1152
|
this._latestContextGcReport = result.report;
|
|
891
1153
|
// Only release/reclaim on the real per-turn pass (writePayloads=true), never on
|
|
@@ -3597,6 +3859,23 @@ export class AgentSession {
|
|
|
3597
3859
|
}
|
|
3598
3860
|
}
|
|
3599
3861
|
}
|
|
3862
|
+
// Strict UAC: the active profile is the COMPLETE grant, so a tool the profile names
|
|
3863
|
+
// explicitly is itself a request for that tool — it must ACTIVATE from the registry even
|
|
3864
|
+
// if the session never requested it. Without this, activation is only ever the requested
|
|
3865
|
+
// defaults ∩ allow-list, and a profile granting non-default tools (a search-only profile's
|
|
3866
|
+
// grep/find) yields an empty or truncated tool set on load and /reload. A blanket "*"
|
|
3867
|
+
// stays grant-only: activation then still derives from the request/defaults above.
|
|
3868
|
+
const explicitAllowPatterns = toolProfileFilter?.allow.filter((pattern) => pattern !== "*") ?? [];
|
|
3869
|
+
if (explicitAllowPatterns.length > 0) {
|
|
3870
|
+
for (const toolName of this._toolRegistry.keys()) {
|
|
3871
|
+
if (!isAllowedTool(toolName))
|
|
3872
|
+
continue;
|
|
3873
|
+
if (matchesResourceProfilePattern(toolName, explicitAllowPatterns)) {
|
|
3874
|
+
nextActiveToolNames.push(toolName);
|
|
3875
|
+
autoActivated.push(toolName);
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3600
3879
|
// artifact_retrieve companion auto-activation is enforced inside
|
|
3601
3880
|
// setActiveToolsByName() itself (not duplicated here), so every activation path --
|
|
3602
3881
|
// including the public, extension-exposed setActiveTools() -- gets the same
|