$request Runtime request to validate. * @return array Validation errors; an empty array means valid. */ function validateRuntimeRequest(array $request): array { $errors = []; $required = ['contractVersion', 'requestId', 'correlationId', 'idempotencyKey', 'timestampUtc', 'actor', 'riskLevel', 'permissions']; foreach ($required as $field) { if (!array_key_exists($field, $request)) { $errors[] = "Missing required field: {$field}."; } } if (($request['contractVersion'] ?? null) !== 'aruntime.runtime-request.v1') { $errors[] = 'Unsupported contractVersion.'; } if (isset($request['timestampUtc']) && !preg_match('/Z$/', (string) $request['timestampUtc'])) { $errors[] = 'timestampUtc must be a UTC ISO 8601 value ending in Z.'; } if (isset($request['riskLevel']) && !in_array($request['riskLevel'], ['low', 'medium', 'high', 'critical'], true)) { $errors[] = 'riskLevel is invalid.'; } return $errors; } /** * Selects an allowed model route without silently weakening request constraints. * * @param array $request Runtime request. * @param array> $routes Available route catalog keyed by route ID. * @return string Selected route ID. * @throws RuntimeException When no route satisfies the contract. */ function selectModelRoute(array $request, array $routes): string { $constraints = (array) ($request['modelRouteConstraints'] ?? []); foreach ((array) ($constraints['allowedRoutes'] ?? []) as $routeId) { $route = $routes[(string) $routeId] ?? null; if (!is_array($route)) { continue; } if (!($constraints['allowHosted'] ?? false) && ($route['hosted'] ?? false)) { continue; } if (($route['estimatedLatencyMs'] ?? PHP_INT_MAX) > ($constraints['maxLatencyMs'] ?? 0)) { continue; } return (string) $routeId; } throw new RuntimeException('No model route satisfies the request contract.'); } /** * Checks tool permission and approval requirements before invocation. * * @param array $request Runtime request. * @param array $tool Tool contract. * @return array{decision:string,reason:string} Authorization decision. */ function authorizeTool(array $request, array $tool): array { $toolId = (string) ($tool['toolId'] ?? ''); if (!in_array($toolId, (array) ($request['allowedTools'] ?? []), true)) { return ['decision' => 'deny', 'reason' => 'tool-not-allowlisted']; } $permissionClass = (string) ($tool['permissionClass'] ?? ''); $required = (array) ($request['toolPolicy']['requiredPermissionClasses'] ?? []); if (!in_array($permissionClass, $required, true)) { return ['decision' => 'deny', 'reason' => 'permission-class-not-granted']; } if (in_array($permissionClass, ['financial-high-impact', 'irreversible', 'administrative'], true)) { return ['decision' => 'approval-required', 'reason' => 'high-impact-tool']; } return ['decision' => 'allow', 'reason' => 'contract-and-policy-allow']; } /** * Executes a synthetic idempotent tool call and reuses a completed result. * * @param string $idempotencyKey Stable operation key. * @param callable():array $operation Operation callback. * @param array> $store In-memory idempotency store. * @return array Existing or newly completed result. */ function executeIdempotently(string $idempotencyKey, callable $operation, array &$store): array { if (isset($store[$idempotencyKey])) { return $store[$idempotencyKey]; } $result = $operation(); $store[$idempotencyKey] = $result; return $result; } /** * Creates a minimized evidence record without persisting raw prompt text. * * @param array $request Runtime request. * @param string $route Selected route. * @param array $result Runtime result. * @return array Evidence record. */ function createEvidenceRecord(array $request, string $route, array $result): array { return [ 'schemaVersion' => 'aruntime.evidence-record.v1', 'evidenceId' => 'ev:' . hash('sha256', (string) $request['requestId']), 'requestId' => $request['requestId'], 'correlationId' => $request['correlationId'], 'recordedUtc' => gmdate('Y-m-d\TH:i:s\Z'), 'actorRef' => $request['actor']['idRef'], 'modelRoute' => ['routeRef' => $route], 'finalOutputReference' => $result['artifactRef'] ?? null, 'redaction' => ['applied' => true, 'omittedFields' => ['input']], ]; } try { $request = json_decode((string) file_get_contents(__DIR__ . '/../json/runtime-request.minimal.json'), true, 512, JSON_THROW_ON_ERROR); $errors = validateRuntimeRequest($request); if ($errors !== []) { throw new InvalidArgumentException(implode(' ', $errors)); } $route = selectModelRoute($request, [ 'hosted-general-v3' => ['hosted' => true, 'estimatedLatencyMs' => 2400], ]); $store = []; $result = executeIdempotently((string) $request['idempotencyKey'], static fn (): array => [ 'artifactRef' => 'artifact:answer:001', 'status' => 'completed', ], $store); echo json_encode([ 'route' => $route, 'result' => $result, 'evidence' => createEvidenceRecord($request, $route, $result), ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . PHP_EOL; } catch (Throwable $error) { fwrite(STDERR, 'Runtime pipeline failed: ' . $error->getMessage() . PHP_EOL); exit(1); }