feat(runner): admit Claude effort before native prompts and resume

Version the shared Pi and pending Copilot source attestations without changing their executable closures or platform qualification.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-12 12:24:15 -05:00
1 parent a200495cc7
commit 2e8d5e906e
44 files changed
+681 -149

No files matched your search

+4 -4
View File
@@ -14,12 +14,12 @@
"requiresProviderPolicy": false
},
"pi": {
"agentProfileVersion": 22,
"agentProfileVersion": 23,
"agentServerPackage": "pi-acp",
"agentServerVersion": "0.0.33",
"agentRuntimePackage": "@earendil-works/pi-coding-agent",
"agentRuntimeVersion": "1.0.0",
"commandDigest": "sha256:e92078bee3c23bec4100aa589013a44613d054cd686826534025d8019e9f39a9",
"commandDigest": "sha256:eba77e17e5e874d0dc80585abbf0266f3005c6d6fb746dda29cafad95657f6c6",
"requiresProviderPolicy": true
},
"cursor": {
@@ -32,12 +32,12 @@
"requiresProviderPolicy": true
},
"copilot": {
"agentProfileVersion": 17,
"agentProfileVersion": 18,
"agentServerPackage": "@github/copilot",
"agentServerVersion": "1.0.88",
"agentRuntimePackage": null,
"agentRuntimeVersion": null,
"commandDigest": "sha256:481d0852ae8272a90f9912723d540518a874a0da65a9070e33b52ea1a14cbd7f",
"commandDigest": "sha256:bc0827bd362d01b5595738515f0ca1adcd6961ec622034cbda4633b0f5ec4701",
"requiresProviderPolicy": true,
"qualificationStatus": "pending"
},
@@ -154,7 +154,16 @@ policy in the closed provider configuration: `approvalPolicy` for Codex and
provider-session identity, so an incompatible idle or recovered session is
replaced on the next execution. An active turn is never mutated in place.
`paperclip.native-execution-input.v5` is the current input format. It retains
`paperclip.native-execution-input.v5` remains the ordinary input format.
Claude inputs with explicitly configured effort use v7 with the optional
`provider.claudeEffort` field. V7 retains the compact v3 model envelope; effort
is host configuration, never model-visible task text. V6 remains the separate
OpenAI Dot format. Readers retain persisted v1-v6 contracts. Claude effort is
acknowledged on each ACP connection before prompting, bound into the session
identity and recovery checkpoint, and cross-checked against the public provider
descriptor. Missing effort retains the provider default; values are never clamped.
The v5 input format It retains
the v4 permission pinning and adds optional completion-source references;
`paperclip.native-model-envelope.v3` is the corresponding explicit model
projection. Readers continue to accept persisted v1-v4 inputs, and v5 readers
@@ -80,7 +80,7 @@
{
"path": "schemas/provider-descriptor.schema.json",
"id": "https://paperclip.dev/schemas/prp/v1/provider-descriptor.schema.json",
"sha256": "bf691bda4a58490dba680b74559bfe1c9254b19e3265a603d9cbf3e169094519"
"sha256": "ef37ebba071ac239e0b4ff680a469eca6948b8eace451c5c88089c81b7acdae1"
},
{
"path": "schemas/provider-event.schema.json",
@@ -59,7 +59,7 @@
},
{
"if": { "properties": { "command": { "const": "session.open" } } },
"then": { "properties": { "params": { "$ref": "#/$defs/sessionOpenThinkingLevel" } } }
"then": { "properties": { "params": { "allOf": [{ "$ref": "#/$defs/sessionOpenThinkingLevel" }, { "$ref": "#/$defs/sessionOpenClaudeEffort" }] } } }
}
]
},
@@ -89,6 +89,19 @@
}
}
},
"claudeEffort": { "enum": ["low", "medium", "high", "xhigh", "max"] },
"sessionOpenClaudeEffort": {
"type": "object",
"properties": { "claudeEffort": { "$ref": "#/$defs/claudeEffort" } },
"if": { "properties": { "agent": { "const": "claude" } }, "required": ["agent"] },
"then": { "properties": { "expectedIdentity": {
"type": ["object", "null"], "properties": { "claudeEffort": { "$ref": "#/$defs/claudeEffort" } }
} } },
"else": { "properties": {
"claudeEffort": false,
"expectedIdentity": { "type": ["object", "null"], "properties": { "claudeEffort": false } }
} }
},
"response": {
"type": "object",
"required": ["protocolVersion", "id", "ok"],
@@ -153,6 +153,15 @@
"high",
"max"
]
},
"claudeEffort": {
"enum": [
"low",
"medium",
"high",
"xhigh",
"max"
]
}
},
"allOf": [
@@ -176,6 +185,26 @@
]
}
},
{
"if": {
"required": [
"claudeEffort"
]
},
"then": {
"properties": {
"provider": {
"const": "acpx"
},
"agent": {
"const": "claude"
}
},
"required": [
"agent"
]
}
},
{
"oneOf": [
{
@@ -143,6 +143,8 @@ struct AcpxProviderDescriptor {
mode: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pi_thinking_level: Option<PiThinkingLevel>,
#[serde(default, skip_serializing_if = "Option::is_none")]
claude_effort: Option<String>,
permission_mode_pinned: bool,
#[serde(default)]
provider_policy: Option<AcpxProviderRuntimePolicy>,
@@ -267,6 +269,10 @@ impl AcpxProviderDescriptor {
|| self.model.len() > 240
|| self.model.contains('\0')
|| ((self.agent == "pi") != self.pi_thinking_level.is_some())
|| self.claude_effort.as_ref().is_some_and(|effort| {
self.agent != "claude"
|| !matches!(effort.as_str(), "low" | "medium" | "high" | "xhigh" | "max")
})
|| self.mode.as_ref().is_some_and(|mode| {
mode.trim().is_empty()
|| mode.chars().count() > 240
@@ -368,6 +374,7 @@ impl AcpxProviderDescriptor {
permission_mode: self.permission_mode,
mode: self.mode.clone(),
pi_thinking_level: self.pi_thinking_level,
claude_effort: self.claude_effort.clone(),
permission_mode_pinned: self.permission_mode_pinned,
provider_policy: self.provider_policy.clone(),
system_instructions: self.instructions.clone(),
@@ -472,6 +479,9 @@ impl AcpxProviderDescriptor {
"acpxRecordId": identity.map(|value| value.acpx_record_id.as_str()),
"permissionMode": self.permission_mode,
});
if let Some(effort) = &self.claude_effort {
descriptor["claudeEffort"] = json!(effort);
}
if let Some(level) = self.pi_thinking_level {
descriptor["piThinkingLevel"] = json!(level);
}
@@ -611,6 +621,7 @@ impl AcpxDurableState {
if identity.profile_digest != self.descriptor.command_digest
|| identity.mode != self.descriptor.mode
|| identity.pi_thinking_level != self.descriptor.pi_thinking_level
|| identity.claude_effort != self.descriptor.claude_effort
{
return Err(DurableRunnerError::invalid(
"ACPX durable identity no longer matches its qualified profile or provider mode",
@@ -1295,6 +1306,9 @@ impl AcpxCommandExecutor {
"sessionId": identity.agent_session_id,
"processId": process_id,
});
if let Some(effort) = &identity.claude_effort {
result["claudeEffort"] = json!(effort);
}
if let Some(level) = identity.pi_thinking_level {
// This identity is admitted only after the sidecar's effective-mode ACK.
result["piThinkingLevel"] = json!(level);
@@ -2754,6 +2768,7 @@ mod tests {
permission_mode: Some(descriptor.permission_mode),
mode: descriptor.mode.clone(),
pi_thinking_level: descriptor.pi_thinking_level,
claude_effort: descriptor.claude_effort.clone(),
provider_lifetime_fence_candidates: [60_001, 60_002, 60_003],
};
let operations = Vec::new();
@@ -3030,6 +3045,7 @@ mod tests {
permission_mode: Some(AcpxPermissionMode::ApproveReads),
mode: None,
pi_thinking_level: None,
claude_effort: None,
provider_lifetime_fence_candidates: [60_001, 60_002, 60_003],
};
@@ -3184,7 +3200,7 @@ mod tests {
#[test]
fn pi_profile_matches_published_identity_and_rejects_prior_profiles() {
let published: Value = serde_json::from_str(include_str!(
"../../../../test-fixtures/pi-acp/profile-v22-identity.json"
"../../../../test-fixtures/pi-acp/profile-v23-identity.json"
))
.unwrap();
let mut value = descriptor("codex");
@@ -3208,6 +3224,7 @@ mod tests {
include_str!("../../../../test-fixtures/pi-acp/profile-v19-identity.json"),
include_str!("../../../../test-fixtures/pi-acp/profile-v20-identity.json"),
include_str!("../../../../test-fixtures/pi-acp/profile-v21-identity.json"),
include_str!("../../../../test-fixtures/pi-acp/profile-v22-identity.json"),
] {
let prior: Value = serde_json::from_str(prior).unwrap();
value["commandDigest"] = prior["commandDigest"].clone();
@@ -3507,6 +3524,7 @@ mod tests {
permission_mode: Some(original_descriptor.permission_mode),
mode: original_descriptor.mode.clone(),
pi_thinking_level: original_descriptor.pi_thinking_level,
claude_effort: original_descriptor.claude_effort.clone(),
provider_lifetime_fence_candidates: [60_001, 60_002, 60_003],
};
let operations = Vec::new();
@@ -3792,6 +3810,7 @@ mod tests {
permission_mode: Some(original_descriptor.permission_mode),
mode: original_descriptor.mode.clone(),
pi_thinking_level: original_descriptor.pi_thinking_level,
claude_effort: original_descriptor.claude_effort.clone(),
provider_lifetime_fence_candidates: [60_001, 60_002, 60_003],
};
let operations = Vec::new();
@@ -4086,6 +4105,7 @@ mod tests {
permission_mode: Some(descriptor.permission_mode),
mode: descriptor.mode.clone(),
pi_thinking_level: descriptor.pi_thinking_level,
claude_effort: descriptor.claude_effort.clone(),
provider_lifetime_fence_candidates,
};
let operations = Vec::new();
@@ -4259,6 +4279,7 @@ mod tests {
permission_mode: Some(provider_descriptor.permission_mode),
mode: provider_descriptor.mode.clone(),
pi_thinking_level: provider_descriptor.pi_thinking_level,
claude_effort: provider_descriptor.claude_effort.clone(),
provider_lifetime_fence_candidates,
});
state.provider_exit_unconfirmed = true;
@@ -53,6 +53,8 @@ struct PersistedAcpxProviderSessionIdentity {
mode: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pi_thinking_level: Option<PiThinkingLevel>,
#[serde(default, skip_serializing_if = "Option::is_none")]
claude_effort: Option<String>,
provider_lifetime_fence_candidates: [u16; 3],
}
@@ -77,6 +79,7 @@ impl PersistedAcpxProviderSessionIdentity {
permission_mode,
mode: identity.mode,
pi_thinking_level: identity.pi_thinking_level,
claude_effort: identity.claude_effort.clone(),
provider_lifetime_fence_candidates: identity.provider_lifetime_fence_candidates,
})
}
@@ -95,6 +98,7 @@ impl PersistedAcpxProviderSessionIdentity {
permission_mode: Some(self.permission_mode),
mode: self.mode.clone(),
pi_thinking_level: self.pi_thinking_level,
claude_effort: self.claude_effort.clone(),
provider_lifetime_fence_candidates: self.provider_lifetime_fence_candidates,
}
}
@@ -117,6 +121,7 @@ impl AcpxSuspensionCheckpoint {
|| identity.permission_mode != Some(config.permission_mode)
|| identity.mode != config.mode
|| identity.pi_thinking_level != config.pi_thinking_level
|| identity.claude_effort != config.claude_effort
|| config
.expected_identity
.as_ref()
@@ -72,6 +72,8 @@ pub struct AcpxProviderSessionIdentity {
pub mode: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pi_thinking_level: Option<PiThinkingLevel>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub claude_effort: Option<String>,
pub provider_lifetime_fence_candidates: [u16; 3],
}
@@ -94,6 +96,7 @@ pub struct AcpxProviderSessionConfig {
pub permission_mode: AcpxPermissionMode,
pub mode: Option<String>,
pub pi_thinking_level: Option<PiThinkingLevel>,
pub claude_effort: Option<String>,
pub permission_mode_pinned: bool,
pub provider_policy: Option<AcpxProviderRuntimePolicy>,
pub system_instructions: String,
@@ -109,6 +112,14 @@ impl AcpxProviderSessionConfig {
LocalRunnerError::invalid("ACPX agent must name a known immutable profile")
})?;
validate_text(&self.model, MAX_MODEL_CHARS, "ACPX model")?;
if self.claude_effort.as_ref().is_some_and(|effort| {
self.agent != "claude"
|| !matches!(effort.as_str(), "low" | "medium" | "high" | "xhigh" | "max")
}) {
return Err(LocalRunnerError::invalid(
"Claude effort must be a supported Claude-only value",
));
}
if (self.agent == "pi") != self.pi_thinking_level.is_some() {
return Err(LocalRunnerError::invalid(
"ACPX Pi thinking level must be explicit for Pi and absent for other agents",
@@ -188,6 +199,7 @@ impl AcpxProviderSessionConfig {
|| expected_identity.permission_mode != Some(self.permission_mode)
|| expected_identity.mode != self.mode
|| expected_identity.pi_thinking_level != self.pi_thinking_level
|| expected_identity.claude_effort != self.claude_effort
{
return Err(LocalRunnerError::invalid(
"ACPX expected identity conflicts with the requested session",
@@ -208,6 +220,13 @@ impl AcpxProviderSessionIdentity {
if let Some(mode) = self.mode.as_deref() {
validate_text(mode, MAX_ID_CHARS, "ACPX provider mode")?;
}
if self.claude_effort.as_ref().is_some_and(|effort| {
!matches!(effort.as_str(), "low" | "medium" | "high" | "xhigh" | "max")
}) {
return Err(LocalRunnerError::invalid(
"ACPX identity contains invalid Claude effort",
));
}
for (value, label) in [
(&self.normalized_session_id, "normalized session"),
(&self.acpx_record_id, "record"),
@@ -1313,6 +1332,9 @@ fn session_open_params(config: &AcpxProviderSessionConfig, sidecar_tools: &[Valu
"tools": &sidecar_tools,
"expectedIdentity": config.expected_identity,
});
if let Some(effort) = &config.claude_effort {
params["claudeEffort"] = json!(effort);
}
if let Some(level) = config.pi_thinking_level {
params["piThinkingLevel"] = json!(level);
}
@@ -1430,6 +1452,7 @@ fn verify_open_response(
|| identity.permission_mode != Some(config.permission_mode)
|| identity.mode != config.mode
|| identity.pi_thinking_level != config.pi_thinking_level
|| identity.claude_effort != config.claude_effort
|| config
.expected_identity
.as_ref()
@@ -1755,6 +1778,7 @@ mod mode_tests {
permission_mode: AcpxPermissionMode::ApproveReads,
mode: Some("plan".to_owned()),
pi_thinking_level: None,
claude_effort: None,
permission_mode_pinned: true,
provider_policy: Some(AcpxProviderRuntimePolicy { read_only: false }),
system_instructions: String::new(),
@@ -1782,6 +1806,7 @@ mod mode_tests {
permission_mode: Some(AcpxPermissionMode::ApproveReads),
mode: Some("plan".to_owned()),
pi_thinking_level: None,
claude_effort: None,
provider_lifetime_fence_candidates: [60_001, 60_002, 60_003],
}
}
@@ -1921,6 +1946,27 @@ mod mode_tests {
identity
}
#[test]
fn claude_effort_is_explicit_claude_only_and_forwarded_without_clamping() {
let mut cfg = config();
cfg.agent = "claude".to_owned();
for effort in ["low", "medium", "high", "xhigh", "max"] {
cfg.claude_effort = Some(effort.to_owned());
cfg.validate().unwrap();
assert_eq!(
session_open_params(&cfg, &[])["claudeEffort"],
json!(effort)
);
}
cfg.claude_effort = Some("ultra".to_owned());
assert!(cfg.validate().is_err());
cfg.claude_effort = Some("high".to_owned());
cfg.agent = "codex".to_owned();
assert!(cfg.validate().is_err());
cfg.claude_effort = None;
cfg.validate().unwrap();
}
#[test]
fn pi_thinking_level_is_closed_explicit_and_pi_only() {
for (name, level) in [
@@ -29,7 +29,7 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option<AcpxReleaseProfile> {
agent_runtime_package: Some("@earendil-works/pi-coding-agent"),
agent_runtime_version: Some("1.0.0"),
command_digest:
"sha256:e92078bee3c23bec4100aa589013a44613d054cd686826534025d8019e9f39a9",
"sha256:eba77e17e5e874d0dc80585abbf0266f3005c6d6fb746dda29cafad95657f6c6",
requires_provider_policy: true,
},
"cursor" => AcpxReleaseProfile {
@@ -47,7 +47,7 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option<AcpxReleaseProfile> {
agent_runtime_package: None,
agent_runtime_version: None,
command_digest:
"sha256:481d0852ae8272a90f9912723d540518a874a0da65a9070e33b52ea1a14cbd7f",
"sha256:bc0827bd362d01b5595738515f0ca1adcd6961ec622034cbda4633b0f5ec4701",
requires_provider_policy: true,
},
"claude" => AcpxReleaseProfile {
@@ -52,6 +52,7 @@ fn config(directory: &std::path::Path) -> AcpxProviderSessionConfig {
permission_mode: AcpxPermissionMode::ApproveReads,
mode: None,
pi_thinking_level: None,
claude_effort: None,
permission_mode_pinned: true,
provider_policy: None,
system_instructions: "Complete the supplied task.".to_owned(),
@@ -80,6 +81,7 @@ fn identity() -> AcpxProviderSessionIdentity {
permission_mode: Some(AcpxPermissionMode::ApproveReads),
mode: None,
pi_thinking_level: None,
claude_effort: None,
provider_lifetime_fence_candidates: [60_001, 60_002, 60_003],
}
}
@@ -51,6 +51,7 @@ fn config(mode: &str) -> AcpxProviderSessionConfig {
permission_mode: AcpxPermissionMode::ApproveReads,
mode: None,
pi_thinking_level: None,
claude_effort: None,
permission_mode_pinned: true,
provider_policy: None,
system_instructions: "Complete the supplied task.".to_owned(),
@@ -47,6 +47,7 @@ fn config(mode: &str) -> AcpxProviderSessionConfig {
permission_mode: AcpxPermissionMode::ApproveReads,
mode: None,
pi_thinking_level: None,
claude_effort: None,
permission_mode_pinned: true,
provider_policy: None,
system_instructions: "Complete the supplied task.".to_owned(),
@@ -70,6 +71,7 @@ fn expected_identity() -> AcpxProviderSessionIdentity {
permission_mode: Some(AcpxPermissionMode::ApproveReads),
mode: None,
pi_thinking_level: None,
claude_effort: None,
provider_lifetime_fence_candidates: [60_001, 60_002, 60_003],
}
}
@@ -27,6 +27,7 @@ fn config(mode: &str) -> AcpxProviderSessionConfig {
permission_mode: AcpxPermissionMode::ApproveReads,
mode: None,
pi_thinking_level: None,
claude_effort: None,
permission_mode_pinned: true,
provider_policy: None,
system_instructions: "Complete the supplied task.".to_owned(),
@@ -50,6 +50,7 @@ fn config(mode: &str) -> AcpxProviderSessionConfig {
working_directory: std::env::temp_dir(),
permission_mode: AcpxPermissionMode::ApproveReads,
mode: None,
claude_effort: None,
pi_thinking_level: if mode.starts_with("controls") {
Some(paperclip_runner_core::acpx_provider_session::PiThinkingLevel::Low)
} else {
@@ -17,7 +17,7 @@ use sha2::{Digest, Sha256};
const CODEX_ACPX_DIGEST: &str =
"sha256:c4538599d1ab767db5dff50934f13bb5ba313a59d9c4a83e993fac4617ea63d3";
const PI_ACPX_DIGEST: &str =
"sha256:e92078bee3c23bec4100aa589013a44613d054cd686826534025d8019e9f39a9";
"sha256:eba77e17e5e874d0dc80585abbf0266f3005c6d6fb746dda29cafad95657f6c6";
fn temporary_directory(label: &str) -> PathBuf {
let nonce = SystemTime::now()
@@ -39,7 +39,7 @@ const { commandDigest, ...attestation } = contract;
assert.equal(commandDigest, `sha256:${createHash("sha256").update(canonicalJson(attestation)).digest("hex")}`);
assert.equal(contract.acpxPatchSha256, createHash("sha256")
.update(await readFile(new URL("../../patches/acpx@0.13.1.patch", root))).digest("hex"));
for (const [agent, path] of [["pi", "test-fixtures/pi-acp/profile-v22-identity.json"], ["copilot", "test/fixtures/copilot-profile-v17-identity.json"]]) {
for (const [agent, path] of [["pi", "test-fixtures/pi-acp/profile-v23-identity.json"], ["copilot", "test/fixtures/copilot-profile-v18-identity.json"]]) {
const identity = await readJson(path);
assert.equal(manifest.profiles[agent].agentProfileVersion, identity.declaration.agentProfileVersion);
assert.equal(manifest.profiles[agent].commandDigest, identity.commandDigest);
@@ -15,7 +15,7 @@ import {
export interface CodexAcpxNativeSessionBackendOptions extends Omit<
CodexAcpxDriverOptions,
"model" | "permissionMode" | "mode" | "piThinkingLevel" | "systemInstructions" | "providerPolicy" | "runtimeContext"
"model" | "permissionMode" | "mode" | "piThinkingLevel" | "claudeEffort" | "systemInstructions" | "providerPolicy" | "runtimeContext"
> {}
export type AcpxNativeSessionBackendOptions =
@@ -76,6 +76,7 @@ export function createAcpxNativeSessionBackend(
permissionMode: input.provider.permissionMode ?? "approve-reads",
mode: input.provider.mode,
piThinkingLevel: input.provider.piThinkingLevel,
claudeEffort: "claudeEffort" in input.provider ? input.provider.claudeEffort : undefined,
systemInstructions,
runtimeContext: "runtimeContext" in input ? input.runtimeContext : null,
providerPolicy: { readOnly: "executionMode" in input && input.executionMode === "plan" },
@@ -1,4 +1,5 @@
#!/usr/bin/env node
import { resolveClaudeEffort } from "../drivers/acpx/claude-effort.js";
import { parseProviderMode } from "../contracts/provider-mode.js";
import { acpxProfileActivity, type AcpxActivityAdapter, type AcpxToolEvidence } from "../drivers/acpx/profile-activity.js";
@@ -297,6 +298,7 @@ async function dispatch(
permissionMode: params.permissionMode,
mode: params.mode,
piThinkingLevel: params.piThinkingLevel,
claudeEffort: params.claudeEffort,
providerPolicy: params.providerPolicy,
systemInstructions: params.systemInstructions,
runtimeContext: params.runtimeContext,
@@ -1209,12 +1211,13 @@ function safeOutput(value: unknown): Record<string, unknown> {
function parseOpenParams(
value: Record<string, unknown>,
): AcpxSidecarOpenParams {
const fields = new Set(["runtimeDirectory", "normalizedSessionId", "workingDirectory", "agent", "model", "permissionMode", "mode", "piThinkingLevel", "permissionModePinned", "providerPolicy", "systemInstructions", "runtimeContext", "tools", "providerSessionKey", "expectedIdentity"]);
const fields = new Set(["runtimeDirectory", "normalizedSessionId", "workingDirectory", "agent", "model", "permissionMode", "mode", "piThinkingLevel", "claudeEffort", "permissionModePinned", "providerPolicy", "systemInstructions", "runtimeContext", "tools", "providerSessionKey", "expectedIdentity"]);
if (Object.keys(value).some(key => !fields.has(key))) throw new Error("ACPX open parameters include an unsupported field");
const agent = requireQualifiedAgent(value.agent);
const model = requiredText(value.model, "model");
if (value.mode !== undefined && agent !== "cursor") throw new Error("mode is supported only for Cursor");
const piThinkingLevel = resolvePiThinkingLevel(agent, value.piThinkingLevel);
const claudeEffort = resolveClaudeEffort(agent, value.claudeEffort);
resolveQualifiedAcpxProfile(agent, model);
if (
value.providerSessionKey !== undefined &&
@@ -1236,6 +1239,7 @@ function parseOpenParams(
permissionMode: requiredPermissionMode(value.permissionMode),
...(value.mode === undefined ? {} : { mode: parseProviderMode(value.mode) }),
...(piThinkingLevel ? { piThinkingLevel } : {}),
...(claudeEffort ? { claudeEffort } : {}),
permissionModePinned: value.permissionModePinned === true,
...(value.providerPolicy == null ? {} : { providerPolicy: parseProviderPolicy(value.providerPolicy) }),
systemInstructions: boundedText(
@@ -1307,6 +1311,7 @@ function parseExpectedIdentity(value: unknown): AcpxExpectedSessionIdentity {
: { permissionMode: requiredPermissionMode(input.permissionMode) }),
...(input.mode === undefined ? {} : { mode: parseProviderMode(input.mode) }),
...(input.piThinkingLevel === undefined ? {} : { piThinkingLevel: resolvePiThinkingLevel("pi", input.piThinkingLevel) }),
...(input.claudeEffort === undefined ? {} : { claudeEffort: resolveClaudeEffort("claude", input.claudeEffort) }),
providerLifetimeFenceCandidates: requiredFenceCandidates(
input.providerLifetimeFenceCandidates,
),
@@ -457,6 +457,7 @@ export interface AcpxSessionIdentity {
/** Effective provider mode identifier, bound to the session identity. */
mode?: string;
piThinkingLevel?: "off" | "low" | "high" | "max";
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
providerLifetimeFenceCandidates: readonly [number, number, number];
}
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
import { QUALIFIED_ACPX_PROFILES } from "../drivers/acpx/qualified-profiles.js";
import { describe, expect, it } from "vitest";
import { buildNativeModelEnvelope, parseNativeExecutionInput, NATIVE_EXECUTION_INPUT_SCHEMA, NATIVE_EXECUTION_INPUT_SCHEMA_V6, type NativeExecutionInputV1 } from "./native-execution.js";
import { buildNativeModelEnvelope, parseNativeExecutionInput, NATIVE_EXECUTION_INPUT_SCHEMA, NATIVE_EXECUTION_INPUT_SCHEMA_V6, NATIVE_EXECUTION_INPUT_SCHEMA_V7, type NativeExecutionInputV1 } from "./native-execution.js";
import {
NATIVE_RUNTIME_ASSET_SCHEMA,
PAPERCLIP_EXECUTION_PROMPT,
@@ -680,3 +680,30 @@ describe("native task context ownership", () => {
}
});
});
describe("Claude effort input v7", () => {
const { permissionPolicy: _permission, qualificationModel: _model, reportedModelId: _reported, ...profile } = QUALIFIED_ACPX_PROFILES.claude;
const digest = "0".repeat(64);
const context = { prompt: { revision: PAPERCLIP_EXECUTION_PROMPT_REVISION, text: PAPERCLIP_EXECUTION_PROMPT, digest: nativeRuntimePromptDigest() },
instructions: { entryPath: "AGENTS.md", bundle: { schema: NATIVE_RUNTIME_ASSET_SCHEMA, digest, manifestDigest: digest, rootPath: "/runtime/instructions", fileCount: 1, totalBytes: 1 } },
skills: [], mcp: { assignmentSetId: "none", digest, bindingId: null } };
const current = { ...input, schema: NATIVE_EXECUTION_INPUT_SCHEMA_V7, executionMode: "default", planningContext: null,
runtimeContext: { ...context, aggregateDigest: canonicalNativeRuntimeContextDigest(context) },
session: { ...input.session, driverKind: "acpx_runtime" },
provider: { kind: "acpx", agent: "claude", model: "claude-opus-5-5", permissionMode: "approve-all", profile } };
it.each(["low", "medium", "high", "xhigh", "max"])("round trips explicit %s and retains the compact model envelope", claudeEffort => {
const parsed = parseNativeExecutionInput({ ...current, provider: { ...current.provider, claudeEffort } });
expect(parsed).toMatchObject({ schema: NATIVE_EXECUTION_INPUT_SCHEMA_V7, provider: { claudeEffort } });
expect(buildNativeModelEnvelope(parsed).schema).toBe("paperclip.native-model-envelope.v3");
expect(JSON.stringify(buildNativeModelEnvelope(parsed))).not.toContain("claudeEffort");
});
it("keeps historical readers closed and rejects aliases and foreign effort", () => {
for (const schema of ["paperclip.native-execution-input.v4", NATIVE_EXECUTION_INPUT_SCHEMA]) {
expect(() => parseNativeExecutionInput({ ...current, schema, provider: { ...current.provider, claudeEffort: "high" } })).toThrow("claudeEffort");
expect(parseNativeExecutionInput({ ...current, schema }).schema).toBe(schema);
}
expect(() => parseNativeExecutionInput({ ...current, provider: { ...current.provider, claudeEffort: "ultra" } })).toThrow("Claude effort");
expect(() => parseNativeExecutionInput({ ...current, provider: { ...current.provider, agent: "codex", profile: QUALIFIED_ACPX_PROFILES.codex, claudeEffort: "high" } })).toThrow("Claude effort");
});
});
@@ -1,3 +1,4 @@
import { resolveClaudeEffort } from "../drivers/acpx/claude-effort.js";
import type { DotBindingSnapshot } from "./external-provider.js";
import { QUALIFIED_ACPX_VERSION } from "../drivers/acpx/generated-profiles.js";
import { isSupportedAcpxProfileVersion, type AcpxProfileVersion } from "../drivers/acpx/profile-compatibility.js";
@@ -9,6 +10,7 @@ import { explicitTaskSkillNames, parseNativeRuntimeContext, type NativeRuntimeCo
export const NATIVE_EXECUTION_INPUT_SCHEMA_V1 = "paperclip.native-execution-input.v1" as const;
export const NATIVE_EXECUTION_INPUT_SCHEMA_V2 = "paperclip.native-execution-input.v2" as const;
export const NATIVE_EXECUTION_INPUT_SCHEMA_V3 = "paperclip.native-execution-input.v3" as const;
export const NATIVE_EXECUTION_INPUT_SCHEMA_V7 = "paperclip.native-execution-input.v7" as const;
export const NATIVE_EXECUTION_INPUT_SCHEMA_V6 = "paperclip.native-execution-input.v6" as const;
export const NATIVE_EXECUTION_INPUT_SCHEMA_V4 = "paperclip.native-execution-input.v4" as const;
export const NATIVE_EXECUTION_INPUT_SCHEMA = "paperclip.native-execution-input.v5" as const;
@@ -238,6 +240,13 @@ export interface NativeExecutionInputV5 extends Omit<NativeExecutionInputV4, "sc
completionSources?: NativeCompletionSources;
}
export type NativeProviderConfigV7 = Exclude<NativeProviderConfigV5, { kind: "acpx" }>
| (Extract<NativeProviderConfigV5, { kind: "acpx" }> & { claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max" });
export interface NativeExecutionInputV7 extends Omit<NativeExecutionInputV5, "schema" | "provider"> {
schema: typeof NATIVE_EXECUTION_INPUT_SCHEMA_V7;
provider: NativeProviderConfigV7;
}
export interface NativeExecutionInputV6 extends Omit<NativeExecutionInputV5, "schema" | "provider" | "workspace" | "session"> {
schema: typeof NATIVE_EXECUTION_INPUT_SCHEMA_V6;
provider: { kind: "openai_dot"; model: null; binding: DotBindingSnapshot };
@@ -245,7 +254,7 @@ export interface NativeExecutionInputV6 extends Omit<NativeExecutionInputV5, "sc
session: Omit<NativeExecutionInputV5["session"], "driverKind"> & { driverKind: "openai_dot_mcp" };
}
export type NativeExecutionInput = NativeExecutionInputV1 | NativeExecutionInputV2 | NativeExecutionInputV3 | NativeExecutionInputV4 | NativeExecutionInputV5 | NativeExecutionInputV6;
export type NativeExecutionInput = NativeExecutionInputV1 | NativeExecutionInputV2 | NativeExecutionInputV3 | NativeExecutionInputV4 | NativeExecutionInputV5 | NativeExecutionInputV6 | NativeExecutionInputV7;
/** The only task data that may enter provider-visible model input. */
export interface NativeModelEnvelopeV1 {
@@ -372,7 +381,8 @@ function parseDotNativeExecutionInput(input: Record<string, unknown>): NativeExe
export function parseNativeExecutionInput(value: unknown): NativeExecutionInput {
const input = record(value, "input");
if (input.schema === NATIVE_EXECUTION_INPUT_SCHEMA_V6) return parseDotNativeExecutionInput(input);
const isV5 = input.schema === NATIVE_EXECUTION_INPUT_SCHEMA;
const isV7 = input.schema === NATIVE_EXECUTION_INPUT_SCHEMA_V7;
const isV5 = isV7 || input.schema === NATIVE_EXECUTION_INPUT_SCHEMA;
const isV4 = isV5 || input.schema === NATIVE_EXECUTION_INPUT_SCHEMA_V4;
const isV3 = isV4 || input.schema === NATIVE_EXECUTION_INPUT_SCHEMA_V3;
const isV2 = isV3 || input.schema === NATIVE_EXECUTION_INPUT_SCHEMA_V2;
@@ -504,7 +514,7 @@ export function parseNativeExecutionInput(value: unknown): NativeExecutionInput
: provider.kind === "aws_agentcore"
? ["kind", "model", "agentCoreProfile", "maxEstimatedSessionCostUsd", "invocationLimits"]
: provider.kind === "acpx"
? ["kind", "agent", "model", isV4 ? "permissionMode" : "permissionPolicy", "profile", "piThinkingLevel", ...(isV4 ? ["mode"] : [])]
? ["kind", "agent", "model", isV4 ? "permissionMode" : "permissionPolicy", "profile", "piThinkingLevel", ...(isV4 ? ["mode"] : []), ...(isV7 ? ["claudeEffort"] : [])]
: provider.kind === "codex" && isV4
? ["kind", "model", "approvalPolicy", ...(isV5 ? ["reasoningEffort"] : [])]
: provider.kind === "opencode" && isV4
@@ -527,7 +537,7 @@ export function parseNativeExecutionInput(value: unknown): NativeExecutionInput
if (provider.kind === "opencode" && (providerModel === null || !providerModel.includes("/"))) {
throw new NativeExecutionInputError("input.provider.model is required for opencode in provider/model form");
}
let parsedProvider: NativeProviderConfig | NativeProviderConfigV5;
let parsedProvider: NativeProviderConfigV7 | NativeProviderConfig | NativeProviderConfigV5;
if (provider.kind === "claude_managed") {
if (providerModel === null) {
throw new NativeExecutionInputError("input.provider.model is required for claude_managed");
@@ -643,6 +653,7 @@ export function parseNativeExecutionInput(value: unknown): NativeExecutionInput
} else if (provider.permissionPolicy !== "interactive") {
throw new NativeExecutionInputError("input.provider.permissionPolicy must be interactive");
}
const claudeEffort = resolveClaudeEffort(provider.agent, provider.claudeEffort);
const profile = record(provider.profile, "input.provider.profile");
exactKeys(profile, [
"driverKind",
@@ -681,6 +692,7 @@ export function parseNativeExecutionInput(value: unknown): NativeExecutionInput
? { permissionMode: provider.permissionMode as NativeAcpxPermissionMode }
: { permissionPolicy: "interactive" as const }),
...(provider.mode === undefined ? {} : { mode: provider.mode as string }),
...(claudeEffort === undefined ? {} : { claudeEffort }),
...(provider.piThinkingLevel === undefined ? {} : { piThinkingLevel: provider.piThinkingLevel as "off" | "low" | "high" | "max" }),
profile: {
driverKind: "acpx_runtime",
@@ -835,6 +847,12 @@ export function parseNativeExecutionInput(value: unknown): NativeExecutionInput
provider: parsedProvider as NativeProviderConfigV4,
};
if (!isV5) return { ...withPermissions, schema: NATIVE_EXECUTION_INPUT_SCHEMA_V4 };
if (isV7) return {
...withPermissions,
schema: NATIVE_EXECUTION_INPUT_SCHEMA_V7,
provider: parsedProvider as NativeProviderConfigV7,
...(input.completionSources !== undefined ? { completionSources: parseCompletionSources(input.completionSources) } : {}),
};
return {
...withPermissions,
schema: NATIVE_EXECUTION_INPUT_SCHEMA,
@@ -901,7 +919,7 @@ export function buildNativeModelEnvelope(input: NativeExecutionInput, options?:
interactionResponses: structuredClone(input.interactionResponses),
};
}
if (input.schema === NATIVE_EXECUTION_INPUT_SCHEMA || input.schema === NATIVE_EXECUTION_INPUT_SCHEMA_V6) {
if (input.schema === NATIVE_EXECUTION_INPUT_SCHEMA || input.schema === NATIVE_EXECUTION_INPUT_SCHEMA_V6 || input.schema === NATIVE_EXECUTION_INPUT_SCHEMA_V7) {
const sourceBinding = input.completionSources;
const references = sourceBinding?.contractRevision === input.completionContract.contract.revision
&& sourceBinding.promptSha256 === createHash("sha256").update(input.task.prompt).digest("hex")
@@ -0,0 +1,67 @@
import { describe, expect, it } from "vitest";
import { admittedClaudeEffort, createClaudeEffortAdmission, resolveClaudeEffort } from "./claude-effort.js";
const response = (value: string, sessionId?: string) => ({ ...(sessionId ? { sessionId } : {}), configOptions: [{ id: "effort", currentValue: value }] });
function loaded(method = "session/new") {
const admission = createClaudeEffortAdmission("high");
const guard = admission.createGuard();
guard("outbound", { id: 1, method, params: method === "session/new" ? {} : { sessionId: "s" } });
guard("inbound", { id: 1, result: response("medium", method === "session/new" ? "s" : undefined) });
return { admission, guard };
}
function select(guard: ReturnType<ReturnType<typeof createClaudeEffortAdmission>["createGuard"]>, acknowledged = "high") {
guard("outbound", { id: 2, method: "session/set_config_option", params: { sessionId: "s", configId: "effort", value: "high" } });
guard("inbound", { id: 2, result: response(acknowledged) });
}
describe("Claude effort admission", () => {
it.each(["low", "medium", "high", "xhigh", "max"] as const)("accepts exact %s without aliases", value => {
expect(resolveClaudeEffort("claude", value)).toBe(value);
expect(admittedClaudeEffort("claude", value, { claudeEffort: value }, { claudeEffort: value })).toBe(value);
});
it("rejects unsupported, foreign and conflicting effort", () => {
for (const value of ["ultra", "off", null, 2]) expect(() => resolveClaudeEffort("claude", value)).toThrow();
expect(() => resolveClaudeEffort("pi", "high")).toThrow("only supported");
expect(() => admittedClaudeEffort("claude", "high", { claudeEffort: "high" }, { claudeEffort: "medium" })).toThrow("differs");
});
it.each(["session/new", "session/load", "session/resume"])("requires acknowledged effort before prompting after %s", method => {
const { admission, guard } = loaded(method);
expect(admission.isReady()).toBe(false);
select(guard);
expect(admission.isReady()).toBe(true);
expect(() => guard("outbound", { id: 3, method: "session/prompt", params: { sessionId: "s" } })).not.toThrow();
});
it("rejects a resumed session with missing effort configuration", () => {
const admission = createClaudeEffortAdmission("high");
const guard = admission.createGuard();
guard("outbound", { id: 1, method: "session/resume", params: { sessionId: "s" } });
expect(() => guard("inbound", { id: 1, result: {} })).toThrow("missing or ambiguous");
});
it("blocks a prompt based only on a restored default", () => {
const { guard } = loaded("session/load");
expect(() => guard("outbound", { id: 3, method: "session/prompt", params: { sessionId: "s" } })).toThrow("does not acknowledge");
});
it("requires the bound effort acknowledgement on each cold connection", () => {
const { admission, guard } = loaded();
select(guard);
const resumed = admission.createGuard();
resumed("outbound", { id: 4, method: "session/load", params: { sessionId: "s" } });
expect(() => resumed("outbound", { id: 5, method: "session/prompt", params: { sessionId: "s" } })).toThrow("does not acknowledge");
const fresh = loaded(); select(fresh.guard);
const acknowledged = fresh.admission.createGuard();
acknowledged("outbound", { id: 4, method: "session/load", params: { sessionId: "s" } });
acknowledged("inbound", { id: 4, result: response("high") });
expect(() => acknowledged("outbound", { id: 5, method: "session/prompt", params: { sessionId: "s" } })).not.toThrow();
});
it("rejects a provider that ignores the requested effort", () => {
const { guard } = loaded();
expect(() => select(guard, "medium")).toThrow("did not apply");
});
it("rejects drift and wrong-session prompts after admission", () => {
const { guard } = loaded(); select(guard);
expect(() => guard("inbound", { method: "session/update", params: { sessionId: "s", update: { sessionUpdate: "config_option_update", ...response("low") } } })).toThrow("drifted");
const fresh = loaded(); select(fresh.guard);
expect(() => fresh.guard("outbound", { id: 3, method: "session/prompt", params: { sessionId: "foreign" } })).toThrow("different session");
});
});
@@ -0,0 +1,21 @@
import { createAcpxConfigAdmission } from "./config-admission.js";
export type ClaudeEffort = "low" | "medium" | "high" | "xhigh" | "max";
export function resolveClaudeEffort(agent: string, value: unknown): ClaudeEffort | undefined {
if (value === undefined || value === "") return undefined;
if (agent !== "claude") throw new Error("Claude effort is only supported by Claude");
if (value !== "low" && value !== "medium" && value !== "high" && value !== "xhigh" && value !== "max") throw new Error("Claude effort must be low, medium, high, xhigh, or max");
return value;
}
export function createClaudeEffortAdmission(expected: ClaudeEffort) {
// Every connection, including session/load, must select and acknowledge the
// configured effort. A restored preference alone never admits a prompt.
return createAcpxConfigAdmission(expected, { label: "Claude effort", configId: "effort",
supportedValues: ["default", "low", "medium", "high", "xhigh", "max"] });
}
export function admittedClaudeEffort(agent: string, requested: unknown, descriptor: Record<string, unknown>, identity: Record<string, unknown>): ClaudeEffort | undefined {
const expected = resolveClaudeEffort(agent, requested);
const effective = resolveClaudeEffort(agent, identity.claudeEffort);
if (effective !== expected || descriptor.claudeEffort !== expected) throw new Error("Claude effort differs from the admitted configuration");
return effective;
}
@@ -148,6 +148,7 @@ export interface CodexAcpxDriverOptions {
permissionMode?: NativeAcpxPermissionMode;
mode?: string;
piThinkingLevel?: "off" | "low" | "high" | "max";
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
providerPolicy?: { readOnly: boolean };
runtimeContext?: OpenAcpxRuntimeHostOptions["runtimeContext"];
systemInstructions?: string;
@@ -463,6 +464,7 @@ export class CodexAcpxDriver implements HarnessDriver {
permissionMode: this.#options.permissionMode ?? "approve-all",
mode: this.#options.mode,
piThinkingLevel: this.#options.piThinkingLevel,
claudeEffort: this.#options.claudeEffort,
providerPolicy: this.#options.providerPolicy,
runtimeContext: this.#options.runtimeContext,
systemInstructions: this.#options.systemInstructions,
@@ -1290,6 +1292,7 @@ class CodexAcpxSession implements HarnessSession {
permissionMode: identity.permissionMode,
...(identity.mode === undefined ? {} : { mode: identity.mode }),
...(identity.piThinkingLevel === undefined ? {} : { piThinkingLevel: identity.piThinkingLevel }),
...(identity.claudeEffort === undefined ? {} : { claudeEffort: identity.claudeEffort }),
providerLifetimeFenceCandidates:
identity.providerLifetimeFenceCandidates,
},
@@ -2153,6 +2156,7 @@ function validateRecoverySnapshot(snapshot: PersistedHarnessSession): void {
identity.permissionMode,
)) ||
(identity.mode !== undefined && !isProviderMode(identity.mode)) ||
(identity.claudeEffort !== undefined && !["low", "medium", "high", "xhigh", "max"].includes(identity.claudeEffort)) ||
(identity.piThinkingLevel !== undefined && !["off", "low", "high", "max"].includes(identity.piThinkingLevel)) ||
!validProviderLifetimeFenceCandidates(
identity.providerLifetimeFenceCandidates,
@@ -29,6 +29,31 @@ const HANDLE: AcpRuntimeHandle = {
};
describe("Codex ACPX runtime adapter", () => {
it.each([false, true])("acknowledges configured Claude effort before admission (restoring=%s)", async restoring => {
const runtime = fakeRuntime();
const options = openOptions(fakeCommand());
options.profile = { ...options.profile, agent: "claude" };
options.claudeEffort = "high";
options.restoringSession = restoring;
options.refreshConsumedCommand = vi.fn(async () => undefined);
let created!: AcpRuntimeOptions;
vi.mocked(runtime.setConfigOption).mockImplementation(async input => {
const guard = created.protocolGuardFactory!();
guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } });
guard("inbound", { id: 0, result: { configOptions: [{ id: "effort", currentValue: "default" }] } });
guard("outbound", { id: 1, method: "session/set_config_option", params: { sessionId: "backend-1", configId: input.key, value: input.value } });
guard("inbound", { id: 1, result: { configOptions: [{ id: "effort", currentValue: input.value }] } });
});
const port = await openCodexAcpxRuntime(options, {
createRegistry: () => registry(), createStore: () => store(), createRuntime: value => { created = value; return runtime; },
});
expect(runtime.setConfigOption).toHaveBeenCalledExactlyOnceWith({ handle: HANDLE, key: "effort", value: "high" });
expect(await port.identity()).toMatchObject({ claudeEffort: "high" });
expect(options.refreshConsumedCommand).toHaveBeenCalledOnce();
expect(runtime.startTurn).not.toHaveBeenCalled();
await port.close({ reason: "test complete" });
});
it("installs per-connection policy authority only for Copilot and rejects native commands before a turn starts", async () => {
for (const agent of ["copilot", "codex", "claude", "grok"] as const) {
const runtime = fakeRuntime();
@@ -1,3 +1,4 @@
import { resolveClaudeEffort, createClaudeEffortAdmission } from "./claude-effort.js";
import { createPiThinkingAdmission, resolvePiThinkingLevel } from "./pi-thinking.js";
import type { ChildProcess } from "node:child_process";
@@ -305,6 +306,8 @@ export async function openQualifiedAcpxRuntime(
const modeBinding = createAcpxModeBinding(options.profile.agent, options.mode);
const selectedPiThinkingLevel = resolvePiThinkingLevel(options.profile.agent, options.piThinkingLevel);
const piThinking = selectedPiThinkingLevel ? createPiThinkingAdmission(selectedPiThinkingLevel, { restoring: options.restoringSession }) : undefined;
const selectedClaudeEffort = resolveClaudeEffort(options.profile.agent, options.claudeEffort);
const claudeEffort = selectedClaudeEffort ? createClaudeEffortAdmission(selectedClaudeEffort) : undefined;
const cursorInstructions = options.profile.agent === "cursor"
? createCursorInstructionAdmission(options.systemInstructions)
: null;
@@ -316,6 +319,7 @@ export async function openQualifiedAcpxRuntime(
return (direction: "inbound" | "outbound", message: unknown) => { instructions?.(direction, message); mode?.(direction, message); };
} } : {}),
...(piThinking ? { protocolGuardFactory: () => piThinking.createGuard() } : {}),
...(claudeEffort ? { protocolGuardFactory: () => claudeEffort.createGuard() } : {}),
sessionStore,
agentRegistry: createRegistry({
// Preserve Claude's ACP capability identity. This is metadata only: the
@@ -527,6 +531,18 @@ export async function openQualifiedAcpxRuntime(
await commandLaunches.refreshConsumedCommand?.();
}
return ensuredHandle;
}) : claudeEffort ? ensuredSession.then(async (ensuredHandle) => {
handle = ensuredHandle;
options.signal?.throwIfAborted();
if (!claudeEffort.isReady()) {
if (!runtime.setConfigOption) throw new Error("Claude effort admission requires native configuration");
await runtime.setConfigOption({ handle: ensuredHandle, key: "effort", value: selectedClaudeEffort! });
claudeEffort.assertReady();
await children.verifyLifetimeOwnership();
options.signal?.throwIfAborted();
await commandLaunches.refreshConsumedCommand?.();
}
return ensuredHandle;
}) : ensuredSession)
.catch((error: unknown) => {
throw classifySessionEnsureFailure(error);
@@ -543,6 +559,7 @@ export async function openQualifiedAcpxRuntime(
cursorInstructions?.assertReady();
modeBinding?.assertReady();
piThinking?.assertReady();
claudeEffort?.assertReady();
// A provider can answer only after the verified sentinel is armed, but do
// not admit the session until the owner has observed that exact handoff.
await children.verifyLifetimeOwnership();
@@ -597,7 +614,7 @@ export async function openQualifiedAcpxRuntime(
return runtimePort(
runtime,
handle,
{ ...requireIdentity(handle), ...(modeBinding ? { mode: modeBinding.selectedMode } : {}), ...(selectedPiThinkingLevel ? { piThinkingLevel: selectedPiThinkingLevel } : {}) },
{ ...requireIdentity(handle), ...(modeBinding ? { mode: modeBinding.selectedMode } : {}), ...(selectedPiThinkingLevel ? { piThinkingLevel: selectedPiThinkingLevel } : {}), ...(selectedClaudeEffort ? { claudeEffort: selectedClaudeEffort } : {}) },
baseStore,
children,
runtimeCloseTimeoutMs,
@@ -0,0 +1,104 @@
function object(value: unknown): Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
}
/** Native mode is independent of permissions. Every connection must prove its
* current mode before a prompt; a persisted ACPX preference is not proof. */
export function createAcpxConfigAdmission(expected: string, options: { label: string; configId: string; supportedValues: readonly string[]; restoring?: boolean; sessionMode?: boolean }) {
type State = { sessionId: string | null; mode: string | null; error: Error | null };
let current: State | null = null;
let selectedOnce = options.restoring === true;
const failure = (detail: string) => new Error(`${options.label} admission failed: ${detail}`);
const assertReady = () => {
if (current?.error) throw current.error;
if (!selectedOnce || !current?.sessionId || current.mode !== expected) throw failure("native mode does not acknowledge the selected mode");
};
const readMode = (result: Record<string, unknown>, session: boolean): string => {
const configs = Array.isArray(result.configOptions) ? result.configOptions.filter(value => object(value).id === options.configId) : [];
if (configs.length !== 1) throw failure("native mode configuration is missing or ambiguous");
const mode = object(configs[0]).currentValue;
// Before exact model selection, Pi can legitimately report a different
// native model's level. Observation alone never admits the first prompt.
if (typeof mode !== "string" || !options.supportedValues.includes(mode)) throw failure("native mode is unsupported");
if (session && options.sessionMode && object(result.modes).currentModeId !== mode) throw failure("native mode acknowledgements conflict");
return mode;
};
return {
assertReady,
isReady() { assertNoError(); return selectedOnce && current?.sessionId != null && current.mode === expected; },
createGuard() {
const state: State = { sessionId: null, mode: null, error: null };
current = state;
const pending = new Map<string | number, { method: string; sessionId: unknown; selectsMode: boolean }>();
return (direction: "inbound" | "outbound", value: unknown): void => {
if (current !== state) throw failure("connection authority was replaced");
assertNoError();
const message = object(value);
const params = object(message.params);
try {
if (direction === "outbound") {
const method = message.method;
if (method === "session/prompt") {
assertReady();
if (params.sessionId !== state.sessionId) throw failure("prompt belongs to a different session");
} else if (method === "session/new" || method === "session/load" || method === "session/resume" || method === "session/set_config_option") {
if (typeof message.id !== "string" && typeof message.id !== "number") throw failure("uncorrelated mode request");
if (pending.has(message.id)) throw failure("duplicate mode request identity");
if (pending.size) throw failure("overlapping native admission controls");
if (method === "session/new" || method === "session/load" || method === "session/resume") {
if (pending.size) throw failure("overlapping session admission");
state.sessionId = null; state.mode = null;
} else {
if (!state.sessionId || params.sessionId !== state.sessionId) throw failure("configuration belongs to a different session");
if (params.configId === options.configId && params.value !== expected) throw failure("configuration changes the selected mode");
state.mode = null; // No prompt may race unacknowledged native configuration.
}
pending.set(message.id, { method, sessionId: params.sessionId, selectsMode: params.configId === options.configId });
} else if (options.sessionMode && method === "session/set_mode") {
if (!state.sessionId || params.sessionId !== state.sessionId || params.modeId !== expected) throw failure("mode control changes the selected mode");
// This method only returns {}; require a later config/session
// acknowledgement rather than treating that empty result as proof.
state.mode = null;
}
return;
}
if (message.method === "session/update" && object(params.update).sessionUpdate === "config_option_update") {
const update = object(params.update);
// Config updates may contain only model options. A mode entry is
// authoritative observation, but never an admission acknowledgement.
if (Array.isArray(update.configOptions) && update.configOptions.some(value => object(value).id === options.configId)) {
if (state.sessionId && params.sessionId !== state.sessionId) throw failure("mode update belongs to a different session");
const observed = readMode(update, false);
if (selectedOnce && observed !== expected) throw failure("native mode drifted from the selected mode");
}
return;
}
if (options.sessionMode && message.method === "session/update" && object(params.update).sessionUpdate === "current_mode_update") {
if (state.sessionId && params.sessionId !== state.sessionId) throw failure("mode update belongs to a different session");
if (selectedOnce && object(params.update).currentModeId !== expected) throw failure("native mode drifted from the selected mode");
return; // Notifications alone never admit a prompt.
}
if (message.method !== undefined || (typeof message.id !== "string" && typeof message.id !== "number")) return;
const request = pending.get(message.id);
if (!request) return;
pending.delete(message.id);
if (message.error !== undefined) { state.mode = null; return; }
const result = object(message.result);
const session = request.method !== "session/set_config_option";
const sessionId = request.method === "session/new" ? result.sessionId : request.sessionId;
if (typeof sessionId !== "string" || !sessionId.trim()) throw failure("mode acknowledgement has no session identity");
const mode = readMode(result, session);
if (session && selectedOnce && mode !== expected) throw failure("restored native mode drifted from the selected mode");
if (!session && (request.selectsMode || selectedOnce) && mode !== expected) throw failure("configuration did not apply the selected mode");
if (!session && request.selectsMode) selectedOnce = true;
state.sessionId = sessionId; state.mode = mode;
} catch (error) {
state.error = error instanceof Error ? error : failure("invalid mode acknowledgement");
throw state.error;
}
};
},
};
function assertNoError() { if (current?.error) throw current.error; }
}
@@ -87,8 +87,16 @@ describe("Copilot build-owned installation", () => {
expect(QUALIFIED_ACPX_PROFILES.copilot.qualificationStatus).toBe("pending");
});
it("admits the current v17 declaration and binds its source policy, receipts and patch hashes", () => {
const identity = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v17-identity.json", import.meta.url), "utf8"));
it("preserves v17 executable and policy identity while binding the shared Claude effort sources", () => {
const prior = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v17-identity.json", import.meta.url), "utf8"));
const current = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v18-identity.json", import.meta.url), "utf8"));
expect(Object.keys(current.declaration).filter(key => current.declaration[key] !== prior.declaration[key]).sort())
.toEqual(["agentProfileVersion", "semanticDirectDriverSourceSha256", "semanticSchemaBundleSourceSha256", "semanticSidecarProtocolSourceSha256", "semanticSidecarSourceSha256"]);
expect(QUALIFIED_ACPX_PROFILES.copilot.qualificationStatus).toBe("pending");
});
it("admits the current v18 declaration and binds its source policy, receipts and patch hashes", () => {
const identity = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v18-identity.json", import.meta.url), "utf8"));
expect(identity.declaration.systemInstructionDelivery).toBe(COPILOT_SYSTEM_INSTRUCTION_DELIVERY);
expect(identity.declaration.sharedRuntimeContract).toBe("paperclip.acpx-runtime-contract.v1");
expect(identity.declaration.permissionContextContract).toBe(COPILOT_PERMISSION_CONTEXT_CONTRACT);
@@ -119,7 +127,7 @@ describe("Copilot build-owned installation", () => {
expect([...closure].filter(path => !bound.has(path))).toEqual(["negative-control:unbound-policy"]);
});
it.each([...PERMISSION_POLICY_SOURCES, ...SEMANTIC_RECEIPT_SOURCES])("changing %s changes the candidate identity", (_path, field) => {
const identity = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v17-identity.json", import.meta.url), "utf8"));
const identity = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v18-identity.json", import.meta.url), "utf8"));
identity.declaration[field] = "0".repeat(64);
const sorted = Object.fromEntries(Object.entries(identity.declaration).sort(([a], [b]) => a.localeCompare(b)));
expect(`sha256:${createHash("sha256").update(JSON.stringify(sorted)).digest("hex")}`).not.toBe(identity.commandDigest);
@@ -22,12 +22,12 @@ export const QUALIFIED_ACPX_PROFILE_DATA = {
"protocolVersion": 1,
"acpxVersion": "0.13.1",
"agent": "pi",
"agentProfileVersion": 22,
"agentProfileVersion": 23,
"agentServerPackage": "pi-acp",
"agentServerVersion": "0.0.33",
"agentRuntimePackage": "@earendil-works/pi-coding-agent",
"agentRuntimeVersion": "1.0.0",
"commandDigest": "sha256:e92078bee3c23bec4100aa589013a44613d054cd686826534025d8019e9f39a9",
"commandDigest": "sha256:eba77e17e5e874d0dc80585abbf0266f3005c6d6fb746dda29cafad95657f6c6",
"permissionPolicy": "interactive"
},
"cursor": {
@@ -48,12 +48,12 @@ export const QUALIFIED_ACPX_PROFILE_DATA = {
"protocolVersion": 1,
"acpxVersion": "0.13.1",
"agent": "copilot",
"agentProfileVersion": 17,
"agentProfileVersion": 18,
"agentServerPackage": "@github/copilot",
"agentServerVersion": "1.0.88",
"agentRuntimePackage": null,
"agentRuntimeVersion": null,
"commandDigest": "sha256:481d0852ae8272a90f9912723d540518a874a0da65a9070e33b52ea1a14cbd7f",
"commandDigest": "sha256:bc0827bd362d01b5595738515f0ca1adcd6961ec622034cbda4633b0f5ec4701",
"qualificationStatus": "pending",
"permissionPolicy": "interactive"
},
@@ -88,11 +88,19 @@ describe("Pi installation factory", () => {
expect(current.commandDigest).not.toBe(prior.commandDigest);
});
it("preserves profile 22 runtime bytes and revises only shared Claude effort source attestations", async () => {
const read = async (version: number) => JSON.parse(await readFile(new URL(`../../../test-fixtures/pi-acp/profile-v${version}-identity.json`, import.meta.url), "utf8"));
const prior = await read(22), current = await read(23);
expect(Object.keys(current.declaration).filter(key => JSON.stringify(current.declaration[key]) !== JSON.stringify(prior.declaration[key])).sort())
.toEqual(["agentProfileVersion", "recoveryIdentitySourceSha256", "runtimeSandboxSourceSha256"]);
expect(current.commandDigest).not.toBe(prior.commandDigest);
});
it("binds the profile declaration to the reviewed patch and platform closure pins", async () => {
const hash = (bytes: string | Uint8Array) => createHash("sha256").update(bytes).digest("hex");
const canonical = (value: any): string => Array.isArray(value) ? `[${value.map(canonical).join(",")}]`
: value && typeof value === "object" ? `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}` : JSON.stringify(value);
const identity = JSON.parse(await readFile(new URL("../../../test-fixtures/pi-acp/profile-v22-identity.json", import.meta.url), "utf8"));
const identity = JSON.parse(await readFile(new URL("../../../test-fixtures/pi-acp/profile-v23-identity.json", import.meta.url), "utf8"));
const declaration = identity.declaration;
expect(declaration.sharedRuntimeContract).toBe("paperclip.acpx-runtime-contract.v1");
expect(identity.commandDigest).toBe(`sha256:${hash(canonical(declaration))}`);
@@ -123,21 +131,21 @@ describe("Pi installation factory", () => {
it("rejects legacy profiles and tampered executable identities", () => {
expect(() => assertPiInstallationProfile(candidate())).not.toThrow();
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 1 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 2 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 3 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 4 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 5 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 6 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 7 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 8 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 9 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 10 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 11 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 12 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 13 })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 14 })).toThrow("version 22");
for (const priorVersion of [15, 16, 17, 18, 19, 20, 21]) expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: priorVersion })).toThrow("version 22");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 1 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 2 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 3 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 4 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 5 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 6 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 7 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 8 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 9 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 10 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 11 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 12 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 13 })).toThrow("version 23");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 14 })).toThrow("version 23");
for (const priorVersion of [15, 16, 17, 18, 19, 20, 21, 22]) expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: priorVersion })).toThrow("version 23");
for (const changed of [{ agentServerVersion: "latest" }, { commandDigest: `sha256:${"0".repeat(64)}` }, { agentRuntimePackage: "ambient-pi" }]) {
expect(() => assertPiInstallationProfile({ ...candidate(), ...changed })).toThrow("trusted declaration");
}
@@ -1,3 +1,5 @@
import { createAcpxConfigAdmission } from "./config-admission.js";
export type PiThinkingLevel = "off" | "low" | "high" | "max";
export function resolvePiThinkingLevel(agent: string, mode: unknown): PiThinkingLevel | undefined {
@@ -10,108 +12,13 @@ export function resolvePiThinkingLevel(agent: string, mode: unknown): PiThinking
return mode;
}
function object(value: unknown): Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
export function createPiThinkingAdmission(expected: PiThinkingLevel, options: { restoring?: boolean } = {}) {
return createAcpxConfigAdmission(expected, { ...options, label: "Pi thinking mode", configId: "thought_level",
supportedValues: ["off", "minimal", "low", "medium", "high", "xhigh", "max"], sessionMode: true });
}
/** Native mode is independent of permissions. Every connection must prove its
* current mode before a prompt; a persisted ACPX preference is not proof. */
export function createPiThinkingAdmission(expected: PiThinkingLevel, options: { restoring?: boolean } = {}) {
type State = { sessionId: string | null; mode: string | null; error: Error | null };
let current: State | null = null;
let selectedOnce = options.restoring === true;
const failure = (detail: string) => new Error(`Pi thinking mode admission failed: ${detail}`);
const assertReady = () => {
if (current?.error) throw current.error;
if (!selectedOnce || !current?.sessionId || current.mode !== expected) throw failure("native mode does not acknowledge the selected mode");
};
const readMode = (result: Record<string, unknown>, session: boolean): string => {
const configs = Array.isArray(result.configOptions) ? result.configOptions.filter(value => object(value).id === "thought_level") : [];
if (configs.length !== 1) throw failure("native mode configuration is missing or ambiguous");
const mode = object(configs[0]).currentValue;
// Before exact model selection, Pi can legitimately report a different
// native model's level. Observation alone never admits the first prompt.
if (typeof mode !== "string" || !["off", "minimal", "low", "medium", "high", "xhigh", "max"].includes(mode)) throw failure("native mode is unsupported");
if (session && object(result.modes).currentModeId !== mode) throw failure("native mode acknowledgements conflict");
return mode;
};
return {
assertReady,
isReady() { assertNoError(); return selectedOnce && current?.sessionId != null && current.mode === expected; },
createGuard() {
const state: State = { sessionId: null, mode: null, error: null };
current = state;
const pending = new Map<string | number, { method: string; sessionId: unknown; selectsMode: boolean }>();
return (direction: "inbound" | "outbound", value: unknown): void => {
if (current !== state) throw failure("connection authority was replaced");
assertNoError();
const message = object(value);
const params = object(message.params);
try {
if (direction === "outbound") {
const method = message.method;
if (method === "session/prompt") {
assertReady();
if (params.sessionId !== state.sessionId) throw failure("prompt belongs to a different session");
} else if (method === "session/new" || method === "session/load" || method === "session/set_config_option") {
if (typeof message.id !== "string" && typeof message.id !== "number") throw failure("uncorrelated mode request");
if (pending.has(message.id)) throw failure("duplicate mode request identity");
if (pending.size) throw failure("overlapping native admission controls");
if (method === "session/new" || method === "session/load") {
if (pending.size) throw failure("overlapping session admission");
state.sessionId = null; state.mode = null;
} else {
if (!state.sessionId || params.sessionId !== state.sessionId) throw failure("configuration belongs to a different session");
if (params.configId === "thought_level" && params.value !== expected) throw failure("configuration changes the selected mode");
state.mode = null; // No prompt may race unacknowledged native configuration.
}
pending.set(message.id, { method, sessionId: params.sessionId, selectsMode: params.configId === "thought_level" });
} else if (method === "session/set_mode") {
if (!state.sessionId || params.sessionId !== state.sessionId || params.modeId !== expected) throw failure("mode control changes the selected mode");
// This method only returns {}; require a later config/session
// acknowledgement rather than treating that empty result as proof.
state.mode = null;
}
return;
}
if (message.method === "session/update" && object(params.update).sessionUpdate === "config_option_update") {
const update = object(params.update);
// Config updates may contain only model options. A mode entry is
// authoritative observation, but never an admission acknowledgement.
if (Array.isArray(update.configOptions) && update.configOptions.some(value => object(value).id === "thought_level")) {
if (state.sessionId && params.sessionId !== state.sessionId) throw failure("mode update belongs to a different session");
const observed = readMode(update, false);
if (selectedOnce && observed !== expected) throw failure("native mode drifted from the selected mode");
}
return;
}
if (message.method === "session/update" && object(params.update).sessionUpdate === "current_mode_update") {
if (state.sessionId && params.sessionId !== state.sessionId) throw failure("mode update belongs to a different session");
if (selectedOnce && object(params.update).currentModeId !== expected) throw failure("native mode drifted from the selected mode");
return; // Notifications alone never admit a prompt.
}
if (message.method !== undefined || (typeof message.id !== "string" && typeof message.id !== "number")) return;
const request = pending.get(message.id);
if (!request) return;
pending.delete(message.id);
if (message.error !== undefined) { state.mode = null; return; }
const result = object(message.result);
const session = request.method !== "session/set_config_option";
const sessionId = request.method === "session/new" ? result.sessionId : request.sessionId;
if (typeof sessionId !== "string" || !sessionId.trim()) throw failure("mode acknowledgement has no session identity");
const mode = readMode(result, session);
if (session && selectedOnce && mode !== expected) throw failure("restored native mode drifted from the selected mode");
if (!session && (request.selectsMode || selectedOnce) && mode !== expected) throw failure("configuration did not apply the selected mode");
if (!session && request.selectsMode) selectedOnce = true;
state.sessionId = sessionId; state.mode = mode;
} catch (error) {
state.error = error instanceof Error ? error : failure("invalid mode acknowledgement");
throw state.error;
}
};
},
};
function assertNoError() { if (current?.error) throw current.error; }
function object(value: unknown): Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
}
/** Cross-check independently returned durable identity and public descriptor. */
@@ -75,6 +75,21 @@ describe("ACPX recovery identity", () => {
expect(customMode.mode).toBe("provider-custom-mode");
});
it("binds Claude effort to recovery and rejects changed or omitted effort", async () => {
const fixture = await recoveryFixture();
const profile = resolveQualifiedAcpxProfile("claude", "claude-opus-5-5");
const input = { ...fixture.input, profile, requestedModel: profile.qualificationModel, claudeEffort: "high" as const };
const high = await createAcpxRecoveryBinding(input);
const low = await createAcpxRecoveryBinding({ ...input, claudeEffort: "low" });
expect(low.profileSessionKey).not.toBe(high.profileSessionKey);
const expected = { ...fixture.expected, profileDigest: high.commandDigest, requestedModel: high.requestedModel, effectiveModel: high.effectiveModel, claudeEffort: "high" as const };
const saved = createAcpxIdentityRecord(expected, high);
expect(acpxProviderSessionIdentity(saved, high).claudeEffort).toBe("high");
expect(() => verifyExpectedAcpxIdentity({ ...expected, claudeEffort: undefined }, high, saved)).toThrow("immutable");
expect(() => verifyExpectedAcpxIdentity(expected, high, { ...saved, claudeEffort: undefined })).toThrow("persisted");
expect(() => verifyExpectedAcpxIdentity(expected, low, saved)).toThrow("immutable");
});
it("binds Pi thinking mode and rejects old or mismatched warm identities", async () => {
const fixture = await recoveryFixture();
const profile = resolveQualifiedAcpxProfile("pi", "openrouter/deepseek/deepseek-v4-flash-0731");
@@ -1,3 +1,4 @@
import { resolveClaudeEffort } from "./claude-effort.js";
import { parseProviderMode } from "../../contracts/provider-mode.js";
import { resolvePiThinkingLevel, type PiThinkingLevel } from "./pi-thinking.js";
import { createHash } from "node:crypto";
@@ -23,6 +24,7 @@ export interface AcpxRecoveryBinding {
permissionMode: NativeAcpxPermissionMode;
mode?: string;
piThinkingLevel?: PiThinkingLevel;
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
profileSessionKey: string;
}
@@ -39,6 +41,7 @@ export interface AcpxIdentityRecord {
permissionMode: NativeAcpxPermissionMode;
mode?: string;
piThinkingLevel?: PiThinkingLevel;
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
providerLifetimeFenceCandidates: readonly [number, number, number];
}
@@ -51,12 +54,14 @@ export async function createAcpxRecoveryBinding(input: {
permissionMode: NativeAcpxPermissionMode;
mode?: string;
piThinkingLevel?: PiThinkingLevel;
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
providerConfigurationDigest?: string;
providerPolicy?: { readOnly: boolean; readRoots?: readonly string[]; protectedPaths?: readonly string[] };
}): Promise<AcpxRecoveryBinding> {
validateIdentity(input.normalizedSessionId, "normalized session");
const mode = parseProviderMode(input.mode);
const piThinkingLevel = resolvePiThinkingLevel(input.profile.agent, input.piThinkingLevel);
const claudeEffort = resolveClaudeEffort(input.profile.agent, input.claudeEffort);
if (input.providerPolicy !== undefined && typeof input.providerPolicy.readOnly !== "boolean") throw new Error("ACPX recovery requires a valid task execution policy");
if (input.requestedModel !== input.profile.qualificationModel) {
throw new Error("ACPX recovery requested model does not match its admitted profile");
@@ -104,6 +109,7 @@ export async function createAcpxRecoveryBinding(input: {
permissionMode: input.permissionMode,
...(mode ? { mode } : {}),
...(piThinkingLevel ? { piThinkingLevel } : {}),
...(claudeEffort ? { claudeEffort } : {}),
}),
).replace("sha256:", "paperclip-");
return {
@@ -118,6 +124,7 @@ export async function createAcpxRecoveryBinding(input: {
permissionMode: input.permissionMode,
...(mode ? { mode } : {}),
...(piThinkingLevel ? { piThinkingLevel } : {}),
...(claudeEffort ? { claudeEffort } : {}),
profileSessionKey,
};
}
@@ -140,6 +147,7 @@ export function createAcpxIdentityRecord(
permissionMode: binding.permissionMode,
...(binding.mode ? { mode: binding.mode } : {}),
...(binding.piThinkingLevel ? { piThinkingLevel: binding.piThinkingLevel } : {}),
...(binding.claudeEffort ? { claudeEffort: binding.claudeEffort } : {}),
providerLifetimeFenceCandidates: Object.freeze([
...expected.providerLifetimeFenceCandidates,
]) as readonly [number, number, number],
@@ -167,6 +175,7 @@ export function acpxProviderSessionIdentity(
permissionMode: record.permissionMode,
...(record.mode ? { mode: record.mode } : {}),
...(record.piThinkingLevel ? { piThinkingLevel: record.piThinkingLevel } : {}),
...(record.claudeEffort ? { claudeEffort: record.claudeEffort } : {}),
providerLifetimeFenceCandidates: record.providerLifetimeFenceCandidates,
};
verifyExpectedAcpxIdentity(identity, binding, record);
@@ -193,7 +202,8 @@ export function verifyExpectedAcpxIdentity(
expected.effectiveModel !== binding.effectiveModel ||
expected.permissionMode !== binding.permissionMode ||
expected.mode !== binding.mode ||
expected.piThinkingLevel !== binding.piThinkingLevel
expected.piThinkingLevel !== binding.piThinkingLevel ||
expected.claudeEffort !== binding.claudeEffort
) {
throw new Error(
"ACPX recovery identity conflicts with the immutable session configuration",
@@ -214,6 +224,7 @@ export function verifyExpectedAcpxIdentity(
record.permissionMode !== binding.permissionMode ||
record.mode !== binding.mode ||
record.piThinkingLevel !== binding.piThinkingLevel ||
record.claudeEffort !== binding.claudeEffort ||
!sameFenceCandidates(
record.providerLifetimeFenceCandidates,
expected.providerLifetimeFenceCandidates,
@@ -240,6 +251,7 @@ function parsePersistedRecord(value: unknown): AcpxIdentityRecord {
"permissionMode",
"mode",
"piThinkingLevel",
"claudeEffort",
"providerLifetimeFenceCandidates",
]);
return validatedRecord(record);
@@ -267,6 +279,7 @@ function validatedRecord(value: Record<string, unknown>): AcpxIdentityRecord {
throw new Error("ACPX identity permission mode is invalid");
}
if (value.mode !== undefined) parseProviderMode(value.mode);
resolveClaudeEffort("claude", value.claudeEffort);
if (value.piThinkingLevel !== undefined) resolvePiThinkingLevel("pi", value.piThinkingLevel);
validateFenceCandidates(value.providerLifetimeFenceCandidates);
return value as unknown as AcpxIdentityRecord;
@@ -297,6 +310,7 @@ function validateExpected(expected: AcpxExpectedSessionIdentity): void {
throw new Error("Expected ACPX permission mode is invalid");
}
if (expected.mode !== undefined) parseProviderMode(expected.mode);
resolveClaudeEffort("claude", expected.claudeEffort);
if (expected.piThinkingLevel !== undefined) resolvePiThinkingLevel("pi", expected.piThinkingLevel);
validateFenceCandidates(expected.providerLifetimeFenceCandidates);
}
@@ -1,3 +1,4 @@
import { resolveClaudeEffort } from "./claude-effort.js";
import { piProviderConfiguration } from "./pi-provider-config.js";
import { withAcpxTurnCancellation } from "./turn-cancellation.js";
import { resolveAcpxProviderMode } from "./provider-mode.js";
@@ -83,6 +84,7 @@ const ACPX_ADMISSION_CLEANUP_RESCHEDULE_MS = 1_000;
export interface AcpxRuntimePortIdentity {
mode?: string;
piThinkingLevel?: PiThinkingLevel;
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
acpxRecordId: string;
backendSessionId: string;
agentSessionId: string;
@@ -163,6 +165,7 @@ export interface AcpxRuntimePortOpenOptions {
permissionMode: NativeAcpxPermissionMode;
mode?: string;
piThinkingLevel?: PiThinkingLevel;
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
permissionPolicy: ReturnType<typeof acpxRuntimePermissionPolicy>;
launchEnvironment: Readonly<NodeJS.ProcessEnv>;
/** Kernel credential-home quorum inherited by the provider sentinel. */
@@ -238,6 +241,7 @@ export interface OpenAcpxRuntimeHostOptions {
permissionMode: NativeAcpxPermissionMode;
mode?: string;
piThinkingLevel?: PiThinkingLevel;
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
systemInstructions?: string;
runtimeContext?: NativeRuntimeContextSnapshot | null;
environment?: NodeJS.ProcessEnv;
@@ -350,6 +354,7 @@ export class AcpxRuntimeHost {
permissionMode: options.permissionMode,
mode: resolveAcpxProviderMode(options.agent, options.mode),
piThinkingLevel: resolvePiThinkingLevel(options.agent, options.piThinkingLevel),
claudeEffort: resolveClaudeEffort(options.agent, options.claudeEffort),
...(["cursor", "copilot", "pi"].includes(options.agent) && options.providerPolicy !== undefined
? { providerPolicy: options.providerPolicy } : {}),
}),
@@ -581,6 +586,7 @@ export class AcpxRuntimeHost {
permissionMode: binding.permissionMode,
mode: binding.mode,
piThinkingLevel: binding.piThinkingLevel,
claudeEffort: binding.claudeEffort,
permissionPolicy: acpxRuntimePermissionPolicy(
binding.permissionMode,
),
@@ -644,6 +650,7 @@ export class AcpxRuntimeHost {
if (runtimeIdentity.piThinkingLevel !== binding.piThinkingLevel) {
throw new Error("ACPX runtime Pi thinking level conflicts with session binding");
}
if (runtimeIdentity.claudeEffort !== binding.claudeEffort) throw new Error("ACPX runtime Claude effort conflicts with session binding");
const observedIdentity: AcpxExpectedSessionIdentity = {
kind: "acpx",
normalizedSessionId: binding.normalizedSessionId,
@@ -119,6 +119,20 @@ describe("ACPX runtime sandbox", () => {
}
});
it("restores bound Claude effort in private settings across cold connections", async () => {
const fixture = await sandboxFixture("claude");
for (const claudeEffort of ["high", "xhigh", "max"] as const) {
const binding = { ...fixture.binding, claudeEffort };
for (let attempt = 0; attempt < 2; attempt += 1) {
const sandbox = await prepareAcpxRuntimeSandbox({ binding, agent: "claude" });
const settings = JSON.parse(await readFile(join(sandbox.agentHomeDirectory, "settings.json"), "utf8"));
expect(settings.effortLevel).toBe(claudeEffort);
}
}
const sandbox = await prepareAcpxRuntimeSandbox({ binding: fixture.binding, agent: "claude" });
expect(JSON.parse(await readFile(join(sandbox.agentHomeDirectory, "settings.json"), "utf8"))).not.toHaveProperty("effortLevel");
});
it.each([
["pi", "OPENROUTER_API_KEY", "pi-home"],
["claude", "ANTHROPIC_API_KEY", "claude-home"],
@@ -403,6 +403,10 @@ export async function prepareAcpxRuntimeSandbox(input: {
`${JSON.stringify({
model: input.binding.requestedModel,
availableModels: [input.binding.requestedModel],
// Claude reloads effort from settings on a new ACP connection. Keep
// the bound choice available during model verification and resume;
// the protocol guard still requires a provider acknowledgement.
...(input.binding.claudeEffort ? { effortLevel: input.binding.claudeEffort } : {}),
...((input.binding.permissionMode === "approve-reads" || input.binding.permissionMode === "approve-paperclip")
? { permissions: { allow: claudePaperclipPermissionRules(input.tools ?? [], input.binding.permissionMode) } }
: {}),
@@ -47,6 +47,7 @@ export interface AcpxSidecarOpenParams {
permissionMode: NativeAcpxPermissionMode;
mode?: string;
piThinkingLevel?: "off" | "low" | "high" | "max";
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
permissionModePinned: boolean;
providerPolicy?: { readOnly: boolean };
systemInstructions: string;
@@ -69,6 +70,7 @@ export interface AcpxExpectedSessionIdentity {
permissionMode?: NativeAcpxPermissionMode;
mode?: string;
piThinkingLevel?: "off" | "low" | "high" | "max";
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
providerLifetimeFenceCandidates: readonly [number, number, number];
}
@@ -1,3 +1,4 @@
import { resolveClaudeEffort } from "../acpx/claude-effort.js";
import { resolvePiThinkingLevel } from "../acpx/pi-thinking.js";
import { isProviderMode } from "../../contracts/provider-mode.js";
import type { PersistedHarnessProviderIdentity } from "../../contracts/harness-driver.js";
@@ -81,6 +82,7 @@ export function parseProviderIdentity(
);
}
const piThinkingLevel = identity.piThinkingLevel === undefined ? undefined : resolvePiThinkingLevel("pi", identity.piThinkingLevel);
const claudeEffort = resolveClaudeEffort("claude", identity.claudeEffort);
const mode = identity.mode;
if (mode !== undefined && !isProviderMode(mode)) {
throw new Error("ACPX provider identity contains an invalid provider mode");
@@ -112,6 +114,7 @@ export function parseProviderIdentity(
...(permissionMode === undefined ? {} : { permissionMode }),
...(mode === undefined ? {} : { mode }),
...(piThinkingLevel === undefined ? {} : { piThinkingLevel }),
...(claudeEffort === undefined ? {} : { claudeEffort }),
providerLifetimeFenceCandidates: fenceCandidates as [
number,
number,
+1
View File
@@ -57,6 +57,7 @@ export {
type QualifiedAcpxEnvironmentProbe,
} from "./drivers/acpx/codex-acpx-driver.js";
export * from "./drivers/acpx/sidecar-protocol.js";
export { resolveClaudeEffort } from "./drivers/acpx/claude-effort.js";
export * from "./drivers/runner-tool-bridge.js";
export {
createRunnerdCodexTransport,
@@ -1,5 +1,6 @@
import { resolvePinnedOpenCodeCommand } from "../drivers/opencode/opencode-server-driver.js";
import { ACPX_CAPABILITY_PROFILES } from "../drivers/acpx/capability-profiles.js";
import { resolveClaudeEffort, admittedClaudeEffort } from "../drivers/acpx/claude-effort.js";
import { admittedPiThinkingLevel, resolvePiThinkingLevel } from "../drivers/acpx/pi-thinking.js";
import { configuredEnvironment } from "../configured-environment.js";
import { resolveAcpxProviderMode } from "../drivers/acpx/provider-mode.js";
@@ -1191,6 +1192,7 @@ export interface CapabilityRunnerdProcessEvidence {
providerDriver: string | null;
/** Effective native mode, populated only from the admitted Pi identity. */
piThinkingLevel?: "off" | "low" | "high" | "max";
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
providerVersion: string | null;
acpxAgent: QualifiedAcpxAgent | null;
agentServerVersion: string | null;
@@ -1215,6 +1217,7 @@ export interface CapabilityRunnerdCodexTransportOptions {
acpxPermissionMode?: NativeAcpxPermissionMode;
acpxMode?: string;
piThinkingLevel?: "off" | "low" | "high" | "max";
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
acpxPermissionModePinned?: boolean;
acpxSidecarPath?: string;
/** SHA-256 verified by the provider-pack authority before runner startup. */
@@ -3617,6 +3620,7 @@ class DurablePrpCodexTransport implements CodexAppServerTransport {
readonly #bridgedRuntimeInputs = new Map<string, { durableTurnId: string; permission?: boolean }>();
constructor(readonly options: CapabilityRunnerdCodexTransportOptions) {
resolveClaudeEffort(options.provider === "acpx" ? options.acpxAgent ?? "codex" : "", options.claudeEffort);
resolvePiThinkingLevel(options.provider === "acpx" ? options.acpxAgent ?? "codex" : "", options.piThinkingLevel);
if (options.acpxMode !== undefined && options.provider !== "acpx") {
throw new Error("acpxMode requires the ACPX provider");
@@ -4912,6 +4916,7 @@ class DurablePrpCodexTransport implements CodexAppServerTransport {
providerPolicy: { readOnly: params.permissions === "paperclip-runner-workspace-read-only" },
...(selectedAcpxMode === undefined ? {} : { mode: selectedAcpxMode }),
...(acpxProfile!.agent === "pi" ? { piThinkingLevel: this.options.piThinkingLevel } : {}),
...(acpxProfile!.agent === "claude" && this.options.claudeEffort ? { claudeEffort: this.options.claudeEffort } : {}),
permissionMode: resolveRunnerdAcpxPermissionMode(
this.options.acpxPermissionMode,
),
@@ -6597,6 +6602,8 @@ class DurablePrpCodexTransport implements CodexAppServerTransport {
if (descriptor.turnControls !== undefined && descriptor.agent !== (this.options.acpxAgent ?? "codex")) {
throw new Error("ACPX capability identity differs from the admitted profile");
}
const claudeEffort = admittedClaudeEffort(this.options.acpxAgent ?? "codex", this.options.claudeEffort, descriptor, providerIdentity);
if (claudeEffort !== undefined) this.#evidence.claudeEffort = claudeEffort;
const piThinkingLevel = admittedPiThinkingLevel(this.options.acpxAgent ?? "codex", this.options.piThinkingLevel, descriptor, providerIdentity);
if (piThinkingLevel !== undefined) this.#evidence.piThinkingLevel = piThinkingLevel;
this.#turnControls = parseAcpxTurnControlCapabilities(descriptor.turnControls, descriptor.agent);
@@ -1178,6 +1178,15 @@ export const providerDescriptorSchema = {
"high",
"max"
]
},
"claudeEffort": {
"enum": [
"low",
"medium",
"high",
"xhigh",
"max"
]
}
},
"allOf": [
@@ -1201,6 +1210,26 @@ export const providerDescriptorSchema = {
]
}
},
{
"if": {
"required": [
"claudeEffort"
]
},
"then": {
"properties": {
"provider": {
"const": "acpx"
},
"agent": {
"const": "claude"
}
},
"required": [
"agent"
]
}
},
{
"oneOf": [
{
@@ -0,0 +1,50 @@
{
"commandDigest": "sha256:eba77e17e5e874d0dc80585abbf0266f3005c6d6fb746dda29cafad95657f6c6",
"declaration": {
"schema": "paperclip.acpx_profile_declaration.v1",
"agent": "pi",
"agentProfileVersion": 23,
"acpxVersion": "0.13.1",
"agentServerVersion": "0.0.33",
"agentRuntimeVersion": "1.0.0",
"nodeVersion": "24.21.0",
"closure": {
"darwin-arm64": "282022db10150c6632b3444df421342e7d534bdf5d5fb1097a2e79d0625a2bcf",
"darwin-x64": "64e251e19009f755c0b04f73ce2138246faab71a961b0f13d75ebfcc34bef12e",
"linux-x64": "713b1fdff42fb56a1518bdc084f181d70bee8ebadc3e4b1d76321ed9108c8410"
},
"wrapperSha256": "9d129b3d38772e93e97080aa6c4e574ac5df4e47ce5bc331a484a9fbf8188b36",
"helperSha256": "41e0490b617da0d60e0c8ec58ef311236f945129d99f816cff6897f78da7f91d",
"extensionSha256": "f5818573a355702388072b28926897db546a5cf346b5171f646582ce5f9102a4",
"agentFilesBinding": "registered-runtime-context-v1",
"nativeQuestions": "select-confirm-input-editor-v4-root-field-types",
"pathPolicy": "sdk-normalization-v1",
"mcpToolNames": "provider-safe-exact-source-v1",
"acpxPatchSha256": "00d6af17216cb0cad902b48633eb19bf0ec6b05837a984cb24661fca593f09c1",
"nativeAssistantMessages": "pi1-rpc-deltas-v3-bound-message-and-tool-receipts",
"nativeNotices": "session-extension-v2-bounded-runtime-failure",
"messageProjectionSha256": "17d1e91b948dfa6a56e1905645add52b502041ad843c8d50a13b07956f22aadd",
"noticeProjectionSha256": "014485adc690998e395d334ea3ae67f72be6060a2fc78a1185df7b8d1da28fbf",
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1",
"nativeInputDisposition": "queued-only-v1",
"cacheWarming": "disabled-owned-decision-v1",
"thinkingLevel": {
"supported": [
"off",
"low",
"high",
"max"
],
"newConfigurationDefault": "low",
"admission": "explicit-native-effective-before-prompt-v1",
"recovery": "mode-bound-v1"
},
"modelSelection": "caller-selected-native-verified-v1",
"providerConfigurationSourceSha256": "d5b4c1a426afc85f040c9b55358d1d645299586dc8cec72d1be2509187ca3865",
"credentialEnvironmentSourceSha256": "f8dc40ea1aa894bd44754a35a8f65fbcaa2f4c7ffc88cc5fdaa965e0d378d9bc",
"runtimeSandboxSourceSha256": "8767456c125b53b1e85fbfce84245c9b213f9ef27a58970cf73dbc2ff82005d8",
"recoveryIdentitySourceSha256": "8479e04da1ae2f7742e34f045b0270b99e9c3ae39a5b8fb9f164e5776d83224c",
"nativeCancellation": "acknowledged-cancelled-terminal-preserves-usage-v1",
"dependencySecurityPatchSha256": "5273a195b952f233336122842faaf650193c04f36b9359d20d4d63079b0ce814"
}
}
@@ -0,0 +1,43 @@
{
"commandDigest": "sha256:bc0827bd362d01b5595738515f0ca1adcd6961ec622034cbda4633b0f5ec4701",
"declaration": {
"schema": "paperclip.acpx_profile_declaration.v1",
"agent": "copilot",
"agentProfileVersion": 18,
"acpxVersion": "0.13.1",
"agentServerVersion": "1.0.88",
"protocolPolicyRevision": "copilot-agent-manual-v1",
"acpxPatchSha256": "00d6af17216cb0cad902b48633eb19bf0ec6b05837a984cb24661fca593f09c1",
"policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3",
"distributionSourceSha256": "c0a65e247fc47b263bd369a3d5ab505c4b323d4e6493be8a9ecbeb0e9dbb9107",
"agentFilesBinding": "registered-runtime-context-v1",
"systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1",
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1",
"innerDistributionSourceSha256": "9bb08347eed01f1ab44e065c8b06ead9be3a25973d6e6329dc4c0a099096279a",
"upstreamAppSha256": "7b48282a19b5b0814a0c96ad5e3a125173d792cc55f9525b2effea962f6063c0",
"patchedAppSha256": "61f8eebe0c30cc03b311c6818b2acb158a6d16ca58c8bfac5ebd42db095c2679",
"messageIdentityContract": "copilot-native-message-id-v1",
"ownedDistributionDelivery": "COPILOT_CLI_DIST_DIR:lease-owned-guarded-inner-distribution:v1",
"permissionContextContract": "copilot-edit-permission-context-v1",
"permissionContextSourceSha256": "fe1bb611bea289d81b1c7c973fbb01b93de133a7caa8fe78b7ff4ee1f485b224",
"permissionAdapterSourceSha256": "49421a779c76e286ce65d75052026eab9630b1995af9ccf045a2d43d284633ce",
"permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
"permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
"permissionClassifierSourceSha256": "1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
"permissionIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1",
"semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2",
"semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791",
"semanticBridgeSourceSha256": "cf136da18e2cdbefae8e01d23d2c93f64eb7aebccaec938e84c2887c888f87ee",
"toolEvidenceSourceSha256": "49d7d09eb6957e596a173b0afd763544afe582f0c1f05cc0992fe51916a8fdb3",
"semanticNormalizedInputContract": "validated-forwarded-input-commit-v1",
"semanticSidecarSourceSha256": "d2777bfc3e15ee7861ce57a069ffb55d3dc266364adf7915845bfbad5aa2e402",
"semanticDirectDriverSourceSha256": "187772a17e5d4f854ec10f37abfcd294cf4f1db58a91d18e7645f4a8c617f329",
"semanticValidationSourceSha256": "3b0ac8d3f3cb92747d0bcea472ab0419a140e1cfdc43047e2c22542acc2d105f",
"semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e",
"semanticCompletionContractSourceSha256": "fa2b0c81cb9a62965839c905db81b930794774d8d557a01bce077dae5830b594",
"semanticValidatorsSourceSha256": "4f0219ed00938c5d8bc1b11532ecab9c13230012b23bfd6b7dd6b992e58e98d5",
"semanticSchemaBundleSourceSha256": "fb4a232460dc9e6c7e7221cb4ddd66d7cb82fa0b35e1b3eb04f2d1ee5ee18c86",
"semanticSidecarProtocolSourceSha256": "03c0c95b900f9dc18f29f6be27b8c79bc0bcab29780190d46a74e95328081236",
"semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1"
}
}
@@ -180,7 +180,7 @@ describe("Pi controls catalog admission", () => {
expect(cells).toHaveLength(4); expect(cells.every(c => c.profile.qualificationCandidate === "pi" && c.task.expectedRunCount === 1)).toBe(true);
expect(cells.map(c => `${c.environment.id}/${c.task.id}`).sort()).toEqual(["daytona/pending-permission-stop", "daytona/same-turn-steering", "local/pending-permission-stop", "local/same-turn-steering"]);
expect(selectRunnerExecutions(parseRunnerSelectors(["--all"])).some(c => c.suite.id === "pi-controls")).toBe(false);
expect(validateRunnerCatalog()).toHaveLength(722);
expect(validateRunnerCatalog()).toHaveLength(724);
for (const cell of cells) expect(buildRunnerE2EProcessEnvironment({}, [cell]).PAPERCLIP_RUNNER_ACPX_QUALIFICATION).toBeUndefined();
expect(() => assertRemoteNativeEvidencePrerequisites(cells, {})).toThrow();
});
@@ -189,8 +189,8 @@ describe("Pi controls catalog admission", () => {
// Extended v3 states the strict file oracle's task-wide Bash limit.
const pi = runnerMatrix.find(c => c.profile.qualificationCandidate === "pi")!.profile;
expect(pi.modelQualification?.qualificationId).toBe("pi:0.0.33:1.0.0:openrouter");
expect(runnerSuites.find(s => s.id === "pi-native")!.definitionMetadata).toMatchObject({ version: 23, remoteProcExit: "separately-confirmed-absence-after-read-failure", taskCreation: "explicit-title-and-creation-response-id", agentMemoryParent: "public-managed-file-seed-before-admission", incompleteTerminalCleanup: "retirement-retained-with-failed-watch", profileVersion: 19, agentMemoryContent: "utf8-nonce-plus-final-lf", agentMemoryPrompt: "single-json-write-and-content-bound-native-read-both-runs", agentMemoryReadAuthority: "local-withheld-or-exact-remote-agent-run-file", taskPromptTransport: "fenced-markdown-paste-and-multiline-literal-escapes", nativeFinish: "current-contract-objective-evidence-refs", providerFaultExecutable: "stable-preinstalled-runner-link-and-snapshot-node-inode-with-held-bootstrap-fd-3-or-7" });
expect(runnerSuites.find(s => s.id === "pi-controls")!.definitionMetadata).toMatchObject({ version: 10, remoteProcExit: "separately-confirmed-absence-after-read-failure", taskCreation: "explicit-title-and-creation-response-id", profileVersion: 19,
expect(runnerSuites.find(s => s.id === "pi-native")!.definitionMetadata).toMatchObject({ version: 23, remoteProcExit: "separately-confirmed-absence-after-read-failure", taskCreation: "explicit-title-and-creation-response-id", agentMemoryParent: "public-managed-file-seed-before-admission", incompleteTerminalCleanup: "retirement-retained-with-failed-watch", profileVersion: 23, agentMemoryContent: "utf8-nonce-plus-final-lf", agentMemoryPrompt: "single-json-write-and-content-bound-native-read-both-runs", agentMemoryReadAuthority: "local-withheld-or-exact-remote-agent-run-file", taskPromptTransport: "fenced-markdown-paste-and-multiline-literal-escapes", nativeFinish: "current-contract-objective-evidence-refs", providerFaultExecutable: "stable-preinstalled-runner-link-and-snapshot-node-inode-with-held-bootstrap-fd-3-or-7" });
expect(runnerSuites.find(s => s.id === "pi-controls")!.definitionMetadata).toMatchObject({ version: 10, remoteProcExit: "separately-confirmed-absence-after-read-failure", taskCreation: "explicit-title-and-creation-response-id", profileVersion: 23,
remoteProcessIdentity: "observer-pid-startTicks-bootId",
controlPlaneSettlement: "required-scoped-result-and-terminal-after-runner" });
expect(runnerSuites.find(s => s.id === "extended-harnesses")!.definitionMetadata).toMatchObject({ version: 5, piFileArtifactTitle: "exact-filename", piFileCommandTransport: "fenced-bash-markdown-paste" });
@@ -200,7 +200,7 @@ describe("Pi controls catalog admission", () => {
}
const hashes = Object.fromEntries(runnerSuites.filter(s => ["pi-native", "native-active-stop", "extended-harnesses", "rich-acp-warm-continuity"].includes(s.id)).map(s => [s.id, suiteDefinitionHash(s)]));
expect(hashes).toEqual({
"pi-native": "90f2e901d7f39bfb6bbadcf63bc78e410a3b071af980f3a23979747e3d5f2021",
"pi-native": "3c77ced7edfb99aa12052f451398da7580466a54abadcaf287d31ee12c16382d",
"native-active-stop": "2d4fdeeb75bd531b309bd1b35cfa5680ceefdeee6b7155b068dd011ce9901980",
"rich-acp-warm-continuity": "331b88d9538a132670789fb7864df7df5f4bf294ae19b62f53d8a02f901774f0",
"extended-harnesses": "5de4fac78d4d581bc0954f07b5cf2df2862d37f8b0205325eede6d9ef6d9f5e6",