Integrate MCP write-boundary repair for real Pi plan qualification

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/runner-copilot-acp:
  Preserve MCP tool display identity in native ACP events
This commit is contained in:
DottaandPaperclip committed 2026-09-28 16:37:51 -05:00
commit e35b11db21
3 files changed
+234 -5

No files matched your search

@@ -1222,6 +1222,40 @@ fn normalize_acpx_status(
)]
}
/// Matches the TypeScript ACP display-name parser. This is presentation metadata;
/// semantic dispatch and the preclassified operation never derive authority from it.
fn acpx_mcp_tool_identity(value: &str) -> Option<(&str, &str)> {
let has_line_terminator = |text: &str| text.contains(['\n', '\r', '\u{2028}', '\u{2029}']);
if value
.get(..5)
.is_some_and(|prefix| prefix.eq_ignore_ascii_case("mcp__"))
{
let rest = &value[5..];
if !has_line_terminator(rest) {
// The namespace is nonempty and the first eligible separator wins,
// including overlapping separators when the namespace starts with `_`.
for (index, _) in rest.char_indices().skip(1) {
if let Some(name) = rest[index..]
.strip_prefix("__")
.filter(|name| !name.is_empty())
{
return Some((&rest[..index], name));
}
}
}
}
if value
.get(..4)
.is_some_and(|prefix| prefix.eq_ignore_ascii_case("mcp."))
{
let (namespace, name) = value[4..].split_once('.')?;
if !namespace.is_empty() && !name.is_empty() && !has_line_terminator(name) {
return Some((namespace, name));
}
}
None
}
fn normalize_acpx_tool_call(
payload: &Value,
item_id: &str,
@@ -1230,8 +1264,9 @@ fn normalize_acpx_tool_call(
let native_status = string(payload.get("status"));
let status = provider_status(native_status, native_status == "completed");
let terminal = status != "running";
let raw_title = string(payload.get("title"));
let title = bounded_text(raw_title, 240);
let raw_title = string(payload.get("title")).trim();
let mcp = acpx_mcp_tool_identity(raw_title);
let name = bounded_text(mcp.map_or(raw_title, |(_, name)| name), 240);
let output = match payload.get("rawOutput").or_else(|| payload.get("output")) {
Some(Value::String(value)) => value.clone(),
Some(value) => serde_json::to_string(value).unwrap_or_default(),
@@ -1240,11 +1275,11 @@ fn normalize_acpx_tool_call(
let mut normalized = json!({
"schema": "paperclip.tool.execution.v1",
"executionId": item_id,
"transport": "builtin",
"transport": if mcp.is_some() { "mcp" } else { "builtin" },
"operation": operation,
"name": if title.is_empty() { Value::Null } else { Value::String(title) },
"name": if name.is_empty() { Value::Null } else { Value::String(name) },
"target": safe_acpx_location(payload.pointer("/locations/0"), operation == "edit"),
"namespace": Value::Null,
"namespace": mcp.map(|(namespace, _)| bounded_text(namespace, 240)),
"readOnly": matches!(operation, "read" | "search" | "list"),
"status": status,
"durationMs": Value::Null,
@@ -336,6 +336,131 @@ fn does_not_label_non_usd_acpx_cost_as_usd() {
assert_eq!(usage[0].payload["cumulative"]["providerCostUsd"], 0.0);
}
#[test]
fn matches_typescript_mcp_display_identity_across_tool_lifecycle() {
for (title, namespace, name) in [
(
"mcp__paperclip__write_document",
"paperclip",
"write_document",
),
(
"mcp.paperclip.write_document",
"paperclip",
"write_document",
),
(
" MCP__Paperclip__write_document ",
"Paperclip",
"write_document",
),
("McP.other.read_document", "other", "read_document"),
("mcp__paperclip__nested__tool", "paperclip", "nested__tool"),
("mcp.paperclip.nested.tool", "paperclip", "nested.tool"),
("mcp_____write_document", "_", "write_document"),
("mcp__é__write_document", "é", "write_document"),
] {
for (tag, status, event_type) in [
("tool_call", "pending", "tool.execution.started"),
(
"tool_call_update",
"in_progress",
"tool.execution.progressed",
),
("tool_call_update", "completed", "tool.execution.completed"),
] {
let events = normalize_acpx_runtime_event(
AcpxRuntimeEventKind::ToolCall,
&json!({
"type": "tool_call", "tag": tag, "status": status,
"toolCallId": "write-plan", "title": title,
"rawInput": {"private": "not-for-the-log"},
"operationId": "finish_task", "semantic_tool": {"phase": "result"}
}),
Some("execute"),
"event-7",
"turn-1",
3,
);
assert_eq!(events.len(), 1);
assert_eq!(events[0].event_type, event_type);
let payload = &events[0].payload;
assert_eq!(payload["executionId"], "write-plan");
assert_eq!(payload["transport"], "mcp");
assert_eq!(payload["namespace"], namespace);
assert_eq!(payload["name"], name);
// Parsing the display title never changes preclassified operation authority.
assert_eq!(payload["operation"], "execute");
assert_eq!(payload["readOnly"], false);
for dropped in ["rawInput", "operationId", "semantic_tool"] {
assert!(payload.get(dropped).is_none());
}
assert!(!payload.to_string().contains("not-for-the-log"));
}
}
}
#[test]
fn bounds_mcp_namespace_and_name_independently_after_parsing() {
let namespace = "é".repeat(300);
let events = normalize(
AcpxRuntimeEventKind::ToolCall,
json!({"type": "tool_call", "title": format!("mcp__{namespace}__write_document")}),
);
assert_eq!(events[0].payload["transport"], "mcp");
assert_eq!(events[0].payload["name"], "write_document");
assert_eq!(
events[0].payload["namespace"]
.as_str()
.unwrap()
.chars()
.count(),
240
);
let events = normalize(
AcpxRuntimeEventKind::ToolCall,
json!({"type": "tool_call", "title": format!("mcp.paperclip.{}", "雪".repeat(300))}),
);
assert_eq!(events[0].payload["namespace"], "paperclip");
assert_eq!(
events[0].payload["name"].as_str().unwrap().chars().count(),
240
);
for title in [
"mcp__Authorization: Bearer namespace-secret__write_document",
"mcp.paperclip.Authorization: Bearer tool-secret",
] {
let events = normalize(
AcpxRuntimeEventKind::ToolCall,
json!({"type": "tool_call", "title": title}),
);
assert!(!events[0].payload.to_string().contains("namespace-secret"));
assert!(!events[0].payload.to_string().contains("tool-secret"));
}
}
#[test]
fn leaves_builtin_and_malformed_mcp_titles_as_display_only_builtin_names() {
for title in [
"Read file",
"write_document",
"paperclip__write_document",
"mcp____write_document",
"mcp__paperclip__",
"mcp..write_document",
"mcp.paperclip.",
"mcp__paperclip__write\ndocument",
] {
let events = normalize(
AcpxRuntimeEventKind::ToolCall,
json!({"type": "tool_call", "title": title}),
);
assert_eq!(events[0].payload["transport"], "builtin");
assert!(events[0].payload["namespace"].is_null());
assert_eq!(events[0].payload["name"], title);
}
}
#[test]
fn maps_tool_lifecycle_and_preserves_safe_display_paths() {
let started = normalize(
+69
View File
@@ -784,6 +784,75 @@ describe("TaskChatThread runtime transcript selection", () => {
expect(rows.findIndex((row) => row.contains(preview))).toBeGreaterThan(0);
});
it.each([
{ name: "write_document", anchored: true },
{ name: "mcp__paperclip__write_document", anchored: false },
])("anchors a settled ACP Plan only at the normalized display name $name", ({ name, anchored }) => {
const runId = "acpx-plan";
planState.data = planDocument({ updatedAt: new Date("2026-08-25T18:00:02.000Z") });
const events = ["started", "completed"].map((phase, index) => ({
id: index + 1,
companyId: "company-1",
agentId: "agent-1",
stream: "system",
level: "info",
color: null,
message: null,
runId,
seq: index + 1,
eventType: `tool.execution.${phase}`,
createdAt: new Date(`2026-08-25T18:00:0${index + 1}.000Z`),
payload: {
prpEvent: {
schema: "paperclip.prp.event.v1",
schemaVersion: 1,
runId,
eventType: `tool.execution.${phase}`,
sourceEventId: `acpx-write-${index + 1}`,
sourceKind: "runner",
sourceInstanceId: "runner-1",
sourceSeq: index + 1,
normalizedSessionId: "session-1",
emittedAt: `2026-08-25T18:00:0${index + 1}.000Z`,
payload: {
schema: "paperclip.tool.execution.v1",
executionId: "write-plan",
transport: anchored ? "mcp" : "builtin",
namespace: anchored ? "paperclip" : null,
name,
operation: "execute",
readOnly: false,
status: phase === "completed" ? "completed" : "running",
output: null,
outputBytes: 0,
outputTruncated: false,
outputDigest: null,
},
},
},
} satisfies HeartbeatRunEvent));
// Exercise persisted ACP display events, without native raw arguments or
// a fabricated semantic-tool receipt, through the actual transcript parser.
nativeTranscriptState.transcriptByRun.set(runId, nativeRunEventsToTranscript(events));
render(<TaskChatThread comments={[]} onAdd={async () => {}} issueStatus="blocked" linkedRuns={[{
runId,
runtimeMode: "native",
status: "succeeded",
agentId: "agent-1",
agentName: "Runner",
adapterType: "paperclip_runner",
createdAt: "2026-08-25T18:00:00.000Z",
startedAt: "2026-08-25T18:00:00.000Z",
finishedAt: "2026-08-25T18:00:03.000Z",
}]} />);
const preview = container.querySelector('[data-testid="task-chat-plan-preview"]');
const fallback = container.querySelector('[data-testid="task-chat-plan-preview-fallback"]');
expect(Boolean(preview)).toBe(anchored);
expect(Boolean(fallback)).toBe(!anchored);
expect((preview ?? fallback)?.textContent).toContain("Preview the Plan");
expect((preview ?? fallback)?.closest('[data-testid="task-chat-turn"][data-settled="true"]')).not.toBeNull();
});
it("retains a native Plan as a visible fallback while its write boundary is unavailable", () => {
planState.data = planDocument({
updatedAt: new Date("2026-08-25T18:00:02.000Z"),