mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-31 22:24:16 +01:00
feat(runner): add pinned Kimi Code native runtime (#18028)
Add the pinned Kimi Code runtime, native instruction and model admission, scoped credentials, and pending-candidate recovery contracts. Retain current master release attestations and provider-pack compatibility. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
909e29dd07
commit
b866fa4bdc
111 files changed
+7180
-249
No files matched your search
@@ -48,3 +48,11 @@ packages/grok-acp/bin
|
||||
# Images build their pinned Copilot assets instead of copying host binaries.
|
||||
/packages/paperclip-runner/provider-assets/
|
||||
/server/provider-assets/
|
||||
|
||||
!packages/paperclip-runner/test/fixtures/kimi-profile-v*-identity.json
|
||||
|
||||
!packages/paperclip-runner/test/fixtures/grok-profile-v*-identity.json
|
||||
|
||||
!packages/paperclip-runner/test/fixtures/copilot-profile-v*-identity.json
|
||||
|
||||
!packages/paperclip-runner/test-fixtures/pi-acp/profile-v34-identity.json
|
||||
@@ -12,6 +12,10 @@ import {
|
||||
} from "./paperclip-runner-permissions.js";
|
||||
|
||||
describe("Paperclip Runner permission defaults", () => {
|
||||
it("keeps Kimi Code pending with an explicit runtime credential", () => {
|
||||
expect(PAPERCLIP_RUNNER_ACPX_PROFILES.find(profile => profile.value === "kimi"))
|
||||
.toMatchObject({ qualified: false, 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: false, credentialEnvironment: ["KIMI_API_KEY"] },
|
||||
{ value: "antigravity", label: "Google Antigravity", qualified: false, credentialEnvironment: ["GEMINI_API_KEY"] },
|
||||
] as const);
|
||||
|
||||
|
||||
@@ -981,6 +981,34 @@ 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 candidate
|
||||
|
||||
Kimi Code uses the pinned `@moonshot-ai/kimi-code@2.3.3` distribution through
|
||||
ACPX. Candidate support is pending local and Daytona Product E2E qualification.
|
||||
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 ordinary hiring path stays hidden while qualification is pending. 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,16 @@
|
||||
"agentRuntimeVersion": "0.160.0",
|
||||
"commandDigest": "sha256:443491f4e814501a5dc47347e4510e84faeec9c9382e2d4e53bfe69386d5aabc",
|
||||
"requiresProviderPolicy": false
|
||||
},
|
||||
"kimi": {
|
||||
"agentProfileVersion": 16,
|
||||
"agentServerPackage": "@moonshot-ai/kimi-code",
|
||||
"agentServerVersion": "2.3.3",
|
||||
"agentRuntimePackage": null,
|
||||
"agentRuntimeVersion": null,
|
||||
"commandDigest": "sha256:0c1e6ce460407ec973a37a2d192d665fcde744e2b9875c791c90308afbf4c52b",
|
||||
"requiresProviderPolicy": true,
|
||||
"qualificationStatus": "pending"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,11 @@ 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 keeps revision 16 from its pending candidate history. Its release
|
||||
attestation replaces the earlier source-bound digest without advancing that
|
||||
revision. Historical candidate records stay readable, while exact current
|
||||
digest admission refuses their old launch identities. Kimi remains pending.
|
||||
|
||||
## External controller checkpoints
|
||||
|
||||
The optional controller persistence callback stores recovery control state, not
|
||||
|
||||
@@ -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",
|
||||
|
||||
+215
-12
@@ -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,163 @@ 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 kimi_candidate_requires_exact_two_platform_closures_scope_and_time() {
|
||||
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;
|
||||
candidate.validate_at(now, os, host_arch).unwrap();
|
||||
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 kimi_next_run_rebinds_only_the_authenticated_run_identity() {
|
||||
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();
|
||||
current
|
||||
.validate_next_run(
|
||||
&next,
|
||||
"kimi",
|
||||
"kimi-for-coding",
|
||||
"kimi-next-run",
|
||||
"kimi-session",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(current
|
||||
.validate_next_run(
|
||||
¤t,
|
||||
"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,510 @@ 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 kimi_candidate_attachment_atomically_rebinds_exact_grant_and_profile_digest() {
|
||||
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: Some(grant.clone()),
|
||||
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 = grant.clone();
|
||||
next.run_id = "run-2".to_owned();
|
||||
let mut provider = value;
|
||||
provider["runId"] = json!("run-2");
|
||||
let payload = json!({"provider":provider,"paperclipNextAuthority":{
|
||||
"identity":{"runId":"run-2","normalizedSessionId":"session-1"},"candidateAdmission":next
|
||||
}});
|
||||
for (field, changed) in [
|
||||
("companyId", json!("foreign")),
|
||||
("model", json!("wrong")),
|
||||
("expiresAt", json!(grant.expires_at + 1)),
|
||||
("runId", json!("unbound")),
|
||||
] {
|
||||
let mut invalid = payload.clone();
|
||||
invalid["paperclipNextAuthority"]["candidateAdmission"][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_eq!(profile.candidate_admission.as_ref().unwrap(), &next);
|
||||
assert_ne!(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_eq!(launch.candidate_admission.as_ref().unwrap(), &next);
|
||||
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) {
|
||||
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: Some(grant.clone()),
|
||||
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();
|
||||
let next_grant = next_config
|
||||
.acpx_launch_profile
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.candidate_admission
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
next_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,
|
||||
)
|
||||
}
|
||||
|
||||
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 kimi_candidate_governed_wait_cold_restore_uses_authenticated_next_startup() {
|
||||
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("authorized cold Kimi restore must retain the settled prior session until authenticated attachment");
|
||||
assert!(recovered.session.is_none());
|
||||
assert!(recovered.poll_events().unwrap().is_empty());
|
||||
assert!(!sentinel.exists());
|
||||
recovered.attach_run(&payload)
|
||||
.expect("cold replacement uses its already authenticated startup authority, not a second warm next-authority grant");
|
||||
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 kimi_candidate_cold_restore_rejects_changed_startup_authority_before_provider_open() {
|
||||
for field in [
|
||||
"company",
|
||||
"model",
|
||||
"expiry",
|
||||
"expired",
|
||||
"profile",
|
||||
"closure",
|
||||
"platform",
|
||||
"architecture",
|
||||
"session",
|
||||
"grant_run",
|
||||
"context_run",
|
||||
"authority",
|
||||
"command",
|
||||
"args",
|
||||
"artifact",
|
||||
"missing_grant",
|
||||
"saved_digest",
|
||||
] {
|
||||
let (directory, mut config, payload, before, sentinel) =
|
||||
kimi_cold_restore_fixture("kimi-candidate-cold-reject");
|
||||
match field {
|
||||
"context_run" => config.run_id = "foreign-run".into(),
|
||||
"missing_grant" => {
|
||||
config
|
||||
.acpx_launch_profile
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.candidate_admission = 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();
|
||||
let grant = profile.candidate_admission.as_mut().unwrap();
|
||||
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(),
|
||||
"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))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
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 kimi_candidate_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 kimi_candidate_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_eq!(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();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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");
|
||||
|
||||
@@ -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,129 @@ 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 kimi_candidate_handoff_binds_the_existing_authenticated_next_authority() {
|
||||
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",
|
||||
¤t.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: Some(grant.clone()),
|
||||
});
|
||||
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"},"candidateAdmission":next_grant
|
||||
}});
|
||||
let next = next_authority_config(&attach, ¤t).unwrap().unwrap();
|
||||
let actual = next
|
||||
.acpx_launch_profile
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.candidate_admission
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
assert_eq!(actual, &next_grant);
|
||||
assert_eq!(
|
||||
current
|
||||
.acpx_launch_profile
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.candidate_admission
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
&grant
|
||||
);
|
||||
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 = attach.clone();
|
||||
changed.payload["paperclipNextAuthority"]["candidateAdmission"][field] = value;
|
||||
assert!(
|
||||
next_authority_config(&changed, ¤t).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, ¤t).is_err(),
|
||||
"{field}"
|
||||
);
|
||||
}
|
||||
let mut missing = attach.clone();
|
||||
missing.payload["paperclipNextAuthority"]
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.remove("candidateAdmission");
|
||||
assert!(next_authority_config(&missing, ¤t).is_err());
|
||||
let mut no_prior = current.clone();
|
||||
no_prior
|
||||
.acpx_launch_profile
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.candidate_admission = None;
|
||||
assert!(next_authority_config(&attach, &no_prior).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:0c1e6ce460407ec973a37a2d192d665fcde744e2b9875c791c90308afbf4c52b",
|
||||
requires_provider_policy: true,
|
||||
qualification_pending: true,
|
||||
},
|
||||
_ => 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,
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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`);
|
||||
|
||||
@@ -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,7 +1,7 @@
|
||||
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"]);
|
||||
|
||||
/** Only materialized providers enter the serialized manifest and its digest. */
|
||||
export function providerPackManifestFields(providers, candidates) {
|
||||
@@ -34,6 +34,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,6 +43,27 @@ 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("Kimi stays explicit, pending and separate from released provider inventory", () => {
|
||||
const kimi = { version: "2.3.3", qualification: "pending", profileDigest: "sha256:profile", closureDigest: "sha256:closure" };
|
||||
for (const [platform, architecture] of [["darwin", "arm64"], ["linux", "x64"]]) {
|
||||
assert.equal(providerPackProviders(platform, architecture, []).includes("kimi"), false);
|
||||
assert.equal(providerPackProviders(platform, architecture, ["kimi"]).includes("kimi"), true);
|
||||
}
|
||||
const fields = providerPackManifestFields({ kimi }, ["kimi"]);
|
||||
assert.equal(fields.providers?.kimi, undefined);
|
||||
assert.equal(fields.candidateProviders.kimi, kimi);
|
||||
assert.equal(providerPackManifestFields({ kimi }, []).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"]]) {
|
||||
|
||||
@@ -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,112 @@
|
||||
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",
|
||||
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 unqualified = 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(unqualified.error); assert.equal(unqualified.status, 1);
|
||||
if (process.versions.node === "24.21.0" || node === pinnedNode) {
|
||||
assert.equal(JSON.parse(unqualified.stdout).code, "KIMI_QUALIFICATION_REQUIRED");
|
||||
}
|
||||
assert.doesNotMatch(unqualified.stdout + unqualified.stderr, /UNEXPECTED_NETWORK_OR_CHILD|selected-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,119 @@
|
||||
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 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 candidate = manifest.payload.candidateProviders?.kimi;
|
||||
assert.equal(candidate?.qualification, "pending");
|
||||
assert.equal(candidate.version, "2.3.3");
|
||||
assert.equal(candidate.path, `provider-assets/kimi/${process.platform}-${process.arch}`);
|
||||
assert.equal(manifest.payload.providers?.kimi, undefined);
|
||||
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"),
|
||||
]);
|
||||
assert.equal(candidate.profileDigest, QUALIFIED_ACPX_PROFILES.kimi.commandDigest);
|
||||
assert.equal(candidate.closureDigest, `sha256:${KIMI_DISTRIBUTION_CLOSURE_SHA256[`${process.platform}-${process.arch}`]}`);
|
||||
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: candidate.profileDigest, closureDigest: candidate.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,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,576 @@
|
||||
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 { tmpdir } from "node:os";
|
||||
import { 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 [{ verifyKimiInstallation }, { QUALIFIED_ACPX_PROFILES }, { kimiManagedConfiguration }, instructionModule, { createKimiMessageProjection }, { normalizeAcpxPermission }] = 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),
|
||||
]);
|
||||
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); 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 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; 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");
|
||||
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(); }
|
||||
}
|
||||
};
|
||||
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 = await verifyKimiInstallation(QUALIFIED_ACPX_PROFILES.kimi);
|
||||
const store = createFileSessionStore({ stateDir: join(root, "acpx-state") });
|
||||
const spawnNative = (lease, owner) => {
|
||||
const child = lease.spawn([], { cwd: workspace, stdio: "pipe", env: { PATH: "/usr/bin:/bin", HOME: home,
|
||||
LANG: "C.UTF-8", CI: "1", NO_COLOR: "1", TERM: "dumb", KIMI_CODE_HOME: home,
|
||||
KIMI_API_KEY: "simulated-peer-not-a-credential", KIMI_CODE_NO_AUTO_UPDATE: "1", KIMI_DISABLE_TELEMETRY: "1",
|
||||
KIMI_CODE_WATCH: "0", KIMI_DISABLE_CRON: "1", PAPERCLIP_KIMI_INSTRUCTIONS: binding.payload,
|
||||
PAPERCLIP_TASK_SCOPED: "assigned-task-canary" } });
|
||||
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 owner = { lease, 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 an assigned task variable, 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);
|
||||
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 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);
|
||||
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,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);
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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,4 +1,5 @@
|
||||
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";
|
||||
@@ -95,3 +96,59 @@ 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 => {
|
||||
expect(resolveQualifiedAcpxProfile("kimi", kimiExpected.model)).toMatchObject({ agentProfileVersion: 16, qualificationStatus: "pending" });
|
||||
expect(parseAcpxCandidateAdmission(kimiCandidate(target), kimiExpected, now)).toEqual(kimiCandidate(target));
|
||||
expect(() => assertAcpxProfileReleased("kimi")).toThrow("qualification is pending");
|
||||
expect(() => assertAcpxExecutionAdmission(resolveQualifiedAcpxProfile("kimi", kimiExpected.model))).toThrow("qualification is pending");
|
||||
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 startup = bindAcpxCandidateAdmission({ ...kimiCandidate(target), expiresAt: Date.now() + 60_000 },
|
||||
{ runId: "kimi-run", normalizedSessionId: "kimi-session" });
|
||||
const profile = resolveQualifiedAcpxProfile("kimi", kimiExpected.model);
|
||||
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 });
|
||||
expect(() => assertAcpxExecutionAdmission(profile, startup)).not.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: "pending", 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,44 @@ describe("Codex ACPX harness driver", () => {
|
||||
await session.close({ reason: "native attribution verified" });
|
||||
});
|
||||
|
||||
it.runIf(QUALIFIED_ACPX_PROFILES.kimi.qualificationStatus === "pending")("rejects pending Kimi before opening a direct runtime host", () => {
|
||||
const openHost = vi.fn();
|
||||
expect(() => driverFixture({ agent: "kimi", model: "kimi-for-coding", providerPolicy: { readOnly: true } }, { openHost }))
|
||||
.toThrow(expect.objectContaining({ code: "ACPX_PROFILE_QUALIFICATION_PENDING", retryable: false }));
|
||||
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,20 @@ 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": 16,
|
||||
"agentServerPackage": "@moonshot-ai/kimi-code",
|
||||
"agentServerVersion": "2.3.3",
|
||||
"agentRuntimePackage": null,
|
||||
"agentRuntimeVersion": null,
|
||||
"commandDigest": "sha256:0c1e6ce460407ec973a37a2d192d665fcde744e2b9875c791c90308afbf4c52b",
|
||||
"qualificationStatus": "pending",
|
||||
"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,110 @@
|
||||
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 the old pending revision while refusing its source-bound launch digest", () => {
|
||||
const historical = JSON.parse(readFileSync(new URL("../../../test/fixtures/kimi-profile-v16-identity.json", import.meta.url), "utf8"));
|
||||
expect(isSupportedAcpxProfileVersion("kimi", historical.declaration.agentProfileVersion)).toBe(true);
|
||||
expect(QUALIFIED_ACPX_PROFILES.kimi.agentProfileVersion).toBe(historical.declaration.agentProfileVersion);
|
||||
expect(QUALIFIED_ACPX_PROFILES.kimi.commandDigest).not.toBe(historical.commandDigest);
|
||||
expect(() => assertKimiInstallationProfile({ ...QUALIFIED_ACPX_PROFILES.kimi,
|
||||
commandDigest: historical.commandDigest })).toThrow(/trusted declaration/);
|
||||
});
|
||||
|
||||
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/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
import { constants } from "node:fs";
|
||||
import { lstat, open, realpath } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { verifyNativeAcpxInstallation, type VerifiedAcpxInstallation } from "./installation-integrity.js";
|
||||
import { KIMI_DISTRIBUTION_CLOSURE_SHA256, KIMI_DISTRIBUTION_LOCK_SHA256, KIMI_NODE_VERSION, KIMI_RUNTIME_VERSION } from "./kimi-closure-pins.js";
|
||||
import { resolveRunnerProviderAssetsRoot } from "./provider-assets-root.js";
|
||||
import { QUALIFIED_ACPX_PROFILES, type AcpxReleaseProfile } from "./qualified-profiles.js";
|
||||
|
||||
export function assertKimiInstallationProfile(profile: AcpxReleaseProfile): void {
|
||||
const trusted = QUALIFIED_ACPX_PROFILES.kimi;
|
||||
for (const key of ["agent", "driverKind", "protocolVersion", "acpxVersion", "agentProfileVersion", "agentServerPackage", "agentServerVersion", "agentRuntimePackage", "agentRuntimeVersion", "commandDigest", "permissionPolicy"] as const) {
|
||||
if (profile[key] !== trusted[key]) throw new Error("Kimi distribution profile differs from its trusted declaration");
|
||||
}
|
||||
if (profile.agentServerPackage !== "@moonshot-ai/kimi-code" || profile.agentServerVersion !== KIMI_RUNTIME_VERSION || profile.permissionPolicy !== "interactive") throw new Error("Kimi distribution profile has an unsupported runtime");
|
||||
}
|
||||
|
||||
async function readMetadata(path: string): Promise<Record<string, unknown>> {
|
||||
const file = await open(path, 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 distribution metadata is not a bounded regular file");
|
||||
const bytes = await file.readFile();
|
||||
const after = await file.stat({ bigint: true });
|
||||
const named = await lstat(path, { 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 distribution metadata changed while read");
|
||||
const value: unknown = JSON.parse(bytes.toString("utf8"));
|
||||
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("Kimi distribution metadata is invalid");
|
||||
return value as Record<string, unknown>;
|
||||
} finally { await file.close(); }
|
||||
}
|
||||
|
||||
export async function verifyKimiInstallation(profile: AcpxReleaseProfile): Promise<VerifiedAcpxInstallation> {
|
||||
assertKimiInstallationProfile(profile);
|
||||
const target = `${process.platform}-${process.arch}`;
|
||||
if (!Object.hasOwn(KIMI_DISTRIBUTION_CLOSURE_SHA256, target)) throw new Error(`Kimi runtime target is unsupported: ${target}`);
|
||||
const expectedClosureSha256 = KIMI_DISTRIBUTION_CLOSURE_SHA256[target as keyof typeof KIMI_DISTRIBUTION_CLOSURE_SHA256];
|
||||
const assets = join(resolveRunnerProviderAssetsRoot(import.meta.url, "kimi"), target);
|
||||
const distributionRoot = join(assets, "runtime");
|
||||
const assertDirectories = async (): Promise<void> => {
|
||||
for (const path of [assets, distributionRoot]) {
|
||||
const stat = await lstat(path);
|
||||
if (!stat.isDirectory() || stat.isSymbolicLink() || await realpath(path) !== path) throw new Error("Kimi runtime escaped its fixed asset directory");
|
||||
}
|
||||
};
|
||||
try { await assertDirectories(); }
|
||||
catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === "ENOENT") throw new Error("Kimi runtime is not installed on this host; run paperclipai runtime setup kimi before selecting Kimi Code");
|
||||
throw error;
|
||||
}
|
||||
const metadata = await readMetadata(join(assets, "kimi-distribution.json"));
|
||||
const entrypoint = "node_modules/@moonshot-ai/kimi-code/dist/main.mjs";
|
||||
if (metadata.schema !== "paperclip.kimi-distribution.v1" || metadata.version !== KIMI_RUNTIME_VERSION || metadata.target !== target || metadata.nodeVersion !== KIMI_NODE_VERSION || metadata.runtimeRoot !== "runtime" || metadata.entrypoint !== entrypoint || metadata.lockSha256 !== KIMI_DISTRIBUTION_LOCK_SHA256 || metadata.nativeClosureSha256 !== expectedClosureSha256) throw new Error("Kimi distribution metadata differs from its source-owned pin");
|
||||
const native = await verifyNativeAcpxInstallation({
|
||||
distributionRoot,
|
||||
manifestPath: join(assets, "native-closure.json"),
|
||||
expectedClosureSha256,
|
||||
executable: "node/bin/node",
|
||||
entrypoint,
|
||||
fixedArguments: ["acp"],
|
||||
});
|
||||
return Object.freeze({
|
||||
commandDigest: profile.commandDigest,
|
||||
agentServerPackageJsonPath: join(distributionRoot, "node_modules/@moonshot-ai/kimi-code/package.json"),
|
||||
agentRuntimePackageJsonPath: null,
|
||||
async openCommand(options?: { signal?: AbortSignal }) {
|
||||
options?.signal?.throwIfAborted();
|
||||
await assertDirectories();
|
||||
return native.openCommand(options);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createKimiInstructionAdmission, kimiInstructionBinding } from "./kimi-instructions.js";
|
||||
import { kimiManagedConfiguration, kimiReadOnlyPermissionAllowed } from "./kimi-config.js";
|
||||
|
||||
function acknowledgment(content: string, sessionId = "native-1") {
|
||||
return { schema: "paperclip.kimi.instructions.v1", ...kimiInstructionBinding(content), sessionId,
|
||||
effectiveDigest: `sha256:${"a".repeat(64)}`, effectiveByteLength: 500 };
|
||||
}
|
||||
|
||||
describe("Kimi native instruction admission", () => {
|
||||
it("keeps human questions and plans on the qualified semantic tool path", () => {
|
||||
const config = kimiManagedConfiguration("caller-model");
|
||||
for (const name of ["AskUserQuestion", "EnterPlanMode", "ExitPlanMode", "Agent", "AgentSwarm"]) {
|
||||
expect(config).toContain(JSON.stringify(name));
|
||||
}
|
||||
});
|
||||
it("admits exact UTF-8 instructions only after the correlated native reply, including ID zero", () => {
|
||||
const content = "Follow café instructions 🥔\n";
|
||||
const admission = createKimiInstructionAdmission(content);
|
||||
const guard = admission.createGuard();
|
||||
guard("outbound", { id: 0, method: "session/new", params: {} });
|
||||
guard("inbound", { id: 4, result: { sessionId: "native-1", _meta: { paperclipKimiInstructions: acknowledgment(content) } } });
|
||||
expect(admission.assertReady).toThrow("did not acknowledge");
|
||||
guard("inbound", { id: 0, result: { sessionId: "native-1", _meta: { paperclipKimiInstructions: acknowledgment(content) } } });
|
||||
expect(admission.assertReady).not.toThrow();
|
||||
guard("outbound", { id: 1, method: "session/prompt", params: { sessionId: "native-1" } });
|
||||
});
|
||||
|
||||
it.each(["missing", "content", "session", "length", "effective"])("rejects %s proof before a prompt", kind => {
|
||||
const admission = createKimiInstructionAdmission("rules");
|
||||
const guard = admission.createGuard();
|
||||
guard("outbound", { id: "load", method: "session/load", params: { sessionId: "native-1" } });
|
||||
const ack = acknowledgment("rules");
|
||||
if (kind === "content") ack.digest = kimiInstructionBinding("different").digest;
|
||||
if (kind === "session") ack.sessionId = "other";
|
||||
if (kind === "length") ack.byteLength += 1;
|
||||
if (kind === "effective") ack.effectiveDigest = "unknown";
|
||||
expect(() => guard("inbound", { id: "load", result: { _meta: kind === "missing" ? {} : { paperclipKimiInstructions: ack } } })).toThrow("mismatched");
|
||||
expect(() => guard("outbound", { id: 2, method: "session/prompt", params: { sessionId: "native-1" } })).toThrow();
|
||||
});
|
||||
|
||||
it("revokes the old connection and requires proof again after load", () => {
|
||||
const admission = createKimiInstructionAdmission("");
|
||||
const old = admission.createGuard();
|
||||
old("outbound", { id: 1, method: "session/new", params: {} });
|
||||
old("inbound", { id: 1, result: { sessionId: "native-1", _meta: { paperclipKimiInstructions: acknowledgment("") } } });
|
||||
const current = admission.createGuard();
|
||||
expect(() => old("outbound", { method: "session/prompt" })).toThrow("replaced");
|
||||
current("outbound", { id: 2, method: "session/load", params: { sessionId: "native-1" } });
|
||||
expect(admission.assertReady).toThrow();
|
||||
current("inbound", { id: 2, result: { _meta: { paperclipKimiInstructions: acknowledgment("") } } });
|
||||
expect(admission.assertReady).not.toThrow();
|
||||
expect(() => current("outbound", { method: "session/prompt", params: { sessionId: "other" } })).toThrow("unacknowledged");
|
||||
});
|
||||
|
||||
it("bounds exact byte content rather than JavaScript character count", () => {
|
||||
expect(() => kimiInstructionBinding("🧪".repeat(8193))).toThrow("bounded");
|
||||
expect(() => kimiInstructionBinding("bad\0input")).toThrow("NUL");
|
||||
});
|
||||
|
||||
it("keeps read-only authority separate from native tool labels and auto-approval", () => {
|
||||
const owned = new Set(["paperclip"]);
|
||||
for (const title of ["Write", "Edit", "Bash", "Read", "mcp__other__read", "mcp__paperclip__"]) {
|
||||
expect(kimiReadOnlyPermissionAllowed({ inferredKind: "read", raw: { toolCall: { title, rawInput: {} } } }, owned)).toBe(false);
|
||||
}
|
||||
expect(kimiReadOnlyPermissionAllowed({ raw: { toolCall: { title: "mcp__paperclip__get_task_context", rawInput: {} } } }, owned)).toBe(true);
|
||||
expect(kimiReadOnlyPermissionAllowed({ raw: { toolCall: { title: "mcp__paperclip__get_task_context" } } }, owned)).toBe(false);
|
||||
expect(kimiReadOnlyPermissionAllowed({ raw: { toolCall: { title: "mcp__paperclip__get_task_context", rawInput: [] } } }, owned)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
import { createHash } from "node:crypto";
|
||||
|
||||
const SCHEMA = "paperclip.kimi.instructions.v1";
|
||||
const MAX_BYTES = 32 * 1024;
|
||||
|
||||
export function kimiInstructionBinding(content: string) {
|
||||
if (typeof content !== "string" || content.includes("\0") || Buffer.from(content).toString("utf8") !== content || Buffer.byteLength(content) > MAX_BYTES) {
|
||||
throw new Error("Kimi Code instructions exceed their bounded size or contain NUL");
|
||||
}
|
||||
const digest = `sha256:${createHash("sha256").update(content).digest("hex")}`;
|
||||
const byteLength = Buffer.byteLength(content);
|
||||
return { payload: JSON.stringify({ schema: SCHEMA, content, digest, byteLength }), digest, byteLength };
|
||||
}
|
||||
|
||||
function record(value: unknown): Record<string, unknown> {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
|
||||
}
|
||||
|
||||
export function createKimiInstructionAdmission(content: string) {
|
||||
const expected = kimiInstructionBinding(content);
|
||||
let current: { sessionId: string | null; error: Error | null } | null = null;
|
||||
const failure = (message: string) => new Error(`Kimi Code instruction admission failed: ${message}`);
|
||||
const assertReady = () => {
|
||||
if (current?.error) throw current.error;
|
||||
if (!current?.sessionId) throw failure("provider did not acknowledge the composed instructions");
|
||||
};
|
||||
return {
|
||||
assertReady,
|
||||
createGuard() {
|
||||
const state = { sessionId: null as string | null, error: null as Error | null };
|
||||
current = state;
|
||||
const pending = new Map<string | number, { method: string; sessionId: unknown }>();
|
||||
return (direction: "inbound" | "outbound", value: unknown): void => {
|
||||
if (current !== state) throw failure("connection authority was replaced");
|
||||
if (state.error) throw state.error;
|
||||
const message = record(value);
|
||||
try {
|
||||
if (direction === "outbound") {
|
||||
if (["session/new", "session/load", "session/resume"].includes(String(message.method))) {
|
||||
if ((typeof message.id !== "string" && typeof message.id !== "number") || pending.size !== 0) throw failure("session admission request is ambiguous");
|
||||
state.sessionId = null;
|
||||
pending.set(message.id, { method: String(message.method), sessionId: record(message.params).sessionId });
|
||||
} else if (message.method === "session/prompt") {
|
||||
assertReady();
|
||||
if (record(message.params).sessionId !== state.sessionId) throw failure("prompt belongs to an unacknowledged session");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (message.method !== undefined || (typeof message.id !== "string" && typeof message.id !== "number")) return;
|
||||
const request = pending.get(message.id);
|
||||
if (!request) return;
|
||||
pending.delete(message.id);
|
||||
if (message.error !== undefined) return;
|
||||
const result = record(message.result);
|
||||
const ack = record(record(result._meta).paperclipKimiInstructions);
|
||||
const sessionId = request.method === "session/new" ? result.sessionId : request.sessionId;
|
||||
if (ack.schema !== SCHEMA || ack.digest !== expected.digest || ack.byteLength !== expected.byteLength
|
||||
|| typeof sessionId !== "string" || !sessionId.trim() || ack.sessionId !== sessionId
|
||||
|| typeof ack.effectiveDigest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(ack.effectiveDigest)
|
||||
|| !Number.isSafeInteger(ack.effectiveByteLength) || (ack.effectiveByteLength as number) < expected.byteLength) {
|
||||
throw failure("native instruction acknowledgment is missing or mismatched");
|
||||
}
|
||||
state.sessionId = sessionId;
|
||||
} catch (error) {
|
||||
state.error = error instanceof Error ? error : failure("invalid acknowledgment");
|
||||
throw state.error;
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createKimiMessageProjection, kimiMessageBoundaryClears } from "./kimi-message-boundary.js";
|
||||
|
||||
const id = (n: number) => `kimi-message-${String(n).padStart(64, "0")}`;
|
||||
const chunk = (n: number, kind: string, text = "", stream = "output") => ({ type: "text_delta", messageId: id(n), text, stream,
|
||||
meta: { origin: "kimi-native-assistant", source: "kimi-turn-step-v1", kind } });
|
||||
function project(events: ReturnType<typeof chunk>[]) {
|
||||
const projection = createKimiMessageProjection<ReturnType<typeof chunk>>();
|
||||
const progress: string[] = []; let final = "";
|
||||
for (const raw of events) {
|
||||
const event = projection.normalize(raw);
|
||||
if (kimiMessageBoundaryClears(event)) final = "";
|
||||
if (event.stream === "output") { final += event.text; progress.push(event.text); }
|
||||
}
|
||||
projection.settle(); return { final, progress };
|
||||
}
|
||||
|
||||
describe("Kimi native message boundaries", () => {
|
||||
it("preserves separate native steps while finalizing only the actual end-turn answer", () => {
|
||||
const projection = createKimiMessageProjection<ReturnType<typeof chunk>>();
|
||||
const started = projection.normalize(chunk(1, "start"));
|
||||
expect(started).toMatchObject({ messageId: id(1), text: "", assistantMessageBoundary: { phase: "start" } });
|
||||
expect(projection.normalize(chunk(1, "delta", "I will call the tool."))).toMatchObject({ messageId: id(1), text: "I will call the tool." });
|
||||
expect(projection.normalize(chunk(1, "end:tool_use"))).toMatchObject({ messageId: id(1), text: "", assistantMessageBoundary: { phase: "end", stopReason: "toolUse" } });
|
||||
projection.normalize(chunk(2, "start"));
|
||||
expect(projection.normalize(chunk(2, "delta", "private reasoning", "thought"))).toMatchObject({ messageId: id(2), stream: "thought" });
|
||||
projection.normalize(chunk(2, "delta", "EXACT_MARKER"));
|
||||
expect(projection.normalize(chunk(2, "end:end_turn"))).toMatchObject({ messageId: id(2), assistantMessageBoundary: { phase: "end", stopReason: "stop" } });
|
||||
projection.settle();
|
||||
const result = project([chunk(1, "start"), chunk(1, "delta", "I will call the tool."), chunk(1, "end:tool_use"),
|
||||
chunk(2, "start"), chunk(2, "delta", "private reasoning", "thought"), chunk(2, "delta", "EXACT_"), chunk(2, "delta", "MARKER"), chunk(2, "end:end_turn")]);
|
||||
expect(result.final).toBe("EXACT_MARKER");
|
||||
expect(result.progress).toContain("I will call the tool.");
|
||||
expect(result.progress).not.toContain("private reasoning");
|
||||
});
|
||||
it.each([
|
||||
["end_turn", "stop"], ["max_tokens", "length"], ["tool_use", "toolUse"], ["error", "error"],
|
||||
["filtered", "error"], ["other", "error"], ["aborted", "aborted"], ["paused", "aborted"], ["max_steps", "aborted"],
|
||||
])("maps the actual native %s reason to closed canonical %s", (native, canonical) => {
|
||||
const projection = createKimiMessageProjection<ReturnType<typeof chunk>>();
|
||||
projection.normalize(chunk(1, "start"));
|
||||
expect(projection.normalize(chunk(1, `end:${native}`)).assistantMessageBoundary).toEqual({ phase: "end", stopReason: canonical });
|
||||
projection.settle();
|
||||
expect(project([chunk(1, "start"), chunk(1, "delta", "native output"), chunk(1, `end:${native}`)]).final)
|
||||
.toBe(["end_turn", "max_tokens"].includes(native) ? "native output" : "");
|
||||
});
|
||||
it("retains empty final boundaries and permits notice-only turns without inventing an answer", () => {
|
||||
expect(project([chunk(1, "start"), chunk(1, "delta", "old answer"), chunk(1, "end:end_turn"), chunk(2, "start"), chunk(2, "end:end_turn")]).final).toBe("");
|
||||
const projection = createKimiMessageProjection<{ type: string }>();
|
||||
expect(projection.normalize({ type: "status" })).toEqual({ type: "status" }); projection.settle();
|
||||
});
|
||||
it.each([
|
||||
[chunk(1, "delta", "missing start")], [chunk(1, "end:end_turn")], [chunk(1, "start"), chunk(1, "start")],
|
||||
[chunk(1, "start"), chunk(2, "delta", "wrong id")], [chunk(1, "start"), chunk(2, "end:end_turn")],
|
||||
[chunk(1, "start"), chunk(1, "end:invented")], [chunk(1, "start"), chunk(1, "end:constructor")],
|
||||
[chunk(1, "start"), chunk(1, "end:end_turn"), chunk(1, "start")], [chunk(1, "start", "nonempty boundary")],
|
||||
[chunk(1, "start", "", "thought")], [chunk(1, "start"), chunk(1, "end:end_turn", "nonempty end")], [chunk(1, "start")],
|
||||
[{ ...chunk(1, "start"), messageId: "fabricated-id" }], [{ ...chunk(1, "start"), meta: { origin: "forged", source: "kimi-turn-step-v1", kind: "start" } }],
|
||||
[{ ...chunk(1, "start"), meta: { origin: "kimi-native-assistant", source: "unversioned", kind: "start" } }],
|
||||
[{ ...chunk(1, "start"), assistantMessageBoundary: { phase: "start" } }],
|
||||
])("fails closed for missing, reused or forged native provenance %#", (...events) => {
|
||||
expect(() => project(events)).toThrow("provenance");
|
||||
});
|
||||
it("poisons a malformed stream instead of accepting a later valid-looking boundary", () => {
|
||||
const projection = createKimiMessageProjection<ReturnType<typeof chunk>>();
|
||||
expect(() => projection.normalize(chunk(1, "delta", "orphan"))).toThrow("provenance");
|
||||
expect(() => projection.normalize(chunk(2, "start"))).toThrow("provenance");
|
||||
expect(() => projection.settle()).toThrow("provenance");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
type RuntimeMessageEvent = { type: string; text?: string; stream?: string; messageId?: string; meta?: Record<string, unknown> };
|
||||
export interface KimiMessageBoundary { phase: "start" | "end"; stopReason?: "stop" | "length" | "toolUse" | "error" | "aborted" }
|
||||
export type KimiProjectedMessageEvent<T> = T & { assistantMessageBoundary?: KimiMessageBoundary; assistantMessageHistory?: true };
|
||||
|
||||
const STOP_REASONS: Readonly<Record<string, KimiMessageBoundary["stopReason"]>> = Object.freeze({
|
||||
end_turn: "stop", max_tokens: "length", tool_use: "toolUse", error: "error", filtered: "error", other: "error",
|
||||
aborted: "aborted", paused: "aborted", max_steps: "aborted",
|
||||
});
|
||||
|
||||
export function createKimiMessageProjection<T extends RuntimeMessageEvent>() {
|
||||
let active: string | null = null;
|
||||
const seen = new Set<string>();
|
||||
let poisoned = false;
|
||||
const fail = (): never => { poisoned = true; throw new Error("Kimi native message provenance is invalid or incomplete"); };
|
||||
return {
|
||||
normalize(event: T): KimiProjectedMessageEvent<T> {
|
||||
if (poisoned) return fail();
|
||||
if ("assistantMessageBoundary" in event || "assistantMessageHistory" in event) return fail();
|
||||
if (event.type !== "text_delta") return event;
|
||||
const { messageId, meta } = event;
|
||||
if (typeof messageId !== "string" || !/^kimi-message-[a-f0-9]{64}$/.test(messageId)
|
||||
|| meta?.origin !== "kimi-native-assistant" || meta.source !== "kimi-turn-step-v1"
|
||||
|| typeof event.text !== "string" || (event.stream !== "output" && event.stream !== "thought")) return fail();
|
||||
if (meta.kind === "delta") {
|
||||
if (active !== messageId) return fail();
|
||||
return event;
|
||||
}
|
||||
if (event.text !== "" || event.stream !== "output") return fail();
|
||||
if (meta.kind === "start") {
|
||||
if (active || seen.has(messageId) || seen.size >= 4096) return fail();
|
||||
seen.add(messageId); active = messageId;
|
||||
return { ...event, assistantMessageBoundary: { phase: "start" } };
|
||||
}
|
||||
if (active !== messageId || typeof meta.kind !== "string" || !meta.kind.startsWith("end:")) return fail();
|
||||
const reason = meta.kind.slice(4);
|
||||
if (!Object.hasOwn(STOP_REASONS, reason)) return fail();
|
||||
active = null;
|
||||
return { ...event, assistantMessageBoundary: { phase: "end", stopReason: STOP_REASONS[reason] } };
|
||||
},
|
||||
settle(): void { if (poisoned || active) fail(); },
|
||||
};
|
||||
}
|
||||
|
||||
export function kimiMessageBoundaryClears(event: { assistantMessageBoundary?: KimiMessageBoundary }): boolean {
|
||||
const boundary = event.assistantMessageBoundary;
|
||||
return boundary?.phase === "start" || (boundary?.phase === "end" && !["stop", "length"].includes(boundary.stopReason ?? ""));
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import { bindAcpxCandidateAdmission, type AcpxCandidateAdmission } from "./candidate-admission.js";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { KIMI_DISTRIBUTION_CLOSURE_SHA256, KIMI_NODE_DISTRIBUTIONS, KIMI_NODE_VERSION, KIMI_RUNTIME_VERSION } from "./kimi-closure-pins.js";
|
||||
import { KimiMetadataProbeError } from "./kimi-models.js";
|
||||
import { QUALIFIED_ACPX_PROFILES } from "./qualified-profiles.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({ installation: vi.fn(), open: vi.fn(), close: vi.fn(), metadata: vi.fn() }));
|
||||
vi.mock("./kimi-installation.js", () => ({ verifyKimiInstallation: mocks.installation }));
|
||||
vi.mock("./kimi-models.js", async original => ({ ...await original<object>(), probeKimiModels: mocks.metadata }));
|
||||
import { kimiMetadataProbeNodeDigest, probeKimiMetadata, validateKimiMetadata } from "./kimi-metadata-probe.js";
|
||||
|
||||
const model = "selected-model";
|
||||
const apiKey = "selected-secret-canary";
|
||||
const target = `${process.platform}-${process.arch}` as keyof typeof KIMI_DISTRIBUTION_CLOSURE_SHA256;
|
||||
const admission = () => bindAcpxCandidateAdmission({ schema: "paperclip.acpx-candidate-admission.v1", companyId: "metadata-company", agent: "kimi", model,
|
||||
profileDigest: QUALIFIED_ACPX_PROFILES.kimi.commandDigest,
|
||||
nativeClosureDigest: `sha256:${KIMI_DISTRIBUTION_CLOSURE_SHA256[target]}`,
|
||||
platform: process.platform, architecture: process.arch, expiresAt: Date.now() + 60_000 } as AcpxCandidateAdmission,
|
||||
{ runId: "metadata-operation", normalizedSessionId: "metadata-session" });
|
||||
const receipt = () => ({ schema: "paperclip.kimi.metadata.v1", status: "verified", version: KIMI_RUNTIME_VERSION,
|
||||
nodeVersion: KIMI_NODE_VERSION, profileDigest: QUALIFIED_ACPX_PROFILES.kimi.commandDigest, target,
|
||||
closureDigest: `sha256:${KIMI_DISTRIBUTION_CLOSURE_SHA256[target]}`, model, contextLength: 1048576, promptSent: false });
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
mocks.installation.mockResolvedValue({ openCommand: mocks.open });
|
||||
mocks.open.mockResolvedValue({ close: mocks.close });
|
||||
mocks.close.mockResolvedValue(undefined);
|
||||
mocks.metadata.mockResolvedValue({ model, contextLength: 1048576 });
|
||||
});
|
||||
afterEach(() => { vi.unstubAllEnvs(); });
|
||||
|
||||
describe("Kimi setup metadata admission", () => {
|
||||
it("verifies and closes the complete installation before authenticating the exact selected model", async () => {
|
||||
const order: string[] = [];
|
||||
mocks.open.mockImplementation(async () => { order.push("verify-closure"); return { close: mocks.close }; });
|
||||
mocks.close.mockImplementation(async () => { order.push("cleanup"); });
|
||||
mocks.metadata.mockImplementation(async () => { order.push("authenticate"); return { model, contextLength: 1048576 }; });
|
||||
vi.stubEnv("KIMI_API_KEY", "unselected-ambient-secret");
|
||||
vi.stubEnv("KIMI_MODEL_DEFAULT", "unselected-model");
|
||||
await expect(probeKimiMetadata(model, apiKey, admission())).resolves.toEqual(receipt());
|
||||
expect(order).toEqual(["verify-closure", "cleanup", "authenticate"]);
|
||||
expect(mocks.installation).toHaveBeenCalledWith(expect.objectContaining({
|
||||
agent: "kimi", commandDigest: QUALIFIED_ACPX_PROFILES.kimi.commandDigest,
|
||||
qualificationModel: model, reportedModelId: model,
|
||||
}));
|
||||
expect(mocks.metadata).toHaveBeenCalledExactlyOnceWith(model, apiKey);
|
||||
});
|
||||
|
||||
it.each(["metadata", "closure", "cleanup"])("rejects %s installation failure before authenticating", async stage => {
|
||||
const error = new Error(`private runtime failure ${apiKey}`);
|
||||
if (stage === "metadata") mocks.installation.mockRejectedValue(error);
|
||||
if (stage === "closure") mocks.open.mockRejectedValue(error);
|
||||
if (stage === "cleanup") mocks.close.mockRejectedValue(error);
|
||||
const result = await probeKimiMetadata(model, apiKey, admission());
|
||||
expect(result).toMatchObject({ status: "failed", code: "KIMI_INSTALLATION_INVALID", promptSent: false });
|
||||
expect(JSON.stringify(result)).not.toContain(apiKey);
|
||||
expect(mocks.metadata).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("refuses pending metadata work without exact trusted startup authority before files or HTTP", async () => {
|
||||
expect(await probeKimiMetadata(model, apiKey)).toMatchObject({ status: "failed", code: "KIMI_QUALIFICATION_REQUIRED", promptSent: false });
|
||||
const valid = admission();
|
||||
for (const altered of [{ ...valid, model: "other-model" }, { ...valid, agent: "antigravity" },
|
||||
{ ...valid, expiresAt: 0 }, { ...valid, profileDigest: `sha256:${"f".repeat(64)}` },
|
||||
{ ...valid, nativeClosureDigest: `sha256:${"e".repeat(64)}` }, { ...valid, extra: true }]) {
|
||||
expect(await probeKimiMetadata(model, apiKey, altered as typeof valid)).toMatchObject({ code: "KIMI_QUALIFICATION_REQUIRED" });
|
||||
}
|
||||
expect(mocks.installation).not.toHaveBeenCalled(); expect(mocks.metadata).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("requires no operator grant after canonical release metadata admits Kimi", async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("./generated-profiles.js", async original => {
|
||||
const data = await original<typeof import("./generated-profiles.js")>();
|
||||
const current = structuredClone(data.QUALIFIED_ACPX_PROFILE_DATA);
|
||||
const released = { ...current.kimi } as Record<string, unknown>;
|
||||
delete released.qualificationStatus;
|
||||
return { ...data, QUALIFIED_ACPX_PROFILE_DATA: { ...current, kimi: released } };
|
||||
});
|
||||
try {
|
||||
const released = await import("./kimi-metadata-probe.js");
|
||||
expect(await released.probeKimiMetadata(model, apiKey)).toMatchObject({ status: "verified", model, promptSent: false });
|
||||
expect(mocks.metadata).toHaveBeenCalledExactlyOnceWith(model, apiKey);
|
||||
} finally { vi.doUnmock("./generated-profiles.js"); vi.resetModules(); }
|
||||
});
|
||||
|
||||
it("requires an explicit bounded credential and exact model before installation or HTTP work", async () => {
|
||||
vi.stubEnv("KIMI_API_KEY", apiKey);
|
||||
for (const invalid of ["", " ", "key\nheader", "key\rheader", "key\0header", "x".repeat(16385)]) {
|
||||
expect(await probeKimiMetadata(model, invalid)).toMatchObject({ code: "KIMI_AUTH_REQUIRED" });
|
||||
}
|
||||
for (const invalid of ["", " ", "model\0value", "x".repeat(513), "\ud800"]) {
|
||||
expect(await probeKimiMetadata(invalid, apiKey)).toMatchObject({ code: "KIMI_MODEL_UNAVAILABLE" });
|
||||
}
|
||||
expect(mocks.installation).not.toHaveBeenCalled();
|
||||
expect(mocks.metadata).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each(["KIMI_AUTH_REJECTED", "KIMI_MODEL_UNAVAILABLE", "KIMI_METADATA_TIMEOUT", "KIMI_METADATA_INVALID", "KIMI_PROVIDER_ERROR"] as const)("preserves the canonical %s failure without provider text", async code => {
|
||||
mocks.metadata.mockRejectedValue(new KimiMetadataProbeError(code));
|
||||
expect(await probeKimiMetadata(model, apiKey, admission())).toMatchObject({ status: "failed", code, promptSent: false });
|
||||
mocks.metadata.mockRejectedValue(new Error(`provider failure ${apiKey}`));
|
||||
const result = await probeKimiMetadata(model, apiKey, admission());
|
||||
expect(result).toMatchObject({ status: "failed", code: "KIMI_REQUEST_FAILED", promptSent: false });
|
||||
expect(JSON.stringify(result)).not.toContain(apiKey);
|
||||
});
|
||||
});
|
||||
|
||||
describe("untrusted Kimi metadata receipts", () => {
|
||||
it("accepts only the current source identity and exact selected model", () => {
|
||||
expect(validateKimiMetadata(receipt(), model)).toEqual(receipt());
|
||||
expect(kimiMetadataProbeNodeDigest()).toBe(`sha256:${KIMI_NODE_DISTRIBUTIONS["linux-x64"].executableSha256}`);
|
||||
expect(validateKimiMetadata(receipt(), "different-model")).toMatchObject({ code: "KIMI_MODEL_UNAVAILABLE" });
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ version: "latest" }, { nodeVersion: "24.0.0" }, { profileDigest: `sha256:${"0".repeat(64)}` },
|
||||
{ closureDigest: `sha256:${"0".repeat(64)}` }, { target: "linux-arm64" },
|
||||
])("rejects an incompatible installation %#", alteration => {
|
||||
expect(validateKimiMetadata({ ...receipt(), ...alteration }, model)).toMatchObject({
|
||||
status: "failed", code: "KIMI_INSTALLATION_INVALID", promptSent: false,
|
||||
});
|
||||
});
|
||||
|
||||
it.each([null, [], {}, { ...receipt(), promptSent: true }, { ...receipt(), injected: true },
|
||||
{ ...receipt(), schema: "other" }, { schema: "paperclip.kimi.metadata.v1", status: "failed", code: "invented", promptSent: false },
|
||||
])("rejects malformed or open receipt contracts %#", value => {
|
||||
expect(validateKimiMetadata(value, model)).toMatchObject({ status: "failed", code: "KIMI_REQUEST_FAILED" });
|
||||
});
|
||||
|
||||
it.each([0, -1, 1.5, "1048576", Number.MAX_SAFE_INTEGER + 1, undefined])("rejects invalid context length %s", contextLength => {
|
||||
expect(validateKimiMetadata({ ...receipt(), contextLength }, model)).toMatchObject({ code: "KIMI_METADATA_INVALID" });
|
||||
});
|
||||
|
||||
it("replaces remote failure messages with canonical text", () => {
|
||||
const result = validateKimiMetadata({ schema: "paperclip.kimi.metadata.v1", status: "failed", code: "KIMI_AUTH_REJECTED", message: apiKey, promptSent: false }, model);
|
||||
expect(result).toMatchObject({ code: "KIMI_AUTH_REJECTED", promptSent: false });
|
||||
expect(JSON.stringify(result)).not.toContain(apiKey);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,94 @@
|
||||
import { assertAcpxExecutionAdmission, type AcpxCandidateStartupAdmission } from "./candidate-admission.js";
|
||||
import { createHash } from "node:crypto";
|
||||
import { constants } from "node:fs";
|
||||
import { lstat, open } from "node:fs/promises";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { KIMI_DISTRIBUTION_CLOSURE_SHA256, KIMI_NODE_DISTRIBUTIONS, KIMI_NODE_VERSION, KIMI_RUNTIME_VERSION } from "./kimi-closure-pins.js";
|
||||
import { verifyKimiInstallation } from "./kimi-installation.js";
|
||||
import { KimiMetadataProbeError, probeKimiModels, type KimiMetadataProbeErrorCode } from "./kimi-models.js";
|
||||
import { resolveQualifiedAcpxProfile, QUALIFIED_ACPX_PROFILES } from "./qualified-profiles.js";
|
||||
|
||||
const SCHEMA = "paperclip.kimi.metadata.v1";
|
||||
const providerCodes = ["KIMI_AUTH_REQUIRED", "KIMI_AUTH_REJECTED", "KIMI_MODEL_UNAVAILABLE", "KIMI_METADATA_TIMEOUT", "KIMI_METADATA_INVALID", "KIMI_PROVIDER_ERROR"] as const;
|
||||
type FailureCode = KimiMetadataProbeErrorCode | "KIMI_INSTALLATION_INVALID" | "KIMI_REQUEST_FAILED" | "KIMI_QUALIFICATION_REQUIRED";
|
||||
export type KimiMetadataResult = {
|
||||
schema: typeof SCHEMA; status: "verified"; version: typeof KIMI_RUNTIME_VERSION;
|
||||
nodeVersion: typeof KIMI_NODE_VERSION; profileDigest: string; target: string;
|
||||
closureDigest: string; model: string; contextLength: number; promptSent: false;
|
||||
} | { schema: typeof SCHEMA; status: "failed"; code: FailureCode; message: string; promptSent: false };
|
||||
|
||||
function failed(code: FailureCode): Extract<KimiMetadataResult, { status: "failed" }> {
|
||||
const message = code === "KIMI_QUALIFICATION_REQUIRED"
|
||||
? "Kimi Code qualification is pending; metadata verification requires trusted operator admission."
|
||||
: code === "KIMI_INSTALLATION_INVALID"
|
||||
? "Install the current verified Kimi Code runtime in this execution environment."
|
||||
: code === "KIMI_REQUEST_FAILED" ? "Kimi Code metadata verification failed."
|
||||
: new KimiMetadataProbeError(code).message;
|
||||
return { schema: SCHEMA, status: "failed", code, message, promptSent: false };
|
||||
}
|
||||
|
||||
export async function probeKimiMetadata(model: string, apiKey: string, candidateAdmission?: AcpxCandidateStartupAdmission): Promise<KimiMetadataResult> {
|
||||
if (typeof apiKey !== "string" || !apiKey.trim() || /[\0\r\n]/.test(apiKey) || Buffer.byteLength(apiKey) > 16 * 1024) return failed("KIMI_AUTH_REQUIRED");
|
||||
if (typeof model !== "string" || !model.trim() || model.includes("\0") || Buffer.byteLength(model) > 512 || Buffer.from(model).toString("utf8") !== model) return failed("KIMI_MODEL_UNAVAILABLE");
|
||||
const target = `${process.platform}-${process.arch}`;
|
||||
if (!Object.hasOwn(KIMI_DISTRIBUTION_CLOSURE_SHA256, target)) return failed("KIMI_INSTALLATION_INVALID");
|
||||
const profile = resolveQualifiedAcpxProfile("kimi", model);
|
||||
try { assertAcpxExecutionAdmission(profile, candidateAdmission); }
|
||||
catch { return failed("KIMI_QUALIFICATION_REQUIRED"); }
|
||||
try {
|
||||
const installation = await verifyKimiInstallation(profile);
|
||||
const lease = await installation.openCommand();
|
||||
await lease.close();
|
||||
} catch { return failed("KIMI_INSTALLATION_INVALID"); }
|
||||
try {
|
||||
const metadata = await probeKimiModels(model, apiKey);
|
||||
return { schema: SCHEMA, status: "verified", version: KIMI_RUNTIME_VERSION, nodeVersion: KIMI_NODE_VERSION,
|
||||
profileDigest: QUALIFIED_ACPX_PROFILES.kimi.commandDigest, target,
|
||||
closureDigest: `sha256:${KIMI_DISTRIBUTION_CLOSURE_SHA256[target as keyof typeof KIMI_DISTRIBUTION_CLOSURE_SHA256]}`,
|
||||
model: metadata.model, contextLength: metadata.contextLength, promptSent: false };
|
||||
} catch (error) { return failed(error instanceof KimiMetadataProbeError ? error.code : "KIMI_REQUEST_FAILED"); }
|
||||
}
|
||||
|
||||
export function validateKimiMetadata(value: unknown, model: string): KimiMetadataResult {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return failed("KIMI_REQUEST_FAILED");
|
||||
const row = value as Record<string, unknown>;
|
||||
if (row.schema !== SCHEMA || row.promptSent !== false) return failed("KIMI_REQUEST_FAILED");
|
||||
if (row.status === "failed") {
|
||||
if (Object.keys(row).some(key => !["schema", "status", "code", "message", "promptSent"].includes(key))
|
||||
|| typeof row.code !== "string" || ![...providerCodes, "KIMI_INSTALLATION_INVALID", "KIMI_REQUEST_FAILED", "KIMI_QUALIFICATION_REQUIRED"].includes(row.code)) return failed("KIMI_REQUEST_FAILED");
|
||||
return failed(row.code as FailureCode);
|
||||
}
|
||||
if (row.status !== "verified" || Object.keys(row).some(key => !["schema", "status", "version", "nodeVersion", "profileDigest", "target", "closureDigest", "model", "contextLength", "promptSent"].includes(key))) return failed("KIMI_REQUEST_FAILED");
|
||||
if (row.version !== KIMI_RUNTIME_VERSION || row.nodeVersion !== KIMI_NODE_VERSION
|
||||
|| row.profileDigest !== QUALIFIED_ACPX_PROFILES.kimi.commandDigest || typeof row.target !== "string"
|
||||
|| !Object.hasOwn(KIMI_DISTRIBUTION_CLOSURE_SHA256, row.target)
|
||||
|| row.closureDigest !== `sha256:${KIMI_DISTRIBUTION_CLOSURE_SHA256[row.target as keyof typeof KIMI_DISTRIBUTION_CLOSURE_SHA256]}`) return failed("KIMI_INSTALLATION_INVALID");
|
||||
if (row.model !== model) return failed("KIMI_MODEL_UNAVAILABLE");
|
||||
if (!Number.isSafeInteger(row.contextLength) || (row.contextLength as number) <= 0) return failed("KIMI_METADATA_INVALID");
|
||||
return row as Extract<KimiMetadataResult, { status: "verified" }>;
|
||||
}
|
||||
|
||||
export function kimiMetadataProbeNodeDigest(): string {
|
||||
return `sha256:${KIMI_NODE_DISTRIBUTIONS["linux-x64"].executableSha256}`;
|
||||
}
|
||||
|
||||
export async function kimiMetadataProbeDigest(): Promise<string> {
|
||||
const url = new URL(import.meta.url);
|
||||
const relative = /\/dist\/vendor\/paperclip-runner\/drivers\/acpx\/kimi-metadata-probe\.js$/.test(url.pathname)
|
||||
? "../../cli/kimi-metadata-probe.cjs"
|
||||
: /\/(?:src|dist)\/drivers\/acpx\/kimi-metadata-probe\.(?:ts|js)$/.test(url.pathname)
|
||||
? "../../../dist/cli/kimi-metadata-probe.cjs" : null;
|
||||
if (url.protocol !== "file:" || url.search || url.hash || relative === null) throw new Error("Kimi metadata probe requires its fixed build layout");
|
||||
const path = fileURLToPath(new URL(relative, url));
|
||||
const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
||||
try {
|
||||
const before = await file.stat({ bigint: true });
|
||||
if (!before.isFile() || before.size < 1n || before.size > 4n * 1024n * 1024n) throw new Error("Kimi metadata probe artifact is invalid");
|
||||
const bytes = await file.readFile();
|
||||
const after = await file.stat({ bigint: true }), named = await lstat(path, { bigint: true });
|
||||
if (bytes.length !== Number(before.size) || before.dev !== after.dev || before.ino !== after.ino
|
||||
|| before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs || before.size !== after.size
|
||||
|| named.dev !== before.dev || named.ino !== before.ino || named.isSymbolicLink()) throw new Error("Kimi metadata probe artifact changed during admission");
|
||||
return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
||||
} finally { await file.close(); }
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { KimiMetadataProbeError, probeKimiModels } from "./kimi-models.js";
|
||||
|
||||
const model = "kimi-for-coding";
|
||||
const key = "selected-key-canary";
|
||||
const payload = { data: [{ id: "別のモデル", limit: { context: 262144 } }, { id: model, limit: { context: 1048576 } }] };
|
||||
const response = (value: unknown) => new Response(JSON.stringify(value), { headers: { "content-type": "application/json" } });
|
||||
const dependency = (value: Response) => ({ fetch: vi.fn<typeof fetch>().mockResolvedValue(value) });
|
||||
|
||||
afterEach(() => { vi.useRealTimers(); vi.unstubAllEnvs(); });
|
||||
|
||||
describe("Kimi Code authenticated model metadata", () => {
|
||||
it("uses only the selected key, pinned endpoint and official client identity without inference", async () => {
|
||||
vi.stubEnv("KIMI_API_KEY", "ambient-unselected-key");
|
||||
vi.stubEnv("KIMI_CODE_BASE_URL", "https://unselected.example");
|
||||
const bytes = Buffer.from(JSON.stringify(payload));
|
||||
const split = bytes.indexOf(Buffer.from("別")) + 1;
|
||||
const deps = dependency(new Response(new ReadableStream({ start(controller) {
|
||||
controller.enqueue(bytes.subarray(0, split)); controller.enqueue(bytes.subarray(split)); controller.close();
|
||||
} })));
|
||||
await expect(probeKimiModels(model, key, deps)).resolves.toEqual({
|
||||
code: "kimi_metadata_verified", model, contextLength: 1048576,
|
||||
message: expect.stringContaining("No model prompt was sent"),
|
||||
});
|
||||
expect(deps.fetch).toHaveBeenCalledTimes(1);
|
||||
const [url, init] = deps.fetch.mock.calls[0]!;
|
||||
expect(url).toBe("https://api.kimi.ai/coding/v1/models");
|
||||
expect(init).toMatchObject({ method: "GET", redirect: "error", headers: {
|
||||
Authorization: `Bearer ${key}`, Accept: "application/json", "User-Agent": "KimiCLI/2.3.3",
|
||||
} });
|
||||
expect(init?.body).toBeUndefined();
|
||||
expect(init?.signal).toBeInstanceOf(AbortSignal);
|
||||
});
|
||||
|
||||
it("rejects absent credentials before fetching and never falls back to ambient login", async () => {
|
||||
vi.stubEnv("KIMI_API_KEY", key);
|
||||
const deps = dependency(response(payload));
|
||||
for (const invalid of ["", " ", "x\ny", "x\ry", "x\0y", "x".repeat(16385)]) {
|
||||
await expect(probeKimiModels(model, invalid, deps)).rejects.toMatchObject({ code: "KIMI_AUTH_REQUIRED" });
|
||||
}
|
||||
expect(deps.fetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([401, 402, 403])("preserves typed HTTP %i credential/access rejection without reading its body", async status => {
|
||||
const cancel = vi.fn();
|
||||
const body = new ReadableStream({ cancel });
|
||||
const getReader = vi.spyOn(body, "getReader");
|
||||
const deps = dependency(new Response(body, { status }));
|
||||
await expect(probeKimiModels(model, key, deps)).rejects.toMatchObject({
|
||||
name: "KimiMetadataProbeError", code: "KIMI_AUTH_REJECTED", status,
|
||||
});
|
||||
expect(getReader).not.toHaveBeenCalled();
|
||||
expect(cancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("requires the exact selected model without aliases or fallback", async () => {
|
||||
for (const selected of ["KIMI-FOR-CODING", "kimi-for-coding ", "unlisted", "", "x\0y", "x".repeat(513)]) {
|
||||
await expect(probeKimiModels(selected, key, dependency(response(payload))))
|
||||
.rejects.toMatchObject({ code: "KIMI_MODEL_UNAVAILABLE" });
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
null, [], {}, { data: {} }, { data: [null] }, { data: [{ id: "", limit: { context: 1 } }] },
|
||||
{ data: [{ id: model }] }, { data: [{ id: model, limit: { context: 0 } }] },
|
||||
{ data: [{ id: model, limit: { context: -1 } }] }, { data: [{ id: model, limit: { context: 1.2 } }] },
|
||||
{ data: [{ id: model, limit: { context: true } }] }, { data: [{ id: model, limit: { context: "" } }] },
|
||||
{ data: [{ id: model, limit: { context: Number.MAX_SAFE_INTEGER + 1 } }] },
|
||||
{ data: [{ id: model, limit: { context: 1 } }, { id: model, limit: { context: 2 } }] },
|
||||
])("rejects invalid or ambiguous metadata %#", async value => {
|
||||
await expect(probeKimiModels(model, key, dependency(response(value)))).rejects.toMatchObject({ code: "KIMI_METADATA_INVALID" });
|
||||
});
|
||||
|
||||
it("accepts the native numeric-string context representation", async () => {
|
||||
await expect(probeKimiModels(model, key, dependency(response({ data: [{ id: model, limit: { context: "1048576" } }] }))))
|
||||
.resolves.toMatchObject({ contextLength: 1048576 });
|
||||
});
|
||||
|
||||
it("bounds streamed bytes even when Content-Length is missing or false", async () => {
|
||||
for (const headers of [{}, { "content-length": "10" }]) {
|
||||
const cancel = vi.fn();
|
||||
const deps = dependency(new Response(new ReadableStream({ start(controller) {
|
||||
controller.enqueue(new Uint8Array(256 * 1024)); controller.enqueue(new Uint8Array(1));
|
||||
}, cancel }), { headers }));
|
||||
await expect(probeKimiModels(model, key, deps)).rejects.toMatchObject({ code: "KIMI_METADATA_INVALID" });
|
||||
expect(deps.fetch.mock.calls[0]?.[1]?.signal?.aborted).toBe(true);
|
||||
expect(cancel).toHaveBeenCalled();
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects oversized declared bodies before consuming them", async () => {
|
||||
const cancel = vi.fn();
|
||||
const deps = dependency(new Response(new ReadableStream({ cancel }), { headers: { "content-length": "262145" } }));
|
||||
await expect(probeKimiModels(model, key, deps)).rejects.toMatchObject({ code: "KIMI_METADATA_INVALID" });
|
||||
expect(cancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([new Uint8Array([0xff]), Buffer.from("invalid JSON")])("rejects invalid UTF-8 or JSON %#", async bytes => {
|
||||
await expect(probeKimiModels(model, key, dependency(new Response(bytes))))
|
||||
.rejects.toMatchObject({ code: "KIMI_METADATA_INVALID" });
|
||||
});
|
||||
|
||||
it("aborts an outstanding request at the fixed eight-second deadline", async () => {
|
||||
vi.useFakeTimers();
|
||||
let signal: AbortSignal | undefined;
|
||||
const aborted = vi.fn();
|
||||
const deps = { fetch: vi.fn<typeof fetch>((_url, init) => new Promise((_, reject) => {
|
||||
signal = init?.signal ?? undefined;
|
||||
signal?.addEventListener("abort", () => { aborted(); reject(new Error(`request failure ${key}`)); });
|
||||
})) };
|
||||
const rejected = expect(probeKimiModels(model, key, deps)).rejects.toMatchObject({ code: "KIMI_METADATA_TIMEOUT" });
|
||||
await vi.advanceTimersByTimeAsync(7999);
|
||||
expect(aborted).not.toHaveBeenCalled();
|
||||
await vi.advanceTimersByTimeAsync(1);
|
||||
await rejected;
|
||||
expect(signal?.aborted).toBe(true);
|
||||
expect(aborted).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("keeps the deadline active after headers and cancels a stalled response body", async () => {
|
||||
vi.useFakeTimers();
|
||||
const cancel = vi.fn();
|
||||
const deps = dependency(new Response(new ReadableStream({ cancel })));
|
||||
const rejected = expect(probeKimiModels(model, key, deps)).rejects.toMatchObject({ code: "KIMI_METADATA_TIMEOUT" });
|
||||
await vi.advanceTimersByTimeAsync(8000);
|
||||
await rejected;
|
||||
expect(deps.fetch.mock.calls[0]?.[1]?.signal?.aborted).toBe(true);
|
||||
expect(cancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("sanitizes provider responses and transport exceptions", async () => {
|
||||
for (const deps of [dependency(new Response(`private response ${key}`, { status: 500 })),
|
||||
{ fetch: vi.fn<typeof fetch>().mockRejectedValue(new Error(`private request ${key}`)) }]) {
|
||||
let failure: unknown;
|
||||
try { await probeKimiModels(model, key, deps); } catch (error) { failure = error; }
|
||||
expect(failure).toBeInstanceOf(KimiMetadataProbeError);
|
||||
expect(failure).toMatchObject({ code: "KIMI_PROVIDER_ERROR" });
|
||||
expect(JSON.stringify(failure)).not.toContain(key);
|
||||
expect(String(failure)).not.toContain(key);
|
||||
expect(failure).not.toHaveProperty("cause");
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,140 @@
|
||||
const MODELS_URL = "https://api.kimi.ai/coding/v1/models";
|
||||
const USER_AGENT = "KimiCLI/2.3.3";
|
||||
const MAX_BYTES = 256 * 1024;
|
||||
const TIMEOUT_MS = 8_000;
|
||||
|
||||
export type KimiMetadataProbeErrorCode =
|
||||
| "KIMI_AUTH_REQUIRED"
|
||||
| "KIMI_AUTH_REJECTED"
|
||||
| "KIMI_MODEL_UNAVAILABLE"
|
||||
| "KIMI_METADATA_TIMEOUT"
|
||||
| "KIMI_METADATA_INVALID"
|
||||
| "KIMI_PROVIDER_ERROR";
|
||||
|
||||
const messages: Record<KimiMetadataProbeErrorCode, string> = {
|
||||
KIMI_AUTH_REQUIRED: "Bind a valid company secret to KIMI_API_KEY.",
|
||||
KIMI_AUTH_REJECTED: "Kimi Code rejected the selected credential or account access. Check the credential and Code membership.",
|
||||
KIMI_MODEL_UNAVAILABLE: "The exact selected model is unavailable in the authenticated Kimi Code catalog.",
|
||||
KIMI_METADATA_TIMEOUT: "Kimi Code model verification timed out.",
|
||||
KIMI_METADATA_INVALID: "Kimi Code returned invalid model metadata.",
|
||||
KIMI_PROVIDER_ERROR: "Kimi Code model verification failed. Try again when the provider is available.",
|
||||
};
|
||||
|
||||
export class KimiMetadataProbeError extends Error {
|
||||
readonly code: KimiMetadataProbeErrorCode;
|
||||
readonly status?: number;
|
||||
|
||||
constructor(code: KimiMetadataProbeErrorCode, status?: number) {
|
||||
super(messages[code]);
|
||||
this.name = "KimiMetadataProbeError";
|
||||
this.code = code;
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
export interface KimiVerifiedModelMetadata {
|
||||
code: "kimi_metadata_verified";
|
||||
model: string;
|
||||
contextLength: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
function record(value: unknown): Record<string, unknown> | undefined {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : undefined;
|
||||
}
|
||||
|
||||
function selectedModel(payload: unknown, requestedModel: string): number {
|
||||
const data = record(payload)?.data;
|
||||
if (!Array.isArray(data)) throw new KimiMetadataProbeError("KIMI_METADATA_INVALID");
|
||||
const ids = new Set<string>();
|
||||
let contextLength: number | undefined;
|
||||
for (const value of data) {
|
||||
const row = record(value);
|
||||
const id = row?.id;
|
||||
const context = record(row?.limit)?.context;
|
||||
const parsed = typeof context === "number" || (typeof context === "string" && /^[0-9]+$/.test(context)) ? Number(context) : NaN;
|
||||
if (typeof id !== "string" || !id.trim() || ids.has(id) || !Number.isSafeInteger(parsed) || parsed <= 0) {
|
||||
throw new KimiMetadataProbeError("KIMI_METADATA_INVALID");
|
||||
}
|
||||
ids.add(id);
|
||||
if (id === requestedModel) contextLength = parsed;
|
||||
}
|
||||
if (contextLength === undefined) throw new KimiMetadataProbeError("KIMI_MODEL_UNAVAILABLE");
|
||||
return contextLength;
|
||||
}
|
||||
|
||||
export async function probeKimiModels(
|
||||
model: string,
|
||||
apiKey: string,
|
||||
testDependencies?: { fetch: typeof globalThis.fetch },
|
||||
): Promise<KimiVerifiedModelMetadata> {
|
||||
if (typeof apiKey !== "string" || !apiKey.trim() || Buffer.byteLength(apiKey) > 16 * 1024 || /[\0\r\n]/.test(apiKey)) {
|
||||
throw new KimiMetadataProbeError("KIMI_AUTH_REQUIRED");
|
||||
}
|
||||
if (typeof model !== "string" || !model.trim() || Buffer.byteLength(model) > 512 || model.includes("\0")
|
||||
|| Buffer.from(model).toString("utf8") !== model) throw new KimiMetadataProbeError("KIMI_MODEL_UNAVAILABLE");
|
||||
const controller = new AbortController();
|
||||
const timeoutError = new KimiMetadataProbeError("KIMI_METADATA_TIMEOUT");
|
||||
let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
|
||||
let response: Response | undefined;
|
||||
let rejectAbort!: (reason: unknown) => void;
|
||||
const aborted = new Promise<never>((_, reject) => { rejectAbort = reject; });
|
||||
const onAbort = () => {
|
||||
rejectAbort(timeoutError);
|
||||
void reader?.cancel().catch(() => {});
|
||||
};
|
||||
controller.signal.addEventListener("abort", onAbort, { once: true });
|
||||
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
||||
try {
|
||||
const request = Promise.resolve().then(() => (testDependencies?.fetch ?? globalThis.fetch)(MODELS_URL, {
|
||||
method: "GET", redirect: "error", signal: controller.signal,
|
||||
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json", "User-Agent": USER_AGENT },
|
||||
})).then(value => {
|
||||
if (controller.signal.aborted) {
|
||||
void value.body?.cancel().catch(() => {});
|
||||
throw timeoutError;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
response = await Promise.race([request, aborted]);
|
||||
if (!response.ok) throw new KimiMetadataProbeError(
|
||||
[401, 402, 403].includes(response.status) ? "KIMI_AUTH_REJECTED" : "KIMI_PROVIDER_ERROR", response.status,
|
||||
);
|
||||
const declaredLength = response.headers.get("content-length");
|
||||
if (declaredLength !== null && (!/^[0-9]+$/.test(declaredLength) || Number(declaredLength) > MAX_BYTES)) {
|
||||
controller.abort();
|
||||
throw new KimiMetadataProbeError("KIMI_METADATA_INVALID");
|
||||
}
|
||||
if (!response.body) throw new KimiMetadataProbeError("KIMI_METADATA_INVALID");
|
||||
reader = response.body.getReader();
|
||||
const chunks: Uint8Array[] = [];
|
||||
let bytes = 0;
|
||||
for (;;) {
|
||||
const chunk = await Promise.race([reader.read(), aborted]);
|
||||
if (controller.signal.aborted) throw timeoutError;
|
||||
if (chunk.done) break;
|
||||
bytes += chunk.value.byteLength;
|
||||
if (bytes > MAX_BYTES) {
|
||||
controller.abort();
|
||||
throw new KimiMetadataProbeError("KIMI_METADATA_INVALID");
|
||||
}
|
||||
chunks.push(chunk.value);
|
||||
}
|
||||
let payload: unknown;
|
||||
try { payload = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks, bytes))); }
|
||||
catch { throw new KimiMetadataProbeError("KIMI_METADATA_INVALID"); }
|
||||
const contextLength = selectedModel(payload, model);
|
||||
return { code: "kimi_metadata_verified", model, contextLength,
|
||||
message: "Kimi Code accepted the selected credential and listed the exact selected model. No model prompt was sent; inference and remaining quota were not tested." };
|
||||
} catch (error) {
|
||||
if (error instanceof KimiMetadataProbeError) throw error;
|
||||
throw new KimiMetadataProbeError(controller.signal.aborted ? "KIMI_METADATA_TIMEOUT" : "KIMI_PROVIDER_ERROR");
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
controller.signal.removeEventListener("abort", onAbort);
|
||||
if (reader) {
|
||||
void reader.cancel().catch(() => {});
|
||||
try { reader.releaseLock(); } catch {}
|
||||
} else { void response?.body?.cancel().catch(() => {}); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { constants } from "node:fs";
|
||||
import { lstat, open, readdir, realpath } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
export interface KimiRuntimeFile { path: string; sha256: string; size: number; executable: boolean }
|
||||
const MAX_FILES = 30_000;
|
||||
const MAX_FILE_BYTES = 384 * 1024 * 1024;
|
||||
const MAX_TREE_BYTES = 1024 * 1024 * 1024;
|
||||
const HASH_CONCURRENCY = 16;
|
||||
const safePath = (path: string) => !/[\u0000-\u001f\u007f\\]/.test(path) && path.split("/").every(part => part !== "" && part !== "." && part !== "..");
|
||||
|
||||
export function kimiRuntimeClosureDigest(entries: readonly KimiRuntimeFile[]): string {
|
||||
return createHash("sha256").update(JSON.stringify(entries)).digest("hex");
|
||||
}
|
||||
|
||||
export async function inventoryKimiRuntimeFiles(root: string): Promise<KimiRuntimeFile[]> {
|
||||
if (await realpath(root) !== root || !(await lstat(root)).isDirectory()) throw new Error("Kimi runtime root is not a physical directory");
|
||||
const files: { path: string; size: number; executable: boolean }[] = [];
|
||||
let total = 0;
|
||||
const visit = async (directory: string, prefix = ""): Promise<void> => {
|
||||
for (const name of (await readdir(directory)).sort()) {
|
||||
const path = prefix ? `${prefix}/${name}` : name;
|
||||
if (!safePath(path) || path.startsWith(".paperclip-") || path === "manifest.json") throw new Error("Kimi runtime contains an invalid path");
|
||||
const absolute = join(directory, name);
|
||||
const stat = await lstat(absolute);
|
||||
if (stat.isSymbolicLink() || await realpath(absolute) !== absolute) throw new Error("Kimi runtime contains a symbolic link");
|
||||
if (stat.isDirectory()) await visit(absolute, path);
|
||||
else {
|
||||
if (!stat.isFile() || stat.nlink !== 1 || stat.size > MAX_FILE_BYTES) throw new Error("Kimi runtime contains an invalid file");
|
||||
total += stat.size;
|
||||
if (files.length >= MAX_FILES || total > MAX_TREE_BYTES) throw new Error("Kimi runtime inventory exceeds its bound");
|
||||
files.push({ path, size: stat.size, executable: Boolean(stat.mode & 0o111) });
|
||||
}
|
||||
}
|
||||
};
|
||||
await visit(root);
|
||||
files.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
||||
const entries: KimiRuntimeFile[] = [];
|
||||
for (let index = 0; index < files.length; index += HASH_CONCURRENCY) {
|
||||
const batch = await Promise.allSettled(files.slice(index, index + HASH_CONCURRENCY).map(async entry => {
|
||||
const path = join(root, entry.path);
|
||||
const file = await open(path, 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 !== BigInt(entry.size) || Boolean(before.mode & 0o111n) !== entry.executable) throw new Error("Kimi runtime file changed before inventory");
|
||||
const digest = createHash("sha256");
|
||||
for await (const chunk of file.createReadStream({ autoClose: false })) digest.update(chunk);
|
||||
const after = await file.stat({ bigint: true });
|
||||
const named = await lstat(path, { bigint: true });
|
||||
if (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 runtime file changed during inventory");
|
||||
return { path: entry.path, sha256: digest.digest("hex"), size: entry.size, executable: entry.executable };
|
||||
} finally { await file.close(); }
|
||||
}));
|
||||
const failed = batch.find(result => result.status === "rejected");
|
||||
if (failed?.status === "rejected") throw failed.reason;
|
||||
for (const result of batch) if (result.status === "fulfilled") entries.push(result.value);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import { isSupportedAcpxProfileVersion } from "./profile-compatibility.js";
|
||||
// at 1. Update this table, with the reason, only for a deliberate reset.
|
||||
const PROFILE_VERSIONS: Readonly<Record<string, number>> = {
|
||||
antigravity: 1, claude: 1, codex: 1, copilot: 92, cursor: 17, grok: 20, pi: 37,
|
||||
// Retain the existing pending Kimi revision; only its release attestation changes.
|
||||
kimi: 16,
|
||||
};
|
||||
|
||||
describe("ACPX profile versions", () => {
|
||||
@@ -28,7 +30,7 @@ describe("historical ACPX profile decoding", () => {
|
||||
}
|
||||
});
|
||||
it("does not decode a revision newer than the current one", () => {
|
||||
for (const agent of ["copilot", "grok", "pi", "cursor", "antigravity"] as const) {
|
||||
for (const agent of ["copilot", "grok", "pi", "cursor", "antigravity", "kimi"] as const) {
|
||||
expect(isSupportedAcpxProfileVersion(agent, QUALIFIED_ACPX_PROFILE_DATA[agent].agentProfileVersion + 1)).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { verifyKimiInstallation } from "./kimi-installation.js";
|
||||
import { assertAcpxExecutionAdmission, type AcpxCandidateStartupAdmission } from "./candidate-admission.js";
|
||||
import { assertCopilotCredentials, classifyCopilotFailure } from "./copilot-profile.js";
|
||||
import { verifyCopilotInstallation } from "./copilot-installation.js";
|
||||
@@ -12,6 +13,7 @@ import { verifyQualifiedAcpxInstallation, type VerifiedAcpxInstallation } from "
|
||||
export async function verifyAcpxProfileInstallation(profile: QualifiedAcpxProfile, candidateAdmission?: AcpxCandidateStartupAdmission): Promise<VerifiedAcpxInstallation> {
|
||||
assertAcpxExecutionAdmission(profile, candidateAdmission);
|
||||
if (profile.agent === "antigravity") return verifyAntigravityInstallation(profile, candidateAdmission);
|
||||
if (profile.agent === "kimi") return verifyKimiInstallation(profile);
|
||||
if (profile.agent === "pi") return verifyPiInstallation(profile);
|
||||
if (profile.agent === "copilot") return verifyCopilotInstallation(profile);
|
||||
if (profile.agent === "cursor") {
|
||||
@@ -35,6 +37,9 @@ export async function assertAcpxProfileWorkspace(agent: QualifiedAcpxAgent, work
|
||||
/** Called with the sanitized launch environment, never ambient process.env. */
|
||||
export function assertAcpxProfileEnvironment(agent: QualifiedAcpxAgent, environment: Readonly<NodeJS.ProcessEnv>): void {
|
||||
if (agent === "copilot") assertCopilotCredentials(environment);
|
||||
if (agent === "kimi" && !environment.KIMI_API_KEY?.trim()) {
|
||||
throw Object.assign(new Error("Kimi Code credentials are missing. Bind a company secret to KIMI_API_KEY in the agent environment."), { code: "KIMI_CREDENTIALS_MISSING", retryable: false });
|
||||
}
|
||||
if (agent === "antigravity" && !environment.GEMINI_API_KEY?.trim() && !environment.PAPERCLIP_ACPX_ANTIGRAVITY_AUTH_JSON_SECRET?.trim()) {
|
||||
throw Object.assign(new Error("Google Antigravity requires an explicitly bound Google runtime connection. Connect a Google account or Gemini API key before starting a task."), { code: "ANTIGRAVITY_CREDENTIALS_MISSING", retryable: false });
|
||||
}
|
||||
@@ -77,3 +82,8 @@ export async function probeAcpxPiInstallation(model: string): Promise<void> {
|
||||
const installation = await verifyAcpxProfileInstallation(resolveQualifiedAcpxProfile("pi", model));
|
||||
await (await installation.openCommand()).close();
|
||||
}
|
||||
|
||||
export async function probeAcpxKimiInstallation(model: string): Promise<void> {
|
||||
const installation = await verifyAcpxProfileInstallation(resolveQualifiedAcpxProfile("kimi", model));
|
||||
await (await installation.openCommand()).close();
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { constants, closeSync, fstatSync, lstatSync, openSync, readFileSync, rea
|
||||
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
type NativeProvider = "cursor" | "copilot" | "pi" | "antigravity";
|
||||
type NativeProvider = "cursor" | "copilot" | "pi" | "kimi" | "antigravity";
|
||||
const RUNNER_PACKAGE_NAME = "@paperclipai/paperclip-runner";
|
||||
const SERVER_PACKAGE_NAME = "@paperclipai/server";
|
||||
function assertServerVendorLayout(root: string): void {
|
||||
@@ -17,7 +17,7 @@ function assertServerVendorLayout(root: string): void {
|
||||
|
||||
/** Resolve only runner-owned package assets, including descriptor-loaded sidecars. */
|
||||
export function resolveRunnerProviderAssetsRoot(moduleUrl: string, provider: NativeProvider): string {
|
||||
if (!["cursor", "copilot", "pi", "antigravity"].includes(provider)) throw new Error("Unknown native ACP provider assets");
|
||||
if (!["cursor", "copilot", "pi", "kimi", "antigravity"].includes(provider)) throw new Error("Unknown native ACP provider assets");
|
||||
const boundRoot = process.env.PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT;
|
||||
const boundManifest = process.env.PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST;
|
||||
let packageRoot: string;
|
||||
|
||||
@@ -173,6 +173,7 @@ export interface AcpxRuntimePortOpenOptions {
|
||||
stateDirectory: string;
|
||||
providerSessionKey: string;
|
||||
permissionMode: NativeAcpxPermissionMode;
|
||||
readOnly?: boolean;
|
||||
mode?: string;
|
||||
piThinkingLevel?: PiThinkingLevel;
|
||||
claudeEffort?: "low" | "medium" | "high" | "xhigh" | "max";
|
||||
@@ -394,7 +395,7 @@ export class AcpxRuntimeHost {
|
||||
grokEffort: resolveGrokEffort(options.agent, options.grokEffort),
|
||||
grokDisableWebSearch: resolveGrokDisableWebSearch(options.agent, options.grokDisableWebSearch),
|
||||
...(preconfigured || preparedHome ? { homeBinding: preconfiguredHomeEnvironment(options.environment) } : {}),
|
||||
...(["cursor", "copilot", "pi"].includes(options.agent) && options.providerPolicy !== undefined
|
||||
...(["cursor", "copilot", "pi", "kimi"].includes(options.agent) && options.providerPolicy !== undefined
|
||||
? { providerPolicy: options.providerPolicy } : {}),
|
||||
}),
|
||||
dependencies.retainAdmissionCleanup,
|
||||
@@ -558,7 +559,7 @@ export class AcpxRuntimeHost {
|
||||
// agent and run. Environment/config values cannot widen the grant. Each
|
||||
// resumed process receives the newly registered copy; collection already
|
||||
// requires verified provider shutdown in the native executor.
|
||||
const agentFiles = ["cursor", "copilot", "pi"].includes(options.agent)
|
||||
const agentFiles = ["cursor", "copilot", "pi", "kimi"].includes(options.agent)
|
||||
? bindAcpxAgentFiles(options.runtimeContext, [sandbox.root,
|
||||
...(installation.agentServerPackageJsonPath === null ? [] : [dirname(installation.agentServerPackageJsonPath)]),
|
||||
]) : null;
|
||||
@@ -601,7 +602,7 @@ export class AcpxRuntimeHost {
|
||||
PAPERCLIP_PI_READ_ROOTS: JSON.stringify([...(options.providerPolicy?.readRoots ?? []), ...skills.readRoots]),
|
||||
});
|
||||
}
|
||||
if (!preconfigured && !preparedHome && (options.agent === "claude" || options.agent === "grok")) {
|
||||
if (!preconfigured && !preparedHome && (options.agent === "claude" || options.agent === "grok" || options.agent === "kimi")) {
|
||||
// The lifetime lease proves the previous provider has stopped. Refresh
|
||||
// the assigned snapshot before every launch, including durable resume;
|
||||
// Claude discovers user skills beneath its isolated CLAUDE_CONFIG_DIR.
|
||||
@@ -666,6 +667,7 @@ export class AcpxRuntimeHost {
|
||||
providerSessionKey: binding.profileSessionKey,
|
||||
restoringSession: options.expectedIdentity !== undefined,
|
||||
permissionMode: binding.permissionMode,
|
||||
readOnly: options.providerPolicy?.readOnly,
|
||||
mode: binding.mode,
|
||||
piThinkingLevel: binding.piThinkingLevel,
|
||||
claudeEffort: binding.claudeEffort,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { kimiManagedConfiguration } from "./kimi-config.js";
|
||||
import { kimiInstructionBinding } from "./kimi-instructions.js";
|
||||
import { AGENT_OBSERVABILITY_ENV_KEYS, codexObservabilityToml } from "../agent-observability.js";
|
||||
import { piProviderConfiguration } from "./pi-provider-config.js";
|
||||
import { configuredEnvironmentKeys } from "../../configured-environment.js";
|
||||
@@ -421,6 +423,9 @@ export async function prepareAcpxRuntimeSandbox(input: {
|
||||
workspaceRecordPath,
|
||||
`${input.binding.workspacePath}\n`,
|
||||
);
|
||||
if (input.agent === "kimi") {
|
||||
await writePrivateFile(join(agentHomeDirectory, "config.toml"), kimiManagedConfiguration(input.binding.requestedModel));
|
||||
}
|
||||
if (input.agent === "pi") {
|
||||
const configuration = piProviderConfiguration(input.environment);
|
||||
if (configuration) await writePrivateFile(join(agentHomeDirectory, "models.json"), configuration.json);
|
||||
@@ -533,6 +538,7 @@ export async function prepareAcpxRuntimeSandbox(input: {
|
||||
XDG_CACHE_HOME: cacheDirectory,
|
||||
PAPERCLIP_ACPX_PROFILE: input.agent,
|
||||
PAPERCLIP_ACPX_ISOLATED_CONTEXT: "1",
|
||||
...(input.agent === "kimi" ? { KIMI_CODE_HOME: agentHomeDirectory, PAPERCLIP_KIMI_INSTRUCTIONS: kimiInstructionBinding(policy.systemInstructions).payload } : {}),
|
||||
...(input.agent === "antigravity" ? { GEMINI_HOME: agentHomeDirectory, AGY_ACP_FORCE_FILE_STORAGE: "1" } : {}),
|
||||
...(input.agent === "grok" ? { GROK_HOME: agentHomeDirectory, NO_BROWSER: "1", GROK_DISABLE_AUTOUPDATER: "1" } : {}),
|
||||
...(input.agent === "pi"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from "./configured-environment.js";
|
||||
export * from "./catalog/index.js";
|
||||
export { CURSOR_DISTRIBUTION_PINS } from "./drivers/acpx/generated-profiles.js";
|
||||
export { KIMI_DISTRIBUTION_CLOSURE_SHA256 } from "./drivers/acpx/kimi-closure-pins.js";
|
||||
export * from "./contracts/control-plane-port.js";
|
||||
export * from "./contracts/completion-result.js";
|
||||
export * from "./contracts/codex.js";
|
||||
@@ -101,7 +102,7 @@ export * as acceptedCapabilitySemanticTools from "./semantic-tools/index.js";
|
||||
export * from "./compatibility.js";
|
||||
|
||||
export { probeAcpxClaudeInstallation, probeAcpxGrokInstallation } from "./drivers/acpx/installation-integrity.js";
|
||||
export { probeAcpxPiInstallation } from "./drivers/acpx/profile-installation.js";
|
||||
export { probeAcpxPiInstallation, probeAcpxKimiInstallation } from "./drivers/acpx/profile-installation.js";
|
||||
export { PROVIDER_PACK_COMPATIBILITY, bundledRemoteProviderPackManifestPath, bundledRemoteRunnerBinary } from "./live/bundled-remote-provider-pack.js";
|
||||
|
||||
export { RunnerdMuseDriver, describeRunnerdMuseDriver, type RunnerdMuseDriverOptions } from "./drivers/muse/runnerd-muse-driver.js";
|
||||
|
||||
@@ -9,6 +9,8 @@ export * from "./linux-process-start.js";
|
||||
export { probeAcpxClaudeInstallation, probeAcpxGrokInstallation } from "../drivers/acpx/installation-integrity.js";
|
||||
export { probeCopilotMetadata, validateCopilotMetadata, type CopilotMetadataResult } from "../drivers/copilot-metadata-probe.js";
|
||||
|
||||
export { probeAcpxCursorInstallation, probeAcpxPiInstallation } from "../drivers/acpx/profile-installation.js";
|
||||
export { probeAcpxCursorInstallation, probeAcpxPiInstallation, probeAcpxKimiInstallation } from "../drivers/acpx/profile-installation.js";
|
||||
export { probeKimiModels, KimiMetadataProbeError, type KimiVerifiedModelMetadata } from "../drivers/acpx/kimi-models.js";
|
||||
export { probeKimiMetadata, validateKimiMetadata, kimiMetadataProbeDigest, kimiMetadataProbeNodeDigest, type KimiMetadataResult } from "../drivers/acpx/kimi-metadata-probe.js";
|
||||
|
||||
export { bundledRemoteProviderPackManifestPath, bundledRemoteRunnerBinary } from "./bundled-remote-provider-pack.js";
|
||||
@@ -0,0 +1,37 @@
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { type AcpxCandidateAdmission } from "../drivers/acpx/candidate-admission.js";
|
||||
import { KIMI_DISTRIBUTION_CLOSURE_SHA256 } from "../drivers/acpx/kimi-closure-pins.js";
|
||||
import { resolveQualifiedAcpxProfile } from "../drivers/acpx/qualified-profiles.js";
|
||||
import { createRunnerdCodexTransport } from "./runnerd-codex-transport.js";
|
||||
|
||||
const candidate = (remote = false): AcpxCandidateAdmission => ({ schema: "paperclip.acpx-candidate-admission.v1", companyId: "company-kimi", agent: "kimi", model: "kimi-for-coding",
|
||||
profileDigest: resolveQualifiedAcpxProfile("kimi", "kimi-for-coding").commandDigest,
|
||||
nativeClosureDigest: `sha256:${KIMI_DISTRIBUTION_CLOSURE_SHA256[remote ? "linux-x64" : "darwin-arm64"]}`,
|
||||
platform: remote ? "linux" : "darwin", architecture: remote ? "x64" : "arm64", expiresAt: Date.now() + 60_000 });
|
||||
|
||||
describe("Kimi trusted qualification startup", () => {
|
||||
it("retains exact provider and isolated home authority for a valid Kimi grant", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "kimi-candidate-startup-"));
|
||||
try {
|
||||
const options = { provider: "acpx" as const, acpxAgent: "kimi" as const, stateDirectory: root, acpxCandidateAdmission: candidate() };
|
||||
for (const changes of [{ provider: "codex" as const }, { provider: "opencode" as const }, { homeMode: "preconfigured" as const },
|
||||
{ acpxAgent: "copilot" as const }, { acpxCandidateAdmission: { ...candidate(), profileDigest: `sha256:${"f".repeat(64)}` } }]) {
|
||||
expect(() => createRunnerdCodexTransport({ ...options, ...changes })).toThrow();
|
||||
}
|
||||
expect(() => createRunnerdCodexTransport({ ...options, acpxCandidateAdmission: undefined })).toThrow("evaluation opt-in");
|
||||
} finally { await rm(root, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
it.each([false, true])("keeps ordinary Kimi warm transport capability under target-bound authority: remote=%s", async remote => {
|
||||
const root = await mkdtemp(join(tmpdir(), "kimi-candidate-warm-"));
|
||||
try {
|
||||
const owner = createRunnerdCodexTransport({ provider: "acpx", acpxAgent: "kimi", stateDirectory: root,
|
||||
acpxCandidateAdmission: candidate(remote), lifecyclePolicy: { mode: "warm", idleTimeoutMs: 1000 },
|
||||
...(remote ? { runnerFilesystemRoot: "/owned-runner" } : {}) });
|
||||
await owner.transport.close();
|
||||
} finally { await rm(root, { recursive: true, force: true }); }
|
||||
});
|
||||
});
|
||||
@@ -1245,7 +1245,7 @@ export interface CapabilityRunnerdCodexTransportOptions extends RunnerExecutionD
|
||||
opencodePermissionMode?: NativeOpenCodePermissionMode;
|
||||
acpxAgent?: QualifiedAcpxAgent;
|
||||
/** Explicit evaluation-only candidate selection, never derived from persisted session input. */
|
||||
acpxCandidateProfile?: "pi" | "cursor" | "copilot";
|
||||
acpxCandidateProfile?: "pi" | "cursor" | "copilot" | "kimi";
|
||||
/** Server/operator authority; never derived from persisted agent or provider input. */
|
||||
acpxCandidateAdmission?: AcpxCandidateAdmission;
|
||||
acpxPermissionMode?: NativeAcpxPermissionMode;
|
||||
@@ -3097,7 +3097,7 @@ function acpxProviderPackageAuthority(
|
||||
// Native provider closures belong to the runner package itself. Their asset
|
||||
// guard requires the bound manifest directly inside that same package root;
|
||||
// the workspace dependency root used by JS providers is a different owner.
|
||||
if (agent === "copilot" || agent === "pi" || agent === "antigravity") {
|
||||
if (agent === "copilot" || agent === "pi" || agent === "kimi" || agent === "antigravity") {
|
||||
return {
|
||||
root: sidecarPackageRoot,
|
||||
manifest: resolve(sidecarPackageRoot, "package.json"),
|
||||
@@ -3146,7 +3146,7 @@ function acpxRunnerLaunchProfile(
|
||||
`sha256:${createHash("sha256").update(readFileSync(path)).digest("hex")}`;
|
||||
let candidateAdmission: AcpxCandidateStartupAdmission | undefined;
|
||||
if (options.acpxCandidateAdmission) {
|
||||
if (!identity || options.runnerFilesystemRoot) throw new Error("ACPX candidate requires an actual local startup identity");
|
||||
if (!identity || options.acpxCandidateAdmission.agent === "antigravity" && options.runnerFilesystemRoot) throw new Error("ACPX candidate requires an actual authorized startup identity");
|
||||
candidateAdmission = bindAcpxCandidateAdmission(options.acpxCandidateAdmission, identity);
|
||||
}
|
||||
if (!options.runnerFilesystemRoot) {
|
||||
@@ -3200,6 +3200,7 @@ function acpxRunnerLaunchProfile(
|
||||
);
|
||||
}
|
||||
return {
|
||||
...(candidateAdmission ? { candidateAdmission } : {}),
|
||||
authorityDigest,
|
||||
command,
|
||||
commandSha256,
|
||||
@@ -3392,7 +3393,7 @@ function createCapabilityRunnerdProviderEnvironmentBase(input: {
|
||||
PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT: providerPackageAuthority.root,
|
||||
PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST:
|
||||
providerPackageAuthority.manifest,
|
||||
...(!input.options.acpxCandidateAdmission && input.options.providerRecoveryPolicy ===
|
||||
...(input.options.acpxCandidateAdmission?.agent !== "antigravity" && input.options.providerRecoveryPolicy ===
|
||||
"allow_replacement_after_governed_wait"
|
||||
? {
|
||||
PAPERCLIP_ACPX_PROVIDER_RECOVERY_POLICY:
|
||||
@@ -3685,18 +3686,21 @@ class DurablePrpCodexTransport implements CodexAppServerTransport {
|
||||
}
|
||||
if (options.acpxCandidateAdmission !== undefined) {
|
||||
const candidate = options.acpxCandidateAdmission;
|
||||
if (options.provider !== "acpx") {
|
||||
throw new Error("ACPX candidate admission requires the exact ACPX provider");
|
||||
}
|
||||
const admitted = parseAcpxCandidateAdmission(candidate, { companyId: candidate.companyId, agent: options.acpxAgent ?? "", model: candidate.model });
|
||||
this.options = { ...this.options, acpxCandidateAdmission: Object.freeze(admitted), providerRecoveryPolicy: "same_session_only" };
|
||||
this.prepareConversationResume = params => this.#prepareCandidateConversationResume(params);
|
||||
if (options.provider !== "acpx" || process.platform !== "darwin" || process.arch !== "arm64"
|
||||
this.options = { ...this.options, acpxCandidateAdmission: Object.freeze(admitted), ...(candidate.agent === "antigravity" ? { providerRecoveryPolicy: "same_session_only" as const } : {}) };
|
||||
if (candidate.agent === "antigravity") this.prepareConversationResume = params => this.#prepareCandidateConversationResume(params);
|
||||
if (candidate.agent === "antigravity" && (options.provider !== "acpx" || process.platform !== "darwin" || process.arch !== "arm64"
|
||||
|| options.runnerFilesystemRoot || options.prepareLaunch || options.runnerProcessLauncher
|
||||
|| options.homeMode === "preconfigured" || options.lifecyclePolicy?.mode === "warm"
|
||||
|| options.adoptExistingRunner || options.resumeActiveTurnId
|
||||
) {
|
||||
)) {
|
||||
throw new Error("ACPX candidate admission supports only a fresh local isolated per-turn execution");
|
||||
}
|
||||
}
|
||||
if (options.acpxCandidateAdmission && options.resumeProviderSession) {
|
||||
if (options.acpxCandidateAdmission?.agent === "antigravity" && options.resumeProviderSession) {
|
||||
const prior = options.resumeProviderSession;
|
||||
const identity = record(prior.providerIdentity);
|
||||
if (!prior.driverSessionId?.trim() || !prior.providerSessionId?.trim()
|
||||
@@ -3709,7 +3713,7 @@ class DurablePrpCodexTransport implements CodexAppServerTransport {
|
||||
if (options.provider === "acpx" && options.acpxAgent !== undefined
|
||||
&& ACPX_CAPABILITY_PROFILES[options.acpxAgent].qualification === "pending"
|
||||
&& options.acpxCandidateProfile !== options.acpxAgent
|
||||
&& !(options.acpxAgent === "antigravity" && options.acpxCandidateAdmission)) {
|
||||
&& !(options.acpxCandidateAdmission?.agent === options.acpxAgent)) {
|
||||
throw new Error("The candidate ACPX profile requires explicit evaluation opt-in");
|
||||
}
|
||||
this.#failureSignal = new Promise<never>((_resolve, reject) => {
|
||||
@@ -4268,7 +4272,10 @@ class DurablePrpCodexTransport implements CodexAppServerTransport {
|
||||
this.#runAttachTemplate = structuredClone(runAttachTemplate);
|
||||
const payload = {
|
||||
...runAttachTemplate,
|
||||
paperclipNextAuthority: { identity: desired, connection },
|
||||
paperclipNextAuthority: { identity: desired, connection,
|
||||
...(this.options.acpxCandidateAdmission?.agent === "kimi"
|
||||
? { candidateAdmission: bindAcpxCandidateAdmission(this.options.acpxCandidateAdmission, desired) } : {}),
|
||||
},
|
||||
};
|
||||
core.queueCommand("run.attach", payload, commandId, true);
|
||||
// ACPX run attachment checkpoints the old sidecar and starts a fresh
|
||||
@@ -5385,7 +5392,7 @@ class DurablePrpCodexTransport implements CodexAppServerTransport {
|
||||
}
|
||||
|
||||
async #resumeWork(): Promise<void> {
|
||||
if (this.options.acpxCandidateAdmission) throw new Error("ACPX candidate durable runner recovery is not admitted");
|
||||
if (this.options.acpxCandidateAdmission?.agent === "antigravity") throw new Error("ACPX candidate durable runner recovery is not admitted");
|
||||
const desiredIdentity = this.options.prpIdentity;
|
||||
if (desiredIdentity === undefined) {
|
||||
throw new Error("PRP provider resume state is unavailable");
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
# Kimi Code native boundary
|
||||
|
||||
Research date: 2026-10-28. These are source observations and deterministic
|
||||
package checks. They are not authenticated Product E2E qualification.
|
||||
|
||||
The maintained runtime is **`@moonshot-ai/kimi-code@2.3.3`**, release commit
|
||||
[`ec495f0`](https://github.com/MoonshotAI/kimi-code/releases/tag/%40moonshot-ai%2Fkimi-code%402.3.3).
|
||||
It is a Node.js/TypeScript application distributed both as a packaged native
|
||||
executable and an npm package. The Python
|
||||
[`MoonshotAI/kimi-cli`](https://github.com/MoonshotAI/kimi-cli) repository was
|
||||
archived on September 23, 2026. Its Wire 1.10 documentation does not describe
|
||||
the maintained runtime's public interface. The new npm bundle is inspectable
|
||||
JavaScript; its internal ACP/core/Klient packages are private workspace packages.
|
||||
|
||||
## Selected transport and instruction delivery
|
||||
|
||||
[`kimi acp`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/docs/en/reference/kimi-acp.md)
|
||||
speaks stdio JSON-RPC ACP. The maintained alternative is the REST/WebSocket
|
||||
[`kimi web`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/docs/en/reference/server-api.md)
|
||||
server. ACP reuses the existing Runner integration and avoids owning a second
|
||||
HTTP-server lifecycle.
|
||||
|
||||
Native `AcpServer.newSession` accepts the workspace, extra directories and MCP
|
||||
servers; load/resume accept current ephemeral MCP configuration. It does not
|
||||
accept system instructions. The ACP CLI only consumes its login/region flags;
|
||||
ordinary `--agent-file` does not establish ACP instruction admission.
|
||||
|
||||
The owned patch in `scripts/patch-kimi-runtime.mjs` verifies the entire official
|
||||
npm `dist/main.mjs` SHA-256 before matching each unique source anchor. The
|
||||
materializer verifies the complete patched output hash. The embedded helper
|
||||
uses a single canonical owned source string so the raw patcher and installed
|
||||
esbuild provisioner produce identical native bytes. The patch reads one
|
||||
bounded process-owned `PAPERCLIP_KIMI_INSTRUCTIONS` JSON snapshot, validates its
|
||||
content digest and byte length, then removes that variable from the environment
|
||||
inherited by tools. Exact literal bytes are appended after native profile
|
||||
composition through `AgentProfileService.systemPrompt`.
|
||||
|
||||
The native requester uses `getSystemPrompt()` in `captureTurnRequestConfig`
|
||||
and `createModelRequest`; compaction uses the same getter. A restored or
|
||||
recomposed profile therefore still receives the current literal suffix.
|
||||
At the ACP native-runtime binding callback, the live main-agent scope reads
|
||||
its effective `getSystemPrompt()`. Only that readback produces the session
|
||||
receipt returned by new/load/resume. The receipt carries the bound-content
|
||||
digest/length and the effective native prompt digest/length. Runner must match
|
||||
the response to the active request and connection before sending a prompt.
|
||||
The receipt proves delivery; an authenticated behavior case remains required.
|
||||
|
||||
Relevant source: [`profileService.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/agent-core-v2/src/agent/profile/profileService.ts),
|
||||
[`llmRequesterService.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/agent-core-v2/src/agent/llmRequester/llmRequesterService.ts),
|
||||
[`server.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/acp-server/src/server.ts),
|
||||
[`start.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/acp-server/src/start.ts).
|
||||
|
||||
## Capability and loss inventory
|
||||
|
||||
| Native capability | ACP boundary and release implication |
|
||||
| --- | --- |
|
||||
| Model selection | `session/set_config_option` with `configId: model` returns current `configOptions`; `session/set_model` returns `{}` and emits a config update. Require the exact model acknowledgment before a prompt. Model aliases and provider-native model IDs are separate config fields. |
|
||||
| Thinking | Config option exposes declared effort values or on/off; unsupported values fail. It must be tested for the selected account/model before advertising it. |
|
||||
| Tools | Start, streaming arguments, full input, result/error and file diffs are projected. Native `tool.progress` only projects status text; stdout/stderr/progress/custom updates are dropped. |
|
||||
| Reasoning | Thinking deltas become ACP thought chunks. Apply the existing Runner reasoning visibility policy. |
|
||||
| Assistant messages | Upstream ACP emits text without the native step identity, merging progress and final messages. Patch v5 forwards actual main-agent step UUID/start/end facts with their native finish reason. The provider projection preserves separate messages, clears tool-use progress from final selection, and rejects incomplete provenance. |
|
||||
| Children | ACP subscribes only to the main agent. Native child activity, identity and usage are not preserved. Disable native delegation for the initial supported release. |
|
||||
| Questions | `elicitation.form` carries all questions and multi-selection. Permission fallback drops every question after the first, converts multi-selection to single-selection, and cannot represent Other/free text. Do not advertise native questions from fallback parsing; semantic Paperclip questions require their own live continuation proof. |
|
||||
| Plans | Todos become ACP plan updates. `ExitPlanMode` approvals use the permission channel and plan-specific options. Native plan review, feedback and post-accept behavior need separate qualification. |
|
||||
| Permission | Native normal mode has auto-approval heuristics and prior approval rules. The managed patch asks Runner for every tool operation, includes the actual `toolInput` as `rawInput`, removes session-wide approval choices, and disables ambient hooks/MCP/profiles. |
|
||||
| Usage | ACP's `usage_update.used` is current context occupancy and `size` is model capacity. It is not per-turn input/output usage or cost; cost is omitted. Internal Klient has richer token counters, but the selected ACP path does not publish them. Do not turn these context values into charges or a dollar-budget guarantee. |
|
||||
| Failures | Upstream ACP maps non-auth failed turns to `end_turn`. The managed patch preserves typed auth failure and rejects every other native failed turn on the prompt RPC. |
|
||||
| Continuity | New/load/resume/list/fork/close/delete are implemented. Load replays history; resume skips replay. A cold restore accepts fresh ephemeral MCP config; loading an already-live session ignores replacement MCP configuration. Reopen after a configuration change requires a new owned process. |
|
||||
| Stop and shutdown | Native cancel is turn-addressed and handles launch races. ACP close cancels/disposes the session. Connection EOF starts a flush/dispose path. These implementations do not prove bounded process-tree cleanup; test spontaneous child/process loss and forced termination independently. |
|
||||
| Steering | Native Klient and REST have steering/queued prompts. ACP has no steering method. Sending a second prompt does not prove same-turn steering. |
|
||||
| Media | ACP advertises image and text embedded-resource support; blob resources are dropped and audio is unsupported. End-to-end Runner forwarding still needs its own proof. |
|
||||
|
||||
Source: [`session.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/acp-server/src/session.ts),
|
||||
[`events-map.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/acp-server/src/events-map.ts),
|
||||
[`interaction-bridge.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/acp-server/src/interaction-bridge.ts),
|
||||
[`question.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/acp-server/src/question.ts),
|
||||
[`approval.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/acp-server/src/approval.ts).
|
||||
|
||||
## Configuration and authority
|
||||
|
||||
An empty isolated `KIMI_CODE_HOME` does **not** make an ambient `KIMI_API_KEY`
|
||||
usable automatically. A provider must explicitly declare `api_key_env`.
|
||||
Use the selected provider endpoint and a configured model alias whose `model`
|
||||
is the caller's exact native model ID. `max_context_size` is required by the
|
||||
native schema; it is configuration metadata, not entitlement evidence.
|
||||
Strip unselected `KIMI_MODEL_*` variables, which can synthesize a competing
|
||||
provider/model. Do not import ambient OAuth credentials, MCP grants or hooks.
|
||||
|
||||
The managed patch keeps user skills only under the owned Kimi home and drops
|
||||
project/global skill directories. It empties ambient workspace MCP baseline
|
||||
while leaving ACP session MCP separate. Root configuration must disable
|
||||
native delegation, cron/background task tools and automatic backgrounding;
|
||||
keep-alive-on-exit stays off. Removing TaskList/TaskOutput/TaskStop makes native
|
||||
`Bash.allowBackground()` false and causes explicit background requests to fail.
|
||||
|
||||
Native `HostProcessService.buildEnv()` otherwise inherits the complete provider
|
||||
environment, including `KIMI_API_KEY`; Bash supplies only noninteractive
|
||||
overrides. The patch always builds an explicit child environment and removes
|
||||
`KIMI_API_KEY` and `PAPERCLIP_KIMI_INSTRUCTIONS` after merging overrides. The
|
||||
native PTY path uses the same filter. Assigned task variables remain available;
|
||||
the provider process retains its key for model authentication. This is a child
|
||||
environment boundary, not an operating-system sandbox for arbitrary native
|
||||
filesystem or shell tools. Read-only work must use the controller's scoped
|
||||
semantic tools.
|
||||
|
||||
Patch v4 also ignores the ACP client's terminal capability. Otherwise native
|
||||
`AcpProcessService` sends Bash to ACPX's separate terminal process, bypassing
|
||||
the provider-owned environment filter and dropping configured task variables.
|
||||
Keeping the existing native local process service makes the filter apply on
|
||||
the actual Runner execution path. The controlled-peer check requires ACPX to
|
||||
advertise its normal terminal capability while observing zero `terminal/create`
|
||||
requests from Kimi.
|
||||
|
||||
Native OAuth resolves the access token into the same `{ apiKey }` credential
|
||||
as an explicit API-key provider. A selected unexpired access token can bound
|
||||
a finite test, but that explicit binding has no OAuth refresh behavior. Never
|
||||
copy refresh tokens into qualification homes or describe this as managed
|
||||
OAuth support.
|
||||
|
||||
The setup Test can verify Code metadata without inference through
|
||||
`probeKimiModels(model, apiKey)`. It sends one GET to the fixed global
|
||||
`https://api.kimi.ai/coding/v1/models` endpoint, using the explicitly selected
|
||||
key and pinned `KimiCLI/2.3.3` user agent. Native Kimi uses this endpoint for
|
||||
both OAuth and API-key providers and treats HTTP 401/402/403 as credential or
|
||||
membership-access failures. The probe requires a valid model catalog with the
|
||||
exact caller-selected ID and positive context metadata. It never substitutes a
|
||||
model or imports an ambient credential. Redirects are rejected, streamed JSON
|
||||
is capped at 256 KiB, and the eight-second deadline aborts both the request and
|
||||
response body. Errors contain fixed messages and typed codes, not provider
|
||||
response text or credential values.
|
||||
|
||||
A successful metadata probe establishes catalog authentication and exact model
|
||||
listing. It does not establish successful inference, remaining quota, usage or
|
||||
charges. The native ACP catalog alone cannot establish authentication because
|
||||
it projects local configuration. Normal model and instruction admission still
|
||||
applies before a task prompt.
|
||||
|
||||
Source: [`overrides.md`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/docs/en/configuration/overrides.md),
|
||||
[`config-files.md`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/docs/en/configuration/config-files.md),
|
||||
[`credentials.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/agent-core-v2/src/human/credentials/credentials.ts),
|
||||
[`bashTool.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/agent-core-v2/src/agent/tools/os/bash/bashTool.ts),
|
||||
[`hostProcessService.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/agent-core-v2/src/os/backends/node-local/hostProcessService.ts),
|
||||
[`hostTerminalService.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/agent-core-v2/src/os/backends/node-local/hostTerminalService.ts),
|
||||
[`acpConnection.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/acp-server/src/acp-fs/acpConnection.ts),
|
||||
[`acpTerminalRunner.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/acp-server/src/acp-terminal/acpTerminalRunner.ts),
|
||||
[`managed-kimi-code.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/oauth/src/managed-kimi-code.ts),
|
||||
[`refreshProviderModels.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/oauth/src/refreshProviderModels.ts).
|
||||
|
||||
## Reproducible boundary check
|
||||
|
||||
From a clean verified npm installation, provide its unmodified `dist/main.mjs`
|
||||
to the package contract cell:
|
||||
|
||||
```sh
|
||||
PAPERCLIP_KIMI_VENDOR_MAIN=/absolute/path/to/dist/main.mjs \
|
||||
node --test packages/paperclip-runner/scripts/patch-kimi-runtime.test.mjs
|
||||
```
|
||||
|
||||
This executes the actual pinned getter and ACP lifecycle/error methods with
|
||||
engine/transport dependencies doubled. It verifies literal Unicode/whitespace,
|
||||
explicit empty content, native readback, create/load/resume receipts, missing
|
||||
receipts, ambient-source isolation, host permission delegation, malformed
|
||||
bindings, child environment filtering, esbuild bundle reproducibility and
|
||||
terminal failure mapping. It performs no inference and does not
|
||||
replace installed-package, authenticated semantic-tool, browser persistence,
|
||||
warm recovery or physical-cleanup acceptance.
|
||||
|
||||
## Controlled model-service regression
|
||||
|
||||
After installing the fixed runtime assets, a second opt-in cell runs the real
|
||||
pinned Kimi command through the real ACPX manager against a loopback-only,
|
||||
scripted OpenAI-compatible service and MCP receiver:
|
||||
|
||||
```sh
|
||||
PAPERCLIP_TEST_KIMI_MODEL_PEER=1 \
|
||||
node --test packages/paperclip-runner/scripts/patch-kimi-runtime.test.mjs
|
||||
```
|
||||
|
||||
The test changes only its private native test configuration below the production
|
||||
boundary. It does not add an endpoint override to production configuration, use
|
||||
a provider credential, or call an external model. It captures exact instruction
|
||||
bytes at the native HTTP request, fragmented/interleaved MCP JSON including a
|
||||
terminal `{}`, accepted tool results in the next model request, Write denial
|
||||
before any file effect, a native Bash credential fence, typed provider failure,
|
||||
new/resume admission and bounded owned-process closure. The production patch
|
||||
keeps Bash in the provider's own local process service even when ACPX advertises
|
||||
client terminals. ACPX prefers `session/resume` when advertised; the
|
||||
installed-runtime metadata smoke checks `session/load` separately.
|
||||
|
||||
The first controlled-peer attempt against patch v1 failed: the native blocking
|
||||
approval payload omitted `toolInput`, although a sibling native event carried
|
||||
it. The ACP callback therefore lost `rawInput`, and the strict host rejected
|
||||
the operation before the MCP receiver ran. That failure is retained as the
|
||||
reason for patch v2: add the original arguments at native `approvalRequest`
|
||||
construction before the interaction kernel, then preserve them at the ACP edge.
|
||||
An empty object remains a complete valid argument. A passing simulated service
|
||||
cell remains protocol-boundary evidence and does not qualify model behavior,
|
||||
account entitlement, browser journeys or Daytona delivery.
|
||||
|
||||
The v2 controlled-peer cell passed on 2026-10-28 against the verified
|
||||
`darwin-arm64` closure through the actual pinned ACPX manager: five simulated
|
||||
model requests, two accepted MCP receiver calls including `{}`, one denied
|
||||
Write with independent before/after file-absence checks, typed failed-turn
|
||||
settlement and two closed native processes. Both initial and resumed native
|
||||
acknowledgments matched the SHA-256 of the complete system prompt observed at
|
||||
the HTTP request. Permission response-delivery receipts settled. This cell
|
||||
uses fake credentials and records zero external model calls.
|
||||
|
||||
The v3 Bash regression exposed the ACP client-terminal bypass: native filter
|
||||
unit checks passed, but normal ACPX terminal execution lost the assigned task
|
||||
variable and did not exercise that filter. V4 therefore forces the native local process path.
|
||||
The extended cell checks both key/private-variable absence and task-variable
|
||||
presence in real Bash, while every native model HTTP request must retain the
|
||||
fake provider authorization. Earlier v2 evidence remains valid for its narrower
|
||||
instruction/MCP/denial/error scope; the credential fence requires v4 evidence.
|
||||
|
||||
The final v4 native runtime passed the seven-request controlled-peer cell on
|
||||
2026-10-28. ACPX advertised its normal client-terminal capability, Kimi sent zero
|
||||
`terminal/create` requests, and native Bash returned its success marker only
|
||||
after verifying both private variables were absent and an assigned task variable
|
||||
was retained. All seven model requests still carried the fake provider key.
|
||||
The exact native SHA-256 was
|
||||
`cfd11b57b3d75419539bd16f237d73d9ca896d45ae41bbfed8c71beab4daaee9`;
|
||||
the verified macOS closure was
|
||||
`c05a0cd0686a4988fed6cb3c69ca1d9845ea233c53b391a84bc442241cef551b`.
|
||||
|
||||
Enable the additional pending-work lifecycle checks with:
|
||||
|
||||
```sh
|
||||
PAPERCLIP_TEST_KIMI_MODEL_PEER=1 PAPERCLIP_TEST_KIMI_LIFECYCLE_PEER=1 \
|
||||
node --test --test-name-pattern='simulated local model service' \
|
||||
packages/paperclip-runner/scripts/patch-kimi-runtime.test.mjs
|
||||
```
|
||||
|
||||
The extended nine-request cell also passed on 2026-10-28. Both cases held a real
|
||||
native Write approval before any file effect. Stop settled as cancelled. For
|
||||
spontaneous provider loss, the test observed the unique native worker beneath
|
||||
its verified command root and revalidated its parent, process start and group
|
||||
before sending SIGKILL to that worker. It settled as a typed failure. Both cases
|
||||
aborted the waiting host callback, suppressed a subsequently supplied stale
|
||||
`allow_once` response on that exact connection, and produced no file or further
|
||||
model request. The test independently checked file absence after settlement and
|
||||
cleanup, and observed physical worker retirement within five seconds. Four
|
||||
owned native processes closed across the complete cell. These remain simulated
|
||||
service proofs; they do not qualify authenticated behavior, browser journeys or
|
||||
Daytona delivery.
|
||||
|
||||
## Message-boundary regression
|
||||
|
||||
The first completed local Product attempt on 2026-10-28 exposed an upstream
|
||||
ACP framing loss. Campaign `local-2026-10-28T13-43-42-062Z` reached an accepted
|
||||
completion report and durable Done state, but the displayed final comment
|
||||
concatenated a pre-tool progress message with the final answer. The attempt
|
||||
remains failed; completion state alone is insufficient user-facing acceptance.
|
||||
|
||||
The engine already emits `TurnStepStarted` and `TurnStepCompleted` with the
|
||||
real model-step UUID and normalized finish reason. The owned v5 patch registers
|
||||
those existing events in Klient's agent event hub and subscribes through the
|
||||
same active-turn dispatch gate as text deltas. Each message ID hashes the
|
||||
native session, turn and step UUID. Empty start/end chunks carry bounded
|
||||
provenance; text and reasoning deltas use the same identity. Public interrupted
|
||||
step events close only the matching active step. No text inspection or tool
|
||||
notification creates a message boundary. Restored history remains outside the
|
||||
active prompt event stream.
|
||||
|
||||
Source: [`loopService.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/agent-core-v2/src/agent/loop/loopService.ts),
|
||||
[`turnEvents.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/agent-core-v2/src/agent/loop/turnEvents.ts),
|
||||
[`Klient events.ts`](https://github.com/MoonshotAI/kimi-code/blob/ec495f0/packages/klient/src/contract/agent/events.ts).
|
||||
|
||||
The v5 nine-request controlled-peer check passed in 13.4 seconds on 2026-10-28
|
||||
against native SHA-256
|
||||
`9ea2cf23ed4a7386de7f01147d5f7c83719568d814b9d6e1d5ebb43ca3067d86`
|
||||
and verified macOS closure
|
||||
`429433e76fc8889b2262b3d0739bc955e80d958da4a9aa378653d6690030c2ef`.
|
||||
Each ordinary simulated turn emitted a pre-tool message ending in `tool_use`
|
||||
and a separate UUID for the final message ending in `end_turn`, even though
|
||||
the scripted HTTP service deliberately reused its response ID. Actual ACPX
|
||||
normalization retained every empty boundary. The real provider projection
|
||||
validated the native provenance and returned canonical `toolUse` and `stop`
|
||||
boundaries. The same check repeated instruction admission, MCP arguments and
|
||||
results, the child credential fence, denial without effects, typed failure,
|
||||
pending permission Stop, exact native-worker loss, stale-answer suppression
|
||||
and bounded physical cleanup. Four owned native processes retired; there were
|
||||
zero external model calls. This does not replace the failed Product attempt
|
||||
or prove its browser journey passed after the fix.
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"commandDigest": "sha256:00ce5ecc7f0e904811b70584b936c673182c1029b500e8d4823f44ae3cd755e2",
|
||||
"declaration": {
|
||||
"schema": "paperclip.acpx_profile_declaration.v1",
|
||||
"agent": "pi",
|
||||
"agentProfileVersion": 35,
|
||||
"acpxVersion": "0.13.1",
|
||||
"agentServerVersion": "0.0.33",
|
||||
"agentRuntimeVersion": "1.0.0",
|
||||
"nodeVersion": "24.21.0",
|
||||
"closure": {
|
||||
"darwin-arm64": "282022db10150c6632b3444df421342e7d534bdf5d5fb1097a2e79d0625a2bcf",
|
||||
"darwin-x64": "64e251e19009f755c0b04f73ce2138246faab71a961b0f13d75ebfcc34bef12e",
|
||||
"linux-x64": "713b1fdff42fb56a1518bdc084f181d70bee8ebadc3e4b1d76321ed9108c8410"
|
||||
},
|
||||
"wrapperSha256": "9d129b3d38772e93e97080aa6c4e574ac5df4e47ce5bc331a484a9fbf8188b36",
|
||||
"helperSha256": "41e0490b617da0d60e0c8ec58ef311236f945129d99f816cff6897f78da7f91d",
|
||||
"extensionSha256": "f5818573a355702388072b28926897db546a5cf346b5171f646582ce5f9102a4",
|
||||
"agentFilesBinding": "registered-runtime-context-v1",
|
||||
"nativeQuestions": "select-confirm-input-editor-v4-root-field-types",
|
||||
"pathPolicy": "sdk-normalization-v1",
|
||||
"mcpToolNames": "provider-safe-exact-source-v1",
|
||||
"acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"nativeAssistantMessages": "pi1-rpc-deltas-v3-bound-message-and-tool-receipts",
|
||||
"nativeNotices": "session-extension-v2-bounded-runtime-failure",
|
||||
"messageProjectionSha256": "17d1e91b948dfa6a56e1905645add52b502041ad843c8d50a13b07956f22aadd",
|
||||
"noticeProjectionSha256": "014485adc690998e395d334ea3ae67f72be6060a2fc78a1185df7b8d1da28fbf",
|
||||
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1",
|
||||
"nativeInputDisposition": "queued-only-v1",
|
||||
"cacheWarming": "disabled-owned-decision-v1",
|
||||
"thinkingLevel": {
|
||||
"supported": [
|
||||
"off",
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
],
|
||||
"newConfigurationDefault": "low",
|
||||
"admission": "explicit-native-effective-before-prompt-v1",
|
||||
"recovery": "mode-bound-v1"
|
||||
},
|
||||
"modelSelection": "caller-selected-native-verified-v1",
|
||||
"providerConfigurationSourceSha256": "75a9a13f13f224dc092bd8ebf49e8747162172943761e44476c8eb18d80c52d4",
|
||||
"credentialEnvironmentSourceSha256": "a6733e28a9468b744e1e72f1af0c2236e17372e51a9a3ba254a406e085591d2d",
|
||||
"runtimeSandboxSourceSha256": "d631dc7a3554c5eb4ed47bef966007245a133862277c96011b1fe1639135f59d",
|
||||
"recoveryIdentitySourceSha256": "f74d061cc07b0c7ca04df62ca991d940a733d9ef6594b3362451769fc10030b8",
|
||||
"nativeCancellation": "acknowledged-cancelled-terminal-preserves-usage-v1",
|
||||
"dependencySecurityPatchSha256": "5273a195b952f233336122842faaf650193c04f36b9359d20d4d63079b0ce814",
|
||||
"openRouterAttributionSourceSha256": "1211191529d6073b733b311c3d69cada4973eede0f3e540c46e261f78e0b90f9",
|
||||
"preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"commandDigest": "sha256:5186e5acd48dd5f951ee62656be9e266d05e880127fb1d8d6f26161e444770af",
|
||||
"declaration": {
|
||||
"schema": "paperclip.acpx_profile_declaration.v1",
|
||||
"agent": "pi",
|
||||
"agentProfileVersion": 36,
|
||||
"acpxVersion": "0.13.1",
|
||||
"agentServerVersion": "0.0.33",
|
||||
"agentRuntimeVersion": "1.0.0",
|
||||
"nodeVersion": "24.21.0",
|
||||
"closure": {
|
||||
"darwin-arm64": "282022db10150c6632b3444df421342e7d534bdf5d5fb1097a2e79d0625a2bcf",
|
||||
"darwin-x64": "64e251e19009f755c0b04f73ce2138246faab71a961b0f13d75ebfcc34bef12e",
|
||||
"linux-x64": "713b1fdff42fb56a1518bdc084f181d70bee8ebadc3e4b1d76321ed9108c8410"
|
||||
},
|
||||
"wrapperSha256": "9d129b3d38772e93e97080aa6c4e574ac5df4e47ce5bc331a484a9fbf8188b36",
|
||||
"helperSha256": "41e0490b617da0d60e0c8ec58ef311236f945129d99f816cff6897f78da7f91d",
|
||||
"extensionSha256": "f5818573a355702388072b28926897db546a5cf346b5171f646582ce5f9102a4",
|
||||
"agentFilesBinding": "registered-runtime-context-v1",
|
||||
"nativeQuestions": "select-confirm-input-editor-v4-root-field-types",
|
||||
"pathPolicy": "sdk-normalization-v1",
|
||||
"mcpToolNames": "provider-safe-exact-source-v1",
|
||||
"acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"nativeAssistantMessages": "pi1-rpc-deltas-v3-bound-message-and-tool-receipts",
|
||||
"nativeNotices": "session-extension-v2-bounded-runtime-failure",
|
||||
"messageProjectionSha256": "17d1e91b948dfa6a56e1905645add52b502041ad843c8d50a13b07956f22aadd",
|
||||
"noticeProjectionSha256": "014485adc690998e395d334ea3ae67f72be6060a2fc78a1185df7b8d1da28fbf",
|
||||
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1",
|
||||
"nativeInputDisposition": "queued-only-v1",
|
||||
"cacheWarming": "disabled-owned-decision-v1",
|
||||
"thinkingLevel": {
|
||||
"supported": [
|
||||
"off",
|
||||
"low",
|
||||
"high",
|
||||
"max"
|
||||
],
|
||||
"newConfigurationDefault": "low",
|
||||
"admission": "explicit-native-effective-before-prompt-v1",
|
||||
"recovery": "mode-bound-v1"
|
||||
},
|
||||
"modelSelection": "caller-selected-native-verified-v1",
|
||||
"providerConfigurationSourceSha256": "75a9a13f13f224dc092bd8ebf49e8747162172943761e44476c8eb18d80c52d4",
|
||||
"credentialEnvironmentSourceSha256": "9ace39a71ba287838c1b9c56bed141248e805fd017eb8490770c3b579567e55b",
|
||||
"runtimeSandboxSourceSha256": "86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"recoveryIdentitySourceSha256": "f74d061cc07b0c7ca04df62ca991d940a733d9ef6594b3362451769fc10030b8",
|
||||
"nativeCancellation": "acknowledged-cancelled-terminal-preserves-usage-v1",
|
||||
"dependencySecurityPatchSha256": "5273a195b952f233336122842faaf650193c04f36b9359d20d4d63079b0ce814",
|
||||
"openRouterAttributionSourceSha256": "1211191529d6073b733b311c3d69cada4973eede0f3e540c46e261f78e0b90f9",
|
||||
"preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e"
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"commandDigest": "sha256:e0eddf258e824bb0cd7e0ec0a12dc5b8eeff72262e176249b5e6e7d5ea977caf",
|
||||
"declaration": {
|
||||
"schema": "paperclip.acpx_profile_declaration.v1",
|
||||
"agent": "copilot",
|
||||
"agentProfileVersion": 82,
|
||||
"acpxVersion": "0.13.1",
|
||||
"agentServerVersion": "1.0.88",
|
||||
"protocolPolicyRevision": "copilot-agent-manual-v1",
|
||||
"acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3",
|
||||
"distributionSourceSha256": "c0a65e247fc47b263bd369a3d5ab505c4b323d4e6493be8a9ecbeb0e9dbb9107",
|
||||
"agentFilesBinding": "registered-runtime-context-v1",
|
||||
"systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1",
|
||||
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1",
|
||||
"innerDistributionSourceSha256": "9bb08347eed01f1ab44e065c8b06ead9be3a25973d6e6329dc4c0a099096279a",
|
||||
"upstreamAppSha256": "7b48282a19b5b0814a0c96ad5e3a125173d792cc55f9525b2effea962f6063c0",
|
||||
"patchedAppSha256": "61f8eebe0c30cc03b311c6818b2acb158a6d16ca58c8bfac5ebd42db095c2679",
|
||||
"messageIdentityContract": "copilot-native-message-id-v1",
|
||||
"ownedDistributionDelivery": "COPILOT_CLI_DIST_DIR:lease-owned-guarded-inner-distribution:v1",
|
||||
"permissionContextContract": "copilot-edit-permission-context-v1",
|
||||
"permissionContextSourceSha256": "14791781934eb8d91c2a304e257fdb8c7825ae3a40c2bd44fd149c7492470852",
|
||||
"permissionAdapterSourceSha256": "444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"permissionClassifierSourceSha256": "1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
|
||||
"semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2",
|
||||
"semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791",
|
||||
"semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5",
|
||||
"toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706",
|
||||
"semanticNormalizedInputContract": "validated-forwarded-input-commit-v1",
|
||||
"semanticSidecarSourceSha256": "152ec3e5066695c7a28b56cb9b4d5fbd30500c8e7beee54efb9a33fc8a532c9d",
|
||||
"semanticDirectDriverSourceSha256": "9ee8f25f1ebcd44af15f12a46e5735980083672cd317f44cd10cf2e77699e352",
|
||||
"semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975",
|
||||
"semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e",
|
||||
"semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59",
|
||||
"semanticValidatorsSourceSha256": "b20ec8ee6e7165117233ce4421a975bb9d3f276220ce31655c0b9a717cb2d9cb",
|
||||
"semanticSchemaBundleSourceSha256": "81ef98ca7b34896a0f76390ac7af3dd8ef7609fe6f38d54bd6a69373d50fe40b",
|
||||
"semanticSidecarProtocolSourceSha256": "26dd0cfbef8b2b401de6b7862a4af7f243fb703e23678a1a5ed8fa68d4784d42",
|
||||
"semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1",
|
||||
"displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877",
|
||||
"displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e",
|
||||
"configuredEnvironmentSourceSha256": "a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"configuredEnvironmentRustSourceSha256": "fef93985d703f6b484e58746df5c93d60cb95c085fbf5de1af557913358e900d",
|
||||
"profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282",
|
||||
"profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571",
|
||||
"profileInstallationSourceSha256": "4af2280930d6382ff4acef6a09515050991746d8ad5358705f536227d548555f",
|
||||
"attachmentPolicySourceSha256": "e07e2dbc1fdb6895165f2433d1e9627e37cb3d3ad94a8c9601b4b9cafbc44b17",
|
||||
"singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f",
|
||||
"nativeSessionRuntimeSourceSha256": "1fe6fa9e6b96d84d70432b85a57c473f833606d4a521563d5022b4d3c41576ce",
|
||||
"nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842",
|
||||
"nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1",
|
||||
"nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90",
|
||||
"harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5",
|
||||
"harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249",
|
||||
"harnessSessionSourceSha256": "81361733e86b56dd5a66415aefc6f72e69be24acc8d3417370154ebb7fef6e94",
|
||||
"transportContractSourceSha256": "03fca93d36a89b0cc0917335e293ad097fdf04356d21b307827bb0ea4c13f523",
|
||||
"nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586",
|
||||
"nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44",
|
||||
"nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b",
|
||||
"providerLaunchSourceSha256": "c744235b3067ea75973523f78f1e13399337bc155f1775af694e34782d5c9bef",
|
||||
"launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296",
|
||||
"runtimeRequestBridgeSourceSha256": "55aec95e7c7f204454cbd2961e4ee7e7507851da99f0ced899b61154333ae927",
|
||||
"runtimeRequestFacadeSourceSha256": "d0ed28e0c5ddad34db8f437e704c8a755eb0b47f27e08e44185f66d1d2205295",
|
||||
"runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211",
|
||||
"guardedInstallationSourceSha256": "56a177d3e702914818dca4ca50e061f0d25f988bfc4c0582c36ce23c1696d82f",
|
||||
"nativeExecutionSourceSha256": "927b98c90f482cd10ce66fe6730ecd91eb3854935fe6f454b4898da726c0fd69",
|
||||
"profileCompatibilitySourceSha256": "addf190c6b561d48cbc013ba08326e9d922e49e6adc3fa4460d1fb733b06f6bb",
|
||||
"installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d",
|
||||
"assetOwnershipSourceSha256": "b4e8c5b4e2c1757bcbf2049b5cb013723876168c00c24c28048fc80c7ca16ace",
|
||||
"assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f",
|
||||
"credentialBindingSourceSha256": "a6733e28a9468b744e1e72f1af0c2236e17372e51a9a3ba254a406e085591d2d",
|
||||
"admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456",
|
||||
"metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727",
|
||||
"nativeEditContextSourceSha256": "7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1",
|
||||
"semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811",
|
||||
"preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"runtimeHostSourceSha256": "981a18aad23e34c3c675123d1dd0a920748821bad88885bb1a2e1a060c870921",
|
||||
"runtimeAdapterSourceSha256": "601cbfe4e2811f771a3a59bf23fdbb05e33a04901af987f2ff96a8acf56374a3",
|
||||
"runtimeSandboxSourceSha256": "d631dc7a3554c5eb4ed47bef966007245a133862277c96011b1fe1639135f59d",
|
||||
"cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca",
|
||||
"agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba",
|
||||
"nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b"
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"commandDigest": "sha256:4cecc2ba4bc9674fbc6f585d81934789a98b75a36f074dfe2b40391ae9ef8a19",
|
||||
"declaration": {
|
||||
"schema": "paperclip.acpx_profile_declaration.v1",
|
||||
"agent": "copilot",
|
||||
"agentProfileVersion": 83,
|
||||
"acpxVersion": "0.13.1",
|
||||
"agentServerVersion": "1.0.88",
|
||||
"protocolPolicyRevision": "copilot-agent-manual-v1",
|
||||
"acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3",
|
||||
"distributionSourceSha256": "c0a65e247fc47b263bd369a3d5ab505c4b323d4e6493be8a9ecbeb0e9dbb9107",
|
||||
"agentFilesBinding": "registered-runtime-context-v1",
|
||||
"systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1",
|
||||
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1",
|
||||
"innerDistributionSourceSha256": "9bb08347eed01f1ab44e065c8b06ead9be3a25973d6e6329dc4c0a099096279a",
|
||||
"upstreamAppSha256": "7b48282a19b5b0814a0c96ad5e3a125173d792cc55f9525b2effea962f6063c0",
|
||||
"patchedAppSha256": "61f8eebe0c30cc03b311c6818b2acb158a6d16ca58c8bfac5ebd42db095c2679",
|
||||
"messageIdentityContract": "copilot-native-message-id-v1",
|
||||
"ownedDistributionDelivery": "COPILOT_CLI_DIST_DIR:lease-owned-guarded-inner-distribution:v1",
|
||||
"permissionContextContract": "copilot-edit-permission-context-v1",
|
||||
"permissionContextSourceSha256": "14791781934eb8d91c2a304e257fdb8c7825ae3a40c2bd44fd149c7492470852",
|
||||
"permissionAdapterSourceSha256": "444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"permissionClassifierSourceSha256": "1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
|
||||
"semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2",
|
||||
"semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791",
|
||||
"semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5",
|
||||
"toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706",
|
||||
"semanticNormalizedInputContract": "validated-forwarded-input-commit-v1",
|
||||
"semanticSidecarSourceSha256": "152ec3e5066695c7a28b56cb9b4d5fbd30500c8e7beee54efb9a33fc8a532c9d",
|
||||
"semanticDirectDriverSourceSha256": "9ee8f25f1ebcd44af15f12a46e5735980083672cd317f44cd10cf2e77699e352",
|
||||
"semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975",
|
||||
"semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e",
|
||||
"semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59",
|
||||
"semanticValidatorsSourceSha256": "b20ec8ee6e7165117233ce4421a975bb9d3f276220ce31655c0b9a717cb2d9cb",
|
||||
"semanticSchemaBundleSourceSha256": "81ef98ca7b34896a0f76390ac7af3dd8ef7609fe6f38d54bd6a69373d50fe40b",
|
||||
"semanticSidecarProtocolSourceSha256": "26dd0cfbef8b2b401de6b7862a4af7f243fb703e23678a1a5ed8fa68d4784d42",
|
||||
"semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1",
|
||||
"displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877",
|
||||
"displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e",
|
||||
"configuredEnvironmentSourceSha256": "a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"configuredEnvironmentRustSourceSha256": "fef93985d703f6b484e58746df5c93d60cb95c085fbf5de1af557913358e900d",
|
||||
"profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282",
|
||||
"profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571",
|
||||
"profileInstallationSourceSha256": "4af2280930d6382ff4acef6a09515050991746d8ad5358705f536227d548555f",
|
||||
"attachmentPolicySourceSha256": "e07e2dbc1fdb6895165f2433d1e9627e37cb3d3ad94a8c9601b4b9cafbc44b17",
|
||||
"singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f",
|
||||
"nativeSessionRuntimeSourceSha256": "1fe6fa9e6b96d84d70432b85a57c473f833606d4a521563d5022b4d3c41576ce",
|
||||
"nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842",
|
||||
"nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1",
|
||||
"nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90",
|
||||
"harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5",
|
||||
"harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249",
|
||||
"harnessSessionSourceSha256": "81361733e86b56dd5a66415aefc6f72e69be24acc8d3417370154ebb7fef6e94",
|
||||
"transportContractSourceSha256": "03fca93d36a89b0cc0917335e293ad097fdf04356d21b307827bb0ea4c13f523",
|
||||
"nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586",
|
||||
"nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44",
|
||||
"nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b",
|
||||
"providerLaunchSourceSha256": "c744235b3067ea75973523f78f1e13399337bc155f1775af694e34782d5c9bef",
|
||||
"launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296",
|
||||
"runtimeRequestBridgeSourceSha256": "55aec95e7c7f204454cbd2961e4ee7e7507851da99f0ced899b61154333ae927",
|
||||
"runtimeRequestFacadeSourceSha256": "d0ed28e0c5ddad34db8f437e704c8a755eb0b47f27e08e44185f66d1d2205295",
|
||||
"runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211",
|
||||
"guardedInstallationSourceSha256": "56a177d3e702914818dca4ca50e061f0d25f988bfc4c0582c36ce23c1696d82f",
|
||||
"nativeExecutionSourceSha256": "927b98c90f482cd10ce66fe6730ecd91eb3854935fe6f454b4898da726c0fd69",
|
||||
"profileCompatibilitySourceSha256": "68f12855fc4f51f31b79ee48851204cd8424449df3b7f33147813aaa21959e83",
|
||||
"installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d",
|
||||
"assetOwnershipSourceSha256": "b4e8c5b4e2c1757bcbf2049b5cb013723876168c00c24c28048fc80c7ca16ace",
|
||||
"assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f",
|
||||
"credentialBindingSourceSha256": "a6733e28a9468b744e1e72f1af0c2236e17372e51a9a3ba254a406e085591d2d",
|
||||
"admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456",
|
||||
"metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727",
|
||||
"nativeEditContextSourceSha256": "7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1",
|
||||
"semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811",
|
||||
"preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"runtimeHostSourceSha256": "981a18aad23e34c3c675123d1dd0a920748821bad88885bb1a2e1a060c870921",
|
||||
"runtimeAdapterSourceSha256": "601cbfe4e2811f771a3a59bf23fdbb05e33a04901af987f2ff96a8acf56374a3",
|
||||
"runtimeSandboxSourceSha256": "d631dc7a3554c5eb4ed47bef966007245a133862277c96011b1fe1639135f59d",
|
||||
"cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca",
|
||||
"agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba",
|
||||
"nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b"
|
||||
}
|
||||
}
|
||||
Vendored
+89
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"commandDigest": "sha256:0c3328c3fecbe730fa0c4d5239f1084eada804bcb140e16bb41efa0e5a4c4380",
|
||||
"declaration": {
|
||||
"schema": "paperclip.acpx_profile_declaration.v1",
|
||||
"agent": "copilot",
|
||||
"agentProfileVersion": 88,
|
||||
"acpxVersion": "0.13.1",
|
||||
"agentServerVersion": "1.0.88",
|
||||
"protocolPolicyRevision": "copilot-agent-manual-v1",
|
||||
"acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3",
|
||||
"distributionSourceSha256": "c0a65e247fc47b263bd369a3d5ab505c4b323d4e6493be8a9ecbeb0e9dbb9107",
|
||||
"agentFilesBinding": "registered-runtime-context-v1",
|
||||
"systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1",
|
||||
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1",
|
||||
"innerDistributionSourceSha256": "9bb08347eed01f1ab44e065c8b06ead9be3a25973d6e6329dc4c0a099096279a",
|
||||
"upstreamAppSha256": "7b48282a19b5b0814a0c96ad5e3a125173d792cc55f9525b2effea962f6063c0",
|
||||
"patchedAppSha256": "61f8eebe0c30cc03b311c6818b2acb158a6d16ca58c8bfac5ebd42db095c2679",
|
||||
"messageIdentityContract": "copilot-native-message-id-v1",
|
||||
"ownedDistributionDelivery": "COPILOT_CLI_DIST_DIR:lease-owned-guarded-inner-distribution:v1",
|
||||
"permissionContextContract": "copilot-edit-permission-context-v1",
|
||||
"permissionContextSourceSha256": "14791781934eb8d91c2a304e257fdb8c7825ae3a40c2bd44fd149c7492470852",
|
||||
"permissionAdapterSourceSha256": "444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"permissionClassifierSourceSha256": "1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
|
||||
"semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2",
|
||||
"semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791",
|
||||
"semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5",
|
||||
"toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706",
|
||||
"semanticNormalizedInputContract": "validated-forwarded-input-commit-v1",
|
||||
"semanticSidecarSourceSha256": "6816fe12cdd274746b5241b75823b3c347b245f3cc03cdbe70f05ec1a0250663",
|
||||
"semanticDirectDriverSourceSha256": "d11dea41590394225d622c2f4b4141ede0445fdb529fec1d6ce3fe749f354234",
|
||||
"semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975",
|
||||
"semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e",
|
||||
"semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59",
|
||||
"semanticValidatorsSourceSha256": "b20ec8ee6e7165117233ce4421a975bb9d3f276220ce31655c0b9a717cb2d9cb",
|
||||
"semanticSchemaBundleSourceSha256": "81ef98ca7b34896a0f76390ac7af3dd8ef7609fe6f38d54bd6a69373d50fe40b",
|
||||
"semanticSidecarProtocolSourceSha256": "26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1",
|
||||
"displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877",
|
||||
"displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e",
|
||||
"configuredEnvironmentSourceSha256": "a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"configuredEnvironmentRustSourceSha256": "fef93985d703f6b484e58746df5c93d60cb95c085fbf5de1af557913358e900d",
|
||||
"profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282",
|
||||
"profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571",
|
||||
"profileInstallationSourceSha256": "d229b6afef695991313acdf490991136d40d638359665e0c48afb675af94f863",
|
||||
"attachmentPolicySourceSha256": "e07e2dbc1fdb6895165f2433d1e9627e37cb3d3ad94a8c9601b4b9cafbc44b17",
|
||||
"singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f",
|
||||
"nativeSessionRuntimeSourceSha256": "cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea",
|
||||
"nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842",
|
||||
"nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1",
|
||||
"nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90",
|
||||
"harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5",
|
||||
"harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249",
|
||||
"harnessSessionSourceSha256": "bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851",
|
||||
"transportContractSourceSha256": "06f06c53ecae304f72034ffb2f659ff087c82a70ed4994daf21a9d2edaf56e4e",
|
||||
"nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586",
|
||||
"nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44",
|
||||
"nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b",
|
||||
"providerLaunchSourceSha256": "c744235b3067ea75973523f78f1e13399337bc155f1775af694e34782d5c9bef",
|
||||
"launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296",
|
||||
"runtimeRequestBridgeSourceSha256": "1554851fd4b32561e8575b8ad72a28528172dfc0372a014d11f02668991d5e49",
|
||||
"runtimeRequestFacadeSourceSha256": "23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa",
|
||||
"runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211",
|
||||
"guardedInstallationSourceSha256": "64b137e3ef695e7d03013ea2b6c8830171e0a73e0bf2e73d55e9ced57a47bcf6",
|
||||
"nativeExecutionSourceSha256": "927b98c90f482cd10ce66fe6730ecd91eb3854935fe6f454b4898da726c0fd69",
|
||||
"profileCompatibilitySourceSha256": "3937581ffc20b248bb24e2e7109e8adb1035a750d1713994988986d3572a84bc",
|
||||
"installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d",
|
||||
"assetOwnershipSourceSha256": "36064b48d889c41070091a75c1645b7c8caa987a7ed1810a931dc378ddbb1356",
|
||||
"assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f",
|
||||
"credentialBindingSourceSha256": "9ace39a71ba287838c1b9c56bed141248e805fd017eb8490770c3b579567e55b",
|
||||
"admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456",
|
||||
"metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727",
|
||||
"nativeEditContextSourceSha256": "7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1",
|
||||
"semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811",
|
||||
"preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"runtimeHostSourceSha256": "f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"runtimeAdapterSourceSha256": "c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"runtimeSandboxSourceSha256": "86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca",
|
||||
"agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba",
|
||||
"nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"commandDigest": "sha256:995381b2cec7a0faf3f5cefbe2421eae7936a2bfa50bb153645f57341f968db3",
|
||||
"declaration": {
|
||||
"schema": "paperclip.acpx_profile_declaration.v1",
|
||||
"agent": "copilot",
|
||||
"agentProfileVersion": 89,
|
||||
"acpxVersion": "0.13.1",
|
||||
"agentServerVersion": "1.0.88",
|
||||
"protocolPolicyRevision": "copilot-agent-manual-v1",
|
||||
"acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3",
|
||||
"distributionSourceSha256": "c0a65e247fc47b263bd369a3d5ab505c4b323d4e6493be8a9ecbeb0e9dbb9107",
|
||||
"agentFilesBinding": "registered-runtime-context-v1",
|
||||
"systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1",
|
||||
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1",
|
||||
"innerDistributionSourceSha256": "9bb08347eed01f1ab44e065c8b06ead9be3a25973d6e6329dc4c0a099096279a",
|
||||
"upstreamAppSha256": "7b48282a19b5b0814a0c96ad5e3a125173d792cc55f9525b2effea962f6063c0",
|
||||
"patchedAppSha256": "61f8eebe0c30cc03b311c6818b2acb158a6d16ca58c8bfac5ebd42db095c2679",
|
||||
"messageIdentityContract": "copilot-native-message-id-v1",
|
||||
"ownedDistributionDelivery": "COPILOT_CLI_DIST_DIR:lease-owned-guarded-inner-distribution:v1",
|
||||
"permissionContextContract": "copilot-edit-permission-context-v1",
|
||||
"permissionContextSourceSha256": "14791781934eb8d91c2a304e257fdb8c7825ae3a40c2bd44fd149c7492470852",
|
||||
"permissionAdapterSourceSha256": "444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"permissionClassifierSourceSha256": "1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
|
||||
"semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2",
|
||||
"semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791",
|
||||
"semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5",
|
||||
"toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706",
|
||||
"semanticNormalizedInputContract": "validated-forwarded-input-commit-v1",
|
||||
"semanticSidecarSourceSha256": "6816fe12cdd274746b5241b75823b3c347b245f3cc03cdbe70f05ec1a0250663",
|
||||
"semanticDirectDriverSourceSha256": "d11dea41590394225d622c2f4b4141ede0445fdb529fec1d6ce3fe749f354234",
|
||||
"semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975",
|
||||
"semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e",
|
||||
"semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59",
|
||||
"semanticValidatorsSourceSha256": "b20ec8ee6e7165117233ce4421a975bb9d3f276220ce31655c0b9a717cb2d9cb",
|
||||
"semanticSchemaBundleSourceSha256": "81ef98ca7b34896a0f76390ac7af3dd8ef7609fe6f38d54bd6a69373d50fe40b",
|
||||
"semanticSidecarProtocolSourceSha256": "26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1",
|
||||
"displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877",
|
||||
"displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e",
|
||||
"configuredEnvironmentSourceSha256": "a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"configuredEnvironmentRustSourceSha256": "fef93985d703f6b484e58746df5c93d60cb95c085fbf5de1af557913358e900d",
|
||||
"profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282",
|
||||
"profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571",
|
||||
"profileInstallationSourceSha256": "d229b6afef695991313acdf490991136d40d638359665e0c48afb675af94f863",
|
||||
"attachmentPolicySourceSha256": "e07e2dbc1fdb6895165f2433d1e9627e37cb3d3ad94a8c9601b4b9cafbc44b17",
|
||||
"singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f",
|
||||
"nativeSessionRuntimeSourceSha256": "cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea",
|
||||
"nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842",
|
||||
"nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1",
|
||||
"nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90",
|
||||
"harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5",
|
||||
"harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249",
|
||||
"harnessSessionSourceSha256": "bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851",
|
||||
"transportContractSourceSha256": "06f06c53ecae304f72034ffb2f659ff087c82a70ed4994daf21a9d2edaf56e4e",
|
||||
"nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586",
|
||||
"nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44",
|
||||
"nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b",
|
||||
"providerLaunchSourceSha256": "c744235b3067ea75973523f78f1e13399337bc155f1775af694e34782d5c9bef",
|
||||
"launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296",
|
||||
"runtimeRequestBridgeSourceSha256": "1554851fd4b32561e8575b8ad72a28528172dfc0372a014d11f02668991d5e49",
|
||||
"runtimeRequestFacadeSourceSha256": "23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa",
|
||||
"runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211",
|
||||
"guardedInstallationSourceSha256": "64b137e3ef695e7d03013ea2b6c8830171e0a73e0bf2e73d55e9ced57a47bcf6",
|
||||
"nativeExecutionSourceSha256": "927b98c90f482cd10ce66fe6730ecd91eb3854935fe6f454b4898da726c0fd69",
|
||||
"profileCompatibilitySourceSha256": "6332462e69b990dcba4104389e76ba4198ba284242dc1b963aedfdc5dba93f31",
|
||||
"installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d",
|
||||
"assetOwnershipSourceSha256": "36064b48d889c41070091a75c1645b7c8caa987a7ed1810a931dc378ddbb1356",
|
||||
"assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f",
|
||||
"credentialBindingSourceSha256": "9ace39a71ba287838c1b9c56bed141248e805fd017eb8490770c3b579567e55b",
|
||||
"admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456",
|
||||
"metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727",
|
||||
"nativeEditContextSourceSha256": "7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1",
|
||||
"semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811",
|
||||
"preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"runtimeHostSourceSha256": "f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"runtimeAdapterSourceSha256": "c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"runtimeSandboxSourceSha256": "86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca",
|
||||
"agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba",
|
||||
"nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"commandDigest": "sha256:8922d3d41dae0a7b7f7e2de69d50b272b58283232fefb22ffdb6c9b34fa26a6b",
|
||||
"declaration": {
|
||||
"schema": "paperclip.acpx_profile_declaration.v1",
|
||||
"agent": "copilot",
|
||||
"agentProfileVersion": 90,
|
||||
"acpxVersion": "0.13.1",
|
||||
"agentServerVersion": "1.0.88",
|
||||
"protocolPolicyRevision": "copilot-agent-manual-v1",
|
||||
"acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3",
|
||||
"distributionSourceSha256": "c0a65e247fc47b263bd369a3d5ab505c4b323d4e6493be8a9ecbeb0e9dbb9107",
|
||||
"agentFilesBinding": "registered-runtime-context-v1",
|
||||
"systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1",
|
||||
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1",
|
||||
"innerDistributionSourceSha256": "9bb08347eed01f1ab44e065c8b06ead9be3a25973d6e6329dc4c0a099096279a",
|
||||
"upstreamAppSha256": "7b48282a19b5b0814a0c96ad5e3a125173d792cc55f9525b2effea962f6063c0",
|
||||
"patchedAppSha256": "61f8eebe0c30cc03b311c6818b2acb158a6d16ca58c8bfac5ebd42db095c2679",
|
||||
"messageIdentityContract": "copilot-native-message-id-v1",
|
||||
"ownedDistributionDelivery": "COPILOT_CLI_DIST_DIR:lease-owned-guarded-inner-distribution:v1",
|
||||
"permissionContextContract": "copilot-edit-permission-context-v1",
|
||||
"permissionContextSourceSha256": "14791781934eb8d91c2a304e257fdb8c7825ae3a40c2bd44fd149c7492470852",
|
||||
"permissionAdapterSourceSha256": "444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"permissionClassifierSourceSha256": "1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
|
||||
"semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2",
|
||||
"semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791",
|
||||
"semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5",
|
||||
"toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706",
|
||||
"semanticNormalizedInputContract": "validated-forwarded-input-commit-v1",
|
||||
"semanticSidecarSourceSha256": "6816fe12cdd274746b5241b75823b3c347b245f3cc03cdbe70f05ec1a0250663",
|
||||
"semanticDirectDriverSourceSha256": "d11dea41590394225d622c2f4b4141ede0445fdb529fec1d6ce3fe749f354234",
|
||||
"semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975",
|
||||
"semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e",
|
||||
"semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59",
|
||||
"semanticValidatorsSourceSha256": "b20ec8ee6e7165117233ce4421a975bb9d3f276220ce31655c0b9a717cb2d9cb",
|
||||
"semanticSchemaBundleSourceSha256": "81ef98ca7b34896a0f76390ac7af3dd8ef7609fe6f38d54bd6a69373d50fe40b",
|
||||
"semanticSidecarProtocolSourceSha256": "26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1",
|
||||
"displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877",
|
||||
"displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e",
|
||||
"configuredEnvironmentSourceSha256": "a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"configuredEnvironmentRustSourceSha256": "fef93985d703f6b484e58746df5c93d60cb95c085fbf5de1af557913358e900d",
|
||||
"profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282",
|
||||
"profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571",
|
||||
"profileInstallationSourceSha256": "d229b6afef695991313acdf490991136d40d638359665e0c48afb675af94f863",
|
||||
"attachmentPolicySourceSha256": "e07e2dbc1fdb6895165f2433d1e9627e37cb3d3ad94a8c9601b4b9cafbc44b17",
|
||||
"singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f",
|
||||
"nativeSessionRuntimeSourceSha256": "cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea",
|
||||
"nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842",
|
||||
"nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1",
|
||||
"nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90",
|
||||
"harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5",
|
||||
"harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249",
|
||||
"harnessSessionSourceSha256": "bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851",
|
||||
"transportContractSourceSha256": "06f06c53ecae304f72034ffb2f659ff087c82a70ed4994daf21a9d2edaf56e4e",
|
||||
"nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586",
|
||||
"nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44",
|
||||
"nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b",
|
||||
"providerLaunchSourceSha256": "c744235b3067ea75973523f78f1e13399337bc155f1775af694e34782d5c9bef",
|
||||
"launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296",
|
||||
"runtimeRequestBridgeSourceSha256": "1554851fd4b32561e8575b8ad72a28528172dfc0372a014d11f02668991d5e49",
|
||||
"runtimeRequestFacadeSourceSha256": "23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa",
|
||||
"runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211",
|
||||
"guardedInstallationSourceSha256": "64b137e3ef695e7d03013ea2b6c8830171e0a73e0bf2e73d55e9ced57a47bcf6",
|
||||
"nativeExecutionSourceSha256": "927b98c90f482cd10ce66fe6730ecd91eb3854935fe6f454b4898da726c0fd69",
|
||||
"profileCompatibilitySourceSha256": "09eac296f1f511678ffa057b9bcc0de5321902a9607eeec0177cd1957f22d735",
|
||||
"installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d",
|
||||
"assetOwnershipSourceSha256": "36064b48d889c41070091a75c1645b7c8caa987a7ed1810a931dc378ddbb1356",
|
||||
"assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f",
|
||||
"credentialBindingSourceSha256": "9ace39a71ba287838c1b9c56bed141248e805fd017eb8490770c3b579567e55b",
|
||||
"admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456",
|
||||
"metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727",
|
||||
"nativeEditContextSourceSha256": "7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1",
|
||||
"semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811",
|
||||
"preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"runtimeHostSourceSha256": "f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"runtimeAdapterSourceSha256": "c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"runtimeSandboxSourceSha256": "86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca",
|
||||
"agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba",
|
||||
"nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"commandDigest": "sha256:75ae0628c140e51b466b898a394032849d6eca5272279fa7782192a6d316a75d",
|
||||
"declaration": {
|
||||
"schema": "paperclip.acpx_profile_declaration.v1",
|
||||
"agent": "copilot",
|
||||
"agentProfileVersion": 91,
|
||||
"acpxVersion": "0.13.1",
|
||||
"agentServerVersion": "1.0.88",
|
||||
"protocolPolicyRevision": "copilot-agent-manual-v1",
|
||||
"acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3",
|
||||
"distributionSourceSha256": "c0a65e247fc47b263bd369a3d5ab505c4b323d4e6493be8a9ecbeb0e9dbb9107",
|
||||
"agentFilesBinding": "registered-runtime-context-v1",
|
||||
"systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1",
|
||||
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1",
|
||||
"innerDistributionSourceSha256": "9bb08347eed01f1ab44e065c8b06ead9be3a25973d6e6329dc4c0a099096279a",
|
||||
"upstreamAppSha256": "7b48282a19b5b0814a0c96ad5e3a125173d792cc55f9525b2effea962f6063c0",
|
||||
"patchedAppSha256": "61f8eebe0c30cc03b311c6818b2acb158a6d16ca58c8bfac5ebd42db095c2679",
|
||||
"messageIdentityContract": "copilot-native-message-id-v1",
|
||||
"ownedDistributionDelivery": "COPILOT_CLI_DIST_DIR:lease-owned-guarded-inner-distribution:v1",
|
||||
"permissionContextContract": "copilot-edit-permission-context-v1",
|
||||
"permissionContextSourceSha256": "14791781934eb8d91c2a304e257fdb8c7825ae3a40c2bd44fd149c7492470852",
|
||||
"permissionAdapterSourceSha256": "444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"permissionClassifierSourceSha256": "b60e4b924e827e122860d2a4cc13b465ba748ee521620887c88f3c58c0df6081",
|
||||
"semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2",
|
||||
"semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791",
|
||||
"semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5",
|
||||
"toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706",
|
||||
"semanticNormalizedInputContract": "validated-forwarded-input-commit-v1",
|
||||
"semanticSidecarSourceSha256": "477a05847187159f9c6f07bf90f1c0c1d432a9eb6fdb15d5620daf54443dafd8",
|
||||
"semanticDirectDriverSourceSha256": "d11dea41590394225d622c2f4b4141ede0445fdb529fec1d6ce3fe749f354234",
|
||||
"semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975",
|
||||
"semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e",
|
||||
"semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59",
|
||||
"semanticValidatorsSourceSha256": "b20ec8ee6e7165117233ce4421a975bb9d3f276220ce31655c0b9a717cb2d9cb",
|
||||
"semanticSchemaBundleSourceSha256": "81ef98ca7b34896a0f76390ac7af3dd8ef7609fe6f38d54bd6a69373d50fe40b",
|
||||
"semanticSidecarProtocolSourceSha256": "26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1",
|
||||
"displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877",
|
||||
"displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e",
|
||||
"configuredEnvironmentSourceSha256": "a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"configuredEnvironmentRustSourceSha256": "fef93985d703f6b484e58746df5c93d60cb95c085fbf5de1af557913358e900d",
|
||||
"profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282",
|
||||
"profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571",
|
||||
"profileInstallationSourceSha256": "d229b6afef695991313acdf490991136d40d638359665e0c48afb675af94f863",
|
||||
"attachmentPolicySourceSha256": "e07e2dbc1fdb6895165f2433d1e9627e37cb3d3ad94a8c9601b4b9cafbc44b17",
|
||||
"singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f",
|
||||
"nativeSessionRuntimeSourceSha256": "cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea",
|
||||
"nativeRuntimeContextSourceSha256": "152d7187ce1dc91a15377d368776555c9897d6e018b65f0f59a92dcdbd6497d9",
|
||||
"nativeRuntimeContextFieldsSourceSha256": "069f4bc3c4dc95888f30d3fea331adb0ee0a164d09f4f70ec9c132f24f69c7dd",
|
||||
"nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1",
|
||||
"nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90",
|
||||
"harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5",
|
||||
"harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249",
|
||||
"harnessSessionSourceSha256": "bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851",
|
||||
"transportContractSourceSha256": "06f06c53ecae304f72034ffb2f659ff087c82a70ed4994daf21a9d2edaf56e4e",
|
||||
"nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586",
|
||||
"nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44",
|
||||
"nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b",
|
||||
"providerLaunchSourceSha256": "c744235b3067ea75973523f78f1e13399337bc155f1775af694e34782d5c9bef",
|
||||
"launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296",
|
||||
"runtimeRequestBridgeSourceSha256": "1554851fd4b32561e8575b8ad72a28528172dfc0372a014d11f02668991d5e49",
|
||||
"runtimeRequestFacadeSourceSha256": "23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa",
|
||||
"runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211",
|
||||
"guardedInstallationSourceSha256": "64b137e3ef695e7d03013ea2b6c8830171e0a73e0bf2e73d55e9ced57a47bcf6",
|
||||
"nativeExecutionSourceSha256": "927b98c90f482cd10ce66fe6730ecd91eb3854935fe6f454b4898da726c0fd69",
|
||||
"profileCompatibilitySourceSha256": "aaa189a1e8b921887a5d83c7576617a5b46ceb77d43ed7ec0466653eb29fc37c",
|
||||
"installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d",
|
||||
"assetOwnershipSourceSha256": "36064b48d889c41070091a75c1645b7c8caa987a7ed1810a931dc378ddbb1356",
|
||||
"assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f",
|
||||
"credentialBindingSourceSha256": "9ace39a71ba287838c1b9c56bed141248e805fd017eb8490770c3b579567e55b",
|
||||
"admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456",
|
||||
"metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727",
|
||||
"nativeEditContextSourceSha256": "7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1",
|
||||
"semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811",
|
||||
"preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"runtimeHostSourceSha256": "f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"runtimeAdapterSourceSha256": "c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"runtimeSandboxSourceSha256": "86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca",
|
||||
"agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba",
|
||||
"nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b"
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"commandDigest": "sha256:c4beba5aff2708dbab58b00da718e112dc29b91159a4f9cf449104a5ef141a40",
|
||||
"sourceDigest": "sha256:e61fd575a05d284ba10ef17122adabccd08169ae0d06004a365198a603014b1e",
|
||||
"declaration": {
|
||||
"schema": "paperclip.grok-profile-identity.v1",
|
||||
"agent": "grok",
|
||||
"agentProfileVersion": 13,
|
||||
"agentServerPackage": "builtin:grok-acp",
|
||||
"agentServerVersion": "1",
|
||||
"agentRuntimePackage": "native:grok",
|
||||
"agentRuntimeVersion": "1.0.13",
|
||||
"requiresProviderPolicy": false,
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"patches/acpx@0.13.1.patch": "sha256:1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"packages/paperclip-runner/src/cli/acpx-runtime-sidecar.ts": "sha256:152ec3e5066695c7a28b56cb9b4d5fbd30500c8e7beee54efb9a33fc8a532c9d",
|
||||
"packages/paperclip-runner/src/cli/acpx-sidecar-input.ts": "sha256:17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts": "sha256:df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-effort.ts": "sha256:f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-web-search.ts": "sha256:754b5ca2345c045421f6007f34999508d996e1cea035f0f1952b3d5f00072b9c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-credentials.ts": "sha256:1c4ff0be6da798b6fcc604d20a9198dcc8523a3806d563b5b5345b7260549f73",
|
||||
"packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts": "sha256:601cbfe4e2811f771a3a59bf23fdbb05e33a04901af987f2ff96a8acf56374a3",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-host.ts": "sha256:981a18aad23e34c3c675123d1dd0a920748821bad88885bb1a2e1a060c870921",
|
||||
"packages/paperclip-runner/src/drivers/preconfigured-home.ts": "sha256:bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts": "sha256:d631dc7a3554c5eb4ed47bef966007245a133862277c96011b1fe1639135f59d",
|
||||
"packages/paperclip-runner/src/drivers/acpx/sidecar-protocol.ts": "sha256:26dd0cfbef8b2b401de6b7862a4af7f243fb703e23678a1a5ed8fa68d4784d42",
|
||||
"packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts": "sha256:aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b",
|
||||
"packages/paperclip-runner/src/providers/grok/launcher.cjs": "sha256:f0b698395a3704ed2ffaf84ea19bdb20c36c8a0a70b7c629c7b6ffe144e59e55",
|
||||
"packages/paperclip-runner/src/providers/grok/platforms.json": "sha256:931eed3abbf2bf041e6c72e6e9486d976f4352613cf069b216ddf010c9a8e704",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:ff22cccd54160797428e6c7affc70f7d30e3a6678d13058ca972b146c3cdd7d2",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:e97a7c4d2cf9be9c6e0ada78076a5551b01401518597ab81cb6fbd5a1faf4a5e",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:cfbbb39a2e20f449df53ceaea4a6cbd068c954c10405bea2d908b93e3f387fdf"
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"commandDigest": "sha256:3140bc190a025afa2f7380a3eb1a2d8aeecd24850ac69f82871847e78c3b17a0",
|
||||
"sourceDigest": "sha256:8da223681ae5deefd04e192d181e09e5446729d4caf2f96eefac76769717bb09",
|
||||
"declaration": {
|
||||
"schema": "paperclip.grok-profile-identity.v1",
|
||||
"agent": "grok",
|
||||
"agentProfileVersion": 14,
|
||||
"agentServerPackage": "builtin:grok-acp",
|
||||
"agentServerVersion": "1",
|
||||
"agentRuntimePackage": "native:grok",
|
||||
"agentRuntimeVersion": "1.0.13",
|
||||
"requiresProviderPolicy": false,
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"patches/acpx@0.13.1.patch": "sha256:1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"packages/paperclip-runner/src/cli/acpx-runtime-sidecar.ts": "sha256:152ec3e5066695c7a28b56cb9b4d5fbd30500c8e7beee54efb9a33fc8a532c9d",
|
||||
"packages/paperclip-runner/src/cli/acpx-sidecar-input.ts": "sha256:17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts": "sha256:df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-effort.ts": "sha256:f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-web-search.ts": "sha256:754b5ca2345c045421f6007f34999508d996e1cea035f0f1952b3d5f00072b9c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-credentials.ts": "sha256:1c4ff0be6da798b6fcc604d20a9198dcc8523a3806d563b5b5345b7260549f73",
|
||||
"packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts": "sha256:601cbfe4e2811f771a3a59bf23fdbb05e33a04901af987f2ff96a8acf56374a3",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-host.ts": "sha256:981a18aad23e34c3c675123d1dd0a920748821bad88885bb1a2e1a060c870921",
|
||||
"packages/paperclip-runner/src/drivers/preconfigured-home.ts": "sha256:bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts": "sha256:d631dc7a3554c5eb4ed47bef966007245a133862277c96011b1fe1639135f59d",
|
||||
"packages/paperclip-runner/src/drivers/acpx/sidecar-protocol.ts": "sha256:26dd0cfbef8b2b401de6b7862a4af7f243fb703e23678a1a5ed8fa68d4784d42",
|
||||
"packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts": "sha256:aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b",
|
||||
"packages/paperclip-runner/src/providers/grok/launcher.cjs": "sha256:f0b698395a3704ed2ffaf84ea19bdb20c36c8a0a70b7c629c7b6ffe144e59e55",
|
||||
"packages/paperclip-runner/src/providers/grok/platforms.json": "sha256:931eed3abbf2bf041e6c72e6e9486d976f4352613cf069b216ddf010c9a8e704",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:401b2edf0678e3880eed4120137fd1fef96da423955b222f506ddf582a7cc520",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:e97a7c4d2cf9be9c6e0ada78076a5551b01401518597ab81cb6fbd5a1faf4a5e",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:cfbbb39a2e20f449df53ceaea4a6cbd068c954c10405bea2d908b93e3f387fdf"
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"commandDigest": "sha256:99a7c5a38e3786fc08cc27e54902b0d36b07be15c61e3ca5645088c301c35067",
|
||||
"sourceDigest": "sha256:1c16b81b5743471f4b5143943fcfc860f14f8ae0ca9255bc8b2fc8542bdede5a",
|
||||
"declaration": {
|
||||
"schema": "paperclip.grok-profile-identity.v1",
|
||||
"agent": "grok",
|
||||
"agentProfileVersion": 15,
|
||||
"agentServerPackage": "builtin:grok-acp",
|
||||
"agentServerVersion": "1",
|
||||
"agentRuntimePackage": "native:grok",
|
||||
"agentRuntimeVersion": "1.0.13",
|
||||
"requiresProviderPolicy": false,
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"patches/acpx@0.13.1.patch": "sha256:1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"packages/paperclip-runner/src/cli/acpx-runtime-sidecar.ts": "sha256:6816fe12cdd274746b5241b75823b3c347b245f3cc03cdbe70f05ec1a0250663",
|
||||
"packages/paperclip-runner/src/cli/acpx-sidecar-input.ts": "sha256:17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts": "sha256:df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-effort.ts": "sha256:f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-web-search.ts": "sha256:754b5ca2345c045421f6007f34999508d996e1cea035f0f1952b3d5f00072b9c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-credentials.ts": "sha256:1c4ff0be6da798b6fcc604d20a9198dcc8523a3806d563b5b5345b7260549f73",
|
||||
"packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts": "sha256:c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-host.ts": "sha256:f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"packages/paperclip-runner/src/drivers/preconfigured-home.ts": "sha256:bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts": "sha256:86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"packages/paperclip-runner/src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts": "sha256:aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b",
|
||||
"packages/paperclip-runner/src/providers/grok/launcher.cjs": "sha256:f0b698395a3704ed2ffaf84ea19bdb20c36c8a0a70b7c629c7b6ffe144e59e55",
|
||||
"packages/paperclip-runner/src/providers/grok/platforms.json": "sha256:931eed3abbf2bf041e6c72e6e9486d976f4352613cf069b216ddf010c9a8e704",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:8c1d2bdc1cdd808b51b624b1bd1b576b8f72e72e89fbac3ff1f9beef14878480",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:fc8d7cce62e0c02efddece8696e8d24f6a9fe19e5288a5e67a01ffbf931a3f6e",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:37a552c1a8a59b1815df99b79552e0fda711cfa934055073ffd682cd17d7fb54"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"commandDigest": "sha256:4d6b4c857ba6e63a3eb8c5c406280e4bd0164d3109c940918392c52868bb4263",
|
||||
"sourceDigest": "sha256:a32325cdbc87730f602b1b9349310fcabab03de77b3e4b07a514948a03628601",
|
||||
"declaration": {
|
||||
"schema": "paperclip.grok-profile-identity.v1",
|
||||
"agent": "grok",
|
||||
"agentProfileVersion": 16,
|
||||
"agentServerPackage": "builtin:grok-acp",
|
||||
"agentServerVersion": "1",
|
||||
"agentRuntimePackage": "native:grok",
|
||||
"agentRuntimeVersion": "1.0.13",
|
||||
"requiresProviderPolicy": false,
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"patches/acpx@0.13.1.patch": "sha256:1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"packages/paperclip-runner/src/cli/acpx-runtime-sidecar.ts": "sha256:6816fe12cdd274746b5241b75823b3c347b245f3cc03cdbe70f05ec1a0250663",
|
||||
"packages/paperclip-runner/src/cli/acpx-sidecar-input.ts": "sha256:17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts": "sha256:df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-effort.ts": "sha256:f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-web-search.ts": "sha256:754b5ca2345c045421f6007f34999508d996e1cea035f0f1952b3d5f00072b9c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-credentials.ts": "sha256:1c4ff0be6da798b6fcc604d20a9198dcc8523a3806d563b5b5345b7260549f73",
|
||||
"packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts": "sha256:c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-host.ts": "sha256:f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"packages/paperclip-runner/src/drivers/preconfigured-home.ts": "sha256:bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts": "sha256:86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"packages/paperclip-runner/src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts": "sha256:aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b",
|
||||
"packages/paperclip-runner/src/providers/grok/launcher.cjs": "sha256:f0b698395a3704ed2ffaf84ea19bdb20c36c8a0a70b7c629c7b6ffe144e59e55",
|
||||
"packages/paperclip-runner/src/providers/grok/platforms.json": "sha256:931eed3abbf2bf041e6c72e6e9486d976f4352613cf069b216ddf010c9a8e704",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:da62bf0a159b6fe65c3fff5d9ed7ad58ae32cff6b22ed1b787f909e8eaa0f564",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:fc8d7cce62e0c02efddece8696e8d24f6a9fe19e5288a5e67a01ffbf931a3f6e",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:37a552c1a8a59b1815df99b79552e0fda711cfa934055073ffd682cd17d7fb54"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"commandDigest": "sha256:8c9cb917026844e362e1b28da8529662a15da23cad1002ab5bcec79031b1339e",
|
||||
"sourceDigest": "sha256:57da46dc476d78a55ffb61111a8ab328c879f1d2689f615ef65bc757149b24b7",
|
||||
"declaration": {
|
||||
"schema": "paperclip.grok-profile-identity.v1",
|
||||
"agent": "grok",
|
||||
"agentProfileVersion": 17,
|
||||
"agentServerPackage": "builtin:grok-acp",
|
||||
"agentServerVersion": "1",
|
||||
"agentRuntimePackage": "native:grok",
|
||||
"agentRuntimeVersion": "1.0.13",
|
||||
"requiresProviderPolicy": false,
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"patches/acpx@0.13.1.patch": "sha256:1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"packages/paperclip-runner/src/cli/acpx-runtime-sidecar.ts": "sha256:6816fe12cdd274746b5241b75823b3c347b245f3cc03cdbe70f05ec1a0250663",
|
||||
"packages/paperclip-runner/src/cli/acpx-sidecar-input.ts": "sha256:17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts": "sha256:df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-effort.ts": "sha256:f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-web-search.ts": "sha256:754b5ca2345c045421f6007f34999508d996e1cea035f0f1952b3d5f00072b9c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-credentials.ts": "sha256:1c4ff0be6da798b6fcc604d20a9198dcc8523a3806d563b5b5345b7260549f73",
|
||||
"packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts": "sha256:c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-host.ts": "sha256:f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"packages/paperclip-runner/src/drivers/preconfigured-home.ts": "sha256:bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts": "sha256:86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"packages/paperclip-runner/src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts": "sha256:aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b",
|
||||
"packages/paperclip-runner/src/providers/grok/launcher.cjs": "sha256:f0b698395a3704ed2ffaf84ea19bdb20c36c8a0a70b7c629c7b6ffe144e59e55",
|
||||
"packages/paperclip-runner/src/providers/grok/platforms.json": "sha256:931eed3abbf2bf041e6c72e6e9486d976f4352613cf069b216ddf010c9a8e704",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:01836e46cba936845c5e612775a9fa5dfc277cdcb7f54cb97da9a334d9580fe1",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:fc8d7cce62e0c02efddece8696e8d24f6a9fe19e5288a5e67a01ffbf931a3f6e",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:37a552c1a8a59b1815df99b79552e0fda711cfa934055073ffd682cd17d7fb54"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"commandDigest": "sha256:4996686ac076be94980dc3f2e2ad58aac10b2a70e28e4507c263d7587b33dd4e",
|
||||
"sourceDigest": "sha256:b10fe1bd9c5e5928d2afe984992093107ce10590e22296e1e3a91897567bfeb6",
|
||||
"declaration": {
|
||||
"schema": "paperclip.grok-profile-identity.v1",
|
||||
"agent": "grok",
|
||||
"agentProfileVersion": 18,
|
||||
"agentServerPackage": "builtin:grok-acp",
|
||||
"agentServerVersion": "1",
|
||||
"agentRuntimePackage": "native:grok",
|
||||
"agentRuntimeVersion": "1.0.13",
|
||||
"requiresProviderPolicy": false,
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"patches/acpx@0.13.1.patch": "sha256:1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2",
|
||||
"packages/paperclip-runner/src/cli/acpx-runtime-sidecar.ts": "sha256:477a05847187159f9c6f07bf90f1c0c1d432a9eb6fdb15d5620daf54443dafd8",
|
||||
"packages/paperclip-runner/src/cli/acpx-sidecar-input.ts": "sha256:17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts": "sha256:df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-effort.ts": "sha256:f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-web-search.ts": "sha256:754b5ca2345c045421f6007f34999508d996e1cea035f0f1952b3d5f00072b9c",
|
||||
"packages/paperclip-runner/src/drivers/acpx/grok-credentials.ts": "sha256:1c4ff0be6da798b6fcc604d20a9198dcc8523a3806d563b5b5345b7260549f73",
|
||||
"packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts": "sha256:c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-host.ts": "sha256:f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"packages/paperclip-runner/src/drivers/preconfigured-home.ts": "sha256:bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e",
|
||||
"packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts": "sha256:86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"packages/paperclip-runner/src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"packages/paperclip-runner/src/drivers/acpx/generated-sidecar-contract.ts": "sha256:b60e4b924e827e122860d2a4cc13b465ba748ee521620887c88f3c58c0df6081",
|
||||
"packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts": "sha256:aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b",
|
||||
"packages/paperclip-runner/src/providers/grok/launcher.cjs": "sha256:f0b698395a3704ed2ffaf84ea19bdb20c36c8a0a70b7c629c7b6ffe144e59e55",
|
||||
"packages/paperclip-runner/src/providers/grok/platforms.json": "sha256:931eed3abbf2bf041e6c72e6e9486d976f4352613cf069b216ddf010c9a8e704",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:01836e46cba936845c5e612775a9fa5dfc277cdcb7f54cb97da9a334d9580fe1",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:fc8d7cce62e0c02efddece8696e8d24f6a9fe19e5288a5e67a01ffbf931a3f6e",
|
||||
"packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:37a552c1a8a59b1815df99b79552e0fda711cfa934055073ffd682cd17d7fb54"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"declaration": {
|
||||
"schema": "paperclip.kimi-profile-identity.v1",
|
||||
"agent": "kimi",
|
||||
"agentProfileVersion": 1,
|
||||
"agentServerPackage": "@moonshot-ai/kimi-code",
|
||||
"agentServerVersion": "2.3.3",
|
||||
"agentRuntimePackage": null,
|
||||
"agentRuntimeVersion": null,
|
||||
"requiresProviderPolicy": true,
|
||||
"qualificationStatus": "pending",
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"scripts/patch-kimi-runtime.mjs": "sha256:94a141bcd76dfa5a92ecf9ff51623474a5b46e4fe9a5955c0e030a6fbe00af62",
|
||||
"scripts/kimi-instructions-runtime.mjs": "sha256:7697cd40f1123ce607ec61548dde1c31dff496f3fd7aed1afbe75bbe434e7859",
|
||||
"scripts/materialize-kimi-distribution.mjs": "sha256:7d61f91d2b32a8d22e5949619faed009c2fded8e034e84a2615a76548e2aa4d0",
|
||||
"scripts/provision-kimi.mjs": "sha256:d11a6320b47c1549572f192e6a3d532b4c48a7f2499afd91539626c04e917bca",
|
||||
"scripts/kimi-distribution/package.json": "sha256:13511f26a31248e42063c49eeaf6235b7448df12049c1bf45c2170cc20767175",
|
||||
"scripts/kimi-distribution/package-lock.json": "sha256:ff2cf7356dc031cac15d9f3339cf82bd1811399af59396e2d7629648c21298cf",
|
||||
"src/drivers/acpx/kimi-closure-pins.ts": "sha256:9c291ba7a09e1dd5a6774e7c096c1e0829bb99d48aef927880890944e6676d45",
|
||||
"src/drivers/acpx/kimi-config.ts": "sha256:718e238d97922211fa95e3df88a40321bf1ac2b63b395cd2e8f03e89b56616ed",
|
||||
"src/drivers/acpx/kimi-installation.ts": "sha256:753a1f8505bcfaff83ac76c425e45e4ff780861a6b6b0fbcb71e96c0c482669d",
|
||||
"src/drivers/acpx/kimi-instructions.ts": "sha256:a04e2d17aac37da2463c8a2c057f9e922ecdb68d9e922e9288e0445c870f9a36",
|
||||
"src/drivers/acpx/instruction-admission.ts": "sha256:371c17f328abed44f90ac37f3b686e1c0a0d6b647518e62935262ecc31450b16"
|
||||
}
|
||||
},
|
||||
"commandDigest": "sha256:260b99cef27309297ebf1366c6f588bcd83b2c35e4374106bdae2315a0edb84c"
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"declaration": {
|
||||
"schema": "paperclip.kimi-profile-identity.v1",
|
||||
"agent": "kimi",
|
||||
"agentProfileVersion": 10,
|
||||
"agentServerPackage": "@moonshot-ai/kimi-code",
|
||||
"agentServerVersion": "2.3.3",
|
||||
"agentRuntimePackage": null,
|
||||
"agentRuntimeVersion": null,
|
||||
"requiresProviderPolicy": true,
|
||||
"qualificationStatus": "pending",
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"scripts/patch-kimi-runtime.mjs": "sha256:dd2f3608a875cf49e9093a9b76d6bda77fa9df6067fb5d5c40765d701247d97b",
|
||||
"scripts/kimi-instructions-runtime.mjs": "sha256:c9a62629a7ed58119ed383a4ffdcc8def4337f1c429bb95363d41b8ffb23a746",
|
||||
"scripts/materialize-kimi-distribution.mjs": "sha256:8960525611571023a18832e7b0fa8673dc52fd289433d6e555de066f68e9e879",
|
||||
"scripts/provision-kimi.mjs": "sha256:b234438e37acd5f3698382bc53f14babd261bbf041334f8f793c055d4f694045",
|
||||
"scripts/kimi-distribution/package.json": "sha256:3abd5a37af052ac1e4417f675453ed47b433edfa3e072c42bb7725811cfe7bbb",
|
||||
"scripts/kimi-distribution/package-lock.json": "sha256:87f854ddd7f92254ccd8596b85962684ec176f92e93863771843bf416825f16b",
|
||||
"src/drivers/acpx/kimi-closure-pins.ts": "sha256:d1ffc2f0be70c2f7eba3c5c67d3a24394b5bb5aed879003a31e333d189f50e2f",
|
||||
"src/drivers/acpx/kimi-config.ts": "sha256:2307b7dd06bf33f2474243c801ee7d1a0a692ab3b47d2c709b6b4397ac4919c2",
|
||||
"src/drivers/acpx/kimi-installation.ts": "sha256:83b9d821b5dda0a09b40d8828e6ac3b4ebeaafb58467ee14847c7ffa2efde453",
|
||||
"src/drivers/acpx/kimi-instructions.ts": "sha256:6b306f98ba4c9ebe40cac24edc952f58177f4014def37718ed017ca2018f2991",
|
||||
"src/drivers/acpx/instruction-admission.ts": "sha256:adf5301e86f2b652679f1b18d00659b9b7214d16539dbd12ef9041586ee97944",
|
||||
"src/drivers/acpx/kimi-runtime-files.ts": "sha256:9ff7ea33588c423a50d0194b4327aa2840bbd8bdc7da9ec4b6da62fa827f054b",
|
||||
"src/configured-environment.ts": "sha256:a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"src/drivers/acpx/environment.ts": "sha256:a6733e28a9468b744e1e72f1af0c2236e17372e51a9a3ba254a406e085591d2d",
|
||||
"src/drivers/acpx/codex-runtime-adapter.ts": "sha256:601cbfe4e2811f771a3a59bf23fdbb05e33a04901af987f2ff96a8acf56374a3",
|
||||
"src/drivers/acpx/runtime-host.ts": "sha256:981a18aad23e34c3c675123d1dd0a920748821bad88885bb1a2e1a060c870921",
|
||||
"src/drivers/acpx/runtime-sandbox.ts": "sha256:d631dc7a3554c5eb4ed47bef966007245a133862277c96011b1fe1639135f59d",
|
||||
"src/cli/acpx-runtime-sidecar.ts": "sha256:152ec3e5066695c7a28b56cb9b4d5fbd30500c8e7beee54efb9a33fc8a532c9d",
|
||||
"scripts/build-verified-provider-entrypoints.mjs": "sha256:2860a13ee316924221c3bf4944e97ecde23c2f582270924b80e1e41648e21f31",
|
||||
"src/drivers/acpx/kimi-models.ts": "sha256:9d4bb2d1e0db4f929ed819150c1b030dbf3c917d15d15acb7418db2479dd7a92",
|
||||
"src/drivers/acpx/kimi-metadata-probe.ts": "sha256:56151bc4c58d5ba88018edb3588cd6f22451a4d83b2a129c9e23ab3c94cf2978",
|
||||
"src/cli/kimi-metadata-probe.ts": "sha256:953cb37cbf16accde2f81848ad4d64c7c30355b02952f6528f966eee1511fbf9",
|
||||
"src/drivers/acpx/kimi-message-boundary.ts": "sha256:11dbdabaa07d7909540b5041c642f3f60b20b660f72150e7b39155cf6abbfdd0",
|
||||
"src/drivers/acpx/codex-acpx-driver.ts": "sha256:9ee8f25f1ebcd44af15f12a46e5735980083672cd317f44cd10cf2e77699e352",
|
||||
"src/drivers/acpx/sidecar-protocol.ts": "sha256:26dd0cfbef8b2b401de6b7862a4af7f243fb703e23678a1a5ed8fa68d4784d42",
|
||||
"src/drivers/acpx/acp-edit-context.ts": "sha256:7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"src/drivers/acpx/acp-permission-adapter.ts": "sha256:444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"src/drivers/acpx/safe-locations.ts": "sha256:f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"src/drivers/acpx/generated-sidecar-contract.ts": "sha256:1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
|
||||
"src/semantic-tools/redaction.ts": "sha256:ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"runner/crates/runner-core/src/acpx_provider_state.rs": "sha256:be0e1006763902efb6bdd3952edd58058ab9139859647d413ada4c853110a324"
|
||||
}
|
||||
},
|
||||
"commandDigest": "sha256:c8a39af9d580cdb5fe20a3333a566e2f97a1b4ec3a0025cb647d2144df358604"
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"declaration": {
|
||||
"schema": "paperclip.kimi-profile-identity.v1",
|
||||
"agent": "kimi",
|
||||
"agentProfileVersion": 11,
|
||||
"agentServerPackage": "@moonshot-ai/kimi-code",
|
||||
"agentServerVersion": "2.3.3",
|
||||
"agentRuntimePackage": null,
|
||||
"agentRuntimeVersion": null,
|
||||
"requiresProviderPolicy": true,
|
||||
"qualificationStatus": "pending",
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"scripts/patch-kimi-runtime.mjs": "sha256:dd2f3608a875cf49e9093a9b76d6bda77fa9df6067fb5d5c40765d701247d97b",
|
||||
"scripts/kimi-instructions-runtime.mjs": "sha256:c9a62629a7ed58119ed383a4ffdcc8def4337f1c429bb95363d41b8ffb23a746",
|
||||
"scripts/materialize-kimi-distribution.mjs": "sha256:8960525611571023a18832e7b0fa8673dc52fd289433d6e555de066f68e9e879",
|
||||
"scripts/provision-kimi.mjs": "sha256:b234438e37acd5f3698382bc53f14babd261bbf041334f8f793c055d4f694045",
|
||||
"scripts/kimi-distribution/package.json": "sha256:3abd5a37af052ac1e4417f675453ed47b433edfa3e072c42bb7725811cfe7bbb",
|
||||
"scripts/kimi-distribution/package-lock.json": "sha256:87f854ddd7f92254ccd8596b85962684ec176f92e93863771843bf416825f16b",
|
||||
"src/drivers/acpx/kimi-closure-pins.ts": "sha256:d1ffc2f0be70c2f7eba3c5c67d3a24394b5bb5aed879003a31e333d189f50e2f",
|
||||
"src/drivers/acpx/kimi-config.ts": "sha256:2307b7dd06bf33f2474243c801ee7d1a0a692ab3b47d2c709b6b4397ac4919c2",
|
||||
"src/drivers/acpx/kimi-installation.ts": "sha256:83b9d821b5dda0a09b40d8828e6ac3b4ebeaafb58467ee14847c7ffa2efde453",
|
||||
"src/drivers/acpx/kimi-instructions.ts": "sha256:6b306f98ba4c9ebe40cac24edc952f58177f4014def37718ed017ca2018f2991",
|
||||
"src/drivers/acpx/instruction-admission.ts": "sha256:adf5301e86f2b652679f1b18d00659b9b7214d16539dbd12ef9041586ee97944",
|
||||
"src/drivers/acpx/kimi-runtime-files.ts": "sha256:9ff7ea33588c423a50d0194b4327aa2840bbd8bdc7da9ec4b6da62fa827f054b",
|
||||
"src/configured-environment.ts": "sha256:a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"src/drivers/acpx/environment.ts": "sha256:a6733e28a9468b744e1e72f1af0c2236e17372e51a9a3ba254a406e085591d2d",
|
||||
"src/drivers/acpx/codex-runtime-adapter.ts": "sha256:601cbfe4e2811f771a3a59bf23fdbb05e33a04901af987f2ff96a8acf56374a3",
|
||||
"src/drivers/acpx/runtime-host.ts": "sha256:981a18aad23e34c3c675123d1dd0a920748821bad88885bb1a2e1a060c870921",
|
||||
"src/drivers/acpx/runtime-sandbox.ts": "sha256:d631dc7a3554c5eb4ed47bef966007245a133862277c96011b1fe1639135f59d",
|
||||
"src/cli/acpx-runtime-sidecar.ts": "sha256:152ec3e5066695c7a28b56cb9b4d5fbd30500c8e7beee54efb9a33fc8a532c9d",
|
||||
"scripts/build-verified-provider-entrypoints.mjs": "sha256:70f7b314e86a22ad2df192eb6ff984e5f286002e165eec71292fff75064f7b9f",
|
||||
"src/drivers/acpx/kimi-models.ts": "sha256:9d4bb2d1e0db4f929ed819150c1b030dbf3c917d15d15acb7418db2479dd7a92",
|
||||
"src/drivers/acpx/kimi-metadata-probe.ts": "sha256:56151bc4c58d5ba88018edb3588cd6f22451a4d83b2a129c9e23ab3c94cf2978",
|
||||
"src/cli/kimi-metadata-probe.ts": "sha256:953cb37cbf16accde2f81848ad4d64c7c30355b02952f6528f966eee1511fbf9",
|
||||
"src/drivers/acpx/kimi-message-boundary.ts": "sha256:11dbdabaa07d7909540b5041c642f3f60b20b660f72150e7b39155cf6abbfdd0",
|
||||
"src/drivers/acpx/codex-acpx-driver.ts": "sha256:9ee8f25f1ebcd44af15f12a46e5735980083672cd317f44cd10cf2e77699e352",
|
||||
"src/drivers/acpx/sidecar-protocol.ts": "sha256:26dd0cfbef8b2b401de6b7862a4af7f243fb703e23678a1a5ed8fa68d4784d42",
|
||||
"src/drivers/acpx/acp-edit-context.ts": "sha256:7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"src/drivers/acpx/acp-permission-adapter.ts": "sha256:444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"src/drivers/acpx/safe-locations.ts": "sha256:f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"src/drivers/acpx/generated-sidecar-contract.ts": "sha256:1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
|
||||
"src/semantic-tools/redaction.ts": "sha256:ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"runner/crates/runner-core/src/acpx_provider_state.rs": "sha256:be0e1006763902efb6bdd3952edd58058ab9139859647d413ada4c853110a324"
|
||||
}
|
||||
},
|
||||
"commandDigest": "sha256:6bdee2692b41fea57ac9be810a5009bb5d65650beca9c816e310b27f0974cccc"
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"declaration": {
|
||||
"schema": "paperclip.kimi-profile-identity.v1",
|
||||
"agent": "kimi",
|
||||
"agentProfileVersion": 13,
|
||||
"agentServerPackage": "@moonshot-ai/kimi-code",
|
||||
"agentServerVersion": "2.3.3",
|
||||
"agentRuntimePackage": null,
|
||||
"agentRuntimeVersion": null,
|
||||
"requiresProviderPolicy": true,
|
||||
"qualificationStatus": "pending",
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"scripts/patch-kimi-runtime.mjs": "sha256:dd2f3608a875cf49e9093a9b76d6bda77fa9df6067fb5d5c40765d701247d97b",
|
||||
"scripts/kimi-instructions-runtime.mjs": "sha256:c9a62629a7ed58119ed383a4ffdcc8def4337f1c429bb95363d41b8ffb23a746",
|
||||
"scripts/materialize-kimi-distribution.mjs": "sha256:8960525611571023a18832e7b0fa8673dc52fd289433d6e555de066f68e9e879",
|
||||
"scripts/provision-kimi.mjs": "sha256:b234438e37acd5f3698382bc53f14babd261bbf041334f8f793c055d4f694045",
|
||||
"scripts/kimi-distribution/package.json": "sha256:3abd5a37af052ac1e4417f675453ed47b433edfa3e072c42bb7725811cfe7bbb",
|
||||
"scripts/kimi-distribution/package-lock.json": "sha256:87f854ddd7f92254ccd8596b85962684ec176f92e93863771843bf416825f16b",
|
||||
"src/drivers/acpx/kimi-closure-pins.ts": "sha256:d1ffc2f0be70c2f7eba3c5c67d3a24394b5bb5aed879003a31e333d189f50e2f",
|
||||
"src/drivers/acpx/kimi-config.ts": "sha256:2307b7dd06bf33f2474243c801ee7d1a0a692ab3b47d2c709b6b4397ac4919c2",
|
||||
"src/drivers/acpx/kimi-installation.ts": "sha256:83b9d821b5dda0a09b40d8828e6ac3b4ebeaafb58467ee14847c7ffa2efde453",
|
||||
"src/drivers/acpx/kimi-instructions.ts": "sha256:6b306f98ba4c9ebe40cac24edc952f58177f4014def37718ed017ca2018f2991",
|
||||
"src/drivers/acpx/session-context-admission.ts": "sha256:c4026e6c51b9a7fb731b178e03077917f6f0f71835da0cdb083dce11f2ddae93",
|
||||
"src/drivers/acpx/kimi-runtime-files.ts": "sha256:9ff7ea33588c423a50d0194b4327aa2840bbd8bdc7da9ec4b6da62fa827f054b",
|
||||
"src/configured-environment.ts": "sha256:a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"src/drivers/acpx/environment.ts": "sha256:9ace39a71ba287838c1b9c56bed141248e805fd017eb8490770c3b579567e55b",
|
||||
"src/drivers/acpx/codex-runtime-adapter.ts": "sha256:c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"src/drivers/acpx/runtime-host.ts": "sha256:f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"src/drivers/acpx/runtime-sandbox.ts": "sha256:86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"src/cli/acpx-runtime-sidecar.ts": "sha256:6816fe12cdd274746b5241b75823b3c347b245f3cc03cdbe70f05ec1a0250663",
|
||||
"scripts/build-verified-provider-entrypoints.mjs": "sha256:70f7b314e86a22ad2df192eb6ff984e5f286002e165eec71292fff75064f7b9f",
|
||||
"src/drivers/acpx/kimi-models.ts": "sha256:9d4bb2d1e0db4f929ed819150c1b030dbf3c917d15d15acb7418db2479dd7a92",
|
||||
"src/drivers/acpx/kimi-metadata-probe.ts": "sha256:0f6f2865065500d77460ec761fa13661a3149dd34422812201026709f7cb84f8",
|
||||
"src/cli/kimi-metadata-probe.ts": "sha256:306e2caca87977c3ea511ec43c937fd801f4c8cf9b4337b1ed8a3e315c1cdb37",
|
||||
"src/drivers/acpx/kimi-message-boundary.ts": "sha256:11dbdabaa07d7909540b5041c642f3f60b20b660f72150e7b39155cf6abbfdd0",
|
||||
"src/drivers/acpx/codex-acpx-driver.ts": "sha256:d11dea41590394225d622c2f4b4141ede0445fdb529fec1d6ce3fe749f354234",
|
||||
"src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"src/drivers/acpx/acp-edit-context.ts": "sha256:7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"src/drivers/acpx/acp-permission-adapter.ts": "sha256:444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"src/drivers/acpx/safe-locations.ts": "sha256:f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"src/drivers/acpx/generated-sidecar-contract.ts": "sha256:1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
|
||||
"src/semantic-tools/redaction.ts": "sha256:ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"runner/crates/runner-core/src/acpx_provider_state.rs": "sha256:be0e1006763902efb6bdd3952edd58058ab9139859647d413ada4c853110a324",
|
||||
"src/drivers/acpx/candidate-admission.ts": "sha256:72a3c8b1e97f6aeedaeff4fd4a75edfbd2104000607517c190dd4c34f6d6327c",
|
||||
"src/drivers/acpx/qualified-profiles.ts": "sha256:f0c3948ba291aa4b9ae54c0aebd2bd7967739ce40d48ccbe717491500fd9105b",
|
||||
"src/drivers/acpx/profile-installation.ts": "sha256:d229b6afef695991313acdf490991136d40d638359665e0c48afb675af94f863",
|
||||
"src/drivers/acpx/driver-profile.ts": "sha256:857d75d11cb1b82d22275443dab79ef674214b35f103dff30e1ab14915d80a47",
|
||||
"src/live/runnerd-codex-transport.ts": "sha256:1554851fd4b32561e8575b8ad72a28528172dfc0372a014d11f02668991d5e49",
|
||||
"runner/crates/runner-core/src/acpx_candidate_admission.rs": "sha256:65e14dd8cd7304c92b63972801dad535d52b5a6a4ea499f6c315dbc82ce258d6",
|
||||
"runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:8c1d2bdc1cdd808b51b624b1bd1b576b8f72e72e89fbac3ff1f9beef14878480",
|
||||
"runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:fc8d7cce62e0c02efddece8696e8d24f6a9fe19e5288a5e67a01ffbf931a3f6e",
|
||||
"runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:37a552c1a8a59b1815df99b79552e0fda711cfa934055073ffd682cd17d7fb54",
|
||||
"runner/crates/runner-core/src/native_provider_backend.rs": "sha256:d2aa6a65be9195e5db0fbe83103be7f6897dedb8195872191d3eb0392cb4a1cc",
|
||||
"runner/crates/runner-core/src/durable/runner.rs": "sha256:7335562b0393c011fe60d7820afeea965345478dd72757dbf91f025d44d3c36c",
|
||||
"runner/crates/runner-core/src/durable/mod.rs": "sha256:b5989da5304dfdfe04d0060a0a249e853c784af1cdd933bcf0d5a8fd07f0e895",
|
||||
"src/provider-events.ts": "sha256:a6b77d969fd2f767dd8e583e81b907973c429b6e32d2c2d7fef0856f3568f07a",
|
||||
"src/native-session-runtime.ts": "sha256:cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea",
|
||||
"src/control-plane/durable-prp-control-plane.ts": "sha256:a8a8bed373cdd18397a762b9f67b04c789d9e5d63208f06c5ebe000faff090ac",
|
||||
"src/drivers/codex/codex-harness-session.ts": "sha256:bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851",
|
||||
"src/drivers/codex/codex-session-notifications.ts": "sha256:23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa",
|
||||
"src/drivers/codex/codex-session-state.ts": "sha256:c2f1059c5f1532cf3c2059792324a6f55d0c06ec8a62c3a4ab2c9451d617988a",
|
||||
"runner/crates/runner-core/src/provider_backend.rs": "sha256:03a57d83901f0fbccfb7bcdaeae79baf8918f658ceb3463406211431944a461e",
|
||||
"runner/crates/runner-core/src/provider_bridge.rs": "sha256:d417840ecd87e1a8a407629074f6c64bad4071c5e9add4b5a2aef0b3403b5529",
|
||||
"runner/crates/runner-core/src/provider_events.rs": "sha256:91dfa4985691f86ab7f60514e46d924fb815900d6d456d78780bd94d29bff461",
|
||||
"runner/crates/runner-core/src/acpx_event_scope.rs": "sha256:088f4b5c67b5663cbce2ef0b8b8cb3e6fe04d2b5c9cfa7a8139f8f0bf91e476e",
|
||||
"runner/crates/runner-core/src/acpx_event_payload.rs": "sha256:f3b2f6ea9c2e9cf673790eef2f7d4e738c36a2e6347e1fd61ae501793cef1f15",
|
||||
"scripts/generate-acpx-profiles.mjs": "sha256:e878a3f8afa9ce38940e61343cfbdc55d6431e9574108ca5fb17f0c72ead7423"
|
||||
}
|
||||
},
|
||||
"commandDigest": "sha256:8fcd50b260d61244499bb2de97be745dca7652b9901acd04aa54a135c4bf23cb"
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"declaration": {
|
||||
"schema": "paperclip.kimi-profile-identity.v1",
|
||||
"agent": "kimi",
|
||||
"agentProfileVersion": 14,
|
||||
"agentServerPackage": "@moonshot-ai/kimi-code",
|
||||
"agentServerVersion": "2.3.3",
|
||||
"agentRuntimePackage": null,
|
||||
"agentRuntimeVersion": null,
|
||||
"requiresProviderPolicy": true,
|
||||
"qualificationStatus": "pending",
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"scripts/patch-kimi-runtime.mjs": "sha256:dd2f3608a875cf49e9093a9b76d6bda77fa9df6067fb5d5c40765d701247d97b",
|
||||
"scripts/kimi-instructions-runtime.mjs": "sha256:c9a62629a7ed58119ed383a4ffdcc8def4337f1c429bb95363d41b8ffb23a746",
|
||||
"scripts/materialize-kimi-distribution.mjs": "sha256:8960525611571023a18832e7b0fa8673dc52fd289433d6e555de066f68e9e879",
|
||||
"scripts/provision-kimi.mjs": "sha256:b234438e37acd5f3698382bc53f14babd261bbf041334f8f793c055d4f694045",
|
||||
"scripts/kimi-distribution/package.json": "sha256:3abd5a37af052ac1e4417f675453ed47b433edfa3e072c42bb7725811cfe7bbb",
|
||||
"scripts/kimi-distribution/package-lock.json": "sha256:87f854ddd7f92254ccd8596b85962684ec176f92e93863771843bf416825f16b",
|
||||
"src/drivers/acpx/kimi-closure-pins.ts": "sha256:d1ffc2f0be70c2f7eba3c5c67d3a24394b5bb5aed879003a31e333d189f50e2f",
|
||||
"src/drivers/acpx/kimi-config.ts": "sha256:2307b7dd06bf33f2474243c801ee7d1a0a692ab3b47d2c709b6b4397ac4919c2",
|
||||
"src/drivers/acpx/kimi-installation.ts": "sha256:83b9d821b5dda0a09b40d8828e6ac3b4ebeaafb58467ee14847c7ffa2efde453",
|
||||
"src/drivers/acpx/kimi-instructions.ts": "sha256:6b306f98ba4c9ebe40cac24edc952f58177f4014def37718ed017ca2018f2991",
|
||||
"src/drivers/acpx/session-context-admission.ts": "sha256:c4026e6c51b9a7fb731b178e03077917f6f0f71835da0cdb083dce11f2ddae93",
|
||||
"src/drivers/acpx/kimi-runtime-files.ts": "sha256:9ff7ea33588c423a50d0194b4327aa2840bbd8bdc7da9ec4b6da62fa827f054b",
|
||||
"src/configured-environment.ts": "sha256:a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"src/drivers/acpx/environment.ts": "sha256:9ace39a71ba287838c1b9c56bed141248e805fd017eb8490770c3b579567e55b",
|
||||
"src/drivers/acpx/codex-runtime-adapter.ts": "sha256:c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"src/drivers/acpx/runtime-host.ts": "sha256:f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"src/drivers/acpx/runtime-sandbox.ts": "sha256:86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"src/cli/acpx-runtime-sidecar.ts": "sha256:6816fe12cdd274746b5241b75823b3c347b245f3cc03cdbe70f05ec1a0250663",
|
||||
"scripts/build-verified-provider-entrypoints.mjs": "sha256:70f7b314e86a22ad2df192eb6ff984e5f286002e165eec71292fff75064f7b9f",
|
||||
"src/drivers/acpx/kimi-models.ts": "sha256:9d4bb2d1e0db4f929ed819150c1b030dbf3c917d15d15acb7418db2479dd7a92",
|
||||
"src/drivers/acpx/kimi-metadata-probe.ts": "sha256:0f6f2865065500d77460ec761fa13661a3149dd34422812201026709f7cb84f8",
|
||||
"src/cli/kimi-metadata-probe.ts": "sha256:306e2caca87977c3ea511ec43c937fd801f4c8cf9b4337b1ed8a3e315c1cdb37",
|
||||
"src/drivers/acpx/kimi-message-boundary.ts": "sha256:11dbdabaa07d7909540b5041c642f3f60b20b660f72150e7b39155cf6abbfdd0",
|
||||
"src/drivers/acpx/codex-acpx-driver.ts": "sha256:d11dea41590394225d622c2f4b4141ede0445fdb529fec1d6ce3fe749f354234",
|
||||
"src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"src/drivers/acpx/acp-edit-context.ts": "sha256:7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"src/drivers/acpx/acp-permission-adapter.ts": "sha256:444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"src/drivers/acpx/safe-locations.ts": "sha256:f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"src/drivers/acpx/generated-sidecar-contract.ts": "sha256:1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
|
||||
"src/semantic-tools/redaction.ts": "sha256:ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"runner/crates/runner-core/src/acpx_provider_state.rs": "sha256:be0e1006763902efb6bdd3952edd58058ab9139859647d413ada4c853110a324",
|
||||
"src/drivers/acpx/candidate-admission.ts": "sha256:72a3c8b1e97f6aeedaeff4fd4a75edfbd2104000607517c190dd4c34f6d6327c",
|
||||
"src/drivers/acpx/qualified-profiles.ts": "sha256:f0c3948ba291aa4b9ae54c0aebd2bd7967739ce40d48ccbe717491500fd9105b",
|
||||
"src/drivers/acpx/profile-installation.ts": "sha256:d229b6afef695991313acdf490991136d40d638359665e0c48afb675af94f863",
|
||||
"src/drivers/acpx/driver-profile.ts": "sha256:857d75d11cb1b82d22275443dab79ef674214b35f103dff30e1ab14915d80a47",
|
||||
"src/live/runnerd-codex-transport.ts": "sha256:1554851fd4b32561e8575b8ad72a28528172dfc0372a014d11f02668991d5e49",
|
||||
"runner/crates/runner-core/src/acpx_candidate_admission.rs": "sha256:65e14dd8cd7304c92b63972801dad535d52b5a6a4ea499f6c315dbc82ce258d6",
|
||||
"runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:da62bf0a159b6fe65c3fff5d9ed7ad58ae32cff6b22ed1b787f909e8eaa0f564",
|
||||
"runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:fc8d7cce62e0c02efddece8696e8d24f6a9fe19e5288a5e67a01ffbf931a3f6e",
|
||||
"runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:37a552c1a8a59b1815df99b79552e0fda711cfa934055073ffd682cd17d7fb54",
|
||||
"runner/crates/runner-core/src/native_provider_backend.rs": "sha256:d2aa6a65be9195e5db0fbe83103be7f6897dedb8195872191d3eb0392cb4a1cc",
|
||||
"runner/crates/runner-core/src/durable/runner.rs": "sha256:7335562b0393c011fe60d7820afeea965345478dd72757dbf91f025d44d3c36c",
|
||||
"runner/crates/runner-core/src/durable/mod.rs": "sha256:b5989da5304dfdfe04d0060a0a249e853c784af1cdd933bcf0d5a8fd07f0e895",
|
||||
"src/provider-events.ts": "sha256:a6b77d969fd2f767dd8e583e81b907973c429b6e32d2c2d7fef0856f3568f07a",
|
||||
"src/native-session-runtime.ts": "sha256:cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea",
|
||||
"src/control-plane/durable-prp-control-plane.ts": "sha256:a8a8bed373cdd18397a762b9f67b04c789d9e5d63208f06c5ebe000faff090ac",
|
||||
"src/drivers/codex/codex-harness-session.ts": "sha256:bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851",
|
||||
"src/drivers/codex/codex-session-notifications.ts": "sha256:23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa",
|
||||
"src/drivers/codex/codex-session-state.ts": "sha256:c2f1059c5f1532cf3c2059792324a6f55d0c06ec8a62c3a4ab2c9451d617988a",
|
||||
"runner/crates/runner-core/src/provider_backend.rs": "sha256:03a57d83901f0fbccfb7bcdaeae79baf8918f658ceb3463406211431944a461e",
|
||||
"runner/crates/runner-core/src/provider_bridge.rs": "sha256:d417840ecd87e1a8a407629074f6c64bad4071c5e9add4b5a2aef0b3403b5529",
|
||||
"runner/crates/runner-core/src/provider_events.rs": "sha256:91dfa4985691f86ab7f60514e46d924fb815900d6d456d78780bd94d29bff461",
|
||||
"runner/crates/runner-core/src/acpx_event_scope.rs": "sha256:088f4b5c67b5663cbce2ef0b8b8cb3e6fe04d2b5c9cfa7a8139f8f0bf91e476e",
|
||||
"runner/crates/runner-core/src/acpx_event_payload.rs": "sha256:f3b2f6ea9c2e9cf673790eef2f7d4e738c36a2e6347e1fd61ae501793cef1f15",
|
||||
"scripts/generate-acpx-profiles.mjs": "sha256:e878a3f8afa9ce38940e61343cfbdc55d6431e9574108ca5fb17f0c72ead7423"
|
||||
}
|
||||
},
|
||||
"commandDigest": "sha256:d917ef7222b71c8dbe1df46c33f7a439f15492db862307aa6b5427bf82ed196b"
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"declaration": {
|
||||
"schema": "paperclip.kimi-profile-identity.v1",
|
||||
"agent": "kimi",
|
||||
"agentProfileVersion": 15,
|
||||
"agentServerPackage": "@moonshot-ai/kimi-code",
|
||||
"agentServerVersion": "2.3.3",
|
||||
"agentRuntimePackage": null,
|
||||
"agentRuntimeVersion": null,
|
||||
"requiresProviderPolicy": true,
|
||||
"qualificationStatus": "pending",
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"scripts/patch-kimi-runtime.mjs": "sha256:dd2f3608a875cf49e9093a9b76d6bda77fa9df6067fb5d5c40765d701247d97b",
|
||||
"scripts/kimi-instructions-runtime.mjs": "sha256:c9a62629a7ed58119ed383a4ffdcc8def4337f1c429bb95363d41b8ffb23a746",
|
||||
"scripts/materialize-kimi-distribution.mjs": "sha256:8960525611571023a18832e7b0fa8673dc52fd289433d6e555de066f68e9e879",
|
||||
"scripts/provision-kimi.mjs": "sha256:b234438e37acd5f3698382bc53f14babd261bbf041334f8f793c055d4f694045",
|
||||
"scripts/kimi-distribution/package.json": "sha256:3abd5a37af052ac1e4417f675453ed47b433edfa3e072c42bb7725811cfe7bbb",
|
||||
"scripts/kimi-distribution/package-lock.json": "sha256:87f854ddd7f92254ccd8596b85962684ec176f92e93863771843bf416825f16b",
|
||||
"src/drivers/acpx/kimi-closure-pins.ts": "sha256:d1ffc2f0be70c2f7eba3c5c67d3a24394b5bb5aed879003a31e333d189f50e2f",
|
||||
"src/drivers/acpx/kimi-config.ts": "sha256:2307b7dd06bf33f2474243c801ee7d1a0a692ab3b47d2c709b6b4397ac4919c2",
|
||||
"src/drivers/acpx/kimi-installation.ts": "sha256:83b9d821b5dda0a09b40d8828e6ac3b4ebeaafb58467ee14847c7ffa2efde453",
|
||||
"src/drivers/acpx/kimi-instructions.ts": "sha256:6b306f98ba4c9ebe40cac24edc952f58177f4014def37718ed017ca2018f2991",
|
||||
"src/drivers/acpx/session-context-admission.ts": "sha256:c4026e6c51b9a7fb731b178e03077917f6f0f71835da0cdb083dce11f2ddae93",
|
||||
"src/drivers/acpx/kimi-runtime-files.ts": "sha256:9ff7ea33588c423a50d0194b4327aa2840bbd8bdc7da9ec4b6da62fa827f054b",
|
||||
"src/configured-environment.ts": "sha256:a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"src/drivers/acpx/environment.ts": "sha256:9ace39a71ba287838c1b9c56bed141248e805fd017eb8490770c3b579567e55b",
|
||||
"src/drivers/acpx/codex-runtime-adapter.ts": "sha256:c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"src/drivers/acpx/runtime-host.ts": "sha256:f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"src/drivers/acpx/runtime-sandbox.ts": "sha256:86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"src/cli/acpx-runtime-sidecar.ts": "sha256:6816fe12cdd274746b5241b75823b3c347b245f3cc03cdbe70f05ec1a0250663",
|
||||
"scripts/build-verified-provider-entrypoints.mjs": "sha256:70f7b314e86a22ad2df192eb6ff984e5f286002e165eec71292fff75064f7b9f",
|
||||
"src/drivers/acpx/kimi-models.ts": "sha256:9d4bb2d1e0db4f929ed819150c1b030dbf3c917d15d15acb7418db2479dd7a92",
|
||||
"src/drivers/acpx/kimi-metadata-probe.ts": "sha256:0f6f2865065500d77460ec761fa13661a3149dd34422812201026709f7cb84f8",
|
||||
"src/cli/kimi-metadata-probe.ts": "sha256:306e2caca87977c3ea511ec43c937fd801f4c8cf9b4337b1ed8a3e315c1cdb37",
|
||||
"src/drivers/acpx/kimi-message-boundary.ts": "sha256:11dbdabaa07d7909540b5041c642f3f60b20b660f72150e7b39155cf6abbfdd0",
|
||||
"src/drivers/acpx/codex-acpx-driver.ts": "sha256:d11dea41590394225d622c2f4b4141ede0445fdb529fec1d6ce3fe749f354234",
|
||||
"src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"src/drivers/acpx/acp-edit-context.ts": "sha256:7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"src/drivers/acpx/acp-permission-adapter.ts": "sha256:444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"src/drivers/acpx/safe-locations.ts": "sha256:f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"src/drivers/acpx/generated-sidecar-contract.ts": "sha256:1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4",
|
||||
"src/semantic-tools/redaction.ts": "sha256:ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"runner/crates/runner-core/src/acpx_provider_state.rs": "sha256:be0e1006763902efb6bdd3952edd58058ab9139859647d413ada4c853110a324",
|
||||
"src/drivers/acpx/candidate-admission.ts": "sha256:72a3c8b1e97f6aeedaeff4fd4a75edfbd2104000607517c190dd4c34f6d6327c",
|
||||
"src/drivers/acpx/qualified-profiles.ts": "sha256:f0c3948ba291aa4b9ae54c0aebd2bd7967739ce40d48ccbe717491500fd9105b",
|
||||
"src/drivers/acpx/profile-installation.ts": "sha256:d229b6afef695991313acdf490991136d40d638359665e0c48afb675af94f863",
|
||||
"src/drivers/acpx/driver-profile.ts": "sha256:857d75d11cb1b82d22275443dab79ef674214b35f103dff30e1ab14915d80a47",
|
||||
"src/live/runnerd-codex-transport.ts": "sha256:1554851fd4b32561e8575b8ad72a28528172dfc0372a014d11f02668991d5e49",
|
||||
"runner/crates/runner-core/src/acpx_candidate_admission.rs": "sha256:65e14dd8cd7304c92b63972801dad535d52b5a6a4ea499f6c315dbc82ce258d6",
|
||||
"runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:01836e46cba936845c5e612775a9fa5dfc277cdcb7f54cb97da9a334d9580fe1",
|
||||
"runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:fc8d7cce62e0c02efddece8696e8d24f6a9fe19e5288a5e67a01ffbf931a3f6e",
|
||||
"runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:37a552c1a8a59b1815df99b79552e0fda711cfa934055073ffd682cd17d7fb54",
|
||||
"runner/crates/runner-core/src/native_provider_backend.rs": "sha256:d2aa6a65be9195e5db0fbe83103be7f6897dedb8195872191d3eb0392cb4a1cc",
|
||||
"runner/crates/runner-core/src/durable/runner.rs": "sha256:7335562b0393c011fe60d7820afeea965345478dd72757dbf91f025d44d3c36c",
|
||||
"runner/crates/runner-core/src/durable/mod.rs": "sha256:b5989da5304dfdfe04d0060a0a249e853c784af1cdd933bcf0d5a8fd07f0e895",
|
||||
"src/provider-events.ts": "sha256:a6b77d969fd2f767dd8e583e81b907973c429b6e32d2c2d7fef0856f3568f07a",
|
||||
"src/native-session-runtime.ts": "sha256:cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea",
|
||||
"src/control-plane/durable-prp-control-plane.ts": "sha256:a8a8bed373cdd18397a762b9f67b04c789d9e5d63208f06c5ebe000faff090ac",
|
||||
"src/drivers/codex/codex-harness-session.ts": "sha256:bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851",
|
||||
"src/drivers/codex/codex-session-notifications.ts": "sha256:23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa",
|
||||
"src/drivers/codex/codex-session-state.ts": "sha256:c2f1059c5f1532cf3c2059792324a6f55d0c06ec8a62c3a4ab2c9451d617988a",
|
||||
"runner/crates/runner-core/src/provider_backend.rs": "sha256:03a57d83901f0fbccfb7bcdaeae79baf8918f658ceb3463406211431944a461e",
|
||||
"runner/crates/runner-core/src/provider_bridge.rs": "sha256:d417840ecd87e1a8a407629074f6c64bad4071c5e9add4b5a2aef0b3403b5529",
|
||||
"runner/crates/runner-core/src/provider_events.rs": "sha256:91dfa4985691f86ab7f60514e46d924fb815900d6d456d78780bd94d29bff461",
|
||||
"runner/crates/runner-core/src/acpx_event_scope.rs": "sha256:088f4b5c67b5663cbce2ef0b8b8cb3e6fe04d2b5c9cfa7a8139f8f0bf91e476e",
|
||||
"runner/crates/runner-core/src/acpx_event_payload.rs": "sha256:f3b2f6ea9c2e9cf673790eef2f7d4e738c36a2e6347e1fd61ae501793cef1f15",
|
||||
"scripts/generate-acpx-profiles.mjs": "sha256:e878a3f8afa9ce38940e61343cfbdc55d6431e9574108ca5fb17f0c72ead7423"
|
||||
}
|
||||
},
|
||||
"commandDigest": "sha256:606c0cfcaee89813bef5531d666eb208e839116de27cdd46035a08d38c00ecce"
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"declaration": {
|
||||
"schema": "paperclip.kimi-profile-identity.v1",
|
||||
"agent": "kimi",
|
||||
"agentProfileVersion": 16,
|
||||
"agentServerPackage": "@moonshot-ai/kimi-code",
|
||||
"agentServerVersion": "2.3.3",
|
||||
"agentRuntimePackage": null,
|
||||
"agentRuntimeVersion": null,
|
||||
"requiresProviderPolicy": true,
|
||||
"qualificationStatus": "pending",
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"scripts/patch-kimi-runtime.mjs": "sha256:dd2f3608a875cf49e9093a9b76d6bda77fa9df6067fb5d5c40765d701247d97b",
|
||||
"scripts/kimi-instructions-runtime.mjs": "sha256:c9a62629a7ed58119ed383a4ffdcc8def4337f1c429bb95363d41b8ffb23a746",
|
||||
"scripts/materialize-kimi-distribution.mjs": "sha256:8960525611571023a18832e7b0fa8673dc52fd289433d6e555de066f68e9e879",
|
||||
"scripts/provision-kimi.mjs": "sha256:b234438e37acd5f3698382bc53f14babd261bbf041334f8f793c055d4f694045",
|
||||
"scripts/kimi-distribution/package.json": "sha256:3abd5a37af052ac1e4417f675453ed47b433edfa3e072c42bb7725811cfe7bbb",
|
||||
"scripts/kimi-distribution/package-lock.json": "sha256:87f854ddd7f92254ccd8596b85962684ec176f92e93863771843bf416825f16b",
|
||||
"src/drivers/acpx/kimi-closure-pins.ts": "sha256:d1ffc2f0be70c2f7eba3c5c67d3a24394b5bb5aed879003a31e333d189f50e2f",
|
||||
"src/drivers/acpx/kimi-config.ts": "sha256:2307b7dd06bf33f2474243c801ee7d1a0a692ab3b47d2c709b6b4397ac4919c2",
|
||||
"src/drivers/acpx/kimi-installation.ts": "sha256:83b9d821b5dda0a09b40d8828e6ac3b4ebeaafb58467ee14847c7ffa2efde453",
|
||||
"src/drivers/acpx/kimi-instructions.ts": "sha256:6b306f98ba4c9ebe40cac24edc952f58177f4014def37718ed017ca2018f2991",
|
||||
"src/drivers/acpx/session-context-admission.ts": "sha256:c4026e6c51b9a7fb731b178e03077917f6f0f71835da0cdb083dce11f2ddae93",
|
||||
"src/drivers/acpx/kimi-runtime-files.ts": "sha256:9ff7ea33588c423a50d0194b4327aa2840bbd8bdc7da9ec4b6da62fa827f054b",
|
||||
"src/configured-environment.ts": "sha256:a872e5b939c496955615ce87889ad27eb1ba2a9d8fc25e6bb74e03b1c2293feb",
|
||||
"src/drivers/acpx/environment.ts": "sha256:9ace39a71ba287838c1b9c56bed141248e805fd017eb8490770c3b579567e55b",
|
||||
"src/drivers/acpx/codex-runtime-adapter.ts": "sha256:c676b83d9dbef86d781069a8b25e160615b288177729974267fe0c2da25afb71",
|
||||
"src/drivers/acpx/runtime-host.ts": "sha256:f44e5555b722553ab7ea1c982d355b1694d3c5fe1cdf19b9868e4bfbaa587b72",
|
||||
"src/drivers/acpx/runtime-sandbox.ts": "sha256:86dcc0abbbe2e32f88febbbab22f662d8490d438a9e9cb5df04589fdf66ebd88",
|
||||
"src/cli/acpx-runtime-sidecar.ts": "sha256:477a05847187159f9c6f07bf90f1c0c1d432a9eb6fdb15d5620daf54443dafd8",
|
||||
"scripts/build-verified-provider-entrypoints.mjs": "sha256:70f7b314e86a22ad2df192eb6ff984e5f286002e165eec71292fff75064f7b9f",
|
||||
"src/drivers/acpx/kimi-models.ts": "sha256:9d4bb2d1e0db4f929ed819150c1b030dbf3c917d15d15acb7418db2479dd7a92",
|
||||
"src/drivers/acpx/kimi-metadata-probe.ts": "sha256:0f6f2865065500d77460ec761fa13661a3149dd34422812201026709f7cb84f8",
|
||||
"src/cli/kimi-metadata-probe.ts": "sha256:306e2caca87977c3ea511ec43c937fd801f4c8cf9b4337b1ed8a3e315c1cdb37",
|
||||
"src/drivers/acpx/kimi-message-boundary.ts": "sha256:11dbdabaa07d7909540b5041c642f3f60b20b660f72150e7b39155cf6abbfdd0",
|
||||
"src/drivers/acpx/codex-acpx-driver.ts": "sha256:d11dea41590394225d622c2f4b4141ede0445fdb529fec1d6ce3fe749f354234",
|
||||
"src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab",
|
||||
"src/drivers/acpx/acp-edit-context.ts": "sha256:7be0bd911bc6bb3587e57d25ab5e75a961801ad0c58f06e713dd64cfe17a3c43",
|
||||
"src/drivers/acpx/acp-permission-adapter.ts": "sha256:444482de2abb23d9c9c39a3a93bc9b27af241db56f8329e9935bb5b603c08ae0",
|
||||
"src/drivers/acpx/safe-locations.ts": "sha256:f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9",
|
||||
"src/drivers/acpx/generated-sidecar-contract.ts": "sha256:b60e4b924e827e122860d2a4cc13b465ba748ee521620887c88f3c58c0df6081",
|
||||
"src/semantic-tools/redaction.ts": "sha256:ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f",
|
||||
"runner/crates/runner-core/src/acpx_provider_state.rs": "sha256:be0e1006763902efb6bdd3952edd58058ab9139859647d413ada4c853110a324",
|
||||
"src/drivers/acpx/candidate-admission.ts": "sha256:72a3c8b1e97f6aeedaeff4fd4a75edfbd2104000607517c190dd4c34f6d6327c",
|
||||
"src/drivers/acpx/qualified-profiles.ts": "sha256:f0c3948ba291aa4b9ae54c0aebd2bd7967739ce40d48ccbe717491500fd9105b",
|
||||
"src/drivers/acpx/profile-installation.ts": "sha256:d229b6afef695991313acdf490991136d40d638359665e0c48afb675af94f863",
|
||||
"src/drivers/acpx/driver-profile.ts": "sha256:857d75d11cb1b82d22275443dab79ef674214b35f103dff30e1ab14915d80a47",
|
||||
"src/live/runnerd-codex-transport.ts": "sha256:1554851fd4b32561e8575b8ad72a28528172dfc0372a014d11f02668991d5e49",
|
||||
"runner/crates/runner-core/src/acpx_candidate_admission.rs": "sha256:65e14dd8cd7304c92b63972801dad535d52b5a6a4ea499f6c315dbc82ce258d6",
|
||||
"runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:01836e46cba936845c5e612775a9fa5dfc277cdcb7f54cb97da9a334d9580fe1",
|
||||
"runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:fc8d7cce62e0c02efddece8696e8d24f6a9fe19e5288a5e67a01ffbf931a3f6e",
|
||||
"runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:37a552c1a8a59b1815df99b79552e0fda711cfa934055073ffd682cd17d7fb54",
|
||||
"runner/crates/runner-core/src/native_provider_backend.rs": "sha256:d2aa6a65be9195e5db0fbe83103be7f6897dedb8195872191d3eb0392cb4a1cc",
|
||||
"runner/crates/runner-core/src/durable/runner.rs": "sha256:7335562b0393c011fe60d7820afeea965345478dd72757dbf91f025d44d3c36c",
|
||||
"runner/crates/runner-core/src/durable/mod.rs": "sha256:b5989da5304dfdfe04d0060a0a249e853c784af1cdd933bcf0d5a8fd07f0e895",
|
||||
"src/contracts/runtime-context.ts": "sha256:152d7187ce1dc91a15377d368776555c9897d6e018b65f0f59a92dcdbd6497d9",
|
||||
"src/contracts/generated-runtime-context-contract.ts": "sha256:069f4bc3c4dc95888f30d3fea331adb0ee0a164d09f4f70ec9c132f24f69c7dd",
|
||||
"src/contracts/runtime-context-prompts.ts": "sha256:fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1",
|
||||
"src/provider-events.ts": "sha256:a6b77d969fd2f767dd8e583e81b907973c429b6e32d2c2d7fef0856f3568f07a",
|
||||
"src/native-session-runtime.ts": "sha256:cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea",
|
||||
"src/control-plane/durable-prp-control-plane.ts": "sha256:a8a8bed373cdd18397a762b9f67b04c789d9e5d63208f06c5ebe000faff090ac",
|
||||
"src/drivers/codex/codex-harness-session.ts": "sha256:bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851",
|
||||
"src/drivers/codex/codex-session-notifications.ts": "sha256:23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa",
|
||||
"src/drivers/codex/codex-session-state.ts": "sha256:c2f1059c5f1532cf3c2059792324a6f55d0c06ec8a62c3a4ab2c9451d617988a",
|
||||
"runner/crates/runner-core/src/provider_backend.rs": "sha256:03a57d83901f0fbccfb7bcdaeae79baf8918f658ceb3463406211431944a461e",
|
||||
"runner/crates/runner-core/src/provider_bridge.rs": "sha256:d417840ecd87e1a8a407629074f6c64bad4071c5e9add4b5a2aef0b3403b5529",
|
||||
"runner/crates/runner-core/src/provider_events.rs": "sha256:91dfa4985691f86ab7f60514e46d924fb815900d6d456d78780bd94d29bff461",
|
||||
"runner/crates/runner-core/src/acpx_event_scope.rs": "sha256:088f4b5c67b5663cbce2ef0b8b8cb3e6fe04d2b5c9cfa7a8139f8f0bf91e476e",
|
||||
"runner/crates/runner-core/src/acpx_event_payload.rs": "sha256:f3b2f6ea9c2e9cf673790eef2f7d4e738c36a2e6347e1fd61ae501793cef1f15",
|
||||
"scripts/generate-acpx-profiles.mjs": "sha256:e878a3f8afa9ce38940e61343cfbdc55d6431e9574108ca5fb17f0c72ead7423"
|
||||
}
|
||||
},
|
||||
"commandDigest": "sha256:e027eb4ec2f99fc485b76a3e17212810d0cefa873c55365b44b4e5ba815439bf"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"declaration": {
|
||||
"schema": "paperclip.kimi-profile-identity.v1",
|
||||
"agent": "kimi",
|
||||
"agentProfileVersion": 2,
|
||||
"agentServerPackage": "@moonshot-ai/kimi-code",
|
||||
"agentServerVersion": "2.3.3",
|
||||
"agentRuntimePackage": null,
|
||||
"agentRuntimeVersion": null,
|
||||
"requiresProviderPolicy": true,
|
||||
"qualificationStatus": "pending",
|
||||
"acpxVersion": "0.13.1",
|
||||
"sources": {
|
||||
"scripts/patch-kimi-runtime.mjs": "sha256:7bea24c45b7f32a72168bf6da57cb6fd472cd2ef9fac0c196b7f050e81a8b62a",
|
||||
"scripts/kimi-instructions-runtime.mjs": "sha256:7697cd40f1123ce607ec61548dde1c31dff496f3fd7aed1afbe75bbe434e7859",
|
||||
"scripts/materialize-kimi-distribution.mjs": "sha256:7d61f91d2b32a8d22e5949619faed009c2fded8e034e84a2615a76548e2aa4d0",
|
||||
"scripts/provision-kimi.mjs": "sha256:d11a6320b47c1549572f192e6a3d532b4c48a7f2499afd91539626c04e917bca",
|
||||
"scripts/kimi-distribution/package.json": "sha256:13511f26a31248e42063c49eeaf6235b7448df12049c1bf45c2170cc20767175",
|
||||
"scripts/kimi-distribution/package-lock.json": "sha256:ff2cf7356dc031cac15d9f3339cf82bd1811399af59396e2d7629648c21298cf",
|
||||
"src/drivers/acpx/kimi-closure-pins.ts": "sha256:b40e709dbd3fdc04bd1bc1f2ec5a34a2eb09b353a11d3b1b86c35bbe3dcbd513",
|
||||
"src/drivers/acpx/kimi-config.ts": "sha256:b927c3f2437094d4492a1499559247220e154fcae719ecfe75bb63defc7df05e",
|
||||
"src/drivers/acpx/kimi-installation.ts": "sha256:753a1f8505bcfaff83ac76c425e45e4ff780861a6b6b0fbcb71e96c0c482669d",
|
||||
"src/drivers/acpx/kimi-instructions.ts": "sha256:a04e2d17aac37da2463c8a2c057f9e922ecdb68d9e922e9288e0445c870f9a36",
|
||||
"src/drivers/acpx/instruction-admission.ts": "sha256:371c17f328abed44f90ac37f3b686e1c0a0d6b647518e62935262ecc31450b16"
|
||||
}
|
||||
},
|
||||
"commandDigest": "sha256:103f3a3d84353fa6d3244fe51065bd62159dc32adb88a69d16dbde495c012360"
|
||||
}
|
||||
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user