Merge qualified backup expiration parent into GitHub connection work

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

* commit 'aae32f2f493848199f6cb8c0a9996af3ee0061b1': (30 commits)
  Scope status-card refresh tasks to their claim revision
  test(chat): retain completion identity before link metadata loads
  test(chat): locate completion through its rendered task link
  fix(chat): recover committed native Agent Chat replies (#18465)
  perf(heartbeat): drive retry promotion from durable deadlines (#18387)
  fix(ui): make the Coordinator composer footer opaque (#18455)
  fix(testops): verify unexpanded result job runs (#18462)
  test(server): observe restored Retry before draining unrelated work
  test(runner): own listener cleanup across setup failures
  test(runner): buffer upgrade frames until the wire subscribes
  fix(runner): preserve OpenCode written-answer defaults (#18456)
  fix(native): fence retries when replaying an audited Stop (#18457)
  test: await async outcomes and drive saved deadlines deterministically (#18416)
  fix(ui): retain startup autosave through session recovery (#18412)
  perf(server): recover independent native replies concurrently (#18421)
  perf(chat): scope task questions and batch inbox queue checks (#18422)
  fix(native): recover controllers across webserver replacement (#18343)
  fix(ui): restore task document authoring and add persistence journey (#18356)
  fix(tools): stabilize tied profile entry order (#18427)
  fix(adapter-utils): tolerate entries removed during workspace snapshots (#18426)
  ...
This commit is contained in:
DottaandPaperclip committed 2026-10-31 09:41:51 -05:00
commit f522ab07e2
484 files changed
+33834 -1620

No files matched your search

@@ -12,6 +12,10 @@ import {
} from "./paperclip-runner-permissions.js";
describe("Paperclip Runner permission defaults", () => {
it("exposes qualified Kimi Code with an explicit runtime credential", () => {
expect(PAPERCLIP_RUNNER_ACPX_PROFILES.find(profile => profile.value === "kimi"))
.toMatchObject({ qualified: true, credentialEnvironment: ["KIMI_API_KEY"] });
});
it("marks Pi and Copilot qualified with their provider credential bindings", () => {
expect(PAPERCLIP_RUNNER_ACPX_PROFILES.find(profile => profile.value === "pi"))
.toMatchObject({ qualified: true, credentialEnvironment: expect.arrayContaining(["OPENROUTER_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY"]) });
@@ -253,6 +253,7 @@ export const PAPERCLIP_RUNNER_ACPX_PROFILES = Object.freeze([
{ value: "cursor", label: "Cursor", qualified: true, credentialEnvironment: ["CURSOR_API_KEY", "CURSOR_AUTH_TOKEN"] },
{ value: "copilot", label: "GitHub Copilot", qualified: true, credentialEnvironment: ["COPILOT_GITHUB_TOKEN"] },
{ value: "pi", label: "Pi", qualified: true, credentialEnvironment: ["OPENROUTER_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY"] },
{ value: "kimi", label: "Kimi Code", qualified: true, credentialEnvironment: ["KIMI_API_KEY"] },
{ value: "antigravity", label: "Google Antigravity", qualified: false, credentialEnvironment: ["GEMINI_API_KEY"] },
] as const);
@@ -50,6 +50,21 @@ it("keeps Pi draft models empty and preserves saved native Pi settings", () => {
expect(normalizePaperclipRunnerAdapterConfig("paperclip_runner", saved)).toMatchObject(saved);
});
it.each([undefined, "", "kimi-for-coding"])("keeps Kimi draft model selection explicit: %s", model => {
const config = { provider: "acpx", acpxAgent: "kimi", ...(model === undefined ? {} : { model }),
acpxPermissionMode: "approve-reads", lifecycleMode: "warm" };
const normalized = normalizePaperclipRunnerAdapterConfig("paperclip_runner", config);
expect(normalized).toMatchObject(config);
expect(normalized.model).toBe(model);
});
it("keeps Claude draft defaults and explicit model selection", () => {
expect(normalizePaperclipRunnerAdapterConfig("paperclip_runner", { provider: "acpx", acpxAgent: "claude" }))
.toMatchObject({ model: "claude-opus-5-5" });
expect(normalizePaperclipRunnerAdapterConfig("paperclip_runner", { provider: "acpx", acpxAgent: "claude", model: "claude-sonnet-5" }))
.toMatchObject({ model: "claude-sonnet-5" });
});
it("reserves identity credentials even for mixed-case inherited or configured keys", () => {
const keys = ["PAPERCLIP_AGENT_KEY_ID", "Paperclip_Agent_Public_Key", "paperclip_agent_private_key"];
const inherited = Object.fromEntries(keys.map(key => [key, "host-override"]));
+3 -1
View File
@@ -4299,7 +4299,9 @@ export function normalizePaperclipRunnerAdapterConfig(
}
if (next.provider === "acpx") {
next.acpxAgent ??= "claude";
if (next.acpxAgent !== "pi") next.model = resolvePaperclipRunnerModel("acpx", config.model);
if (next.acpxAgent !== "pi" && next.acpxAgent !== "kimi") {
next.model = resolvePaperclipRunnerModel("acpx", config.model);
}
}
return normalizePaperclipOperationalSkillPreference(adapterType, next);
}
@@ -183,6 +183,86 @@ describe("workspace restore merge", () => {
}
});
describe("entries removed by a concurrent writer during capture", () => {
async function captureWhileRemoving(
removeAt: "before-lstat" | "after-lstat",
setup: (rootDir: string) => Promise<string>,
) {
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-restore-merge-"));
cleanupDirs.push(rootDir);
await writeFile(path.join(rootDir, "kept.txt"), "kept");
const vanishing = await setup(rootDir);
const lstatSpy = vi.spyOn(fsPromises, "lstat").mockImplementation(async (target, ...rest) => {
if (target !== vanishing) return lstat(target, ...rest);
if (removeAt === "before-lstat") await rm(vanishing, { recursive: true, force: true });
const stats = await lstat(target, ...rest);
await rm(vanishing, { recursive: true, force: true });
return stats;
});
try {
return await captureDirectorySnapshot(rootDir, { exclude: [] });
} finally { lstatSpy.mockRestore(); }
}
it("omits a listed file that is renamed away before it is read", async () => {
const snapshot = await captureWhileRemoving("before-lstat", async (rootDir) => {
await mkdir(path.join(rootDir, "tests", "runner-e2e"), { recursive: true });
const temporary = path.join(rootDir, "tests", "runner-e2e", ".paperclip-merge-d3bcd290-dd7e-47bf-b2cd-7d2eb1d2b7f2");
await writeFile(temporary, "in flight");
return temporary;
});
expect(snapshot.entries.get("kept.txt")).toMatchObject({ kind: "file" });
expect(snapshot.entries.get("tests/runner-e2e")).toEqual({ kind: "dir" });
expect(snapshot.entries.has("tests/runner-e2e/.paperclip-merge-d3bcd290-dd7e-47bf-b2cd-7d2eb1d2b7f2")).toBe(false);
});
it("omits a file that is removed before its contents are hashed", async () => {
const snapshot = await captureWhileRemoving("after-lstat", async (rootDir) => {
const file = path.join(rootDir, "removed.txt");
await writeFile(file, "removed");
return file;
});
expect(snapshot.entries.get("kept.txt")).toMatchObject({ kind: "file" });
expect(snapshot.entries.has("removed.txt")).toBe(false);
});
it("omits the children of a directory that is removed before it is opened", async () => {
const snapshot = await captureWhileRemoving("after-lstat", async (rootDir) => {
const dir = path.join(rootDir, "removed");
await mkdir(dir);
await writeFile(path.join(dir, "child.txt"), "child");
return dir;
});
expect(snapshot.entries.get("kept.txt")).toMatchObject({ kind: "file" });
expect(snapshot.entries.has("removed/child.txt")).toBe(false);
});
it("still fails when the capture root itself is missing", async () => {
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-restore-merge-"));
cleanupDirs.push(rootDir);
await expect(captureDirectorySnapshot(path.join(rootDir, "missing"), { exclude: [] }))
.rejects.toMatchObject({ code: "ENOENT" });
});
it("still fails on errors other than a vanished entry", async () => {
const rootDir = await mkdtemp(path.join(os.tmpdir(), "paperclip-restore-merge-"));
cleanupDirs.push(rootDir);
const unreadable = path.join(rootDir, "unreadable.txt");
await writeFile(unreadable, "secret");
const lstatSpy = vi.spyOn(fsPromises, "lstat").mockImplementation(async (target, ...rest) => {
if (target === unreadable) throw Object.assign(new Error("EACCES: permission denied"), { code: "EACCES" });
return lstat(target, ...rest);
});
try {
await expect(captureDirectorySnapshot(rootDir, { exclude: [] })).rejects.toMatchObject({ code: "EACCES" });
} finally { lstatSpy.mockRestore(); }
});
});
describe("classifyWorkspaceRestoreFailure", () => {
it.each([
"Daytona syncOut refusing tarball with an unparseable entry listing: private listing",
@@ -144,15 +144,21 @@ async function hashFile(filePath: string): Promise<string> {
});
}
const isVanishedEntry = (error: unknown) => (error as NodeJS.ErrnoException | null)?.code === "ENOENT";
async function* walkDirectory(
root: string, exclude: readonly string[], ignored: ReturnType<typeof workspacePathMatcher>, relative = "",
): AsyncGenerator<[string, SnapshotEntry]> {
const readEntry = async (nextRelative: string): Promise<[string, SnapshotEntry | null]> => {
const fullPath = path.join(root, nextRelative);
const stats = await fs.lstat(fullPath);
if (stats.isDirectory()) return [nextRelative, { kind: "dir" }];
if (stats.isSymbolicLink()) return [nextRelative, { kind: "symlink", target: await fs.readlink(fullPath) }];
if (stats.isFile()) return [nextRelative, { kind: "file", mode: stats.mode, hash: await hashFile(fullPath) }];
try {
const stats = await fs.lstat(fullPath);
if (stats.isDirectory()) return [nextRelative, { kind: "dir" }];
if (stats.isSymbolicLink()) return [nextRelative, { kind: "symlink", target: await fs.readlink(fullPath) }];
if (stats.isFile()) return [nextRelative, { kind: "file", mode: stats.mode, hash: await hashFile(fullPath) }];
} catch (error) {
if (!isVanishedEntry(error)) throw error;
}
return [nextRelative, null];
};
const visitBatch = async function* (paths: string[]): AsyncGenerator<[string, SnapshotEntry]> {
@@ -167,8 +173,13 @@ async function* walkDirectory(
}
};
const current = relative ? path.join(root, relative) : root;
const dir = await fs.opendir(current).catch((error: unknown) => {
if (relative && isVanishedEntry(error)) return null;
throw error;
});
if (!dir) return;
let batch: string[] = [];
for await (const entry of await fs.opendir(current)) {
for await (const entry of dir) {
const nextRelative = relative ? path.posix.join(relative, entry.name) : entry.name;
if (shouldExcludePath(nextRelative, exclude) || ignored.matches(nextRelative)) continue;
batch.push(nextRelative);
@@ -36,6 +36,14 @@ function makeValues(overrides: Partial<CreateConfigValues> = {}): CreateConfigVa
}
describe("buildCodexLocalConfig", () => {
it("round-trips qualified Kimi Code with its exact selected model", () => {
const first = buildPaperclipRunnerConfig(makeValues({ model: "explicit-kimi-model", adapterSchemaValues: { provider: "acpx", acpxAgent: "kimi" } }));
const reopened = buildPaperclipRunnerConfig(makeValues({ model: "", adapterSchemaValues: JSON.parse(JSON.stringify(first)) }));
expect(reopened).toMatchObject({ provider: "acpx", acpxAgent: "kimi", model: "explicit-kimi-model" });
expect(first).not.toHaveProperty("candidateHarnesses");
expect(() => buildPaperclipRunnerConfig(makeValues({ model: "", adapterSchemaValues: { provider: "acpx", acpxAgent: "kimi" } })))
.toThrow("kimi requires an explicit provider model");
});
it("refuses pending Antigravity selection before saving Runner config", () => {
expect(() => buildPaperclipRunnerConfig(makeValues({ model: "exact-antigravity-model",
adapterSchemaValues: { provider: "acpx", acpxAgent: "antigravity" } })))
@@ -131,7 +131,7 @@ export function buildPaperclipRunnerConfig(v: CreateConfigValues, context?: {
if (provider === "acpx" && acpxAgent === "copilot" && (!configuredModel && !schemaModel || ["auto", "default"].includes((configuredModel || schemaModel).toLowerCase()))) {
throw new Error("Select an available Copilot model before saving.");
}
if (provider === "acpx" && ["cursor", "pi"].includes(acpxAgent) && !configuredModel && !schemaModel) {
if (provider === "acpx" && ["cursor", "pi", "kimi"].includes(acpxAgent) && !configuredModel && !schemaModel) {
throw new Error(`${acpxAgent} requires an explicit provider model`);
}
const managedProfileId = typeof schemaValues.managedProfileId === "string"
@@ -0,0 +1,63 @@
import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";
import postgres from "postgres";
import { describe, expect, it } from "vitest";
import { applyPendingMigrations } from "./client.js";
import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./test-embedded-postgres.js";
const support = await getEmbeddedPostgresTestSupport();
const indexName = "issue_comments_agent_client_request_uq";
const migrationFile = "0419_slippery_impossible_man.sql";
(support.supported ? describe : describe.skip)("agent chat message migration", () => {
it("repairs an interrupted index build and backfills old chat owners only once", async () => {
const database = await startEmbeddedPostgresTestDatabase("agent-chat-message-migration-");
const sql = postgres(database.connectionString, { max: 1, onnotice: () => {} });
try {
const content = await readFile(new URL(`./migrations/${migrationFile}`, import.meta.url), "utf8");
const hash = createHash("sha256").update(content).digest("hex");
await sql`DELETE FROM drizzle.__drizzle_migrations WHERE hash=${hash}`;
await sql.unsafe(`ALTER TABLE issue_comments DROP CONSTRAINT ${indexName}`);
const companies = await sql`INSERT INTO companies (name,issue_prefix) VALUES ('Chat migration','CMI1'),('Other company','CMI2') RETURNING id`;
const companyId = companies[0]!.id;
const [agent] = await sql`INSERT INTO agents (company_id,name,adapter_type) VALUES (${companyId},'Kodi','process') RETURNING id`;
const chats = await sql`INSERT INTO issues (company_id,title,conversation_agent_id,conversation_user_id,conversation_state,assignee_agent_id)
VALUES (${companyId},'Missing read state',${agent!.id},'legacy-owner','waiting',${agent!.id}),
(${companyId},'Existing read state',${agent!.id},'existing-owner','waiting',${agent!.id}) RETURNING id,conversation_user_id`;
for (const chat of chats) {
await sql`INSERT INTO issue_comments (company_id,issue_id,author_agent_id,body,created_at)
VALUES (${companyId},${chat.id},${agent!.id},'Existing message','2026-01-02T00:00:00Z')`;
}
await sql`INSERT INTO issue_read_states (company_id,issue_id,user_id,last_read_at)
VALUES (${companyId},${chats[1]!.id},'existing-owner','2026-01-01T00:00:00Z')`;
await expect(sql.unsafe(`CREATE UNIQUE INDEX CONCURRENTLY ${indexName} ON companies ((true))`)).rejects.toThrow();
await Promise.all([applyPendingMigrations(database.connectionString), applyPendingMigrations(database.connectionString)]);
const [index] = await sql`SELECT indisvalid,indisunique FROM pg_index WHERE indexrelid=${indexName}::regclass`;
expect(index).toEqual({ indisvalid: true, indisunique: true });
const [constraint] = await sql`SELECT count(*)::int count FROM pg_constraint WHERE conname=${indexName} AND conrelid='issue_comments'::regclass`;
expect(constraint!.count).toBe(1);
const readStates = await sql`SELECT user_id,last_read_at FROM issue_read_states WHERE company_id=${companyId} ORDER BY user_id`;
expect(readStates.map(row => [row.user_id, row.last_read_at.toISOString()])).toEqual([
["existing-owner", "2026-01-01T00:00:00.000Z"],
["legacy-owner", "2026-01-02T00:00:00.000Z"],
]);
const [newChat] = await sql`INSERT INTO issues (company_id,title,conversation_agent_id,conversation_user_id,conversation_state,assignee_agent_id)
VALUES (${companyId},'New unread chat',${agent!.id},'new-owner','waiting',${agent!.id}) RETURNING id`;
await sql`INSERT INTO issue_comments (company_id,issue_id,author_agent_id,body)
VALUES (${companyId},${newChat!.id},${agent!.id},'New digest')`;
for (let replay = 0; replay < 2; replay++) {
for (const statement of content.split("--> statement-breakpoint").filter(statement => statement.trim())) {
await sql.unsafe(statement);
}
}
expect(await sql`SELECT id FROM issue_read_states WHERE issue_id=${newChat!.id}`).toEqual([]);
const afterReplay = await sql`SELECT user_id,last_read_at FROM issue_read_states WHERE company_id=${companyId} ORDER BY user_id`;
expect(afterReplay).toEqual(readStates);
const [history] = await sql`SELECT count(*)::int count FROM drizzle.__drizzle_migrations WHERE hash=${hash}`;
expect(history!.count).toBe(1);
} finally {
await sql.end();
await database.cleanup();
}
}, 120_000);
});
+15 -1
View File
@@ -1,5 +1,5 @@
import { afterEach, expect, it } from "vitest";
import { checkpointDb, closeCheckpointDb, closeRegisteredClients, createDb, type Db } from "./client.js";
import { checkpointDb, closeCheckpointDb, closeRegisteredClients, createDb, livenessDb, type Db } from "./client.js";
// postgres.js pools are lazy: these tests exercise actual client lifetimes but
// never connect to a database or depend on embedded Postgres availability.
@@ -17,6 +17,7 @@ it("fences checkpoints when shutdown precedes the first pool request", async ()
it("keeps an already-created checkpoint pool ended after shutdown", async () => {
const db = createDb(url);
const pool = checkpointDb(db);
expect(pool.$client.options.max).toBe(3);
await closeCheckpointDb(db, 1);
expect(checkpointDb(db)).toBe(pool);
await expect(pool.$client`select 1`).rejects.toMatchObject({ code: "CONNECTION_ENDED" });
@@ -30,3 +31,16 @@ it("preserves other owner lifetimes and non-owning test-double behavior", async
await closeCheckpointDb(nonOwner, 1);
expect(checkpointDb(nonOwner)).toBe(nonOwner);
});
it("bounds one cached checkpoint pool per owner without changing request or renewal capacity", () => {
const first = createDb(url, { maxConnections: 10 });
const second = createDb(url, { maxConnections: 10 });
const pool = checkpointDb(first);
expect(pool).not.toBe(first);
expect(checkpointDb(first)).toBe(pool);
expect(checkpointDb(second)).not.toBe(pool);
expect(pool.$client.options.max).toBe(3);
expect(checkpointDb(second).$client.options.max).toBe(3);
expect(first.$client.options.max).toBe(10);
expect(livenessDb(first).$client.options.max).toBe(2);
});
+4 -2
View File
@@ -295,9 +295,10 @@ export async function closeLivenessDb(db: Db, timeoutSeconds: number): Promise<v
}
// Control checkpoints must not occupy the request or lease-renewal pools
// during an aligned burst. Share at most two lazy connections per owner.
// during an aligned burst. Share at most three lazy connections per owner.
const checkpointDbFactories = new WeakMap<object, () => Db>();
const checkpointDbs = new WeakMap<object, Db>();
const CHECKPOINT_POOL_MAX_CONNECTIONS = 3;
const closedCheckpointOwners = new WeakSet<object>();
/** The bounded controller checkpoint pool; transactions and test doubles keep
@@ -342,7 +343,7 @@ function createDbClient(url: string, resolved: DatabaseClientOptions, owner?: ob
...resolved, maxConnections: LIVENESS_POOL_MAX_CONNECTIONS, applicationName: "paperclip-controller-lease",
}, db));
checkpointDbFactories.set(db, () => createDbClient(url, {
...resolved, maxConnections: 2, applicationName: "paperclip-runner-checkpoint",
...resolved, maxConnections: CHECKPOINT_POOL_MAX_CONNECTIONS, applicationName: "paperclip-runner-checkpoint",
}, db));
return db;
}
@@ -564,6 +565,7 @@ const MIGRATIONS_WITH_TOP_LEVEL_STATEMENTS = new Set([
"0412_tearful_squadron_supreme.sql",
"0413_tired_kronos.sql",
"0414_closed_edwin_jarvis.sql",
"0419_slippery_impossible_man.sql",
"0384_funny_adam_warlock.sql",
@@ -0,0 +1,24 @@
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "issue_comments_agent_client_request_uq"
ON "issue_comments" ("issue_id", "author_agent_id", "client_request_id");
--> statement-breakpoint
DO $$ BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'issue_comments_agent_client_request_uq'
AND conrelid = 'issue_comments'::regclass
) THEN
INSERT INTO "issue_read_states" ("company_id", "issue_id", "user_id", "last_read_at")
SELECT i."company_id", i."id", i."conversation_user_id", max(c."created_at")
FROM "issues" i
JOIN "issue_comments" c ON c."company_id" = i."company_id" AND c."issue_id" = i."id"
WHERE i."conversation_agent_id" IS NOT NULL AND i."conversation_user_id" IS NOT NULL
AND c."deleted_at" IS NULL AND c."author_agent_id" IS NOT NULL
AND c."author_type" IS DISTINCT FROM 'system'
AND c."presentation" ->> 'kind' IS DISTINCT FROM 'system_notice'
AND c."origin" = 'comment'
GROUP BY i."company_id", i."id", i."conversation_user_id"
ON CONFLICT ("company_id", "issue_id", "user_id") DO NOTHING;
ALTER TABLE "issue_comments" ADD CONSTRAINT "issue_comments_agent_client_request_uq"
UNIQUE USING INDEX "issue_comments_agent_client_request_uq";
END IF;
END $$;
+10 -7
View File
@@ -1,5 +1,5 @@
{
"id": "cce00822-b7a4-4acd-9c59-0f6f5822c0cf",
"id": "52cf22f3-3bd9-4b0f-8d23-b422bb0d2540",
"prevId": "c777fa84-85a6-418f-bea1-0d60b56cf071",
"version": "7",
"dialect": "postgresql",
@@ -37105,6 +37105,15 @@
"client_request_id"
]
},
"issue_comments_agent_client_request_uq": {
"name": "issue_comments_agent_client_request_uq",
"nullsNotDistinct": false,
"columns": [
"issue_id",
"author_agent_id",
"client_request_id"
]
},
"issue_comments_company_id_uq": {
"name": "issue_comments_company_id_uq",
"nullsNotDistinct": false,
@@ -52770,12 +52779,6 @@
"primaryKey": false,
"notNull": false
},
"next_eval_reason": {
"name": "next_eval_reason",
"type": "text",
"primaryKey": false,
"notNull": false
},
"next_eval_at": {
"name": "next_eval_at",
"type": "timestamp with time zone",
@@ -1,6 +1,6 @@
{
"id": "dce41524-29a3-4552-ba5d-f0abefbf1ea1",
"prevId": "27c9008c-60e3-449b-9630-9e4977fad444",
"id": "8f75a1bb-32d9-4aed-9f93-134a6ae71f79",
"prevId": "52cf22f3-3bd9-4b0f-8d23-b422bb0d2540",
"version": "7",
"dialect": "postgresql",
"tables": {
@@ -19052,6 +19052,98 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.company_skill_mutation_receipts": {
"name": "company_skill_mutation_receipts",
"schema": "",
"columns": {
"company_id": {
"name": "company_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"key": {
"name": "key",
"type": "text",
"primaryKey": false,
"notNull": true
},
"fingerprint": {
"name": "fingerprint",
"type": "text",
"primaryKey": false,
"notNull": true
},
"skill_id": {
"name": "skill_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"version_id": {
"name": "version_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"response": {
"name": "response",
"type": "jsonb",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"company_skill_mutation_receipts_key_idx": {
"name": "company_skill_mutation_receipts_key_idx",
"columns": [
{
"expression": "company_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "key",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"company_skill_mutation_receipts_company_id_companies_id_fk": {
"name": "company_skill_mutation_receipts_company_id_companies_id_fk",
"tableFrom": "company_skill_mutation_receipts",
"tableTo": "companies",
"columnsFrom": [
"company_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.company_skill_repository_snapshots": {
"name": "company_skill_repository_snapshots",
"schema": "",
@@ -34070,7 +34162,7 @@
}
],
"isUnique": false,
"where": "\"heartbeat_runs\".\"runtime_mode\" = 'native' AND \"heartbeat_runs\".\"status\" = 'cancelled'\n AND \"heartbeat_runs\".\"runner_profile_json\"->'nativeWorkspaceSync'->>'state' = 'prepared'\n AND \"heartbeat_runs\".\"result_json\"->'nativeCancellation'->>'dispatchState' = 'acknowledged'\n AND (\"heartbeat_runs\".\"result_json\"->'workspaceRestoreRecovery'->>'schema' = 'paperclip.workspace-restore-recovery.v1'\n AND \"heartbeat_runs\".\"result_json\"->>'workspaceRestoreFailure' IN ('restore_permission_denied', 'restore_lock_timeout', 'restore_unsafe_archive', 'restore_failed')) IS NOT TRUE",
"where": "\"heartbeat_runs\".\"runtime_mode\" = 'native' AND \"heartbeat_runs\".\"status\" = 'cancelled'\n AND \"heartbeat_runs\".\"runner_profile_json\"->'nativeWorkspaceSync'->>'state' = 'prepared'\n AND \"heartbeat_runs\".\"result_json\"->'nativeCancellation'->>'dispatchState' = 'acknowledged'\n AND (\"heartbeat_runs\".\"result_json\"->'workspaceRestoreRecovery'->>'schema' = 'paperclip.workspace-restore-recovery.v1'\n AND \"heartbeat_runs\".\"result_json\"->>'workspaceRestoreFailure' IN ('restore_permission_denied', 'restore_lock_timeout', 'restore_unsafe_archive', 'restore_invalid_metadata', 'restore_failed')) IS NOT TRUE",
"concurrently": false,
"method": "btree",
"with": {}
@@ -37013,6 +37105,15 @@
"client_request_id"
]
},
"issue_comments_agent_client_request_uq": {
"name": "issue_comments_agent_client_request_uq",
"nullsNotDistinct": false,
"columns": [
"issue_id",
"author_agent_id",
"client_request_id"
]
},
"issue_comments_company_id_uq": {
"name": "issue_comments_company_id_uq",
"nullsNotDistinct": false,
@@ -51482,6 +51583,195 @@
"checkConstraints": {},
"isRLSEnabled": false
},
"public.sandbox_creation_operations": {
"name": "sandbox_creation_operations",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true
},
"company_id": {
"name": "company_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"environment_id": {
"name": "environment_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"original_environment_id": {
"name": "original_environment_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"run_id": {
"name": "run_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"request_key": {
"name": "request_key",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "'run'"
},
"issue_id": {
"name": "issue_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"execution_workspace_id": {
"name": "execution_workspace_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"provider": {
"name": "provider",
"type": "text",
"primaryKey": false,
"notNull": true
},
"plugin_id": {
"name": "plugin_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"driver_key": {
"name": "driver_key",
"type": "text",
"primaryKey": false,
"notNull": true
},
"state": {
"name": "state",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "'pending'"
},
"config": {
"name": "config",
"type": "jsonb",
"primaryKey": false,
"notNull": true
},
"metadata": {
"name": "metadata",
"type": "jsonb",
"primaryKey": false,
"notNull": true
},
"lease_id": {
"name": "lease_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"next_attempt_at": {
"name": "next_attempt_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"sandbox_creation_operations_pending_deadline_idx": {
"name": "sandbox_creation_operations_pending_deadline_idx",
"columns": [
{
"expression": "next_attempt_at",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"where": "\"sandbox_creation_operations\".\"state\" = 'pending'",
"concurrently": false,
"method": "btree",
"with": {}
},
"sandbox_creation_operations_owner_idx": {
"name": "sandbox_creation_operations_owner_idx",
"columns": [
{
"expression": "company_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "original_environment_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "run_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "request_key",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"sandbox_creation_operations_environment_id_environments_id_fk": {
"name": "sandbox_creation_operations_environment_id_environments_id_fk",
"tableFrom": "sandbox_creation_operations",
"tableTo": "environments",
"columnsFrom": [
"environment_id"
],
"columnsTo": [
"id"
],
"onDelete": "set null",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.secret_access_events": {
"name": "secret_access_events",
"schema": "",
@@ -52489,6 +52779,12 @@
"primaryKey": false,
"notNull": false
},
"next_eval_reason": {
"name": "next_eval_reason",
"type": "text",
"primaryKey": false,
"notNull": false
},
"next_eval_at": {
"name": "next_eval_at",
"type": "timestamp with time zone",
+9 -2
View File
@@ -2924,8 +2924,15 @@
{
"idx": 419,
"version": "7",
"when": 1793402408013,
"tag": "0419_moaning_vulture",
"when": 1793400383797,
"tag": "0419_slippery_impossible_man",
"breakpoints": true
},
{
"idx": 420,
"version": "7",
"when": 1793453735600,
"tag": "0420_broad_johnny_storm",
"breakpoints": true
}
]
+1
View File
@@ -49,6 +49,7 @@ export const issueComments = pgTable(
},
(table) => ({
clientRequestUq: unique("issue_comments_client_request_uq").on(table.issueId, table.authorUserId, table.clientRequestId),
agentClientRequestUq: unique("issue_comments_agent_client_request_uq").on(table.issueId, table.authorAgentId, table.clientRequestId),
companyIdUq: unique("issue_comments_company_id_uq").on(table.companyId, table.id),
issueIdx: index("issue_comments_issue_idx").on(table.issueId),
reviewResponseReceiptIdx: index("issue_comments_review_response_receipt_idx").on(
@@ -0,0 +1,111 @@
import { createHash, randomUUID } from "node:crypto";
import { readFile } from "node:fs/promises";
import postgres from "postgres";
import { describe, expect, it } from "vitest";
import { applyPendingMigrations, inspectMigrations } from "./client.js";
import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./test-embedded-postgres.js";
const adminUrl = process.env.PAPERCLIP_TEST_POSTGRES_ADMIN_URL;
const support = adminUrl ? { supported: true } : await getEmbeddedPostgresTestSupport();
const journal = JSON.parse(await readFile(new URL("./migrations/meta/_journal.json", import.meta.url), "utf8")) as {
entries: Array<{ idx: number; tag: string }>;
};
const master = journal.entries.find(entry => entry.tag === "0419_slippery_impossible_man")!;
const status = journal.entries.find(entry => entry.tag === "0420_broad_johnny_storm")!;
const statusSql = await readFile(new URL(`./migrations/${status.tag}.sql`, import.meta.url), "utf8");
const hash = async (tag: string) => createHash("sha256")
.update(await readFile(new URL(`./migrations/${tag}.sql`, import.meta.url), "utf8")).digest("hex");
async function database() {
if (!adminUrl) return startEmbeddedPostgresTestDatabase("status-reason-upgrade-");
const name = `status_reason_${randomUUID().replaceAll("-", "")}_test`;
const admin = postgres(adminUrl, { max: 1, onnotice: () => {} });
try { await admin.unsafe(`create database "${name}"`); }
finally { await admin.end(); }
const url = new URL(adminUrl); url.pathname = `/${name}`;
const cleanup = async () => {
const admin = postgres(adminUrl, { max: 1, onnotice: () => {} });
try { await admin.unsafe(`drop database "${name}"`); }
finally { await admin.end(); }
};
try { await applyPendingMigrations(url.toString()); }
catch (error) { await cleanup(); throw error; }
return { connectionString: url.toString(), cleanup };
}
async function seed(sql: postgres.Sql) {
const companyId = randomUUID(), agentId = randomUUID(), issueId = randomUUID(), userId = randomUUID();
const lastMessage = new Date("2026-10-20T12:00:00.000Z");
await sql`insert into companies (id, name, issue_prefix) values (${companyId}, 'Migration proof', ${`M${companyId.slice(0, 10)}`})`;
await sql`insert into agents (id, company_id, name, adapter_type) values (${agentId}, ${companyId}, 'Agent', 'process')`;
await sql`insert into "user" (id, name, email, email_verified, created_at, updated_at)
values (${userId}, 'Reader', ${`${userId}@example.test`}, true, now(), now())`;
await sql`insert into issues (id, company_id, title, status, assignee_agent_id,
conversation_agent_id, conversation_user_id, conversation_state)
values (${issueId}, ${companyId}, 'Conversation', 'in_review', ${agentId}, ${agentId}, ${userId}, 'waiting')`;
await sql`insert into issue_comments (company_id, issue_id, author_agent_id, author_type, body, origin, client_request_id, created_at)
values (${companyId}, ${issueId}, ${agentId}, 'agent', 'Preserved reply', 'comment', 'retry-key', ${lastMessage})`;
await sql`insert into issue_comments (company_id, issue_id, author_agent_id, author_type, body, origin, created_at)
values (${companyId}, ${issueId}, ${agentId}, 'system', 'Excluded system notice', 'comment', ${new Date(lastMessage.getTime() + 1000)})`;
const cardId = randomUUID(), savedDeadline = new Date("2026-10-21T12:00:00.000Z");
await sql`insert into status_cards (id, company_id, interest_prompt, refresh_policy, next_eval_at, pending_change_hash)
values (${cardId}, ${companyId}, 'Watch preserved work', '{"mode":"manual"}'::jsonb, ${savedDeadline}, 'preserved-change')`;
return { companyId, agentId, issueId, userId, lastMessage, cardId, savedDeadline };
}
async function assertFinal(sql: postgres.Sql, row: Awaited<ReturnType<typeof seed>>, expectedRead: Date) {
const [column] = await sql`select data_type, is_nullable from information_schema.columns
where table_schema = 'public' and table_name = 'status_cards' and column_name = 'next_eval_reason'`;
expect(column).toMatchObject({ data_type: "text", is_nullable: "YES" });
const [constraint] = await sql`select i.indisvalid, i.indisunique from pg_constraint c
join pg_index i on i.indexrelid = c.conindid
where c.conrelid = 'issue_comments'::regclass and c.conname = 'issue_comments_agent_client_request_uq'`;
expect(constraint).toMatchObject({ indisvalid: true, indisunique: true });
const [read] = await sql`select last_read_at from issue_read_states
where company_id = ${row.companyId} and issue_id = ${row.issueId} and user_id = ${row.userId}`;
expect(read.last_read_at).toEqual(expectedRead);
const [card] = await sql`select next_eval_at, next_eval_reason, pending_change_hash from status_cards where id = ${row.cardId}`;
expect(card).toMatchObject({ next_eval_at: row.savedDeadline, next_eval_reason: null, pending_change_hash: "preserved-change" });
await sql`update status_cards set next_eval_reason = 'retry' where id = ${row.cardId}`;
expect((await sql`select next_eval_at, next_eval_reason from status_cards where id = ${row.cardId}`)[0])
.toMatchObject({ next_eval_at: row.savedDeadline, next_eval_reason: "retry" });
await expect(sql`insert into issue_comments (company_id, issue_id, author_agent_id, body, client_request_id)
values (${row.companyId}, ${row.issueId}, ${row.agentId}, 'Duplicate', 'retry-key')`)
.rejects.toMatchObject({ code: "23505", constraint_name: "issue_comments_agent_client_request_uq" });
}
describe.skipIf(!support.supported)("status-card reason migration after the published comment migration", () => {
it.each(["0418", "master0419"] as const)("upgrades from %s without dropping comment uniqueness or read history", async previous => {
expect(master.idx).toBe(419);
expect(status.idx).toBeGreaterThan(master.idx);
expect(statusSql.trim()).toBe('ALTER TABLE "status_cards" ADD COLUMN "next_eval_reason" text;');
const db = await database();
const sql = postgres(db.connectionString, { max: 1, onnotice: () => {} });
try {
// Reconstruct exactly the two migrations' preimages; all older schema and
// receipts stay intact, as in the repository's other replay regressions.
await sql`alter table status_cards drop column next_eval_reason`;
await sql`delete from drizzle.__drizzle_migrations where hash = ${await hash(status.tag)}`;
if (previous === "0418") {
await sql`alter table issue_comments drop constraint issue_comments_agent_client_request_uq`;
await sql`delete from drizzle.__drizzle_migrations where hash = ${await hash(master.tag)}`;
}
const row = await seed(sql);
const preservedRead = new Date("2026-10-19T12:00:00.000Z");
if (previous === "master0419") await sql`insert into issue_read_states (company_id, issue_id, user_id, last_read_at)
values (${row.companyId}, ${row.issueId}, ${row.userId}, ${preservedRead})`;
expect(await inspectMigrations(db.connectionString)).toMatchObject({
status: "needsMigrations",
pendingMigrations: previous === "0418" ? [`${master.tag}.sql`, `${status.tag}.sql`] : [`${status.tag}.sql`],
});
await applyPendingMigrations(db.connectionString);
expect(await inspectMigrations(db.connectionString)).toMatchObject({ status: "upToDate" });
await assertFinal(sql, row, previous === "0418" ? row.lastMessage : preservedRead);
const receipts = await sql`select hash from drizzle.__drizzle_migrations order by id`;
const hashes = receipts.map(receipt => receipt.hash);
expect(hashes.indexOf(await hash(master.tag))).toBeLessThan(hashes.indexOf(await hash(status.tag)));
await applyPendingMigrations(db.connectionString);
expect(await sql`select hash from drizzle.__drizzle_migrations order by id`).toEqual(receipts);
} finally { await sql.end(); await db.cleanup(); }
}, 90_000);
});
+41 -1
View File
@@ -311,7 +311,8 @@ installation journey or a paid provider task on ARM64.
Automatic creation selects legacy execution when a Linux host has musl, unknown
libc, or a measured GNU libc version below the baseline. Existing agents retain
their stored runner. Windows, 32-bit ARM, and Alpine/musl native qualification
remain deferred.
remain deferred. The `Runner Windows` workflow builds and tests the Rust
workspace on Windows x64. It does not qualify Windows agent runs.
Native release jobs cache Cargo dependencies per platform and OS baseline,
including compiler, lockfile, and external generated-contract inputs. Workspace
@@ -484,6 +485,12 @@ stable free-text option IDs. Legacy callers remain supported. Calls that supply
both forms must describe the same complete form; partial forms remain invalid.
The native recovery bridge uses the same projection for answer delivery.
OpenCode 1.18.34 native questions allow a written answer by default when `custom`
is omitted. The driver preserves that default and explicit `custom:false`; the
native flag takes precedence over the legacy `allowCustom` alias. Choice options
remain intact. Questions without options remain text-only, without a separate
custom-answer field. See the [pinned native question contract](https://github.com/anomalyco/opencode/blob/v1.18.34/packages/schema/src/v1/question.ts).
The server validates canonical answer constraints before persistence. Regex
matching runs in isolated workers with a one-second deadline and at most four
active workers. A timeout or capacity error leaves the question pending. The
@@ -980,6 +987,39 @@ while daemon work might still be running. The socket and its parent must be priv
canonical paths owned by the same account. Local stdio assignment requires Unix;
other platforms retain the HTTP path.
## Kimi Code
Kimi Code uses the pinned `@moonshot-ai/kimi-code@2.3.3` distribution through
ACPX through the qualified native profile. Ordinary selection and Runtime Test
require no candidate grant.
The installation targets are macOS ARM64 and Linux x64. Platform startup,
authenticated driver checks, and product acceptance are separate evidence levels.
Run `paperclipai runtime setup kimi` on the execution host to install its complete
verified runtime. Setup downloads public pinned dependencies without model calls
or provider credentials. An invalid existing installation fails without ambient
Kimi fallback. Bind a company secret as `KIMI_API_KEY` and select an exact model.
This release uses the global Code endpoint at `https://api.kimi.ai/coding/v1`.
Model suggestions are empty; a compatible typed model is decided by the provider.
The account needs Kimi Code entitlement. Runtime Test verifies installation and
queries the authenticated Code model catalog for the exact selected model. It
sends no model prompt; an actual task verifies inference and tool execution.
The qualified native profile uses ordinary setup, hiring, metadata Test and task
startup on these two supported targets without operator admission flags. Protected
candidate startup records remain reserved for future pending profiles; saved agent
configuration and public requests cannot supply that authority.
Existing legacy Kimi agents retain their runtime and history. Managed Kimi connections,
ambient login borrowing, other regions/platforms, steering, native child work,
and per-run token/dollar accounting are outside this release. Read-only Kimi work
uses scoped Paperclip tools; native filesystem and shell tools are denied.
The provider-owned patch binds native instructions, preserves permission arguments,
and removes provider auth from tool subprocesses. See the
[protocol dossier](src/providers/kimi/README.md) and
[release ledger](../../doc/plans/2026-10-28-kimi-code-runner-integration.md) for
current evidence and limits.
## ACPX controller replacement regression tests
`src/control-plane/acpx-controller-recovery.test.ts` runs the production Rust
@@ -67,6 +67,15 @@
"agentRuntimeVersion": "0.160.0",
"commandDigest": "sha256:443491f4e814501a5dc47347e4510e84faeec9c9382e2d4e53bfe69386d5aabc",
"requiresProviderPolicy": false
},
"kimi": {
"agentProfileVersion": 17,
"agentServerPackage": "@moonshot-ai/kimi-code",
"agentServerVersion": "2.3.3",
"agentRuntimePackage": null,
"agentRuntimeVersion": null,
"commandDigest": "sha256:6ee5fb7ac18c1361ac63effe9117d08ed129add8349756a140e73e0c86ba5da7",
"requiresProviderPolicy": true
}
}
}
@@ -80,7 +80,7 @@ Each provider declares how in `ACPX_RELEASE_ATTESTATIONS`
| --- | --- | --- |
| `installed-command` | Claude, Codex | the upstream package's launch command, hashed at installation |
| `launcher` | Grok | the SHA-256 of the launcher shipped verbatim |
| `closure` | Copilot, Pi | upstream versions and the complete platform closure pins |
| `closure` | Copilot, Pi, Kimi | upstream versions and the complete platform closure pins |
| `declaration` | Cursor, Antigravity | a checked-in release declaration, as canonical JSON |
`generate:acpx-profiles` writes the computed digests and `check:acpx-profiles`
@@ -127,6 +127,12 @@ An administrator-owned read-only Runner can therefore use a user-writable cache,
but this provides weaker protection against that same user than hashing every
executable on each admission. Removing the cache restores full hashing.
Kimi retains released revision 17, which separates ordinary admission from
pending revision 16. Its upstream-only release attestation replaces the earlier
source-bound digest without another revision bump. Historical candidate and
released records remain unchanged and readable; exact current digest admission
refuses their old launch identities.
## External controller checkpoints
The optional controller persistence callback stores recovery control state, not
@@ -1 +1 @@
[{"annotations":{"exposure":"always","operationId":"get_task_context","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read the active task and actor, including the exact approved Markdown revision when this issue has an accepted plan.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"get_task_context","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"get_task_history","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read bounded comments on the active task.","inputSchema":{"additionalProperties":false,"properties":{"limit":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},"required":[],"type":"object"},"name":"get_task_history","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"list_documents","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"List revisioned documents on the active task.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"list_documents","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"read_document","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read the current revision of one active-task document.","inputSchema":{"additionalProperties":false,"properties":{"key":{"description":"Stable issue-document key.","maxLength":120,"minLength":1,"type":"string"}},"required":["key"],"type":"object"},"name":"read_document","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"list_document_revisions","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read bounded revision history for one active-task document.","inputSchema":{"additionalProperties":false,"properties":{"key":{"description":"Stable issue-document key.","maxLength":120,"minLength":1,"type":"string"},"limit":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},"required":["key"],"type":"object"},"name":"list_document_revisions","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"report_progress","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Append a durable progress comment to the active task.","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Multiline progress update.","maxLength":20000,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","body"],"type":"object"},"name":"report_progress","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"answer_status_question","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Append the answer to a status-only wake without changing task disposition.","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Concise status answer.","maxLength":20000,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","body"],"type":"object"},"name":"answer_status_question","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"write_document","requiredClaimLine truncated
[{"annotations":{"exposure":"always","operationId":"get_task_context","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read the active task and actor, including the exact approved Markdown revision when this issue has an accepted plan.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"get_task_context","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"get_task_history","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read bounded comments on the active task.","inputSchema":{"additionalProperties":false,"properties":{"limit":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},"required":[],"type":"object"},"name":"get_task_history","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"list_documents","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"List revisioned documents on the active task.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"list_documents","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"read_document","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read the current revision of one active-task document.","inputSchema":{"additionalProperties":false,"properties":{"key":{"description":"Stable issue-document key.","maxLength":120,"minLength":1,"type":"string"}},"required":["key"],"type":"object"},"name":"read_document","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"list_document_revisions","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read bounded revision history for one active-task document.","inputSchema":{"additionalProperties":false,"properties":{"key":{"description":"Stable issue-document key.","maxLength":120,"minLength":1,"type":"string"},"limit":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},"required":["key"],"type":"object"},"name":"list_document_revisions","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"report_progress","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Append a durable progress comment to the active task.","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Multiline progress update.","maxLength":20000,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","body"],"type":"object"},"name":"report_progress","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"answer_status_question","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Append the answer to a status-only wake without changing task disposition.","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Concise status answer.","maxLength":20000,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","body"],"type":"object"},"name":"answer_status_question","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"write_document","requiredClaimLine truncated
@@ -533,6 +533,45 @@
"title": "Report durable progress",
"version": 1
},
{
"allowedModes": [
"standard"
],
"description": "Send a Markdown message directly to your own persistent chat with this task's responsible human. The destination is automatic. Use for requested digests and proactive task updates; report_progress writes to this task and comment_on_task writes to a chosen task. This sends no message to other agents, starts no chat run, and changes no task status. Reuse idempotencyKey on retries.",
"effect": "write",
"inputSchema": {
"additionalProperties": false,
"properties": {
"body": {
"description": "The message the human should see.",
"maxLength": 20000,
"minLength": 1,
"type": "string"
},
"idempotencyKey": {
"description": "Stable key for this message within the source task, including across run retries.",
"maxLength": 240,
"minLength": 1,
"type": "string"
}
},
"required": [
"body",
"idempotencyKey"
],
"type": "object"
},
"operationId": "send_chat_message",
"outputSchema": {
"additionalProperties": true,
"type": "object"
},
"placement": "always",
"requiredClaims": [],
"schema": "paperclip.semantic-action.v1",
"title": "Send message to your chat",
"version": 1
},
{
"allowedModes": [
"standard",
+1 -1
View File
@@ -85,7 +85,7 @@
"typecheck:browser": "tsc -p tsconfig.browser.json --noEmit",
"test": "pnpm run test:typescript && pnpm run test:rust",
"test:typescript": "pnpm run test:typescript:prep && vitest run",
"test:typescript:prep": "pnpm run ensure:eval-build-deps && pnpm run build:rust && node ../../scripts/ci-profile/node-test.mjs test/protocol-contract.test.mjs test/acpx-sidecar-contract.test.mjs test/acpx-codex-package-contract.test.mjs test/acpx-model-selection-package-contract.test.mjs test/acpx-message-boundaries-package-contract.test.mjs test/acpx-rich-extensions-package-contract.test.mjs test/acpx-pi-receipt-package-contract.test.mjs test/acpx-response-delivery-package-contract.test.mjs scripts/candidate-provider-pack.test.mjs scripts/materialize-pi-distribution.test.mjs scripts/cursor-provisioner-layout.test.mjs scripts/release-provider-pack.test.mjs scripts/acpx-release-attestations.test.mjs scripts/provider-pack-runtime-tree.test.mjs scripts/aws-agentcore-provisioning.test.mjs scripts/build-verified-provider-entrypoints.test.mjs scripts/local-provider-smoke-environment.test.mjs scripts/materialize-opencode-binary.test.mjs scripts/installed-package-pack.test.mjs scripts/runner-runtime-dependency-roots.test.mjs scripts/runner-package-executable.test.mjs scripts/stage-runner-binary.test.mjs scripts/build-inputs.test.mjs scripts/contracts/contract-fingerprints.test.mjs scripts/provider-pack-executable-shims.test.mjs scripts/controlled-evals/controller.test.mjs scripts/controlled-evals/portable.test.mjs && node --test scripts/build-copilot-distribution.test.mjs scripts/copilot-inner-distribution.test.mjs scripts/copilot-smoke-resources.test.mjs scripts/discover-copilot-acp.test.mjs scripts/materialize-copilot-binary.test.mjs scripts/install-copilot-assets.test.mjs scripts/probe-copilot-acp.test.mjs scripts/qualify-copilot-acp.test.mjs test/acpx-message-boundaries-package-contract.test.mjs",
"test:typescript:prep": "pnpm run ensure:eval-build-deps && pnpm run build:rust && node ../../scripts/ci-profile/node-test.mjs test/protocol-contract.test.mjs test/acpx-sidecar-contract.test.mjs test/acpx-codex-package-contract.test.mjs test/acpx-model-selection-package-contract.test.mjs test/acpx-message-boundaries-package-contract.test.mjs test/acpx-rich-extensions-package-contract.test.mjs test/acpx-pi-receipt-package-contract.test.mjs test/acpx-response-delivery-package-contract.test.mjs scripts/candidate-provider-pack.test.mjs scripts/materialize-pi-distribution.test.mjs scripts/cursor-provisioner-layout.test.mjs scripts/release-provider-pack.test.mjs scripts/acpx-release-attestations.test.mjs scripts/provider-pack-runtime-tree.test.mjs scripts/aws-agentcore-provisioning.test.mjs scripts/build-verified-provider-entrypoints.test.mjs scripts/local-provider-smoke-environment.test.mjs scripts/materialize-opencode-binary.test.mjs scripts/materialize-kimi-distribution.test.mjs scripts/provision-kimi-package.test.mjs scripts/kimi-metadata-probe.test.mjs scripts/patch-kimi-runtime.test.mjs test/kimi-installed-runtime.test.mjs scripts/installed-package-pack.test.mjs scripts/runner-runtime-dependency-roots.test.mjs scripts/runner-package-executable.test.mjs scripts/stage-runner-binary.test.mjs scripts/build-inputs.test.mjs scripts/contracts/contract-fingerprints.test.mjs scripts/provider-pack-executable-shims.test.mjs scripts/controlled-evals/controller.test.mjs scripts/controlled-evals/portable.test.mjs && node --test scripts/build-copilot-distribution.test.mjs scripts/copilot-inner-distribution.test.mjs scripts/copilot-smoke-resources.test.mjs scripts/discover-copilot-acp.test.mjs scripts/materialize-copilot-binary.test.mjs scripts/install-copilot-assets.test.mjs scripts/probe-copilot-acp.test.mjs scripts/qualify-copilot-acp.test.mjs test/acpx-message-boundaries-package-contract.test.mjs",
"test:typescript:vitest": "node ./scripts/run-pr-vitest-lane.mjs",
"test:rust": "cargo test --release --manifest-path runner/Cargo.toml --locked --workspace",
"test:codex": "cargo test --manifest-path runner/Cargo.toml --locked -p paperclip-runner-core --test codex_provider",
@@ -24,7 +24,7 @@
"prpVersion": 1,
"nativeExecutionVersion": 1,
"catalogVersion": 1,
"catalogSha256": "sha256:015531b07c9906c6edfa6029890cc7b2c69435c1c0c37432ffe21459e6f15c43",
"catalogSha256": "sha256:bb6aec4a12397e82d4f881fbce993e796cb8e8cc6852b29b308fc01ade7ef3d2",
"driverContractVersion": 1,
"driverKind": "paperclip-deterministic",
"driverVersion": "1.0.0"
@@ -180,7 +180,7 @@
},
{
"path": "fixtures/evals/native-execution-seeded.json",
"sha256": "0b878d30ad4b5514a14f7387c3792662cbfeebc7149188ffe304197e41c20e40",
"sha256": "810659b1b73ed62e70598bee5cc693d5632471c0f457788167f9b8eecb0d0022",
"expectation": "accept",
"compatibilityCase": "canonical"
},
@@ -3,7 +3,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::generated_acpx_profiles::acpx_release_profile;
use crate::generated_acpx_profiles::{acpx_release_profile, kimi_native_closure};
use crate::local_runner::LocalRunnerError;
use crate::stable_identity::{is_stable_id, DURABLE_STABLE_ID_CHARS, SHORT_STABLE_ID_CHARS};
@@ -63,15 +63,41 @@ impl AcpxCandidateAdmission {
}
fn validate_at(&self, now: u64, os: &str, architecture: &str) -> Result<(), LocalRunnerError> {
let profile = acpx_release_profile("antigravity").ok_or_else(invalid)?;
let manifest: Value = serde_json::from_str(DISTRIBUTIONS).map_err(|_| invalid())?;
let closure = manifest
.pointer("/platforms/darwin-arm64/closureSha256")
.and_then(Value::as_str)
.ok_or_else(invalid)?;
let profile = acpx_release_profile(&self.agent).ok_or_else(invalid)?;
let (closure, host_matches) = match self.agent.as_str() {
"antigravity" => {
let manifest: Value = serde_json::from_str(DISTRIBUTIONS).map_err(|_| invalid())?;
let closure = manifest
.pointer("/platforms/darwin-arm64/closureSha256")
.and_then(Value::as_str)
.ok_or_else(invalid)?
.to_owned();
(
closure,
self.platform == "darwin"
&& self.architecture == "arm64"
&& os == "macos"
&& architecture == "aarch64",
)
}
"kimi" if profile.qualification_pending => {
let closure =
kimi_native_closure(&self.platform, &self.architecture).ok_or_else(invalid)?;
let host_matches = matches!(
(
self.platform.as_str(),
self.architecture.as_str(),
os,
architecture
),
("darwin", "arm64", "macos", "aarch64") | ("linux", "x64", "linux", "x86_64")
);
(closure.to_owned(), host_matches)
}
_ => return Err(invalid()),
};
if self.schema != SCHEMA
|| !is_stable_id(&self.company_id, DURABLE_STABLE_ID_CHARS)
|| self.agent != "antigravity"
|| self.model.trim().is_empty()
|| self.model != self.model.trim()
|| self.model.len() > 512
@@ -79,10 +105,7 @@ impl AcpxCandidateAdmission {
|| matches!(self.model.to_ascii_lowercase().as_str(), "auto" | "default")
|| self.profile_digest != profile.command_digest
|| self.native_closure_digest != format!("sha256:{closure}")
|| self.platform != "darwin"
|| self.architecture != "arm64"
|| os != "macos"
|| architecture != "aarch64"
|| !host_matches
|| self.expires_at > MAX_JSON_SAFE_INTEGER
|| self.expires_at <= now
|| self.expires_at > now.saturating_add(MAX_LIFETIME_MS)
@@ -94,6 +117,27 @@ impl AcpxCandidateAdmission {
Ok(())
}
pub fn validate_next_run(
&self,
next: &Self,
agent: &str,
model: &str,
run_id: &str,
normalized_session_id: &str,
) -> Result<(), LocalRunnerError> {
if self.agent != "kimi" || self.run_id == next.run_id {
return Err(invalid());
}
self.validate()?;
next.validate_binding(agent, model, run_id, normalized_session_id)?;
let mut rebound = next.clone();
rebound.run_id = self.run_id.clone();
if &rebound != self {
return Err(invalid());
}
Ok(())
}
pub fn validate_binding(
&self,
agent: &str,
@@ -208,4 +252,169 @@ mod tests {
let error = AcpxCandidateAdmission::parse_json("private malformed input").unwrap_err();
assert!(!error.to_string().contains("private"));
}
fn kimi_admission(platform: &str, architecture: &str) -> AcpxCandidateAdmission {
AcpxCandidateAdmission {
agent: "kimi".to_owned(),
model: "kimi-for-coding".to_owned(),
profile_digest: acpx_release_profile("kimi")
.unwrap()
.command_digest
.to_owned(),
native_closure_digest: format!(
"sha256:{}",
kimi_native_closure(platform, architecture).unwrap()
),
platform: platform.to_owned(),
architecture: architecture.to_owned(),
..test_admission("kimi-run", "kimi-session")
}
}
#[test]
fn released_kimi_rejects_candidate_records_on_both_native_platforms() {
assert!(!acpx_release_profile("kimi").unwrap().qualification_pending);
for (platform, architecture, os, host_arch) in [
("darwin", "arm64", "macos", "aarch64"),
("linux", "x64", "linux", "x86_64"),
] {
let candidate = kimi_admission(platform, architecture);
let now = candidate.expires_at - 1;
assert!(candidate.validate_at(now, os, host_arch).is_err());
for (wrong_os, wrong_arch) in [
("windows", "x86_64"),
("linux", "aarch64"),
("macos", "x86_64"),
] {
assert!(candidate.validate_at(now, wrong_os, wrong_arch).is_err());
}
let (other_os, other_arch) = if os == "macos" {
("linux", "x86_64")
} else {
("macos", "aarch64")
};
assert!(candidate.validate_at(now, other_os, other_arch).is_err());
assert!(candidate
.validate_at(candidate.expires_at, os, host_arch)
.is_err());
assert!(candidate
.validate_at(candidate.expires_at - MAX_LIFETIME_MS - 1, os, host_arch)
.is_err());
for (field, value) in [
("companyId", json!("bad company")),
("agent", json!("copilot")),
("model", json!("default")),
("profileDigest", json!("sha256:wrong")),
("nativeClosureDigest", json!("sha256:wrong")),
("platform", json!("windows")),
("architecture", json!("s390x")),
("runId", json!("bad/run")),
("normalizedSessionId", json!("bad/session")),
] {
let mut changed = serde_json::to_value(&candidate).unwrap();
changed[field] = value;
let changed: AcpxCandidateAdmission = serde_json::from_value(changed).unwrap();
assert!(changed.validate_at(now, os, host_arch).is_err(), "{field}");
}
}
}
#[test]
#[cfg(any(
all(target_os = "macos", target_arch = "aarch64"),
all(target_os = "linux", target_arch = "x86_64")
))]
fn released_kimi_rejects_candidate_rebinding_and_startup_records() {
assert!(!acpx_release_profile("kimi").unwrap().qualification_pending);
let (platform, architecture) = if cfg!(target_os = "macos") {
("darwin", "arm64")
} else {
("linux", "x64")
};
let current = kimi_admission(platform, architecture);
let mut next = current.clone();
next.run_id = "kimi-next-run".to_owned();
assert!(current.validate().is_err());
assert!(
AcpxCandidateAdmission::parse_json(&serde_json::to_string(&current).unwrap()).is_err()
);
assert!(current
.validate_next_run(
&next,
"kimi",
"kimi-for-coding",
"kimi-next-run",
"kimi-session",
)
.is_err());
assert!(current
.validate_next_run(
&current,
"kimi",
"kimi-for-coding",
"kimi-run",
"kimi-session"
)
.is_err());
for (field, change) in [
("companyId", json!("another-company")),
("model", json!("another-model")),
("profileDigest", json!("sha256:wrong")),
("nativeClosureDigest", json!("sha256:wrong")),
("normalizedSessionId", json!("another-session")),
("expiresAt", json!(next.expires_at + 1)),
] {
let mut value = serde_json::to_value(&next).unwrap();
value[field] = change;
let changed = serde_json::from_value(value).unwrap();
assert!(current
.validate_next_run(
&changed,
"kimi",
"kimi-for-coding",
"kimi-next-run",
"kimi-session"
)
.is_err());
}
assert!(current
.validate_next_run(
&next,
"kimi",
"kimi-for-coding",
"foreign-run",
"kimi-session"
)
.is_err());
assert!(current
.validate_next_run(
&next,
"kimi",
"kimi-for-coding",
"kimi-next-run",
"foreign-session"
)
.is_err());
let mut expired = current.clone();
expired.expires_at = 0;
assert!(expired
.validate_next_run(
&next,
"kimi",
"kimi-for-coding",
"kimi-next-run",
"kimi-session"
)
.is_err());
let antigravity = test_admission("run-1", "session-1");
assert!(antigravity
.validate_next_run(
&next,
"kimi",
"kimi-for-coding",
"kimi-next-run",
"kimi-session"
)
.is_err());
}
}
@@ -927,6 +927,35 @@ impl AcpxCommandExecutor {
.canonical_digest()
}
fn restore_launch_profile_digest(
&self,
state: &AcpxDurableState,
) -> Result<String, DurableRunnerError> {
let Some(profile) = self.launch_profile.as_ref() else {
return self.launch_profile_digest();
};
let Some(current) = profile.candidate_admission.as_ref().filter(|grant| {
grant.agent == "kimi" && state.descriptor.run_id != self.context.run_id
}) else {
return profile.canonical_digest();
};
let mut prior = profile.clone();
prior.candidate_admission.as_mut().unwrap().run_id = state.descriptor.run_id.clone();
prior
.candidate_admission
.as_ref()
.unwrap()
.validate_next_run(
current,
&state.descriptor.agent,
&state.descriptor.model,
&self.context.run_id,
&self.context.normalized_session_id,
)
.map_err(|error| DurableRunnerError::invalid(error.to_string()))?;
prior.canonical_digest()
}
fn restore(&mut self) -> Result<(), DurableRunnerError> {
self.ensure_persistence_healthy()?;
if self.restore_checked {
@@ -958,7 +987,10 @@ impl AcpxCommandExecutor {
)))
}
};
if self.candidate_admission().is_some() {
if self
.candidate_admission()
.is_some_and(|admission| admission.agent == "antigravity")
{
return Err(DurableRunnerError::invalid(
"ACPX candidate cannot adopt a prior durable runner activation",
));
@@ -983,7 +1015,7 @@ impl AcpxCommandExecutor {
let state: AcpxDurableState = serde_json::from_slice(&bytes).map_err(|error| {
DurableRunnerError::invalid(format!("ACPX provider state is malformed: {error}"))
})?;
let launch_profile_digest = self.launch_profile_digest()?;
let launch_profile_digest = self.restore_launch_profile_digest(&state)?;
state.validate(&self.context, &launch_profile_digest)?;
self.state = Some(state);
self.restore_session_if_needed()
@@ -1218,7 +1250,10 @@ impl AcpxCommandExecutor {
}
fn attach_run(&mut self, payload: &Value) -> Result<(), DurableRunnerError> {
if self.candidate_admission().is_some() {
if self
.candidate_admission()
.is_some_and(|admission| admission.agent == "antigravity")
{
return Err(DurableRunnerError::invalid(
"ACPX candidate cannot rotate or adopt a live runner authority",
));
@@ -1258,6 +1293,62 @@ impl AcpxCommandExecutor {
attachment_context.run_id = run_id.to_owned();
}
descriptor.validate(&attachment_context)?;
let cold_candidate_attachment = self
.candidate_admission()
.is_some_and(|grant| grant.agent == "kimi")
&& payload.get("paperclipNextAuthority").is_none()
&& self
.state
.as_ref()
.is_some_and(|state| state.descriptor.run_id != self.context.run_id);
let next_candidate = if let Some(admission) = self
.candidate_admission()
.filter(|admission| admission.agent == "kimi")
{
if cold_candidate_attachment {
let state = self.state.as_ref().unwrap();
let digest = self.restore_launch_profile_digest(state)?;
state.validate(&self.context, &digest)?;
admission
.validate_binding(
&descriptor.agent,
&descriptor.model,
&self.context.run_id,
&self.context.normalized_session_id,
)
.map_err(|error| DurableRunnerError::invalid(error.to_string()))?;
Some(admission.clone())
} else {
let proposed: crate::acpx_candidate_admission::AcpxCandidateAdmission =
serde_json::from_value(
payload
.pointer("/paperclipNextAuthority/candidateAdmission")
.cloned()
.ok_or_else(|| {
DurableRunnerError::invalid(
"Kimi run.attach requires exact trusted candidate admission",
)
})?,
)
.map_err(|_| {
DurableRunnerError::invalid(
"Kimi run.attach candidate admission is invalid",
)
})?;
admission
.validate_next_run(
&proposed,
&descriptor.agent,
&descriptor.model,
&descriptor.run_id,
&descriptor.normalized_session_id,
)
.map_err(|error| DurableRunnerError::invalid(error.to_string()))?;
Some(proposed)
}
} else {
None
};
let tool_set = authorized_tool_set(payload)?;
let state = self
.state
@@ -1327,16 +1418,30 @@ impl AcpxCommandExecutor {
})?;
}
self.session = None;
let state = self
if let Some(admission) = next_candidate {
self.launch_profile
.as_mut()
.expect("candidate launch profile remains available")
.candidate_admission = Some(admission);
}
let launch_profile_digest = self.launch_profile_digest()?;
let mut state = self
.state
.as_mut()
.expect("ACPX state remains available while attaching a run");
.as_ref()
.expect("ACPX state remains available while attaching a run")
.clone();
state.launch_profile_digest = launch_profile_digest;
state.descriptor = descriptor;
state.tool_set = tool_set;
state.semantic_result = None;
state.pending_events.clear();
state.lifecycle = "suspended".to_owned();
self.save_state()
if cold_candidate_attachment {
self.commit_staged_state(state)
} else {
self.state = Some(state);
self.save_state()
}
}
fn open_session(&mut self) -> Result<CommandExecution, DurableRunnerError> {
@@ -2032,7 +2137,10 @@ impl AcpxCommandExecutor {
}
fn suspend(&mut self) -> Result<CommandExecution, DurableRunnerError> {
if self.candidate_admission().is_some() {
if self
.candidate_admission()
.is_some_and(|admission| admission.agent == "antigravity")
{
return self.close_session("candidate runner.suspend retires the owned process");
}
if self
@@ -2270,7 +2378,9 @@ fn goal_terminal_disposition<'a>(
impl CommandExecutor for AcpxCommandExecutor {
fn execute(&mut self, command: &Command) -> Result<CommandExecution, DurableRunnerError> {
self.restore()?;
if self.candidate_admission().is_some()
if self
.candidate_admission()
.is_some_and(|admission| admission.agent == "antigravity")
&& command.command_type == "run.attach"
&& command.payload.get("paperclipNextAuthority").is_some()
{
@@ -2346,6 +2456,12 @@ impl CommandExecutor for AcpxCommandExecutor {
self.context.normalized_session_id = config.normalized_session_id.clone();
self.context.turn_id = config.turn_id.clone();
self.context.item_id = config.item_id.clone();
if self
.candidate_admission()
.is_some_and(|admission| admission.agent == "kimi")
{
self.launch_profile = config.acpx_launch_profile.clone();
}
}
fn poll_events(&mut self) -> Result<Vec<PolledEvent>, DurableRunnerError> {
@@ -4824,4 +4940,536 @@ mod tests {
drop(listeners.pop());
(candidates, listeners)
}
#[test]
#[cfg(any(
all(target_os = "macos", target_arch = "aarch64"),
all(target_os = "linux", target_arch = "x86_64")
))]
fn released_kimi_attachment_preserves_ordinary_profile_without_candidate_admission() {
assert!(!acpx_release_profile("kimi").unwrap().qualification_pending);
let directory = temporary_directory("kimi-candidate-attachment");
let mut grant = crate::acpx_candidate_admission::test_admission("run-1", "session-1");
grant.agent = "kimi".to_owned();
grant.model = "explicit-test-model".to_owned();
grant.profile_digest = acpx_release_profile("kimi")
.unwrap()
.command_digest
.to_owned();
grant.platform = if cfg!(target_os = "macos") {
"darwin"
} else {
"linux"
}
.to_owned();
grant.architecture = if cfg!(target_os = "macos") {
"arm64"
} else {
"x64"
}
.to_owned();
grant.native_closure_digest = format!(
"sha256:{}",
crate::generated_acpx_profiles::kimi_native_closure(
&grant.platform,
&grant.architecture
)
.unwrap()
);
let command = directory.join("owned-sidecar");
write_artifact(&command, b"#!/bin/sh\nexit 1\n", true);
let profile = AcpxLaunchProfile {
candidate_admission: None,
authority_digest: format!("sha256:{}", "d".repeat(64)),
command: command.clone(),
args: vec![],
artifacts: vec![artifact(&command)],
};
let runtime = directory.join("runtime");
let workspace = directory.join("workspace");
fs::create_dir_all(&runtime).unwrap();
fs::create_dir_all(&workspace).unwrap();
fs::set_permissions(&runtime, fs::Permissions::from_mode(0o700)).unwrap();
fs::set_permissions(&workspace, fs::Permissions::from_mode(0o700)).unwrap();
let mut value = descriptor("kimi");
value["runtimeDirectory"] = json!(runtime);
value["cwd"] = json!(workspace);
value["sidecarCommand"] = json!(command);
value["sidecarArgs"] = json!([]);
let desc: AcpxProviderDescriptor = serde_json::from_value(value.clone()).unwrap();
let identity = AcpxProviderSessionIdentity {
kind: "acpx".to_owned(),
normalized_session_id: "session-1".to_owned(),
acpx_record_id: "record-1".to_owned(),
backend_session_id: "backend-1".to_owned(),
agent_session_id: "agent-1".to_owned(),
profile_digest: desc.command_digest.clone(),
workspace_digest: format!("sha256:{}", "a".repeat(64)),
requested_model: desc.model.clone(),
effective_model: desc.model.clone(),
permission_mode: Some(desc.permission_mode),
mode: None,
pi_thinking_level: None,
claude_effort: None,
grok_effort: None,
grok_disable_web_search: None,
provider_lifetime_fence_candidates: [60_001, 60_002, 60_003],
};
let operations = Vec::new();
let tools = AuthorizedToolSet {
schema: TOOL_SET_SCHEMA.to_owned(),
schema_version: 1,
catalog_digest: authorized_tool_catalog_digest(&operations).unwrap(),
operations,
};
let previous_digest = profile.canonical_digest().unwrap();
let mut state = AcpxDurableState::new(desc, tools, previous_digest.clone());
state.lifecycle = "suspended".to_owned();
state.identity = Some(identity);
let config = test_config(&directory, Some(profile));
let mut executor = AcpxCommandExecutor::with_runner_config(&directory, &config);
executor.state = Some(state);
executor.save_state().unwrap();
let before = fs::read(executor.state_path()).unwrap();
let mut provider = value;
provider["runId"] = json!("run-2");
let payload = json!({"provider":provider,"paperclipNextAuthority":{
"identity":{"runId":"run-2","normalizedSessionId":"session-1"}
}});
let mut injected_profile = executor.launch_profile.as_ref().unwrap().clone();
injected_profile.candidate_admission = Some(grant.clone());
let injected_config = test_config(&directory, Some(injected_profile.clone()));
assert!(injected_config.validate().is_err());
let prior = executor.state.as_ref().unwrap();
assert!(prior
.descriptor
.session_config(
prior.tool_set.clone(),
prior.identity.clone(),
Some(&injected_profile),
)
.is_err());
assert_eq!(fs::read(executor.state_path()).unwrap(), before);
for (field, changed) in [
("normalizedSessionId", json!("foreign-session")),
("runId", json!("run-1")),
] {
let mut invalid = payload.clone();
invalid["paperclipNextAuthority"]["identity"][field] = changed;
assert!(executor.attach_run(&invalid).is_err(), "{field}");
assert_eq!(fs::read(executor.state_path()).unwrap(), before);
}
assert!(executor.attach_run(&json!({"provider":provider})).is_err());
executor.attach_run(&payload).unwrap();
let updated = executor.state.as_ref().unwrap();
assert_eq!(updated.descriptor.run_id, "run-2");
assert_eq!(executor.context.run_id, "run-1");
let profile = executor.launch_profile.as_ref().unwrap();
assert!(profile.candidate_admission.is_none());
assert_eq!(updated.launch_profile_digest, previous_digest);
assert_eq!(
updated.launch_profile_digest,
profile.canonical_digest().unwrap()
);
let launch = updated
.descriptor
.session_config(
updated.tool_set.clone(),
updated.identity.clone(),
Some(profile),
)
.unwrap();
assert!(launch.candidate_admission.is_none());
let saved: AcpxDurableState =
serde_json::from_slice(&fs::read(executor.state_path()).unwrap()).unwrap();
assert_eq!(saved.descriptor.run_id, "run-2");
assert_eq!(saved.launch_profile_digest, updated.launch_profile_digest);
assert!(executor.session.is_none());
fs::remove_dir_all(directory).unwrap();
}
#[cfg(any(
all(target_os = "macos", target_arch = "aarch64"),
all(target_os = "linux", target_arch = "x86_64")
))]
fn kimi_cold_restore_fixture(
label: &str,
) -> (
PathBuf,
DurableRunnerConfig,
Value,
Vec<u8>,
PathBuf,
crate::acpx_candidate_admission::AcpxCandidateAdmission,
) {
assert!(!acpx_release_profile("kimi").unwrap().qualification_pending);
let directory = temporary_directory(label);
let mut grant = crate::acpx_candidate_admission::test_admission("run-1", "session-1");
grant.agent = "kimi".to_owned();
grant.model = "explicit-test-model".to_owned();
grant.profile_digest = acpx_release_profile("kimi")
.unwrap()
.command_digest
.to_owned();
grant.platform = if cfg!(target_os = "macos") {
"darwin"
} else {
"linux"
}
.to_owned();
grant.architecture = if cfg!(target_os = "macos") {
"arm64"
} else {
"x64"
}
.to_owned();
grant.native_closure_digest = format!(
"sha256:{}",
crate::generated_acpx_profiles::kimi_native_closure(
&grant.platform,
&grant.architecture
)
.unwrap()
);
let command = directory.join("owned-sidecar");
let sentinel = directory.join("provider-opened");
write_artifact(
&command,
format!("#!/bin/sh\ntouch '{}'\nexit 1\n", sentinel.display()).as_bytes(),
true,
);
let profile = AcpxLaunchProfile {
candidate_admission: None,
authority_digest: format!("sha256:{}", "d".repeat(64)),
command: command.clone(),
args: vec![],
artifacts: vec![artifact(&command)],
};
let runtime = directory.join("runtime");
let workspace = directory.join("workspace");
fs::create_dir_all(&runtime).unwrap();
fs::create_dir_all(&workspace).unwrap();
fs::set_permissions(&runtime, fs::Permissions::from_mode(0o700)).unwrap();
fs::set_permissions(&workspace, fs::Permissions::from_mode(0o700)).unwrap();
let mut value = descriptor("kimi");
value["runtimeDirectory"] = json!(runtime);
value["cwd"] = json!(workspace);
value["sidecarCommand"] = json!(command);
value["sidecarArgs"] = json!([]);
let desc: AcpxProviderDescriptor = serde_json::from_value(value.clone()).unwrap();
let identity = AcpxProviderSessionIdentity {
kind: "acpx".to_owned(),
normalized_session_id: "session-1".to_owned(),
acpx_record_id: "record-1".to_owned(),
backend_session_id: "backend-1".to_owned(),
agent_session_id: "agent-1".to_owned(),
profile_digest: desc.command_digest.clone(),
workspace_digest: format!("sha256:{}", "a".repeat(64)),
requested_model: desc.model.clone(),
effective_model: desc.model.clone(),
permission_mode: Some(desc.permission_mode),
mode: None,
pi_thinking_level: None,
claude_effort: None,
grok_effort: None,
grok_disable_web_search: None,
provider_lifetime_fence_candidates: [60_001, 60_002, 60_003],
};
let operations = Vec::new();
let tools = AuthorizedToolSet {
schema: TOOL_SET_SCHEMA.to_owned(),
schema_version: 1,
catalog_digest: authorized_tool_catalog_digest(&operations).unwrap(),
operations,
};
let previous_digest = profile.canonical_digest().unwrap();
let mut state = AcpxDurableState::new(desc, tools, previous_digest.clone());
state.lifecycle = "suspended".to_owned();
state.identity = Some(identity);
let config = test_config(&directory, Some(profile));
let mut executor = AcpxCommandExecutor::with_runner_config(&directory, &config);
executor.state = Some(state);
executor.save_state().unwrap();
let before = fs::read(executor.state_path()).unwrap();
let mut next_config = config.clone();
next_config.run_id = "run-2".to_owned();
next_config.turn_id = "turn-2".to_owned();
next_config.item_id = "item-2".to_owned();
grant.run_id = "run-2".to_owned();
next_config.validate().unwrap();
let mut next_provider = value;
next_provider["runId"] = json!("run-2");
(
directory,
next_config,
json!({"provider": next_provider}),
before,
sentinel,
grant,
)
}
fn cold_attach_command(payload: Value) -> Command {
Command {
schema: "paperclip.prp.command.v1".into(),
command_id: "cold-attach".into(),
controller_seq: 1,
command_type: "run.attach".into(),
issued_at: "2026-10-30T00:00:00.000Z".into(),
deadline_at: None,
precondition: None,
payload,
}
}
#[test]
#[cfg(any(
all(target_os = "macos", target_arch = "aarch64"),
all(target_os = "linux", target_arch = "x86_64")
))]
fn released_kimi_governed_wait_cold_restore_uses_ordinary_startup_without_grant() {
let (directory, next_config, payload, before, sentinel, _) =
kimi_cold_restore_fixture("kimi-candidate-cold-governed-wait");
let mut recovered = AcpxCommandExecutor::with_runner_config(&directory, &next_config);
recovered.restore().expect("released cold Kimi restore retains its settled prior session under the same full ordinary profile");
assert!(recovered.session.is_none());
assert!(recovered.poll_events().unwrap().is_empty());
assert!(!sentinel.exists());
recovered.attach_run(&payload)
.expect("released cold replacement attaches with the ordinary current startup and no candidate record");
let saved: AcpxDurableState =
serde_json::from_slice(&fs::read(recovered.state_path()).unwrap()).unwrap();
assert_eq!(saved.descriptor.run_id, "run-2");
assert_eq!(
saved.launch_profile_digest,
next_config
.acpx_launch_profile
.as_ref()
.unwrap()
.canonical_digest()
.unwrap()
);
assert!(recovered.session.is_none());
assert!(!sentinel.exists());
assert_ne!(fs::read(recovered.state_path()).unwrap(), before);
fs::remove_dir_all(directory).unwrap();
}
#[test]
#[cfg(any(
all(target_os = "macos", target_arch = "aarch64"),
all(target_os = "linux", target_arch = "x86_64")
))]
fn released_kimi_cold_restore_rejects_candidate_injection_and_changed_full_profile() {
for field in [
"company",
"model",
"expiry",
"expired",
"profile",
"closure",
"platform",
"architecture",
"session",
"grant_run",
"injected_candidate",
"context_run",
"authority",
"command",
"args",
"artifact",
"missing_profile",
"saved_digest",
] {
let (directory, mut config, payload, before, sentinel, candidate) =
kimi_cold_restore_fixture("kimi-released-cold-reject");
assert!(config
.acpx_launch_profile
.as_ref()
.unwrap()
.candidate_admission
.is_none());
match field {
"context_run" => config.run_id = "foreign-run".into(),
"missing_profile" => config.acpx_launch_profile = None,
"saved_digest" => {
let mut state: AcpxDurableState = serde_json::from_slice(&before).unwrap();
state.launch_profile_digest = format!("sha256:{}", "0".repeat(64));
fs::write(
directory.join("acpx-provider-state.json"),
serde_json::to_vec(&state).unwrap(),
)
.unwrap();
}
_ => {
let profile = config.acpx_launch_profile.as_mut().unwrap();
match field {
"authority" => {
profile.authority_digest = format!("sha256:{}", "0".repeat(64))
}
"command" => profile.command = directory.join("foreign-command"),
"args" => profile.args.push("foreign-argument".into()),
"artifact" => {
profile.artifacts[0].sha256 = format!("sha256:{}", "0".repeat(64))
}
_ => {
let mut grant = candidate;
match field {
"company" => grant.company_id = "foreign-company".into(),
"model" => grant.model = "foreign-model".into(),
"expiry" => grant.expires_at += 1,
"expired" => grant.expires_at = 1,
"profile" => {
grant.profile_digest = format!("sha256:{}", "0".repeat(64))
}
"closure" => {
grant.native_closure_digest =
format!("sha256:{}", "0".repeat(64))
}
"platform" => grant.platform = "foreign-platform".into(),
"architecture" => {
grant.architecture = "foreign-architecture".into()
}
"session" => grant.normalized_session_id = "foreign-session".into(),
"grant_run" => grant.run_id = "foreign-run".into(),
"injected_candidate" => {}
_ => unreachable!(),
}
profile.candidate_admission = Some(grant);
assert!(config.validate().is_err(), "{field}");
}
}
}
}
let mut recovered = AcpxCommandExecutor::with_runner_config(&directory, &config);
let retained = fs::read(recovered.state_path()).unwrap();
assert!(
recovered.execute(&cold_attach_command(payload)).is_err(),
"{field}"
);
assert!(recovered.session.is_none(), "{field}");
assert!(!sentinel.exists(), "{field}");
assert_eq!(
fs::read(recovered.state_path()).unwrap(),
retained,
"{field}"
);
fs::remove_dir_all(directory).unwrap();
}
}
#[test]
#[cfg(any(
all(target_os = "macos", target_arch = "aarch64"),
all(target_os = "linux", target_arch = "x86_64")
))]
fn released_kimi_cold_attach_rejects_unsettled_or_changed_session_before_provider_open() {
for field in [
"run",
"session",
"model",
"workspace",
"active_turn",
"pending_event",
"exit_unconfirmed",
"identity",
"closed",
] {
let (directory, config, mut payload, before, sentinel, _) =
kimi_cold_restore_fixture("kimi-candidate-cold-binding");
let mut recovered = AcpxCommandExecutor::with_runner_config(&directory, &config);
match field {
"run" => payload["provider"]["runId"] = json!("foreign-run"),
"session" => payload["provider"]["normalizedSessionId"] = json!("foreign-session"),
"model" => payload["provider"]["model"] = json!("foreign-model"),
"workspace" => {
payload["provider"]["cwd"] = json!(directory.join("foreign-workspace"))
}
_ => {
let mut state: AcpxDurableState = serde_json::from_slice(&before).unwrap();
match field {
"active_turn" => state.active_turn_id = Some("active-turn".into()),
"pending_event" => {
state
.push(NormalizedProviderEvent {
event_type: "runtime_request.created".into(),
priority: EventPriority::P0,
payload: json!({"request":pending_input_request("input-1")}),
})
.unwrap();
}
"exit_unconfirmed" => state.provider_exit_unconfirmed = true,
"identity" => state.identity = None,
"closed" => state.lifecycle = "closed".into(),
_ => unreachable!(),
}
fs::write(recovered.state_path(), serde_json::to_vec(&state).unwrap()).unwrap();
}
}
let retained = fs::read(recovered.state_path()).unwrap();
assert!(
recovered.execute(&cold_attach_command(payload)).is_err(),
"{field}"
);
assert!(recovered.session.is_none(), "{field}");
assert!(!sentinel.exists(), "{field}");
assert_eq!(
fs::read(recovered.state_path()).unwrap(),
retained,
"{field}"
);
fs::remove_dir_all(directory).unwrap();
}
}
#[test]
#[cfg(any(
all(target_os = "macos", target_arch = "aarch64"),
all(target_os = "linux", target_arch = "x86_64")
))]
fn released_kimi_cold_attach_post_rename_failure_quarantines_public_execution() {
let (directory, config, payload, before, sentinel, _) =
kimi_cold_restore_fixture("kimi-candidate-cold-write-failure");
let mut recovered = AcpxCommandExecutor::with_runner_config(&directory, &config);
recovered.restore().unwrap();
let previous_state = recovered.state.clone();
recovered.fail_after_state_rename = true;
let command = cold_attach_command(payload);
let error = recovered.execute(&command).unwrap_err();
assert!(error
.to_string()
.contains("injected post-rename sync failure"));
assert_ne!(recovered.state, previous_state);
assert!(recovered.session.is_none());
assert!(!sentinel.exists());
let disk = fs::read(recovered.state_path()).unwrap();
assert_ne!(disk, before);
let saved: AcpxDurableState = serde_json::from_slice(&disk).unwrap();
assert_eq!(saved.descriptor.run_id, "run-2");
assert_eq!(
saved.launch_profile_digest,
config
.acpx_launch_profile
.as_ref()
.unwrap()
.canonical_digest()
.unwrap()
);
recovered.fail_after_state_rename = false;
for result in [
recovered.retained_events().map(|_| ()),
recovered.poll_events().map(|_| ()),
recovered.acknowledge_events(0),
recovered.restore(),
recovered.save_state(),
recovered.shutdown(),
] {
assert_eq!(result.unwrap_err().to_string(), error.to_string());
}
assert_eq!(
recovered.execute(&command).unwrap_err().to_string(),
error.to_string()
);
assert_eq!(fs::read(recovered.state_path()).unwrap(), disk);
assert!(!sentinel.exists());
fs::remove_dir_all(directory).unwrap();
}
}
@@ -11,7 +11,7 @@ pub(crate) enum RunAttachmentPolicy {
pub(crate) fn run_attachment_policy(agent: &str) -> RunAttachmentPolicy {
match agent {
"cursor" | "copilot" | "pi" => RunAttachmentPolicy::AuthenticatedRunGrants,
"cursor" | "copilot" | "pi" | "kimi" => RunAttachmentPolicy::AuthenticatedRunGrants,
"claude" => RunAttachmentPolicy::AuthenticatedAssetPaths,
_ => RunAttachmentPolicy::ImmutableInstructions,
}
@@ -109,8 +109,8 @@ pub struct AcpxProviderState {
plan_revision: u64,
assistant_text: String,
assistant_message_id: Option<String>,
pi_assistant_message: Option<String>,
pi_assistant_message_ids: BTreeSet<String>,
native_assistant_message: Option<String>,
native_assistant_message_ids: BTreeSet<String>,
pending_tools: BTreeMap<String, AcpxPendingTool>,
pending_tool_input_bytes: usize,
pending_permissions: BTreeMap<String, (usize, Value)>,
@@ -128,8 +128,8 @@ impl AcpxProviderState {
plan_revision: 0,
assistant_text: String::new(),
assistant_message_id: None,
pi_assistant_message: None,
pi_assistant_message_ids: BTreeSet::new(),
native_assistant_message: None,
native_assistant_message_ids: BTreeSet::new(),
pending_tools: BTreeMap::new(),
pending_tool_input_bytes: 0,
pending_permissions: BTreeMap::new(),
@@ -222,8 +222,8 @@ impl AcpxProviderState {
self.plan_revision = 0;
self.assistant_text.clear();
self.assistant_message_id = None;
self.pi_assistant_message = None;
self.pi_assistant_message_ids.clear();
self.native_assistant_message = None;
self.native_assistant_message_ids.clear();
self.semantic_result = None;
self.attempted_turn_controls.clear();
Ok(())
@@ -371,9 +371,9 @@ impl AcpxProviderState {
}])
}
AcpxEventPayload::TurnTerminal { status, error } => {
if status == AcpxTurnStatus::Completed && self.pi_assistant_message.is_some() {
if status == AcpxTurnStatus::Completed && self.native_assistant_message.is_some() {
return Err(LocalRunnerError::invalid(
"Pi assistant message ended without its native boundary",
"ACP assistant message ended without its native boundary",
));
}
let turn_id = event
@@ -394,8 +394,8 @@ impl AcpxProviderState {
self.assistant_text.clear();
}
self.assistant_message_id = None;
self.pi_assistant_message = None;
self.pi_assistant_message_ids.clear();
self.native_assistant_message = None;
self.native_assistant_message_ids.clear();
events.push(AcpxProviderStateEvent::TurnTerminal {
turn_id,
status,
@@ -518,6 +518,10 @@ impl AcpxProviderState {
};
if kind == AcpxRuntimeEventKind::TextDelta
&& payload.get("piMessageHistory").and_then(Value::as_bool) != Some(true)
&& payload
.get("assistantMessageHistory")
.and_then(Value::as_bool)
!= Some(true)
{
let provider_message_id = payload
.get("messageId")
@@ -534,55 +538,67 @@ impl AcpxProviderState {
.and_then(Value::as_str)
.unwrap_or_default();
let mut native_boundary_clears = false;
if let Some(boundary) = payload.get("piMessageBoundary") {
if payload.get("assistantMessageBoundary").is_some()
&& payload.get("piMessageBoundary").is_some()
{
return Err(LocalRunnerError::invalid(
"ACP assistant boundary has ambiguous representations",
));
}
if let Some(boundary) = payload
.get("assistantMessageBoundary")
.or_else(|| payload.get("piMessageBoundary"))
{
let phase = boundary.get("phase").and_then(Value::as_str);
let reason = boundary.get("stopReason").and_then(Value::as_str);
if !raw_text.is_empty() || provider_message_id.is_none() {
return Err(LocalRunnerError::invalid(
"Pi assistant boundary contains invalid content or identity",
"ACP assistant boundary contains invalid content or identity",
));
}
match phase {
Some("start") if self.pi_assistant_message.is_none() && reason.is_none() => {
if self.pi_assistant_message_ids.len() >= 4096
Some("start")
if self.native_assistant_message.is_none() && reason.is_none() =>
{
if self.native_assistant_message_ids.len() >= 4096
|| !self
.pi_assistant_message_ids
.native_assistant_message_ids
.insert(provider_message_id.clone().unwrap())
{
return Err(LocalRunnerError::invalid(
"Pi assistant identity is duplicated or exceeds its bound",
"ACP assistant identity is duplicated or exceeds its bound",
));
}
self.pi_assistant_message = provider_message_id.clone();
self.native_assistant_message = provider_message_id.clone();
native_boundary_clears = true;
}
Some("end")
if self.pi_assistant_message.is_some()
&& self.pi_assistant_message == provider_message_id =>
if self.native_assistant_message.is_some()
&& self.native_assistant_message == provider_message_id =>
{
if !matches!(
reason,
Some("stop" | "length" | "toolUse" | "error" | "aborted")
) {
return Err(LocalRunnerError::invalid(
"Pi assistant stop reason is invalid",
"ACP assistant stop reason is invalid",
));
}
self.pi_assistant_message = None;
self.native_assistant_message = None;
native_boundary_clears = !matches!(reason, Some("stop" | "length"));
}
_ => {
return Err(LocalRunnerError::invalid(
"Pi assistant boundaries are out of order",
"ACP assistant boundaries are out of order",
))
}
}
} else if !self.pi_assistant_message_ids.is_empty()
&& (self.pi_assistant_message.is_none()
|| self.pi_assistant_message != provider_message_id)
} else if !self.native_assistant_message_ids.is_empty()
&& (self.native_assistant_message.is_none()
|| self.native_assistant_message != provider_message_id)
{
return Err(LocalRunnerError::invalid(
"Pi assistant delta has no matching native message",
"ACP assistant delta has no matching native message",
));
}
// ACP can produce more than one assistant message in a turn. All
@@ -216,6 +216,7 @@ impl AcpxSidecarTransport {
"pi" => &[],
"cursor" => &["CURSOR_API_KEY", "CURSOR_AUTH_TOKEN"],
"copilot" => &["COPILOT_GITHUB_TOKEN"],
"kimi" => &["KIMI_API_KEY"],
"antigravity" => &[
"GEMINI_API_KEY",
"PAPERCLIP_ACPX_ANTIGRAVITY_AUTH_JSON_SECRET",
@@ -254,7 +255,7 @@ impl AcpxSidecarTransport {
"PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT",
"PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST",
];
if matches!(agent, "pi" | "cursor" | "copilot" | "antigravity") {
if matches!(agent, "pi" | "cursor" | "copilot" | "kimi" | "antigravity") {
// Credential values alone are not proof of an explicit task binding.
// The sidecar checks this controller-minted provider/session marker.
keys.push("PAPERCLIP_ACPX_CREDENTIAL_BINDING");
@@ -769,8 +769,8 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
.map(|value| value.parse::<usize>())
.transpose()?
.unwrap_or(64);
if !(1..=4096).contains(&held_output_count) {
return Err("--held-output-count must be between 1 and 4096".into());
if !(1..=8192).contains(&held_output_count) {
return Err("--held-output-count must be between 1 and 8192".into());
}
let exit_after_turn_start = args.iter().any(|value| value == "--exit-after-turn-start");
let exit_after_turn_completion = args
@@ -671,10 +671,15 @@ mod tests {
args.extend(options.into_iter().map(str::to_owned));
assert!(metadata_with_provider_packs(&args).is_err());
}
let missing_root = if cfg!(windows) {
r"C:\missing-provider-pack"
} else {
"/missing-provider-pack"
};
let metadata = metadata_with_provider_packs(&[
"--build-metadata".to_owned(),
"--provider-pack-root".to_owned(),
"/missing-provider-pack".to_owned(),
missing_root.to_owned(),
])
.unwrap();
assert_eq!(metadata["binaryContractVersion"], 2);
@@ -4874,6 +4874,7 @@ done
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn verified_opencode_proxy_executes_the_descriptor_safe_commonjs_bundle() {
let nonce = SystemTime::now()
.duration_since(UNIX_EPOCH)
@@ -52,6 +52,7 @@ const RESERVED: &[&str] = &[
"GITHUB_ENTERPRISE_TOKEN",
"GITHUB_TOKEN",
"HOME",
"KIMI_API_KEY",
"NODE_OPTIONS",
"NODE_PATH",
"OPENAI_API_KEY",
@@ -209,6 +210,7 @@ mod tests {
"[\"PAPERCLIP_RUNTIME_NOTIFICATION_CONTEXT\"]",
"[\"PAPERCLIP_RUNTIME_NOTIFICATION_HELPER\"]",
"[\"OPENAI_API_KEY\"]",
"[\"KIMI_API_KEY\"]",
"[\"LD_PRELOAD\"]",
"[\"bad=name\"]",
"[\"CUSTOM\",\"CUSTOM\"]",
@@ -226,6 +228,14 @@ mod tests {
assert_eq!(command.get_envs().count(), 0);
}
let mut command = Command::new("unused");
assert!(apply_configured_environment(&mut command, |key| match key {
MARKER => Some("[\"KIMI_API_KEY\"]".into()),
"KIMI_API_KEY" => Some("selected-code-key".into()),
_ => None,
})
.is_err());
assert_eq!(command.get_envs().count(), 0);
let mut command = Command::new("unused");
assert!(
apply_configured_environment(&mut command, |key| Some(if key == MARKER {
"[\"CUSTOM\"]".into()
@@ -199,7 +199,6 @@ pub(crate) fn next_authority_config(
next.normalized_session_id = read_identity("normalizedSessionId")?;
next.turn_id = read_identity("turnId")?;
next.item_id = read_identity("itemId")?;
next.validate()?;
if next.runner_instance_id != current.runner_instance_id
|| next.environment_lease_id != current.environment_lease_id
|| next.normalized_session_id != current.normalized_session_id
@@ -209,6 +208,47 @@ pub(crate) fn next_authority_config(
"run.attach authority changed an immutable session binding",
));
}
if let Some(admission) = current
.acpx_launch_profile
.as_ref()
.and_then(|profile| profile.candidate_admission.as_ref())
{
if admission.agent == "kimi" {
let proposed: crate::acpx_candidate_admission::AcpxCandidateAdmission =
serde_json::from_value(boundary.get("candidateAdmission").cloned().ok_or_else(
|| {
DurableRunnerError::invalid(
"Kimi authority rotation requires exact trusted candidate admission",
)
},
)?)
.map_err(|_| {
DurableRunnerError::invalid("Kimi authority rotation admission is invalid")
})?;
admission
.validate_next_run(
&proposed,
"kimi",
&admission.model,
&next.run_id,
&next.normalized_session_id,
)
.map_err(|error| DurableRunnerError::invalid(error.to_string()))?;
next.acpx_launch_profile
.as_mut()
.unwrap()
.candidate_admission = Some(proposed);
} else if boundary.get("candidateAdmission").is_some() {
return Err(DurableRunnerError::invalid(
"candidate authority rotation is not admitted",
));
}
} else if boundary.get("candidateAdmission").is_some() {
return Err(DurableRunnerError::invalid(
"authority rotation cannot create candidate admission",
));
}
next.validate()?;
Ok(Some(next))
}
@@ -5104,4 +5144,130 @@ mod tests {
assert_eq!(executor.calls, 1);
fs::remove_dir_all(directory).unwrap();
}
#[test]
#[cfg(any(
all(target_os = "macos", target_arch = "aarch64"),
all(target_os = "linux", target_arch = "x86_64")
))]
fn released_kimi_handoff_uses_ordinary_authority_and_rejects_candidate_injection() {
assert!(
!crate::generated_acpx_profiles::acpx_release_profile("kimi")
.unwrap()
.qualification_pending
);
let directory =
std::env::temp_dir().join(format!("kimi-grant-handoff-{}", std::process::id()));
let mut current = config(directory);
let mut grant = crate::acpx_candidate_admission::test_admission(
"run_1",
&current.normalized_session_id,
);
grant.agent = "kimi".to_owned();
grant.profile_digest = crate::generated_acpx_profiles::acpx_release_profile("kimi")
.unwrap()
.command_digest
.to_owned();
grant.platform = if cfg!(target_os = "macos") {
"darwin"
} else {
"linux"
}
.to_owned();
grant.architecture = if cfg!(target_os = "macos") {
"arm64"
} else {
"x64"
}
.to_owned();
grant.native_closure_digest = format!(
"sha256:{}",
crate::generated_acpx_profiles::kimi_native_closure(
&grant.platform,
&grant.architecture
)
.unwrap()
);
current.acpx_launch_profile = Some(crate::durable::AcpxLaunchProfile {
authority_digest: format!("sha256:{}", "a".repeat(64)),
command: PathBuf::from("/fixture/node"),
args: vec!["/fixture/sidecar.cjs".to_owned()],
artifacts: vec![crate::durable::QualifiedLaunchArtifact {
path: PathBuf::from("/fixture/node"),
sha256: format!("sha256:{}", "b".repeat(64)),
}],
candidate_admission: None,
});
current.validate().unwrap();
let mut next_grant = grant.clone();
next_grant.run_id = "run_2".to_owned();
let mut attach = command("run.attach");
attach.payload = json!({"paperclipNextAuthority":{
"identity":{"runnerInstanceId":current.runner_instance_id,"environmentLeaseId":current.environment_lease_id,
"runId":"run_2","normalizedSessionId":current.normalized_session_id,"turnId":"turn_2","itemId":"item_2"},
"connection":{"mode":"external"}
}});
let next = next_authority_config(&attach, &current).unwrap().unwrap();
assert!(next
.acpx_launch_profile
.as_ref()
.unwrap()
.candidate_admission
.is_none());
assert_eq!(next.acpx_launch_profile, current.acpx_launch_profile);
assert_eq!(next.run_id, "run_2");
assert_eq!(next.turn_id, "turn_2");
assert_eq!(next.item_id, "item_2");
let mut injected = attach.clone();
injected.payload["paperclipNextAuthority"]["candidateAdmission"] = json!(next_grant);
assert!(next_authority_config(&injected, &current).is_err());
assert_eq!(next.runner_instance_id, current.runner_instance_id);
assert_eq!(next.environment_lease_id, current.environment_lease_id);
assert_eq!(next.normalized_session_id, current.normalized_session_id);
for (field, value) in [
("companyId", json!("foreign-company")),
("agent", json!("antigravity")),
("model", json!("different-model")),
("profileDigest", json!(format!("sha256:{}", "f".repeat(64)))),
(
"nativeClosureDigest",
json!(format!("sha256:{}", "e".repeat(64))),
),
("expiresAt", json!(grant.expires_at + 1)),
("runId", json!("unbound-run")),
("normalizedSessionId", json!("foreign-session")),
] {
let mut changed = injected.clone();
changed.payload["paperclipNextAuthority"]["candidateAdmission"][field] = value;
assert!(
next_authority_config(&changed, &current).is_err(),
"{field}"
);
}
for field in [
"runnerInstanceId",
"environmentLeaseId",
"normalizedSessionId",
] {
let mut changed = attach.clone();
changed.payload["paperclipNextAuthority"]["identity"][field] = json!("foreign");
assert!(
next_authority_config(&changed, &current).is_err(),
"{field}"
);
}
let mut missing = injected.clone();
missing.payload["paperclipNextAuthority"]
.as_object_mut()
.unwrap()
.remove("candidateAdmission");
assert!(next_authority_config(&missing, &current).unwrap().is_some());
let mut stale_candidate = current.clone();
stale_candidate
.acpx_launch_profile
.as_mut()
.unwrap()
.candidate_admission = Some(grant);
assert!(stale_candidate.validate().is_err());
assert!(next_authority_config(&injected, &stale_candidate).is_err());
}
}
@@ -13,6 +13,18 @@ pub(crate) struct AcpxReleaseProfile {
pub qualification_pending: bool,
}
pub(crate) fn kimi_native_closure(platform: &str, architecture: &str) -> Option<&'static str> {
match (platform, architecture) {
("darwin", "arm64") => {
Some("6038325abd0431c30097ff1f572a9b29816e1b4da372d8cb86b2ca5d18ea34e7")
}
("linux", "x64") => {
Some("105d8394adbbc76a953a12cc51b5288eb940ad693291e584f6cdc6407213164d")
}
_ => None,
}
}
pub(crate) fn acpx_release_profile(agent: &str) -> Option<AcpxReleaseProfile> {
Some(match agent {
"antigravity" => AcpxReleaseProfile {
@@ -85,6 +97,16 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option<AcpxReleaseProfile> {
requires_provider_policy: false,
qualification_pending: false,
},
"kimi" => AcpxReleaseProfile {
agent_server_package: "@moonshot-ai/kimi-code",
agent_server_version: "2.3.3",
agent_runtime_package: None,
agent_runtime_version: None,
command_digest:
"sha256:6ee5fb7ac18c1361ac63effe9117d08ed129add8349756a140e73e0c86ba5da7",
requires_provider_policy: true,
qualification_pending: false,
},
_ => return None,
})
}
@@ -138,7 +138,12 @@ impl NativeProviderCommandExecutor {
.config
.acpx_launch_profile
.as_ref()
.is_some_and(|profile| profile.candidate_admission.is_some())
.is_some_and(|profile| {
profile
.candidate_admission
.as_ref()
.is_some_and(|admission| admission.agent == "antigravity")
})
&& [
CODEX_PROVIDER_STATE_FILE,
ACPX_PROVIDER_STATE_FILE,
@@ -454,6 +454,7 @@ impl VerifiedProcessLaunch {
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(crate) fn spawn_stdio(
&self,
environment: &std::collections::BTreeMap<String, String>,
@@ -176,92 +176,103 @@ fn promotes_only_the_latest_provider_message_as_the_terminal_reply() {
}
#[test]
fn pi_native_boundaries_keep_progress_but_never_promote_tool_narration_or_history() {
for final_reason in ["stop", "length", "toolUse", "error", "aborted", "empty"] {
let mut state = AcpxProviderState::new("run-1").unwrap();
state.begin_turn("turn-1").unwrap();
let mut messages = vec![
json!({"type":"text_delta","messageId":"history","text":"Earlier session answer","piMessageHistory":true}),
json!({"type":"text_delta","messageId":"first","text":"","piMessageBoundary":{"phase":"start"}}),
json!({"type":"text_delta","messageId":"first","text":"Calling finish."}),
json!({"type":"text_delta","messageId":"first","text":"","piMessageBoundary":{"phase":"end","stopReason":"toolUse"}}),
json!({"type":"text_delta","messageId":"last","text":"","piMessageBoundary":{"phase":"start"}}),
];
if final_reason != "empty" {
messages.push(json!({"type":"text_delta","messageId":"last","text":"EXACT_MARKER"}));
}
messages.push(json!({"type":"text_delta","messageId":"last","text":"","piMessageBoundary":{"phase":"end","stopReason":if final_reason == "empty" { "stop" } else { final_reason }}}));
let count = messages.len() as u64;
for (index, payload) in messages.into_iter().enumerate() {
state
fn native_boundaries_keep_progress_but_never_promote_tool_narration_or_history() {
for (boundary_field, history_field) in [
("piMessageBoundary", "piMessageHistory"),
("assistantMessageBoundary", "assistantMessageHistory"),
] {
for final_reason in ["stop", "length", "toolUse", "error", "aborted", "empty"] {
let mut state = AcpxProviderState::new("run-1").unwrap();
state.begin_turn("turn-1").unwrap();
let mut messages = vec![
json!({"type":"text_delta","messageId":"history","text":"Earlier session answer",(history_field):true}),
json!({"type":"text_delta","messageId":"first","text":"",(boundary_field):{"phase":"start"}}),
json!({"type":"text_delta","messageId":"first","text":"Calling finish."}),
json!({"type":"text_delta","messageId":"first","text":"",(boundary_field):{"phase":"end","stopReason":"toolUse"}}),
json!({"type":"text_delta","messageId":"last","text":"",(boundary_field):{"phase":"start"}}),
];
if final_reason != "empty" {
messages
.push(json!({"type":"text_delta","messageId":"last","text":"EXACT_MARKER"}));
}
messages.push(json!({"type":"text_delta","messageId":"last","text":"",(boundary_field):{"phase":"end","stopReason":if final_reason == "empty" { "stop" } else { final_reason }}}));
let count = messages.len() as u64;
for (index, payload) in messages.into_iter().enumerate() {
state
.accept_event(&event(
index as u64 + 1,
GeneratedAcpxSidecarEventType::RuntimeEvent,
Some("turn-1"),
payload,
))
.unwrap();
}
let terminal = state
.accept_event(&event(
index as u64 + 1,
GeneratedAcpxSidecarEventType::RuntimeEvent,
count + 1,
GeneratedAcpxSidecarEventType::RuntimeTurnTerminal,
Some("turn-1"),
payload,
json!({"status":"completed"}),
))
.unwrap();
}
let terminal = state
.accept_event(&event(
count + 1,
GeneratedAcpxSidecarEventType::RuntimeTurnTerminal,
Some("turn-1"),
json!({"status":"completed"}),
))
.unwrap();
let replies: Vec<_> = terminal
.iter()
.filter_map(|entry| {
if let AcpxProviderStateEvent::AssistantMessage { text, .. } = entry {
Some(text.as_str())
let replies: Vec<_> = terminal
.iter()
.filter_map(|entry| {
if let AcpxProviderStateEvent::AssistantMessage { text, .. } = entry {
Some(text.as_str())
} else {
None
}
})
.collect();
assert_eq!(
replies,
if matches!(final_reason, "stop" | "length") {
vec!["EXACT_MARKER"]
} else {
None
vec![]
}
})
.collect();
assert_eq!(
replies,
if matches!(final_reason, "stop" | "length") {
vec!["EXACT_MARKER"]
} else {
vec![]
}
);
);
}
}
}
#[test]
fn pi_native_boundary_order_and_terminal_completeness_fail_closed() {
for malformed in [
json!({"type":"text_delta","messageId":"first","text":"","piMessageBoundary":{"phase":"start"}}),
json!({"type":"text_delta","messageId":"other","text":"","piMessageBoundary":{"phase":"end","stopReason":"stop"}}),
json!({"type":"text_delta","messageId":"first","text":"","piMessageBoundary":{"phase":"end","stopReason":"invented"}}),
json!({"type":"text_delta","messageId":"other","text":"Out of order"}),
fn native_boundary_order_and_terminal_completeness_fail_closed() {
for (boundary_field, _history_field) in [
("piMessageBoundary", "piMessageHistory"),
("assistantMessageBoundary", "assistantMessageHistory"),
] {
for malformed in [
json!({"type":"text_delta","messageId":"first","text":"",(boundary_field):{"phase":"start"}}),
json!({"type":"text_delta","messageId":"other","text":"",(boundary_field):{"phase":"end","stopReason":"stop"}}),
json!({"type":"text_delta","messageId":"first","text":"",(boundary_field):{"phase":"end","stopReason":"invented"}}),
json!({"type":"text_delta","messageId":"other","text":"Out of order"}),
] {
let mut state = AcpxProviderState::new("run-1").unwrap();
state.begin_turn("turn-1").unwrap();
state.accept_event(&event(1, GeneratedAcpxSidecarEventType::RuntimeEvent, Some("turn-1"), json!({"type":"text_delta","messageId":"first","text":"",(boundary_field):{"phase":"start"}}))).unwrap();
assert!(state
.accept_event(&event(
2,
GeneratedAcpxSidecarEventType::RuntimeEvent,
Some("turn-1"),
malformed
))
.is_err());
}
let mut state = AcpxProviderState::new("run-1").unwrap();
state.begin_turn("turn-1").unwrap();
state.accept_event(&event(1, GeneratedAcpxSidecarEventType::RuntimeEvent, Some("turn-1"), json!({"type":"text_delta","messageId":"first","text":"","piMessageBoundary":{"phase":"start"}}))).unwrap();
state.accept_event(&event(1, GeneratedAcpxSidecarEventType::RuntimeEvent, Some("turn-1"), json!({"type":"text_delta","messageId":"first","text":"",(boundary_field):{"phase":"start"}}))).unwrap();
assert!(state
.accept_event(&event(
2,
GeneratedAcpxSidecarEventType::RuntimeEvent,
GeneratedAcpxSidecarEventType::RuntimeTurnTerminal,
Some("turn-1"),
malformed
json!({"status":"completed"})
))
.is_err());
}
let mut state = AcpxProviderState::new("run-1").unwrap();
state.begin_turn("turn-1").unwrap();
state.accept_event(&event(1, GeneratedAcpxSidecarEventType::RuntimeEvent, Some("turn-1"), json!({"type":"text_delta","messageId":"first","text":"","piMessageBoundary":{"phase":"start"}}))).unwrap();
assert!(state
.accept_event(&event(
2,
GeneratedAcpxSidecarEventType::RuntimeTurnTerminal,
Some("turn-1"),
json!({"status":"completed"})
))
.is_err());
}
#[test]
@@ -6970,7 +6970,12 @@ fn explicit_skill_input_survives_durable_turn_and_cold_restore() {
executor.shutdown().unwrap();
drop(executor);
let mut restored = CodexCommandExecutor::with_runner_config(&directory, &runner_config);
let skill = json!({"type":"skill", "name":"first-task", "path":"/materialized/skills/first-task/SKILL.md"});
let skill_path = if cfg!(windows) {
r"C:\materialized\skills\first-task\SKILL.md"
} else {
"/materialized/skills/first-task/SKILL.md"
};
let skill = json!({"type":"skill", "name":"first-task", "path":skill_path});
restored
.execute(&command(
"turn",
@@ -558,6 +558,7 @@ fn shutdown_recovered_acpx_fixture(executor: &mut NativeProviderCommandExecutor,
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn acpx_response_delivery_survives_crash_before_journaling_without_replaying_the_response() {
for mode in [
"permissions-interactive",
@@ -633,6 +634,7 @@ fn acpx_response_delivery_survives_crash_before_journaling_without_replaying_the
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn acpx_delivered_response_is_acknowledged_once_before_session_close() {
for mode in ["permissions-interactive", "resolutions"] {
let directory = temporary_directory("response-ack");
@@ -662,6 +664,7 @@ fn acpx_delivered_response_is_acknowledged_once_before_session_close() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn acpx_uncommitted_delivery_expires_after_failed_state_write_without_replay() {
for mode in ["permissions-interactive", "resolutions"] {
let directory = temporary_directory("response-save-failure");
@@ -743,6 +746,7 @@ fn acpx_uncommitted_delivery_expires_after_failed_state_write_without_replay() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn acpx_failed_settlement_ack_replays_the_same_delivered_event_without_expiry() {
for mode in ["permissions-interactive", "resolutions"] {
let directory = temporary_directory("response-ack-save-failure");
@@ -808,6 +812,7 @@ fn acpx_failed_settlement_ack_replays_the_same_delivered_event_without_expiry()
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn acpx_uncommitted_expiry_and_cancellation_cannot_escape_through_poll_or_drain() {
for (mode, close) in [
("permissions-wrong-ack", false),
@@ -879,6 +884,7 @@ fn acpx_uncommitted_expiry_and_cancellation_cannot_escape_through_poll_or_drain(
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn acpx_failed_or_cancelled_responses_retain_one_durable_settlement() {
for (mode, close) in [
("permissions-wrong-ack", false),
@@ -937,6 +943,7 @@ fn acpx_failed_or_cancelled_responses_retain_one_durable_settlement() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn preserves_acpx_semantic_disposition_in_the_run_terminal() {
let directory = temporary_directory("acpx-blocked");
let config = acpx_config(&directory, "turns-reserved-block-terminal");
@@ -1072,6 +1079,7 @@ fn preserves_managed_provider_descriptors_through_the_native_selector() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn executes_a_qualified_acpx_profile_through_the_native_selector() {
let directory = temporary_directory("acpx");
let config = acpx_config(&directory, "turns-reserved-result-terminal");
@@ -1131,6 +1139,7 @@ fn executes_a_qualified_acpx_profile_through_the_native_selector() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn routes_reserved_completion_feedback_with_the_full_controller_catalog() {
let directory = temporary_directory("acpx-controller-completion");
let mode = "turns-reserved-feedback-roundtrip";
@@ -1215,6 +1224,7 @@ fn routes_reserved_completion_feedback_with_the_full_controller_catalog() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn acpx_transport_failure_retains_its_error_without_reopening_the_provider() {
let directory = temporary_directory("acpx-transport-failure");
let mode = "turns-malformed-activity";
@@ -1276,6 +1286,7 @@ fn acpx_transport_failure_retains_its_error_without_reopening_the_provider() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn provider_death_expires_input_and_commits_failure_without_querying_the_dead_provider() {
let directory = temporary_directory("acpx-provider-death");
let mode = "turns-provider-death";
@@ -1381,6 +1392,7 @@ fn provider_death_expires_input_and_commits_failure_without_querying_the_dead_pr
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn resumes_an_idle_acpx_session_in_a_cold_replacement_runner() {
let directory = temporary_directory("acpx-cold-idle-recovery");
let config = acpx_config(&directory, "turns-reserved-result-terminal");
@@ -1432,6 +1444,7 @@ fn resumes_an_idle_acpx_session_in_a_cold_replacement_runner() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn keeps_native_acpx_semantic_events_on_the_durable_controller_turn() {
let directory = temporary_directory("acpx-durable-turn-correlation");
let config = acpx_config(&directory, "turns-tool");
@@ -1481,6 +1494,7 @@ fn keeps_native_acpx_semantic_events_on_the_durable_controller_turn() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn starts_a_distinct_acpx_provider_turn_for_same_run_recovery() {
let directory = temporary_directory("acpx-same-run-recovery");
let config = acpx_config(&directory, "turns-reserved-result-terminal");
@@ -1537,6 +1551,7 @@ fn starts_a_distinct_acpx_provider_turn_for_same_run_recovery() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn executes_opencode_through_the_local_facade_without_codex_event_labels() {
let directory = temporary_directory("opencode");
let config = opencode_config(&directory);
@@ -1605,6 +1620,7 @@ fn executes_opencode_through_the_local_facade_without_codex_event_labels() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn replacement_shutdown_restores_the_persisted_provider_before_cleanup() {
let directory = temporary_directory("opencode-replacement-shutdown");
let config = opencode_config(&directory);
@@ -1730,6 +1746,7 @@ fn rejects_pi_without_an_explicit_model_before_starting_a_sidecar() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn pi_thinking_change_cannot_attach_to_an_existing_provider_identity() {
let directory = temporary_directory("pi-thinking-attach");
let config = pi_acpx_config(&directory, "bootstrap");
@@ -1770,6 +1787,7 @@ fn pi_thinking_change_cannot_attach_to_an_existing_provider_identity() {
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn stops_an_idle_pi_provider_before_suspension_without_waiting_for_its_close_rpc() {
let directory = temporary_directory("pi-idle-suspension");
let mut config = pi_acpx_config(&directory, "bootstrap");
@@ -1818,6 +1836,7 @@ fn stops_an_idle_pi_provider_before_suspension_without_waiting_for_its_close_rpc
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn idle_pi_stop_cannot_publish_a_reusable_identity_while_its_lifetime_fence_is_held() {
let directory = temporary_directory("pi-idle-held-lifetime");
// Linux can assign port 0 below the identity contract's dynamic-port range.
@@ -1886,6 +1905,7 @@ fn idle_pi_stop_cannot_publish_a_reusable_identity_while_its_lifetime_fence_is_h
}
#[test]
#[cfg_attr(windows, ignore = "verified launch is not supported on Windows yet")]
fn publishes_only_changed_pi_controls_before_the_new_turn() {
for (mode, initially_available, turn_available) in [
("controls-lazy", false, true),
@@ -28,6 +28,10 @@ export const ACPX_RELEASE_ATTESTATIONS = Object.freeze({
nodeVersion: ["pi-node-pins.ts", "PI_NODE_VERSION"],
closure: ["pi-closure-pins.ts", "PI_DISTRIBUTION_CLOSURE_SHA256"],
} },
kimi: { kind: "closure", pins: {
nodeVersion: ["kimi-closure-pins.ts", "KIMI_NODE_VERSION"],
closure: ["kimi-closure-pins.ts", "KIMI_DISTRIBUTION_CLOSURE_SHA256"],
} },
cursor: { kind: "declaration", path: "cursor-contract.json", digestField: "commandDigest" },
antigravity: { kind: "declaration", path: "antigravity-distributions.json" },
});
@@ -16,6 +16,7 @@ const packageFiles = [
"scripts/acpx-release-attestations.mjs", "scripts/generate-acpx-profiles.mjs",
"src/providers/grok/launcher.cjs", "src/drivers/acpx/copilot-closure-pins.ts",
"src/drivers/acpx/pi-closure-pins.ts", "src/drivers/acpx/pi-node-pins.ts",
"src/drivers/acpx/kimi-closure-pins.ts",
"src/drivers/acpx/generated-profiles.ts", "runner/crates/runner-core/src/generated_acpx_profiles.rs",
];
@@ -37,7 +38,7 @@ const generate = (root, ...args) => spawnSync(process.execPath, [join(root, "scr
test("every profile declares how its digest attests the upstream release", async () => {
assert.deepEqual(Object.keys(ACPX_RELEASE_ATTESTATIONS).sort(), Object.keys(manifest.profiles).sort());
assert.deepEqual(COMPUTED.sort(), ["antigravity", "copilot", "cursor", "grok", "pi"]);
assert.deepEqual(COMPUTED.sort(), ["antigravity", "copilot", "cursor", "grok", "kimi", "pi"]);
for (const agent of COMPUTED) {
assert.equal(manifest.profiles[agent].commandDigest, (await acpxReleaseAttestation(agent, packageRoot)).commandDigest, agent);
}
@@ -66,10 +67,11 @@ test("a relocated copy of the provider inputs computes the same attestations", a
});
test("the attestations bind no Paperclip source beyond the Grok launcher", async () => {
for (const agent of ["copilot", "pi"]) {
for (const agent of ["copilot", "pi", "kimi"]) {
const { declaration } = await acpxReleaseAttestation(agent, packageRoot);
assert.deepEqual(Object.keys(declaration).filter(key => /source/iu.test(key)), [], agent);
assert.deepEqual(Object.keys(declaration.closure).sort(), ["darwin-arm64", "darwin-x64", "linux-x64"]);
assert.deepEqual(Object.keys(declaration.closure).sort(), agent === "kimi"
? ["darwin-arm64", "linux-x64"] : ["darwin-arm64", "darwin-x64", "linux-x64"]);
}
});
@@ -86,6 +88,10 @@ for (const [name, agents, edit] of [
const path = join(root, "src/drivers/acpx/pi-closure-pins.ts");
writeFileSync(path, readFileSync(path, "utf8").replace(/"linux-x64": "[a-f0-9]{64}"/u, `"linux-x64": "${"0".repeat(64)}"`));
}],
["a Kimi platform closure", ["kimi"], root => {
const path = join(root, "src/drivers/acpx/kimi-closure-pins.ts");
writeFileSync(path, readFileSync(path, "utf8").replace(/"linux-x64": "[a-f0-9]{64}"/u, `"linux-x64": "${"0".repeat(64)}"`));
}],
["the Grok launcher bytes", ["grok"], root => {
const path = join(root, "src/providers/grok/launcher.cjs");
writeFileSync(path, `${readFileSync(path, "utf8")}\n// changed\n`);
@@ -21,6 +21,7 @@ import { dirname, join, relative, resolve } from "node:path";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import { parseProviderPackArguments, materializeCandidateProviderPack, providerPackProviders, providerPackManifestFields } from "./candidate-provider-pack.mjs";
import { smokeKimiProviderPack } from "./kimi-provider-pack-smoke.mjs";
import { buildNodeStartupTimeout } from "./build-node-startup-timeout.mjs";
import { writePortableExecutableShim, writePortableCopilotShims } from "./provider-pack-executable-shims.mjs";
import { providerPackLibc, removeControllerArtifactsFromProviderPack, removeForeignLibcPackagesFromProviderPack } from "./provider-pack-runtime-tree.mjs";
@@ -183,7 +184,7 @@ try {
|| !/^sha256:[a-f0-9]{64}$/.test(metadata.profileDigest)
|| !/^sha256:[a-f0-9]{64}$/.test(metadata.closureDigest)) throw new Error("Candidate builder omitted its pinned identity");
candidateProviders[provider] = { version: metadata.version, profileDigest: metadata.profileDigest,
closureDigest: metadata.closureDigest, qualification: ["pi", "cursor", "copilot"].includes(provider) ? "qualified" : "pending", path: assetPath,
closureDigest: metadata.closureDigest, qualification: ["pi", "cursor", "copilot"].includes(provider) || provider === "kimi" && profiles.profiles.kimi.qualificationStatus !== "pending" ? "qualified" : "pending", path: assetPath,
sha256: sha256Tree(join(temporaryRoot, assetPath)) };
}
@@ -386,6 +387,11 @@ try {
{ mode: 0o600 },
);
if (payload.providers?.kimi && profiles.profiles.kimi.qualificationStatus !== "pending") {
const smoke = await smokeKimiProviderPack(temporaryRoot);
process.stdout.write(`${JSON.stringify(smoke)}\n`);
}
rmSync(outputRoot, { recursive: true, force: true });
renameSync(temporaryRoot, outputRoot);
process.stdout.write(`${outputRoot}\n`);
@@ -8,6 +8,12 @@ import { build } from "esbuild";
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
export const verifiedProviderEntrypoints = Object.freeze([
Object.freeze({
name: "kimi-metadata-probe",
source: resolve(packageRoot, "src/cli/kimi-metadata-probe.ts"),
output: resolve(packageRoot, "dist/cli/kimi-metadata-probe.js"),
verifiedOutput: resolve(packageRoot, "dist/cli/kimi-metadata-probe.cjs"),
}),
Object.freeze({
name: "provision-antigravity",
source: resolve(packageRoot, "scripts/provision-antigravity.mjs"),
@@ -80,6 +86,7 @@ export async function bundleVerifiedProviderEntrypoints({ write = true } = {}) {
platform: "node",
format,
target: "node24",
absWorkingDir: entrypoint.name === "kimi-metadata-probe" ? packageRoot : undefined,
packages: "bundle",
splitting: false,
sourcemap: false,
@@ -116,6 +123,7 @@ export async function bundleVerifiedProviderEntrypoints({ write = true } = {}) {
results.push({ entrypoint, result, verifiedResult });
}
await bundlePiProvisioner({ write });
await bundleKimiProvisioner({ write });
return results;
}
@@ -146,6 +154,28 @@ export async function bundlePiProvisioner({ write = true, outputRoot = resolve(p
return result;
}
export async function bundleKimiProvisioner({ write = true, outputRoot = resolve(packageRoot, "dist/cli") } = {}) {
const entrypoint = { name: "provision-kimi", source: resolve(packageRoot, "scripts/provision-kimi.mjs") };
const result = await build({
entryPoints: [entrypoint.source], outfile: resolve(outputRoot, "provision-kimi.cjs"),
bundle: true, platform: "node", format: "cjs", target: "node24", packages: "bundle",
splitting: false, sourcemap: false, legalComments: "none", metafile: true,
treeShaking: true, write, logLevel: "silent",
banner: { js: 'const __paperclipVerifiedEntrypointUrl = require("node:url").pathToFileURL(__filename).href;' },
define: { "import.meta.dirname": "__dirname", "import.meta.url": "__paperclipVerifiedEntrypointUrl" },
});
assertSelfContainedBundle(entrypoint, result);
if (write) {
const inputs = resolve(outputRoot, "kimi-provision-inputs");
await mkdir(inputs, { recursive: true });
for (const [source, name] of [
["scripts/kimi-distribution/package.json", "package.json"],
["scripts/kimi-distribution/package-lock.json", "package-lock.json"],
]) await cp(resolve(packageRoot, source), resolve(inputs, name));
}
return result;
}
const invokedPath = process.argv[1]
? pathToFileURL(resolve(process.argv[1])).href
: null;
@@ -41,6 +41,11 @@ test("written provider entrypoints satisfy qualified launch permissions", async
// Package-layout regression runs in the existing verified-entrypoint test lane.
import "./provision-pi-package.test.mjs";
import "./materialize-kimi-distribution.test.mjs";
import "./provision-kimi-package.test.mjs";
import "./patch-kimi-runtime.test.mjs";
import "./kimi-metadata-probe.test.mjs";
import "./antigravity-provisioner-layout.test.mjs";
import "./materialize-antigravity-distribution.test.mjs";
import "./probe-antigravity-metadata.test.mjs";
@@ -1,19 +1,22 @@
import { materializePiDistribution } from "./materialize-pi-distribution.mjs";
import profiles from "../acpx-profiles.json" with { type: "json" };
import { materializePinnedCursorDistribution } from "./materialize-cursor-distribution.mjs";
const CANDIDATES = new Set(["cursor", "copilot", "pi"]);
const CANDIDATES = new Set(["cursor", "copilot", "pi", "kimi"]);
const releasedKimi = () => profiles.profiles.kimi.qualificationStatus !== "pending";
const ordinaryProviders = () => ["pi", "cursor", "copilot", ...(releasedKimi() ? ["kimi"] : [])];
/** Only materialized providers enter the serialized manifest and its digest. */
export function providerPackManifestFields(providers, candidates) {
return {
...(["pi", "cursor", "copilot"].some(provider => providers[provider]) ? { providers: Object.fromEntries(["pi", "cursor", "copilot"].filter(provider => providers[provider]).map(provider => [provider, providers[provider]])) } : {}),
...(["pi", ...candidates].some(provider => provider !== "copilot" && providers[provider]) ? { candidateProviders: Object.fromEntries([...new Set(["pi", ...candidates])].filter(provider => provider !== "copilot" && providers[provider]).map(provider => [provider, providers[provider]])) } : {}),
...(ordinaryProviders().some(provider => providers[provider]) ? { providers: Object.fromEntries(ordinaryProviders().filter(provider => providers[provider]).map(provider => [provider, providers[provider]])) } : {}),
...(["pi", ...candidates].some(provider => provider !== "copilot" && !(provider === "kimi" && releasedKimi()) && providers[provider]) ? { candidateProviders: Object.fromEntries([...new Set(["pi", ...candidates])].filter(provider => provider !== "copilot" && !(provider === "kimi" && releasedKimi()) && providers[provider]).map(provider => [provider, providers[provider]])) } : {}),
};
}
export function providerPackProviders(platform, architecture, candidates) {
const nativeSupported = ["darwin-arm64", "darwin-x64", "linux-x64"].includes(`${platform}-${architecture}`);
return [...new Set([...(nativeSupported ? ["pi", "cursor", "copilot"] : []), ...candidates])];
const kimiSupported = ["darwin-arm64", "linux-x64"].includes(`${platform}-${architecture}`);
return [...new Set([...(nativeSupported ? ["pi", "cursor", "copilot"] : []), ...(releasedKimi() && kimiSupported ? ["kimi"] : []), ...candidates])];
}
export function parseProviderPackArguments(args) {
@@ -34,6 +37,11 @@ export function parseProviderPackArguments(args) {
/** Closed source-owned builder registry; provider branches add their exact pins. */
export async function materializeCandidateProviderPack({ provider, outputRoot }) {
if (!CANDIDATES.has(provider)) throw new Error("Unknown candidate provider");
if (provider === "kimi") {
const { materializeKimiDistribution } = await import("./materialize-kimi-distribution.mjs");
const result = await materializeKimiDistribution({ outputRoot });
return { version: result.version, profileDigest: profiles.profiles.kimi.commandDigest, closureDigest: result.closureDigest };
}
if (provider === "pi") return materializePiDistribution({ outputRoot });
if (provider === "copilot") {
const { buildPinnedCopilotDistribution } = await import("./build-copilot-distribution.mjs");
@@ -43,11 +43,36 @@ test("provider manifest digest survives disk JSON on supported and unsupported t
test("candidate options stay explicit and supported native assets are included by default", () => {
assert.deepEqual(parseProviderPackArguments(["--", "/pack"]), { output: "/pack", candidates: [] });
assert.deepEqual(parseProviderPackArguments(["/pack", "--candidate-providers=pi,cursor"]), { output: "/pack", candidates: ["pi", "cursor"] });
assert.deepEqual(parseProviderPackArguments(["/pack", "--candidate-providers=kimi"]), { output: "/pack", candidates: ["kimi"] });
});
test("released Kimi enters ordinary inventory on only its two supported targets", () => {
const kimi = { version: "2.3.3", qualification: "qualified", profileDigest: "sha256:profile", closureDigest: "sha256:closure" };
for (const [platform, architecture] of [["darwin", "arm64"], ["linux", "x64"]]) {
assert.equal(providerPackProviders(platform, architecture, []).filter(provider => provider === "kimi").length, 1);
assert.equal(providerPackProviders(platform, architecture, ["kimi"]).filter(provider => provider === "kimi").length, 1);
}
for (const [platform, architecture] of [["darwin", "x64"], ["linux", "arm64"], ["win32", "x64"]]) {
assert.equal(providerPackProviders(platform, architecture, []).includes("kimi"), false);
}
for (const candidates of [[], ["kimi"]]) {
const fields = providerPackManifestFields({ kimi }, candidates);
assert.equal(fields.providers.kimi, kimi);
assert.equal(fields.candidateProviders?.kimi, undefined);
assert.equal(digest(JSON.parse(JSON.stringify(fields))), digest(fields));
}
});
test("Kimi's candidate builder rejects invalid paths before any public download", async t => {
let requests = 0;
t.mock.method(globalThis, "fetch", async () => { requests++; throw new Error("Unexpected Kimi download"); });
await assert.rejects(materializeCandidateProviderPack({ provider: "kimi", outputRoot: "relative-kimi-output" }), /Kimi distribution output must be absolute/);
assert.equal(requests, 0);
});
test("normal packs include Cursor and Copilot on their three pinned targets without breaking other hosts", () => {
for (const [platform, architecture] of [["darwin", "arm64"], ["darwin", "x64"], ["linux", "x64"]]) {
assert.deepEqual(providerPackProviders(platform, architecture, []), ["pi", "cursor", "copilot"]);
assert.deepEqual(providerPackProviders(platform, architecture, ["cursor"]), ["pi", "cursor", "copilot"]);
assert.deepEqual(providerPackProviders(platform, architecture, []), ["pi", "cursor", "copilot", ...((platform === "darwin" && architecture === "arm64") || platform === "linux" ? ["kimi"] : [])]);
assert.deepEqual(providerPackProviders(platform, architecture, ["cursor"]), providerPackProviders(platform, architecture, []));
}
assert.deepEqual(providerPackProviders("linux", "arm64", []), []);
assert.deepEqual(providerPackProviders("win32", "x64", []), []);
@@ -1,3 +1,4 @@
import { KIMI_DISTRIBUTION_CLOSURE_SHA256 } from "../src/drivers/acpx/kimi-closure-pins.ts";
import assert from "node:assert/strict";
import { readFile, writeFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
@@ -90,6 +91,13 @@ pub(crate) struct AcpxReleaseProfile {
pub qualification_pending: bool,
}
pub(crate) fn kimi_native_closure(platform: &str, architecture: &str) -> Option<&'static str> {
match (platform, architecture) {
${Object.entries(KIMI_DISTRIBUTION_CLOSURE_SHA256).map(([target, digest]) => { const [platform, architecture] = target.split("-"); return ` (${quote(platform)}, ${quote(architecture)}) => {\n Some(${quote(digest)})\n }`; }).join("\n")}
_ => None,
}
}
pub(crate) fn acpx_release_profile(agent: &str) -> Option<AcpxReleaseProfile> {
Some(match agent {
${entries.map(([agent, p]) => ` ${quote(agent)} => AcpxReleaseProfile {
@@ -0,0 +1,606 @@
{
"name": "paperclip-kimi-runtime-distribution",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "paperclip-kimi-runtime-distribution",
"version": "1.0.0",
"dependencies": {
"@moonshot-ai/kimi-code": "2.3.3"
},
"engines": {
"node": ">=24.11.0"
}
},
"node_modules/@mariozechner/clipboard": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.9.tgz",
"integrity": "sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@mariozechner/clipboard-darwin-arm64": "0.3.9",
"@mariozechner/clipboard-darwin-universal": "0.3.9",
"@mariozechner/clipboard-darwin-x64": "0.3.9",
"@mariozechner/clipboard-linux-arm64-gnu": "0.3.9",
"@mariozechner/clipboard-linux-arm64-musl": "0.3.9",
"@mariozechner/clipboard-linux-riscv64-gnu": "0.3.9",
"@mariozechner/clipboard-linux-x64-gnu": "0.3.9",
"@mariozechner/clipboard-linux-x64-musl": "0.3.9",
"@mariozechner/clipboard-win32-arm64-msvc": "0.3.9",
"@mariozechner/clipboard-win32-x64-msvc": "0.3.9"
}
},
"node_modules/@mariozechner/clipboard-darwin-arm64": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-arm64/-/clipboard-darwin-arm64-0.3.9.tgz",
"integrity": "sha512-BfgV7vCEWZwJwZJw03r6bP5+tf0iI/ANuQYCxi9RNn7FrWB3yzGuMKCrNLRl6V761vXRdL8+OqZ0wd4TqlsNOQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@mariozechner/clipboard-darwin-universal": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.9.tgz",
"integrity": "sha512-BGGR4iA9Z2shAjI65eI5xtyb3LYNlDW9X3gxKxDbqtbnREohsrqznov6zpKoIrsRWpzlYVEdKphS7ksJ0/ndSQ==",
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@mariozechner/clipboard-darwin-x64": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-x64/-/clipboard-darwin-x64-0.3.9.tgz",
"integrity": "sha512-4kURmCbS6nt8uYhtmWpUcJWyPHfmAr5dTpXD1nO3pIfa+TSQ9DbrGOYCKH+aEFW47XhQ4Vp8ZTszie+wfFvDKg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@mariozechner/clipboard-linux-arm64-gnu": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-gnu/-/clipboard-linux-arm64-gnu-0.3.9.tgz",
"integrity": "sha512-g59OkUGP2DDfCOIKypHeYgv2M55u/cKvXa5dSxFbEJ34XvIQMdcVmpKCkGUro3ZgefXiGVdwguvTMQGpHWzIXw==",
"cpu": [
"arm64"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@mariozechner/clipboard-linux-arm64-musl": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-musl/-/clipboard-linux-arm64-musl-0.3.9.tgz",
"integrity": "sha512-AGuJdgKsmJdm4Pych7kv3sqe591ERRaAHW3xjLooiFzn8J+PxUyof++7YZrB5Y5tpnTO+K18Og3taj2NpluCRQ==",
"cpu": [
"arm64"
],
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@mariozechner/clipboard-linux-riscv64-gnu": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-riscv64-gnu/-/clipboard-linux-riscv64-gnu-0.3.9.tgz",
"integrity": "sha512-DXBEAiuMpk7dhS1a9NzNxVAFi1vaKoPu7rQNgY8LIDLGrK3lnIp3nT10DUum+PKVJoJppIP+NAA8IZe4DMNDPw==",
"cpu": [
"riscv64"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@mariozechner/clipboard-linux-x64-gnu": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.9.tgz",
"integrity": "sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw==",
"cpu": [
"x64"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@mariozechner/clipboard-linux-x64-musl": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.9.tgz",
"integrity": "sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ==",
"cpu": [
"x64"
],
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@mariozechner/clipboard-win32-arm64-msvc": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-arm64-msvc/-/clipboard-win32-arm64-msvc-0.3.9.tgz",
"integrity": "sha512-O5FHD3ErkMwMhNzAfu3ggy0ug4z7btZuoQgwwxlzPrwV2bxlD6WDpqBY4NCgICAgZdDKdp+loUEKVAVt8aYnhQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@mariozechner/clipboard-win32-x64-msvc": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-x64-msvc/-/clipboard-win32-x64-msvc-0.3.9.tgz",
"integrity": "sha512-ihQC3EufqEY81vhXBgVBtK4prL+wc62zJsSvxrgz7K1hsdt6OObz6v9p3Rn1OG3GJksTTKMJF0u/guMISHPhSA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@moonshot-ai/kimi-code": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@moonshot-ai/kimi-code/-/kimi-code-2.3.3.tgz",
"integrity": "sha512-3V8bycucf9KgPRH27S8De2u0ip84/Ry3FpNn6gi5z9JmoEHt9vxiYHHZqiMdyDf5nPm4bv+YKGwKEQL4oYpRTw==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"chokidar": "^4.0.3",
"qrcode": "^1.5.4",
"ws": "^8.18.0"
},
"bin": {
"kimi": "dist/main.mjs"
},
"engines": {
"node": ">=22.19.0"
},
"optionalDependencies": {
"@mariozechner/clipboard": "^0.3.9",
"node-pty": "^1.1.0"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/dijkstrajs": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
"license": "MIT"
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"license": "MIT",
"optional": true
},
"node_modules/node-pty": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz",
"integrity": "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"node-addon-api": "^7.1.0"
}
},
"node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/pngjs": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
"license": "MIT",
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/qrcode": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
"license": "MIT",
"dependencies": {
"dijkstrajs": "^1.0.1",
"pngjs": "^5.0.0",
"yargs": "^15.3.1"
},
"bin": {
"qrcode": "bin/qrcode"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"license": "ISC"
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"license": "ISC"
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/which-module": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
"license": "ISC"
},
"node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ws": {
"version": "8.22.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz",
"integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"license": "ISC"
},
"node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"license": "MIT",
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"license": "ISC",
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"engines": {
"node": ">=6"
}
}
}
}
@@ -0,0 +1,11 @@
{
"name": "paperclip-kimi-runtime-distribution",
"version": "1.0.0",
"private": true,
"dependencies": {
"@moonshot-ai/kimi-code": "2.3.3"
},
"engines": {
"node": ">=24.11.0"
}
}
@@ -0,0 +1,34 @@
export const KIMI_INSTRUCTION_RUNTIME_SOURCE = [
"function createKimiInstructionRuntime(raw, { createHash, byteLength }) {",
" const schema = \"paperclip.kimi.instructions.v1\";",
" const fail = () => { throw new Error(\"Kimi instruction binding is missing or invalid\"); };",
" if (typeof raw !== \"string\" || byteLength(raw) > 200_000) fail();",
" let value;",
" try { value = JSON.parse(raw); } catch { fail(); }",
" if (!value || typeof value !== \"object\" || Array.isArray(value)",
" || Object.keys(value).sort().join(\",\") !== \"byteLength,content,digest,schema\"",
" || value.schema !== schema || typeof value.content !== \"string\"",
" || value.content.includes(\"\\0\") || !value.content.isWellFormed()",
" || byteLength(value.content) > 32_768 || value.byteLength !== byteLength(value.content)",
" || typeof value.digest !== \"string\") fail();",
" const digest = text => `sha256:${createHash(\"sha256\").update(text, \"utf8\").digest(\"hex\")}`;",
" const content = value.content;",
" if (value.digest !== digest(content)) fail();",
" const suffix = content.length === 0 ? \"\" : `\\n\\n${content}`;",
" const append = base => {",
" if (typeof base !== \"string\" || byteLength(base) > 2_097_152) fail();",
" return suffix.length === 0 || base.endsWith(suffix) ? base : `${base}${suffix}`;",
" };",
" return Object.freeze({",
" append,",
" receipt(sessionId, effectivePrompt) {",
" if (typeof sessionId !== \"string\" || !sessionId.trim() || append(effectivePrompt) !== effectivePrompt) fail();",
" return Object.freeze({ schema, sessionId, digest: value.digest,",
" byteLength: byteLength(content), effectiveDigest: digest(effectivePrompt),",
" effectiveByteLength: byteLength(effectivePrompt) });",
" },",
" });",
"}",
].join("\n");
export const createKimiInstructionRuntime = new Function(`return (${KIMI_INSTRUCTION_RUNTIME_SOURCE});`)();
@@ -0,0 +1,127 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdtemp, mkdir, readFile, realpath, rm, stat, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import test from "node:test";
import { fileURLToPath, pathToFileURL } from "node:url";
import { build } from "esbuild";
import { bundleVerifiedProviderEntrypoints } from "./build-verified-provider-entrypoints.mjs";
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const sha = bytes => `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
test("Kimi metadata CLI is self-contained and rejects incomplete assets without inheriting credentials or making requests", { timeout: 20_000 }, async t => {
const root = await realpath(await mkdtemp(join(tmpdir(), "paperclip-kimi-metadata-cli-")));
t.after(() => rm(root, { recursive: true, force: true }));
const bundles = await bundleVerifiedProviderEntrypoints({ write: false });
const bundle = bundles.find(value => value.entrypoint.name === "kimi-metadata-probe");
assert.ok(bundle, "The ordinary provider pack build must include the metadata CLI");
const bytes = bundle.verifiedResult.outputFiles[0].contents;
const relocatedBuild = spawnSync(process.execPath, ["--input-type=module", "-e", `
import { createHash } from "node:crypto";
import { bundleVerifiedProviderEntrypoints } from ${JSON.stringify(new URL("./build-verified-provider-entrypoints.mjs", import.meta.url).href)};
const bundles = await bundleVerifiedProviderEntrypoints({ write: false });
const bytes = bundles.find(value => value.entrypoint.name === "kimi-metadata-probe").verifiedResult.outputFiles[0].contents;
process.stdout.write("sha256:" + createHash("sha256").update(bytes).digest("hex"));
`], { cwd: root, env: { PATH: "/usr/bin:/bin" }, encoding: "utf8", timeout: 10_000 });
assert.ifError(relocatedBuild.error);
assert.equal(relocatedBuild.status, 0, relocatedBuild.stderr);
assert.equal(relocatedBuild.stdout, sha(bytes), "Metadata CLI admission bytes must be independent of the build cwd");
const entry = join(root, "kimi-metadata-probe.cjs");
await writeFile(entry, bytes);
assert.doesNotMatch(Buffer.from(bytes).toString(), /\bimport\.meta\b/);
for (const output of Object.values(bundle.verifiedResult.metafile.outputs)) {
for (const dependency of output.imports) assert.match(dependency.path, /^node:/);
}
const manifest = join(root, "package.json");
await writeFile(manifest, '{"name":"@paperclipai/paperclip-runner","type":"module"}');
const preload = join(root, "deny-external-work.cjs");
await writeFile(preload, `
const assert = require("node:assert/strict");
process.nextTick(() => {
for (const name of ["KIMI_API_KEY", "PAPERCLIP_KIMI_PROBE_MODEL", "KIMI_MODEL_DEFAULT", "HOME", "HTTPS_PROXY", "NODE_OPTIONS", "NODE_PATH", "ANTHROPIC_API_KEY", "PAPERCLIP_ACPX_CANDIDATE_ADMISSION"]) {
assert.equal(process.env[name], undefined, name);
}
assert.equal(process.env.PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT, ${JSON.stringify(root)});
});
const fail = () => { throw Error("UNEXPECTED_NETWORK_OR_CHILD"); };
globalThis.fetch = fail;
for (const name of ["node:net", "node:tls", "node:http", "node:https"]) {
const dependency = require(name);
for (const key of ["connect", "createConnection", "request", "get"]) if (key in dependency) dependency[key] = fail;
}
const child = require("node:child_process");
for (const key of ["spawn", "exec", "execFile"]) child[key] = fail;
`);
const pinnedNode = join(packageRoot, "provider-assets/kimi", `${process.platform}-${process.arch}`, "runtime/node/bin/node");
const node = await stat(pinnedNode).then(() => pinnedNode, () => process.execPath);
const profile = JSON.parse(await readFile(join(packageRoot, "acpx-profiles.json"), "utf8")).profiles.kimi;
const { KIMI_DISTRIBUTION_CLOSURE_SHA256 } = await import(new URL("../src/drivers/acpx/kimi-closure-pins.ts", import.meta.url));
const admission = { schema: "paperclip.acpx-candidate-admission.v1", companyId: "metadata-cli-company", agent: "kimi", model: "selected-model",
profileDigest: profile.commandDigest, nativeClosureDigest: `sha256:${KIMI_DISTRIBUTION_CLOSURE_SHA256[`${process.platform}-${process.arch}`]}`,
platform: process.platform, architecture: process.arch, expiresAt: Date.now() + 60_000,
runId: "metadata-operation", normalizedSessionId: "metadata-session" };
const child = spawnSync(node, ["--require", preload, entry], {
env: { KIMI_API_KEY: "selected-secret-canary", PAPERCLIP_KIMI_PROBE_MODEL: "selected-model",
...(profile.qualificationStatus === "pending" ? { PAPERCLIP_ACPX_CANDIDATE_ADMISSION: JSON.stringify(admission) } : {}),
PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT: root, PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST: manifest,
KIMI_MODEL_DEFAULT: "unselected-model", HOME: "/private/unselected-home", HTTPS_PROXY: "http://127.0.0.1:9",
ANTHROPIC_API_KEY: "unselected-secret-canary", NODE_OPTIONS: "", NODE_PATH: "/unselected-module-path" },
encoding: "utf8", timeout: 10_000,
});
assert.ifError(child.error);
assert.equal(child.status, 1);
assert.equal(child.signal, null);
assert.doesNotMatch(child.stdout + child.stderr, /UNEXPECTED_NETWORK_OR_CHILD|selected-secret-canary|unselected-secret-canary/);
assert.deepEqual(JSON.parse(child.stdout), { schema: "paperclip.kimi.metadata.v1", status: "failed",
code: "KIMI_INSTALLATION_INVALID", message: process.versions.node === "24.21.0" || node === pinnedNode
? "Install the current verified Kimi Code runtime in this execution environment."
: "Kimi metadata verification requires the pinned Node runtime.", promptSent: false });
const withoutAdmission = spawnSync(node, ["--require", preload, entry], {
env: { KIMI_API_KEY: "selected-secret-canary", PAPERCLIP_KIMI_PROBE_MODEL: "selected-model",
PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT: root, PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST: manifest },
encoding: "utf8", timeout: 10_000,
});
assert.ifError(withoutAdmission.error); assert.equal(withoutAdmission.status, 1);
if (process.versions.node === "24.21.0" || node === pinnedNode) {
assert.equal(JSON.parse(withoutAdmission.stdout).code,
profile.qualificationStatus === "pending" ? "KIMI_QUALIFICATION_REQUIRED" : "KIMI_INSTALLATION_INVALID");
}
assert.doesNotMatch(withoutAdmission.stdout + withoutAdmission.stderr, /UNEXPECTED_NETWORK_OR_CHILD|selected-secret-canary/);
if (profile.qualificationStatus !== "pending") {
const formerAdmission = spawnSync(node, ["--require", preload, entry], {
env: { KIMI_API_KEY: "selected-secret-canary", PAPERCLIP_KIMI_PROBE_MODEL: "selected-model",
PAPERCLIP_ACPX_CANDIDATE_ADMISSION: JSON.stringify(admission),
PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT: root, PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST: manifest,
HOME: "/private/unselected-home", HTTPS_PROXY: "http://127.0.0.1:9", ANTHROPIC_API_KEY: "unselected-secret-canary" },
encoding: "utf8", timeout: 10_000,
});
assert.ifError(formerAdmission.error); assert.equal(formerAdmission.status, 1); assert.equal(formerAdmission.signal, null);
assert.deepEqual(JSON.parse(formerAdmission.stdout), { schema: "paperclip.kimi.metadata.v1", status: "failed",
code: "KIMI_REQUEST_FAILED", message: "Kimi Code metadata verification failed.", promptSent: false });
assert.doesNotMatch(formerAdmission.stdout + formerAdmission.stderr, /UNEXPECTED_NETWORK_OR_CHILD|selected-secret-canary|unselected-secret-canary/);
}
for (const layout of ["runner", "server"]) {
const packageDirectory = join(root, layout);
const outputRoot = layout === "server" ? join(packageDirectory, "dist/vendor/paperclip-runner") : join(packageDirectory, "dist");
const helper = join(outputRoot, "drivers/acpx/kimi-metadata-probe.js");
const cli = join(outputRoot, "cli/kimi-metadata-probe.cjs");
await mkdir(dirname(cli), { recursive: true });
await writeFile(join(packageDirectory, "package.json"), '{"type":"module"}');
await writeFile(cli, bytes);
await build({ entryPoints: [join(packageRoot, "src/drivers/acpx/kimi-metadata-probe.ts")], outfile: helper,
bundle: true, platform: "node", format: "esm", target: "node24", logLevel: "silent" });
const api = await import(pathToFileURL(helper).href);
assert.equal(await api.kimiMetadataProbeDigest(), sha(bytes), `${layout} fixed artifact`);
await writeFile(cli, "different artifact bytes");
assert.equal(await api.kimiMetadataProbeDigest(), sha("different artifact bytes"), `${layout} digest is fresh`);
await rm(cli);
await symlink(entry, cli);
await assert.rejects(api.kimiMetadataProbeDigest(), /ELOOP|symbolic/i);
}
});
@@ -0,0 +1,131 @@
import assert from "node:assert/strict";
import { once } from "node:events";
import { mkdir, mkdtemp, readFile, realpath, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { pathToFileURL } from "node:url";
export function assertKimiProviderPackInventory(manifest, profile, closures, platform = process.platform, architecture = process.arch) {
assert.equal(manifest.schema, "paperclip-runner/remote-provider-pack/v1");
assert.deepEqual(manifest.payload.target, { platform, architecture });
const target = `${platform}-${architecture}`;
assert.ok(["darwin-arm64", "linux-x64"].includes(target));
const pending = profile.qualificationStatus === "pending";
const identity = pending ? manifest.payload.candidateProviders?.kimi : manifest.payload.providers?.kimi;
assert.equal(pending ? manifest.payload.providers?.kimi : manifest.payload.candidateProviders?.kimi, undefined);
assert.equal(identity?.qualification, pending ? "pending" : "qualified");
assert.equal(identity.version, profile.agentServerVersion);
assert.equal(identity.path, `provider-assets/kimi/${target}`);
assert.equal(identity.profileDigest, profile.commandDigest);
assert.match(closures[target] ?? "", /^[a-f0-9]{64}$/);
assert.equal(identity.closureDigest, `sha256:${closures[target]}`);
assert.match(identity.sha256, /^sha256:[a-f0-9]{64}$/);
return identity;
}
export async function smokeKimiProviderPack(directory) {
const pack = await realpath(resolve(directory));
const manifest = JSON.parse(await readFile(join(pack, "provider-pack.json"), "utf8"));
assert.equal(manifest.schema, "paperclip-runner/remote-provider-pack/v1");
assert.equal(manifest.payload.target.platform, process.platform);
assert.equal(manifest.payload.target.architecture, process.arch);
if (process.env.PAPERCLIP_EXPECTED_SOURCE_REVISION) assert.equal(manifest.payload.runnerSourceRevision, process.env.PAPERCLIP_EXPECTED_SOURCE_REVISION);
const load = path => import(pathToFileURL(join(pack, "dist/drivers/acpx", path)));
const [{ verifyKimiInstallation }, { QUALIFIED_ACPX_PROFILES }, { kimiManagedConfiguration }, { kimiInstructionBinding }, { KIMI_DISTRIBUTION_CLOSURE_SHA256 }] = await Promise.all([
load("kimi-installation.js"), load("qualified-profiles.js"), load("kimi-config.js"), load("kimi-instructions.js"), load("kimi-closure-pins.js"),
]);
const provider = assertKimiProviderPackInventory(manifest, QUALIFIED_ACPX_PROFILES.kimi, KIMI_DISTRIBUTION_CLOSURE_SHA256);
const keys = ["PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT", "PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST"];
const previous = keys.map(key => process.env[key]);
process.env[keys[0]] = pack; process.env[keys[1]] = join(pack, "package.json");
const root = await realpath(await mkdtemp(join(tmpdir(), "paperclip-kimi-pack-smoke-")));
const home = join(root, "home"), workspace = join(root, "workspace");
await mkdir(home); await mkdir(workspace);
const model = "kimi-for-coding";
const binding = kimiInstructionBinding("Do not call a model during this installed-package smoke. Exact suffix: café.\n");
await writeFile(join(home, "config.toml"), kimiManagedConfiguration(model), { mode: 0o600 });
const active = new Set(); let sequence = 0;
try {
const installation = await verifyKimiInstallation(QUALIFIED_ACPX_PROFILES.kimi);
const connect = async () => {
const lease = await installation.openCommand();
const child = lease.spawn([], { cwd: workspace, stdio: "pipe", env: {
PATH: "/usr/bin:/bin", LANG: "C.UTF-8", KIMI_CODE_HOME: home,
KIMI_CODE_NO_AUTO_UPDATE: "1", KIMI_API_KEY: "paperclip-offline-placeholder",
PAPERCLIP_KIMI_INSTRUCTIONS: binding.payload, CI: "1", NO_COLOR: "1", TERM: "dumb",
} });
const closed = once(child, "close"), pending = new Map();
let buffer = "", bytes = 0;
const fail = error => { for (const request of pending.values()) request.reject(error); pending.clear(); };
child.on("error", fail); child.on("close", () => fail(new Error("Kimi exited with a pending ACP request")));
child.stderr.resume();
child.stdout.on("data", chunk => {
bytes += chunk.length;
if (bytes > 1024 * 1024) { fail(new Error("Kimi ACP output exceeded its package smoke bound")); return; }
buffer += chunk.toString("utf8");
let newline;
while ((newline = buffer.indexOf("\n")) >= 0) {
const line = buffer.slice(0, newline); buffer = buffer.slice(newline + 1);
if (!line.trim()) continue;
let value;
try { value = JSON.parse(line); } catch { fail(new Error("Kimi emitted malformed ACP JSON")); return; }
const request = pending.get(value.id);
if (request && value.method === undefined) {
pending.delete(value.id);
if (value.error) request.reject(new Error(`Kimi rejected ACP request (${value.error.code})`));
else request.resolve(value.result);
}
}
});
const request = (method, params) => new Promise((accept, reject) => {
const id = sequence++;
const timeout = setTimeout(() => { pending.delete(id); reject(new Error(`Kimi ${method} timed out`)); }, 15_000);
const settle = callback => value => { clearTimeout(timeout); callback(value); };
pending.set(id, { resolve: settle(accept), reject: settle(reject) });
child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}\n`);
});
let closing;
const close = () => closing ??= (async () => {
const started = Date.now(); child.stdin.end();
const term = setTimeout(() => child.kill("SIGTERM"), 4_000), kill = setTimeout(() => child.kill("SIGKILL"), 5_000);
try {
const [code, signal] = await closed;
assert.equal(code, 0, "Kimi did not exit cleanly after EOF"); assert.equal(signal, null);
return Date.now() - started;
} finally { clearTimeout(term); clearTimeout(kill); await lease.close(); active.delete(close); }
})();
active.add(close);
const initialized = await request("initialize", { protocolVersion: 1, clientCapabilities: {}, clientInfo: { name: "paperclip-provider-pack-smoke", version: "1" } });
assert.equal(initialized.agentInfo?.version, "2.3.3");
return { request, close };
};
const assertBinding = (result, sessionId) => {
const ack = result._meta?.paperclipKimiInstructions;
assert.equal(ack?.sessionId, sessionId); assert.equal(ack.digest, binding.digest); assert.equal(ack.byteLength, binding.byteLength);
assert.match(ack.effectiveDigest, /^sha256:[a-f0-9]{64}$/);
};
const first = await connect();
const created = await first.request("session/new", { cwd: workspace, mcpServers: [] });
assertBinding(created, created.sessionId);
const selected = await first.request("session/set_config_option", { sessionId: created.sessionId, configId: "model", value: model });
assert.equal(selected.configOptions.find(option => option.id === "model").currentValue, model);
const createExitMs = await first.close();
const second = await connect();
const loaded = await second.request("session/load", { sessionId: created.sessionId, cwd: workspace, mcpServers: [] });
assertBinding(loaded, created.sessionId);
assert.equal(loaded.configOptions.find(option => option.id === "model").currentValue, model);
const loadExitMs = await second.close();
return { schema: "paperclip.kimi-provider-pack-smoke.v1", sourceRevision: manifest.payload.runnerSourceRevision, providerPackDigest: manifest.digest,
platform: process.platform, architecture: process.arch, runtimeVersion: "2.3.3", profileDigest: provider.profileDigest, closureDigest: provider.closureDigest,
createAcknowledged: true, loadAcknowledged: true, model, createExitMs, loadExitMs, providerPrompts: 0, providerCredentials: false,
qualification: "installed runtime only; no model entitlement or task completion" };
} finally {
try { await Promise.all([...active].map(close => close())); }
finally { keys.forEach((key, index) => { if (previous[index] === undefined) delete process.env[key]; else process.env[key] = previous[index]; }); await rm(root, { recursive: true, force: true }); }
}
}
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
if (process.argv.length !== 3) throw new Error("Usage: node scripts/kimi-provider-pack-smoke.mjs /absolute/installed-provider-pack");
smokeKimiProviderPack(process.argv[2]).then(result => console.log(JSON.stringify(result))).catch(error => { console.error(error.message); process.exitCode = 1; });
}
@@ -0,0 +1,62 @@
import assert from "node:assert/strict";
import test from "node:test";
import { readFileSync } from "node:fs";
import { assertKimiProviderPackInventory } from "./kimi-provider-pack-smoke.mjs";
import profiles from "../acpx-profiles.json" with { type: "json" };
import historical from "../test/fixtures/kimi-profile-v14-identity.json" with { type: "json" };
import { KIMI_DISTRIBUTION_CLOSURE_SHA256 as closures } from "../src/drivers/acpx/kimi-closure-pins.ts";
function fixture(profile, target) {
const [platform, architecture] = target.split("-");
const pending = profile.qualificationStatus === "pending";
return { schema: "paperclip-runner/remote-provider-pack/v1", payload: { target: { platform, architecture },
[pending ? "candidateProviders" : "providers"]: { kimi: { version: profile.agentServerVersion,
qualification: pending ? "pending" : "qualified", profileDigest: profile.commandDigest,
closureDigest: `sha256:${closures[target]}`, path: `provider-assets/kimi/${target}`, sha256: `sha256:${"a".repeat(64)}` } } } };
}
for (const target of ["darwin-arm64", "linux-x64"]) {
test(`ordinary ${target} smoke needs released current17 and no candidate entry`, () => {
const profile = profiles.profiles.kimi;
assert.equal(profile.agentProfileVersion, 17);
assert.notEqual(profile.qualificationStatus, "pending");
const manifest = fixture(profile, target), [platform, architecture] = target.split("-");
assert.equal(assertKimiProviderPackInventory(manifest, profile, closures, platform, architecture), manifest.payload.providers.kimi);
for (const change of [{ profileDigest: historical.commandDigest }, { closureDigest: "sha256:" + "b".repeat(64) },
{ qualification: "pending" }, { path: "provider-assets/kimi/foreign" }]) {
const invalid = structuredClone(manifest); Object.assign(invalid.payload.providers.kimi, change);
assert.throws(() => assertKimiProviderPackInventory(invalid, profile, closures, platform, architecture));
}
const duplicate = structuredClone(manifest); duplicate.payload.candidateProviders = { kimi: duplicate.payload.providers.kimi };
assert.throws(() => assertKimiProviderPackInventory(duplicate, profile, closures, platform, architecture));
assert.throws(() => assertKimiProviderPackInventory(manifest, profile, closures, "linux", "arm64"));
});
test(`historical pending14 ${target} remains a distinct inventory contract`, () => {
const profile = { ...historical.declaration, commandDigest: historical.commandDigest };
const manifest = fixture(profile, target), [platform, architecture] = target.split("-");
assert.equal(profile.qualificationStatus, "pending");
assert.equal(assertKimiProviderPackInventory(manifest, profile, closures, platform, architecture), manifest.payload.candidateProviders.kimi);
assert.throws(() => assertKimiProviderPackInventory(manifest, profiles.profiles.kimi, closures, platform, architecture));
});
}
test("the actual builder verifies ordinary Kimi before replacing the retained pack", async () => {
const source = readFileSync(new URL("./build-provider-pack.mjs", import.meta.url), "utf8");
const begin = source.indexOf(' if (payload.providers?.kimi && profiles.profiles.kimi.qualificationStatus !== "pending") {');
const end = source.indexOf('\n} finally {', begin);
assert.ok(begin > 0 && end > begin);
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
const run = new AsyncFunction("payload", "profiles", "smokeKimiProviderPack", "temporaryRoot", "outputRoot", "process", "rmSync", "renameSync", source.slice(begin, end));
for (const failed of [false, true]) {
const order = [], smoke = async root => { assert.equal(root, "/owned/temporary"); order.push("smoke"); if (failed) throw Error("smoke failed"); return { providerPrompts: 0 }; };
const work = run({ providers: { kimi: {} } }, profiles, smoke, "/owned/temporary", "/owned/output",
{ stdout: { write() {} } }, () => order.push("remove-old"), () => order.push("publish"));
if (failed) { await assert.rejects(work, /smoke failed/); assert.deepEqual(order, ["smoke"]); }
else { await work; assert.deepEqual(order, ["smoke", "remove-old", "publish"]); }
}
for (const [payload, status] of [[{ candidateProviders: { kimi: {} } }, "pending"], [{ providers: {} }, undefined]]) {
let calls = 0;
await run(payload, { profiles: { kimi: { qualificationStatus: status } } }, () => { calls++; }, "/owned/temporary", "/owned/output",
{ stdout: { write() {} } }, () => {}, () => {});
assert.equal(calls, 0);
}
});
@@ -0,0 +1,182 @@
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import { chmod, copyFile, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, unlink, writeFile } from "node:fs/promises";
import { dirname, isAbsolute, join, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { promisify } from "node:util";
import { KIMI_DISTRIBUTION_CLOSURE_SHA256, KIMI_DISTRIBUTION_LOCK_SHA256, KIMI_NODE_DISTRIBUTIONS, KIMI_NODE_VERSION, KIMI_RUNTIME_VERSION } from "../src/drivers/acpx/kimi-closure-pins.ts";
import { inventoryKimiRuntimeFiles, kimiRuntimeClosureDigest } from "../src/drivers/acpx/kimi-runtime-files.ts";
import { patchKimiRuntime } from "./patch-kimi-runtime.mjs";
const run = promisify(execFile);
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const repositoryRoot = resolve(packageRoot, "../..");
const hash = bytes => createHash("sha256").update(bytes).digest("hex");
export const KIMI_DISTRIBUTION_ENTRYPOINT = "node_modules/@moonshot-ai/kimi-code/dist/main.mjs";
export async function runKimiBuildCommand(command, args, options, checkCancelled = () => {}, signal) {
checkCancelled(); signal?.throwIfAborted();
const pending = run(command, args, { ...options, signal, killSignal: "SIGKILL" });
const closed = new Promise(resolve => pending.child.once("close", resolve));
try {
const result = await pending;
checkCancelled(); signal?.throwIfAborted();
return result;
} finally { await closed; }
}
export function kimiDistributionInstallCommand() {
return ["ci", "--ignore-scripts", "--omit=optional", "--omit=dev", "--no-audit", "--no-fund", "--registry=https://registry.npmjs.org", "--userconfig=.npmrc", "--globalconfig=.npmrc-global"];
}
export function kimiDistributionEnvironment(source = process.env) {
return Object.fromEntries(["PATH", "LANG", "LC_ALL", "HTTPS_PROXY", "HTTP_PROXY", "NO_PROXY", "https_proxy", "http_proxy", "no_proxy", "SSL_CERT_FILE", "SSL_CERT_DIR", "NODE_EXTRA_CA_CERTS"].flatMap(key => typeof source[key] === "string" ? [[key, source[key]]] : []));
}
export async function verifyLockedKimiPackageGraph(root, lock) {
if (lock.lockfileVersion !== 3 || !lock.packages || lock.packages[""]?.dependencies?.["@moonshot-ai/kimi-code"] !== KIMI_RUNTIME_VERSION) throw new Error("Kimi distribution lock is invalid");
let packages = 0;
for (const [path, entry] of Object.entries(lock.packages)) {
if (!path) continue;
if (!path.startsWith("node_modules/") || /[\\\u0000-\u001f\u007f]/.test(path) || path.split("/").some(part => !part || part === "." || part === "..") || entry.link || typeof entry.version !== "string" || !/^https:\/\/registry\.npmjs\.org\//.test(entry.resolved ?? "") || !/^sha512-[A-Za-z0-9+/]+=*$/.test(entry.integrity ?? "")) throw new Error("Kimi distribution lock contains an unpinned package");
if (entry.optional === true) {
try { await lstat(join(root, path)); } catch (error) { if (error.code === "ENOENT") continue; throw error; }
throw new Error("Kimi headless distribution unexpectedly installed an optional TUI dependency");
}
const file = join(root, path, "package.json");
if (await realpath(file) !== file || !(await lstat(file)).isFile()) throw new Error("Kimi distribution package metadata is not a regular file");
const actual = JSON.parse(await readFile(file, "utf8"));
if (actual.version !== entry.version) throw new Error(`Kimi distribution package differs from its lock: ${path}`);
packages++;
}
return packages;
}
export function assertKimiNodeSystemDependencies(listing, platform) {
for (const line of listing.split("\n").map(value => value.trim()).filter(Boolean)) {
if (platform === "darwin") {
if (line.endsWith(":")) continue;
if (!line.startsWith("/usr/lib/") && !line.startsWith("/System/Library/")) throw new Error("Kimi Node depends on an unbundled library");
} else if (!line.startsWith("linux-vdso.so.") && !/^(?:[^\s]+ => )?\/(?:usr\/)?lib(?:64)?\//.test(line)) throw new Error("Kimi Node depends on an unbundled library");
}
}
async function nodeDistribution(staging, target, archivePath, environment, runOwned, signal) {
const pin = KIMI_NODE_DISTRIBUTIONS[target];
if (!pin) throw new Error(`Kimi distribution target is unsupported: ${target}`);
let bytes;
if (archivePath) bytes = await readFile(archivePath);
else {
const timeout = AbortSignal.timeout(60_000);
const response = await fetch(`https://nodejs.org/dist/v${KIMI_NODE_VERSION}/node-v${KIMI_NODE_VERSION}-${target}.tar.gz`, { redirect: "error", signal: signal ? AbortSignal.any([signal, timeout]) : timeout });
if (!response.ok || !response.body) throw new Error("Pinned Kimi Node download failed");
const chunks = []; let length = 0;
for await (const chunk of response.body) {
length += chunk.length;
if (length > 128 * 1024 * 1024) throw new Error("Kimi Node archive exceeds its bound");
chunks.push(Buffer.from(chunk));
}
bytes = Buffer.concat(chunks);
}
if (bytes.length > 128 * 1024 * 1024 || hash(bytes) !== pin.archiveSha256) throw new Error("Kimi Node archive does not match its release pin");
const archive = join(staging, `${target}.tar.gz`); await writeFile(archive, bytes);
const output = join(staging, `node-${target}`); await mkdir(output);
await runOwned("tar", ["-xzf", archive, "-C", output, "--strip-components=1", `node-v${KIMI_NODE_VERSION}-${target}/bin/node`, `node-v${KIMI_NODE_VERSION}-${target}/lib/node_modules/npm`], { env: environment, timeout: 30_000 });
const executable = join(output, "bin/node");
if (hash(await readFile(executable)) !== pin.executableSha256 || (await lstat(executable)).size !== pin.executableSize) throw new Error("Kimi Node executable does not match its target pin");
const npm = join(output, "lib/node_modules/npm/bin/npm-cli.js");
if (JSON.parse(await readFile(join(output, "lib/node_modules/npm/package.json"), "utf8")).version !== "11.19.0") throw new Error("Kimi Node archive has an unexpected npm version");
return { executable, npm };
}
async function removePackageManagerLinks(root) {
const visit = async directory => {
for (const name of await readdir(directory)) {
const path = join(directory, name), stat = await lstat(path);
if (stat.isSymbolicLink()) {
if (dirname(path).endsWith("/node_modules/.bin")) await unlink(path);
else throw new Error("Kimi package payload contains an unsupported symbolic link");
} else if (stat.isDirectory()) await visit(path);
else if (!stat.isFile()) throw new Error("Kimi package payload contains a non-file resource");
}
};
await visit(root);
await rm(join(root, "node_modules/.package-lock.json"), { force: true });
}
export async function buildKimiDistribution({ outputRoot, target = `${process.platform}-${process.arch}`, lockDirectory = join(packageRoot, "scripts/kimi-distribution"), nodeArchives = {}, checkCancelled = () => {}, signal }) {
checkCancelled(); signal?.throwIfAborted();
if (typeof outputRoot !== "string" || !isAbsolute(outputRoot)) throw new Error("Kimi distribution output must be absolute");
const output = resolve(outputRoot);
if (["/", packageRoot, repositoryRoot].includes(output)) throw new Error("Refusing unsafe Kimi distribution output");
if (!Object.hasOwn(KIMI_NODE_DISTRIBUTIONS, target)) throw new Error("Kimi distribution target is unsupported");
const host = `${process.platform}-${process.arch}`;
if (!Object.hasOwn(KIMI_NODE_DISTRIBUTIONS, host)) throw new Error("Kimi distribution build host is unsupported");
try { await lstat(output); throw new Error("Kimi distribution output already exists"); } catch (error) { if (error.code !== "ENOENT") throw error; }
const lockBytes = await readFile(join(lockDirectory, "package-lock.json"));
if (hash(lockBytes) !== KIMI_DISTRIBUTION_LOCK_SHA256) throw new Error("Kimi distribution lock differs from its source pin");
await mkdir(dirname(output), { recursive: true });
const staging = await mkdtemp(join(await realpath(dirname(output)), ".paperclip-kimi-distribution-"));
const environment = kimiDistributionEnvironment();
const runOwned = (command, args, options) => runKimiBuildCommand(command, args, options, checkCancelled, signal);
const runtime = join(staging, "runtime");
try {
const buildHome = join(staging, "build-home"); await mkdir(buildHome); environment.HOME = buildHome;
const installedNode = await nodeDistribution(staging, target, nodeArchives[target], environment, runOwned, signal);
const buildNode = host === target ? installedNode : await nodeDistribution(staging, host, nodeArchives[host], environment, runOwned, signal);
if ((await runOwned(buildNode.executable, ["--version"], { env: {}, timeout: 10_000 })).stdout.trim() !== `v${KIMI_NODE_VERSION}`) throw new Error("Kimi distribution build Node cannot execute");
await mkdir(runtime);
for (const name of ["package.json", "package-lock.json"]) await copyFile(join(lockDirectory, name), join(runtime, name));
await writeFile(join(runtime, ".npmrc"), "registry=https://registry.npmjs.org/\nignore-scripts=true\naudit=false\nfund=false\n");
await writeFile(join(runtime, ".npmrc-global"), "");
await runOwned(buildNode.executable, [buildNode.npm, ...kimiDistributionInstallCommand()], { cwd: runtime, env: environment, timeout: 300_000, maxBuffer: 4 * 1024 * 1024 });
if (!(await readFile(join(runtime, "package-lock.json"))).equals(lockBytes)) throw new Error("Kimi installation changed its committed lock");
const packageCount = await verifyLockedKimiPackageGraph(runtime, JSON.parse(lockBytes.toString("utf8")));
const patch = await patchKimiRuntime(join(runtime, KIMI_DISTRIBUTION_ENTRYPOINT));
await mkdir(join(runtime, "node/bin"), { recursive: true });
await copyFile(installedNode.executable, join(runtime, "node/bin/node")); await chmod(join(runtime, "node/bin/node"), 0o755);
if (host === target) {
const listing = process.platform === "darwin"
? await runOwned("/usr/bin/otool", ["-L", join(runtime, "node/bin/node")], { env: {}, timeout: 10_000 })
: await runOwned("ldd", [join(runtime, "node/bin/node")], { env: { PATH: "/usr/bin:/bin" }, timeout: 10_000 });
assertKimiNodeSystemDependencies(listing.stdout, process.platform);
if ((await runOwned(join(runtime, "node/bin/node"), ["--version"], { env: {}, timeout: 10_000 })).stdout.trim() !== `v${KIMI_NODE_VERSION}`) throw new Error("Kimi Node cannot execute after relocation");
}
await removePackageManagerLinks(runtime);
const entries = await inventoryKimiRuntimeFiles(runtime);
const closure = kimiRuntimeClosureDigest(entries);
const metadata = { schema: "paperclip.kimi-distribution.v1", version: KIMI_RUNTIME_VERSION, target, nodeVersion: KIMI_NODE_VERSION, runtimeRoot: "runtime", entrypoint: KIMI_DISTRIBUTION_ENTRYPOINT, lockSha256: KIMI_DISTRIBUTION_LOCK_SHA256, patch, packageCount, nativeClosureSha256: closure };
await writeFile(join(staging, "native-closure.json"), `${JSON.stringify({ entries })}\n`);
await writeFile(join(staging, "kimi-distribution.json"), `${JSON.stringify(metadata, null, 2)}\n`);
for (const name of await readdir(staging)) if (!["runtime", "native-closure.json", "kimi-distribution.json"].includes(name)) await rm(join(staging, name), { recursive: true, force: true });
checkCancelled();
await rename(staging, output);
return { outputRoot: output, runtimeRoot: join(output, "runtime"), version: KIMI_RUNTIME_VERSION, closureDigest: `sha256:${closure}`, metadata };
} catch (error) { await rm(staging, { recursive: true, force: true }); throw error; }
}
export async function materializeKimiDistribution(options) {
if (typeof options.outputRoot !== "string" || !isAbsolute(options.outputRoot)) throw new Error("Kimi distribution output must be absolute");
const output = resolve(options.outputRoot);
if (["/", packageRoot, repositoryRoot].includes(output)) throw new Error("Refusing unsafe Kimi distribution output");
const target = options.target ?? `${process.platform}-${process.arch}`;
if (!Object.hasOwn(KIMI_DISTRIBUTION_CLOSURE_SHA256, target)) throw new Error("Kimi distribution target is unsupported");
try { await lstat(output); throw new Error("Kimi distribution output already exists"); } catch (error) { if (error.code !== "ENOENT") throw error; }
await mkdir(dirname(output), { recursive: true });
const parent = await realpath(dirname(output));
const temporary = await mkdtemp(join(parent, ".paperclip-kimi-admission-"));
const candidate = join(temporary, "candidate");
try {
const result = await buildKimiDistribution({ ...options, outputRoot: candidate });
if (result.metadata.nativeClosureSha256 !== KIMI_DISTRIBUTION_CLOSURE_SHA256[target]) throw new Error("Kimi native closure differs from its trusted target pin");
try { await lstat(options.outputRoot); throw new Error("Kimi distribution output already exists"); } catch (error) { if (error.code !== "ENOENT") throw error; }
await rename(candidate, options.outputRoot);
return { ...result, outputRoot: options.outputRoot, runtimeRoot: join(options.outputRoot, "runtime") };
} finally { await rm(temporary, { recursive: true, force: true }); }
}
if (import.meta.url.endsWith("/materialize-kimi-distribution.mjs") && process.argv[1] && pathToFileURL(resolve(process.argv[1])).href === import.meta.url) {
const args = process.argv.slice(2);
if (args.length !== 1) throw new Error("Usage: node scripts/materialize-kimi-distribution.mjs /absolute/output-directory");
materializeKimiDistribution({ outputRoot: args[0] }).then(result => console.log(JSON.stringify(result))).catch(error => { console.error(error.message); process.exitCode = 1; });
}
@@ -0,0 +1,121 @@
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { chmod, link, mkdir, mkdtemp, readFile, readdir, realpath, rm, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import test from "node:test";
import { setTimeout as delay } from "node:timers/promises";
import { assertKimiNodeSystemDependencies, buildKimiDistribution, kimiDistributionEnvironment, kimiDistributionInstallCommand, materializeKimiDistribution, runKimiBuildCommand, verifyLockedKimiPackageGraph } from "./materialize-kimi-distribution.mjs";
import { KIMI_DISTRIBUTION_LOCK_SHA256, KIMI_NODE_DISTRIBUTIONS } from "../src/drivers/acpx/kimi-closure-pins.ts";
import { inventoryKimiRuntimeFiles, kimiRuntimeClosureDigest } from "../src/drivers/acpx/kimi-runtime-files.ts";
async function fixture(t) {
const root = await realpath(await mkdtemp(join(tmpdir(), "paperclip-kimi-distribution-test-")));
t.after(() => rm(root, { recursive: true, force: true }));
const write = async (path, content) => { const file = join(root, path); await mkdir(dirname(file), { recursive: true }); await writeFile(file, content); };
return { root, write };
}
test("Kimi's portable graph pins public integrity and excludes lifecycle and optional TUI dependencies", async () => {
const bytes = await readFile(new URL("./kimi-distribution/package-lock.json", import.meta.url));
assert.equal(createHash("sha256").update(bytes).digest("hex"), KIMI_DISTRIBUTION_LOCK_SHA256);
const lock = JSON.parse(bytes.toString("utf8"));
const pkg = JSON.parse(await readFile(new URL("./kimi-distribution/package.json", import.meta.url), "utf8"));
assert.deepEqual(pkg.dependencies, { "@moonshot-ai/kimi-code": "2.3.3" });
assert.deepEqual(lock.packages[""].dependencies, pkg.dependencies);
assert.equal(lock.packages["node_modules/@moonshot-ai/kimi-code"].integrity, "sha512-3V8bycucf9KgPRH27S8De2u0ip84/Ry3FpNn6gi5z9JmoEHt9vxiYHHZqiMdyDf5nPm4bv+YKGwKEQL4oYpRTw==");
assert.equal(kimiDistributionInstallCommand()[0], "ci");
assert.ok(kimiDistributionInstallCommand().includes("--ignore-scripts"));
assert.ok(kimiDistributionInstallCommand().includes("--omit=optional"));
assert.ok(kimiDistributionInstallCommand().includes("--registry=https://registry.npmjs.org"));
assert.deepEqual(Object.keys(KIMI_NODE_DISTRIBUTIONS).sort(), ["darwin-arm64", "linux-x64"]);
});
test("distribution build admits network settings without provider keys or Node injection", () => {
const network = { PATH: "/usr/bin:/bin", LANG: "C.UTF-8", HTTPS_PROXY: "http://proxy.invalid", NODE_EXTRA_CA_CERTS: "/public/ca.pem" };
assert.deepEqual(kimiDistributionEnvironment({ ...network, HOME: "/private/home", KIMI_MODEL_API_KEY: "canary-secret", NPM_TOKEN: "canary-secret", NODE_OPTIONS: "--require /foreign.js", NODE_PATH: "/foreign", NODE_TLS_REJECT_UNAUTHORIZED: "0" }), network);
});
test("setup cancellation physically stops an in-flight owned child", { timeout: 5_000 }, async t => {
const { root } = await fixture(t), pidFile = join(root, "pid");
const controller = new AbortController();
const result = runKimiBuildCommand(process.execPath, ["-e", "require('node:fs').writeFileSync(process.argv[1],String(process.pid));setInterval(()=>{},1000)", pidFile], { env: {}, timeout: 3_000 }, undefined, controller.signal).then(() => null, error => error);
let pid;
try {
for (let attempt = 0; attempt < 100; attempt++) {
try { pid = Number(await readFile(pidFile, "utf8")); break; }
catch (error) { if (error.code !== "ENOENT") throw error; await delay(10); }
}
assert.ok(Number.isSafeInteger(pid) && pid > 0, "The real child did not start");
controller.abort();
assert.equal((await result).code, "ABORT_ERR");
assert.throws(() => process.kill(pid, 0), error => error.code === "ESRCH");
} finally { controller.abort(); await result; }
});
test("setup cancellation aborts the pending download and removes its staging tree", { timeout: 5_000 }, async t => {
const { root } = await fixture(t), controller = new AbortController();
let started;
const downloading = new Promise(resolve => { started = resolve; });
t.mock.method(globalThis, "fetch", (_url, options) => new Promise((_resolve, reject) => {
started(); options.signal.addEventListener("abort", () => reject(options.signal.reason), { once: true });
}));
const result = buildKimiDistribution({ outputRoot: join(root, "output"), signal: controller.signal });
await downloading; controller.abort();
await assert.rejects(result, error => error.name === "AbortError");
assert.deepEqual(await readdir(root), []);
});
test("the installed graph rejects version drift and optional packages", async t => {
const { root, write } = await fixture(t);
const entry = { version: "2.3.3", resolved: "https://registry.npmjs.org/@moonshot-ai/kimi-code/-/kimi-code-2.3.3.tgz", integrity: "sha512-YWJjZA==" };
const lock = { lockfileVersion: 3, packages: { "": { dependencies: { "@moonshot-ai/kimi-code": "2.3.3" } }, "node_modules/@moonshot-ai/kimi-code": entry, "node_modules/optional-native": { ...entry, optional: true } } };
await write("node_modules/@moonshot-ai/kimi-code/package.json", '{"version":"2.3.3"}');
assert.equal(await verifyLockedKimiPackageGraph(root, lock), 1);
await write("node_modules/@moonshot-ai/kimi-code/package.json", '{"version":"2.3.4"}');
await assert.rejects(verifyLockedKimiPackageGraph(root, lock), /differs from its lock/);
await write("node_modules/@moonshot-ai/kimi-code/package.json", '{"version":"2.3.3"}');
await write("node_modules/optional-native/package.json", '{"version":"2.3.3"}');
await assert.rejects(verifyLockedKimiPackageGraph(root, lock), /optional TUI dependency/);
});
test("inventory binds native assets, workers, permissions and bytes in stable path order", async t => {
const { root, write } = await fixture(t);
await write("node_modules/provider/worker.mjs", "worker");
await write("node_modules/provider/native/addon.node", Buffer.from([0, 1, 2, 3]));
await write("node/bin/node", "node");
const first = await inventoryKimiRuntimeFiles(root);
assert.deepEqual(first.map(entry => entry.path), ["node/bin/node", "node_modules/provider/native/addon.node", "node_modules/provider/worker.mjs"]);
const digest = kimiRuntimeClosureDigest(first);
await write("node_modules/provider/worker.mjs", "worker changed");
assert.notEqual(kimiRuntimeClosureDigest(await inventoryKimiRuntimeFiles(root)), digest);
await chmod(join(root, "node/bin/node"), 0o755);
assert.equal((await inventoryKimiRuntimeFiles(root))[0].executable, true);
});
test("inventory rejects external links, hardlinks and special files", async t => {
const { root, write } = await fixture(t);
await write("original", "content");
await symlink("/outside", join(root, "escape"));
await assert.rejects(inventoryKimiRuntimeFiles(root), /symbolic link/);
await rm(join(root, "escape"));
await link(join(root, "original"), join(root, "alias"));
await assert.rejects(inventoryKimiRuntimeFiles(root), /invalid file/);
});
test("setup refuses relative, root, unsupported and existing outputs before network work", async t => {
const { root } = await fixture(t);
for (const operation of [buildKimiDistribution, materializeKimiDistribution]) {
await assert.rejects(operation({ outputRoot: "relative" }), /must be absolute/);
await assert.rejects(operation({ outputRoot: "/" }), /unsafe/);
await assert.rejects(operation({ outputRoot: join(root, "output"), target: "linux-arm64" }), /unsupported/);
await assert.rejects(operation({ outputRoot: root }), /already exists/);
}
});
test("the bundled Node may depend only on platform system libraries", () => {
assert.doesNotThrow(() => assertKimiNodeSystemDependencies("node:\n /usr/lib/libSystem.B.dylib (version 0)\n", "darwin"));
assert.throws(() => assertKimiNodeSystemDependencies("node:\n /opt/homebrew/lib/libnode.dylib (version 0)\n", "darwin"), /unbundled/);
assert.doesNotThrow(() => assertKimiNodeSystemDependencies("linux-vdso.so.1 (0x0)\nlibc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0)\n/lib64/ld-linux-x86-64.so.2 (0x0)\n", "linux"));
assert.throws(() => assertKimiNodeSystemDependencies("libcustom.so => /opt/private/libcustom.so (0x0)\n", "linux"), /unbundled/);
});
@@ -0,0 +1,160 @@
import { createHash } from "node:crypto";
import { readFile, writeFile } from "node:fs/promises";
import { pathToFileURL } from "node:url";
import { KIMI_INSTRUCTION_RUNTIME_SOURCE } from "./kimi-instructions-runtime.mjs";
export const KIMI_RUNTIME_PATCH = Object.freeze({
version: "paperclip-kimi-2.3.3-instructions-v6",
vendorSha256: "f18b711e22947d473175126d0c2da437d641187963a3bc300d66e183060703e3",
patchedSha256: "79980aa95c54f325a05cbdf883f74443705db28f3e412a1b5ae22929d939eb66",
});
const sha256 = value => createHash("sha256").update(value).digest("hex");
export function replaceKimiPatchAnchor(source, before, after) {
if (source.split(before).length !== 2) throw new Error("Kimi runtime patch requires exactly one source anchor");
return source.replace(before, () => after);
}
export function patchKimiRuntimeSource(input) {
if (sha256(input) !== KIMI_RUNTIME_PATCH.vendorSha256) throw new Error("Kimi runtime patch input digest mismatch");
let source = input;
const replace = (before, after) => { source = replaceKimiPatchAnchor(source, before, after); };
replace("#!/usr/bin/env node\n", `#!/usr/bin/env node
import { createHash as paperclipKimiCreateHash } from "node:crypto";
let paperclipKimiInstructionSnapshot;
function paperclipKimiInstructionState() {
if (paperclipKimiInstructionSnapshot) return paperclipKimiInstructionSnapshot;
const raw = process.env.PAPERCLIP_KIMI_INSTRUCTIONS;
delete process.env.PAPERCLIP_KIMI_INSTRUCTIONS;
return paperclipKimiInstructionSnapshot = (${KIMI_INSTRUCTION_RUNTIME_SOURCE})(raw, {
createHash: paperclipKimiCreateHash, byteLength: value => Buffer.byteLength(value, "utf8")
});
}
function paperclipKimiChildEnvironment(overrides) {
const environment = { ...process.env, ...overrides };
delete environment.KIMI_API_KEY;
delete environment.PAPERCLIP_KIMI_INSTRUCTIONS;
return environment;
}
`);
replace('function buildEnv(overrides) {\n\tif (overrides === void 0) return;\n\treturn {\n\t\t...process.env,\n\t\t...overrides\n\t};\n}',
'function buildEnv(overrides) {\n\treturn paperclipKimiChildEnvironment(overrides);\n}');
replace('env: globalThis.process.env', 'env: paperclipKimiChildEnvironment()');
replace('\t\tbindTerminalCapability(terminal) {\n\t\t\tthis._terminalEnabled = terminal === true;\n\t\t}',
'\t\tbindTerminalCapability(terminal) {\n\t\t\tthis._terminalEnabled = false;\n\t\t}');
replace("\t\tget systemPrompt() {\n\t\t\treturn this.profileState.systemPrompt;\n\t\t}",
"\t\tget systemPrompt() {\n\t\t\treturn paperclipKimiInstructionState().append(this.profileState.systemPrompt);\n\t\t}");
replace("async function runAcpServerWithStream(stream, opts = {}) {\n",
"async function runAcpServerWithStream(stream, opts = {}) {\n\tpaperclipKimiInstructionState();\n");
replace('\t\tmerged() {\n\t\t\treturn {\n\t\t\t\t...Object.fromEntries(this.pluginServers),\n\t\t\t\t...Object.fromEntries(this.fileServers)\n\t\t\t};\n\t\t}',
'\t\tmerged() {\n\t\t\treturn {};\n\t\t}');
replace('\tawait pushFirstExisting$1(roots, USER_GENERIC_DIRS$1, osHomeDir, "user");', '');
replace('async function projectRoots(workDir, options = {}) {\n', 'async function projectRoots(workDir, options = {}) {\n\treturn [];\n');
replace('async function projectAgentRoots(fs, workDir, warn) {\n', 'async function projectAgentRoots(fs, workDir, warn) {\n\treturn [];\n');
replace('async function userAgentRoots(fs, homeDir, osHomeDir, warn) {\n', 'async function userAgentRoots(fs, homeDir, osHomeDir, warn) {\n\treturn [];\n');
replace('this.byEvent = indexHooks([...configured ?? [], ...pluginHooks]);', 'this.byEvent = new Map();');
replace('\t\tasync evaluate(context) {\n\t\t\tfor (const policy of this.policies) {',
'\t\tasync evaluate(context) {\n\t\t\treturn { policyName: "paperclip-runner", result: { kind: "ask" } };\n\t\t\tfor (const policy of this.policies) {');
replace('if (req.display.kind !== "plan_review") return CANONICAL_OPTIONS;',
'if (req.display.kind !== "plan_review") return CANONICAL_OPTIONS.filter(option => option.kind !== "allow_always");');
replace('\t\t\t\ttoolName: name,\n\t\t\t\taction,\n\t\t\t\tdisplay\n\t\t\t};\n\t\t\tconst approvalContext = {',
'\t\t\t\ttoolName: name,\n\t\t\t\ttoolInput: context.args,\n\t\t\t\taction,\n\t\t\t\tdisplay\n\t\t\t};\n\t\t\tconst approvalContext = {');
replace('\t\ttitle: req.toolName,\n\t\tcontent\n', '\t\ttitle: req.toolName,\n\t\tkind: inferToolKind(req.toolName),\n\t\trawInput: req.toolInput,\n\t\tcontent\n');
const bindRuntime = '\t\t\thandle.accessor.get(IAgentLifecycleService).handleOf(agentContext.agentId).accessor.get(IAgentRuntimeBindingService).switch(runtimeId);';
replace(bindRuntime, `${bindRuntime}
\t\t\tconst paperclipProfile = handle.accessor.get(IAgentLifecycleService).handleOf(agentContext.agentId).accessor.get(IAgentProfileService);
\t\t\treturn paperclipKimiInstructionState().receipt(sessionId, paperclipProfile.getSystemPrompt());`);
replace("\t\t\tawait this.bindSessionRuntime?.(sessionId);\n",
"\t\t\tconst paperclipReceipt = await this.bindSessionRuntime?.(sessionId);\n\t\t\tif (!paperclipReceipt || paperclipReceipt.sessionId !== sessionId) throw new Error(\"Kimi native instruction receipt is missing\");\n\t\t\tthis.paperclipInstructionReceipts ??= new Map();\n\t\t\tthis.paperclipInstructionReceipts.set(sessionId, paperclipReceipt);\n");
replace("\t\t\t\t...await this.activateSession(meta.id)\n",
"\t\t\t\t...await this.activateSession(meta.id),\n\t\t\t\t_meta: { paperclipKimiInstructions: this.paperclipInstructionReceipts.get(meta.id) }\n");
const lifecycleState = "\t\t\t\tconfigOptions: await acpSession.configOptions(),\n\t\t\t\tmodes: acpSession.modeState()\n\t\t\t};\n\t\t}\n\t\tasync ";
replace(`${lifecycleState}resumeSession(params)`,
`${lifecycleState.replace("modes: acpSession.modeState()", "modes: acpSession.modeState(),\n\t\t\t\t_meta: { paperclipKimiInstructions: this.paperclipInstructionReceipts.get(params.sessionId) }")}resumeSession(params)`);
replace(`${lifecycleState}listSessions(params)`,
`${lifecycleState.replace("modes: acpSession.modeState()", "modes: acpSession.modeState(),\n\t\t\t\t_meta: { paperclipKimiInstructions: this.paperclipInstructionReceipts.get(params.sessionId) }")}listSessions(params)`);
replace('\t\t\t\tdriver.resolve({ stopReason: turnEndReasonToStopReason(event.reason, error) });',
'\t\t\t\tif (event.reason === "failed") {\n\t\t\t\t\tdriver.reject(RequestError$1.internalError({ schema: "paperclip.kimi.provider-error.v1", kind: "provider_failed" }, "Kimi provider turn failed"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdriver.resolve({ stopReason: turnEndReasonToStopReason(event.reason, error) });');
const stepSchema = 'object({ type: string(), turnId: number$1().int().nonnegative(), step: number$1().int().positive(), stepId: string().min(1).max(256).optional(), finishReason: string().max(32).optional(), reason: string().max(32).optional() })';
replace('\tagentEvents = {\n\t\t"turn.started": {', `\tagentEvents = {
\t\t"turn.step.started": { kind: "stream", name: "events", type: "turn.step.started", schema: ${stepSchema} },
\t\t"turn.step.completed": { kind: "stream", name: "events", type: "turn.step.completed", schema: ${stepSchema} },
\t\t"turn.step.interrupted": { kind: "stream", name: "events", type: "turn.step.interrupted", schema: ${stepSchema} },
\t\t"turn.started": {`);
replace('\t\t\tthis.subscriptions.push(events.on("assistant.delta", (event) => {', `\t\t\tthis.subscriptions.push(
\t\t\t\tevents.on("turn.step.started", event => this.dispatchTurnEvent(event.turnId, () => this.paperclipKimiStep(event, "start"))),
\t\t\t\tevents.on("turn.step.completed", event => this.dispatchTurnEvent(event.turnId, () => this.paperclipKimiStep(event, "end"))),
\t\t\t\tevents.on("turn.step.interrupted", event => this.dispatchTurnEvent(event.turnId, () => this.paperclipKimiStep(event, "interrupted"))),
\t\t\t\tevents.on("assistant.delta", (event) => {`);
replace('\t\tonAssistantDelta(event) {\n\t\t\tif (this.driverFor(event.turnId) === void 0) return;\n\t\t\tthis.emit(assistantDeltaToSessionUpdate(this.sessionId, event));\n\t\t}', `\t\tpaperclipKimiMessageFailure(driver) {
\t\t\tthis.settleDriver(driver, () => driver.reject(RequestError$1.internalError({ schema: "paperclip.kimi.provider-error.v1", kind: "message_boundary_invalid" }, "Kimi native message boundary is invalid")));
\t\t}
\t\tpaperclipKimiChunk(driver, text, kind, thought = false) {
\t\t\tthis.emit({ sessionId: this.sessionId, update: {
\t\t\t\tsessionUpdate: thought ? "agent_thought_chunk" : "agent_message_chunk",
\t\t\t\tmessageId: driver.paperclipKimiMessage.id, content: { type: "text", text },
\t\t\t\t_meta: { origin: "kimi-native-assistant", source: "kimi-turn-step-v1", kind }
\t\t\t} });
\t\t}
\t\tpaperclipKimiStep(event, phase) {
\t\t\tconst driver = this.driverFor(event.turnId);
\t\t\tif (driver === void 0) return;
\t\t\tconst current = driver.paperclipKimiMessage;
\t\t\tif (!Number.isSafeInteger(event.step) || event.step < 1 || !Number.isSafeInteger(event.turnId) || event.turnId < 0) return this.paperclipKimiMessageFailure(driver);
\t\t\tif (phase === "start") {
\t\t\t\tif (current?.active || typeof event.stepId !== "string" || !/^[A-Za-z0-9_-]{1,256}$/.test(event.stepId) || current && event.step <= current.step) return this.paperclipKimiMessageFailure(driver);
\t\t\t\tdriver.paperclipKimiMessageIds ??= new Set();
\t\t\t\tconst id = "kimi-message-" + paperclipKimiCreateHash("sha256").update(JSON.stringify([this.sessionId, event.turnId, event.stepId])).digest("hex");
\t\t\t\tif (driver.paperclipKimiMessageIds.size >= 4096 || driver.paperclipKimiMessageIds.has(id)) return this.paperclipKimiMessageFailure(driver);
\t\t\t\tdriver.paperclipKimiMessageIds.add(id);
\t\t\t\tdriver.paperclipKimiMessage = { id, step: event.step, stepId: event.stepId, active: true };
\t\t\t\tthis.paperclipKimiChunk(driver, "", "start");
\t\t\t\treturn;
\t\t\t}
\t\t\tif (!current || current.step !== event.step || event.stepId !== void 0 && event.stepId !== current.stepId) return this.paperclipKimiMessageFailure(driver);
\t\t\tconst reason = phase === "interrupted" ? event.reason : event.finishReason;
\t\t\tconst reasons = phase === "interrupted" ? ["aborted", "max_steps", "error"] : ["end_turn", "max_tokens", "tool_use", "filtered", "paused", "other"];
\t\t\tif (!reasons.includes(reason)) return this.paperclipKimiMessageFailure(driver);
\t\t\tif (!current.active) {
\t\t\t\tif (phase === "interrupted") return;
\t\t\t\treturn this.paperclipKimiMessageFailure(driver);
\t\t\t}
\t\t\tthis.paperclipKimiChunk(driver, "", "end:" + reason);
\t\t\tcurrent.active = false;
\t\t}
\t\tonAssistantDelta(event) {
\t\t\tconst driver = this.driverFor(event.turnId);
\t\t\tif (driver === void 0) return;
\t\t\tif (!driver.paperclipKimiMessage?.active || typeof event.delta !== "string") return this.paperclipKimiMessageFailure(driver);
\t\t\tthis.paperclipKimiChunk(driver, event.delta, "delta");
\t\t}`);
replace('\t\tonThinkingDelta(event) {\n\t\t\tif (this.driverFor(event.turnId) === void 0) return;\n\t\t\tthis.emit(thinkingDeltaToSessionUpdate(this.sessionId, event));\n\t\t}', `\t\tonThinkingDelta(event) {
\t\t\tconst driver = this.driverFor(event.turnId);
\t\t\tif (driver === void 0) return;
\t\t\tif (!driver.paperclipKimiMessage?.active || typeof event.delta !== "string") return this.paperclipKimiMessageFailure(driver);
\t\t\tthis.paperclipKimiChunk(driver, event.delta, "delta", true);
\t\t}`);
return source;
}
export async function patchKimiRuntime(path) {
const input = await readFile(path, "utf8");
const digest = sha256(input);
if (digest === KIMI_RUNTIME_PATCH.patchedSha256) return { ...KIMI_RUNTIME_PATCH };
const patched = patchKimiRuntimeSource(input);
if (sha256(patched) !== KIMI_RUNTIME_PATCH.patchedSha256) throw new Error("Kimi runtime patch output digest mismatch");
await writeFile(path, patched);
return { ...KIMI_RUNTIME_PATCH };
}
if (import.meta.url?.endsWith("/patch-kimi-runtime.mjs") && process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
const path = process.argv[2];
if (!path || process.argv.length !== 3) throw new Error("Usage: node patch-kimi-runtime.mjs <dist/main.mjs>");
patchKimiRuntime(path).then(result => {
process.stdout.write(`${JSON.stringify(result)}\n`);
}).catch(error => {
process.stderr.write(`${error instanceof Error ? error.message : "Kimi runtime patch failed"}\n`);
process.exitCode = 1;
});
}
@@ -0,0 +1,654 @@
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { access, mkdir, mkdtemp, readFile, realpath, rm, writeFile } from "node:fs/promises";
import { createServer } from "node:http";
import { createRequire } from "node:module";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import test from "node:test";
import { runInNewContext } from "node:vm";
import { createKimiInstructionRuntime } from "./kimi-instructions-runtime.mjs";
import { KIMI_RUNTIME_PATCH, patchKimiRuntime, patchKimiRuntimeSource, replaceKimiPatchAnchor } from "./patch-kimi-runtime.mjs";
const sha = text => createHash("sha256").update(text, "utf8").digest("hex");
const binding = content => JSON.stringify({ schema: "paperclip.kimi.instructions.v1", content,
digest: `sha256:${sha(content)}`, byteLength: Buffer.byteLength(content) });
const runtime = raw => createKimiInstructionRuntime(raw, { createHash, byteLength: text => Buffer.byteLength(text, "utf8") });
test("native instruction binding preserves exact literals and rejects false readback", () => {
const content = "\nGovernance: résumé 日本語 ${cwd} ${base_prompt}\n\n";
const state = runtime(binding(content));
const native = "Original native system prompt";
const effective = `${native}\n\n${content}`;
assert.equal(state.append(native), effective);
assert.equal(state.append(effective), effective);
const receipt = state.receipt("native-session", effective);
assert.deepEqual(receipt, { schema: "paperclip.kimi.instructions.v1", sessionId: "native-session",
digest: `sha256:${sha(content)}`, byteLength: Buffer.byteLength(content),
effectiveDigest: `sha256:${sha(effective)}`, effectiveByteLength: Buffer.byteLength(effective) });
assert.throws(() => state.receipt("native-session", native), /binding/);
assert.throws(() => state.receipt("", effective), /binding/);
assert.throws(() => state.append("x".repeat(2_097_153)), /binding/);
assert.ok(Object.isFrozen(receipt));
});
test("explicit empty instructions retain the native prompt and acknowledge zero bytes", () => {
const state = runtime(binding(""));
assert.equal(state.append("native"), "native");
assert.equal(state.receipt("s", "native").byteLength, 0);
});
test("malformed, changed, oversized, and ill-formed instruction bindings fail closed", () => {
const valid = JSON.parse(binding("test"));
for (const raw of [undefined, "{", "[]", "null", " ".repeat(200_001),
binding("x\0y"), binding("x".repeat(32_769)), binding("\ud800"),
JSON.stringify({ ...valid, byteLength: 1 }), JSON.stringify({ ...valid, digest: "sha256:" + "0".repeat(64) }),
JSON.stringify({ ...valid, extra: true }), JSON.stringify({ schema: valid.schema, content: "test", digest: valid.digest })]) {
assert.throws(() => runtime(raw), /binding/);
}
});
test("patch rejects unknown source and ambiguous anchors before writing", async () => {
assert.throws(() => patchKimiRuntimeSource("unverified runtime"), /input digest/);
assert.throws(() => replaceKimiPatchAnchor("absent", "needle", "replacement"), /exactly one/);
assert.throws(() => replaceKimiPatchAnchor("needle needle", "needle", "replacement"), /exactly one/);
const root = await mkdtemp(join(tmpdir(), "kimi-patch-test-"));
const path = join(root, "main.mjs");
try {
await writeFile(path, "unverified runtime");
await assert.rejects(patchKimiRuntime(path), /input digest/);
assert.equal(await readFile(path, "utf8"), "unverified runtime");
} finally { await rm(root, { recursive: true, force: true }); }
});
function segment(source, start, end) {
const first = source.indexOf(start);
assert.ok(first >= 0, `Missing native start: ${start}`);
const last = source.indexOf(end, first + start.length);
assert.ok(last > first, `Missing native end: ${end}`);
return source.slice(first, last);
}
test("pinned native package binds new/load and rejects failed turns", {
skip: !process.env.PAPERCLIP_KIMI_VENDOR_MAIN,
}, async () => {
const input = await readFile(process.env.PAPERCLIP_KIMI_VENDOR_MAIN, "utf8");
const patched = patchKimiRuntimeSource(input);
assert.equal(sha(patched), KIMI_RUNTIME_PATCH.patchedSha256);
const { build } = await import("esbuild");
const bundledRoot = await mkdtemp(join(tmpdir(), "kimi-bundled-patch-test-"));
try {
const bundledPath = join(bundledRoot, "patch-kimi-runtime.bundle.cjs");
await build({ entryPoints: [fileURLToPath(new URL("./patch-kimi-runtime.mjs", import.meta.url))],
outfile: bundledPath, bundle: true, platform: "node", format: "cjs", target: "node24", logLevel: "silent",
define: { "import.meta.url": JSON.stringify(pathToFileURL(bundledPath).href) } });
const bundled = (await import(pathToFileURL(bundledPath))).default;
assert.equal(sha(bundled.patchKimiRuntimeSource(input)), KIMI_RUNTIME_PATCH.patchedSha256,
"installed bundled patcher produces exactly the same pinned native bytes");
} finally { await rm(bundledRoot, { recursive: true, force: true }); }
const content = " Exact ${cwd} instructions\n日本語\n";
const env = { PAPERCLIP_KIMI_INSTRUCTIONS: binding(content) };
const helper = segment(patched, "let paperclipKimiInstructionSnapshot;", "import { fileURLToPath");
const context = { process: { env }, Buffer, paperclipKimiCreateHash: createHash };
const state = runInNewContext(`${helper};paperclipKimiInstructionState()`, context);
assert.equal(env.PAPERCLIP_KIMI_INSTRUCTIONS, undefined);
env.PAPERCLIP_KIMI_INSTRUCTIONS = binding("replacement");
assert.equal(runInNewContext("paperclipKimiInstructionState()", context), state);
env.KIMI_API_KEY = "fake-provider-key";
env.PAPERCLIP_TASK_SCOPED = "assigned-task-value";
const childEnvironment = segment(patched, "function buildEnv(overrides) {", "function waitForSpawn(child)");
const buildEnvironment = runInNewContext(`${childEnvironment};buildEnv`, context);
for (const overrides of [undefined, { KIMI_API_KEY: "override-key", PAPERCLIP_KIMI_INSTRUCTIONS: "private", EXTRA: "assigned" }]) {
const child = buildEnvironment(overrides);
assert.equal(child.KIMI_API_KEY, undefined);
assert.equal(child.PAPERCLIP_KIMI_INSTRUCTIONS, undefined);
assert.equal(child.PAPERCLIP_TASK_SCOPED, "assigned-task-value");
assert.equal(child.EXTRA, overrides?.EXTRA);
}
assert.equal(env.KIMI_API_KEY, "fake-provider-key");
const terminalBinding = segment(patched, "\t\tbindTerminalCapability(terminal) {", "\n\t\tget terminalEnabled()");
const bindTerminal = runInNewContext(`({${terminalBinding}}).bindTerminalCapability`);
const terminalHolder = {};
bindTerminal.call(terminalHolder, true);
assert.equal(terminalHolder._terminalEnabled, false);
const getter = segment(patched, "\t\tget systemPrompt() {", "\t\tget thinkingLevel()");
const native = runInNewContext(`({profileState:{systemPrompt:"native"},${getter}})`, context);
assert.equal(native.systemPrompt, `native\n\n${content}`);
assert.equal(native.profileState.systemPrompt, "native");
const receipt = state.receipt("s", native.systemPrompt);
const baseline = segment(patched, "\t\tmerged() {\n\t\t\treturn {};", "\n\t\tasync watchConfigFiles()");
const merged = runInNewContext(`({${baseline}}).merged`, context);
assert.deepEqual(Object.keys(merged.call(new Proxy({}, { get: () => assert.fail("ambient MCP was read") }))), []);
const policy = segment(patched, '\t\tasync evaluate(context) {\n\t\t\treturn { policyName: "paperclip-runner"', '\n\t};');
const evaluate = runInNewContext(`({${policy}}).evaluate`, context);
assert.equal((await evaluate.call(new Proxy({}, { get: () => assert.fail("native heuristic was read") }), {})).result.kind, "ask");
const userRoots = segment(patched, "async function userRoots(homeDir, osHomeDir, options = {}) {", "async function projectRoots(");
const discovered = [];
const roots = await runInNewContext(`(${userRoots})`, { USER_BRAND_DIRS$1: ["skills"],
pushBrandGroup: async (_roots, _dirs, home) => { discovered.push(home); },
pushFirstExisting$1: () => assert.fail("global skills were read") })("/owned-kimi", "/ambient-home");
assert.deepEqual(discovered, ["/owned-kimi"]);
assert.equal(roots.length, 0);
const host = { klient: { session: () => ({ agent: () => ({}) }) },
bindDefaultModel: async () => {}, bindSessionRuntime: async () => undefined };
const wire = segment(patched, "\t\tasync wireSession(sessionId) {", "\n\t\t/**");
await assert.rejects(runInNewContext(`({${wire}}).wireSession`, context).call(host, "s"), /native instruction receipt/);
const admitted = { ensureAuthed: async () => {}, klient: { global: { sessions: { create: async () => ({ id: "s" }) } } },
activateSession: async () => ({}), paperclipInstructionReceipts: new Map([["s", receipt]]) };
const create = segment(patched, "\t\tasync newSession(params) {", "\n\t\t/**");
const createContext = { ...context, acpMcpServersToConfigRecord: value => value };
const created = await runInNewContext(`({${create}}).newSession`, createContext).call(admitted, { cwd: "/workspace", mcpServers: [] });
assert.equal(created._meta.paperclipKimiInstructions, receipt);
for (const [name, next] of [["loadSession", "resumeSession"], ["resumeSession", "listSessions"]]) {
const method = segment(patched, `\t\tasync ${name}(params) {`, `\t\tasync ${next}(params)`);
const response = await runInNewContext(`({${method}}).${name}`, createContext).call({ ...admitted,
warnIgnoredAdditionalDirs() {}, scheduleAvailableCommandsUpdate() {},
resumeAcpSession: async () => ({ replayHistory: async () => {}, configOptions: async () => [], modeState: () => ({}) }) },
{ sessionId: "s", mcpServers: [] });
assert.equal(response._meta.paperclipKimiInstructions, receipt);
}
const terminal = segment(patched, "\t\tonTurnEnded(event) {", "\n\t\t/**");
const rejection = [];
const RequestError = { authRequired: () => ({ kind: "auth" }), internalError: data => data };
const end = runInNewContext(`({${terminal}}).onTurnEnded`, { RequestError$1: RequestError,
isAuthError: error => error?.code === "provider.auth_error", turnEndReasonToStopReason: () => "end_turn" });
for (const code of ["provider.auth_error", "provider.server_error"]) {
end.call({ driverFor: () => ({ reject: error => rejection.push(error), resolve: () => assert.fail("failed turn resolved") }),
settleDriver: (_driver, action) => action(), emitUsageUpdate() {} }, { turnId: 1, reason: "failed", error: { code } });
}
assert.equal(rejection[0].kind, "auth");
assert.equal(rejection[1].kind, "provider_failed");
const infer = segment(patched, "function inferToolKind(name) {", "\n/**");
const approval = segment(patched, "function buildPermissionToolCallUpdate(req) {", "\n/**");
const permission = runInNewContext(`${infer}\n${approval}\nbuildPermissionToolCallUpdate`, {
displayBlockToAcpContent: () => null, acpToolCallId: (turn, id) => `${turn}:${id}`,
});
for (const [toolName, kind] of [["Write", "edit"], ["Edit", "edit"], ["Read", "read"], ["Bash", "execute"], ["mcp__paperclip__read_workspace_file", "other"]]) {
const args = { path: "/workspace/exact.txt", content: "body" };
const request = permission({ toolName, toolInput: args, toolCallId: "native-tool", turnId: 1, display: {}, action: "test" });
assert.equal(request.kind, kind);
assert.equal(request.rawInput, args);
assert.equal(request.toolCallId, "1:native-tool");
}
});
test("native step identity separates identical commentary/final text and retains empty boundaries", {
skip: !process.env.PAPERCLIP_KIMI_VENDOR_MAIN,
}, async () => {
const patched = patchKimiRuntimeSource(await readFile(process.env.PAPERCLIP_KIMI_VENDOR_MAIN, "utf8"));
const methods = segment(patched, "\t\tpaperclipKimiMessageFailure(driver) {", "\n\t\tonToolCallStarted(event)");
const host = () => {
const events = [], rejected = [];
const driver = { reject: value => rejected.push(value) };
const session = runInNewContext(`new class {${methods}}`, { paperclipKimiCreateHash: createHash,
RequestError$1: { internalError: value => value } });
Object.assign(session, { sessionId: "native-session", emit: value => events.push(value),
driverFor: turnId => turnId === 7 && !driver.settled ? driver : undefined,
settleDriver: (value, action) => { value.settled = true; action(); } });
return { session, events, rejected, driver };
};
const h = host();
h.session.paperclipKimiStep({ turnId: 7, step: 1, stepId: "native-step-uuid-one" }, "start");
h.session.onAssistantDelta({ turnId: 7, delta: "SAME café" });
h.session.onThinkingDelta({ turnId: 7, delta: "reasoning" });
h.session.paperclipKimiStep({ turnId: 7, step: 1, stepId: "native-step-uuid-one", finishReason: "tool_use" }, "end");
h.session.paperclipKimiStep({ turnId: 7, step: 2, stepId: "native-step-uuid-two" }, "start");
h.session.onAssistantDelta({ turnId: 7, delta: "SAME café" });
h.session.paperclipKimiStep({ turnId: 7, step: 2, stepId: "native-step-uuid-two", finishReason: "end_turn" }, "end");
h.session.paperclipKimiStep({ turnId: 7, step: 3, stepId: "native-step-uuid-empty" }, "start");
h.session.paperclipKimiStep({ turnId: 7, step: 3, stepId: "native-step-uuid-empty", finishReason: "end_turn" }, "end");
const chunks = h.events.map(event => event.update);
assert.equal(h.rejected.length, 0);
assert.deepEqual(chunks.map(chunk => chunk._meta.kind), ["start", "delta", "delta", "end:tool_use", "start", "delta", "end:end_turn", "start", "end:end_turn"]);
assert.equal(chunks[0].messageId, `kimi-message-${sha(JSON.stringify(["native-session", 7, "native-step-uuid-one"]))}`);
assert.equal(new Set(chunks.map(chunk => chunk.messageId)).size, 3);
assert.equal(chunks[2].sessionUpdate, "agent_thought_chunk");
assert.ok(chunks.every(chunk => chunk._meta.origin === "kimi-native-assistant" && chunk._meta.source === "kimi-turn-step-v1"));
assert.ok(chunks.filter(chunk => chunk._meta.kind !== "delta").every(chunk => chunk.content.text === ""));
h.session.onAssistantDelta({ turnId: 8, delta: "foreign turn" });
assert.equal(h.events.length, 9);
for (const mutate of [
value => value.session.onAssistantDelta({ turnId: 7, delta: "orphan" }),
value => { value.session.paperclipKimiStep({ turnId: 7, step: 1, stepId: "uuid" }, "start"); value.session.paperclipKimiStep({ turnId: 7, step: 2, stepId: "other" }, "start"); },
value => { value.session.paperclipKimiStep({ turnId: 7, step: 1, stepId: "uuid" }, "start"); value.session.paperclipKimiStep({ turnId: 7, step: 1, stepId: "foreign", finishReason: "end_turn" }, "end"); },
value => { value.session.paperclipKimiStep({ turnId: 7, step: 1, stepId: "uuid" }, "start"); value.session.paperclipKimiStep({ turnId: 7, step: 1, stepId: "uuid", finishReason: "invented" }, "end"); },
value => { value.session.paperclipKimiStep({ turnId: 7, step: 1, stepId: "uuid" }, "start"); value.session.paperclipKimiStep({ turnId: 7, step: 1, reason: "aborted" }, "interrupted"); value.session.paperclipKimiStep({ turnId: 7, step: 2, stepId: "uuid" }, "start"); },
]) {
const invalid = host(); mutate(invalid);
assert.equal(invalid.rejected.length, 1);
assert.equal(invalid.rejected[0].kind, "message_boundary_invalid");
const count = invalid.events.length;
invalid.session.onAssistantDelta({ turnId: 7, delta: "late after failure" });
assert.equal(invalid.events.length, count);
}
});
test("real pinned Kimi and ACPX exchange instructions/tools/denial/errors with a simulated local model service", {
skip: process.env.PAPERCLIP_TEST_KIMI_MODEL_PEER !== "1",
timeout: 60_000,
}, async t => {
const packageRoot = await realpath(fileURLToPath(new URL("../", import.meta.url)));
const assets = join(packageRoot, "provider-assets/kimi", `${process.platform}-${process.arch}`);
try { await access(join(assets, "native-closure.json")); }
catch (error) {
if (error.code !== "ENOENT") throw error;
t.skip("Install the pinned Kimi runtime with paperclipai runtime setup kimi first");
return;
}
const { tsImport } = await import("tsx/esm/api");
const selectorPeer = process.env.PAPERCLIP_TEST_KIMI_FAULT_SELECTOR_PEER === "1";
let compiledFactory;
if (selectorPeer) {
assert.equal(process.platform, "darwin"); assert.equal(process.arch, "arm64");
const cli = join(packageRoot, "dist/cli/acpx-runtime-sidecar.cjs");
const compiled = await readFile(cli, "utf8");
const boundary = "var lines = (0, import_node_readline.createInterface)({";
const at = compiled.indexOf(boundary);
assert.ok(at > 0 && compiled.lastIndexOf(boundary) === at && Buffer.byteLength(compiled) <= 16 * 1024 * 1024);
const module = { exports: {} };
new Function("require", "module", "exports", "__filename", "__dirname", compiled.slice(0, at).replace(/^#![^\n]*\n/, "\n")
+ "\nmodule.exports={verifyKimiInstallation,QUALIFIED_ACPX_PROFILES,acquireAcpxProviderLifetimeLease,createAcpxSidecarHostEnvironment,createSanitizedAcpxSpawnInput};")(
createRequire(cli), module, module.exports, cli, dirname(cli));
compiledFactory = module.exports;
}
const [{ verifyKimiInstallation }, { QUALIFIED_ACPX_PROFILES }, { kimiManagedConfiguration }, instructionModule, { createKimiMessageProjection }, { normalizeAcpxPermission }, environmentModule, { configuredEnvironmentProjection, CONFIGURED_ENVIRONMENT_KEYS }] = await Promise.all([
tsImport("../src/drivers/acpx/kimi-installation.ts", import.meta.url),
tsImport("../src/drivers/acpx/qualified-profiles.ts", import.meta.url),
tsImport("../src/drivers/acpx/kimi-config.ts", import.meta.url),
tsImport("../src/drivers/acpx/kimi-instructions.ts", import.meta.url),
tsImport("../src/drivers/acpx/kimi-message-boundary.ts", import.meta.url),
tsImport("../src/drivers/acpx/acp-permission-adapter.ts", import.meta.url),
tsImport("../src/drivers/acpx/environment.ts", import.meta.url),
tsImport("../src/configured-environment.ts", import.meta.url),
]);
const { createAcpRuntime, createFileSessionStore } = await import(pathToFileURL(join(packageRoot, "node_modules/acpx/dist/runtime.js")));
const root = await realpath(await mkdtemp(join(tmpdir(), "kimi-simulated-model-peer-")));
const home = join(root, "home"), workspace = join(root, "workspace");
await mkdir(home, { mode: 0o700 }); await mkdir(workspace);
const instructions = "\nExact governance café 日本語 ${cwd} ${base_prompt}\nKeep the final LF.\n";
const selectedModel = "kimi-for-coding";
const binding = instructionModule.kimiInstructionBinding(instructions);
const argumentsObject = { payload: "café 🧪\n${literal}", nested: { count: 0, empty: {}, flag: false } };
const toolReceipt = sha(JSON.stringify(argumentsObject));
const deniedPath = join(workspace, "denied-peer.txt");
const notificationEnvironment = {
PAPERCLIP_RUNTIME_NOTIFICATION_CONTEXT: join(root, "controlled-notification-context.json"),
PAPERCLIP_RUNTIME_NOTIFICATION_HELPER: join(root, "controlled-notification-helper"),
};
await writeFile(notificationEnvironment.PAPERCLIP_RUNTIME_NOTIFICATION_CONTEXT, '{"controlledEnvironmentOnly":true}\n', { flag: "wx", mode: 0o600 });
await writeFile(notificationEnvironment.PAPERCLIP_RUNTIME_NOTIFICATION_HELPER, "controlled path fixture\n", { flag: "wx", mode: 0o700 });
const shellQuote = value => "'" + value.replaceAll("'", "'\\''") + "'";
const notificationChecks = Object.entries(notificationEnvironment).map(([key, value]) => `if test "$${key}" != ${shellQuote(value)}; then printf "NOTIFICATION_DISCOVERY_CHANGED"; exit 1; fi; `).join("");
const bashArguments = { command: 'if test -n "${KIMI_API_KEY+x}"; then printf "PROVIDER_KEY_PRESENT"; exit 1; fi; if test -n "${PAPERCLIP_KIMI_INSTRUCTIONS+x}"; then printf "PRIVATE_BINDING_PRESENT"; exit 1; fi; if test "$PAPERCLIP_TASK_SCOPED" != "assigned-task-canary"; then printf "TASK_VARIABLE_MISSING"; exit 1; fi; ' + notificationChecks + 'printf "KIMI_CHILD_CREDENTIAL_FENCE_OK"', timeout: 5 };
const modelRequests = [], mcpCalls = [], permissions = [], deliveries = [], nativeMessages = [], owners = [];
let scenario = "echo", modelStep = 0, peerFailure, permissionGate;
const complete = (response, chunks) => {
response.writeHead(200, { "content-type": "text/event-stream" });
const bytes = Buffer.from(chunks.map(chunk => `data: ${JSON.stringify({ id: "simulated-peer", ...chunk })}\n\n`).join("") + "data: [DONE]\n\n");
const index = bytes.indexOf(Buffer.from("é"));
if (index >= 0) { response.write(bytes.subarray(0, index + 1)); response.end(bytes.subarray(index + 1)); }
else response.end(bytes);
};
const server = createServer(async (request, response) => {
try {
if (request.method !== "POST") { response.writeHead(405); response.end(); return; }
let body = "";
for await (const chunk of request) { body += chunk; assert.ok(Buffer.byteLength(body) < 2_097_152); }
const value = JSON.parse(body);
if (request.url === "/v1/chat/completions") {
assert.equal(request.headers.authorization, "Bearer simulated-peer-not-a-credential");
assert.equal(value.model, selectedModel);
const system = value.messages.find(message => message.role === "system")?.content;
assert.equal(typeof system, "string");
assert.ok(system.endsWith(`\n\n${instructions}`), "exact literal instructions reached native HTTP request");
const ack = nativeMessages.map(entry => entry.message.result?._meta?.paperclipKimiInstructions).filter(Boolean).at(-1);
assert.equal(ack?.digest, binding.digest);
assert.equal(ack?.effectiveDigest, `sha256:${sha(system)}`, "native instruction receipt matches the full actual model-request prompt");
modelRequests.push({ scenario, value });
assert.ok(modelRequests.length <= 9, "bounded simulated model requests");
modelStep++;
if (scenario === "error") {
response.writeHead(418, { "content-type": "application/json" });
response.end(JSON.stringify({ error: { type: "simulated_provider_error", message: "private-peer-error-canary" } }));
return;
}
if (modelStep === 1) {
const tools = scenario === "echo"
? [{ id: "peer-echo", name: "mcp__peer__echo_peer", args: argumentsObject }, { id: "peer-empty", name: "mcp__peer__empty_peer", args: {} }]
: scenario === "bash" ? [{ id: "peer-bash", name: "Bash", args: bashArguments }]
: [{ id: `peer-${scenario}`, name: "Write", args: { path: permissionGate?.path ?? deniedPath, content: "must never be written" } }];
for (const tool of tools) assert.ok(value.tools.some(declared => declared.function?.name === tool.name), `native request discloses ${tool.name}`);
complete(response, [
{ choices: [{ index: 0, delta: { content: "Before tool café." } }] },
{ choices: [{ index: 0, delta: { tool_calls: tools.map((tool, index) => ({ index, id: tool.id, type: "function", function: { name: tool.name, arguments: JSON.stringify(tool.args).slice(0, 1) } })) } }] },
...tools.map((tool, index) => ({ choices: [{ index: 0, delta: { tool_calls: [{ index, function: { arguments: JSON.stringify(tool.args).slice(1) } }] } }] })),
{ choices: [{ index: 0, delta: {}, finish_reason: "tool_calls" }] },
]);
} else {
const result = value.messages.find(message => message.role === "tool" && message.tool_call_id === `peer-${scenario}`);
assert.ok(result, "the exact native tool result returned to the model");
if (scenario === "echo") {
assert.ok(JSON.stringify(result).includes(toolReceipt), `MCP receiver result reached the next native request: ${JSON.stringify({ result, mcpCalls: mcpCalls.length, permissions })}`);
assert.ok(value.messages.some(message => message.role === "tool" && message.tool_call_id === "peer-empty" && JSON.stringify(message).includes("EMPTY_TOOL_ACCEPTED")), "zero-argument MCP result returned to the native model");
}
else if (scenario === "bash") assert.match(result.content, /^(?:Wall time: [0-9.]+ seconds\n)?KIMI_CHILD_CREDENTIAL_FENCE_OK\n?$/, "native Bash excluded provider/private credentials and retained the assigned task variable plus owned Runner notification discovery");
else assert.match(JSON.stringify(result), /denied|rejected|approval/i);
complete(response, [{ choices: [{ index: 0, delta: { content: scenario === "echo" ? "PEER_ECHO_OK café" : scenario === "bash" ? "PEER_BASH_OK" : "PEER_DENIED_OK" } }] },
{ choices: [{ index: 0, delta: {}, finish_reason: "stop" }], usage: { prompt_tokens: 12, completion_tokens: 3, total_tokens: 15 } }]);
}
return;
}
assert.equal(request.url, "/mcp");
if (value.id === undefined) { response.writeHead(202); response.end(); return; }
if (value.method === "tools/call") {
assert.ok(["echo_peer", "empty_peer"].includes(value.params.name));
assert.deepEqual(value.params.arguments, value.params.name === "echo_peer" ? argumentsObject : {});
mcpCalls.push(value.params);
}
const result = value.method === "initialize"
? { protocolVersion: value.params.protocolVersion, capabilities: { tools: {} }, serverInfo: { name: "simulated-peer", version: "1" } }
: value.method === "tools/list" ? { tools: [{ name: "echo_peer", description: "Echo the supplied JSON through the controlled peer.",
inputSchema: { type: "object", properties: { payload: { type: "string" }, nested: { type: "object", additionalProperties: true } }, required: ["payload", "nested"] } },
{ name: "empty_peer", description: "Return a receipt from an empty object argument.", inputSchema: { type: "object", properties: {}, additionalProperties: false } }] }
: { content: [{ type: "text", text: value.params.name === "empty_peer" ? "EMPTY_TOOL_ACCEPTED" : JSON.stringify({ echoed: argumentsObject, receipt: toolReceipt }) }] };
response.writeHead(200, { "content-type": "application/json" });
response.end(JSON.stringify({ jsonrpc: "2.0", id: value.id, result }));
} catch (error) {
peerFailure ??= error;
if (!response.headersSent) response.writeHead(400, { "content-type": "application/json" });
response.end(JSON.stringify({ error: { type: "peer_assertion_failed", message: "simulated peer assertion failed" } }));
}
});
const keys = ["PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT", "PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST"];
const previous = keys.map(key => process.env[key]);
const closeOwner = async owner => {
if (owner.closed) return;
owner.closed = true;
const started = Date.now();
const terminate = setTimeout(() => { for (const child of owner.children) if (child.exitCode === null && child.signalCode === null) child.kill("SIGTERM"); }, 2_000);
const kill = setTimeout(() => { for (const child of owner.children) if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL"); }, 3_000);
try {
if (owner.handle) await owner.runtime.close({ handle: owner.handle, reason: "simulated peer cleanup" });
} finally {
try {
await Promise.all(owner.completions);
assert.ok(Date.now() - started < 5_000, "native process cleanup is bounded");
} finally { clearTimeout(terminate); clearTimeout(kill); await owner.lease.close(); await owner.fence?.close(); }
}
};
try {
await new Promise((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", resolve); });
const endpoint = `http://127.0.0.1:${server.address().port}`;
const config = kimiManagedConfiguration(selectedModel).replace(/^base_url = .+$/m, `base_url = ${JSON.stringify(`${endpoint}/v1`)}`)
+ "\n[loop_control]\nmax_attempts_per_step = 1\n";
await writeFile(join(home, "config.toml"), config, { mode: 0o600 });
process.env[keys[0]] = packageRoot; process.env[keys[1]] = join(packageRoot, "package.json");
const installation = compiledFactory
? await compiledFactory.verifyKimiInstallation(compiledFactory.QUALIFIED_ACPX_PROFILES.kimi)
: await verifyKimiInstallation(QUALIFIED_ACPX_PROFILES.kimi);
const store = createFileSessionStore({ stateDir: join(root, "acpx-state") });
const taskEnvironment = configuredEnvironmentProjection({ PAPERCLIP_TASK_SCOPED: "assigned-task-canary",
...Object.fromEntries(Object.keys(notificationEnvironment).map(key => [key, "forged-task-override"])) });
for (const key of Object.keys(notificationEnvironment)) assert.ok(!Object.hasOwn(taskEnvironment, key));
const explicitEnvironment = { ...taskEnvironment, ...notificationEnvironment, PATH: "/usr/bin:/bin", LANG: "C.UTF-8", KIMI_API_KEY: "simulated-peer-not-a-credential" };
const sessionId = "controlled-native-environment";
const compose = compiledFactory ?? environmentModule;
const sidecarEnvironment = compose.createAcpxSidecarHostEnvironment({ ...explicitEnvironment,
[environmentModule.ACPX_CREDENTIAL_BINDING_ENV]: environmentModule.createAcpxCredentialBinding(explicitEnvironment, "kimi", sessionId) }, "kimi", sessionId);
const launchEnvironment = compose.createSanitizedAcpxSpawnInput(sidecarEnvironment, "kimi").env;
for (const [key, value] of Object.entries(notificationEnvironment)) {
assert.equal(launchEnvironment[key], value);
assert.throws(() => compose.createSanitizedAcpxSpawnInput({ ...sidecarEnvironment,
[CONFIGURED_ENVIRONMENT_KEYS]: JSON.stringify([key]), [key]: "forged-task-override" }, "kimi"), /configured environment projection/);
}
assert.equal(launchEnvironment.PAPERCLIP_TASK_SCOPED, "assigned-task-canary");
assert.equal(launchEnvironment.KIMI_API_KEY, "simulated-peer-not-a-credential");
const spawnNative = (lease, owner) => {
const child = lease.spawn([], { cwd: workspace, stdio: "pipe", env: { ...launchEnvironment, HOME: home,
CI: "1", NO_COLOR: "1", TERM: "dumb", KIMI_CODE_HOME: home,
KIMI_CODE_NO_AUTO_UPDATE: "1", KIMI_DISABLE_TELEMETRY: "1",
KIMI_CODE_WATCH: "0", KIMI_DISABLE_CRON: "1", PAPERCLIP_KIMI_INSTRUCTIONS: binding.payload,
...(selectorPeer ? { PAPERCLIP_FAULT_ENV_CANARY: "must-not-appear-in-observed-argv" } : {}) } },
owner.fence ? { credentialFenceFds: owner.fence.lifetimeFenceFds, activateCredentialFenceOwner: pid => owner.fence.activateLifetimeOwner(pid) } : undefined);
owner.children.push(child);
owner.completions.push(new Promise((resolve, reject) => { child.once("close", resolve); child.once("error", reject); }));
return child;
};
const connect = async (resumeSessionId, sessionKey = "scripted-kimi-peer") => {
const lease = await installation.openCommand();
const admission = instructionModule.createKimiInstructionAdmission(instructions);
const fence = compiledFactory ? await compiledFactory.acquireAcpxProviderLifetimeLease({ agentHomeDirectory: home }) : undefined;
const owner = { lease, fence, children: [], completions: [], handle: undefined, runtime: undefined, closed: false };
owners.push(owner);
owner.runtime = createAcpRuntime({ cwd: workspace, sessionStore: store,
agentRegistry: { resolve: () => "paperclip-verified-kimi-command", list: () => ["kimi"] },
mcpServers: [{ type: "http", name: "peer", url: `${endpoint}/mcp`, headers: [] }],
permissionMode: "approve-all", permissionPolicy: { defaultAction: "escalate" }, timeoutMs: 15_000,
protocolGuardFactory: () => admission.createGuard(),
onAcpMessage: (direction, message) => nativeMessages.push({ owner, direction, message }),
onPermissionRequest: async (request, context) => {
permissions.push(request.raw);
assert.ok(context.responseDelivery instanceof Promise);
assert.ok(request.raw.options.every(option => option.kind !== "allow_always"));
const normalized = normalizeAcpxPermission(request, { provider: "kimi", workingDirectory: workspace });
if (request.raw.toolCall.title === "Write") {
assert.equal(request.raw.toolCall.kind, "edit", "native approval carries the actual tool's canonical kind");
const path = request.raw.toolCall.rawInput.path;
assert.equal(normalized.title, `Change file: ${path.slice(workspace.length + 1)}`);
assert.equal(normalized.kind, "edit");
assert.deepEqual(normalized.choices.map(choice => choice.key), ["accept", "decline", "cancel"]);
for (const workingDirectory of [undefined, home]) {
const unsafe = normalizeAcpxPermission(request, { provider: "kimi", workingDirectory });
assert.equal(unsafe.title, "File change requested; target unavailable. Deny or cancel this request.");
assert.deepEqual(unsafe.choices.map(choice => choice.key), ["decline", "cancel"]);
assert.throws(() => unsafe.resolve({ action: "accept" }), /not an offered choice/);
}
}
if (permissionGate) {
assert.equal(request.raw.toolCall.title, "Write");
assert.deepEqual(request.raw.toolCall.rawInput, { path: permissionGate.path, content: "must never be written" });
permissionGate.context = context;
permissionGate.delivery = context.responseDelivery.then(() => "delivered", () => "suppressed");
permissionGate.requestId = nativeMessages.findLast(entry => entry.owner === owner && entry.direction === "inbound"
&& entry.message.method === "session/request_permission"
&& entry.message.params.toolCall.rawInput?.path === permissionGate.path)?.message.id;
assert.ok(permissionGate.requestId !== undefined);
permissionGate.markSeen();
return permissionGate.decision;
}
deliveries.push(context.responseDelivery);
if (request.raw.toolCall.title === "Write") {
assert.deepEqual(request.raw.toolCall.rawInput, { path: deniedPath, content: "must never be written" });
await assert.rejects(access(deniedPath), { code: "ENOENT" });
return { outcome: "reject_once" };
}
if (request.raw.toolCall.title === "Bash") {
assert.deepEqual(request.raw.toolCall.rawInput, bashArguments);
return { outcome: "allow_once" };
}
assert.ok(["mcp__peer__echo_peer", "mcp__peer__empty_peer"].includes(request.raw.toolCall.title));
assert.deepEqual(request.raw.toolCall.rawInput, request.raw.toolCall.title === "mcp__peer__echo_peer" ? argumentsObject : {});
return { outcome: "allow_once" };
},
spawnAgent: () => spawnNative(lease, owner),
});
owner.handle = await owner.runtime.ensureSession({ sessionKey, agent: "kimi", mode: "persistent", cwd: workspace,
...(resumeSessionId ? { resumeSessionId } : {}),
sessionOptions: { model: selectedModel } });
return owner;
};
const prompt = async (owner, name) => {
scenario = name; modelStep = 0;
const turn = owner.runtime.startTurn({ handle: owner.handle, mode: "prompt", text: `Controlled ${name} boundary.`, requestId: `peer-${name}` });
const events = [];
const drained = (async () => { for await (const event of turn.events) events.push(event); })();
const result = await turn.result;
await drained;
if (peerFailure) throw peerFailure;
if (name !== "error") {
const chunks = events.filter(event => event.type === "text_delta" && event.stream === "output");
assert.deepEqual(chunks.map(event => event.meta?.kind), ["start", "delta", "end:tool_use", "start", "delta", "end:end_turn"]);
assert.ok(chunks.every(event => /^kimi-message-[a-f0-9]{64}$/.test(event.messageId)
&& event.meta.origin === "kimi-native-assistant" && event.meta.source === "kimi-turn-step-v1"));
assert.equal(new Set(chunks.map(event => event.messageId)).size, 2);
assert.equal(chunks[0].messageId, chunks[2].messageId);
assert.equal(chunks[3].messageId, chunks[5].messageId);
assert.notEqual(chunks[0].messageId, chunks[3].messageId);
assert.equal(chunks[1].text, "Before tool café.");
assert.ok(chunks.filter(event => event.meta.kind !== "delta").every(event => event.text === ""));
const projection = createKimiMessageProjection();
const projected = events.map(event => projection.normalize(event));
projection.settle();
assert.deepEqual(projected.filter(event => event.assistantMessageBoundary).map(event => event.assistantMessageBoundary), [
{ phase: "start" }, { phase: "end", stopReason: "toolUse" },
{ phase: "start" }, { phase: "end", stopReason: "stop" },
]);
}
return { result, events };
};
const first = await connect();
const echoed = await prompt(first, "echo");
assert.equal(echoed.result.status, "completed");
assert.equal(mcpCalls.length, 2);
assert.ok(echoed.events.some(event => event.type === "text_delta" && event.text.includes("PEER_ECHO_OK")));
const shell = await prompt(first, "bash");
assert.equal(shell.result.status, "completed");
assert.ok(shell.events.some(event => event.type === "text_delta" && event.text.includes("PEER_BASH_OK")));
assert.ok(nativeMessages.some(entry => entry.direction === "outbound" && entry.message.method === "initialize" && entry.message.params.clientCapabilities.terminal === true), "actual ACPX offered its mandatory client terminal");
assert.ok(!nativeMessages.some(entry => entry.direction === "inbound" && entry.message.method === "terminal/create"), "Kimi kept Bash in its native fenced process service despite ACPX client terminal capability");
await closeOwner(first);
const second = await connect(first.handle.backendSessionId);
const denied = await prompt(second, "deny");
assert.equal(denied.result.status, "completed");
await assert.rejects(access(deniedPath), { code: "ENOENT" });
const failed = await prompt(second, "error");
assert.equal(failed.result.status, "failed");
assert.match(JSON.stringify(failed.result), /Kimi provider turn failed/);
assert.ok(!JSON.stringify(failed.result).includes("private-peer-error-canary"));
assert.ok(nativeMessages.some(entry => entry.direction === "outbound" && ["session/load", "session/resume"].includes(entry.message.method)), "ACPX restored the native session before the second model turn");
assert.ok(permissions.some(request => request.toolCall.title === "Write"));
assert.equal(permissions.length, 4);
await Promise.all(deliveries);
assert.equal(modelRequests.length, 7);
await closeOwner(second);
assert.ok(owners.every(owner => owner.children.every(child => child.exitCode !== null || child.signalCode !== null)));
await assert.rejects(access(deniedPath), { code: "ENOENT" });
t.diagnostic("SIMULATED provider service; real pinned Kimi 2.3.3 + ACPX manager: 7 native model requests with provider auth intact, exact create/resume instruction suffix, native UUID start/end separates pre-tool commentary from final text despite reused model response IDs, actual provider projection yields toolUse/stop boundaries, two MCP JSON/result round trips including terminal {}, native Bash excludes provider/private credentials and retains the assigned task variable and exact owned Runner notification paths after actual configured-environment projection rejected forged task overrides, rejected Write with no file, typed provider failure, owned process closure; zero external model calls");
if (process.env.PAPERCLIP_TEST_KIMI_LIFECYCLE_PEER === "1") {
const { readProcessTable, observeDescendantProcessTree } = await tsImport("../../../tests/runner-e2e/process-tree.ts", import.meta.url);
const bounded = async (promise, label, milliseconds = 5_000) => {
let timer;
try {
return await Promise.race([promise, new Promise((_, reject) => {
timer = setTimeout(() => reject(new Error(`${label} exceeded its bound`)), milliseconds);
})]);
} finally { clearTimeout(timer); }
};
const nativeWorker = async owner => {
assert.equal(owner.children.length, 1);
if (selectorPeer) {
const { readKimiLocalProcesses, selectKimiLocalTarget } = await tsImport("../../../tests/runner-e2e/kimi-local-provider-fault.ts", import.meta.url);
const { kimiFaultRuntimeContract } = await tsImport("../../../tests/runner-e2e/kimi-provider-fault-contract.ts", import.meta.url);
const table = await readProcessTable(); assert.ok(table);
const root = table.find(row => row.pid === process.pid); assert.ok(root && root.processGroupId === process.pid);
const tree = observeDescendantProcessTree(table, process.pid);
const observed = tree.members.map(row => row.process);
const processes = readKimiLocalProcesses(observed.map(row => row.pid));
assert.ok(!JSON.stringify(processes).includes("must-not-appear-in-observed-argv"));
const runId = process.argv[process.argv.indexOf("--run-id") + 1];
const input = { authority: { pid: root.pid, groupId: root.processGroupId, startedAt: root.started, runId },
journal: observed.map(row => ({ pid: row.pid, parent: row.parentPid, start: row.started })), processes,
workspacePath: workspace, guardLoaderSha256: kimiFaultRuntimeContract("darwin-arm64").guardLoaderSha256 };
const selected = selectKimiLocalTarget(input);
assert.equal(selected.target.command, "kimi-code");
assert.equal(selected.target.argv[0], "kimi-code");
assert.ok(selected.ancestry.some(row => row.pid === owner.children[0].pid));
for (const kind of ["missing", "foreign", "ambiguous"]) {
const wrong = structuredClone(input);
if (kind === "missing") wrong.processes = wrong.processes.filter(row => row.pid !== selected.target.pid);
if (kind === "foreign") wrong.processes.find(row => row.pid === selected.target.ppid).argv[2] = "foreign loader";
if (kind === "ambiguous") { const extra = { ...selected.target, pid: 2147483000 }; wrong.processes.push(extra); wrong.journal.push({ pid: extra.pid, parent: extra.ppid, start: new Date(Number(extra.startUs) / 1000).toISOString() }); }
assert.throws(() => selectKimiLocalTarget(wrong), /kimi_local_provider_fault:/);
}
owner.selectorInput = input;
return table.find(row => row.pid === selected.target.pid);
}
const rootPid = owner.children[0].pid;
const table = await readProcessTable();
assert.ok(table);
const tree = observeDescendantProcessTree(table, rootPid);
const candidates = tree.members.filter(member => member.depth === 1);
assert.equal(candidates.length, 1, `one native Kimi worker belongs directly to the verified command root: ${JSON.stringify(tree.members.map(member => ({ depth: member.depth, kind: member.process.kind })))}`);
const rootProcess = table.find(row => row.pid === rootPid);
const worker = candidates[0].process;
assert.ok(rootProcess && worker.pid > 1 && worker.pid !== rootPid && worker.pid !== process.pid);
const fresh = await readProcessTable();
assert.ok(fresh?.some(row => row.pid === rootPid && row.started === rootProcess.started && row.processGroupId === rootProcess.processGroupId));
assert.ok(fresh?.some(row => row.pid === worker.pid && row.started === worker.started
&& row.parentPid === rootPid && row.processGroupId === worker.processGroupId));
return worker;
};
const assertRetired = async worker => {
const deadline = Date.now() + 5_000;
while (Date.now() < deadline) {
const table = await readProcessTable();
assert.ok(table);
if (!table.some(row => row.pid === worker.pid && row.started === worker.started)) return;
await new Promise(resolve => setTimeout(resolve, 25));
}
assert.fail("the observed native worker survived owned cleanup");
};
for (const boundary of ["stop", "loss"]) {
const path = join(workspace, `${boundary}-must-not-exist.txt`);
let markSeen, decide;
const seen = new Promise(resolve => { markSeen = resolve; });
const decision = new Promise(resolve => { decide = resolve; });
permissionGate = { path, markSeen, decision, decide };
const owner = await connect(undefined, `scripted-kimi-peer-${boundary}`);
scenario = boundary; modelStep = 0;
const turn = owner.runtime.startTurn({ handle: owner.handle, mode: "prompt", text: `Controlled pending permission ${boundary}.`, requestId: `peer-${boundary}` });
const drained = (async () => { for await (const _event of turn.events) {} })();
try {
await bounded(seen, `${boundary} native permission observation`);
await assert.rejects(access(path), { code: "ENOENT" });
const worker = await nativeWorker(owner);
if (boundary === "stop") await bounded(turn.cancel({ reason: "controlled pending permission Stop" }), "native Stop");
else if (selectorPeer) {
const { terminateKimiLocalProvider } = await tsImport("../../../tests/runner-e2e/kimi-local-provider-fault.ts", import.meta.url);
const receipt = terminateKimiLocalProvider(owner.selectorInput);
assert.equal(receipt.target.pid, worker.pid); assert.equal(receipt.workerSignalled, false);
} else process.kill(worker.pid, "SIGKILL");
const result = await bounded(turn.result, `${boundary} terminal result`);
await bounded(drained, `${boundary} event drain`);
if (peerFailure) throw peerFailure;
assert.equal(result.status, boundary === "stop" ? "cancelled" : "failed");
if (boundary === "loss") assert.equal(typeof result.error?.code, "string");
assert.equal(permissionGate.context.signal.aborted, true, "pending host callback was revoked at settlement");
const countAtSettlement = modelRequests.length;
permissionGate.decide({ outcome: "allow_once" });
assert.equal(await bounded(permissionGate.delivery, `${boundary} stale permission suppression`), "suppressed");
await new Promise(resolve => setTimeout(resolve, 100));
assert.equal(modelRequests.length, countAtSettlement, "a stale callback cannot restart provider inference");
assert.ok(!nativeMessages.some(entry => entry.owner === owner && entry.direction === "outbound" && entry.message.id === permissionGate.requestId && entry.message.result?.outcome?.outcome === "selected"), "no stale allow response crossed the provider connection");
await assert.rejects(access(path), { code: "ENOENT" });
await closeOwner(owner);
await assertRetired(worker);
await assert.rejects(access(path), { code: "ENOENT" });
} finally { permissionGate.decide({ outcome: "reject_once" }); permissionGate = undefined; }
}
assert.equal(modelRequests.length, 9);
assert.equal(permissions.length, 6);
if (selectorPeer) t.diagnostic("Compiled production factories and real lifetime fences selected the exact native Kimi child despite ps rendering; actual title mutation and environment exclusion passed, missing/foreign/ambiguous targets were refused, and sealed-byte/inode/run-bound fault injection retired only the owned native tree");
t.diagnostic("SIMULATED provider service; pending native Write permission Stop and exact observed native-worker SIGKILL both revoked host callbacks, suppressed stale allow responses, produced no file/model continuation, settled typed results and retired owned native processes; zero external model calls");
}
} finally {
try { await Promise.all(owners.map(closeOwner)); }
finally {
server.closeAllConnections();
await new Promise(resolve => server.close(resolve));
keys.forEach((key, index) => { if (previous[index] === undefined) delete process.env[key]; else process.env[key] = previous[index]; });
await rm(root, { recursive: true, force: true });
}
}
});
@@ -0,0 +1,111 @@
import assert from "node:assert/strict";
import { execFile, spawn, spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import { once } from "node:events";
import { mkdtemp, mkdir, readFile, readdir, realpath, rm, symlink, writeFile } from "node:fs/promises";
import { createRequire } from "node:module";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import test from "node:test";
import { setTimeout as delay } from "node:timers/promises";
import { promisify } from "node:util";
import { bundleKimiProvisioner } from "./build-verified-provider-entrypoints.mjs";
import { KIMI_DISTRIBUTION_CLOSURE_SHA256 } from "../src/drivers/acpx/kimi-closure-pins.ts";
const run = promisify(execFile);
test("installed Kimi setup is self-contained, rejects corrupt assets and cancels pending downloads", { timeout: 10_000 }, async t => {
const root = await realpath(await mkdtemp(join(tmpdir(), "paperclip-kimi-public-layout-")));
t.after(() => rm(root, { recursive: true, force: true }));
const pkg = join(root, "package"), cli = join(pkg, "dist/vendor/paperclip-runner/cli");
await mkdir(cli, { recursive: true });
await writeFile(join(pkg, "package.json"), '{"name":"@paperclipai/server","type":"module"}');
await writeFile(join(pkg, "dist/index.js"), 'throw Error("server must not start");');
await writeFile(join(cli, "acpx-runtime-sidecar.cjs"), "// installed layout fixture");
const entry = join(cli, "provision-kimi.cjs");
const packageRoot = resolve(dirname(new URL(import.meta.url).pathname), "..");
const result = await bundleKimiProvisioner({ outputRoot: cli });
for (const output of Object.values(result.metafile.outputs)) for (const dependency of output.imports) assert.match(dependency.path, /^node:/);
assert.deepEqual((await readdir(join(cli, "kimi-provision-inputs"))).sort(), ["package-lock.json", "package.json"]);
for (const name of ["package-lock.json", "package.json"]) assert.deepEqual(await readFile(join(cli, "kimi-provision-inputs", name)), await readFile(join(packageRoot, "scripts/kimi-distribution", name)));
const setup = createRequire(import.meta.url)(entry);
const layout = await setup.provisionKimiPackageRoot(entry);
assert.equal(layout.root, pkg);
assert.equal(layout.assetRoot, join(pkg, "dist/vendor/paperclip-runner"));
const linked = join(cli, "linked.cjs"); await symlink(entry, linked);
await assert.rejects(setup.provisionKimiPackageRoot(linked), /installed entrypoint/);
assert.deepEqual(setup.provisionEnvironment({ PATH: "/usr/bin:/bin", HTTPS_PROXY: "http://127.0.0.1:9", KIMI_MODEL_API_KEY: "sensitive-canary", HOME: "/private/home", NODE_OPTIONS: "--require /foreign", NODE_PATH: "/foreign", NPM_TOKEN: "sensitive-canary" }), { PATH: "/usr/bin:/bin", LANG: "C.UTF-8", HTTPS_PROXY: "http://127.0.0.1:9" });
const target = `${process.platform}-${process.arch}`;
const parent = join(layout.assetRoot, "provider-assets/kimi");
await mkdir(join(parent, target, "runtime"), { recursive: true });
const deny = join(root, "deny.cjs");
await writeFile(deny, `process.nextTick(()=>{const assert=require('node:assert/strict');for(const key of ['KIMI_MODEL_API_KEY','NPM_TOKEN','HOME','NODE_OPTIONS','NODE_PATH'])assert.equal(process.env[key],undefined,key)});const fail=()=>{throw Error('UNEXPECTED_NETWORK_OR_CHILD')};globalThis.fetch=fail;for(const m of ['node:net','node:tls','node:http','node:https']){const x=require(m);for(const key of ['connect','createConnection','request','get'])if(key in x)x[key]=fail}const c=require('node:child_process');for(const key of ['spawn','execFile','exec'])c[key]=fail;`);
const child = spawnSync(process.execPath, ["--require", deny, entry], { env: { PATH: "/usr/bin:/bin", KIMI_MODEL_API_KEY: "sensitive-canary", NPM_TOKEN: "sensitive-canary", HOME: "/private/home", NODE_OPTIONS: "", NODE_PATH: "/foreign" }, encoding: "utf8", timeout: 10_000 });
assert.ifError(child.error); assert.equal(child.status, 1); assert.match(child.stderr, /Kimi setup failed/);
assert.doesNotMatch(child.stderr + child.stdout, /UNEXPECTED_NETWORK_OR_CHILD|sensitive-canary/);
assert.deepEqual(await readdir(parent), [target]);
assert.equal(JSON.parse(await readFile(join(pkg, "package.json"), "utf8")).name, "@paperclipai/server");
await rm(join(parent, target), { recursive: true });
const ready = join(root, "download-ready"), aborted = join(root, "download-aborted");
const preload = join(root, "pending-download.cjs");
await writeFile(preload, `globalThis.fetch=(_url,{signal})=>new Promise((_resolve,reject)=>{const pending=setInterval(()=>{},1000);require('node:fs').writeFileSync(${JSON.stringify(ready)},'ready');signal.addEventListener('abort',()=>{clearInterval(pending);require('node:fs').writeFileSync(${JSON.stringify(aborted)},'aborted');reject(signal.reason)},{once:true})});`);
const cancelled = spawn(process.execPath, ["--require", preload, entry], { env: { PATH: "/usr/bin:/bin" }, stdio: ["ignore", "ignore", "pipe"] });
const closed = once(cancelled, "close");
let stderr = ""; cancelled.stderr.on("data", chunk => { stderr = (stderr + chunk).slice(-4096); });
const kill = setTimeout(() => cancelled.kill("SIGKILL"), 5_000);
try {
for (let attempt = 0; attempt < 100; attempt++) {
try { await readFile(ready); break; }
catch (error) { if (error.code !== "ENOENT") throw error; await delay(10); }
}
assert.equal(await readFile(ready, "utf8"), "ready");
cancelled.kill("SIGTERM");
const [code, signal] = await closed;
assert.equal(code, 1); assert.equal(signal, null);
assert.match(stderr, /Kimi setup failed/);
assert.equal(await readFile(aborted, "utf8"), "aborted");
assert.deepEqual(await readdir(parent), []);
} finally { clearTimeout(kill); if (cancelled.exitCode === null && cancelled.signalCode === null) { cancelled.kill("SIGKILL"); await closed; } }
});
test("fresh archived installed provisioner downloads the exact closure, reuses it offline and rejects tampering", {
skip: process.env.PAPERCLIP_TEST_KIMI_PUBLIC_SETUP !== "1" && "Set PAPERCLIP_TEST_KIMI_PUBLIC_SETUP=1 for public registry downloads; no model calls or provider credentials",
timeout: 180_000,
}, async t => {
const root = await realpath(await mkdtemp(join(tmpdir(), "paperclip-kimi-public-setup-")));
t.after(() => rm(root, { recursive: true, force: true }));
const packed = join(root, "packed"), pkg = join(packed, "package"), cli = join(pkg, "dist/vendor/paperclip-runner/cli");
await mkdir(cli, { recursive: true });
await writeFile(join(pkg, "package.json"), '{"name":"@paperclipai/server","type":"module"}');
await writeFile(join(pkg, "dist/index.js"), 'throw Error("server must not start");');
await writeFile(join(cli, "acpx-runtime-sidecar.cjs"), "// installed layout fixture");
await bundleKimiProvisioner({ outputRoot: cli });
const archive = join(root, "setup.tgz"), installed = join(root, "installed");
await run("tar", ["-czf", archive, "-C", packed, "package"], { timeout: 10_000 });
await mkdir(installed);
await run("tar", ["-xzf", archive, "-C", installed], { timeout: 10_000 });
const entry = join(installed, "package/dist/vendor/paperclip-runner/cli/provision-kimi.cjs");
const api = createRequire(import.meta.url)(entry);
const environment = api.provisionEnvironment(process.env);
const fresh = await run(process.execPath, ["--use-env-proxy", entry], { env: { ...environment, KIMI_API_KEY: "sensitive-canary", NPM_TOKEN: "sensitive-canary", HOME: "/private/foreign-home" }, timeout: 120_000, maxBuffer: 1024 * 1024 });
assert.doesNotMatch(fresh.stdout + fresh.stderr, /sensitive-canary/);
const receipt = JSON.parse(fresh.stdout.trim());
assert.equal(receipt.status, "installed_verified");
const target = `${process.platform}-${process.arch}`;
const assets = join(installed, "package/dist/vendor/paperclip-runner/provider-assets/kimi", target);
const metadata = JSON.parse(await readFile(join(assets, "kimi-distribution.json"), "utf8"));
assert.equal(metadata.nativeClosureSha256, KIMI_DISTRIBUTION_CLOSURE_SHA256[target]);
const offline = { ...environment, HTTPS_PROXY: "http://127.0.0.1:9", HTTP_PROXY: "http://127.0.0.1:9", https_proxy: "http://127.0.0.1:9", http_proxy: "http://127.0.0.1:9", NO_PROXY: "", no_proxy: "" };
const reused = await run(process.execPath, ["--use-env-proxy", entry], { env: offline, timeout: 20_000, maxBuffer: 1024 * 1024 });
assert.equal(JSON.parse(reused.stdout.trim()).status, "verified_existing");
const worker = join(assets, "runtime/node_modules/@moonshot-ai/kimi-code/dist/search-worker.mjs");
const changed = Buffer.concat([await readFile(worker), Buffer.from("\n// integrity test\n")]);
await writeFile(worker, changed);
await assert.rejects(run(process.execPath, ["--use-env-proxy", entry], { env: offline, timeout: 20_000, maxBuffer: 1024 * 1024 }), error => {
assert.match(error.stderr, /Kimi setup failed/);
assert.doesNotMatch(error.stderr, /download|ECONNREFUSED/i);
return true;
});
assert.deepEqual(await readFile(worker), changed, "Invalid existing assets must not be silently replaced");
t.diagnostic(JSON.stringify({ target, closure: metadata.nativeClosureSha256, patch: metadata.patch.version, provisionerSha256: createHash("sha256").update(await readFile(entry)).digest("hex"), freshInstalled: true, offlineReuse: true, tamperRejected: true, modelPrompts: 0, scope: "self-contained public-layout setup component; full Paperclip tarball is a separate proof" }));
});
@@ -0,0 +1,120 @@
#!/usr/bin/env node
import { constants } from "node:fs";
import { lstat, mkdir, mkdtemp, open, readdir, realpath, rename, rm, unlink, writeFile } from "node:fs/promises";
import { basename, dirname, join, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { materializeKimiDistribution, kimiDistributionEnvironment } from "./materialize-kimi-distribution.mjs";
import { KIMI_DISTRIBUTION_CLOSURE_SHA256 } from "../src/drivers/acpx/kimi-closure-pins.ts";
import { verifyKimiInstallation } from "../src/drivers/acpx/kimi-installation.ts";
import { QUALIFIED_ACPX_PROFILES } from "../src/drivers/acpx/qualified-profiles.ts";
export function provisionEnvironment(source = process.env) {
return { ...kimiDistributionEnvironment(source), LANG: "C.UTF-8", PATH: source.PATH ?? "/usr/bin:/bin" };
}
export async function provisionKimiPackageRoot(entrypoint) {
const canonical = await realpath(entrypoint);
if (canonical !== resolve(entrypoint) || basename(canonical) !== "provision-kimi.cjs" || !(await lstat(canonical)).isFile()) throw new Error("Kimi setup requires its installed entrypoint");
const cli = dirname(canonical);
const vendored = cli.endsWith("/dist/vendor/paperclip-runner/cli");
if (!vendored && !cli.endsWith("/dist/cli")) throw new Error("Kimi setup requires the installed runner or server layout");
const root = resolve(cli, vendored ? "../../../.." : "../..");
const manifest = join(root, "package.json");
const file = await open(manifest, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
try {
const before = await file.stat({ bigint: true });
if (!before.isFile() || before.nlink !== 1n || before.size < 1n || before.size > 64n * 1024n) throw new Error("Kimi setup package manifest is invalid");
const bytes = await file.readFile(), after = await file.stat({ bigint: true }), named = await lstat(manifest, { bigint: true });
if (bytes.length !== Number(before.size) || before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs || named.dev !== before.dev || named.ino !== before.ino || named.isSymbolicLink()) throw new Error("Kimi setup package manifest changed");
if (JSON.parse(bytes.toString("utf8")).name !== (vendored ? "@paperclipai/server" : "@paperclipai/paperclip-runner")) throw new Error("Kimi setup package identity does not match its layout");
} finally { await file.close(); }
return { root, manifest, cli, assetRoot: vendored ? resolve(cli, "..") : root };
}
async function verifyInstallation(root, manifest) {
const keys = ["PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT", "PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST"], previous = keys.map(key => process.env[key]);
process.env[keys[0]] = root; process.env[keys[1]] = manifest;
try {
const installation = await verifyKimiInstallation(QUALIFIED_ACPX_PROFILES.kimi);
const lease = await installation.openCommand();
await lease.close();
} finally { keys.forEach((key, index) => { if (previous[index] === undefined) delete process.env[key]; else process.env[key] = previous[index]; }); }
}
async function absent(path) { try { await lstat(path); return false; } catch (error) { if (error.code === "ENOENT") return true; throw error; } }
async function containedDirectory(root, path) {
await mkdir(path, { recursive: true, mode: 0o700 });
if (await realpath(path) !== path || !(await lstat(path)).isDirectory() || !path.startsWith(root + "/")) throw new Error("Kimi setup asset directory escapes its package");
}
export async function provisionKimi(entrypoint, checkCancelled = () => {}, signal) {
checkCancelled(); signal?.throwIfAborted();
const target = `${process.platform}-${process.arch}`;
if (!Object.hasOwn(KIMI_DISTRIBUTION_CLOSURE_SHA256, target)) throw new Error(`Kimi is not supported on ${target}`);
const { root, manifest, cli, assetRoot } = await provisionKimiPackageRoot(entrypoint);
const assets = join(assetRoot, "provider-assets"), parent = join(assets, "kimi");
await containedDirectory(root, assets); await containedDirectory(root, parent);
const lockPath = join(parent, `.setup-${target}.lock`);
const lock = await open(lockPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600);
let lockIdentity, temporary, temporaryIdentity, published, committed = false;
const output = join(parent, target);
try {
lockIdentity = await lock.stat({ bigint: true });
checkCancelled();
if (!(await absent(output))) {
await verifyInstallation(root, manifest);
return { status: "verified_existing", target, profileDigest: QUALIFIED_ACPX_PROFILES.kimi.commandDigest };
}
temporary = await mkdtemp(join(parent, ".setup-private-")); temporaryIdentity = await lstat(temporary, { bigint: true });
const stagingRoot = join(temporary, "package"); await mkdir(stagingRoot, { mode: 0o700 });
await writeFile(join(stagingRoot, "package.json"), JSON.stringify({ name: "@paperclipai/paperclip-runner" }), { flag: "wx", mode: 0o600 });
const stagedOutput = join(stagingRoot, "provider-assets/kimi", target);
await materializeKimiDistribution({ outputRoot: stagedOutput, lockDirectory: join(cli, "kimi-provision-inputs"), checkCancelled, signal });
await verifyInstallation(stagingRoot, join(stagingRoot, "package.json"));
if (!(await absent(output))) throw new Error("Kimi setup destination appeared during installation; refusing replacement");
checkCancelled();
await mkdir(output, { mode: 0o700 }); published = await lstat(output, { bigint: true });
for (const name of await readdir(stagedOutput)) {
const named = await lstat(output, { bigint: true });
if (named.dev !== published.dev || named.ino !== published.ino || named.isSymbolicLink()) throw new Error("Kimi setup output ownership changed during publication");
await rename(join(stagedOutput, name), join(output, name));
}
await verifyInstallation(root, manifest);
checkCancelled(); committed = true;
return { status: "installed_verified", target, profileDigest: QUALIFIED_ACPX_PROFILES.kimi.commandDigest };
} finally {
const cleanup = [];
if (published && !committed) cleanup.push((async () => {
const named = await lstat(output, { bigint: true });
if (named.dev !== published.dev || named.ino !== published.ino || named.isSymbolicLink()) throw new Error("Kimi setup output ownership changed; refusing cleanup");
await rm(output, { recursive: true });
})());
if (temporary) cleanup.push((async () => {
const named = await lstat(temporary, { bigint: true });
if (!temporaryIdentity || named.dev !== temporaryIdentity.dev || named.ino !== temporaryIdentity.ino || named.isSymbolicLink()) throw new Error("Kimi setup staging ownership changed; refusing cleanup");
await rm(temporary, { recursive: true });
})());
cleanup.push((async () => {
try {
lockIdentity ??= await lock.stat({ bigint: true }); const named = await lstat(lockPath, { bigint: true });
if (named.dev !== lockIdentity.dev || named.ino !== lockIdentity.ino) throw new Error("Kimi setup lock ownership changed; refusing cleanup");
await unlink(lockPath);
} finally { await lock.close(); }
})());
const results = await Promise.allSettled(cleanup), failed = results.filter(result => result.status === "rejected");
if (failed.length) throw new AggregateError(failed.map(result => result.reason), "Kimi setup cleanup failed; inspect the package before retrying");
}
}
if (process.argv[1] && pathToFileURL(resolve(process.argv[1])).href === import.meta.url) {
if (process.argv.length !== 2) throw new Error("Usage: paperclipai runtime setup kimi (explicit host installation; no model calls)");
const environment = provisionEnvironment();
for (const key of Object.keys(process.env)) delete process.env[key]; Object.assign(process.env, environment);
const controller = new AbortController();
const cancel = () => controller.abort();
process.on("SIGINT", cancel); process.on("SIGTERM", cancel);
const deadline = setTimeout(cancel, 900_000); deadline.unref();
provisionKimi(fileURLToPath(import.meta.url), () => { if (controller.signal.aborted) throw new Error("Kimi setup cancelled; no installation was admitted"); }, controller.signal).finally(() => {
clearTimeout(deadline); process.off("SIGINT", cancel); process.off("SIGTERM", cancel);
}).then(result => console.log(JSON.stringify(result))).catch(error => { console.error(`Kimi setup failed: ${error.message}`); process.exitCode = 1; });
}
@@ -1,13 +1,14 @@
import { createHash } from "node:crypto";
import profiles from "../acpx-profiles.json" with { type: "json" };
import distributions from "../cursor-distributions.json" with { type: "json" };
import { KIMI_DISTRIBUTION_CLOSURE_SHA256 as kimiClosures } from "../src/drivers/acpx/kimi-closure-pins.ts";
const canonical = value => 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);
/** Release assembly must reject a self-consistent pack built for another release. */
export function verifyReleaseProviderPack(identity, sourceRevision, target = "linux-x64", expected = { profiles, distributions }) {
export function verifyReleaseProviderPack(identity, sourceRevision, target = "linux-x64", expected = { profiles, distributions, kimiClosures }) {
const { profiles: releaseProfiles, distributions: releaseDistributions } = expected;
const payload = identity?.payload;
if (identity?.schema !== "paperclip-runner/remote-provider-pack/v1" || !payload
@@ -46,5 +47,20 @@ export function verifyReleaseProviderPack(identity, sourceRevision, target = "li
|| !/^sha256:[a-f0-9]{64}$/.test(copilot.sha256 ?? "")) {
throw new Error("Remote provider-pack Copilot identity does not match this release");
}
const kimiProfile = releaseProfiles.profiles.kimi;
if (kimiProfile && kimiProfile.qualificationStatus !== "pending") {
const releaseKimiClosures = expected.kimiClosures
?? (kimiProfile.commandDigest === profiles.profiles.kimi.commandDigest ? kimiClosures : undefined);
const kimi = payload.providers?.kimi;
if (Object.hasOwn(payload.candidateProviders ?? {}, "kimi") || !kimi
|| kimi.version !== kimiProfile.agentServerVersion || kimi.profileDigest !== kimiProfile.commandDigest
|| !/^[a-f0-9]{64}$/.test(releaseKimiClosures?.["linux-x64"] ?? "")
|| kimi.closureDigest !== `sha256:${releaseKimiClosures["linux-x64"]}`
|| kimi.qualification !== "qualified" || kimi.path !== "provider-assets/kimi/linux-x64"
|| Object.keys(kimi).sort().join(",") !== "closureDigest,path,profileDigest,qualification,sha256,version"
|| !/^sha256:[a-f0-9]{64}$/.test(kimi.sha256 ?? "")) {
throw new Error("Remote provider-pack Kimi identity does not match this release");
}
}
return identity;
}
@@ -7,6 +7,8 @@ import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import test from "node:test";
import profiles from "../acpx-profiles.json" with { type: "json" };
import historicalKimi from "../test/fixtures/kimi-profile-v14-identity.json" with { type: "json" };
import { KIMI_DISTRIBUTION_CLOSURE_SHA256 as kimiClosures } from "../src/drivers/acpx/kimi-closure-pins.ts";
import distributions from "../cursor-distributions.json" with { type: "json" };
import { verifyReleaseProviderPack } from "./release-provider-pack.mjs";
import { existingReleaseProviderPack, releaseProviderPackManifest } from "./stage-release-runner-binaries.mjs";
@@ -25,6 +27,9 @@ function fixture() {
payload.providers.copilot = { version: profiles.profiles.copilot.agentServerVersion,
profileDigest: profiles.profiles.copilot.commandDigest, closureDigest: "sha256:a3d8f4367cfa1694d79e3f8b7930b6fbfe42a229c272b375b11951d631db8d07",
qualification: "qualified", path: "provider-assets/copilot/linux-x64", sha256: digest };
payload.providers.kimi = { version: profiles.profiles.kimi.agentServerVersion,
profileDigest: profiles.profiles.kimi.commandDigest, closureDigest: `sha256:${kimiClosures["linux-x64"]}`,
qualification: "qualified", path: "provider-assets/kimi/linux-x64", sha256: digest };
return payload;
}
function seal(payload) { return { schema: "paperclip-runner/remote-provider-pack/v1", payload,
@@ -55,7 +60,7 @@ test("release assembly rejects rehashed stale or missing packs before staging an
const source = dirname(dirname(fileURLToPath(import.meta.url)));
try {
// Run the actual assembler in an isolated layout so it cannot overwrite built artifacts.
for (const file of ["scripts/stage-release-runner-binaries.mjs", "scripts/release-provider-pack.mjs", "src/live/runner-binary.ts", "acpx-profiles.json", "cursor-distributions.json", "release-runner-targets.json"]) {
for (const file of ["scripts/stage-release-runner-binaries.mjs", "scripts/release-provider-pack.mjs", "src/live/runner-binary.ts", "src/drivers/acpx/kimi-closure-pins.ts", "acpx-profiles.json", "cursor-distributions.json", "release-runner-targets.json"]) {
mkdirSync(dirname(join(root, file)), { recursive: true }); cpSync(join(source, file), join(root, file));
}
const platforms = {};
@@ -172,3 +177,29 @@ test("uses the frozen release's Copilot profile when the workflow checkout has a
assert.equal(verifyReleaseProviderPack(manifest, revision, "linux-x64", { profiles: releaseProfiles, distributions }), manifest);
assert.throws(() => verifyReleaseProviderPack(manifest, revision), /Copilot identity/);
});
for (const field of ["version", "profileDigest", "closureDigest", "qualification", "path", "sha256"]) {
test(`rejects an independently rehashed stale Kimi ${field}`, () => {
const payload = fixture(); payload.providers.kimi[field] = "stale";
assert.throws(() => verifyReleaseProviderPack(seal(payload), revision), /Kimi identity/);
});
}
test("released Kimi requires one ordinary identity and exact frozen closure", () => {
const missing = fixture(); delete missing.providers.kimi;
assert.throws(() => verifyReleaseProviderPack(seal(missing), revision), /Kimi identity/);
const duplicate = fixture(); duplicate.candidateProviders = { kimi: duplicate.providers.kimi };
assert.throws(() => verifyReleaseProviderPack(seal(duplicate), revision), /Kimi identity/);
const frozen = structuredClone(profiles); frozen.profiles.kimi.commandDigest = "sha256:" + "e".repeat(64);
const payload = fixture(); payload.providers.kimi.profileDigest = frozen.profiles.kimi.commandDigest;
assert.throws(() => verifyReleaseProviderPack(seal(payload), revision, "linux-x64", { profiles: frozen, distributions }), /Kimi identity/);
assert.equal(verifyReleaseProviderPack(seal(payload), revision, "linux-x64", { profiles: frozen, distributions, kimiClosures }).payload, payload);
});
test("historical pending releases keep their separate candidate inventory", () => {
const historical = structuredClone(profiles);
historical.profiles.kimi = { ...historical.profiles.kimi, agentProfileVersion: 14,
commandDigest: historicalKimi.commandDigest, qualificationStatus: historicalKimi.declaration.qualificationStatus };
const payload = fixture(); payload.candidateProviders = { kimi: { ...payload.providers.kimi,
profileDigest: historicalKimi.commandDigest, qualification: "pending" } }; delete payload.providers.kimi;
const pack = seal(payload);
assert.equal(verifyReleaseProviderPack(pack, revision, "linux-x64", { profiles: historical, distributions }), pack);
});
@@ -7,7 +7,7 @@
"src/scenarios/scenario-plan.ts"
],
"counts": {
"actions": 69,
"actions": 70,
"legacyRequirements": 106
},
"actions": [
@@ -106,6 +106,25 @@
"src/scenarios/scenario-explorer.test.ts::renders every scenario with exposure, control plane, authorization, diff, and parity"
]
},
{
"id": "send_chat_message",
"ownership": "always_agent_tool",
"surfaces": [
"live"
],
"legacyAliases": [],
"contractCase": "protocol-action:send_chat_message",
"contractOwner": "src/catalog/protocol-action-contracts.test.ts::send_chat_message has a schema-valid canonical example and every declared projection",
"legacyBehavioralCases": [],
"deterministicCases": [
"protocol-action:send_chat_message"
],
"legacyRequirementCases": [],
"deterministicOwners": [
"src/catalog/protocol-action-contracts.test.ts::send_chat_message has a schema-valid canonical example and every declared projection",
"src/scenarios/scenario-explorer.test.ts::renders every scenario with exposure, control plane, authorization, diff, and parity"
]
},
{
"id": "set_task_monitor",
"ownership": "optional_agent_tool",
@@ -262,12 +262,13 @@
"owner": "always tools",
"operationIds": [
"get_task_history",
"report_progress"
"report_progress",
"send_chat_message"
],
"controlPlaneOperationIds": [
"append_audit_record"
],
"realSurface": "issue comment list/get/create routes",
"realSurface": "issue comment list/get/create routes and own-agent chat messaging service",
"mockStateDomains": [
"task",
"comments",
@@ -275,8 +276,8 @@
"idempotency",
"audit"
],
"prpEvidence": "bounded read result or idempotent comment-write receipt plus audit reference",
"gap": "Active-task binding is unbound; cross-task comment mutation is deliberately outside V1."
"prpEvidence": "bounded read result, idempotent progress receipt, or persisted proactive chat comment with source task/run provenance",
"gap": "Proactive chat delivery is bound to the running agent and responsible human; arbitrary chat destinations are unsupported. Other comment/history binding gaps remain recorded per operation."
},
{
"id": "se",
@@ -6,7 +6,7 @@ Status: canonical explanatory contract for the Paperclip runner V1 surface.
This document keeps three independent meanings of **group** separate. PRP families describe wire evidence and controller commands; capability placement decides who owns an operation; behavioral eval groups organize the 106 scenario corpus. None of the three axes can be used as a substitute for another.
The generated totals are **107 PRP events in 32 event families**, **18 controller commands in 7 command families**, **10 control-plane operations**, **69 reconciled semantic operations** (18 always, 51 optional), and **106 scenarios in 16 behavior groups**.
The generated totals are **107 PRP events in 32 event families**, **18 controller commands in 7 command families**, **10 control-plane operations**, **70 reconciled semantic operations** (19 always, 51 optional), and **106 scenarios in 16 behavior groups**.
## Axis 1: PRP v1 event and command families
@@ -84,9 +84,9 @@ Placement has exactly three outcomes:
| `schedule_blocker_wake` | Dependency-resolution wake scheduling. |
| `reconcile_run` | Work assessment, terminal status arbitration, and recovery reconciliation. |
### Always-agent operations (18)
### Always-agent operations (19)
`answer_status_question`, `block_task`, `finish_task`, `get_agent_instruction_history`, `get_task_context`, `get_task_history`, `inspect_operation_result`, `list_document_revisions`, `list_documents`, `read_agent_instructions`, `read_document`, `register_deliverable`, `report_progress`, `request_human_input`, `request_review`, `restore_agent_instructions`, `update_agent_instructions`, `write_document`.
`answer_status_question`, `block_task`, `finish_task`, `get_agent_instruction_history`, `get_task_context`, `get_task_history`, `inspect_operation_result`, `list_document_revisions`, `list_documents`, `read_agent_instructions`, `read_document`, `register_deliverable`, `report_progress`, `request_human_input`, `request_review`, `restore_agent_instructions`, `send_chat_message`, `update_agent_instructions`, `write_document`.
### Optional operations (51) and grant groups (14)
@@ -172,6 +172,7 @@ Grant groups are documentation/exposure bundles, not additional authority. The o
| `search_api` | `optional_agent_tool` | `api:discover` | `standard`<br>`ask`<br>`planning`<br>`skill_test` | `read` | `none` | no | inline/no mapping | `live`<br>`live_codex` | `PaperclipRunnerToolAuthority`<br>Authenticated PRP tool input/result and existing HTTP route authorization/activity records.<br>catalog PRP status: `bound` |
| `search_tasks` | `optional_agent_tool` | `discovery:tasks:read` | `standard`<br>`ask`<br>`planning`<br>`skill_test` | `read` | `none` | no | `snapshot_read:company_tasks` | `scenario` + `live`<br>`live_codex` | `unbound`<br>read projection surfaced via a tool-result item event; no control-plane state diff<br>catalog PRP status: `audit_pending` |
| `select_workspace` | `optional_agent_tool` | none | `standard`<br>`skill_test` | `write` | `required` | no | inline/no mapping | `live`<br>`live_codex` | `PaperclipRunnerToolAuthority`<br>Authenticated task workspace routes and durable selection/repository receipts.<br>catalog PRP status: `bound` |
| `send_chat_message` | `always_agent_tool` | none | `standard` | `task_write` | `required` | no | inline/no mapping | `live`<br>`live_codex` | `sendAgentChatMessage`<br>Attributed chat comment and transactional delivery receipt without execution or task disposition changes.<br>catalog PRP status: `bound` |
| `set_dependencies` | `optional_agent_tool` | `dependencies:write` | `standard`<br>`skill_test` | `company_write` | `required` | no | `semantic_command:set_dependencies` | `scenario` + `live`<br>`live_codex` | `issues.update.blockedByIssueIds`<br>semantic-operation item event plus company-entity state diff and audit record<br>catalog PRP status: `bound` |
| `set_task_monitor` | `optional_agent_tool` | none | `standard` | `task_write` | `required` | no | inline/no mapping | `live`<br>`live_codex` | `prepareIssueMonitorUpdate`<br>Transactional persisted issue monitor, audit activity and idempotent run receipt.<br>catalog PRP status: `bound` |
| `set_task_title` | `optional_agent_tool` | none | `standard`<br>`ask`<br>`planning`<br>`skill_test` | `task_write` | `required` | no | inline/no mapping | `live`<br>`live_codex` | `setIssueTitle`<br>Run-bound title update and transactional activity record.<br>catalog PRP status: `bound` |
@@ -196,7 +197,7 @@ Behavior groups describe expected outcomes and trajectories. They do not grant t
| [`hb` — Heartbeat](#behavior-group-hb-heartbeat) | control plane + always tools | `get_task_context`<br>`set_task_title` | `select_work`<br>`enforce_budget` | agent identity, inbox-lite, heartbeat context, budget and active-issue services | `company`<br>`actor`<br>`wake`<br>`task`<br>`budget`<br>`run` | 5 | runner/session/run context plus bounded task-context tool results | Production semantic binding is unbound; identity and work selection remain injected/control-plane-owned. |
| [`co` — Checkout](#behavior-group-co-checkout) | control plane | none | `checkout_task` | POST /api/issues/:id/checkout and execution-lock services | `task`<br>`actor`<br>`run`<br>`idempotency`<br>`fault` | 6 | run preparation and issue-status decision evidence with checkout receipt | Intentionally no model tool; the production checkout receipt still needs the additive semantic-receipt envelope. |
| [`st` — Status](#behavior-group-st-status) | always tools + control-plane arbitration | `answer_status_question`<br>`finish_task`<br>`block_task`<br>`request_review` | `reconcile_run`<br>`append_audit_record` | issue PATCH, review/liveness policy, and native finalization arbitration | `task`<br>`comments`<br>`interactions`<br>`blockers`<br>`audit`<br>`run` | 8 | semantic operation receipt, work assessment, issue-status decision, and terminal causality | Production semantic binding and additive typed operation/conflict receipts remain unimplemented. |
| [`cm` — Comments](#behavior-group-cm-comments) | always tools | `get_task_history`<br>`report_progress` | `append_audit_record` | issue comment list/get/create routes | `task`<br>`comments`<br>`actor`<br>`idempotency`<br>`audit` | 6 | bounded read result or idempotent comment-write receipt plus audit reference | Active-task binding is unbound; cross-task comment mutation is deliberately outside V1. |
| [`cm` — Comments](#behavior-group-cm-comments) | always tools | `get_task_history`<br>`report_progress`<br>`send_chat_message` | `append_audit_record` | issue comment list/get/create routes and own-agent chat messaging service | `task`<br>`comments`<br>`actor`<br>`idempotency`<br>`audit` | 6 | bounded read result, idempotent progress receipt, or persisted proactive chat comment with source task/run provenance | Proactive chat delivery is bound to the running agent and responsible human; arbitrary chat destinations are unsupported. Other comment/history binding gaps remain recorded per operation. |
| [`se` — Search](#behavior-group-se-search) | optional discovery tools | `search_tasks`<br>`list_agents`<br>`get_agent`<br>`list_projects`<br>`list_goals`<br>`list_project_repositories` | none | company issue search and agent/project/goal list/get routes | `company`<br>`task`<br>`actor`<br>`project`<br>`goal` | 4 | bounded redacted read projections through tool-result item events | Goal operations remain scenario-only. Project and repository discovery contracts are delivered by the live server authority; they are not implemented by the mock command dispatcher. |
| [`su` — Subtasks](#behavior-group-su-subtasks) | optional delegation tools | `create_task`<br>`reassign_task`<br>`hire_agent`<br>`handoff_case` | `route_wake` | company issue create, child issue, guarded reassignment, and wake services | `company`<br>`task`<br>`actor`<br>`blockers`<br>`wake`<br>`audit` | 4 | company/task state diff, audit reference, and continuation wake evidence | create_task is production-bound to ordinary active-issue child creation with assignment, dependency-ready wake, company checks, child limits, and durable source-scoped idempotency. handoff_case is live-only: the dedicated case service moves the same primary task after source execution settles, or records completion intent subject to ordinary completion gates; legacy scenarios do not claim that coverage. |
| [`bl` — Blockers](#behavior-group-bl-blockers) | always/optional tools + control plane | `block_task`<br>`set_dependencies` | `schedule_blocker_wake`<br>`route_wake` | issue relations, blocker projection, liveness validation, and blocker wake services | `task`<br>`blockers`<br>`wake`<br>`actor`<br>`audit`<br>`fault` | 5 | dependency diff, block receipt, attention routing, and issue-status decision | set_dependencies is production-bound for the active issue; block_task remains unbound, and cancelled-blocker receipts still need typed additive evidence. |
@@ -482,10 +483,10 @@ Current responsibility-based paths are normative. Numbered `phase-*` or mileston
### Catalog split and deliberate replacement
- Scenario/eval catalog: **40** operations.
- Live dispatcher catalog: **57** operations.
- Shared: **28**; union/canonical authority: **69**.
- Live dispatcher catalog: **58** operations.
- Shared: **28**; union/canonical authority: **70**.
- Scenario-only: `administer_company`, `export_company`, `inspect_operation_result`, `list_cases`, `list_company_skills`, `list_goals`, `list_routines`, `list_secret_metadata`, `manage_routine`, `read_secret_value`, `sync_company_skills`, `upsert_case`.
- Live-only: `call_api`, `create_case`, `create_project`, `create_workflow`, `get_agent`, `get_agent_instruction_history`, `get_approval`, `get_approval_context`, `get_workspace`, `handoff_case`, `hire_agent`, `list_project_repositories`, `list_workflows`, `list_workspaces`, `prepare_repository`, `read_agent_instructions`, `read_case`, `read_workflow`, `restore_agent_instructions`, `schedule_wake`, `search_api`, `select_workspace`, `set_task_monitor`, `set_task_title`, `submit_complaint`, `submit_suggestion`, `update_agent_instructions`, `update_case`, `update_workflow`.
- Live-only: `call_api`, `create_case`, `create_project`, `create_workflow`, `get_agent`, `get_agent_instruction_history`, `get_approval`, `get_approval_context`, `get_workspace`, `handoff_case`, `hire_agent`, `list_project_repositories`, `list_workflows`, `list_workspaces`, `prepare_repository`, `read_agent_instructions`, `read_case`, `read_workflow`, `restore_agent_instructions`, `schedule_wake`, `search_api`, `select_workspace`, `send_chat_message`, `set_task_monitor`, `set_task_title`, `submit_complaint`, `submit_suggestion`, `update_agent_instructions`, `update_case`, `update_workflow`.
- The generated provider contract contains exactly the live catalog; the canonical union remains the migration authority until all scenario-only operations are either implemented, deferred, or removed by an explicit reconciliation decision.
- `generic_api_request` stays exported only for controlled tests and cannot be cited as real-surface, mock-parity, or PRP product coverage.
@@ -20,9 +20,9 @@ describe("canonical semantic-catalog reconciliation authority", () => {
it("pins the reconciled op-set relationship between the two catalogs", () => {
const summary = capabilityCatalogReconciliation();
expect(summary.scenarioCount).toBe(40);
expect(summary.liveCount).toBe(57);
expect(summary.liveCount).toBe(58);
expect(summary.sharedCount).toBe(28);
expect(summary.unionCount).toBe(69);
expect(summary.unionCount).toBe(70);
// Any operation added to or removed from either catalog without a
// reconciliation decision changes these exact sets and fails the gate.
expect(summary.liveOnly).toEqual([
@@ -48,6 +48,7 @@ describe("canonical semantic-catalog reconciliation authority", () => {
"schedule_wake",
"search_api",
"select_workspace",
"send_chat_message",
"set_task_monitor",
"set_task_title",
"submit_complaint",
@@ -73,7 +74,7 @@ describe("canonical semantic-catalog reconciliation authority", () => {
});
it("is the single source both catalogs derive their operation set from", () => {
expect(CAPABILITY_CANONICAL_OPERATIONS).toHaveLength(69);
expect(CAPABILITY_CANONICAL_OPERATIONS).toHaveLength(70);
const canonicalIds = new Set(CAPABILITY_CANONICAL_OPERATIONS.map((operation) => operation.operationId));
// Neither catalog may contain an operation absent from the canonical source.
for (const tool of SCENARIO_CATALOG) expect(canonicalIds.has(tool.operationId)).toBe(true);
@@ -107,7 +108,7 @@ describe("canonical semantic-catalog reconciliation authority", () => {
});
it("names placement, claims, task modes, side-effect class, idempotency, redaction, mock mapping, real binding status, and PRP evidence for every operation", () => {
expect(CAPABILITY_CANONICAL_CATALOG).toHaveLength(69);
expect(CAPABILITY_CANONICAL_CATALOG).toHaveLength(70);
for (const operation of CAPABILITY_CANONICAL_CATALOG) {
expect(operation.placement).toMatch(/^(always|optional)_agent_tool$/);
expect(Array.isArray(operation.requiredClaims)).toBe(true);
@@ -133,7 +134,7 @@ describe("canonical semantic-catalog reconciliation authority", () => {
it("classifies real binding status so generic_api_request is never product coverage", () => {
const summary = capabilityCatalogReconciliation();
expect(summary.byRealBindingStatus).toEqual({
live_codex: 56,
live_codex: 57,
scenario_mock: 12,
test_only: 1,
});
@@ -127,8 +127,9 @@ describe("semantic action catalog", () => {
(action) => action.operationId,
);
expect(operationIds).toHaveLength(49);
expect(operationIds).toHaveLength(50);
expect(operationIds).toContain("set_task_monitor");
expect(operationIds).toContain("send_chat_message");
expect(new Set(operationIds).size).toBe(operationIds.length);
expect(operationIds).not.toContain("generic_api_request");
expect(Object.isFrozen(PAPERCLIP_SEMANTIC_ACTION_CATALOG)).toBe(true);
@@ -8,6 +8,7 @@ import { prepareRepositoryAction } from "../protocol-actions/prepare-repository.
import { setTaskMonitorInputSchema } from "../protocol-actions/set-task-monitor.js";
import { listProjectsDescription, listProjectsInputSchema } from "../protocol-actions/list-projects.js";
import { setTaskTitleAction } from "../protocol-actions/set-task-title.js";
import { sendChatMessageAction } from "../protocol-actions/send-chat-message.js";
import { reassignTaskAction } from "../protocol-actions/reassign-task.js";
import type {
PaperclipJsonSchema,
@@ -191,6 +192,12 @@ const descriptors: readonly PaperclipSemanticActionDescriptor[] = [
),
outputSchema: operationReceipt,
}),
descriptor({
operationId: "send_chat_message", title: "Send message to your chat",
description: sendChatMessageAction.documentation.description, effect: "write",
allowedModes: ["standard"], inputSchema: sendChatMessageAction.live.descriptor.inputSchema,
outputSchema: openObject,
}),
descriptor({
operationId: "answer_status_question",
title: "Answer status question",
@@ -2,6 +2,7 @@ export type PaperclipSemanticActionId =
| "search_api"
| "call_api"
| "set_task_title"
| "send_chat_message"
| "set_task_monitor"
| "get_task_context"
| "get_task_history"
@@ -1,4 +1,5 @@
import { bindAcpxCandidateAdmission } from "../drivers/acpx/candidate-admission.js";
import { KIMI_DISTRIBUTION_CLOSURE_SHA256 } from "../drivers/acpx/kimi-closure-pins.js";
import { ANTIGRAVITY_DISTRIBUTION_PINS } from "../drivers/acpx/generated-profiles.js";
import { canonicalNativeRuntimeContextDigest } from "../contracts/runtime-context.js";
import { acpxProfileActivity } from "../drivers/acpx/profile-activity.js";
@@ -15,6 +16,7 @@ import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { createAcpxSidecarHostEnvironment, createSanitizedAcpxSpawnInput } from "../drivers/acpx/environment.js";
import { createKimiMessageProjection } from "../drivers/acpx/kimi-message-boundary.js";
import { afterEach, describe, expect, it, vi } from "vitest";
@@ -563,6 +565,31 @@ describe("qualified ACPX runtime sidecar", () => {
expect(wire.writes).toEqual([]); expect(wire.sequence()).toBe(0);
});
it("preserves validated native assistant boundaries without exposing Kimi provenance metadata", () => {
const source = readFileSync(fileURLToPath(new URL("./acpx-runtime-sidecar.ts", import.meta.url)), "utf8");
const start = source.indexOf(' if (event.type === "text_delta") {', source.indexOf("function sanitizeRuntimeEvent"));
const end = source.indexOf(' if (event.type === "status") {', start);
const sanitize = new Function("boundedOptionalText", "stableProviderIdentity", `return event => {${source.slice(start, end)} return null;}`)(
(value: string) => value, (value: string) => `stable-${value}`,
);
const projection = createKimiMessageProjection<{ type: string; text: string; stream: string; messageId: string; meta: Record<string, string> }>();
const event = (kind: string, text = "", stream = "output") => ({ type: "text_delta", text, stream,
messageId: `kimi-message-${"a".repeat(64)}`,
meta: { origin: "kimi-native-assistant", source: "kimi-turn-step-v1", kind, secret: "PRIVATE_PROVENANCE" } });
const frames = [sanitize(projection.normalize(event("start"))),
sanitize(projection.normalize(event("delta", "Working."))),
sanitize(projection.normalize(event("delta", "Private reasoning", "thought"))),
sanitize(projection.normalize(event("end:tool_use")))];
expect(frames[0]).toMatchObject({ text: "", assistantMessageBoundary: { phase: "start" } });
expect(frames[1]).toMatchObject({ type: "text_delta", text: "Working." });
expect(frames[2]).toMatchObject({ type: "thinking" });
expect(frames[3]).toMatchObject({ text: "", assistantMessageBoundary: { phase: "end", stopReason: "toolUse" } });
expect(new Set(frames.map(frame => frame.messageId)).size).toBe(1);
expect(JSON.stringify(frames)).not.toContain("PRIVATE_PROVENANCE");
expect(JSON.stringify(frames)).not.toContain("kimi-native-assistant");
projection.settle();
});
it("passes only validated Pi native boundaries and history through the real text sanitizer", () => {
const source = readFileSync(fileURLToPath(new URL("./acpx-runtime-sidecar.ts", import.meta.url)), "utf8");
const start = source.indexOf(' if (event.type === "text_delta") {', source.indexOf("function sanitizeRuntimeEvent"));
@@ -1258,17 +1285,56 @@ describe("qualified ACPX runtime sidecar", () => {
});
it.each([
["cursor", "explicit-cursor-model"],
["copilot", "explicit-copilot-model"],
] as const)("initializes the declared %s profile with its current admission status", async (agent, model) => {
["cursor", "explicit-cursor-model", "qualified"],
["copilot", "explicit-copilot-model", "pending"],
["kimi", "explicit-kimi-model", resolveQualifiedAcpxProfile("kimi", "explicit-kimi-model").qualificationStatus === "pending" ? "pending" : "qualified"],
] as const)("initializes the declared %s profile with its current admission status", async (agent, model, qualification) => {
const sidecar = startSidecar();
const profile = resolveQualifiedAcpxProfile(agent, model);
sidecar.write(initializeRequest(1, agent, model));
const frame = await sidecar.next((value) => value.id === 1);
expect(ACPX_CAPABILITY_PROFILES[agent].qualification).toBe(qualification);
if (agent === "kimi" && profile.qualificationStatus === "pending") {
expect(frame).toMatchObject({ id: 1, ok: false,
error: { message: expect.stringContaining("qualification is pending"), retryable: false } });
return;
}
expect(frame).toMatchObject({ id: 1, ok: true });
const result = frame.result as Record<string, unknown>;
expect(result.profile).toEqual(resolveQualifiedAcpxProfile(agent, model));
expect(result.profile).toEqual(profile);
expect(result.profile).toMatchObject({ reportedModelId: model });
expect(ACPX_CAPABILITY_PROFILES[agent].qualification).toBe(agent === "cursor" ? "qualified" : "pending");
});
it.each([
["darwin", "arm64"], ["linux", "x64"],
] as const)("binds Kimi startup to its declared %s/%s host and session before provider launch", async (platform, architecture) => {
const sidecar = startSidecar(), model = "explicit-kimi-model";
const profile = resolveQualifiedAcpxProfile("kimi", model);
const request = initializeRequest(1, "kimi", model);
if (profile.qualificationStatus !== "pending") {
sidecar.write(request);
expect(await sidecar.next(frame => frame.id === 1)).toMatchObject({ ok: true, result: { profile } });
return;
}
const target = `${platform}-${architecture}` as "darwin-arm64" | "linux-x64";
const candidateAdmission = bindAcpxCandidateAdmission({ schema: "paperclip.acpx-candidate-admission.v1",
companyId: "company-kimi-candidate", agent: "kimi", model, profileDigest: profile.commandDigest,
nativeClosureDigest: `sha256:${KIMI_DISTRIBUTION_CLOSURE_SHA256[target]}`,
platform, architecture, expiresAt: Date.now() + 60_000,
}, { runId: "run-kimi-candidate", normalizedSessionId: "session-kimi-candidate" });
sidecar.write({ ...request, params: { ...request.params, candidateAdmission } });
const frame = await sidecar.next(value => value.id === 1);
if (process.platform !== platform || process.arch !== architecture) {
expect(frame).toMatchObject({ ok: false, error: { message: expect.stringContaining("candidate admission"), retryable: false } });
return;
}
expect(frame).toMatchObject({ ok: true, result: { profile } });
expect((frame.result as Record<string, unknown>).profile).toMatchObject({ reportedModelId: model });
sidecar.write({ protocolVersion: ACPX_SIDECAR_PROTOCOL_VERSION, id: 2, command: "session.open", params: {
runtimeDirectory: "/unused", workingDirectory: "/unused", normalizedSessionId: "other-kimi-session", agent: "kimi", model,
candidateAdmission, permissionMode: "deny-all", permissionModePinned: true, providerPolicy: { readOnly: true }, systemInstructions: "", tools: [],
} });
expect(await sidecar.next(value => value.id === 2)).toMatchObject({ ok: false, error: { message: expect.stringContaining("candidate admission") } });
});
it("refuses pending Antigravity bootstrap before any native process opens", async () => {
@@ -1312,7 +1378,7 @@ describe("qualified ACPX runtime sidecar", () => {
ok: false,
error: {
code: "acpx_sidecar_command_failed",
message: "ACPX agent must be claude, codex, grok, cursor, copilot, pi, or antigravity",
message: "ACPX agent must be claude, codex, grok, cursor, copilot, kimi, pi, or antigravity",
retryable: false,
},
});
@@ -1338,7 +1404,7 @@ function initializeRequest(
id: number,
agent: string,
model = "gpt-5.6-sol",
): Record<string, unknown> {
) {
return {
protocolVersion: ACPX_SIDECAR_PROTOCOL_VERSION,
id,
@@ -27,6 +27,7 @@ import { acpxProfileClientCapabilities, bindAcpxExtensionTurn, validateAcpxRichE
import type { PaperclipQuestionSet } from "../contracts/question-set.js";
import { createAcpxToolEventNormalizer, createGrokMessageNormalizer } from "../provider-events.js";
import { createPiMessageProjection, type PiProjectedMessageEvent } from "../drivers/acpx/pi-message-projection.js";
import { createKimiMessageProjection, type KimiProjectedMessageEvent } from "../drivers/acpx/kimi-message-boundary.js";
import { parseNativeRuntimeContext } from "../contracts/runtime-context.js";
import {
PRP_BLOCK_TOOL_NAME,
@@ -667,7 +668,8 @@ async function pumpTurn(
// the sidecar boundary, matching the in-process ACPX driver path.
const normalizeToolEvent = createAcpxToolEventNormalizer<AcpRuntimeEvent>();
const piMessages = initializedAgent === "pi" ? createPiMessageProjection<AcpRuntimeEvent>() : null;
const normalizeMessage = piMessages ? piMessages.normalize : initializedAgent === "grok"
const kimiMessages = initializedAgent === "kimi" ? createKimiMessageProjection<AcpRuntimeEvent>() : null;
const normalizeMessage = kimiMessages ? kimiMessages.normalize : piMessages ? piMessages.normalize : initializedAgent === "grok"
? createGrokMessageNormalizer<AcpRuntimeEvent>() : (event: AcpRuntimeEvent) => event;
for await (const event of runtimeTurn.events) {
toolEvidence?.tool(event);
@@ -680,7 +682,7 @@ async function pumpTurn(
);
}
const result = await runtimeTurn.result;
if (result.status === "completed") piMessages?.settle();
if (result.status === "completed") { piMessages?.settle(); kimiMessages?.settle(); }
await drainExtensions();
try {
const usageAfter = await readSidecarHostStatusWithin(activeHost);
@@ -1037,7 +1039,7 @@ function boundRuntimeEventForNormalization(
} as BoundedRuntimeToolEvent;
}
function sanitizeRuntimeEvent(event: PiProjectedMessageEvent<AcpRuntimeEvent>): Record<string, unknown> {
function sanitizeRuntimeEvent(event: PiProjectedMessageEvent<AcpRuntimeEvent> & KimiProjectedMessageEvent<AcpRuntimeEvent>): Record<string, unknown> {
const runtimeType = text(record(event).type);
if (runtimeType === "plan") {
return {
@@ -1053,6 +1055,8 @@ function sanitizeRuntimeEvent(event: PiProjectedMessageEvent<AcpRuntimeEvent>):
tag: event.tag ?? null,
...(event.piMessageBoundary ? { piMessageBoundary: event.piMessageBoundary } : {}),
...(event.piMessageHistory ? { piMessageHistory: true } : {}),
...(event.assistantMessageBoundary ? { assistantMessageBoundary: event.assistantMessageBoundary } : {}),
...(event.assistantMessageHistory ? { assistantMessageHistory: true } : {}),
messageId:
typeof event.messageId === "string" && event.messageId.length > 0
? stableProviderIdentity(event.messageId, "message")
@@ -1478,8 +1482,8 @@ function requireHost(
}
function requireQualifiedAgent(value: unknown, model?: string, candidateAdmission?: AcpxCandidateStartupAdmission): QualifiedAcpxAgent {
if (value !== "grok" && value !== "codex" && value !== "claude" && value !== "pi" && value !== "cursor" && value !== "copilot" && value !== "antigravity") {
throw new Error("ACPX agent must be claude, codex, grok, cursor, copilot, pi, or antigravity");
if (value !== "grok" && value !== "codex" && value !== "claude" && value !== "pi" && value !== "cursor" && value !== "copilot" && value !== "kimi" && value !== "antigravity") {
throw new Error("ACPX agent must be claude, codex, grok, cursor, copilot, kimi, pi, or antigravity");
}
if (candidateAdmission) assertAcpxExecutionAdmission(resolveQualifiedAcpxProfile(value, model ?? ""), candidateAdmission);
else assertAcpxProfileReleased(value);
@@ -120,7 +120,7 @@ describe("eval-session request contract", () => {
expect(parseEvalSessionCliArgs([...args, "--candidate-profile", "pi", "--expected-acpx-profile", JSON.stringify(expected)]))
.toMatchObject({ candidateProfile: "pi", expectedAcpxProfile: expected });
expect(() => parseEvalSessionCliArgs([...args, "--candidate-profile", "pi"])).toThrow("require --expected-acpx-profile");
expect(() => parseEvalSessionCliArgs([...args, "--candidate-profile", "codex"])).toThrow("must be pi, cursor, or copilot");
expect(() => parseEvalSessionCliArgs([...args, "--candidate-profile", "codex"])).toThrow("must be pi, cursor, copilot, or kimi");
expect(() => parseEvalSessionCliArgs([...args, "--candidate-profile"])).toThrow("missing");
expect(() => parseEvalSessionCliArgs([...args, "--request", "/tmp/other.json"])).toThrow("duplicate");
expect(() => parseEvalSessionRequest(request(), { candidateProfile: "pi" })).toThrow("must match");
@@ -60,7 +60,7 @@ export interface EvalSessionAgentCoreProfile {
timeoutSeconds: number;
}
export type EvalCandidateProfile = "pi" | "cursor" | "copilot";
export type EvalCandidateProfile = "pi" | "cursor" | "copilot" | "kimi";
export interface EvalSessionRequest {
schema: typeof EVAL_SESSION_REQUEST_SCHEMA;
@@ -255,7 +255,7 @@ export function parseEvalSessionRequest(
acpxAgent !== undefined &&
acpxAgent !== "codex" &&
acpxAgent !== "claude" && acpxAgent !== "grok" &&
acpxAgent !== "pi" && acpxAgent !== "cursor" && acpxAgent !== "copilot"
acpxAgent !== "pi" && acpxAgent !== "cursor" && acpxAgent !== "copilot" && acpxAgent !== "kimi"
) {
throw new Error("eval-session acpxAgent must be a registered ACPX profile");
}
@@ -263,7 +263,7 @@ export function parseEvalSessionRequest(
throw new Error("eval-session acpxAgent requires provider acpx");
}
const piThinkingLevel = resolvePiThinkingLevel(provider === "acpx" ? String(acpxAgent) : "", input.piThinkingLevel);
const candidate = acpxAgent === "pi" || acpxAgent === "cursor" || acpxAgent === "copilot";
const candidate = acpxAgent === "pi" || acpxAgent === "cursor" || acpxAgent === "copilot" || acpxAgent === "kimi";
if (options.candidateProfile !== undefined && (provider !== "acpx" || acpxAgent !== options.candidateProfile || !candidate)) {
throw new Error("--candidate-profile must match the request's registered candidate ACPX agent");
}
@@ -401,7 +401,7 @@ export function evalSessionUsage(
throw new Error("completed turn omitted usage accounting");
}
const candidate = snapshot.config?.provider === "acpx"
&& ["pi", "cursor", "copilot"].includes(snapshot.config.acpxAgent ?? "");
&& ["pi", "cursor", "copilot", "kimi"].includes(snapshot.config.acpxAgent ?? "");
const codex = snapshot.config?.provider === "codex";
const providerCostUnavailable = candidate || codex;
let estimate: EstimatedModelCost | null;
@@ -71,8 +71,8 @@ export function parseEvalSessionCliArgs(args: string[]): EvalSessionCliOptions {
if (seen.has("--observability-config") && !seen.has("--gateway-config")) throw new Error("Eval observability requires an explicit controller gateway");
const candidateIndex = args.indexOf("--candidate-profile");
const candidateProfile = candidateIndex < 0 ? undefined : args[candidateIndex + 1];
if (candidateProfile !== undefined && candidateProfile !== "pi" && candidateProfile !== "cursor" && candidateProfile !== "copilot") {
throw new Error("--candidate-profile must be pi, cursor, or copilot");
if (candidateProfile !== undefined && candidateProfile !== "pi" && candidateProfile !== "cursor" && candidateProfile !== "copilot" && candidateProfile !== "kimi") {
throw new Error("--candidate-profile must be pi, cursor, copilot, or kimi");
}
const expectedIndex = args.indexOf("--expected-acpx-profile");
const expectedText = expectedIndex < 0 ? undefined : args[expectedIndex + 1];
@@ -317,7 +317,7 @@ export function boundedEvalSessionUsage(
}
const candidate = request.provider === "acpx"
&& request.acpxAgent !== undefined
&& ["pi", "cursor", "copilot"].includes(request.acpxAgent)
&& ["pi", "cursor", "copilot", "kimi"].includes(request.acpxAgent)
&& turn.snapshot.config.provider === "acpx"
&& turn.snapshot.config.acpxAgent === request.acpxAgent;
const measuredTurns = new Set(turn.snapshot.usageLedger?.map((entry) => entry.turnId));
@@ -0,0 +1,30 @@
#!/usr/bin/env node
import { parseAcpxCandidateStartupAdmission, type AcpxCandidateStartupAdmission } from "../drivers/acpx/candidate-admission.js";
import { probeKimiMetadata } from "../drivers/acpx/kimi-metadata-probe.js";
import { KIMI_NODE_VERSION } from "../drivers/acpx/kimi-closure-pins.js";
async function main() {
const model = process.env.PAPERCLIP_KIMI_PROBE_MODEL ?? "", apiKey = process.env.KIMI_API_KEY ?? "";
const encodedAdmission = process.env.PAPERCLIP_ACPX_CANDIDATE_ADMISSION;
const bindings = { PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT: process.env.PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT,
PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST: process.env.PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST };
for (const name of Object.keys(process.env)) delete process.env[name];
for (const [name, value] of Object.entries(bindings)) if (value !== undefined) process.env[name] = value;
let candidateAdmission: AcpxCandidateStartupAdmission | undefined;
if (encodedAdmission !== undefined) {
if (Buffer.byteLength(encodedAdmission) > 4096) throw new Error("Invalid metadata admission");
candidateAdmission = parseAcpxCandidateStartupAdmission(JSON.parse(encodedAdmission), { agent: "kimi", model });
}
const result = process.versions.node !== KIMI_NODE_VERSION
? { schema: "paperclip.kimi.metadata.v1", status: "failed", code: "KIMI_INSTALLATION_INVALID",
message: "Kimi metadata verification requires the pinned Node runtime.", promptSent: false }
: await probeKimiMetadata(model, apiKey, candidateAdmission);
process.stdout.write(`${JSON.stringify(result)}\n`);
if (result.status !== "verified") process.exitCode = 1;
}
void main().catch(() => {
process.stdout.write(`${JSON.stringify({ schema: "paperclip.kimi.metadata.v1", status: "failed", code: "KIMI_REQUEST_FAILED",
message: "Kimi Code metadata verification failed.", promptSent: false })}\n`);
process.exitCode = 1;
});
@@ -72,6 +72,23 @@ describe("configured environment authority", () => {
expect(projected.PAPERCLIP_API_KEY).toBeUndefined();
});
it("reserves Kimi credentials for the selected provider instead of the generic task projection", async () => {
const { createSanitizedAcpxSpawnInput } = await import("./drivers/acpx/environment.js");
const projected = configuredEnvironmentProjection({ KIMI_API_KEY: "selected-kimi-secret", CUSTOM_FLAG: "allowed" });
expect(projected.KIMI_API_KEY).toBeUndefined();
expect(JSON.parse(projected[CONFIGURED_ENVIRONMENT_KEYS]!)).toEqual(["CUSTOM_FLAG"]);
expect(() => configuredEnvironment({ [CONFIGURED_ENVIRONMENT_KEYS]: '["KIMI_API_KEY"]', KIMI_API_KEY: "selected-kimi-secret" }))
.toThrow("Invalid configured environment projection");
const selected = { ...projected, KIMI_API_KEY: "selected-kimi-secret" };
expect(createSanitizedCodexEnvironment(selected).KIMI_API_KEY).toBeUndefined();
for (const agent of ["claude", "codex", "grok", "cursor", "copilot"] as const) {
expect(createSanitizedAcpxSpawnInput(selected, agent).env.KIMI_API_KEY).toBeUndefined();
}
expect(createSanitizedAcpxSpawnInput(selected, "kimi").env).toMatchObject({ KIMI_API_KEY: "selected-kimi-secret", CUSTOM_FLAG: "allowed" });
// Pi already supports this credential through its explicit SDK allowlist.
expect(createSanitizedAcpxSpawnInput(selected, "pi").env.KIMI_API_KEY).toBe("selected-kimi-secret");
});
it("does not accept an ambient projection in the provider or shell policy", () => {
vi.stubEnv(CONFIGURED_ENVIRONMENT_KEYS, JSON.stringify(["PAPERCLIP_PAGE_AWS_SECRET_ACCESS_KEY"]));
vi.stubEnv("PAPERCLIP_PAGE_AWS_SECRET_ACCESS_KEY", "ambient-secret");
@@ -81,7 +98,7 @@ describe("configured environment authority", () => {
} finally { vi.unstubAllEnvs(); }
});
it.each(["not-json", "[1]", '["NODE_OPTIONS"]', '["PAPERCLIP_API_KEY"]', '["PAPERCLIP_RUNNER_BOOTSTRAP_TICKET"]', '["OPENAI_API_KEY"]', '["LD_PRELOAD"]', '["bad=name"]', '["CUSTOM","CUSTOM"]', '["MISSING"]'])
it.each(["not-json", "[1]", '["NODE_OPTIONS"]', '["PAPERCLIP_API_KEY"]', '["PAPERCLIP_RUNNER_BOOTSTRAP_TICKET"]', '["OPENAI_API_KEY"]', '["KIMI_API_KEY"]', '["LD_PRELOAD"]', '["bad=name"]', '["CUSTOM","CUSTOM"]', '["MISSING"]'])
("rejects malformed or reserved projection %s", raw => {
expect(() => configuredEnvironment({ [CONFIGURED_ENVIRONMENT_KEYS]: raw })).toThrow("Invalid configured environment");
});
@@ -30,7 +30,7 @@ const RESERVED_KEYS = new Set([
"PYTHONPATH", "PYTHONHOME", "RUBYOPT", "RUBYLIB", "PERL5OPT", "PERL5LIB", "BASH_ENV", "ENV", "ZDOTDIR", "GH_CONFIG_DIR",
"OPENAI_API_KEY", "CODEX_API_KEY", "ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN",
"AWS_BEARER_TOKEN_BEDROCK", "OPENROUTER_API_KEY", "XAI_API_KEY", "CURSOR_API_KEY", "CURSOR_AUTH_TOKEN",
"COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN", "GH_ENTERPRISE_TOKEN", "GITHUB_ENTERPRISE_TOKEN", "PAPERCLIP_GIT_TOKEN",
"COPILOT_GITHUB_TOKEN", "KIMI_API_KEY", "GH_TOKEN", "GITHUB_TOKEN", "GH_ENTERPRISE_TOKEN", "GITHUB_ENTERPRISE_TOKEN", "PAPERCLIP_GIT_TOKEN",
]);
function eligibleKey(key: string): boolean {
@@ -2,7 +2,7 @@ import { resolveGrokDisableWebSearch } from "../drivers/acpx/grok-web-search.js"
import { resolveGrokEffort } from "../drivers/acpx/grok-effort.js";
import { resolveClaudeEffort } from "../drivers/acpx/claude-effort.js";
import type { DotBindingSnapshot, MuseBindingSnapshot, ExternalBotProviderKind } from "./external-provider.js";
import { QUALIFIED_ACPX_VERSION } from "../drivers/acpx/generated-profiles.js";
import { QUALIFIED_ACPX_VERSION, QUALIFIED_ACPX_PROFILE_DATA } from "../drivers/acpx/generated-profiles.js";
import { isSupportedAcpxProfileVersion, type AcpxProfileVersion } from "../drivers/acpx/profile-compatibility.js";
import { isProviderMode } from "./provider-mode.js";
import { createHash } from "node:crypto";
@@ -93,7 +93,11 @@ export interface NativeAwsAgentCoreProfileSnapshot {
eventExpiryDays: 90;
}
export type NativeAcpxAgent = "pi" | "claude" | "codex" | "grok" | "cursor" | "copilot" | "antigravity";
export type NativeAcpxAgent = keyof typeof QUALIFIED_ACPX_PROFILE_DATA;
function isNativeAcpxAgent(value: unknown): value is NativeAcpxAgent {
return typeof value === "string" && Object.hasOwn(QUALIFIED_ACPX_PROFILE_DATA, value);
}
export type NativeCodexApprovalPolicy = "never" | "on-request" | "untrusted";
export type NativeOpenCodePermissionMode = "allow" | "ask" | "deny";
export type NativeAcpxPermissionMode = "approve-all" | "approve-paperclip" | "approve-reads" | "deny-all";
@@ -709,8 +713,8 @@ export function parseNativeExecutionInput(value: unknown): NativeExecutionInput
if (providerModel === null) {
throw new NativeExecutionInputError("input.provider.model is required for acpx");
}
if (provider.agent !== "pi" && provider.agent !== "claude" && provider.agent !== "codex" && provider.agent !== "grok" && provider.agent !== "cursor" && provider.agent !== "copilot" && provider.agent !== "antigravity") {
throw new NativeExecutionInputError("input.provider.agent must be pi, claude, codex, grok, cursor, copilot, or antigravity");
if (!isNativeAcpxAgent(provider.agent)) {
throw new NativeExecutionInputError("input.provider.agent must name a registered ACPX profile");
}
if (isV4) {
if (provider.permissionMode !== "approve-all" && provider.permissionMode !== "approve-paperclip" && provider.permissionMode !== "approve-reads" && provider.permissionMode !== "deny-all") {
@@ -32,6 +32,8 @@ import {
DurablePrpControlPlane,
decodeDurablePrpCheckpoint,
inspectDurablePrpCheckpointHeader,
inspectStagedRunAttachment,
inspectStagedRunAttachmentHeader,
type DurablePrpCheckpoint,
durableRecoveryInternals,
SemanticToolNotDispatchedError,
@@ -53,6 +55,66 @@ const identity: DurableRecoveryIdentity = {
const expectedRunnerVersion = "0.3.0";
const expectedRunnerDigest = `sha256:${"a".repeat(64)}`;
it("persists an undispatched source-to-target attachment before bootstrap and preserves it after cache loss", async () => {
const root = mkdtempSync(resolve(tmpdir(), "runner-staged-attachment-"));
const target = { ...identity, runId: "00000000-0000-4000-8000-000000000002", turnId: "next-turn", itemId: "next-item" };
const template = { provider: { provider: "codex", command: "fixture", instructions: "assigned instructions ".repeat(1_500) }, authorizedTools: {} };
const core = new DurablePrpControlPlane({ stateDirectory: resolve(root, "first"), identity, expectedRunnerVersion, expectedRunnerDigest });
let checkpoint!: DurablePrpCheckpoint, templateWrites = 0;
core.setCheckpointPersistence(async value => { checkpoint = value; if (value.changedRecords.has("staged-attachment-template")) templateWrites++; });
let restored: DurablePrpControlPlane | undefined;
let client: AuthenticatedClient | null = null;
try {
core.stageRunAttachment(target, template);
await core.flushCheckpoint();
expect(checkpoint.runId).toBe(target.runId);
expect(checkpoint.predecessor).toEqual({ runId: identity.runId, normalizedSessionId: identity.normalizedSessionId });
expect(checkpoint.workCommandIds).toEqual([]);
const proof = inspectStagedRunAttachmentHeader(checkpoint.records.get("header")!);
expect(proof).toMatchObject({ attachment: { sourceIdentity: identity, targetIdentity: target, commandId: null } });
expect(proof?.attachment).not.toHaveProperty("runAttachTemplate");
expect(checkpoint.records.get("header")!.length).toBeLessThan(JSON.stringify(template).length / 2);
expect(JSON.parse(checkpoint.records.get("staged-attachment-template")!)).toEqual(template);
expect(proof?.runnerVersion).toBe(expectedRunnerVersion);
expect(proof?.runnerDigest).toBe(expectedRunnerDigest);
checkpoint.restoreSessionDirectory(resolve(root, "replacement"));
restored = new DurablePrpControlPlane({ stateDirectory: resolve(root, "replacement", "control-plane"), identity, expectedRunnerVersion, expectedRunnerDigest });
expect(inspectStagedRunAttachment(restored.store.state, target)).toMatchObject({ ...proof?.attachment, runAttachTemplate: template });
expect(restored.store.state.commands).toEqual([]);
expect(() => restored!.stageRunAttachment({ ...target, itemId: "foreign" }, template)).toThrow("conflicts");
expect(() => restored!.queueCommand("turn.start", {})).toThrow("maintenance only");
expect(() => restored!.queueCommand("run.attach", { ...template, paperclipNextAuthority: { identity: { ...target, itemId: "foreign" } } })).toThrow("conflicts");
const corruptedRecords = new Map(checkpoint.records);
corruptedRecords.set("staged-attachment-template", JSON.stringify({ provider: { command: "changed" } }));
expect(() => decodeDurablePrpCheckpoint(corruptedRecords)).toThrow("native_runner_checkpoint_invalid");
expect(inspectStagedRunAttachment(restored.store.state, { ...target, turnId: "foreign" })).toBeNull();
for (const field of ["runnerInstanceId", "environmentLeaseId", "normalizedSessionId"] as const) {
const changed = JSON.parse(checkpoint.records.get("header")!);
changed.state.stagedRunAttachment.targetIdentity[field] = "foreign";
expect(inspectStagedRunAttachmentHeader(JSON.stringify(changed))).toBeNull();
}
const downgraded = JSON.parse(checkpoint.records.get("header")!);
downgraded.state.schema = "paperclip.runner.durable.control-plane-state.v1";
expect(inspectStagedRunAttachmentHeader(JSON.stringify(downgraded))).toBeNull();
const beforeRetry = checkpoint;
core.stageRunAttachment(target, template); await core.flushCheckpoint(); expect(checkpoint).toBe(beforeRetry);
core.queueCommand("session.snapshot", {}); await core.flushCheckpoint();
expect(checkpoint.changedRecords.has("staged-attachment-template")).toBe(false);
await core.start(); client = await authenticate(core, core.issueBootstrapTicket());
expect(client).not.toBeNull();
core.queueCommand("run.attach", { ...template, paperclipNextAuthority: { identity: target,
connection: { mode: "connect", connectUrl: core.connectUrl } } }, "staged-next-attach");
await core.flushCheckpoint();
expect(templateWrites).toBe(1);
expect(checkpoint.records.has("staged-attachment-template")).toBe(false);
expect(checkpoint.removedRecords).toContain("staged-attachment-template");
expect(() => decodeDurablePrpCheckpoint(checkpoint.records)).not.toThrow();
} finally {
client?.socket.destroy();
await restored?.stop(); await core.stop(); rmSync(root, { recursive: true, force: true });
}
});
it("continues externally persisted commands after losing the local controller directory", async () => {
const root = mkdtempSync(resolve(tmpdir(), "runner-external-store-"));
const options = { stateDirectory: resolve(root, "control-plane"), identity, expectedRunnerVersion, expectedRunnerDigest };
@@ -106,6 +168,7 @@ it("restores authentication and commands from opaque checkpoint records", async
try {
const ticket = first.issueBootstrapTicket();
first.queueCommand("session.snapshot", {}, "maintenance");
first.queueCommand("turn.stop", {}, "retirement-maintenance");
first.queueCommand("turn.start", {}, "execution");
first.persistPreparedLaunchConfiguration(identity, "retained-configuration");
await first.flushCheckpoint();
@@ -54,6 +54,7 @@ const protocolVersion = RUNNER_PRP_PROTOCOL_MAX;
const secureFrameSchema = "paperclip.runner.secure-frame.v1";
const websocketGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
const coreStateSchema = "paperclip.runner.durable.control-plane-state.v1";
const stagedCoreStateSchema = "paperclip.runner.durable.control-plane-state.staged-attachment.v1";
const externalCoreStateSchema = "paperclip.runner.external-control-plane-state.v1";
const transitionCoreStateSchema =
"paperclip.runner.durable.control-plane-state.warm-transition.v1";
@@ -140,8 +141,17 @@ interface ConnectionLeaseRecord {
revokedAt: string | null;
}
export interface StagedRunAttachment {
readonly sourceIdentity: DurableRecoveryIdentity;
readonly targetIdentity: DurableRecoveryIdentity;
readonly runAttachTemplate: Record<string, unknown>;
readonly templateDigest: string;
readonly commandId: string | null;
}
interface StoredCoreState {
schema: typeof coreStateSchema | typeof transitionCoreStateSchema;
schema: typeof coreStateSchema | typeof transitionCoreStateSchema | typeof stagedCoreStateSchema;
stagedRunAttachment?: Omit<StagedRunAttachment, "runAttachTemplate"> & { runAttachTemplate?: Record<string, unknown> };
identity: DurableRecoveryIdentity;
/** External checkpoints contain control state, not a duplicate output log. */
checkpointEventPolicy?: "control-v1";
@@ -311,10 +321,12 @@ type CheckpointHeader = {
schema: "paperclip.runner.controller-checkpoint.v2";
runnerVersion: string;
runnerDigest: string;
state: Omit<StoredCoreState, "commands" | "committedEvents" | "ackedSourceSeq" | "replayDeliveries">;
state: Omit<StoredCoreState, "commands" | "committedEvents" | "ackedSourceSeq" | "replayDeliveries" | "stagedRunAttachment">
& { stagedRunAttachment?: Omit<StagedRunAttachment, "runAttachTemplate"> };
};
function checkpointRunId(state: StoredCoreState): string {
if (state.stagedRunAttachment) return state.stagedRunAttachment.targetIdentity.runId;
if (state.warmTransition) return state.warmTransition.receipt.newIdentity.runId;
const attachment = state.commands.findLast(command => command.type === "run.attach");
const next = attachment?.payload.paperclipNextAuthority as { identity?: { runId?: string } } | undefined;
@@ -347,17 +359,19 @@ function checkpointChanges(records: ReadonlyMap<string, string>, runId: string,
function captureCheckpoint(state: StoredCoreState, runnerVersion: string, runnerDigest: string): DurablePrpCheckpoint {
const runId = checkpointRunId(state);
const records = new Map<string, string>();
const { commands, committedEvents, ackedSourceSeq: _cursor, replayDeliveries: _replays, ...metadata } = state;
const { commands, committedEvents, ackedSourceSeq: _cursor, replayDeliveries: _replays, stagedRunAttachment, ...metadata } = state;
const stagedDescriptor = stagedRunAttachment ? (({ runAttachTemplate: _template, ...descriptor }) => descriptor)(stagedRunAttachment) : undefined;
if (stagedRunAttachment?.commandId === null) records.set("staged-attachment-template", JSON.stringify(stagedRunAttachment.runAttachTemplate));
const latestUsage = committedEvents.findLast(event => event.eventType === "usage.reported");
const recoveryEvents = committedEvents.filter(event => !isStreamingEvent(event) || event === latestUsage);
for (const command of commands) records.set(`command:${command.controllerSeq}`, JSON.stringify(command));
for (const event of recoveryEvents) records.set(`event:${event.sourceSeq}`, JSON.stringify(event));
const header: CheckpointHeader = { schema: "paperclip.runner.controller-checkpoint.v2",
runnerVersion, runnerDigest, state: { ...metadata, checkpointEventPolicy: "control-v1" } };
runnerVersion, runnerDigest, state: { ...metadata, ...(stagedDescriptor ? { stagedRunAttachment: stagedDescriptor } : {}), checkpointEventPolicy: "control-v1" } };
records.set("header", JSON.stringify(header));
records.set("cursor", JSON.stringify({ ackedSourceSeq: state.ackedSourceSeq, replayDeliveries: state.replayDeliveries,
commandCount: commands.length, eventCount: recoveryEvents.length }));
const maintenance = new Set(["runner.drain", "runner.suspend", "session.snapshot", "session.close"]);
const maintenance = new Set(["turn.stop", "runner.drain", "runner.suspend", "session.snapshot", "session.close"]);
return {
runnerVersion, runnerDigest, runId, records, ...checkpointChanges(records, runId),
recoveryEvidence: { runnerInstanceId: state.identity.runnerInstanceId, normalizedSessionId: state.identity.normalizedSessionId,
@@ -397,11 +411,43 @@ export function inspectDurablePrpCheckpointHeader(contents: string): DurableReco
const header = parseCheckpointHeader(contents);
if (header.schema !== "paperclip.runner.controller-checkpoint.v2"
|| header.state.checkpointEventPolicy !== "control-v1"
|| ![coreStateSchema, transitionCoreStateSchema].includes(String(header.state.schema))) return null;
|| ![coreStateSchema, transitionCoreStateSchema, stagedCoreStateSchema].includes(String(header.state.schema))
|| (header.state.schema === stagedCoreStateSchema && !validStagedRunAttachment(header.state as unknown as StoredCoreState, true))) return null;
return { ...header.state.identity };
} catch { return null; }
}
/** Bounded checkpoint metadata inspection; never itself grants recovery authority. */
export function inspectStagedRunAttachmentHeader(contents: string): {
attachment: Omit<StagedRunAttachment, "runAttachTemplate">; runnerVersion: string; runnerDigest: string;
protocolIdentity: DurableRecoveryIdentity;
phase: "source_bootstrap" | "attachment_queued" | "awaiting_result" | "prepared" | "activated" | "completed";
receipt: DurableWarmRunTransition | null;
} | null {
try {
if (Buffer.byteLength(contents) > maxStateBytes) return null;
const header = parseCheckpointHeader(contents), state = header.state as unknown as StoredCoreState;
if (header.schema !== "paperclip.runner.controller-checkpoint.v2"
|| state.checkpointEventPolicy !== "control-v1" || !validStagedRunAttachment(state, true)
|| !state.stagedRunAttachment) return null;
const transition = state.warmTransition;
const completed = stagedTargetActivated(state) ? state.completedWarmTransition : undefined;
const bound = transition ?? completed;
if (bound) {
const receipt = bound.receipt;
const result = transition?.phase === "awaiting_result" ? transition.expectedResult : bound.command.result;
const expected = warmTransitionReceipt(receipt.oldIdentity, bound.command, result!, receipt.oldAckedSourceSeq,
{ leaseId: receipt.leaseId, expiresAtUnixMs: receipt.leaseExpiresAtUnixMs, revocationEpoch: receipt.leaseRevocationEpoch },
header.runnerVersion, header.runnerDigest);
if (canonicalJson(expected) !== canonicalJson(receipt)) return null;
}
return { attachment: structuredClone(state.stagedRunAttachment), protocolIdentity: structuredClone(state.identity),
phase: transition?.phase ?? (completed ? "completed" : state.stagedRunAttachment.commandId ? "attachment_queued" : "source_bootstrap"),
receipt: bound ? structuredClone(bound.receipt) : null,
runnerVersion: header.runnerVersion, runnerDigest: header.runnerDigest };
} catch { return null; }
}
function checkpointState(records: ReadonlyMap<string, string>) {
const header = parseCheckpointHeader(records.get("header") ?? "null");
const read = (key: string): unknown => {
@@ -423,9 +469,14 @@ function checkpointState(records: ReadonlyMap<string, string>) {
}
const cursor = v2 ? read("cursor") : header.state;
if (!isRecord(cursor) || (v2 && (cursor.commandCount !== commandKeys.length || cursor.eventCount !== eventKeys.length
|| records.size !== commandKeys.length + eventKeys.length + 2))) throw new Error("native_runner_checkpoint_incomplete");
|| records.size !== commandKeys.length + eventKeys.length + 2 + (isRecord(header.state.stagedRunAttachment) && header.state.stagedRunAttachment.commandId === null ? 1 : 0)))) throw new Error("native_runner_checkpoint_incomplete");
const state = { ...header.state, ackedSourceSeq: cursor.ackedSourceSeq, replayDeliveries: cursor.replayDeliveries,
commands: commandKeys.map(read), committedEvents: eventKeys.map(read) };
commands: commandKeys.map(read), committedEvents: eventKeys.map(read) } as unknown as StoredCoreState;
if (state.stagedRunAttachment?.commandId === null) {
const template = read("staged-attachment-template");
if (!isRecord(template)) throw new Error("native_runner_checkpoint_invalid");
state.stagedRunAttachment = { ...state.stagedRunAttachment, runAttachTemplate: template };
}
if (!isStoredCoreState(state, header.state.identity)) throw new Error("native_runner_checkpoint_invalid");
if (state.commands.some((command, index) => commandKeys[index] !== `command:${command.controllerSeq}`)
|| state.committedEvents.some((event, index) => eventKeys[index] !== `event:${event.sourceSeq}`)) {
@@ -683,9 +734,9 @@ function warmTransitionReceipt(
function validStoredWarmTransition(state: StoredCoreState): boolean {
const transition = state.warmTransition;
if (!transition) return state.schema === coreStateSchema;
if (!transition) return state.schema === coreStateSchema || (state.schema === stagedCoreStateSchema && validStagedRunAttachment(state));
if (
state.schema !== transitionCoreStateSchema ||
(state.schema !== transitionCoreStateSchema && state.schema !== stagedCoreStateSchema) ||
!["awaiting_result", "prepared", "activated"].includes(transition.phase) ||
!isRecord(transition.receipt) ||
!isRecord(transition.command) ||
@@ -802,6 +853,76 @@ function unsettledSemanticInput(
}
}
function stagedCommandMatches(staged: Pick<StagedRunAttachment, "targetIdentity" | "templateDigest" | "commandId">, command: DurableRecoveryCoreCommand): boolean {
if (command.type !== "run.attach" || command.commandId !== staged.commandId) return false;
const { paperclipNextAuthority, ...template } = command.payload;
return isRecord(paperclipNextAuthority)
&& canonicalJson(paperclipNextAuthority.identity) === canonicalJson(staged.targetIdentity)
&& `sha256:${canonicalDigest(template)}` === staged.templateDigest;
}
function stagedAttachmentTemplate(state: StoredCoreState): Record<string, unknown> | null {
const staged = state.stagedRunAttachment;
if (!staged) return null;
if (staged.commandId === null) return staged.runAttachTemplate ?? null;
const command = state.commands?.find(command => command.commandId === staged.commandId)
?? state.warmTransition?.command ?? state.completedWarmTransition?.command;
if (!command || !stagedCommandMatches(staged, command)) return null;
const { paperclipNextAuthority: _authority, ...template } = command.payload;
return template;
}
function stagedTargetActivated(state: StoredCoreState): boolean {
const staged = state.stagedRunAttachment, completed = state.completedWarmTransition;
return !!staged && !state.warmTransition && !!completed
&& canonicalJson(state.identity) === canonicalJson(staged.targetIdentity)
&& completed.receipt.commandId === staged.commandId
&& canonicalJson(completed.receipt.oldIdentity) === canonicalJson(staged.sourceIdentity)
&& canonicalJson(completed.receipt.newIdentity) === canonicalJson(staged.targetIdentity)
&& stagedCommandMatches(staged, completed.command);
}
function validStagedRunAttachment(state: StoredCoreState, headerOnly = false): boolean {
const staged = state.stagedRunAttachment;
if (staged === undefined) return state.schema !== stagedCoreStateSchema;
if (!(state.schema === stagedCoreStateSchema && isRecord(staged)
&& Object.keys(staged).sort().join(",") === (headerOnly || staged.commandId !== null ? "commandId,sourceIdentity,targetIdentity,templateDigest" : "commandId,runAttachTemplate,sourceIdentity,targetIdentity,templateDigest")
&& exactIdentity(staged.sourceIdentity) && exactIdentity(staged.targetIdentity)
&& staged.sourceIdentity.runId !== staged.targetIdentity.runId
&& ["runnerInstanceId", "environmentLeaseId", "normalizedSessionId"].every(key =>
staged.sourceIdentity[key as keyof DurableRecoveryIdentity] === staged.targetIdentity[key as keyof DurableRecoveryIdentity])
&& /^sha256:[0-9a-f]{64}$/.test(staged.templateDigest)
&& (headerOnly || staged.commandId !== null || (isRecord(staged.runAttachTemplate) && isRecord(staged.runAttachTemplate.provider)
&& staged.runAttachTemplate.paperclipNextAuthority === undefined
&& `sha256:${canonicalDigest(staged.runAttachTemplate)}` === staged.templateDigest
&& Buffer.byteLength(JSON.stringify(staged.runAttachTemplate)) <= maxCommandBytes))
&& (staged.commandId === null || (typeof staged.commandId === "string" && stableIdPattern.test(staged.commandId))))) return false;
const sourceCurrent = canonicalJson(state.identity) === canonicalJson(staged.sourceIdentity);
if (!sourceCurrent && canonicalJson(state.identity) !== canonicalJson(staged.targetIdentity)) return false;
if (state.warmTransition) {
const transition = state.warmTransition;
return staged.commandId !== null && stagedCommandMatches(staged, transition.command)
&& canonicalJson(transition.receipt.oldIdentity) === canonicalJson(staged.sourceIdentity)
&& canonicalJson(transition.receipt.newIdentity) === canonicalJson(staged.targetIdentity)
&& (sourceCurrent ? transition.phase !== "activated" : transition.phase === "activated");
}
if (!sourceCurrent) return stagedTargetActivated(state);
if (staged.commandId === null) return true;
// Header-only inspection cannot certify the separate command record. Full
// restoration below requires its exact tuple, payload and command ID.
return !Array.isArray(state.commands) || state.commands.some(command => stagedCommandMatches(staged, command));
}
/** Inspection is evidence only; the caller still owns recovery authorization. */
export function inspectStagedRunAttachment(state: unknown, expectedTarget?: DurableRecoveryIdentity): StagedRunAttachment | null {
try {
if (!isRecord(state) || !exactIdentity(state.identity) || !isStoredCoreState(state, state.identity)
|| !state.stagedRunAttachment || (expectedTarget && canonicalJson(state.stagedRunAttachment.targetIdentity) !== canonicalJson(expectedTarget))) return null;
const template = stagedAttachmentTemplate(state);
return template ? { ...structuredClone(state.stagedRunAttachment), runAttachTemplate: structuredClone(template) } : null;
} catch { return null; }
}
function isStoredCoreState(
value: unknown,
identity: DurableRecoveryIdentity,
@@ -811,7 +932,7 @@ function isStoredCoreState(
const events = value.committedEvents;
if (
(value.schema !== coreStateSchema &&
value.schema !== transitionCoreStateSchema) ||
value.schema !== transitionCoreStateSchema && value.schema !== stagedCoreStateSchema) ||
canonicalJson(value.identity) !== canonicalJson(identity) ||
(value.checkpointEventPolicy !== undefined && value.checkpointEventPolicy !== "control-v1") ||
!isRecord(value.tickets) ||
@@ -828,6 +949,7 @@ function isStoredCoreState(
) {
return false;
}
if (!validStagedRunAttachment(value as unknown as StoredCoreState)) return false;
if (!validStoredWarmTransition(value as unknown as StoredCoreState))
return false;
if (value.completedWarmTransition !== undefined) {
@@ -1758,6 +1880,7 @@ class AuthorityConnection {
/** Authenticated, replay-safe PRP transport authority. Business operations are caller supplied. */
export class DurablePrpControlPlane {
#identity: DurableRecoveryIdentity;
#stagedTargetCheckpointPublished: string | null = null;
readonly #store: DurableCoreStore;
#expectedRunnerVersion: string;
#expectedRunnerDigest: string;
@@ -1854,7 +1977,12 @@ export class DurablePrpControlPlane {
cached.set(part, prior?.record === record ? prior : { record, command: JSON.parse(record) });
}
this.#publicationCommands = cached;
return { ...header.state, ...cursor, commands: [...cached.values()].map(value => value.command),
const descriptor = header.state.stagedRunAttachment;
const workingStage = this.#store.state.stagedRunAttachment;
const staged = descriptor?.commandId === null ? { ...descriptor,
runAttachTemplate: workingStage?.templateDigest === descriptor.templateDigest && workingStage.runAttachTemplate
? workingStage.runAttachTemplate : JSON.parse(snapshot.records.get("staged-attachment-template")!) as Record<string, unknown> } : descriptor;
return { ...header.state, ...(staged ? { stagedRunAttachment: staged } : {}), ...cursor, commands: [...cached.values()].map(value => value.command),
// Event records are immutable after insertion. Retain their array, not
// another deep copy of the streaming payload window.
committedEvents: this.#store.state.committedEvents };
@@ -2323,6 +2451,23 @@ export class DurablePrpControlPlane {
return ticket;
}
/** Import exact durable handoff evidence for a caller-verified surviving participant; issues no credential. */
prepareWarmTransitionAdoption(input: { transitionId: string; runnerState: Record<string, unknown> }): void {
this.#store.assertWritable();
const pending = input.runnerState.warmTransition;
const proof = warmTransitionRecoveryProof({ controlPlaneState: this.#store.state, runnerState: input.runnerState,
expectedNewIdentity: (isRecord(pending) && isRecord(pending.receipt) ? pending.receipt.newIdentity : null) as DurableRecoveryIdentity,
expectedRunnerVersion: this.#expectedRunnerVersion, expectedRunnerDigest: this.#expectedRunnerDigest });
if (!proof || proof.transition.receipt.transitionId !== input.transitionId || !this.#store.state.stagedRunAttachment) {
throw new Error("Warm transition adoption snapshot proof is not authorized.");
}
if (this.#store.state.warmTransition && canonicalJson(this.#store.state.warmTransition) === canonicalJson(proof.transition)) return;
const candidate = structuredClone(this.#store.state);
candidate.schema = stagedCoreStateSchema;
candidate.warmTransition = structuredClone(proof.transition);
this.#store.commit(candidate);
}
/** Caller-owned recovery admission is required; a receipt is not a credential. */
issueWarmTransitionBootstrapTicket(
input: {
@@ -2361,7 +2506,7 @@ export class DurablePrpControlPlane {
original.expiresAtUnixMs,
);
const candidate = structuredClone(this.#store.state);
candidate.schema = transitionCoreStateSchema;
candidate.schema = candidate.stagedRunAttachment ? stagedCoreStateSchema : transitionCoreStateSchema;
candidate.warmTransition = structuredClone(transition);
candidate.tickets[material.credentialId] = {
recordId: `bootstrap_ticket_${randomUUID()}`,
@@ -2380,6 +2525,36 @@ export class DurablePrpControlPlane {
return ticket;
}
/** Caller confirms its existing session-checkpoint transaction committed. No journal write. */
markStagedTargetCheckpointPublished(identity: DurableRecoveryIdentity): boolean {
if (!stagedTargetActivated(this.#store.state) || canonicalJson(identity) !== canonicalJson(this.#store.state.identity)) return false;
this.#stagedTargetCheckpointPublished = canonicalJson(identity);
return true;
}
/** Durable intent precedes source restoration; it is never a dispatched command. */
stageRunAttachment(targetIdentity: DurableRecoveryIdentity, runAttachTemplate: Record<string, unknown>): void {
this.#store.assertWritable();
const existing = this.#store.state.stagedRunAttachment;
if (existing && !(stagedTargetActivated(this.#store.state)
&& this.#stagedTargetCheckpointPublished === canonicalJson(this.#store.state.identity)
&& targetIdentity.runId !== existing.targetIdentity.runId)) {
if (!validStagedRunAttachment(this.#store.state)
|| canonicalJson(existing.targetIdentity) !== canonicalJson(targetIdentity)
|| canonicalJson(stagedAttachmentTemplate(this.#store.state)) !== canonicalJson(runAttachTemplate)) {
throw new Error("Staged run attachment conflicts with its durable intent.");
}
return;
}
const candidate = structuredClone(this.#store.state);
candidate.schema = stagedCoreStateSchema;
candidate.stagedRunAttachment = { sourceIdentity: structuredClone(candidate.identity),
targetIdentity: structuredClone(targetIdentity), runAttachTemplate: structuredClone(runAttachTemplate),
templateDigest: `sha256:${canonicalDigest(runAttachTemplate)}`, commandId: null };
if (!validStagedRunAttachment(candidate)) throw new Error("Staged run attachment binding is invalid.");
this.#store.commit(candidate);
}
queueCommand(
type: string,
payload: Record<string, unknown> = {},
@@ -2387,6 +2562,21 @@ export class DurablePrpControlPlane {
deliverImmediately = false,
): DurableRecoveryCoreCommand {
this.#store.assertWritable();
const staged = this.#store.state.stagedRunAttachment;
const retireStaged = stagedTargetActivated(this.#store.state) && (type === "turn.start"
|| (this.#stagedTargetCheckpointPublished === canonicalJson(this.#store.state.identity)
&& !["runner.drain", "session.snapshot", "turn.stop", "runner.suspend", "session.close"].includes(type)));
if (staged && !retireStaged) {
if (type === "run.attach") {
const { paperclipNextAuthority, ...template } = payload;
if (!isRecord(paperclipNextAuthority)
|| canonicalJson(paperclipNextAuthority.identity) !== canonicalJson(staged.targetIdentity)
|| `sha256:${canonicalDigest(template)}` !== staged.templateDigest
|| (staged.commandId !== null && staged.commandId !== commandId)) throw new Error("Staged run attachment command conflicts.");
} else if (!stagedTargetActivated(this.#store.state) && !["runner.drain", "session.open", "session.snapshot", "turn.stop", "runner.suspend", "session.close", "semantic_tool.result"].includes(type)) {
throw new Error("Staged run attachment permits recovery maintenance only.");
}
}
const transition = this.#store.state.warmTransition;
if (transition && transition.phase !== "activated") {
if (
@@ -2461,6 +2651,16 @@ export class DurablePrpControlPlane {
if (Buffer.byteLength(JSON.stringify(command)) > maxCommandBytes)
throw new CommandJournalLimitError("command_payload_too_large");
this.#store.state.commands.push(command);
if (staged && !retireStaged && type === "run.attach") {
const { runAttachTemplate: _template, ...descriptor } = staged;
this.#store.state.stagedRunAttachment = { ...descriptor, commandId: command.commandId };
} else if (retireStaged) {
// Native runtime persists the recovered session before its first turn.
// Read/goal controls during opening must not retire startup evidence.
delete this.#store.state.stagedRunAttachment;
this.#store.state.schema = coreStateSchema;
this.#stagedTargetCheckpointPublished = null;
}
this.#store.save();
void this.flushCheckpoint().catch(() => undefined);
if (deliverImmediately) {
@@ -2677,7 +2877,7 @@ export class DurablePrpControlPlane {
}
if (transition) {
const candidate = structuredClone(this.#store.state);
candidate.schema = coreStateSchema;
candidate.schema = candidate.stagedRunAttachment ? stagedCoreStateSchema : coreStateSchema;
candidate.leases[transition.credentialId]!.identity = structuredClone(
receipt.newIdentity,
);
@@ -3205,8 +3405,9 @@ export class DurablePrpControlPlane {
}
if (transition.phase === "prepared") {
const candidate = initialCoreState(transition.receipt.newIdentity);
candidate.stagedRunAttachment = structuredClone(this.#store.state.stagedRunAttachment);
candidate.preparedLaunchConfigurations = structuredClone(this.#store.state.preparedLaunchConfigurations);
candidate.schema = transitionCoreStateSchema;
candidate.schema = candidate.stagedRunAttachment ? stagedCoreStateSchema : transitionCoreStateSchema;
candidate.warmTransition = {
...structuredClone(transition),
phase: "activated",
@@ -3507,7 +3708,7 @@ export class DurablePrpControlPlane {
)!;
completed.status = "completed";
completed.result = structuredClone(result);
candidate.schema = transitionCoreStateSchema;
candidate.schema = candidate.stagedRunAttachment ? stagedCoreStateSchema : transitionCoreStateSchema;
candidate.warmTransition = {
receipt,
phase: "prepared",
@@ -36,6 +36,10 @@ export interface RunnerExecutionDriver {
checkpoint?: (settlement: "settled" | "unsettled") => Promise<void> | void;
release: (reason?: "detach") => Promise<void> | void;
}>;
/** Register source and target checkpoint ownership before staging changes the stored run. */
onStagedRunAttachment?: (authority: DurablePrpControlPlane, proof: {
sourceIdentity: DurableRecoveryIdentity; targetIdentity: DurableRecoveryIdentity; templateDigest: string;
}) => Promise<void>;
/** Override process ownership; omission uses the SDK's local process owner. */
runnerProcessLauncher?: (
spec: RunnerProcessLaunchSpec,
@@ -0,0 +1,46 @@
import { redactPaperclipSemanticValue } from "../../semantic-tools/redaction.js";
import { safeAcpxLocations } from "./safe-locations.js";
import { lstatSync, realpathSync } from "node:fs";
import { basename, dirname, isAbsolute, join } from "node:path";
const record = (value: unknown): Record<string, unknown> =>
value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
function aliasedLocation(path: string, cwd: string): string | undefined {
if (!isAbsolute(path)) return;
try {
const canonicalCwd = realpathSync(cwd);
let canonicalPath: string;
try {
lstatSync(path);
canonicalPath = realpathSync(path);
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") return;
try { lstatSync(path); return; } catch (missing) {
if ((missing as NodeJS.ErrnoException).code !== "ENOENT") return;
}
canonicalPath = join(realpathSync(dirname(path)), basename(path));
}
return safeAcpxLocations([{ path: canonicalPath }], canonicalCwd, "edit")[0]?.path as string | undefined;
} catch { return; }
}
export function safeAcpxEditTarget(value: unknown, workingDirectory: string | undefined): string | undefined {
const call = record(value);
if (call.kind !== "edit" || !workingDirectory) return;
const input = record(call.rawInput);
if (call.locations !== undefined && !Array.isArray(call.locations)) return;
if (Array.isArray(call.locations) && (call.locations.length > 16 || call.locations.some(x => typeof record(x).path !== "string"))) return;
const paths = [input.path, input.fileName, ...(Array.isArray(call.locations) ? call.locations.map(x => record(x).path) : [])].filter(x => x !== undefined);
if (!paths.length || paths.some(x => typeof x !== "string" || !x || x.trim() !== x || x.length > 2048 || /[\\\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(x))) return;
const safe = paths.flatMap(path => {
const locations = safeAcpxLocations([{ path }], workingDirectory, call.kind, call.title);
if (locations.length) return locations;
const aliased = aliasedLocation(path as string, workingDirectory);
return aliased ? [{ path: aliased }] : [];
});
const names = safe.map(x => x.path).filter((x): x is string => typeof x === "string" && x.length <= 1024 && x.trim() === x && !x.includes(":"));
if (names.length !== paths.length || new Set(names).size !== 1) return;
const target = names[0];
return target && redactPaperclipSemanticValue(target) === target ? target : undefined;
}
@@ -13,7 +13,7 @@ function request(kinds = ["allow_once", "allow_always", "reject_once"]): AcpPerm
} } as AcpPermissionRequest;
}
describe("ACP permission normalization", () => {
it.each([undefined, "codex", "claude", "pi", "copilot"])("preserves existing %s native identity behavior", provider => {
it.each([undefined, "codex", "claude", "pi", "copilot", "kimi"])("preserves existing %s native identity behavior", provider => {
const native = request(); native.raw.toolCall.toolCallId = "native\u0000tool";
expect(normalizeAcpxPermission(native, { provider }).toolCallId).toBe("native\u0000tool");
});
@@ -93,6 +93,42 @@ describe("ACP permission normalization", () => {
});
});
describe("Kimi canonical file permission context", () => {
const context = { provider: "kimi", workingDirectory: "/fixture/workspace", allowAlwaysScope: "session" as const };
function write(call: Record<string, unknown> = {}) {
const value = request();
Object.assign(value.raw.toolCall, { toolCallId: "kimi-write-native-id", kind: "edit", title: "Write", rawInput: { path: "/fixture/workspace/nested/result.txt", content: "PRIVATE_KIMI_CONTENT" }, locations: [{ path: "nested/result.txt" }], ...call });
return value;
}
it("shows the native Write target and preserves its exact operation and decision identity", () => {
const normalized = normalizeAcpxPermission(write(), context);
expect(normalized.title).toBe("Change file: nested/result.txt");
expect(normalized.toolCallId).toBe("kimi-write-native-id");
expect(JSON.stringify(normalized)).not.toContain("PRIVATE_KIMI_CONTENT");
expect(normalized.resolve({ action: "accept" })).toEqual({ outcome: "allow_once" });
expect(normalized.resolve({ action: "accept_for_session" })).toEqual({ outcome: "allow_always" });
expect(normalized.resolve({ action: "decline" })).toEqual({ outcome: "reject_once" });
});
it.each([
{ name: "unknown", rawInput: {}, locations: [] },
{ name: "conflicting", rawInput: { path: "one.txt" }, locations: [{ path: "two.txt" }] },
{ name: "outside", rawInput: { path: "/outside/result.txt" }, locations: [] },
])("offers only denial or cancellation for a $name Write target", ({ rawInput, locations }) => {
const normalized = normalizeAcpxPermission(write({ rawInput, locations }), context);
expect(normalized.title).toContain("target unavailable");
expect(normalized.toolCallId).toBe("kimi-write-native-id");
expect(normalized.choices.map(choice => choice.key)).toEqual(["decline", "cancel"]);
expect(normalized.resolve({ action: "decline" })).toEqual({ outcome: "reject_once" });
expect(normalized.resolve({ action: "cancel" })).toEqual({ outcome: "cancel" });
for (const action of ["accept", "accept_for_session"] as const) expect(() => normalized.resolve({ action })).toThrow("not an offered choice");
});
it("requires a bound workspace before allowing a native Write", () => {
const normalized = normalizeAcpxPermission(write(), { provider: "kimi" });
expect(normalized.choices.map(choice => choice.key)).toEqual(["decline", "cancel"]);
expect(() => normalized.resolve({ action: "accept" })).toThrow("not an offered choice");
});
});
describe("Copilot canonical file permission context", () => {
const context = { provider: "copilot", workingDirectory: "/fixture/workspace", allowAlwaysScope: "session" as const };
@@ -4,7 +4,7 @@ import { cursorToolIdentity } from "./cursor-plan-tool-identity.js";
import { safeCopilotEditTarget } from "./copilot-permission-context.js";
import { safeAcpxEditTarget } from "./acp-edit-context.js";
export type AcpxPermissionAction = "accept" | "accept_for_session" | "decline" | "cancel";
export interface NormalizedAcpxPermission {
@@ -39,8 +39,8 @@ export function normalizeAcpxPermission(request: AcpPermissionRequest, options:
|| (options.provider === "cursor" && !call.toolCallId.trim())) {
throw new Error("ACP permission request omitted its tool identity");
}
const needsEditContext = options.provider === "copilot" && call.kind === "edit";
const target = needsEditContext ? safeCopilotEditTarget(call, options.workingDirectory) : undefined;
const needsEditContext = (options.provider === "copilot" || options.provider === "kimi") && call.kind === "edit";
const target = needsEditContext ? safeAcpxEditTarget(call, options.workingDirectory) : undefined;
const bindings = new Map<AcpxPermissionAction, AcpPermissionDecision["outcome"]>();
const choices: NormalizedAcpxPermission["choices"] = [];
for (const [key, kind, label] of [
@@ -1,7 +1,9 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { KIMI_DISTRIBUTION_CLOSURE_SHA256 } from "./kimi-closure-pins.js";
import { ANTIGRAVITY_DISTRIBUTION_PINS } from "./generated-profiles.js";
import { assertAcpxProfileReleased, resolveQualifiedAcpxProfile } from "./qualified-profiles.js";
import { assertAcpxExecutionAdmission, bindAcpxCandidateAdmission, parseAcpxCandidateAdmission, parseAcpxCandidateStartupAdmission, type AcpxCandidateAdmission } from "./candidate-admission.js";
import historicalKimi from "../../../test/fixtures/kimi-profile-v16-identity.json" with { type: "json" };
import { verifyAcpxProfileInstallation } from "./profile-installation.js";
import { AcpxRuntimeHost } from "./runtime-host.js";
import * as integrity from "./installation-integrity.js";
@@ -95,3 +97,73 @@ describe("candidate installation remains cryptographically verified", () => {
expect(openRuntime).not.toHaveBeenCalled();
});
});
describe("trusted Kimi candidate admission", () => {
const kimiExpected = { companyId: "company-kimi", agent: "kimi", model: "kimi-for-coding" };
const kimiCandidate = (target: "darwin-arm64" | "linux-x64"): AcpxCandidateAdmission => ({
schema: "paperclip.acpx-candidate-admission.v1", ...kimiExpected, agent: "kimi",
profileDigest: resolveQualifiedAcpxProfile("kimi", kimiExpected.model).commandDigest,
nativeClosureDigest: `sha256:${KIMI_DISTRIBUTION_CLOSURE_SHA256[target]}`,
platform: target === "darwin-arm64" ? "darwin" : "linux",
architecture: target === "darwin-arm64" ? "arm64" : "x64", expiresAt: now + 60_000,
});
it.each(["darwin-arm64", "linux-x64"] as const)("binds only the current pending Kimi profile and exact %s closure", target => {
const profile = resolveQualifiedAcpxProfile("kimi", kimiExpected.model);
if (profile.qualificationStatus === "pending") {
expect(parseAcpxCandidateAdmission(kimiCandidate(target), kimiExpected, now)).toEqual(kimiCandidate(target));
expect(() => assertAcpxProfileReleased("kimi")).toThrow("qualification is pending");
expect(() => assertAcpxExecutionAdmission(profile)).toThrow("qualification is pending");
} else {
expect(profile.agentProfileVersion).toBe(17);
expect(() => assertAcpxProfileReleased("kimi")).not.toThrow();
expect(() => assertAcpxExecutionAdmission(profile)).not.toThrow();
expect(() => parseAcpxCandidateAdmission(kimiCandidate(target), kimiExpected, now)).toThrow();
expect(() => parseAcpxCandidateAdmission({ ...kimiCandidate(target), profileDigest: historicalKimi.commandDigest }, kimiExpected, now)).toThrow();
}
const other = target === "darwin-arm64" ? "linux-x64" : "darwin-arm64";
for (const change of [
{ companyId: "foreign-company" }, { agent: "antigravity" }, { model: "another-model" },
{ model: "auto" }, { model: "default" }, { model: " kimi-for-coding" },
{ profileDigest: `sha256:${"0".repeat(64)}` },
{ nativeClosureDigest: `sha256:${KIMI_DISTRIBUTION_CLOSURE_SHA256[other]}` },
{ platform: "linux", architecture: "arm64" }, { platform: "darwin", architecture: "x64" },
{ expiresAt: now }, { expiresAt: now + 86_400_001 }, { expiresAt: "1800000060000" },
{ unknown: true }, { runId: "injected-run" },
]) expect(() => parseAcpxCandidateAdmission({ ...kimiCandidate(target), ...change }, kimiExpected, now)).toThrow(expect.objectContaining({ code: "ACPX_CANDIDATE_ADMISSION_INVALID" }));
});
it.each(["darwin-arm64", "linux-x64"] as const)("requires the exact actual host and startup identity for %s", target => {
const profile = resolveQualifiedAcpxProfile("kimi", kimiExpected.model);
const source = { ...kimiCandidate(target), expiresAt: Date.now() + 60_000 };
const identity = { runId: "kimi-run", normalizedSessionId: "kimi-session" };
const startup = profile.qualificationStatus === "pending" ? bindAcpxCandidateAdmission(source, identity) : { ...source, ...identity };
if (profile.qualificationStatus !== "pending") expect(() => bindAcpxCandidateAdmission(source, identity)).toThrow();
const platform = Object.getOwnPropertyDescriptor(process, "platform")!;
const architecture = Object.getOwnPropertyDescriptor(process, "arch")!;
try {
Object.defineProperty(process, "platform", { ...platform, value: startup.platform });
Object.defineProperty(process, "arch", { ...architecture, value: startup.architecture });
if (profile.qualificationStatus === "pending") expect(() => assertAcpxExecutionAdmission(profile, startup)).not.toThrow();
else {
expect(() => assertAcpxExecutionAdmission(profile)).not.toThrow();
expect(() => assertAcpxExecutionAdmission(profile, startup)).toThrow();
}
for (const identity of [{ runId: "foreign-run" }, { normalizedSessionId: "foreign-session" }, { model: "foreign-model" }]) {
expect(() => parseAcpxCandidateStartupAdmission(startup, { ...kimiExpected, runId: "kimi-run", normalizedSessionId: "kimi-session", ...identity })).toThrow();
}
expect(() => assertAcpxExecutionAdmission({ ...profile, commandDigest: `sha256:${"f".repeat(64)}` }, startup)).toThrow();
expect(() => parseAcpxCandidateStartupAdmission(startup, kimiExpected, startup.expiresAt)).toThrow();
for (const [os, arch] of [["win32", "x64"], ["linux", "arm64"], ["darwin", "x64"],
target === "darwin-arm64" ? ["linux", "x64"] : ["darwin", "arm64"]]) {
Object.defineProperty(process, "platform", { ...platform, value: os });
Object.defineProperty(process, "arch", { ...architecture, value: arch });
expect(() => assertAcpxExecutionAdmission(profile, startup)).toThrow();
}
} finally {
Object.defineProperty(process, "platform", platform);
Object.defineProperty(process, "arch", architecture);
}
});
});
@@ -1,3 +1,4 @@
import { KIMI_DISTRIBUTION_CLOSURE_SHA256 } from "./kimi-closure-pins.js";
import { ANTIGRAVITY_DISTRIBUTION_PINS } from "./generated-profiles.js";
import { QUALIFIED_ACPX_PROFILES, assertAcpxProfileReleased, type QualifiedAcpxProfile } from "./qualified-profiles.js";
@@ -5,12 +6,12 @@ import { QUALIFIED_ACPX_PROFILES, assertAcpxProfileReleased, type QualifiedAcpxP
export interface AcpxCandidateAdmission {
schema: "paperclip.acpx-candidate-admission.v1";
companyId: string;
agent: "antigravity";
agent: "antigravity" | "kimi";
model: string;
profileDigest: string;
nativeClosureDigest: string;
platform: "darwin";
architecture: "arm64";
platform: "darwin" | "linux";
architecture: "arm64" | "x64";
expiresAt: number;
}
@@ -31,16 +32,23 @@ export function parseAcpxCandidateAdmission(
): AcpxCandidateAdmission {
if (value === null || typeof value !== "object" || Array.isArray(value)) return invalid();
const entry = value as Record<string, unknown>;
const profile = entry.agent === "antigravity" ? QUALIFIED_ACPX_PROFILES.antigravity
: entry.agent === "kimi" ? QUALIFIED_ACPX_PROFILES.kimi : undefined;
const target = `${entry.platform}-${entry.architecture}`;
const closure = entry.agent === "antigravity" && target === "darwin-arm64"
? ANTIGRAVITY_DISTRIBUTION_PINS["darwin-arm64"].closureSha256
: entry.agent === "kimi" && (target === "darwin-arm64" || target === "linux-x64")
? KIMI_DISTRIBUTION_CLOSURE_SHA256[target] : undefined;
if (!Number.isSafeInteger(now) || now < 0 || Object.keys(entry).length !== BASE_FIELDS.length || Object.keys(entry).some(key => !BASE_FIELDS.includes(key))
|| entry.schema !== "paperclip.acpx-candidate-admission.v1"
|| !stableId(entry.companyId) || entry.companyId !== expected.companyId
|| entry.agent !== "antigravity" || entry.agent !== expected.agent
|| !profile || entry.agent !== expected.agent
|| entry.agent === "kimi" && profile.qualificationStatus !== "pending"
|| typeof entry.model !== "string" || entry.model !== expected.model || !entry.model.trim()
|| entry.model !== entry.model.trim() || Buffer.byteLength(entry.model) > 512 || /[\u0000-\u001f\u007f]/.test(entry.model)
|| ["auto", "default"].includes(entry.model.toLowerCase())
|| entry.profileDigest !== QUALIFIED_ACPX_PROFILES.antigravity.commandDigest
|| entry.nativeClosureDigest !== `sha256:${ANTIGRAVITY_DISTRIBUTION_PINS["darwin-arm64"].closureSha256}`
|| entry.platform !== "darwin" || entry.architecture !== "arm64"
|| entry.profileDigest !== profile.commandDigest
|| !closure || entry.nativeClosureDigest !== `sha256:${closure}`
|| !Number.isSafeInteger(entry.expiresAt) || Number(entry.expiresAt) <= now
|| Number(entry.expiresAt) > now + 24 * 60 * 60 * 1000) return invalid();
return { ...entry } as unknown as AcpxCandidateAdmission;
@@ -74,7 +82,7 @@ export function parseAcpxCandidateStartupAdmission(
/** This exception is deliberately absent from direct driver/config admission. */
export function assertAcpxExecutionAdmission(profile: QualifiedAcpxProfile, candidate?: AcpxCandidateStartupAdmission): void {
if (candidate === undefined) return assertAcpxProfileReleased(profile.agent);
if (process.platform !== "darwin" || process.arch !== "arm64") return invalid();
parseAcpxCandidateStartupAdmission(candidate, { agent: profile.agent, model: profile.qualificationModel });
if (candidate.platform !== process.platform || candidate.architecture !== process.arch) return invalid();
if (profile.commandDigest !== candidate.profileDigest) return invalid();
}
@@ -62,6 +62,12 @@ export const ACPX_CAPABILITY_PROFILES: Readonly<Record<QualifiedAcpxAgent, AcpxC
artifacts: "references-pending", extensionRequests: [],
extensionNotifications: ["github.com/copilot/sessionEvent"],
},
kimi: {
displayName: "Kimi Code", qualification: "qualified", models: "explicit-provider-verified",
permissions: "interactive", questions: "semantic-only", plans: "semantic-only", tools: "authenticated-mcp",
recovery: "unverified", usage: "unverified", steering: "unsupported", followUp: "controller-queue",
artifacts: "policy_disabled", extensionRequests: [], extensionNotifications: [],
},
pi: {
displayName: "Pi", qualification: "qualified", models: "explicit-provider-verified",
permissions: "interactive", questions: "form", plans: "semantic-only", tools: "owned-extension",
@@ -4,6 +4,7 @@ import { createHash } from "node:crypto";
import { cursorToolIdentity } from "./cursor-plan-tool-identity.js";
import { describe, expect, it, vi } from "vitest";
import { AcpxProviderProcessLostError } from "./codex-runtime-adapter.js";
import { QUALIFIED_ACPX_PROFILES } from "./qualified-profiles.js";
import type { AcpRuntimeEvent } from "acpx/runtime";
@@ -209,6 +210,46 @@ describe("Codex ACPX harness driver", () => {
await session.close({ reason: "native attribution verified" });
});
it("enforces the current Kimi release gate before opening a direct runtime host", () => {
const openHost = vi.fn();
const construct = () => driverFixture({ agent: "kimi", model: "kimi-for-coding", providerPolicy: { readOnly: true } }, { openHost });
if (QUALIFIED_ACPX_PROFILES.kimi.qualificationStatus === "pending") {
expect(construct).toThrow(expect.objectContaining({ code: "ACPX_PROFILE_QUALIFICATION_PENDING", retryable: false }));
} else expect(construct).not.toThrow();
expect(openHost).not.toHaveBeenCalled();
});
it.skipIf(QUALIFIED_ACPX_PROFILES.kimi.qualificationStatus === "pending").each(["exact-final", "tool-only", "empty-final", "missing-end", "cancelled", "filtered", "paused", "max-tokens"])("qualified Kimi direct-driver step boundaries separate commentary from the actual final response: %s", async scenario => {
const id = (n: number) => `kimi-message-${String(n).padStart(64, "0")}`;
const chunk = (n: number, kind: string, text = "", stream: "output" | "thought" = "output"): AcpRuntimeEvent => ({
type: "text_delta", text, stream, messageId: id(n), meta: { origin: "kimi-native-assistant", source: "kimi-turn-step-v1", kind },
});
const runtimeEvents = [chunk(1, "start"), chunk(1, "delta", "Calling finish."), chunk(1, "end:tool_use")];
if (scenario !== "tool-only") {
runtimeEvents.push(chunk(2, "start"), chunk(2, "delta", "private thought", "thought"));
if (scenario !== "empty-final") runtimeEvents.push(chunk(2, "delta", "EXACT_"), chunk(2, "delta", "MARKER"));
if (scenario !== "missing-end" && scenario !== "cancelled") runtimeEvents.push(chunk(2,
`end:${scenario === "filtered" || scenario === "paused" ? scenario : scenario === "max-tokens" ? "max_tokens" : "end_turn"}`));
}
const fixture = driverFixture({ agent: "kimi", model: "kimi-for-coding", providerPolicy: { readOnly: true } }, { runtimeEvents });
const session = await fixture.driver.openSession({ runId: "run-kimi-boundaries", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
const { turnId } = await session.startTurn({ message: { text: "Answer exactly" } });
fixture.finishTurn({ status: scenario === "cancelled" ? "cancelled" : "completed" });
const events = await collectUntil(session.events(), scenario === "missing-end" ? "turn.failed" : scenario === "cancelled" ? "turn.interrupted" : "turn.completed");
const progress = events.find(event => event.eventType === "item.delta" && event.payload.text === "Calling finish.")!;
expect(progress).toBeDefined();
expect(progress.itemId).toBe(`${turnId}:assistant-message:${id(1)}`);
const commentary = events.filter(event => event.eventType === "item.completed" && event.payload.kind === "agentMessage" && event.payload.channel === "commentary");
expect(commentary.filter(event => event.payload.text === "Calling finish.")).toHaveLength(1);
expect(commentary[0]!.itemId).toBe(progress.itemId);
const final = events.filter(event => event.eventType === "item.completed" && event.payload.kind === "agentMessage" && event.payload.channel === "final");
expect(final.map(event => event.payload.text)).toEqual(["exact-final", "max-tokens"].includes(scenario) ? ["EXACT_MARKER"] : []);
if (final.length) expect(final[0]!.itemId).toBe(`${turnId}:assistant-message:${id(2)}`);
expect(events.filter(event => event.payload.kind === "agentMessage").some(event => event.payload.text === "private thought")).toBe(false);
expect(events.filter(event => event.eventType === "item.delta" && event.payload.text === "").length).toBeGreaterThanOrEqual(2);
await session.close({ reason: "Kimi native final attribution verified" });
});
it("delivers negotiated steering and follow-up distinctly, once, for the active turn", async () => {
const fixture = driverFixture({ agent: "pi", model: "openrouter/deepseek/deepseek-v4-flash-0731", providerPolicy: { readOnly: true } }, { runtimeEvents: [] });
fixture.host.steeringCapability.mockReturnValue({ steering: true, queuedFollowUp: true });
@@ -5,6 +5,7 @@ import { acpxProfileActivity, type AcpxActivityAdapter, type AcpxToolEvidence }
import { requireAcpxResponseDelivery } from "./response-delivery.js";
import { createPiMessageProjection, piBoundaryClearsFinal, type PiProjectedMessageEvent } from "./pi-message-projection.js";
import { createKimiMessageProjection, kimiMessageBoundaryClears, type KimiProjectedMessageEvent } from "./kimi-message-boundary.js";
import { acpxProfileClientCapabilities, bindAcpxExtensionTurn, validateAcpxRichEvent, createAcpxProfileExtensionAdapter, type AcpxExtensionInput } from "./profile-extensions.js";
import { createHash, randomBytes } from "node:crypto";
@@ -290,7 +291,7 @@ export class CodexAcpxDriver implements HarnessDriver {
dependencies: CodexAcpxDriverDependencies = {},
) {
assertAcpxProfileReleased(options.agent ?? "codex");
if (["pi", "cursor", "copilot"].includes(options.agent ?? "codex") && typeof options.providerPolicy?.readOnly !== "boolean") {
if (["pi", "cursor", "copilot", "kimi"].includes(options.agent ?? "codex") && typeof options.providerPolicy?.readOnly !== "boolean") {
throw new Error("ACP candidate requires an explicit provider read-only policy");
}
this.#options = {
@@ -330,7 +331,7 @@ export class CodexAcpxDriver implements HarnessDriver {
},
capabilities: {
...descriptor.capabilities,
resume: true,
resume: descriptor.capabilities.resume,
runtimeRequestResolution: true,
runtimeRequestHandoff: true,
unsupported: descriptor.capabilities.unsupported,
@@ -1481,7 +1482,8 @@ class CodexAcpxSession implements HarnessSession {
const normalizeToolEvent =
createAcpxToolEventNormalizer<AcpRuntimeEvent>();
const piMessages = this.#agent === "pi" ? createPiMessageProjection<AcpRuntimeEvent>() : null;
const normalizeMessage = piMessages ? piMessages.normalize : this.#agent === "grok"
const kimiMessages = this.#agent === "kimi" ? createKimiMessageProjection<AcpRuntimeEvent>() : null;
const normalizeMessage = piMessages ? piMessages.normalize : kimiMessages ? kimiMessages.normalize : this.#agent === "grok"
? createGrokMessageNormalizer<AcpRuntimeEvent>() : (event: AcpRuntimeEvent) => event;
for await (const event of turn.events) {
toolEvidence?.tool(event);
@@ -1490,7 +1492,7 @@ class CodexAcpxSession implements HarnessSession {
this.#mapRuntimeEvent(normalizeMessage(normalizeToolEvent(projected)), turnId, ++index);
}
const result = await turn.result;
if (result.status === "completed") piMessages?.settle();
if (result.status === "completed") { piMessages?.settle(); kimiMessages?.settle(); }
await drainExtensions();
const usageAfter = await readUsageStatus(this.#host);
// Diagnostic projection failures cannot replace the provider's terminal result.
@@ -1676,12 +1678,12 @@ class CodexAcpxSession implements HarnessSession {
}
#assistantItemId(turnId: string, messageId: string | null): string {
if (this.#agent !== "copilot") return `${turnId}:assistant-message`;
if (this.#agent !== "copilot" && this.#agent !== "kimi") return `${turnId}:assistant-message`;
return messageId ? `${turnId}:assistant-message:${messageId}` : `${turnId}:assistant-activity`;
}
#mapRuntimeEvent(
event: PiProjectedMessageEvent<AcpRuntimeEvent>,
event: PiProjectedMessageEvent<KimiProjectedMessageEvent<AcpRuntimeEvent>>,
turnId: string,
index: number,
): void {
@@ -1691,10 +1693,15 @@ class CodexAcpxSession implements HarnessSession {
const isReasoning =
event.stream === "thought" || event.tag === "agent_thought_chunk";
const messageId = typeof event.messageId === "string" && event.messageId ? event.messageId : null;
let kimiCommentary: { text: string; itemId: string } | undefined;
// The pinned Copilot mapper supplies actual native identity, including
// empty starts. Unidentified session info/warnings remain activity only.
if (!isReasoning && !event.piMessageHistory && (this.#agent !== "copilot" || messageId)) {
if (piBoundaryClearsFinal(event)) this.#assistantText = "";
if (!isReasoning && !event.piMessageHistory && !event.assistantMessageHistory && (this.#agent !== "copilot" || messageId)) {
const kimiClears = this.#agent === "kimi" && kimiMessageBoundaryClears(event);
if (kimiClears && this.#assistantText) {
kimiCommentary = { text: this.#assistantText, itemId: this.#assistantItemId(turnId, this.#assistantMessageId) };
}
if (piBoundaryClearsFinal(event) || kimiClears) this.#assistantText = "";
if (messageId && this.#assistantMessageId && messageId !== this.#assistantMessageId) {
if (this.#agent === "copilot" && this.#assistantText) {
this.#emit("item.completed", { kind: "agentMessage", channel: "commentary", text: this.#assistantText },
@@ -1722,6 +1729,8 @@ class CodexAcpxSession implements HarnessSession {
: this.#assistantItemId(turnId, messageId),
},
);
if (kimiCommentary) this.#emit("item.completed", { kind: "agentMessage", channel: "commentary", text: kimiCommentary.text },
{ turnId, itemId: kimiCommentary.itemId });
}
if (event.type === "status" && event.tag === "usage_update") {
this.#usage = boundedRecord({
@@ -1,3 +1,6 @@
import { admitAcpxSessionContext } from "./session-context-admission.js";
import { createKimiInstructionAdmission } from "./kimi-instructions.js";
import { kimiReadOnlyPermissionAllowed } from "./kimi-config.js";
import { resolveGrokDisableWebSearch } from "./grok-web-search.js";
import { resolveGrokEffort, createGrokEffortAdmission } from "./grok-effort.js";
import { resolveClaudeEffort, createClaudeEffortAdmission } from "./claude-effort.js";
@@ -37,7 +40,6 @@ import { AcpxApprovalRequiredError, decideAcpxPermission } from "./permission-po
import { ACPX_CAPABILITY_PROFILES } from "./capability-profiles.js";
import { createCursorInstructionAdmission } from "./cursor-instructions.js";
import { createAntigravityAdmission } from "./antigravity-admission.js";
import { admitAcpxSessionContext } from "./session-context-admission.js";
import { createAcpxModeBinding } from "./provider-mode.js";
import { assertCopilotPromptPolicy, createCopilotProtocolGuard } from "./copilot-policy.js";
@@ -326,17 +328,19 @@ export async function openQualifiedAcpxRuntime(
const grokDisableWebSearch = resolveGrokDisableWebSearch(options.profile.agent, options.grokDisableWebSearch);
const effortAdmission = selectedClaudeEffort ? createClaudeEffortAdmission(selectedClaudeEffort)
: selectedGrokEffort ? createGrokEffortAdmission(selectedGrokEffort, options.profile.reportedModelId) : undefined;
const cursorInstructions = options.profile.agent === "cursor"
const instructionAdmission = options.profile.agent === "kimi"
? createKimiInstructionAdmission(options.systemInstructions)
: options.profile.agent === "cursor"
? createCursorInstructionAdmission(options.systemInstructions)
: null;
const antigravityAdmission = options.profile.agent === "antigravity"
? createAntigravityAdmission(options.systemInstructions, options.profile.reportedModelId)
: null;
const contextAdmission = cursorInstructions ?? antigravityAdmission;
const contextAdmission = instructionAdmission ?? antigravityAdmission;
const runtimeOptions: GoalAwareAcpRuntimeOptions = {
cwd: options.cwd,
...(cursorInstructions || modeBinding ? { protocolGuardFactory: () => {
const instructions = cursorInstructions?.createGuard();
...(instructionAdmission || modeBinding ? { protocolGuardFactory: () => {
const instructions = instructionAdmission?.createGuard();
const mode = modeBinding?.createGuard();
return (direction: "inbound" | "outbound", message: unknown) => { instructions?.(direction, message); mode?.(direction, message); };
} } : {}),
@@ -412,6 +416,8 @@ export async function openQualifiedAcpxRuntime(
}
if ((permissionBoundary.hasAdmittedTurn && !extensionBoundary.active)
|| extensionBoundary.active?.signal.aborted || context.signal.aborted) return { outcome: "cancel" };
if (options.profile.agent === "kimi" && options.readOnly === true
&& !kimiReadOnlyPermissionAllowed(request, runnerOwnedMcpServerNames)) return { outcome: "reject_once" };
const disposition = decideAcpxPermission(
options.profile.agent,
options.permissionMode,
@@ -532,7 +538,7 @@ export async function openQualifiedAcpxRuntime(
// Its supported exact-model control forces session/load and the
// current instruction acknowledgement before host admission.
if (!runtime.setConfigOption) {
throw new Error("ACP context admission requires exact model configuration");
throw new Error("Cold instruction admission requires exact model configuration");
}
await runtime.setConfigOption({
handle: ensuredHandle, key: "model", value: options.profile.reportedModelId,
@@ -1,52 +1,2 @@
import { redactPaperclipSemanticValue } from "../../semantic-tools/redaction.js";
import { safeAcpxLocations } from "./safe-locations.js";
import { lstatSync, realpathSync } from "node:fs";
import { basename, dirname, isAbsolute, join } from "node:path";
export const COPILOT_PERMISSION_CONTEXT_CONTRACT = "copilot-edit-permission-context-v1";
const record = (value: unknown): Record<string, unknown> =>
value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
/** Native providers can spell an absolute workspace path through a host alias
* (macOS /tmp and /private/tmp). Attest the real entry, or its existing parent
* for a new file, before converting it to a display target. */
function aliasedLocation(path: string, cwd: string): string | undefined {
if (!isAbsolute(path)) return;
try {
const canonicalCwd = realpathSync(cwd);
let canonicalPath: string;
try {
lstatSync(path);
canonicalPath = realpathSync(path);
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") return;
// A dangling symlink must not become an apparently safe create target.
try { lstatSync(path); return; } catch (missing) {
if ((missing as NodeJS.ErrnoException).code !== "ENOENT") return;
}
canonicalPath = join(realpathSync(dirname(path)), basename(path));
}
return safeAcpxLocations([{ path: canonicalPath }], canonicalCwd, "edit")[0]?.path as string | undefined;
} catch { return; }
}
/** Display context only; this does not authorize filesystem access. */
export function safeCopilotEditTarget(value: unknown, workingDirectory: string | undefined): string | undefined {
const call = record(value);
if (call.kind !== "edit" || !workingDirectory) return;
const input = record(call.rawInput);
if (call.locations !== undefined && !Array.isArray(call.locations)) return;
if (Array.isArray(call.locations) && (call.locations.length > 16 || call.locations.some(x => typeof record(x).path !== "string"))) return;
const paths = [input.path, input.fileName, ...(Array.isArray(call.locations) ? call.locations.map(x => record(x).path) : [])].filter(x => x !== undefined);
if (!paths.length || paths.some(x => typeof x !== "string" || !x || x.trim() !== x || x.length > 2048 || /[\\\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(x))) return;
const safe = paths.flatMap(path => {
const locations = safeAcpxLocations([{ path }], workingDirectory, call.kind, call.title);
if (locations.length) return locations;
const aliased = aliasedLocation(path as string, workingDirectory);
return aliased ? [{ path: aliased }] : [];
});
const names = safe.map(x => x.path).filter((x): x is string => typeof x === "string" && x.length <= 1024 && x.trim() === x && !x.includes(":"));
if (names.length !== paths.length || new Set(names).size !== 1) return;
const target = names[0];
return target && redactPaperclipSemanticValue(target) === target ? target : undefined;
}
export { safeAcpxEditTarget as safeCopilotEditTarget } from "./acp-edit-context.js";
@@ -16,7 +16,7 @@ import {
import { ACPX_CAPABILITY_PROFILES } from "./capability-profiles.js";
const ACPX_AGENTS = ["claude", "codex", "grok", "pi", "cursor", "copilot", "antigravity"] as const;
const ACPX_AGENTS = ["claude", "codex", "grok", "pi", "cursor", "copilot", "kimi", "antigravity"] as const;
const ACPX_PERMISSION_MODES = [
"approve-all",
"approve-paperclip",
@@ -113,7 +113,7 @@ export function validateAcpxDriverConfig(
return invalid(
"agent",
"invalid_agent",
"ACPX agent must be claude, codex, grok, cursor, copilot, pi, or antigravity.",
"ACPX agent must be claude, codex, grok, cursor, copilot, kimi, pi, or antigravity.",
);
}
if (ACPX_CAPABILITY_PROFILES[agent].qualification !== "qualified") {
@@ -44,9 +44,9 @@ describe("ACPX launch environment", () => {
expect(launch.ACPX_CLAUDE_INCLUDE_USER_SETTINGS).toBeUndefined();
expect(environment.CLAUDE_CONFIG_DIR).toBe(configured);
});
it("retains Runner notification discovery while denying task-supplied overrides", () => {
it.each(["claude", "kimi"] as const)("retains %s Runner notification discovery while denying task-supplied overrides", agent => {
for (const key of ["PAPERCLIP_RUNTIME_NOTIFICATION_CONTEXT", "PAPERCLIP_RUNTIME_NOTIFICATION_HELPER"]) {
expect(createSanitizedAcpxSpawnInput({ [key]: "/private/runner/context" }, "claude").env[key]).toBe("/private/runner/context");
expect(createSanitizedAcpxSpawnInput({ [key]: "/private/runner/context" }, agent).env[key]).toBe("/private/runner/context");
expect(configuredEnvironmentProjection({ [key]: "/caller/override" })).not.toHaveProperty(key);
}
});
@@ -137,7 +137,9 @@ describe("ACPX launch environment", () => {
it.each([
["pi", "OPENROUTER_API_KEY"], ["cursor", "CURSOR_API_KEY"],
["cursor", "CURSOR_AUTH_TOKEN"], ["copilot", "COPILOT_GITHUB_TOKEN"], ["antigravity", "GEMINI_API_KEY"],
["cursor", "CURSOR_AUTH_TOKEN"], ["copilot", "COPILOT_GITHUB_TOKEN"],
["kimi", "KIMI_API_KEY"],
["antigravity", "GEMINI_API_KEY"],
] as const)("requires an explicit %s credential binding for %s", (agent, name) => {
vi.stubEnv(name, "ambient-secret");
expect(createSanitizedAcpxSpawnInput(undefined, agent).env[name]).toBeUndefined();
@@ -157,9 +159,33 @@ describe("ACPX launch environment", () => {
expect(createSanitizedAcpxSpawnInput(source, "pi").env).toEqual({ COPILOT_GITHUB_TOKEN: "copilot-key" });
});
it("strips configured Kimi model redirects and home overrides while retaining selected task and MCP values", () => {
const overrides = {
KIMI_MODEL_NAME: "__kimi_env_model__", KIMI_MODEL_API_KEY: "unselected-model-key",
KIMI_MODEL_BASE_URL: "https://unselected.invalid/v1", KIMI_MODEL_PROVIDER_TYPE: "openai_legacy",
KIMI_MODEL_FUTURE_OVERRIDE: "cannot-bypass-the-prefix-check", KIMI_SECONDARY_MODEL: "other-model",
KIMI_CODE_HOME: "/unselected/home",
};
const source = { ...configuredEnvironmentProjection({ ...overrides, CUSTOM_SETTING: "allowed" }),
KIMI_API_KEY: "selected-code-key", PAPERCLIP_NATIVE_MCP_NAME: "assigned",
PAPERCLIP_NATIVE_MCP_URL: "https://assigned.example/mcp", PAPERCLIP_NATIVE_MCP_TOKEN: "controller-only" };
const binding = createAcpxCredentialBinding(source, "kimi", "session-1")!;
const sidecar = createAcpxSidecarHostEnvironment({ ...source, [ACPX_CREDENTIAL_BINDING_ENV]: binding }, "kimi", "session-1");
for (const environment of [sidecar, createSanitizedAcpxSpawnInput(source, "kimi").env,
createSanitizedAcpxSpawnInput(sidecar, "kimi").env]) {
for (const name of Object.keys(overrides)) expect(environment).not.toHaveProperty(name);
expect(environment).toMatchObject({ KIMI_API_KEY: "selected-code-key", CUSTOM_SETTING: "allowed",
PAPERCLIP_NATIVE_MCP_NAME: "assigned", PAPERCLIP_NATIVE_MCP_URL: "https://assigned.example/mcp" });
expect(JSON.parse(environment[CONFIGURED_ENVIRONMENT_KEYS]!)).toEqual(["CUSTOM_SETTING"]);
}
expect(createSanitizedAcpxSpawnInput(source, "kimi").env).not.toHaveProperty("PAPERCLIP_NATIVE_MCP_TOKEN");
});
it.each([
["pi", "OPENROUTER_API_KEY"], ["cursor", "CURSOR_API_KEY"],
["cursor", "CURSOR_AUTH_TOKEN"], ["copilot", "COPILOT_GITHUB_TOKEN"], ["antigravity", "GEMINI_API_KEY"],
["cursor", "CURSOR_AUTH_TOKEN"], ["copilot", "COPILOT_GITHUB_TOKEN"],
["kimi", "KIMI_API_KEY"],
["antigravity", "GEMINI_API_KEY"],
] as const)("admits only session-bound %s sidecar credentials for %s", (agent, name) => {
const source = { [name]: "task-secret" };
const binding = createAcpxCredentialBinding(source, agent, "session-1");
@@ -10,6 +10,7 @@ export const ACPX_CREDENTIAL_BINDING_ENV = "PAPERCLIP_ACPX_CREDENTIAL_BINDING";
export const ACPX_CREDENTIAL_NAMES: Readonly<Record<QualifiedAcpxAgent, readonly string[]>> = {
antigravity: ["GEMINI_API_KEY", "PAPERCLIP_ACPX_ANTIGRAVITY_AUTH_JSON_SECRET"],
grok: ["XAI_API_KEY"],
kimi: ["KIMI_API_KEY"],
pi: PI_CREDENTIAL_NAMES,
cursor: ["CURSOR_API_KEY", "CURSOR_AUTH_TOKEN"],
copilot: ["COPILOT_GITHUB_TOKEN"],
@@ -27,7 +28,17 @@ function removePiIamCredentials(environment: NodeJS.ProcessEnv): void {
}
}
const isCandidate = (agent: QualifiedAcpxAgent) => agent === "pi" || agent === "cursor" || agent === "copilot" || agent === "antigravity";
function removeKimiRuntimeOverrides(environment: NodeJS.ProcessEnv): void {
const isOverride = (name: string) => /^KIMI_MODEL_/.test(name.toUpperCase())
|| ["KIMI_SECONDARY_MODEL", "KIMI_CODE_HOME"].includes(name.toUpperCase());
const names = configuredEnvironmentKeys(environment);
for (const name of Object.keys(environment)) if (isOverride(name)) delete environment[name];
if (environment[CONFIGURED_ENVIRONMENT_KEYS] !== undefined) {
environment[CONFIGURED_ENVIRONMENT_KEYS] = JSON.stringify(names.filter(name => !isOverride(name)));
}
}
const isCandidate = (agent: QualifiedAcpxAgent) => agent === "pi" || agent === "cursor" || agent === "copilot" || agent === "kimi" || agent === "antigravity";
/** Mint only at the controller's explicit task-environment boundary, never by copying a marker. */
export function createAcpxCredentialBinding(
@@ -79,6 +90,7 @@ export function createAcpxSidecarHostEnvironment(
}
}
if (agent === "pi") removePiIamCredentials(result);
if (agent === "kimi") removeKimiRuntimeOverrides(result);
if (agent === "antigravity") sanitizeAntigravityEnvironment(result);
return result;
}
@@ -164,6 +176,7 @@ export function createSanitizedAcpxSpawnInput(
result[key] = value;
}
if (agent === "pi") removePiIamCredentials(result);
if (agent === "kimi") removeKimiRuntimeOverrides(result);
if (agent === "antigravity") sanitizeAntigravityEnvironment(result);
// The controller-to-sidecar document is staged under a lifetime fence. The
// native child receives the private token file, never its launch document.
@@ -95,6 +95,19 @@ export const QUALIFIED_ACPX_PROFILE_DATA = {
"agentRuntimeVersion": "0.160.0",
"commandDigest": "sha256:443491f4e814501a5dc47347e4510e84faeec9c9382e2d4e53bfe69386d5aabc",
"permissionPolicy": "interactive"
},
"kimi": {
"driverKind": "acpx_runtime",
"protocolVersion": 1,
"acpxVersion": "0.13.1",
"agent": "kimi",
"agentProfileVersion": 17,
"agentServerPackage": "@moonshot-ai/kimi-code",
"agentServerVersion": "2.3.3",
"agentRuntimePackage": null,
"agentRuntimeVersion": null,
"commandDigest": "sha256:6ee5fb7ac18c1361ac63effe9117d08ed129add8349756a140e73e0c86ba5da7",
"permissionPolicy": "interactive"
}
} as const;
@@ -0,0 +1,12 @@
export const KIMI_RUNTIME_VERSION = "2.3.3" as const;
export const KIMI_NODE_VERSION = "24.21.0" as const;
export const KIMI_DISTRIBUTION_LOCK_SHA256 = "87f854ddd7f92254ccd8596b85962684ec176f92e93863771843bf416825f16b";
export const KIMI_NODE_DISTRIBUTIONS = Object.freeze({
"darwin-arm64": Object.freeze({ archiveSha256: "bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057", executableSha256: "e4b5a3af0e05c75de2eae013904145f40fe7fc2a6e6f17510128bf45cca4e79b", executableSize: 122129232 }),
"linux-x64": Object.freeze({ archiveSha256: "6e1db87ef58b8819e5d5402eff1536491b18edd8eb7bee5ef7897876e88dc5ff", executableSha256: "7fde7b8afa198da66257f42ee2001d874c7355631e6d1579a5fb5ef1f246df4c", executableSize: 126595440 }),
});
export const KIMI_DISTRIBUTION_CLOSURE_SHA256 = Object.freeze({
"darwin-arm64": "6038325abd0431c30097ff1f572a9b29816e1b4da372d8cb86b2ca5d18ea34e7",
"linux-x64": "105d8394adbbc76a953a12cc51b5288eb940ad693291e584f6cdc6407213164d",
});
@@ -0,0 +1,44 @@
export function kimiManagedConfiguration(model: string): string {
if (!model.trim() || model.includes("\0") || Buffer.byteLength(model) > 512) throw new Error("Kimi Code requires a bounded explicit model ID");
const quote = JSON.stringify;
return [
`default_model = ${quote(model)}`,
"telemetry = false",
"builtin_product_skills = false",
"merge_all_available_skills = false",
"hooks = []",
"[providers.paperclip]",
'type = "kimi"',
'base_url = "https://api.kimi.ai/coding/v1"',
'api_key_env = "KIMI_API_KEY"',
`[models.${quote(model)}]`,
'provider = "paperclip"',
`model = ${quote(model)}`,
"max_context_size = 262144",
'capabilities = ["tool_use"]',
"[tools]",
'disabled = ["Agent", "AgentSwarm", "TaskList", "TaskOutput", "TaskStop", "CronCreate", "CronList", "CronDelete", "CreateGoal", "GetGoal", "SetGoalBudget", "UpdateGoal", "AskUserQuestion", "EnterPlanMode", "ExitPlanMode"]',
"[background]",
"keep_alive_on_exit = false",
"bash_auto_background_on_timeout = false",
"[watch]",
"enabled = false",
"",
].join("\n");
}
export function kimiReadOnlyPermissionAllowed(request: unknown, runnerOwnedMcpServerNames: ReadonlySet<string>): boolean {
if (!request || typeof request !== "object") return false;
const raw = (request as { raw?: unknown }).raw;
if (!raw || typeof raw !== "object") return false;
const call = (raw as { toolCall?: unknown }).toolCall;
if (!call || typeof call !== "object") return false;
const title = (call as { title?: unknown }).title;
const input = (call as { rawInput?: unknown }).rawInput;
if (typeof title !== "string" || !input || typeof input !== "object" || Array.isArray(input)) return false;
for (const name of runnerOwnedMcpServerNames) {
const prefix = `mcp__${name}__`;
if (title.startsWith(prefix) && title.length > prefix.length) return true;
}
return false;
}
@@ -0,0 +1,126 @@
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import { mkdir, mkdtemp, realpath, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { assertKimiInstallationProfile, verifyKimiInstallation } from "./kimi-installation.js";
import { QUALIFIED_ACPX_PROFILES } from "./qualified-profiles.js";
import { isSupportedAcpxProfileVersion } from "./profile-compatibility.js";
const roots: string[] = [];
const previous = new Map<string, string | undefined>();
function environment(key: string, value: string) {
if (!previous.has(key)) previous.set(key, process.env[key]);
process.env[key] = value;
}
afterEach(async () => {
for (const [key, value] of previous) { if (value === undefined) delete process.env[key]; else process.env[key] = value; }
previous.clear();
await Promise.all(roots.splice(0).map(root => rm(root, { recursive: true, force: true })));
});
describe("Kimi installed runtime admission", () => {
it("rejects caller-selected package/version/command changes before filesystem resolution", () => {
const profile = QUALIFIED_ACPX_PROFILES.kimi;
expect(() => assertKimiInstallationProfile(profile)).not.toThrow();
for (const alteration of [{ agentServerVersion: "latest" }, { agentServerPackage: "foreign" }, { commandDigest: `sha256:${"f".repeat(64)}` }, { agentProfileVersion: profile.agentProfileVersion + 1 }]) {
expect(() => assertKimiInstallationProfile({ ...profile, ...alteration })).toThrow(/trusted declaration/);
}
});
it("retains pending profile 10 while binding the merged entrypoint builder in profile 11", () => {
const bytes = readFileSync(new URL("../../../test/fixtures/kimi-profile-v10-identity.json", import.meta.url));
expect(createHash("sha256").update(bytes).digest("hex")).toBe("b859f4aefcc36fc5e75b3aeeb02135ca9f7acc84866c856a3939effdd13cb358");
const prior = JSON.parse(bytes.toString("utf8"));
const current = JSON.parse(readFileSync(new URL("../../../test/fixtures/kimi-profile-v11-identity.json", import.meta.url), "utf8"));
expect(Object.keys(current.declaration.sources).filter(key => current.declaration.sources[key] !== prior.declaration.sources[key])).toEqual([
"scripts/build-verified-provider-entrypoints.mjs",
]);
expect(() => assertKimiInstallationProfile({ ...QUALIFIED_ACPX_PROFILES.kimi,
agentProfileVersion: 10, commandDigest: prior.commandDigest })).toThrow(/trusted declaration/);
});
it("preserves historical profile 11 while binding the reconciled authority and event boundaries in 13", () => {
const bytes = readFileSync(new URL("../../../test/fixtures/kimi-profile-v11-identity.json", import.meta.url));
expect(createHash("sha256").update(bytes).digest("hex")).toBe("17b3db5659cfbaa4d3f5dddd8a97ffeab38353795aa4dbfa6c6fe1520f415ab1");
const prior = JSON.parse(bytes.toString("utf8"));
const current = JSON.parse(readFileSync(new URL("../../../test/fixtures/kimi-profile-v13-identity.json", import.meta.url), "utf8"));
expect(prior.declaration.agentProfileVersion).toBe(11);
expect(Object.keys(prior.declaration.sources)).toHaveLength(31);
expect(current.declaration.agentProfileVersion).toBe(13);
expect(Object.keys(current.declaration.sources)).toHaveLength(55);
expect(Object.hasOwn(current.declaration.sources, "src/drivers/acpx/candidate-admission.ts")).toBe(true);
expect(Object.hasOwn(current.declaration.sources, "runner/crates/runner-core/src/provider_events.rs")).toBe(true);
expect(isSupportedAcpxProfileVersion("kimi", 11)).toBe(true);
expect(() => assertKimiInstallationProfile({ ...QUALIFIED_ACPX_PROFILES.kimi,
agentProfileVersion: 11, commandDigest: prior.commandDigest })).toThrow(/trusted declaration/);
});
it("decodes pending14 history without installing it as the repaired pending15 runtime", () => {
const bytes = readFileSync(new URL("../../../test/fixtures/kimi-profile-v14-identity.json", import.meta.url));
expect(createHash("sha256").update(bytes).digest("hex")).toBe("13ce8e301e6b7d622f4b3ffdca7981e18538e7a5ca2f1b2208ec466259469e7b");
const prior = JSON.parse(bytes.toString("utf8"));
const current = JSON.parse(readFileSync(new URL("../../../test/fixtures/kimi-profile-v15-identity.json", import.meta.url), "utf8"));
expect(current.declaration.agentProfileVersion).toBe(15);
expect(Object.keys(current.declaration.sources)).toHaveLength(55);
expect(Object.keys(current.declaration.sources).filter(key => current.declaration.sources[key] !== prior.declaration.sources[key])).toEqual([
"runner/crates/runner-core/src/acpx_provider_backend.rs",
]);
expect(isSupportedAcpxProfileVersion("kimi", 14)).toBe(true);
expect(() => assertKimiInstallationProfile({ ...QUALIFIED_ACPX_PROFILES.kimi,
agentProfileVersion: 14, commandDigest: prior.commandDigest })).toThrow(/trusted declaration/);
expect(() => assertKimiInstallationProfile(QUALIFIED_ACPX_PROFILES.kimi)).not.toThrow();
});
it("decodes pending15 history without installing it as merged pending16", () => {
const bytes = readFileSync(new URL("../../../test/fixtures/kimi-profile-v15-identity.json", import.meta.url));
expect(createHash("sha256").update(bytes).digest("hex")).toBe("5221f6f9f64c02ca7ee68b185e08ed46b1c82a13037b001854065b9fad28ff87");
const prior = JSON.parse(bytes.toString("utf8"));
const current = JSON.parse(readFileSync(new URL("../../../test/fixtures/kimi-profile-v16-identity.json", import.meta.url), "utf8"));
expect(current.declaration.agentProfileVersion).toBe(16);
expect(current.declaration.qualificationStatus).toBe("pending");
expect(Object.keys(current.declaration.sources)).toHaveLength(58);
expect(isSupportedAcpxProfileVersion("kimi", 15)).toBe(true);
expect(() => assertKimiInstallationProfile({ ...QUALIFIED_ACPX_PROFILES.kimi,
agentProfileVersion: 15, commandDigest: prior.commandDigest })).toThrow(/trusted declaration/);
expect(() => assertKimiInstallationProfile(QUALIFIED_ACPX_PROFILES.kimi)).not.toThrow();
});
it("decodes pending16 history without installing it as ordinary released17", () => {
const bytes = readFileSync(new URL("../../../test/fixtures/kimi-profile-v16-identity.json", import.meta.url));
expect(createHash("sha256").update(bytes).digest("hex")).toBe("69e5fb7e7c445be7eee37bf7e6a8fa135cf2f88e0a997a95065ca79526e91470");
const prior = JSON.parse(bytes.toString("utf8"));
expect(prior.declaration.qualificationStatus).toBe("pending");
expect(isSupportedAcpxProfileVersion("kimi", 16)).toBe(true);
expect(QUALIFIED_ACPX_PROFILES.kimi.agentProfileVersion).toBe(17);
expect(QUALIFIED_ACPX_PROFILES.kimi.qualificationStatus).toBeUndefined();
expect(() => assertKimiInstallationProfile({ ...QUALIFIED_ACPX_PROFILES.kimi,
agentProfileVersion: 16, commandDigest: prior.commandDigest })).toThrow(/trusted declaration/);
expect(() => assertKimiInstallationProfile(QUALIFIED_ACPX_PROFILES.kimi)).not.toThrow();
});
it("preserves the source-bound released17 identity without accepting its old digest", () => {
const bytes = readFileSync(new URL("../../../test/fixtures/kimi-profile-v17-identity.json", import.meta.url));
expect(createHash("sha256").update(bytes).digest("hex")).toBe("c3b6a1bf95e7de1abf7a07f042b5dbe9c10d1a3ae2007a886b6387d779a7a205");
const historical = JSON.parse(bytes.toString("utf8"));
expect(historical.declaration.agentProfileVersion).toBe(17);
expect(QUALIFIED_ACPX_PROFILES.kimi.agentProfileVersion).toBe(17);
expect(QUALIFIED_ACPX_PROFILES.kimi.commandDigest).not.toBe(historical.commandDigest);
expect(() => assertKimiInstallationProfile({ ...QUALIFIED_ACPX_PROFILES.kimi,
commandDigest: historical.commandDigest })).toThrow(/trusted declaration/);
expect(() => assertKimiInstallationProfile(QUALIFIED_ACPX_PROFILES.kimi)).not.toThrow();
});
it("gives the normal setup command and never falls back to ambient kimi when assets are missing", async () => {
const root = await realpath(await mkdtemp(join(tmpdir(), "paperclip-kimi-installation-test-"))); roots.push(root);
await writeFile(join(root, "package.json"), '{"name":"@paperclipai/paperclip-runner"}');
environment("PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT", root);
environment("PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST", join(root, "package.json"));
await expect(verifyKimiInstallation(QUALIFIED_ACPX_PROFILES.kimi)).rejects.toThrow(/paperclipai runtime setup kimi/);
const assets = join(root, "provider-assets/kimi", `${process.platform}-${process.arch}`);
await mkdir(join(assets, "runtime"), { recursive: true });
await writeFile(join(assets, "kimi-distribution.json"), JSON.stringify({ schema: "paperclip.kimi-distribution.v1", nativeClosureSha256: "f".repeat(64) }));
await expect(verifyKimiInstallation(QUALIFIED_ACPX_PROFILES.kimi)).rejects.toThrow(/source-owned pin/);
});
});
Loaded 100 of 484 files, more files were not shown because too many files have changed in this diff. Show more