@app-vitals/agent-admin 0.0.1 → 0.1.1
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/Dockerfile +43 -0
- package/README.md +72 -2
- package/e2e/agents-page.e2e.ts +338 -0
- package/e2e/chat-degraded.e2e.ts +153 -0
- package/e2e/chat-page.e2e.ts +169 -0
- package/e2e/chat-progress.e2e.ts +206 -0
- package/e2e/chat-thread-page.e2e.ts +444 -0
- package/e2e/login-page.e2e.ts +148 -0
- package/e2e/test-server.ts +427 -0
- package/openapi.json +6098 -0
- package/openapi.platform.json +5553 -0
- package/package.json +62 -4
- package/playwright.config.ts +25 -0
- package/prisma/client/browser.ts +134 -0
- package/prisma/client/client.ts +158 -0
- package/prisma/client/commonInputTypes.ts +578 -0
- package/prisma/client/enums.ts +15 -0
- package/prisma/client/internal/class.ts +424 -0
- package/prisma/client/internal/prismaNamespace.ts +2898 -0
- package/prisma/client/internal/prismaNamespaceBrowser.ts +476 -0
- package/prisma/client/models/Account.ts +1774 -0
- package/prisma/client/models/AccountInvite.ts +1386 -0
- package/prisma/client/models/AccountMember.ts +1351 -0
- package/prisma/client/models/Agent.ts +4554 -0
- package/prisma/client/models/AgentAsk.ts +1373 -0
- package/prisma/client/models/AgentChatTokenUsageDailyByModel.ts +1638 -0
- package/prisma/client/models/AgentCronJob.ts +2416 -0
- package/prisma/client/models/AgentCronRun.ts +2858 -0
- package/prisma/client/models/AgentCronRunModelBreakdown.ts +1532 -0
- package/prisma/client/models/AgentCronRunSkillUsage.ts +1642 -0
- package/prisma/client/models/AgentEnv.ts +1421 -0
- package/prisma/client/models/AgentGitHubInstallationsSnapshot.ts +1289 -0
- package/prisma/client/models/AgentMember.ts +1316 -0
- package/prisma/client/models/AgentPhaseMethodology.ts +1355 -0
- package/prisma/client/models/AgentPlugin.ts +1421 -0
- package/prisma/client/models/AgentToken.ts +1380 -0
- package/prisma/client/models/AgentTool.ts +1351 -0
- package/prisma/client/models/AgentWorkQueueSnapshot.ts +1297 -0
- package/prisma/client/models/ChatThreadWatch.ts +1183 -0
- package/prisma/client/models/PushSubscription.ts +1233 -0
- package/prisma/client/models/SessionAlertState.ts +1187 -0
- package/prisma/client/models/SessionFollow.ts +1187 -0
- package/prisma/client/models/UserNotificationPrefs.ts +1221 -0
- package/prisma/client/models.ts +34 -0
- package/prisma/migrations/20260608000000_init/migration.sql +112 -0
- package/prisma/migrations/20260616000000_agent_members/migration.sql +18 -0
- package/prisma/migrations/20260624000000_add_agent_self_hosted/migration.sql +2 -0
- package/prisma/migrations/20260624120000_add_agent_repos/migration.sql +3 -0
- package/prisma/migrations/20260625000000_add_agent_cron_run/migration.sql +26 -0
- package/prisma/migrations/20260626000000_add_cron_run_token_fields/migration.sql +10 -0
- package/prisma/migrations/20260627000000_add_agent_chat_token_usage_daily/migration.sql +18 -0
- package/prisma/migrations/20260627010000_add_agent_chat_token_usage_fk/migration.sql +2 -0
- package/prisma/migrations/20260701000000_add_chat_threads_messages/migration.sql +37 -0
- package/prisma/migrations/20260701010000_drop_chat_threads_messages/migration.sql +17 -0
- package/prisma/migrations/20260701020000_add_cron_run_model_breakdown/migration.sql +23 -0
- package/prisma/migrations/20260701020000_chat_token_by_model/migration.sql +25 -0
- package/prisma/migrations/20260701030000_add_secret_flag_to_agent_env/migration.sql +2 -0
- package/prisma/migrations/20260701030000_drop_cron_run_legacy_columns/migration.sql +5 -0
- package/prisma/migrations/20260706000000_consolidate_cron_run_token_accounting/migration.sql +54 -0
- package/prisma/migrations/20260709000000_add_phase_to_agent_cron_run/migration.sql +8 -0
- package/prisma/migrations/20260717000000_add_item_tracking_to_agent_cron_run/migration.sql +10 -0
- package/prisma/migrations/20260717010000_add_agent_work_queue_snapshot/migration.sql +20 -0
- package/prisma/migrations/20260718000000_add_cron_phase_columns/migration.sql +25 -0
- package/prisma/migrations/20260718100000_drop_agent_cron_run_phase_column/migration.sql +8 -0
- package/prisma/migrations/20260724000000_add_agent_type_name/migration.sql +3 -0
- package/prisma/migrations/20260725232433_add_agent_author_allowlist/migration.sql +2 -0
- package/prisma/migrations/20260727120000_add_not_null_to_agent_author_allowlist/migration.sql +3 -0
- package/prisma/migrations/20260731000000_add_session_id_to_agent_cron_run/migration.sql +2 -0
- package/prisma/migrations/20260821000000_add_agent_restrict_slack_to_members/migration.sql +3 -0
- package/prisma/migrations/20260901000000_add_push_subscription_and_chat_thread_watch/migration.sql +40 -0
- package/prisma/migrations/20260903000000_add_agent_patch_author_allowlist/migration.sql +2 -0
- package/prisma/migrations/20260903120000_add_agent_review_author_allowlist/migration.sql +18 -0
- package/prisma/migrations/20260903150000_drop_agent_author_allowlist/migration.sql +14 -0
- package/prisma/migrations/20260905000000_add_started_at_index_to_agent_cron_run/migration.sql +11 -0
- package/prisma/migrations/20260906000000_add_session_follow_notification_prefs_alert_state/migration.sql +47 -0
- package/prisma/migrations/20260911000000_backfill_auto_follow_since/migration.sql +16 -0
- package/prisma/migrations/20260923115814_add_last_heartbeat_at_to_agent_cron_run/migration.sql +2 -0
- package/prisma/migrations/20260924120000_add_trial_expiry_fields_to_agent/migration.sql +6 -0
- package/prisma/migrations/20260929000000_add_agent_phase_methodology/migration.sql +21 -0
- package/prisma/migrations/20260929120000_add_agent_policy_fields/migration.sql +15 -0
- package/prisma/migrations/20261005000000_add_agent_github_installations_snapshot/migration.sql +21 -0
- package/prisma/migrations/20261008000000_add_cron_run_telemetry/migration.sql +50 -0
- package/prisma/migrations/20261008010000_add_account_models/migration.sql +48 -0
- package/prisma/migrations/20261008020000_add_cron_lockdown_disabled_at/migration.sql +3 -0
- package/prisma/migrations/20261009000000_chat_token_usage_bigint/migration.sql +7 -0
- package/prisma/migrations/20261010000000_add_cron_run_command_variant/migration.sql +2 -0
- package/prisma/migrations/20261019000000_add_agent_anthropic_identity/migration.sql +9 -0
- package/prisma/migrations/20261025000000_add_agent_ask/migration.sql +23 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema/coding.prisma +85 -0
- package/prisma/schema/schema.prisma +506 -0
- package/prisma.config.ts +34 -0
- package/pwa-assets/icons/apple-touch-icon.png +0 -0
- package/pwa-assets/icons/favicon-32.png +0 -0
- package/pwa-assets/icons/icon-192.png +0 -0
- package/pwa-assets/icons/icon-512.png +0 -0
- package/pwa-assets/icons/icon-maskable-192.png +0 -0
- package/pwa-assets/icons/icon-maskable-512.png +0 -0
- package/src/account-agent-create.ts +42 -0
- package/src/account-agent-create.unit.test.ts +28 -0
- package/src/account-created-notifier.ts +71 -0
- package/src/account-created-notifier.unit.test.ts +70 -0
- package/src/account-email.ts +9 -0
- package/src/account-email.unit.test.ts +21 -0
- package/src/account-invites.ts +70 -0
- package/src/account-lifecycle.integration.test.ts +329 -0
- package/src/account-lifecycle.ts +206 -0
- package/src/account-lifecycle.unit.test.ts +108 -0
- package/src/account-members.ts +128 -0
- package/src/account-onboarding.integration.test.ts +101 -0
- package/src/account-onboarding.ts +95 -0
- package/src/account-trial-expiry-sweeper.ts +85 -0
- package/src/account-trial-warning-sweeper.integration.test.ts +228 -0
- package/src/account-trial-warning-sweeper.ts +216 -0
- package/src/accounts-api.integration.test.ts +196 -0
- package/src/accounts-api.smoke.test.ts +292 -0
- package/src/accounts-api.ts +289 -0
- package/src/accounts-api.unit.test.ts +93 -0
- package/src/accounts.integration.test.ts +178 -0
- package/src/accounts.ts +208 -0
- package/src/admin-boot-config.ts +574 -0
- package/src/admin-boot-config.unit.test.ts +28 -0
- package/src/admin-boot.boot-order.unit.test.ts +91 -0
- package/src/admin-boot.ts +690 -0
- package/src/admin-dev-login.smoke.test.ts +317 -0
- package/src/admin-extension.ts +432 -0
- package/src/admin-extension.unit.test.ts +532 -0
- package/src/admin-local-agent.smoke.test.ts +1643 -0
- package/src/admin-spec.smoke.test.ts +96 -0
- package/src/admin-spec.ts +186 -0
- package/src/admin-ui-account-nav.ts +20 -0
- package/src/admin-ui-account-pages.ts +131 -0
- package/src/admin-ui-account-pages.unit.test.ts +112 -0
- package/src/admin-ui-account.smoke.test.ts +286 -0
- package/src/admin-ui-account.ts +185 -0
- package/src/admin-ui-accounts-pages.ts +202 -0
- package/src/admin-ui-accounts-pages.unit.test.ts +138 -0
- package/src/admin-ui-accounts.smoke.test.ts +434 -0
- package/src/admin-ui-accounts.ts +266 -0
- package/src/admin-ui-layout.ts +60 -0
- package/src/admin-ui-layout.unit.test.ts +140 -0
- package/src/admin-ui-pages.ts +3489 -0
- package/src/admin-ui-pages.unit.test.ts +4355 -0
- package/src/admin-ui-push-test.smoke.test.ts +125 -0
- package/src/admin-ui-push-test.ts +102 -0
- package/src/admin-ui-styles.ts +656 -0
- package/src/admin-ui-styles.unit.test.ts +103 -0
- package/src/admin-ui.fixtures.ts +457 -0
- package/src/admin-ui.smoke.test.ts +7227 -0
- package/src/admin-ui.ts +3824 -0
- package/src/agent-account-assignment.ts +49 -0
- package/src/agent-account-assignment.unit.test.ts +99 -0
- package/src/agent-anthropic-identity.integration.test.ts +99 -0
- package/src/agent-anthropic-identity.ts +78 -0
- package/src/agent-anthropic-identity.unit.test.ts +77 -0
- package/src/agent-ask-config.ts +59 -0
- package/src/agent-ask-config.unit.test.ts +70 -0
- package/src/agent-ask.integration.test.ts +175 -0
- package/src/agent-ask.smoke.test.ts +457 -0
- package/src/agent-ask.ts +378 -0
- package/src/agent-chat-tokens.integration.test.ts +420 -0
- package/src/agent-chat-tokens.smoke.test.ts +551 -0
- package/src/agent-chat-tokens.ts +259 -0
- package/src/agent-cron-jobs.integration.test.ts +582 -0
- package/src/agent-cron-jobs.ts +615 -0
- package/src/agent-cron-jobs.unit.test.ts +420 -0
- package/src/agent-cron-run-stats.integration.test.ts +1551 -0
- package/src/agent-cron-run-stats.smoke.test.ts +953 -0
- package/src/agent-cron-run-stats.ts +752 -0
- package/src/agent-cron-runs.integration.test.ts +1491 -0
- package/src/agent-cron-runs.ts +489 -0
- package/src/agent-deletion-checklist.ts +128 -0
- package/src/agent-deletion-checklist.unit.test.ts +201 -0
- package/src/agent-deletion-per-agent.integration.test.ts +323 -0
- package/src/agent-deletion.integration.test.ts +323 -0
- package/src/agent-deletion.ts +329 -0
- package/src/agent-deletion.unit.test.ts +412 -0
- package/src/agent-envs.integration.test.ts +323 -0
- package/src/agent-envs.ts +244 -0
- package/src/agent-github-installations.integration.test.ts +115 -0
- package/src/agent-github-installations.ts +52 -0
- package/src/agent-manifest.ts +685 -0
- package/src/agent-manifest.unit.test.ts +870 -0
- package/src/agent-members.ts +69 -0
- package/src/agent-members.unit.test.ts +250 -0
- package/src/agent-patch-author-allowlist.integration.test.ts +218 -0
- package/src/agent-plugins.integration.test.ts +157 -0
- package/src/agent-plugins.ts +95 -0
- package/src/agent-plugins.unit.test.ts +43 -0
- package/src/agent-provisioner-per-agent.integration.test.ts +949 -0
- package/src/agent-provisioner.integration.test.ts +1245 -0
- package/src/agent-provisioner.ts +1650 -0
- package/src/agent-provisioner.unit.test.ts +1852 -0
- package/src/agent-tokens.integration.test.ts +150 -0
- package/src/agent-tokens.ts +112 -0
- package/src/agent-tools.integration.test.ts +129 -0
- package/src/agent-tools.ts +111 -0
- package/src/agents-api.integration.test.ts +705 -0
- package/src/agents-api.sentry.smoke.test.ts +478 -0
- package/src/agents-api.smoke.test.ts +5986 -0
- package/src/agents-api.ts +2826 -0
- package/src/agents.integration.test.ts +512 -0
- package/src/agents.ts +1097 -0
- package/src/agents.unit.test.ts +1736 -0
- package/src/anthropic-credential-guard.ts +44 -0
- package/src/anthropic-credential-guard.unit.test.ts +88 -0
- package/src/anthropic-federated-token-provider.integration.test.ts +248 -0
- package/src/anthropic-federated-token-provider.ts +249 -0
- package/src/anthropic-identity-client.fixtures.ts +237 -0
- package/src/anthropic-identity-client.integration.test.ts +342 -0
- package/src/anthropic-identity-client.ts +473 -0
- package/src/anthropic-identity-fixture-spec.integration.test.ts +169 -0
- package/src/anthropic-identity-name.integration.test.ts +83 -0
- package/src/anthropic-identity-name.ts +163 -0
- package/src/anthropic-identity-name.unit.test.ts +345 -0
- package/src/anthropic-openapi-contract.fixtures.ts +192 -0
- package/src/api-auth.ts +383 -0
- package/src/api-auth.unit.test.ts +837 -0
- package/src/api.smoke.test.ts +1225 -0
- package/src/api.ts +376 -0
- package/src/attachment-validation.ts +124 -0
- package/src/attachment-validation.unit.test.ts +218 -0
- package/src/caller-scope.ts +131 -0
- package/src/caller-scope.unit.test.ts +116 -0
- package/src/chat-credential-config.ts +22 -0
- package/src/chat-credentials-logging.unit.test.ts +92 -0
- package/src/chat-markers.ts +114 -0
- package/src/chat-markers.unit.test.ts +116 -0
- package/src/chat-service-provisioning-client.integration.test.ts +412 -0
- package/src/chat-service-provisioning-client.ts +221 -0
- package/src/chat.smoke.test.ts +1875 -0
- package/src/clock.ts +47 -0
- package/src/coding-task-store-provisioning-client.integration.test.ts +372 -0
- package/src/coding-task-store-provisioning-client.ts +156 -0
- package/src/crypto.ts +83 -0
- package/src/dev-auth-guard.ts +29 -0
- package/src/dev-auth-guard.unit.test.ts +51 -0
- package/src/errors.ts +77 -0
- package/src/errors.unit.test.ts +52 -0
- package/src/fixtures/anthropic-identity-recorded-fixtures.json +850 -0
- package/src/fixtures/anthropic-openapi/SDK_VERSION +1 -0
- package/src/fixtures/anthropic-openapi/mock-spec.json.gz +0 -0
- package/src/fixtures/chat-service-provisioning-cassette.json +156 -0
- package/src/fixtures/github-app-manifest-cassette.json +44 -0
- package/src/fixtures/google-auth-cassette.json +36 -0
- package/src/fixtures/http-chat-client-cassette.json +226 -0
- package/src/fixtures/k8s-cassette.json +470 -0
- package/src/fixtures/okta-auth-cassette.json +36 -0
- package/src/fixtures/slack-provision-cassette.json +12 -0
- package/src/fixtures/slack-provisioning-http-cassette.json +98 -0
- package/src/fixtures/task-store-provisioning-cassette.json +85 -0
- package/src/github-app-provisioning-client.integration.test.ts +149 -0
- package/src/github-app-provisioning-client.ts +161 -0
- package/src/github-app-provisioning-client.unit.test.ts +114 -0
- package/src/github-provisioning-service.ts +569 -0
- package/src/github-provisioning-service.unit.test.ts +123 -0
- package/src/google-auth-client.integration.test.ts +195 -0
- package/src/google-auth-client.ts +121 -0
- package/src/http-chat-client.integration.test.ts +554 -0
- package/src/http-chat-client.ts +533 -0
- package/src/http-chat-client.unit.test.ts +119 -0
- package/src/import-boundary.unit.test.ts +240 -0
- package/src/kubernetes-client.integration.test.ts +697 -0
- package/src/kubernetes-client.ts +1027 -0
- package/src/kubernetes-client.unit.test.ts +726 -0
- package/src/main.smoke.test.ts +91 -0
- package/src/main.ts +21 -0
- package/src/main.unit.test.ts +801 -0
- package/src/okta-auth-client.integration.test.ts +263 -0
- package/src/okta-auth-client.ts +152 -0
- package/src/openapi-schemas.ts +1310 -0
- package/src/openapi-schemas.unit.test.ts +348 -0
- package/src/per-agent-identity.fixtures.ts +305 -0
- package/src/platform-doc-route.ts +40 -0
- package/src/prisma-client.integration.test.ts +57 -0
- package/src/prisma-client.ts +187 -0
- package/src/prisma-client.unit.test.ts +178 -0
- package/src/prisma-tx.ts +23 -0
- package/src/push-content.ts +177 -0
- package/src/push-content.unit.test.ts +213 -0
- package/src/push-crypto.ts +135 -0
- package/src/push-crypto.unit.test.ts +66 -0
- package/src/push-sender.ts +169 -0
- package/src/push-sender.unit.test.ts +107 -0
- package/src/push-service.ts +332 -0
- package/src/push-service.unit.test.ts +679 -0
- package/src/push-toggle.ts +135 -0
- package/src/push-toggle.unit.test.ts +78 -0
- package/src/pwa-https-gating.smoke.test.ts +229 -0
- package/src/pwa-icons.unit.test.ts +41 -0
- package/src/pwa-routes.smoke.test.ts +276 -0
- package/src/pwa.ts +380 -0
- package/src/pwa.unit.test.ts +321 -0
- package/src/schema.unit.test.ts +122 -0
- package/src/self-serve-config.ts +52 -0
- package/src/self-serve-config.unit.test.ts +79 -0
- package/src/server.smoke.test.ts +273 -0
- package/src/server.ts +50 -0
- package/src/service-integration.ts +67 -0
- package/src/service-integration.unit.test.ts +129 -0
- package/src/slack-provisioning-client.integration.test.ts +308 -0
- package/src/slack-provisioning-client.ts +411 -0
- package/src/slack-provisioning-client.unit.test.ts +80 -0
- package/src/slack-provisioning-service.integration.test.ts +637 -0
- package/src/slack-provisioning-service.ts +451 -0
- package/src/slack-provisioning.integration.test.ts +395 -0
- package/src/specialty-manifest-loader.fixture.integration.test.ts +63 -0
- package/src/specialty-manifest-loader.ts +255 -0
- package/src/specialty-manifest-loader.unit.test.ts +301 -0
- package/src/specialty-registry.schema.unit.test.ts +35 -0
- package/src/specialty-registry.ts +236 -0
- package/src/specialty-registry.unit.test.ts +272 -0
- package/src/startup-policy-reconcile.ts +77 -0
- package/src/startup-policy-reconcile.unit.test.ts +251 -0
- package/src/theme-tokens.json +12 -0
- package/src/token-crypto.ts +60 -0
- package/src/token-crypto.unit.test.ts +182 -0
- package/src/trial-expiry-sweeper.integration.test.ts +318 -0
- package/src/trial-expiry-sweeper.ts +132 -0
- package/src/trial-expiry-sweeper.unit.test.ts +391 -0
- package/src/trial-expiry-warning-sweeper.integration.test.ts +468 -0
- package/src/trial-expiry-warning-sweeper.ts +447 -0
- package/src/trial-expiry-warning-sweeper.unit.test.ts +148 -0
- package/src/voice-contract.unit.test.ts +102 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,1310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* admin/src/openapi-schemas.ts
|
|
3
|
+
* Zod schemas for the admin API — entity types, request bodies, and common
|
|
4
|
+
* error shapes. Imported by route migrations (OAS-2.1, OAS-2.2).
|
|
5
|
+
*
|
|
6
|
+
* Import z from "@hono/zod-openapi" so .openapi() metadata is available.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { isGithubLogin } from "@app-vitals/agent-lib/github-login";
|
|
10
|
+
import { isOrgRepo } from "@app-vitals/agent-lib/org-repo";
|
|
11
|
+
import { z } from "@hono/zod-openapi";
|
|
12
|
+
|
|
13
|
+
// ─── Common ───────────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
export const ErrorSchema = z
|
|
16
|
+
.object({
|
|
17
|
+
error: z.string().openapi({ example: "not found" }),
|
|
18
|
+
})
|
|
19
|
+
.openapi("Error");
|
|
20
|
+
|
|
21
|
+
export type ErrorResponse = z.infer<typeof ErrorSchema>;
|
|
22
|
+
|
|
23
|
+
export const OkSchema = z
|
|
24
|
+
.object({
|
|
25
|
+
ok: z.literal(true),
|
|
26
|
+
})
|
|
27
|
+
.openapi("Ok");
|
|
28
|
+
|
|
29
|
+
// ─── Agent ────────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
/** Minimal Agent shape for list endpoints (GET /agents). */
|
|
32
|
+
export const AgentSummarySchema = z
|
|
33
|
+
.object({
|
|
34
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
35
|
+
name: z.string().openapi({ example: "Bodhi" }),
|
|
36
|
+
selfHosted: z.boolean().openapi({ example: false }),
|
|
37
|
+
typeName: z.string().openapi({ example: "coding" }),
|
|
38
|
+
})
|
|
39
|
+
.openapi("AgentSummary");
|
|
40
|
+
|
|
41
|
+
export const PatchAgentBodySchema = z
|
|
42
|
+
.object({
|
|
43
|
+
/**
|
|
44
|
+
* Backfills agent.slackId for agents that completed Slack OAuth before
|
|
45
|
+
* UAP-1.3 shipped (it is otherwise resolved automatically via auth.test
|
|
46
|
+
* right after OAuth completes). Nullable so it can also be explicitly
|
|
47
|
+
* cleared.
|
|
48
|
+
*/
|
|
49
|
+
slackId: z
|
|
50
|
+
.string()
|
|
51
|
+
.nullable()
|
|
52
|
+
.optional()
|
|
53
|
+
.openapi({ example: "U0AALR8M69X" }),
|
|
54
|
+
selfHosted: z.boolean().optional().openapi({ example: false }),
|
|
55
|
+
repos: z
|
|
56
|
+
.array(
|
|
57
|
+
z.string().refine(isOrgRepo, {
|
|
58
|
+
message: "each repo must be in org/repo format",
|
|
59
|
+
}),
|
|
60
|
+
)
|
|
61
|
+
.optional()
|
|
62
|
+
.openapi({ example: ["my-org/my-repo"] }),
|
|
63
|
+
/**
|
|
64
|
+
* GitHub logins permitted to trigger this agent's review/dev-task work.
|
|
65
|
+
*/
|
|
66
|
+
reviewAuthorAllowlist: z
|
|
67
|
+
.array(
|
|
68
|
+
z.string().refine(isGithubLogin, {
|
|
69
|
+
message: "each entry must be a valid GitHub login",
|
|
70
|
+
}),
|
|
71
|
+
)
|
|
72
|
+
.optional()
|
|
73
|
+
.openapi({ example: ["octocat"] }),
|
|
74
|
+
/**
|
|
75
|
+
* GitHub logins permitted to trigger patch runs for this agent.
|
|
76
|
+
* Independent of reviewAuthorAllowlist.
|
|
77
|
+
*/
|
|
78
|
+
patchAuthorAllowlist: z
|
|
79
|
+
.array(
|
|
80
|
+
z.string().refine(isGithubLogin, {
|
|
81
|
+
message: "each entry must be a valid GitHub login",
|
|
82
|
+
}),
|
|
83
|
+
)
|
|
84
|
+
.optional()
|
|
85
|
+
.openapi({ example: ["octocat"] }),
|
|
86
|
+
restrictSlackToMembers: z.boolean().optional().openapi({ example: false }),
|
|
87
|
+
/**
|
|
88
|
+
* SSP-5.3: assign the agent to an account (or null to clear). Platform
|
|
89
|
+
* admin only. Does not move historical task-store rows.
|
|
90
|
+
*/
|
|
91
|
+
accountId: z
|
|
92
|
+
.string()
|
|
93
|
+
.min(1)
|
|
94
|
+
.nullable()
|
|
95
|
+
.optional()
|
|
96
|
+
.openapi({ example: "acct_123" }),
|
|
97
|
+
/**
|
|
98
|
+
* ATE-1.1: trial expiry timestamp. Nullable so it can also be explicitly
|
|
99
|
+
* cleared. trialExpiryWarnedAt is intentionally NOT part of this schema —
|
|
100
|
+
* it is written internally by ATE-2.1's warning check, not user-editable
|
|
101
|
+
* via this route.
|
|
102
|
+
*/
|
|
103
|
+
trialExpiresAt: z
|
|
104
|
+
.string()
|
|
105
|
+
.datetime()
|
|
106
|
+
.nullable()
|
|
107
|
+
.optional()
|
|
108
|
+
.openapi({ example: "2026-12-01T00:00:00.000Z" }),
|
|
109
|
+
/**
|
|
110
|
+
* APM-1.3: the six real agent-policy fields (APM-1.1 DB columns). No
|
|
111
|
+
* extra bounds validation beyond int/boolean — matches the existing
|
|
112
|
+
* looseness of this schema (e.g. no min/max on other numeric fields).
|
|
113
|
+
*/
|
|
114
|
+
autoPostReviews: z.boolean().optional().openapi({ example: true }),
|
|
115
|
+
allowSelfReview: z.boolean().optional().openapi({ example: false }),
|
|
116
|
+
minConfidence: z.number().int().optional().openapi({ example: 75 }),
|
|
117
|
+
maxFindings: z.number().int().optional().openapi({ example: 5 }),
|
|
118
|
+
cleanupMergedWorktrees: z.boolean().optional().openapi({ example: true }),
|
|
119
|
+
cleanupAfterDays: z.number().int().optional().openapi({ example: 14 }),
|
|
120
|
+
})
|
|
121
|
+
.openapi("PatchAgentBody");
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* POST /agents request body — mirrors createAgent()'s CreateAgentFormInput
|
|
125
|
+
* (admin/src/agents.ts, APA-1.1) field for field: raw, unparsed strings.
|
|
126
|
+
* createAgent() owns all of the parsing (multi-line repo/allowlist/member
|
|
127
|
+
* lists, dedup, format validation), so every optional field here stays a
|
|
128
|
+
* plain string rather than a pre-parsed array — matching exactly what the
|
|
129
|
+
* web UI form at /admin/agents/new submits.
|
|
130
|
+
*/
|
|
131
|
+
export const CreateAgentBodySchema = z
|
|
132
|
+
.object({
|
|
133
|
+
name: z.string().min(1).openapi({ example: "Bodhi" }),
|
|
134
|
+
typeName: z.string().min(1).openapi({ example: "coding" }),
|
|
135
|
+
/**
|
|
136
|
+
* "in-cluster" means K8s-provisioned; anything else (including absent)
|
|
137
|
+
* means self-hosted.
|
|
138
|
+
*/
|
|
139
|
+
runtime: z.string().optional().openapi({ example: "in-cluster" }),
|
|
140
|
+
/** Newline-separated org/repo list. */
|
|
141
|
+
reposRaw: z.string().optional().openapi({ example: "my-org/my-repo" }),
|
|
142
|
+
/** Newline-separated GitHub logins allowed to trigger review/dev-task work. */
|
|
143
|
+
authorAllowlistRaw: z.string().optional().openapi({ example: "octocat" }),
|
|
144
|
+
/** Newline-separated GitHub logins allowed to trigger patch runs. */
|
|
145
|
+
patchAuthorAllowlistRaw: z
|
|
146
|
+
.string()
|
|
147
|
+
.optional()
|
|
148
|
+
.openapi({ example: "octocat" }),
|
|
149
|
+
/** Newline-separated member emails. */
|
|
150
|
+
memberEmailsRaw: z
|
|
151
|
+
.string()
|
|
152
|
+
.optional()
|
|
153
|
+
.openapi({ example: "dev@example.com" }),
|
|
154
|
+
/** Only the literal string "true" enables restrictSlackToMembers. */
|
|
155
|
+
restrictSlackToMembersRaw: z
|
|
156
|
+
.string()
|
|
157
|
+
.optional()
|
|
158
|
+
.openapi({ example: "true" }),
|
|
159
|
+
claudeCodeOauthToken: z.string().optional(),
|
|
160
|
+
anthropicApiKey: z.string().optional(),
|
|
161
|
+
})
|
|
162
|
+
.openapi("CreateAgentBody");
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* DELETE /agents/:id request body. Entirely optional — omit it (or the
|
|
166
|
+
* xoxpToken field) to skip automatic Slack app deletion; a present Slack app
|
|
167
|
+
* then becomes a manual checklist entry instead of a hard failure.
|
|
168
|
+
*/
|
|
169
|
+
export const DeleteAgentBodySchema = z
|
|
170
|
+
.object({
|
|
171
|
+
xoxpToken: z.string().optional().openapi({ example: "xoxp-user-token" }),
|
|
172
|
+
})
|
|
173
|
+
.openapi("DeleteAgentBody");
|
|
174
|
+
|
|
175
|
+
/** A single manual operator reminder in DeleteAgentResult.manualStepsRequired. */
|
|
176
|
+
const ManualStepSchema = z
|
|
177
|
+
.object({
|
|
178
|
+
key: z.string().openapi({ example: "GH_TOKEN" }),
|
|
179
|
+
message: z.string().openapi({
|
|
180
|
+
example:
|
|
181
|
+
"GH_TOKEN was not automatically revoked — rotate or revoke it manually.",
|
|
182
|
+
}),
|
|
183
|
+
})
|
|
184
|
+
.openapi("ManualStep");
|
|
185
|
+
|
|
186
|
+
/** A single failed step in DeleteAgentResult.failed. */
|
|
187
|
+
const FailedStepSchema = z
|
|
188
|
+
.object({
|
|
189
|
+
step: z.string().openapi({ example: "k8s" }),
|
|
190
|
+
error: z.string().openapi({ example: "k8s API timeout" }),
|
|
191
|
+
})
|
|
192
|
+
.openapi("FailedStep");
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* DELETE /agents/:id response body — the outcome of deleteAgentFully().
|
|
196
|
+
* `agentDeleted: false` means at least one automatable step failed and the
|
|
197
|
+
* Agent row was intentionally preserved for a later retry (see
|
|
198
|
+
* agent-deletion.ts for the full retry/idempotency contract).
|
|
199
|
+
*/
|
|
200
|
+
export const DeleteAgentResultSchema = z
|
|
201
|
+
.object({
|
|
202
|
+
agentDeleted: z.boolean().openapi({ example: true }),
|
|
203
|
+
completed: z.array(z.string()).openapi({
|
|
204
|
+
example: ["k8s", "task-store-tokens", "chat-service-tokens-and-threads"],
|
|
205
|
+
}),
|
|
206
|
+
failed: z.array(FailedStepSchema),
|
|
207
|
+
manualStepsRequired: z.array(ManualStepSchema),
|
|
208
|
+
})
|
|
209
|
+
.openapi("DeleteAgentResult");
|
|
210
|
+
|
|
211
|
+
// ─── AgentCronJob ─────────────────────────────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
export const AgentCronJobSchema = z
|
|
214
|
+
.object({
|
|
215
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
216
|
+
agentId: z.string().openapi({ example: "clx1234567890" }),
|
|
217
|
+
schedule: z.string().openapi({ example: "0 9 * * 1-5" }),
|
|
218
|
+
prompt: z.string().openapi({ example: "Run the morning brief." }),
|
|
219
|
+
channel: z.string().nullable().openapi({ example: "C01234567" }),
|
|
220
|
+
user: z.string().nullable().openapi({ example: "U0AALR8M69X" }),
|
|
221
|
+
silent: z.boolean().openapi({ example: false }),
|
|
222
|
+
enabled: z.boolean().openapi({ example: true }),
|
|
223
|
+
preCheck: z
|
|
224
|
+
.string()
|
|
225
|
+
.nullable()
|
|
226
|
+
.openapi({ example: "shipwright:check-dev-task.ts" }),
|
|
227
|
+
name: z.string().nullable().openapi({ example: "morning-brief" }),
|
|
228
|
+
system: z.boolean().openapi({ example: false }),
|
|
229
|
+
parentCronId: z.string().nullable().openapi({ example: null }),
|
|
230
|
+
createdAt: z
|
|
231
|
+
.string()
|
|
232
|
+
.datetime()
|
|
233
|
+
.openapi({ example: "2026-01-01T00:00:00.000Z" }),
|
|
234
|
+
updatedAt: z
|
|
235
|
+
.string()
|
|
236
|
+
.datetime()
|
|
237
|
+
.openapi({ example: "2026-01-01T00:00:00.000Z" }),
|
|
238
|
+
})
|
|
239
|
+
.openapi("AgentCronJob");
|
|
240
|
+
|
|
241
|
+
export type AgentCronJob = z.infer<typeof AgentCronJobSchema>;
|
|
242
|
+
|
|
243
|
+
export const CreateAgentCronJobBodySchema = z
|
|
244
|
+
.object({
|
|
245
|
+
schedule: z.string().openapi({ example: "0 9 * * 1-5" }),
|
|
246
|
+
prompt: z.string().openapi({ example: "Run the morning brief." }),
|
|
247
|
+
channel: z.string().nullable().optional().openapi({ example: "C01234567" }),
|
|
248
|
+
user: z.string().nullable().optional().openapi({ example: "U0AALR8M69X" }),
|
|
249
|
+
silent: z.boolean().optional().openapi({ example: false }),
|
|
250
|
+
enabled: z.boolean().optional().openapi({ example: true }),
|
|
251
|
+
preCheck: z
|
|
252
|
+
.string()
|
|
253
|
+
.nullable()
|
|
254
|
+
.optional()
|
|
255
|
+
.openapi({ example: "shipwright:check-dev-task.ts" }),
|
|
256
|
+
name: z
|
|
257
|
+
.string()
|
|
258
|
+
.nullable()
|
|
259
|
+
.optional()
|
|
260
|
+
.openapi({ example: "morning-brief" }),
|
|
261
|
+
})
|
|
262
|
+
.openapi("CreateAgentCronJobBody");
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* PATCH /agents/:id/crons/:cronId body.
|
|
266
|
+
* schedule and prompt must be provided together (content update).
|
|
267
|
+
* enabled and preCheck are orthogonal — each may be sent alone.
|
|
268
|
+
*/
|
|
269
|
+
export const PatchAgentCronJobBodySchema = z
|
|
270
|
+
.object({
|
|
271
|
+
schedule: z.string().optional().openapi({ example: "0 9 * * 1-5" }),
|
|
272
|
+
prompt: z
|
|
273
|
+
.string()
|
|
274
|
+
.optional()
|
|
275
|
+
.openapi({ example: "Run the morning brief." }),
|
|
276
|
+
channel: z.string().nullable().optional().openapi({ example: "C01234567" }),
|
|
277
|
+
user: z.string().nullable().optional().openapi({ example: "U0AALR8M69X" }),
|
|
278
|
+
silent: z.boolean().optional().openapi({ example: false }),
|
|
279
|
+
preCheck: z
|
|
280
|
+
.string()
|
|
281
|
+
.nullable()
|
|
282
|
+
.optional()
|
|
283
|
+
.openapi({ example: "shipwright:check-dev-task.ts" }),
|
|
284
|
+
enabled: z.boolean().optional().openapi({ example: true }),
|
|
285
|
+
})
|
|
286
|
+
.openapi("PatchAgentCronJobBody");
|
|
287
|
+
|
|
288
|
+
// ─── Model breakdown entry ────────────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
const ModelBreakdownEntrySchema = z
|
|
291
|
+
.object({
|
|
292
|
+
model: z.string().openapi({ example: "claude-sonnet-4-5" }),
|
|
293
|
+
inputTokens: z.number().int().default(0).openapi({ example: 200 }),
|
|
294
|
+
outputTokens: z.number().int().default(0).openapi({ example: 100 }),
|
|
295
|
+
cacheReadTokens: z.number().int().default(0).openapi({ example: 8 }),
|
|
296
|
+
cacheCreationTokens: z.number().int().default(0).openapi({ example: 4 }),
|
|
297
|
+
costUsd: z.number().default(0).openapi({ example: 0.002 }),
|
|
298
|
+
})
|
|
299
|
+
.openapi("ModelBreakdownEntry");
|
|
300
|
+
|
|
301
|
+
export type ModelBreakdownEntry = z.infer<typeof ModelBreakdownEntrySchema>;
|
|
302
|
+
|
|
303
|
+
// ─── AgentCronRun ─────────────────────────────────────────────────────────────
|
|
304
|
+
|
|
305
|
+
const ContextBaselineEntrySchema = z
|
|
306
|
+
.object({
|
|
307
|
+
model: z.string().openapi({ example: "claude-sonnet-4-6" }),
|
|
308
|
+
contextTokens: z.number().int().openapi({ example: 79188 }),
|
|
309
|
+
inputTokens: z.number().int().openapi({ example: 2 }),
|
|
310
|
+
cacheCreationTokens: z.number().int().openapi({ example: 41415 }),
|
|
311
|
+
cacheReadTokens: z.number().int().openapi({ example: 37771 }),
|
|
312
|
+
})
|
|
313
|
+
.openapi("ContextBaselineEntry");
|
|
314
|
+
|
|
315
|
+
const SkillUsageEntrySchema = z
|
|
316
|
+
.object({
|
|
317
|
+
kind: z.enum(["skill", "agent", "root"]).openapi({ example: "skill" }),
|
|
318
|
+
name: z.string().openapi({ example: "shipwright:task-store" }),
|
|
319
|
+
invocations: z.number().int().openapi({ example: 1 }),
|
|
320
|
+
turns: z.number().int().openapi({ example: 3 }),
|
|
321
|
+
inputTokens: z.number().int().openapi({ example: 12 }),
|
|
322
|
+
outputTokens: z.number().int().openapi({ example: 100 }),
|
|
323
|
+
cacheReadTokens: z.number().int().openapi({ example: 219200 }),
|
|
324
|
+
cacheCreationTokens: z.number().int().openapi({ example: 9300 }),
|
|
325
|
+
invokeContextDelta: z.number().int().nullable().optional().openapi({
|
|
326
|
+
example: 9103,
|
|
327
|
+
description:
|
|
328
|
+
"input + cacheCreation of the first usage-bearing turn after the skill's first invoke — the new context admitted when its body loaded. Null for root/agent rows.",
|
|
329
|
+
}),
|
|
330
|
+
})
|
|
331
|
+
.openapi("SkillUsageEntry");
|
|
332
|
+
|
|
333
|
+
export const AgentCronRunSchema = z
|
|
334
|
+
.object({
|
|
335
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
336
|
+
cronId: z.string().openapi({ example: "clx0987654321" }),
|
|
337
|
+
agentId: z.string().openapi({ example: "clx1234567890" }),
|
|
338
|
+
startedAt: z
|
|
339
|
+
.string()
|
|
340
|
+
.datetime()
|
|
341
|
+
.openapi({ example: "2026-01-01T08:00:00.000Z" }),
|
|
342
|
+
completedAt: z
|
|
343
|
+
.string()
|
|
344
|
+
.datetime()
|
|
345
|
+
.nullable()
|
|
346
|
+
.openapi({ example: "2026-01-01T08:00:05.000Z" }),
|
|
347
|
+
skipped: z.boolean().openapi({ example: false }),
|
|
348
|
+
skipReason: z
|
|
349
|
+
.string()
|
|
350
|
+
.nullable()
|
|
351
|
+
.openapi({ example: "pre-check returned false" }),
|
|
352
|
+
outcome: z.string().nullable().openapi({ example: "success" }),
|
|
353
|
+
error: z.string().nullable().openapi({ example: null }),
|
|
354
|
+
phaseId: z.string().nullable().openapi({
|
|
355
|
+
example: "clx0987654321",
|
|
356
|
+
description:
|
|
357
|
+
"Child AgentCronJob id (FK) of the pipeline phase this run served (dev-task/review/patch/deploy). Null for legacy five-job crons or ticks with no phase attribution.",
|
|
358
|
+
}),
|
|
359
|
+
itemType: z.string().nullable().openapi({
|
|
360
|
+
example: "task",
|
|
361
|
+
description:
|
|
362
|
+
'Work item type this run was dispatched against ("task" | "pr"). Null when the tick had no dispatch (skipped tick, empty queue).',
|
|
363
|
+
}),
|
|
364
|
+
itemId: z.string().nullable().openapi({
|
|
365
|
+
example: "WLS-2.2",
|
|
366
|
+
description:
|
|
367
|
+
'Work item id this run was dispatched against (e.g. "WLS-2.2" or "acme/x#123"). Null when the tick had no dispatch.',
|
|
368
|
+
}),
|
|
369
|
+
commandVariant: z.string().nullable().openapi({
|
|
370
|
+
example: "default",
|
|
371
|
+
description:
|
|
372
|
+
'Slash-command variant this dispatch ran ("default" | "alternate"). Null when the run had no variant attribution.',
|
|
373
|
+
}),
|
|
374
|
+
inputTokens: z.number().int().nullable().openapi({ example: 1234 }),
|
|
375
|
+
outputTokens: z.number().int().nullable().openapi({ example: 567 }),
|
|
376
|
+
cacheReadTokens: z.number().int().nullable().openapi({ example: 89 }),
|
|
377
|
+
cacheCreationTokens: z.number().int().nullable().openapi({ example: 10 }),
|
|
378
|
+
sessionId: z.string().nullable().openapi({
|
|
379
|
+
example: "session-abc-123",
|
|
380
|
+
description:
|
|
381
|
+
"Agent session id this run was executed under. Null for runs with no recorded session.",
|
|
382
|
+
}),
|
|
383
|
+
lastHeartbeatAt: z.string().datetime().nullable().openapi({
|
|
384
|
+
example: "2026-01-01T08:00:03.000Z",
|
|
385
|
+
description:
|
|
386
|
+
"Most recent debounced progress-push (recordProgress()) timestamp, sourced from the agent's injected Clock. Null for runs that never reported progress (short/legacy runs).",
|
|
387
|
+
}),
|
|
388
|
+
createdAt: z
|
|
389
|
+
.string()
|
|
390
|
+
.datetime()
|
|
391
|
+
.openapi({ example: "2026-01-01T08:00:00.000Z" }),
|
|
392
|
+
modelBreakdown: z.array(ModelBreakdownEntrySchema).optional(),
|
|
393
|
+
contextBaseline: ContextBaselineEntrySchema.nullable().optional().openapi({
|
|
394
|
+
description:
|
|
395
|
+
"First-turn context baseline (null for resumed sessions and runs from older agent builds).",
|
|
396
|
+
}),
|
|
397
|
+
turns: z.number().int().nullable().optional().openapi({ example: 12 }),
|
|
398
|
+
toolCalls: z.number().int().nullable().optional().openapi({ example: 7 }),
|
|
399
|
+
contextFingerprint: z
|
|
400
|
+
.string()
|
|
401
|
+
.nullable()
|
|
402
|
+
.optional()
|
|
403
|
+
.openapi({ example: "abc123def456" }),
|
|
404
|
+
pluginVersion: z
|
|
405
|
+
.string()
|
|
406
|
+
.nullable()
|
|
407
|
+
.optional()
|
|
408
|
+
.openapi({ example: "1.363.0" }),
|
|
409
|
+
claudeCodeVersion: z
|
|
410
|
+
.string()
|
|
411
|
+
.nullable()
|
|
412
|
+
.optional()
|
|
413
|
+
.openapi({ example: "2.1.285" }),
|
|
414
|
+
})
|
|
415
|
+
.openapi("AgentCronRun");
|
|
416
|
+
|
|
417
|
+
export const CronRunsListSchema = z
|
|
418
|
+
.object({
|
|
419
|
+
items: z.array(AgentCronRunSchema),
|
|
420
|
+
total: z.number().int().openapi({ example: 42 }),
|
|
421
|
+
limit: z.number().int().openapi({ example: 20 }),
|
|
422
|
+
offset: z.number().int().openapi({ example: 0 }),
|
|
423
|
+
})
|
|
424
|
+
.openapi("CronRunsList");
|
|
425
|
+
|
|
426
|
+
export type CronRunsList = z.infer<typeof CronRunsListSchema>;
|
|
427
|
+
|
|
428
|
+
export const CreateAgentCronRunBodySchema = z
|
|
429
|
+
.object({
|
|
430
|
+
startedAt: z
|
|
431
|
+
.string()
|
|
432
|
+
.datetime()
|
|
433
|
+
.openapi({ example: "2026-01-01T08:00:00.000Z" }),
|
|
434
|
+
completedAt: z
|
|
435
|
+
.string()
|
|
436
|
+
.datetime()
|
|
437
|
+
.nullable()
|
|
438
|
+
.optional()
|
|
439
|
+
.openapi({ example: null }),
|
|
440
|
+
skipped: z.boolean().optional().openapi({ example: false }),
|
|
441
|
+
skipReason: z
|
|
442
|
+
.string()
|
|
443
|
+
.nullable()
|
|
444
|
+
.optional()
|
|
445
|
+
.openapi({ example: "pre-check returned false" }),
|
|
446
|
+
outcome: z.string().nullable().optional().openapi({ example: "success" }),
|
|
447
|
+
error: z.string().nullable().optional().openapi({ example: null }),
|
|
448
|
+
phaseId: z.string().nullable().optional().openapi({
|
|
449
|
+
example: "clx0987654321",
|
|
450
|
+
description:
|
|
451
|
+
"Child AgentCronJob id (FK) of the pipeline phase this run served (dev-task/review/patch/deploy)",
|
|
452
|
+
}),
|
|
453
|
+
itemType: z.string().nullable().optional().openapi({
|
|
454
|
+
example: "task",
|
|
455
|
+
description:
|
|
456
|
+
'Work item type this run was dispatched against ("task" | "pr")',
|
|
457
|
+
}),
|
|
458
|
+
itemId: z.string().nullable().optional().openapi({
|
|
459
|
+
example: "WLS-2.2",
|
|
460
|
+
description:
|
|
461
|
+
'Work item id this run was dispatched against (e.g. "WLS-2.2" or "acme/x#123")',
|
|
462
|
+
}),
|
|
463
|
+
commandVariant: z.string().nullable().optional().openapi({
|
|
464
|
+
example: "alternate",
|
|
465
|
+
description:
|
|
466
|
+
'Slash-command variant this dispatch ran ("default" | "alternate")',
|
|
467
|
+
}),
|
|
468
|
+
})
|
|
469
|
+
.openapi("CreateAgentCronRunBody");
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* PATCH /agents/:id/crons/:cronId/runs/:runId body.
|
|
473
|
+
* All fields are optional. At least one must be provided (enforced at the handler level).
|
|
474
|
+
*/
|
|
475
|
+
export const PatchAgentCronRunBodySchema = z
|
|
476
|
+
.object({
|
|
477
|
+
completedAt: z
|
|
478
|
+
.string()
|
|
479
|
+
.datetime()
|
|
480
|
+
.nullable()
|
|
481
|
+
.optional()
|
|
482
|
+
.openapi({ example: "2026-01-01T08:05:00.000Z" }),
|
|
483
|
+
outcome: z.string().nullable().optional().openapi({ example: "success" }),
|
|
484
|
+
error: z.string().nullable().optional().openapi({ example: null }),
|
|
485
|
+
skipped: z.boolean().optional().openapi({ example: false }),
|
|
486
|
+
skipReason: z
|
|
487
|
+
.string()
|
|
488
|
+
.nullable()
|
|
489
|
+
.optional()
|
|
490
|
+
.openapi({ example: "pre-check returned false" }),
|
|
491
|
+
inputTokens: z
|
|
492
|
+
.number()
|
|
493
|
+
.int()
|
|
494
|
+
.nullable()
|
|
495
|
+
.optional()
|
|
496
|
+
.openapi({ example: 1234 }),
|
|
497
|
+
outputTokens: z
|
|
498
|
+
.number()
|
|
499
|
+
.int()
|
|
500
|
+
.nullable()
|
|
501
|
+
.optional()
|
|
502
|
+
.openapi({ example: 567 }),
|
|
503
|
+
cacheReadTokens: z
|
|
504
|
+
.number()
|
|
505
|
+
.int()
|
|
506
|
+
.nullable()
|
|
507
|
+
.optional()
|
|
508
|
+
.openapi({ example: 89 }),
|
|
509
|
+
cacheCreationTokens: z
|
|
510
|
+
.number()
|
|
511
|
+
.int()
|
|
512
|
+
.nullable()
|
|
513
|
+
.optional()
|
|
514
|
+
.openapi({ example: 10 }),
|
|
515
|
+
sessionId: z.string().nullable().optional().openapi({
|
|
516
|
+
example: "session-abc-123",
|
|
517
|
+
description: "Agent session id this run was executed under.",
|
|
518
|
+
}),
|
|
519
|
+
lastHeartbeatAt: z.string().datetime().nullable().optional().openapi({
|
|
520
|
+
example: "2026-01-01T08:00:03.000Z",
|
|
521
|
+
description:
|
|
522
|
+
"Most recent debounced progress-push (recordProgress()) timestamp, sourced from the agent's injected Clock.",
|
|
523
|
+
}),
|
|
524
|
+
modelBreakdown: z
|
|
525
|
+
.array(ModelBreakdownEntrySchema)
|
|
526
|
+
.optional()
|
|
527
|
+
.openapi({ description: "Per-model token breakdown for this run" }),
|
|
528
|
+
contextBaseline: ContextBaselineEntrySchema.optional().openapi({
|
|
529
|
+
description:
|
|
530
|
+
"First-turn context baseline: usage of the run's first assistant message. contextTokens = input + cacheCreation + cacheRead — the full always-loaded context independent of cache warmth. Omitted for resumed sessions.",
|
|
531
|
+
}),
|
|
532
|
+
turns: z.number().int().nullable().optional().openapi({
|
|
533
|
+
example: 12,
|
|
534
|
+
description: "Distinct usage-bearing assistant turns in the run.",
|
|
535
|
+
}),
|
|
536
|
+
toolCalls: z.number().int().nullable().optional().openapi({
|
|
537
|
+
example: 7,
|
|
538
|
+
description: "Distinct tool_use blocks in the run.",
|
|
539
|
+
}),
|
|
540
|
+
contextFingerprint: z.string().nullable().optional().openapi({
|
|
541
|
+
example: "abc123def456",
|
|
542
|
+
description:
|
|
543
|
+
"sha256[:12] over the workspace's always-loaded markdown (CLAUDE.md, its @imports, no-`paths` rules) + plugin version. Groups runs by what the model was given.",
|
|
544
|
+
}),
|
|
545
|
+
pluginVersion: z.string().nullable().optional().openapi({
|
|
546
|
+
example: "1.363.0",
|
|
547
|
+
}),
|
|
548
|
+
claudeCodeVersion: z.string().nullable().optional().openapi({
|
|
549
|
+
example: "2.1.285",
|
|
550
|
+
}),
|
|
551
|
+
skillUsage: z.array(SkillUsageEntrySchema).optional().openapi({
|
|
552
|
+
description:
|
|
553
|
+
"Per-skill / per-subagent token attribution rows for this run; upserted per [kind, name].",
|
|
554
|
+
}),
|
|
555
|
+
})
|
|
556
|
+
.openapi("PatchAgentCronRunBody");
|
|
557
|
+
|
|
558
|
+
export const ListCronRunsQuerySchema = z
|
|
559
|
+
.object({
|
|
560
|
+
limit: z
|
|
561
|
+
.string()
|
|
562
|
+
.optional()
|
|
563
|
+
.transform((v) => (v ? Number.parseInt(v, 10) : 20))
|
|
564
|
+
.openapi({ example: "20" }),
|
|
565
|
+
offset: z
|
|
566
|
+
.string()
|
|
567
|
+
.optional()
|
|
568
|
+
.transform((v) => (v ? Number.parseInt(v, 10) : 0))
|
|
569
|
+
.openapi({ example: "0" }),
|
|
570
|
+
/** Narrow to runs dispatched against this work item (e.g. "WLS-2.2" or "acme/x#123"). */
|
|
571
|
+
itemId: z.string().optional().openapi({ example: "acme/x#123" }),
|
|
572
|
+
/** Narrow to runs dispatched by this phase cron (a child AgentCronJob id). */
|
|
573
|
+
phaseId: z.string().optional().openapi({ example: "clx1234567890" }),
|
|
574
|
+
})
|
|
575
|
+
.openapi("ListCronRunsQuery");
|
|
576
|
+
|
|
577
|
+
const CronRunLastRunSchema = z
|
|
578
|
+
.object({
|
|
579
|
+
startedAt: z
|
|
580
|
+
.string()
|
|
581
|
+
.datetime()
|
|
582
|
+
.openapi({ example: "2026-01-01T08:00:00.000Z" }),
|
|
583
|
+
completedAt: z.string().datetime().nullable().openapi({ example: null }),
|
|
584
|
+
skipped: z.boolean().openapi({ example: false }),
|
|
585
|
+
outcome: z.string().nullable().openapi({ example: "success" }),
|
|
586
|
+
})
|
|
587
|
+
.openapi("CronRunLastRun");
|
|
588
|
+
|
|
589
|
+
const AgentCronJobWithRunSummarySchema = AgentCronJobSchema.extend({
|
|
590
|
+
lastRun: CronRunLastRunSchema.nullable().openapi({ example: null }),
|
|
591
|
+
runCountToday: z.number().int().openapi({ example: 3 }),
|
|
592
|
+
}).openapi("AgentCronJobWithRunSummary");
|
|
593
|
+
|
|
594
|
+
export const CronsWithSummaryWrapperSchema = z
|
|
595
|
+
.object({ crons: z.array(AgentCronJobWithRunSummarySchema) })
|
|
596
|
+
.openapi("CronsWithSummaryWrapper");
|
|
597
|
+
|
|
598
|
+
// ─── AgentTool ────────────────────────────────────────────────────────────────
|
|
599
|
+
|
|
600
|
+
export const AgentToolSchema = z
|
|
601
|
+
.object({
|
|
602
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
603
|
+
agentId: z.string().openapi({ example: "clx1234567890" }),
|
|
604
|
+
pattern: z.string().openapi({ example: "Read" }),
|
|
605
|
+
enabled: z.boolean().openapi({ example: true }),
|
|
606
|
+
createdAt: z
|
|
607
|
+
.string()
|
|
608
|
+
.datetime()
|
|
609
|
+
.openapi({ example: "2026-01-01T00:00:00.000Z" }),
|
|
610
|
+
})
|
|
611
|
+
.openapi("AgentTool");
|
|
612
|
+
|
|
613
|
+
export type AgentTool = z.infer<typeof AgentToolSchema>;
|
|
614
|
+
|
|
615
|
+
export const CreateAgentToolBodySchema = z
|
|
616
|
+
.object({
|
|
617
|
+
pattern: z.string().min(1).openapi({ example: "Bash" }),
|
|
618
|
+
})
|
|
619
|
+
.openapi("CreateAgentToolBody");
|
|
620
|
+
|
|
621
|
+
export const PatchAgentToolBodySchema = z
|
|
622
|
+
.object({
|
|
623
|
+
enabled: z.boolean().openapi({ example: false }),
|
|
624
|
+
})
|
|
625
|
+
.openapi("PatchAgentToolBody");
|
|
626
|
+
|
|
627
|
+
// ─── AgentToken ───────────────────────────────────────────────────────────────
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Token metadata returned from list/create (never the hash).
|
|
631
|
+
*/
|
|
632
|
+
export const AgentTokenSchema = z
|
|
633
|
+
.object({
|
|
634
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
635
|
+
agentId: z.string().openapi({ example: "clx1234567890" }),
|
|
636
|
+
label: z.string().nullable().optional().openapi({ example: "ci-runner" }),
|
|
637
|
+
createdAt: z
|
|
638
|
+
.string()
|
|
639
|
+
.datetime()
|
|
640
|
+
.openapi({ example: "2026-01-01T00:00:00.000Z" }),
|
|
641
|
+
revokedAt: z
|
|
642
|
+
.string()
|
|
643
|
+
.datetime()
|
|
644
|
+
.nullable()
|
|
645
|
+
.optional()
|
|
646
|
+
.openapi({ example: null }),
|
|
647
|
+
})
|
|
648
|
+
.openapi("AgentToken");
|
|
649
|
+
|
|
650
|
+
export type AgentToken = z.infer<typeof AgentTokenSchema>;
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* POST /agents/:id/tokens → 201. rawToken is returned once and not stored.
|
|
654
|
+
*/
|
|
655
|
+
export const CreateAgentTokenResponseSchema = z
|
|
656
|
+
.object({
|
|
657
|
+
token: AgentTokenSchema,
|
|
658
|
+
rawToken: z
|
|
659
|
+
.string()
|
|
660
|
+
.openapi({ example: "swt_v1_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }),
|
|
661
|
+
})
|
|
662
|
+
.openapi("CreateAgentTokenResponse");
|
|
663
|
+
|
|
664
|
+
export const CreateAgentTokenBodySchema = z
|
|
665
|
+
.object({
|
|
666
|
+
label: z.string().optional().openapi({ example: "ci-runner" }),
|
|
667
|
+
})
|
|
668
|
+
.openapi("CreateAgentTokenBody");
|
|
669
|
+
|
|
670
|
+
// ─── Chat credentials ─────────────────────────────────────────────────────────
|
|
671
|
+
|
|
672
|
+
/** Strict: an `agentId` (or any other) field is rejected with 400. */
|
|
673
|
+
export const CreateChatCredentialBodySchema = z
|
|
674
|
+
.object({
|
|
675
|
+
label: z.string().min(1).openapi({ example: "ask-caller" }),
|
|
676
|
+
})
|
|
677
|
+
.strict()
|
|
678
|
+
.openapi("CreateChatCredentialBody");
|
|
679
|
+
|
|
680
|
+
export const CreateChatCredentialResponseSchema = z
|
|
681
|
+
.object({
|
|
682
|
+
tokenId: z.string().openapi({ example: "clx0987654321" }),
|
|
683
|
+
rawToken: z.string().openapi({ example: "ct_xxxxxxxxxxxxxxxxxxxxxxxx" }),
|
|
684
|
+
label: z.string().openapi({ example: "ask-caller" }),
|
|
685
|
+
agentId: z.string().openapi({ example: "clx1234567890" }),
|
|
686
|
+
})
|
|
687
|
+
.openapi("CreateChatCredentialResponse");
|
|
688
|
+
|
|
689
|
+
export const ChatCredentialSchema = z
|
|
690
|
+
.object({
|
|
691
|
+
tokenId: z.string().openapi({ example: "clx0987654321" }),
|
|
692
|
+
label: z.string().nullable().openapi({ example: "ask-caller" }),
|
|
693
|
+
agentId: z.string().openapi({ example: "clx1234567890" }),
|
|
694
|
+
})
|
|
695
|
+
.openapi("ChatCredential");
|
|
696
|
+
|
|
697
|
+
export const ChatCredentialsWrapperSchema = z
|
|
698
|
+
.object({ credentials: z.array(ChatCredentialSchema) })
|
|
699
|
+
.openapi("ChatCredentialsWrapper");
|
|
700
|
+
|
|
701
|
+
// ─── Agent ask (COS-31.1) ─────────────────────────────────────────────────────
|
|
702
|
+
|
|
703
|
+
export const AgentAskBodySchema = z
|
|
704
|
+
.object({
|
|
705
|
+
from: z.string().min(1).openapi({
|
|
706
|
+
description:
|
|
707
|
+
"Id of the asking agent. An agent key may only ask as itself (`from` must equal its own id).",
|
|
708
|
+
example: "clx0987654321",
|
|
709
|
+
}),
|
|
710
|
+
question: z.string().min(1).max(8000).openapi({
|
|
711
|
+
description:
|
|
712
|
+
"The question, posted as the thread's user message. Never stored or logged by admin.",
|
|
713
|
+
example: "What is the deploy status of acme/web?",
|
|
714
|
+
}),
|
|
715
|
+
waitMs: z.number().int().min(0).max(60000).default(0).openapi({
|
|
716
|
+
description:
|
|
717
|
+
"How long to wait for the reply, polling once per second. 0 (default) returns `pending` immediately.",
|
|
718
|
+
example: 0,
|
|
719
|
+
}),
|
|
720
|
+
depth: z.number().int().default(1).openapi({
|
|
721
|
+
description: "Ask chain depth. Capped at 1: any other value is a 400.",
|
|
722
|
+
example: 1,
|
|
723
|
+
}),
|
|
724
|
+
ttlSeconds: z.number().int().min(60).max(3600).default(600).openapi({
|
|
725
|
+
description:
|
|
726
|
+
"How long the ask stays relevant, in seconds. Recorded on the audit row.",
|
|
727
|
+
example: 600,
|
|
728
|
+
}),
|
|
729
|
+
})
|
|
730
|
+
.openapi("AgentAskBody");
|
|
731
|
+
|
|
732
|
+
export const AgentAskResponseSchema = z
|
|
733
|
+
.object({
|
|
734
|
+
askId: z.string().openapi({ example: "clx1111111111" }),
|
|
735
|
+
threadId: z.string().openapi({ example: "clx2222222222" }),
|
|
736
|
+
status: z.enum(["replied", "pending"]).openapi({ example: "pending" }),
|
|
737
|
+
reply: z.string().optional().openapi({
|
|
738
|
+
description:
|
|
739
|
+
"The agent's reply body; present only when status is `replied`.",
|
|
740
|
+
example: "acme/web is deployed and green.",
|
|
741
|
+
}),
|
|
742
|
+
})
|
|
743
|
+
.openapi("AgentAskResponse");
|
|
744
|
+
|
|
745
|
+
export const AgentAskRecordSchema = z
|
|
746
|
+
.object({
|
|
747
|
+
id: z.string().openapi({ example: "clx1111111111" }),
|
|
748
|
+
fromAgentId: z.string().openapi({ example: "clx0987654321" }),
|
|
749
|
+
toAgentId: z.string().openapi({ example: "clx1234567890" }),
|
|
750
|
+
threadId: z.string().nullable().openapi({ example: "clx2222222222" }),
|
|
751
|
+
depth: z.number().int().openapi({ example: 1 }),
|
|
752
|
+
waitMs: z.number().int().openapi({ example: 0 }),
|
|
753
|
+
ttlSeconds: z.number().int().openapi({ example: 600 }),
|
|
754
|
+
status: z.string().openapi({
|
|
755
|
+
description: "created | pending | replied | failed",
|
|
756
|
+
example: "pending",
|
|
757
|
+
}),
|
|
758
|
+
callerLabel: z
|
|
759
|
+
.string()
|
|
760
|
+
.openapi({ example: "ops (platform.ops) [platform-ops]" }),
|
|
761
|
+
createdAt: z.string().openapi({ example: "2026-10-25T12:00:00.000Z" }),
|
|
762
|
+
repliedAt: z.string().nullable().openapi({ example: null }),
|
|
763
|
+
})
|
|
764
|
+
.openapi("AgentAskRecord");
|
|
765
|
+
|
|
766
|
+
export const AgentAskListSchema = z
|
|
767
|
+
.object({
|
|
768
|
+
asks: z.array(AgentAskRecordSchema),
|
|
769
|
+
total: z.number().int().openapi({ example: 1 }),
|
|
770
|
+
limit: z.number().int().openapi({ example: 50 }),
|
|
771
|
+
offset: z.number().int().openapi({ example: 0 }),
|
|
772
|
+
})
|
|
773
|
+
.openapi("AgentAskList");
|
|
774
|
+
|
|
775
|
+
export const ListAgentAsksQuerySchema = z
|
|
776
|
+
.object({
|
|
777
|
+
limit: z.coerce.number().int().min(1).max(200).default(50).openapi({
|
|
778
|
+
example: 50,
|
|
779
|
+
}),
|
|
780
|
+
offset: z.coerce.number().int().min(0).default(0).openapi({ example: 0 }),
|
|
781
|
+
})
|
|
782
|
+
.openapi("ListAgentAsksQuery");
|
|
783
|
+
|
|
784
|
+
// ─── AgentPlugin ──────────────────────────────────────────────────────────────
|
|
785
|
+
|
|
786
|
+
export const AgentPluginSchema = z
|
|
787
|
+
.object({
|
|
788
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
789
|
+
agentId: z.string().openapi({ example: "clx1234567890" }),
|
|
790
|
+
name: z.string().openapi({ example: "@shipwright/plugin" }),
|
|
791
|
+
version: z.string().nullable().optional().openapi({ example: "1.2.3" }),
|
|
792
|
+
enabled: z.boolean().openapi({ example: true }),
|
|
793
|
+
createdAt: z
|
|
794
|
+
.string()
|
|
795
|
+
.datetime()
|
|
796
|
+
.openapi({ example: "2026-01-01T00:00:00.000Z" }),
|
|
797
|
+
updatedAt: z
|
|
798
|
+
.string()
|
|
799
|
+
.datetime()
|
|
800
|
+
.openapi({ example: "2026-01-01T00:00:00.000Z" }),
|
|
801
|
+
})
|
|
802
|
+
.openapi("AgentPlugin");
|
|
803
|
+
|
|
804
|
+
export type AgentPlugin = z.infer<typeof AgentPluginSchema>;
|
|
805
|
+
|
|
806
|
+
export const CreateAgentPluginBodySchema = z
|
|
807
|
+
.object({
|
|
808
|
+
name: z.string().min(1).openapi({ example: "@shipwright/plugin" }),
|
|
809
|
+
version: z.string().nullable().optional().openapi({ example: "1.2.3" }),
|
|
810
|
+
})
|
|
811
|
+
.openapi("CreateAgentPluginBody");
|
|
812
|
+
|
|
813
|
+
export const PatchAgentPluginBodySchema = z
|
|
814
|
+
.object({
|
|
815
|
+
version: z.string().nullable().optional().openapi({ example: "1.3.0" }),
|
|
816
|
+
})
|
|
817
|
+
.openapi("PatchAgentPluginBody");
|
|
818
|
+
|
|
819
|
+
// ─── Pipeline phases ────────────────────────────────────────────────────────────
|
|
820
|
+
//
|
|
821
|
+
// The six pipeline phases a methodology override can target — the keys of the
|
|
822
|
+
// runtime config's `phaseMethodology` map (AgentConfigResponse below; the
|
|
823
|
+
// override CRUD schemas live with the coding extension's API,
|
|
824
|
+
// coding/openapi-schemas-coding.ts). Must stay in lockstep with the phases the
|
|
825
|
+
// shipwright-loop cron can dispatch (PDR-4.1 added "plan-session"/"prd" to the
|
|
826
|
+
// wider pipeline) — see PMC-1.1.
|
|
827
|
+
|
|
828
|
+
export const AGENT_PHASES = [
|
|
829
|
+
"prd",
|
|
830
|
+
"plan-session",
|
|
831
|
+
"review",
|
|
832
|
+
"patch",
|
|
833
|
+
"deploy",
|
|
834
|
+
"dev-task",
|
|
835
|
+
] as const;
|
|
836
|
+
|
|
837
|
+
// ─── AgentEnv ─────────────────────────────────────────────────────────────────
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* GET /agents/:id/envs response. Values are decrypted at the service layer.
|
|
841
|
+
* Secret keys are masked as "***" in env and listed in secretKeys.
|
|
842
|
+
*/
|
|
843
|
+
export const AgentEnvResponseSchema = z
|
|
844
|
+
.object({
|
|
845
|
+
env: z
|
|
846
|
+
.record(z.string(), z.string())
|
|
847
|
+
.openapi({ example: { MY_VAR: "value" } }),
|
|
848
|
+
secretKeys: z.array(z.string()).openapi({ example: ["MY_SECRET"] }),
|
|
849
|
+
})
|
|
850
|
+
.openapi("AgentEnvResponse");
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* POST /agents/:id/envs body — a plain key/value map (full replace).
|
|
854
|
+
*/
|
|
855
|
+
export const AgentEnvBodySchema = z
|
|
856
|
+
.record(z.string(), z.string())
|
|
857
|
+
.openapi("AgentEnvBody");
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* PATCH /agents/:id/envs body — partial update with optional secret designation.
|
|
861
|
+
* `env` is a map of key/value pairs to upsert.
|
|
862
|
+
* `secretKeys` lists which keys should be flagged as secret (masked in GET responses).
|
|
863
|
+
*/
|
|
864
|
+
export const AgentEnvPatchBodySchema = z
|
|
865
|
+
.object({
|
|
866
|
+
env: z
|
|
867
|
+
.record(z.string(), z.string())
|
|
868
|
+
.openapi({ example: { MY_VAR: "value" } }),
|
|
869
|
+
secretKeys: z
|
|
870
|
+
.array(z.string())
|
|
871
|
+
.optional()
|
|
872
|
+
.openapi({ example: ["MY_SECRET"] }),
|
|
873
|
+
})
|
|
874
|
+
.openapi("AgentEnvPatchBody");
|
|
875
|
+
|
|
876
|
+
// ─── AgentGitHubInstallationsSnapshot ─────────────────────────────────────────
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* One GitHub App installation as reported by an agent. The schema is CLOSED
|
|
880
|
+
* (.strict()): unknown fields — notably tokens or other credentials — are
|
|
881
|
+
* rejected with 400 rather than silently stored.
|
|
882
|
+
*/
|
|
883
|
+
const GitHubInstallationSchema = z
|
|
884
|
+
.object({
|
|
885
|
+
owner: z.string().min(1).max(200).openapi({ example: "app-vitals" }),
|
|
886
|
+
installationId: z.number().int().positive().openapi({ example: 12345678 }),
|
|
887
|
+
state: z.string().min(1).max(50).openapi({ example: "active" }),
|
|
888
|
+
lastError: z.string().max(500).nullable().optional().openapi({
|
|
889
|
+
description:
|
|
890
|
+
"Sanitized last error message (max 500 chars). Must not contain credentials.",
|
|
891
|
+
example: "installation suspended",
|
|
892
|
+
}),
|
|
893
|
+
})
|
|
894
|
+
.strict()
|
|
895
|
+
.openapi("GitHubInstallation");
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* PUT /agents/:id/github-installations body. Replace-all: the installations
|
|
899
|
+
* array overwrites whatever was previously stored.
|
|
900
|
+
*/
|
|
901
|
+
export const PutGitHubInstallationsBodySchema = z
|
|
902
|
+
.object({
|
|
903
|
+
reportedAt: z
|
|
904
|
+
.string()
|
|
905
|
+
.datetime()
|
|
906
|
+
.openapi({ example: "2026-01-01T00:00:00.000Z" }),
|
|
907
|
+
installations: z.array(GitHubInstallationSchema),
|
|
908
|
+
})
|
|
909
|
+
.strict()
|
|
910
|
+
.openapi("PutGitHubInstallationsBody");
|
|
911
|
+
|
|
912
|
+
export const AgentGitHubInstallationsSnapshotSchema = z
|
|
913
|
+
.object({
|
|
914
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
915
|
+
agentId: z.string().openapi({ example: "clx1234567890" }),
|
|
916
|
+
reportedAt: z
|
|
917
|
+
.string()
|
|
918
|
+
.datetime()
|
|
919
|
+
.openapi({ example: "2026-01-01T00:00:00.000Z" }),
|
|
920
|
+
installations: z.array(GitHubInstallationSchema),
|
|
921
|
+
createdAt: z
|
|
922
|
+
.string()
|
|
923
|
+
.datetime()
|
|
924
|
+
.openapi({ example: "2026-01-01T00:00:00.000Z" }),
|
|
925
|
+
})
|
|
926
|
+
.openapi("AgentGitHubInstallationsSnapshot");
|
|
927
|
+
|
|
928
|
+
// ─── Path param schemas ───────────────────────────────────────────────────────
|
|
929
|
+
|
|
930
|
+
export const AgentIdParamSchema = z.object({
|
|
931
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
export const AddAgentReposBodySchema = z
|
|
935
|
+
.object({
|
|
936
|
+
repos: z
|
|
937
|
+
.array(
|
|
938
|
+
z.string().refine(isOrgRepo, {
|
|
939
|
+
message: "each repo must be in org/repo format",
|
|
940
|
+
}),
|
|
941
|
+
)
|
|
942
|
+
.openapi({ example: ["my-org/my-repo"] }),
|
|
943
|
+
})
|
|
944
|
+
.openapi("AddAgentReposBody");
|
|
945
|
+
|
|
946
|
+
export const AgentRepoParamSchema = z.object({
|
|
947
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
948
|
+
owner: z.string().openapi({ example: "my-org" }),
|
|
949
|
+
name: z.string().openapi({ example: "my-repo" }),
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
export const CronIdParamSchema = z.object({
|
|
953
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
954
|
+
cronId: z.string().openapi({ example: "clx0987654321" }),
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
export const CronRunIdParamSchema = z.object({
|
|
958
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
959
|
+
cronId: z.string().openapi({ example: "clx0987654321" }),
|
|
960
|
+
runId: z.string().openapi({ example: "clx1111111111" }),
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
export const ToolIdParamSchema = z.object({
|
|
964
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
965
|
+
toolId: z.string().openapi({ example: "clx0987654321" }),
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
export const TokenIdParamSchema = z.object({
|
|
969
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
970
|
+
tokenId: z.string().openapi({ example: "clx0987654321" }),
|
|
971
|
+
});
|
|
972
|
+
|
|
973
|
+
export const EnvKeyParamSchema = z.object({
|
|
974
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
975
|
+
key: z.string().openapi({ example: "MY_VAR" }),
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
export const PluginNameQuerySchema = z.object({
|
|
979
|
+
name: z.string().openapi({ example: "@shipwright/plugin" }),
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
// ─── AgentChatTokenUsageDailyByModel ─────────────────────────────────────────
|
|
983
|
+
|
|
984
|
+
/** One entry in the per-model breakdown sent to POST /agents/:id/chat-tokens/daily. */
|
|
985
|
+
const ChatTokenModelEntrySchema = z
|
|
986
|
+
.object({
|
|
987
|
+
model: z.string().openapi({ example: "claude-sonnet-4-5" }),
|
|
988
|
+
inputTokens: z.number().int().min(0).openapi({ example: 100 }),
|
|
989
|
+
outputTokens: z.number().int().min(0).openapi({ example: 50 }),
|
|
990
|
+
cacheReadTokens: z.number().int().min(0).openapi({ example: 10 }),
|
|
991
|
+
cacheCreationTokens: z.number().int().min(0).openapi({ example: 5 }),
|
|
992
|
+
costUsd: z.number().min(0).openapi({ example: 0.0012 }),
|
|
993
|
+
})
|
|
994
|
+
.openapi("ChatTokenModelEntry");
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* POST /agents/:id/chat-tokens/daily request body.
|
|
998
|
+
* date is YYYY-MM-DD; modelBreakdown carries per-model additive increments.
|
|
999
|
+
*/
|
|
1000
|
+
export const UpsertChatTokenDailyBodySchema = z
|
|
1001
|
+
.object({
|
|
1002
|
+
date: z
|
|
1003
|
+
.string()
|
|
1004
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/, "date must be in YYYY-MM-DD format")
|
|
1005
|
+
.openapi({ example: "2026-01-15" }),
|
|
1006
|
+
modelBreakdown: z
|
|
1007
|
+
.array(ChatTokenModelEntrySchema)
|
|
1008
|
+
.min(1)
|
|
1009
|
+
.openapi({ description: "Per-model token usage increments" }),
|
|
1010
|
+
})
|
|
1011
|
+
.openapi("UpsertChatTokenDailyBody");
|
|
1012
|
+
|
|
1013
|
+
export type UpsertChatTokenDailyBody = z.infer<
|
|
1014
|
+
typeof UpsertChatTokenDailyBodySchema
|
|
1015
|
+
>;
|
|
1016
|
+
|
|
1017
|
+
/** Serialized AgentChatTokenUsageDailyByModel row returned by the upsert endpoint. */
|
|
1018
|
+
export const AgentChatTokenUsageDailySchema = z
|
|
1019
|
+
.object({
|
|
1020
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
1021
|
+
agentId: z.string().openapi({ example: "clx1234567890" }),
|
|
1022
|
+
date: z.string().openapi({ example: "2026-01-15" }),
|
|
1023
|
+
model: z.string().openapi({ example: "claude-sonnet-4-5" }),
|
|
1024
|
+
inputTokens: z.number().int().openapi({ example: 100 }),
|
|
1025
|
+
outputTokens: z.number().int().openapi({ example: 50 }),
|
|
1026
|
+
cacheReadTokens: z.number().int().openapi({ example: 10 }),
|
|
1027
|
+
cacheCreationTokens: z.number().int().openapi({ example: 5 }),
|
|
1028
|
+
costUsd: z.number().openapi({ example: 0.0012 }),
|
|
1029
|
+
createdAt: z
|
|
1030
|
+
.string()
|
|
1031
|
+
.datetime()
|
|
1032
|
+
.openapi({ example: "2026-01-15T00:00:00.000Z" }),
|
|
1033
|
+
updatedAt: z
|
|
1034
|
+
.string()
|
|
1035
|
+
.datetime()
|
|
1036
|
+
.openapi({ example: "2026-01-15T12:00:00.000Z" }),
|
|
1037
|
+
})
|
|
1038
|
+
.openapi("AgentChatTokenUsageDailyByModel");
|
|
1039
|
+
|
|
1040
|
+
// ─── CronRunTokenStats ────────────────────────────────────────────────────────
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* A single rolled-up token aggregate (totals row or per-dimension bucket).
|
|
1044
|
+
*/
|
|
1045
|
+
const TokenAggregateSchema = z
|
|
1046
|
+
.object({
|
|
1047
|
+
input: z.number().int().openapi({ example: 600 }),
|
|
1048
|
+
output: z.number().int().openapi({ example: 300 }),
|
|
1049
|
+
cacheRead: z.number().int().openapi({ example: 60 }),
|
|
1050
|
+
cacheCreation: z.number().int().openapi({ example: 30 }),
|
|
1051
|
+
total: z.number().int().openapi({ example: 990 }),
|
|
1052
|
+
costUsd: z.number().optional().openapi({ example: 0.006 }),
|
|
1053
|
+
})
|
|
1054
|
+
.openapi("TokenAggregate");
|
|
1055
|
+
|
|
1056
|
+
/** A token aggregate keyed by a single grouping value (e.g. agentId). */
|
|
1057
|
+
const KeyedTokenAggregateSchema = TokenAggregateSchema.extend({
|
|
1058
|
+
key: z.string().openapi({ example: "agent-id-123" }),
|
|
1059
|
+
}).openapi("KeyedTokenAggregate");
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* A token aggregate keyed by two grouping values (e.g. agentId + cronName).
|
|
1063
|
+
* `phase` is populated on byCron/byCronModel rows (WL-3.5): the pipeline
|
|
1064
|
+
* phase (dev-task/review/patch/deploy) the row's runs served, or null for
|
|
1065
|
+
* legacy runs that predate phase tracking. Omitted/undefined on dimensions
|
|
1066
|
+
* that don't group by phase (e.g. byModel).
|
|
1067
|
+
*/
|
|
1068
|
+
const DoubleKeyedTokenAggregateSchema = TokenAggregateSchema.extend({
|
|
1069
|
+
key1: z.string().openapi({ example: "agent-id-123" }),
|
|
1070
|
+
key2: z.string().openapi({ example: "morning-brief" }),
|
|
1071
|
+
phase: z.string().nullable().optional().openapi({ example: "dev-task" }),
|
|
1072
|
+
}).openapi("DoubleKeyedTokenAggregate");
|
|
1073
|
+
|
|
1074
|
+
/** A token aggregate bucketed by day (YYYY-MM-DD). */
|
|
1075
|
+
const DailyTokenAggregateSchema = TokenAggregateSchema.extend({
|
|
1076
|
+
period: z.string().openapi({ example: "2026-01-10" }),
|
|
1077
|
+
}).openapi("DailyTokenAggregate");
|
|
1078
|
+
|
|
1079
|
+
/** Per-(kind, name) skill / subagent usage rollup (PAU-1.6). */
|
|
1080
|
+
const SkillUsageAggregateSchema = z
|
|
1081
|
+
.object({
|
|
1082
|
+
kind: z.string().openapi({ example: "skill" }),
|
|
1083
|
+
name: z.string().openapi({ example: "shipwright:dev-task" }),
|
|
1084
|
+
runs: z.number().int().openapi({ example: 4 }),
|
|
1085
|
+
invocations: z.number().int().openapi({ example: 5 }),
|
|
1086
|
+
turns: z.number().int().openapi({ example: 40 }),
|
|
1087
|
+
input: z.number().int().openapi({ example: 600 }),
|
|
1088
|
+
output: z.number().int().openapi({ example: 300 }),
|
|
1089
|
+
cacheRead: z.number().int().openapi({ example: 60 }),
|
|
1090
|
+
cacheCreation: z.number().int().openapi({ example: 30 }),
|
|
1091
|
+
avgInvokeContextDelta: z.number().nullable().openapi({ example: 1200 }),
|
|
1092
|
+
})
|
|
1093
|
+
.openapi("SkillUsageAggregate");
|
|
1094
|
+
|
|
1095
|
+
/** First-turn context baseline per (contextFingerprint, baselineModel, phase). */
|
|
1096
|
+
const ContextBaselineAggregateSchema = z
|
|
1097
|
+
.object({
|
|
1098
|
+
contextFingerprint: z.string().nullable().openapi({ example: "a1b2c3" }),
|
|
1099
|
+
baselineModel: z
|
|
1100
|
+
.string()
|
|
1101
|
+
.nullable()
|
|
1102
|
+
.openapi({ example: "claude-sonnet-5-5" }),
|
|
1103
|
+
phase: z.string().nullable().openapi({ example: "dev-task" }),
|
|
1104
|
+
runs: z.number().int().openapi({ example: 12 }),
|
|
1105
|
+
avgContextTokens: z.number().openapi({ example: 24000 }),
|
|
1106
|
+
minContextTokens: z.number().int().openapi({ example: 23000 }),
|
|
1107
|
+
maxContextTokens: z.number().int().openapi({ example: 25000 }),
|
|
1108
|
+
avgTurns: z.number().nullable().openapi({ example: 30 }),
|
|
1109
|
+
avgToolCalls: z.number().nullable().openapi({ example: 55 }),
|
|
1110
|
+
firstSeen: z.string().openapi({ example: "2026-01-10T09:00:00.000Z" }),
|
|
1111
|
+
lastSeen: z.string().openapi({ example: "2026-01-15T09:00:00.000Z" }),
|
|
1112
|
+
})
|
|
1113
|
+
.openapi("ContextBaselineAggregate");
|
|
1114
|
+
|
|
1115
|
+
/**
|
|
1116
|
+
* Response shape for GET /agents/all/cron-runs/stats.
|
|
1117
|
+
* Matches the CronRunTokenStats interface in admin-metrics-client.ts exactly.
|
|
1118
|
+
*/
|
|
1119
|
+
export const CronRunTokenStatsSchema = z
|
|
1120
|
+
.object({
|
|
1121
|
+
totals: TokenAggregateSchema,
|
|
1122
|
+
byAgent: z.array(KeyedTokenAggregateSchema),
|
|
1123
|
+
byCron: z.array(DoubleKeyedTokenAggregateSchema),
|
|
1124
|
+
byModel: z.array(DoubleKeyedTokenAggregateSchema),
|
|
1125
|
+
daily: z.array(DailyTokenAggregateSchema),
|
|
1126
|
+
byCronModel: z.array(DoubleKeyedTokenAggregateSchema),
|
|
1127
|
+
/** Keyed by phase (dev-task/review/patch/deploy). Runs with a null phase are excluded. */
|
|
1128
|
+
byPhase: z.array(KeyedTokenAggregateSchema),
|
|
1129
|
+
/** Per (kind, name); skipped runs excluded. */
|
|
1130
|
+
bySkill: z.array(SkillUsageAggregateSchema).optional(),
|
|
1131
|
+
/** Per (fingerprint, model, phase); skipped runs included, runs without a baseline excluded. */
|
|
1132
|
+
baselines: z.array(ContextBaselineAggregateSchema).optional(),
|
|
1133
|
+
})
|
|
1134
|
+
.openapi("CronRunTokenStats");
|
|
1135
|
+
|
|
1136
|
+
/** One (phase, contextFingerprint) outcome series for GET /agents/all/cron-runs/outcomes. */
|
|
1137
|
+
const CronRunOutcomeSeriesSchema = z
|
|
1138
|
+
.object({
|
|
1139
|
+
phase: z.string().nullable().openapi({ example: "dev-task" }),
|
|
1140
|
+
contextFingerprint: z
|
|
1141
|
+
.string()
|
|
1142
|
+
.nullable()
|
|
1143
|
+
.openapi({ example: "a1b2c3d4e5f6" }),
|
|
1144
|
+
runs: z.number().int(),
|
|
1145
|
+
completed: z.number().int(),
|
|
1146
|
+
failed: z.number().int(),
|
|
1147
|
+
skipped: z.number().int(),
|
|
1148
|
+
skipReasons: z
|
|
1149
|
+
.record(z.string(), z.number().int())
|
|
1150
|
+
.openapi({ example: { "no-ready-task": 3 } }),
|
|
1151
|
+
avgDurationMs: z.number().nullable(),
|
|
1152
|
+
p50DurationMs: z.number().nullable(),
|
|
1153
|
+
avgTurns: z.number().nullable(),
|
|
1154
|
+
avgToolCalls: z.number().nullable(),
|
|
1155
|
+
avgContextTokens: z.number().nullable(),
|
|
1156
|
+
})
|
|
1157
|
+
.openapi("CronRunOutcomeSeries");
|
|
1158
|
+
|
|
1159
|
+
/** Response shape for GET /agents/all/cron-runs/outcomes. */
|
|
1160
|
+
export const CronRunOutcomesSchema = z
|
|
1161
|
+
.object({ series: z.array(CronRunOutcomeSeriesSchema) })
|
|
1162
|
+
.openapi("CronRunOutcomes");
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* Response shape for GET /agents/chat-tokens/daily/stats.
|
|
1166
|
+
* Matches the ChatTokenStats interface in admin-metrics-client.ts exactly.
|
|
1167
|
+
* byModel carries per-(agentId, model) groupings; no byCron dimension.
|
|
1168
|
+
*/
|
|
1169
|
+
export const ChatTokenStatsSchema = z
|
|
1170
|
+
.object({
|
|
1171
|
+
totals: TokenAggregateSchema,
|
|
1172
|
+
byAgent: z.array(KeyedTokenAggregateSchema),
|
|
1173
|
+
byModel: z.array(DoubleKeyedTokenAggregateSchema),
|
|
1174
|
+
daily: z.array(DailyTokenAggregateSchema),
|
|
1175
|
+
})
|
|
1176
|
+
.openapi("ChatTokenStats");
|
|
1177
|
+
|
|
1178
|
+
// ─── AgentConfig (runtime GET /agents/:id/config response) ───────────────────
|
|
1179
|
+
|
|
1180
|
+
const AgentConfigPluginSchema = z
|
|
1181
|
+
.object({
|
|
1182
|
+
marketplace: z.string().openapi({ example: "shipwright" }),
|
|
1183
|
+
plugin: z.string().openapi({ example: "shipwright" }),
|
|
1184
|
+
})
|
|
1185
|
+
.openapi("AgentConfigPlugin");
|
|
1186
|
+
|
|
1187
|
+
export const AgentConfigResponseSchema = z
|
|
1188
|
+
.object({
|
|
1189
|
+
env: z
|
|
1190
|
+
.record(z.string(), z.string())
|
|
1191
|
+
.openapi({ example: { SLACK_BOT_TOKEN: "xoxb-..." } }),
|
|
1192
|
+
allowedTools: z.array(z.string()).openapi({ example: ["Read", "Write"] }),
|
|
1193
|
+
plugins: z.array(AgentConfigPluginSchema),
|
|
1194
|
+
repos: z.array(z.string()).openapi({ example: ["org/repo1", "org/repo2"] }),
|
|
1195
|
+
reviewAuthorAllowlist: z
|
|
1196
|
+
.array(z.string())
|
|
1197
|
+
.openapi({ example: ["octocat"] }),
|
|
1198
|
+
patchAuthorAllowlist: z.array(z.string()).openapi({ example: ["octocat"] }),
|
|
1199
|
+
/**
|
|
1200
|
+
* APM-1.5: direct passthrough of the 6 agent-policy fields APM-1.1 added
|
|
1201
|
+
* to the Agent row — required, populated by the handler unconditionally.
|
|
1202
|
+
*/
|
|
1203
|
+
autoPostReviews: z.boolean().openapi({ example: true }),
|
|
1204
|
+
allowSelfReview: z.boolean().openapi({ example: false }),
|
|
1205
|
+
minConfidence: z.number().int().openapi({ example: 75 }),
|
|
1206
|
+
maxFindings: z.number().int().openapi({ example: 5 }),
|
|
1207
|
+
cleanupMergedWorktrees: z.boolean().openapi({ example: true }),
|
|
1208
|
+
cleanupAfterDays: z.number().int().openapi({ example: 14 }),
|
|
1209
|
+
restrictSlackToMembers: z.boolean().openapi({ example: false }),
|
|
1210
|
+
memberEmails: z.array(z.string()).openapi({ example: ["dev@example.com"] }),
|
|
1211
|
+
/**
|
|
1212
|
+
* ATE-3.1: ISO timestamp of trial expiry, or null when no trial is
|
|
1213
|
+
* configured. Optional — mirrors AgentConfigResponse's own
|
|
1214
|
+
* trialExpiresAt (admin/src/api.ts) being optional rather than required.
|
|
1215
|
+
*/
|
|
1216
|
+
trialExpiresAt: z
|
|
1217
|
+
.string()
|
|
1218
|
+
.datetime()
|
|
1219
|
+
.nullable()
|
|
1220
|
+
.optional()
|
|
1221
|
+
.openapi({ example: "2026-12-01T00:00:00.000Z" }),
|
|
1222
|
+
/**
|
|
1223
|
+
* SSP-8.3: status of the agent's owning Account, or null when the agent
|
|
1224
|
+
* has no account. The agent runtime pauses Slack for 'suspended' and
|
|
1225
|
+
* 'trial_expired'. Optional, mirroring trialExpiresAt.
|
|
1226
|
+
*/
|
|
1227
|
+
accountStatus: z
|
|
1228
|
+
.string()
|
|
1229
|
+
.nullable()
|
|
1230
|
+
.optional()
|
|
1231
|
+
.openapi({ example: "active" }),
|
|
1232
|
+
/**
|
|
1233
|
+
* ARD-6.1: the agent's Specialty name. Optional (additive) — the runtime
|
|
1234
|
+
* falls back to "coding" templates when absent.
|
|
1235
|
+
*/
|
|
1236
|
+
typeName: z.string().optional().openapi({ example: "coding" }),
|
|
1237
|
+
/**
|
|
1238
|
+
* PMC-1.1: phase -> subagentType (or null) map, covering all six pipeline
|
|
1239
|
+
* phases (see AGENT_PHASES) regardless of whether the agent has an
|
|
1240
|
+
* explicit override row for each — phases without a row default to null.
|
|
1241
|
+
*/
|
|
1242
|
+
phaseMethodology: z.record(z.string(), z.string().nullable()).openapi({
|
|
1243
|
+
example: {
|
|
1244
|
+
prd: null,
|
|
1245
|
+
"plan-session": null,
|
|
1246
|
+
review: "shipwright:code-reviewer",
|
|
1247
|
+
patch: null,
|
|
1248
|
+
deploy: null,
|
|
1249
|
+
"dev-task": null,
|
|
1250
|
+
},
|
|
1251
|
+
}),
|
|
1252
|
+
})
|
|
1253
|
+
.openapi("AgentConfigResponse");
|
|
1254
|
+
|
|
1255
|
+
// Simple error shape for runtime API responses (no status field)
|
|
1256
|
+
export const RuntimeErrorSchema = z
|
|
1257
|
+
.object({
|
|
1258
|
+
error: z.string().openapi({ example: "Not found" }),
|
|
1259
|
+
})
|
|
1260
|
+
.openapi("RuntimeError");
|
|
1261
|
+
|
|
1262
|
+
// ─── Accounts (SSP-5.1) ───────────────────────────────────────────────────────
|
|
1263
|
+
|
|
1264
|
+
export const AccountIdParamSchema = z.object({
|
|
1265
|
+
id: z
|
|
1266
|
+
.string()
|
|
1267
|
+
.min(1)
|
|
1268
|
+
.openapi({ param: { name: "id", in: "path" }, example: "clx1abc" }),
|
|
1269
|
+
});
|
|
1270
|
+
|
|
1271
|
+
const AccountStatusSchema = z.enum(["active", "suspended", "trial_expired"]);
|
|
1272
|
+
|
|
1273
|
+
export const AccountResponseSchema = z
|
|
1274
|
+
.object({
|
|
1275
|
+
id: z.string().openapi({ example: "clx1abc" }),
|
|
1276
|
+
name: z.string().openapi({ example: "Acme" }),
|
|
1277
|
+
status: AccountStatusSchema,
|
|
1278
|
+
maxAgents: z.number().int().openapi({ example: 3 }),
|
|
1279
|
+
plan: z.string().nullable(),
|
|
1280
|
+
trialExpiresAt: z.string().datetime().nullable(),
|
|
1281
|
+
createdAt: z.string().datetime(),
|
|
1282
|
+
updatedAt: z.string().datetime(),
|
|
1283
|
+
agentCount: z.number().int().openapi({ example: 1 }),
|
|
1284
|
+
memberCount: z.number().int().openapi({ example: 2 }),
|
|
1285
|
+
})
|
|
1286
|
+
.openapi("Account");
|
|
1287
|
+
|
|
1288
|
+
export const AccountsListResponseSchema = z
|
|
1289
|
+
.object({ accounts: z.array(AccountResponseSchema) })
|
|
1290
|
+
.openapi("AccountsList");
|
|
1291
|
+
|
|
1292
|
+
export const CreateAccountBodySchema = z
|
|
1293
|
+
.object({
|
|
1294
|
+
name: z.string().trim().min(1).openapi({ example: "Acme" }),
|
|
1295
|
+
ownerEmail: z.string().trim().email().openapi({ example: "o@acme.com" }),
|
|
1296
|
+
maxAgents: z.number().int().min(0).openapi({ example: 3 }),
|
|
1297
|
+
plan: z.string().nullable().optional(),
|
|
1298
|
+
trialExpiresAt: z.string().datetime().nullable().optional(),
|
|
1299
|
+
})
|
|
1300
|
+
.openapi("CreateAccountBody");
|
|
1301
|
+
|
|
1302
|
+
export const PatchAccountBodySchema = z
|
|
1303
|
+
.object({
|
|
1304
|
+
name: z.string().trim().min(1).optional(),
|
|
1305
|
+
maxAgents: z.number().int().min(0).optional(),
|
|
1306
|
+
plan: z.string().nullable().optional(),
|
|
1307
|
+
status: AccountStatusSchema.optional(),
|
|
1308
|
+
trialExpiresAt: z.string().datetime().nullable().optional(),
|
|
1309
|
+
})
|
|
1310
|
+
.openapi("PatchAccountBody");
|