1+ import { rmSync } from "node:fs"
12import { appendFile , mkdir , readFile , rm , writeFile } from "node:fs/promises"
23import { join , resolve } from "node:path"
34import { spawn } from "node:child_process"
4- import { materializeExternalNativePackage , normalizeExternalPackageSource , parseExternalPackageSourcePolicy } from "./materialize-external-native-package.mjs"
5+ import { materializeExternalNativePackage , materializeRuntimeSources , normalizeExternalPackageSource , normalizeRuntimeSources , parseExternalPackageSourcePolicy } from "./materialize-external-native-package.mjs"
56import { readNativeResult } from "./native-result-file.mjs"
67
78const requestPath = process . env . AGENT_TASK_REQUEST_PATH || ".codebox/agent-task-request.json"
@@ -12,6 +13,7 @@ const outputPath = process.env.GITHUB_OUTPUT
1213const MAX_CAPTURE_BYTES = 32768
1314const MAX_OUTPUT_CHARS = 8192
1415const secretValues = [ "OPENAI_API_KEY" , "MODEL_PROVIDER_SECRET_1" , "MODEL_PROVIDER_SECRET_2" , "MODEL_PROVIDER_SECRET_3" , "MODEL_PROVIDER_SECRET_4" , "MODEL_PROVIDER_SECRET_5" , "GITHUB_TOKEN" , "GH_TOKEN" , "ACCESS_TOKEN" , "EXTERNAL_PACKAGE_SOURCE_POLICY" ] . map ( ( name ) => process . env [ name ] ) . filter ( Boolean )
16+ const PRIVATE_RUNTIME_PATH_FIELDS = new Set ( [ "source" , "path" , "sourceRoot" , "originalSource" , "preparedPath" , "requestedPath" , "source_package_root" , "artifacts_path" , "runtime_input_path" , "task_path" , "result_path" , "event_stream_path" , "materialization_result_path" ] )
1517function redact ( value ) {
1618 if ( typeof value === "string" ) return secretValues . reduce ( ( output , secret ) => output . split ( secret ) . join ( "[REDACTED]" ) , value )
1719 if ( Array . isArray ( value ) ) return value . map ( redact )
@@ -94,6 +96,27 @@ function record(value) {
9496 return value && typeof value === "object" && ! Array . isArray ( value ) ? value : { }
9597}
9698
99+ function isPrivateRuntimePath ( value ) {
100+ if ( ! privateRuntimeSourceRoot || typeof value !== "string" ) return false
101+ const path = resolve ( value )
102+ return path === privateRuntimeSourceRoot || path . startsWith ( `${ privateRuntimeSourceRoot } /` )
103+ }
104+
105+ function omitPrivateRuntimeSourcePaths ( value ) {
106+ if ( Array . isArray ( value ) ) return value . map ( omitPrivateRuntimeSourcePaths )
107+ if ( ! value || typeof value !== "object" ) return value
108+ return Object . fromEntries ( Object . entries ( value ) . flatMap ( ( [ key , entry ] ) => {
109+ if ( PRIVATE_RUNTIME_PATH_FIELDS . has ( key ) && isPrivateRuntimePath ( entry ) ) return [ ]
110+ return [ [ key , omitPrivateRuntimeSourcePaths ( entry ) ] ]
111+ } ) )
112+ }
113+
114+ function assertNoPrivateRuntimePaths ( value ) {
115+ if ( privateRuntimeSourceRoot && JSON . stringify ( value ) . includes ( privateRuntimeSourceRoot ) ) {
116+ throw new Error ( "Runtime source paths must never be persisted in workflow results or artifacts." )
117+ }
118+ }
119+
97120function string ( value ) {
98121 return typeof value === "string" ? value . trim ( ) : ""
99122}
@@ -189,11 +212,25 @@ const driftChecks = commandEntries(request.drift_checks, "drift_checks")
189212const runId = `${ request . workload ?. id || "agent-task" } -${ process . env . GITHUB_RUN_ID || "local" } ` . replace ( / [ ^ A - Z a - z 0 - 9 . _ - ] + / g, "-" )
190213const externalPackagePolicy = parseExternalPackageSourcePolicy ( string ( process . env . EXTERNAL_PACKAGE_SOURCE_POLICY ) )
191214const externalPackageSource = normalizeExternalPackageSource ( request . external_package_source , externalPackagePolicy )
215+ const runtimeSources = normalizeRuntimeSources ( request . runtime_sources ?? [ ] , externalPackagePolicy )
192216const artifactsPath = join ( workspace , ".codebox" , "agent-task-artifacts" )
193217const runtimeInputPath = join ( workspace , ".codebox" , "native-agent-task-input.json" )
194218const resultPath = join ( workspace , ".codebox" , "agent-task-workflow-result.json" )
195219const controlledCodeboxPath = resolve ( requestPath , ".." )
196220const nativeResultPath = join ( controlledCodeboxPath , "native-agent-task-result.json" )
221+ let privateRuntimeSourceRoot = ""
222+ let cleaningPrivateRuntimeSources = false
223+ async function cleanupPrivateRuntimeSources ( ) {
224+ if ( cleaningPrivateRuntimeSources || ! privateRuntimeSourceRoot ) return
225+ cleaningPrivateRuntimeSources = true
226+ const root = privateRuntimeSourceRoot
227+ privateRuntimeSourceRoot = ""
228+ await rm ( root , { recursive : true , force : true } )
229+ }
230+ process . once ( "exit" , ( ) => { if ( privateRuntimeSourceRoot ) rmSync ( privateRuntimeSourceRoot , { recursive : true , force : true } ) } )
231+ for ( const signal of [ "SIGINT" , "SIGTERM" , "SIGHUP" ] ) {
232+ process . once ( signal , ( ) => { cleanupPrivateRuntimeSources ( ) . finally ( ( ) => process . exit ( 128 ) ) } )
233+ }
197234const runnerWorkspaceTools = [
198235 "workspace-read" , "workspace-ls" , "workspace-grep" , "workspace-write" , "workspace-edit" , "workspace-apply-patch" ,
199236 "workspace-git-status" , "workspace-git-diff" , "workspace-git-add" , "workspace-git-commit" , "workspace-git-push" ,
@@ -220,11 +257,22 @@ if (accessError) {
220257const materializedPackage = request . run_agent && ! request . dry_run
221258 ? await materializeExternalNativePackage ( externalPackageSource , { policy : externalPackagePolicy } )
222259 : undefined
260+ const materializedRuntimeSources = request . run_agent && ! request . dry_run
261+ ? await materializeRuntimeSources ( runtimeSources , { policy : externalPackagePolicy , forbiddenRoots : [ workspace , artifactsPath ] } )
262+ : undefined
263+ privateRuntimeSourceRoot = materializedRuntimeSources ?. root ?? ""
264+ const runtimeSourceInputs = ( materializedRuntimeSources ?. lowered ?? [ ] ) . reduce ( ( input , lowered ) => {
265+ for ( const [ key , entries ] of Object . entries ( lowered ) ) input [ key ] = [ ...( input [ key ] ?? [ ] ) , ...entries ]
266+ return input
267+ } , { } )
268+ const sourcePackageRoot = privateRuntimeSourceRoot ? join ( privateRuntimeSourceRoot , "prepared-packages" ) : artifactsPath
269+ const executionInputPath = privateRuntimeSourceRoot ? join ( privateRuntimeSourceRoot , "native-agent-task-input.json" ) : runtimeInputPath
223270
224271const taskInput = {
225272 schema : "wp-codebox/agent-task-run-request/v1" ,
226273 task_id : runId ,
227- artifacts_path : artifactsPath ,
274+ artifacts_path : artifactsPath ,
275+ source_package_root : sourcePackageRoot ,
228276 callback_data : record ( request . callback_data ) ,
229277 task_input : {
230278 schema : "wp-codebox/task-input/v1" ,
@@ -235,6 +283,7 @@ const taskInput = {
235283 provider : request . model ?. provider ,
236284 model : request . model ?. name ,
237285 secret_env : [ "OPENAI_API_KEY" , "MODEL_PROVIDER_SECRET_1" , "MODEL_PROVIDER_SECRET_2" , "MODEL_PROVIDER_SECRET_3" , "MODEL_PROVIDER_SECRET_4" , "MODEL_PROVIDER_SECRET_5" ] . filter ( ( name ) => process . env [ name ] ) ,
286+ ...runtimeSourceInputs ,
238287 allowed_tools : runnerWorkspaceTools ,
239288 sandbox_tool_policy : {
240289 schema : "wp-codebox/sandbox-tool-policy/v1" ,
@@ -265,26 +314,30 @@ const taskInput = {
265314 artifact_declarations : request . artifacts ?. declarations || [ ] ,
266315 required_artifacts : request . artifacts ?. expected || [ ] ,
267316 output_projections : [ ] ,
268- metadata : { workload : request . workload , ...( materializedPackage ? { imported_agent : materializedPackage . identity } : { } ) } ,
317+ metadata : { workload : request . workload , ...( materializedPackage ? { imported_agent : materializedPackage . identity } : { } ) , runtime_sources : materializedRuntimeSources ?. descriptors ?? [ ] } ,
269318 } ,
270319 } ,
271320 } ,
272321}
273322
274- await writeFile ( runtimeInputPath , `${ JSON . stringify ( taskInput , null , 2 ) } \n` )
323+ await writeFile ( executionInputPath , `${ JSON . stringify ( taskInput , null , 2 ) } \n` )
275324
276325let execution = { code : 0 , stdout : "" , stderr : "" , stdout_truncated : false , stderr_truncated : false }
277326if ( request . run_agent && ! request . dry_run ) {
278- execution = await command ( "node" , [ codeboxCliPath , "agent-task-run" , "--input-file" , runtimeInputPath , "--result-file" , nativeResultPath ] , workspace , agentEnvironment ( ) )
327+ execution = await command ( "node" , [ codeboxCliPath , "agent-task-run" , "--input-file" , executionInputPath , "--result-file" , nativeResultPath ] , workspace , agentEnvironment ( ) )
279328}
280329
281330// Public package bytes are embedded in the runtime recipe and consumed only by
282331// the Playground bootstrap before the agent's tools are resolved.
283332
284- const runtimeResult = request . run_agent && ! request . dry_run
333+ const nativeRuntimeResult = request . run_agent && ! request . dry_run
285334 ? await readNativeResult ( nativeResultPath , controlledCodeboxPath , secretValues , redact )
286335 : { }
287336await rm ( nativeResultPath , { force : true } )
337+ assertNoPrivateRuntimePaths ( nativeRuntimeResult )
338+ const runtimeResult = omitPrivateRuntimeSourcePaths ( nativeRuntimeResult )
339+ assertNoPrivateRuntimePaths ( runtimeResult )
340+ await cleanupPrivateRuntimeSources ( )
288341
289342await redactArtifactFiles ( artifactsPath )
290343
0 commit comments