import { basename, dirname, resolve } from 'node:path';
import type {
ChildStepPolicy,
ExtractedChildPolicy,
} from '../../domain/index.ts';
import { DEFAULT_CHILD_POLICY_ENVIRONMENT } from './child-policy-paths.ts';
import type { ChildPolicyEnvironment } from './child-policy-paths.ts';
import { parseChildPolicy } from './child-policy-validation.ts';
const CHILD_POLICY_OPEN = '';
const CHILD_POLICY_CLOSE = '';
const UPSTREAM_TASK_PREFIX = 'Task: ';
const UPSTREAM_TASK_FILE_OPEN = '\n';
const UPSTREAM_TASK_FILE_CLOSE = '\n\n';
const UPSTREAM_TASK_DIRECTORY_PREFIX = 'pi-agent-';
/**
* Encodes a validated child policy into the delegated task envelope.
*/
export const encodeChildPolicy = (policy: ChildStepPolicy): string => {
const encoded = Buffer.from(JSON.stringify(policy), 'utf8').toString(
'base64url',
);
return `${CHILD_POLICY_OPEN}${encoded}${CHILD_POLICY_CLOSE}`;
};
type UnwrapTaskFileOptions = {
readonly text: string;
readonly environment: ChildPolicyEnvironment;
};
const unwrapTaskFile = ({
text,
environment,
}: UnwrapTaskFileOptions): string | undefined => {
if (
!text.startsWith(UPSTREAM_TASK_FILE_OPEN) ||
!text.endsWith(UPSTREAM_TASK_FILE_CLOSE)
) {
return undefined;
}
const pathStart = UPSTREAM_TASK_FILE_OPEN.length;
const headerEnd = text.indexOf(UPSTREAM_TASK_FILE_HEADER_CLOSE, pathStart);
if (headerEnd === -1) return undefined;
const taskFilePath = text.slice(pathStart, headerEnd);
const taskDirectory = dirname(resolve(taskFilePath));
const isExpectedTaskFile =
basename(taskFilePath) === 'task.md' &&
basename(taskDirectory).startsWith(UPSTREAM_TASK_DIRECTORY_PREFIX) &&
dirname(taskDirectory) === resolve(environment.temporaryDirectory());
if (!isExpectedTaskFile) return undefined;
const bodyStart = headerEnd + UPSTREAM_TASK_FILE_HEADER_CLOSE.length;
const body = text.slice(bodyStart, -UPSTREAM_TASK_FILE_CLOSE.length);
if (!body.startsWith(`${UPSTREAM_TASK_PREFIX}${CHILD_POLICY_OPEN}`)) {
return undefined;
}
return body.slice(UPSTREAM_TASK_PREFIX.length);
};
type UnwrapUpstreamTaskOptions = {
readonly text: string;
readonly environment: ChildPolicyEnvironment;
};
const unwrapUpstreamTask = ({
text,
environment,
}: UnwrapUpstreamTaskOptions): string | undefined => {
if (text.startsWith(CHILD_POLICY_OPEN)) return text;
if (text.startsWith(`${UPSTREAM_TASK_PREFIX}${CHILD_POLICY_OPEN}`)) {
return text.slice(UPSTREAM_TASK_PREFIX.length);
}
return unwrapTaskFile({ text, environment });
};
const decodePolicy = (encoded: string): unknown => {
try {
return JSON.parse(Buffer.from(encoded, 'base64url').toString('utf8'));
} catch {
throw new Error('delegated task child policy cannot be decoded');
}
};
/**
* Extracts and validates a child policy from a supported delegated task shape.
*
* @returns The validated policy and task, or `undefined` for ordinary input.
* @throws When a policy envelope is present but malformed.
*/
export const extractChildPolicy = (
text: string,
environment: ChildPolicyEnvironment = DEFAULT_CHILD_POLICY_ENVIRONMENT,
): ExtractedChildPolicy | undefined => {
const taskWithPolicy = unwrapUpstreamTask({ text, environment });
if (taskWithPolicy === undefined) return undefined;
const payloadStart = CHILD_POLICY_OPEN.length;
const payloadEnd = taskWithPolicy.indexOf(CHILD_POLICY_CLOSE, payloadStart);
const hasNestedEnvelope =
taskWithPolicy.indexOf(CHILD_POLICY_OPEN, payloadStart) !== -1;
if (payloadEnd === -1 || hasNestedEnvelope) {
throw new Error('delegated task contains an invalid child policy envelope');
}
const encoded = taskWithPolicy.slice(payloadStart, payloadEnd);
const task = taskWithPolicy
.slice(payloadEnd + CHILD_POLICY_CLOSE.length)
.trim();
if (!task) throw new Error('delegated task is empty after policy extraction');
return {
policy: parseChildPolicy(decodePolicy(encoded), environment),
task,
};
};