@claudexor/schema 1.0.1 → 2.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.
Files changed (278) hide show
  1. package/dist/agent-capabilities.d.ts +102 -30
  2. package/dist/agent-capabilities.d.ts.map +1 -1
  3. package/dist/agent-capabilities.js +49 -18
  4. package/dist/agent-capabilities.js.map +1 -1
  5. package/dist/apply-eligibility.d.ts.map +1 -1
  6. package/dist/apply-eligibility.js +22 -6
  7. package/dist/apply-eligibility.js.map +1 -1
  8. package/dist/attachment.d.ts +117 -52
  9. package/dist/attachment.d.ts.map +1 -1
  10. package/dist/attachment.js +60 -45
  11. package/dist/attachment.js.map +1 -1
  12. package/dist/auth.d.ts +1444 -0
  13. package/dist/auth.d.ts.map +1 -0
  14. package/dist/auth.js +371 -0
  15. package/dist/auth.js.map +1 -0
  16. package/dist/budget.d.ts +147 -18
  17. package/dist/budget.d.ts.map +1 -1
  18. package/dist/budget.js +80 -22
  19. package/dist/budget.js.map +1 -1
  20. package/dist/config.d.ts +829 -135
  21. package/dist/config.d.ts.map +1 -1
  22. package/dist/config.js +195 -41
  23. package/dist/config.js.map +1 -1
  24. package/dist/context.d.ts +6 -6
  25. package/dist/context.d.ts.map +1 -1
  26. package/dist/context.js +31 -7
  27. package/dist/context.js.map +1 -1
  28. package/dist/control-gc.d.ts +185 -0
  29. package/dist/control-gc.d.ts.map +1 -0
  30. package/dist/control-gc.js +81 -0
  31. package/dist/control-gc.js.map +1 -0
  32. package/dist/control-operation-responses.d.ts +1298 -0
  33. package/dist/control-operation-responses.d.ts.map +1 -0
  34. package/dist/control-operation-responses.js +35 -0
  35. package/dist/control-operation-responses.js.map +1 -0
  36. package/dist/control-run-detail.d.ts +556 -247
  37. package/dist/control-run-detail.d.ts.map +1 -1
  38. package/dist/control-run-detail.js +72 -17
  39. package/dist/control-run-detail.js.map +1 -1
  40. package/dist/control-run-retry.d.ts +88 -0
  41. package/dist/control-run-retry.d.ts.map +1 -0
  42. package/dist/control-run-retry.js +40 -0
  43. package/dist/control-run-retry.js.map +1 -0
  44. package/dist/control-run-scope.d.ts +25 -0
  45. package/dist/control-run-scope.d.ts.map +1 -0
  46. package/dist/control-run-scope.js +22 -0
  47. package/dist/control-run-scope.js.map +1 -0
  48. package/dist/control-secret.d.ts +90 -0
  49. package/dist/control-secret.d.ts.map +1 -0
  50. package/dist/control-secret.js +35 -0
  51. package/dist/control-secret.js.map +1 -0
  52. package/dist/control-spec.d.ts +592 -0
  53. package/dist/control-spec.d.ts.map +1 -0
  54. package/dist/control-spec.js +85 -0
  55. package/dist/control-spec.js.map +1 -0
  56. package/dist/control-thread-apply.d.ts +175 -0
  57. package/dist/control-thread-apply.d.ts.map +1 -0
  58. package/dist/control-thread-apply.js +31 -0
  59. package/dist/control-thread-apply.js.map +1 -0
  60. package/dist/control-trust.d.ts +74 -10
  61. package/dist/control-trust.d.ts.map +1 -1
  62. package/dist/control-trust.js +31 -11
  63. package/dist/control-trust.js.map +1 -1
  64. package/dist/control.d.ts +1688 -1334
  65. package/dist/control.d.ts.map +1 -1
  66. package/dist/control.js +592 -333
  67. package/dist/control.js.map +1 -1
  68. package/dist/credential-profile.d.ts +454 -0
  69. package/dist/credential-profile.d.ts.map +1 -0
  70. package/dist/credential-profile.js +140 -0
  71. package/dist/credential-profile.js.map +1 -0
  72. package/dist/decision.d.ts +10 -10
  73. package/dist/decision.d.ts.map +1 -1
  74. package/dist/decision.js +62 -15
  75. package/dist/decision.js.map +1 -1
  76. package/dist/delivery.d.ts +99 -0
  77. package/dist/delivery.d.ts.map +1 -0
  78. package/dist/delivery.js +19 -0
  79. package/dist/delivery.js.map +1 -0
  80. package/dist/events.d.ts +41 -4
  81. package/dist/events.d.ts.map +1 -1
  82. package/dist/events.js +47 -4
  83. package/dist/events.js.map +1 -1
  84. package/dist/gate.d.ts +5 -5
  85. package/dist/gate.d.ts.map +1 -1
  86. package/dist/gate.js +25 -5
  87. package/dist/gate.js.map +1 -1
  88. package/dist/harness.d.ts +775 -237
  89. package/dist/harness.d.ts.map +1 -1
  90. package/dist/harness.js +314 -111
  91. package/dist/harness.js.map +1 -1
  92. package/dist/index.d.ts +19 -0
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +19 -0
  95. package/dist/index.js.map +1 -1
  96. package/dist/operation.d.ts +185 -0
  97. package/dist/operation.d.ts.map +1 -0
  98. package/dist/operation.js +50 -0
  99. package/dist/operation.js.map +1 -0
  100. package/dist/orchestrate.d.ts +150 -22
  101. package/dist/orchestrate.d.ts.map +1 -1
  102. package/dist/orchestrate.js +117 -7
  103. package/dist/orchestrate.js.map +1 -1
  104. package/dist/primitives.d.ts.map +1 -1
  105. package/dist/primitives.js +4 -1
  106. package/dist/primitives.js.map +1 -1
  107. package/dist/problem.d.ts +29 -0
  108. package/dist/problem.d.ts.map +1 -0
  109. package/dist/problem.js +17 -0
  110. package/dist/problem.js.map +1 -0
  111. package/dist/project.d.ts +97 -0
  112. package/dist/project.d.ts.map +1 -0
  113. package/dist/project.js +27 -0
  114. package/dist/project.js.map +1 -0
  115. package/dist/quota.d.ts +269 -0
  116. package/dist/quota.d.ts.map +1 -0
  117. package/dist/quota.js +58 -0
  118. package/dist/quota.js.map +1 -0
  119. package/dist/raw.d.ts +195 -0
  120. package/dist/raw.d.ts.map +1 -0
  121. package/dist/raw.js +58 -0
  122. package/dist/raw.js.map +1 -0
  123. package/dist/readiness.d.ts +596 -0
  124. package/dist/readiness.d.ts.map +1 -0
  125. package/dist/readiness.js +87 -0
  126. package/dist/readiness.js.map +1 -0
  127. package/dist/recovery.d.ts +515 -0
  128. package/dist/recovery.d.ts.map +1 -0
  129. package/dist/recovery.js +93 -0
  130. package/dist/recovery.js.map +1 -0
  131. package/dist/request-requirements.d.ts +36 -0
  132. package/dist/request-requirements.d.ts.map +1 -0
  133. package/dist/request-requirements.js +29 -0
  134. package/dist/request-requirements.js.map +1 -0
  135. package/dist/review.d.ts +26 -26
  136. package/dist/review.d.ts.map +1 -1
  137. package/dist/review.js +50 -10
  138. package/dist/review.js.map +1 -1
  139. package/dist/route.d.ts +13 -13
  140. package/dist/route.d.ts.map +1 -1
  141. package/dist/route.js +19 -4
  142. package/dist/route.js.map +1 -1
  143. package/dist/setup.d.ts +9811 -0
  144. package/dist/setup.d.ts.map +1 -0
  145. package/dist/setup.js +475 -0
  146. package/dist/setup.js.map +1 -0
  147. package/dist/spec.d.ts +178 -61
  148. package/dist/spec.d.ts.map +1 -1
  149. package/dist/spec.js +69 -16
  150. package/dist/spec.js.map +1 -1
  151. package/dist/surface-run-controls.d.ts.map +1 -1
  152. package/dist/surface-run-controls.js +25 -21
  153. package/dist/surface-run-controls.js.map +1 -1
  154. package/dist/task.d.ts +1657 -60
  155. package/dist/task.d.ts.map +1 -1
  156. package/dist/task.js +144 -18
  157. package/dist/task.js.map +1 -1
  158. package/dist/telemetry.d.ts +487 -72
  159. package/dist/telemetry.d.ts.map +1 -1
  160. package/dist/telemetry.js +194 -16
  161. package/dist/telemetry.js.map +1 -1
  162. package/dist/thread.d.ts +89 -51
  163. package/dist/thread.d.ts.map +1 -1
  164. package/dist/thread.js +56 -11
  165. package/dist/thread.js.map +1 -1
  166. package/dist/tool-ref.d.ts +45 -0
  167. package/dist/tool-ref.d.ts.map +1 -0
  168. package/dist/tool-ref.js +45 -0
  169. package/dist/tool-ref.js.map +1 -0
  170. package/dist/workproduct.d.ts +3 -3
  171. package/dist/workproduct.d.ts.map +1 -1
  172. package/dist/workproduct.js.map +1 -1
  173. package/dist/workspace.d.ts +3 -3
  174. package/dist/workspace.d.ts.map +1 -1
  175. package/dist/workspace.js +12 -3
  176. package/dist/workspace.js.map +1 -1
  177. package/generated/AgentCapabilityCatalog.schema.json +53 -11
  178. package/generated/BudgetLease.schema.json +55 -6
  179. package/generated/BudgetObservation.schema.json +44 -7
  180. package/generated/ConformanceReport.schema.json +49 -0
  181. package/generated/ControlApplyCheckResponse.schema.json +34 -0
  182. package/generated/ControlArtifactListResponse.schema.json +57 -0
  183. package/generated/ControlAuthReadinessRefreshRequest.schema.json +38 -0
  184. package/generated/ControlAuthReadinessRefreshResponse.schema.json +85 -0
  185. package/generated/ControlCredentialProfileCreateRequest.schema.json +32 -0
  186. package/generated/ControlCredentialProfileCreateResponse.schema.json +142 -0
  187. package/generated/ControlCredentialProfileDeleteResponse.schema.json +107 -0
  188. package/generated/ControlCredentialProfilesResponse.schema.json +155 -0
  189. package/generated/ControlDeliveryResponse.schema.json +136 -0
  190. package/generated/ControlGcReceipt.schema.json +170 -0
  191. package/generated/ControlGcRequest.schema.json +18 -0
  192. package/generated/ControlHandshakeRequest.schema.json +26 -0
  193. package/generated/ControlHandshakeResponse.schema.json +30 -0
  194. package/generated/ControlHarnessListResponse.schema.json +198 -14
  195. package/generated/ControlHarnessModelsResponse.schema.json +19 -0
  196. package/generated/ControlHarnessSettingsPatch.schema.json +9 -12
  197. package/generated/ControlJournalEvent.schema.json +42 -0
  198. package/generated/ControlJournalExportReceipt.schema.json +49 -0
  199. package/generated/ControlJournalInspection.schema.json +149 -0
  200. package/generated/ControlJournalQuarantineReceipt.schema.json +55 -0
  201. package/generated/ControlJournalQuarantineRequest.schema.json +24 -0
  202. package/generated/ControlJournalValidation.schema.json +181 -0
  203. package/generated/ControlOperationCatalog.schema.json +128 -0
  204. package/generated/ControlOperationDescriptor.schema.json +110 -0
  205. package/generated/ControlProblem.schema.json +59 -0
  206. package/generated/ControlProject.schema.json +42 -0
  207. package/generated/ControlProjectListResponse.schema.json +54 -0
  208. package/generated/ControlProjectRegisterRequest.schema.json +19 -0
  209. package/generated/ControlProjectRelinkRequest.schema.json +19 -0
  210. package/generated/ControlQueuedRunInfo.schema.json +4 -2
  211. package/generated/ControlQuotaResponse.schema.json +177 -0
  212. package/generated/ControlResource.schema.json +59 -0
  213. package/generated/ControlRunAgainDraft.schema.json +539 -0
  214. package/generated/ControlRunDetail.schema.json +373 -34
  215. package/generated/ControlRunListResponse.schema.json +968 -0
  216. package/generated/ControlRunRetryResponse.schema.json +54 -0
  217. package/generated/ControlRunStartRequest.schema.json +123 -55
  218. package/generated/ControlRunStartResponse.schema.json +78 -0
  219. package/generated/ControlRunSummary.schema.json +312 -21
  220. package/generated/ControlSecretListResponse.schema.json +8 -14
  221. package/generated/ControlSecretMutationResponse.schema.json +32 -0
  222. package/generated/ControlSecretSetRequest.schema.json +27 -0
  223. package/generated/ControlSettingsSnapshot.schema.json +128 -44
  224. package/generated/ControlSettingsUpdateRequest.schema.json +119 -40
  225. package/generated/ControlSetupJob.schema.json +770 -64
  226. package/generated/ControlSetupJobCreateRequest.schema.json +16 -13
  227. package/generated/ControlSetupJobEvent.schema.json +869 -5
  228. package/generated/ControlSetupJobListFilter.schema.json +35 -0
  229. package/generated/ControlSetupJobListResponse.schema.json +770 -64
  230. package/generated/ControlSetupJobSnapshot.schema.json +878 -0
  231. package/generated/ControlSpecAnswersRequest.schema.json +74 -0
  232. package/generated/ControlSpecQuestionsRequest.schema.json +128 -2
  233. package/generated/ControlSpecSession.schema.json +249 -0
  234. package/generated/ControlSpecSessionListResponse.schema.json +261 -0
  235. package/generated/ControlThread.schema.json +36 -6
  236. package/generated/ControlThreadApplyResponse.schema.json +139 -0
  237. package/generated/ControlThreadCreateRequest.schema.json +6 -2
  238. package/generated/ControlThreadDetail.schema.json +64 -12
  239. package/generated/ControlThreadListResponse.schema.json +36 -6
  240. package/generated/ControlThreadTurnRequest.schema.json +413 -0
  241. package/generated/ControlThreadTurnResponse.schema.json +73 -0
  242. package/generated/ControlThreadUpdateRequest.schema.json +12 -1
  243. package/generated/ControlTrustListResponse.schema.json +6 -0
  244. package/generated/ControlTrustState.schema.json +6 -0
  245. package/generated/ControlTrustUpdateRequest.schema.json +65 -3
  246. package/generated/ControlUploadCreateRequest.schema.json +37 -0
  247. package/generated/ControlUploadFinalizeRequest.schema.json +17 -0
  248. package/generated/ControlUploadStatus.schema.json +40 -0
  249. package/generated/CredentialProfile.schema.json +78 -0
  250. package/generated/CredentialProfileStatus.schema.json +61 -0
  251. package/generated/DecisionRecord.schema.json +6 -3
  252. package/generated/GlobalConfig.schema.json +238 -55
  253. package/generated/HarnessEvent.schema.json +259 -11
  254. package/generated/HarnessManifest.schema.json +96 -14
  255. package/generated/HarnessModel.schema.json +19 -0
  256. package/generated/HarnessStatusDto.schema.json +198 -14
  257. package/generated/OrchestrateContract.schema.json +32 -6
  258. package/generated/OrchestratePlan.schema.json +20 -0
  259. package/generated/OrchestratePlanProgress.schema.json +61 -0
  260. package/generated/PaidBudget.schema.json +42 -0
  261. package/generated/Project.schema.json +45 -0
  262. package/generated/ProjectConfig.schema.json +45 -18
  263. package/generated/RawContextPacket.schema.json +184 -0
  264. package/generated/RawGitPatchEnvelope.schema.json +70 -0
  265. package/generated/RunEvent.schema.json +3 -0
  266. package/generated/RunTelemetry.schema.json +280 -0
  267. package/generated/SecretMetadata.schema.json +4 -7
  268. package/generated/Session.schema.json +14 -0
  269. package/generated/SpecPack.schema.json +118 -7
  270. package/generated/TaskContract.schema.json +224 -30
  271. package/generated/TestCommandInvocation.schema.json +45 -0
  272. package/generated/Thread.schema.json +74 -6
  273. package/generated/ThreadHeadPing.schema.json +40 -0
  274. package/generated/ThreadTurn.schema.json +24 -11
  275. package/generated/TrustConfig.schema.json +77 -0
  276. package/package.json +3 -2
  277. package/generated/ControlSetupJobConfirmRequest.schema.json +0 -18
  278. package/generated/ControlSpecFreezeRequest.schema.json +0 -83
package/dist/control.js CHANGED
@@ -1,34 +1,23 @@
1
1
  import { z } from "zod";
2
2
  import { AccessProfile, AuthPreference, ContentHash, ExternalContextPolicy, Id, ModeKind, NonBlankString, OutputReadyState, ProviderFamily, } from "./primitives.js";
3
- import { Portfolio } from "./budget.js";
4
- import { AdapterStatus, ConformanceCheck, EffortHint, HarnessManifest, HarnessModel, InteractionQuestion, } from "./harness.js";
3
+ import { AuthMode, PaidBudget, PaidFallback, QualityTierSet, RoutingGoal } from "./budget.js";
4
+ export { ControlQuotaResponse } from "./quota.js";
5
+ import { AuthRouteReason, AuthSourceKind } from "./auth.js";
6
+ import { EffortHint, HarnessModel, InteractionQuestion } from "./harness.js";
5
7
  import { ThreadState, ThreadTurnKind, WorkspaceMode } from "./thread.js";
6
8
  import { OrchestrateAutonomy } from "./orchestrate.js";
7
- import { AttachmentInput } from "./attachment.js";
8
- import { ProtectedPathApproval } from "./task.js";
9
- /** Project context depth. The "deep" tier never shipped a distinct behavior
10
- * (v0.15 triage): auto is the only mode; off exists solely on projections of
11
- * no-project runs. */
12
- export const RunScopeContext = z.enum(["auto"]).describe("Project context depth; auto is the only mode.");
13
- export const RunScope = z
14
- .discriminatedUnion("kind", [
15
- z
16
- .object({
17
- kind: z.literal("project"),
18
- root: z.string().describe("Absolute path of the project root."),
19
- context: RunScopeContext.default("auto"),
20
- })
21
- .strict()
22
- .describe("Run anchored to a project."),
23
- z.object({ kind: z.literal("none") }).strict().describe("Run with no project (pure ask)."),
24
- ])
25
- .describe("What the run operates on: a project (with root) or nothing.");
9
+ import { ResourceAttachmentRef } from "./attachment.js";
10
+ import { RequestRequirementResolution } from "./request-requirements.js";
11
+ import { ProtectedPathApproval, TestCommandInvocation } from "./task.js";
12
+ import { RunScope } from "./control-run-scope.js";
13
+ import { HarnessStatusDto } from "./readiness.js";
14
+ import { makeControlRunRetrySchemas } from "./control-run-retry.js";
26
15
  export const RunExecution = z
27
16
  .object({
28
17
  isolation: z
29
18
  .enum(["envelope", "live"])
30
19
  .default("envelope")
31
- .describe("Run isolation: envelope (isolated worktree under .claudexor/workspaces, the default) or live (the project tree itself)."),
20
+ .describe("Run isolation: envelope (isolated worktree in the external per-project runtime namespace, the default) or live (the project tree itself)."),
32
21
  })
33
22
  .strict()
34
23
  .describe("Execution isolation settings for a run.");
@@ -47,18 +36,28 @@ export const ControlReviewerPanelEntry = z
47
36
  export const ControlRunStartRequest = z
48
37
  .object({
49
38
  prompt: z.string().default("").describe("The user's prompt for the run."),
50
- /** Inbound files/images for this turn; the daemon resolves each to a scoped
51
- * on-disk Attachment before the run spec is built. */
39
+ /** Caller-supplied system-level instructions layered onto every
40
+ * task-producing lane (primary, candidate, planner, explorer,
41
+ * orchestrate-planner) — never reviewers, synthesis, or the auth smoke.
42
+ * Scanned by the inline-secret fence like the prompt (INV-062). */
43
+ instructions: z
44
+ .string()
45
+ .optional()
46
+ .describe("System-level instructions layered onto every task-producing lane; delivered natively (append-system-prompt / developer_instructions) or as a delimited prompt prefix."),
47
+ /** Immutable daemon resource ids; upload/finalize happens before enqueue. */
52
48
  attachments: z
53
- .array(AttachmentInput)
49
+ .array(ResourceAttachmentRef)
54
50
  .optional()
55
- .describe("Inbound files/images for this turn; the daemon resolves each to a scoped on-disk attachment before the run spec is built."),
51
+ .describe("Immutable daemon resource ids returned by upload finalize; paths and inline bytes are not accepted."),
56
52
  mode: ModeKind.default("agent"),
57
53
  scope: RunScope.default({ kind: "none" }),
58
54
  execution: RunExecution.default({ isolation: "envelope" }),
59
- harnesses: z.array(NonBlankString).optional().describe("Eligible harness pool for the run; omitted = engine auto-pools."),
55
+ harnesses: z
56
+ .array(NonBlankString)
57
+ .optional()
58
+ .describe("Eligible harness pool for the run; omitted = engine auto-pools."),
60
59
  primaryHarness: NonBlankString.optional().describe("Primary harness the run should prefer."),
61
- portfolio: Portfolio.optional(),
60
+ routingGoal: RoutingGoal.optional(),
62
61
  /** Scalar model convenience: expands to the RESOLVED PRIMARY harness only
63
62
  * (never the pool). With a multi-harness pool and no primary it is
64
63
  * rejected — use `models` instead (INV-103). */
@@ -79,10 +78,24 @@ export const ControlRunStartRequest = z
79
78
  .record(ProviderFamily, EffortHint)
80
79
  .optional()
81
80
  .describe("Per-provider-family reviewer effort overrides (legacy; reviewerPanel wins when present)."),
82
- n: z.number().int().positive().optional().describe("Race width: number of best-of-N candidates."),
83
- attempts: z.number().int().positive().nullable().optional().describe("Cap on convergence attempts; null = engine default."),
81
+ n: z
82
+ .number()
83
+ .int()
84
+ .positive()
85
+ .optional()
86
+ .describe("Race width: number of best-of-N candidates."),
87
+ attempts: z
88
+ .number()
89
+ .int()
90
+ .positive()
91
+ .nullable()
92
+ .optional()
93
+ .describe("Cap on convergence attempts; null = engine default."),
84
94
  /** agent flag: iterate until the convergence predicate is clean (no fixed cap). */
85
- untilClean: z.boolean().optional().describe("Agent flag: iterate until the convergence predicate is clean (no fixed cap)."),
95
+ untilClean: z
96
+ .boolean()
97
+ .optional()
98
+ .describe("Agent flag: iterate until the convergence predicate is clean (no fixed cap)."),
86
99
  /** audit flag: bounded read-only research swarm (the old `explore`). */
87
100
  swarm: z.boolean().optional().describe("Audit flag: bounded read-only research swarm."),
88
101
  /** agent flag: create-from-scratch intent (the old `create` mode). */
@@ -94,18 +107,20 @@ export const ControlRunStartRequest = z
94
107
  .enum(["auto", "always", "never"])
95
108
  .optional()
96
109
  .describe("Best-of-N synthesis policy: auto only synthesizes an extra candidate when n>=3 and candidates genuinely complement; always/never force it."),
97
- maxUsd: z.number().nonnegative().nullable().optional().describe("USD cap for the run; null = no cap."),
110
+ paidBudget: PaidBudget.optional().describe("Explicit incremental-cash budget for the run."),
98
111
  /** Requested access profile. Effective access is derived by the engine and never client-supplied. */
99
112
  access: AccessProfile.optional().describe("Requested access profile; effective access is derived by the engine and never client-supplied."),
100
113
  web: ExternalContextPolicy.optional().describe("Web policy for the run (alias of externalContextPolicy; must match when both set)."),
101
114
  externalContextPolicy: ExternalContextPolicy.optional().describe("External web/context policy for the run."),
102
- /** Opt this run into the agent-driven browser (Playwright MCP). Honored only
103
- * for browser-capable harnesses when web policy is not `off`. */
115
+ /** Opt this run into the agent-driven browser (Playwright MCP). */
104
116
  browser: z
105
117
  .boolean()
106
118
  .optional()
107
- .describe("Opt this run into the agent-driven browser; honored only for browser-capable harnesses when web policy is not off."),
108
- tests: z.array(NonBlankString).optional().describe("Test commands to run as deterministic gates."),
119
+ .describe("Request the agent-driven browser; preflight records per-lane effectiveness and refuses when no selected lane can receive it."),
120
+ tests: z
121
+ .array(TestCommandInvocation)
122
+ .optional()
123
+ .describe("Typed-argv commands to run as deterministic gates."),
109
124
  /** Typed per-run approval for changing auto-protected gate/test paths. This
110
125
  * does not bypass built-in critical/security path human gates. */
111
126
  protectedPathApprovals: z
@@ -123,6 +138,7 @@ export const ControlRunStartRequest = z
123
138
  * lineage; POST /threads/:id/turns is the public turn surface. */
124
139
  turnId: Id.optional().describe("Internal daemon handoff only; rejected (400) when supplied by a client on POST /runs — use POST /threads/:id/turns instead."),
125
140
  parentRunId: Id.optional().describe("Run this turn follows up on."),
141
+ retryOf: Id.optional().describe("Server-owned Exact Retry lineage; direct POST /runs rejects it."),
126
142
  /** When set, this turn implements an approved plan: the engine prefixes the
127
143
  * parent plan run's final/plan.md into the prompt (mode is forced to agent). */
128
144
  planRunId: Id.optional().describe("Internal daemon handoff only; rejected (400) on POST /runs. When set, the turn implements an approved plan from that run."),
@@ -136,6 +152,15 @@ export const ControlRunStartRequest = z
136
152
  .describe("Explicit reviewer panel; overrides the legacy reviewerModels/reviewerEfforts maps and preserves duplicate harness entries for multi-model same-provider reviews."),
137
153
  /** Per-run auth route override (subscription/api_key/auto). */
138
154
  authPreference: AuthPreference.optional().describe("Per-run auth route override."),
155
+ /** Explicit per-run credential profile (INV-135); resolved against the
156
+ * durable registry by the engine — an unknown/disabled/mismatched id is a
157
+ * typed refusal, never a silent default. */
158
+ credentialProfileId: z
159
+ .string()
160
+ .min(1)
161
+ .nullable()
162
+ .optional()
163
+ .describe("Explicit per-run credential profile id; null FORCES the engine-default ladder past a thread-sticky profile; unknown/disabled ids refuse, never default."),
139
164
  /** How much the orchestrate planner may act without confirmation
140
165
  * (suggest/auto_safe/auto_full). Only meaningful for mode=orchestrate;
141
166
  * consumed by the executor in runOrchestrate. */
@@ -148,130 +173,58 @@ export const ControlRunStartRequest = z
148
173
  .positive()
149
174
  .optional()
150
175
  .describe("Cap on orchestrate plan tool calls; only meaningful for mode=orchestrate."),
176
+ /** Hard wall-clock deadline for the WHOLE run, measured from scheduler
177
+ * start. On expiry the run is cooperatively cancelled (hard-kill fallback)
178
+ * and ends `cancelled` with reason `wall_clock_exceeded`, preserving partial
179
+ * artifacts. Distinct from the inactivity watchdog (per-attempt silence). */
180
+ maxSeconds: z
181
+ .number()
182
+ .int()
183
+ .positive()
184
+ // 7 days. Bounded so `maxSeconds * 1000` never exceeds setTimeout's 32-bit
185
+ // ms ceiling (~24.8 days), where an over-large delay silently wraps to 1ms
186
+ // and cancels the run almost immediately.
187
+ .max(604_800)
188
+ .optional()
189
+ .describe("Hard wall-clock deadline for the whole run (seconds, from scheduler start, max 7 days); on expiry the run is cancelled with reason wall_clock_exceeded and partial artifacts are kept."),
190
+ /** Per-run globs no candidate may touch at all (create/modify/delete).
191
+ * Envelope/isolated runs only: enforced by the engine's authoritative
192
+ * post-diff policy gate (violation → blocking finding → blocked, patch
193
+ * undelivered); an in-place run with denyPaths is refused at preflight
194
+ * because a live tree cannot guarantee pre-delivery containment. Native
195
+ * per-lane enforcement is disclosed via path_deny receipts (postdiff_only
196
+ * until an adapter supports native deny). accept_risk MAY still deliver a
197
+ * violating patch (INV-111: the human is the final authority). */
198
+ denyPaths: z
199
+ .array(z.string().min(1))
200
+ .optional()
201
+ .describe("Per-run globs no candidate may touch at all; envelope-only (in-place runs are refused), enforced by the engine post-diff gate, disclosed per lane via path_deny receipts. accept_risk may still deliver (INV-111)."),
202
+ /** JSON Schema the run's final answer must conform to. MANDATORY when
203
+ * present: a selected lane that cannot natively constrain its output is a
204
+ * typed preflight refusal, never best-effort. The engine validates the
205
+ * winner's answer once, writes final/output.json, and reports a typed
206
+ * conformance receipt (outputConformance); a non-conformant answer ends
207
+ * success-with-warnings so the embedder can retry. Unsupported schema
208
+ * shapes ($ref, non-object root) are refused at the boundary. */
209
+ outputSchema: z
210
+ .record(z.unknown())
211
+ .optional()
212
+ .describe("JSON Schema for the run's final answer; mandatory (incapable lane => preflight refusal), engine-validated into final/output.json with a typed outputConformance receipt."),
213
+ /** Per-run turn cap; beats per-harness settings, and a lane without native
214
+ * max_turns support discloses the ignored knob instead of dropping it. */
215
+ maxTurns: z
216
+ .number()
217
+ .int()
218
+ .positive()
219
+ .optional()
220
+ .describe("Per-run turn cap (beats per-harness settings)."),
151
221
  })
152
222
  .strict()
153
223
  .describe("Request body for POST /runs: prompt, mode, scope, routing, strategy flags, budget, policies, and spec/thread linkage.");
154
- /** Harness ids that have a managed setup flow (shared by the async setup-jobs path). */
155
- export const ControlHarnessSetupHarness = z
156
- .enum(["codex", "claude", "cursor", "opencode", "raw"])
157
- .describe("Harness ids that have a managed setup flow.");
158
- export const ControlSetupJobAction = z
159
- .enum(["install", "login", "doctor", "store_key"])
160
- .describe("Setup action to perform: install the vendor CLI, log in, run the doctor, or store an API key.");
161
- export const ControlSetupJobState = z
162
- .enum(["queued", "running", "waiting_for_input", "succeeded", "failed", "cancelled", "not_supported"])
163
- .describe("Lifecycle state of a setup job, including waiting_for_input (needs user confirmation/input) and not_supported.");
164
- export const ControlSetupJobCreateRequest = z
165
- .object({
166
- harness: ControlHarnessSetupHarness,
167
- action: ControlSetupJobAction,
168
- })
169
- .strict()
170
- .describe("Request body to create a harness setup job.");
171
- export const ControlSetupJob = z
172
- .object({
173
- jobId: Id.describe("Setup job id."),
174
- harness: ControlHarnessSetupHarness,
175
- action: ControlSetupJobAction,
176
- state: ControlSetupJobState,
177
- command: z.string().nullable().default(null).describe("Shell command the job runs, when applicable."),
178
- guideUrl: z.string().url().nullable().default(null).describe("Vendor guide URL for manual steps, when applicable."),
179
- logPath: z.string().nullable().default(null).describe("On-disk log path for the job's output."),
180
- message: z.string().describe("Human-readable status message."),
181
- riskFlags: z.array(z.string()).default([]).describe("Risk flags for the action (e.g. network_download, shell_pipe), surfaced before confirmation."),
182
- requiresConfirmation: z.boolean().default(false).describe("Whether the job waits for explicit user confirmation before running."),
183
- createdAt: z.string().describe("When the job was created."),
184
- startedAt: z.string().nullable().default(null).describe("When the job started running."),
185
- firstOutputAt: z.string().nullable().default(null).describe("When the job produced its first output."),
186
- lastOutputAt: z.string().nullable().default(null).describe("When the job last produced output."),
187
- finishedAt: z.string().nullable().default(null).describe("When the job finished."),
188
- retryCount: z.number().int().nonnegative().default(0).describe("How many times the job was retried."),
189
- })
190
- .strict()
191
- .describe("One managed harness setup job (install/login/doctor/store_key) with its lifecycle timestamps.");
192
- export const ControlSetupJobEvent = z
193
- .object({
194
- jobId: Id.describe("Setup job the event belongs to."),
195
- seq: z.number().int().nonnegative().describe("Monotonic event sequence within the job."),
196
- time: z.string().describe("Event timestamp."),
197
- /** Only "status" is ever produced (v0.15 triage: the log/end kinds had no
198
- * producer; SSE stream end is a transport frame, not a payload kind). */
199
- kind: z.enum(["status"]).describe("Event kind; only status is ever produced."),
200
- state: ControlSetupJobState.optional(),
201
- message: z.string().describe("Human-readable status message."),
202
- })
203
- .strict()
204
- .describe("Status event on a setup job's event stream.");
205
- export const ControlSetupJobListResponse = z
206
- .object({
207
- jobs: z.array(ControlSetupJob).describe("All known setup jobs."),
208
- })
209
- .describe("Response for listing setup jobs.");
210
- export const ControlSetupJobConfirmRequest = z
211
- .object({
212
- confirmed: z.boolean().default(true).describe("Whether the user confirmed the pending action."),
213
- })
214
- .strict()
215
- .describe("Request body confirming a setup job that waits for user confirmation.");
216
- export const ControlSpecQuestionsRequest = z
217
- .object({
218
- prompt: z.string().describe("The user's request the interview is clarifying."),
219
- scope: z
220
- .object({
221
- kind: z.literal("project"),
222
- root: z.string().describe("Absolute path of the project root."),
223
- context: RunScopeContext.default("auto"),
224
- })
225
- .strict()
226
- .describe("Project the spec is about."),
227
- harnesses: z.array(NonBlankString).optional().describe("Harnesses eligible to generate the questions."),
228
- /** Already-answered decisions from prior tiers; carried so each round goes
229
- * DEEPER instead of re-asking (multi-tier adaptive interview). */
230
- priorDecisions: z
231
- .array(z.object({
232
- question: z.string().describe("Question asked in a prior tier."),
233
- answer: z.string().describe("The user's answer."),
234
- }))
235
- .optional()
236
- .describe("Already-answered decisions from prior tiers, carried so each round goes deeper instead of re-asking."),
237
- })
238
- .strict()
239
- .describe("Request body to generate the next tier of spec interview questions.");
240
- export const ControlSpecFreezeRequest = z
241
- .object({
242
- prompt: z.string().describe("The user's request the spec captures."),
243
- scope: z
244
- .object({
245
- kind: z.literal("project"),
246
- root: z.string().describe("Absolute path of the project root."),
247
- context: RunScopeContext.default("auto"),
248
- })
249
- .strict()
250
- .describe("Project the spec is about."),
251
- planDir: z.string().optional().describe("Directory to write the frozen spec artifacts into."),
252
- plan: z.string().optional().describe("Plan text to fold into the spec."),
253
- answers: z.array(z.unknown()).optional().describe("Interview answers for the current tier."),
254
- /** Accumulated prior-tier interview decisions. Folded into the frozen
255
- * SpecPack's decided_tradeoffs so a MULTI-TIER spec carries every tier, not
256
- * just the last (mirror of ControlSpecQuestionsRequest.priorDecisions). */
257
- priorDecisions: z
258
- .array(z.object({
259
- question: z.string().describe("Question asked in a prior tier."),
260
- answer: z.string().describe("The user's answer."),
261
- }))
262
- .optional()
263
- .describe("Accumulated prior-tier interview decisions, folded into the frozen SpecPack's decided tradeoffs."),
264
- })
265
- .strict()
266
- .describe("Request body to freeze a SpecPack from the interview.");
267
- export const ControlRunStartInfo = z
268
- .object({
269
- jobId: z.string().optional().describe("Daemon job id backing the run."),
270
- runId: z.string().describe("Run id."),
271
- taskId: z.string().optional().describe("Task id, when already allocated."),
272
- runDir: z.string().describe("On-disk run artifact directory."),
273
- })
274
- .describe("Response for a successfully enqueued run.");
224
+ const RunRetrySchemas = makeControlRunRetrySchemas(ControlRunStartRequest);
225
+ export const ControlRunStartInfo = RunRetrySchemas.startInfo;
226
+ export const ControlRunRetryResponse = RunRetrySchemas.response;
227
+ export const ControlRunAgainDraft = RunRetrySchemas.draft;
275
228
  export const ControlRunState = z
276
229
  .enum([
277
230
  "queued",
@@ -283,12 +236,14 @@ export const ControlRunState = z
283
236
  "review_not_run",
284
237
  "failed",
285
238
  "cancelled",
286
- "interrupted",
239
+ "interrupted_unknown",
240
+ "cost_unverifiable",
241
+ "exhausted_overshoot",
287
242
  "exhausted",
288
243
  "not_converged",
289
244
  "stuck_no_progress",
290
245
  ])
291
- .describe("Control-plane run state: queued/running while live; blocked awaiting a human; then a terminal outcome (succeeded, no_op, ungated, review_not_run, failed, cancelled, interrupted, exhausted, not_converged, stuck_no_progress).");
246
+ .describe("Control-plane run state: queued/running while live, then an honest success, block, failure, interruption, cost, exhaustion, non-convergence, or cancellation terminal.");
292
247
  export const ControlQueuedRunInfo = z
293
248
  .object({
294
249
  jobId: z.string().describe("Daemon job id."),
@@ -314,22 +269,50 @@ export const RunFailure = z
314
269
  ])
315
270
  .default("unknown")
316
271
  .describe("Typed failure category (validation, project, auth, harness, budget, policy, cancelled, internal, unknown)."),
317
- harnessId: z.string().nullable().default(null).describe("Harness involved in the failure, when known."),
318
- attemptId: z.string().nullable().default(null).describe("Attempt involved in the failure, when known."),
272
+ harnessId: z
273
+ .string()
274
+ .nullable()
275
+ .default(null)
276
+ .describe("Harness involved in the failure, when known."),
277
+ attemptId: z
278
+ .string()
279
+ .nullable()
280
+ .default(null)
281
+ .describe("Attempt involved in the failure, when known."),
319
282
  safeMessage: z.string().describe("Redacted human-readable failure message."),
320
- rawDetailRef: z.string().nullable().default(null).describe("Artifact path holding the raw (redacted) failure detail."),
321
- logRefs: z.array(z.string()).default([]).describe("Log artifact paths relevant to the failure."),
322
- eventRefs: z.array(z.string()).default([]).describe("Event references relevant to the failure."),
283
+ rawDetailRef: z
284
+ .string()
285
+ .nullable()
286
+ .default(null)
287
+ .describe("Artifact path holding the raw (redacted) failure detail."),
288
+ logRefs: z
289
+ .array(z.string())
290
+ .default([])
291
+ .describe("Log artifact paths relevant to the failure."),
292
+ eventRefs: z
293
+ .array(z.string())
294
+ .default([])
295
+ .describe("Event references relevant to the failure."),
323
296
  runDir: z.string().nullable().default(null).describe("Run artifact directory."),
324
297
  nextActions: z.array(z.string()).default([]).describe("Suggested operator next actions."),
325
298
  })
326
299
  .describe("Typed failure record for a run: phase, category, evidence references, and suggested next actions.");
327
300
  export const ControlProjectMetadata = z
328
301
  .object({
329
- kind: z.enum(["project", "none"]).default("none").describe("Whether the run was anchored to a project."),
302
+ kind: z
303
+ .enum(["project", "none"])
304
+ .default("none")
305
+ .describe("Whether the run was anchored to a project."),
330
306
  root: z.string().nullable().default(null).describe("Project root, when anchored."),
331
- projectName: z.string().nullable().default(null).describe("Project display name, when anchored."),
332
- context: z.enum(["off", "auto"]).default("off").describe("Project context depth used for the run."),
307
+ projectName: z
308
+ .string()
309
+ .nullable()
310
+ .default(null)
311
+ .describe("Project display name, when anchored."),
312
+ context: z
313
+ .enum(["off", "auto"])
314
+ .default("off")
315
+ .describe("Project context depth used for the run."),
333
316
  })
334
317
  .describe("Project metadata projected onto a run summary.");
335
318
  export const ControlWebEvidence = z
@@ -340,15 +323,34 @@ export const ControlWebEvidence = z
340
323
  /** Mode the selected route actually executed (disclosed upgrades, e.g. claude cached->live). */
341
324
  effectiveMode: ExternalContextPolicy.default("auto").describe("Policy the selected route actually executed (disclosed upgrades, e.g. cached to live)."),
342
325
  attempted: z.boolean().default(false).describe("Whether any web activity was attempted."),
343
- satisfied: z.boolean().default(false).describe("Whether the web-evidence requirement was satisfied."),
326
+ satisfied: z
327
+ .boolean()
328
+ .default(false)
329
+ .describe("Whether the web-evidence requirement was satisfied."),
344
330
  status: z
345
331
  .enum(["none", "attempted", "satisfied", "failed", "unverified"])
346
332
  .default("none")
347
333
  .describe("Web-evidence verdict for the run."),
348
- tool: z.string().nullable().default(null).describe("Web tool that produced the evidence, when any."),
349
- target: z.string().nullable().default(null).describe("Redacted target (query/url) of the web activity, when any."),
350
- errorSummary: z.string().nullable().default(null).describe("Redacted error detail when web activity failed."),
351
- rawDetailRef: z.string().nullable().default(null).describe("Artifact path holding the raw (redacted) evidence detail."),
334
+ tool: z
335
+ .string()
336
+ .nullable()
337
+ .default(null)
338
+ .describe("Web tool that produced the evidence, when any."),
339
+ target: z
340
+ .string()
341
+ .nullable()
342
+ .default(null)
343
+ .describe("Redacted target (query/url) of the web activity, when any."),
344
+ errorSummary: z
345
+ .string()
346
+ .nullable()
347
+ .default(null)
348
+ .describe("Redacted error detail when web activity failed."),
349
+ rawDetailRef: z
350
+ .string()
351
+ .nullable()
352
+ .default(null)
353
+ .describe("Artifact path holding the raw (redacted) evidence detail."),
352
354
  /** False when the run predates telemetry.yaml; surfaces must render "telemetry unavailable". */
353
355
  available: z
354
356
  .boolean()
@@ -364,7 +366,11 @@ export const ControlWebEvidence = z
364
366
  export const ControlRouteInfo = z
365
367
  .object({
366
368
  requestedModel: z.string().nullable().default(null).describe("Model requested for the run."),
367
- observedModel: z.string().nullable().default(null).describe("Model the harness stream actually reported."),
369
+ observedModel: z
370
+ .string()
371
+ .nullable()
372
+ .default(null)
373
+ .describe("Model the harness stream actually reported."),
368
374
  harnessId: z.string().nullable().default(null).describe("Harness that ran the final attempt."),
369
375
  verified: z
370
376
  .boolean()
@@ -408,7 +414,12 @@ export const ControlRunResult = z
408
414
  .nullable()
409
415
  .default(null)
410
416
  .describe("Diff statistics; null unless a patch exists."),
411
- blockers: z.number().int().nonnegative().default(0).describe("Count of accepted blocking review findings."),
417
+ blockers: z
418
+ .number()
419
+ .int()
420
+ .nonnegative()
421
+ .default(0)
422
+ .describe("Count of accepted blocking review findings."),
412
423
  /** True when the live in-place tree was mutated this turn (regardless of review). */
413
424
  adopted: z
414
425
  .boolean()
@@ -417,21 +428,19 @@ export const ControlRunResult = z
417
428
  .describe("True when the live in-place tree was mutated this turn (regardless of review); null when unknown."),
418
429
  /** Honest application state (decoupled from clean-terminal). */
419
430
  applyState: RunApplyState.default("not_applied"),
420
- /** Tree SHA before this turn mutated the in-place tree (revert restore target). */
421
431
  preTurnSha: z
422
432
  .string()
423
433
  .nullable()
424
434
  .default(null)
425
435
  .describe("Tree SHA before this turn mutated the in-place tree (revert restore target)."),
426
- /** Tree SHA right after this turn's mutation (revert divergence fence: refuse
427
- * to revert if the working tree has diverged from this since). */
428
436
  postTurnSha: z
429
437
  .string()
430
438
  .nullable()
431
439
  .default(null)
432
440
  .describe("Tree SHA right after this turn's mutation; revert refuses if the working tree has diverged from this since."),
441
+ revertAnchorId: z.string().nullable().default(null).describe("GC-independent revert anchor."),
433
442
  /** Revert metadata is available (the turn mutated the live tree in place and
434
- * pre/post-turn snapshots were recorded), so a Revert affordance may be offered.
443
+ * an external patch anchor was recorded), so a Revert affordance may be offered.
435
444
  * This is NOT a live-safe guarantee: the server re-checks tree divergence at
436
445
  * revert time and refuses (fail loud) if the working tree changed since. */
437
446
  revertable: z
@@ -448,7 +457,9 @@ export const ControlRunSummary = z
448
457
  state: ControlRunState,
449
458
  runDir: z.string().optional().describe("On-disk run artifact directory."),
450
459
  error: z.string().optional().describe("Error message, when the run failed."),
451
- failure: RunFailure.nullable().default(null).describe("Typed failure record; null unless the run failed."),
460
+ failure: RunFailure.nullable()
461
+ .default(null)
462
+ .describe("Typed failure record; null unless the run failed."),
452
463
  project: ControlProjectMetadata.default({}),
453
464
  mode: ModeKind.optional(),
454
465
  /** v0.9 engine strategy on the mode (flags, not modes): race width / repair caps / swarm / create. */
@@ -460,14 +471,79 @@ export const ControlRunSummary = z
460
471
  prompt: z.string().optional().describe("The user's prompt for the run."),
461
472
  harnesses: z.array(z.string()).optional().describe("Harness pool the run used."),
462
473
  primaryHarness: z.string().optional().describe("Primary harness the run preferred."),
463
- portfolio: Portfolio.optional(),
474
+ routingGoal: RoutingGoal.optional(),
464
475
  model: z.string().optional().describe("Scalar model requested for the run."),
465
- reviewerPanel: z.array(ControlReviewerPanelEntry).optional().describe("Explicit reviewer panel used for the run."),
466
- protectedPathApprovals: z.array(ProtectedPathApproval).optional().describe("Per-run protected-path approvals supplied."),
476
+ reviewerPanel: z
477
+ .array(ControlReviewerPanelEntry)
478
+ .optional()
479
+ .describe("Explicit reviewer panel used for the run."),
480
+ protectedPathApprovals: z
481
+ .array(ProtectedPathApproval)
482
+ .optional()
483
+ .describe("Per-run protected-path approvals supplied."),
467
484
  n: z.number().int().optional().describe("Race width, when the run was a race."),
468
- maxUsd: z.number().nullable().optional().describe("USD cap for the run; null = no cap."),
469
- spendUsd: z.number().nullable().optional().describe("Settled spend in USD; null when unknown."),
470
- spendEstimated: z.boolean().optional().describe("True when spend is token-derived rather than natively reported."),
485
+ paidBudget: PaidBudget.optional().describe("Explicit incremental-cash budget for the run."),
486
+ spendUsd: z.number().nullable().optional().describe("Settled cash in USD; null when unknown."),
487
+ spendEstimated: z
488
+ .boolean()
489
+ .optional()
490
+ .describe("True when spend is token-derived rather than natively reported."),
491
+ /** Token usage summed across every attempt (money stays in spendUsd). Each
492
+ * field null until a harness reported it — never render null as 0, and never
493
+ * sum into a grand total (codex cached ⊆ input; claude cached disjoint). */
494
+ inputTokens: z
495
+ .number()
496
+ .int()
497
+ .nonnegative()
498
+ .nullable()
499
+ .optional()
500
+ .describe("Input tokens summed across all attempts; null when no harness reported them."),
501
+ outputTokens: z
502
+ .number()
503
+ .int()
504
+ .nonnegative()
505
+ .nullable()
506
+ .optional()
507
+ .describe("Output tokens summed across all attempts; null when no harness reported them."),
508
+ cachedInputTokens: z
509
+ .number()
510
+ .int()
511
+ .nonnegative()
512
+ .nullable()
513
+ .optional()
514
+ .describe("Cached input tokens summed across all attempts; null when no harness reported them."),
515
+ /** Typed conformance receipt for a run started with outputSchema: passed =
516
+ * final/output.json conforms; failed = the answer was missing, unparsable,
517
+ * or non-conformant (the run still ends success-with-warnings — the
518
+ * embedder retries). Null when the run had no structured-output contract. */
519
+ outputConformance: z
520
+ .enum(["passed", "failed"])
521
+ .nullable()
522
+ .optional()
523
+ .describe("Structured-output conformance receipt (passed/failed); null when the run had no outputSchema."),
524
+ /** Auth ROUTE RECEIPT (INV-061 disclosure) projected verbatim from the
525
+ * engine telemetry: requested preference, effective route/source the
526
+ * deciding attempt disclosed, and a deterministic typed reason. Null on
527
+ * runs whose telemetry predates the receipt. */
528
+ authRoute: z
529
+ .object({
530
+ requested: AuthPreference,
531
+ effective: AuthMode.nullable().default(null),
532
+ source: AuthSourceKind.nullable().default(null),
533
+ reason: AuthRouteReason,
534
+ harnessId: z.string().nullable().default(null),
535
+ attemptId: z.string().nullable().default(null),
536
+ /** Requested-vs-observed model mismatch on the deciding attempt; null
537
+ * when they match or either side is unknown. */
538
+ modelMismatch: z
539
+ .object({ requested: z.string(), observed: z.string() })
540
+ .nullable()
541
+ .default(null)
542
+ .describe("Requested-vs-observed model mismatch; null when none."),
543
+ })
544
+ .nullable()
545
+ .optional()
546
+ .describe("Auth route receipt (requested/effective/source/reason + disclosing attempt), projected verbatim from telemetry; null when unavailable."),
471
547
  access: AccessProfile.optional().describe("Access profile of the run: the effective profile when known, else the requested one (prefer requestedAccess/effectiveAccess)."),
472
548
  requestedAccess: AccessProfile.optional().describe("Access profile the caller requested."),
473
549
  effectiveAccess: AccessProfile.optional().describe("Access profile actually enforced by the engine."),
@@ -475,7 +551,14 @@ export const ControlRunSummary = z
475
551
  webRequired: z.boolean().optional().describe("Whether the run required web evidence."),
476
552
  webMode: ExternalContextPolicy.optional().describe("Web policy actually executed by the selected route."),
477
553
  webEvidence: ControlWebEvidence.default({}),
478
- toolPermissionPolicy: z.record(z.string(), z.unknown()).optional().describe("Tool allow/deny policy applied to the run."),
554
+ requestRequirements: z
555
+ .array(RequestRequirementResolution)
556
+ .default([])
557
+ .describe("Engine-computed requested/effective capability receipts for selected harness lanes."),
558
+ toolPermissionPolicy: z
559
+ .record(z.string(), z.unknown())
560
+ .optional()
561
+ .describe("Tool allow/deny policy applied to the run."),
479
562
  outputReadyState: OutputReadyState.default("pending"),
480
563
  /** Non-blocking tool warnings projected from final/telemetry.yaml. */
481
564
  toolWarningsTotal: z
@@ -487,10 +570,15 @@ export const ControlRunSummary = z
487
570
  /** Honest terminal outcome (what the turn did): patch/answer/plan/report/none. */
488
571
  result: ControlRunResult.default({}),
489
572
  /** True while at least one interaction.requested has no answered/timeout. */
490
- waitingOnUser: z.boolean().default(false).describe("True while at least one interactive question is awaiting the user's answer."),
573
+ waitingOnUser: z
574
+ .boolean()
575
+ .default(false)
576
+ .describe("True while at least one interactive question is awaiting the user's answer."),
491
577
  /** Route evidence from telemetry; null when no telemetry exists (legacy). */
492
- route: ControlRouteInfo.nullable().default(null).describe("Route evidence from telemetry; null when no telemetry exists (legacy runs)."),
493
- tests: z.array(z.string()).optional().describe("Test commands configured as gates."),
578
+ route: ControlRouteInfo.nullable()
579
+ .default(null)
580
+ .describe("Route evidence from telemetry; null when no telemetry exists (legacy runs)."),
581
+ tests: z.array(TestCommandInvocation).optional().describe("Typed argv gates."),
494
582
  specId: z.string().optional().describe("SpecPack id the run was held to."),
495
583
  specHash: ContentHash.optional().describe("Content hash of the SpecPack the run was held to."),
496
584
  createdAt: z.string().optional().describe("When the run was created."),
@@ -501,8 +589,8 @@ export const ControlRunSummary = z
501
589
  export const ControlPrimaryOutput = z
502
590
  .object({
503
591
  kind: z
504
- .enum(["answer", "report", "plan", "summary", "patch", "diagnostic"])
505
- .describe("What kind of output this is: answer, report, plan, summary, patch, or diagnostic."),
592
+ .enum(["answer", "report", "plan", "summary", "patch", "diagnostic", "structured_output"])
593
+ .describe("What kind of output this is: answer, report, plan, summary, patch, diagnostic, or structured_output (schema-conformant final/output.json)."),
506
594
  path: z.string().describe("Artifact path of the output."),
507
595
  text: z.string().nullable().default(null).describe("Inline text content, when loaded."),
508
596
  bytes: z.number().int().nonnegative().optional().describe("Size of the output in bytes."),
@@ -516,19 +604,41 @@ export const ControlTimelineEvent = z
516
604
  attemptId: z.string().nullable().default(null).describe("Attempt involved, when any."),
517
605
  title: z.string().describe("Human-readable event title."),
518
606
  detail: z.string().nullable().default(null).describe("Human-readable event detail."),
519
- severity: z.enum(["info", "warning", "error"]).default("info").describe("Display severity of the event."),
607
+ severity: z
608
+ .enum(["info", "warning", "error"])
609
+ .default("info")
610
+ .describe("Display severity of the event."),
520
611
  toolName: z.string().nullable().default(null).describe("Tool name for tool events."),
521
612
  target: z.string().nullable().default(null).describe("Redacted tool target for tool events."),
522
- errorSummary: z.string().nullable().default(null).describe("Redacted error detail for error events."),
523
- rawRef: z.string().nullable().default(null).describe("Reference to the raw underlying event/artifact."),
613
+ errorSummary: z
614
+ .string()
615
+ .nullable()
616
+ .default(null)
617
+ .describe("Redacted error detail for error events."),
618
+ rawRef: z
619
+ .string()
620
+ .nullable()
621
+ .default(null)
622
+ .describe("Reference to the raw underlying event/artifact."),
524
623
  })
525
624
  .describe("One projected timeline row of a run for display.");
526
625
  export const ControlBudgetSnapshot = z
527
626
  .object({
528
- maxUsd: z.number().nullable().default(null).describe("USD cap for the run; null = no cap."),
529
- spendUsd: z.number().nullable().default(null).describe("Spend so far in USD; null when unknown."),
530
- remainingUsd: z.number().nullable().default(null).describe("Remaining budget in USD; null when no cap or unknown spend."),
531
- estimated: z.boolean().default(false).describe("True when spend is token-derived rather than natively reported."),
627
+ paidBudget: PaidBudget.default({ kind: "unlimited" }),
628
+ spendUsd: z
629
+ .number()
630
+ .nullable()
631
+ .default(null)
632
+ .describe("CASH spend so far in USD; null when unknown."),
633
+ remainingUsd: z
634
+ .number()
635
+ .nullable()
636
+ .default(null)
637
+ .describe("Remaining budget in USD; null when no cap or unknown spend."),
638
+ estimated: z
639
+ .boolean()
640
+ .default(false)
641
+ .describe("True when spend is token-derived rather than natively reported."),
532
642
  source: z
533
643
  .enum(["decision", "events", "settings", "unknown"])
534
644
  .default("unknown")
@@ -539,11 +649,19 @@ export const ControlArtifactInfo = z
539
649
  .object({
540
650
  path: z.string().describe("Artifact path relative to the run directory."),
541
651
  kind: z.enum(["file", "directory"]).describe("Whether the artifact is a file or a directory."),
542
- bytes: z.number().int().nonnegative().optional().describe("Size in bytes; absent for directories."),
652
+ bytes: z
653
+ .number()
654
+ .int()
655
+ .nonnegative()
656
+ .optional()
657
+ .describe("Size in bytes; absent for directories."),
543
658
  /** Clean MIME type derived from the extension (e.g. `image/png`, `text/plain`,
544
659
  * `application/pdf`); lets a gallery render text vs image vs pdf. Absent for
545
660
  * directories. */
546
- mime: z.string().optional().describe("MIME type derived from the extension; absent for directories."),
661
+ mime: z
662
+ .string()
663
+ .optional()
664
+ .describe("MIME type derived from the extension; absent for directories."),
547
665
  })
548
666
  .describe("One artifact in the run's artifact tree.");
549
667
  /** A live interaction awaiting the user's answer (snapshot projection). */
@@ -551,12 +669,24 @@ export const ControlPendingInteraction = z
551
669
  .object({
552
670
  interactionId: Id.describe("Interaction id used to answer."),
553
671
  runId: Id.describe("Run the interaction belongs to."),
554
- attemptId: z.string().nullable().default(null).describe("Attempt the interaction was raised in, when known."),
672
+ attemptId: z
673
+ .string()
674
+ .nullable()
675
+ .default(null)
676
+ .describe("Attempt the interaction was raised in, when known."),
555
677
  harnessId: z.string().nullable().default(null).describe("Harness that raised the interaction."),
556
- sourceTool: z.string().nullable().default(null).describe("Native tool that raised the request."),
678
+ sourceTool: z
679
+ .string()
680
+ .nullable()
681
+ .default(null)
682
+ .describe("Native tool that raised the request."),
557
683
  questions: z.array(InteractionQuestion).default([]).describe("Questions awaiting answers."),
558
684
  requestedAt: z.string().describe("When the interaction was requested."),
559
- timeoutAt: z.string().nullable().default(null).describe("When the interaction times out into a benign decline; null = no timeout."),
685
+ timeoutAt: z
686
+ .string()
687
+ .nullable()
688
+ .default(null)
689
+ .describe("When the interaction times out into a benign decline; null = no timeout."),
560
690
  })
561
691
  .describe("A live interactive question awaiting the user's answer (snapshot projection).");
562
692
  export const ControlInteractionAnswerRequest = z
@@ -565,8 +695,15 @@ export const ControlInteractionAnswerRequest = z
565
695
  .array(z
566
696
  .object({
567
697
  questionId: Id.describe("Id of the question being answered."),
568
- selectedLabels: z.array(z.string()).default([]).describe("Labels of the selected options."),
569
- freeText: z.string().nullable().default(null).describe("Free-text answer; null when only options were selected."),
698
+ selectedLabels: z
699
+ .array(z.string())
700
+ .default([])
701
+ .describe("Labels of the selected options."),
702
+ freeText: z
703
+ .string()
704
+ .nullable()
705
+ .default(null)
706
+ .describe("Free-text answer; null when only options were selected."),
570
707
  })
571
708
  .strict())
572
709
  .default([])
@@ -618,9 +755,15 @@ export const ControlRunControlResponse = z
618
755
  .describe("Response to a run control request.");
619
756
  export const ApplyTarget = z
620
757
  .discriminatedUnion("kind", [
621
- z.object({ kind: z.literal("original_project") }).strict().describe("Apply to the project the run originally came from."),
622
758
  z
623
- .object({ kind: z.literal("project"), root: z.string().describe("Absolute path of the target project root.") })
759
+ .object({ kind: z.literal("original_project") })
760
+ .strict()
761
+ .describe("Apply to the project the run originally came from."),
762
+ z
763
+ .object({
764
+ kind: z.literal("project"),
765
+ root: z.string().describe("Absolute path of the target project root."),
766
+ })
624
767
  .strict()
625
768
  .describe("Apply to an explicitly named project root."),
626
769
  ])
@@ -663,11 +806,17 @@ export const ControlRunDecisionRequest = z
663
806
  .object({
664
807
  action: RunDecisionAction,
665
808
  /** Findings the decision targets (override/accept_risk). */
666
- findingIds: z.array(Id).default([]).describe("Findings the decision targets (override/accept_risk)."),
809
+ findingIds: z
810
+ .array(Id)
811
+ .default([])
812
+ .describe("Findings the decision targets (override/accept_risk)."),
667
813
  /** Reviewer feedback to seed a rerun turn. */
668
814
  feedback: z.string().optional().describe("Reviewer feedback to seed a rerun turn."),
669
815
  /** Risk reasons being explicitly accepted (recorded, never silent). */
670
- acceptedRisks: z.array(z.string()).default([]).describe("Risk reasons being explicitly accepted (recorded, never silent)."),
816
+ acceptedRisks: z
817
+ .array(z.string())
818
+ .default([])
819
+ .describe("Risk reasons being explicitly accepted (recorded, never silent)."),
671
820
  /** Apply mode + target for accept_clean_patch. */
672
821
  applyMode: z
673
822
  .enum(["artifact_only", "apply", "branch", "commit", "pr"])
@@ -693,19 +842,42 @@ export const ControlThread = z
693
842
  .object({
694
843
  id: Id.describe("Thread id."),
695
844
  title: z.string().nullable().default(null).describe("Thread title; null until set."),
696
- repoRoot: z.string().nullable().default(null).describe("Project root the thread is anchored to; null for a no-project thread."),
845
+ repoRoot: z
846
+ .string()
847
+ .nullable()
848
+ .default(null)
849
+ .describe("Project root the thread is anchored to; null for a no-project thread."),
697
850
  mode: ModeKind.optional().describe("Default mode for new turns."),
698
851
  /** How turns touch files (in-place live tree vs isolated worktree). */
699
852
  workspaceMode: WorkspaceMode.default("in_place"),
700
853
  authPreference: AuthPreference.default("auto"),
701
- primaryHarness: z.string().nullable().default(null).describe("Sticky primary harness for the thread; null = engine routing."),
854
+ primaryHarness: z
855
+ .string()
856
+ .nullable()
857
+ .default(null)
858
+ .describe("Sticky primary harness for the thread; null = engine routing."),
702
859
  /** Sticky eligible pool for the thread (empty => engine auto-pools). */
703
- eligibleHarnesses: z.array(z.string()).default([]).describe("Sticky eligible harness pool; empty = the engine auto-pools."),
860
+ eligibleHarnesses: z
861
+ .array(z.string())
862
+ .default([])
863
+ .describe("Sticky eligible harness pool; empty = the engine auto-pools."),
864
+ /** Sticky credential profile (INV-135): clients can SET it via
865
+ * create/PATCH, so the projection must report it back (round-15 #3). */
866
+ credentialProfileId: Id.nullable()
867
+ .default(null)
868
+ .describe("Sticky credential profile for the thread; per-turn selection wins, null = engine-default credentials."),
704
869
  state: ThreadState.default("active"),
870
+ trashedAt: z.string().nullable().default(null).describe("When the thread entered trash."),
871
+ purgeAfter: z.string().nullable().default(null).describe("When trash retention expires."),
705
872
  runIds: z.array(Id).default([]).describe("Ordered run lineage of the thread."),
706
- headRunId: Id.nullable().default(null).describe("Most recent run of the thread; null before the first turn runs."),
873
+ headRunId: Id.nullable()
874
+ .default(null)
875
+ .describe("Most recent run of the thread; null before the first turn runs."),
707
876
  /** True when the head turn is blocked on a human decision (needs-me inbox). */
708
- needsHuman: z.boolean().default(false).describe("True when the head turn is blocked on a human decision (needs-me inbox)."),
877
+ needsHuman: z
878
+ .boolean()
879
+ .default(false)
880
+ .describe("True when the head turn is blocked on a human decision (needs-me inbox)."),
709
881
  createdAt: z.string().describe("When the thread was created."),
710
882
  updatedAt: z.string().describe("When the thread was last updated."),
711
883
  })
@@ -715,9 +887,26 @@ export const ControlSession = z
715
887
  id: Id.describe("Session id."),
716
888
  threadId: Id.describe("Thread the session belongs to."),
717
889
  harnessId: Id.describe("Harness the session is bound to."),
718
- nativeSessionId: z.string().nullable().default(null).describe("The vendor CLI session id; null when none exists."),
719
- observedModel: z.string().nullable().default(null).describe("Model last observed on the session's stream."),
720
- state: z.enum(["live", "stale", "rebound"]).default("live").describe("Session cache state: live, stale, or rebound."),
890
+ nativeSessionId: z
891
+ .string()
892
+ .nullable()
893
+ .default(null)
894
+ .describe("The vendor CLI session id; null when none exists."),
895
+ observedModel: z
896
+ .string()
897
+ .nullable()
898
+ .default(null)
899
+ .describe("Model last observed on the session's stream."),
900
+ /** Credential profile the vendor session was created under (INV-135):
901
+ * resume never crosses profiles, so clients must be able to SEE the
902
+ * binding they are subject to (round-15 #3). */
903
+ profileId: Id.nullable()
904
+ .default(null)
905
+ .describe("Credential profile the vendor session was created under; resume never crosses profiles (null = engine-default credentials)."),
906
+ state: z
907
+ .enum(["live", "stale", "rebound"])
908
+ .default("live")
909
+ .describe("Session cache state: live, stale, or rebound."),
721
910
  })
722
911
  .describe("Control-plane projection of a vendor CLI session bound to a thread.");
723
912
  /**
@@ -735,10 +924,17 @@ export const ControlTurnRunCard = z
735
924
  .describe("Engine strategy flag on the mode, when any."),
736
925
  n: z.number().int().optional().describe("Race width, when the run was a race."),
737
926
  result: ControlRunResult.default({}),
738
- spendUsd: z.number().nullable().optional().describe("Settled spend in USD; null when unknown."),
927
+ spendUsd: z.number().nullable().optional().describe("Settled cash in USD; null when unknown."),
739
928
  outputReadyState: OutputReadyState.default("pending"),
740
- waitingOnUser: z.boolean().default(false).describe("True while an interactive question is awaiting the user's answer."),
741
- finishedAt: z.string().nullable().default(null).describe("When the run finished; null while live."),
929
+ waitingOnUser: z
930
+ .boolean()
931
+ .default(false)
932
+ .describe("True while an interactive question is awaiting the user's answer."),
933
+ finishedAt: z
934
+ .string()
935
+ .nullable()
936
+ .default(null)
937
+ .describe("When the run finished; null while live."),
742
938
  })
743
939
  .describe("Compact run state embedded on a turn so a chat surface renders the whole conversation from one thread fetch.");
744
940
  export const ControlThreadTurn = z
@@ -748,11 +944,15 @@ export const ControlThreadTurn = z
748
944
  runId: Id.nullable().default(null).describe("Run backing this turn; null while unbound."),
749
945
  parentRunId: Id.nullable().default(null).describe("Run this turn follows up on, when any."),
750
946
  /** Set when this turn implements an approved plan from an earlier run. */
751
- planRunId: Id.nullable().default(null).describe("Set when this turn implements an approved plan from an earlier run."),
947
+ planRunId: Id.nullable()
948
+ .default(null)
949
+ .describe("Set when this turn implements an approved plan from an earlier run."),
752
950
  kind: ThreadTurnKind.default("followup"),
753
951
  prompt: z.string().default("").describe("The user's message for this turn."),
754
952
  /** Embedded run card (outcome/state) so the chat renders without N+1 fetches. */
755
- run: ControlTurnRunCard.nullable().default(null).describe("Embedded run card (outcome/state); null while no run is bound."),
953
+ run: ControlTurnRunCard.nullable()
954
+ .default(null)
955
+ .describe("Embedded run card (outcome/state); null while no run is bound."),
756
956
  /** Why this turn has NO run (enqueue/preflight refusal, e.g. the trust
757
957
  * gate) — surfaces render it as an inline failure card with the remedy;
758
958
  * null once a run binds (retry clears it). `code` is the typed throw's
@@ -762,7 +962,11 @@ export const ControlThreadTurn = z
762
962
  enqueueError: z
763
963
  .object({
764
964
  message: z.string().describe("Human-readable refusal message."),
765
- code: z.string().nullable().default(null).describe("Machine-readable refusal code; remedies key on it, never on the message text."),
965
+ code: z
966
+ .string()
967
+ .nullable()
968
+ .default(null)
969
+ .describe("Machine-readable refusal code; remedies key on it, never on the message text."),
766
970
  retryable: z
767
971
  .boolean()
768
972
  .default(true)
@@ -782,9 +986,13 @@ export const ControlThreadCreateRequest = z
782
986
  mode: ModeKind.optional().describe("Default mode for new turns."),
783
987
  workspace: WorkspaceMode.optional().describe("Workspace mode for the thread (in_place or isolated)."),
784
988
  authPreference: AuthPreference.optional().describe("Per-thread auth preference override."),
989
+ credentialProfileId: NonBlankString.optional().describe("Sticky credential profile for the thread (INV-135); per-turn selection wins."),
785
990
  primaryHarness: NonBlankString.optional().describe("Sticky primary harness for the thread."),
786
991
  /** Sticky eligible pool for the thread; turns inherit it when unset. */
787
- eligibleHarnesses: z.array(NonBlankString).optional().describe("Sticky eligible harness pool; turns inherit it when unset."),
992
+ eligibleHarnesses: z
993
+ .array(NonBlankString)
994
+ .optional()
995
+ .describe("Sticky eligible harness pool; turns inherit it when unset."),
788
996
  })
789
997
  .strict()
790
998
  .describe("Request body for POST /threads.");
@@ -793,35 +1001,20 @@ export const ControlThreadCreateRequest = z
793
1001
  export const ControlThreadUpdateRequest = z
794
1002
  .object({
795
1003
  title: z.string().optional().describe("New thread title."),
796
- state: ThreadState.optional().describe("New thread state (active/closed)."),
797
- primaryHarness: NonBlankString.nullable().optional().describe("New sticky primary harness; null clears back to engine routing."),
798
- eligibleHarnesses: z.array(NonBlankString).optional().describe("New sticky eligible harness pool."),
1004
+ state: z.enum(["active", "closed"]).optional().describe("New open/archive state."),
1005
+ primaryHarness: NonBlankString.nullable()
1006
+ .optional()
1007
+ .describe("New sticky primary harness; null clears back to engine routing."),
1008
+ credentialProfileId: NonBlankString.nullable()
1009
+ .optional()
1010
+ .describe("New sticky credential profile; null clears back to engine-default credentials."),
1011
+ eligibleHarnesses: z
1012
+ .array(NonBlankString)
1013
+ .optional()
1014
+ .describe("New sticky eligible harness pool."),
799
1015
  })
800
1016
  .strict()
801
1017
  .describe("Request body for PATCH /threads/:id: rename, archive, or switch sticky routing.");
802
- /** Apply an isolated thread's accumulated worktree diff to the project. */
803
- export const ControlThreadApplyRequest = z
804
- .object({
805
- mode: z
806
- .enum(["apply", "branch", "commit", "pr"])
807
- .default("apply")
808
- .describe("Delivery mode: apply to the tree, or as a branch, commit, or PR."),
809
- branch: z.string().optional().describe("Branch name for branch/pr modes."),
810
- message: z.string().optional().describe("Commit message for commit/pr modes."),
811
- })
812
- .strict()
813
- .describe("Request body applying an isolated thread's accumulated worktree diff to the project.");
814
- export const ControlThreadApplyResponse = z
815
- .object({
816
- applied: z.boolean().describe("Whether anything was delivered."),
817
- status: z
818
- .enum(["applied", "branched", "committed", "pr_opened", "empty", "conflict", "rejected"])
819
- .describe("Delivery outcome: applied, branched, committed, pr_opened, empty (no diff), conflict, or rejected."),
820
- /** True when the project HEAD moved past the thread base since the thread started. */
821
- headMoved: z.boolean().default(false).describe("True when the project HEAD moved past the thread base since the thread started."),
822
- detail: z.string().nullable().default(null).describe("Human-readable detail."),
823
- })
824
- .describe("Response to a thread apply.");
825
1018
  export const ControlThreadListResponse = z
826
1019
  .object({
827
1020
  threads: z.array(ControlThread).default([]).describe("All threads."),
@@ -834,33 +1027,12 @@ export const ControlThreadDetail = z
834
1027
  turns: z.array(ControlThreadTurn).default([]).describe("Turns of the conversation, in order."),
835
1028
  })
836
1029
  .describe("Full thread detail served by GET /threads/:id: the thread, its vendor sessions, and its turns.");
837
- export const HarnessStatusDto = z
838
- .object({
839
- id: z.string().describe("Harness id."),
840
- status: AdapterStatus,
841
- manifest: HarnessManifest.nullable().optional().describe("The harness's declared manifest, when available."),
842
- enabledIntents: z.array(z.string()).default([]).describe("Intents the gateway will route to this harness."),
843
- disabledIntents: z.array(z.string()).default([]).describe("Intents the doctor disabled."),
844
- checks: z.array(ConformanceCheck).default([]).describe("Doctor probe results."),
845
- reasons: z.array(z.string()).default([]).describe("Human-readable reasons for degraded/unavailable status."),
846
- /** The user's configured per-harness default model, if any. */
847
- configuredModel: z.string().nullable().default(null).describe("The user's configured per-harness default model, if any."),
848
- /** Strict truth-source check of `configuredModel`: null when no model
849
- * is configured; a rejection carries the actionable message so UIs render
850
- * the same honesty `claudexor doctor` prints. */
851
- configuredModelCheck: z
852
- .object({
853
- status: z.enum(["ok", "rejected"]).describe("Whether the configured model passes the strict truth-source check."),
854
- message: z.string().nullable().default(null).describe("Actionable rejection message, when rejected."),
855
- })
856
- .nullable()
857
- .default(null)
858
- .describe("Strict truth-source check of configuredModel; null when no model is configured."),
859
- })
860
- .describe("Doctor-backed status row for one harness: status, intents, checks, and configured-model validity.");
861
1030
  export const ControlHarnessListResponse = z
862
1031
  .object({
863
- harnesses: z.array(HarnessStatusDto).default([]).describe("Status rows for all known harnesses."),
1032
+ harnesses: z
1033
+ .array(HarnessStatusDto)
1034
+ .default([])
1035
+ .describe("Status rows for all known harnesses."),
864
1036
  })
865
1037
  .describe("Response for GET /harnesses.");
866
1038
  /**
@@ -873,7 +1045,10 @@ export const ControlHarnessListResponse = z
873
1045
  export const ControlHarnessModelsResponse = z
874
1046
  .object({
875
1047
  harnessId: z.string().describe("Harness the models belong to."),
876
- models: z.array(HarnessModel).default([]).describe("Enumerable models; empty when the harness has no model truth source."),
1048
+ models: z
1049
+ .array(HarnessModel)
1050
+ .default([])
1051
+ .describe("Enumerable models; empty when the harness has no model truth source."),
877
1052
  source: z
878
1053
  .enum(["api", "manifest", "none"])
879
1054
  .describe("Provenance of the list: api (a live vendor enumeration), manifest (the manifest's known-good hint set), or none (no model truth source; explicit models are refused)."),
@@ -888,8 +1063,10 @@ export const ControlHarnessModelsResponse = z
888
1063
  .describe("Models enumerable for one harness, with honest provenance.");
889
1064
  export const ControlSettingsSnapshot = z
890
1065
  .object({
891
- sources: z.array(z.string()).default([]).describe("Config file paths that contributed to the snapshot."),
892
- defaultPortfolio: Portfolio.default("subscription-first").describe("Budget portfolio used when a run does not specify one."),
1066
+ sources: z
1067
+ .array(z.string())
1068
+ .default([])
1069
+ .describe("Config file paths that contributed to the snapshot."),
893
1070
  /** How long a run waits for an interactive answer before a benign decline. */
894
1071
  interactionTimeoutMs: z
895
1072
  .number()
@@ -899,26 +1076,44 @@ export const ControlSettingsSnapshot = z
899
1076
  .describe("How long a run waits for an interactive answer before a benign decline, in milliseconds."),
900
1077
  routing: z
901
1078
  .object({
902
- defaultPolicy: z.enum(["auto", "primary"]).default("auto").describe("Default routing policy."),
903
- primaryHarness: z.string().nullable().default(null).describe("Global default primary harness; null = engine decides."),
904
- eligibleHarnesses: z.array(z.string()).default([]).describe("Harness pool eligible for routing/races; empty = all available."),
1079
+ defaultPolicy: z
1080
+ .enum(["auto", "primary"])
1081
+ .default("auto")
1082
+ .describe("Default routing policy."),
1083
+ primaryHarness: z
1084
+ .string()
1085
+ .nullable()
1086
+ .default(null)
1087
+ .describe("Global default primary harness; null = engine decides."),
1088
+ eligibleHarnesses: z
1089
+ .array(z.string())
1090
+ .default([])
1091
+ .describe("Harness pool eligible for routing/races; empty = all available."),
905
1092
  envInheritance: z
906
1093
  .enum(["mirror_native", "clean"])
907
1094
  .default("mirror_native")
908
1095
  .describe("How the child harness env is built: mirror_native inherits the shell env; clean spawns from a minimal allowlist."),
909
1096
  authPreference: AuthPreference.default("auto"),
1097
+ goal: RoutingGoal.default("auto"),
1098
+ paidFallback: PaidFallback.default("when_unavailable"),
1099
+ qualityTiers: QualityTierSet,
910
1100
  })
911
1101
  .default({})
912
1102
  .describe("Global routing settings."),
913
1103
  budget: z
914
1104
  .object({
915
- maxUsdPerRun: z.number().nullable().default(null).describe("Global USD cap per run; null = no cap."),
1105
+ paidBudgetPerRun: PaidBudget.default({ kind: "unlimited" }),
916
1106
  })
917
1107
  .default({})
918
1108
  .describe("Global budget limits."),
919
1109
  runtime: z
920
1110
  .object({
921
- reviewerTimeoutMs: z.number().int().positive().default(600_000).describe("Wall-clock timeout for a reviewer run, in milliseconds."),
1111
+ reviewerTimeoutMs: z
1112
+ .number()
1113
+ .int()
1114
+ .positive()
1115
+ .default(600_000)
1116
+ .describe("Wall-clock timeout for a reviewer run, in milliseconds."),
922
1117
  harnessInactivityTimeoutMs: z
923
1118
  .number()
924
1119
  .int()
@@ -927,9 +1122,24 @@ export const ControlSettingsSnapshot = z
927
1122
  .describe("Inactivity watchdog for harness streams, in milliseconds."),
928
1123
  transientRetry: z
929
1124
  .object({
930
- maxRetries: z.number().int().nonnegative().default(2).describe("Maximum retries for a transient failure."),
931
- initialDelayMs: z.number().int().nonnegative().default(1_000).describe("Initial retry delay in milliseconds."),
932
- maxDelayMs: z.number().int().nonnegative().default(10_000).describe("Maximum retry delay in milliseconds."),
1125
+ maxRetries: z
1126
+ .number()
1127
+ .int()
1128
+ .nonnegative()
1129
+ .default(2)
1130
+ .describe("Maximum retries for a transient failure."),
1131
+ initialDelayMs: z
1132
+ .number()
1133
+ .int()
1134
+ .nonnegative()
1135
+ .default(1_000)
1136
+ .describe("Initial retry delay in milliseconds."),
1137
+ maxDelayMs: z
1138
+ .number()
1139
+ .int()
1140
+ .nonnegative()
1141
+ .default(10_000)
1142
+ .describe("Maximum retry delay in milliseconds."),
933
1143
  })
934
1144
  .default({})
935
1145
  .describe("Bounded retry policy for transient failures."),
@@ -939,17 +1149,54 @@ export const ControlSettingsSnapshot = z
939
1149
  harnesses: z
940
1150
  .record(z.string(), z
941
1151
  .object({
942
- enabled: z.boolean().default(true).describe("Whether the harness participates in routing."),
943
- defaultModel: z.string().nullable().default(null).describe("Per-harness default model; null = the harness's own default."),
944
- effort: EffortHint.nullable().default(null).describe("Default reasoning effort; null = harness default."),
945
- maxTurns: z.number().int().positive().nullable().default(null).describe("Default max agent turns; null = no limit."),
946
- maxRounds: z.number().int().positive().nullable().default(null).describe("Default max convergence rounds; null = engine default."),
947
- maxUsd: z.number().nonnegative().nullable().default(null).describe("Per-harness USD cap; null = no cap."),
948
- toolsAllow: z.array(z.string()).default([]).describe("Tool names allowed for this harness."),
949
- toolsDeny: z.array(z.string()).default([]).describe("Tool names denied for this harness."),
950
- fallbackModel: z.string().nullable().default(null).describe("Model to fall back to on typed fallback signals; null = none."),
1152
+ enabled: z
1153
+ .boolean()
1154
+ .default(true)
1155
+ .describe("Whether the harness participates in routing."),
1156
+ defaultModel: z
1157
+ .string()
1158
+ .nullable()
1159
+ .default(null)
1160
+ .describe("Per-harness default model; null = the harness's own default."),
1161
+ effort: EffortHint.nullable()
1162
+ .default(null)
1163
+ .describe("Default reasoning effort; null = harness default."),
1164
+ maxTurns: z
1165
+ .number()
1166
+ .int()
1167
+ .positive()
1168
+ .nullable()
1169
+ .default(null)
1170
+ .describe("Default max agent turns; null = no limit."),
1171
+ maxRounds: z
1172
+ .number()
1173
+ .int()
1174
+ .positive()
1175
+ .nullable()
1176
+ .default(null)
1177
+ .describe("Default max convergence rounds; null = engine default."),
1178
+ toolsAllow: z
1179
+ .array(z.string())
1180
+ .default([])
1181
+ .describe("Tool names allowed for this harness."),
1182
+ toolsDeny: z
1183
+ .array(z.string())
1184
+ .default([])
1185
+ .describe("Tool names denied for this harness."),
1186
+ fallbackModel: z
1187
+ .string()
1188
+ .nullable()
1189
+ .default(null)
1190
+ .describe("Model to fall back to on typed fallback signals; null = none."),
951
1191
  web: ExternalContextPolicy.default("auto").describe("Default web policy for this harness."),
952
1192
  authPreference: AuthPreference.default("auto"),
1193
+ /** Profile-selection policy (INV-135): what happens when the
1194
+ * selected account hits its quota. Drives the app's auto-switch
1195
+ * toggle read-back. */
1196
+ profileLimitAction: z
1197
+ .enum(["fail", "ask", "rotate"])
1198
+ .default("fail")
1199
+ .describe("Profile-selection limit action: fail (stop), ask (record), rotate (auto-switch to the next eligible account)."),
953
1200
  })
954
1201
  .describe("Per-harness settings."))
955
1202
  .default({})
@@ -963,29 +1210,66 @@ export const ControlSettingsSnapshot = z
963
1210
  export const ControlHarnessSettingsPatch = z
964
1211
  .object({
965
1212
  enabled: z.boolean().optional().describe("Enable/disable the harness for routing."),
966
- defaultModel: NonBlankString.nullable().optional().describe("New per-harness default model; null clears it."),
967
- effort: EffortHint.nullable().optional().describe("New default reasoning effort; null clears it."),
968
- maxTurns: z.number().int().positive().nullable().optional().describe("New max agent turns; null clears the limit."),
969
- maxRounds: z.number().int().positive().nullable().optional().describe("New max convergence rounds; null clears it."),
970
- maxUsd: z.number().nonnegative().nullable().optional().describe("New per-harness USD cap; null clears it."),
1213
+ defaultModel: NonBlankString.nullable()
1214
+ .optional()
1215
+ .describe("New per-harness default model; null clears it."),
1216
+ effort: EffortHint.nullable()
1217
+ .optional()
1218
+ .describe("New default reasoning effort; null clears it."),
1219
+ maxTurns: z
1220
+ .number()
1221
+ .int()
1222
+ .positive()
1223
+ .nullable()
1224
+ .optional()
1225
+ .describe("New max agent turns; null clears the limit."),
1226
+ maxRounds: z
1227
+ .number()
1228
+ .int()
1229
+ .positive()
1230
+ .nullable()
1231
+ .optional()
1232
+ .describe("New max convergence rounds; null clears it."),
971
1233
  toolsAllow: z.array(NonBlankString).optional().describe("New tool allowlist."),
972
1234
  toolsDeny: z.array(NonBlankString).optional().describe("New tool denylist."),
973
- fallbackModel: NonBlankString.nullable().optional().describe("New fallback model; null clears it."),
1235
+ fallbackModel: NonBlankString.nullable()
1236
+ .optional()
1237
+ .describe("New fallback model; null clears it."),
974
1238
  web: ExternalContextPolicy.optional().describe("New default web policy."),
975
1239
  authPreference: AuthPreference.optional().describe("New auth route preference."),
1240
+ /** Auto-switch accounts on quota limits (INV-135): rotate enables the
1241
+ * profile-rotation engine for this harness; fail restores the default. */
1242
+ profileLimitAction: z
1243
+ .enum(["fail", "ask", "rotate"])
1244
+ .optional()
1245
+ .describe("New profile-selection limit action (fail | ask | rotate)."),
976
1246
  })
977
1247
  .strict()
978
1248
  .describe("Partial per-harness settings patch; absent fields keep their stored value, and a typoed key 400s (strict).");
979
1249
  export const ControlSettingsUpdateRequest = z
980
1250
  .object({
981
- defaultPortfolio: Portfolio.optional(),
982
- interactionTimeoutMs: z.number().int().positive().optional().describe("New interactive-answer timeout, in milliseconds."),
1251
+ routingGoal: RoutingGoal.optional(),
1252
+ paidFallback: PaidFallback.optional(),
1253
+ qualityTiers: QualityTierSet.optional(),
1254
+ interactionTimeoutMs: z
1255
+ .number()
1256
+ .int()
1257
+ .positive()
1258
+ .optional()
1259
+ .describe("New interactive-answer timeout, in milliseconds."),
983
1260
  routingPolicy: z.enum(["auto", "primary"]).optional().describe("New default routing policy."),
984
- primaryHarness: NonBlankString.nullable().optional().describe("New global primary harness; null clears back to engine routing."),
985
- eligibleHarnesses: z.array(NonBlankString).optional().describe("New global eligible harness pool."),
986
- envInheritance: z.enum(["mirror_native", "clean"]).optional().describe("New child harness env composition mode."),
987
- maxUsdPerRun: z.number().nonnegative().optional().describe("New global USD cap per run (mutually exclusive with clearMaxUsdPerRun)."),
988
- clearMaxUsdPerRun: z.boolean().optional().describe("Clear the global USD cap per run (mutually exclusive with maxUsdPerRun)."),
1261
+ primaryHarness: NonBlankString.nullable()
1262
+ .optional()
1263
+ .describe("New global primary harness; null clears back to engine routing."),
1264
+ eligibleHarnesses: z
1265
+ .array(NonBlankString)
1266
+ .optional()
1267
+ .describe("New global eligible harness pool."),
1268
+ envInheritance: z
1269
+ .enum(["mirror_native", "clean"])
1270
+ .optional()
1271
+ .describe("New child harness env composition mode."),
1272
+ paidBudgetPerRun: PaidBudget.optional().describe("New global incremental-cash budget per run."),
989
1273
  authPreference: AuthPreference.optional().describe("New global auth route preference."),
990
1274
  harnesses: z
991
1275
  .record(NonBlankString, ControlHarnessSettingsPatch)
@@ -993,30 +1277,5 @@ export const ControlSettingsUpdateRequest = z
993
1277
  .describe("Per-harness settings patches keyed by harness id."),
994
1278
  })
995
1279
  .strict()
996
- .superRefine((value, ctx) => {
997
- if (value.maxUsdPerRun !== undefined && value.clearMaxUsdPerRun === true) {
998
- ctx.addIssue({
999
- code: z.ZodIssueCode.custom,
1000
- path: ["maxUsdPerRun"],
1001
- message: "maxUsdPerRun and clearMaxUsdPerRun are mutually exclusive",
1002
- });
1003
- }
1004
- })
1005
1280
  .describe("Request body for PATCH /settings; absent fields keep their stored values.");
1006
- /** Secret list row: exactly what the store's list() can honestly produce
1007
- * (the never-populated harnesses/env/description fields were retired in the
1008
- * v0.15 triage). */
1009
- export const SecretMetadata = z
1010
- .object({
1011
- name: z.string().describe("Secret name/label."),
1012
- backend: z.enum(["keychain", "file"]).describe("Where the secret is stored: the OS keychain or a file store."),
1013
- present: z.boolean().default(true).describe("Whether a value is actually stored."),
1014
- })
1015
- .describe("Secret list row: name, storage backend, and presence — never the value.");
1016
- export const ControlSecretListResponse = z
1017
- .object({
1018
- backend: z.enum(["keychain", "file"]).describe("Active secret store backend."),
1019
- secrets: z.array(SecretMetadata).default([]).describe("Stored secrets (metadata only, never values)."),
1020
- })
1021
- .describe("Response for listing stored secrets (metadata only).");
1022
1281
  //# sourceMappingURL=control.js.map