fix(runner): admit Grok effort through native session metadata (#17942)

## Thinking Path

> - Paperclip lets people manage AI agents for work.
> - Paperclip Runner must preserve each agent's model and thinking
effort.
> - The Grok migration in #17922 passes explicit effort to native
Runner.
> - The pinned Grok 1.0.13 CLI reports native session metadata instead
of standard ACP config options.
> - The standard effort guard rejects that valid session before the
first prompt.
> - This pull request binds model and effort on Grok session open and
verifies the native reply.
> - Restored sessions keep the selected effort instead of reverting to
Grok's default.

## Linked Issues or Issue Description

Related: https://github.com/paperclipai/paperclip/pull/17922

**What happened?**
A migrated Grok 4.7 agent with High effort fails at native session.open
before it can run a tool. Grok 1.0.13 has no standard configOptions
response and rejects session/set_config_option. Its native session
metadata supports the requested model and effort. A cold load also
resets effort to High unless the request supplies it again. The upgrade
banner also calls Grok “Codex”.

**Expected behavior**
The native session must acknowledge the selected model and effort before
a prompt. A cold restore must preserve both values. The upgrade banner
must describe the selected harness accurately.

**Steps to reproduce**
1. Upgrade a Grok agent to Paperclip Runner with explicit High effort.
2. Assign a bounded shell task. The pre-fix native session fails during
admission.
3. For the restore case, create a Low native session, close the process,
and load it without effort metadata. The pinned CLI reports High.

**Paperclip version or commit**
Observed at f60ac29cf1 with Grok 1.0.13.

**Deployment mode**
Cloud, with the board operator interface.

## What Changed

- Project the bound model and effort into native Grok session/new,
session/load, and session/resume metadata before transport writes and
observation.
- Require a correlated native acknowledgement with the exact model,
effort, and session identity. Reject conflicting metadata, selection
drift, stale connections, and prompts before admission.
- Connect lazy restored handles through read-only native session
inspection. Keep the existing process ownership and launch snapshot
checks.
- Replace Grok's unsupported standard config setter. Keep the existing
Claude effort path.
- Add behavioral guard cases and a real ACPX client fixture for fresh
and cold Low/High sessions. The fixture resets effort on restore if
metadata is absent, as the pinned CLI does.
- Publish Copilot profile v69 because its source identity includes the
shared runtime adapter. Preserve published v66/v67/v68 bytes and the
reviewed Grok v67 candidate as history. Keep old profiles closed for
launch.
- Explain the native protocol in the Grok Runner guide and use
provider-independent upgrade banner text.

## Verification

- Three native create/load/resume cases failed before the repair. The
pre-fix guard rejected the native metadata reply.
- Seven affected Runner test files: 234 tests passed after the master
merge. These include native Grok admission, the real ACPX transport
fixture, runtime ownership, Claude effort, and profile history.
- The review follow-up extends the same Low/High fixture through a
complete first turn after cold restore. It checks the repeated native
load, original session id, actual response event, and completed turn.
All 127 tests in the three affected Grok/adapter files passed again;
Runner typecheck and source identity checks also passed.
- All 167 existing agent form tests passed. Token gates and diff checks
passed.
- Workspace typecheck passed. Final Runner typecheck and generated
profile checks passed.
- Production `pnpm build` passed, including after the master conflict
resolution. Runner typecheck, profile history checks, and the 234
affected tests passed on the merged source. All 55 Rust ACPX library
tests also passed on the merged source.
- Real pinned Grok 1.0.13 metadata probes reproduced the unsupported
standard setter and native create/load/resume behavior. No model prompt
was sent. An earlier actual ACPX runtime probe passed fresh and cold
Low/High metadata checks. A repeat with an explicitly isolated HOME
failed authentication before session creation, so it does not count as
final live acceptance. The deterministic transport fixture is green; a
deployed shell task remains required.
- `pnpm test:run` did not complete locally. Embedded Postgres failed
during bootstrap after five attempts. This is the same local fixture
limitation recorded for #17922. Fresh PR CI must pass before merge.
- After deployment, retry the original bounded shell task with the saved
Grok 4.7/High configuration. Verify actual tool output, a successful
run, and Done state after reload.

- Final commit `810d84fd19728e7d798989582f26b05f69166db2`: all CI gates
passed. Greptile and the formal Paperclip review both reached 5/5. No
unresolved review threads remain.

## Risks

- This depends on the pinned Grok 1.0.13 native metadata format. Missing
or conflicting replies stop admission instead of silently changing
effort.
- Cold restores must make one read-only native inspection call before
admission. The existing handshake timeout and process ownership checks
still apply.
- The shared adapter changes Copilot's source identity. The new current
profile includes the final source hashes; the prior profile remains
decodable but cannot launch.
- This does not change the Grok CLI version, account selection, search
policy, dependency patch, or lockfile.

## Model Used

OpenAI Codex, GPT-6. The exact serving model ID and context window are
not exposed in this session. The agent used reasoning, code editing,
shell tools, and browser tools.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip authored and GitHub committed 2026-10-27 17:53:58 -05:00
1 parent 64a42d2cf0
commit 7a8df30953
15 files changed
+499 -81

No files matched your search

+9 -3
View File
@@ -17,9 +17,15 @@ Grok Build's qualified harness does not require the `enableNativeRunner` flag.
Explicit thinking effort uses `reasoningEffort` (`minimal`, `low`, `medium`,
`high`, or `xhigh`). The immutable v10 execution input, provider descriptor,
and recovery identity bind this value as `grokEffort`. Before a prompt, the
Runner applies ACP `reasoning_effort` and requires Grok to acknowledge the exact
value. Missing configuration, a rejected value, or effort drift stops admission.
and recovery identity bind this value as `grokEffort`. The Grok provider boundary
adds native `_meta.modelId` and `_meta.reasoningEffort` to session create, load,
and resume requests before transport writes and observation. Grok 1.0.13 reports
the selected effort in `_meta["x.ai/sessionConfig"].options`; it does not support
the standard `session/set_config_option` effort API. The Runner checks that native
acknowledgement and the effective model before a prompt. A lazy restored handle
must connect through read-only native session inspection first. Reapplying the
bound metadata on restore prevents Grok from resetting effort to its default.
Missing configuration, a rejected value, or effort drift stops admission.
Omitting effort retains Grok's provider default. This setting remains outside
the model-visible task prompt. Historical execution inputs retain their schemas.
+2 -2
View File
@@ -32,12 +32,12 @@
"requiresProviderPolicy": true
},
"copilot": {
"agentProfileVersion": 68,
"agentProfileVersion": 69,
"agentServerPackage": "@github/copilot",
"agentServerVersion": "1.0.88",
"agentRuntimePackage": null,
"agentRuntimeVersion": null,
"commandDigest": "sha256:1ce8bb0e00df81fa9eb71cff5063ce769637f5db7b3aa0ada9ade651db089c63",
"commandDigest": "sha256:5444547ca83543bc899ae5a8a2e895c19a86559339e60a1970786ce900742719",
"requiresProviderPolicy": true
},
"claude": {
@@ -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:1ce8bb0e00df81fa9eb71cff5063ce769637f5db7b3aa0ada9ade651db089c63",
"sha256:5444547ca83543bc899ae5a8a2e895c19a86559339e60a1970786ce900742719",
requires_provider_policy: true,
},
"claude" => AcpxReleaseProfile {
@@ -7,9 +7,9 @@ import { fileURLToPath } from "node:url";
import { resolve, dirname } from "node:path";
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const fixture = resolve(root, "test/fixtures/copilot-profile-v68-identity.json");
const fixture = resolve(root, "test/fixtures/copilot-profile-v69-identity.json");
const historical = JSON.parse(readFileSync(resolve(root, "test/fixtures/copilot-profile-v12-identity.json"), "utf8"));
const declaration = { ...historical.declaration, agentProfileVersion: 68 };
const declaration = { ...historical.declaration, agentProfileVersion: 69 };
delete declaration.permissionIdentitySourceSha256;
const sources = {
displayEventSourceSha256: "src/drivers/acpx/copilot-events.ts",
@@ -69,36 +69,44 @@ describe("Codex ACPX runtime adapter", () => {
await port.close({ reason: "test complete" });
});
it.each([false, true])("acknowledges configured Grok effort before admission (restoring=%s)", async restoring => {
const runtime = fakeRuntime();
const runtime = Object.assign(fakeRuntime(), { requestExtension: vi.fn(async () => ({})) });
const child = fakeChild();
const options = openOptions({ spawn: () => child, close: async () => undefined });
options.profile = { ...options.profile, agent: "grok" };
options.profile = resolveQualifiedAcpxProfile("grok", "grok-4.7");
options.grokEffort = "high";
options.restoringSession = restoring;
options.refreshConsumedCommand = vi.fn(async () => undefined);
let created!: AcpRuntimeOptions;
const spawnProvider = () => created.spawnAgent!({ command: "ignored", args: [], options: {} });
vi.mocked(runtime.ensureSession).mockImplementation(async () => {
if (!restoring) spawnProvider();
return HANDLE;
});
vi.mocked(runtime.setConfigOption).mockImplementation(async input => {
if (restoring) spawnProvider();
const acknowledge = () => {
const guard = created.protocolGuardFactory!();
guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } });
guard("inbound", { id: 0, result: { configOptions: [{ id: "reasoning_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: "reasoning_effort", currentValue: input.value }] } });
guard("inbound", { id: 0, result: { models: { currentModelId: "grok-4.7" },
_meta: { "x.ai/sessionConfig": { options: [{ id: "high", category: "mode", selected: true }] } } } });
};
vi.mocked(runtime.ensureSession).mockImplementation(async input => {
expect(input.sessionOptions).toMatchObject({ model: "grok-4.7" });
if (!restoring) { spawnProvider(); acknowledge(); }
return HANDLE;
});
runtime.requestExtension.mockImplementation(async () => {
spawnProvider(); acknowledge(); return {};
});
const port = await openCodexAcpxRuntime(options, {
createRegistry: () => registry(), createStore: () => store(), createRuntime: value => { created = value; return runtime; },
awaitProviderOwnership: providerOwnershipEstablished,
awaitProviderExit: providerOwnershipEstablished,
});
expect(runtime.setConfigOption).toHaveBeenCalledExactlyOnceWith({ handle: HANDLE, key: "reasoning_effort", value: "high" });
expect(runtime.setConfigOption).not.toHaveBeenCalled();
expect(await port.identity()).toMatchObject({ grokEffort: "high" });
if (restoring) expect(options.refreshConsumedCommand).toHaveBeenCalledOnce();
else expect(options.refreshConsumedCommand).not.toHaveBeenCalled();
if (restoring) {
expect(runtime.requestExtension).toHaveBeenCalledExactlyOnceWith({ handle: HANDLE, method: "_x.ai/session/info",
params: { sessionId: "backend-1" }, sessionMode: "persistent" });
expect(options.refreshConsumedCommand).toHaveBeenCalledOnce();
} else {
expect(runtime.requestExtension).not.toHaveBeenCalled();
expect(options.refreshConsumedCommand).not.toHaveBeenCalled();
}
expect(runtime.startTurn).not.toHaveBeenCalled();
await port.close({ reason: "test complete" });
});
@@ -323,7 +323,7 @@ export async function openQualifiedAcpxRuntime(
const selectedGrokEffort = resolveGrokEffort(options.profile.agent, options.grokEffort);
const grokDisableWebSearch = resolveGrokDisableWebSearch(options.profile.agent, options.grokDisableWebSearch);
const effortAdmission = selectedClaudeEffort ? createClaudeEffortAdmission(selectedClaudeEffort)
: selectedGrokEffort ? createGrokEffortAdmission(selectedGrokEffort) : undefined;
: selectedGrokEffort ? createGrokEffortAdmission(selectedGrokEffort, options.profile.reportedModelId) : undefined;
const cursorInstructions = options.profile.agent === "cursor"
? createCursorInstructionAdmission(options.systemInstructions)
: null;
@@ -565,9 +565,15 @@ export async function openQualifiedAcpxRuntime(
handle = ensuredHandle;
options.signal?.throwIfAborted();
if (!effortAdmission.isReady()) {
if (!runtime.setConfigOption) throw new Error("Effort admission requires native configuration");
const spawnsBeforeControl = commandLaunches.count;
await runtime.setConfigOption({ handle: ensuredHandle, key: selectedGrokEffort ? "reasoning_effort" : "effort", value: (selectedGrokEffort ?? selectedClaudeEffort)! });
if (selectedGrokEffort) {
if (!runtime.requestExtension) throw new Error("Grok admission requires native session inspection");
await runtime.requestExtension({ handle: ensuredHandle, method: "_x.ai/session/info",
params: { sessionId: requireIdentity(ensuredHandle).backendSessionId }, sessionMode: "persistent" });
} else {
if (!runtime.setConfigOption) throw new Error("Effort admission requires native configuration");
await runtime.setConfigOption({ handle: ensuredHandle, key: "effort", value: selectedClaudeEffort! });
}
effortAdmission.assertReady();
await children.verifyLifetimeOwnership();
options.signal?.throwIfAborted();
@@ -87,9 +87,27 @@ vi.mock("./installation-integrity.js", () => ({ verifyNativeAcpxInstallation: vi
describe("Copilot build-owned installation", () => {
it("admits the current profile with environment launch preparation", () => {
expect(QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion).toBe(68);
expect(QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion).toBe(69);
expect(QUALIFIED_ACPX_PROFILES.copilot.qualificationStatus).toBeUndefined();
expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:1ce8bb0e00df81fa9eb71cff5063ce769637f5db7b3aa0ada9ade651db089c63");
expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:5444547ca83543bc899ae5a8a2e895c19a86559339e60a1970786ce900742719");
});
it("preserves published and candidate history while binding native Grok admission in v69", async () => {
const published = readFileSync(new URL("../../../test/fixtures/copilot-profile-v68-identity.json", import.meta.url));
const candidate = readFileSync(new URL("../../../test/fixtures/copilot-profile-v67-grok-native-effort-candidate-historical-identity.json", import.meta.url));
expect(createHash("sha256").update(published).digest("hex")).toBe("9692e8de4e75682c74f8e635e26b8de1f03f1d3e41966dafbb7314d35b75c3cf");
expect(createHash("sha256").update(candidate).digest("hex")).toBe("0a20ffe33c92d86f58013582b19c2371f96d93e7e4e0e8096fbf0e7236bd02f4");
const prior = JSON.parse(published.toString("utf8"));
const current = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v69-identity.json", import.meta.url), "utf8"));
expect(Object.keys(current.declaration).filter(key => current.declaration[key] !== prior.declaration[key]).sort()).toEqual([
"agentProfileVersion", "profileCompatibilitySourceSha256", "runtimeAdapterSourceSha256",
]);
expect(Object.keys(current.declaration).sort()).toEqual(Object.keys(prior.declaration).sort());
for (const history of [prior, JSON.parse(candidate.toString("utf8"))]) {
expect(isSupportedAcpxProfileVersion("copilot", history.declaration.agentProfileVersion)).toBe(true);
await expect(verifyCopilotInstallation({ ...QUALIFIED_ACPX_PROFILES.copilot,
agentProfileVersion: history.declaration.agentProfileVersion, commandDigest: history.commandDigest })).rejects.toThrow("exact pinned");
}
});
it("preserves both lineages while binding the merged implementation in v68", async () => {
@@ -48,12 +48,12 @@ export const QUALIFIED_ACPX_PROFILE_DATA = {
"protocolVersion": 1,
"acpxVersion": "0.13.1",
"agent": "copilot",
"agentProfileVersion": 68,
"agentProfileVersion": 69,
"agentServerPackage": "@github/copilot",
"agentServerVersion": "1.0.88",
"agentRuntimePackage": null,
"agentRuntimeVersion": null,
"commandDigest": "sha256:1ce8bb0e00df81fa9eb71cff5063ce769637f5db7b3aa0ada9ade651db089c63",
"commandDigest": "sha256:5444547ca83543bc899ae5a8a2e895c19a86559339e60a1970786ce900742719",
"permissionPolicy": "interactive"
},
"claude": {
@@ -1,19 +1,37 @@
import { describe, expect, it } from "vitest";
import { admittedGrokEffort, createGrokEffortAdmission, resolveGrokEffort } from "./grok-effort.js";
const response = (value: string, sessionId?: string) => ({ ...(sessionId ? { sessionId } : {}), configOptions: [{ id: "reasoning_effort", currentValue: value }] });
function loaded(method = "session/new") {
const admission = createGrokEffortAdmission("high");
function response(value: string, sessionId?: string) {
return { ...(sessionId ? { sessionId } : {}), models: { currentModelId: "grok-4.7" },
_meta: { "x.ai/sessionConfig": { options: [
{ id: "grok-4.7", category: "model", selected: true },
{ id: value, category: "mode", selected: true },
] } } };
}
function loaded(method = "session/new", expected: "low" | "high" = "high") {
const admission = createGrokEffortAdmission(expected, "grok-4.7");
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) });
guard("outbound", { id: 1, method, params: { sessionId: "s" } });
guard("inbound", { id: 1, result: response(expected, method === "session/new" ? "s" : undefined) });
return { admission, guard };
}
function select(guard: ReturnType<ReturnType<typeof createGrokEffortAdmission>["createGuard"]>, acknowledged = "high") {
guard("outbound", { id: 2, method: "session/set_config_option", params: { sessionId: "s", configId: "reasoning_effort", value: "high" } });
guard("inbound", { id: 2, result: response(acknowledged) });
}
describe("Grok effort admission", () => {
it.each(["session/new", "session/load", "session/resume"])("admits the pinned Grok native metadata after %s", method => {
const { admission, guard } = loaded(method);
expect(admission.isReady()).toBe(true);
expect(() => guard("outbound", { id: 2, method: "session/prompt", params: { sessionId: "s" } })).not.toThrow();
});
it.each(["session/new", "session/load", "session/resume"])("binds native model and effort without replacing instructions on %s", method => {
const admission = createGrokEffortAdmission("low", "grok-4.7"), guard = admission.createGuard();
const request = { id: 1, method, params: { sessionId: "s", _meta: { rules: "Bound task instructions" } } };
guard("outbound", request);
expect(request.params._meta).toEqual({ rules: "Bound task instructions", modelId: "grok-4.7", reasoningEffort: "low" });
});
it.each([{ modelId: "other-model" }, { reasoningEffort: "high" }])("rejects conflicting native request configuration", meta => {
const admission = createGrokEffortAdmission("low", "grok-4.7"), guard = admission.createGuard();
expect(() => guard("outbound", { id: 1, method: "session/new", params: { _meta: meta } })).toThrow("request conflicts");
});
it.each(["minimal", "low", "medium", "high", "xhigh"] as const)("accepts exact %s without aliases", value => {
expect(resolveGrokEffort("grok", value)).toBe(value);
expect(admittedGrokEffort("grok", value, { grokEffort: value }, { grokEffort: value })).toBe(value);
@@ -23,45 +41,72 @@ describe("Grok effort admission", () => {
expect(() => resolveGrokEffort("pi", "high")).toThrow("only supported");
expect(() => admittedGrokEffort("grok", "high", { grokEffort: "high" }, { grokEffort: "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 = createGrokEffortAdmission("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", () => {
it("requires the native acknowledgement on each cold connection", () => {
const { admission, guard } = loaded();
select(guard);
const cold = admission.createGuard();
expect(() => guard("inbound", { id: 1, result: response("high", "s") })).toThrow("authority was replaced");
expect(() => cold("outbound", { id: 2, method: "session/prompt", params: { sessionId: "s" } })).toThrow("does not acknowledge");
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();
resumed("outbound", { id: 3, method: "session/load", params: { sessionId: "s" } });
resumed("inbound", { id: 3, result: response("high") });
expect(() => resumed("outbound", { id: 4, method: "session/prompt", params: { sessionId: "s" } })).not.toThrow();
});
it("rejects a provider that ignores the requested effort", () => {
it("rejects a restored provider default that differs from the bound effort", () => {
const admission = createGrokEffortAdmission("low", "grok-4.7");
const guard = admission.createGuard();
guard("outbound", { id: 1, method: "session/load", params: { sessionId: "s" } });
expect(() => guard("inbound", { id: 1, result: response("high") })).toThrow("differs");
expect(() => admission.assertReady()).toThrow("differs");
});
it.each([{}, { configOptions: [{ id: "reasoning_effort", currentValue: "high" }] },
{ _meta: { "x.ai/sessionConfig": { options: [{ id: "high", category: "mode", selected: false }] } } }])("rejects missing native effort acknowledgement", result => {
const admission = createGrokEffortAdmission("high", "grok-4.7"), guard = admission.createGuard();
guard("outbound", { id: 1, method: "session/load", params: { sessionId: "s" } });
expect(() => guard("inbound", { id: 1, result })).toThrow("missing or ambiguous");
});
it("rejects ambiguous native effort selections", () => {
const admission = createGrokEffortAdmission("high", "grok-4.7"), guard = admission.createGuard();
const result = response("high", "s");
result._meta["x.ai/sessionConfig"].options.push({ id: "low", category: "mode", selected: true });
guard("outbound", { id: 1, method: "session/new", params: {} });
expect(() => guard("inbound", { id: 1, result })).toThrow("missing or ambiguous");
});
it("rejects an effort acknowledgement for a different effective model", () => {
const admission = createGrokEffortAdmission("high", "grok-4.7"), guard = admission.createGuard();
const result = response("high", "s"); result.models.currentModelId = "grok-4.6";
guard("outbound", { id: 1, method: "session/new", params: {} });
expect(() => guard("inbound", { id: 1, result })).toThrow("model acknowledgement conflicts");
});
it("does not admit an uncorrelated or failed response", () => {
const admission = createGrokEffortAdmission("high", "grok-4.7"), guard = admission.createGuard();
guard("outbound", { id: 1, method: "session/new", params: {} });
guard("inbound", { id: 2, result: response("high", "s") });
expect(admission.isReady()).toBe(false);
guard("inbound", { id: 1, error: { code: -32603 } });
expect(admission.isReady()).toBe(false);
});
it.each(["response", "detail"])("rejects a conflicting native %s session identity", field => {
const admission = createGrokEffortAdmission("high", "grok-4.7"), guard = admission.createGuard();
const result = { ...response("high"), ...(field === "response" ? { sessionId: "other" } : {}),
_meta: { ...response("high")._meta, ...(field === "detail" ? { "x.ai/sessionDetail": { sessionId: "other" } } : {}) } };
guard("outbound", { id: 1, method: "session/load", params: { sessionId: "s" } });
expect(() => guard("inbound", { id: 1, result })).toThrow("different session");
});
it.each(["session/set_config_option", "session/set_model"])("rejects unsupported late %s controls", method => {
const { guard } = loaded();
expect(() => select(guard, "medium")).toThrow("did not apply");
expect(() => guard("outbound", { id: 2, method, params: { sessionId: "s" } })).toThrow("when the native session opens");
});
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");
it("rejects a prompt for another session", () => {
const { guard } = loaded();
expect(() => guard("outbound", { id: 2, method: "session/prompt", params: { sessionId: "other" } })).toThrow("different session");
});
it.each([
{ sessionId: "s", model_id: "grok-4.7", reasoning_effort: "low" },
{ sessionId: "other", model_id: "grok-4.7", reasoning_effort: "high" },
{ sessionId: "s", model_id: "grok-4.6", reasoning_effort: "high" },
])("rejects native model or effort drift", update => {
const { guard } = loaded();
expect(() => guard("inbound", { method: "_x.ai/session_notification", params: { sessionId: update.sessionId,
update: { sessionUpdate: "model_changed", model_id: update.model_id, reasoning_effort: update.reasoning_effort } } })).toThrow("drifted");
});
});
@@ -1,5 +1,3 @@
import { createAcpxConfigAdmission } from "./config-admission.js";
export type GrokEffort = "minimal" | "low" | "medium" | "high" | "xhigh";
export function resolveGrokEffort(agent: string, value: unknown): GrokEffort | undefined {
if (value === undefined || value === "") return undefined;
@@ -7,9 +5,83 @@ export function resolveGrokEffort(agent: string, value: unknown): GrokEffort | u
if (value !== "minimal" && value !== "low" && value !== "medium" && value !== "high" && value !== "xhigh") throw new Error("Grok effort must be minimal, low, medium, high, or xhigh");
return value;
}
export function createGrokEffortAdmission(expected: GrokEffort) {
return createAcpxConfigAdmission(expected, { label: "Grok effort", configId: "reasoning_effort",
supportedValues: ["default", "minimal", "low", "medium", "high", "xhigh"] });
export function createGrokEffortAdmission(expected: GrokEffort, expectedModel: string) {
type State = { sessionId: string | null; ready: boolean; error: Error | null };
let current: State | null = null;
const failure = (detail: string) => new Error(`Grok effort admission failed: ${detail}`);
const assertReady = () => {
if (current?.error) throw current.error;
if (!current?.sessionId || !current.ready) throw failure("native session does not acknowledge the selected effort");
};
const readAcknowledgement = (result: Record<string, unknown>) => {
const config = object(object(result._meta)["x.ai/sessionConfig"]);
const options = Array.isArray(config.options) ? config.options.map(object) : [];
const selected = options.filter(option => option.category === "mode" && option.selected === true);
if (selected.length !== 1) throw failure("native effort selection is missing or ambiguous");
if (selected[0]!.id !== expected) throw failure("native effort differs from the selected effort");
const model = object(result.models).currentModelId;
const selectedModels = options.filter(option => option.category === "model" && option.selected === true);
if (typeof model !== "string" || !model.trim() || model !== expectedModel
|| selectedModels.length > 1 || selectedModels.length === 1 && selectedModels[0]!.id !== model) {
throw failure("native model acknowledgement conflicts with the selected model");
}
};
return {
assertReady,
isReady() { if (current?.error) throw current.error; return current?.ready === true; },
createGuard() {
const state: State = { sessionId: null, ready: false, error: null };
current = state;
let pending: { id: string | number; method: string; sessionId: unknown } | null = null;
return (direction: "inbound" | "outbound", value: unknown): void => {
if (current !== state) throw failure("connection authority was replaced");
if (state.error) throw state.error;
const message = object(value), params = object(message.params);
try {
if (direction === "outbound") {
if (message.method === "session/prompt") {
assertReady();
if (params.sessionId !== state.sessionId) throw failure("prompt belongs to a different session");
} else if (message.method === "session/new" || message.method === "session/load" || message.method === "session/resume") {
if (typeof message.id !== "string" && typeof message.id !== "number" || pending) throw failure("uncorrelated or overlapping session admission");
const meta = object(params._meta);
if (meta.reasoningEffort !== undefined && meta.reasoningEffort !== expected
|| meta.modelId !== undefined && meta.modelId !== expectedModel) {
throw failure("native session request conflicts with the bound configuration");
}
params._meta = { ...meta, modelId: expectedModel, reasoningEffort: expected };
state.ready = false; state.sessionId = null;
pending = { id: message.id, method: message.method, sessionId: params.sessionId };
} else if (message.method === "session/set_config_option" || message.method === "session/set_model") {
throw failure("model and effort must be selected when the native session opens");
}
return;
}
if (message.method === "_x.ai/session_notification" && object(params.update).sessionUpdate === "model_changed") {
if (!state.ready) return;
const update = object(params.update);
if (params.sessionId !== state.sessionId || update.reasoning_effort !== expected
|| update.model_id !== expectedModel) throw failure("native model or effort drifted from the selected configuration");
return;
}
if (message.method !== undefined || !pending || message.id !== pending.id) return;
const request = pending; pending = null;
if (message.error !== undefined) return;
const result = object(message.result);
const sessionId = request.method === "session/new" ? result.sessionId : request.sessionId;
if (typeof sessionId !== "string" || !sessionId.trim()) throw failure("native acknowledgement has no session identity");
const detail = object(object(result._meta)["x.ai/sessionDetail"]);
if (result.sessionId !== undefined && result.sessionId !== sessionId
|| detail.sessionId !== undefined && detail.sessionId !== sessionId) throw failure("native acknowledgement belongs to a different session");
readAcknowledgement(result);
state.sessionId = sessionId; state.ready = true;
} catch (error) {
state.error = error instanceof Error ? error : failure("invalid native acknowledgement");
throw state.error;
}
};
},
};
}
export function admittedGrokEffort(agent: string, requested: unknown, descriptor: Record<string, unknown>, identity: Record<string, unknown>): GrokEffort | undefined {
const expected = resolveGrokEffort(agent, requested);
@@ -17,3 +89,7 @@ export function admittedGrokEffort(agent: string, requested: unknown, descriptor
if (effective !== expected || descriptor.grokEffort !== expected) throw new Error("Grok effort differs from the admitted configuration");
return effective;
}
function object(value: unknown): Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
}
@@ -0,0 +1,89 @@
import { spawn } from "node:child_process";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createAcpRuntime, createAgentRegistry, createRuntimeStore, type AcpRuntimeHandle } from "acpx/runtime";
import { expect, it } from "vitest";
import { createGrokEffortAdmission } from "./grok-effort.js";
it.each(["low", "high"] as const)("transports and restores native Grok %s effort through the real ACPX client", async effort => {
const root = await mkdtemp(join(tmpdir(), "grok-native-session-options-"));
const fixture = join(root, "grok"), wirePath = join(root, "wire.jsonl"), statePath = join(root, "native.json");
await writeFile(fixture, String.raw`
const fs = require("node:fs"), readline = require("node:readline");
const statePath = ${JSON.stringify(statePath)}, wirePath = ${JSON.stringify(wirePath)};
const reply = (id, result) => process.stdout.write(JSON.stringify({jsonrpc:"2.0",id,result})+"\n");
readline.createInterface({input:process.stdin}).on("line", line => {
const request = JSON.parse(line), params = request.params ?? {};
fs.appendFileSync(wirePath, JSON.stringify({method:request.method,sessionId:params.sessionId,meta:params._meta})+"\n");
if (request.method === "initialize") return reply(request.id, {protocolVersion:1,agentCapabilities:{loadSession:true},authMethods:[]});
if (["session/new","session/load","session/resume"].includes(request.method)) {
const previous = fs.existsSync(statePath) ? JSON.parse(fs.readFileSync(statePath,"utf8")) : {};
const model = params._meta?.modelId ?? previous.model;
const effort = params._meta?.reasoningEffort ?? "high";
fs.writeFileSync(statePath,JSON.stringify({model,effort}));
return reply(request.id, {...(request.method === "session/new" ? {sessionId:"grok-native-session"} : {}),
models:{currentModelId:model,availableModels:[{modelId:model,name:model}]},
_meta:{"x.ai/sessionConfig":{options:[{id:model,category:"model",selected:true},{id:effort,category:"mode",selected:true}]}}});
}
if (request.method === "_x.ai/session/info") return reply(request.id, {result:{sessionId:params.sessionId}});
if (request.method === "session/prompt") {
process.stdout.write(JSON.stringify({jsonrpc:"2.0",method:"session/update",params:{sessionId:params.sessionId,
update:{sessionUpdate:"agent_message_chunk",content:{type:"text",text:"GROK_RESTORED_TURN_OK"}}}})+"\n");
return reply(request.id,{stopReason:"end_turn"});
}
process.stdout.write(JSON.stringify({jsonrpc:"2.0",id:request.id,error:{code:-32601,message:"Unsupported native method"}})+"\n");
});
`);
const store = createRuntimeStore({ stateDir: join(root, "store") });
const model = "grok-exact-model";
let admission = createGrokEffortAdmission(effort, model);
const options = {
cwd: root,
sessionStore: store,
agentRegistry: createAgentRegistry({ overrides: { grok: [fixture, "agent", "stdio"] } }),
permissionMode: "approve-reads" as const,
timeoutMs: 5_000,
protocolGuardFactory: () => admission.createGuard(),
spawnAgent: () => spawn(process.execPath, [fixture], { cwd: root, env: {}, stdio: "pipe" }),
};
const input = { sessionKey: "grok-native-options", agent: "grok", mode: "persistent" as const,
sessionOptions: { model } };
let runtime = createAcpRuntime(options);
let handle: AcpRuntimeHandle | undefined;
try {
handle = await runtime.ensureSession(input);
admission.assertReady();
const originalSession = handle.backendSessionId;
await runtime.close({ handle, reason: "cold native restore", discardPersistentState: false });
admission = createGrokEffortAdmission(effort, model);
runtime = createAcpRuntime(options);
handle = await runtime.ensureSession(input);
expect(handle.backendSessionId).toBe(originalSession);
expect(admission.isReady()).toBe(false);
await runtime.requestExtension({ handle, method: "_x.ai/session/info", params: { sessionId: handle.backendSessionId }, sessionMode: "persistent" });
admission.assertReady();
const turn = runtime.startTurn({ handle, text: "Verify the restored fixture", mode: "prompt", requestId: "grok-cold-turn", timeoutMs: 5_000 });
const output: string[] = [];
const drain = (async () => {
for await (const event of turn.events) if (event.type === "text_delta") output.push(event.text);
})();
const result = await turn.result;
await drain;
expect(result.status).toBe("completed");
expect(output.join("")).toBe("GROK_RESTORED_TURN_OK");
admission.assertReady();
const wire = (await readFile(wirePath, "utf8")).trim().split("\n").map(line => JSON.parse(line));
const opens = wire.filter(request => ["session/new", "session/load", "session/resume"].includes(request.method));
expect(opens.map(request => request.method)).toEqual(["session/new", "session/load", "session/load"]);
for (const request of opens) expect(request.meta).toMatchObject({ modelId: model, reasoningEffort: effort });
expect(wire.filter(request => request.method === "session/prompt")).toEqual([
{ method: "session/prompt", sessionId: originalSession },
]);
expect(wire.some(request => request.method === "session/set_config_option")).toBe(false);
} finally {
if (handle) await runtime.close({ handle, reason: "native metadata test complete", discardPersistentState: false });
await rm(root, { recursive: true, force: true });
}
});
@@ -2,13 +2,13 @@ import { QUALIFIED_ACPX_PROFILE_DATA } from "./generated-profiles.js";
/** Decodable historical profile revisions, not permission to launch them.
* Exact current profile/package/digest admission is checked separately. */
type HistoricalAcpxProfileVersion = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67;
type HistoricalAcpxProfileVersion = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68;
type CurrentAcpxProfileVersion = typeof QUALIFIED_ACPX_PROFILE_DATA[keyof typeof QUALIFIED_ACPX_PROFILE_DATA]["agentProfileVersion"];
export type AcpxProfileVersion = HistoricalAcpxProfileVersion | CurrentAcpxProfileVersion;
const historicalVersions: Readonly<Record<string, readonly HistoricalAcpxProfileVersion[]>> = {
pi: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], claude: [1, 2, 3, 4, 5], codex: [1, 2, 3, 4, 5],
grok: [1, 2, 3, 4, 5], copilot: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67],
grok: [1, 2, 3, 4, 5], copilot: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68],
cursor: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
};
@@ -0,0 +1,85 @@
{
"commandDigest": "sha256:2c0854674e3d00fd00cb0659b5ddb9788f47fb2b11f771901f338cb384926318",
"declaration": {
"schema": "paperclip.acpx_profile_declaration.v1",
"agent": "copilot",
"agentProfileVersion": 67,
"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": "de2d38d702b3c574161af0c0c04790fb6f6347916eb3916ce7a131a8c229121b",
"permissionAdapterSourceSha256": "ee5929ed2ee647ad0369465dee9d51a00b2dd28e1ff735b43e969ddc9c164668",
"permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
"permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
"permissionClassifierSourceSha256": "1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
"semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2",
"semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791",
"semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5",
"toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706",
"semanticNormalizedInputContract": "validated-forwarded-input-commit-v1",
"semanticSidecarSourceSha256": "7d6e948b7cff4df52a9afc916db46ace0f42b4b2d3134ba0c21ee0a928857e62",
"semanticDirectDriverSourceSha256": "d683429427dd8a1a5efe50952018ea486f808bdbf0a92fc5946ee267fe2c72ee",
"semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975",
"semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e",
"semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59",
"semanticValidatorsSourceSha256": "f22d9caf2035b600abc1a6625ae23ca97f1409d7e29ed5b7264bb8f9784c7dc9",
"semanticSchemaBundleSourceSha256": "4cae579aac836762fdbf92c5447425d8b756e87a5766bc0c7a1885a90541c8d6",
"semanticSidecarProtocolSourceSha256": "26dd0cfbef8b2b401de6b7862a4af7f243fb703e23678a1a5ed8fa68d4784d42",
"semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1",
"displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877",
"displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e",
"configuredEnvironmentSourceSha256": "5c9008d121979a3a8987e6b8a5752172eafdc4a2b5d725a73f3bbb8fb83be8ff",
"configuredEnvironmentRustSourceSha256": "85c87fae62b8a9377e4f0a4c00302c7519fae4397b1c48253f8f2b7ba7faf994",
"profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282",
"profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571",
"profileInstallationSourceSha256": "3e61c4cdd4b78e38fcb16eaadae4f1fd65ce3c7127184ea7a80948c8e5d77f88",
"attachmentPolicySourceSha256": "4f7638b7cc64e18f6a4ba33fd46de2b3d49225e32aeab41f5c695b268fa4f8e2",
"singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f",
"nativeSessionRuntimeSourceSha256": "1fe6fa9e6b96d84d70432b85a57c473f833606d4a521563d5022b4d3c41576ce",
"nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842",
"nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1",
"nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90",
"harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5",
"harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249",
"harnessSessionSourceSha256": "81361733e86b56dd5a66415aefc6f72e69be24acc8d3417370154ebb7fef6e94",
"transportContractSourceSha256": "03fca93d36a89b0cc0917335e293ad097fdf04356d21b307827bb0ea4c13f523",
"nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586",
"nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44",
"nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b",
"providerLaunchSourceSha256": "4c1abd77bbed9723e669009a78dab6fe024837718ced9f22c159f9e22ce1f26c",
"launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296",
"runtimeRequestBridgeSourceSha256": "9584d9801b87f67e60b0007beafa909fc5b05dc6adac71409db1c7f80a2f7916",
"runtimeRequestFacadeSourceSha256": "d0ed28e0c5ddad34db8f437e704c8a755eb0b47f27e08e44185f66d1d2205295",
"runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211",
"guardedInstallationSourceSha256": "56a177d3e702914818dca4ca50e061f0d25f988bfc4c0582c36ce23c1696d82f",
"nativeExecutionSourceSha256": "d60b8ea63bad975c0144f0702b2749d7db9a7da50680a6147f20d4d11f2d5297",
"profileCompatibilitySourceSha256": "4b0f14e8e2a34e1b6795a5be63265451e36c7583d99a246c5471e01f26f6e170",
"installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d",
"assetOwnershipSourceSha256": "1785e3f31e5dad5a13b53e25d8cf9482a9bd89a27b6c127830aed6b81acc605c",
"assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f",
"credentialBindingSourceSha256": "7586d44b47c83395498dc8806287cd3538ef58177ba7c5949f5766ed105a0161",
"sidecarErrorSourceSha256": "b09af4577f8bf42eee7036b2ad9842202cfc763e72b53c3287f82c6090cdad80",
"profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456",
"metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727",
"permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1",
"semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811",
"runtimeHostSourceSha256": "976fef21ec9f3ae9dd5eabb133a55ae578f1c0729c657c30e42562a80db237d8",
"runtimeAdapterSourceSha256": "7c47c05306ebe7d1ca6b5d046a96f5726a48adec4d27e7c16788f505f25b5db2",
"runtimeSandboxSourceSha256": "981449a4c6d10f682ecf86d834eed07faca30a7d9b419fb8db4973a442a343ec",
"cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca",
"agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba",
"nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b"
}
}
@@ -0,0 +1,85 @@
{
"commandDigest": "sha256:5444547ca83543bc899ae5a8a2e895c19a86559339e60a1970786ce900742719",
"declaration": {
"schema": "paperclip.acpx_profile_declaration.v1",
"agent": "copilot",
"agentProfileVersion": 69,
"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": "de2d38d702b3c574161af0c0c04790fb6f6347916eb3916ce7a131a8c229121b",
"permissionAdapterSourceSha256": "ee5929ed2ee647ad0369465dee9d51a00b2dd28e1ff735b43e969ddc9c164668",
"permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
"permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
"permissionClassifierSourceSha256": "1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
"semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2",
"semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791",
"semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5",
"toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706",
"semanticNormalizedInputContract": "validated-forwarded-input-commit-v1",
"semanticSidecarSourceSha256": "7d6e948b7cff4df52a9afc916db46ace0f42b4b2d3134ba0c21ee0a928857e62",
"semanticDirectDriverSourceSha256": "d683429427dd8a1a5efe50952018ea486f808bdbf0a92fc5946ee267fe2c72ee",
"semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975",
"semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e",
"semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59",
"semanticValidatorsSourceSha256": "f22d9caf2035b600abc1a6625ae23ca97f1409d7e29ed5b7264bb8f9784c7dc9",
"semanticSchemaBundleSourceSha256": "4cae579aac836762fdbf92c5447425d8b756e87a5766bc0c7a1885a90541c8d6",
"semanticSidecarProtocolSourceSha256": "26dd0cfbef8b2b401de6b7862a4af7f243fb703e23678a1a5ed8fa68d4784d42",
"semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1",
"displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877",
"displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e",
"configuredEnvironmentSourceSha256": "5c9008d121979a3a8987e6b8a5752172eafdc4a2b5d725a73f3bbb8fb83be8ff",
"configuredEnvironmentRustSourceSha256": "85c87fae62b8a9377e4f0a4c00302c7519fae4397b1c48253f8f2b7ba7faf994",
"profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282",
"profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571",
"profileInstallationSourceSha256": "3e61c4cdd4b78e38fcb16eaadae4f1fd65ce3c7127184ea7a80948c8e5d77f88",
"attachmentPolicySourceSha256": "4f7638b7cc64e18f6a4ba33fd46de2b3d49225e32aeab41f5c695b268fa4f8e2",
"singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f",
"nativeSessionRuntimeSourceSha256": "1fe6fa9e6b96d84d70432b85a57c473f833606d4a521563d5022b4d3c41576ce",
"nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842",
"nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1",
"nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90",
"harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5",
"harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249",
"harnessSessionSourceSha256": "81361733e86b56dd5a66415aefc6f72e69be24acc8d3417370154ebb7fef6e94",
"transportContractSourceSha256": "03fca93d36a89b0cc0917335e293ad097fdf04356d21b307827bb0ea4c13f523",
"nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586",
"nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44",
"nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b",
"providerLaunchSourceSha256": "c744235b3067ea75973523f78f1e13399337bc155f1775af694e34782d5c9bef",
"launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296",
"runtimeRequestBridgeSourceSha256": "93a631b8439df1590b0cf442cf84a9b05f004f12bfee90291651ccfc0861effe",
"runtimeRequestFacadeSourceSha256": "d0ed28e0c5ddad34db8f437e704c8a755eb0b47f27e08e44185f66d1d2205295",
"runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211",
"guardedInstallationSourceSha256": "56a177d3e702914818dca4ca50e061f0d25f988bfc4c0582c36ce23c1696d82f",
"nativeExecutionSourceSha256": "d60b8ea63bad975c0144f0702b2749d7db9a7da50680a6147f20d4d11f2d5297",
"profileCompatibilitySourceSha256": "3513d4b0c57a17b125f7d83e3a844be4b88de7475577dac99132aef00cbdca63",
"installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d",
"assetOwnershipSourceSha256": "1785e3f31e5dad5a13b53e25d8cf9482a9bd89a27b6c127830aed6b81acc605c",
"assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f",
"credentialBindingSourceSha256": "7586d44b47c83395498dc8806287cd3538ef58177ba7c5949f5766ed105a0161",
"sidecarErrorSourceSha256": "b09af4577f8bf42eee7036b2ad9842202cfc763e72b53c3287f82c6090cdad80",
"profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456",
"metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727",
"permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1",
"semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811",
"runtimeHostSourceSha256": "976fef21ec9f3ae9dd5eabb133a55ae578f1c0729c657c30e42562a80db237d8",
"runtimeAdapterSourceSha256": "7c47c05306ebe7d1ca6b5d046a96f5726a48adec4d27e7c16788f505f25b5db2",
"runtimeSandboxSourceSha256": "981449a4c6d10f682ecf86d834eed07faca30a7d9b419fb8db4973a442a343ec",
"cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca",
"agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba",
"nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b"
}
}
+1 -1
View File
@@ -1592,7 +1592,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
<div className="min-w-0 flex-1 space-y-1">
<p className="text-sm font-medium">Upgrade to Paperclip Runner</p>
<p className="text-sm text-muted-foreground">
Keep {harnessType === "claude_local" ? "Claude" : "Codex"}, your model, account, and compatible settings.
Keep your harness, model, account, and compatible settings.
New sessions start fresh; past runs stay available.
</p>
{canQualifyRunnerUpgrade && runnerAvailability?.defaultRunner !== "paperclip" ? (