{"version":3,"file":"completion-guard.d.ts","sourceRoot":"","sources":["../../../../src/runs/shared/completion-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAEjE,OAAO,EAAE,6BAA6B,EAAE,CAAC;AAmBzC,UAAU,4BAA4B;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,UAAU,6BAA6B;IACtC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,OAAO,CAIpH;AA8BD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAehE;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,4BAA4B,GAAG,6BAA6B,CAUlH","sourcesContent":["import type { Message } from \"@lpb-work/pi-ai\";\nimport { isMutatingTool } from \"./long-running-guard.ts\";\nimport { expectsImplementationMutation } from \"./task-intent.ts\";\n\nexport { expectsImplementationMutation };\n\nconst READ_ONLY_BUILTIN_TOOLS = new Set([\n\t\"read\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n\t\"web_search\",\n\t\"fetch_content\",\n\t\"get_search_content\",\n\t\"intercom\",\n\t\"contact_supervisor\",\n]);\n\n// Cursor native edit/write often land as thinking traces (inactive_trace /\n// transcript_trace) rather than toolCall parts when native tool replay is off\n// or the tool is inactive in context. Match pi-cursor-sdk display labels.\nconst CURSOR_FILE_MUTATION_THINKING = /(?:^|\\n)\\s*Cursor (?:edit|write)\\s*:/i;\n\ninterface CompletionMutationGuardInput {\n\tagent: string;\n\ttask: string;\n\tmessages: Message[];\n\ttools?: string[];\n\tmcpDirectTools?: string[];\n}\n\ninterface CompletionMutationGuardResult {\n\texpectedMutation: boolean;\n\tattemptedMutation: boolean;\n\ttriggered: boolean;\n}\n\nexport function hasMutationToolCapability(tools: string[] | undefined, mcpDirectTools: string[] | undefined): boolean {\n\tif ((mcpDirectTools?.length ?? 0) > 0) return true;\n\tif (tools === undefined) return true;\n\treturn !tools.every((tool) => READ_ONLY_BUILTIN_TOOLS.has(tool));\n}\n\nfunction hasCheckpointMutationEvidence(message: Message): boolean {\n\tconst record = message as unknown as {\n\t\trole?: string;\n\t\ttype?: string;\n\t\tcustomType?: unknown;\n\t\tdata?: unknown;\n\t\tdetails?: unknown;\n\t};\n\tif ((record.role !== \"custom\" && record.type !== \"custom\") || record.customType !== \"pi-checkpoint\") return false;\n\tconst details =\n\t\ttypeof record.details === \"object\" && record.details !== null && !Array.isArray(record.details)\n\t\t\t? (record.details as Record<string, unknown>)\n\t\t\t: undefined;\n\tconst data =\n\t\ttypeof record.data === \"object\" && record.data !== null && !Array.isArray(record.data)\n\t\t\t? (record.data as Record<string, unknown>)\n\t\t\t: typeof details?.beforeCommit === \"string\" || typeof details?.afterCommit === \"string\"\n\t\t\t\t? details\n\t\t\t\t: details?.data && typeof details.data === \"object\" && !Array.isArray(details.data)\n\t\t\t\t\t? (details.data as Record<string, unknown>)\n\t\t\t\t\t: undefined;\n\treturn (\n\t\ttypeof data?.beforeCommit === \"string\" &&\n\t\ttypeof data.afterCommit === \"string\" &&\n\t\tdata.beforeCommit !== data.afterCommit\n\t);\n}\n\nexport function hasMutationToolCall(messages: Message[]): boolean {\n\tfor (const message of messages) {\n\t\tif (hasCheckpointMutationEvidence(message)) return true;\n\t\tif (message.role !== \"assistant\") continue;\n\t\tfor (const part of message.content) {\n\t\t\tif (part.type === \"thinking\" && CURSOR_FILE_MUTATION_THINKING.test(part.thinking)) return true;\n\t\t\tif (part.type !== \"toolCall\") continue;\n\t\t\tconst args =\n\t\t\t\ttypeof part.arguments === \"object\" && part.arguments !== null && !Array.isArray(part.arguments)\n\t\t\t\t\t? (part.arguments as Record<string, unknown>)\n\t\t\t\t\t: {};\n\t\t\tif (isMutatingTool(part.name, args)) return true;\n\t\t}\n\t}\n\treturn false;\n}\n\nexport function evaluateCompletionMutationGuard(input: CompletionMutationGuardInput): CompletionMutationGuardResult {\n\tconst expectedMutation = hasMutationToolCapability(input.tools, input.mcpDirectTools)\n\t\t? expectsImplementationMutation(input.agent, input.task)\n\t\t: false;\n\tconst attemptedMutation = hasMutationToolCall(input.messages);\n\treturn {\n\t\texpectedMutation,\n\t\tattemptedMutation,\n\t\ttriggered: expectedMutation && !attemptedMutation,\n\t};\n}\n"]}