mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-28 20:21:50 +01:00
Read native plugin task output files on the execution host (#17285)
This commit is contained in:
1 parent
d69631b01b
commit
412914cfa7
15 files changed
+202
-24
No files matched your search
@@ -1,6 +1,10 @@
|
||||
import { z } from "zod";
|
||||
import type { PluginEnvironmentDriverBaseParams, PluginEnvironmentLease } from "./protocol.js";
|
||||
|
||||
export const MAX_ENVIRONMENT_TASK_FILE_BYTES = 10 * 1024 * 1024;
|
||||
const taskFilePath = z.string().min(1).max(2000).refine(value =>
|
||||
!/[\\\u0000-\u001f\u007f]/u.test(value) && !/^[a-z][a-z0-9+.-]*:/iu.test(value) &&
|
||||
value.replace(/^\//u, "").split("/").every(part => part !== "" && part !== "." && part !== ".."), "Expected a canonical task file path");
|
||||
// Provider IDs are opaque; providers validate their own addressing constraints.
|
||||
const providerId = z.string().min(1);
|
||||
// PRP identity.schema.json stableId.
|
||||
@@ -85,6 +89,10 @@ export const environmentTaskOperationSchema = z.discriminatedUnion("kind", [
|
||||
z.object({ kind: z.literal("check_project_auth"), configurationId: providerId.max(1024),
|
||||
provider: identifier, projectIds: z.array(z.string().uuid()).max(64)
|
||||
.refine(ids => new Set(ids).size === ids.length, "Duplicate project IDs") }).strict(),
|
||||
z.object({ kind: z.literal("read_file"), identity: durableIdentity,
|
||||
path: taskFilePath, maxBytes: z.number().int().positive().max(MAX_ENVIRONMENT_TASK_FILE_BYTES),
|
||||
projectIds: z.array(z.string().uuid()).max(64).refine(ids => new Set(ids).size === ids.length, "Duplicate project IDs"),
|
||||
}).strict(),
|
||||
z.object({ kind: z.literal("read_state"), identity: durableIdentity, resumeFrom: providerId.optional() }).strict(),
|
||||
z.object({ kind: z.literal("archive_state"), priorIdentity: durableIdentity,
|
||||
archiveKey: z.string().regex(/^[a-f0-9]{24}$/), resumeFrom: providerId }).strict(),
|
||||
@@ -101,6 +109,11 @@ export const environmentTaskResultSchema = z.discriminatedUnion("kind", [
|
||||
executionStopped: z.literal(true) }).strict(),
|
||||
z.object({ kind: z.literal("project_auth"), taskId: providerId, configurationId: providerId.max(1024),
|
||||
provider: identifier, allowed: z.boolean() }).strict(),
|
||||
z.object({ kind: z.literal("file"), taskId: providerId,
|
||||
base64: z.string().max(4 * Math.ceil(MAX_ENVIRONMENT_TASK_FILE_BYTES / 3)),
|
||||
byteSize: z.number().int().nonnegative().max(MAX_ENVIRONMENT_TASK_FILE_BYTES),
|
||||
sha256: z.string().regex(/^[a-f0-9]{64}$/),
|
||||
}).strict(),
|
||||
z.object({ kind: z.literal("state"), taskId: providerId, state: z.record(z.string(), z.unknown()) }).strict(),
|
||||
z.object({ kind: z.literal("prepared"), taskId: providerId, launch: preparedLaunch }).strict(),
|
||||
z.object({ kind: z.literal("credentials"), taskId: providerId, /** Null proves that cancellation fenced admission before the task could start. It must not mean an unavailable export. */
|
||||
@@ -110,6 +123,7 @@ export const environmentTaskResultSchema = z.discriminatedUnion("kind", [
|
||||
launchPreparationVersion: z.literal(1).optional(),
|
||||
freshWorkspaceVersion: z.literal(1).optional(),
|
||||
projectAuthCheckVersion: z.literal(1).optional(),
|
||||
workspaceFileReadVersion: z.literal(1).optional(),
|
||||
}).strict(),
|
||||
z.object({
|
||||
kind: z.literal("status"), taskId: providerId,
|
||||
@@ -158,7 +172,7 @@ export interface PluginEnvironmentTaskParams extends Omit<PluginEnvironmentDrive
|
||||
/** An accepted stop is not proof of process termination. */
|
||||
export function parseEnvironmentTaskResult(operation: PluginEnvironmentTaskOperation, taskId: string, value: unknown): PluginEnvironmentTaskResult {
|
||||
const result = environmentTaskResultSchema.parse(value);
|
||||
const expected = operation.kind === "describe" ? "description" : operation.kind === "prepare" ? "prepared" : operation.kind === "check_project_auth" ? "project_auth" : ["read_state", "archive_state"].includes(operation.kind) ? "state" : ["status", "connection", "credentials", "copilot_metadata"].includes(operation.kind) ? operation.kind : "accepted";
|
||||
const expected = operation.kind === "read_file" ? "file" : operation.kind === "describe" ? "description" : operation.kind === "prepare" ? "prepared" : operation.kind === "check_project_auth" ? "project_auth" : ["read_state", "archive_state"].includes(operation.kind) ? "state" : ["status", "connection", "credentials", "copilot_metadata"].includes(operation.kind) ? operation.kind : "accepted";
|
||||
if (result.taskId !== taskId || result.kind !== expected) throw new Error("Invalid plugin-provided Runner execution response");
|
||||
return result;
|
||||
}
|
||||
@@ -463,7 +463,7 @@ export type { PluginEnvironmentSyncErrorDiagnostic } from "./environment-sync-er
|
||||
|
||||
export type { AiConnectionPool, AiConnectionPoolConfig, AiConnectionPoolMember, AiConnectionRouterRequest, AiConnectionRouterResult, AiConnectionRouterSelection } from "@paperclipai/shared";
|
||||
|
||||
export { environmentRunnerLaunchRequirementsSchema, environmentTaskOperationSchema, environmentTaskResultSchema, environmentTaskCredentialSchema, parseEnvironmentTaskResult } from "./environment-tasks.js";
|
||||
export { MAX_ENVIRONMENT_TASK_FILE_BYTES, environmentRunnerLaunchRequirementsSchema, environmentTaskOperationSchema, environmentTaskResultSchema, environmentTaskCredentialSchema, parseEnvironmentTaskResult } from "./environment-tasks.js";
|
||||
export type { PluginEnvironmentTaskOperation, PluginEnvironmentTaskParams, PluginEnvironmentTaskResult, PluginEnvironmentTaskCredential } from "./environment-tasks.js";
|
||||
export type { AgentLifecycleRequest, AgentLifecycleResult } from "@paperclipai/shared";
|
||||
|
||||
|
||||
Reference in new issue
Block a user