@intentic/sandbox-contract 1.240.0 → 1.243.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/README.md +52 -16
  2. package/dist/agent-catalog.d.ts +5 -28
  3. package/dist/agent-catalog.d.ts.map +1 -1
  4. package/dist/agent-catalog.js +21 -157
  5. package/dist/agent-catalog.js.map +1 -1
  6. package/dist/agent-run-model.d.ts +3 -2
  7. package/dist/agent-run-model.d.ts.map +1 -1
  8. package/dist/agent-run-model.js +9 -6
  9. package/dist/agent-run-model.js.map +1 -1
  10. package/dist/agent-runtimes.d.ts +27 -0
  11. package/dist/agent-runtimes.d.ts.map +1 -0
  12. package/dist/agent-runtimes.js +113 -0
  13. package/dist/agent-runtimes.js.map +1 -0
  14. package/dist/approvals-execution.d.ts +11 -0
  15. package/dist/approvals-execution.d.ts.map +1 -0
  16. package/dist/approvals-execution.js +44 -0
  17. package/dist/approvals-execution.js.map +1 -0
  18. package/dist/arrival.d.ts +151 -0
  19. package/dist/arrival.d.ts.map +1 -0
  20. package/dist/arrival.js +56 -0
  21. package/dist/arrival.js.map +1 -0
  22. package/dist/card-status.d.ts +5 -0
  23. package/dist/card-status.d.ts.map +1 -0
  24. package/dist/card-status.js +74 -0
  25. package/dist/card-status.js.map +1 -0
  26. package/dist/chores/chores.d.ts.map +1 -1
  27. package/dist/chores/chores.js +1 -1
  28. package/dist/chores/chores.js.map +1 -1
  29. package/dist/command-classes.d.ts +0 -1
  30. package/dist/command-classes.d.ts.map +1 -1
  31. package/dist/command-classes.js +9 -2
  32. package/dist/command-classes.js.map +1 -1
  33. package/dist/command-run.d.ts +10 -0
  34. package/dist/command-run.d.ts.map +1 -0
  35. package/dist/command-run.js +46 -0
  36. package/dist/command-run.js.map +1 -0
  37. package/dist/contracts/agent.contract.d.ts +991 -771
  38. package/dist/contracts/agent.contract.d.ts.map +1 -1
  39. package/dist/contracts/agents.contract.d.ts +345 -337
  40. package/dist/contracts/agents.contract.d.ts.map +1 -1
  41. package/dist/contracts/agents.contract.js +20 -2
  42. package/dist/contracts/agents.contract.js.map +1 -1
  43. package/dist/contracts/approvals.contract.d.ts +99 -0
  44. package/dist/contracts/approvals.contract.d.ts.map +1 -0
  45. package/dist/contracts/approvals.contract.js +32 -0
  46. package/dist/contracts/approvals.contract.js.map +1 -0
  47. package/dist/contracts/ci.contract.d.ts +1 -4
  48. package/dist/contracts/ci.contract.d.ts.map +1 -1
  49. package/dist/contracts/ci.contract.js +1 -9
  50. package/dist/contracts/ci.contract.js.map +1 -1
  51. package/dist/contracts/claude.contract.d.ts +9 -0
  52. package/dist/contracts/claude.contract.d.ts.map +1 -1
  53. package/dist/contracts/cursor.contract.d.ts +6 -0
  54. package/dist/contracts/cursor.contract.d.ts.map +1 -1
  55. package/dist/contracts/git.contract.d.ts +63 -1
  56. package/dist/contracts/git.contract.d.ts.map +1 -1
  57. package/dist/contracts/git.contract.js +22 -4
  58. package/dist/contracts/git.contract.js.map +1 -1
  59. package/dist/contracts/grok.contract.d.ts +3 -0
  60. package/dist/contracts/grok.contract.d.ts.map +1 -1
  61. package/dist/contracts/keys.contract.d.ts +81 -0
  62. package/dist/contracts/keys.contract.d.ts.map +1 -0
  63. package/dist/contracts/keys.contract.js +51 -0
  64. package/dist/contracts/keys.contract.js.map +1 -0
  65. package/dist/contracts/panels.contract.d.ts +7 -0
  66. package/dist/contracts/panels.contract.d.ts.map +1 -1
  67. package/dist/contracts/prepush.contract.js +2 -2
  68. package/dist/contracts/providers.contract.d.ts +2 -0
  69. package/dist/contracts/providers.contract.d.ts.map +1 -1
  70. package/dist/contracts/runner.contract.d.ts +84 -86
  71. package/dist/contracts/runner.contract.d.ts.map +1 -1
  72. package/dist/contracts/safety.contract.d.ts +35 -0
  73. package/dist/contracts/safety.contract.d.ts.map +1 -0
  74. package/dist/contracts/safety.contract.js +32 -0
  75. package/dist/contracts/safety.contract.js.map +1 -0
  76. package/dist/contracts/sessions.contract.d.ts +73 -337
  77. package/dist/contracts/sessions.contract.d.ts.map +1 -1
  78. package/dist/contracts/settings.contract.d.ts +30 -32
  79. package/dist/contracts/settings.contract.d.ts.map +1 -1
  80. package/dist/contracts/system.contract.d.ts +139 -337
  81. package/dist/contracts/system.contract.d.ts.map +1 -1
  82. package/dist/contracts/translator.contract.d.ts +31 -3
  83. package/dist/contracts/translator.contract.d.ts.map +1 -1
  84. package/dist/contracts/usage.contract.d.ts +29 -20
  85. package/dist/contracts/usage.contract.d.ts.map +1 -1
  86. package/dist/contracts/usage.contract.js +13 -0
  87. package/dist/contracts/usage.contract.js.map +1 -1
  88. package/dist/contracts/workspace.contract.d.ts +8 -0
  89. package/dist/contracts/workspace.contract.d.ts.map +1 -1
  90. package/dist/conversation-ids.d.ts +2 -0
  91. package/dist/conversation-ids.d.ts.map +1 -1
  92. package/dist/conversation-ids.js +8 -0
  93. package/dist/conversation-ids.js.map +1 -1
  94. package/dist/credential-material.d.ts +1 -0
  95. package/dist/credential-material.d.ts.map +1 -1
  96. package/dist/credential-material.js +24 -2
  97. package/dist/credential-material.js.map +1 -1
  98. package/dist/definition.d.ts +51 -93
  99. package/dist/definition.d.ts.map +1 -1
  100. package/dist/definition.js +6 -28
  101. package/dist/definition.js.map +1 -1
  102. package/dist/events.d.ts +1831 -2285
  103. package/dist/events.d.ts.map +1 -1
  104. package/dist/events.js +155 -55
  105. package/dist/events.js.map +1 -1
  106. package/dist/history-state.d.ts.map +1 -1
  107. package/dist/history-state.js +1 -0
  108. package/dist/history-state.js.map +1 -1
  109. package/dist/index.d.ts +1959 -1903
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +20 -5
  112. package/dist/index.js.map +1 -1
  113. package/dist/ingress-protocol.d.ts +13 -1
  114. package/dist/ingress-protocol.d.ts.map +1 -1
  115. package/dist/ingress-protocol.js +19 -0
  116. package/dist/ingress-protocol.js.map +1 -1
  117. package/dist/mentions.d.ts +3 -0
  118. package/dist/mentions.d.ts.map +1 -0
  119. package/dist/mentions.js +13 -0
  120. package/dist/mentions.js.map +1 -0
  121. package/dist/needs-action.d.ts +7 -0
  122. package/dist/needs-action.d.ts.map +1 -0
  123. package/dist/needs-action.js +3 -0
  124. package/dist/needs-action.js.map +1 -0
  125. package/dist/overlay-lint.d.ts +7 -0
  126. package/dist/overlay-lint.d.ts.map +1 -0
  127. package/dist/overlay-lint.js +68 -0
  128. package/dist/overlay-lint.js.map +1 -0
  129. package/dist/owner-ticket.d.ts +18 -0
  130. package/dist/owner-ticket.d.ts.map +1 -0
  131. package/dist/owner-ticket.js +51 -0
  132. package/dist/owner-ticket.js.map +1 -0
  133. package/dist/plan-pools.d.ts +13 -0
  134. package/dist/plan-pools.d.ts.map +1 -0
  135. package/dist/plan-pools.js +31 -0
  136. package/dist/plan-pools.js.map +1 -0
  137. package/dist/provider-specs.d.ts +228 -0
  138. package/dist/provider-specs.d.ts.map +1 -0
  139. package/dist/provider-specs.js +124 -0
  140. package/dist/provider-specs.js.map +1 -0
  141. package/dist/quick-model.d.ts.map +1 -1
  142. package/dist/quick-model.js +2 -1
  143. package/dist/quick-model.js.map +1 -1
  144. package/dist/runner-protocol.js +2 -2
  145. package/dist/runner-protocol.js.map +1 -1
  146. package/dist/runtime-state.d.ts +2 -2
  147. package/dist/runtime-state.d.ts.map +1 -1
  148. package/dist/runtime-state.js +1 -1
  149. package/dist/runtime-state.js.map +1 -1
  150. package/dist/safety-policy.d.ts +49 -0
  151. package/dist/safety-policy.d.ts.map +1 -0
  152. package/dist/safety-policy.js +52 -0
  153. package/dist/safety-policy.js.map +1 -0
  154. package/dist/schemas/agent.d.ts +21 -3
  155. package/dist/schemas/agent.d.ts.map +1 -1
  156. package/dist/schemas/agent.js +18 -8
  157. package/dist/schemas/agent.js.map +1 -1
  158. package/dist/schemas/agents.d.ts +19 -0
  159. package/dist/schemas/agents.d.ts.map +1 -1
  160. package/dist/schemas/agents.js +15 -0
  161. package/dist/schemas/agents.js.map +1 -1
  162. package/dist/schemas/approvals.d.ts +241 -0
  163. package/dist/schemas/approvals.d.ts.map +1 -0
  164. package/dist/schemas/approvals.js +63 -0
  165. package/dist/schemas/approvals.js.map +1 -0
  166. package/dist/schemas/automations.d.ts +5 -0
  167. package/dist/schemas/automations.d.ts.map +1 -1
  168. package/dist/schemas/ci.d.ts +5 -9
  169. package/dist/schemas/ci.d.ts.map +1 -1
  170. package/dist/schemas/ci.js +3 -10
  171. package/dist/schemas/ci.js.map +1 -1
  172. package/dist/schemas/computers.d.ts +40 -5
  173. package/dist/schemas/computers.d.ts.map +1 -1
  174. package/dist/schemas/computers.js +16 -5
  175. package/dist/schemas/computers.js.map +1 -1
  176. package/dist/schemas/environment.d.ts +11 -131
  177. package/dist/schemas/environment.d.ts.map +1 -1
  178. package/dist/schemas/environment.js +5 -42
  179. package/dist/schemas/environment.js.map +1 -1
  180. package/dist/schemas/git-history.d.ts +4 -0
  181. package/dist/schemas/git-history.d.ts.map +1 -1
  182. package/dist/schemas/git.d.ts +179 -0
  183. package/dist/schemas/git.d.ts.map +1 -1
  184. package/dist/schemas/git.js +39 -1
  185. package/dist/schemas/git.js.map +1 -1
  186. package/dist/schemas/panels.d.ts +21 -0
  187. package/dist/schemas/panels.d.ts.map +1 -1
  188. package/dist/schemas/panels.js +5 -0
  189. package/dist/schemas/panels.js.map +1 -1
  190. package/dist/schemas/plan-limits.d.ts +50 -3
  191. package/dist/schemas/plan-limits.d.ts.map +1 -1
  192. package/dist/schemas/plan-limits.js +11 -6
  193. package/dist/schemas/plan-limits.js.map +1 -1
  194. package/dist/schemas/provider-oauth.d.ts +6 -0
  195. package/dist/schemas/provider-oauth.d.ts.map +1 -1
  196. package/dist/schemas/provider-subscriptions.d.ts +2 -1
  197. package/dist/schemas/provider-subscriptions.d.ts.map +1 -1
  198. package/dist/schemas/provider-subscriptions.js +2 -1
  199. package/dist/schemas/provider-subscriptions.js.map +1 -1
  200. package/dist/schemas/settings.d.ts +29 -16
  201. package/dist/schemas/settings.d.ts.map +1 -1
  202. package/dist/schemas/settings.js +13 -20
  203. package/dist/schemas/settings.js.map +1 -1
  204. package/dist/schemas/system.d.ts +3 -0
  205. package/dist/schemas/system.d.ts.map +1 -1
  206. package/dist/schemas/system.js +5 -1
  207. package/dist/schemas/system.js.map +1 -1
  208. package/dist/schemas/workspace-repos.d.ts +14 -0
  209. package/dist/schemas/workspace-repos.d.ts.map +1 -1
  210. package/dist/schemas/workspace-repos.js +5 -0
  211. package/dist/schemas/workspace-repos.js.map +1 -1
  212. package/dist/schemas/workspace-tree.d.ts +1 -0
  213. package/dist/schemas/workspace-tree.d.ts.map +1 -1
  214. package/dist/schemas/workspace-tree.js +3 -0
  215. package/dist/schemas/workspace-tree.js.map +1 -1
  216. package/dist/transcript-fold.d.ts +31 -0
  217. package/dist/transcript-fold.d.ts.map +1 -0
  218. package/dist/transcript-fold.js +457 -0
  219. package/dist/transcript-fold.js.map +1 -0
  220. package/dist/workspace-state.d.ts +20 -3
  221. package/dist/workspace-state.d.ts.map +1 -1
  222. package/dist/workspace-state.js +22 -2
  223. package/dist/workspace-state.js.map +1 -1
  224. package/package.json +27 -5
  225. package/src/agent-catalog.test.ts +1 -1
  226. package/src/agent-catalog.ts +76 -555
  227. package/src/agent-run-model.test.ts +31 -15
  228. package/src/agent-run-model.ts +26 -12
  229. package/src/agent-runtimes.ts +419 -0
  230. package/src/approvals-execution.ts +96 -0
  231. package/src/arrival.ts +160 -0
  232. package/src/capability-ledger.test.ts +6 -4
  233. package/src/card-status.ts +93 -0
  234. package/src/chores/chores.ts +6 -3
  235. package/src/command-classes.test.ts +25 -0
  236. package/src/command-classes.ts +47 -5
  237. package/src/command-run.ts +78 -0
  238. package/src/contract-lock.test.ts +1 -1
  239. package/src/contract-lock.ts +2 -2
  240. package/src/contracts/agents.contract.ts +35 -2
  241. package/src/contracts/approvals.contract.ts +37 -0
  242. package/src/contracts/ci.contract.ts +0 -13
  243. package/src/contracts/git.contract.ts +28 -3
  244. package/src/contracts/keys.contract.ts +79 -0
  245. package/src/contracts/prepush.contract.ts +3 -3
  246. package/src/contracts/safety.contract.ts +48 -0
  247. package/src/contracts/usage.contract.ts +22 -0
  248. package/src/conversation-ids.test.ts +31 -1
  249. package/src/conversation-ids.ts +37 -3
  250. package/src/credential-material.test.ts +69 -1
  251. package/src/credential-material.ts +86 -5
  252. package/src/definition.ts +25 -53
  253. package/src/events.ts +374 -173
  254. package/src/history-state.ts +6 -0
  255. package/src/index.ts +24 -5
  256. package/src/ingress-protocol.ts +52 -1
  257. package/src/mentions.test.ts +22 -0
  258. package/src/mentions.ts +25 -0
  259. package/src/needs-action.ts +14 -0
  260. package/src/overlay-lint.test.ts +89 -0
  261. package/src/overlay-lint.ts +116 -0
  262. package/src/owner-ticket.test.ts +54 -0
  263. package/src/owner-ticket.ts +102 -0
  264. package/src/plan-pools.test.ts +71 -0
  265. package/src/plan-pools.ts +92 -0
  266. package/src/provider-specs.test.ts +130 -0
  267. package/src/provider-specs.ts +352 -0
  268. package/src/quick-model.ts +9 -5
  269. package/src/runner-protocol.ts +2 -2
  270. package/src/runtime-state.ts +6 -6
  271. package/src/safety-policy.ts +165 -0
  272. package/src/schemas/agent.ts +62 -17
  273. package/src/schemas/agents.ts +71 -0
  274. package/src/schemas/approvals.ts +174 -0
  275. package/src/schemas/ci.ts +19 -26
  276. package/src/schemas/computers.ts +91 -15
  277. package/src/schemas/environment.ts +21 -100
  278. package/src/schemas/git.ts +121 -6
  279. package/src/schemas/issues.ts +1 -1
  280. package/src/schemas/panels.ts +20 -0
  281. package/src/schemas/plan-limits.ts +58 -11
  282. package/src/schemas/provider-subscriptions.ts +7 -2
  283. package/src/schemas/settings.ts +67 -93
  284. package/src/schemas/system.ts +12 -1
  285. package/src/schemas/workspace-repos.ts +9 -0
  286. package/src/schemas/workspace-tree.ts +11 -0
  287. package/src/transcript-fold.test.ts +493 -0
  288. package/src/transcript-fold.ts +655 -0
  289. package/src/workspace-state.test.ts +77 -29
  290. package/src/workspace-state.ts +99 -31
  291. package/dist/contracts/drafts.contract.d.ts +0 -57
  292. package/dist/contracts/drafts.contract.d.ts.map +0 -1
  293. package/dist/contracts/drafts.contract.js +0 -32
  294. package/dist/contracts/drafts.contract.js.map +0 -1
  295. package/dist/definition-action.d.ts +0 -7
  296. package/dist/definition-action.d.ts.map +0 -1
  297. package/dist/definition-action.js +0 -3
  298. package/dist/definition-action.js.map +0 -1
  299. package/dist/publish-drafts.d.ts +0 -7
  300. package/dist/publish-drafts.d.ts.map +0 -1
  301. package/dist/publish-drafts.js +0 -20
  302. package/dist/publish-drafts.js.map +0 -1
  303. package/dist/schemas/drafts.d.ts +0 -84
  304. package/dist/schemas/drafts.d.ts.map +0 -1
  305. package/dist/schemas/drafts.js +0 -44
  306. package/dist/schemas/drafts.js.map +0 -1
  307. package/src/contracts/drafts.contract.ts +0 -35
  308. package/src/definition-action.ts +0 -8
  309. package/src/publish-drafts.ts +0 -59
  310. package/src/schemas/drafts.ts +0 -108
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { KEY_PROVIDERS, NATIVE_PROVIDERS } from "../provider-specs.js";
2
3
  import { AgentPlacementSchema } from "../runner-protocol.js";
3
4
  import { entryId } from "./internal.js";
4
5
  // The agent runtimes the daemon can serve, the vocabulary every surface that picks an agent shares (chat
@@ -8,16 +9,23 @@ import { entryId } from "./internal.js";
8
9
  // ACP (Agent Client Protocol).
9
10
  // Kept as a bare string on the wire (not an enum) so an unknown id is a clean error frame from the agent
10
11
  // route, the same bet RepoParamSchema makes, and adding an ACP agent needs no contract change.
11
- export const NATIVE_PROVIDERS = ["claude", "codex", "grok", "kimi", "gemini", "cursor"] as const;
12
- export type NativeProvider = (typeof NATIVE_PROVIDERS)[number];
12
+ //
13
+ // NATIVE_PROVIDERS itself is DERIVED from the one row-per-provider table (provider-specs.ts) and imported
14
+ // here rather than written out again, so the wire vocabulary cannot name a provider the product does not
15
+ // describe, or miss one it does.
13
16
  export const AgentProviderSchema = z.string().min(1);
14
17
  export type AgentProvider = z.infer<typeof AgentProviderSchema>;
15
18
  // The provider naming a catalog in the one route every native provider shares (providers.contract.ts). An ENUM
16
19
  // rather than the bare-string schema above, and deliberately so: the open vocabulary exists because an ACP agent
17
20
  // or an endpoint can be added without a contract change, but neither has a daemon-held catalog, this route's
18
- // subjects are exactly the five the daemon keeps one for. Closing it here is what makes an unknown id a 400 from
21
+ // subjects are exactly the ones the daemon keeps one for. Closing it here is what makes an unknown id a 400 from
19
22
  // the contract instead of a registry lookup that reads back `undefined` and serves an empty list.
20
23
  export const NativeProviderParamSchema = z.object({ provider: z.enum(NATIVE_PROVIDERS) });
24
+ // The provider naming an account on the routes that connect one by pasting a key (keys.contract.ts). Closed the
25
+ // same way and for the same reason as the catalog param above, narrowed to the providers whose credential this
26
+ // daemon actually stores as a key: pasting one at a provider that authenticates some other way is a 400 from
27
+ // the contract rather than a handler discovering there is no store to write to.
28
+ export const KeyProviderParamSchema = z.object({ provider: z.enum(KEY_PROVIDERS) });
21
29
  // The harness (agentic loop) a turn runs on, orthogonal to the provider. See AgentTurnSchema.harness.
22
30
  export const AgentHarnessSchema = z.enum(["native", "claude-code"]);
23
31
  export type AgentHarness = z.infer<typeof AgentHarnessSchema>;
@@ -275,7 +283,7 @@ export const AgentTurnSchema = z
275
283
  * opposite defaults, the chat wants the provider's own catalog default, an unattended run wants the
276
284
  * tier its owner chose for work that spends money while they are not watching.
277
285
  *
278
- * The daemon fills `agent`/`model`/`effort` from agentRunModels/agentRunEffort for any turn that says
286
+ * The daemon fills `agent`/`model` and the pinned entry's own knobs from agentRunModels for any turn that says
279
287
  * this and names none of them (startConversationTurn), walking that list until one can actually be
280
288
  * started. Naming one still wins: every surface-started run now carries a caret that overrides the list
281
289
  * for that run alone, and Acceptance picks per run because it fans a session out per story. Either way
@@ -384,7 +392,14 @@ export type AgentTurn = z.infer<typeof AgentTurnSchema>;
384
392
  *
385
393
  * Both halves or neither, because a model id is only meaningful to the provider that vends it: half a pick
386
394
  * would send a Codex model id to Claude. Routes that accept this pass it through verbatim; a model this build
387
- * has never heard of is a supported pick, since the picker offers a custom-id escape hatch. */
395
+ * has never heard of is a supported pick, since the picker offers a custom-id escape hatch.
396
+ *
397
+ * AND THE TIER IT RUNS AT, because naming a model is only half of what the standing setting says. A pinned
398
+ * entry carries its own effort (AgentRunPinSchema), and the daemon applies the pin's knobs ONLY to a turn that
399
+ * named no model (turn-resume.ts): so a caret that could re-point the model but not the tier moved every
400
+ * override onto the provider's own default effort, and the one moment somebody reaches for the caret is the
401
+ * failure that just beat the standing order. Optional, and absent means absent, the turn goes out without an
402
+ * effort and the model's own answers. */
388
403
  export const AgentRunPickSchema = z
389
404
  .object({
390
405
  agent: z.string().min(1).describe("Which provider."),
@@ -392,31 +407,61 @@ export const AgentRunPickSchema = z
392
407
  .string()
393
408
  .min(1)
394
409
  .describe("Which of its models. Both or neither, because a model name only means anything to the provider that serves it."),
410
+ effort: z
411
+ .string()
412
+ .optional()
413
+ .describe("How hard that model should think, where it offers a choice. Leave it out to take the model's own default."),
395
414
  })
396
415
  .optional();
397
416
  export type AgentRunPick = z.infer<typeof AgentRunPickSchema>;
417
+ /* A MODEL PINNED FOR EVERY SURFACE-STARTED RUN, one entry of settings.agentRunModels: the standing version of
418
+ * the pick above, and not merely which model but HOW it is to be run.
419
+ *
420
+ * THE KNOBS RIDE THE ENTRY RATHER THAN THE LIST, which is the whole reason this is an object where the setting
421
+ * used to hold a `${provider}:${model}` string. The reasoning effort was a single field beside the list, so one
422
+ * tier answered for every model in it — and the entries of that list are deliberately NOT interchangeable: it
423
+ * is a frontier pin with the cheap account underneath that catches it when the first is spent. A tier scale is
424
+ * a property of the MODEL as well ('max' is off Kimi's scale entirely, and off Claude's own the moment thinking
425
+ * is switched off), so a shared effort was either off-scale for half the list or the lowest common rung for all
426
+ * of it. Each entry now carries what the composer's picker configures for the turn in front of you.
427
+ *
428
+ * EVERY FIELD BUT THE PAIR IS OPTIONAL, AND ABSENT MEANS ABSENT: the turn goes out without the field and the
429
+ * provider's own default answers, exactly as an unconfigured pin always did. Nothing here invents a "low".
430
+ *
431
+ * NO TIER HOLD, and its absence is the rule rather than an omission: automatic tier selection gates on
432
+ * `unattended` (prompt-complexity.ts), so a surface-started run is never downgraded in the first place and a
433
+ * veto over it would be a control whose state can make no difference to anything.
434
+ *
435
+ * The pair is BOTH HALVES for the reason the pick above is: a model id is only meaningful to the provider that
436
+ * vends it, so half a pin would send a Codex id to Claude. Taken verbatim, never validated against a catalog:
437
+ * the picker offers a custom-id escape hatch, so a model this build has never heard of is a supported pin. */
438
+ export const AgentRunPinSchema = z.object({
439
+ provider: AgentProviderSchema.describe("Which provider serves the run."),
440
+ model: z.string().min(1).describe("Which of its models. Both halves, because a model name only means anything to the provider that serves it."),
441
+ effort: z
442
+ .string()
443
+ .optional()
444
+ .describe("How hard this model should think, where it offers a choice. Leave it out to take the model's own default."),
445
+ thinking: z.boolean().optional().describe("Whether this model reasons before it answers, where that is a choice it offers."),
446
+ fast: z.boolean().optional().describe("Ask for this model's work at a higher rate for a higher price. A request rather than a promise."),
447
+ harness: AgentHarnessSchema.optional().describe("Which agentic loop runs it. Leave it out to use the provider's own."),
448
+ });
449
+ export type AgentRunPin = z.infer<typeof AgentRunPinSchema>;
398
450
  // POST /agent's ack: the daemon-minted id of the detached turn run it started. The turn executes daemon-side
399
451
  // regardless of any client connection; every window, the initiator included, renders it via /agent/attach.
400
452
  export const StartedTurnSchema = z.object({
401
453
  run: z.string().describe("The id of the run that just started. Hand it back when you attach, so the stream resumes rather than replaying."),
402
454
  });
403
455
  export type StartedTurn = z.infer<typeof StartedTurnSchema>;
404
- // Attach to a conversation's turn run (live, or finished within the retention window). `run`+`after` is the
405
- // resume cursor of a client whose stream dropped: frames after `after` replay when `run` still names the
406
- // current run; a mismatch (a newer turn started meanwhile) replays that run from its first frame instead.
456
+ // Attach to a conversation's turn run (live, or finished within the retention window). The head carries the
457
+ // run's rows whole, so there is no cursor to resume from: a client that reconnects takes the rows again and
458
+ // applies what follows. `run` names the run the client was watching, so the head's own id tells it whether a
459
+ // newer turn has started meanwhile.
407
460
  export const AttachTurnSchema = z.object({
408
461
  conversationId: ConversationIdSchema.describe("Which conversation to watch."),
409
462
  run: z
410
463
  .string()
411
464
  .optional()
412
- .describe("The run you were watching. If a newer turn has started since, the stream replays that one from its beginning instead."),
413
- after: z
414
- .number()
415
- .int()
416
- .min(0)
417
- .optional()
418
- .describe(
419
- "The last frame you already have. Everything after it replays, then the stream goes live. Leave it out to start from the beginning.",
420
- ),
465
+ .describe("The run you were watching. If a newer turn has started since, the head names that one instead, and its rows are that turn's."),
421
466
  });
422
467
  export type AttachTurn = z.infer<typeof AttachTurnSchema>;
@@ -204,6 +204,52 @@ export const AgentSummarySchema = z.object({
204
204
  .describe(
205
205
  "Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.",
206
206
  ),
207
+ /* WHICH KIND OF FAILURE IT WAS, the error frame's own code (AgentEvent's `error`), beside the sentence it
208
+ * came with. The sentence says what happened to a person; this says it to the board, which has to DRAW the
209
+ * difference and could not.
210
+ *
211
+ * The chat has always had it, and the gap between the two surfaces is the whole reason this exists. A chat
212
+ * reads `rate_limit` and renders a muted notice with a countdown and a press; the board read `status:
213
+ * "error"` and rendered a red crash line with "View error" on it, because the code never reached the
214
+ * summary. So one spent allowance looked exactly like a harness that died mid-run, on the surface where
215
+ * most people meet it, and the state the product knows most about (it knows when it ends) was the one
216
+ * drawn with the least. */
217
+ failureCode: z
218
+ .string()
219
+ .optional()
220
+ .describe(
221
+ "Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.",
222
+ ),
223
+ /* WHEN THE SPENT WINDOW REOPENS (epoch SECONDS), for a card whose last turn a usage limit refused. The one
224
+ * fact this failure has and no other failure here does, and the reason it is not a failure at all so much
225
+ * as a wait: nothing is broken, nobody has anything to fix, and the thing that changes the outcome is a
226
+ * clock. A card that cannot say the hour has to spend its line saying "Error" instead, which is how an
227
+ * 18-hour-old refusal went on reporting a wall that had reopened before breakfast.
228
+ *
229
+ * Absent means the instant is genuinely unknown, which for Grok (no published quota) and Cursor (not routed
230
+ * through the translator) is the honest answer and stays one. Never invent one: a countdown to a guess is
231
+ * worse than no countdown, because the reader plans around it. */
232
+ limitResetsAt: z.number().optional().describe("When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant."),
233
+ /* THE DAEMON IS STILL HOLDING THAT EXACT TURN, so the way on is one press that RE-RUNS it rather than a new
234
+ * message saying "carry on" (turn-resume.ts's pendingLimit has the whole argument, and the transcript full
235
+ * of the word "Continue" that made it). On the summary because the board is where somebody with four
236
+ * stranded agents is standing, and until now the press existed only inside each chat. */
237
+ limitHeld: z.boolean().optional().describe("Whether the refused turn is held whole, so sending again re-runs it instead of appending to it."),
238
+ /* A FIRE IS ALREADY BOOKED FOR THIS ONE: the conversation is armed (`resumeAfterLimit`), the reset instant
239
+ * is known, and the daemon's pass will send the held turn again when the window opens without anybody
240
+ * pressing anything.
241
+ *
242
+ * IT IS THE ONE THING THAT MOVES A STRANDED CARD OUT OF THE ATTENTION LANE, and the only reason it needs to
243
+ * be on the wire at all. "Does this session need me?" is the question that lane answers, and for a spent
244
+ * allowance the honest answer is yes: the window reopening does not send the turn, a person does. Unless
245
+ * this is set, in which case a machine does, and demanding a press for work already booked is the same
246
+ * false alarm as demanding one for a turn that is running.
247
+ *
248
+ * A BOOLEAN THE DAEMON ALREADY DECIDED rather than a posture the client re-folds. The effective posture is
249
+ * two levels deep (this conversation's override, else the sandbox setting), and the lane machine is a leaf
250
+ * with no store to ask: threading a settings read through every caller of `laneOf` would put the answer in
251
+ * five places and let them disagree. The daemon resolves it once, where the failure happened. */
252
+ limitScheduled: z.boolean().optional().describe("Whether the held turn is already booked to go again at the reset, so nobody has to press anything."),
207
253
  provider: AgentProviderSchema.describe("Which model provider it runs on."),
208
254
  harness: AgentHarnessSchema.describe("Which agentic loop it runs on."),
209
255
  // Which machine its turns execute on: a paired runner's id, absent for this sandbox (runners/). Latched
@@ -265,6 +311,19 @@ export const AgentSummarySchema = z.object({
265
311
  * never expressed an opinion follows the sandbox wherever it is pointed next. Written by
266
312
  * `agents.resumeAfterOutage`; every surface shows the EFFECTIVE value (this ?? the setting). */
267
313
  resumeAfterOutage: z.boolean().optional(),
314
+ /* The same two-level shape again, for the blocker that is not a failure: "when a usage limit refused my
315
+ * turn, send it again the moment the allowance comes back".
316
+ *
317
+ * IT IS THE ONE RESUME WITH A KNOWN HOUR, which is what makes it worth automating and what makes it
318
+ * different from its neighbour. An outage resume guesses (a backoff, a bounded number of attempts, no idea
319
+ * when the provider returns); this one waits for an instant the provider published and fires once, at it.
320
+ *
321
+ * OFF unless somebody says otherwise, and that default is load-bearing rather than cautious. Every other
322
+ * blocker here clears at no cost to the user, while this one clears into a window they may have been
323
+ * saving: spending it the second it reopens is not a decision to make on anybody's behalf. What arming it
324
+ * buys is the case nothing else can reach, a turn that hit the wall at 2am on a board nobody is watching,
325
+ * where the alternative is a card that waited eight hours for a press that was always going to come. */
326
+ resumeAfterLimit: z.boolean().optional(),
268
327
  // A collaborator asked for this agent's work to be landed (agents.requestLand), collaborators may drive
269
328
  // agents but not merge into the main tree, so the ask rides the summary where every maintainer's board
270
329
  // sees it. Cleared by the land or discard that answers it. Absent ⇒ nobody is waiting.
@@ -681,6 +740,18 @@ export const AgentResumeAfterOutageSchema = z.object({
681
740
  .nullable()
682
741
  .describe("Whether it retries by itself when the model provider was what failed. Null clears the override back to the sandbox-wide setting."),
683
742
  });
743
+ // resumeAfterLimit's input, the same override in the same three states as the outage one above, for the
744
+ // blocker that comes back on a clock. Written by the card's own offer at the moment a limit strands a turn,
745
+ // which is where somebody looking at a stranded card actually is; the settings toggle writes the default.
746
+ export const AgentResumeAfterLimitSchema = z.object({
747
+ id: z.string().min(1).describe("Which conversation."),
748
+ resumeAfterLimit: z
749
+ .boolean()
750
+ .nullable()
751
+ .describe(
752
+ "Whether the turn a spent allowance refused is sent again by itself once the window reopens. Null clears the override back to the sandbox-wide setting.",
753
+ ),
754
+ });
684
755
  export const AgentFileDiffQuerySchema = z.object({
685
756
  id: z.string().min(1).describe("Which conversation."),
686
757
  repo: z.string().min(1).describe("Which repository."),
@@ -0,0 +1,174 @@
1
+ // approvals: things the agent prepared and may not do until the owner says yes (.intentic/config/approvals/<id>.json)
2
+ import { z } from "zod";
3
+ import { entryId } from "./internal.js";
4
+
5
+ /* ONE QUEUE FOR EVERYTHING THAT WAITS ON A YES, and the test for what belongs in it: the agent prepared an exact
6
+ * thing, the owner's click releases it, a machine then carries out precisely that thing, and the outcome is
7
+ * written back. A post is the first such thing and was the whole of this file (it was `drafts`); a hotel
8
+ * booking, a payment, a message sent under the owner's name are the same shape with a different payload, and
9
+ * each of them arriving as its own inbox is how a product grows five tiles that all say "approve".
10
+ *
11
+ * So the record is an ENVELOPE plus a KIND. The envelope is what every approval shares and what the queue, the
12
+ * badge and the daemon act on: who it acts as, when it is due, where it stands, what happened. The kind is what
13
+ * the row draws and the daemon dispatches on, a discriminated union so a new kind is one variant here, one
14
+ * body component in the extension and one executor in the daemon, and nothing else moves.
15
+ *
16
+ * What does NOT belong here, and why the line is drawn where it is: an inbox of FACTS (the issues queue, where
17
+ * a crash arrived and the owner's verbs are resolve / ignore / investigate) has no prepared thing to release,
18
+ * and a hold a RUNNING turn is blocked on (a permission card, a spend offer) lives in the conversation, because
19
+ * a queue is the wrong latency for a click somebody is spinning on.
20
+ *
21
+ * One JSON file per approval. The AGENT creates them with its normal file tools, it can't call daemon routes,
22
+ * the same split as the environment proposal, while the daemon edits/deletes them on the owner's behalf, so
23
+ * the two writers never share a file. The id IS the filename (entryId charset ⇒ path-safe); the body never
24
+ * carries it. */
25
+
26
+ export const ApprovalKindSchema = z.enum(["post", "action"]);
27
+ export type ApprovalKind = z.infer<typeof ApprovalKindSchema>;
28
+
29
+ /* proposed (agent) → approved (owner) → running (executor, set BEFORE acting so a dead turn can't do it twice)
30
+ * → done | failed. Reject = delete the file; retry = re-approve a failed one. The names are deliberately not a
31
+ * post's ("posting", "posted"): they are read by every kind, and a booking is not "posted". */
32
+ export const ApprovalStatusSchema = z.enum(["proposed", "approved", "running", "done", "failed"]);
33
+ export type ApprovalStatus = z.infer<typeof ApprovalStatusSchema>;
34
+
35
+ /* THE ENVELOPE, spread into every variant below (zod's discriminated union wants flat objects, not an extended
36
+ * base), which is also why a variant's own fields sit beside these rather than under a `payload` key: the file
37
+ * an agent writes stays one level deep, and "which fields are mine" is answered by the variant's schema. */
38
+ const envelope = {
39
+ /* WHOSE NAME THIS ACTS UNDER, a PersonaSchema id, handed to the executing turn as AgentTurnSchema.actsAs.
40
+ * Required in practice for every post outside DIRECT_PUBLISH_PLATFORMS, and the reason is the whole shape
41
+ * of turnPersona: acting through a browser needs a logged-in account, and an UNATTENDED turn that names no
42
+ * persona is denied every account there is. Without this field the executor could only wake such a turn,
43
+ * one structurally unable to reach the login the post needs, which read from inside the turn as "this
44
+ * account is not connected" and cost two approved posts before anyone traced it back here.
45
+ *
46
+ * A PERSONA RATHER THAN AN ACCOUNT ID, because that is the vocabulary the rest of the system already speaks:
47
+ * `actsAs` is the only pin turnPersona honours, and a card carries the workspace scope the turn also needs to
48
+ * write this file's own status back. Naming the account directly would invent a second way to say the same
49
+ * thing, and the two would disagree the first time a card's accounts changed.
50
+ *
51
+ * The daemon never guesses it. One site can be connected several times over, five Reddit logins here, and
52
+ * picking for the owner means picking wrong in public, with no undo. A post that needs a turn and names
53
+ * nobody is failed with that sentence instead of sent. An ACTION that names nobody runs with no accounts,
54
+ * which is a legitimate choice for work that needs none. */
55
+ actsAs: entryId
56
+ .optional()
57
+ .describe(
58
+ "Whose name it acts under. Needed for anything that requires being logged in, because an unwatched turn naming nobody is allowed no account at all. Never guessed: one site can be connected five times over, and picking for you means picking wrong in public with no undo.",
59
+ ),
60
+ // Suggested time (epoch ms, the at/nextRun convention). Optional, the agent may propose without a date and
61
+ // the owner sets one at approval; an approved item with no date is dated one hold ahead by the daemon.
62
+ scheduledAt: z
63
+ .number()
64
+ .optional()
65
+ .describe(
66
+ "When it should happen, in milliseconds. An agent may propose without one and you set it when approving; an approved item with no time goes after a short countdown you can still stop.",
67
+ ),
68
+ // Agent-written files only need the kind's own fields; status defaults, the rest are optional, so a
69
+ // well-formed proposal never lands in `invalid` just for omitting bookkeeping fields.
70
+ status: ApprovalStatusSchema.default("proposed").describe(
71
+ "Where it is: proposed by the agent, approved by you, being carried out, done, or failed. Rejecting is deleting it; retrying is approving a failed one again.",
72
+ ),
73
+ createdAt: z.number().optional().describe("When it was written, in milliseconds."),
74
+ // When execution STARTED, stamped with status "running". The executor needs it to tell a run that is under
75
+ // way from one whose turn died mid-flight, and those two are indistinguishable from the due time: a post
76
+ // scheduled for last week is not a post that has been sending since last week.
77
+ startedAt: z
78
+ .number()
79
+ .optional()
80
+ .describe(
81
+ "When it started being carried out, in milliseconds. Needed to tell a run that is under way from one whose turn died mid-flight, which the scheduled time cannot.",
82
+ ),
83
+ finishedAt: z.number().optional().describe("When it was done, in milliseconds."),
84
+ // What came back, when something did: a post's own address, a booking's confirmation. The one thing a done
85
+ // row can offer that reading the proposal cannot: the result itself, to go and look at. A URL is drawn as
86
+ // a link; anything else as the sentence it is.
87
+ result: z
88
+ .string()
89
+ .optional()
90
+ .describe(
91
+ "What came back, when something did: the post's own address, a confirmation number. The one thing a finished item can offer that reading it cannot.",
92
+ ),
93
+ // Why it failed; set with status "failed". Written for the owner to read in the queue, so it is a sentence
94
+ // rather than a code.
95
+ error: z.string().optional().describe("Why it failed, written as a sentence for a person to read rather than as a code."),
96
+ };
97
+
98
+ /* A POST, the first kind: words that go out in public under the owner's name and cannot be recalled. */
99
+ export const PostApprovalSchema = z.object({
100
+ kind: z.literal("post").describe("A post to publish somewhere."),
101
+ // Which skill posts it: "x" | "reddit" | "youtube" | "discord" | …, a bare string so new platforms need
102
+ // no contract change; an unknown platform simply fails at posting time.
103
+ platform: z
104
+ .string()
105
+ .min(1)
106
+ .describe("Where it should go. A plain name, so a new site needs no change here; an unknown one simply fails when it tries to post."),
107
+ content: z.string().min(1).describe("The post itself."),
108
+ // Reddit posts / YouTube uploads need one.
109
+ title: z.string().optional().describe("A title, where the site wants one."),
110
+ /* Where on the platform: subreddit / Discord channel id / community. OR the URL of the thing this post
111
+ * replies to. A URL target means the post is a reply, and on reddit the difference between a thread's
112
+ * address and one comment's permalink is the difference between talking to the room and answering the
113
+ * person: the publisher opens exactly this and replies where it lands. */
114
+ target: z
115
+ .string()
116
+ .optional()
117
+ .describe(
118
+ "Where on the site: a community, a channel. Or the address of the thing this replies to, in which case it is a reply, and on some sites the difference between a thread's address and one comment's is the difference between talking to the room and answering the person.",
119
+ ),
120
+ // Workspace-relative attachment paths, e.g. ".intentic/config/approvals/media/chart.png".
121
+ media: z.array(z.string()).optional().describe("Anything to attach, as workspace paths."),
122
+ ...envelope,
123
+ });
124
+ export type PostApproval = z.infer<typeof PostApprovalSchema>;
125
+
126
+ /* AN ACTION, the general kind: anything else the agent is about to do that it should not do unasked. A
127
+ * booking, a purchase, a message to a person, a deletion that cannot be undone. The agent writes what it will
128
+ * do in words the owner reads (`summary`, `details`), and what it will tell ITSELF to do once released
129
+ * (`instructions`), because the turn that carries it out is a fresh one, hours later, with none of the
130
+ * conversation that led here. Executed by an agent turn, always: there is no typed door for "whatever it is". */
131
+ export const ActionApprovalSchema = z.object({
132
+ kind: z.literal("action").describe("Something the agent will do once you say so."),
133
+ summary: z.string().min(1).max(200).describe("What will happen, in one line: the row's headline and the confirm dialog's item."),
134
+ // Markdown. The specifics a yes is being asked for: the hotel and the dates, the amount and the account,
135
+ // the exact message and its recipient. Everything the owner has to see to be able to say no.
136
+ details: z.string().optional().describe("The specifics, as Markdown: everything you would want to see before saying yes."),
137
+ // What the executing turn is told. Written by the agent for its later self, so it names files, ids and
138
+ // steps rather than saying "do what we discussed".
139
+ instructions: z
140
+ .string()
141
+ .min(1)
142
+ .describe(
143
+ "What to do once approved, written for the fresh turn that will do it: names, ids and steps, since it has none of this conversation.",
144
+ ),
145
+ ...envelope,
146
+ });
147
+ export type ActionApproval = z.infer<typeof ActionApprovalSchema>;
148
+
149
+ // The on-disk file body, whichever kind it is.
150
+ export const ApprovalSchema = z.discriminatedUnion("kind", [PostApprovalSchema, ActionApprovalSchema]);
151
+ export type Approval = z.infer<typeof ApprovalSchema>;
152
+
153
+ // The list row / upsert input: the file body plus its filename id.
154
+ const withId = { id: entryId.describe("The approval's id.") };
155
+ export const PostApprovalSummarySchema = PostApprovalSchema.extend(withId);
156
+ export type PostApprovalSummary = z.infer<typeof PostApprovalSummarySchema>;
157
+ export const ActionApprovalSummarySchema = ActionApprovalSchema.extend(withId);
158
+ export type ActionApprovalSummary = z.infer<typeof ActionApprovalSummarySchema>;
159
+ export const ApprovalSummarySchema = z.discriminatedUnion("kind", [PostApprovalSummarySchema, ActionApprovalSummarySchema]);
160
+ export type ApprovalSummary = z.infer<typeof ApprovalSummarySchema>;
161
+
162
+ // `invalid` = filenames that failed to parse. Agent-written files are a trust boundary, without this a typo'd
163
+ // approval (or one of a kind this daemon does not know) would silently never run.
164
+ export const ApprovalsListSchema = z.object({
165
+ approvals: z.array(ApprovalSummarySchema).describe("The queue."),
166
+ invalid: z
167
+ .array(z.string())
168
+ .describe(
169
+ "Files that could not be read at all, or name a kind this daemon does not know. Listed rather than skipped, because an agent writes these files directly and a malformed one would otherwise never run and never say why.",
170
+ ),
171
+ });
172
+ export type ApprovalsList = z.infer<typeof ApprovalsListSchema>;
173
+ // entryId, not a bare string: the id becomes a filename under .intentic/config/approvals/.
174
+ export const ApprovalIdParamSchema = z.object({ id: entryId.describe("Which approval.") });
package/src/schemas/ci.ts CHANGED
@@ -124,22 +124,8 @@ export const CiRunsResponseSchema = z.object({
124
124
  repos: z.array(CiRepoSchema).describe("Which workspace repositories are wired to a forge, and how each one's notifications are set up."),
125
125
  // Newest first, across all mapped repos.
126
126
  runs: z.array(PipelineRunSchema).describe("Runs across all of them, newest first."),
127
- // When the owner last opened the pipelines view. Rides the runs response so the rail can decide what is
128
- // NEW without a second call, a breakage older than this has already been seen and must not badge again.
129
- // Absent ⇒ never opened, so everything counts as unseen.
130
- seenAt: z
131
- .number()
132
- .optional()
133
- .describe(
134
- "When this was last looked at, in milliseconds, so a badge can tell new breakages from ones already read without a second call. Absent means never, so everything counts as new.",
135
- ),
136
127
  });
137
128
  export type CiRunsResponse = z.infer<typeof CiRunsResponseSchema>;
138
- // Stamping the view as read hands back the timestamp it wrote, so the client updates without a refetch.
139
- export const CiSeenResponseSchema = z.object({
140
- seenAt: z.number().describe("The timestamp that was written, handed back so a caller can update without asking again."),
141
- });
142
- export type CiSeenResponse = z.infer<typeof CiSeenResponseSchema>;
143
129
  // rerun/cancel/fix address a run by repo + vendor id; the daemon re-resolves repo → project + token per call,
144
130
  // so a stale card can't act on a project the workspace no longer maps to.
145
131
  export const CiRunParamSchema = z.object({
@@ -181,21 +167,28 @@ export type CiFixResponse = z.infer<typeof CiFixResponseSchema>;
181
167
  * dialog that started it, and is gone. Nothing survives a daemon restart because nothing needs to: the next
182
168
  * push asks again. */
183
169
 
184
- /* Where a run is.
170
+ /* A COMMAND RUN ON A CLICK, in a visible terminal, with a verdict and a quotable tail: the ONE shape for
171
+ * everything the daemon runs because the owner pressed a button and then has to be told how it went. The
172
+ * pre-push check is one (above); the push itself is another (PushRunSchema, schemas/git.ts), because a push
173
+ * runs the repository's own pre-push hook, which is a suite in disguise. One shape rather than one per
174
+ * moment, so a field the browser learns to read for the check (the terminal to open, the tail to quote, the
175
+ * kill to distinguish from the timeout) is read for the push by the same code, and cannot drift.
176
+ *
177
+ * Where a run is.
185
178
  *
186
179
  * idle , nothing has run in this daemon's life, or the last run was cleared.
187
- * running , the check is live. Its output is the terminal's (`session`), not this object's.
188
- * passed , exited 0. The push goes.
189
- * failed , exited non-zero, or was killed by prepushTimeoutMs (`timedOut`). The state a fix answers.
190
- * error , the check could not run at all: the command was not spawnable. NOT a fix-able failure, because
191
- * there is nothing wrong with the code, the command is misconfigured, and saying "tests failed"
192
- * would send an agent hunting a bug that isn't there.
180
+ * running , the command is live. Its output is the terminal's (`session`), not this object's.
181
+ * passed , exited 0. The push goes (or went).
182
+ * failed , exited non-zero, or was killed by its ceiling (`timedOut`). The state a fix answers.
183
+ * error , the command could not run at all: it was not spawnable. NOT a fix-able failure, because there
184
+ * is nothing wrong with the code, the command is misconfigured, and saying "tests failed" would
185
+ * send an agent hunting a bug that isn't there.
193
186
  * cancelled, the user stopped the run.
194
187
  */
195
- export const PrepushStatusSchema = z.enum(["idle", "running", "passed", "failed", "error", "cancelled"]);
196
- export type PrepushStatus = z.infer<typeof PrepushStatusSchema>;
197
- export const PrepushRunSchema = z.object({
198
- status: PrepushStatusSchema.describe(
188
+ export const CommandRunStatusSchema = z.enum(["idle", "running", "passed", "failed", "error", "cancelled"]);
189
+ export type CommandRunStatus = z.infer<typeof CommandRunStatusSchema>;
190
+ export const CommandRunSchema = z.object({
191
+ status: CommandRunStatusSchema.describe(
199
192
  "Where the run is. Failed and error are deliberately different: failed means the code is wrong, error means the command could not be run at all, and calling the second one a test failure would send an agent hunting a bug that is not there.",
200
193
  ),
201
194
  // The command this run executed, echoed rather than read back from settings: a result read after the
@@ -232,4 +225,4 @@ export const PrepushRunSchema = z.object({
232
225
  "The end of what it printed, as plain text with the colour codes and redrawn progress lines resolved away. The end rather than the beginning, because a suite's verdict is at the end. Empty while it runs, and for one that was killed.",
233
226
  ),
234
227
  });
235
- export type PrepushRun = z.infer<typeof PrepushRunSchema>;
228
+ export type CommandRun = z.infer<typeof CommandRunSchema>;