@caupulican/pi-adaptative 0.80.66 → 0.80.68

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.
@@ -41,6 +41,7 @@ import { compactToolResultDetailsForRetention } from "./message-retention.js";
41
41
  import { resolveProfileModelSettings } from "./model-resolver.js";
42
42
  import { expandPromptTemplate } from "./prompt-templates.js";
43
43
  import { stripResourceProfileBlocks } from "./resource-profile-blocks.js";
44
+ import { classifyToolTrust, UNTRUSTED_BOUNDARY_SYSTEM_RULE, wrapUntrustedText } from "./security/untrusted-boundary.js";
44
45
  import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
45
46
  import { matchesResourceProfilePattern, } from "./settings-manager.js";
46
47
  import { createSyntheticSourceInfo } from "./source-info.js";
@@ -353,26 +354,37 @@ export class AgentSession {
353
354
  };
354
355
  this.agent.afterToolCall = async ({ toolCall, args, result, isError }) => {
355
356
  const runner = this._extensionRunner;
356
- if (!runner.hasHandlers("tool_result")) {
357
- return undefined;
357
+ let content = result.content;
358
+ let details = result.details;
359
+ let resolvedIsError = isError;
360
+ if (runner.hasHandlers("tool_result")) {
361
+ const hookResult = await runner.emitToolResult({
362
+ type: "tool_result",
363
+ toolName: toolCall.name,
364
+ toolCallId: toolCall.id,
365
+ input: args,
366
+ content,
367
+ details,
368
+ isError,
369
+ });
370
+ if (hookResult) {
371
+ content = hookResult.content ?? content;
372
+ details = hookResult.details;
373
+ resolvedIsError = hookResult.isError ?? isError;
374
+ }
358
375
  }
359
- const hookResult = await runner.emitToolResult({
360
- type: "tool_result",
361
- toolName: toolCall.name,
362
- toolCallId: toolCall.id,
363
- input: args,
364
- content: result.content,
365
- details: result.details,
366
- isError,
367
- });
368
- if (!hookResult) {
376
+ // Untrusted-content boundary: structurally fence output from attacker-controllable sources
377
+ // (web/search, subagents, recall, third-party tools) so injection payloads are framed as data.
378
+ // First-party tools (read/grep/find/ls/edit/write/bash) are trusted and pass through unchanged.
379
+ if (classifyToolTrust(toolCall.name) === "untrusted") {
380
+ const source = `tool:${toolCall.name}`;
381
+ const wrapped = content.map((block) => block.type === "text" ? { ...block, text: wrapUntrustedText(block.text, source) } : block);
382
+ content = wrapped;
383
+ }
384
+ if (content === result.content && details === result.details && resolvedIsError === isError) {
369
385
  return undefined;
370
386
  }
371
- return {
372
- content: hookResult.content,
373
- details: hookResult.details,
374
- isError: hookResult.isError ?? isError,
375
- };
387
+ return { content, details, isError: resolvedIsError };
376
388
  };
377
389
  }
378
390
  // =========================================================================
@@ -901,6 +913,8 @@ export class AgentSession {
901
913
  // R6: situational soul — the active profile's identity prefix, switched atomically with the
902
914
  // profile's capabilities/model. Most prominent, so it comes first.
903
915
  this._buildSituationSoulPrompt(),
916
+ // Always-on untrusted-content boundary contract (gives the <untrusted_content> fences meaning).
917
+ UNTRUSTED_BOUNDARY_SYSTEM_RULE,
904
918
  this._buildSelfModificationPrompt(),
905
919
  this._buildAutonomyPrompt(),
906
920
  // Memory subsystem: static, frozen-per-session block (e.g. file-store MEMORY.md/USER.md).