Arsitektur agentic coding runtime memisahkan antarmuka LLM dari execution environment (sandbox/runner). Model AI tidak langsung menyentuh file atau mengeksekusi shell di mesin pengguna; model mengeluarkan struktur data terencana (structured tool calls) yang kemudian diproses oleh backend runner. Tanpa batas kontrak yang ketat, network retry atau halusinasi model dapat memicu duplikasi patch file, modifikasi destruktif, atau eskalasi hak akses sistem.

Skema JSON Schema untuk Agentic Tool Call

Runner membutuhkan kontrak validasi deterministik sebelum request diserahkan ke sistem operasi target. Eksekusi kode biasanya membutuhkan dua aksi dasar: mutasi file (aplikasi patch) dan eksekusi perintah shell. Menggunakan JSON Schema standar, kita mendefinisikan batasan input secara eksplisit.

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["tool_name", "call_id", "parameters"],
  "properties": {
    "tool_name": {
      "type": "string",
      "enum": ["apply_file_patch", "execute_shell_command"]
    },
    "call_id": {
      "type": "string",
      "format": "uuid"
    },
    "parameters": {
      "type": "object",
      "oneOf": [
        {
          "properties": {
            "path": { "type": "string" },
            "diff": { "type": "string" },
            "base_sha": { "type": "string", "pattern": "^[0-9a-f]{40}$" }
          },
          "required": ["path", "diff", "base_sha"]
        },
        {
          "properties": {
            "command": { "type": "string" },
            "timeout_ms": { "type": "integer", "maximum": 60000 }
          },
          "required": ["command"]
        }
      ]
    }
  }
}

Menyertakan base_sha pada payload mutasi file mencegah dirty writes jika kondisi workspace lokal telah bergeser sebelum patch diaplikasikan.

Idempotency-Key: Mencegah Duplikasi Mutasi Saat Model Retry

Saat koneksi timeout atau output parser mengalami parsing error, LLM client sering mengulangi pemanggilan tool dengan parameter yang sama. Jika mutasi file bersifat aditif (seperti patch atau append), eksekusi ulang tanpa idempotensi merusak struktur file target.

Klien wajib mengirimkan header Idempotency-Key. Server menggunakan distributed cache (seperti Redis) untuk mencatat status transaksi:

  1. PENDING: Mencegah race condition ketika request duplikat datang bersamaan saat eksekusi pertama masih berjalan.
  2. COMPLETED: Mengembalikan respons persis dari cache tanpa mengeksekusi patch ulang ke disk.

Implementasi Middleware Idempotency (Node.js/Express)

import { Request, Response, NextFunction } from 'express';
import Redis from 'ioredis';

const redis = new Redis();

export async function idempotencyMiddleware(req: Request, res: Response, next: NextFunction) {
  const idempotencyKey = req.header('Idempotency-Key');
  if (!idempotencyKey) {
    return res.status(400).json({ error: 'Missing Idempotency-Key header' });
  }

  const cacheKey = `idempotency:${idempotencyKey}`;
  const clientMutationId = await redis.get(cacheKey);

  if (clientMutationId) {
    const parsed = JSON.parse(clientMutationId);
    if (parsed.status === 'PENDING') {
      return res.status(409).json({ error: 'Concurrent execution in progress' });
    }
    return res.status(200).json(parsed.response);
  }

  await redis.set(cacheKey, JSON.stringify({ status: 'PENDING' }), 'EX', 120);

  const originalJson = res.json.bind(res);
  res.json = (body: any) => {
    if (res.statusCode >= 200 && res.statusCode < 300) {
      redis.set(cacheKey, JSON.stringify({ status: 'COMPLETED', response: body }), 'EX', 86400);
    } else {
      redis.del(cacheKey);
    }
    return originalJson(body);
  };

  next();
}

Scoped Auth Token (Least-Privilege) untuk Shell Execution

Model tidak boleh memegang API key master dengan hak akses tak terbatas ketika menjalankan perintah shell. Agen membutuhkan short-lived scoped token yang dibatasi hanya pada direktori kerja spesifik (working directory jail) dan kumpulan biner tertentu.

Middleware Verifikasi Permission

import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

interface ToolTokenPayload {
  agent_id: string;
  run_id: string;
  scopes: string[];
  allowed_paths: string[];
}

export function verifyToolPermission(requiredScope: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    const authHeader = req.headers.authorization;
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      return res.status(401).json({ error: 'Missing or invalid authorization token' });
    }

    const token = authHeader.split(' ')[1];
    try {
      const decoded = jwt.verify(token, process.env.TOOL_RUNNER_SECRET!) as ToolTokenPayload;
      
      if (!decoded.scopes.includes(requiredScope)) {
        return res.status(403).json({
          error: 'Forbidden',
          message: `Required scope: ${requiredScope}`
        });
      }

      req.agent = decoded;
      next();
    } catch (err) {
      return res.status(401).json({ error: 'Invalid or expired token' });
    }
  };
}

WebSocket vs HTTP untuk Streaming Output

Eksekusi shell berdurasi panjang memerlukan arsitektur transportasi yang tepat.

  • HTTP (REST): Cocok untuk mutasi atomik seperti apply_file_patch. Operasi disk ini bersifat instan, deterministik, dan mudah dicatat dalam cache idempotensi.
  • WebSocket: Wajib untuk execute_shell_command. Menghindari connection timeout dari HTTP gateway dan memungkinkan streaming stdout/stderr secara realtime ke agen untuk pemantauan status proses, serta pengiriman sinyal interupsi (misal: SIGINT) secara simultan.
Verifikasi scoped token pada WebSocket harus dilakukan pada fase HTTP handshake awal sebelum koneksi di-upgrade, membatasi konsumsi resource terhadap koneksi yang tidak sah.