refactor: share sessions spawn attachment checks

This commit is contained in:
Peter Steinberger
2026-04-18 21:24:53 +01:00
parent 848f154f3e
commit ea1e933b29
3 changed files with 34 additions and 46 deletions

View File

@@ -2,15 +2,13 @@ import type { AgentMessage, StreamFn } from "@mariozechner/pi-agent-core";
import { streamSimple } from "@mariozechner/pi-ai";
import { normalizeLowercaseStringOrEmpty } from "../../../shared/string-coerce.js";
import { validateAnthropicTurns, validateGeminiTurns } from "../../pi-embedded-helpers.js";
import {
isRedactedSessionsSpawnAttachment,
sanitizeToolUseResultPairing,
} from "../../session-transcript-repair.js";
import { sanitizeToolUseResultPairing } from "../../session-transcript-repair.js";
import {
extractToolCallsFromAssistant,
sanitizeToolCallIdsForCloudCodeAssist,
type ToolCallIdMode,
} from "../../tool-call-id.js";
import { hasUnredactedSessionsSpawnAttachments } from "../../tool-call-shared.js";
import { normalizeToolName } from "../../tool-policy.js";
import { shouldAllowProviderOwnedThinkingReplay } from "../../transcript-policy.js";
import type { TranscriptPolicy } from "../../transcript-policy.js";
@@ -255,28 +253,6 @@ function isThinkingLikeReplayBlock(block: unknown): boolean {
return type === "thinking" || type === "redacted_thinking";
}
function hasUnredactedSessionsSpawnAttachments(block: ReplayToolCallBlock): boolean {
const rawName = typeof block.name === "string" ? block.name.trim() : "";
if (normalizeLowercaseStringOrEmpty(rawName) !== "sessions_spawn") {
return false;
}
for (const payload of [block.arguments, block.input]) {
if (!payload || typeof payload !== "object") {
continue;
}
const attachments = (payload as { attachments?: unknown }).attachments;
if (!Array.isArray(attachments)) {
continue;
}
for (const attachment of attachments) {
if (!isRedactedSessionsSpawnAttachment(attachment)) {
return true;
}
}
}
return false;
}
function isReplaySafeThinkingTurn(content: unknown[], allowedToolNames?: Set<string>): boolean {
const seenToolCallIds = new Set<string>();
for (const block of content) {

View File

@@ -1,9 +1,8 @@
import { createHash } from "node:crypto";
import type { AgentMessage } from "@mariozechner/pi-agent-core";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import {
hasUnredactedSessionsSpawnAttachments,
isAllowedToolCallName,
isRedactedSessionsSpawnAttachment,
normalizeAllowedToolNames,
} from "./tool-call-shared.js";
@@ -118,24 +117,7 @@ function toolCallNeedsReplayMutation(block: ReplaySafeToolCallBlock): boolean {
if (rawName && rawName !== trimmedName) {
return true;
}
if (normalizeLowercaseStringOrEmpty(trimmedName) !== "sessions_spawn") {
return false;
}
for (const payload of [block.arguments, block.input]) {
if (!payload || typeof payload !== "object") {
continue;
}
const attachments = (payload as { attachments?: unknown }).attachments;
if (!Array.isArray(attachments)) {
continue;
}
for (const attachment of attachments) {
if (!isRedactedSessionsSpawnAttachment(attachment)) {
return true;
}
}
}
return false;
return hasUnredactedSessionsSpawnAttachments(block);
}
function isReplaySafeThinkingAssistantMessage(

View File

@@ -65,3 +65,33 @@ export function isRedactedSessionsSpawnAttachment(item: unknown): boolean {
}
return true;
}
export type SessionsSpawnAttachmentToolCallBlock = {
name?: unknown;
input?: unknown;
arguments?: unknown;
};
export function hasUnredactedSessionsSpawnAttachments(
block: SessionsSpawnAttachmentToolCallBlock,
): boolean {
const rawName = typeof block.name === "string" ? block.name.trim() : "";
if (normalizeLowercaseStringOrEmpty(rawName) !== "sessions_spawn") {
return false;
}
for (const payload of [block.arguments, block.input]) {
if (!payload || typeof payload !== "object") {
continue;
}
const attachments = (payload as { attachments?: unknown }).attachments;
if (!Array.isArray(attachments)) {
continue;
}
for (const attachment of attachments) {
if (!isRedactedSessionsSpawnAttachment(attachment)) {
return true;
}
}
}
return false;
}