@crvouga/dev-fleet 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mise/locks/aws-cdk/2.1146.1/aube-lock.yaml +24 -0
- package/.mise/locks/aws-cdk/2.1146.1/package.json +7 -0
- package/.mise/locks/azure-cli/2.91.0~a837e014/pyproject.toml +5 -0
- package/.mise/locks/azure-cli/2.91.0~a837e014/uv.lock +2794 -0
- package/.mise/locks/gemini/0.66.0~84aac8e6/aube-lock.yaml +123 -0
- package/.mise/locks/gemini/0.66.0~84aac8e6/package.json +12 -0
- package/.mise/locks/npm-devcontainers-cli/0.90.0/aube-lock.yaml +24 -0
- package/.mise/locks/npm-devcontainers-cli/0.90.0/package.json +7 -0
- package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/aube-lock.yaml +1432 -0
- package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/package.json +7 -0
- package/.mise/locks/npm-vercel/65.8.0/aube-lock.yaml +3595 -0
- package/.mise/locks/npm-vercel/65.8.0/package.json +7 -0
- package/.mise/locks/npm-wrangler/4.156.0/aube-lock.yaml +895 -0
- package/.mise/locks/npm-wrangler/4.156.0/package.json +7 -0
- package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/aube-lock.yaml +24 -0
- package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/package.json +7 -0
- package/LICENSE +21 -0
- package/README.md +357 -2
- package/agents/antigravity-proxy.ts +3 -0
- package/agents/audit.md +89 -0
- package/agents/claude-proxy +5 -0
- package/agents/claude-proxy.ts +3 -0
- package/agents/codex-proxy.ts +3 -0
- package/agents/grok-proxy.ts +3 -0
- package/agents/managed-proxy.ts +55 -0
- package/agents/opencode-proxy.ts +3 -0
- package/agents/registry.json +209 -0
- package/agents/registry.schema.json +149 -0
- package/agents/setup.md +158 -0
- package/agents/t3.json +40 -0
- package/ansible/macos/manager.yml +30 -0
- package/ansible/macos/policy.json +9 -0
- package/ansible/monitoring/README.md +215 -0
- package/ansible/monitoring/agent-darwin.yml +198 -0
- package/ansible/monitoring/agent.yml +172 -0
- package/ansible/monitoring/beszel-agent.service +42 -0
- package/ansible/monitoring/compose.yml +74 -0
- package/ansible/monitoring/hub-darwin.yml +102 -0
- package/ansible/monitoring/hub-route-darwin.yml +51 -0
- package/ansible/monitoring/lock.json +16 -0
- package/ansible/net/remote-probe.sh +13 -0
- package/ansible/pyproject.toml +12 -0
- package/ansible/ubuntu/authorize-sudo.sh +51 -0
- package/ansible/ubuntu/baseline.yml +89 -0
- package/ansible/ubuntu/disk-gc.py +336 -0
- package/ansible/ubuntu/disk-guard.py +104 -0
- package/ansible/ubuntu/policy.json +52 -0
- package/ansible/ubuntu/recovery.yml +15 -0
- package/ansible/ubuntu/t3/policy.json +7 -0
- package/ansible/ubuntu/t3-desktop.yml +220 -0
- package/ansible/ubuntu/t3-health.sh +33 -0
- package/ansible/ubuntu/t3-policy.yml +76 -0
- package/ansible/ubuntu/t3-route.yml +28 -0
- package/ansible/ubuntu/tailscale.yml +78 -0
- package/ansible/ubuntu/tasks/credentials.yml +47 -0
- package/ansible/ubuntu/tasks/docker.yml +31 -0
- package/ansible/ubuntu/tasks/packages.yml +56 -0
- package/ansible/ubuntu/tasks/power.yml +51 -0
- package/ansible/ubuntu/tasks/recovery.yml +39 -0
- package/ansible/ubuntu/tasks/resources.yml +312 -0
- package/ansible/ubuntu/tasks/services.yml +31 -0
- package/ansible/ubuntu/tasks/ssh.yml +71 -0
- package/ansible/ubuntu/tasks/user.yml +120 -0
- package/ansible/uv.lock +190 -0
- package/bin/devfleet.mjs +36 -0
- package/bootstrap/bootstrap.ps1 +50 -0
- package/bootstrap/bootstrap.sh +80 -0
- package/bootstrap/devfleet +7 -0
- package/bootstrap/docker-desktop.sh +33 -0
- package/bootstrap/doctor.ts +234 -0
- package/bootstrap/fleet-manager-run.sh +103 -0
- package/bootstrap/fleet-remote-bootstrap.sh +258 -0
- package/bootstrap/global-config.ts +171 -0
- package/bootstrap/install-launcher.sh +10 -0
- package/bootstrap/native-aws.ts +86 -0
- package/bootstrap/native-t3.ts +154 -0
- package/bootstrap/npm.ts +358 -0
- package/bootstrap/release-version.ts +157 -0
- package/bootstrap/setup-status.ts +51 -0
- package/bootstrap/setup.ts +88 -0
- package/bootstrap/shell-path.ts +105 -0
- package/bootstrap/verify-t3-archive.py +49 -0
- package/bootstrap/worker-auth.ts +65 -0
- package/bootstrap/worker-github.ts +47 -0
- package/bootstrap/worker-tools.ts +24 -0
- package/cliproxy/AGENTS.md +11 -0
- package/cliproxy/README.md +269 -0
- package/cliproxy/clients/bootstrap.py +143 -0
- package/cliproxy/clients/launcher.py +57 -0
- package/cliproxy/compose.yaml +90 -0
- package/cliproxy/config.template.yaml +70 -0
- package/cliproxy/docs/cloud-adoption.md +16 -0
- package/cliproxy/docs/deployment.png +0 -0
- package/cliproxy/docs/deployment.svg +109 -0
- package/cliproxy/docs/health-workflow.example.yml +185 -0
- package/cliproxy/docs/migration.md +131 -0
- package/cliproxy/docs/model-health-report.schema.json +364 -0
- package/cliproxy/docs/model-health.md +393 -0
- package/cliproxy/image.lock.json +5 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-backup-check.plist +38 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-backup.plist +48 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-fleet.plist +32 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-gc.plist +38 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-monitor.plist +29 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-vault-renew.plist +31 -0
- package/cliproxy/models.json +1618 -0
- package/cliproxy/requirements.lock +78 -0
- package/cliproxy/scripts/accounts.py +38 -0
- package/cliproxy/scripts/audit.py +123 -0
- package/cliproxy/scripts/backup +234 -0
- package/cliproxy/scripts/check.py +34 -0
- package/cliproxy/scripts/checkpoint.py +226 -0
- package/cliproxy/scripts/cli.py +64 -0
- package/cliproxy/scripts/cliproxy_host.py +257 -0
- package/cliproxy/scripts/cloud_state.py +150 -0
- package/cliproxy/scripts/container-health +6 -0
- package/cliproxy/scripts/docker +24 -0
- package/cliproxy/scripts/external-health.py +79 -0
- package/cliproxy/scripts/fleet_config.py +43 -0
- package/cliproxy/scripts/fleet_sync.py +274 -0
- package/cliproxy/scripts/initialize +84 -0
- package/cliproxy/scripts/install-dependencies +48 -0
- package/cliproxy/scripts/install.py +406 -0
- package/cliproxy/scripts/model_health.py +16 -0
- package/cliproxy/scripts/modelhealth/__init__.py +5 -0
- package/cliproxy/scripts/modelhealth/checks.py +429 -0
- package/cliproxy/scripts/modelhealth/cli.py +117 -0
- package/cliproxy/scripts/modelhealth/config.py +186 -0
- package/cliproxy/scripts/modelhealth/emulator.py +536 -0
- package/cliproxy/scripts/modelhealth/expect.py +88 -0
- package/cliproxy/scripts/modelhealth/findings.py +183 -0
- package/cliproxy/scripts/modelhealth/protocol.py +330 -0
- package/cliproxy/scripts/modelhealth/redact.py +28 -0
- package/cliproxy/scripts/modelhealth/report.py +150 -0
- package/cliproxy/scripts/modelhealth/runner.py +390 -0
- package/cliproxy/scripts/modelhealth/scenario.py +131 -0
- package/cliproxy/scripts/modelhealth/schema.py +63 -0
- package/cliproxy/scripts/modelhealth/simulate.py +111 -0
- package/cliproxy/scripts/modelhealth/transport.py +213 -0
- package/cliproxy/scripts/monitor +68 -0
- package/cliproxy/scripts/ops +527 -0
- package/cliproxy/scripts/public-endpoint +127 -0
- package/cliproxy/scripts/recover_node.py +177 -0
- package/cliproxy/scripts/restore-verify +60 -0
- package/cliproxy/scripts/retention.py +144 -0
- package/cliproxy/scripts/service-start +7 -0
- package/cliproxy/scripts/validate-config +56 -0
- package/cliproxy/scripts/vault-access.py +57 -0
- package/cliproxy/scripts/vault-backup-login +55 -0
- package/cliproxy/scripts/vault-recovery +56 -0
- package/cliproxy/scripts/vault-renew +30 -0
- package/cliproxy/systemd/cliproxy-backup-check.service +10 -0
- package/cliproxy/systemd/cliproxy-backup-check.timer +8 -0
- package/cliproxy/systemd/cliproxy-backup.service +13 -0
- package/cliproxy/systemd/cliproxy-backup.timer +8 -0
- package/cliproxy/systemd/cliproxy-fleet.service +17 -0
- package/cliproxy/systemd/cliproxy-gc.service +13 -0
- package/cliproxy/systemd/cliproxy-gc.timer +8 -0
- package/cliproxy/systemd/cliproxy-monitor.service +8 -0
- package/cliproxy/systemd/cliproxy-monitor.timer +7 -0
- package/cliproxy/systemd/cliproxy-vault-renew.service +8 -0
- package/cliproxy/systemd/cliproxy-vault-renew.timer +7 -0
- package/cliproxy/vendor/LICENSE +22 -0
- package/cliproxy/vendor/config.upstream.yaml +1349 -0
- package/docs/consumer-setup.md +408 -0
- package/docs/declared.md +89 -0
- package/docs/machine-setup.md +399 -0
- package/docs/operations.md +276 -0
- package/docs/releasing.md +103 -0
- package/docs/state.md +254 -0
- package/docs/topology.md +67 -0
- package/docs/ubuntu-sync.md +351 -0
- package/docs/worker-contract.md +67 -0
- package/examples/fleet.json +100 -0
- package/examples/roster.json +13 -0
- package/llms.txt +35 -0
- package/mise.lock +1521 -0
- package/mise.toml +110 -0
- package/package.json +74 -4
- package/schema/fleet.schema.json +420 -0
- package/schema/roster.schema.json +77 -0
- package/src/agents/account-relay.ts +111 -0
- package/src/agents/accounts.ts +85 -0
- package/src/agents/audit.ts +77 -0
- package/src/agents/cli.ts +13 -0
- package/src/agents/command.ts +204 -0
- package/src/agents/converge.ts +123 -0
- package/src/agents/doctor.ts +107 -0
- package/src/agents/errors.ts +2 -0
- package/src/agents/files.ts +73 -0
- package/src/agents/fleet-verify.ts +165 -0
- package/src/agents/launch.ts +290 -0
- package/src/agents/native-verify.ts +371 -0
- package/src/agents/provider-errors.ts +57 -0
- package/src/agents/registry.ts +61 -0
- package/src/agents/render.ts +110 -0
- package/src/agents/round-trip.ts +192 -0
- package/src/agents/secrets.ts +13 -0
- package/src/agents/toolchain.ts +34 -0
- package/src/agents/verify-cache.ts +103 -0
- package/src/agents/verify.ts +127 -0
- package/src/auth-checks.ts +163 -0
- package/src/cli/agent-guide.ts +129 -0
- package/src/cli/app.ts +122 -0
- package/src/cli/cli.ts +28 -0
- package/src/cli/command-index.ts +57 -0
- package/src/cli/contracts.ts +25 -0
- package/src/cli/fleet-reference.ts +17 -0
- package/src/cli/fleet-state.ts +499 -0
- package/src/cli/fleet-sync-checkout.ts +130 -0
- package/src/cli/fleet-sync-enrollment.ts +140 -0
- package/src/cli/fleet-sync-forward.ts +134 -0
- package/src/cli/fleet-sync-manager.ts +249 -0
- package/src/cli/fleet-sync-monitoring.ts +185 -0
- package/src/cli/fleet-sync-output.ts +317 -0
- package/src/cli/fleet-sync-privilege.ts +27 -0
- package/src/cli/fleet-sync-progress.ts +109 -0
- package/src/cli/fleet-sync-remote.ts +242 -0
- package/src/cli/fleet-sync-report.ts +26 -0
- package/src/cli/fleet-sync-schedule.ts +138 -0
- package/src/cli/fleet-sync-state.ts +86 -0
- package/src/cli/fleet-sync-vault.ts +166 -0
- package/src/cli/fleet-sync.ts +375 -0
- package/src/cli/fleet.ts +165 -0
- package/src/cli/managed-health.ts +219 -0
- package/src/cli/node-deployment.ts +34 -0
- package/src/cli/node-sync.ts +121 -0
- package/src/cli/node-t3.ts +31 -0
- package/src/cli/options.ts +141 -0
- package/src/cli/probe-source.ts +7 -0
- package/src/cli/program.ts +185 -0
- package/src/cli/project.ts +51 -0
- package/src/cli/register-fleet.ts +296 -0
- package/src/cli/registry.ts +185 -0
- package/src/cli/repair-apply.ts +67 -0
- package/src/cli/repair-guard.ts +11 -0
- package/src/cli/repair-inspect.ts +150 -0
- package/src/cli/repair-script.ts +76 -0
- package/src/cli/repair.ts +209 -0
- package/src/cli/roster.ts +105 -0
- package/src/cli/search.ts +35 -0
- package/src/cli/settings.ts +67 -0
- package/src/cli/source-operations.ts +102 -0
- package/src/cli/t3/command.ts +90 -0
- package/src/cli/t3/register.ts +55 -0
- package/src/cli/tailnet.ts +127 -0
- package/src/cli/ui-actions.ts +133 -0
- package/src/cli/ui-controller.ts +113 -0
- package/src/cli/ui-data.ts +106 -0
- package/src/cli/ui-execute.ts +163 -0
- package/src/cli/ui-repairs.ts +34 -0
- package/src/cli/ui-reports.ts +211 -0
- package/src/cli/ui-targets.ts +100 -0
- package/src/cli/ui-terminal.ts +109 -0
- package/src/cli/ui-types.ts +49 -0
- package/src/cli/ui-wizard.ts +101 -0
- package/src/cli/ui.ts +9 -0
- package/src/cli/worker-health-output.ts +37 -0
- package/src/config.ts +186 -0
- package/src/configure.ts +255 -0
- package/src/definition.ts +290 -0
- package/src/diagnostics.ts +92 -0
- package/src/doctor-check.ts +10 -0
- package/src/errors.ts +1 -0
- package/src/fingerprint.ts +120 -0
- package/src/fleet-config.ts +174 -0
- package/src/input-hashes.ts +73 -0
- package/src/machine/local-source.ts +52 -0
- package/src/machine/lock-error.ts +2 -0
- package/src/machine/platform.ts +44 -0
- package/src/machine/source.ts +68 -0
- package/src/machine/state.ts +173 -0
- package/src/machine-health.ts +95 -0
- package/src/managed-source.ts +47 -0
- package/src/manager-failure.ts +48 -0
- package/src/manager-sync.ts +405 -0
- package/src/manager.ts +62 -0
- package/src/monitoring/agent-evidence.ts +146 -0
- package/src/monitoring/apply.ts +183 -0
- package/src/monitoring/beszel.ts +198 -0
- package/src/monitoring/enroll.ts +111 -0
- package/src/monitoring/hub-access.ts +114 -0
- package/src/monitoring/hub-backup.ts +121 -0
- package/src/monitoring/hub-viewer.ts +142 -0
- package/src/monitoring/hub.ts +93 -0
- package/src/monitoring/manager-agent.ts +73 -0
- package/src/monitoring/manager-hub.ts +248 -0
- package/src/monitoring/remote.ts +65 -0
- package/src/network-definition.ts +83 -0
- package/src/node-converge-linux.ts +374 -0
- package/src/node-converge-shared.ts +83 -0
- package/src/node-converge.ts +40 -0
- package/src/onboard.ts +35 -0
- package/src/package-info.ts +45 -0
- package/src/platform/ansible-receipt.ts +128 -0
- package/src/platform/ansible.ts +66 -0
- package/src/platform/darwin.ts +203 -0
- package/src/platform/detect.ts +11 -0
- package/src/platform/fallback.ts +110 -0
- package/src/platform/index.ts +38 -0
- package/src/platform/launchd.ts +98 -0
- package/src/platform/linux.ts +139 -0
- package/src/platform/ps.ts +45 -0
- package/src/platform/resource-verify.ts +132 -0
- package/src/platform/systemd-user.ts +95 -0
- package/src/platform/t3-policy.ts +137 -0
- package/src/platform/t3-route.ts +68 -0
- package/src/platform/t3-service.ts +75 -0
- package/src/platform/types.ts +123 -0
- package/src/platform/ubuntu-audit.ts +66 -0
- package/src/platform/ubuntu-verify.ts +105 -0
- package/src/platform/windows.ts +148 -0
- package/src/power-recovery.ts +55 -0
- package/src/probe.ts +51 -0
- package/src/protocol.ts +119 -0
- package/src/proxy-converge.ts +85 -0
- package/src/remote-bootstrap.ts +210 -0
- package/src/remote-entry.ts +89 -0
- package/src/remote-lock.ts +12 -0
- package/src/repair-catalog.ts +206 -0
- package/src/repair-plan.ts +78 -0
- package/src/repair-runner.ts +84 -0
- package/src/report.ts +71 -0
- package/src/runner.ts +104 -0
- package/src/secrets.ts +169 -0
- package/src/service-auth.ts +115 -0
- package/src/ssh.ts +135 -0
- package/src/state/bucket.ts +208 -0
- package/src/state/check.ts +74 -0
- package/src/state/connection.ts +92 -0
- package/src/state/enrollment.ts +45 -0
- package/src/state/legacy.ts +89 -0
- package/src/state/litestream.ts +143 -0
- package/src/state/operator.ts +20 -0
- package/src/state/paths.ts +40 -0
- package/src/state/roster.ts +62 -0
- package/src/state/store.ts +169 -0
- package/src/state/sync.ts +352 -0
- package/src/sync.ts +104 -0
- package/src/t3/accounts.ts +81 -0
- package/src/t3/desired.ts +165 -0
- package/src/t3/files.ts +81 -0
- package/src/t3/names-check.ts +53 -0
- package/src/t3/names.ts +155 -0
- package/src/t3/plan.ts +82 -0
- package/src/t3/providers.ts +74 -0
- package/src/t3/reconcile.ts +123 -0
- package/src/t3-endpoint.ts +104 -0
- package/src/t3-linked-check.ts +123 -0
- package/src/tailnet-health.ts +231 -0
- package/src/tailnet-matching.ts +37 -0
- package/src/tailscale.ts +141 -0
- package/src/toolchain-checks.ts +108 -0
- package/src/topology.ts +24 -0
- package/src/transport.ts +122 -0
- package/src/types.ts +99 -0
- package/src/vault-config.ts +38 -0
- package/src/worker-access-checks.ts +168 -0
- package/src/worker-capacity.ts +34 -0
- package/src/worker-host-checks.ts +181 -0
- package/src/worker-policy.ts +64 -0
- package/src/worker-readiness.ts +64 -0
- package/src/workstation-checks.ts +29 -0
|
@@ -0,0 +1,1618 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": 1,
|
|
3
|
+
"endpoint": {
|
|
4
|
+
"key_env": "CLIPROXY_API_KEY",
|
|
5
|
+
"vault_field": "CLIPROXY_FLEET_CLIENT_API_KEY"
|
|
6
|
+
},
|
|
7
|
+
"runs": {
|
|
8
|
+
"liveness": {
|
|
9
|
+
"description": "Reachability and TLS only. No credentials, no inference.",
|
|
10
|
+
"levels": ["liveness"]
|
|
11
|
+
},
|
|
12
|
+
"readiness": {
|
|
13
|
+
"description": "Liveness plus authentication, model discovery and configured model ids. No inference.",
|
|
14
|
+
"levels": ["liveness", "readiness"]
|
|
15
|
+
},
|
|
16
|
+
"light": {
|
|
17
|
+
"description": "Readiness plus the cheapest real inference per model (frequent monitoring).",
|
|
18
|
+
"levels": ["liveness", "readiness", "e2e"],
|
|
19
|
+
"max_tier": "light",
|
|
20
|
+
"budget": {
|
|
21
|
+
"max_requests": 16,
|
|
22
|
+
"max_requests_per_model": 3,
|
|
23
|
+
"max_output_tokens": 2000,
|
|
24
|
+
"concurrency": 2
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"nightly": {
|
|
28
|
+
"description": "Comprehensive protocol, tool-calling, streaming and agent workflows per model.",
|
|
29
|
+
"levels": ["liveness", "readiness", "e2e"],
|
|
30
|
+
"max_tier": "full",
|
|
31
|
+
"budget": {
|
|
32
|
+
"max_requests": 240,
|
|
33
|
+
"max_requests_per_model": 60,
|
|
34
|
+
"max_output_tokens": 24000,
|
|
35
|
+
"concurrency": 2
|
|
36
|
+
},
|
|
37
|
+
"simulations": true
|
|
38
|
+
},
|
|
39
|
+
"manual": {
|
|
40
|
+
"description": "Everything in nightly, plus wrong-key probes and the emulator fault-injection scenarios.",
|
|
41
|
+
"levels": ["liveness", "readiness", "e2e"],
|
|
42
|
+
"max_tier": "manual",
|
|
43
|
+
"simulations": true,
|
|
44
|
+
"budget": {
|
|
45
|
+
"max_requests": 300,
|
|
46
|
+
"max_requests_per_model": 75,
|
|
47
|
+
"max_output_tokens": 36000,
|
|
48
|
+
"concurrency": 2
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"timeouts": {
|
|
53
|
+
"request_s": 60,
|
|
54
|
+
"stream_idle_s": 45,
|
|
55
|
+
"stream_deadline_s": 120,
|
|
56
|
+
"liveness_s": 15
|
|
57
|
+
},
|
|
58
|
+
"retry": {
|
|
59
|
+
"inference": 0,
|
|
60
|
+
"read_only": 1,
|
|
61
|
+
"backoff_s": 2,
|
|
62
|
+
"note": "Inference is never retried: a retry would multiply cost and hide a persistent failure."
|
|
63
|
+
},
|
|
64
|
+
"health": {
|
|
65
|
+
"abort_model_after_consecutive_failures": 4,
|
|
66
|
+
"unhealthy_if_failed_ratio": 0.5,
|
|
67
|
+
"latency_ms": {
|
|
68
|
+
"ttft_p50_warn": 20000,
|
|
69
|
+
"total_p95_warn": 90000
|
|
70
|
+
},
|
|
71
|
+
"required_model_fields": ["id"],
|
|
72
|
+
"model_consistency": true,
|
|
73
|
+
"rate_limit_backoff_s": 1800
|
|
74
|
+
},
|
|
75
|
+
"protocols": {
|
|
76
|
+
"chat": {
|
|
77
|
+
"path": "/chat/completions",
|
|
78
|
+
"token_limit_field": "max_tokens"
|
|
79
|
+
},
|
|
80
|
+
"responses": {
|
|
81
|
+
"path": "/responses",
|
|
82
|
+
"token_limit_field": "max_output_tokens"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"capability_profiles": {
|
|
86
|
+
"frontier-agent": {
|
|
87
|
+
"default": "required",
|
|
88
|
+
"optional": [
|
|
89
|
+
"*.developer",
|
|
90
|
+
"*.json_schema",
|
|
91
|
+
"*.parallel_tools",
|
|
92
|
+
"chat.finish_length",
|
|
93
|
+
"*.stream_long"
|
|
94
|
+
],
|
|
95
|
+
"unsupported": {},
|
|
96
|
+
"extra": {
|
|
97
|
+
"chat": {},
|
|
98
|
+
"responses": {}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"models": [
|
|
103
|
+
{
|
|
104
|
+
"provider": "anthropic",
|
|
105
|
+
"profile": "frontier-agent",
|
|
106
|
+
"id": "claude-opus-5-5",
|
|
107
|
+
"capabilities": {}
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"provider": "anthropic",
|
|
111
|
+
"profile": "frontier-agent",
|
|
112
|
+
"id": "claude-sonnet-5-5",
|
|
113
|
+
"capabilities": {}
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"provider": "openai",
|
|
117
|
+
"profile": "frontier-agent",
|
|
118
|
+
"extra": {
|
|
119
|
+
"chat": {
|
|
120
|
+
"reasoning_effort": "low"
|
|
121
|
+
},
|
|
122
|
+
"responses": {
|
|
123
|
+
"reasoning": {
|
|
124
|
+
"effort": "low"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"id": "gpt-6-astra",
|
|
129
|
+
"capabilities": {}
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"provider": "openai",
|
|
133
|
+
"profile": "frontier-agent",
|
|
134
|
+
"extra": {
|
|
135
|
+
"chat": {
|
|
136
|
+
"reasoning_effort": "low"
|
|
137
|
+
},
|
|
138
|
+
"responses": {
|
|
139
|
+
"reasoning": {
|
|
140
|
+
"effort": "low"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"id": "gpt-6.1-sol",
|
|
145
|
+
"capabilities": {}
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"simulated_capabilities": {
|
|
149
|
+
"routing.failover": "Request succeeds on another account after the first account's quota is exhausted.",
|
|
150
|
+
"routing.recovery": "A cooled-down account serves requests again after its cooldown elapses.",
|
|
151
|
+
"routing.model_consistency": "Every account behind a model returns the same response shape and model id.",
|
|
152
|
+
"routing.all_unavailable": "When every account is unavailable the client gets a prompt OpenAI-style error.",
|
|
153
|
+
"limits.rate_limit": "429 and Retry-After are classified and never retried by the harness.",
|
|
154
|
+
"limits.timeout": "A stalled upstream is cut off by the harness deadline.",
|
|
155
|
+
"limits.upstream_failure": "5xx and truncated streams are classified as upstream or protocol failures.",
|
|
156
|
+
"routing.affinity": "Requests in one session stay on one account while it is available.",
|
|
157
|
+
"routing.fill_first": "One account takes all traffic until its quota is exhausted; only then does the next account serve."
|
|
158
|
+
},
|
|
159
|
+
"tools": {
|
|
160
|
+
"get_weather": {
|
|
161
|
+
"description": "Get the current weather for a city.",
|
|
162
|
+
"parameters": {
|
|
163
|
+
"type": "object",
|
|
164
|
+
"properties": {
|
|
165
|
+
"city": {
|
|
166
|
+
"type": "string",
|
|
167
|
+
"minLength": 2
|
|
168
|
+
},
|
|
169
|
+
"unit": {
|
|
170
|
+
"type": "string",
|
|
171
|
+
"enum": ["celsius", "fahrenheit"]
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"required": ["city", "unit"],
|
|
175
|
+
"additionalProperties": false
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"echo_token": {
|
|
179
|
+
"description": "Record a verification token exactly as given.",
|
|
180
|
+
"parameters": {
|
|
181
|
+
"type": "object",
|
|
182
|
+
"properties": {
|
|
183
|
+
"token": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"pattern": "^[A-Z]{3}-[0-9]{4}$"
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
"required": ["token"],
|
|
189
|
+
"additionalProperties": false
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"read_file": {
|
|
193
|
+
"description": "Read a UTF-8 text file from the workspace.",
|
|
194
|
+
"parameters": {
|
|
195
|
+
"type": "object",
|
|
196
|
+
"properties": {
|
|
197
|
+
"path": {
|
|
198
|
+
"type": "string",
|
|
199
|
+
"minLength": 1
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"required": ["path"],
|
|
203
|
+
"additionalProperties": false
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
"write_file": {
|
|
207
|
+
"description": "Replace the full contents of a workspace file.",
|
|
208
|
+
"parameters": {
|
|
209
|
+
"type": "object",
|
|
210
|
+
"properties": {
|
|
211
|
+
"path": {
|
|
212
|
+
"type": "string",
|
|
213
|
+
"minLength": 1
|
|
214
|
+
},
|
|
215
|
+
"content": {
|
|
216
|
+
"type": "string",
|
|
217
|
+
"minLength": 1
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
"required": ["path", "content"],
|
|
221
|
+
"additionalProperties": false
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
"run_tests": {
|
|
225
|
+
"description": "Run the test file and report the result.",
|
|
226
|
+
"parameters": {
|
|
227
|
+
"type": "object",
|
|
228
|
+
"properties": {
|
|
229
|
+
"path": {
|
|
230
|
+
"type": "string",
|
|
231
|
+
"minLength": 1
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
"required": ["path"],
|
|
235
|
+
"additionalProperties": false
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
"checks": [
|
|
240
|
+
{
|
|
241
|
+
"id": "basic",
|
|
242
|
+
"title": "Non-streaming completion",
|
|
243
|
+
"suites": ["core"],
|
|
244
|
+
"protocols": ["chat", "responses"],
|
|
245
|
+
"tier": {
|
|
246
|
+
"chat": "light",
|
|
247
|
+
"responses": "full"
|
|
248
|
+
},
|
|
249
|
+
"severity": "critical",
|
|
250
|
+
"kind": "request",
|
|
251
|
+
"turn": {
|
|
252
|
+
"messages": [
|
|
253
|
+
{
|
|
254
|
+
"role": "user",
|
|
255
|
+
"content": "Reply with the single word OK."
|
|
256
|
+
}
|
|
257
|
+
],
|
|
258
|
+
"max_tokens": 256
|
|
259
|
+
},
|
|
260
|
+
"expect": {
|
|
261
|
+
"finish_reason": ["stop"],
|
|
262
|
+
"content": {
|
|
263
|
+
"non_empty": true
|
|
264
|
+
},
|
|
265
|
+
"usage": true
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"id": "stream",
|
|
270
|
+
"title": "Streaming completion, SSE framing and termination",
|
|
271
|
+
"suites": ["core", "cursor"],
|
|
272
|
+
"protocols": ["chat", "responses"],
|
|
273
|
+
"tier": {
|
|
274
|
+
"chat": "full",
|
|
275
|
+
"responses": "light"
|
|
276
|
+
},
|
|
277
|
+
"severity": "critical",
|
|
278
|
+
"kind": "request",
|
|
279
|
+
"turn": {
|
|
280
|
+
"messages": [
|
|
281
|
+
{
|
|
282
|
+
"role": "user",
|
|
283
|
+
"content": "Reply with the single word OK."
|
|
284
|
+
}
|
|
285
|
+
],
|
|
286
|
+
"max_tokens": 256,
|
|
287
|
+
"stream": true
|
|
288
|
+
},
|
|
289
|
+
"expect": {
|
|
290
|
+
"finish_reason": ["stop"],
|
|
291
|
+
"content": {
|
|
292
|
+
"non_empty": true
|
|
293
|
+
},
|
|
294
|
+
"stream": {
|
|
295
|
+
"min_events": 2
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"id": "stream_usage",
|
|
301
|
+
"title": "Streaming usage metadata and finish reason",
|
|
302
|
+
"suites": ["core", "cursor"],
|
|
303
|
+
"protocols": ["chat", "responses"],
|
|
304
|
+
"tier": "full",
|
|
305
|
+
"severity": "major",
|
|
306
|
+
"kind": "request",
|
|
307
|
+
"turn": {
|
|
308
|
+
"messages": [
|
|
309
|
+
{
|
|
310
|
+
"role": "user",
|
|
311
|
+
"content": "Reply with the single word OK."
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
"max_tokens": 256,
|
|
315
|
+
"stream": true,
|
|
316
|
+
"include_usage": true
|
|
317
|
+
},
|
|
318
|
+
"expect": {
|
|
319
|
+
"finish_reason": ["stop"],
|
|
320
|
+
"usage": true,
|
|
321
|
+
"stream": {
|
|
322
|
+
"min_events": 2
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"id": "multi_turn",
|
|
328
|
+
"title": "Multi-turn context preservation",
|
|
329
|
+
"suites": ["core"],
|
|
330
|
+
"protocols": ["chat", "responses"],
|
|
331
|
+
"tier": "full",
|
|
332
|
+
"severity": "major",
|
|
333
|
+
"kind": "request",
|
|
334
|
+
"turn": {
|
|
335
|
+
"messages": [
|
|
336
|
+
{
|
|
337
|
+
"role": "user",
|
|
338
|
+
"content": "Remember this code word: {codeword}. Reply only with READY."
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"role": "assistant",
|
|
342
|
+
"content": "READY"
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"role": "user",
|
|
346
|
+
"content": "What was the code word? Reply with the code word only."
|
|
347
|
+
}
|
|
348
|
+
],
|
|
349
|
+
"max_tokens": 256
|
|
350
|
+
},
|
|
351
|
+
"expect": {
|
|
352
|
+
"content": {
|
|
353
|
+
"contains": "{codeword}"
|
|
354
|
+
},
|
|
355
|
+
"finish_reason": ["stop"]
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"id": "system",
|
|
360
|
+
"title": "System instruction is honored",
|
|
361
|
+
"suites": ["core"],
|
|
362
|
+
"protocols": ["chat", "responses"],
|
|
363
|
+
"tier": "full",
|
|
364
|
+
"severity": "major",
|
|
365
|
+
"kind": "request",
|
|
366
|
+
"turn": {
|
|
367
|
+
"system": "You are a terse assistant. Always end your reply with the exact marker {marker}.",
|
|
368
|
+
"messages": [
|
|
369
|
+
{
|
|
370
|
+
"role": "user",
|
|
371
|
+
"content": "Say hello."
|
|
372
|
+
}
|
|
373
|
+
],
|
|
374
|
+
"max_tokens": 256
|
|
375
|
+
},
|
|
376
|
+
"expect": {
|
|
377
|
+
"content": {
|
|
378
|
+
"contains": "{marker}"
|
|
379
|
+
},
|
|
380
|
+
"finish_reason": ["stop"]
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
"id": "developer",
|
|
385
|
+
"title": "Developer instruction is honored",
|
|
386
|
+
"suites": ["core"],
|
|
387
|
+
"protocols": ["chat", "responses"],
|
|
388
|
+
"tier": "full",
|
|
389
|
+
"severity": "minor",
|
|
390
|
+
"kind": "request",
|
|
391
|
+
"turn": {
|
|
392
|
+
"developer": "Always end your reply with the exact marker {marker}.",
|
|
393
|
+
"messages": [
|
|
394
|
+
{
|
|
395
|
+
"role": "user",
|
|
396
|
+
"content": "Say hello."
|
|
397
|
+
}
|
|
398
|
+
],
|
|
399
|
+
"max_tokens": 256
|
|
400
|
+
},
|
|
401
|
+
"expect": {
|
|
402
|
+
"content": {
|
|
403
|
+
"contains": "{marker}"
|
|
404
|
+
},
|
|
405
|
+
"finish_reason": ["stop"]
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
"id": "tools",
|
|
410
|
+
"title": "Forced tool call with structured arguments",
|
|
411
|
+
"suites": ["core", "cursor"],
|
|
412
|
+
"protocols": ["chat", "responses"],
|
|
413
|
+
"tier": "full",
|
|
414
|
+
"severity": "critical",
|
|
415
|
+
"kind": "request",
|
|
416
|
+
"turn": {
|
|
417
|
+
"messages": [
|
|
418
|
+
{
|
|
419
|
+
"role": "user",
|
|
420
|
+
"content": "What is the weather in [[Paris]]? Use the tool with celsius."
|
|
421
|
+
}
|
|
422
|
+
],
|
|
423
|
+
"tools": ["get_weather"],
|
|
424
|
+
"tool_choice": {
|
|
425
|
+
"name": "get_weather"
|
|
426
|
+
},
|
|
427
|
+
"max_tokens": 256
|
|
428
|
+
},
|
|
429
|
+
"expect": {
|
|
430
|
+
"finish_reason": ["tool_calls", "stop"],
|
|
431
|
+
"tool_calls": {
|
|
432
|
+
"min": 1,
|
|
433
|
+
"max": 1,
|
|
434
|
+
"names": ["get_weather"],
|
|
435
|
+
"unique_ids": true,
|
|
436
|
+
"arguments_schema": "from_tools",
|
|
437
|
+
"arguments": {
|
|
438
|
+
"city": "Paris",
|
|
439
|
+
"unit": "celsius"
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
"id": "tools_stream",
|
|
446
|
+
"title": "Streaming tool call, incremental argument assembly",
|
|
447
|
+
"suites": ["core", "cursor"],
|
|
448
|
+
"protocols": ["chat", "responses"],
|
|
449
|
+
"tier": "full",
|
|
450
|
+
"severity": "critical",
|
|
451
|
+
"kind": "request",
|
|
452
|
+
"turn": {
|
|
453
|
+
"messages": [
|
|
454
|
+
{
|
|
455
|
+
"role": "user",
|
|
456
|
+
"content": "What is the weather in [[Tokyo]]? Use the tool with fahrenheit."
|
|
457
|
+
}
|
|
458
|
+
],
|
|
459
|
+
"tools": ["get_weather"],
|
|
460
|
+
"tool_choice": {
|
|
461
|
+
"name": "get_weather"
|
|
462
|
+
},
|
|
463
|
+
"max_tokens": 256,
|
|
464
|
+
"stream": true,
|
|
465
|
+
"include_usage": true
|
|
466
|
+
},
|
|
467
|
+
"expect": {
|
|
468
|
+
"finish_reason": ["tool_calls", "stop"],
|
|
469
|
+
"stream": {
|
|
470
|
+
"min_events": 2
|
|
471
|
+
},
|
|
472
|
+
"tool_calls": {
|
|
473
|
+
"min": 1,
|
|
474
|
+
"max": 1,
|
|
475
|
+
"names": ["get_weather"],
|
|
476
|
+
"unique_ids": true,
|
|
477
|
+
"arguments_schema": "from_tools",
|
|
478
|
+
"arguments": {
|
|
479
|
+
"city": "Tokyo",
|
|
480
|
+
"unit": "fahrenheit"
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
"id": "tool_roundtrip",
|
|
487
|
+
"title": "Tool call, result and final answer",
|
|
488
|
+
"suites": ["core", "cursor"],
|
|
489
|
+
"protocols": ["chat", "responses"],
|
|
490
|
+
"tier": "full",
|
|
491
|
+
"severity": "critical",
|
|
492
|
+
"kind": "roundtrip",
|
|
493
|
+
"turn": {
|
|
494
|
+
"messages": [
|
|
495
|
+
{
|
|
496
|
+
"role": "user",
|
|
497
|
+
"content": "What is the weather in [[Berlin]]? Use the tool with celsius, then report the temperature."
|
|
498
|
+
}
|
|
499
|
+
],
|
|
500
|
+
"tools": ["get_weather"],
|
|
501
|
+
"tool_choice": {
|
|
502
|
+
"name": "get_weather"
|
|
503
|
+
},
|
|
504
|
+
"max_tokens": 256
|
|
505
|
+
},
|
|
506
|
+
"expect": {
|
|
507
|
+
"tool_calls": {
|
|
508
|
+
"min": 1,
|
|
509
|
+
"max": 1,
|
|
510
|
+
"names": ["get_weather"],
|
|
511
|
+
"arguments_schema": "from_tools",
|
|
512
|
+
"arguments": {
|
|
513
|
+
"city": "Berlin"
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
"results": {
|
|
518
|
+
"get_weather": "Berlin is {marker} degrees celsius and clear."
|
|
519
|
+
},
|
|
520
|
+
"followup": {
|
|
521
|
+
"tool_choice": "none",
|
|
522
|
+
"max_tokens": 256,
|
|
523
|
+
"expect": {
|
|
524
|
+
"finish_reason": ["stop"],
|
|
525
|
+
"content": {
|
|
526
|
+
"contains": "{marker}"
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
"id": "parallel_tools",
|
|
533
|
+
"title": "Multiple tool calls in one turn, with results",
|
|
534
|
+
"suites": ["core", "cursor"],
|
|
535
|
+
"protocols": ["chat", "responses"],
|
|
536
|
+
"tier": "full",
|
|
537
|
+
"severity": "minor",
|
|
538
|
+
"kind": "roundtrip",
|
|
539
|
+
"turn": {
|
|
540
|
+
"messages": [
|
|
541
|
+
{
|
|
542
|
+
"role": "user",
|
|
543
|
+
"content": "Call get_weather once for each city: [[Paris]] and [[Tokyo]]. Use celsius. Make both calls in the same turn."
|
|
544
|
+
}
|
|
545
|
+
],
|
|
546
|
+
"tools": ["get_weather"],
|
|
547
|
+
"tool_choice": "required",
|
|
548
|
+
"parallel_tool_calls": true,
|
|
549
|
+
"max_tokens": 400
|
|
550
|
+
},
|
|
551
|
+
"expect": {
|
|
552
|
+
"tool_calls": {
|
|
553
|
+
"min": 2,
|
|
554
|
+
"names": ["get_weather"],
|
|
555
|
+
"unique_ids": true,
|
|
556
|
+
"arguments_schema": "from_tools",
|
|
557
|
+
"values": {
|
|
558
|
+
"field": "city",
|
|
559
|
+
"include": ["Paris", "Tokyo"]
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
"results": {
|
|
564
|
+
"get_weather": "Reading for the requested city: {marker} degrees."
|
|
565
|
+
},
|
|
566
|
+
"followup": {
|
|
567
|
+
"tool_choice": "none",
|
|
568
|
+
"max_tokens": 400,
|
|
569
|
+
"expect": {
|
|
570
|
+
"finish_reason": ["stop"],
|
|
571
|
+
"content": {
|
|
572
|
+
"non_empty": true
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
"id": "json_schema",
|
|
579
|
+
"title": "Structured JSON output",
|
|
580
|
+
"suites": ["core"],
|
|
581
|
+
"protocols": ["chat", "responses"],
|
|
582
|
+
"tier": "full",
|
|
583
|
+
"severity": "minor",
|
|
584
|
+
"kind": "request",
|
|
585
|
+
"turn": {
|
|
586
|
+
"messages": [
|
|
587
|
+
{
|
|
588
|
+
"role": "user",
|
|
589
|
+
"content": "Return a JSON object with city set to Rome and population_millions set to a number."
|
|
590
|
+
}
|
|
591
|
+
],
|
|
592
|
+
"json_schema": {
|
|
593
|
+
"name": "city_report",
|
|
594
|
+
"schema": {
|
|
595
|
+
"type": "object",
|
|
596
|
+
"properties": {
|
|
597
|
+
"city": {
|
|
598
|
+
"type": "string"
|
|
599
|
+
},
|
|
600
|
+
"population_millions": {
|
|
601
|
+
"type": "number",
|
|
602
|
+
"minimum": 0
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
"required": ["city", "population_millions"],
|
|
606
|
+
"additionalProperties": false
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
"max_tokens": 300
|
|
610
|
+
},
|
|
611
|
+
"expect": {
|
|
612
|
+
"finish_reason": ["stop"],
|
|
613
|
+
"json": {
|
|
614
|
+
"schema": {
|
|
615
|
+
"type": "object",
|
|
616
|
+
"properties": {
|
|
617
|
+
"city": {
|
|
618
|
+
"type": "string",
|
|
619
|
+
"const": "Rome"
|
|
620
|
+
},
|
|
621
|
+
"population_millions": {
|
|
622
|
+
"type": "number",
|
|
623
|
+
"minimum": 0
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
"required": ["city", "population_millions"]
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
"id": "finish_length",
|
|
633
|
+
"title": "Output limit reports finish_reason length",
|
|
634
|
+
"suites": ["core"],
|
|
635
|
+
"protocols": ["chat"],
|
|
636
|
+
"tier": "full",
|
|
637
|
+
"severity": "minor",
|
|
638
|
+
"kind": "request",
|
|
639
|
+
"turn": {
|
|
640
|
+
"messages": [
|
|
641
|
+
{
|
|
642
|
+
"role": "user",
|
|
643
|
+
"content": "Write the numbers from 1 to 200 separated by spaces."
|
|
644
|
+
}
|
|
645
|
+
],
|
|
646
|
+
"max_tokens": 16
|
|
647
|
+
},
|
|
648
|
+
"expect": {
|
|
649
|
+
"finish_reason": ["length"]
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
"id": "agent_workflow",
|
|
654
|
+
"title": "Multi-step coding-agent workflow (read, edit, test, report)",
|
|
655
|
+
"suites": ["core", "cursor"],
|
|
656
|
+
"protocols": ["chat", "responses"],
|
|
657
|
+
"tier": "full",
|
|
658
|
+
"severity": "major",
|
|
659
|
+
"kind": "agent",
|
|
660
|
+
"system": "You are a coding agent. Use the provided tools. Make exactly one tool call per turn.",
|
|
661
|
+
"task": "The file calc.py has a bug: add(a, b) must return the sum. Read calc.py, fix it with write_file, run the tests in test_calc.py, then report the outcome in one sentence.",
|
|
662
|
+
"tools": ["read_file", "write_file", "run_tests"],
|
|
663
|
+
"files": {
|
|
664
|
+
"calc.py": "def add(a, b):\n return a - b\n",
|
|
665
|
+
"test_calc.py": "from calc import add\nassert add(1, 2) == 3\n"
|
|
666
|
+
},
|
|
667
|
+
"steps": [
|
|
668
|
+
{
|
|
669
|
+
"force": "read_file",
|
|
670
|
+
"arguments": {
|
|
671
|
+
"path": "calc.py"
|
|
672
|
+
}
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
"force": "write_file",
|
|
676
|
+
"arguments": {
|
|
677
|
+
"path": "calc.py",
|
|
678
|
+
"content": {
|
|
679
|
+
"regex": "a\\s*\\+\\s*b"
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
"force": "run_tests",
|
|
685
|
+
"arguments": {
|
|
686
|
+
"path": "test_calc.py"
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
],
|
|
690
|
+
"handlers": {
|
|
691
|
+
"read_file": {
|
|
692
|
+
"op": "read"
|
|
693
|
+
},
|
|
694
|
+
"write_file": {
|
|
695
|
+
"op": "write"
|
|
696
|
+
},
|
|
697
|
+
"run_tests": {
|
|
698
|
+
"op": "run",
|
|
699
|
+
"path": "calc.py",
|
|
700
|
+
"needle": "+",
|
|
701
|
+
"pass": "1 passed",
|
|
702
|
+
"fail": "FAILED: add(1, 2) returned -1"
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
"final": {
|
|
706
|
+
"tool_choice": "none",
|
|
707
|
+
"max_tokens": 300,
|
|
708
|
+
"expect": {
|
|
709
|
+
"finish_reason": ["stop"],
|
|
710
|
+
"content": {
|
|
711
|
+
"non_empty": true
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
"max_tokens": 400
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
"id": "stream_long",
|
|
719
|
+
"title": "Long-running stream stays live and terminates",
|
|
720
|
+
"suites": ["core", "cursor"],
|
|
721
|
+
"protocols": ["chat", "responses"],
|
|
722
|
+
"tier": "full",
|
|
723
|
+
"severity": "minor",
|
|
724
|
+
"kind": "request",
|
|
725
|
+
"turn": {
|
|
726
|
+
"messages": [
|
|
727
|
+
{
|
|
728
|
+
"role": "user",
|
|
729
|
+
"content": "Count from 1 to 40, one number per line, with no other text."
|
|
730
|
+
}
|
|
731
|
+
],
|
|
732
|
+
"max_tokens": 300,
|
|
733
|
+
"stream": true,
|
|
734
|
+
"include_usage": true
|
|
735
|
+
},
|
|
736
|
+
"expect": {
|
|
737
|
+
"finish_reason": ["stop"],
|
|
738
|
+
"content": {
|
|
739
|
+
"non_empty": true
|
|
740
|
+
},
|
|
741
|
+
"stream": {
|
|
742
|
+
"min_events": 10,
|
|
743
|
+
"max_gap_s": 30
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
"id": "cancel_recover",
|
|
749
|
+
"title": "Client cancels a stream, then the next request succeeds",
|
|
750
|
+
"suites": ["core", "cursor"],
|
|
751
|
+
"protocols": ["chat", "responses"],
|
|
752
|
+
"tier": "full",
|
|
753
|
+
"severity": "major",
|
|
754
|
+
"kind": "cancel_recover",
|
|
755
|
+
"turn": {
|
|
756
|
+
"messages": [
|
|
757
|
+
{
|
|
758
|
+
"role": "user",
|
|
759
|
+
"content": "Count from 1 to 200, one number per line, with no other text."
|
|
760
|
+
}
|
|
761
|
+
],
|
|
762
|
+
"max_tokens": 200,
|
|
763
|
+
"stream": true
|
|
764
|
+
},
|
|
765
|
+
"cancel_after_frames": 3,
|
|
766
|
+
"recover": {
|
|
767
|
+
"messages": [
|
|
768
|
+
{
|
|
769
|
+
"role": "user",
|
|
770
|
+
"content": "Reply with the single word OK."
|
|
771
|
+
}
|
|
772
|
+
],
|
|
773
|
+
"max_tokens": 256
|
|
774
|
+
},
|
|
775
|
+
"expect": {
|
|
776
|
+
"finish_reason": ["stop"],
|
|
777
|
+
"content": {
|
|
778
|
+
"non_empty": true
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
"id": "concurrency_isolation",
|
|
784
|
+
"title": "Concurrent requests do not cross-contaminate",
|
|
785
|
+
"suites": ["core"],
|
|
786
|
+
"protocols": ["chat", "responses"],
|
|
787
|
+
"tier": "full",
|
|
788
|
+
"severity": "major",
|
|
789
|
+
"kind": "concurrent",
|
|
790
|
+
"parallel": 3,
|
|
791
|
+
"turn": {
|
|
792
|
+
"messages": [
|
|
793
|
+
{
|
|
794
|
+
"role": "user",
|
|
795
|
+
"content": "Record the verification token [[{nonce}]] with the tool."
|
|
796
|
+
}
|
|
797
|
+
],
|
|
798
|
+
"tools": ["echo_token"],
|
|
799
|
+
"tool_choice": {
|
|
800
|
+
"name": "echo_token"
|
|
801
|
+
},
|
|
802
|
+
"max_tokens": 128
|
|
803
|
+
},
|
|
804
|
+
"expect": {
|
|
805
|
+
"tool_calls": {
|
|
806
|
+
"min": 1,
|
|
807
|
+
"max": 1,
|
|
808
|
+
"names": ["echo_token"],
|
|
809
|
+
"arguments_schema": "from_tools",
|
|
810
|
+
"arguments": {
|
|
811
|
+
"token": "{nonce}"
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
"id": "errors_unknown_model",
|
|
818
|
+
"title": "Unknown model returns an OpenAI-style error",
|
|
819
|
+
"suites": ["core"],
|
|
820
|
+
"protocols": ["chat", "responses"],
|
|
821
|
+
"tier": "full",
|
|
822
|
+
"severity": "major",
|
|
823
|
+
"kind": "error_probe",
|
|
824
|
+
"model": "model-health-nonexistent",
|
|
825
|
+
"turn": {
|
|
826
|
+
"messages": [
|
|
827
|
+
{
|
|
828
|
+
"role": "user",
|
|
829
|
+
"content": "x"
|
|
830
|
+
}
|
|
831
|
+
],
|
|
832
|
+
"max_tokens": 16
|
|
833
|
+
},
|
|
834
|
+
"expect": {
|
|
835
|
+
"status": [400, 404, 422],
|
|
836
|
+
"error_envelope": true
|
|
837
|
+
},
|
|
838
|
+
"spends_inference": false
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
"id": "errors_malformed",
|
|
842
|
+
"title": "Malformed JSON body returns an OpenAI-style 400",
|
|
843
|
+
"suites": ["core"],
|
|
844
|
+
"protocols": ["chat", "responses"],
|
|
845
|
+
"tier": "full",
|
|
846
|
+
"severity": "major",
|
|
847
|
+
"kind": "error_probe",
|
|
848
|
+
"raw_body": "{\"model\": ",
|
|
849
|
+
"expect": {
|
|
850
|
+
"status": [400, 422],
|
|
851
|
+
"error_envelope": true
|
|
852
|
+
},
|
|
853
|
+
"spends_inference": false
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
"id": "errors_missing_input",
|
|
857
|
+
"title": "Missing messages/input returns an OpenAI-style 400",
|
|
858
|
+
"suites": ["core"],
|
|
859
|
+
"protocols": ["chat", "responses"],
|
|
860
|
+
"tier": "full",
|
|
861
|
+
"severity": "minor",
|
|
862
|
+
"kind": "error_probe",
|
|
863
|
+
"body": {},
|
|
864
|
+
"expect": {
|
|
865
|
+
"status": [400, 422],
|
|
866
|
+
"error_envelope": true
|
|
867
|
+
},
|
|
868
|
+
"spends_inference": false
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
"id": "invalid_key",
|
|
872
|
+
"title": "Wrong API key is rejected",
|
|
873
|
+
"suites": ["core"],
|
|
874
|
+
"protocols": ["chat"],
|
|
875
|
+
"tier": "manual",
|
|
876
|
+
"severity": "major",
|
|
877
|
+
"kind": "error_probe",
|
|
878
|
+
"key": "invalid-model-health-probe-key-0000000000",
|
|
879
|
+
"turn": {
|
|
880
|
+
"messages": [
|
|
881
|
+
{
|
|
882
|
+
"role": "user",
|
|
883
|
+
"content": "x"
|
|
884
|
+
}
|
|
885
|
+
],
|
|
886
|
+
"max_tokens": 16
|
|
887
|
+
},
|
|
888
|
+
"expect": {
|
|
889
|
+
"status": [401, 403]
|
|
890
|
+
},
|
|
891
|
+
"spends_inference": false
|
|
892
|
+
}
|
|
893
|
+
],
|
|
894
|
+
"cursor": {
|
|
895
|
+
"note": "Protocol-level compatibility only. Requests follow the public OpenAI Chat Completions and Responses specifications, which Cursor documents for custom OpenAI-compatible endpoints. Cursor's private request shaping is not reproduced and the Cursor application is not driven by this suite.",
|
|
896
|
+
"application_verified": false,
|
|
897
|
+
"documentation": [
|
|
898
|
+
"https://platform.openai.com/docs/api-reference/chat",
|
|
899
|
+
"https://platform.openai.com/docs/api-reference/responses"
|
|
900
|
+
]
|
|
901
|
+
},
|
|
902
|
+
"diagnostics": {
|
|
903
|
+
"credentials": "No client API key was available. Set CLIPROXY_API_KEY, provide an existing VAULT_TOKEN, or run on the host with an installed client key. Nothing was tested.",
|
|
904
|
+
"auth": "The client key was rejected. Confirm the fleet's proxy-client-key secret matches access.api-keys in the live config; never paste the key into logs.",
|
|
905
|
+
"not_found": "The model id is not served. Check `bun run cliproxy models` on the host and the provider account behind it.",
|
|
906
|
+
"rate_limit": "The provider account is rate limited or out of quota (HTTP 429 model_cooldown means every account for that model is cooling down). Inspect account usage in the dashboard; do not retry in a loop. Add an account or wait for the limit to reset.",
|
|
907
|
+
"timeout": "No response within the deadline. Check the host (`bun run cliproxy status`), the Cloudflare tunnel and provider status.",
|
|
908
|
+
"transport": "The connection failed (DNS, TLS, tunnel or host down). Run the liveness profile first.",
|
|
909
|
+
"upstream": "The proxy returned a 5xx or reported a provider failure. Check `bun run cliproxy logs` for the model's channel and the provider's status page.",
|
|
910
|
+
"protocol": "The response violates the wire contract. Compare the failing check's detail with the protocol validators; this can follow a CLIProxyAPI upgrade.",
|
|
911
|
+
"assertion": "The response was well-formed but did not satisfy the expected invariant. Re-run the single check manually; if it is stable, the model behavior changed.",
|
|
912
|
+
"request_rejected": "The proxy or provider rejected a valid request (HTTP 400/422). If the capability is genuinely unsupported, declare it optional or unsupported in models.json.",
|
|
913
|
+
"budget": "The request or token budget was exhausted before this check ran.",
|
|
914
|
+
"aborted": "Remaining checks were skipped after repeated consecutive failures to bound spend.",
|
|
915
|
+
"harness": "The test harness itself raised an error; this is a bug in the checks, not the endpoint.",
|
|
916
|
+
"model_missing": "The model id is not in /v1/models, so no inference was attempted for it. Compare `bun run cliproxy models` with models.json; the provider account may be unauthenticated or the model retired."
|
|
917
|
+
},
|
|
918
|
+
"simulations": [
|
|
919
|
+
{
|
|
920
|
+
"id": "failover_after_quota",
|
|
921
|
+
"capability": "routing.failover",
|
|
922
|
+
"title": "First account's quota is exhausted; the request fails over to the second",
|
|
923
|
+
"accounts": [
|
|
924
|
+
{
|
|
925
|
+
"id": "acct-a",
|
|
926
|
+
"quota": 0
|
|
927
|
+
},
|
|
928
|
+
{
|
|
929
|
+
"id": "acct-b"
|
|
930
|
+
}
|
|
931
|
+
],
|
|
932
|
+
"steps": [
|
|
933
|
+
{
|
|
934
|
+
"request": {
|
|
935
|
+
"protocol": "chat",
|
|
936
|
+
"turn": {
|
|
937
|
+
"messages": [
|
|
938
|
+
{
|
|
939
|
+
"role": "user",
|
|
940
|
+
"content": "Reply with the single word OK."
|
|
941
|
+
}
|
|
942
|
+
],
|
|
943
|
+
"max_tokens": 64
|
|
944
|
+
}
|
|
945
|
+
},
|
|
946
|
+
"count": 1,
|
|
947
|
+
"expect": {
|
|
948
|
+
"status": 200,
|
|
949
|
+
"served_by_all": ["acct-b"]
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
"request": {
|
|
954
|
+
"protocol": "chat",
|
|
955
|
+
"turn": {
|
|
956
|
+
"messages": [
|
|
957
|
+
{
|
|
958
|
+
"role": "user",
|
|
959
|
+
"content": "Reply with the single word OK."
|
|
960
|
+
}
|
|
961
|
+
],
|
|
962
|
+
"max_tokens": 64
|
|
963
|
+
}
|
|
964
|
+
},
|
|
965
|
+
"count": 3,
|
|
966
|
+
"expect": {
|
|
967
|
+
"status": 200,
|
|
968
|
+
"served_by_all": ["acct-b"]
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
]
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
"id": "fill_first_drains_one_account_before_the_next",
|
|
975
|
+
"capability": "routing.fill_first",
|
|
976
|
+
"title": "Three accounts with limited quota are used strictly in order: all of the first, then the second, then the third",
|
|
977
|
+
"accounts": [
|
|
978
|
+
{
|
|
979
|
+
"id": "acct-a",
|
|
980
|
+
"quota": 3
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
"id": "acct-b",
|
|
984
|
+
"quota": 3
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
"id": "acct-c"
|
|
988
|
+
}
|
|
989
|
+
],
|
|
990
|
+
"steps": [
|
|
991
|
+
{
|
|
992
|
+
"request": {
|
|
993
|
+
"protocol": "chat"
|
|
994
|
+
},
|
|
995
|
+
"count": 8,
|
|
996
|
+
"expect": {
|
|
997
|
+
"status": 200,
|
|
998
|
+
"served_sequence": [
|
|
999
|
+
"acct-a",
|
|
1000
|
+
"acct-a",
|
|
1001
|
+
"acct-a",
|
|
1002
|
+
"acct-b",
|
|
1003
|
+
"acct-b",
|
|
1004
|
+
"acct-b",
|
|
1005
|
+
"acct-c",
|
|
1006
|
+
"acct-c"
|
|
1007
|
+
]
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
]
|
|
1011
|
+
},
|
|
1012
|
+
{
|
|
1013
|
+
"id": "fill_first_does_not_spread_load",
|
|
1014
|
+
"capability": "routing.fill_first",
|
|
1015
|
+
"title": "With capacity available, every new session goes to the first account and the others stay idle",
|
|
1016
|
+
"accounts": [
|
|
1017
|
+
{
|
|
1018
|
+
"id": "acct-a"
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
"id": "acct-b"
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
"id": "acct-c"
|
|
1025
|
+
}
|
|
1026
|
+
],
|
|
1027
|
+
"steps": [
|
|
1028
|
+
{
|
|
1029
|
+
"request": {
|
|
1030
|
+
"protocol": "chat"
|
|
1031
|
+
},
|
|
1032
|
+
"count": 6,
|
|
1033
|
+
"expect": {
|
|
1034
|
+
"status": 200,
|
|
1035
|
+
"served_by_all": ["acct-a"]
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
]
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
"id": "fill_first_returns_to_the_first_account_after_it_recovers",
|
|
1042
|
+
"capability": "routing.fill_first",
|
|
1043
|
+
"title": "After the first account's quota returns and its cooldown ends, new sessions fill it again before the second",
|
|
1044
|
+
"accounts": [
|
|
1045
|
+
{
|
|
1046
|
+
"id": "acct-a",
|
|
1047
|
+
"quota": 1
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
"id": "acct-b"
|
|
1051
|
+
}
|
|
1052
|
+
],
|
|
1053
|
+
"steps": [
|
|
1054
|
+
{
|
|
1055
|
+
"request": {
|
|
1056
|
+
"protocol": "chat"
|
|
1057
|
+
},
|
|
1058
|
+
"count": 2,
|
|
1059
|
+
"expect": {
|
|
1060
|
+
"status": 200,
|
|
1061
|
+
"served_sequence": ["acct-a", "acct-b"]
|
|
1062
|
+
}
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
"action": "account",
|
|
1066
|
+
"id": "acct-a",
|
|
1067
|
+
"quota": null
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
"action": "advance",
|
|
1071
|
+
"seconds": 31
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
"request": {
|
|
1075
|
+
"protocol": "chat"
|
|
1076
|
+
},
|
|
1077
|
+
"count": 3,
|
|
1078
|
+
"expect": {
|
|
1079
|
+
"status": 200,
|
|
1080
|
+
"served_by_all": ["acct-a"]
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
]
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
"id": "transient_failure_cools_the_account_then_it_rejoins",
|
|
1087
|
+
"capability": "routing.recovery",
|
|
1088
|
+
"title": "A transient upstream error sends the request to the next account, keeps the failed one cooling for 60s, then it serves again",
|
|
1089
|
+
"accounts": [
|
|
1090
|
+
{
|
|
1091
|
+
"id": "acct-a",
|
|
1092
|
+
"fail_next": 1
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
"id": "acct-b"
|
|
1096
|
+
}
|
|
1097
|
+
],
|
|
1098
|
+
"steps": [
|
|
1099
|
+
{
|
|
1100
|
+
"request": {
|
|
1101
|
+
"protocol": "chat"
|
|
1102
|
+
},
|
|
1103
|
+
"count": 2,
|
|
1104
|
+
"expect": {
|
|
1105
|
+
"status": 200,
|
|
1106
|
+
"served_by_all": ["acct-b"]
|
|
1107
|
+
}
|
|
1108
|
+
},
|
|
1109
|
+
{
|
|
1110
|
+
"action": "advance",
|
|
1111
|
+
"seconds": 61
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
"request": {
|
|
1115
|
+
"protocol": "chat"
|
|
1116
|
+
},
|
|
1117
|
+
"count": 1,
|
|
1118
|
+
"expect": {
|
|
1119
|
+
"status": 200,
|
|
1120
|
+
"served_by_all": ["acct-a"]
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
]
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
"id": "session_binding_survives_recovery_and_rebinds_on_failover",
|
|
1127
|
+
"capability": "routing.affinity",
|
|
1128
|
+
"title": "A session stays on its account, moves when that account is exhausted, and keeps the new binding after the first account recovers",
|
|
1129
|
+
"accounts": [
|
|
1130
|
+
{
|
|
1131
|
+
"id": "acct-a",
|
|
1132
|
+
"quota": 2
|
|
1133
|
+
},
|
|
1134
|
+
{
|
|
1135
|
+
"id": "acct-b"
|
|
1136
|
+
}
|
|
1137
|
+
],
|
|
1138
|
+
"steps": [
|
|
1139
|
+
{
|
|
1140
|
+
"request": {
|
|
1141
|
+
"protocol": "chat",
|
|
1142
|
+
"session": "same"
|
|
1143
|
+
},
|
|
1144
|
+
"count": 2,
|
|
1145
|
+
"expect": {
|
|
1146
|
+
"status": 200,
|
|
1147
|
+
"served_by_all": ["acct-a"]
|
|
1148
|
+
}
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
"request": {
|
|
1152
|
+
"protocol": "chat",
|
|
1153
|
+
"session": "same"
|
|
1154
|
+
},
|
|
1155
|
+
"count": 1,
|
|
1156
|
+
"expect": {
|
|
1157
|
+
"status": 200,
|
|
1158
|
+
"served_by_all": ["acct-b"]
|
|
1159
|
+
}
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
"action": "account",
|
|
1163
|
+
"id": "acct-a",
|
|
1164
|
+
"quota": null
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
"action": "advance",
|
|
1168
|
+
"seconds": 31
|
|
1169
|
+
},
|
|
1170
|
+
{
|
|
1171
|
+
"request": {
|
|
1172
|
+
"protocol": "chat",
|
|
1173
|
+
"session": "same"
|
|
1174
|
+
},
|
|
1175
|
+
"count": 2,
|
|
1176
|
+
"expect": {
|
|
1177
|
+
"status": 200,
|
|
1178
|
+
"served_by_all": ["acct-b"]
|
|
1179
|
+
}
|
|
1180
|
+
},
|
|
1181
|
+
{
|
|
1182
|
+
"request": {
|
|
1183
|
+
"protocol": "chat"
|
|
1184
|
+
},
|
|
1185
|
+
"count": 1,
|
|
1186
|
+
"expect": {
|
|
1187
|
+
"status": 200,
|
|
1188
|
+
"served_by_all": ["acct-a"]
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
]
|
|
1192
|
+
},
|
|
1193
|
+
{
|
|
1194
|
+
"id": "recovery_after_quota_cooldown",
|
|
1195
|
+
"capability": "routing.recovery",
|
|
1196
|
+
"title": "A cooled-down account rejoins routing once its cooldown elapses and is filled first again",
|
|
1197
|
+
"accounts": [
|
|
1198
|
+
{
|
|
1199
|
+
"id": "acct-a",
|
|
1200
|
+
"quota": 0
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
"id": "acct-b"
|
|
1204
|
+
}
|
|
1205
|
+
],
|
|
1206
|
+
"steps": [
|
|
1207
|
+
{
|
|
1208
|
+
"request": {
|
|
1209
|
+
"protocol": "chat",
|
|
1210
|
+
"turn": {
|
|
1211
|
+
"messages": [
|
|
1212
|
+
{
|
|
1213
|
+
"role": "user",
|
|
1214
|
+
"content": "Reply with the single word OK."
|
|
1215
|
+
}
|
|
1216
|
+
],
|
|
1217
|
+
"max_tokens": 64
|
|
1218
|
+
}
|
|
1219
|
+
},
|
|
1220
|
+
"count": 1,
|
|
1221
|
+
"expect": {
|
|
1222
|
+
"status": 200,
|
|
1223
|
+
"served_by_all": ["acct-b"]
|
|
1224
|
+
}
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
"action": "account",
|
|
1228
|
+
"id": "acct-a",
|
|
1229
|
+
"quota": null
|
|
1230
|
+
},
|
|
1231
|
+
{
|
|
1232
|
+
"action": "advance",
|
|
1233
|
+
"seconds": 31
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
"request": {
|
|
1237
|
+
"protocol": "chat"
|
|
1238
|
+
},
|
|
1239
|
+
"count": 2,
|
|
1240
|
+
"expect": {
|
|
1241
|
+
"status": 200,
|
|
1242
|
+
"served_by_all": ["acct-a"]
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
]
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
"id": "model_consistent_across_accounts",
|
|
1249
|
+
"capability": "routing.model_consistency",
|
|
1250
|
+
"title": "Both accounts return the same model id and valid shapes on both protocols",
|
|
1251
|
+
"accounts": [
|
|
1252
|
+
{
|
|
1253
|
+
"id": "acct-a",
|
|
1254
|
+
"quota": 2
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
"id": "acct-b"
|
|
1258
|
+
}
|
|
1259
|
+
],
|
|
1260
|
+
"steps": [
|
|
1261
|
+
{
|
|
1262
|
+
"request": {
|
|
1263
|
+
"protocol": "chat"
|
|
1264
|
+
},
|
|
1265
|
+
"count": 4,
|
|
1266
|
+
"expect": {
|
|
1267
|
+
"status": 200,
|
|
1268
|
+
"served_sequence": ["acct-a", "acct-a", "acct-b", "acct-b"],
|
|
1269
|
+
"single_model": true
|
|
1270
|
+
}
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
"action": "account",
|
|
1274
|
+
"id": "acct-a",
|
|
1275
|
+
"quota": 2
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
"action": "advance",
|
|
1279
|
+
"seconds": 31
|
|
1280
|
+
},
|
|
1281
|
+
{
|
|
1282
|
+
"request": {
|
|
1283
|
+
"protocol": "responses"
|
|
1284
|
+
},
|
|
1285
|
+
"count": 4,
|
|
1286
|
+
"expect": {
|
|
1287
|
+
"status": 200,
|
|
1288
|
+
"served_sequence": ["acct-a", "acct-a", "acct-b", "acct-b"],
|
|
1289
|
+
"single_model": true
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
]
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
"id": "all_accounts_exhausted",
|
|
1296
|
+
"capability": "routing.all_unavailable",
|
|
1297
|
+
"title": "Every account is out of quota: a prompt 429 error envelope with Retry-After",
|
|
1298
|
+
"accounts": [
|
|
1299
|
+
{
|
|
1300
|
+
"id": "acct-a",
|
|
1301
|
+
"quota": 0
|
|
1302
|
+
},
|
|
1303
|
+
{
|
|
1304
|
+
"id": "acct-b",
|
|
1305
|
+
"quota": 0
|
|
1306
|
+
}
|
|
1307
|
+
],
|
|
1308
|
+
"steps": [
|
|
1309
|
+
{
|
|
1310
|
+
"request": {
|
|
1311
|
+
"protocol": "chat",
|
|
1312
|
+
"turn": {
|
|
1313
|
+
"messages": [
|
|
1314
|
+
{
|
|
1315
|
+
"role": "user",
|
|
1316
|
+
"content": "Reply with the single word OK."
|
|
1317
|
+
}
|
|
1318
|
+
],
|
|
1319
|
+
"max_tokens": 64
|
|
1320
|
+
}
|
|
1321
|
+
},
|
|
1322
|
+
"count": 1,
|
|
1323
|
+
"expect": {
|
|
1324
|
+
"status": 429,
|
|
1325
|
+
"category": "rate_limit",
|
|
1326
|
+
"retry_after": true,
|
|
1327
|
+
"envelope": true,
|
|
1328
|
+
"max_elapsed_s": 3,
|
|
1329
|
+
"no_retry": true
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
{
|
|
1333
|
+
"request": {
|
|
1334
|
+
"protocol": "chat",
|
|
1335
|
+
"turn": {
|
|
1336
|
+
"messages": [
|
|
1337
|
+
{
|
|
1338
|
+
"role": "user",
|
|
1339
|
+
"content": "Reply with the single word OK."
|
|
1340
|
+
}
|
|
1341
|
+
],
|
|
1342
|
+
"max_tokens": 64
|
|
1343
|
+
}
|
|
1344
|
+
},
|
|
1345
|
+
"count": 1,
|
|
1346
|
+
"expect": {
|
|
1347
|
+
"status": 429,
|
|
1348
|
+
"category": "rate_limit",
|
|
1349
|
+
"envelope": true,
|
|
1350
|
+
"max_elapsed_s": 3
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
]
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
"id": "all_accounts_failing",
|
|
1357
|
+
"capability": "routing.all_unavailable",
|
|
1358
|
+
"title": "Every account is failing upstream: a prompt 5xx error envelope",
|
|
1359
|
+
"accounts": [
|
|
1360
|
+
{
|
|
1361
|
+
"id": "acct-a",
|
|
1362
|
+
"fail_next": 9
|
|
1363
|
+
},
|
|
1364
|
+
{
|
|
1365
|
+
"id": "acct-b",
|
|
1366
|
+
"fail_next": 9
|
|
1367
|
+
}
|
|
1368
|
+
],
|
|
1369
|
+
"steps": [
|
|
1370
|
+
{
|
|
1371
|
+
"request": {
|
|
1372
|
+
"protocol": "chat",
|
|
1373
|
+
"turn": {
|
|
1374
|
+
"messages": [
|
|
1375
|
+
{
|
|
1376
|
+
"role": "user",
|
|
1377
|
+
"content": "Reply with the single word OK."
|
|
1378
|
+
}
|
|
1379
|
+
],
|
|
1380
|
+
"max_tokens": 64
|
|
1381
|
+
}
|
|
1382
|
+
},
|
|
1383
|
+
"count": 1,
|
|
1384
|
+
"expect": {
|
|
1385
|
+
"status": 503,
|
|
1386
|
+
"category": "upstream",
|
|
1387
|
+
"envelope": true,
|
|
1388
|
+
"max_elapsed_s": 3
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
]
|
|
1392
|
+
},
|
|
1393
|
+
{
|
|
1394
|
+
"id": "rate_limit_is_classified_not_retried",
|
|
1395
|
+
"capability": "limits.rate_limit",
|
|
1396
|
+
"title": "A 429 with Retry-After is classified, never retried, and the next request succeeds",
|
|
1397
|
+
"accounts": [
|
|
1398
|
+
{
|
|
1399
|
+
"id": "acct-a"
|
|
1400
|
+
}
|
|
1401
|
+
],
|
|
1402
|
+
"steps": [
|
|
1403
|
+
{
|
|
1404
|
+
"action": "fault",
|
|
1405
|
+
"faults": {
|
|
1406
|
+
"status": 429,
|
|
1407
|
+
"status_remaining": 1,
|
|
1408
|
+
"retry_after": 7
|
|
1409
|
+
}
|
|
1410
|
+
},
|
|
1411
|
+
{
|
|
1412
|
+
"request": {
|
|
1413
|
+
"protocol": "chat",
|
|
1414
|
+
"turn": {
|
|
1415
|
+
"messages": [
|
|
1416
|
+
{
|
|
1417
|
+
"role": "user",
|
|
1418
|
+
"content": "Reply with the single word OK."
|
|
1419
|
+
}
|
|
1420
|
+
],
|
|
1421
|
+
"max_tokens": 64
|
|
1422
|
+
}
|
|
1423
|
+
},
|
|
1424
|
+
"count": 1,
|
|
1425
|
+
"expect": {
|
|
1426
|
+
"status": 429,
|
|
1427
|
+
"category": "rate_limit",
|
|
1428
|
+
"retry_after": true,
|
|
1429
|
+
"envelope": true,
|
|
1430
|
+
"no_retry": true
|
|
1431
|
+
}
|
|
1432
|
+
},
|
|
1433
|
+
{
|
|
1434
|
+
"request": {
|
|
1435
|
+
"protocol": "chat",
|
|
1436
|
+
"turn": {
|
|
1437
|
+
"messages": [
|
|
1438
|
+
{
|
|
1439
|
+
"role": "user",
|
|
1440
|
+
"content": "Reply with the single word OK."
|
|
1441
|
+
}
|
|
1442
|
+
],
|
|
1443
|
+
"max_tokens": 64
|
|
1444
|
+
}
|
|
1445
|
+
},
|
|
1446
|
+
"count": 1,
|
|
1447
|
+
"expect": {
|
|
1448
|
+
"status": 200
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
]
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
"id": "stalled_upstream_times_out",
|
|
1455
|
+
"capability": "limits.timeout",
|
|
1456
|
+
"title": "A stalled response is cut off at the harness deadline",
|
|
1457
|
+
"accounts": [
|
|
1458
|
+
{
|
|
1459
|
+
"id": "acct-a"
|
|
1460
|
+
}
|
|
1461
|
+
],
|
|
1462
|
+
"timeouts": {
|
|
1463
|
+
"request_s": 0.4,
|
|
1464
|
+
"stream_idle_s": 0.4,
|
|
1465
|
+
"stream_deadline_s": 1.0
|
|
1466
|
+
},
|
|
1467
|
+
"steps": [
|
|
1468
|
+
{
|
|
1469
|
+
"action": "fault",
|
|
1470
|
+
"faults": {
|
|
1471
|
+
"stall_s": 1.5
|
|
1472
|
+
}
|
|
1473
|
+
},
|
|
1474
|
+
{
|
|
1475
|
+
"request": {
|
|
1476
|
+
"protocol": "chat",
|
|
1477
|
+
"turn": {
|
|
1478
|
+
"messages": [
|
|
1479
|
+
{
|
|
1480
|
+
"role": "user",
|
|
1481
|
+
"content": "Reply with the single word OK."
|
|
1482
|
+
}
|
|
1483
|
+
],
|
|
1484
|
+
"max_tokens": 64
|
|
1485
|
+
}
|
|
1486
|
+
},
|
|
1487
|
+
"count": 1,
|
|
1488
|
+
"expect": {
|
|
1489
|
+
"category": "timeout",
|
|
1490
|
+
"max_elapsed_s": 1.2,
|
|
1491
|
+
"no_retry": true
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
]
|
|
1495
|
+
},
|
|
1496
|
+
{
|
|
1497
|
+
"id": "upstream_5xx_is_classified_not_retried",
|
|
1498
|
+
"capability": "limits.upstream_failure",
|
|
1499
|
+
"title": "A 502 is classified as an upstream failure and not retried",
|
|
1500
|
+
"accounts": [
|
|
1501
|
+
{
|
|
1502
|
+
"id": "acct-a"
|
|
1503
|
+
}
|
|
1504
|
+
],
|
|
1505
|
+
"steps": [
|
|
1506
|
+
{
|
|
1507
|
+
"action": "fault",
|
|
1508
|
+
"faults": {
|
|
1509
|
+
"status": 502,
|
|
1510
|
+
"status_remaining": 1
|
|
1511
|
+
}
|
|
1512
|
+
},
|
|
1513
|
+
{
|
|
1514
|
+
"request": {
|
|
1515
|
+
"protocol": "chat",
|
|
1516
|
+
"turn": {
|
|
1517
|
+
"messages": [
|
|
1518
|
+
{
|
|
1519
|
+
"role": "user",
|
|
1520
|
+
"content": "Reply with the single word OK."
|
|
1521
|
+
}
|
|
1522
|
+
],
|
|
1523
|
+
"max_tokens": 64
|
|
1524
|
+
}
|
|
1525
|
+
},
|
|
1526
|
+
"count": 1,
|
|
1527
|
+
"expect": {
|
|
1528
|
+
"status": 502,
|
|
1529
|
+
"category": "upstream",
|
|
1530
|
+
"envelope": true,
|
|
1531
|
+
"no_retry": true
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
]
|
|
1535
|
+
},
|
|
1536
|
+
{
|
|
1537
|
+
"id": "truncated_chat_stream_is_detected",
|
|
1538
|
+
"capability": "limits.upstream_failure",
|
|
1539
|
+
"title": "A chat stream cut off mid-response is reported as a protocol failure, not a success",
|
|
1540
|
+
"accounts": [
|
|
1541
|
+
{
|
|
1542
|
+
"id": "acct-a"
|
|
1543
|
+
}
|
|
1544
|
+
],
|
|
1545
|
+
"steps": [
|
|
1546
|
+
{
|
|
1547
|
+
"action": "fault",
|
|
1548
|
+
"faults": {
|
|
1549
|
+
"truncate_after": 2
|
|
1550
|
+
}
|
|
1551
|
+
},
|
|
1552
|
+
{
|
|
1553
|
+
"request": {
|
|
1554
|
+
"protocol": "chat",
|
|
1555
|
+
"turn": {
|
|
1556
|
+
"messages": [
|
|
1557
|
+
{
|
|
1558
|
+
"role": "user",
|
|
1559
|
+
"content": "Reply with the single word OK."
|
|
1560
|
+
}
|
|
1561
|
+
],
|
|
1562
|
+
"max_tokens": 64,
|
|
1563
|
+
"stream": true
|
|
1564
|
+
}
|
|
1565
|
+
},
|
|
1566
|
+
"count": 1,
|
|
1567
|
+
"expect": {
|
|
1568
|
+
"violation": "[DONE]",
|
|
1569
|
+
"no_retry": true
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
]
|
|
1573
|
+
},
|
|
1574
|
+
{
|
|
1575
|
+
"id": "truncated_responses_stream_is_detected",
|
|
1576
|
+
"capability": "limits.upstream_failure",
|
|
1577
|
+
"title": "A Responses stream with no terminal event is reported as a protocol failure",
|
|
1578
|
+
"accounts": [
|
|
1579
|
+
{
|
|
1580
|
+
"id": "acct-a"
|
|
1581
|
+
}
|
|
1582
|
+
],
|
|
1583
|
+
"steps": [
|
|
1584
|
+
{
|
|
1585
|
+
"action": "fault",
|
|
1586
|
+
"faults": {
|
|
1587
|
+
"truncate_after": 3
|
|
1588
|
+
}
|
|
1589
|
+
},
|
|
1590
|
+
{
|
|
1591
|
+
"request": {
|
|
1592
|
+
"protocol": "responses",
|
|
1593
|
+
"turn": {
|
|
1594
|
+
"messages": [
|
|
1595
|
+
{
|
|
1596
|
+
"role": "user",
|
|
1597
|
+
"content": "Reply with the single word OK."
|
|
1598
|
+
}
|
|
1599
|
+
],
|
|
1600
|
+
"max_tokens": 64,
|
|
1601
|
+
"stream": true
|
|
1602
|
+
}
|
|
1603
|
+
},
|
|
1604
|
+
"count": 1,
|
|
1605
|
+
"expect": {
|
|
1606
|
+
"violation": "terminal response event",
|
|
1607
|
+
"no_retry": true
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
]
|
|
1611
|
+
}
|
|
1612
|
+
],
|
|
1613
|
+
"routing": {
|
|
1614
|
+
"strategy": "fill-first",
|
|
1615
|
+
"session_affinity": true,
|
|
1616
|
+
"note": "Mirrors routing in config.template.yaml (a test enforces this). Fill-first drains one account before using the next; the live proxy config is applied separately and is not observable from outside."
|
|
1617
|
+
}
|
|
1618
|
+
}
|