All files / src/server config-schema.ts

100% Statements 2/2
100% Branches 0/0
100% Functions 1/1
100% Lines 2/2

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155                                                      6x                                                                                                                                                                                                                                                         6x    
// NOTE: These types must match what Paperclip's SchemaConfigFields component
// expects. Paperclip's server at GET /api/adapters/:type/config-schema
// calls adapter.getConfigSchema() and the UI reads the JSON — types are only
// used at build time here. The Paperclip types in @paperclipai/adapter-utils
// may lag behind; these locals are the source of truth for this adapter.
 
interface ConfigFieldOption {
  label: string;
  value: string;
  group?: string;
}
 
type ConfigFieldSchema =
  | { type: "text"; key: string; label: string; hint?: string; default?: unknown; meta?: Record<string, unknown> }
  | { type: "number"; key: string; label: string; hint?: string; default?: unknown; meta?: Record<string, unknown> }
  | { type: "toggle"; key: string; label: string; hint?: string; default?: unknown; meta?: Record<string, unknown> }
  | { type: "select"; key: string; label: string; hint?: string; options: ConfigFieldOption[]; default?: unknown; meta?: Record<string, unknown> }
  | { type: "textarea"; key: string; label: string; hint?: string; default?: unknown; meta?: Record<string, unknown> }
  | { type: "combobox"; key: string; label: string; hint?: string; options?: ConfigFieldOption[]; default?: unknown; meta?: Record<string, unknown> };
 
interface AdapterConfigSchema {
  fields: ConfigFieldSchema[];
}
 
export function getConfigSchema(): AdapterConfigSchema {
  // model, effort, instructionsFilePath, timeoutSec, graceSec are provided
  // by the platform UI and must not be duplicated here.
  const fields: ConfigFieldSchema[] = [
    // Core Claude fields
    {
      type: "number",
      key: "maxTurnsPerRun",
      label: "Max Turns Per Run",
      hint: "Maximum number of agentic turns (tool calls) per heartbeat run. 0 means unlimited.",
      default: 1000,
    },
    // Kubernetes
    {
      type: "text",
      key: "serviceAccountName",
      label: "Service Account",
      hint: "Service Account name for Job pods. Defaults to the cluster default.",
    },
    {
      type: "text",
      key: "namespace",
      label: "Namespace",
      hint: "Kubernetes namespace for Jobs. Defaults to the Deployment namespace.",
    },
    {
      type: "text",
      key: "image",
      label: "Container Image",
      hint: "Override the container image used for Job pods. Defaults to the running Deployment image.",
    },
    {
      type: "select",
      key: "imagePullPolicy",
      label: "Image Pull Policy",
      hint: "Image pull policy for the container image.",
      options: [
        { value: "IfNotPresent", label: "IfNotPresent" },
        { value: "Always", label: "Always" },
        { value: "Never", label: "Never" },
      ],
    },
    {
      type: "text",
      key: "kubeconfig",
      label: "Kubeconfig Path",
      hint: "Absolute path to a kubeconfig file on disk. Defaults to in-cluster service account auth.",
    },
    {
      type: "number",
      key: "ttlSecondsAfterFinished",
      label: "TTL Seconds After Finished",
      hint: "Auto-cleanup delay for completed Jobs in seconds. Default: 300.",
    },
    {
      type: "toggle",
      key: "retainJobs",
      label: "Retain Jobs",
      hint: "Skip cleanup of completed Jobs for debugging purposes.",
    },
    {
      type: "toggle",
      key: "reattachOrphanedJobs",
      label: "Reattach to Orphaned Jobs",
      hint: "If a prior K8s Job for the same agent/task/session is still running (e.g. Paperclip restarted mid-run), attach to it and stream its output instead of blocking the new run. When false, any non-terminal orphan blocks the new run. Default: on.",
      default: true,
    },
    // Resource Limits
    {
      type: "text",
      key: "resources.requests.cpu",
      label: "CPU Request",
      hint: "CPU request for Job pods (e.g. 100m, 0.5, 1).",
    },
    {
      type: "text",
      key: "resources.requests.memory",
      label: "Memory Request",
      hint: "Memory request for Job pods (e.g. 128Mi, 512Mi, 1Gi).",
    },
    {
      type: "text",
      key: "resources.limits.cpu",
      label: "CPU Limit",
      hint: "CPU limit for Job pods (e.g. 100m, 0.5, 1).",
    },
    {
      type: "text",
      key: "resources.limits.memory",
      label: "Memory Limit",
      hint: "Memory limit for Job pods (e.g. 128Mi, 512Mi, 1Gi).",
    },
    // Scheduling
    {
      type: "textarea",
      key: "nodeSelector",
      label: "Node Selector",
      hint: "Node selector for Job pods. One key=value per line (e.g. disktype=ssd).",
    },
    {
      type: "textarea",
      key: "tolerations",
      label: "Tolerations",
      hint: "Tolerations for Job pods as JSON array.",
    },
    {
      type: "textarea",
      key: "labels",
      label: "Labels",
      hint: "Extra labels added to Job metadata. One key=value per line.",
    },
    // Output filtering (RTK-compatible)
    {
      type: "toggle",
      key: "enableRtk",
      label: "Enable Output Filtering",
      hint: "Truncate oversized tool outputs before they reach the model, reducing token consumption. Implemented natively in Node.js — no external binary required. Installs a PostToolUse hook in ~/.claude/settings.json for each run.",
      default: false,
    },
    {
      type: "number",
      key: "rtkMaxOutputBytes",
      label: "Max Tool Output Bytes",
      hint: "Maximum bytes of tool output to pass to the model when output filtering is enabled. Outputs exceeding this threshold are truncated with a summary. Default: 50000.",
      default: 50000,
    },
  ];
 
  return { fields };
}