import { mkdir, writeFile } from "node:fs/promises";
import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
import { dirname } from "node:path";

export type JsonSchema = Record<string, unknown>;
export type AgentDslLanguage = "typescript" | "javascript";
export type AuthStrategy = "public" | "api_key" | "platform_user" | "jwt" | "custom";

export type AgentIdentity = {
  name: string;
  description: string;
  version?: string;
};

export type AgentDslAuth = {
  model: string;
  strategy: AuthStrategy;
  principal_schema: JsonSchema;
  resolver?: string | null;
  required: boolean;
};

export type SkillPolicy = {
  timeout_seconds?: number | null;
  idempotent?: boolean;
  max_retries?: number;
  cost_class?: string | null;
  allow_scope_expansion?: boolean;
  grant_mode?: string | null;
  grant_allow_patterns?: string[];
  grant_deny_patterns?: string[];
  grant_outputs_prefix?: string | null;
  grant_write_prefixes?: string[];
  grant_ttl_seconds?: number | null;
  grant_run_timeout_seconds?: number | null;
  grant_approval_timeout_seconds?: number | null;
  grant_scope_approval_timeout_seconds?: number | null;
};

export type SkillDeclaration = {
  name: string;
  description?: string;
  handler?: string;
  tags?: string[];
  scopes?: string[];
  stream?: boolean;
  policy?: SkillPolicy;
  input_schema: JsonSchema;
  output_schema: JsonSchema;
};

export type AgentRuntime = {
  lifecycle?: "ephemeral" | "session" | "warm";
  state?: "none" | "session" | "durable";
  sandbox?: "microsandbox";
  resources?: {
    cpu?: string;
    memory?: string;
    gpu?: number;
    max_runtime_seconds?: number;
  };
  concurrency?: number;
  egress?: {
    allow_hosts?: string[];
    allow_internal_services?: string[];
    deny_internet_by_default?: boolean;
  };
  tools_used?: string[];
  llm_provisioning?: "platform" | "platform_or_caller_provided" | "caller_provided" | "agent_byok";
  pricing?: {
    price_per_call_usd?: number;
    caller_pays_llm?: boolean;
    notes?: string;
  };
  wants_cp_jwt?: boolean;
};

export type AgentDslEntrypoint = {
  module?: string | null;
  class_name?: string | null;
  function?: string | null;
  command?: string[];
};

export type AgentDslSkill = Required<Pick<SkillDeclaration, "name" | "input_schema" | "output_schema">> & {
  description: string;
  handler: string;
  tags: string[];
  scopes: string[];
  stream: boolean;
  policy: Required<SkillPolicy>;
};

export type AgentDsl = {
  schema_version: "2026-06-04";
  language: AgentDslLanguage;
  name: string;
  description: string;
  version: string;
  entrypoint: AgentDslEntrypoint;
  skills: AgentDslSkill[];
  capabilities: Record<string, unknown>;
  input_modes: string[];
  output_modes: string[];
  required_secrets: string[];
  required_env: string[];
  consumer_setup: { fields: unknown[] };
  runtime: AgentRuntime;
  template_lineage?: unknown | null;
  meta_agent_manifest?: unknown | null;
  state_schema?: JsonSchema | null;
  workspace_access: Record<string, unknown>;
  config_schema?: JsonSchema | null;
  auth: AgentDslAuth;
  metadata: Record<string, unknown>;
};

export type AgentClass<T extends A2AAgent = A2AAgent> = {
  new (): T;
  agent: AgentIdentity;
  auth: AgentDslAuth;
  skills: SkillDeclaration[];
  capabilities: Record<string, unknown>;
  inputModes: string[];
  outputModes: string[];
  requiredSecrets: string[];
  requiredEnv: string[];
  consumerSetup: { fields: unknown[] };
  runtime: AgentRuntime;
  workspaceAccess: Record<string, unknown>;
  configSchema: JsonSchema | null;
  stateSchema: JsonSchema | null;
  templateLineage: unknown | null;
  metaAgentManifest: unknown | null;
};

export abstract class A2AAgent {
  static agent: AgentIdentity;
  static auth: AgentDslAuth = publicAuth();
  static skills: SkillDeclaration[] = [];
  static capabilities: Record<string, unknown> = {};
  static inputModes: string[] = ["application/json"];
  static outputModes: string[] = ["application/json"];
  static requiredSecrets: string[] = [];
  static requiredEnv: string[] = [];
  static consumerSetup: { fields: unknown[] } = { fields: [] };
  static runtime: AgentRuntime = {};
  static workspaceAccess: Record<string, unknown> = { enabled: false };
  static configSchema: JsonSchema | null = null;
  static stateSchema: JsonSchema | null = null;
  static templateLineage: unknown | null = null;
  static metaAgentManifest: unknown | null = null;

  async dispatch(request: WorkerRequest): Promise<WorkerResponse> {
    const handler = (this as Record<string, unknown>)[request.handler];
    if (typeof handler !== "function") {
      throw new Error(`agent does not implement handler method: ${request.handler}`);
    }
    const context = new RunContext(request);
    const legacyRequest = { ...request, context };
    const raw = handler.length >= 2
      ? await handler.call(this, context, request.arguments)
      : await handler.call(this, legacyRequest);
    return context.finalizeResponse(normalizeWorkerResponse(raw));
  }
}

export type WorkerRequest = {
  agent: string;
  skill: string;
  handler: string;
  arguments: Record<string, unknown>;
  task_id?: string | null;
  grant?: string | null;
  llm_creds?: Record<string, unknown> | null;
  composition?: Record<string, unknown> | null;
  consumer_config?: Record<string, unknown> | null;
  consumer_secrets?: Record<string, string> | null;
  cp_jwt?: string | null;
  cp_url?: string | null;
  auth?: Record<string, unknown> | null;
  emit?: (event: Record<string, unknown>) => void | Promise<void>;
  context?: RunContext;
  cancelled?: boolean;
};

export type WorkerResponse = {
  result: unknown;
  events?: Record<string, unknown>[];
  artifacts?: Record<string, unknown>[];
};

export type Handler = (request: WorkerRequest) => WorkerResponse | Promise<WorkerResponse>;

export type ContextHandler = (
  context: RunContext,
  input: Record<string, unknown>
) => unknown | Promise<unknown>;

export type AgentEvent = {
  kind: string;
  payload?: Record<string, unknown>;
};

export type ArtifactRef = {
  name?: string;
  path?: string;
  uri?: string;
  mime_type?: string;
  bytes?: number;
  content_type?: string;
  metadata?: Record<string, unknown>;
};

export type ArtifactData = string | Uint8Array | Buffer;

export type LLMCreds = Record<string, unknown>;

export type WorkspaceMode = "read_only" | "read_write_overlay" | "read_write_direct";

export type WorkspaceGrantPayload = {
  grant_id?: string;
  issuer?: string;
  audience?: string;
  bucket?: string;
  mode?: WorkspaceMode;
  allow_patterns?: string[];
  deny_patterns?: string[];
  outputs_prefix?: string | null;
  write_prefixes?: string[];
  expires_at?: number;
};

export type WorkspaceFileInfo = {
  path: string;
  size_bytes?: number;
  file_type?: string;
};

export type ExecResult = {
  stdout: string;
  stderr: string;
  exit_code: number;
  truncated?: boolean;
  files?: Record<string, unknown>[];
};

export type SandboxSpec = {
  name: string;
  image?: string;
  memory_mib?: number;
  cpus?: number;
  workspace?: string | null;
  secrets?: string[];
  egress?: string[];
  labels?: Record<string, string>;
};

export class WorkspaceDenied extends Error {
  constructor(message: string) {
    super(message);
    this.name = "WorkspaceDenied";
  }
}

export class SandboxUnavailable extends Error {
  constructor(message = "no sandbox runtime attached to this context") {
    super(message);
    this.name = "SandboxUnavailable";
  }
}

export class SandboxRuntimeError extends Error {
  readonly statusCode?: number;
  readonly responseBody?: string;

  constructor(message: string, options: { statusCode?: number; responseBody?: string } = {}) {
    super(message);
    this.name = "SandboxRuntimeError";
    this.statusCode = options.statusCode;
    this.responseBody = options.responseBody;
  }
}

export class HttpSandboxHandle {
  readonly baseUrl: string;
  readonly name: string;
  readonly timeoutSeconds: number;
  readonly authToken?: string;
  readonly grantToken?: string;

  constructor(options: {
    baseUrl: string;
    name: string;
    timeoutSeconds?: number;
    authToken?: string | null;
    grantToken?: string | null;
  }) {
    this.baseUrl = options.baseUrl.replace(/\/+$/, "");
    this.name = options.name;
    this.timeoutSeconds = options.timeoutSeconds ?? 1200;
    this.authToken = options.authToken ?? undefined;
    this.grantToken = options.grantToken ?? undefined;
  }

  async exec(cmd: string, args: string[] = [], options: { timeout?: number } = {}): Promise<ExecResult> {
    return this.postExec({
      cmd,
      args,
      timeout_seconds: options.timeout ?? this.timeoutSeconds
    });
  }

  async shell(script: string, options: { timeout?: number } = {}): Promise<ExecResult> {
    return this.postExec({
      script,
      timeout_seconds: options.timeout ?? this.timeoutSeconds
    });
  }

  async stop(): Promise<void> {
    await this.kill();
  }

  async kill(): Promise<void> {
    await sandboxFetch(
      `${this.baseUrl}/v1/sandboxes/${encodeURIComponent(this.name)}`,
      {
        method: "DELETE",
        headers: sandboxHeaders(this.authToken, this.grantToken),
        timeoutSeconds: 10
      }
    );
  }

  async logs(_options: { tail?: number } = {}): Promise<string> {
    return "";
  }

  private async postExec(body: Record<string, unknown>): Promise<ExecResult> {
    const timeoutSeconds = Number(body.timeout_seconds ?? this.timeoutSeconds) + 30;
    const response = await sandboxFetch(
      `${this.baseUrl}/v1/sandboxes/${encodeURIComponent(this.name)}/exec`,
      {
        method: "POST",
        headers: { ...sandboxHeaders(this.authToken, this.grantToken), "content-type": "application/json" },
        body: JSON.stringify(body),
        timeoutSeconds
      }
    );
    return normalizeExecResult(await response.json());
  }
}

export class HttpSandboxClient {
  readonly baseUrl: string;
  readonly defaultWorkspace?: string;
  readonly timeoutSeconds: number;
  readonly authToken?: string;
  readonly grantToken?: string;

  constructor(baseUrl: string, options: {
    defaultWorkspace?: string | null;
    timeoutSeconds?: number;
    authToken?: string | null;
    grantToken?: string | null;
  } = {}) {
    this.baseUrl = baseUrl.replace(/\/+$/, "");
    this.defaultWorkspace = options.defaultWorkspace ?? undefined;
    this.timeoutSeconds = options.timeoutSeconds ?? 1200;
    this.authToken = options.authToken ?? undefined;
    this.grantToken = options.grantToken ?? undefined;
  }

  async create(spec: SandboxSpec): Promise<HttpSandboxHandle> {
    const body = {
      name: spec.name,
      bucket: spec.workspace ?? this.defaultWorkspace ?? `agent-${spec.name}`,
      image: spec.image ?? "python:3.11-slim",
      memory_mib: spec.memory_mib ?? 512,
      cpus: spec.cpus ?? 1,
      labels: spec.labels ?? {}
    };
    const response = await sandboxFetch(`${this.baseUrl}/v1/sandboxes`, {
      method: "POST",
      headers: { ...sandboxHeaders(this.authToken, this.grantToken), "content-type": "application/json" },
      body: JSON.stringify(body),
      timeoutSeconds: this.timeoutSeconds + 30
    });
    const data = await response.json();
    const name = isObject(data) && typeof data.name === "string" ? data.name : spec.name;
    return this.get(name);
  }

  async get(name: string): Promise<HttpSandboxHandle> {
    return new HttpSandboxHandle({
      baseUrl: this.baseUrl,
      name,
      timeoutSeconds: this.timeoutSeconds,
      authToken: this.authToken,
      grantToken: this.grantToken
    });
  }

  async list(): Promise<string[]> {
    const response = await sandboxFetch(`${this.baseUrl}/v1/sandboxes`, {
      headers: sandboxHeaders(this.authToken, this.grantToken),
      timeoutSeconds: 10
    });
    const data = await response.json();
    return Array.isArray(data) ? data.filter((item): item is string => typeof item === "string") : [];
  }

  async remove(name: string): Promise<void> {
    await sandboxFetch(`${this.baseUrl}/v1/sandboxes/${encodeURIComponent(name)}`, {
      method: "DELETE",
      headers: sandboxHeaders(this.authToken, this.grantToken),
      timeoutSeconds: 10
    });
  }

  async runPython(code: string, options: Record<string, unknown> = {}): Promise<ExecResult> {
    return this.runOneShot("/v1/run_python", {
      code,
      image: options.image ?? "python:3.11-slim",
      ...options
    });
  }

  async run_python(code: string, options: Record<string, unknown> = {}): Promise<ExecResult> {
    return this.runPython(code, options);
  }

  async runShell(script: string, options: Record<string, unknown> = {}): Promise<ExecResult> {
    return this.runOneShot("/v1/run_shell", {
      script,
      image: options.image ?? "python:3.11-slim",
      ...options
    });
  }

  async run_shell(script: string, options: Record<string, unknown> = {}): Promise<ExecResult> {
    return this.runShell(script, options);
  }

  private async runOneShot(path: string, body: Record<string, unknown>): Promise<ExecResult> {
    const payload = { ...body };
    const workspace = payload.workspace;
    delete payload.workspace;
    payload.bucket ??= workspace ?? this.defaultWorkspace ?? "agent-workspace";
    payload.timeout_seconds ??= this.timeoutSeconds;
    const response = await sandboxFetch(`${this.baseUrl}${path}`, {
      method: "POST",
      headers: { ...sandboxHeaders(this.authToken, this.grantToken), "content-type": "application/json" },
      body: JSON.stringify(payload),
      timeoutSeconds: Number(payload.timeout_seconds) + 30
    });
    return normalizeExecResult(await response.json());
  }
}

export class WorkspaceClient {
  readonly grantToken: string;
  readonly grant: WorkspaceGrantPayload | null;
  private readonly baseUrl: string;

  constructor(options: {
    cpUrl: string;
    grantToken: string;
    grant?: WorkspaceGrantPayload | null;
  }) {
    this.baseUrl = options.cpUrl.replace(/\/+$/, "");
    this.grantToken = options.grantToken;
    this.grant = options.grant ?? decodeGrantPayload(options.grantToken);
  }

  get outputsPrefix(): string | null {
    return this.grant?.outputs_prefix ?? null;
  }

  async exists(path: string): Promise<boolean> {
    const response = await fetch(this.url(`files/${path}`), {
      method: "HEAD",
      headers: this.headers()
    });
    if (response.status === 404) return false;
    if (response.status >= 400) throw new WorkspaceDenied(`workspace stat failed: ${response.status}`);
    return true;
  }

  async readBytes(path: string): Promise<Uint8Array> {
    const response = await fetch(this.url(`files/${path}`), { headers: this.headers() });
    if (response.status === 404) throw new Error(`workspace file not found: ${path}`);
    if (response.status >= 400) throw new WorkspaceDenied(`workspace read failed: ${response.status}`);
    return new Uint8Array(await response.arrayBuffer());
  }

  async readText(path: string): Promise<string> {
    return new TextDecoder().decode(await this.readBytes(path));
  }

  async writeBytes(path: string, content: Uint8Array | Buffer, contentType = "application/octet-stream"): Promise<void> {
    const response = await fetch(this.url(`files/${path}`), {
      method: "PUT",
      headers: { ...this.headers(), "content-type": contentType },
      body: content
    });
    if (response.status >= 400) throw new WorkspaceDenied(`workspace write failed: ${response.status}`);
  }

  async writeText(path: string, content: string, contentType = "text/plain; charset=utf-8"): Promise<void> {
    await this.writeBytes(path, Buffer.from(content, "utf8"), contentType);
  }

  async delete(path: string): Promise<void> {
    const response = await fetch(this.url(`files/${path}`), {
      method: "DELETE",
      headers: this.headers()
    });
    if (![204, 404].includes(response.status) && response.status >= 400) {
      throw new WorkspaceDenied(`workspace delete failed: ${response.status}`);
    }
  }

  async list(): Promise<WorkspaceFileInfo[]> {
    const response = await fetch(this.url("files"), { headers: this.headers() });
    if (response.status >= 400) throw new WorkspaceDenied(`workspace list failed: ${response.status}`);
    const data = await response.json();
    return Array.isArray(data) ? data.filter(isObject).map((item) => ({
      path: String(item.path ?? ""),
      size_bytes: typeof item.size_bytes === "number" ? item.size_bytes : undefined,
      file_type: typeof item.file_type === "string" ? item.file_type : undefined
    })).filter((item) => item.path) : [];
  }

  private headers(): Record<string, string> {
    return { "x-a2a-grant": this.grantToken };
  }

  private url(path: string): string {
    const suffix = path.split("/").map(encodeURIComponent).join("/");
    return `${this.baseUrl}/v1/workspace-grants/${suffix}`;
  }
}

export class MissingScopes extends Error {
  missing: string[];

  constructor(missing: string[]) {
    super(`missing scopes: ${missing.sort().join(", ")}`);
    this.name = "MissingScopes";
    this.missing = [...missing].sort();
  }
}

export class RunContext {
  readonly request: WorkerRequest;
  readonly agent: string;
  readonly skill: string;
  readonly handler: string;
  readonly arguments: Record<string, unknown>;
  readonly auth: Record<string, unknown> | null;
  private readonly events: Record<string, unknown>[] = [];
  private readonly artifacts: Record<string, unknown>[] = [];
  private workspaceClient?: WorkspaceClient;
  private sandboxClient?: HttpSandboxClient;

  constructor(request: WorkerRequest) {
    this.request = request;
    this.agent = request.agent;
    this.skill = request.skill;
    this.handler = request.handler;
    this.arguments = request.arguments;
    this.auth = request.auth ?? null;
  }

  get emittedEvents(): Record<string, unknown>[] {
    return [...this.events];
  }

  get emittedArtifacts(): Record<string, unknown>[] {
    return [...this.artifacts];
  }

  get taskId(): string {
    if (this.request.task_id) return this.request.task_id;
    const runId = this.request.composition?.run_id;
    if (typeof runId === "string" && runId) return runId;
    const taskId = this.request.composition?.task_id;
    if (typeof taskId === "string" && taskId) return taskId;
    return "local";
  }

  get grant(): WorkspaceGrantPayload | null {
    return this.request.grant ? decodeGrantPayload(this.request.grant) : null;
  }

  get workspace(): WorkspaceClient {
    if (!this.request.grant) {
      throw new Error("missing workspace grant");
    }
    const cpUrl = this.cpUrl();
    if (!cpUrl) {
      throw new Error("missing control plane URL for workspace grant");
    }
    this.workspaceClient ??= new WorkspaceClient({
      cpUrl,
      grantToken: this.request.grant,
      grant: this.grant
    });
    return this.workspaceClient;
  }

  get sandbox(): HttpSandboxClient {
    const baseUrl = process.env.A2A_SANDBOX_URL;
    if (!baseUrl) throw new SandboxUnavailable();
    this.sandboxClient ??= new HttpSandboxClient(baseUrl, {
      defaultWorkspace: this.grant?.bucket,
      timeoutSeconds: Number(process.env.A2A_SANDBOX_TIMEOUT_S || "1200"),
      authToken: process.env.A2A_SANDBOX_TOKEN,
      grantToken: this.request.grant
    });
    return this.sandboxClient;
  }

  async emitEvent(event: AgentEvent | Record<string, unknown>): Promise<void> {
    const normalized = normalizeEvent(event);
    this.events.push(normalized);
    await this.request.emit?.(normalized);
  }

  async emitProgress(message: string, payload: Record<string, unknown> = {}): Promise<void> {
    await this.emitEvent({ kind: "progress", payload: { message, ...payload } });
  }

  async emitTextDelta(text: string): Promise<void> {
    await this.emitEvent({ kind: "text_delta", payload: { text } });
  }

  async emitError(message: string, options: { code?: string } = {}): Promise<void> {
    await this.emitEvent({ kind: "error", payload: { message, ...options } });
  }

  async emitArtifact(ref: ArtifactRef): Promise<void> {
    const artifact = { ...ref };
    this.artifacts.push(artifact);
    await this.emitEvent({ kind: "artifact", payload: artifact });
  }

  async writeArtifact(
    name: string,
    data: ArtifactData,
    contentType = "application/octet-stream",
    metadata: Record<string, unknown> = {}
  ): Promise<ArtifactRef> {
    const bytes = bytesFromArtifactData(data);
    const path = this.artifactPath(name);
    let ref: ArtifactRef;
    if (this.request.grant && this.cpUrl()) {
      await this.workspace.writeBytes(path, bytes, contentType);
      ref = {
        name,
        path,
        uri: this.workspace.grant?.bucket ? `s3://${this.workspace.grant.bucket}/${path}` : `workspace://${path}`,
        mime_type: contentType,
        content_type: contentType,
        bytes: bytes.byteLength,
        metadata
      };
    } else {
      ref = {
        name,
        path,
        uri: `memory://${this.taskId}/${safeArtifactName(name)}`,
        mime_type: contentType,
        content_type: contentType,
        bytes: bytes.byteLength,
        metadata
      };
    }
    await this.emitArtifact(ref);
    return ref;
  }

  secret(name: string): string {
    const value = process.env[name];
    if (value == null || value === "") {
      throw new Error(`missing secret: ${name}`);
    }
    return value;
  }

  consumerConfig<T = unknown>(name: string, defaultValue?: T): T {
    const value = this.request.consumer_config?.[name];
    if (value === undefined) return defaultValue as T;
    return value as T;
  }

  consumerSecret(name: string): string {
    const value = this.request.consumer_secrets?.[name];
    if (value == null || value === "") {
      throw new Error(`missing consumer secret: ${name}`);
    }
    return value;
  }

  llm(): LLMCreds {
    if (!this.request.llm_creds) {
      throw new Error("missing LLM credentials");
    }
    return this.request.llm_creds;
  }

  cpJwt(): string | null {
    return this.request.cp_jwt ?? null;
  }

  cpUrl(): string | null {
    return this.request.cp_url ?? process.env.A2A_CP_URL ?? null;
  }

  requireScopes(required: string[]): void {
    const present = scopesFromAuth(this.auth);
    const missing = required.filter((scope) => !present.has(scope));
    if (missing.length > 0) throw new MissingScopes(missing);
  }

  async checkCancelled(): Promise<void> {
    if (this.request.cancelled) {
      throw new Error("operation cancelled");
    }
  }

  finalizeResponse(response: WorkerResponse): WorkerResponse {
    const finalized: WorkerResponse = { ...response };
    if (finalized.events === undefined && this.events.length > 0) {
      finalized.events = this.emittedEvents;
    }
    if (finalized.artifacts === undefined && this.artifacts.length > 0) {
      finalized.artifacts = this.emittedArtifacts;
    }
    return finalized;
  }

  private artifactPath(name: string): string {
    const safeName = safeArtifactName(name);
    const prefix = this.workspaceClient?.outputsPrefix ?? this.grant?.outputs_prefix ?? "outputs/";
    const cleanPrefix = prefix ? `${prefix.replace(/^\/+/, "").replace(/\/+$/, "")}/` : "";
    return `${cleanPrefix}${safeArtifactName(this.taskId)}/${safeName}`;
  }
}

export function publicAuth(): AgentDslAuth {
  return {
    model: "NoAuth",
    strategy: "public",
    principal_schema: { title: "NoAuth", type: "object", properties: {}, additionalProperties: false },
    required: false
  };
}

export function skill(declaration: SkillDeclaration): SkillDeclaration {
  validateSkill(declaration);
  return declaration;
}

export function compileAgent(
  agentClass: AgentClass,
  options: {
    language?: AgentDslLanguage;
    entrypoint?: AgentDslEntrypoint;
    metadata?: Record<string, unknown>;
  } = {}
): AgentDsl {
  const identity = agentClass.agent;
  if (!identity?.name) throw new Error("agent.agent.name is required");
  const skills = agentClass.skills.map(normalizeSkill);
  if (skills.length === 0) throw new Error("agent must declare at least one skill");
  const names = new Set<string>();
  for (const item of skills) {
    if (names.has(item.name)) throw new Error(`duplicate skill: ${item.name}`);
    names.add(item.name);
  }
  return {
    schema_version: "2026-06-04",
    language: options.language ?? "typescript",
    name: identity.name,
    description: identity.description ?? "",
    version: identity.version ?? "0.1.0",
    entrypoint: options.entrypoint ?? {},
    skills,
    capabilities: agentClass.capabilities,
    input_modes: agentClass.inputModes,
    output_modes: agentClass.outputModes,
    required_secrets: agentClass.requiredSecrets,
    required_env: agentClass.requiredEnv,
    consumer_setup: agentClass.consumerSetup,
    runtime: { sandbox: "microsandbox", ...agentClass.runtime },
    template_lineage: agentClass.templateLineage,
    meta_agent_manifest: agentClass.metaAgentManifest,
    state_schema: agentClass.stateSchema,
    workspace_access: agentClass.workspaceAccess,
    config_schema: agentClass.configSchema,
    auth: agentClass.auth,
    metadata: options.metadata ?? {}
  };
}

export async function writeAgentDsl(
  agentClass: AgentClass,
  path = ".a2a/agent.dsl.json",
  options: Parameters<typeof compileAgent>[1] = {}
): Promise<AgentDsl> {
  const dsl = compileAgent(agentClass, options);
  await mkdir(dirname(path), { recursive: true });
  await writeFile(path, `${JSON.stringify(dsl, null, 2)}\n`, "utf8");
  return dsl;
}

export function serveWorker(
  agentClass: AgentClass,
  handlers: Record<string, Handler>,
  options: { port?: number; host?: string } = {}
): Server {
  const dsl = compileAgent(agentClass);
  const handlerNames = new Set(dsl.skills.map((item) => item.handler));
  const server = createServer(async (req, res) => {
    try {
      if (req.method === "POST" && req.url?.startsWith("/_a2a/invoke-stream/")) {
        await handleStreamingWorkerInvoke(req, res, handlerNames, handlers);
        return;
      }
      if (req.method !== "POST" || !req.url?.startsWith("/_a2a/invoke/")) {
        writeJson(res, 404, { error: "not found" });
        return;
      }
      const handlerName = decodeURIComponent(req.url.slice("/_a2a/invoke/".length));
      if (!handlerNames.has(handlerName) || typeof handlers[handlerName] !== "function") {
        writeJson(res, 404, { error: `unknown handler: ${handlerName}` });
        return;
      }
      const body = await readJson(req);
      const request = body as WorkerRequest;
      const raw = await handlers[handlerName](request);
      const response = normalizeWorkerResponse(raw);
      writeJson(res, 200, response);
    } catch (error) {
      writeJson(res, 500, { error: error instanceof Error ? error.message : String(error) });
    }
  });
  disableLongRunningWorkerTimeouts(server);
  server.listen(options.port ?? 9001, options.host ?? "127.0.0.1");
  return server;
}

export function serveAgent(
  agentClass: AgentClass,
  options: { port?: number; host?: string } = {}
): Server {
  const dsl = compileAgent(agentClass);
  const agent = new agentClass();
  const handlerNames = new Set(dsl.skills.map((item) => item.handler));
  const server = createServer(async (req, res) => {
    try {
      if (req.method === "POST" && req.url?.startsWith("/_a2a/invoke-stream/")) {
        await handleStreamingAgentInvoke(req, res, handlerNames, agent);
        return;
      }
      if (req.method !== "POST" || !req.url?.startsWith("/_a2a/invoke/")) {
        writeJson(res, 404, { error: "not found" });
        return;
      }
      const handlerName = decodeURIComponent(req.url.slice("/_a2a/invoke/".length));
      if (!handlerNames.has(handlerName)) {
        writeJson(res, 404, { error: `unknown handler: ${handlerName}` });
        return;
      }
      const body = await readJson(req);
      const request = { ...(body as WorkerRequest), handler: handlerName };
      const response = await agent.dispatch(request);
      writeJson(res, 200, response);
    } catch (error) {
      writeJson(res, 500, { error: error instanceof Error ? error.message : String(error) });
    }
  });
  disableLongRunningWorkerTimeouts(server);
  server.listen(options.port ?? 9001, options.host ?? "127.0.0.1");
  return server;
}

async function handleStreamingWorkerInvoke(
  req: IncomingMessage,
  res: ServerResponse,
  handlerNames: Set<string>,
  handlers: Record<string, Handler>
): Promise<void> {
  const handlerName = decodeURIComponent(req.url!.slice("/_a2a/invoke-stream/".length));
  if (!handlerNames.has(handlerName) || typeof handlers[handlerName] !== "function") {
    writeSseError(res, 404, `unknown handler: ${handlerName}`);
    return;
  }
  await streamDispatch(res, async (emit) => {
    const body = await readJson(req);
    const request = { ...(body as WorkerRequest), handler: handlerName, emit };
    return normalizeWorkerResponse(await handlers[handlerName](request));
  });
}

async function handleStreamingAgentInvoke(
  req: IncomingMessage,
  res: ServerResponse,
  handlerNames: Set<string>,
  agent: A2AAgent
): Promise<void> {
  const handlerName = decodeURIComponent(req.url!.slice("/_a2a/invoke-stream/".length));
  if (!handlerNames.has(handlerName)) {
    writeSseError(res, 404, `unknown handler: ${handlerName}`);
    return;
  }
  await streamDispatch(res, async (emit) => {
    const body = await readJson(req);
    const request = { ...(body as WorkerRequest), handler: handlerName, emit };
    return agent.dispatch(request);
  });
}

async function streamDispatch(
  res: ServerResponse,
  dispatch: (emit: (event: Record<string, unknown>) => void) => Promise<WorkerResponse>
): Promise<void> {
  const emittedEvents: Record<string, unknown>[] = [];
  writeSseHead(res);
  writeSse(res, { type: "started" });
  try {
    const response = await dispatch((event) => {
      emittedEvents.push(event);
      writeSse(res, toEventFrame(event));
    });
    writeSse(res, {
      type: "result",
      result: response.result,
      events: response.events ?? emittedEvents,
      artifacts: response.artifacts ?? []
    });
  } catch (error) {
    writeSse(res, {
      type: "error",
      status: 500,
      detail: error instanceof Error ? error.message : String(error)
    });
  } finally {
    res.write("data: [DONE]\n\n");
    res.end();
  }
}

function toEventFrame(event: Record<string, unknown>): Record<string, unknown> {
  const kind = typeof event.kind === "string"
    ? event.kind
    : typeof event.type === "string"
      ? event.type
      : "progress";
  const { kind: _kind, type: _type, payload: rawPayload, ...rest } = event;
  const payload = isObject(rawPayload) ? rawPayload : rest;
  return { type: "event", kind, payload };
}

function disableLongRunningWorkerTimeouts(server: Server): void {
  server.timeout = 0;
  server.requestTimeout = 0;
  server.headersTimeout = 0;
  server.keepAliveTimeout = 0;
}

function normalizeSkill(declaration: SkillDeclaration): AgentDslSkill {
  validateSkill(declaration);
  return {
    name: declaration.name,
    description: declaration.description ?? "",
    handler: declaration.handler ?? declaration.name,
    tags: declaration.tags ?? [],
    scopes: declaration.scopes ?? [],
    stream: declaration.stream ?? false,
    policy: {
      timeout_seconds: declaration.policy?.timeout_seconds ?? null,
      idempotent: declaration.policy?.idempotent ?? false,
      max_retries: declaration.policy?.max_retries ?? 0,
      cost_class: declaration.policy?.cost_class ?? null,
      allow_scope_expansion: declaration.policy?.allow_scope_expansion ?? false,
      grant_mode: declaration.policy?.grant_mode ?? null,
      grant_allow_patterns: declaration.policy?.grant_allow_patterns ?? [],
      grant_deny_patterns: declaration.policy?.grant_deny_patterns ?? [],
      grant_outputs_prefix: declaration.policy?.grant_outputs_prefix ?? null,
      grant_write_prefixes: declaration.policy?.grant_write_prefixes ?? [],
      grant_ttl_seconds: declaration.policy?.grant_ttl_seconds ?? null,
      grant_run_timeout_seconds: declaration.policy?.grant_run_timeout_seconds ?? null,
      grant_approval_timeout_seconds: declaration.policy?.grant_approval_timeout_seconds ?? null,
      grant_scope_approval_timeout_seconds: declaration.policy?.grant_scope_approval_timeout_seconds ?? null
    },
    input_schema: declaration.input_schema,
    output_schema: declaration.output_schema
  };
}

function validateSkill(declaration: SkillDeclaration): void {
  if (!declaration.name) throw new Error("skill.name is required");
  if (declaration.input_schema?.type !== "object") {
    throw new Error(`skill ${declaration.name}: input_schema must be an object schema`);
  }
  if (typeof declaration.input_schema.properties !== "object" || declaration.input_schema.properties === null) {
    throw new Error(`skill ${declaration.name}: input_schema.properties is required`);
  }
  if (!Array.isArray(declaration.input_schema.required)) {
    throw new Error(`skill ${declaration.name}: input_schema.required must be an array`);
  }
  for (const key of declaration.input_schema.required) {
    if (typeof key !== "string") throw new Error(`skill ${declaration.name}: required values must be strings`);
    if (!(key in (declaration.input_schema.properties as Record<string, unknown>))) {
      throw new Error(`skill ${declaration.name}: required field ${key} missing from properties`);
    }
  }
  if (!declaration.output_schema || Object.keys(declaration.output_schema).length === 0) {
    throw new Error(`skill ${declaration.name}: output_schema is required`);
  }
}

function normalizeWorkerResponse(value: unknown): WorkerResponse {
  if (isObject(value) && "result" in value) {
    return value as WorkerResponse;
  }
  return { result: value };
}

function isObject(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

function normalizeEvent(event: AgentEvent | Record<string, unknown>): Record<string, unknown> {
  const raw = event as Record<string, unknown>;
  if (typeof raw.kind === "string") {
    return {
      kind: raw.kind,
      payload: isObject(raw.payload) ? raw.payload : {}
    };
  }
  if (typeof raw.type === "string") {
    const { type, payload, ...rest } = raw;
    return {
      kind: type,
      payload: isObject(payload) ? payload : rest
    };
  }
  return { kind: "event", payload: raw };
}

function decodeGrantPayload(token: string): WorkspaceGrantPayload | null {
  const rawPayload = token.split(".", 1)[0];
  if (!rawPayload) return null;
  try {
    const padded = rawPayload.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(rawPayload.length / 4) * 4, "=");
    const parsed = JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
    if (!isObject(parsed)) return null;
    return parsed as WorkspaceGrantPayload;
  } catch {
    return null;
  }
}

function bytesFromArtifactData(data: ArtifactData): Uint8Array {
  if (typeof data === "string") return Buffer.from(data, "utf8");
  if (Buffer.isBuffer(data)) return data;
  return data;
}

function safeArtifactName(name: string): string {
  const clean = name.replace(/\\/g, "/").split("/").filter(Boolean).join("/");
  return clean.replace(/[^A-Za-z0-9._/-]/g, "_").replace(/\.\.(\/|$)/g, "");
}

function sandboxHeaders(authToken?: string, grantToken?: string): Record<string, string> {
  const headers: Record<string, string> = {};
  if (authToken) headers.authorization = `Bearer ${authToken}`;
  if (grantToken) headers["x-a2a-grant"] = grantToken;
  return headers;
}

async function sandboxFetch(
  url: string,
  options: RequestInit & { timeoutSeconds: number }
): Promise<Response> {
  const { timeoutSeconds, ...request } = options;
  const response = await fetch(url, {
    ...request,
    signal: AbortSignal.timeout(Math.max(1, timeoutSeconds) * 1000)
  });
  if (response.status >= 400) {
    const body = await response.text().catch(() => "");
    throw new SandboxRuntimeError(`sandbox request failed: ${request.method ?? "GET"} ${url} status=${response.status}`, {
      statusCode: response.status,
      responseBody: body || undefined
    });
  }
  return response;
}

function normalizeExecResult(value: unknown): ExecResult {
  const data = isObject(value) ? value : {};
  return {
    stdout: typeof data.stdout === "string" ? data.stdout : "",
    stderr: typeof data.stderr === "string" ? data.stderr : "",
    exit_code: typeof data.exit_code === "number" ? data.exit_code : 0,
    truncated: typeof data.truncated === "boolean" ? data.truncated : false,
    files: Array.isArray(data.files)
      ? data.files.filter(isObject).map((item) => ({ ...item }))
      : []
  };
}

function scopesFromAuth(auth: Record<string, unknown> | null): Set<string> {
  if (auth === null) return new Set();
  const scopes = new Set<string>();
  const list = auth.scopes;
  if (Array.isArray(list)) {
    for (const item of list) {
      if (typeof item === "string") scopes.add(item);
    }
  }
  const scope = auth.scope;
  if (typeof scope === "string") {
    for (const item of scope.split(/\s+/)) {
      if (item) scopes.add(item);
    }
  }
  return scopes;
}

async function readJson(req: IncomingMessage): Promise<unknown> {
  const chunks: Buffer[] = [];
  for await (const chunk of req) chunks.push(Buffer.from(chunk));
  const raw = Buffer.concat(chunks).toString("utf8");
  return raw ? JSON.parse(raw) : {};
}

function writeJson(res: ServerResponse, status: number, body: unknown): void {
  res.statusCode = status;
  res.setHeader("content-type", "application/json");
  res.end(JSON.stringify(body));
}

function writeSseHead(res: ServerResponse): void {
  if (res.headersSent) return;
  res.statusCode = 200;
  res.setHeader("content-type", "text/event-stream");
  res.setHeader("cache-control", "no-cache");
  res.setHeader("connection", "keep-alive");
  res.setHeader("x-accel-buffering", "no");
}

function writeSse(res: ServerResponse, body: unknown): void {
  writeSseHead(res);
  res.write(`data: ${JSON.stringify(body)}\n\n`);
}

function writeSseError(res: ServerResponse, status: number, detail: string): void {
  writeSse(res, { type: "error", status, detail });
  res.write("data: [DONE]\n\n");
  res.end();
}
