mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-01 23:43:16 +01:00
Integrate current master before rich ACP foundation review
Co-Authored-By: Paperclip <noreply@paperclip.ing> * commit '53aad90b9': fix: retry sandbox ACP input delivery after gateway failures (#14485) fix: retain project defaults in partial workspace overrides (#14502) test: control Telegram subscription retry timing (#14501) fix: retain diagnostic reasons for native runner failures (#14481) fix: prevent run identity locks from blocking audit checks (#14478) fix: preserve company context across browser hot reload (#14482) feat(ui): improve task composer controls and pending input (#14322) Improve task artifacts with rich cards and editable stories (#14469)
This commit is contained in:
commit
9774086a73
111 files changed
+7338
-1164
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import {
|
||||
access,
|
||||
cp,
|
||||
@@ -57,6 +58,11 @@ import { buildNativeHeartbeatPreparationSpans } from "./native-run-trace.js";
|
||||
import { NativeRunnerOwnershipUnverifiedError } from "./native-runner-ownership.js";
|
||||
import type { AdapterRuntimeEvent } from "../../adapters/index.js";
|
||||
|
||||
vi.mock("node:fs", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:fs")>();
|
||||
return { ...actual, readFileSync: vi.fn(actual.readFileSync) };
|
||||
});
|
||||
|
||||
const githubAccess = vi.hoisted(() => ({
|
||||
activate: vi.fn((_binding: { runId: string }) => vi.fn()),
|
||||
stop: vi.fn(async () => undefined),
|
||||
@@ -1201,6 +1207,54 @@ describe("remote provider pack manifest", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("provider pack read diagnostics", () => {
|
||||
it.each([
|
||||
["EACCES", "permission_denied"],
|
||||
["EPERM", "permission_denied"],
|
||||
["EIO", "io_error"],
|
||||
])("reports %s without exposing the underlying filesystem message", (code, reason) => {
|
||||
const root = join(tmpdir(), "private-provider-pack");
|
||||
const manifestPath = join(root, "provider-pack.json");
|
||||
const cause = Object.assign(new Error(`${code}: cannot read ${manifestPath}`), {
|
||||
code,
|
||||
path: manifestPath,
|
||||
});
|
||||
vi.mocked(readFileSync).mockImplementationOnce(() => { throw cause; });
|
||||
let failure: Error | undefined;
|
||||
try { readRemoteProviderPackManifest(root); } catch (error) { failure = error as Error; }
|
||||
expect(readFileSync).toHaveBeenLastCalledWith(manifestPath, "utf8");
|
||||
expect(failure?.message).toBe(`runner_remote_provider_artifact_incompatible: provider-pack.json is unreadable (${reason})`);
|
||||
expect(failure?.message).not.toContain(root);
|
||||
expect(failure?.message).not.toContain(code);
|
||||
expect(failure?.cause).toBe(cause);
|
||||
});
|
||||
|
||||
it("classifies a JSON null manifest as incompatible instead of a TypeError", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "paperclip-null-pack-"));
|
||||
try {
|
||||
await writeFile(join(root, "provider-pack.json"), "null");
|
||||
expect(() => readRemoteProviderPackManifest(root)).toThrow(
|
||||
"runner_remote_provider_artifact_incompatible: provider pack pins or source revision do not match",
|
||||
);
|
||||
} finally { await rm(root, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
it.each(["missing", "invalid_json", "invalid_path_type"])("reports %s without putting the path in the terminal message", async (reason) => {
|
||||
const root = await mkdtemp(join(tmpdir(), "paperclip-private-pack-"));
|
||||
try {
|
||||
const manifestPath = join(root, "provider-pack.json");
|
||||
if (reason === "invalid_json") await writeFile(manifestPath, "{ private-invalid-json");
|
||||
if (reason === "invalid_path_type") await mkdir(manifestPath);
|
||||
let failure: Error | undefined;
|
||||
try { readRemoteProviderPackManifest(root); } catch (error) { failure = error as Error; }
|
||||
expect(failure?.message).toBe(`runner_remote_provider_artifact_incompatible: provider-pack.json is unreadable (${reason})`);
|
||||
expect(failure?.message).not.toContain(root);
|
||||
expect(failure?.message).not.toContain("private-invalid-json");
|
||||
expect(failure?.cause).toBeDefined();
|
||||
} finally { await rm(root, { recursive: true, force: true }); }
|
||||
});
|
||||
});
|
||||
|
||||
describe("native harness persistence profiles", () => {
|
||||
const profile = (provider: Record<string, unknown>, driverKind: string) =>
|
||||
resolveNativeHarnessPersistenceProfile({
|
||||
@@ -1507,7 +1561,7 @@ describe("verified native harness backups", () => {
|
||||
},
|
||||
sourceProviderLeaseId: "sandbox-1",
|
||||
}),
|
||||
).toThrow("runner_harness_state_mismatch");
|
||||
).toThrow("runner_harness_state_mismatch: backup_provider_identity_missing");
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
@@ -9166,7 +9220,7 @@ describe("runnerd provider runtime wiring", () => {
|
||||
execution: currentExecution,
|
||||
runnerInstanceId: "runner-after-running-prior-scope",
|
||||
}),
|
||||
).rejects.toThrow("runner_state_identity_mismatch");
|
||||
).rejects.toThrow("runner_state_identity_mismatch: prior_owner_active");
|
||||
await expect(access(scopedRoot)).resolves.toBeUndefined();
|
||||
await expect(access(join(stateBase, "quarantine"))).rejects.toThrow();
|
||||
expect(state.createBackend).not.toHaveBeenCalled();
|
||||
@@ -10738,7 +10792,7 @@ describe("runnerd provider runtime wiring", () => {
|
||||
}
|
||||
// Ambiguous ordinary recovery must still fail closed. Only the runtime's
|
||||
// explicitly admitted replacement may retire these prior-session backups.
|
||||
await expect(prepareReplacement()).rejects.toThrow("runner_harness_state_mismatch");
|
||||
await expect(prepareReplacement()).rejects.toThrow("runner_harness_state_mismatch: backup_without_reusable_lease");
|
||||
await expect(backend.openReplacementSession!({
|
||||
identity: { runId: execution.binding.runId }, workingDirectory: execution.workspace.cwd,
|
||||
} as never, {} as never)).resolves.toBe(replacement);
|
||||
|
||||
Reference in new issue
Block a user